728x90

PHP 절차지형 방식으로 MySQL 을 연동한 코드를 PDO 방식으로 변환할 때 필요한 정보를 적어둔다.

PDO 는 CUBRID, MS SQL Server, Firebird/Interbase, IBM, Informix, MySQL, Oracle, ODBC and DB2, PostgreSQL, SQLite, 4D 등 12가지 DB를 지원한다.

 

PHP 절차지향
PDO
 mysql_fetch_assoc($result)  $stmt->fetch(PDO::FETCH_ASSOC)
 mysql_num_rows($result)  $stmt->rowCount()
 while ($row = mysql_fetch_assoc($result)) {}  foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {}

 

SELECT 변환 예제

 $sql="select count(*), access from members where userID='".$userID."'";
 $result=mysqli_query($db,$sql);
 if($row=mysqli_fetch_row($result)){
 $sql="select count(*), access from members where userID=?";
 $params = array($userID);
 $stmt = $this->db->prepare($sql);
 $stmt->execute($params);
 if($row = $stmt->fetch()){

 

Update 변환 예제

 $sql = "UPDATE rb_accessLog SET hit=hit+1 Where ipaddr='".$access_ip."' and LogID='".$userID."'";
 $result=mysqli_query($db,$sql);
 $sql = "UPDATE rb_accessLog SET hit=hit+1 Where ipaddr=? and LogID=?";
 try {
    $this->db->beginTransaction();
    $params = array($access_ip,$userID);
    $stmt = $this->db->prepare($sql);
    $stmt->execute($params);
    $this->db->commit();
 } catch (PDOException $pex) {
    $this->db->rollBack();
    echo "에러 : " . $pex -> getMessage();
 }

 

Insert 변환 예제

 $sql ="INSERT INTO bbs_imgpath (uid,relateduid,imgpath) values(NULL,";
 $sql.="'".$uid."', '".$v."')";
 mysqli_query($this->db,$sql);
 $key = "uid,relateduid,imgpath";
 $val = "NULL,$uid,$v";
 $this->getDbInsert('bbs_imgpath', $key, $val);

 

getDbInsert 함수는 아래와 같이 만들었다.

<?php
 function getDbInsert($table$key$val) {
        try {
            $params = explode(','$val); // 문자열을 분리하여 배열로 만듬
            $this->db->beginTransaction();
            $sql = "insert into " . $table . " (" . $key . ") values(" . $this->recur_quot(count($params)) . ")";
            $stmt = $this->db->prepare($sql);
            $status = $stmt->execute($params); // $params 는 배열 값
            if ($status) {
                $this->db->commit();
                return 1;
            } else {
                return 0;
            }
        } catch (PDOException $pex) {
            $this->db->rollBack();
            echo "에러 : " . $pex->getMessage();
        }
    }
 
    public function recur_quot($cnt) {
        $R = array();
        for ($i = 0$i < $cnt$i++) {
            array_push($R"?");
        }
        return implode(","$R); // 배열을 문자열로
    } 
 
?
 

 

Delete 변환 예제

 $sql = "DELETE FROM bbs_imgpath where imgpath='".$v."' and relateduid=".$uid;
 mysqli_query($this->db,$sql);
 
 $where = "imgpath=? and relateduid=?";
 $params = array($v,$uid);
 $this->getDbDelete('bbs_imgpath', $where, $params);

 

getDbDelete 함수는 아래와 같이 만들었다.

 
 
<?php
    public function getDbDelete($table$where$params) {
        if ($this->isDataExisted($table$where$params)) {
            try {
                $this->db->beginTransaction();
                $sql = "delete from " . $table . ($where ? ' where ' . $where : '');
                $stmt = $this->db->prepare($sql);
                $status = $stmt->execute($params);
                if ($status) {// action 실행여부에 대한 결과이지 실제 데이터 삭제와는 무관하네.
                    $this->db->commit();
                    return 1// 삭제 성공
                } else {
                    return 0// 삭제 실패
                }
            } catch (PDOException $pex) {
                $this->db->rollBack();
                echo "에러 : " . $pex->getMessage();
            }
        } else {
            return 0// 삭제할 데이터가 없음
        }
 
    }
 
    // 삭제할 데이터의 존재 유무 파악
    public function isDataExisted($table$where$params) {
        $sql = 'select * from ' . $table . ($where ? ' where ' . $where : '');
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        if ($row = $stmt->fetch()) {
            return $row;
        } else {
            return false;
        }
    }
 
?>
 

 

사용자 함수

편리하게 사용하기 위해 만든 함수는 아래와 같이 변경하면 된다.

다만, 검색조건을 입력시 SQL Injection 필터링 함수를 한번 거쳐서 where 조건문에서 해킹 방지를 해주는 것은 필수요소다.

    function getDbData($table,$where,$column) {
        global $db;
        $sql ='select '.$column.' from '.$table.($where?' where '. $where:'');
        //echo $sql;
        $result = mysqli_query($db,$sql);
        $row = mysqli_fetch_array($result);
        return $row;
    }
     // 검색조건에 일치하는 데이터 가져오기
    public function getDbData($table, $where, $column, $returntype = '') {
        $sql = 'select ' . $column . ' from ' . $table . ($where ? ' where ' . $where : '');
        $stmt = $this->db->prepare($sql);
        $stmt->execute();
        if ($returntype == 1) {
            return $stmt->fetch(PDO::FETCH_ASSOC);
        } else {
            return $stmt->fetch();
        }
    }
     // 검색조건에 일치하는 데이터 가져오기
    public function getDbDataAll($table, $where, $column) {
        $sql = 'select ' . $column . ' from ' . $table . ($where ? ' where ' . $this->getSqlFilter($where) : '');
        $stmt = $this->db->prepare($sql);
        $stmt->execute();
        return $stmt->fetchAll();
    }

 

 

관련으로 만든 Class 대부분은 아래와 같다.

 
<?php
class DBDataClass {
    protected $db// 변수를 선언한 클래스와 상속받은 클래스에서 참조할 수 있다.
 
    public function __construct() {
        $this->dbConnect();
        // construct 메소드는 객체가 생성(인스턴스화)될 때 자동으로 실행되는 특수한 메소드다.
    }
 
    private function dbConnect() {
        require_once 'dbinfo.php';
        try {
            // SQL PDO 객체 생성
            $this->db = new PDO(_DSN, _DBUSER, _DBPASS);
            $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
        } catch(PDOException $ex) {
            die("오류 : " . $ex->getMessage());
        }
    }
 
    // 신규 자료 추가(ok)
    function getDbInsert($table$key$val) {
        try {
            $params = explode(','$val); // 문자열을 분리하여 배열로 만듬
            $this->db->beginTransaction();
            $sql = "insert into " . $table . " (" . $key . ") values(" . $this->recur_quot(count($params)) . ")";
            $stmt = $this->db->prepare($sql);
            $status = $stmt->execute($params); // $params 는 배열 값
            $this->db->commit();
            return 1;
        } catch (PDOException $pex) {
            $this->db->rollBack();
            echo "에러 : " . $pex->getMessage();
            return 0;
        }
    }
 
    public function recur_quot($cnt) {
        $R = array();
        for ($i = 0$i < $cnt$i++) {
            array_push($R"?");
        }
        return implode(","$R); // 배열을 문자열로
    }
 
    function getDbUpdate($table$set$params$where) {
        $sql = "update " . $table . " set " . $set . ($where ? ' where ' . $where : '');
        try {
            $this->db->beginTransaction();
            $stmt = $this->db->prepare($sql);
            $status = $stmt->execute($params);
            $this->db->commit();
            return 1;
        } catch (PDOException $pex) {
            $this->db->rollBack();
            echo "에러 : " . $pex->getMessage();
            return 0;
        }
    }
 
    public function getDbDelete($table$where$params) {
        if ($this->isDataExisted($table$where$params)) {
            try {
                $this->db->beginTransaction();
                $sql = "delete from " . $table . ($where ? ' where ' . $where : '');
                $stmt = $this->db->prepare($sql);
                $status = $stmt->execute($params);
 
                $this->db->commit();
 
                return 1// 삭제 성공
            } catch (PDOException $pex) {
                $this->db->rollBack();
                echo "에러 : " . $pex->getMessage();
            }
        } else {
            return 0// 삭제할 데이터가 없음
        }
 
    }
 
    // 삭제할 데이터의 존재 유무 파악
    public function isDataExisted($table$where$params) {
        $sql = 'select * from ' . $table . ($where ? ' where ' . $where : '');
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        if ($row = $stmt->fetch()) {
            return $row;
        } else {
            return false;
        }
    }
 
 
    // 검색조건에 일치하는 데이터 가져오기
    public function getDbData($table$where$column$returntype = '') {
        $sql = 'select ' . $column . ' from ' . $table . ($where ? ' where ' . $where : '');
        $stmt = $this->db->prepare($sql);
        $stmt->execute();
        if ($returntype == 1) {
            return $stmt->fetch(PDO::FETCH_ASSOC);
        } else {
            return $stmt->fetch();
        }
    }
 
    // 검색조건에 일치하는 데이터 가져오기
    public function getDbDataAll($table$where$column) {
        $sql = 'select ' . $column . ' from ' . $table . ($where ? ' where ' . $where : '');
        $stmt = $this->db->prepare($sql);
        $stmt->execute();
        return $stmt->fetchAll();
    }
 
    // DB Query result 함수
    function getDbresult($table,$where,$column) {
        $sql = 'select ' . $column . ' from ' . $table . ($where ? ' where ' .$where : '');
        $stmt = $this->db->prepare($sql);
        $stmt->execute();
        return $stmt->fetchAll();
    }
 
    // DB 레코드 총 수(ok)
    public function getDbRows($table$where) {
        $sql = 'select count(*) from ' . $table . ($where ? ' where ' . $where : '');
        $stmt = $this->db->prepare($sql);
        $stmt->execute();
        if ($row = $stmt->fetch()) {
            return $row[0];
        } else {
            return false;
        }
    }
}
?> 

 

 

 

 

728x90
블로그 이미지

Link2Me

,
728x90

Web 사이트 개발을 하고 나서 취약점을 점검해볼 수 있는 TOOL 을 설치해봤다.

www.zaproxy.org/download/

에서 다운로드 한다.

 

 

지자체 사이트는 시험삼아 넣어봤더니 결과를 한참 늦게 피드백을 준다.

내 호스팅 사이트는 결과를 엄청 빠르게 보여준다.

보안 설정이 취약한 부분이 뭔지 결과를 경고 메시지로 보여준다.

 

Secure Coding 관련으로 공부를 좀 하고 경고 메시지가 없어지도록 해야겠다.

728x90
블로그 이미지

Link2Me

,
728x90

메일 발송 조건

리눅스 CentOS 6 에서 sendmail 을 설치하고 관련 설정을 한 후 발송 테스트하고 적어둔다.

아래 코드는 회원 정보와 연동한 메일 발송 코드만 발췌한 것이다.

PHP는 자체적으로 메일을 발송하는 기능은 없다.

mail()함수는 메일 발송 폼을 작성하여 SMTP 서버로 전송해주는 역할을 한다.


<?php
$to = $email;
$from = "webmaster@abc.com";
$nameFrom = "AppMaster";

$subject = "비밀번호 변경";
$message ="<table>";
$message.="<tr>";
$message.= "<td>비밀번호는 암호화되어 있으므로, 회원님의 이메일 주소로 임시 비밀번호를 발급해 드립니다.</td>";
$message.="</tr>";
$message.="<tr>";
$message.= "<td>임시 비밀번호로 로그인 후, 반드시 새로운 비밀번호를 설정해 주십시요.</td>";
$message.="</tr>";
$message.="<tr>";
$message.= "<td>임시 비밀번호는  {$newPW}  입니다.</td>";
$message.="</tr>";
$message.="<tr>";
$message.= "<td>Web 사이트로 <a href='http://www.abc.com'>바로 이동</a> 하기</td>";
$message.="</tr>";
$message.="<tr>";
$message.= "<td>본 메일은 <font color=red>발신 전용</font>이라 수신이 불가능합니다.</td>";
$message.="</tr>";
$message.="</table>";

$mailheaders = "Return-Path: $from\r\n";
$mailheaders.= "From: $nameFrom <$from>\r\n";
$mailheaders.= "Content-Type: text/html;charset=utf-8\r\n";
$mailheaders.= "MIME-Version: 1.0\r\n";

mail($to, $subject, $message, $mailheaders, $from);
?>

728x90
블로그 이미지

Link2Me

,
728x90

jQuery 와 PHP 를 조합하여 로그인 실패시 접속시도 5회가 넘으면 접속을 차단하고, 관리자한테 문의하여 비밀번호를 초기화 요청할 수도 있고, 5분(지정시간) 경과후 다시 로그인을 시도할 수 있도록 하는 코드를 구현했다.


jQuery 함수만 적어둔다.


$('#login_submit').on('click',function(e){
    e.preventDefault();
    var userID = $('#loginID');
    var userPW = $('#loginPW');

    if(userID.val() ==''){
        alert('아이디를 입력하세요');
        userID.focus();
        return false;
    }

    if(userPW.val() ==''){
        alert('비밀번호를 입력하세요');
        userPW.focus();
        return false;
    }

    if(userPW.val().search(/\s/) != -1){
        alert("비밀번호는 공백없이 입력해주세요.");
        return false;
    }

    $.ajax({
        url:loginpath+'loginChk.php',
        type: 'POST',
        data: {
            userID:$("#loginID").val(),
            password:$('#loginPW').val()
        },
        dataType:'json',
        success:function(msg){
            switch(msg.result){
                case 0:
                    alert('입력한 정보를 다시 한번 확인하세요.');
                    break;
                case 1:
                case 2:
                    alert('로그인 '+msg.result+'회 실패했습니다');
                    break;
                case 3:
                case 4:
                case 5:
                    alert('로그인 '+msg.result+'회 실패했습니다\n 5회 틀릴 경우 접속이 차단됩니다.');
                    break;
                case 10:
                    alert('관리자가 로그인을 차단했습니다.');
                    break;
                case 11:
                    location.replace('index.php'); // 화면 갱신
                    break;
                case 12:
                    alert('가입 승인대기 중입니다.');
                    break;
                case 13:
                    alert('로그인 횟수 초과로 계정이 잠겼습니다.\n5분 후에 다시 로그인 하세요.');
                    break;
                case 21:
                    location.replace('index.php'); // 화면 갱신
                    break;
                default:
                    alert('다시 한번 확인하세요\n 에러메시지 : '+msg.result);
                    break;
            }
        },
        error: function(jqXHR, textStatus, errorThrown){
            alert("arjax error : " + textStatus + "\n" + errorThrown);
        }
    });

});
 


PHP 핵심 함수 코드이고 나머지 함수는 직접 구현하면 된다.

function LoginSuccessChk($userID,$password){ // Web 접속
    global $db;
    if(empty($userID) || empty($password)){
        return 0;
    } else {
        if($this->isUserExisted($userID) ==1){ // 가입된 userID가 존재하면
            if($this->PasswordChk($userID,$password) == 0){ // 패스워드 불일치
                // 1. access_failed_count 가 5 이상이면 access 를 3 으로 세팅한다.
                $rs = $this->updateAccountLock($userID);
                if($rs == 1){
                    // 3. DB에 저장된 시간과 현재 접속한 시간이 5분이 경과되면 자등으로 access=1 로 설정 변경한다.
                    $gaptime = $this->TimeDiffChk($userID);
                    if($gaptime == 1){
                        $this->clearAccountLock($userID);
                        return 0;
                    } else {
                        // 4. 5분이 경과되지 않았으면 메시지를 팝업한다.
                        return 13;
                    }
                } else {
                    // 2. access_failed_count 를 1 을 증가시킨다.
                    $this->plusLoginFailCount($userID);
                    return $this->LoginFailCountChk($userID); // 로그인 실패 횟수 반환
                }
            } else { // 패스워드 일치
                $rs = $this->AccessStatusChk($userID);
                if($rs == 11) { // 로그인 허용
                    // 1. access_failed_count 를 0 으로 초기화한다.
                    $this->clearLoginFailCount($userID);
                    // 2. access 를 1로 초기화한다.
                    $this->clearAccountLock($userID);
                    // 3. 접속일자 및 시간을 수정한다.
                    $this->AccessLog($userID,'');
                    return 11;
                } else if($rs == 13){
                    // DB에 저장된 시간과 현재 접속한 시간이 5분이 경과되면 자등으로 access=1 로 설정 변경한다.
                    $gaptime = $this->TimeDiffChk($userID);
                    if($gaptime == 1) {
                        $this->clearAccountLock($userID);
                        $this->AccessLog($userID,'');
                        return 11; // 로그인 허용
                    } else {
                        return 13; // 로그인 차단
                    }
                } else {
                    return $rs; // 10:불허, 12:승인대기, 13:차단
                }
            }
        } else {
            return 0; // 가입된 userID가 없으면
        }
    }
}


728x90
블로그 이미지

Link2Me

,
728x90

PHP 날짜와 시간 차이에 대한 테스트 결과다.

그냥 복사해서 테스트해보면 결과를 확인 할 수 있다.

 

<?php
date_default_timezone_set('Asia/Seoul');
$start = '2019-03-23';
$end = '2019-03-25';
$gap = DateDiff($start,$end);
echo '<pre>';print_r($gap);echo '</pre>';

echo DateDisplay( $gap->y, $gap->m, $gap->d).'<br />';
echo $gap->days.'일<br /><br />';

$curTime = date("Y-m-d H:i:s"); // 현재 날짜 및 시간
$access_date = "2019-03-26 09:56:22";

$date1 = strtotime($access_date);
$date2 = strtotime($curTime);
$gaptime = $date2 - $date1;

echo $gaptime.'<br />'; // 초단위 시간으로 반환
echo time_diff($access_date,$curTime).'<br />'; // 초단위 시간으로 반환

$seconds = $gaptime % 60;
$minutes = floor(($gaptime % 3600) / 60);
$hours = floor($gaptime / 3600);
echo $hours.":".$minutes.":".$seconds.'<br />';

$gap = DateDiff($access_date,$curTime);
echo $gap->format('%h:%i:%s').'<br /><br />';

echo '<pre>';print_r($gap);echo '</pre>';
echo DateDisplay( $gap->y, $gap->m, $gap->d).'<br />';

function DateDiff($startDate,$endDate){
    $date1 = new DateTime($startDate);
    $date2 = new DateTime($endDate);

    $gap = $date1->diff($date2);
    return $gap;
}

function DateDisplay($y,$m,$d){
    $gap ='Diff : ';
    if($y > 0) $gap.=$y."년 ";
    if($m > 0) $gap.=$m."월 ";
    $gap.=$d."일 ";
    return $gap;
}

function time_diff($datetime1, $datetime2) {
  return date('U',strtotime($datetime2))-date('U',strtotime($datetime1));
}
?>

 

 

로그인 시도 실패후 5분이 경과되면 다시 로그인을 할 수 있도록 만들기 위한 함수를 만들어 보자.

5분 = 300초 이므로

 

DB 테이블 칼럼에 저장된 시간을 $access_date = "2019-03-26 09:56:22";
이라 하고

현재 시간을 $curTime = date("Y-m-d H:i:s"); // 현재 날짜 및 시간

$gap = time_diff($access_date,$curTime); // 초단위 시간으로 반환

if($gap > 300){

   로그인 차단 설정을 허용으로 변경

} else {

   로그인 차단 메시지 팝업

}

728x90
블로그 이미지

Link2Me

,
728x90

MySQL DB 데이터를 csv 파일 형태로 저장하는 예제코드다.


<?php
$db['host'] = "localhost";
$db['name'] = "db명";
$db['user'] = "사용자id";
$db['pass'] = "비밀번호";
$db['port'] = "3306";
?>

<?php
include_once 'dbinfo.php';
$db = isConnectDb($db);

function isConnectDb($db)
{
    $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
    mysqli_set_charset($conn, "utf8"); 
    if (mysqli_connect_errno()) {
        echo "Failed to connect to MySQL: " . mysqli_connect_error();
        exit;
    } else {
        return $conn;
    }
}
?>

 <?php
$file_name = "cate_backup.xls";
header( "Content-type: application/vnd.ms-excel; charset=utf-8");
header( "Cache-Control: must-revalidate, post-check=0,pre-check=0" );
header( "Content-Disposition: attachment; filename=$file_name" );
header( "Content-Description: PHP4 Generated Data" );
//header() 함수를 쓰기전에 어떠한 출력도 있어선 안된다

include_once 'dbconect.php'; // DB 연동

$tblName = category;    // 테이블명
$sql ="SELECT uid, classname, relateduid FROM $tblName ORDER BY uid DESC";
// DESC 내림차순 정렬, ASC 오름차순 정렬
$result = mysqli_query($db,$sql);

// 테이블 상단 만들기
$EXCEL_STR = "
<table border='1'>
<tr>
<td align=center BGCOLOR='#9DEEE1'>번호</td>
<td align=center BGCOLOR='#9DEEE1'>구분자</td>
<td align=center BGCOLOR='#9DEEE1'>연관번호</td>
</tr>";

while ($row = mysqli_fetch_array($result) ){
    $EXCEL_STR .= "
    <tr>
    <td align=center>".$row['uid']."</td>
    <td align=center>".$row['classname']."</td>
    <td align=center>".$row['relateduid']."</td>
    </tr>
    ";
}

$EXCEL_STR .= "</table>";
echo $EXCEL_STR;
exit;
?>


728x90
블로그 이미지

Link2Me

,
728x90

csv 파일을 PHP를 이용하여 MySQL 에 업로드하는 예제코드다.

실전에서 활용하는 편리한 코드다.


<?php
error_reporting(0); // 경고 출력 방지, 주석처리해서 경고, 에러메시지 나오도록 하자.
require_once 'dbconnect.php';

date_default_timezone_set('Asia/Seoul');

// Edit upload location here
$tmpname    = $_FILES['file']['tmp_name'];
$realname    = $_FILES['file']['name'];

$fileExt    = getExt($realname);
$destination_path='uploads/';
$readFile = $destination_path . 'contactsdata.csv';

$errorFilename ="errorFile.txt";
$errorFile = $destination_path . $errorFilename;

$TABLENAME ='contacts'; // 테이블명

if (is_uploaded_file($tmpname)) {
    if (!strstr('[csv]',$fileExt)) {
        echo '<script type="text/javascript">alert("csv 파일만 등록할 수 있습니다.");</script>';
        exit;
    }
    move_uploaded_file($tmpname,$readFile);
    @chmod($readFile,0606);
}

/////////////////////////////////////////////////////////////////////////////////
$file_read = fopen($readFile,"r");
if(!$file_read){
    echo '<script type="text/javascript">alert("파일을 찾을 수가 없습니다!");</script>';
    echo "-2";
    exit;
}

// 파일 인코딩 모드 검사
$current_encoding = detectFileEncoding($readFile);
$total_line = 0;
$newcount = 0;
$upcount=0;
$ok = 0;

while($line = fgetcsv($file_read,1000, ";")) { // 구분자는 ; 로 지정
    // 파일 인코딩 모드 검사
    if($current_encoding != 'utf-8') {
        $line00 = iconv('euc-kr','utf-8',trim($line[0])); // 구분1(cat1)
        $line01 = iconv('euc-kr','utf-8',trim($line[1])); // 구분2(cat2)
        $line02 = iconv('euc-kr','utf-8',trim($line[3])); // officeNO
        $line03 = iconv('euc-kr','utf-8',trim($line[4])); // mobileNO
    } else {
        $line00 = trim($line[0]); // 구분1(cat1)
        $line01 = trim($line[1]); // 구분2(cat2)
        $line02 = trim($line[3]); // officeNO
        $line03 = trim($line[4]); // mobileNO
    }

    $cat1NM = $line00;
    $cat2NM = $line01;
    $officeNO = isset($line02) ? TelNumSplitRemove($line02) : '';
    $mobileNO = isset($line03) ? TelNumSplitRemove($line03) : '';

    $total_line++;
    if(strlen($mobileNO)<1) continue;

    // 구분1 코드 구하기
    $CA1= getDbData('category','name="'.$cat1NM.'" and parent_id=0','id');
    $cat1 = $CA1['id'];

    // 구분2 코드 구하기
    if(strlen($cat2NM)>0){
        $CA2= getDbData('category','parent_id="'.$cat1.'" and name="'.$cat2NM.'"','id');
        $cat2 = $CA2['id'];
    } else {
        $cat2 =0;
        continue; // 상황에 따라 체크 필요한 코드
    }

    $d_regis = date('Ymd');

    // 중복 등록 여부 검사
    $cnt = DataExistedChk($cat1,$cat2);
    if($cnt == "0"){
        $d_regis = date('Ymd');
        $QKEY = "cat1,cat2,officeNO,mobileNO";
        $QVAL = "'$cat1','$cat2','$officeNO','$mobileNO'";
        $rs = getDbInsert('contacts',$QKEY,$QVAL);
        $newcount++;
    } else {
        $QSET="officeNO='".$officeNO."',";
        $QSET.="mobileNO='".$mobileNO."'";
        $QVAL="cat1='".$cat1."' and cat2='".$cat2."' ";

        getDbUpdate('contacts',$QSET,$QVAL);
        $upcount++;
    }

    $ok ++;
    if (($ok % 500) == '0') {
        echo(" $ok 건 저장");
        flush();
        sleep(2); //500개 저장할때마다 2초씩 쉰다.
    }
} // while 문 종료
fclose($file_read);
unlink($readFile);  // 업로드 완료후에 파일 삭제 처리

$msg = '전체'.number_format($total_line).'건中 신규'.number_format($newcount).'건, 갱신'.number_format($upcount).'건 등록완료';
echo "<script type=\"text/javascript\">alert('$msg');</script>";

function getExt($filename){
    $ext = substr(strrchr($filename,"."),1);
    $ext = strtolower($ext);
    return $ext;
}

function detectFileEncoding($filepath) {
    // 리눅스 기본 기능을 활용한 파일 인코딩 검사
    $output = array();
    exec('file -i ' . $filepath, $output);
    if (isset($output[0])){
        $ex = explode('charset=', $output[0]);
        return isset($ex[1]) ? $ex[1] : null;
    }
    return null;
}

// ID Exist Check
function DataExistedChk($cat1,$cat2){
    global $db;
    $sql = "select count(*) from contacts where cat1='".$cat1."' and cat2='".$cat2."'";
    $result = mysqli_query($db,$sql);
    if($row = mysqli_fetch_row($result)){
        return $row[0];
    } else {
        return "0";
    }
}

//DB삽입
function getDbInsert($table,$key,$val){
    global $db;
    $sql ="insert into ".$table." (".$key.")values(".$val.")";
    if(mysqli_query($db,$sql)){
        return 1;
    } else {
        return 0;
    }
}

//DB업데이트
function getDbUpdate($table,$set,$where){
    global $db;
    mysqli_query($db,'set names utf8');
    mysqli_query($db,'set sql_mode=\'\'');
    $sql ="update ".$table." set ".$set.($where?' where '.getSqlFilter($where):'');
    if(mysqli_query($db,$sql)){
        return 1;
    } else {
        return 0;
    }
}

//SQL필터링
function getSqlFilter($sql)    {
    return $sql;
}


function TelNumSplitRemove($tel){
    return preg_replace("/[^0-9]/", "", $tel);    // 숫자 이외 제거
}
?>


728x90
블로그 이미지

Link2Me

,
728x90

PHP 로 jQuery 와 AJAX를 이용해서 파일을 업로드하는데 한참 삽질을 하고 적어둔다.

어쩌다 필요해서 하려고 하면 생각도 안나고 다시 제자리 걸음을 반복하는 초보자의 길을 언제나 벗어나려나 ㅠㅠ


MDB(Meterial Design Bootstrap) 기반 템플릿을 활용하여 코딩을 하느라고 class 가 이상것이 보이는 것이니 혹시라도 이 게시글을 읽으시는 분은 그냥 그런게 있나보다 하고 참조만 하시라.


파일 전송 Form 파일

<form id="uploadphoto" action="MemberPhotoUpload.php" method="post" enctype="multipart/form-data">
    <div class="card border-light mb-3" style="max-width: 30rem;">
      <div class="card-body text-dark">
        <h5 class="card-title">Image File Upload</h5>
        <input type="file" id="file" name="file" multiple />
        <input type="submit" value="Upload Photo" class="btnSubmit" />
      </div>
    </div>
</form> 

<table class='table table-responsive-sm' id='photoIDX' uid='<?php echo $idx;?>'>
    <tr><td colspan='5'>★★사진 등록시 [등록하기] , 사진 수정시 [사진을 클릭]하세요.</td></tr>
    <tr>
        <td rowspan='3'>
        <?php
            if(file_exists($imgpath)){
                echo "<img src='MemberPhotoView.php?idx=$idx&uid=$uid' width=115 height=140>";
            } else {
                echo "사진없음<br><span class='btn-floating peach-gradient photoBtn'><i class='fas fa-image'></i>등록하기</span>";
            }
        ?>
        </td>
        <th>아이디</th>
        <td><?php echo $row['userID'];?></td>
        <th>사무실번호</th>
        <td style='text-align:left;height:15px;'><?php echo $row['telNO'];?></td>
    </tr>
    <tr>
        <th>성명</th>
        <td><?php echo $row['userNM'];?></td>
        <th>휴대폰번호</th>
        <td style='text-align:left;height:15px;'><?php echo $mobileNO;?></td>
    </tr>
    <tr>
        <th>직위</th>
        <td><?php echo $chargeArr[$row['codeID']];?></td>
        <th>주업무</th>
        <td style='text-align:left;height:15px;'><?php echo $row['workrole'];?></td>
    </tr>
    <tr>
        <th>E-Mail</th>
        <td colspan='4' style='text-align:left;height:15px;'><?php echo $row['email'];?></td>
    </tr>
</table>


Script 파일

<script>

 $('.photoBtn').click(function(){
    var $obj=$(this);
    var idx=$(this).closest('#photoIDX').attr('uid');

    $('#dialog').load('MemberPhotoForm.php?idx='+idx, function() {
        $(this).dialog({
            autoOpen : true,
            height : 'auto',
            width : 'auto',
            position: { my: "center", at: "center", of: window },
            title : '사진 업로드',
            buttons : {
                "닫기" : function() {
                    $(this).dialog("close");
                }
            }
        });
        PhotoUploadChk(idx);
    });

});

function PhotoUploadChk(idx){
    $('form#uploadphoto').submit(function(e){
        e.preventDefault();
        var formData = new FormData();
        var files = $('#file')[0].files[0];
        formData.append('file',files);
        formData.append('uid',idx);

        formData.append('osType',$('select[name=osType]').val());

        var upfiles_cnt = $("input:file", this)[0].files.length;
        if(upfiles_cnt == 0){
            alert('선택된 파일이 없습니다.');
            return false;
        }

        $.ajax({
            url: 'MemberPhotoUpload.php',
            data: formData,
            cache: false,
            contentType: false,
            processData: false,
            type: 'post',
            success: function (msg) {
                if(msg == 1){
                    MemberView(idx);
                } else if(msg == 0){
                    alert('form 데이터 오류인지 확인하세요.');
                } else {
                    alert('파일이 업로드되지 못했습니다.');
                }
            }
        });

    });

}
</script>


PHP 파일 업로드 관련 파일

<?php
if(isset($_POST['uid']) && $_POST['uid']>0){
    $idx=$_POST['uid'];

    $tmpname    = $_FILES['file']['tmp_name'];
    $realname    = $_FILES['file']['name'];
    $fileExt    = getExt($realname);

    $file_path='./photos/'.$idx.'.jpg';

    if (is_uploaded_file($tmpname)) {
        if (!strstr('[jpg]',$fileExt)) {
            echo "-1";
            exit;
        }
        move_uploaded_file($tmpname,$file_path);
        @chmod($file_path,0606);
        echo "1";
    }
} else {
    echo "0";
}

function getExt($filename){
    $ext = substr(strrchr($filename,"."),1);
    $ext = strtolower($ext);
    return $ext;
}
?>


파일 사이즈 검사하여 파일 사이즈를 줄이는 코드는 포함되지 않았다.


$("#uploadbutton").click(function(){
    var form = $('form')[0]; // 폼객체를 불러와서
    var formData = new FormData(form); // FormData parameter 에 담아줌
    $.ajax({
        url: 'fileupload',
        processData: false,
        contentType: false,
        data: formData,
        type: 'POST',
        success: function(result){
            alert("업로드 성공!!");
        }
    });
});

728x90
블로그 이미지

Link2Me

,
728x90

MDB(Meterial Design for Bootstrap) 에서 sidenav 에 표시할 메뉴를 PHP 와 연동하여 자동으로 표시하는 방법을 구현했다.


먼저, 구글에서 multi level sidenav 를 검색하면 https://mdbootstrap.com/support/jquery/multi-level-in-sidenav-items/ 가 검색될 것이다.


검색된 Tree 구조는 아래와 같다.

 <li>
    <ul class="collapsible collapsible-accordion">
    <li><a class="collapsible-header waves-effect arrow-r"><i class="fa fa-hand-pointer-o"></i>메뉴1-1<i class="fa fa-angle-down rotate-icon"></i></a>
    <div class="collapsible-body">
        <ul>
            <li>
            <ul class="collapsible collapsible-accordion">
            <li><a class="collapsible-header waves-effect arrow-r"><i class="fa fa-hand-pointer-o"></i>메뉴2-1</a>
            <div class="collapsible-body">
                <ul>
                <li><a href="#" class="waves-effect">메뉴3-1</a></li>
                <li><a href="#" class="waves-effect">메뉴3-2</a></li>
                <li><a href="#" class="waves-effect">메뉴3-3</a></li>
                </ul>
            </div>
            </li>
        </ul>

        <ul>
        <li><a href="#" class="collapsible-header waves-effect">메뉴2-2</a></li>
        </ul>

    </div>
    </li>
    </ul>
</li>

<li>
    <a href="#" class="collapsible-header waves-effect">메뉴1-2</a>
</li>


이걸 가지고 PHP 와 연동된 자동 메뉴를 만들어 보자.

MDB 는 bootstrap 4 기반으로 연동된 템플릿이다.

bootstrap 4 에서는 순환구조가 매끄럽지 못한 거 같다. (내가 제대로 이해를 못했겠지만....)


테이블 구조

CREATE TABLE IF NOT EXISTS `menu_item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) NOT NULL DEFAULT '0',
  `depth` int(3) NOT NULL DEFAULT '1',
  `name` varchar(50) NOT NULL,
  `text` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 


Treeview 메뉴 코드

아래 코드는 https://link2me.tistory.com/1194 게시글과 비교하면서 살펴보면 도움될 것이다.

파일명 : muti_treeview.php

 <?php
if(!isset($_SESSION)) {
    session_start();
}
require_once 'path.php'; // root 폴더를 기준으로 상대적인 경로 자동 구하기
require_once $g['path_config'].'config.php';
require_once $g['path_config'].'dbconnect.php';
require_once $g['path_class'].'dbDataClass.php';
$d = new DBDataClass();
$res = $d->getDbresult("menu_item", "", "id,parent_id,name");
while($row = mysqli_fetch_assoc($res)){
    $sidemenu[] = $row;
}

$side_menu = bootstrap4_sideMenu($sidemenu);
echo $side_menu;

function bootstrap4_sideMenu($array,$parent_id = 0,$parents = array())
{
    global $d,$key;
    if($parent_id==0){ // sub가 있는 parent_id 값 추출
        foreach ($array as $element) {
            if (($element['parent_id'] != 0) && !in_array($element['parent_id'],$parents)) {
                $parents[] = $element['parent_id'];
            }
        }
    }

    $menu_html = '';
    foreach($array as $element){
        if($element['parent_id']==$parent_id){
            $url = $element['id'].";".$element['parent_id'];            

            if(in_array($element['id'],$parents)){
                $menu_html .= '<li>';
                $menu_html .= '<ul class="collapsible collapsible-accordion">';
                $menu_html .= '<li><a class="collapsible-header waves-effect arrow-r" href="'.$url.'"><i class="fa-hand-pointer-o">';
                $menu_html .= '</i>'.$element['name'].'<i class="fas fa-angle-down rotate-icon"></i></a>';
                $menu_html .= '<div class="collapsible-body">';

            } else {
                $menu_html .= '<li>';
                $menu_html .= '<a class="collapsible-header waves-effect" href="'.$url.'">'.$element['name'].'</a>';
                $menu_html .= '</li>';
            }

            if(in_array($element['id'],$parents)){
                $menu_html .= '<ul>';
                $menu_html .= bootstrap4_sideMenu($array, $element['id'], $parents);
                $menu_html .= '</ul>';
                $menu_html .= '</div>';
                $menu_html .= '</ul>';
                $menu_html .= '</li>';
            }
           
        }
    }
    return $menu_html;
}
?>


이제 MDB 템플릿 구조에 포함된 HTML 파일구조다. jQuery 코드는 모두 생략했다.

<!DOCTYPE html>
<head>
<?php require_once 'path.php';// root 폴더를 기준으로 상대적인 경로 자동 구하기 ?>
<?php require_once $g['path_config'].'config.php';?>
<?php require_once $g['path_layout'].'default/_import.head.php';?>
<?php require_once $g['path_root'].'deviceChk.php';?>
</head>

<body class="fixed-sn mdb-skin">
<header>
    <!-- Sidebar navigation -->
    <div id="slide-out" class="side-nav sn-bg-4 fixed">
        <ul class="custom-scrollbar">
            <!-- Side navigation links -->
            <?php require_once $g['path_menu'].'item/multi_treeview.php' ?>
        </ul>
        <div class="sidenav-bg mask-strong"></div>
    </div><!--/. Sidebar navigation -->
    <!-- Navbar -->
    <nav class="navbar fixed-top navbar-toggleable-md navbar-expand-lg scrolling-navbar double-nav">
        <!-- SideNav slide-out button -->
        <div class="float-left">
            <a href="#" data-activates="slide-out" class="button-collapse"><i class="fas fa-bars"></i></a>
        </div>
        <!-- Breadcrumb-->
        <div class="breadcrumb-dn mr-auto">
            <p><?=$hostName;?></p>
        </div>
        <ul class="nav navbar-nav nav-flex-icons ml-auto">
            <?php require_once $g['path_menu'].'item/item_login.php' ?>
        </ul>
    </nav>
    <!-- /.Navbar -->
</header>

<!--Main layout-->
<main>
    <div class="container-fluid text-center">
        <div class="row">
            <div class="col-md-12">
                <div class="content" id="panel_content">
                </div>
                <div id="ajaxPath" menu-path="<?php echo $g['path_help'];?>"  ></div>
            </div>
        </div>
    </div>
</main>
<!--/Main layout-->
<!-- SCRIPTS -->
<?php require_once $g['path_layout'].'default/_import.tail.php';?>
</body>
</html>


728x90
블로그 이미지

Link2Me

,
728x90

네이버 지식인에 문의된 대구와 부산의 날씨 정보 파싱 예제다.


<?php
include_once 'simple_html_dom.php';

$url = "https://weather.naver.com/rgn/cityWetrCity.nhn?cityRgnCd=CT001024"; // 용인
$url1 = "https://weather.naver.com/rgn/cityWetrCity.nhn?cityRgnCd=CT007007"; // 대구
$url2 = "https://weather.naver.com/rgn/cityWetrCity.nhn?cityRgnCd=CT008008"; // 부산

$R = weather_parse($url1);
$S = weather_parse($url2);
echo json_encode(array("Time1"=>$R[0], "Weather1"=>$R[1],"Finedust1"=>$R[2],"Time2"=>$S[0], "Weather2"=>$S[1],"Finedust2"=>$S[2])); // 대구와 부산 날씨 정보

function weather_parse($url){
    $str = file_get_contents_curl($url);
    $enc = mb_detect_encoding($str, array('EUC-KR', 'UTF-8', 'shift_jis', 'CN-GB'));
    if ($enc != 'UTF-8') {
        $str = iconv($enc, 'UTF-8', $str);
    }

    $html = new simple_html_dom(); // Create a DOM object
    $html->load($str); // Load HTML from a string

    $R = array();
    $item = $html->find('div[class=c_tit2]',0)->find('em',0)->plaintext;
    array_push($R,$item);
    $item = $html->find('div[class=w_now2]',0)->find('em',0)->plaintext;
    array_push($R,$item);
    $item = $html->find('div[class=w_now2]',0)->find('em',1)->plaintext;
    array_push($R,$item);
    return $R;
}

function file_get_contents_curl($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // https 일때 이 한줄 추가 필요
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
}
?>


파일에 대한 설명은 https://link2me.tistory.com/1603 참조하라.


728x90
블로그 이미지

Link2Me

,
728x90

PHP Simple HTML DOM Parser 를 활용하여 네이버 날씨정보를 파싱하는 방법이다.


준비물

http://simplehtmldom.sourceforge.net/ 에서 파일을 다운로드하고 simple_html_dom.php 를 include 한다.

다운로드 받은 파일에 포함된 예제는 읽어보거나, 구글링해서 사용법에 대한 예제를 찾아보면 된다.


<?php
include_once 'simple_html_dom.php';
function file_get_contents_curl($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // https 일때 이 한줄 추가 필요
    //Set curl to return the data instead of printing it to the browser.
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
}

?> 


그럼 2단계로 파싱할 곳을 선택한다.

내가 사는 지역인 용인지역을 파싱할 곳으로 선택하고, 네이버 날씨정보 URL을 알아낸다.


$url = "https://weather.naver.com/rgn/cityWetrCity.nhn?cityRgnCd=CT001024";


$str = file_get_contents_curl($url);
$enc = mb_detect_encoding($str, array('EUC-KR', 'UTF-8', 'shift_jis', 'CN-GB'));
if ($enc != 'UTF-8') {
    $str = iconv($enc, 'UTF-8', $str);
}

print_r($str);


여기까지 하면 날씨정보에 대한 소스보기를 할 수 있다.

마우스 우클릭하여 소스보기를 하면 소스 정보가 나온다.

소스에서 파싱할 곳을 찾아내야 한다.


소스보기에 아래 그림을 찾아낼 수 있다.

<em>태그안에 있는 정보가 파싱할 정보라는 걸 확인할 수 있다.


그럼 이제 simple_html_dom 파싱 처리를 위한 단계로 코드를 구현하자.

// Create a DOM object
$html = new simple_html_dom();
// Load HTML from a string
$html->load($str);


<em> 태그를 파싱하는 방법은 아래와 같이 하면 모든 em 태그를 찾아준다.

foreach($html->find('em') as $element){
    echo $element->innertext . '<br>';
}


시간정보만 파싱해보자.

foreach($html->find('div[class=c_tit2]',0)->find('em') as $element){
    echo $element->innertext . '<br>';
}
와 같이하면 시간정보만 파싱되는 걸 확인된다.

$html->find('div[class=c_tit2]',0) 와 같이 0을 넣어주어야 하는 거 잊지말자. 소스와 비교하여 확인하면 된다.

find는 선택한 태크를 찾고 그 자식들을 모두 찾는데 다시 find 로 em 태크를 선택한 것이다.

육안으로 확인하면 하나 밖에 없다는 것을 알 수 있으므로

$time = $html->find('div[class=c_tit2]',0)->find('em');

를 하면 Trying to get property of non-object라고 하면서 에러가 발생한다.

$item = $html->find('div[class=c_tit2]',0)->find('em',0); 라고 하면 에러가 발생하지 않는다.

echo $item->plaintext; 를 하면 시간 정보가 출력되는 걸 확인할 수 있다.

한번에 하면 $item = $html->find('div[class=c_tit2]',0)->find('em',0)->plaintext; 로 하면 된다.


원하는 결과만 뽑아내면

$R = array();
foreach($html->find('em') as $element){
    array_push($R,$element->innertext);
}
echo '시간 : '.$R[1].'<br />';
echo '날씨 : '.$R[2].'<br />';
echo '미세먼지 : '.$R[3].'<br />';


하나 하나 분리해서 코드를 작성해보면....

$item = $html->find('div[class=c_tit2]',0)->find('em',0)->plaintext;
echo $item.'<br />';

$item = $html->find('div[class=w_now2]',0)->find('em',0)->plaintext;
echo $item.'<br />';

$item = $html->find('div[class=w_now2]',0)->find('em',1)->plaintext;
echo $item.'<br />';


코드를 Android 에서 JSON 으로 파싱처리하도록 수정했다.

<?php
include_once 'simple_html_dom.php';
function file_get_contents_curl($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // https 일때 이 한줄 추가 필요
    //Set curl to return the data instead of printing it to the browser.
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
}

$url = "https://weather.naver.com/rgn/cityWetrCity.nhn?cityRgnCd=CT001024";

$str = file_get_contents_curl($url);
$enc = mb_detect_encoding($str, array('EUC-KR', 'UTF-8', 'shift_jis', 'CN-GB'));
if ($enc != 'UTF-8') {
    $str = iconv($enc, 'UTF-8', $str);
}

//print_r($str);

$html = new simple_html_dom(); // Create a DOM object
$html->load($str); // Load HTML from a string

$R = array();
foreach($html->find('em') as $element){
    array_push($R,$element->innertext);
}
$R[2] = preg_replace("(\<(/?[^\>]+)\>)", "", $R[2]); // html 태그를 없애는 정규식
echo json_encode(array("Time"=>$R[1], "Temperature"=>$R[2],"Fine dust"=>$R[3]));
?>


이렇게 하면 네이버 날씨정보 소스코드가 달라져도 이 PHP 파일만 수정해주면 되므로 Android 앱에서는 잘못된 정보로 업데이트할 일이 없을 것이다.


728x90
블로그 이미지

Link2Me

,
728x90

사용자가 직접 클릭하지 않고 자동으로 클릭이 되는 기능을 동작시키고 싶을 때가 있다.

왼쪽 메뉴를 클릭하거나 로그인 버튼을 눌렀을 때 팝업 모달이 자동으로 실행되게 하고 싶은데 처음에는 좀 해맸다.


로직에 대한 기본 이해 부족이었다.

동작 순서를 고려하여 순차적으로 실행되게 해야 한다.


1. 왼쪽 메뉴를 누르면

2. a href 링크의 고유 기능은 이벤트 실행 금지 시킨다. e.preventDefault();

3. 우측 content 에 원하는 걸 load 시킨다.

    $('.memberModify').on('click',function(e){
        var uri = $(this).attr('href');
        if(uri != '#'){
            $('#panel_content').load(uri);
        }
    });

4. load 되는 곳에 특정 키워드가 포함되어 있고 또 클릭을 해야 하는 불편이 있다면...

    $('.memberModify').on('click',function(e){
        e.preventDefault(); // a 링크 실행 방지
        var uri = $(this).attr('href');
        if(uri != '#'){
            $('#panel_content').load(uri,function(){
                if($('#modalLoginClick:contains("눌러주세요")')){
                    $('#modalLoginClick').trigger('click'); // 자동 클릭
                }
            });
        }
    });


trigger 메소드를 사용하면 자동 클릭이 되는 걸 확인할 수 있다.

load 시킨 다음에 자동 클릭이 되어야 정확하게 원하는 결과가 나왔다.


ㅇ trigger(eventType)
  eventType는 이벤트의 타입을 지정하는 문자열로써 click, dblclick, focus, blur 등등 해당 이벤트를 구동한다.




728x90
블로그 이미지

Link2Me

,
728x90

jQuery 에서 검색어를 입력하고 엔터키를 누르면 검색이 실행되도록 하는 코드를 테스트하는데 자꾸 오동작이 발생한다.

한마디로 개념 부족에서 오는 현상이었다.

프로그램은 절대 거짓말 하지 않는다는 지인의 말이 생각난다.

구현하려는 로직과 내가 코딩하는 것의 사이에서 놓치는 점이 무엇인가 다시 고민해보자.


처리하려는 로직을 순서대로 적어본다.

1. 엔터키를 입력할 때까지는 어떠한 것도 하지 않는다.

2. 엔터키가 입력되면 event.preventDefault(); 를 실행하여 자동으로 서밋되는 걸 방지한다.

3. 원하는 결과를 실행한다.


if(parseInt(device_type) === 3){ // PC에서 입력한 코드인지 확인
    // 27 is the ESC key (익스플로어나 크롬에서는 27로 나오고 파이어폭스는 0)
    $('#BBSSearchKeyword').on('keypress', function(event){
        var keycode = (event.keyCode ? event.keyCode : event.which);
        if(keycode == 13){ // 엔터키가 입력될 때까지는 어떤 것도 하지 않는다.
            event.preventDefault(); // 엔터키가 입력되면 이벤트의 기본 동작을 중단한다.
            //event.stopPropagation(); // 현재 이벤트가 상위로 전파되지 않도록 중단한다.
            BBSSearch(uri,where,keyword);
        }
    });
}

크롬브라우저 정상 동작, Firefox 브라우저 정상 동작
익스플로러에서 실행하니까 한글 입력 결과의 텍스트가 깨지면서 정확한 결과를 반환하지 않는다.
코드에서 UTF-8 을 기본으로 인식하는 코드를 추가해줘야 하나보다.

encodeURIComponent : URI로 데이터를 전달하기 위해서 문자열을 UTF-8 인코딩, IE8 이상의 브라우저에서 모두 지원한다.


728x90
블로그 이미지

Link2Me

,
728x90

jQuery 에서 값 입력 여부를 체크하는 걸 반복적으로 하니까 좀 귀찮은 거 같아서 간단하게 처리하는 걸 시도했다.


함수 만들기 이전 입력값 체크

if($('select[name=cat1]').val().length == 0){
    alert('카테고리1 선택을 안했습니다.');
    $('select[name=cat1]').focus();
    return false;
}
if($('select[name=cat2]').val().length == 0){
    alert('카테고리2 선택을 안했습니다.');
    $('select[name=cat2]').focus();
    return false;
}
if($('input[name=userID]').val().length == 0){
    alert('아이디를 입력하세요');
    $('input[name=userID]').focus();
    return false;
}
if($('input[name=userNM]').val().length == 0){
    alert('성명을 입력하세요');
    $('input[name=userNM]').focus();
    return false;
}


함수를 만들고 나서 입력값 체크

function CheckErr(vsel,msg){
    var count = vsel.val().length;
    if(count < 1){
        alert(msg);
        vsel.focus();
        return false;
    }
    return true;
}


if(CheckErr($('select[name=cat1]'),'카테고리1 선택을 안했습니다.') == false) return false;
if(CheckErr($('select[name=cat2]'),'카테고리2 선택을 안했습니다.') == false) return false;
if(CheckErr($('input[name=userID]'),'아이디를 입력하세요.') == false) return false;
if(CheckErr($('input[name=userNM]'),'성명을 입력하세요.') == false) return false;


728x90
블로그 이미지

Link2Me

,
728x90

PHP 에서 배열 데이터를 json_encode 로 생각없이 넘겨주면 jQuery JSON 에서 처리를 쉽게 해결될 줄 알고 구글링하면서 이것 저것 찾아서 해결하면 금방 잊어버리고 그랬다.

분명히 예전에 했던 것인데 다시 할려고 하니까 시간을 또 허비하고 있다.

뭔가 대충해서 문제가 있는 거 같아서 집중적으로 테스트를 해봤다.


먼저 PHP에서 넘겨주는 데이터 파일을 보자.

 <?php
require_once 'path.php';
require_once $g['path_center'].'config/config.php';
require_once $g['path_class'].'connect.php';
require_once $g['path_class'].'dbDataClass.php';
$c = new DBDataClass;
$sql ="select idx,userNM,telNO,mobileNO,codeID FROM member ";
$sql.="where userID='".$_REQUEST['userID']."'";
$result = mysqli_query($db,$sql);
$R = array();
if($row = mysqli_fetch_assoc($result)):
    array_push($R,$row);
endif;

//echo '<pre>';print_r($R);echo '</pre>';
echo json_encode($R);
//echo json_encode(array('rs' => $R));
?>


보다시피 배열로 만들어진 결과를 json_encode 한 다음에 echo 문으로 출력을 한 것이다.


이제 클라이언트 JSON 처리 파일을 살펴보자.

결과 그림부터 보고 아래 코드를 살펴보면 뭐가 다른지 보인다.

문자열은 중괄호({})로 둘러쌓여 있는데, PHP에서 넘겨온 데이터는 대괄호([])가 있고 중괄호({})가 있는 형태다.

이렇게 데이터 형태가 다른데 이걸 JSON.parse 만 하면 원하는 object 로 되는 줄 알고 처리를 시도했더니 결과가 나오지 않았던 것이다.

이걸 stringify 하여 문자열로 변환한 다음에 split 으로 대괄호 부분을 제거하고 다시 JSON.parse 를 하면 원하는 결과가 나온다.

var obj = JSON.stringify(msg); // 문자열로 변경된다.
var objsplit1 = obj.split(']');
var objsplit2 = objsplit1[0].split('[');
var rs = JSON.parse(objsplit2[1]);
console.log('idx = '+rs.idx);


이렇게 처리하거나 배열로 뽑아내서 원하는 결과를 얻어서 처리하는 방법이 최선이다.

var arr= new Array();
$.each(msg, function(key, obj){
    $.each(obj,function(k,value){
        arr.push(value)
    });
});


<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<h2>JSON Object, String</h2>
<p id="txt"></p>
<p id="obj_parse"></p>
<p id="obj"></p>
<p id="post_msg"></p>
<p id="ajax_msg"></p>
<p id="json2string"></p>
<p id="stringify"></p>
<script>
var txt = '{"name":"홍길동", "age":30, "city":"서울"}';
$('#txt').html('문자열 : '+txt);

var obj = JSON.parse(txt);
$("#obj_parse").html(obj.name + ", " + obj.age);

document.getElementById("obj").innerHTML = obj;
// $("#obj").html(obj); // 출력 결과물이 화면에 표시하지 못한다.

$.post('json.php',{userID:72771381},function(msg){
    console.log(msg); // 결과가 문자열로 넘어온다.
    //document.getElementById("post_msg").innerHTML = msg;
    $('#post_msg').html(msg);
});

$.ajax({
    url: "json.php",
    type: "POST",
    data: {userID:72771381},
    dataType: "JSON",
    success: function (msg) {
        console.log(msg);
        document.getElementById("ajax_msg").innerHTML = msg;
        var obj = JSON.stringify(msg); // 문자열로 변경된다.
        $('#json2string').html(obj);
        var objsplit1 = obj.split(']');
        var objsplit2 = objsplit1[0].split('[');
        console.log(objsplit2[1]);
        var rs = JSON.parse(objsplit2[1]);
        console.log('idx = '+rs.idx);

        var arr= new Array();
        $.each(msg, function(key, obj){
            $.each(obj,function(k,value){
                arr.push(value)
            });
        });
        console.log(arr);
    }
});


var myObject = new Object();
myObject.name = "왈패";
myObject.age = 12;
myObject.pets = ["cat", "dog"];
var myString = JSON.stringify(myObject);
$("#stringify").html(myString);

</script>
</body>
</html>
 


마지막 myObject 를 JSON.stringify 를 하면 맨 처음과 똑같은 형태의 중괄호({})로 둘러쌓인 문자열이 만들어진다는 걸 확인할 수 있다.


결론 PHP 에서 넘어온 JSON 형태는 jQuery 가 처리하려는 형태와 약간 다르므로 이걸 고려해서 파싱처리해야 한다는 것이다.

728x90
블로그 이미지

Link2Me

,
728x90

PHP에서 json_encode 로 데이터를 만들어서 jQuery 에서 ajax를 이용하여 원하는 데이터를 처리하는 방법이다.


$sql ="select userNM,telNO,mobileNO,codeID FROM member ";
$sql.="where userID='".$_REQUEST['userID']."'";
$result = mysqli_query($db,$sql);
$R = array();
if($row = mysqli_fetch_assoc($result)):
    array_push($R,$row);
endif;
echo json_encode($R);
 



jQuery ajax 로 결과를 받은 데이터는 JSON object 인데 그냥은 원하는 결과를 파싱할 수가 없다.

실력이 부족해서인지 개념부족인지 여기서 막힌다.

( 참조하면 왜 그런지 알 수 있다 : https://link2me.tistory.com/1595 )

그래서 Array로 변환하여 데이터를 처리했다.


 $('#getStaffData').on('click',function(){
    var userID = $('input[name=userID]').val();
    if(userID.length == 0){
        alert('입력값이 없습니다.');
        $('input[name=userID]').focus();
        return false;
    }

    $.ajax({
        url: "StaffFetchData.php",
        type: "POST",
        data: {userID:userID},
        dataType: "JSON",
        success: function (data) {
            //console.log(data);
            var arr= new Array();
            $.each(data, function(key, obj){
                $.each(obj,function(k,value){
                    arr.push(value)
                });
            });
            console.log(arr);
            $('input[name=userNM]').val(arr[0]);
            $('input[name=telNO]').val(arr[1]);
            $('input[name=mobileNO]').val(arr[2]);
            $('#codeID').val(arr[3]);
        }
    });

});


로컬에서 만든 임의의 데이터와 PHP를 통해 가져온 데이터의 차이점이 있다는 걸 알아야 한다.

Android 에서 JSON 데이터를 파싱처리하고자 한다면 파싱 처리하는 코드를 알아야 처리할 수 있다.

728x90
블로그 이미지

Link2Me

,
728x90

정해진 클래스가 아니면 동작이 안된다. bootstrap 3에서 잘 동작하던 걸 MDBootstrap 기반에서 동작되도록 하려면 Class 속성을 정확하게 알아야만 가능한 거 같다.


select 일반 속성을 적용하고자 할 때


<select name="where" class="browser-default custom-select" >
    <option value="unify">통합</option>
    <option value="userNM">성명</option>
    <option value="mNO">휴대폰</option>
</select>


자료 입력을 편하기 위해서 엔터키를 치면 자동으로 검사하는 코드를 추가했더니 먹통이 되는 증상이 보였다.

스마트폰에서는 엔터키를 입력하는 걸 추가하면 안되더라.

그래서 PC에서만 동작하는 코드를 검사하여 추가하는 걸로 코드를 변경했다.

구글링으로 검색해서 찾은 코드로 테스트했더니 잘 안되어서 PHP 코드에서 Device를 검사한 다음에 코드화를 통해서 해당 코드값을 인식하고 자바스크립트 코드는 코드의 하단에 추가하여 순차적으로 읽을 때 PHP 값을 읽을 수 있도록 했다.


<?php require_once 'deviceChk.php';?>

<script>

var device_type = "<?php echo $mtype;?>";

//alert(typeof device_type); // string 으로 인식하는 걸 확인할 수 있다.


if(parseInt(device_type) > 2){ // 정확한 검사를 위해 정수형으로 변환하여 부등 비교를 했음
    $('#BBSSearchKeyword').keypress(function(e){
        e.preventDefault();
        if(e.keyCode == 13 || e.which==13){ // 검색어 입력하고 엔터키를 누르면
            BBSSearch(uri,where,keyword);
        }
    });
}






728x90
블로그 이미지

Link2Me

,
728x90

jQuery UI 팝업창이 최상단 정중앙에 오지 않고 이상하게 보이는 증상 때문에 삽질을 엄청했다.


https://jqueryui.com/download/all/ 에서 가장 최신버전을 받아서 홈페이지에 필요한 파일을 옮긴다.

가장 최신버전이 2016.9월에 올라온 버전이다.


<div id="dialog" title="회원 현황"></div>


HTML 파일 하단에 팝업창을 띄울 dialog DIV를 적어준다.


헤더에 다음과 같이 경로에 맞게 두줄을 추가한다.

<link href="css/jquery-ui.css" rel="stylesheet">
<script type="text/javascript" src="js/jquery-ui.js"></script>


테이블에 id를 추가하고 tr 에 uid 값이 기록되도록 코드에 추가한다.

<table id="BBSListTable" class="table table-striped table-bordered table-hover">
<tbody>
<tr id="<?php echo $R['uid']; ?>">


제어를 위한 jQuery 코드를 아래와 같이 작성한다.

$('#BBSListTable tbody tr').click(function() {
    var idx = $(this).attr('id');
    BBSView(idx);
}).mouseover(function() {
    $(this).children().css({
        'backgroundColor' : '#DCDCDC',
        'cursor' : 'pointer'
    });
}).mouseout(function() {
    $(this).children().css({
        'backgroundColor' : '#FFFFFF',
        'cursor' : 'default'
    });
});

function BBSView(idx){
    $('#dialog').load('BBSView.php?idx=' + idx, function() {
        $(this).dialog({
            autoOpen : true,
            title : '회원정보',
            width: 450,
            height: 300,
            draggable: true,
            position: { my: "center", at: "center", of: window },
            buttons : {
                "수정" : function() {
                    gocStaffModifyView(idx);
                },
                "삭제" : function() {
                    gocStaffDelete(idx);
                },
                "닫기" : function() {
                    $(this).dialog("close");
                }
            }
        });
    });
}
 


position: { my: "center", at: "center", of: window } 이 한줄의 옵션을 몰라서 엄청 해멨다.

이 한줄로 고민은 싹 해결되었다.







728x90
블로그 이미지

Link2Me

,
728x90

jQuery Paging 처리를 완벽하게 한 것은 아니지만 PHP 와 연동하여 기본적인 처리를 하도록 만들어봤다.

함수는 재귀호출이 가능하다는 점을 활용했다.


https://www.w3schools.com/bootstrap4/bootstrap_pagination.asp 에 Bootstrap 4 Pagination 에 대한 기본 설명이 잘 나왔다. Pagination 은 HTML 코드의 <ul>태그와 <li>태그를 활용하여 모양이 예쁜 Pagination 이 만들어진다.

보통 jQuery 와 연동되는 Pagination 처리에서는 href="#" 으로 해서 해당 URL로 연결되지 않고 jQuery 코드상에서 처리한다.

PHP 코드와 잘 연계하여 Paging 이 처리되는 함수를 만든 다음에 jQuery 와 연동하면 코드를 크게 고려하지 않고도 잘 만들 수 있을 거 같다.


function BBSListTable(where,keyword,curPage){
    $('#panel_content').load('BBSList.php?where='+where+'&keyword='+keyword+'&p='+curPage, function() {
        var curPage = $('#paging .act a').text();
        $('#paging li').click(function(e) {
            e.preventDefault();
            switch($(this).text()){
                case '◁':
                    curPage=parseInt($(this).next().text()) - 1;
                    break;
                case '▷':
                    curPage=parseInt($(this).prev().text()) + 1;
                    break;
                default:
                    curPage = $(this).text();
                    break;
            }
            BBSListTable(where,keyword,curPage); // 재귀호출 가능
        });

        $('#BBSHome').click(function(e){
            e.preventDefault();
            BBSListTable('','','',1);
        });

        $('#BBSSearch').click(function(e){
            var where = $('[name=where]').val();
            var keyword = $('[name=keyword]').val();
            if(keyword =='' || keyword == 'undefined'){
                alert('검색어를 입력하세요');
                $('input[name=keyword]').focus();
                return false;
            }
            BBSListTable(where,keyword,1);
        });

        // 검색어 초기화
        $('#BBSKeywordReset').click(function(){
            $('input[name=keyword]').val('');
        });

    });
}
 





728x90
블로그 이미지

Link2Me

,
728x90

JSON.parse() : JSON 문자열을 JSON object로 변환시켜준다.

$.parseJSON(data); // JSON 문자열을 JSON 객체(JavaScript object) 로 변환시켜 준다

JSON.stringify : JSON object를 JSON 문자열로 변환시켜 준다.



다음 예제를 직접 실행해보면 내용을 이해할 수 있다.

 <!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<form id="form1" runat="server">
    <div>
        <div id="result"></div>
    </div>
</form>

<script>
$(function () {
    // json 객체 정의
    var employee = { "name": "홍길동", "address": "용인시 죽전동", "phone": "555-4567" };

    // JSON.stringify를 사용하여 String으로 변환한다.
    var jsonstring = JSON.stringify(employee);
    $("#result").append('<p>json string: ' + jsonstring + '</p>');

    // JSON.parse function을 사용하여 JSON string을 JSON 객체(object)로 변환
    var jsonobject = JSON.parse(jsonstring); // $.parseJSON(jsonstring) 사용해도 된다.
    var info = '<ul><li>Name:' + jsonobject.name + '</li><li>Street:' + jsonobject.address + '</li><li>Phone:' + jsonobject.phone + '</li></ul>';

    $("#result").append('<p>json object:</p>');
    $("#result").append(info);
});
</script>
</body>
</html>


728x90
블로그 이미지

Link2Me

,