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

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


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

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


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



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

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


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

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


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

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


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

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

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

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


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

예제를 살펴보자.

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


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

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

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

?>


Web 에서의 특수문자

공백

&nbsp;

>

&gt;

 <

&lt;


728x90
블로그 이미지

Link2Me

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

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

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


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

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



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


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

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


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


PHP

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 Android

PHP 와 Android 간 연동 처리 개념

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 C#

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

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

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

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

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

 

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

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

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

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

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

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


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


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

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


참고사항


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

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


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


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


require "파일명";

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

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

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


require와 include의 차이점

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

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

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

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


PHP Connect to MySQL

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

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

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


Connecting to MySQL using the legacy MySQL functions

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

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

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

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

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


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

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

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

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


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

MySQLi

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

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

 MySQL

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

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


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

MySQL

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

 MySQLi

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


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

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

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

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


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

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

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

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

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

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


=== loginChk.php ====

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

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

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

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

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

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

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

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

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

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

        } else {
            echo -1; // Phone Dismatch

        }
    }

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


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

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


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

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

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

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


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

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

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



첨부파일 (db 연결함수)

dbconn.ZIP


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

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

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



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

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

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

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


=== statClass.php ===


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

<?php

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

?>

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


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


728x90
블로그 이미지

Link2Me

,

테이블 칼럼을 조인(JOIN)을 고려하여 명칭이 가급적이면 중복되지 않게 처리하는 것이 편하고 중요하다는 걸 느끼고 있다.


// 테이블 칼럼 변경/추가

ALTER TABLE MEMBER ADD mfoneNO VARCHAR( 16 ) NULL DEFAULT NULL;
ALTER TABLE MEMBER ADD phoneBrand VARCHAR( 20 ) NULL DEFAULT NULL;
ALTER TABLE MEMBER ADD phoneModel VARCHAR( 20 ) NULL DEFAULT NULL;


ALTER TABLE MEMBER ADD `isUpdate` TINYINT( 2 ) NOT NULL DEFAULT '0' AFTER `isGanbu`;
ALTER TABLE MEMBER ADD `isDelete` TINYINT( 2 ) NOT NULL DEFAULT '0' AFTER `isUpdate`;

ALTER TABLE AccessLog CHANGE idx uid INT( 11 ) NOT NULL AUTO_INCREMENT;
ALTER TABLE AccessLog CHANGE userID LogID VARCHAR( 60 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL;
ALTER TABLE AccessLog CHANGE userNM LogNM VARCHAR( 20 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ;
ALTER TABLE AccessLog ADD date CHAR( 8 ) NOT NULL AFTER ipaddr,ADD YM CHAR( 6 ) NOT NULL AFTER date,ADD MD CHAR( 4 ) NOT NULL AFTER YM ;
update AccessLog a, AccessLog b SET a.date=concat(a.year,a.month,a.day),a.YM=concat(a.year,a.month),a.MD=concat(a.month,a.day) where a.uid=b.uid;

// 테이블 칼럼 삭제
ALTER TABLE AccessLog DROP date,DROP YM,DROP MD;


// 임시테이블에 저장하기

가장 최근에 접속한 날짜 기준으로 정렬하고 LogID 기준으로 1개씩만 추출하여 저장

INSERT INTO AccessLog_tmp
(ipaddr,date,YM,MD,year,month,day,time,OS,browser,LogID,LogNM,hit)
(SELECT ipaddr,max(date),YM,MD,year,month,day,time,OS,browser,LogID,LogNM,hit
    FROM AccessLog group by LogID order by date DESC
);

PHP 에서 코드를 작성시

$sql ="INSERT INTO AccessLog_tmp
(ipaddr,date,YM,MD,year,month,day,time,OS,browser,LogID,LogNM,hit)
(SELECT ipaddr,max(date),YM,MD,year,month,day,time,OS,browser,LogID,LogNM,hit
    FROM AccessLog group by LogID order by date DESC
)";
@mysql_query($sql);




728x90

'SQL' 카테고리의 다른 글

[Oracle] PL/SQL  (0) 2016.11.24
[MySQL] 테이블 스키마 설계 고려사항  (0) 2016.11.19
[MySQL] SQL 모음  (0) 2016.09.23
엑셀에서 INSERT 쿼리문 만들기  (0) 2016.07.29
조건별 SUM SQL  (0) 2016.07.27
블로그 이미지

Link2Me

,

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



1. 테이블 구조

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


2. phpClass 함수

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

AccessClass.zip


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

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


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


3. 사용법

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

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


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

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

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

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

?>


728x90
블로그 이미지

Link2Me

,

리눅스에서 날짜와 시간 변경 방법


수동으로 변경하는 방법

#date 월일시분년.초

로 입력해야 한다.

예시) date 102410202016.30


Time Server 와 동기화하는 방법

#rdate -s time.kriss.re.kr


H/W(하드웨어) BIOS 시간 확인

#hwclock --show


H/W BIOS 시간을 리눅스 시간으로 동기화하는 방법

#hwclock --systohc

728x90
블로그 이미지

Link2Me

,

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


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

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

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

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

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


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


// 초기화 처리

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


// 선택된 값 읽기

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

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


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

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

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



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

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


// 선택된 위치 index

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


728x90
블로그 이미지

Link2Me

,

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


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


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

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

}//end class



2. AES 사용법

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


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

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


3. 결과

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


SEED 결과

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


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


4. 비고

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





728x90
블로그 이미지

Link2Me

,

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

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


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

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


   [암호화 및 복호화 과정]


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


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

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


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

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

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


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


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


사용법

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

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


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


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

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

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

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


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


728x90
블로그 이미지

Link2Me

,

    Cells(3, 1).Select
    If ActiveSheet.AutoFilterMode = False Then
        Selection.AutoFilter  '// 상시 필터 적용 옵션
    End If



Sorting 처리 명령어

    ActiveSheet.AutoFilter.Sort.SortFields.Clear
    ActiveSheet.AutoFilter.Sort.SortFields.Add Key:= _
        Range("C3:C" & endRow), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
    With ActiveSheet.AutoFilter.Sort
        .Header = xlYes
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With



찾고자 하는 내용 검색

    ActiveCell.Select
    Cells.Find(What:="검색어", After:=ActiveCell, LookIn:=xlFormulas, _
        LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
        MatchCase:=False, MatchByte:=False, SearchFormat:=False).Activate
    ActiveCell.Offset(-1, 0).Range("A1").Select


설명 : ActiveCell 에서 "검색어" 를 입력하면 결과가 검색된다. 가령 찾고자 하는 게 "업무" 라고 한다면, 검색어 대신에 업무로 변경하면 된다.

ActiveCell.Offset(-1, 0).Range("A1").Select 의 의미는 ActiveCell 에서 Offset(-1,0) 즉 Offset(행,열) 만큼 이동하라는 의미이므로 -1 은 이전행, 0 열은 현재 셀에서 열의 이동은 없이 바로 위의 셀을 가리키게 된다.


728x90
블로그 이미지

Link2Me

,

윈도우 10 에서 잠시 자리를 비울 때 화면 잠금을 하고 싶을 때는

윈도우키 + L 을 누르면 된다.


먼저 화면잠금 설정이 되어 있어야 한다.


728x90
블로그 이미지

Link2Me

,

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


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


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

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

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



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

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


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


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


먼저 jQuery 를 추가해야 한다.

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


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


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

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

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

});

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



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

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

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

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


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

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


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

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



728x90
블로그 이미지

Link2Me

,

베트남 여행 준비물

여행 2016. 10. 8. 12:56

9월 추석연휴에 베트남 여행을 다녀왔는데 잊어버릴까봐 작성해둔다.

베트남 여행은 동호회 회원이 베트남을 여러번 다녀왔다고 해서 호기심이 발동하여 혼자 다녀왔다.

저가항공을 이용하면 2명이 100만원으로 일주일간 여행할 수 있다고 한다.

패키지 여행, 선택관광, 쇼핑 등을 하게 되면 3박4일인데도 100만원(1인기준)이 훌쩍 넘어가게 된다.

음식때문에 고생할 일은 없다.


베트남 여행 준비물


여권

- 여권은 나를 식별해주는 신분증이므로 반드시 챙겨야 한다.

- 패키지 여행을 가면 베트남은 사회주의 국가라서 현지가이드가 여권을 전부 걷었다.

   여행 마지막날 공항으로 가는 버스에서 돌려준다.


신용카드

- 신용카드는 1개 가져가면 된다.

- 쇼핑 가게에 들를 때 필요하더라.

- 쇼핑 가게에서 구입 품목은 사향 족제비 커피, 라텍스, NONI 등이다.

- NONI는 현지가이드가 데려가는 가게에서 절대 구입하지 말라.

  베트남 공항 면세점에서 구입하면 20 ~ 24달러주면 구입할 수 있다.

- 사향 족제비 커피(위즐)도 공항 면세점에 있던데 커피도매점이란 곳에서 구입한 것보다 저렴하다.
  공항면세점에서 커피를 구입해서 비교하지 못한 점이 아쉽다.

  사향 족제비 커피는 먹어본 사람 대다수가 만족한다. 사향 고양이 커피(발리) 보다 더 맛이 좋다.

  다시 베트남을 간다면 사향 족제비 커피를 좀 더 많이 구입하고 싶다.


현금

- 현금은 미리 은행에서 달러로 환전한다. 공항에서 환전하면 손해를 본다.

  특히 베트남 현지화폐로 환전시 12% 정도 손해를 보므로 무조건 달러로 환전한다.

  여행시 베트남 돈은 거의 사용하지 않고 달러를 사용한다고 보면 된다.

- 숙소에서 1달러, 짐을 날라다 주면 1달러

- 베트남 화폐는 단위가 동이다. 1달러 = 1동 X 20,000 이라고 보면 된다.



- 병원이나 약국이 많지 않다고 하므로 비상약은 꼭 준비해야 한다.

- 약, 통/제, 제, 드, 모기퇴치제(선택)

- 상처가 생길 경우를 대비해서 연고도 준비하는게 좋다.

- 피곤을 풀어줄 종합비타민제를 준비하는 것도 좋다.

  여행중에는 면역력이 약해질 수 있으므로 비타민을 먹어주면 감기 예방에 도움이 된다.

  하롱베이 여행시 배를 타는데 너무 천천히 움직이기 때문에 배멀미를 할 염려는 안해도 된다.


우산

- 우산(3단 접이식)과 1회용 비옷은 준비하여 가지고 가는게 좋다.

- 패키지 여행이라면 굳이 준비 안해도 비옷을 준다.

- 태양이 뜨거우므로 양산을 준비하는 것도 좋다.

  현지에서 구입할 경우 3~4천원이면 구입할 수 있다.


- 음식점에서는 물을 준다.

- 관광버스 안에 물을 준비하여 언제든지 먹을 수 있게 해준다. (패키지 여행)


휴대폰 로밍

- 휴대폰 로밍을 해도 베트남은 3G 접속이라 인터넷 속도는 엄청 느리다.

- 충전기는 필수, 보조배터리는 반드시 챙겨가는게 좋다.


멀티어댑터 

- 멀티어댑터는 준비하지 않아도 된다.

- 전기는 220V를 사용한다.


- 날씨가 따뜻하므로 대부분 반팔을 입고 여행을 한다.

- 잠바를 준비해서 비가 올 때 걸치면 좋다.

- 속옷, 양말, 반바지, 긴바지, 반팔

- 운동화, 샌들 모두 챙기는 것이 좋다.



기타 준비물

- 마스크

  베트남은 교통체계가 체계적이지 못하다. 도로에 자동차와 오토바이가 혼재되어 다닌다.

  최고속도 시속 40km, 80km 다. 우리나라처럼 시속 100km 이상으로 달리는 것은 절대 기대하지 말라.

  매연이 많아서 마스크를 준비하는게 좋다. 하노이는 오토바이가 700만대라고 한다.

- 베트남 전통모자 '농'은 1달러에 구입할 수 있다.

- 배낭은 가벼운 짐을 넣고 다닐 수 있는 백팩으로 준비한다.

  배낭은 보통 관광버스에 놓고 다니므로 더 가볍게 가지고 다닐 수 있는 걸 준비하면 좋다.


공항

- 항공권발권은 요즈음은 대부분 무인발급기를 이용한다. 줄을 서서 시간을 낭비할 필요가 없다.

  공항에서 대기하는 시간을 줄일 수 있다.


베트남 여행회화

- 여행시 가장 기본이 되는 현지어 한두개 정도는 알아두어야겠더라.

- 안녕하세요 : 씬 짜오

  안녕하십니까? : 짜오 아인

  감사합니다 : 씬 깜언

  미안합니다 : 또이 씬 로이 아인

  수고하세요 : 짜오 아잉

  기뻐요 : 부이 믕

  오늘 너무 재미었었어요 : 투 비 꾸어

  여보세요 : 알로

  버스 : 쌔빗

  택시 : 딱시

  국수 : 퍼~어 ~어

  COM : 껌 → 밥이란 뜻이며, 쌀밥을 파는 식당이란 뜻이다.

  이게 뭐예요? : 까이나이 까이지

  얼마입니까? : 바우 니우  or 바우 니에우 띠엔?

  비싸요 : 막꽈~ or 닷꽈~

  안사요 : 컹 무어

  다 팔렸습니다 : 헷 로이

  말씀 좀 묻겠습니다만... : 아인 쪼 또이 호이

  화장실이 어디 있습니까? : 오 당 끼어 끼어

  당신은 매우 친철합니다 : 아인 뜨 떼 꽈

  좀 천천히 이야기해주세요 : 씬 아인 노이 짬

  즐거운 여행되시길 바랍니다 : 쭉 렌 등 마이 만


  요즘 더워 겁나게 : 자오 나이 농 낑 쿵 → 자오 나이(요즈음) 농(덥다) 낑 쿵(겁나게)

  네. 요즘 한국은 네트남만큼 더워요 → 네. 요즘 한국은 더워요 베트남 만큼 : 벙. 자오 나이 한 꾸옥 농 방 비에트남


 

728x90
블로그 이미지

Link2Me

,

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

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


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

728x90
블로그 이미지

Link2Me

,

맥북 프로를 서비스센터에서 하루만에 케이블 교체하고 OS 설치했다고 교체하라고 연락이 와서 찾아왔다.
BootCamp 를 찾지 못해서 서비스센터에 전화해서 bootcamp 위치를 찾았다.
맥북이긴 한데 Android 어플 개발 연습을 윈도우10 상에서 하다보니 MAC 기능을 제대로 모른다.

Android 어플 개발환경 설정하는데 제대로 돌아가지 않았던 기억 이후로

맥북은 bootcamp 상에 설치된 windows 10 에서 주로 구동되었다.

bootcamp 상에서 윈도우10 설치를 위한 준비를 했다.

1. windwos 10 ISO 파일을 MAC OS 다운로드 디렉토리에 복사를 한다.

2. bootcamp 에서 windows 10 ISO 파일 경로를 지정한다.

3. 8G 이상되는 USB 메모리를 준비하여 꼽는다.

   진행되면서 USB 메모리에 있던 내용은 전부 포멧이 된다.

   포멧이 되고 나서 windows 10 파일 정보를 USB에 복사를 하는 거 같다.

   시간이 한참 걸린다.

4. 자동 재부팅이 되면서 윈도우 10 설치가 끝난다.

   여기까지는 RJ45 케이블을 연결하여 작업을 했다. 빠른 속도로 설치 작업을 하기 위해서다.

5. Visual Studio 2015 Community 버전을 설치하고 나서

   Android 설치파일은 다시 설치하지 않고 기존 HDD에서 그냥 복사를 했다.

   JDK 파일만 인스톨하고 나서 Path 만 잡아주면 된다.

6. WiFi 접속을 위한 Driver 설치

   USB에 복사된  BootCamp\Drivers\Broadcom\BroadcomWirelessLegacy64.exe 파일을 찾아서 실행하면

   자동으로 WiFi 접속 환경이 설정된다.

   이제 WiFi 접속 가능한 걸 찾아서 패스워드만 지정하면 된다.

   이후부터는 자동으로 모바일 접속이 가능하다.


728x90
블로그 이미지

Link2Me

,

오늘 서비스센터에 전화해서 설치 시도를 했는데 안되어서 결국 맥북 프로를 수원 서비스센터에 맡겼다.

오전에 여성 상담원이 수원에 맥북 서비스 센터가 없단다. ㅠㅠㅠ

그러면서 동탄 이마트에 있는 서비스센터 위치를 알려준다.


오후에 서비스센터에 전화해서 ADATA SSD 설치하는 방법을 문의했다.


Option + Command + R키 + Power 키를 누르면 인터넷 접속 모드로 진입한다.

한참을 기다리니까 언어 선택하는 화면이 나오는데 더이상 진행이 안된다.

그러니까 상담원이 H/W 문제가 있는거 같다면서 서비스센터에 맡겨보란다.

개인별 고유상담번호가 있다면서 상담번호까지 알려준다.

다음에 이 번호를 대고 상담하면 그동안의 상담기록을 다른 상담원이 열람할 수 있다고 한다.


설치하는 방법을 문의한  것을 적어둔다.

도구 Disk Utility --> 파티션 --> 포멧(지우기0 --> Utility 닫기

MAC OS 다시 설치

MAC OS 로 부팅하고 나서 bootcamp 로 윈도우 설치 파티션을 나누면 된다고 한다.


인터넷 검색해도 정보가 정확한지 알 수가 없어서 기록을 해둔다.


수원애플서비스센터 위치 : 아주대 삼거리 근처 UBASE (3층 위치)

http://svc.ubase.co.kr

번지 주소 : 수원시 팔달구 우만동 92-1 블루하우스 3층

도로명주소 : 수원시 팔달구 중부대로 223번길 4 블루하우스 3층


※ 모든 애플 서비스센터에서 맥북 수리가 가능한 것은 아니다. 따라서 확인을 해야 한다.


Command + Shift + Option + R

지구본이 돌고 잠시 후 macOS 유틸리티 나온다.

초기화를 위해서 디스크 유틸리티 선택한다.

왼쪽 SSD 디스크를 선택하고 지우기를 선택하고 MAC OS Extended (Journaled) 를 선택한다.




728x90
블로그 이미지

Link2Me

,

추석 연휴에 Power 전원 스위치를 OFF 했더니 컴퓨터 부팅이 안된다.

플렉스터(Plextor) SSD 부팅정보 등이 완전 날라가 버렸다.

5년 보장한다고 하길래 정말 제품 좋은가 보다 하고 구입했다. 그것도 Pro 로 비싸게 주고 말이다.

그런데 비정상적인 전원 OFF 한번 발생했다고 SSD 가 완전 박살나 버리다니 !!!!

5년 보장이라고 하길래 중요한 데이터를 SSD 에 넣어 두면서 작업을 했는데 한방에 날라가 버렸다.

구글링을 하여 외국 복구 소프트웨어를 하나 찾아서 90불 정도 주고 찾아봤더니 복구 하나도 못한다. 우라질~~~

플렉스터 SSD 믿고 구입했다가 출혈이 이만저만이 아니다.

문제는 안에 들어 있는 데이터인데 그마나 좀 다른 곳에 저장되어 있는 것이 있는 거 같아서 다행이긴 하다.

하지만 내가 기억하지 못하는 것도 있으니 ㅠㅠㅠㅠ


플렉스터 서비스 센터에 전화를 했더니 개소리를 한다.

www.danawa.com 에서 플렉스터 해당 제품 검색해보니 댓글이 수두룩하다.

플렉스터 CDROM 명성이 좋아서 구입했던 건데 전원 문제에 이렇게 약하다니...

삼성 SSD 컴퓨터는 멀쩡하다.

데이터는 완전 포기하고 그냥 포멧하니까 포멧은 잘된다.

좋은 제품을 쓰는 이유는 데이터의 안정성을 위해서다.

그런데 이런 걸 하지도 못하면 저가 제품 사용하는 거랑 다를게 하나도 없다.


이번 계기로 SSD 에 중요데이터는 보관하지 않기로 했다.

SSD는 아예 외장형으로 구입해서 데이터를 보관하는게 낫겠다 싶다.

SSD 제거할 때에는 반드시 안전제거를 사용하는게 좋다.

데이터 날라가는 현상을 겪고 보니 데이터 이중으로 백업하는게 정말 중요하다고 생각되었다.


SSD를 비싸게 주고 살 필요가 있나 싶다.

테크노마트 단골집(10년 넘게 애용)하는 곳에서 ADATA SP900 SSD 256GB를 9만원대에 구입했다.

danawa.com 에서 검색해보니 8.7만원 정도 가는거 같다.

기다리는 시간 등을 고려하면 1만원 추가되는 것은 감수하고 구입했다.


윈도우7 설치했다가 업그레이드가 안먹는다.

정품 CD인데도 메인보드 교체를 해서인지 안된다.

예전에 정품 CD라고 해도 메인보드 기준으로 정품을 인식한다는 말을 들었다.

윈도우 7 에서 Visual Studio Community 2015 설치가 진행되다 멈춤 상태가 지속되어서다.

윈도우 7 정품 인증이 안되어서 보안패치 등이 설치가 안되어서 그런가보다.

몇번 시도를 해도 안된다.


이번에 업그레이드 하면서 정품 CD 인데도 안되길래 윈도우 10으로 다시 설치를 했다.

윈도우 10 에서는 Visual Studio Community 2015 설치가 잘 된다.


728x90
블로그 이미지

Link2Me

,

사무실에서 개발용으로 사용하는 개인 맥북에 윈도우 10을 설치하고 사용하는데 갑자기 하드디스크 인식을 못한다.

그래서 좀 된 맥북에 하드디스크를 연결하여 확인해보니 HDD는 멀쩡하다.

그 하드디스크에 있던 HDD를 연결하니까 인식이 된다.

구글링을 해서 검색해보니 케이블 불량이라 교체해야 된다는 내용도 있고 해서 애플 서비스 센터에 전화해서 확인해보니 몇가지 조치 사항을 알려준다.


SMC 재설정 : Shift + Control + Option + Power 버튼


PRAM 설정 : Option + Command + R + P + Powere 버튼

   이걸 하면 초기설정 값으로 변경되나 보다.

   부팅음이 3번 들리고 나서 눌렀던 버튼들에서 손을 OFF 했다.


복구파티션 : Option + Command + R키


MACBOOK Pro 가 잘 인식이 안되어 이번 연휴 첫날 테크노마트 5층에 가서 점검을 받아보니 멀쩡하단다.

제품이 케이블 불량이 많은 제품이라고 한다.

케이블 불량 교체를 해야 하는데 나중에 다시 찾으러 가기가 귀찮아서 안맡기고 그냥 가져왔다.

테크노마트에서 구입한 ADATA SSD 를 장착하고 인식하려고 하니까 어떻게 하는 줄을 몰라서인지 하다가 포기했다.


사무실이 가까운 수원 애플 서비스 센터에 의뢰를 해야 나중에 찾아올 때 시간이 덜 걸릴거 같아서 가져온 건데 역시나 케이블 불량이 맞는지 인식이 잘 안된다.



728x90
블로그 이미지

Link2Me

,

컴퓨터를 업그레이드 하고 나니 윈도우 재설치를 해야 하고, eclipse를 사용하기 위해서 JDK 를 재설치해야 한다.

JDK 는 기존에 받아 두었던 걸 그냥 설치하면 된다.

경로 설정을 해야 하는데 윈도우 10 에서 경로설정을 위한 화면 찾기가 쉽지 않다.


1. 시작 --> 설정을 눌러준다.





c:\Program Files\Java\jdk1.8.0_91\bin;



설치후에 경로설정을 해줘야 eclipse 에서 제대로 인식한다.



SSD 를 새로 구입해서 설치하는 번거로운 문제, 그리고 설치용량이 매우 크기 때문에 HDD에 설치를 하고 패스만 새로 잡아주는 방식을 택했다.

그런데 문제는 Dirve 가 변경되었다. E 드라이브에서 다른 드라이브로 변경되어서 다시 원래의 드라이브로 변경해야 한다.

몇번의 재설치를 하면서 안전하게 HDD에 설치하는게 좋다는 걸 알게되었다.

그리고 드라이브 경로만 제대로 잡아주면 eclipse 를 재설치하지 않아도 된다.


2. 디스크관리



마우스 우클릭하여 드라이브를 변경하면 된다.



eclipse를 실행하니 에러없이 잘 읽어들인다. 성공!!!

728x90
블로그 이미지

Link2Me

,