728x90

체크박스(checkbox) 전체를 선택/해제하는 jQuery 예제다.

checkall input 박스 변화를 감지하여

$("#check_item").children("input").attr("checked", true); 하거나

$("#check_item").children("input").attr("checked", false); 한다.


attr() 사용시
- HTML attribute 값이 모두 String 으로 넘어옴

prop() 사용시
- Javascript의 프로퍼티 값이 넘어오기 때문에 boolean, date, function 등도 가져올 수 있음


<input type="checkbox" name="" id="chk" checked="checked">

var $checkbox = $('#chk');
console.log($checkbox.attr('checked')); // checked속성의 값을 표시 → "checked"
console.log($checkbox.prop('checked')); // checked프로파티값을 표시 → true or false

체크가 되어있는지 판단을 할경우 .prop()을 사용할 필요가 있다.


체크박스를 전체 선택하고 해제하는 스크립트를 쓰려면 .prop()로 true/false 를 제어하자.

For jQuery 1.6+ :
.attr() is deprecated for properties; use the new .prop() function instead as

$('#myCheckbox').prop('checked', true); // Checks it
$('#myCheckbox').prop('checked', false); // Unchecks it


For jQuery < 1.6:
To check/uncheck a checkbox, use the attribute checked and alter that.
With jQuery you can do:
$('#myCheckbox').attr('checked', true); // Checks it
$('#myCheckbox').attr('checked', false); // Unchecks it


<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>form ex</title>
<style>
body {
    margin: 20px;
    font-family: "맑은 고딕";
}
input[type=checkbox] {
    border: 1px solid #ccc;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(function(){
    $("#checkall").change(function(){
        $("input:checkbox").prop('checked', $(this).prop("checked"));
        /*
        if(this.checked){
            $("#check_item").children("input").attr("checked", true); // checked="true"
        }else{
            $("#check_item").children("input").attr("checked", false); // checked="false"
        }
        */
    });
    $("input[name=chkname]").click(function(){ // 개별 선택을 누르면 전체 선택 체크가 풀리게 처리
        if($("#checkall").is(":checked")==true){
            $("input:checkbox[name=checkall]").prop("checked",false);
        }
       console.log($(this).val());
    });

});
</script>
</head>

<body>
<input type="checkbox"  name="checkall" id="checkall" />
<label>All</label>
<div id="check_item">
    <input type="checkbox" name="chkname" value="1" />
    <label>홍길동</label>
    <input type="checkbox" name="chkname" value="2" />
    <label>강감찬</label>
    <input type="checkbox" name="chkname" value="3" />
    <label>이순신</label>
</div>
</body>
</html>

728x90

'Web 프로그램 > js, jQuery' 카테고리의 다른 글

jQuery animate  (2) 2018.02.14
jQuery addClass()  (0) 2018.02.04
[jQuery] 테이블 다루기 (find)  (0) 2018.01.15
JavaScript 와 jQuery 비교 정리  (0) 2018.01.15
[Javascript] Form 객체 다루기 (자바스크립트 와 jQuery)  (0) 2017.12.16
블로그 이미지

Link2Me

,
728x90

HTML(Hyper Text Markup Language)은 태그로 구성되어 있다.
- HTML 태그는 기본적으로 '<' 기호로 시작하여 '>'로 끝난다.
- 브라우저에서 HTML을 파싱할 때는 꺽쇠기호 <> 를 HTML 태그의 시작과 끝으로 인식 할 수 있다
- 따옴표 " "는 HTML 속성의 값이 시작되거나 끝난 것으로 인식할 수 있다.
- & 기호는 Entity 기호의 시작으로 오인될 수 있다.

HTML 에서 special character 를 나타내는 방법은 두가지가 있다.
Numeric HTML entity, Symbolic HTML entity 다.
http://entitycode.com/ 에 가면 주요 특수문자을 찾을 수 있다.
http://dmobi.tistory.com/72 에도 많은 특수문자를 참조할 수 있다.

"<", ">", "&" 이런 기호들을 Web 브라우저에서 제대로 표현하려면 &lt; &gt; &amp; 로 변환해주어야 한다.

PHP에서는
htmlspecialchars() HTML에서 사용하는 특수문자를 변경해주는 역할을 한다.


stripslashes()는 따옴표에 붙는 백슬래시를 제거해주는 함수다.
따옴표에 백슬래시를 붙여주는 함수는 addslashes() 다.
HTML 코드를 DB에 저장할 때 따옴표나 여러 특수문자 때문에 에러를 발생하기도 한다
DB에 저장할 때는 특수문자를 적절하게 변경해주어야 한다.


PHP에서 HTML 구문을 그대로 출력하기 위해 htmlspecialchars를 사용하여 인코딩된 문자열을  DB에 넣어준다.
반대로 DB에 있는 값을 불러와 HTML 소스로 변환할 때는 일반적으로 htmlspecialchars_decode를 많이 사용한다.
그런데 이 함수를 사용하는데 문제가 있다. htmlspecialchars_decode는 &nbsp;를 공백으로 다시 되돌려 주지 않는다.


&nbsp;와 같은 것은 html_entity_decode를 사용하면 된다.
이 함수는 htmlentities와 대응되는 함수이다.

htmlspecialchars와 htmlentities는 유사하나 htmlentities가 더 많은 문자를 변환한다.
htmlspecialchars_decode와 html_entity_decode는 유사하나 html_entity_decode가 더 많은 문자를 되돌린다.


$str = "I'll \"walk\" the <b>dog</b> now.\n";

$str_encode = htmlentities($str);
echo $str_encode; // I'll &quot;walk&quot; the &lt;b&gt;dog&lt;/b&gt; now.

$str_decode = html_entity_decode($str_encode);
echo $str_decode; // I'll "walk" the <b>dog</b> now.


$str = "<a href=\"https://www.w3schools.com\">w3schools.com</a>\n";
$str_encode = htmlentities($str);
echo $str_encode; // &lt;a href=&quot;https://www.w3schools.com&quot;&gt;w3schools.com&lt;/a&gt;

$str_decode = html_entity_decode($str_encode);
echo $str_decode; //  



PHP 버전이 낮은 경우에는 get_magic_quotes_gpc()함수로 magic_quotes 값을 확인 후 써야 한다.
그렇지 않으면 불필요한 백슬래시까지 데이터로 처리하는 경우가 발생하기 때문이다.
Returns 0 if magic_quotes_gpc is off, 1 otherwise.
PHP 5.4.0. always returns FALSE because the magic quotes feature was removed from PHP.

if ( get_magic_quotes_gpc() ) {
    $str = htmlspecialchars( stripslashes( $str) ) ;
}else{
    $str = htmlspecialchars( $str) ;
}

// Usage across all PHP versions
if (get_magic_quotes_gpc()) {
    $lastname = stripslashes($_POST['name']);
}
else {
    $lastname = $_POST['name'];
}

문자 치환이 필요한 경우
- DB와 연동하기 위해 특수문자 또는 줄바꿈 처리시
- 게시판에서 특정 문자의 제거 또는 치환으로 특수문자와 줄바꿈 변경에 자주 사용

728x90
블로그 이미지

Link2Me

,
728x90

아래 예제는 생활코딩에서 설명하는 강좌 내용을 적는다.

<html>

<head>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>

</head>

<body>

<ul>

    <li class="marked">html</li>

    <li>css</li>

    <li id="active">JavaScript

        <ul>

            <li>JavaScript Core</li>

            <li class="marked">DOM</li>

            <li class="marked">BOM</li>

        </ul>

    </li>

</ul>

<script>

    //$( ".marked", "#active").css( "background-color", "red" );

    //$("#active .marked").css( "background-color", "red" );

    $("#active").find('.marked').css('background-color','blue');

</script>

</body>    

</html>


$(".marked", "#active").css( "background-color", "red" );

$("#active .marked").css( "background-color", "red" );

$("#active").find('.marked').css('background-color','red');

는 모두 동일한 결과를 출력한다.


find는 jQuery 객체 내에서 어떤 요소(기준요소)의 하위 요소 중 특정 요소를 찾을 때 사용한다.



CSS 테이블에 대한 사항은 http://link2me.tistory.com/1463 를 참조하라.


테이블을 jQuery 로 다루는 걸 적어둔다.

children()은 부모 요소의 바로 아래 단계인 자식요소만 선택할 수 있으나, 
find()는 부모 태그의 모든 하위 요소의 자식을 선택하여 가져올 수 있다.


$.each() 메서드는 object 와 배열 모두에서 사용할 수 있는 일반적인 반복 함수다.

첫번째 매개변수로 배열이나 객체를 받는다.

두번째 매개변수로 콜백함수를 받으며, 콜백함수의 인자로는 인덱스와 값을 인자로 갖는다.

$.each(arr, function (index, item) {
});


$('.list li').each(function (index, item) {
});



$("table tbody tr").click(function(){

     var tr = $(this); // 현재 클릭된 row

     var td = tr.children();

     // 반복문을 사용해서 배열에 값을 담아보기

     var tdArray = new Array(); // 배열선언

     td.each(function(i){

         tdArray.push(td.eq(i).text());

     });

     console.log("배열에 담은 값 : " + tdArray);

     // 특정 셀 값 알아내기

     var customerId = $(this).find("td").eq(2).html();

});


$("table tbody tr td").click(function(){

    var td = $(this); // 현재 클릭된 셀

    var val = td.text(); // 현재 선택된 셀의 값

    var id=$(this).attr("id"); // 셀에 지정된 속성값

});


<!DOCTYPE html>
<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" />
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script>
$(function(){
    // tr 태그에 마우스를 올릴때
    $('table tbody tr').mouseover(function() {
        $(this).children().css({
            'backgroundColor' : '#DCDCDC',
            'cursor' : 'pointer'
        });
    }).mouseout(function() {
        $(this).children().css({
            'backgroundColor' : '#FFFFFF',
            'cursor' : 'default'
        });
    });

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

    $('#select_chkbox').click(function(i) {
        var rowData = new Array();
        var chkData = new Array();
        var checkbox = $('input:checkbox[name="uid[]"]:checked');
        checkbox.each(function(i){ // 반복되는 태그 단위를 찾아서 each() 함수 사용
           var select_value = $(this).val();
           chkData.push(select_value);

           // checkbox.parent() : checkbox의 부모는 <td>
           // checkbox.parent().parent() : <td>의 부모이므로 <tr>
           var tr = checkbox.parent().parent().eq(i);
           var td = tr.children();
           rowData.push(tr.text()+"<br />");
        });
        if (rowData.length == 0) {
           $("#chk_result").html("선택항 항목이 없습니다").css("color", "blue");

           $("#rawData_result").html("");

        } else {
           $("#chk_result").html("선택된 체크박스 값 : "+chkData).css("color", "blue");
           $("#rawData_result").html("체크된 Row의 모든 데이터 : <br /> "+rowData);
        }
    });

    var sum = 0;
    $('table tbody tr').each(function(){ //테이블 모든 tr에 순차 접근
       if($.isNumeric($("td:eq(4)",this).text())){ //해당 tr의 다섯번째 td에 접근해서 숫자인지 확인
           sum += parseInt($("td:eq(4)",this).text());//해당하는 값을 숫자로 변환해서 누적합산 처리
       }
    });
    $("#sum").text("수량 합계 : "+sum.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));// sum을 출력

});
</script>
</head>
<body>
<div class="container">
    <div class="row">
        <table class="table table-hover">
        <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>
        <tbody>
        <tr>
            <td><input type="checkbox" class="chkbox" name="uid[]" value="7" /></td>
            <td>7</td>
            <td>사과</td>
            <td>5</td>
            <td>5</td>
        </tr>
        <tr>
            <td><input type="checkbox" class="chkbox" name="uid[]" value="6" /></td>
            <td>6</td>
            <td>수박</td>
            <td>4</td>
            <td>500</td>
        </tr>
        <tr>
            <td><input type="checkbox" class="chkbox" name="uid[]" value="5" /></td>
            <td>5</td>
            <td>사과</td>
            <td>3</td>
            <td>3</td>
        </tr>
        <tr>
            <td><input type="checkbox" class="chkbox" name="uid[]" value="4" /></td>
            <td>4</td>
            <td>수박</td>
            <td>2</td>
            <td>500</td>
        </tr>
        <tr>
            <td>
            <input type="checkbox" class="chkbox" name="uid[]" value="3" />
            </td>
            <td>3</td>
            <td>복숭아</td>
            <td>7</td>
            <td>50</td>
        </tr>
        <tr>
            <td>
            <input type="checkbox" class="chkbox" name="uid[]" value="2" />
            </td>
            <td>2</td>
            <td>사과</td>
            <td>8</td>
            <td>90</td>
        </tr>
        <tr>
            <td>
            <input type="checkbox" class="chkbox" name="uid[]" value="1" />
            </td>
            <td>1</td>
            <td>망고</td>
            <td>555</td>
            <td>40</td>
        </tr>
        </tbody>
        <td colspan="5" style="text-align:left;">
        <button type="button" class="btn btn-success btn-sm" id="select_chkbox">선택</button>
        </table>
    </div>
    <div id="chk_result"></div>
    <div id="rawData_result"></div>
    <div id="sum"></div>
</div>

</body>
</html>
 


728x90

'Web 프로그램 > js, jQuery' 카테고리의 다른 글

jQuery addClass()  (0) 2018.02.04
jQuery all Checked  (0) 2018.01.23
JavaScript 와 jQuery 비교 정리  (0) 2018.01.15
[Javascript] Form 객체 다루기 (자바스크립트 와 jQuery)  (0) 2017.12.16
[jQuery] Table Filtering  (0) 2017.12.13
블로그 이미지

Link2Me

,
728x90

jQuery 로 간단하게 하는 것이 Javascript 코드와 비교표를 통해서 정리하며, 계속 추가할 것이다.


jQuery

JavaScript

$(document).ready(function() {

});

또는

$(function () {
});

 window.onload=function(){

}

 $("p").css("background-color", "#f60");

 var p=document.getElementsByTagName("p");

 p.style.backgroundColor="#f60";

 $("#navi").css("background-color", "#f60");

 var navi=document.getElementById("navi");

 navi.style.backgroundColor="#f60";


 $("#element1").on('click', function() {
        // do something on click
 }

 또는  
$("#element1").click(function() {
        // do something on click
 });

 var x = document.getElementById("element1");
if (x.addEventListener) { // IE 9 이상, 타브라우저
    x.addEventListener("click", myFunction);
} else if (x.attachEvent) { // For IE 8 and earlier versions
    x.attachEvent("onclick", myFunction);
}

 document.getElementById("element1")

.addEventListener('click',doSomething,false);

$('#outer').on('mouseup', function (event) {
    alert('This alert should not show up!');
}, false);

document.getElementById('outer').addEventListener('mouseup', function (event) {
    alert('This alert should not show up!');
}, false);

 $(".class1.class2").eq(0).text(1254);

 document.querySelectorAll('.class1.class2')[0].textContent = 1254;

 document.querySelectorAll('.class1.class2')[0].innerHTML = 1254;


jQuery 개발시 주의사항

jQuery CDN에 있는 jquery-latest.js 라는 최신 버전을 사용하는 것에 주의해야 한다.

개발할 당시에 사용했던 버전에 최적화되어 있을 수 있으니 반드시 최신버전이 좋은 것은 아니다.

Javascript 를 모르고 jQuery 만 배우면 한계에 부딪치므로 기초부터 튼튼히 배우는 게 좋은데 체계적으로 배우지 않아서 그런지 쉽지 않다.

jQuery 라이브러리만 사용하지 말고 어떻게 코딩하면 고객에게 더 좋은 Web  페이지를 제공해 줄 수 있는지 어떻게 하면 더 깔끔하게 개발할 수 있는지 고민을 하는 것이 바로 고급 Web 개발자로 들어서는 길이란다.


728x90
블로그 이미지

Link2Me

,
728x90

array, sub array 등 다차원 배열을 단순화 시키는 경우가 필요하다.

아래 코드와 같이 편법(?) 처리하였다.

SQL 문으로 처리하는 걸 검색해보니 해결하기가 너무 어렵다.

오라클은 쉽게 제공하는 기능이 있는데 MySQL은 너무 복잡하다.


$c = new LoginClass;
$rows = $c -> getDbData('menu_items', '', '*');
foreach ($rows as $row) {
    $TreeMenu[] = $row;
}

$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($c->TreeSQL($TreeMenu)));
$c->TableTruncate('menu_treesort'); // 테이블 비우기
$i=0;
foreach($it as $k=>$v) {
    $key = '_id';
    $val = "$v";
    $c->getDbInsert('menu_treesort', $key, $val);
    $i++;
}
$c->getDbUpdate('menu_items m join menu_treesort s on m.id=s._id', "sort=s.idx", NULL,'');

echo $i.'개 Data inserted';


TreeSQL 함수

이 함수가 TreeView Sort 의 핵심이다.

function TreeSQL($array, $parent_id = 0, $parents = array()) {
    if ($parent_id == 0) {
        foreach ($array as $element) {
            if (($element['parent_id'] != 0) && !in_array($element['parent_id'], $parents)) {
                $parents[] = $element['parent_id'];
            }
        }
    }

    $R = array();
    foreach ($array as $element) {
        if ($element['parent_id'] == $parent_id) {
            if (in_array($element['id'], $parents)) {
                array_push($R,$element['id']);
                array_push($R, TreeSQL($array, $element['id'], $parents));
            } else {
                array_push($R,$element['id']);
            }
        }
    }
    return $R;
}

728x90
블로그 이미지

Link2Me

,
728x90

네이버에서 제공하는 날씨 정보를 파싱하는 방법이다.


<?php
$url = 'http://weather.naver.com/rgn/cityWetrCity.nhn?cityRgnCd=CT001024#nhn_weather_tab';

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);
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
}

$html = file_get_contents_curl($url);

// 가져온 데이터가 깨져보이는 경우는 인코딩 모드가 UTF-8 이 아닐 수 있다.
$enc = mb_detect_encoding($html, array('EUC-KR', 'UTF-8', 'shift_jis', 'CN-GB'));
if ($enc != 'UTF-8') {
    $html = iconv($enc, 'UTF-8', $html);
}
print_r($html);
?>


위와 같이 cURL 을 이용하면 해당 페이지 정보가 보이는데 XML이 아니라 HTML Web 이다.

소스보기를 하면 html 코드들이 보인다.


PHP Simple HTML DOM Parser 를 이용하면 원하는 곳만 쉽게 뽑아낼 수 있다.

http://simplehtmldom.sourceforge.net/ 에 가면 다운로드 받을 수 있다.

jQuery 처럼 selector 로 HTML 페이지에 있는 tags를 찾을 수 있다.


PHP Simple HTML DOM Parser 로 가져온 걸 echo 로 찍어보면 한줄로 보여서 가독성이 떨어진다.

그래서 cURL 로 가져와서 소스보기를 한 다음 원하는 태그를 find로 찾아서 원하는 결과를 출력하면 된다.

http://simplehtmldom.sourceforge.net/manual.htm 매뉴얼에 좀 더 상세한 사용법 예제들이 나온다.


<?php
$url = 'http://weather.naver.com/rgn/cityWetrCity.nhn?cityRgnCd=CT001024#nhn_weather_tab';

include_once 'simple_html_dom.php';
$html = file_get_html($url);

//echo $html->find('li[id="tab_CT001000"]',0);
echo $html->find('div[id="content_sub"]',0)->children(0);
//echo $html->find('div[id="content_sub"]',0)->children(1);
echo $html->find('div[class="w_now2"]',0)->children(0)->children(0)->children(0);

echo $html->find('div[class="fl"]',0);
echo $html->find('div[class="fl"]',0)->plaintext;

//echo $html->find('div[class="fl"]',0)->children(0);
//echo $html->find('div[class="fl"]',0)->children(1);
//echo $html->find('div[class="fl"]',0)->children(2);
?>
 


위 박스에 나온 예제를 소스보기로 비교하면서 실행해보면 금방 이해된다.

728x90

'Web 프로그램 > JSON, 파싱 다루기' 카테고리의 다른 글

PHP json_encode  (0) 2018.06.19
PHP Local JSON 파일 읽어오기  (0) 2018.05.18
기상청 지역 날씨정보 파싱  (0) 2017.12.23
Parse JSON with PHP (JSON 파싱)  (2) 2017.11.27
PHP Array 활용 JSON  (0) 2017.05.11
블로그 이미지

Link2Me

,
728x90

특정 지역의 날씨정보를 기상청으로부터 가져오는 코드에 대해 알아보고 적어둔다.


기상청 rss 를 통해 동네예보 rss 구하기

http://www.kma.go.kr/weather/lifenindustry/sevice_rss.jsp?sido=4100000000&gugun=4146500000&dong=4146554000&x=27&y=14


1번을 누르면 해당지역 rss 에 대한 날씨예보 정보(현재 온도, 시간별 날씨, 강수량, 기온, 습도 등)가 검색된다.

http://www.kma.go.kr/wid/queryDFSRSS.jsp?zone=4146554000


크롬브라우저와 Firefox가 다르게 검색된 결과를 보여준다.

Firefox 에서는


라고 검색된다.


http://www.kma.go.kr/wid/queryDFS.jsp?gridx=62&gridy=122


X좌표값과 Y좌표값을 넣고 위 URL를 웹브라우저에 입력하면 해당지역 XML 코드가 출력된다.


temp : 온도, hour : 시간, wfKor : 날씨, pop : 강수량, reh : 습도



PHP에서 Web상의 XML 문서를 파싱하기 위해서는 cURL 라이브러리를 이용하면 된다.

String 으로 추출된 결과물을 simplexml_load_string 으로 구조체로 변환한다.

XML 데이터를 JSON 으로 변환한 다음 배열(array)를 읽어서 원하는 자료를 추출하는 것은 foreach 문을 사용하면 되는거 같다.

배열을 출력하는 print_r 함수를 이용하여 내용을 봐가면서 원하는 자료만 추출하면 된다.


<?php

function file_get_contents_curl($url) {
    $ch = curl_init();// curl 리소스를 초기화
    curl_setopt($ch, CURLOPT_URL, $url); // url을 설정
    // 헤더는 제외하고 content 만 받음
    curl_setopt($ch, CURLOPT_HEADER, 0);
    // 응답 값을 브라우저에 표시하지 말고 값을 리턴
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $data = curl_exec($ch);
    curl_close($ch);// 리소스 해제를 위해 세션 연결 닫음
    return $data;
}


$url = "http://www.kma.go.kr/wid/queryDFS.jsp?gridx=62&gridy=122";

$xml_string = file_get_contents_curl($url);

$xml = simplexml_load_string($xml_string);

$json = json_encode($xml); // XML to JSON

$R = json_decode($json,TRUE);//배열로 변환


print_r($R);

echo '<br />';

echo '<br />';

echo $R['header']['tm'].'<br />';//날씨 예보시간

$wData = $R['body']['data'][0];

print_r($wData);

echo '<br />';

echo '<br />';

//echo $wData['hour'].'시 온도:'.$wData['temp'].' 날씨:'.$wData['wfKor'];


foreach($R['body']['data'] as $wData){

    echo $wData['hour'].'시 온도:'.$wData['temp'].' 날씨:'.$wData['wfKor'];

    echo '<br />';

}

?>


위 코드에서 JSON 으로 변경하지 않고 처리한다면

$data = file_get_contents_curl($url);
$xml = simplexml_load_string($data);

echo '<pre>';
print_r($xml);
echo '</pre>';

foreach ($xml->body->data as $row) {
    $hour = $row -> hour;
    $temp = $row -> temp;
    $wfkor = $row -> wfKor;
    $pop = $row -> pop;
    $reh = $row -> reh;
    $wdkor = $row -> wdKor;
}

와 같이 처리하면 원하는 걸 뽑아낼 수 있다.


배열에 나오는 날씨는 내일과 모레 날씨 정보를 보여주는 거 같다.

다음날 오전에 다시 확인해보니 내일, 모레 뿐만 아니라 현재 기준으로 앞으로의 오늘 날씨 정보도 보여주더라.

그런데 날짜 정보가 XML 파일에 기록되지 않아서 해당 정보를 정확하게 파악하기가 쉽지 않다.


cURL 함수 사용이 가능한지 여부를 확인하는 방법

 <?php
if (ini_get('allow_url_fopen') == '1') {
   // file_get_contents() 사용
   echo 'php.ini 환경에서 허용';
} else {
   // curl 함수 이용 가능 여부
   if (function_exists('curl_init')) {
       echo 'curl 함수 사용 가능';
   } else {
       echo 'curl 함수 이용 불가';
   }
}
?>


php.ini에는 'allow_url_fopen'라는 설정항목이 있다.
이 항목이 off일 경우에는 fopen 등의 파일관련 함수에서 URL로 파일을 읽어오는 것이 불가능하다.
'allow_url_fopen'을 off로 하는 대부분의 이유는 '보안 취약성'의 우려가 있기 때문이다.
For example, somebody might pass /etc/passwd as a url, and be able to view its contents.

728x90
블로그 이미지

Link2Me

,
728x90

온라인 투표 소스에 대한 네이버지식인 질의가 있어서 구글링으로 검색해보니 https://github.com/rezwanulhaquerezwan/Online-Voting-System-using-php-and-mysql 에 소스가 검색된다.


스마트폰에서 모바일 투표도 가능하겠다 싶어서 한글화 작업을 좀 하면서 코드를 상당부분 수정했다.

- 회원관리 부분은 아예 없어서 신규 추가

- 관리자 전용 접속 로그인 코드 삭제 및 회원관리 코드에 admin 칼럼을 추가하고 관리자 식별토록 수정

- 후보자 생성만 있고 수정하는 기능이 없어서 수정 기능 추가

- positon 생성만 있고 수정하는 기능이 없어서 수정 기능 추가

- DB접속방식을 MySQLi 절차지향방식으로 변경하고 관련 코드 전부 수정

- 중복부분은 header.php 와 tail.php 로 코드 정리


스마트폰에서 잘 되기는 하는데 부트스트랩 코드로 수정해야 메뉴 선택기능이 좀 더 편리할 거 같다.

PC환경에서는 메뉴가 잘 보인다.


1. 첨부파일 코드를 받아서 sql폴더에 있는 SQL 구조를 이용하여 DB를 생성하고 테이블을 추가해준다.

2. id : admin@gmail.com pw : 1234 로 로그인한다.



이 코드에서 정작 중요한 것은 온라인 투표 결과부분을 수정해야 한다는 것이다.

나중에 기회되면 업데이트를 하겠지만, 지금은 배워야 할 기능들이 많아서 여기까지만 해둔다.

PDO 방식으로 코딩해보는 것이 있어서 시간을 더 이상 할애하기는 어려울 거 같다.

직접 수정해도 별로 어렵지 않을 것이다.

코드가 핵심내용을 채우기 전에 필요한 것을 거의 만들었다(?) 라고 보면 될 거 같다.

UI 부분을 부트스트랩으로 변경하면, 온라인 투표가 아니더라도 회원관리 간단하게 할 수 있다.

초보자용으로는 공부하는데 도움이 될 코드가 많다.


MySQLi 방식으로 연동된 코드

online_voting.zip


MySQL 방식 연동으로 된 코드

onlie_voting_mysql.tgz


728x90
블로그 이미지

Link2Me

,
728x90

HTML Form 은 client의 정보를 입력받는 문서라고 보면 된다.

<form> 태그를 이용하여 각종 입력양식을 통해 입력받아 서버로 전달하는 기능을 담당한다.

form객체는 document.forms 배열로 표현한다.


파일을 직접 실행해볼 수 있게 만든 예제 파일

formChk.php

form_javascript.php

form_jquery.php


jQuery 를 사용한 함수는 없지만 jQuery 스크립트를 한줄 추가했다.

첨부한 예제 파일을 다운로드 받아서 실행해보면 금방 이해된다.

forms[0] 는 한 파일안에 form 태그가 2개 존재할 경우 첫번째 form 을 의미한다.

두번째 form 은 forms[1]


var name = document.myform.name.value;
var telNO = document.myform.telNO.value;
var address = document.myform.address.value;

에서 공통적인 변수를 별도로 지정하여 처리하는 편이 더 좋다.

var f = document.myform;


<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
function info_form(){
   str  = " name   : " + document.myform.name + "<br>" ;
   str += " action : " + document.myform.action+ "<br>" ;
   str += " target : " + document.myform.target + "<br>" ;
   str += " method : " + document.myform.method + "<br>" ;
   str += " encoding  : " + document.myform.encoding + "<br>" ;
   str += " length  : " + document.myform.length + "<br>" ;
   str += " 첫번째 요소 value  : " + document.myform.elements[0].value + "<br>" ;
   str += " 첫번째 요소 name  : " + document.myform.elements[0].name + "<br>" ;
   str += " 첫번째 form의 첫번째 요소 name  : " + document.forms[0].elements[0].name + "<br>" ;
   str += " 첫번째 form의 첫번째 요소 value  : " + document.forms[0].elements[0].value + "<br>" ;
   str += " 첫번째 form의 두번째 요소 name  : " + document.forms[0].elements[1].name + "<br>" ;
   str += " 첫번째 form의 두번째 요소 value  : " + document.forms[0].elements[1].value + "<br>" ;
   str += " 첫번째 form의 세번째 요소 name  : " + document.forms[0].elements[2].name + "<br>" ;
   str += " 첫번째 form의 세번째 요소 value  : " + document.forms[0].elements[2].value + "<br>" ;
   document.write(str);

}

function validateForm(){
    var name = document.myform.name.value;
    var telNO = document.myform.telNO.value;
    var address = document.myform.address.value;
    if(name ==""){
        alert('이름을 입력하세요');
        document.myform.name.focus();
        return false;
    }
    if(telNO ==""){
        alert('전화번호를 입력하세요');
        document.myform.telNO.focus();
        return false;
    }
    if(address ==""){
        alert('주소를 입력하세요');
        document.myform.address.focus();
        return false;
    }
}
</SCRIPT>
</head>
<body>
<form name="myform" action="formChk.php" method="post" onsubmit="return validateForm()">
    이름:<input type="text" name="name" id="myname" value="홍길동" size="20" />
    전화:<input type="text" name="telNO" value="010-1234-9999" size="20" />
    주소:<input type="text" name="address" value="" size="50" required />
    <input type="button" value="폼정보보기" onclick="info_form()" />
    <input type="submit" value="확인" />
    <input type="reset" value="취소" />
</form>
<p id="demo"></p>
</body>
</html>

 === formChk.php ===

<?php
extract($_POST);
print_r($_POST);
echo '<br />';
var_dump($_POST);
exit; // 이 명령어 다음에 적힌 코드는 실행하지 말고 빠져나가라.
?>


위 코드를 jQuery 로 변경한 것을 살펴보자.

$("[attribute]")  : [attribute(속성)] selector는 각 selector의 특정 속성을 선택한다.

- [attribute=value] : 특정 속성과 값을 가진 각 element를 선택한다.

- $(selector).attr(attribute) : attribute의 값을 Return

- $(selector).attr(attribute,value) : attribute의 값을 설정

- $(selector).attr(attribute,function(index,currentvalue)) : function을 사용하여 값을 설정


<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>

function info_form(){
   str  = " name   : " + $("form").attr('name') + "<br>" ;
   str += " action : " + $("form").attr('action') + "<br>" ;
   str += " target : " + $("form").attr('target') + "<br>" ;
   str += " method : " + $("form").attr('method') + "<br>" ;
   str += " encoding  : " + $("form").attr('encoding') + "<br>" ;
   str += " length  : " + $("form").val().length + "<br>" ;
   str += " 첫번째 요소 value  : " + $("form input[name=name]").val() + "<br>" ;
   str += " 첫번째 요소 name  : " + $("form input[name=name]") + "<br>" ;
   str += " 첫번째 form의 첫번째 요소 name  : " + document.forms[0].elements[0].name + "<br>" ;
   str += " 첫번째 form의 첫번째 요소 value  : " + document.forms[0].elements[0].value + "<br>" ;
   str += " 첫번째 form의 두번째 요소 name  : " + $("form input[name=telNO]") + "<br>" ;
   str += " 첫번째 form의 두번째 요소 value  : " + $("form input[name=telNO]").val() + "<br>" ;
   str += " 첫번째 form의 세번째 요소 name  : " + $("form input[name=address]") + "<br>" ;
   str += " 첫번째 form의 세번째 요소 value  : " + $("form input[name=address]").val() + "<br>" ;
   document.write(str);

}


$(document).ready(function(){
    $("#info_form").click(function(){
        info_form();

        // $("form :input").each(function(){
           // alert($(this).val());
        // });

    });
    
    $("form").submit(function(){
        var name = $("input[name=name]").val();
        var telNO = $("input[name=telNO]").val();
        var address = $("input[name=address]").val();
        if(name ==""){
            alert('이름을 입력하세요');
            $("input[name=name]").focus();
            return false;
        }
        if(telNO ==""){
            alert('전화번호를 입력하세요');
            $("input[name=telNO]").focus();
            return false;
        }
        if(address==""){
            alert('주소를 입력하세요');
            $("input[name=address]").focus();
            return false;
        }
    });
});
</script>
</head>
<body>
<form name="myform" action="formChk.php" method="post">
    이름:<input type="text" name="name" id="myname" value="홍길동" size="20" />
    전화:<input type="text" name="telNO" value="010-1234-9999" size="20" />
    주소:<input type="text" name="address" value="" size="50" />
    <input type="button" id="info_form" value="폼정보보기" />
    <input type="submit" value="확인" />
    <input type="reset" value="취소" />
</form>
<p id="demo"></p>
</body>
</html>


<button id="button1">button1</button>
<button id="button2">button2</button>
<button id="button3">button3</button>
<button id="button4">button4</button>

<div></div>

<script>
$(function(){
    $("button").each(function(){
        $(this).click(function(){
            var name_var=$(this).attr("id");
            $("div").text("jQuery Object : "+name_var);
        });
    });
})
</script>


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

728x90
블로그 이미지

Link2Me

,
728x90

화면에 출력되는 Table 데이터를 Filtering 하여 보여주는 jQuery 코드 예제다.

화면에 이미 출력된 데이터 중에서 Filtering 한다고 보면 된다.


테스트에 사용한 코드는

jQuery_TableFiltering-01.php


테이블 필터링 예제는 https://www.jqueryscript.net/demo/jQuery-Plugin-To-Filter-Rows-Of-An-Html-Table-Table-Filtering/ 등 검색하면 많이 나온다.



위와 같은 테이블에서 검색어를 입력하면 해당 검색어를 포함하는 리스트만 출력하는 jQuery 에 대해 정리해둔다.


 $(document).ready(function() {
    $("#mykeyword").on("keyup", function() {
        var value = $(this).val().toLowerCase();
        $("#myTable_tbody tr").filter(function() {
            $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
        });
    });
});


위 jQuery 코드는 테이블내에 일치하는 모든 결과를 출력한다. (번호열 검색 제외)

단, 한글을 검색하는 경우에는 즉각적으로 표시되지 않으므로 입력한 엔터키를 한번 눌러야 한다.

- keyup 이벤트는 키보드의 키를 눌렀다 뗄 때 요소에 이벤트를 발생시킨다.

- $("#mykeyword").on("keyup", function() {} 또는 $("#mykeyword").keyup(function() {} 으로 해도 된다.

- toLowerCase() : 문자열에서 영문 대문자를 모두 소문자로 바꾼다.

- indexOf("찾을 문자") : 문자열에서 왼쪽부터 찾을 문자와 일치하는 문자를 찾아 최초로 일치하는 문자의 인덱스 번호를 반환한다. 만일 찾는 문자가 없다면 -1 을 반환한다.


 $(document).ready(function() {
    $("#mykeyword").on("keyup", function() {
        var value = $(this).val().toLowerCase();
        $("#myTable > tbody > tr").hide();
        var temp = $("#myTable > tbody > tr > td:nth-child(5n+4):contains('" + value + "')");
        $(temp).parent().show();
    });
});


Using a formula (an + b) : a 는 테이블의 칼럼 갯수라고 보면 된다. b 는 1부터 시작한다.

td:nth-child(5n+4) : 칼럼 갯수가 5개인 테이블에서 4번째 즉 학과에 대한 검색결과를 출력한다.

5n + 1 로 변경하고 검색(Filtering)하면 번호열 검색결과를 반환한다.


CSS 에

#myTable > tbody > tr td:nth-child(5n-1) {
    align: right;
    text-align: right;
}

를 추가해보면 테이블 정렬이 어떻게 되는지 확인할 수 있다.

-1 은 오른쪽부터 0, -1, -2 라고 보면 된다.

5n 대신에 2n, 3n, 4n 등으로 숫자를 변경해보면 어떻게 변하는지 확인할 수 있다.


첨부파일 코드 내용

<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
    $("#mykeyword").keyup(function() {
        var value = $(this).val().toLowerCase();
        $("#myTable_tbody tr").filter(function() {
            $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
        });
        // $("#myTable > tbody > tr").hide();
        // var temp = $("#myTable > tbody > tr > td:nth-child(5n+4):contains('" + value + "')");
        // $(temp).parent().show();
    });
});
</script>
<style>
#container {width: 900px; margin: 0 auto;}
#myTable {
    font-family: arial, sans-serif;
    border-collapse: collapse;
    width: 100%;
    border: 1px solid #ddd;
}

#myTable td, th {
    border: 1px solid #dddddd;
    text-align: center;
    padding: 8px;
}

#myTable tr {border-bottom: 1px solid #ddd;}

#myTable tr:nth-child(even) {
    background-color: #f1f1f1;
}

#myTable > tbody > tr td:nth-child(5n) {
    align: right;
    text-align: right;
}

#myTable > thead > tr { background-color: #ddee33; color:#000fff; }
#myTable > tbody > tr:hover { background-color: #d1f1f1; }
</style>
</head>
<body>
<div id="container">
    <h2>Filterable Table</h2>
    <input type="text" id="mykeyword" placeholder="검색어를 입력하세요">
    <br>
    <br>
    <table id='myTable'>
        <thead>
            <tr>
                <th>번호</th>
                <th>이름</th>
                <th>생년월일</th>
                <th>학과</th>
                <th>학번</th>
            </tr>
        </thead>
    
        <tbody id="myTable_tbody">
            <tr>
                <td>1</td>
                <td>박진희</td>
                <td>1991.09.07</td>
                <td>경영정보학과</td>
                <td>2014290031</td>
            </tr>
            <tr>
                <td>2</td>
                <td>김보연</td>
                <td>1991.07.07</td>
                <td>경영학과</td>
                <td>2016290032</td>
            </tr>
            <tr>
                <td>3</td>
                <td>하석진</td>
                <td>1991.05.15</td>
                <td>사회학과</td>
                <td>2017290033</td>
            </tr>
            <tr>
                <td>4</td>
                <td>유희열</td>
                <td>1991.08.07</td>
                <td>건축학과</td>
                <td>2011290038</td>
            </tr>
            <tr>
                <td>5</td>
                <td>송승헌</td>
                <td>1989.04.07</td>
                <td>물리학과</td>
                <td>2011290039</td>
            </tr>
            <tr>
                <td>6</td>
                <td>김태희</td>
                <td>1988.02.07</td>
                <td>의류학과</td>
                <td>2010290040</td>
            </tr>
            <tr>
                <td>7</td>
                <td>정지훈</td>
                <td>1988.07.25</td>
                <td>식품학과</td>
                <td>2012290042</td>
            </tr>
            <tr>
                <td>8</td>
                <td>박진영</td>
                <td>1981.02.07</td>
                <td>영어영문학과</td>
                <td>2015290031</td>
            </tr>
            <tr>
                <td>9</td>
                <td>양현석</td>
                <td>1993.09.07</td>
                <td>역사학과</td>
                <td>2017290055</td>
            </tr>
            <tr>
                <td>10</td>
                <td>유재석</td>
                <td>1995.09.07</td>
                <td>경영정보학과</td>
                <td>2013290056</td>
            </tr>
            <tr>
                <td>11</td>
                <td>고아라</td>
                <td>1993.09.07</td>
                <td>역사학과</td>
                <td>2017290060</td>
            </tr>
        </tbody>
    </table>
    <p>테이블 헤더 검색을 방지하기 위해 tbody 내에서 검색 시작</p>
</div>
</body>
</html>


728x90
블로그 이미지

Link2Me

,
728x90

선택된 메뉴가 몇번째인지 찾아내는 jQuery


$('.nav-tabs a').click(function(){
    var idx = $('.nav-tabs a').index(this);
    alert(idx);
    $(this).tab('show');
});


또는

$('.nav-tabs li>a').click(function(){
    var idx = $('.nav-tabs li>a').index(this);
    if(idx == 2){
        alert(idx + '번째 index를 선택했습니다');
    } else {
        alert(idx);
    }
    $(this).tab('show');
});



<ul class="nav nav-tabs">
    <li class="active"><a data-toggle="tab" href="#menu0">메뉴0</a></li>
    <li><a data-toggle="tab" href="#menu1">메뉴1</a></li>
    <li><a data-toggle="tab" href="#menu2">메뉴2</a></li>
    <li><a data-toggle="tab" href="#menu3">메뉴3</a></li>
</ul>

<div class="tab-content">
    <div id="menu0" class="tab-pane fade in active">
      <h4>Menu 0</h4>
      <p>Eaque ipsa quae ab illo inventore veritatis. </p>
    </div>
    <div id="menu1" class="tab-pane fade">
      <h4>Menu 1</h4>
      <p>Eaque ipsa quae ab illo inventore veritatis. </p>
    </div>
    <div id="menu2" class="tab-pane fade">
      <h4>Menu 2</h4>
      <p>Eaque ipsa quae ab illo inventore veritatis. </p>
    </div>
    <div id="menu3" class="tab-pane fade">
      <h4>Menu 3</h4>
      <p>Eaque ipsa quae ab illo inventore veritatis. </p>
    </div>
</div> 


728x90
블로그 이미지

Link2Me

,
728x90

부트스트랩 기반으로 회원관리 테이블을 작성중이다.

PDO 기반으로 Class 함수를 만드느라고 시행착오를 겪어가면서 수정 보완하면서 만들고 있다.



=== admin.php ===

<?php include_once '_common.php';?>
<!DOCTYPE html>
<html lang="en">
    <head>
        <?php
        require_once $g['path_layout'] . 'default/includes/_import.head.php';
        ?>
    </head>
    <body>
        <?php
        require_once $g['path_layout'] . 'default/includes/header.php';
        ?>

        <!-- 메인 화면 -->
        <main class="container-fluid">
            <div class="container main-container">
                <!-- body 화면 -->
                <div class="row">
                    <div class="col-md-12">
                        <div class="panel panel-default">
                            <div class="panel-body" id="panel_content">
                                <?php require_once $g['path_module'] . 'admin/adminMenu.php';?>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </main>
        <div id="ajaxPath" data-path="<?php echo $g['path_page'] . 'process/'; ?>" ></div>
        <div id="dialog"></div>
        <div id="actionPath" data-path="<?php echo $g['path_module'] . 'admin/'; ?>" ></div>
    </body>

</html>

=== _import.head.php ===

<meta charset="UTF-8">
<meta name="robots" content="noindex,nofollow"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="pragma" content="no-cache" />
<meta name="apple-mobile-web-app-capable" content="no" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<title><?=$_site['site_name'];?></title>
<link rel="stylesheet" href="<?php echo $g['path_bootstrap']?>css/bootstrap.min.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="<?php echo $g['path_core']?>css/topmenu.css" />
<link rel="stylesheet" href="<?php echo $g['path_core']?>css/vertical-menu.css" />
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="<?php echo $g['path_bootstrap']?>js/bootstrap.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="<?php echo $g['path_core']?>js/jquery.tablednd.js"></script>
<script src="<?php echo $g['path_core']?>js/user.js"></script>
<script src="<?php echo $g['path_core']?>js/table_display.js"></script>
<!--[if lt IE 9]> <!-- 5. 인터넷익스플로러 9버전 이하일 경우 html5가 인식될 수 있게 해주는 스크립트 -->
<script src="<?php echo $g['path_bootstrap']?>js/respond.min.js"></script>

=== header.php ===

<nav class="navbar navbar-default">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
        <span class="sr-only">메뉴</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="<?php echo $g['path_page'];?>index.php">
          <?=isset($_bbs_info['bbs_name'])?$_bbs_info['bbs_name']:$_site['site_name']?>
      </a>
    </div>
    <div class="navbar-collapse collapse" id="navbar" >
        <ul class="nav navbar-nav">
            <?php require_once $g['path_layout'].'menu/item_topmenu.php';?>
        </ul>

        <ul class="nav navbar-nav navbar-right">
            <?php if(isset($_SESSION['userID']) && !empty($_SESSION['userID'])):?>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown"><? echo $_SESSION['userNM'] ?>
                    <b class="caret"></b></a>
                    <ul class="dropdown-menu">
                        <li><a href="#" onclick="document.getElementById('logout-form').submit();">로그아웃</a></li>
                        <li><a href="<?php echo $g['path_page'];?>member.php?m=info">정보수정</a></li>
                        <?php if(isset($_SESSION['admin']) && $_SESSION['admin']==1):?>
                        <li class="divider"></li>
                        <li><a href="<?php echo $g['path_page']; ?>admin.php">관리자</a></li>
                        <?php endif;?>
                    </ul>
                </li>
            <?php else:?>
                <li><a href="#modal-login" data-toggle="modal" >로그인</a></li>
                <li><a href="<?php echo $g['path_page'];?>member.php?m=join">회원가입</a></li>
            <?php endif;?>            
        </ul>

    </div><!--/.nav-collapse -->
    <div>
        <?php
            include_once $g['path_layout'].'default/includes/login_modal.php';           
        ?>
    </div>

  </div>
</nav>


=== memberList.php ===

<?php
if (!isset($_SESSION)) {
    session_start();
}
if(!isset($_SESSION['userID']) || empty($_SESSION['userID'])){
    echo 0;
    exit;
}

require_once $_SERVER['DOCUMENT_ROOT'].'/pdoclass/dbconfig.php';
$c = new LoginClass;
$b = new boardClass;
$link_url = $_SERVER['PHP_SELF']; // 현재 실행중인 파일명 가져오기
$page = isset($_GET['page'])? trim($_GET['page']):1;//페이지 변수 설정
$rowsPage = 10; // 한 화면에 표시되는 게시글 수
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$table = "members m join member_data d on m.idx=d.relatedidx";
$cat1 = isset($cat1) ? $cat1: '';
$cat2 = isset($cat2) ? $cat2: '';
$where = isset($_GET['where']) ? $_GET['where']: '';
$keyword = isset($_GET['keyword']) ? $_GET['keyword']: '';
$xorderby= isset($xorderby) ? $xorderby : 'idx DESC';
if($where && $keyword) {
    if($where == 'userNM') $sql = "userNM LIKE '%".$keyword."%' ";
    if($where == 'userID') $sql = "userID LIKE '%".$keyword."%' ";
    if($where == 'mobileNO') $sql = "mobileNO LIKE '%".$keyword."%' ";
    if($where == 'unify') {
        $sql = "(userID LIKE '%".$keyword."%' OR userNM LIKE '%".$keyword."%' OR mobileNO LIKE '%".$keyword."%') ";
    }
} else {
    $sql ='';
}
$g['url_link']=($where?'where='.$where.'&amp;':'').($keyword?'keyword='.urlencode(stripslashes($keyword)).'&amp;':'').($cat1?'cat1='.$cat1.'&amp;':'').($cat2?'cat2='.$cat2.'&amp;':'');
$g['bbs_reset'] = $link_url;

$rows= $c->getDbArray($table,$sql,'*',$xorderby,$rowsPage,$curPage);
$NUM = $c->getDbRows($table,$sql); // 전체 게시글 수 또는 검색된 게시글 수
$TPG = $b->getTotalPage($NUM,$rowsPage); // 전체페이지수 및 현재 페이지수
?>

<div class="pull-left info">
    <?php if( $keyword ):?><strong>"<?php echo $keyword?>"</strong> 검색결과 : <?php endif?>
    <?php echo number_format($NUM)?>개 (<?php echo $curPage;?>/<?php echo $TPG;?>페이지)
</div>
<table id="membertable" class="table table-bordred table-striped table-hover">
   <thead>
        <th align="center" width="50"><strong>idx</strong></th>
        <th align="center"><strong>아이디</strong></th>
        <th align="center"><strong>성명</strong></th>
        <th align="center"><strong>휴대폰</strong></th>
        <th align="center"><strong>일반전화</strong></th>
        <th align="center"><strong>가입일</strong></th>
        <th align="center"><strong>접속일</strong></th>
        <th align="center"><strong>권한</strong></th>
        <th align="center"><strong>개인정보</strong></th>
   </thead>
   <tbody>

<?php
foreach($rows as $R):
    $A = $c->getDbDataFromIdx('member_data','relatedidx',$R['idx'] );
?>
<tr id="<?php echo $R['idx']; ?>">
    <td><?php echo $R['idx']; ?></td>
    <td><?php echo $R['userID']; ?></td>
    <td><?php echo $R['userNM']; ?></td>
    <td><?php echo $c->hyphen_telNo($R['mobileNO']); ?></td>
    <td><?php echo $c->hyphen_telNo($A['officeNO']); ?></td>
    <td><?php echo substr($R['regdate'], 0, 8); ?></td>
    <td><?php echo $R['curdate']; ?></td>
    <td><?php echo $R['admin'] == 1 ? '관리자' : ''; ?></td>
    <td><?php echo $A['smart'] == 1 ? '공개동의' : '비공개'; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class='searchbox'>
    <form name="membersearchf" class="form-inline" action="<?php echo $link_url;?>">
        <input type="hidden" name="orderby" value="<?php echo $xorderby;?>" />
        <select name="where" class="form-control input-sm">
            <option value="unify">통합</option>
            <option value="userNM">이름</option>
            <option value="userID">아이디</option>
            <option value="mobileNO">휴대폰</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='userNM';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($link_url, $NUM, $rowsPage, $curPage, $g['url_link']); ?>


관련 작성 함수

※ PDO 조건에 맞게 구현되지 못한 함수도 있고 구현한 함수도 있다. (SQL 인젝션 관점)

   계속 시도를 하면서 방법을 찾을 생각이다.

// table 결과 조회 용도
public 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 : '');
    $stmt = $this -> db -> prepare($sql);
    $stmt -> execute();
    return $stmt;
}


// WhereArgs 조건에 일치하는 선택 DbData 가져오기
public function getDbDataFromIdx($table, $whereArgs, $idx) {
    $sql = 'select * from ' . $table . ' where ' . $whereArgs . '=?';
    $stmt = $this -> db -> prepare($sql);
    $stmt -> execute(array($idx));
    return $stmt -> fetch();
}


vertical-menu.css

/* Bootstrap 테이블 수정 */
.table > thead {
  background-color: #b3c6ff;
}
.table > thead > tr > th {
  text-align: center;
}
.table-hover > tbody > tr:hover {
  background-color: #e6ecff;
}
.table > tbody > tr > td {
  text-align: center;
}
.table > tbody > tr > #title {
  text-align: left;
}
div > #paging {
  text-align: center;
}

/* make sidebar nav vertical */
@media (min-width: 768px) {
  .sidebar .sidebar-nav .navbar .navbar-collapse {
    padding: 0;
    max-height: none;
  }
  .sidebar .sidebar-nav .navbar ul {
    float: none;
  }
  .sidebar .sidebar-nav .navbar ul:not {
    display: block;
  }
  .sidebar .sidebar-nav .navbar li {
    float: none;
    display: block;
  }
  .sidebar .sidebar-nav .navbar li a {
    padding-top: 12px;
    padding-bottom: 12px;
  }
}

 topmenu.css

/* 로그인 폼 */
.modal-header {
      color:black !important;
      text-align: center;
      font-size: 30px;
}
.modal-footer {
      background-color: #f9f9f9;
      padding: 5px 5px 5px 5px;
      border-top: 0px;
}
/* 로그인 폼 End */ 

/* 서브메뉴 */
.dropdown-submenu{position:relative;}
.dropdown-submenu>.dropdown-menu{
    top:0;
    left:100%;
    margin-top:-6px;
    margin-left:-1px;
    -webkit-border-radius:0 6px 6px 6px;
    -moz-border-radius:0 6px 6px 6px;
    border-radius:0 6px 6px 6px;
}

.dropdown-submenu>a:after{
    display:block;
    content:" ";
    float:right;
    width:0;
    height:0;
    border-color:transparent;
    border-style:solid;
    border-width:5px 0 5px 5px;
    border-left-color:#cccccc;
    margin-top:5px;
    margin-right:-10px;
}
.dropdown-submenu:hover>a:after{
    border-left-color:#555;
}
.dropdown-submenu.pull-left{float:none;}
.dropdown-submenu.pull-left>.dropdown-menu{
    left:-100%;
    margin-left:10px;
    -webkit-border-radius:6px 0 6px 6px;
    -moz-border-radius:6px 0 6px 6px;
    border-radius:6px 0 6px 6px;
}

@media only screen and (max-width: 768px) {
   
}

@media only screen and (min-width: 768px) {
    .dropdown-submenu:hover>.dropdown-menu {
        display: block;
    }
}


728x90
블로그 이미지

Link2Me

,
728x90

Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead

메시지가 나온다.


<?php
// Deprecated:
$result = preg_replace(
    "/\{([<>])([a-zA-Z0-9_]*)(\?{0,1})([a-zA-Z0-9_]*)\}(.*)\{\\1\/\\2\}/iseU",
    "CallFunction('\\1','\\2','\\3','\\4','\\5')",
    $result
);

// 대체할 함수
$result = preg_replace_callback(
    "/\{([<>])([a-zA-Z0-9_]*)(\?{0,1})([a-zA-Z0-9_]*)\}(.*)\{\\1\/\\2\}/isU",
    function($m) { return CallFunction($m[1], $m[2], $m[3], $m[4], $m[5]); },
    $result
);

?>




728x90
블로그 이미지

Link2Me

,
728x90

ajax(asynchronous Javascript and XML) 회원가입 처리를 하면서 에러가 발생하면 디버깅을 해야 한다.

ajax를 이용해서 클라이언트와 서버가 사용자에게 영향을 미치지 않고 백그라운드에서 정보를 주고 받을 수 있다.

입력변수로 넘어가는 부분에서 에러가 발생하는지 여부는 alert 창으로 확인하면 된다.


echo '{"result":"0"}'; // echo json_encode(array('result' => '0')); 과 동일

echo json_encode(array('result' => '1')); // 회원 등록 성공

echo json_encode(array('result' => '-1')); // 회원 등록 실패

echo json_encode(array('result' => '-2')); // 입력받은 데이터에 문제가 있는 경우


처리된 결과를 return 받은 곳에서 에러가 발생하는지 파악할 때

dataType 이 json 으로 되어 있으면 alert(response); 를 해도 Object.object 라서 에러 메시지가 무엇인지 내용을 알 수 없다.

이럴 경우에는 dataType 을 text 로 변경한 다음에 alert(response); 를 해보면 경고메시지 등이 반환된 것을 알 수 있다.

경고메시지 발생부분을 모두 수정하고 나서, 다시 dataType 을 json 으로 변경하고, alert(response); 를 주석처리해주면 된다.

간단한 것인데도 이 부분에서 자꾸 순간적인 실수를 해서 그냥 적어둔다.


문제가 발생하는 부분은

1. 넘겨주는 url 경로변수가 잘못된 경우

2. dataType 을 json 으로 했는데 respone == 1 이라고 잘못 처리하는 경우

   response.result == 1로 처리해야 한다.

   만약 넘겨주는 값이 {"status":"true","result":"0"} 라고 한다면

   respone.status == true

   response.result == 0

   와 같이 if 조건문을 설정해야 한다.

3. 넘겨주는 data 값이 잘못되었는지 여부도 2번에서 text 로 받아서 메시지를 확인하면 된다.

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

4. 파일 인코딩 모드가 UTF-8 인지 확인한다.


var loginpath =$("#ajaxPath").attr('data-path');

PHP 변수를 ajax에서 인식 못해서 아래와 같이 html 코드를 추가해서 경로변수를 자동인식하도록 처리했다.

<div id="ajaxPath" data-path="<?php echo $g['path_page'].'process/'; ?>" ></div>


$('#join-submit').click(function(e){
    e.preventDefault();
    // 입력값 체크는 생략

    var loginpath =$("#ajaxPath").attr('data-path');
    $.ajax({
        url: loginpath+'registerChk.php',
        type: 'POST',
        data: {
            userNM:$("#inputName").val(),
            userID:$('#inputEmail').val(),
            password:$('#inputPassword').val(),
            mobileNO:$("#inputMobile").val()
        },
        dataType: "json", // json, text
        success: function (response) {
            //alert(response);
            if(response.result == 1){
                alert('가입 완료');
                location.replace('index.php'); // 화면 갱신
            } else if(response.result == 0){
                alert('이미 가입된 아이디입니다');
            } else if(response.result == -2){
                alert('입력 데이터를 확인하세요');
            } else {
                alert('등록중에 에러가 발생했습니다');
            }
        },
        error: function(jqXHR, textStatus, errorThrown){
            alert("arjax error : " + textStatus + "\n" + errorThrown);
        }
    });       
   
});



ajax 대신에 post 를 사용할 경우에는 text 데이터만 처리가 되더라.

체크박스로 체크한 데이터만 추가하는 것이다.

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

$('#Favorite').click(function() {
    var chkdata = new Array();
    $('input:checkbox[name="uid[]"]:checked').each(function() {
        chkdata.push($(this).val()); // 체크한 아이템 배열로 저장
    });
    if (chkdata.length == 0) {// 배열의 길이가 0 이 아니면
        alert('체크한 항목이 없습니다.');
    }

    $.post(datapath+"favoriteChk.php", {
        userID:$("input[name=userID]").val(), chkdata:chkdata
    }, function(msg) {
        if(msg == 1){
            alert('관심 항목을 추가했습니다.');
            $("input:checkbox").prop('checked', false);
        } else if(msg == 0) {
            alert('수정할 데이터가 없습니다.');
            $("input:checkbox").prop('checked', false);
        } else {
            alert('데이터를 확인하세요\n'+msg);
        }
    });
});

<?php
if(!isset($_SESSION)){
    session_start();
}
//echo '<pre>';print_r($_POST);echo '</pre>';

if(isset($_POST) && $_SERVER['REQUEST_METHOD'] == "POST"){
    @extract($_POST);
    require_once 'path.php';// root 폴더를 기준으로 상대적인 경로 자동 구하기
    require_once $g['path_config'].'dbconnect.php';
    require_once $g['path_class'].'adminClass.php';
    $a = new adminClass();

    if(isset($chkdata)){
        $rs = $a->putFavoriteData($userID,$chkdata);
        echo $rs;
    }
} else {
    echo -1;
}
?>



728x90
블로그 이미지

Link2Me

,
728x90

Bootstrap 메인 화면을 Layout 할 때 Carousel (이미지 회전 화면) 기능을 쉽게 제공한다.


https://www.w3schools.com/bootstrap/tryit.asp?filename=trybs_carousel&stacked=h 는 하나의 파일로 Carousel 을 제공하는 예제다.

그런데 직접 파일명을 갯수만큼 적어주는 방법이다.


그래서 지정한 폴더의 파일명을 자동으로 읽어서 화면에 보여주도록 코드를 보완했다.


보완 전 코드

<div id="myCarousel" class="carousel slide" data-ride="carousel">
    <!-- Indicators -->
    <ol class="carousel-indicators">
        <li data-target="#myCarousel" data-slide-to="0" class="active"></li>
        <li data-target="#myCarousel" data-slide-to="1"></li>
        <li data-target="#myCarousel" data-slide-to="2"></li>
        <li data-target="#myCarousel" data-slide-to="3"></li>
    </ol>

    <!-- Wrapper for slides -->
    <div class="carousel-inner">
        <div class="item active">
            <img src="<?php echo $g['path_image']?>carousel/01.jpg" style="width:100%;">
        </div>

        <div class="item">
            <img src="<?php echo $g['path_image']?>carousel/02.jpg" style="width:100%;">
        </div>

        <div class="item">
            <img src="<?php echo $g['path_image']?>carousel/03.jpg" style="width:100%;">
        </div>

        <div class="item">
            <img src="<?php echo $g['path_image']?>carousel/04.jpg" style="width:100%;">
        </div>
    </div>

    <!-- Left and right controls -->
    <a class="left carousel-control" href="#myCarousel" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left"></span> <span class="sr-only">Previous</span> </a>
    <a class="right carousel-control" href="#myCarousel" data-slide="next"> <span class="glyphicon glyphicon-chevron-right"></span> <span class="sr-only">Next</span> </a>
</div>


보완 후 코드

<?php
$path = $g['path_image'] . 'carousel';
$R = getFileNames($path); // 오픈하고자 하는 폴더
function getFileNames($directory) {
    $valid_formats = array("jpg", "png", "gif");// 그림 확장자 지정
    $results = array();
    $handler = opendir($directory);
    while ($file = readdir($handler)) {
        if ($file === "." || $file === "..") continue; // file명이 ".", ".." 이면 무시함
        $getExt = pathinfo($file, PATHINFO_EXTENSION); // 파일의 확장자를 구함

        if (!empty($getExt)) {
            if (in_array($getExt, $valid_formats)) {
                $results[] = $file;
            }
        }
    }
    closedir($handler);
    return $results;
}
?>
<div id="myCarousel" class="carousel slide" data-ride="carousel">
    <!-- Indicators -->
    <ol class="carousel-indicators">
        <?php
        for ($i = 0; $i < count($R); $i++) {
            if ($i == 0){
                echo '<li data-target="#myCarousel" data-slide-to="$i" class="active"></li>';
            } else {
                echo '<li data-target="#myCarousel" data-slide-to="$i"></li>';
            }
        }
        ?>
    </ol>

    <!-- Wrapper for slides -->
    <div class="carousel-inner">
        <?php
        foreach($R as $key=>$value){
            if($key == 0){
                echo '<div class="item active">';
            } else {
                echo '<div class="item">';
            }
            echo '<img src="'.$g['path_image'].'carousel/'.$value.'" style="width:100%;">';
            echo '</div>';
        }
        ?>
    </div>

    <!-- Left and right controls -->
    <a class="left carousel-control" href="#myCarousel" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left"></span> <span class="sr-only">Previous</span> </a>
    <a class="right carousel-control" href="#myCarousel" data-slide="next"> <span class="glyphicon glyphicon-chevron-right"></span> <span class="sr-only">Next</span> </a>
</div>


부트스트랩이 아닌 경우에는 bxslider 코드에 이미지 자동 읽어오기를 추가한 걸 이용하면 편하다.

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


bxslider.zip



728x90
블로그 이미지

Link2Me

,
728x90

PHP 에서 JSON 데이터를 파싱하는 방법이다.

JSON 데이터는 Local File 을 읽어오는 것과 Web 사이트에서 해당 URL 을 읽어오는 방법이 있다.

가장 먼저 파싱해야 할 데이터 형태 파악을 하는 코드부터 살펴보고자 구글링을 했더니 관련 코드가 있어서 주석을 좀 더 추가하고 이해를 돕는 걸 첨가하여 적어둔다.


 <?php
// Web JSON 파일 읽어오기
$url = 'http://ip주소/getFileList.php';
$json_string = file_get_contents($url);

// Local JSON 파일 읽어오기
//$json_string = file_get_contents('weather.json');
// 다차원 배열 반복처리
$R = new RecursiveIteratorIterator(
    new RecursiveArrayIterator(json_decode($json_string, TRUE)),
    RecursiveIteratorIterator::SELF_FIRST);
// $R : array data
// json_decode : JSON 문자열을 PHP 배열로 바꾼다
// json_decode 함수의 두번째 인자를 true 로 설정하면 무조건 array로 변환된다.

foreach ($R as $key => $val) {
    if(is_array($val)) { // val 이 배열이면
        echo "$key:<br/>";
        //echo $key.' (key), value : (array)<br />';
    } else { // 배열이 아니면
        echo "$key => $val <br />";
    }
}
?>


위 코드로 형태파악을 한 다음에 필요한 것을 파싱처리하면 된다.


Local JSON 파일을 읽어서 처리하는 걸 예제로 보자.

 [
    {
        "firstName": "길동",
        "lastName": "홍",
        "email": "jdhongv@gmail.com",
        "mobile": "010-1234-1111"
    },
    {
        "firstName": "민아",
        "lastName": "김",
        "email": "minakim@gmail.com",
        "mobile": "010-1234-3333"
    },
    {
        "firstName": "진주",
        "lastName": "마",
        "email": "jjmah@gmail.com",
        "mobile": "010-1234-5555"
    },
    {
        "firstName": "서영",
        "lastName": "이",
        "email": "sylee@gmail.com",
        "mobile": "010-1234-7777"
    }
]


<?php
// Local JSON 파일 읽어오기
$json_string = file_get_contents('data.json');
$R = json_decode($json_string, true);
// json_decode : JSON 문자열을 PHP 배열로 바꾼다
// json_decode 함수의 두번째 인자를 true 로 설정하면 무조건 array로 변환된다.
// $R : array data


foreach ($R as $row) {
    print $row['lastName'];
    print $row['firstName'];
    print ' , ';
    print $row['email'];
    print ' , ';
    print $row['mobile'];
    print '<br />';
}
?> 


결과

홍길동 , jdhongv@gmail.com , 010-1234-1111
김민아 , minakim@gmail.com , 010-1234-3333
마진주 , jjmah@gmail.com , 010-1234-5555
이서영 , sylee@gmail.com , 010-1234-7777


조금 더 복잡한 JSON 파일을 검색한 걸 테스트한다.

{
    "name": "홍길동",
    "alias": "LInk",
    "members": [
        "소원",
        "예린",
        "은하",
        "유주",
        "신비",
        "엄지"
    ],
    "albums": {
        "EP 1집": "Season of Glass",
        "EP 2집": "Flower Bud",
        "EP 3집": "Snowflake",
        "EP 4집": "THE AWAKENING"
    }
}


파싱하는 코드를 두가지로 테스트해보면 print_r 에서 결과를 다르게 보여준다.

<?php
// JSON 파일 읽어오기
$json_string = file_get_contents('weather.json');
// 다차원 배열 반복처리
$R = new RecursiveIteratorIterator(
    new RecursiveArrayIterator(json_decode($json_string, TRUE)),
    RecursiveIteratorIterator::SELF_FIRST);
// $R : array data
// json_decode : JSON 문자열을 PHP 배열로 바꾼다
// json_decode 함수의 두번째 인자를 true 로 설정하면 무조건 array로 변환된다.

print_r($R);
echo '<br />';

foreach ($R as $key => $val) {
    if(is_array($val)) {
        echo "$key:<br/>";
    } else {
        echo "$key => $val<br/>";
    }
}
?>

<?php
// Local JSON 파일 읽어오기
$json_string = file_get_contents('weather.json');
$R = json_decode($json_string, true); //
// $R : array data
// json_decode : JSON 문자열을 PHP 배열로 바꾼다
// json_decode 함수의 두번째 인자를 true 로 설정하면 무조건 array로 변환된다.

print_r($R); // 배열 요소를 출력해준다.
echo '<br />';
?>

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>PHP JSON parser sample</title>
    </head>

    <body>
        <h3 id="gname">
        <?php
            echo $R['name'];
            if (array_key_exists('alias', $R))
                printf(" (%s)", $R['alias']);
        ?>
        </h3>
        <p>멤버 구성: <span id="members">
            <?php echo implode(', ', $R['members']);?></span>
        </p>
        <h3>앨범 목록</h3>
        <ul id="albums">
        <?php
            foreach ($R['albums'] as $key => $value) {
                printf("<li>%s: %s</li>\n", $key, $value);
            }
            ?>
        </ul>
    </body>
</html>


테스트에 사용한 파일 첨부


JSONwithPHP.zip


728x90
블로그 이미지

Link2Me

,
728x90

Android Studio 에서 회원가입 폼을 만들고, 회원가입 처리하는 로직을 구현했다.

 

 

1. Layout 구성(activity_register.xml)

더보기

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/bg_register"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="10dp" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical"
        android:paddingLeft="20dp"
        android:paddingRight="20dp">

        <EditText
            android:id="@+id/name"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:background="@color/input_register_bg"
            android:hint="@string/hint_name"
            android:inputType="textCapWords"
            android:padding="10dp"
            android:textColor="@color/input_register"
            android:textColorHint="@color/input_register_hint" />

        <EditText
            android:id="@+id/email"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:background="@color/input_register_bg"
            android:hint="@string/hint_email"
            android:inputType="textEmailAddress"
            android:padding="10dp"
            android:textColor="@color/input_register"
            android:textColorHint="@color/input_register_hint" />

        <EditText
            android:id="@+id/password"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:background="@color/input_register_bg"
            android:hint="@string/hint_password"
            android:inputType="textPassword"
            android:padding="10dp"
            android:textColor="@color/input_register"
            android:textColorHint="@color/input_register_hint" />

        <EditText
            android:id="@+id/mobileNO"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:background="@color/input_register_bg"
            android:hint="@string/hint_mobileNO"
            android:inputType="number"
            android:padding="10dp"
            android:textColor="@color/input_register"
            android:textColorHint="@color/input_register_hint" />

        <EditText
            android:id="@+id/telNO"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:background="@color/input_register_bg"
            android:hint="@string/hint_telNO"
            android:inputType="number"
            android:padding="10dp"
            android:textColor="@color/input_register"
            android:textColorHint="@color/input_register_hint" />
        <!-- Login Button -->

        <Button
            android:id="@+id/btnRegister"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dip"
            android:background="#ea4c88"
            android:text="@string/btn_register"
            android:textColor="@color/white" />

        <!-- Link to Login Screen -->

        <Button
            android:id="@+id/btnLoginScreen"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="40dip"
            android:background="@null"
            android:text="@string/btn_link_to_login"
            android:textAllCaps="false"
            android:textColor="@color/white"
            android:textSize="15dp" />
    </LinearLayout>

</LinearLayout>

 

2. Register.java

- OKHttp 라이브러리를 사용하여 코드를 작성했는데 에러가 발생해서 포기하고

  HttpUrlConnection 을 사용하여 코드를 구현했다. 100% 문제없이 동작하는 코드가 최고다.

package com.tistory.link2me.pdologin;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.tistory.link2me.common.PHPComm;

import okhttp3.MediaType;

public class Register extends AppCompatActivity {
    private static final String TAG = Register.class.getSimpleName();
    private Button btnRegister;
    private Button btnLogin;
    private EditText inputUserName;
    private EditText inputEmail;
    private EditText inputPassword;
    private EditText inputMobileNO;
    private EditText inputTelNO;
    private String userID; // SharedPreferences 저장 목적으로 Class 전역변수 선언

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    public SharedPreferences settings;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);

        inputUserName = (EditText) findViewById(R.id.name);
        inputEmail = (EditText) findViewById(R.id.email);
        inputPassword = (EditText) findViewById(R.id.password);
        inputMobileNO = (EditText) findViewById(R.id.mobileNO);
        inputTelNO = (EditText) findViewById(R.id.telNO);
        btnRegister = (Button) findViewById(R.id.btnRegister);
        btnLogin = (Button) findViewById(R.id.btnLoginScreen);

        // 등록 버튼
        btnRegister.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                String name = inputUserName.getText().toString().trim();
                String email = inputEmail.getText().toString().trim();
                userID = email;
                String password = inputPassword.getText().toString().trim();
                String mobileNO = inputMobileNO.getText().toString().trim();
                String telNO = inputTelNO.getText().toString().trim();

                if (!name.isEmpty() && !email.isEmpty() && !password.isEmpty() && !mobileNO.isEmpty()) {
                    registerUser(name, email, password, mobileNO, telNO);
                } else {
                    Toast.makeText(getApplicationContext(), "필수사항은 모두 입력하세요!", Toast.LENGTH_LONG).show();
                }
            }
        });

        // Login Screen 으로 Activity 이동
        btnLogin.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                startActivity(new Intent(getApplicationContext(), Login.class));
                finish();
            }
        });

    }

    private void registerUser(String name, String userID, String password, String mobileNO, String telNO) {
        // 전달할 인자들
        Uri.Builder builder = new Uri.Builder()
                .appendQueryParameter("name", name)
                .appendQueryParameter("userID", userID)
                .appendQueryParameter("email", userID)
                .appendQueryParameter("password", password)
                .appendQueryParameter("mobileNO", mobileNO)
                .appendQueryParameter("telNO", telNO);
        String urlParameters = builder.build().getEncodedQuery();
        new getJSONData().execute(Value.IPADDRESS + "/register.php", urlParameters);
    }

    private class getJSONData extends AsyncTask<String, Void, String> {
        ProgressDialog pdLoading = new ProgressDialog(Register.this);

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pdLoading.setMessage("\t회원가입 처리중...");
            pdLoading.setCancelable(false);
            pdLoading.show();
        }

        @Override
        protected String doInBackground(String... params) {
            try {
                return PHPComm.getJson(params[0],params[1]);
            } catch (Exception e) {
                return new String("Exception: " + e.getMessage());
            }
        }

        protected void onPostExecute(String result){
            pdLoading.dismiss();
            showJSONResult(result);
        }
    }

    protected void showJSONResult(String result) {
        if(result.equalsIgnoreCase("1")){
            Toast.makeText(this, "회원가입 완료", Toast.LENGTH_SHORT).show();
            storeUserData();
            startActivity(new Intent(getApplicationContext(), Login.class));
            finish();
        } else if(result.equalsIgnoreCase("-1")){
            Toast.makeText(this, "아이디가 이미 가입되어 있습니다", Toast.LENGTH_SHORT).show();
            inputEmail.clearFocus();
        } else {
            Toast.makeText(this, "회원 등록에 문제가 발생했습니다", Toast.LENGTH_SHORT).show();
        }
    }

    private void storeUserData(){
        settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putString("userID", userID);
        editor.putBoolean("autologin", true);
        editor.commit();
    }
}

 

3. PHP 코드 작성(register.php)

<?php
extract($_POST);
if(isset($_POST) && !empty($_POST)) {
    require_once '../config/config.php';
    $c = new MemberClass();

    // 동일한 userID 등록되어 있는지 체크
    if ($c->isUserExisted($userID)) { // E-Mail 이 key value
        // user already existed
        echo -1;
    } else {
        // 사용자 등록
        $telNO = preg_replace("/[^0-9]/", "", $telNO);
        $mobileNO = preg_replace("/[^0-9]/", "", $mobileNO);
        $user = $c->storeUser($userID, $name, $email, $password, $telNO, $mobileNO);
        if ($user) { // 사용자 등록 성공
            echo 1;
        } else {
            echo 0;
        }
    }
} else { // 입력받은 데이터에 문제가 있을 경우
    echo -2;
}
?>
 

 

values.zip
다운로드

 

strings.xml 과 colors.xml 파일 두개가 포함되어 있다.

 

회원가입 테이블에 데이터가 저장되어 있는 걸 확인할 수 있다.

 

 

회원가입과 로그인처리는 완벽하게 처리되도록 구현한 소스코드를 그대로 오픈 것이니 복사해서 이용하면 된다.

AndroidManifest.xml 파일에 Register Activity 추가된 것을 추가해줘야 에러가 발생하지 않는다.소소한 에러를 잡아가면서 해보는게 좋을거 같아서 관련으로 수정된 부분은 첨부하지 않는다.

 

도움이 되셨다면 댓글 달아주세요.

728x90
블로그 이미지

Link2Me

,
728x90

Android Studio 3.0 에서 PHP PDO(PHP Data Object) 기반 함수와 연동하여 회원가입 및 로그인처리하는 예제를 구현하고 입문자를 위해서 상세한 설명과 더불어 일부 코드를 첨부한다.

 

1. Android Studio New Module 추가하는 방법

더보기

아래 그림 순서대로 진행하면 된다.

 

 

 

 

 

앱 build.gradle 을 수정할 준비를 한다.

 

 

2. 앱 build.gradle 수정사항

더보기

 apply plugin: 'com.android.application'

android {
    compileSdkVersion 23

    defaultConfig {
        applicationId "com.tistory.link2me.pdologin"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:23.4.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.android.support:recyclerview-v7:24.2.0' // ListView 개선 버전
    compile "com.android.support:cardview-v7:24.2.0"
    compile 'com.squareup.okhttp3:okhttp:3.9.0'
    compile 'com.squareup.picasso:picasso:2.5.2' // 이미지 라이브러리
    compile 'com.google.code.gson:gson:2.8.2'   
}

 

처음에 세팅된 값은 compileSdkVersion 26, targetSdkVersion 26 으로 되어 있는걸 23으로 낮추면 에러메시지가 몇개 나온다.

이때 수정해줘야 할 사항은

AndroidManifest.xml 파일에서 roundicon 나오는 행을 삭제한다.

그리고 res 폴더에서 아래 그림과 같은 부분만 남기도 나머지 폴더는 과감하게 삭제해버린다.

 

위와 같이 하면 코드를 작성할 준비는 완료된 것이라고 봐도 된다.

compileSdkVersion 26, targetSdkVersion 26 그대로 두고 코드를 작성해도 된다.

추가하는 라이브러리를 하나도 사용하지 않고 코드를 작성해보려고 한다.

 

컴파일을 하다보니 에러가 발생하면서 컴파일이 안된다.

Error:Execution failed for task ':pdologin:transformDexArchiveWithExternalLibsDexMergerForDebug'.
> java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex

그래서 implementation 하는 라이브러리 버전이 일치되지 않아서 생기는 문제인거 같아서 수정했더니 정상적으로 컴파일이 된다.

compileSdkVersion 24, targetSdkVersion 24 로 버전 일치 시킴

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:24.2.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.android.support:recyclerview-v7:24.2.0' // ListView 개선 버전
    implementation "com.android.support:cardview-v7:24.2.0"
    implementation 'com.squareup.okhttp3:okhttp:3.9.0'
    implementation 'com.squareup.picasso:picasso:2.5.2' // 이미지 라이브러리
    implementation 'com.google.code.gson:gson:2.8.2'
} 

 

 

3. 로그인 과정 설계

- 아이디, 패스워드를 입력하고 자동로그인을 체크하면 로그인이 처리되고 MainActivity 로 넘어간다.

- 자동로그인 체크를 해두면 아이디, 패스워드를 묻지 않고 자동으로 로그인되어 MainActivity 로 넘어간다.

 

<여기서 고려할 사항>

자동로그인 체크를 하면 로그인창 화면이 표시되지 않아야 한다.

즉, 앱을 실행하자마자 바로 MainActivity 로 넘어가도록 처리하려면 별도의 Activity가 하나 필요하다는 의미다.

 

 

4. Activtiy 추가과정

더보기

Intro Activity 추가

 

위 그림에서 3번은 생략하면 된다.

테스트삼아 선택을 해봤는데 필요하지 않은 기능이다.

 

Login Activity 추가

위 그림과 같은 과정으로 추가를 한다.

 

 

 

이제 AndroidManifest.xml 에서 실행순서를 로그인 처리과정 순서를 고려하여 변경해야 한다.

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tistory.link2me.pdologin">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".Intro">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".Login"></activity>
        <activity android:name=".MainActivity"></activity>
    </application>

</manifest>

 

5. Layout 구성

Layout 파일은 https://www.androidhive.info/2012/01/android-login-and-registration-with-php-mysql-and-sqlite 에서 받은 소스파일을 활용하여 필요한 사항을 수정하여 작성할 것이다.

activity_login.xml 파일 내용을 수정하고 나니 미리보기 화면이 android...actionbaroverlaylayout 라고 표시되며 보이지가 않는다.

검색해 보니 res/values/styles.xml 내용중에서 parent 부분을 아래 것으로 교체하면 정상적으로 보인다.

<style name="AppTheme" parent="Base.Theme.AppCompat.Light.DarkActionBar">
</style>

 

activity_login.xml

더보기

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bg_login"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="10dp" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical"
        android:paddingLeft="20dp"
        android:paddingRight="20dp" >

        <EditText
            android:id="@+id/login_id_edit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:background="@color/white"
            android:hint="@string/hint_email"
            android:inputType="textEmailAddress"
            android:padding="10dp"
            android:singleLine="true"
            android:textColor="@color/input_login"
            android:textColorHint="@color/input_login_hint" />

        <EditText
            android:id="@+id/login_pw_edit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:background="@color/white"
            android:hint="@string/hint_password"
            android:inputType="textPassword"
            android:padding="10dp"
            android:singleLine="true"
            android:textColor="@color/input_login"
            android:textColorHint="@color/input_login_hint" />

        <Button
            android:id="@+id/login_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dip"
            android:background="@color/btn_login_bg"
            android:text="@string/btn_login"
            android:textColor="@color/btn_login" />

        <CheckBox
            android:id="@+id/autologin"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="5dip"
            android:text="자동 로그인"
            android:textColor="#000000"
            android:textSize="14sp"
            android:textStyle="bold" />

        <!-- Link to Login Screen -->
        <Button
            android:id="@+id/btnRegisterScreen"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dip"
            android:background="@null"
            android:text="@string/btn_link_to_register"
            android:textAllCaps="false"
            android:textColor="@color/white"
            android:textSize="15dp" />
    </LinearLayout>

</LinearLayout>

 

6. Java 코드 작성

다른 코드는 생략하고 Login.java 코드만 적는다.

더보기

package com.tistory.link2me.pdologin;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.telephony.TelephonyManager;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

import com.tistory.link2me.common.BackPressHandler;
import com.tistory.link2me.common.PHPComm;

import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;

public class Login extends AppCompatActivity {
    String getDeviceID; // 스마트기기의 장치 고유값
    EditText etId;
    EditText etPw;

    String userID;
    String userPW;
    CheckBox autologin;
    Boolean loginChecked;
    String idx;
    public SharedPreferences settings;
    private BackPressHandler backPressHandler;

    Context mContext;

    // 멀티 퍼미션 지정
    private String[] permissions = {
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.CALL_PHONE, // 전화걸기 및 관리
            Manifest.permission.WRITE_CONTACTS, // 주소록 액세스 권한
            Manifest.permission.WRITE_EXTERNAL_STORAGE // 기기, 사진, 미디어, 파일 엑세스 권한
    };
    private static final int MULTIPLE_PERMISSIONS = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        mContext = this.getBaseContext();
        backPressHandler = new BackPressHandler(this); // 뒤로 가기 버튼 이벤트

        if (Build.VERSION.SDK_INT >= 23) { // 안드로이드 6.0 이상일 경우 퍼미션 체크
            checkPermissions();
        }

        // 네트워크 연결상태 체크
        if(NetworkConnection() == false){
            NotConnected_showAlert();
        }

        etId = (EditText) findViewById(R.id.login_id_edit);
        etPw = (EditText) findViewById(R.id.login_pw_edit);
        autologin = (CheckBox) findViewById(R.id.autologin);

        settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);
        loginChecked = settings.getBoolean("LoginChecked", false);
        if (loginChecked) {
            etId.setText(settings.getString("userID", ""));
            etPw.setText(settings.getString("userPW", ""));
            autologin.setChecked(true);
        }

        if(!settings.getString("userID", "").equals("")) etPw.requestFocus();

        Button submit = (Button) findViewById(R.id.login_btn);
        submit.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view) {
                userID = etId.getText().toString().trim();
                userPW = etPw.getText().toString().trim();

                if(userID != null && !userID.isEmpty() && userPW != null && !userPW.isEmpty()){
                    login(userID, userPW);
                }
            }
        });

    }

    private void login(String loginID, String loginPW){
        // 단말기의 ID 정보를 얻기 위해서는 READ_PHONE_STATE 권한이 필요
        TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {

        }
        if (mTelephony.getDeviceId() != null) {
            getDeviceID = mTelephony.getDeviceId();  // 스마트폰 기기 정보
        } else {
            getDeviceID = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID);
        }

        // 전달할 인자들
        Uri.Builder builder = new Uri.Builder()
                .appendQueryParameter("userID", loginID)
                .appendQueryParameter("userPW", loginPW)
                .appendQueryParameter("deviceID", getDeviceID);
        String urlParameters = builder.build().getEncodedQuery();
        new getJSONData().execute(Value.IPADDRESS + "/loginChk.php", urlParameters);
    }

    private class getJSONData extends AsyncTask<String, Void, String> {
        ProgressDialog pdLoading = new ProgressDialog(Login.this);
        HttpURLConnection conn;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //this method will be running on UI thread
            pdLoading.setMessage("\tValidating user...");
            pdLoading.setCancelable(false);
            pdLoading.show();
        }

        @Override
        protected String doInBackground(String... params) {
            try {
                return PHPComm.getJson(params[0],params[1]);
            } catch (Exception e) {
                return new String("Exception: " + e.getMessage());
            }
        }

        protected void onPostExecute(String result){
            pdLoading.dismiss();
            showJSONResult(result);
        }
    }

    protected void showJSONResult(String result) {
        if(Integer.parseInt(result) > 0){ // 로그인 정보 일치하면 idx 값 받음
            idx = result;
            AutoLoginChk(); // 정보 저장
            Toast.makeText(Login.this,"로그인 성공", Toast.LENGTH_SHORT).show();
            startActivity(new Intent(getApplication(), MainActivity.class));
            finish(); // 현재 Activity 를 없애줌
        } else if(result.equalsIgnoreCase("-1")){ // 등록된 단말기와 불일치
            deviceDismatch_showAlert();
        } else if (result.equalsIgnoreCase("0")) {
            showAlert();
        } else {
            Toast.makeText(Login.this, "서버로부터 정보가 잘못 전송되었습니다", Toast.LENGTH_SHORT).show();
        }
    }

    private boolean checkPermissions() {
        int result;
        List<String> permissionList = new ArrayList<>();
        for (String pm : permissions) {
            result = ContextCompat.checkSelfPermission(this, pm);
            if (result != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(pm);
            }
        }
        if (!permissionList.isEmpty()) {
            ActivityCompat.requestPermissions(this, permissionList.toArray(new String[permissionList.size()]), MULTIPLE_PERMISSIONS);
            return false;
        }
        return true;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case MULTIPLE_PERMISSIONS: {
                if (grantResults.length > 0) {
                    for (int i = 0; i < permissions.length; i++) {
                        if (permissions[i].equals(this.permissions[i])) {
                            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                                showToast_PermissionDeny();
                            }
                        }
                    }
                } else {
                    showToast_PermissionDeny();
                }
                return;
            }
        }

    }

    private void showToast_PermissionDeny() {
        Toast.makeText(this, "권한 요청에 동의 해주셔야 이용 가능합니다. 설정에서 권한 허용 하시기 바랍니다.", Toast.LENGTH_SHORT).show();
        finish();
    }

    public void onStop(){
        // 어플리케이션이 화면에서 사라질때
        super.onStop();
        //AutoLoginChk();
    }

    private void AutoLoginChk(){
        // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장
        if (autologin.isChecked()) {
            settings = getSharedPreferences("settings",Activity.MODE_PRIVATE);
            SharedPreferences.Editor editor = settings.edit();

            editor.putString("userID", userID);
            editor.putString("userPW", userPW);
            editor.putBoolean("LoginChecked", true);
            editor.putString("idx", idx);

            editor.commit();
        } else {
            // 자동 로그인 체크가 해제되면 폰에 저장된 정보 모두 삭제
            settings = getSharedPreferences("settings",    Activity.MODE_PRIVATE);
            SharedPreferences.Editor editor = settings.edit();
            editor.clear(); // 모든 정보 삭제
            editor.commit();
        }
    }

    public void deviceDismatch_showAlert(){
        AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
        builder.setTitle("등록단말 불일치");
        builder.setMessage("최초 등록된 단말기가 아닙니다.\n" + "관리자에게 문의하여 단말기 변경신청을 하시기 바랍니다.")
                .setCancelable(false)
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.dismiss();
                    }
                });
        AlertDialog alert = builder.create();
        alert.show();
    }

    public void showAlert(){
        AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
        builder.setTitle("로그인 에러");
        builder.setMessage("로그인 정보가 일치하지 않습니다.")
                .setCancelable(false)
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.dismiss();
                    }
                });
        AlertDialog alert = builder.create();
        alert.show();
    }

    private void NotConnected_showAlert() {
        AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
        builder.setTitle("네트워크 연결 오류");
        builder.setMessage("사용 가능한 무선네트워크가 없습니다.\n" + "먼저 무선네트워크 연결상태를 확인해 주세요.")
                .setCancelable(false)
                .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        finish(); // exit
                        //application 프로세스를 강제 종료
                        android.os.Process.killProcess(android.os.Process.myPid() );
                    }
                });
        AlertDialog alert = builder.create();
        alert.show();

    }

    private boolean NetworkConnection() {
        int[] networkTypes = ;
        try {
            ConnectivityManager manager = (ConnectivityManager) getSystemService (Context.CONNECTIVITY_SERVICE);
            for (int networkType : networkTypes) {
                NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
                if(activeNetwork != null && activeNetwork.getType() == networkType){
                    return true;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return false;
    }

    @Override
    public void onBackPressed() {
        backPressHandler.onBackPressed();
    }

}

 

Intro.java 코드는 Login.java 코드를 활용하면 얼마든지 만들 수 있다.

코드의 핵심로직은 다음과 같다.

// 자동 로그인 체크 검사
settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);
loginChecked = settings.getBoolean("LoginChecked", false);
if (loginChecked) {
    userID = settings.getString("userID", "");
    userPW = settings.getString("userPW", "");
    if (userID != null && !userID.isEmpty() && userPW != null && !userPW.isEmpty()) {
        login(userID, userPW);
    } else {
        startActivity(new Intent(getApplication(), Login.class));
        finish();
    }
} else {
    startActivity(new Intent(getApplication(), Login.class));
    finish();
}

 

protected void showJSONResult(String result) {
    if(Integer.parseInt(result) > 0){ // 로그인 정보 일치하면 idx 값 받음
        SharedPreferences.Editor editor = settings.edit();
        editor.putString("idx", result);
        Toast.makeText(Intro.this,"로그인 성공", Toast.LENGTH_SHORT).show();
        startActivity(new Intent(getApplication(), MainActivity.class));
        finish(); // 현재 Activity 를 없애줌
    } else {
        startActivity(new Intent(getApplication(), Login.class));
        finish();
    }
}

 

7. 서버 PHP 코드 작성

loginChk.php

<?php
extract($_POST);

if (isset($_POST['userID']) && isset($_POST['userPW'])) {
    require_once '../config/config.php';
    $c = new MemberClass();

    // get the user by userID and password
    $rs = $c->LoginUserChk($userID,$userPW,$deviceID);
    $user = $c->getUser($userID, $userPW);

    if ($user != false && $rs > 0) {
        // use is found
        echo $user['idx'];
    } else if ($user != false && $rs == -1) {
        // 등록된 폰이 아닙니다. 관리자에게 문의하세요!
        echo -1;
    } else {
        // user is not found with the credentials
        echo 0;
    }
} else {
    echo -2;
}
?>

 

서버 코드 값을 좀 더 세분화하면 등록된 userID가 없는 경우도 안드로이드폰에 반환할 수 있고, 패스워드가 일치하지 않는 경우를 반환할 수도 있다.

이러한 것은 개발자의 몫이다.

 

서버 PHP 와 안드로이드간에 통신 개념부분은 http://link2me.tistory.com/1110 를 참조하면 도움된다.

Bootstrap 기반 로그인 폼 및 로그인 처리(PHP PDO Class 연동) : http://link2me.tistory.com/1402 참조

[부트스트랩] 로그인 폼 및 로그인 처리 (PHP PDO Class 연동)

출처: http://link2me.tistory.com/1402 [소소한 일상 및 업무TIP 다루기]
[부트스트랩] 로그인 폼 및 로그인 처리 (PHP PDO Class 연동)

출처: http://link2me.tistory.com/1402 [소소한 일상 및 업무TIP 다루기]

 

 

 

 

다음에 시간이 되면 안드로이드 회원가입 처리부분을 해볼 생각이다.

 

회원가입이 필요한 분은 https://www.androidhive.info/2012/01/android-login-and-registration-with-php-mysql-and-sqlite 에 명시된 코드를 활용하여 만들어도 될 것이다.

이 코드는 서버에 저장된 DB 데이터를 안드로이드폰 SQLite DB에 저장하는 로직이 추가되어 있으므로 그대로 구현해도 되고, 서버에만 저장하고 저장된 결과를 간단하게 받아서 정상 가입여부만 알 수 있도록 register.php 코드를 수정해도 된다.

Bootstrap 기반 회원가입 폼 및 회원가입 처리(http://link2me.tistory.com/1403) 코드를 참조해서 xml 파일을 수정하고 작성하면 될 것이다.

서버와의 통신에 사용된 HttpURLConnection 부분은 http://link2me.tistory.com/1349 게시글과 첨부파일을 참조하면 된다.

Asynchronous Http Client 를 사용한 서버와의 통신은 http://link2me.tistory.com/1493 게시글을 참조하면 도움된다.

 

첨부파일은 코드 작성에 도움되도록 일부만 발췌하여 첨부한다.

android_Login.zip
다운로드

 

728x90
블로그 이미지

Link2Me

,
728x90

부트스트랩 로그인 폼은 https://bootsnipp.com/tags/login 에 많은 예제가 있다.

Preview, HTML, CSS 코드가 있으므로 원하는 걸 선택하거나 수정해서 사용하면 된다.

 

 

input-group 클래스를 사용해서 아이콘 모양이 나오도록 하려면

<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">

한줄을 header에 추가해준다.

 

<div class="form-group">
    <label for="password">비밀번호</label>
    <input type="password" id="password" value='' placeholder="비밀번호를 입력하세요" class="form-control" />
</div>

 <div class="input-group">
    <span class="input-group-addon"><i class="fa fa-lock"></i></span>
    <input type="password" id="password" value='' placeholder="비밀번호를 입력하세요" class="form-control" />
</div>

 

style.css 파일 추가사항

.input-group {
    margin-top: 1em;
    margin-bottom: 1em;
}

 

.login-box {
    line-height: 2.3em;
    font-size: 1em;
    color: #aaa;
    margin-top: 1em;
    margin-bottom: 1em;
    padding-top: 0.5em;
    padding-bottom: 0.5em;
}

 

로그인 폼 전체 코드

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>로그인</title>
        <!-- Bootstrap -->
        <link href="../plugin/bootstrap/css/bootstrap.min.css" rel="stylesheet">
        <!-- font awesome -->
        <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
        <!-- Custom style -->
        <link rel="stylesheet" href="../plugin/bootstrap/css/style.css" media="screen" title="no title" charset="utf-8">

        <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
        <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
        <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
        <![endif]-->
        <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <!-- Include all compiled plugins (below), or include individual files as needed -->
        <script src="../plugin/bootstrap/js/bootstrap.min.js"></script>
        <script src="../config/js/login.js"></script>
    </head>
    <body>
        <div class="container">
            <div class="row">
                <div class="col-sm-3">

                    <div class="login-box well">
                        <form accept-charset="UTF-8" role="form" method="post" action="">
                            <legend>로그인</legend>
                            <div class="input-group">
                                <span class="input-group-addon"><i class="fa fa-user"></i></span>
                                <input type="text" id="userid" value='' placeholder="E-mail을 입력하세요" class="form-control" />
                            </div>
                            <div class="input-group">
                                <span class="input-group-addon"><i class="fa fa-lock"></i></span>
                                <input type="password" id="password" value='' placeholder="비밀번호를 입력하세요" class="form-control" />
                            </div>
                            <button type="submit" id="login-submit" class="btn btn-default btn-block"/>로그인</button> <span class='text-center'><a href="" class="text-sm">비밀번호 찾기</a></span>
                            <div class="form-group">
                                <a href="registerForm.php" class="btn btn-default btn-block"> 회원가입</a>
                            </div>
                        </form>
                    </div>

                </div>
            </div>
        </div>
    </body>
</html>

 

login.js

$(function() {
    // 로그인 처리
    $('#login-submit').click(function(e) {
        e.preventDefault();
        if ($("#userid").val() == '') {
            alert('아이디를 입력하세요');
            $("#userid").focus();
            return false;
        }

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

        $.ajax({
            url : 'loginChk.php',
            type : 'POST',
            data : {
                userid : $('#userid').val(),
                password : $('#password').val()
            },
            dataType : "json",
            success : function(response) {
                if (response.result == 1) {
                    //alert('로그인 성공');
                    location.replace('../index.php'); // 화면 갱신
                    //location.reload(); // 화면 갱신
                } else if (response.result == -2) {
                    alert('입력된 값이 없습니다');
                } else {
                    alert('로그인 실패');
                }
            },
            error : function(jqXHR, textStatus, errorThrown) {
                alert("arjax error : " + textStatus + "\n" + errorThrown);
            }
        });
    });
    
});

 

loginChk.php

<?php
extract($_POST);
if (isset($_POST)) {
    require_once '../config/config.php';
    $c = new MemberClass();

    $user = $c->getUser($userid, $password);

    if ($user != false) {
        if (!isset($_SESSION)) {
            session_start();
        }
        $_SESSION['userID'] = $user['userID'];
        $_SESSION['admin'] = $user['admin'];
        echo json_encode(array('result' => '1'));
    } else {
        echo json_encode(array('result' => '0'));
    }
} else {// 입력받은 데이터에 문제가 있을 경우
    echo json_encode(array('result' => '-2'));
}
?>

 

로그인 처리에 필요한 파일은 모두 위에 열거하였다.

Class 함수에 대한 코드는 http://link2me.tistory.com/1401 에 있다.

테이블 구조는 부트스트랩 회원가입 처리하면서 칼럼 몇개를 더 추가했다.

 

PDO Class 만든 것과 연계하여 부트스트랩 회원가입과 로그인처리하는 기능을 만들고 있다.

로그인 처리하는 부분을 약간 더 손보고 관련 파일을 첨부한다.

비밀번호 찾기, 회원정보 수정 등은 첨부된 파일에 더 추가해서 직접 작성하면 될 것이다.

pdo.zip
다운로드

 

첨부파일은 기존에 올려진 첨부파일과 내용이 모두 다르다고 보면 된다.

 

다음에는 안드로이드 회원가입 처리와 로그인처리 부분을 작성해서 올릴 예정이다.

 

 

 

728x90
블로그 이미지

Link2Me

,
728x90

부트스트랩 회원가입 폼 예제다.

PDO(PHP Data Object) Class 와 연계하여 회원가입 처리를 하는 입력 폼, jQuery 코드다.



registerForm.php

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>회원가입</title>
        <!-- Bootstrap -->
        <link href="../plugin/bootstrap/css/bootstrap.min.css" rel="stylesheet">
        <!-- font awesome -->
        <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
        <!-- Custom style -->
        <link rel="stylesheet" href="../plugin/bootstrap/css/style.css" media="screen" title="no title" charset="utf-8">

        <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
        <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
        <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
        <![endif]-->
        <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <!-- Include all compiled plugins (below), or include individual files as needed -->
        <script src="../plugin/bootstrap/js/bootstrap.min.js"></script>
        <script src="../config/js/join.js"></script>
    </head>
    <body>
        <article class="container">
            <div class="page-header">
                <div class="col-md-6 col-md-offset-3">
                <h3>회원가입 Form</h3>
                </div>
            </div>
            <div class="col-sm-6 col-md-offset-3">
                <form role="form">
                    <div class="form-group">
                        <label for="inputName">성명</label>
                        <input type="text" class="form-control" id="inputName" placeholder="이름을 입력해 주세요">
                    </div>
                    <div class="form-group">
                        <label for="InputEmail">이메일 주소</label>
                        <input type="email" class="form-control" id="InputEmail" placeholder="이메일 주소를 입력해주세요">
                    </div>
                    <div class="form-group">
                        <label for="inputPassword">비밀번호</label>
                        <input type="password" class="form-control" id="inputPassword" placeholder="비밀번호를 입력해주세요">
                    </div>
                    <div class="form-group">
                        <label for="inputPasswordCheck">비밀번호 확인</label>
                        <input type="password" class="form-control" id="inputPasswordCheck" placeholder="비밀번호 확인을 위해 다시한번 입력 해 주세요">
                    </div>
                    <div class="form-group">
                        <label for="inputMobile">휴대폰 번호</label>
                        <input type="tel" class="form-control" id="inputMobile" placeholder="휴대폰번호를 입력해 주세요">
                    </div>
                    <div class="form-group">
                        <label for="inputtelNO">사무실 번호</label>
                        <input type="tel" class="form-control" id="inputtelNO" placeholder="사무실번호를 입력해 주세요">
                    </div>

                    <div class="form-group">
                    <label>약관 동의</label>
                    <div data-toggle="buttons">
                    <label class="btn btn-primary active">
                    <span class="fa fa-check"></span>
                    <input id="agree" type="checkbox" autocomplete="off" checked>
                    </label>
                    <a href="#">이용약관</a>에 동의합니다.
                    </div>
                    </div>

                    <div class="form-group text-center">
                        <button type="submit" id="join-submit" class="btn btn-primary">
                            회원가입<i class="fa fa-check spaceLeft"></i>
                        </button>
                        <button type="submit" class="btn btn-warning">
                            가입취소<i class="fa fa-times spaceLeft"></i>
                        </button>
                    </div>
                </form>
            </div>

        </article>
    </body>
</html>


join.js

$(function(){
    // 회원 가입 처리
    $('#join-submit').click(function(e){
        e.preventDefault();
        if($("#inputName").val() ==''){
            alert('이름을 입력하세요');
            $("#inputName").focus();
            return false;
        }

        var email = $('#InputEmail').val();
        if(email == ''){
            alert('이메일을 입력하세요');
            $("#InputEmail").focus();
            return false;
        } else {
            var emailRegex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
            if (!emailRegex.test(email)) {
                alert('이메일 주소가 유효하지 않습니다. ex)abc@gmail.com');
                $("#inputEmail").focus();
                return false;
            }
        }

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

        if($("#inputPasswordCheck").val() ==''){
            alert('비밀번호를 다시 한번 더 입력하세요');
            $("#inputPasswordCheck").focus();
            return false;
        }
        
        if($("#inputPassword").val()!== $("#inputPasswordCheck").val()){
            alert('비밀번호를 둘다 동일하게 입력하세요');
            return false;
        }

        if($("#inputMobile").val() ==''){
            alert('휴대폰 번호를 입력하세요');
            $("#inputMobile").focus();
            return false;
        }
        
        if($("#agree").is(":checked") == false){
            alert('약관에 동의하셔야 합니다');
            return false;      
        }
        
        $.ajax({
            url: 'register.php',
            type: 'POST',
            data: {
                name:$("#inputName").val(),
                userID:$('#InputEmail').val(),
                email:$('#InputEmail').val(),
                password:$('#inputPassword').val(),
                telNO:$("#inputtelNO").val(),
                mobileNO:$("#inputMobile").val()
            },
            dataType: "json",
            success: function (response) {
                if(response.result == 1){
                    alert('가입 완료');
                    location.replace('../index.php'); // 화면 갱신
                } else if(response.result == 0){
                    alert('이미 가입된 아이디입니다');
                } else if(response.result == -2){
                    alert('입력된 값이 없습니다');
                } else {
                    alert('등록중에 에러가 발생했습니다');
                }
            },
            error: function(jqXHR, textStatus, errorThrown){
                alert("arjax error : " + textStatus + "\n" + errorThrown);
            }
        });        
        
    });

});


register.php

<?php
extract($_POST);
if (isset($_POST)) {
    require_once '../config/config.php';
    $c = new MemberClass();
    // 동일한 userID(email) 등록되어 있는지 체크
    if ($c->isUserExisted($userID)) {
        echo '{"result":"0"}'; // echo json_encode(array('result' => '0')); 과 동일
    } else {
        // 회원 등록
        $user = $c->storeUser($userID, $name, $email, $password, $telNO, $mobileNO);
        if ($user) {// 사용자 등록 성공
            if (!isset($_SESSION)) {
                session_start();
            }
            $_SESSION['userID'] = $user['userID'];
            $_SESSION['admin'] = $user['admin'];
            echo json_encode(array('result' => '1'));
        } else {
            // 회원 등록 실패
            echo json_encode(array('result' => '-1'));
        }
    }
} else {// 입력받은 데이터에 문제가 있을 경우
    echo json_encode(array('result' => '-2'));
}
?>


코드는 100% 동작되는 코드다. 모두 테스트를 하고 올린 코드들이다.

PDO Class 는 http://link2me.tistory.com/1401 에 올려져 있다.


안드로이드 회원가입 처리 코드 예제는 https://link2me.tistory.com/1405 를 참조하시라.

728x90
블로그 이미지

Link2Me

,