728x90

테이블에서 체크박스를 전체 선택/해제 그리고 체크박스 선택된 것만 특정 액션을 취하도록 하고 싶은 경우가 있다.

이걸 고려한 HTML5 및 JQuery 를 작성해봤다.

프로그램은 테스트를 통해서 몸으로 느끼고 내것으로 만들어야만 하는 작업이라는 걸 계속 체험하고 있다.



전체선택 박스 만들기

<th class="header" width="30"><input type="checkbox" id="checkall" /></th>



선택삭제 박스 만들기

<td colspan="5" style="text-align:left;"><span class="btnbox1">선택삭제</span></td>


.btnbox1 {
    background-color:#BFD7E9;
    border: none;
    padding: 10px 10px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 12px;
    margin: 4px 2px;
    cursor: pointer;
    }


체크 박스 나오게하는 테이블 만들기

=== boardClass.php 파일의 일부 ===

// column 개수, 게시물 총개수 만큼 자동으로 화면 출력
function tablelistView_checkbox($result){
    global $DB_CONNECT;
    if(is_object($DB_CONNECT)  && get_class($DB_CONNECT)=='mysqli'){
        while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
            $view='<tr class="tr1">';
            foreach($row as $column => $value) {
                if($column== 'uid'){
                    $view.='<td><input type="checkbox" class="chkbox" name="uid[]" value="'.$value.'" /></td>';
                }
                $view.='<td class="td2">'.$value.'</td>';
            }
            $view.='</tr>';
            echo $view;
        }
    } else {
        while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
            $view='<tr class="tr1">';
            foreach($row as $column => $value) {
                if($column== 'uid'){
                    $view.='<td><input type="checkbox" class="chkbox" name="uid[]" value="'.$value.'" /></td>';
                }
                $view.='<td class="td2">'.$value.'</td>';
            }
            $view.='</tr>';
            echo $view;
        }
    }
}


==== display.js ====

$(function(){
    // class td1 중에서 짝수번째 요소만 선택해서 배경색을 지정색으로 표시
    $(".tr1:odd").css("background-color","#F4F9FF");

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

    // 전체 선택, 전체 해제
    $("#checkall").change(function () {
        $("input:checkbox").prop('checked', $(this).prop("checked"));
    });

    $('.btnbox1').click(function(){
        var chkdata = new Array();
        // 헤더에 있는 체크박스는 제외
        //$('.chkbox:checked').each(function() {
        $('input:checkbox[name="uid[]"]:checked').each(function() {
            chkdata.push($(this).val());
            //alert($(this).val());
        });
        if(chkdata.length != 0){
            alert(chkdata);
            $.post('ajax.php',{chkdata:chkdata}, function(response) {
                alert(response);
            });
        } else {
            alert('선택한 항목이 없습니다.');
        }
    });

});


==== ajax.PHP ===

값이 POST 방식으로 전달되는 것만 알 수 있게 연습한 파일이다.

실제로는 MySQL DB와 연동하여 수행처리하는 로직을 만들면 된다.


<?php
if(isset($_POST['chkdata'])){
    /*
    foreach($_POST['chkdata'] as $column => $value) {
        echo $value;
        // MySQL 함수와 연동하여 DB Delete 수행 처리
    }
    */
    $response = implode(" , ",$_POST['chkdata']); // 배열을 문자열로 저장
    echo $response;

    if($response) {

       $sql = "delete from tablename where uid in (".$response.")";

        mysqli_query($dbconn,$sql);

    }

}
?>


=== 테이블을 출력하는 파일 전체 ===

http://link2me.tistory.com/1127 와 비교하여 달라진 부분이 뭔지 보면 알 것이다.


<?php
require_once 'dbconnect.php'; // db접속 성공
require_once 'phpclass/dbClass.php';
require_once 'phpclass/boardClass.php';

$c = new MySQLiDbClass();

$link_url = $_SERVER['PHP_SELF']; // 현재 실행중인 파일명 가져오기
$rowsPage = 12;

// 화면에 출력할 칼럼 발췌

$flddata ="uid,ItemName,Price,Quantity";
$where ="";
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$result = $c->getDbArray('items',$where,$flddata,'',$rowsPage,$curPage);
$totalcnt = $c->getDbRows('items',$where);

$d = new boardClass();
?>
<!DOCTYPE html>
<head>
<meta charset=UTF-8" />
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
<meta http-equiv="X-UA Compatible" control="IE=edge,chrome=1" />
<link rel="stylesheet" type="text/css" href="../css/table.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script type="text/javascript" src="../js/display.js"></script>
</head>
<body>
<div class="container">
    <table class="table" width="800">
        <thead>
            <tr>
                <th class="header" width="30"><input type="checkbox" id="checkall" /></th>
                <th class="header" width="100">No</th>
                <th class="header" width="250">아이템</th>
                <th class="header" width="250">가격</th>
                <th class="header" width="200">수량</th>
            </tr>
        </thead>
        <?php
            // 테이블 리스트
            $d->tablelistView_checkbox($result);
        ?>
        <td colspan="5" style="text-align:left;"><span class="btnbox1">선택삭제</span></td>
    </table>
</div>
<?php $d->PageLinkView($link_url,$totalcnt,$rowsPage,$curPage);?>
</body>
</html>

728x90
블로그 이미지

Link2Me

,
728x90

HTML5 기반으로 MySQL 과 연동하여 테이블을 구성하는 것 연습삼아 작성해봤다.

테이블은 Csharp 연동시 샘플 테이블을 가지고 작성했다.

AutoSet9 은 MySQLi 방식으로 연동된다.

dbconnect.php 파일을 통해서 MySQLi DB와 연동하고 dbClass.php 파일을 연결하여 dbClass 함수를 호출하여 코드를 간결하게 작성했으며, 게시판 Class(boardClass) 에서 만든 코드를 이용하여 실제 코드는 몇줄 안되는 것처럼 보인다.


<?php
require_once 'dbconnect.php'; // db접속 성공
require_once 'phpclass/dbClass.php';
require_once 'phpclass/boardClass.php';

$c = new MySQLiDbClass();

$link_url = $_SERVER['PHP_SELF']; // 현재 실행중인 파일명 가져오기
$rowsPage = 12;


// 화면에 출력할 칼럼 발췌

$flddata ="uid,ItemName,Price,Quantity";
$where ="";
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$result = $c->getDbArray('items',$where,$flddata,'',$rowsPage,$curPage);
$totalcnt = $c->getDbRows('items',$where);

$d = new boardClass();
?>
<!DOCTYPE html>
<head>
<meta charset=UTF-8" />
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
<meta http-equiv="X-UA Compatible" control="IE=edge,chrome=1" />
<link rel="stylesheet" type="text/css" href="../css/table.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script type="text/javascript" src="../js/display.js"></script>
</head>
<body>
<div class="container">
    <table class="table" width="800">
        <thead>
            <tr>
                <th class="header" width="100">No</th>
                <th class="header" width="250">아이템</th>
                <th class="header" width="250">가격</th>
                <th class="header" width="200">수량</th>
            </tr>
        </thead>
        <?php
            // 테이블 리스트
            $d->tablelistView($result);
        ?>
    </table>
</div>
<?php $d->PageLinkView($link_url,$totalcnt,$rowsPage,$curPage);?>
</body>
</html>


아래 코드는 위의 HTML 파일과 연관된 내용을 작성한다.


=== dbinfo.php ===

<?php
$db['host'] = "localhost";
$db['name'] = "csharp";
$db['user'] = "root";  // 원래는 root 사용자를 사용하면 안되는데 연습용인지라...
$db['pass'] = "autoset";
$db['port'] = "3306";
?>


=== dbconnect.php ===

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

function isConnectDb($db)
{
    $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
    mysqli_set_charset($conn, "utf8");  // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
    if (mysqli_connect_errno()) {
        echo "Failed to connect to MySQL: " . mysqli_connect_error();
        exit;
    } else {
        return $conn;   
    }
}
?>


=== dbClass.php ===

이 파일은 일부만 발췌하여 적는다.

본 코드에 사용된 함수는 http://link2me.tistory.com/1110 게시글에 올린 첨부파일을 받으면 된다.


class MySQLiDbClass {


    //DB데이터 ARRAY -> 테이블에 출력할 데이터 배열
    function getDbArray($table,$where,$flddata,$orderby,$rowsPage,$curPage){
        global $DB_CONNECT;
        $sql = 'select '.$flddata.' from '.$table.($where?' where '.$this->getSqlFilter($where):'').($orderby?' order by '.$orderby:'').($rowsPage?' limit '.(($curPage-1)*$rowsPage).', '.$rowsPage:'');
        if($result = mysqli_query($DB_CONNECT,$sql)){
            return $result;
        }
    }

    //DB데이터 레코드 총 개수
    function getDbRows($table,$where){
        global $DB_CONNECT;
        $sql = 'select count(*) from '.$table.($where?' where '.$this->getSqlFilter($where):'');
        if($result = mysqli_query($DB_CONNECT,$sql)){
            $rows = mysqli_fetch_row($result);
            return $rows[0] ? $rows[0] : 0;
        }
    }


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

}


=== boardClass.php ===

테이블 리스트를 보여주는 함수는 MySQL 접속인지, MySQLi 접속인지 자동으로 판단하는 함수 로직을 구현하여 코드의 확장성을 고려했다.

게시판에 주로 사용되는 사항을 함수화하여 코드를 간략화하였으나, 개발자 취향에 따라 코드를 함수화해서 사용하든 전부 나열식으로 코딩하든 개발자의 몫이다.

아울러 페이지 링크 부분은 세심하게 테스트까지 마친 코드이다.


class boardClass {

    // column 개수, 게시물 총개수 만큼 자동으로 화면 출력
    function tablelistView($result){
        global $DB_CONNECT;
        if(is_object($DB_CONNECT)  && get_class($DB_CONNECT)=='mysqli'){
            while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
                $view='<tr class="tr1">';
                foreach($row as $column => $value) {
                    $view.='<td class="td2">'.$value.'</td>';
                }
                $view.='</tr>';
                echo $view;
            }
        } else {
            while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
                $view='<tr class="tr1">';
                foreach($row as $column => $value) {
                    $view.='<td class="td2">'.$value.'</td>';
                }
                $view.='</tr>';
                echo $view;
            }
        }
    }

    function PageLinkView($link_url,$totalcnt,$rowsPage,$curPage){
        echo '<div style="position:relative;vertical-align:top;padding-top:0;margin-top:0">';
            echo "<span style='position:absolute;top:10px;'>[총 자료수:".$totalcnt."]</span>";
            echo '<div class="pagelink">';
                $Info = $this->PageList($totalcnt,$rowsPage,$curPage,'');
                if($Info['current_block'] > 2){
                    echo "<a href='".$link_url."?p=1'>◀</a> ";
                }
                if($Info['current_block'] > 1){
                    echo "<a href='".$link_url."?p=".$Info['prev']."'>◁</a> ";
                }
                foreach($Info['current'] as $w) {
                    if($curPage == $w){
                        echo "<a href='".$link_url."?p=".$w."'><span style='color:red;font-size:22pt'>".$w."</span></a> ";
                    } else {
                        echo "<a href='".$link_url."?p=".$w."'>".$w."</a> ";
                    }
                }
                if($Info['current_block'] < ($Info['total_block'])){
                    echo "<a href='".$link_url."?p=".$Info['next']."'>▷</a> ";
                }
                if($Info['current_block'] < ($Info['total_block']-1)){
                    echo "<a href='".$link_url."?p=".$Info['totalPage']."'>▶</a> ";
                }
            echo '</div>';
        echo '</div>';
    }


    // $curPage : 현재 페이지, $totalcnt : 총 게시물수
    // $block_limit : 한 화면에 뿌려질 게시글 개수
    function PageList($totalcnt,$rowsPage,$curPage,$block_limit) {
        $block_limit = $block_limit ? $block_limit : 10;  // 한 화면에 보여줄 개수 기본 10으로 설정

        // 총 페이지수 구하기
        $totalPage = ceil($totalcnt/$rowsPage); 
        if($totalPage == 0) {
            ++$totalPage;
        }
        $total_block = ceil($totalPage / $block_limit); //전체 블록 갯수
       
        $curPage = $curPage ? $curPage : 1; // 현재 페이지

        // 현재 블럭 : 화면에 표시될 페이지 리스트
        $current_block=ceil($curPage/$block_limit);
        // 현재 블럭에서 시작페이지
        $fstPage = (((ceil($curPage/$block_limit)-1)*$block_limit)+1);
        // 현재 블럭에서 마지막 페이지
        $endPage = $fstPage + $block_limit -1;
        if($totalPage < $endPage) {
            $endPage = $totalPage;
        }

        // 시작 바로 전 페이지
        $prev_page = $fstPage - 1;
        // 마지막 다음 페이지
        $next_page = $endPage + 1;

        foreach(range($fstPage, $endPage) as $val) {
            $row[] = $val;
        }
        // 배열로 결과를 돌려준다.
        return array(
            'total_block' => $total_block,
            'current_block' => $current_block,
            'totalPage' => $totalPage,
            'fstPage' => $fstPage,
            'endPage' => $endPage,
            'prev' => $prev_page,
            'next' => $next_page,
            'current' => $row
        );
    }

}

728x90
블로그 이미지

Link2Me

,
728x90

년도와 월을 선택하면 해당월의 마지막 날짜가 자동으로 변경되는 jQuery 를 작성했다.


PHP 와 jQuery 값을 전달하는 방법을 잘 몰라 고생 좀 했다.


jQuery Ajax 를 이용해서 파일 A 에서 전달한 값을 가지고 파일 B(get_LastDay.php)에서 결과를 계산하고 그 결과를 받아서 다시 파일 A에 PHP 변수로는 전달할 수가 없더라. (방법이 있는데 못찾은 것인지는 모름)


년도는 선택하지 않고 월만 선택하는 것도 고려하다보니, 자바스크립트 함수를 사용하지 않고 Ajax 로 전달해서 PHP 함수식을 이용하여 계산된 결과를 받도록 처리했다.


function getLastDay(year, month){
    if(month==4 || month==6 || month==9 || month==11)
        return 30;
    else if(month==2){ //2월
        if(year%4 == 0) // 2월, 윤년일 때
            return 29;
        else    // 2월, 윤년이 아닐 때
            return 28
    } else {
        return 31;
    }
}


Ajax 로 전달받은 결과는 HTML 을 갱신할 수는 있다.

이 방법을 이용해서 코드를 작성했다.


참고로, 년도와 월을 모두 선택한다면 Ajax 사용하지 않고 위 함수를 이용하여 처리해도 된다.

var year = $("#year option:selected").val();
var month = $("#month option:selected").val();
var lastDate = getLastDay(year, month);

$("#day > option").remove(); // remove all items from list
for(i=1; i<=lastDate; i++){
    $("#day").append("<option value='"+ i +"'>"+ i +"</option>");
}


=== 파일 A ===

<?php
date_default_timezone_set('Asia/Seoul');
$YEAR_LAST = date("Y");
$cur_year = $YEAR_LAST;
$cur_month = date("m");
// 현재 월의 마지막 날짜를 일단 선택하도록 처리하고, 년/월을 선택하면 자동으로 변경
$last_day = date("t", mktime(0, 0, 1, $cur_month, 1, $cur_year));
?>

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function(){ // html 문서를 다 읽어들인 후
    var year;
    var month;
    $('#year').on('change', function(){
        if($("#year option:selected").val() !== ""){
            year = $(this).find(":selected").val();
            month = $("#month option:selected").val();
            if($("#month option:selected").val() !== ""){
                $.post('get_LastDay.php',{year:year,month:month}, function(data) {
                    //alert(data);
                    $("#day > option").remove(); // remove all items from list
                    for(i=1; i<=data; i++){
                        $("#day").append("<option value='"+ i +"'>"+ i +"</option>");
                    }
                });
            }
        }
    });


    $('#month').on('change', function(){
        if($("#month option:selected").val() !== ""){
            year = $("#year option:selected").val();
            month = $(this).find(":selected").val();
            $.post('get_LastDay.php',{year:year,month:month}, function(data) {
                $("#day > option").remove(); // remove all items from list
                for(i=1; i<=data; i++){
                    $("#day").append("<option value='"+ i +"'>"+ i +"</option>");
                }
            });
        }
    });

});
</script>
<body>
<label>년도: </label>
<select id="year">
    <option value=''>년도</option>
    <?php for($i=2010; $i <= $YEAR_LAST; $i++){
        echo("<option value='$i'>$i 년</option>");
        } ?>
</select>

<select id="month">
<option value=''> 월 </option>
<?php for($i=1; $i <= 12; $i++){
    echo("<option value='$i'>$i</option>");
    } ?>
</select>

<select id="day">
<option value=''> 일 </option>
<?php for($i=1; $i <= $last_day; $i++){
    echo("<option value='$i'>$i</option>");
    } ?>
</select>

</body>
</html>


=== 파일 B : get_LastDay.php ===

<?php
$select_year = $_POST['year'];
$select_month = $_POST['month'];

$month_day = Array(31,29,31,30,31,30,31,31,30,31,30,31);

if(isset($select_year) && !empty($select_year) && isset($select_month) && !empty($select_month) ){
    // "t"는 두 번째 인자로 넘어온 해당 연,월에 대한 총 일수를 구하기 위한 파라미터
    $last_day = date("t", mktime(0, 0, 1, $select_month, 1, $select_year));
} else if(isset($select_month) && !empty($select_month)) {
    // 월만 선택한 경우에는 2월의 최대값이 28일지, 29일지 알 수 없으므로 최대 29를 선택하도록 처리
    $last_day = $month_day[$select_month-1];
}
echo $last_day;
?>



728x90
블로그 이미지

Link2Me

,
728x90

select 박스를 선택한 값을 jQuery 로 해서 Ajax 로 전송한 결과값을 다시 받은 코드를 구현해봤다.

이 코드를 보면 Ajax 로 값을 어떻게 전달하고, 결과를 다시 어떻게 받은지 알 수 있다.

ajaxPHP.php 파일에서 ehco 의 결과값을 받는다는 걸 알면 응용하는 것은 쉽다.

form 으로 넘기지 않아도 데이터를 전달하고 결과를 받을 수 있다는 걸 알 수 있다.


=== selected html ===

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function(){ // html 문서를 다 읽어들인 후
    $('#selectID').on('change', function(){
        if(this.value !== ""){
            var optVal = $(this).find(":selected").val();
            //alert(optVal);
            $.post('ajaxPHP.php',{optVal:optVal}, function(data) {
                alert(data);  // data는 ajaxPHP.php 파일에서 ehco 문의 결과 값             
            });

        }
    });
});
</script>
<body>
<label>select option: </label>
<select id="selectID">
    <option value="">구분</option>
    <option value="1">Option 1</option>
    <option value="2">Option 2</option>
    <option value="3">Option 3</option>
    <option value="4">Option 4</option>
</select>
</body>
</html>


=== ajaxPHP.php ===

<?php
$ajax = $_REQUEST['optVal'];
if($ajax == 1) {
    echo '첫번째';
} else if($ajax == 2) {
    echo '두번째';
} else if($ajax == 3) {
    echo '세번째';
} else if($ajax == 4) {
    echo '네번째';
}
?>


테스트 해보고 싶으신 분은 코드를 복사해서 테스트 해보세요. 100% 동작되는 코드입니다.


728x90
블로그 이미지

Link2Me

,
728x90

select 박스에서 선택한 값을 읽어오는 jQuery 코드 예제다.

selected 된 값을 표현하는 방법은 아래 3가지 모두 가능하다.

this.value;
$("#selectID option:selected").val();

$(this).find(":selected").val();


라디오버튼에서 선택할 경우

$(this).find(":checked").val();


=== 샘플 ===

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function(){ // html 문서를 다 읽어들인 후
    $('#selectID').on('change', function(){
        if(this.value !== ""){
            //var optVal = $("#selectID option:selected").val();
            var optVal = $(this).find(":selected").val();
            alert(optVal);
        }
    });
});
</script>
<body>
<label>select option: </label>
<select id="selectID">
    <option value="">구분</option>
    <option value="1">Option 1</option>
    <option value="2">Option 2</option>
    <option value="3">Option 3</option>
    <option value="4">Option 4</option>
</select>
</body>
</html>

728x90
블로그 이미지

Link2Me

,
728x90

일일 통계 생성을 위한 테이블 구조 및 테이블에 데이터를 저장하기 위한 코드를 작성했다.

가장 간단하게 userID 별 접속횟수, 날짜별 접속횟수만 기록되게 구현했다.


테이블 구조

CREATE TABLE IF NOT EXISTS Access_Stats (
  uid int(11) NOT NULL AUTO_INCREMENT,
  date char(8) NOT NULL,
  YM char(6) NOT NULL,
  day char(2) NOT NULL,
  IDCnt int(11) NOT NULL DEFAULT '0',
  dailyCnt int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (uid),
  KEY date (date)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


=== 일일단위 통계 생성을 위한 코드 ===

<?php
require_once "dbconnect.php";
require_once "phpclass/statsClass.php";
$a=new statsClass();

// 일일 통계 기록
date_default_timezone_set('Asia/Seoul');
$date = date("Ymd", mktime(0,0,0,date("m"), date("d")-1, date("Y"))); // 어제
$YM = substr($date,0,6);
$day = substr($date,6,2);

if($a->Access_Stats($date) == '0'){ // 해당 날짜에 기록된 정보가 없으면
    $sql = "INSERT INTO Access_Stats (date,YM,day,IDCnt,dailyCnt) (select ".$date.",".$YM.",".$day.",count(distinct LogID),sum(hit) from AccessLog where date='".$date."')";
    @mysql_query($sql);
} else {
    $row = $a->AccessLogCnt($date); // 배열로 가져옴
    $sql = "Update Access_Stats SET IDCnt='".$row[0]."',dailyCnt='".$row[1]."' where date='".$date."'";
    @mysql_query($sql);
}

echo "Work done";
exit;
?>


=== statsClass.php ===

<?php
class statsClass {

    function Access_Stats($date){
        global $db;
        $sql ="select count(date) from Access_Stats where date='".$date."'"; // 기록된 데이터 있는지 조회
        $result=mysql_query($sql);
        if($row=mysql_fetch_row($result)){
            return $row[0];
        }
    }

    function AccessLogCnt($date){
        global $db;
        $sql ="select count(distinct userID),sum(hit) from AccessLog where date='".$date."'";
        $result=mysql_query($sql);
        if($row=mysql_fetch_row($result)){
            return $row; // 배열을 반환
        }
    }

}
?>


매일 일정한 시간에 테이블에 데이터를 자동으로 저장하기 위해서는 crontab 을 이용해야 한다.

crontab 을 이용하기 위한 코드는 아래와 같이 하면 된다.

=== accessLog.php ===

!#/usr/local/php/bin/php -q
<?php
// cron 방식 DB 접속
require_once "/usr/local/apache/htdocs/dbconnect.php";
require_once "/usr/local/apache/htdocs/phpclass/statsClass.php";
$a=new statsClass();

// 일일 통계 기록
date_default_timezone_set('Asia/Seoul');
$date = date("Ymd", mktime(0,0,0,date("m"), date("d")-1, date("Y"))); // 어제
$YM = substr($date,0,6);
$day = substr($date,6,2);
if($a->Access_Stats($date) == '0'){
    $sql = "INSERT INTO Access_Stats (date,YM,day,IDCnt,dailyCnt) (select ".$date.",".$YM.",".$day.",count(distinct LogID),sum(hit) from AccessLog where date='".$date."')";
    @mysql_query($sql);
} else {
    $row = $a->AccessLogCnt($date);
    $sql = "Update Access_Stats SET IDCnt='".$row[0]."',dailyCnt='".$row[1]."' where date='".$date."'";
    @mysql_query($sql);
}

echo "
Work done";
exit;
?>


=== crontab 파일내에 코드 추가 ===

crontab 사용법은 http://link2me.tistory.com/990 참조

PHP 와 crontab 에 대한 사항을 좀 더 알고 싶다면 http://link2me.tistory.com/1013 참조


먼저 php 가 설치된 경로를 찾아야 한다.

// PHP 경로 찾기
whereis php


crontab 에 추가할 내용

// 매일 2시 0분에 실행
0 2 * * * /usr/local/php/bin/php /usr/local/apache/htdocs/stats/accessLog.php

// 매 5분마다 실행 ==> 실제 제대로 저장되는지 여부를 테스트
*/5 * * * * /usr/local/php/bin/php /usr/local/apache/htdocs/
stats/accessLog.php

728x90
블로그 이미지

Link2Me

,
728x90

HTML, javaScript, jQuery 는 Client에서 해석하는 언어이고, PHP는 서버와의 통신을 위한 스크립트 언어다.

서버상에서 가져온 파일을 읽으려면 클라이언트에서 인식할 수 있도록 해줘야 한다.

아니면 PHP 코드를 HTML 파일보다 먼저 읽어들이도록 위치를 위에다 놓은 것도 방법이다.


눈에 보이지 않게 <div id="div1" data-lat="<?php echo $lat;?>"></div> 를 이용해서 PHP 변수를 기록하면, HTML 에 기록된 내용을 jQuery, javaScript 에서 읽어올 수 있다.


PHP 변수를 받는 방법은 다른 파일에서 넘겨준 걸 읽을 때는 $_GET, $_POST 배열로 받아서 처리하면 된다.

아니면 직접 include_once '읽고자하는 파일'; 을 지정해주고 파일을 include 하면 해당 파일의 변수가 인식된다.


속성값 읽어오기

자바스크립트에서는 document.getElementById('div1').getAttribute('data-lat');

jQuery 에서는 $("#div1").attr('data-lat');


속성값 변경하기

자바스크립트에서는 document.getElementById('div1').setAttribute('data-lat','변경할값');

jQuery 에서는 $("#div1").attr('data-lat','변경할값');


DB에서 테이블 형태로 읽어온 걸 화면에 표시하고 테이블내 한 행의 게시글의 특정 정보를 알아내고자 할 때에는 this 를 사용한다.

group=$(this).attr('data-group');


==== 예제 파일 =====

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function(){ // html 문서를 다 읽어들인 후
    //var lat = $("#div1").attr('data-lat'); // jQuery 방식
    var lat = document.getElementById('div1').getAttribute('data-lat'); // javaScript 방식
    var lon = $("#div2").attr('data-lon');
    alert("lat:"+ lat +" lon:"+ lon);
});
</script>
<body>
<?php
//include_once 'getPHP.php'; 다른 파일 내용을 읽어오고자 할 때
$lat = 33.4505296;
$lon = 126.570667;
?>
<div id="div1" data-lat="<?php echo $lat;?>"></div>
<div id="div2" data-lon="<?php echo $lon;?>"></div>
</body>
</html>

728x90
블로그 이미지

Link2Me

,
728x90

jQuery Mobile 템플릿을 사용하여 Layout 기본 구조를 파악해보자.



jQuery Mobile 에 대한 자세한 설명은 http://www.w3schools.com/jquerymobile/default.asp 에서 영문으로 찬찬히 읽어가면서 습득하면 도움된다.

jQuery Mobile 버전이 달라지면서 표시되는 색상도 변경될 수 있으므로 무조건 버전을 높은걸 사용하는게 좋지 않을 수도 있다. 높은 버전을 사용하면 변경된 부분을 파악해서 수정해주어야 한다.


<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="pragma" content="no-cache" />

<!-- Include meta tag to ensure proper rendering and touch zooming -->
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width" />

<!-- Include jQuery Mobile stylesheets -->

<link href="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.css" rel="stylesheet" type="text/css" />

<!-- Include the jQuery library -->

<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>

<!-- Include the jQuery Mobile library -->

<script src="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.js"></script>
</head>


<body>
<div data-role="page">
    <div data-role="header" data-position="fixed" data-theme="b">
        <h1>Header title</h1>
    </div>
    <div id="content" data-role="content" data-theme="e">
        <ul data-role="listview">
        <?php
            echo  '<li>';
            echo  '<a href="#">목록이 없습니다.</a>';
            echo  '</li>';
        ?>
        </ul>
    </div>
    <div data-role="footer" data-position="fixed" data-theme="d">
        <table>
        <tr>
        <td width="15%"><a href="index.html" data-role="button" data-icon="plus" data-iconpos="left">추가</a>
        </td>
        <td width="70%" align=center>Footer Title</td>
        <td width="15%"></td>
        </tr>
        </table>
    </div>
</div>
</body>
</html>


일반적인 웹 사이트의 경우 1 페이지 = 1 HTML 파일이지만, jQuery Mobile 에서는 data-role 속성 'page'를 갖는 div 요소가 1페이지가 된다.

http://www.w3schools.com/jquerymobile/tryit.asp?filename=tryjqmob_pages

에서 보면 1 페이지 안에
<div data-role="page" id="pageone">

<div data-role="page" id="pagetwo">

로 구분하여 작성된 것을 실행해 볼 수 있다. 즉 HTML 페이지는 1Page이지만, jQuery Mobile 에서는 2Page 인 것처럼 만들 수 있다는 의미다.


data-theme="b" 를 사용하여 테마를 변경할 수 있다. (a ~ e 까지 5개 테마)

data-inset="true" 는 모서리를 둥글게 처리

data-split-icon="gear" : 스플릿된 우측모양이 기어모양

data-position="fixed" : 하단에 고정하는 기능

data-rel="dialog" : 다이얼로그 팝업창으로 띄워라

<div data-role="header" data-add-back-btn="true" data-back-btn-text="이전">

data-add-back-btn="true" : Back 버튼 표시

data-back-btn-text="뒤로" : 라벨 변경

<ul data-role="listview" data-theme="e" data-inset="true">

data-theme="e" : 리스트 테마의 변경

data-inset="true" : 모서리를 둥글게 처리

<li data-theme="b" >

data-theme="b" : 해당 항목만 별도로 리스트 테마 변경

<li data-role="list-divider">

data-role="list-divider" : 리스트에 제목 설정

<span class="ui-li-count">10</span><!-- class="ui-li-count" 은 카운트 버블 표시 -->

<ul data-role="listview" data-count-theme="b" data-filter="true" data-filter-placeholder="검색어">
        <!--data-count-theme="c" : 카운드 버블의 테마 변경 -->
        <!--data-filter="true" : 검색 필터 => 일치하는 검색어만 표시됨 -->
        <!--data-filter-placeholder="검색어" : 플레이스 홀더 변경 -->
        <li><a href="#index" data-transition="slideup">
        <!--data-transition="slideup" 위쪽으로 슬라이드되면서 페이지 전환 -->
        <h3>메인 페이지</h3>
        </a></li>
        <li><a href="#about" data-transition="slide">
         <!--data-transition="slide" 왼쪽으로 슬라이드되면서 페이지 전환 -->

data-transition="flip"

data-transition="flip" data-direction="reverse"

<li><a href="#access" data-transition="slide">
    <p class="ui-li-aside">보충정보 표시</p>
    <!--class="ui-li-aside" 속성 추가시 보충정보 표시됨 -->
    <h3>오시는 길</h3>
    <p>설명 내용</p> <!-- 설명문이 있는 리스트 표시됨 -->
    <span class="ui-li-count">99</span>
    <!-- class="ui-li-count" 은 카운트 버블 표시 -->
</a></li>
<li><a href="#contact" data-transition="pop">
    <p class="ui-li-aside">보충정보 표시</p>
    <!--class="ui-li-aside" 속성 추가시 보충정보 표시됨 -->
    <h3>문의</h3>
    <p>설명 내용</p> <!-- 설명문이 있는 리스트 표시됨 -->
</a></li>

<div data-role="content">
    <form action="form.php" method="post">
        <div data-role="fieldcontain">
            <label for="name">이름</label>
            <input type="text" id="name"><!-- id는 전체 소스 기준으로 중복되지 않게 처리해라. -->
        </div>
        <div data-role="fieldcontain">
            <label for="gender">성별</label>
            <select name="gender" id="gender" data-role="slider"><!--플립스위치 표시 -->
            <option value="남자">남자</option>
            <option value="여자">여자</option>
            </select>
        </div>
        <div data-role="fieldcontain">
            <fieldset data-role="controlgroup">
                <legend>주제별 문의</legend>
                <input type="checkbox" name="type1" id="type1" value="수주">
                <label for="type1">프로그램 수주</label>
                <input type="checkbox" name="type2" id="type2" value="기획">
                <label for="type2">프로그램 기획</label>
                <input type="checkbox" name="type3" id="type3" value="개발">
                <label for="type3">프로그램 개발</label>
                <input type="checkbox" name="type4" id="type4" value="배포">
                <label for="type4">프로그램 배포</label>
            </fieldset>
        </div>
        <div data-role="fieldcontain">
            <label for="comment">문의 내용</label>
            <textarea id="comment"></textarea>
        </div>
        <div data-role="fieldcontain">
            <input type="button" value="취소" data-theme="a" data-icon="delete" data-inline="true">
            <input type="submit" value="전송" data-theme="a" data-icon="arrow-r" data-inline="true">

        </div>
    </form>

</div>


jQuery 아이콘 모양은 http://demos.jquerymobile.com/1.4.5/icons/ 에서 참조하면 된다.

data-role 의 설명은 http://blog.naver.com/kimchangyoun/70184929188 참조


728x90
블로그 이미지

Link2Me

,
728x90

홈페이지에 접속시 index.php 처리 흐름도는 다음 그림과 같다.


프로그램에서 중요한 것은 어떤 로직으로 그릴것인가 하는 점이다.

사용하는 언어에 따른 문법, 사용법은 당연히 익혀야 한다.


1. index.php 파일에는 순수한 PHP 코드만으로 session 이 있으면 바로 메인페이지로 접속하도록 처리한다.

    session 정보가 없으면 로그인 화면을 띄우도록 한다.

    <?php
    if(!isset($_SESSION)) {
        session_start();
    }
    if(isset($_SESSION['userID']) && !empty($_SESSION['userID'])){
        // 세션 정보가 있으면
        include "main.php";       
    } else {
        // 세션 정보가 없으면
        include "loginForm.php";
    }
    ?>


    이 골격 기반위에 mobile 접속체크 기능을 추가할 수도 있다.

    추가한다면 loginClass.php 파일안에 mobile 체크 함수를 추가할 수 있다.


2. loginForm.php 파일에는 HTML header, body 정보가 들어가 있다.

    - 로그인 정보는 ID, PW 정보이므로 암호화하여 전송하는 것이 안전하다.

    - POST 전송방식으로 전송하여 ID,PW 정보 내용이 보이지 않도록 한다.

    - POST 전송방식 loginForm.php 파일

    <!DOCTYPE html>
    <html>
    <head>
        <title>로그인</title>
        <meta charset="utf-8" />    
        <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0,user-scalable=no"/>
        <meta http-equiv="cache-control" content="no-cache" />
        <meta http-equiv="expires" content="0" />
        <meta http-equiv="pragma" content="no-cache" />

        <!-- Include jQuery Mobile stylesheets -->

        <link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.css">

        <!-- Include the jQuery library -->

        <script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>

        <!-- Include the jQuery Mobile library -->

        <script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js"></script>

        <!-- 개발자 본인이 만든 jQuery -->

        <script src="js/loginScript.js"></script>
    </head>
    <body>
    <div id="content" data-role="content" style="margin-top:5%;">
        <center>
        <form id="loginForm" method="post" action="#" >
            <input type="text" name="userID" id="userID" value="" placeholder="아이디" style="width:50%" />
            <input type="password" name="password" id="password" value="" placeholder="패스워드" style="width:50%"/>
            <button type="submit" data-inline="true">로그인</button>
            <button type="reset" data-inline="true">다시작성</button>
        </form>
        </center>
    </div>
    </body>
    </html>


    === loginScript.js ===

    $(document).ready(function(){
        $("#loginForm").submit(function(){
            var loginID = $("#userID").val();
            var loginPW = $("#password").val();

            if(loginID =='') {
                alert("아이디를 입력하세요");
                $("#userID").focus();
                return false;
            }
           
            if(loginPW =='') {
                alert("패스워드를 입력하세요!");
                $("input[type='password']").focus();
                //$("#password").focus();
                return false;
            }    
            $.post('loginChk.php',{userID:loginID, password:loginPW}, function(msg) {
                if(msg==1){ // loginChk.php 파일에서 echo 결과값
                    window.location.href = 'main.php';
                } else {
                    alert('다시 로그인 하세요');
                }
            });
            return false;
        });

        $("input[type='reset']").click(function(){
            if(!confirm("정말 입력을 취소하시겠습니까?")){
                return false;
            }
        });
    });


3. loginChk.php 파일

<?php
if(empty($_POST['userID']) || empty($_POST['password'])){
    echo 0; // 실패
} else {
    $loginID=$_POST['userID'];
    $loginPW=$_POST['password'];

    require_once 'dbconnect.php';
    require_once 'phpclass/loginClass.php';

    $c=new LoginClass();
    $row=$c->UserAuthCheck($loginID,$loginPW);
    if(is_array($row)){
        if(!isset($_SESSION)) {
               session_start();
           }
        $_SESSION['userID'] = $row['userID'];
        $_SESSION['level'] = $row['level'];
        echo 1; // 로그인 성공
    } else {
        if(!isset($_SESSION)) {
               session_start();
           }
        session_destroy();
        echo 0; // 실패
    }
}
?>


※ dbconnect.php 파일 만드는 방법은 http://link2me.tistory.com/1110 참조하라.


=== loginClass.php 파일 발췌 ===

로그인 검사는 해킹 공격적인 요소를 필터링 하기 위한 것도 고려했다.

<?php
class LoginClass {
    function UserAuthCheck($u,$p) {
        if(!isset($u) || !isset($p) || empty($u) || empty($p)) {
            return 0;
        } else {
            global $db;           
            $u = preg_replace("/[\s\t\'\;\"\=\--]+/","", $u); // 공백이나 탭 제거(사용자 실수 방지)
            $p = preg_replace("/[\s\t\'\;\"\=\--]+/","", $p); // 공백이나 탭 제거, 특수문자 제거
            // SQL injection 검사
            $u = htmlentities($u); // <script>documnet.cookie();</script> 공격 방지, 한글인식 불가
            $p = htmlentities($p); // < 를 \< 로 바꿔준다.
            $u = str_replace(array("'",""","'",'"'), array("&#39;","&quot;","&#39;","&quot;"), $u);
            if(preg_match('/[\']/',$u)) return 0; // no quotes
            if(preg_match('/[\/\\\\]/', $u)) return 0; // no slashes
            if(preg_match('/(and|null|where|limit)/i', $u)) return 0; // i는 대소문자 구별하지 말라
            if(!preg_match('/^[0-9a-zA-Z\~\!\@\#\$\%\^\&\*\(\)]{7,}$/',$p)) return 0; // 7자리이상 허용 문자만 통과
            $sql = "select * from member where pw=md5('".$p."') and id= '".$u."' ";
            if($result = mysql_query($sql,$db)) { //성공
                $row = mysql_fetch_array($result);
                if($row == NULL) return 0;
                return $row;
            } else {
                return '-1';
            }
        }
    }

}//end class LoginClass

?>


4. main.php 파일에도 HTML header, body 정보가 들어가 있다.

   메인 페이지 내용을 작성하여 원하는 데이터를 출력한다.

   DB에서 조회하여 가져온 데이터를 테이블 형식으로 보여주고자 한다면

   너무 많은 자료를 가져오면 부하문제도 생기고 화면 스크롤 문제도 생기므로

   페이징 처리를 해서 보여줘야 한다.

   http://link2me.tistory.com/1112 참조


5. 변수가 제대로 넘어오는지 체크하고자 할 경우

아래 코드를 추가하고 exit; 를 넣은 이유는 다음 코드를 실행하지 말고 중단해서 변수가 제대로 넘어오는지 여부를 한번에 확인하고자 하는 목적이다.


@extract($_POST);
var_dump($_POST);
exit;

728x90
블로그 이미지

Link2Me

,
728x90

PHP 에서 페이징 처리하는 방법이다.
Paging 이란 전체 자료를 가져오는 것이 아니라 일정한 갯수만큼 화면에 가져오도록 처리하는 로직이다.


1. 한 페이지에 보여줄 개수를 정한다.

2. DB에서 전체 자료수(total Rows)에서 총 페이지수를 구한다.

3. 실제 페이지 번호가 표시될 개수를 정한다. 보편적으로 10개씩 보여주도록 할 수 있다.

4. 첫페이지로 가는 버튼

5. 마지막 페이지로 가는 버튼

6. 이전 블록으로 가는 버튼

7. 다음 블록으로 가는 버튼

 

변수를 정의해보자.

1. $totalcnt : 총 게시물수

2. $rowsPage : 한 페이지에 보여줄 개수

3. $totalPage = ceil($totalcnt/$rowsPage); // 총 페이지수

    총페이지수가 0 이면 1로 표기

    if($totalPage == 0) {

          ++$totalPage;

    }

4. $total_block = ceil($totalPage / $block_limit); //전체 블록 갯수

5. $curPage = $curPage ? $curPage : 1; // 현재 페이지

6. $block_limit : 한 화면에 뿌려질 게시글 개수

7. $current_block=ceil($curPage/$block_limit); // 현재 블럭 : 화면에 표시될 페이지 리스트

8. $fstPage = (((ceil($curPage/$block_limit)-1)*$block_limit)+1); // 현재 블럭의 시작

9. $endPage = $fstPage + $block_limit -1; // 현재 블럭의 마지막

    if($totalPage < $endPage) {
        $endPage = $totalPage;
     }
10. $prev_page = $fstPage - 1; // 시작 바로 전 페이지

11. $next_page = $endPage + 1; // 마지막 다음 페이지

 

 

이제 본문에 뿌릴 개수를 함수로 만들어 보자.

<?php
class MySQLDbClass {

 

    function isConnectDb($db) {
        $conn = mysql_connect($db['host'].':'.$db['port'],$db['user'],$db['pass']);
        //Set encoding
        mysql_query("SET CHARSET utf8");
        mysql_query("SET NAMES 'utf8' COLLATE 'utf8_general_ci'");
        if(!$conn){
        die('Not connected :' . mysql_error());
        exit;
        }
        $selc = mysql_select_db($db['name'],$conn); // 접근한 계정으로 사용할 수 있는 DB 선택
        return $selc ? $conn : false;
    }

 

    //DB데이터 ARRAY -> 테이블에 출력할 데이터 배열
    // order by : 정렬 순서, rowsPage : 화면에 출력할 개수, curPage : 현재 페이지
    function getDbArray($table,$where,$flddata,$orderby,$rowsPage,$curPage){
        global $db;
        $curPage = $curPage ? $curPage : 1; // 현재 페이지가 없으면 1로 설정
        $sql = 'select '.$flddata.' from '.$table.($where?' where '.$this->getSqlFilter($where):'').($orderby?' order by '.$orderby:'').($rowsPage?' limit '.(($curPage-1)*$rowsPage).', '.$rowsPage:'');
        //echo $sql;
        $result = mysql_query($sql);
        return $result;
    }

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

}//end dbClass

 

화면 하단에 뿌릴 페이징 처리 함수 만들기

dbClass 함수 안에 넣어도 되고 별도의 클래스를 만들어서 호출해서 사용해도 된다.

// $curPage : 현재 페이지, $totalcnt : 총 게시물수
// $block_limit : 한 화면에 뿌려질 게시글 개수
function PageList($totalcnt,$rowsPage,$curPage,$block_limit) {
    $block_limit = $block_limit ? $block_limit : 10;  // 한 화면에 보여줄 개수 기본 10으로 설정

    // 총 페이지수 구하기
    $totalPage = ceil($totalcnt/$rowsPage); 
    if($totalPage == 0) {
        ++$totalPage;
    }
    $total_block = ceil($totalPage / $block_limit); //전체 블록 갯수
   
    $curPage = $curPage ? $curPage : 1; // 현재 페이지

    // 현재 블럭 : 화면에 표시될 페이지 리스트
    $current_block=ceil($curPage/$block_limit);
    // 현재 블럭에서 시작페이지
    $fstPage = (((ceil($curPage/$block_limit)-1)*$block_limit)+1);
    // 현재 블럭에서 마지막 페이지
    $endPage = $fstPage + $block_limit -1;
    if($totalPage < $endPage) {
        $endPage = $totalPage;
    }

    // 시작 바로 전 페이지
    $prev_page = $fstPage - 1;
    // 마지막 다음 페이지
    $next_page = $endPage + 1;

    foreach(range($fstPage, $endPage) as $val) {
        $row[] = $val;
    }
    // 배열로 결과를 돌려준다.
    return array(
        'total_block' => $total_block,
        'current_block' => $current_block,
        'totalPage' => $totalPage,
        'fstPage' => $fstPage,
        'endPage' => $endPage,
        'prev' => $prev_page,
        'next' => $next_page,
        'current' => $row
    );
}

 

페이징 화면에 출력하기

HTML 문법은 bootstrap3 또는 bootstrap4에 맞게 Class만 수정해주면 된다.

function PageLinkView($link_url,$totalcnt,$rowsPage,$curPage){
    echo '<div style="position:relative;vertical-align:top;padding-top:0;margin-top:0">';
        echo "<span style='position:absolute;top:10px;'>[총 자료수:".$totalcnt."]</span>";
        echo '<div class="pagelink">';
            $Info = $this->PageList($totalcnt,$rowsPage,$curPage,'');
            if($Info['current_block'] > 2){
                echo "<a href='".$link_url."?p=1'>◀</a> ";
            }
            if($Info['current_block'] > 1){
                echo "<a href='".$link_url."?p=".$Info['prev']."'>◁</a> ";
            }
            foreach($Info['current'] as $w) {
                if($curPage == $w){
                    echo "<a href='".$link_url."?p=".$w."'><span style='color:red;font-size:22pt'>".$w."</span></a> ";
                } else {
                    echo "<a href='".$link_url."?p=".$w."'>".$w."</a> ";
                }
            }
            if($Info['current_block'] < ($Info['total_block'])){
                echo "<a href='".$link_url."?p=".$Info['next']."'>▷</a> ";
            }
            if($Info['current_block'] < ($Info['total_block']-1)){
                echo "<a href='".$link_url."?p=".$Info['totalPage']."'>▶</a> ";
            }
        echo '</div>';
    echo '</div>';
}   

 

사용법 예제

<?php
include_once 'dbinfo.php';
require_once 'phpclass/dbClass.php';

$conn = new MySQLDbClass(); // DB 함수
$db = $conn->isConnectDb($DB);

// 화면에 출력할 칼럼 발췌
$flddata ="(select name from cate where uid=d.cat1) as cat1,subject,content";
$rowsPage = 12;
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$result = $conn->getDbArray('data d','',$flddata,'',$rowsPage,$curPage);
$totalcnt 구하는 함수 처리

 

본문 Layout : 테이블을 작성해서 $result 배열 결과물을 table td 태그안에 기록

페이징 Layout : 페이징 화면 출력

 

?>

 

함수로 만든 모든 것을 표기하지는 않았다.

이런 형태로 만들어 볼 수 있다는 것만 알 수 있게 작성했다.

 

실 사용 예제

- 네이버지식인에 링크를 걸어줬더니 성의없는 답변이라는 댓글을 보고서 실제 사용하는 예제를 추가했음.

- 어떻게 처리하는지에 대한 흐름만 이해하면 되는 사항.

<?php
if(!isset($_SESSION)) {
    session_start();
}
 
require_once '../dbconnect.php';
require_once '../phpclass/dbClass.php';
require_once '../phpclass/bbsClass.php';
require_once '../phpclass/adminClass.php';
$a = new adminClass();
$b = new bbsClass();
$c = new MySQLDbClass();
 
// 설문 테이블 상태 변경 체크
$a->surveyDBupdate();
 
$url = $_SERVER['PHP_SELF']; // 현재 실행중인 파일명 가져오기
$page = isset($_GET['page'])? trim($_GET['page']):1;//페이지 변수 설정
$rowsPage = 10// 한 화면에 표시되는 게시글 수
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$bd_name = isset($_GET['bd_name']) ? $_GET['bd_name'] :'';
$mode = isset($_GET['mode']) ? $_GET['mode'] :'list';
$sel = isset($_GET['sel'])? $_GET['sel'] : '';
 
$flddata ="*";// 화면에 출력할 칼럼 발췌
$where = isset($_GET['where']) ? $_GET['where']: '';
$keyword = isset($_GET['keyword']) ? $_GET['keyword']: '';
$xorderbyisset($xorderby) ? $xorderby : 'uid DESC';
if($where && $keyword) {
    if($where == 'title'$sql = "title LIKE '%".$keyword."%' ";
    if($where == 'content'$sql = "content LIKE '%".$keyword."%' ";
    if($where == 'unify') {
        $sql = "(title LIKE '%".$keyword."%' OR content LIKE '%".$keyword."%') ";
    }
else {
    $sql ='';
}
if(!empty($sel)){
    if($keyword){
        $sql .= " and status = ".$sel;
    } else {
        $sql = " status = ".$sel;
    }
}
 
//echo $sql.'<br />';
 
$link_url=($bd_name?'bd_name='.$bd_name.'&amp;':'').($mode?'mode='.$mode.'&amp;':'').($sel?'sel='.$sel.'&amp;':'').($where?'where='.$where.'&amp;':'').($keyword?'keyword='.urlencode(stripslashes($keyword)).'&amp;':'');
$g['bbs_reset'= $url// 초기화
 
$table ='poll_admin';
$rows$c->getDbArray($table,$sql,$flddata,$xorderby,$rowsPage,$curPage);
$NUM = $c->getDbRows($table,$sql); // 전체 게시글수
$TPG = $b->getTotalPage($NUM,$rowsPage);
?>
 
<!DOCTYPE html>
<head>
<title><?php echo $title;?></title>
<meta charset="utf-8">
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../vendor/bootstrap/css/bootstrap.min.css">
<link href="vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="../css/survey.css" />
<script src="../vendor/jquery/jquery.min.js"></script>
<script src="../vendor/bootstrap/js/bootstrap.min.js"></script>
<script src="../js/modal.js"></script>
</head>
<body>
<div class="container">
<table class="table table-bordered">
<tr>
    <td align=center><font class='title'><b>설문조사<br><br>
    <div class="pull-left info bmargin">
        <select name='sel' onchange='document.searchf.sel.value=this.value;document.searchf.submit();'>
        <option value=''>전체목록보기</option>
        <option value='1' <?php if("1"==$sel):?> selected<?php endif?>>진행중목록</option>
        <option value='2' <?php if("2"==$sel):?> selected<?php endif?>>종료 목록</option>");
        </select>
    </div>
    <div class="pull-right info bmargin">
        <a href='survey_admin.php'><button class="w3-button w3-blue-grey w3-tiny">관리</button></a>&nbsp;
        <a href='add_form.php?mode=new'><button class="w3-button w3-blue-grey w3-tiny">등록</button></a>
    </div>
<!--  설문 내용 -->
<table class='w3-table w3-bordered'>
    <thead>
        <tr class="w3-blue-grey">
            <th width="8%">No</th>
            <th width="43%">설문 제목</th>
            <th width="11%">총응답</th>
            <th width="13%">시작일</th>
            <th width="13%">종료일</th>
            <th width="10%">상 태</th>
        </tr>
    </thead>
    <tbody>
        <?php $i=1;foreach($rows as $R):?>
        <tr>
            <td><?php echo $i;?></td>
            <td>
            <?php
                $memo="&nbsp;<a href='survey_memo.php?bd_name=".$R['bd_name']."&mode=list&sel=".$sel."'><img src='./image/memo.gif' border='0' alt='메모보기'></a>";
                if($R['enddate'< date("Y-m-d")){
                    echo "<a href='survey_res.php?bd_name=".$R['bd_name']."&sel=".$sel."'> ".$R['title']."</a>$memo</td>";
                    $status = "종 료";
                } else {
                    if($R['startdate'> date("Y-m-d")) $status = "준비중";
                    else $status = "진행중</td>";
                    echo "<a href='survey.php?bd_name=".$R['bd_name']."&sel=".$sel."'> ".$R['title']."</a>$memo</td>";
                }
            ?>
            </td>
            <td><?php echo $R['totalcnt'];?></td>
            <td><?php echo $R['startdate'];?></td>
            <td><?php echo $R['enddate'];?></td>
            <td><?php echo $status;?></td>
        </tr>
        <?php $i++;endforeach;?>
    </tbody>
</table>
<div><br></div>
<div class='searchbox'>
    <form name="searchf" class="form-inline" action="<?php echo $url.'?'.$link_url;?>">
        <input type="hidden" name="mode" value="<?php echo $mode?>" />
        <input type="hidden" name="sel" value="<?php echo $sel?>" />
        <input type="hidden" name="p" value="1" />
        <select name="where" class="form-control input-sm">
            <option value="unify">통합</option>
            <option value="title">제목</option>
            <option value="content">설명</option>
        </select>
        <div class="input-group input-group-sm">
            <input type="text" name="keyword" value="" class="form-control input-search" placeholder="검색어">
            <span class="input-group-btn">
                <button type="button" class="btn btn-default" onclick="this.form.where.value='title';this.form.keyword.value='',this.form.submit();" title="리셋"><i class="glyphicon glyphicon-repeat"></i></button>
                <button type="submit" class="btn btn-info" title="검색"><i class="glyphicon glyphicon-search"></i></button>
            </span>
        </div>
    </form>
</div>
<div class="pull-right info">
    <a href="<?php echo $g['bbs_reset']?>" class="btn btn-default btn-sm pull-right">처음목록</a>
</div>
<?php $b->PageLinkView($url,$NUM,$rowsPage,$curPage,$link_url);?>
</td>
</tr>
</table>
</div>
</body>
</html>
<?php mysqli_close($db);?>
 

 

<?php
class bbsClass extends MySQLDbClass {
 
    function PageLinkView($link_url$totalcnt$rowsPage$curPage$m) {
        echo '<div id="paging">';
        echo '<ul class="pagination">';
        $Info = $this -> PageList($totalcnt$rowsPage$curPage'');
        if ($Info['current_block'> 2) {
            echo "<li><a href='" . $link_url . "?p=1&$m'>◀</a></li> ";
        }
        if ($Info['current_block'> 1) {
            echo "<li><a href='" . $link_url . "?p=" . $Info['prev'] . "&$m'>◁</a></li> ";
        }
        foreach ($Info['current'as $w) {
            if ($curPage == $w) {
                echo "<li class='act'><a href='" . $link_url . "?p=" . $w . "&$m'><span style='color:red;'>" . $w . "</span></a></li> ";
            } else {
                echo "<li><a href='" . $link_url . "?p=" . $w . "&$m'>" . $w . "</a></li> ";
            }
        }
        if ($Info['current_block'< ($Info['total_block'])) {
            echo "<li><a href='" . $link_url . "?p=" . $Info['next'] . "&$m'>▷</a></li> ";
        }
        if ($Info['current_block'< ($Info['total_block'- 1)) {
            echo "<li><a href='" . $link_url . "?p=" . $Info['totalPage'] . "&$m'>▶</a></li> ";
        }
        echo '</ul>';
        echo '</div>';
    }
 
    // $curPage : 현재 페이지, $totalcnt : 총 게시물수
    // $block_limit : 한 화면에 뿌려질 게시글 개수
    function PageList($totalcnt,$rowsPage,$curPage,$block_limit) {
        $block_limit = $block_limit ? $block_limit : 10;  // 한 화면에 보여줄 개수 기본 10으로 설정
 
        // 총 페이지수 구하기
        $totalPage = ceil($totalcnt/$rowsPage);
        if($totalPage == 0) {
            ++$totalPage;
        }
        $total_block = ceil($totalPage / $block_limit); //전체 블록 갯수
 
        $curPage = $curPage ? $curPage : 1// 현재 페이지
 
        // 현재 블럭 : 화면에 표시될 페이지 리스트
        $current_block=ceil($curPage/$block_limit);
        // 현재 블럭에서 시작페이지
        $fstPage = (((ceil($curPage/$block_limit)-1)*$block_limit)+1);
        // 현재 블럭에서 마지막 페이지
        $endPage = $fstPage + $block_limit -1;
        if($totalPage < $endPage) {
            $endPage = $totalPage;
        }
 
        // 시작 바로 전 페이지
        $prev_page = $fstPage - 1;
        // 마지막 다음 페이지
        $next_page = $endPage + 1;
 
        foreach(range($fstPage$endPageas $val) {
            $row[] = $val;
        }
        // 배열로 결과를 돌려준다.
        return array(
            'total_block' => $total_block,
            'current_block' => $current_block,
            'totalPage' => $totalPage,
            'fstPage' => $fstPage,
            'endPage' => $endPage,
            'prev' => $prev_page,
            'next' => $next_page,
            'current' => $row
        );
    }
 
    //총페이지수
    function getTotalPage($num,$rec){
        return @intval(($num-1)/$rec)+1;
    }
 
}//end class boardClass

 

 
<?php
extract($_GET);
extract($_POST);
class MySQLDbClass {
    protected $db;
    private $host = 'localhost';
    private $database = 'survey';
    private $userid = 'root';
    private $password = 'autoset';
 
    public function __construct() {
        $this->db = $this->connectDB();
    }
 
    function __destruct(){
        mysqli_close($this->connectDB());
        //mysqli_close($this->db);
    }
 
    private function connectDB() {
        $dbconn = mysqli_connect($this->host, $this->userid, $this->password, $this->database);
        mysqli_set_charset($dbconn"utf8"); // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
        if (mysqli_connect_errno()) {
           printf("Connect failed: %s\n", mysqli_connect_error());
           exit();
        } else {
          return $dbconn;
        }
    }
 
    //DB-UID데이터
    function getUidData($table,$uid){
        return $this->getDbData($table,'uid='.(int)$uid,'*');
    }
 
    // DB Query Cutom 함수
    function getDbData($table,$where,$column) {
        $result = mysqli_query($this->db,'select '.$column.' from '.$table.($where?' where '.$this->getSqlFilter($where):''));
        $row = mysqli_fetch_array($result);
        return $row;
    }
 
    // DB Query result 함수
    function getDbresult($table,$where,$column) {
        $result = mysqli_query($this->db,'select '.$column.' from '.$table.($where?' where '.$this->getSqlFilter($where):''));
        return $result;
    }
 
    //DB데이터 ARRAY -> 테이블에 출력할 데이터 배열
    function getDbArray($table,$where,$flddata,$orderby,$rowsPage,$curPage){
        $sql = 'select '.$flddata.' from '.$table.($where?' where '.$this->getSqlFilter($where):'').($orderby?' order by '.$orderby:'').($rowsPage?' limit '.(($curPage-1)*$rowsPage).', '.$rowsPage:'');
        if($result = mysqli_query($this->db,$sql)){
            return $result;
        }
    }
 
    //DB데이터 레코드 총 개수
    function getDbRows($table,$where){
        $sql = 'select count(*) from '.$table.($where?' where '.$this->getSqlFilter($where):'');
        if($result = mysqli_query($this->db,$sql)){
            $rows = mysqli_fetch_row($result);
            return $rows[0] ? $rows[0] : 0;
        }
    }
 
    //DB삽입
    function getDbInsert($table,$key,$val){
        mysqli_query($this->db,"insert into ".$table." (".$key.")values(".$val.")");
    }
 
    //DB업데이트
    function getDbUpdate($table,$set,$where){
        mysqli_query('set names utf8');
        mysqli_query('set sql_mode=\'\'');
        mysqli_query($this->db,"update ".$table." set ".$set.($where?' where '.$this->getSqlFilter($where):''));
    }
 
    //DB삭제
    function getDbDelete($table,$where)    {
        mysqli_query($this->db,"delete from ".$table.($where?' where '.$this->getSqlFilter($where):''));
    }
 
    //SQL필터링
    function getSqlFilter($sql){
        return $sql;
    }
 
}//end dbClass
 
?>

 

728x90
블로그 이미지

Link2Me

,
728x90

코딩을 배우면서 느끼는 것은 기본기가 중요하다는 것이다.

그래서 기초적인 걸 다시 정리차원에서 적어둔다.


HTML5의 기본적인 템플릿 구조는 이렇게 되어 있다.

스마트폰에서 접속하는 걸 고민하지 않게 하는 한줄이 viewport 기능이다.


익스플로러의 최대 단점은 버전별로 보여지는 웹의 모습이 많이 다르다는 것이다.
마이크로소프트사는 X-UA-Compatible 태그로 웹의 호환성을 지정할 수 있도록 하였다.
바로  X-UA-Compatible 태그다.
<meta http-equiv="X-UA-Compatible" content="IE=8" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
edge라고 기록된건 IE 브라우저의 최신 버전의 엔진을 사용하라는 뜻이다.
모든 버전에서 사용가능한 사이트라고 하면 edge만 추가해주면 된다.



<!DOCTYPE html> <!--HTML5 선언 -->
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/><!--화면 사이즈를 고려하지 않아도 적절하게 표시 -->
        <meta http-equiv="X-UA Compatible" control="IE=edge,chrome=1" /> <!--브라우저 호환성 설정 -->
        <title>HTML 서식</title>

        <!-- Include the jQuery library -->
        <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
    </head>
    <body>
        문서본문
    </body>
</html>


HTML5 에서는 <script type="text/javascript"를 넣지 않아도 된다.

Web은 HTTP(Hyper Text Transfer Protocol) 라는 프로토콜에 따라 서버와 클라이언트 간의 통신을 수행한다.
HTML은 태그를 통해 정보를 구조화한다.
태그란?
- 문서의 글자크기, 글자색, 글자모양, 그래픽, 링크 등을 정의하는 명령어
- 꺽쇠괄호 ‘<>’를 사용 <html>
- 일반적으로 태그는 시작과 끝을 표시하는 2개의 쌍 <table></table>
- 태그의 종류는 엄청 많다. 문법과 용도에 맞게 사용해야 한다.
Web browser마다 랜더링 방식이 서로 달라 HTML5가 나왔다.
- HTML에 화면 표시와 관련된 태그까지 추가하게 되자 간단하게 해결하고자
  CSS(Cascading Style Sheets)가 담당하게 했다.


화면에 정보를 보여주는 방법으로 가장 많이 사용하는 것이 테이블이다.

테이블은 table 태그를 사용한다.
테이블 선언 <table></table>
- 테이블 행은 <tr>을 사용
- 행을 셀로 분할하는 태그는 <td>를 사용, 제목 태그는 <th>를 사용
- 테이블의 너비, 테두리, Padding, 셀간격, 경계설정 등은 CSS를 사용
- CSS 는 head에 정의한다.
  <style>
    table,tr,td {border:1px solid #000000}
  </style>


테이블에서 셀간에 병합을 하는 것은 셀에 해당하는 <tr>, <th> 테그이다.

영어로 행에 해당하는 row, 열에 해당하는 column 을 사용하여

위아래 행을 병합하는 것은 <tr rowspan="합치고 싶은 만큼의 숫자">

같은 행에서 옆으로 병합하는 것은 <tr colspan="합치고 싶은 만큼의 숫자"> 로 하면 된다.


테이블에서 실제 정보가 표현되는 것은 <tr> 태그이다.

예제를 살펴보자.

DB와 연결하기 위해 DB 연결파일을 include 하고 나서, 테이블 태그를 작성하고 실제 내용을 채울 곳은 while 문으로 반복해서 <tr><td>내용</td></tr> 구조로 작성된 것을 알 수 있다.


<?php
include_once 'dbconnect.php'; // DB 연결

echo '<table border="1">';
// 제목
echo '<tr>';
echo '<th ALIGN=CENTER BGCOLOR="#9DEEE1">번호</td>';
echo '<th ALIGN=CENTER BGCOLOR="#9DEEE1">구분자</td>';
echo '<th ALIGN=CENTER BGCOLOR="#9DEEE1">연관번호</td>';
echo '</tr>';

$sql = "SELECT uid, classname, relateduid FROM category ORDER BY uid DESC";
$result = mysql_query($sql);
while ($row = mysql_fetch_array($result) ){ // DB에서 가져온 갯수만큼 테이블 행 생성
    // 내용
    echo '<tr>';
    echo '<td ALIGN=CENTER>'.$row['uid'].'</td>';
    echo '<td ALIGN=CENTER>'.$row['classname'].'</td>';
    echo '<td ALIGN=CENTER>'.$row['relateduid'].'</td>';
    echo '</tr>';
}
echo '</table>';

?>


Web 에서의 특수문자

공백

&nbsp;

>

&gt;

 <

&lt;


728x90
블로그 이미지

Link2Me

,
728x90
본 게시글은 처음 시작은 단순한 정리 개념이었는데, PHP 기본을 이해하는 핵심이라고 봐도 좋다.

네이버지식인 질문을 보면 PHP 개념은 모른 상태에서 Android 앱과의 통신을 어떻게 하는지 알고 싶은 Copy&Paste 초보자 도움 요청을 많이 본다. 가장 중요한 것은 기본 개념을 얼마나 잘 이해하고 있는가다.

스스로 로직을 그려보고 테스트하고 디버깅하면서 조금씩 성장할 수 있다.


모든 내용을 하나의 파일로 처리할 수는 없다. 파일을 모듈단위로 나누어서 처리하는게 좋다.

통신할 때 파일 처리에 대한 기본 개념을 알아야 한다.



(GET,POST 개념을 모른다면 일단 눌러서 읽어본다. http://link2me.tistory.com/398)


파일간에 정보를 넘겨주고 받기 위해서는 $_GET 배열, $_POST 배열을 사용하여 변수를 넘긴다.

$_GET 배열, $_POST 배열 구분없이 둘다 인식하도록 하려면 PHP 파일 B에서 $_REQUEST 배열을 사용한다.


아래 표에서 PHP 관련 게시글은 초보자 도움 링크를 추가한 것이니 꼭 읽어보시길 ^^


PHP

 - LoingForm.php (파일A)에서 loginID, loginPW 값을 POST 방식으로 보낸다.

 - PHP 파일B에서 읽어들일 때 $_POST 배열로 읽어서 처리한다.

 - 배열변수를 MySQL DB 변수로 보내서 결과를 return 받아서 로그인 성공과 실패를 판단한다.

 - 로그인 성공이면 main.php or list.php 와 같은 파일로 이동시킨다.

 - 로그인 실패면 LoginForm.php 파일로 다시 이동시킨다.

 MySQL DB 생성 및 사용자 권한 부여 방법 : http://link2me.tistory.com/431 참조

 MySQL 연동 기본 코딩 방법 : http://link2me.tistory.com/1213 참조

 PHP 디버깅 요령 및 경고 메시지 처리 방법 : https://link2me.tistory.com/1130 참조

 PHP 변수지정 기초 지식 : https://link2me.tistory.com/523 참조

 PHP isset 와 empty 설명 : https://link2me.tistory.com/996 참조

 닥치고 객체지향 MySQLi 연동방식 예제 : http://link2me.tistory.com/1395 참조

 제대로 배우는 자바스크립트 Form 전송 기초 : http://link2me.tistory.com/954 참조

 회원가입 및 중복체크 Form 예제 : https://link2me.tistory.com/1480 참조

 PHP Legacy 함수를 PDO Class 로 변경하는 방법 : https://link2me.tistory.com/1636 참조

 PHP PDO Class 및 부트스트랩 로그인 폼/처리 예제 : http://link2me.tistory.com/1402 참조

 Windows 기반 APM(Apaceh + PHP + MySQL) 설치 https://link2me.tistory.com/797  참조

 PHP 함수의 결과는 정수일까? String일까? https://link2me.tistory.com/1487 참조

 Android

PHP 와 Android 간 연동 처리 개념

 - Android 에서 로그인 정보를 조회한다고 하면 loginID, loginPW 값을 POST 방식으로 보낸다.

 - PHP 파일B에서 읽어들일 때 $_POST 배열로 읽어서 처리한다.

 - DB 조회 결과를 echo 문으로 출력한다.
 - echo 출력 결과를 읽어서 로그인 성공, 실패 여부를 판단 처리한다.

   가령, echo 1 이면, Android 에서는 1의 결과로 로그인 성공으로 다음 Activity 로 전환처리한다.

 - 많은 값을 결과로 돌려 받아야 하는 경우에는

   echo json_encode(배열) 로 받은 결과를 파싱하여 처리한다.

  Android JSON 파헤치기(필수사항) : https://link2me.tistory.com/1247  참조

  Android Volley 라이브러리 사용 예제 : https://link2me.tistory.com/1533 

  Android Retrofit2 라이브러리 사용 예제 : https://link2me.tistory.com/1806

  Android AsyncTask Class화하여 사용하는 방법 : https://link2me.tistory.com/1516 

  Android 암호화 로그인 처리 : https://link2me.tistory.com/1231

  Android - MySQL 연동 Web Part : http://link2me.tistory.com/1022

  Android Studio 기반 로그인 예제 : http://link2me.tistory.com/1230 

  Android Studio & PHP PDO 기반 회원가입 예제 : http://link2me.tistory.com/1405

  Android Studio & PHP PDO 기반 로그인 예제 : http://link2me.tistory.com/1404 

 C#

 - C# 에서 로그인 정보를 조회한다고 하면 loginID, loginPW 값을 POST 방식으로 보낸다.

 - PHP 파일B에서 읽어들일 때 $_POST 배열로 읽어서 처리한다.

 - DB 조회 결과를 echo 문으로 출력한다.

 - echo 출력 결과를 읽어서 C# 에서 로그인 성공, 실패 여부를 판단 처리한다.

  C# Web 접속 로그인 예제 : http://link2me.tistory.com/1118 

 

 윈도우즈 환경에서 리눅스 서버를 설치하고 Web서버를 운용할 수 있다.

 APM(Apache + PHP + MariaDB) 를 설치하는 방법을 자세히 알려주고 있다.

 - Window10 에 CentOS7(리눅스) 설치 및 APM 구성 : https://link2me.tistory.com/1885

 - 윈도우기반 APM 설치 툴 autoset9 : http://link2me.tistory.com/797

 - Apache 환경설정 방법 : http://link2me.tistory.com/426, http://link2me.tistory.com/999

 - PHP 환경 설정 방법 : http://link2me.tistory.com/967, http://link2me.tistory.com/986


Web(PHP 파일B) 에서 변수가 없거나 값이 넘어오지 않을 경우에는 동작하지 않도록 코딩을 해야 한다.


초보자들이 가장 많이 하는 실수(내가 초보시절에 겪은 실수)는 통신방식을 고려하지 않고 코딩을 하거나 서버 세팅정보를 대충 하는 것이다. 대화해보면 코딩 좀 하는 분도 이런걸 대략 하는 분이 있다. (한국어와 영어 소통하는 것과 같은 것)

UTF-8 로 세팅한다는 의미는 파일 Encoding Mode 도 UTF-8 로 하고, 서버에 세팅도 UTF-8 로 하고 DB에도 UTF-8 로 해야 한다는 것이다. (엑셀은 EUC-KR 방식으로 CSV 파일 내보내기를 한다)


참고사항


DB 연결정보는 로그인 처리 뿐만 아니라 거의 모든 PHP에서 사용한다고 볼 수 있다.

모든 PHP 파일에서 중복해서 DB 연결 정보를 코딩하는 것은 비효율적이므로, 파일을 분리하여 dbconnect.php 파일로 작성해두기만 하면 코딩이 심플해지고 가독성도 좋아진다.


dbconnection 을 하는 파일을 만드는 방법을 알아보자.


require문, include문 : 자주 사용하는 PHP 코드를 다른 파일에 모아 두고 필요할 때 파일을 읽어들여서 사용할 수 있도록 하는 명령


require "파일명";

- 만일 지정된 파일이 존재하지 않을 경우에는 php.ini에 설정된 include_path에서 파일을 찾는다.

- include_path에도 파일이 존재하지 않는다면 require문을 작성한 현재 파일이 있는 디렉토리에서 찾는다.

- 현재 디렉토리에도 없다면 오류표시하고 정지된다.


require와 include의 차이점

- require문은 오류가 발생했을 경우에 Fatal Error가 되어 정지가 되어버린다.

- require_once() : 지정한 파일을 한 번 읽어 들이면 같은 처리 중에는 다시 읽어 들일 수 없다.

- include문은 오류가 발생했을 경우에 Warning을 출력하고 처리코드를 수행한다.

- include_once() : 지정된 파일을 이미 읽어 들어 경우에는 다시 파일을 읽어 들이지 않는다.


PHP Connect to MySQL

- MySQL (procedural) : MySQL DB에서만 동작하며, , PHP 7.0 이상에선 동작안됨. PHP 5.5에서 deprecated

- MySQLi (procedural) : MySQL DB에서만 동작한다.

- MySQLi (object-oriented) : MySQL DB에서만 동작한다.
- PDO(PHP Data Objects) :
Oracle, PostgreSQL, SQLite, MySQL 등 12가지의 DB에서 동작한다.


Connecting to MySQL using the legacy MySQL functions

절차지향(procedural) 접속 함수는 MySQLi 방식과 MySQL 방식이 있다.

MySQL 설치방식에 따라 둘다 지원할 수도 있고 둘 중에 하나만 지원할 수도 있다.

MySQL 방식과 MySQLi 방식은 연결 방식이 약간 다르므로 코딩시 실수하지 않도록 주의가 필요하다.

======== dbinfo.php ======

<?php
$db['host'] = "localhost";
$db['name'] = "";
$db['user'] = "";
$db['pass'] = "";
$db['port'] = "3306";
?>


======= dbconnect.php ========

PHP 프로그램 내에서 MySQL 데이터베이스에 접근하는 함수가 mysqli_connect()이다.

$dbconn 라고 했지만 $db 또는 $conn 등 개발자 취향에 따라 변수명을 지정한다.

dbClass.php 에 만든 사용자 함수에 global $dbconn 연결 부분과 관련된 사항이다.


MySQLi 접속용과 MySQL 접속용 코드가 약간 다르다는 걸 확인하고 맞게 사용해야 한다.

MySQLi

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

function isConnectDb($db)
{
    $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
    mysqli_set_charset($conn, "utf8");  // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
    if (mysqli_connect_errno()) {
       echo "Failed to connect to MySQL: " . mysqli_connect_error();
       exit;
    } else {
      return $conn;   
    }
}
?>

 MySQL

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

function isConnectDb($db) {
    $conn = @mysql_connect($db['host'].':'.$db['port'],$db['user'],$db['pass']);
    //Set encoding
    mysql_query("SET CHARSET utf8");
    mysql_query("SET NAMES 'utf8' COLLATE 'utf8_general_ci'");
    if(!$conn){
        die('Not connected :' . mysql_error());
        exit;
    }
    $selc = mysql_select_db($db['name'],$conn); // 접근한 계정으로 사용할 수 있는 DB 선택
    // 연결 식별자($conn) 는 생략 가능하며, 생략시 가장 최근에 설정한 연결 식별자가 사용된다.
    return $selc ? $conn : false;
}
?>


아래 두 명령의 차이점에 대해 이해하자.

MySQL

 mysql_query ("select * from tableName"); // 최근에 열려진 db conection 을 자동으로 사용
 mysql_query ("select * from tableName", $dbconn); // db connection을 지정해서 사용

 MySQLi

 mysqli_query ($dbconn, "select * from tableName"); // db connection 지정만 허용


dbconnect.php 함수를 만들었으면 이제 실제 코드상에서 사용하는 방법을 알아보자.

LoginForm.php 인 A 파일에서 입력한 데이터를 받아서 처리하는 loginChk.php 인 파일 B의 일부 로직이다.

코딩을 하면서 고려할 점은 loginChk.php 파일을 단독으로 실행했을 때 파일이 실행되어서는 안되도록 체크하는 로직을 반드시 추가해야 한다. 즉, 파일 A에서 입력된 변수가 없으면 파일 B는 동작되지 않도록 해야 한다.

파일B에서 값이 제대로 넘어왔는지 체크하는 방법은 http://link2me.tistory.com/1130 를 참조하면 도움된다.


아래 코드는 실제로 사용하는 코드다.

안드로이드에서 받은 입력값과 웹 LoginForm.php 에서 전달받은 값을 구분하여 체크하는 로직이다.

안드로이드폰/아이폰은 고유한 ID값을 가지고 있는데 이 ID값이 넘어오면 안드로이드 로그인으로 구분하도록 if문을 사용했다.

대부분 세션 스타트를 session_start(); 로만 처리하는데

if(!isset($_SESSION)) {
    session_start();
}

로 해주어야 한다. 그래야 경고 메시지가 나오지 않는다.


=== loginChk.php ====

 <?php
if(!isset($_SESSION)) {
    session_start();
}

@extract($_POST); // POST 전송으로 전달받은 값 처리
// $_POST['loginID'] 라고 쓰지 않고, $loginID 라고 써도 인식되게 하려면 extract($_POST); 를 써주면 된다.

// 변수가 존재하거나 값이 있는 경우에만 데이터 업데이트를 하도록 하는 로직
// 이 파일명으로 직접 접속하면 변수 값이 넘어오지 않으므로 실행 불가되도록 처리
if(isset($loginID) && !empty($loginID) && isset($loginPW) && !empty($loginPW)) {

   require_once $_SERVER['DOCUMENT_ROOT'].'/dbconnect.php'; // db접속
   require_once $_SERVER['DOCUMENT_ROOT'].'/loginClass.php';
   $c=new LoginClass(); // 로그인 처리 관련 함수를 모아서 작성한 클래스

   $deviceID = $deviceID ? $deviceID : '';  // 안드로이드 접속인지 PHP Web 접속인지 판단

    if(empty($deviceID)){ // 장치 값이 없으면, 즉 Web 접속이면
        $row = $c->WebUserAuthCheck($loginID,$loginPW);
        if(is_array($row)) {
            if($row['code'] > 0) {
                $_SESSION['userID'] = $row['id'];
                $_SESSION['userPW'] = md5($loginPW);
                $_SESSION['code'] = $row['code'];
                $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
                $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
                $c->AccessLog($_SESSION['userID'],''); // 접속 로그 기록

                echo("<meta http-equiv='Refresh' content='0; URL=mobile/list.php'>");
            } else {
                echo '권한 불가';
            }
        } else if($row == '0'){
            $msg ='정보가 올바르지 않습니다';
            echo "<script>alert('".$msg."');history.go(-1);</script>";
        } else {
            $msg ='정보가 올바르지 않습니다';
            echo "<script>alert('".$msg."');history.go(-1);</script>";
        }

    } else {  // 안드로이드 접속이면
        $result = $c->MobileUserAuthCheck($loginID,$loginPW,$deviceID);
        if($result > 0 ) {
            session_save_path('./_tmp/session');

            $_SESSION['userID'] = $loginID;
            $_SESSION['userPW'] = md5($loginPW);
            $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
            $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
            $c->AccessLog($loginID,$deviceID); // 접속 로그 기록

            echo 1; // 로그인 성공 결과를 1로 처리
            // 안드로이드에서는 화면출력하는 결과값으로 로그인 성공/실패 판단 처리
        } else if($result == 0) {
            echo 0; // Login Fail

        } else {
            echo -1; // Phone Dismatch

        }
    }

} else {  // 이 부분은 Web 접속 변수가 없는 경우를 처리하는 부분
    // $_POST 전송값이 없이 바로 이 파일을 실행하면 로그인 창으로 돌려보내라.
    echo("<meta http-equiv='Refresh' content='0; URL=loginForm.php'>");
}
?>


loginChk.php 파일에서 보면 WebUserAuthCheck($loginID,$loginPW) 함수를 사용한 것을 볼 수 있다. 이 함수는 loginClass.php 파일에 작성되어 있는데, loginClass.php 함수중에서 Web 로그인 체크하는 부분만 발췌했다.

사용자가 해킹을 시도할 의도가 있어도 방지하는 코드를 추가된 것을 확인할 수 있다.


==== loginClass.php =====

 <?php
class LoginClass {
    function WebUserAuthCheck($u,$p) {
        if(!isset($u) || !isset($p) || empty($u) || empty($p)) {
            return 0;
        } else {
            global $dbconn; // global 키워드를 사용하여 변수에 선언할 경우 함수 밖의 변수를 참조할 수 있다
            $u = preg_replace("/[\s\t\'\;\"\=\--]+/","", $u); // 공백이나 탭 제거(사용자 실수 방지)
            $p = preg_replace("/[\s\t\'\;\"\=\--]+/","", $p); // 공백이나 탭 제거, 특수문자 제거
            // SQL injection 검사
            $u = mysqli_real_escape_string($dbconn,$u); // <script>documnet.cookie();</script> 공격 방지
            $p =
mysqli_real_escape_string($dbconn,$p); //
            if(preg_match('/[\/\\\\]/', $u)) $this->popup('비정상적 접근입니다'); // no slashes
            //if(preg_match('/(and|null|where|limit)/i', $u)) { // i는 대소문자 구별하지 말라
            //    $this->popup('비정상적 접근입니다');
            //} // 회원아이디가 일치하는 경우가 생겨서 이 구문은 사용을 자제해야 할 듯해서 주석처리함.
            if(!preg_match('/^[0-9a-zA-Z\~\!\@\#\$\%\^\&\*\(\)]{7,}$/',$p)) { // 최소7자리 이상 허용 문자만 통과
                $this->popup('정보가 올바르지 않습니다');
                // 같은 클래스안에 있는 다른 함수를 사용할 때 $this 로 기술
            }

            $sql = "select code, id ";
            $sql.= "from member where pw=md5('".$p."') and id= '".$u."' ";
            if($result = mysql_query($sql,$dbconn)) { //성공
                $row = mysql_fetch_array($result);
                if($row == NULL) return 0;
                return $row;
            } else {
                return '-1';
            }
        }
    }

    // 로그인함수 경고메시지
    function popup($msg) {
        echo "<script>alert('".$msg."');history.go(-1);</script>";
    }
} // End of LoginClass
?>


위 SQL 인젝션 방지 코드가 완벽하지 않을 수도 있다.

정규식 예제 : https://link2me.tistory.com/1489 를 참조하면 조금 더 도움될 것으로 본다.

그리고 구글링으로 PHP 정규식 을 검색해서 코드를 완벽하게 보완하면 좋을 것이다.



첨부파일 (db 연결함수)

dbconn.ZIP


본 첨부파일은 초보자 분들이 타이핑 하면서 실수하지 않게 제공하는 것입니다.

블로그내 다른 소스를 복사해서 활용하고 싶다면 c browser 를 검색해서 설치하고 사용하면 됩니다.

도움되셨다면 댓글이나 공감 부탁드립니다.



추가 작성 : 2016-11-23일자 (네이버지식인 답변시 추가)

DB를 연결하는 방법만 언급하다보니 함수화하는 것과 연결고리를 잘 모르는 분을 위해서 적어본다.

통계처리용으로 만는 함수다. 함수화를 해두면 나중에 추가할 사항이 있으면 함수만 추가하면 된다.

MySQLi 를 사용하는 환경이면 사용자 함수도 MySQLi 기반으로 수정해주면 된다.


=== statClass.php ===


이런 함수를 호출해서 사용하는 방법은 간단하다.

<?php

require_once "statsClass.php";
$s=new StatsClass(); // 객체 생성(인스턴스화)
$row = $s->today_statsCount(); // 배열이므로 배열로 받아서

?>

<?php echo '개인별(오늘/어제) : '.$row[0].'/'.$yes[0];?></th>


도움이 되셨다면 00 해 주세요. 좋은 글 작성에 큰 힘이 됩니다.


728x90
블로그 이미지

Link2Me

,
728x90

내 Web Site에 로그인한 접속자의 통계를 구해보자.


접속자의 정보를 테이블에 기록하는 로직을 그려본다.

1. 로그인한 ID 정보를 기록한다.

2. 접속자의 IP 주소 정보를 구한다.

   $ip = $_SERVER[REMOTE_ADDR];    //방문자 IP저장

3. 오늘 날짜와 시간을 저장한다.

4. OS 정보를 추출하여 저장한다.

    - Web 브라우저에서 접속한 경우에는 $_SERVER['HTTP_USER_AGENT'] 에서 정보를 추출할 수 있다.

    - Android App, iOS App 에서 접속하는 경우에는 DeviceID를 가지고 식별하게 코드를 추가하면 된다.

5. 동일한 ID에 대해 하루에 한번만 저장할 것인가?

    접속할 때마다 기록할 것인가를 정한다.

    접속할 때마다 기록하면 테이블에 저장되는 Record 수가 많아진다.

    이 경우에는 매일 일정한 시간주기로 Raw Data 를 가지고 일단위 통계, 주간단위 통계, 월단위 통계를 만들고

    2개월이 넘어간 데이터는 삭제하는 방법등을 고려하는 것이 좋다.

    데이터 저장이 너무 많아지는 경우에는 테이블 설계시 partition 까지 고려하는 것이 좋을 수 있다.

6. Index 는 어떤 걸 자주 검색하게 될 것인가를 고려해서 설정한다.

7. 통계 생성을 고려한다면 테이블에 저장되는 값을 숫자로 표시되게 하는 걸 같이 고려한다.

8. 접속자의 접속정보를 테이블에 추가(Insert)한다.



1. 테이블 구조

CREATE TABLE IF NOT EXISTS AccessLog (
  uid int(11) NOT NULL AUTO_INCREMENT,
  ipaddr varchar(20) NOT NULL,
  date char(8) NOT NULL DEFAULT '',
  time varchar(10) NOT NULL,
  OS varchar(60) DEFAULT NULL,
  browser varchar(30) DEFAULT NULL,
  userID varchar(60) DEFAULT NULL,
  hit int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (uid),
  KEY ipaddr (ipaddr)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


2. phpClass 함수

관련 모든 함수는 첨부파일 참조

AccessClass.zip


조건기준 : IP주소와 오늘날짜를 기준으로 카운트

만약 userID 기준으로 접속기록을 남기려면 where 조건문을 수정해주면 된다.


function AccessLog($userID){
    // 테이블 구조 : uid, ipaddr, date, time, OS, browser, userID, hit
    global $db;
    $access_ip=$_SERVER['REMOTE_ADDR'];
    $getOS = $this->getOS(); // 접속 OS 정보
    $getBrowser = $this->getBrowser(); // 브라우저 접속 정보
    $date = date("Ymd"); // 오늘날짜
    $time = date("H:i:s"); // 시간
    $userID = $userID ? $userID : '';
    $sql ="select count(*) from rb_accessLog where ipaddr='".$access_ip."' and date='".$date."'";
    $result=mysql_query($sql);
    if($row=mysql_fetch_row($result)){
        if($row[0] == 0){ // 오늘 접속날짜 기록이 없으면
            $sql = "INSERT INTO rb_accessLog (ipaddr,date,time,OS,browser,userID,hit) ";
            $sql.= "VALUES ('$access_ip','$date','$time','$getOS','$getBrowser','$userID','1');";
            $result=mysql_query($sql);
        } else { // 접속 기록이 있으면 해당 IP주소의 카운트만 증가시켜라.
            $sql = "UPDATE rb_accessLog SET hit=hit+1 Where ipaddr='".$access_ip."'";
            $result=mysql_query($sql);
        }
    }
}


3. 사용법

로그인 정보를 검사하여 로그인이 성공(ID, PW 일치)하면 1, 실패하면 0 을 반환한다.

성공할 경우 SESSION 을 생성하고 접속 로그를 기록한다.


<?php
if(isset($_POST['loginID']) && !empty($_POST['loginID']) && isset($_POST['loginPW']) && !empty($_POST['loginPW'])) {
    $loginID = trim($_POST['loginID']);
    $loginPW = trim($_POST['loginPW']);

    require_once 'dbconnect.php'; // db접속
    require_once 'phpclass/AccessClass.php';
    require_once 'phpclass/loginClass.php';

    $a=new AccessClass();
    $c=new LoginClass();

    $row = $c->UserAuthCheck($loginID,$loginPW); // 성공(1), 실패(0)
    if($row == 1) {
        $_SESSION['userID'] = $row['id'];
        $_SESSION['userPW'] = md5($loginPW);
        $a->AccessLog($_SESSION['userID']); // 접속 로그 기록
    } else {
        $msg ='정보가 올바르지 않습니다';
        echo "<script>alert('".$msg."');history.go(-1);</script>";
    }
}

?>


728x90
블로그 이미지

Link2Me

,
728x90

<th>
<select id="searchName" name="searchName">
    <option value=''>구분</option>
    <option value="userNM">성명</option>
    <option value="userID">아이디</option>
</select>
<input type="text" id="searchValue" name="searchValue">
<span class="spanBtn" id="SearchBtn">검 색</span>           
</th>


위와 같은 option 값을 변경하는 jQuery

// 지정된 index 값으로 select 하기

$('#searchName option:eq(2)').prop('selected', true); // option 3번째 선택

또는 value값을 직접 적어주는 방법도 가능하다.

$('#searchName option[value=userID]').prop('selected', 'selected').change();


$('#searchValue').val(ID); // searchValue 값을 ID로 자동 입력


// 초기화 처리

$('#searchName option:eq(0)').attr('selected','selected');
$('#searchValue').val('');


// 선택된 값 읽기

$("select[name=searchName]").val();

$("#searchName option:selected").val();


// 선택된 내용(text) 읽어오기

$("#searchName option:selected").text(); // 선택된 text 읽어오기

$("select[name=searchName]").text(); // 이건 원하는 결과가 나오지 않더라.



var ID=$(this).children('.gc').eq(6).text(); // 테이블에서 선택 행의 td 태그의 값을 가져오라

<td class="gc">행복</td>


// 선택된 위치 index

var index = $("#searchName option").index($("#searchName option:selected"));


728x90
블로그 이미지

Link2Me

,
728x90

AES 암호화 방식으로 암호화하는 예제이다.


1. AESCrypt Class 를 선언해준다. Class 명칭은 본인 스타일에 맞게 수정한다.


class AESCrypt {
    function encrypt($plain_text){
        global $key, $iv;
        // AES 암호화 기법은 평문을 128비트 단위로 나누어 암호화, 복호화를 수행
        // 각각의 128비트를 4x4 행렬로 표현하여 연산을 수행함
        // 사용하는 암호화 키의 길이는 128, 192, 256비트 중 하나를 선택할 수 있음
        // CBC(Cipher Block Chaining) Mode : 블록 암호화 운영 모드 중 보안 성이 제일 높은 암호화 방법으로 가장 많이 사용
        // 평문의 각 블록은 XOR연산을 통해 이전 암호문과 연산되고 첫번째 암호문에 대해서는 IV(Initial Vector)가 암호문 대신 사용
        // IV (initialization vector)는 제 2의 키가 될 수 있으며, 길이는 16bits
        $encryptedMessage = openssl_encrypt($plain_text, "aes-256-cbc", $key, true, $iv);
        return base64_encode($encryptedMessage);
    }

    function decrypt($base64_text){
        global $key, $iv;
        $decryptedMessage = openssl_decrypt(base64_decode($base64_text), "aes-256-cbc", $key, true, $iv);
        return $decryptedMessage;
    }

}//end class



2. AES 사용법

class.crypto.php 파일안에다 AESCrypt Class 를 선언했다.


<?php
    include 'phpclass/class.crypto.php';
    $crypto = new Crypto(); // 암호화 객체 생성
    $text ='암호화할 문장을 여기에 적어주시면 됩니다.';
    $enc = $crypto->encrypt($text);
    echo $enc.'<br /> 원문 길이 : '.strlen($text).'  암호화 길이 : '.strlen($enc).'<br />';
    $dec = $crypto->decrypt($enc);
    echo $dec.'<br /><br />';

    $aescrypt = new AESCrypt();
    $key = "key996rfy7ja6ng7gisu8kim55s";
    $iv = "f781576ae8dfe846";
    $enc = $aescrypt->encrypt($text);
    echo $enc.'<br /> 원문 길이 : '.strlen($text).'  암호화 길이 : '.strlen($enc).'<br />';
    $dec = $aescrypt->decrypt($enc);
    echo $dec;
   
?>


3. 결과

최대 길이는 SEED 와 AES 모두 입력되는 길이에 비례하여 길어진다.


SEED 결과

48cdef6c9738608cbbfb30965e1c665904a214ef99a0dc20ebf6cf6592de45aaf9eeb48106628db5b3747122251d16145cd9a0f17afc6857623891ff4882864e
원문 길이 : 59 암호화 길이 : 128
암호화할 문장을 여기에 적어주시면 됩니다.


AES 결과
lcFPY8OXrCHTUFNaz7PeQ+xzYMsxxw2SDFD1aR8nb3QNa7z6ELfxuhNjIVg/j/5tyWN13StkGwZoL0TARI9vwA==
원문 길이 : 59 암호화 길이 : 88
암호화할 문장을 여기에 적어주시면 됩니다.


4. 비고

테스트를 해보니 AES 암호화는 key 길이가 32비트까지 인식 하는 거 같다.





728x90
블로그 이미지

Link2Me

,
728x90

SEED PHP 파일은 http://seed.kisa.or.kr/iwt/ko/index.do 에 가면 있다.

KISA 사이트는 소스가 PHP 인지 JSP 인지 식별이 어렵게 do 라는 확장자를 사용했다는 걸 알 수 있다.


알림마당 -> 자료실에서 PHP 버전을 찾아서 다운로드 받을 수 있다.

http://seed.kisa.or.kr/iwt/ko/bbs/EgovReferenceList.do?bbsId=BBSMSTR_000000000002


   [암호화 및 복호화 과정]


SEED는 128비트의 암⋅복호화키를 이용하여 임의의 길이를 갖는 입력 메시지를 128비트의 블록단위로 처리하는 128비트 블록암호 알고리즘이다. 따라서 임의의 길이를 가지는 평문 메시지를 128비트씩 블록단위로 나누어 암호화하여 암호문을 생성한다.


예제를 실행해보니 복호화가 제대로 안된다.

이걸 일일이 분석해볼 시간도 없고 능력도 안되어서 바로 포기했다.


그리고 구글링해서 다른 걸 찾아냈다.

http://qnibus.com/blog/how-to-use-seed128-for-php/

https://github.com/qnibus/seed128/


class.seed.php 파일을 만든 분의 사이트에 가서 class.seed.php 파일 내용을 수정하라고 나온다.


필요한 파일 class.crypto.php, class.seed.php 2개를 폴더에 복사 및 수정해서 소스 디렉토리에 저장한다.


사용법

ㅇ class.crypto.php 파일 내에 있는 $pbUserKey 값은 수정해서 사용한다.

ㅇ $IV 값도 수정해서 사용한다.


<?php
    include 'phpclass/class.crypto.php';
    $crypto = new Crypto(); // 암호화 객체 생성
    $text ='암호화할 문장을 여기에 적어주시면 됩니다.';
    $enc = $crypto->encrypt($text);
    echo $enc.'<br /> 원문 길이 : '.strlen($text).'  암호화 길이 : '.strlen($enc).'<br />';
    $dec = $crypto->decrypt($enc);
    echo $dec;
?>


평문($text)의 길이가 길면 비례해서 암호문의 길이도 길어진다.

그러므로 DB column 설계시 고려해서 해야 한다.

개인정보인 전화번호를 암호화 저장하는게 안전할 거 같아서 테스트를 해보니 암호화시 32 bytes 가 된다.

DB에 암호화 저장하고 화면에 보여줄 때 복호화해서 보여주면 된다.


Android 와 연동해서 테스트는 아직 안해봤다.


728x90
블로그 이미지

Link2Me

,
728x90

jQuery dialog (jQuery 팝업)창을 띄우는 코드를 사용하려면 https://jqueryui.com/dialog/ 사이트를 참조하면 된다.


$("#id").dialog() - 해당 id로 지정된 영역의 내용을 다이얼로그로 생성
$("#id").dialog("open")  - 다이얼로그 오픈
$("#id").dialog("close") - 다이얼로그를 화면상에서 보이지 않게함
$("#id").dialog("isOpen") - 해당 id를 가진 다이얼로그가 열려진 상태인 경우 true값
$("#id").dialog("moveToTop") - 다이얼로그를 최상위로 배치


- autoOpen : 페이지 로드시 자동으로 다이얼로그 열리는 것을 막고자 할 때 사용 ex) autoOpen:false

- modal : 페이지의 다른 내용위에 최상위로 표시되면서 다른 영역은 선택할수 없도록 할 때 사용 ex) modal:true

- width, height : 다이얼로그 창의 너비, 높이를 설정



아래 파일 구조는 http://link2me.tistory.com/1098 게시글과 같이 보면 이해가 될 것이다.

기본적인 Layout 에서 팝업창으로 새 그룹을 생성하고 바로 화면을 갱신처리하는 일련의 코드이다.


    <div data-role="footer" data-position="fixed" data-theme="d">
        <table>
        <tr>
        <td width="15%"></td>
        <td width="70%" align=center>그룹 추가</td>
        <td width="15%" align=right><a href="#" data-role="button" data-icon="plus" data-iconpos="left" id="groupadd">추가</a></td>
        </tr>
        </table>       
    </div>
</div>
<div id="dialog-add" title="그룹 추가">
    <form>
        <p>새 그룹명</p>
        <input type="text" name="group" />
        <input type="hidden" name="uid" value='<?php echo $uid?>' />
        <input type="hidden" name="status" value='n' />
    </form>
</div>


추가 버튼(id="groupadd")을 누르면 dialog 창이 떠서 새 그룹명을 입력 받고 처리하도록 하고 싶다면??


먼저 jQuery 를 추가해야 한다.

https://jqueryui.com/dialog/ 사이트를 참조해서 아래 색깔로 표시된 것을 복사하여 붙여넣기 한다.


<link href="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="js/group_dialog.js"></script> <!-- 작성할 jQuery 스크립트 -->


===== group_dialog.js ======

$(function(){
    $("#dialog-add").dialog({
        autoOpen: false,
        buttons:[
            {
                text: "추가",
                click: function(){
                    var uid = $("input[name='uid']").val();
                    var group = $("input[name='group']").val();                   
                    if(!group) { // 입력 여부 검사
                        alert("그룹명을 입력하세요");
                        $("input[name='group']").focus();
                        return false;
                    }
                    var status = $("input[name='status']").val();
                    GroupAdd(uid,group,status);
                }
            },
            {
                text: "취소",
                click: function(){
                    $(this).dialog("close");
                }
            }
        ]
    });

    $("#groupadd").click(function(){       
        $("#dialog-add").dialog("open");
    });

});

function GroupAdd(uid,group,status){
    $.post("GroupMake.php", {uid:uid, group:group,status:status}, function(msg){
        if(msg==1){
            location.reload(); // 화면 갱신처리 (같은 화면을 갱신처리하는 것임)          
        } else if(msg==0){
            alert('그룹 추가에 실패했습니다');
        } else if(msg=='-1'){
            alert('그룹이 이미 존재합니다');
        } else if(msg=='-2'){
            alert('DB 에러가 발생했습니다');
        }
        $("#dialog-add").dialog("close");
    });
}



======= GroupMake.php =====

바로 접속하는 걸 방지하는 코드는 생략했다. 간편하게 내용 중심으로 작성해두기 위해서....

<?php
include ('dbconnect.php');
require_once "class/GroupClass.php";
$c=new GroupClass;

if(strcmp($_REQUEST['status'],'n')==0){//추가   
    $rs=$c->NewGroup($_REQUEST['uid'],$group); // 즐겨찾기 그룹명 추가
    // rs : 1(성공), 0(실패), -1(중복), -2(DB error)
} else if (strcmp($_REQUEST['status'],'d')==0){//삭제
    $rs=$c->DelGroup($_REQUEST['uid'],$group);
    // rs : 1(성공), 0(실패)
}
echo $rs;  // jQuery 의
function(msg) 로 던져지는 결과값
?>


개념을 설명하는 자료이므로 class/GroupClass.php 파일 내용은 적지 않는다.

이 정도면 봐도 충분히 응용이 가능하다고 본다.


참고하면 도움되는 게시글 링크

https://www.oreilly.com/library/view/jquery-ui/9781449325176/ch04s05.html



728x90
블로그 이미지

Link2Me

,
728x90

mysql_num_rows() 함수는 데이터베이스에서 쿼리를 날려서 나온 레코드들의 갯수를 카운트할 때 사용한다.
값이 없으면 0, 값이 있으면 레코드 갯수를 반환한다.

function MemberInfoView($id,$pw){
    global $db;
    $query="select * from member ";
    $query.="where userID='".$id."' and passwd=md5('".$pw."')";
   
    if($result=mysql_query($query)){
        $cnt=mysql_num_rows($result); // 개수 반환
        if($cnt==1) {
            $row=mysql_fetch_array($result);
            return $row;
        } else {
            return $cnt;
        }
       
    } else {
        return 0;
    }
}


mysql_num_fields($result); // 칼럼의 개수를 구할 때 사용

728x90
블로그 이미지

Link2Me

,
728x90

로그인 체크함수다.

Web에서 로그인하는 경우와 안드로이드에서 로그인하는 경우 2가지를 모두 수용할 수 있게 작성했다.

안드로이드폰 직접 접속시에는 폰의 장치정보를 인식하므로 deviceID 정보를 식별자로 사용하여 Web 접속과 Mobile 접속을 구분 처리했다.


<?php
if(!isset($_SESSION)) {
    session_start();
}
if(isset($_POST['loginID']) && !empty($_POST['loginID']) && isset($_POST['loginPW']) && !empty($_POST['loginPW'])) {
    $loginID = trim($_POST['loginID']);
    $loginPW = trim($_POST['loginPW']);
    $deviceID = trim($_POST['deviceID']);

    $deviceID = $deviceID ? $deviceID : '';

    if(empty($deviceID)){
        require_once 'dbconnect.php'; // db접속
        require_once 'phpclass/loginClass.php';

        $c=new LoginClass();

        $row = $c->WebUserAuthCheck($loginID,$loginPW);
        if(is_array($row)) {
            if($row['code'] > 0) {
                $_SESSION['userID'] = $row['id'];
                $_SESSION['userPW'] = md5($loginPW);
                $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
                $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];

                echo("<meta http-equiv='Refresh' content='0; URL=list.php'>");
            } else {
                echo '권한 불가';
            }
        } else if($row == '0'){
            $msg ='정보가 올바르지 않습니다';
            echo "<script>alert('".$msg."');history.go(-1);</script>";
        } else {
            $msg ='정보가 올바르지 않습니다';
            echo "<script>alert('".$msg."');history.go(-1);</script>";
        }

    } else {
        require_once 'db.info.php';
        require_once 'phpclass/dbClass.php';
        $conn=new MySQLDbClass();
        $DB_CONNECT = $conn->isConnectDb($DB); // 안드로이드폰에서는 반드시 객체로 생성해야 정상접속
        require_once 'phpclass/loginClass.php';
        $c=new LoginClass();

        $result = $c->MobileUserAuthCheck($loginID,$loginPW,$deviceID);
        if($result > 0 ) {
            session_save_path('./_tmp/session');

            $_SESSION['userID'] = $loginID;
            $_SESSION['userPW'] = md5($loginPW);
            $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
            $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
            echo 'Login Success';
        } else if($result == 0) {
            echo 'Login Fail';
        } else {
            echo 'Phone Dismatch';
        }
    }
   
} else {
    echo("<meta http-equiv='Refresh' content='0; URL=loginForm.php'>");
}
?>


728x90
블로그 이미지

Link2Me

,
728x90

배열이 아닌 것을 배열 변수로 만들어서 검색결과를 알고 싶은 경우가 있다.

검색조건(search_item) 은 이름, 전화번호, 부서명 등으로 지정하고

검색어를 넣어서 배열에서 검색결과 처리를 하고자 할 경우 로직의 예이다.


첫번째 행의 변수는 $search[0] 로 할당하고, 두번째 행

$search_keyword = isset($_REQUEST['searchValue']) ? urldecode($_REQUEST['searchValue']) : '';


$search[1] = isset($_REQUEST['searchValue']) ? urldecode($_REQUEST['searchValue']) : '';

로 하면 배열 선언이 없는 상태라서 에러가 발생한다.


이렬 경우 보통은

php.ini 에서

error_reporting = E_ALL & ~E_DEPRECATED & ~E_NOTICE

로 처리하라고 되어 있다.

개발하면서 이 옵션은

error_reporting = E_ALL

로 설정해서 개발을 해야 에러가 발생하는 부분을 전부 화면에 표시를 해주므로 로직상의 문제점을 제거하면서 개발을 할 수 있다.


$search_item = isset($_REQUEST['searchName']) ? $_REQUEST['searchName'] : '';
$search_keyword = isset($_REQUEST['searchValue']) ? urldecode($_REQUEST['searchValue']) : '';

if(strlen($search_keyword) == 0){  // 입력값이 없으면 목록 없음 출력
    echo '<li data-icon="false">';
    echo '<a href="#">목록이 없습니다</a>';
    echo '</li>';
} else {
    // 검색어 값이 존재하면 배열 변수로 저장

    $search = array($search_item,$search_keyword);


    $c=new MemberClass;
    $rs=$c->MemberSearch($search);

    if($rs==0){    // 검색결과가 없으면
        echo '<li data-icon="false">';
        echo '<a href="#">목록이 없습니다.</a>';
        echo '</li>';

    } else { // 검색결과가 있으면
        // 검색결과 처리
    }

}


728x90
블로그 이미지

Link2Me

,