'분류 전체보기'에 해당되는 글 99건

  1. 2019.01.22 AJAX with CodeIgniter
  2. 2019.01.19 jQuery AJAX example
  3. 2019.01.14 CI Session example
  4. 2019.01.14 Query in CI Model
  5. 2019.01.12 WAMP Setup
  6. 2019.01.12 CodeIgniter Login example
  7. 2019.01.05 MySQL Transaction
  8. 2019.01.05 MySQL 계층구조 질의 1
  9. 2018.12.26 Game Data DB Input
  10. 2018.12.22 VaRest Plugin Intro 2
PHP/CI AJAX2019. 1. 22. 16:45

CodeIgniter에서 AJAX 사용하기


사원번호를 클릭할 때마다 jQuery를 이용한 AJAX 요청이 CodeIgniter에 전달되면 콘트롤러와 모델, 뷰 컴포넌트를 거쳐 사원정보를 JSON 포맷으로 응답한다

응답을 수신한 success 함수는 JSON 오브젝트를 사용하여 각 데이터가 표시될 위치에 출력하는 예이다


실행환경

웹서버 상의 CodeIgniter 루트에 css, js  등의 리소스 폴더를 생성하고 css 폴더에는 스타일시트 파일을 저장하고 js 폴더에는 jQuery.min.js  등의 자바스크립트 파일을 복사해둔다


emp 테이블

 - empno INT                # 사번

 - ename VARCHAR(20)   # 이름

 - dname VARCHAR(20)   # 부서

 - sal INT                      # 급여

 - pic VARCHAR(20)        # 사진파일명



웹서버의 폴더 구조

- www : 웹서버 루트 폴더

   - ciBoard 폴더 : CodeIgniter 구성파일 저장

      - css : css 파일 저장

      - js : jQuery 파일 저장

      -application/controllers/Emp.php

      -application/models/EmpModel.php

      -application/views/emp.html, emp_json.php



콘트롤러 생성

ciBoard/application/controllers/Emp.php  (요청 url : http://localhost/ciBoard/emp )

<?php  

 defined('BASEPATH') OR exit('No direct script access allowed');


 class Emp extends CI_Controller 

 {  

    public function __construct() {

         parent::__construct();

         $this->load->helper('url');

    }

    

    public function index() {

        $this->load->view("emp.html");  

    }


    function getInfo($num) {

        $this->load->model('EmpModel');

        $data = $this->EmpModel->getInfo($num);

        $row = array('row'=>($data->row_array()));

        $this->load->view('emp_json', $row);

    }

 }



모델 클래스 생성

ciBoard/application/models/EmpModel.php

<?php  

 defined('BASEPATH') OR exit('No direct script access allowed');

 

 class EmpModel extends CI_Model

 {

     public function __construct() {

         parent::__construct();

     }

     

     public function getInfo($num)

     {

         $sql = "SELECT * FROM emp WHERE empno=$num ";

         $result = $this->db->query( $sql );

         return $result;

     }

 }


뷰 파일

ciBoard/application/views/emp.html (요청 url : http://localhost/ciBoard/emp )

<!DOCTYPE html>

<html>

    <head>

        <title>사원정보 페이지</title>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <script src="js/jquery-3.3.1.min.js"></script>

        <script>

            $(function(){

              alert('jQuery Ready!');

            });

            function requestEmpInfo(num) {

                $.ajax({

                    url : '<?php echo base_url();?>emp/getinfo/'+num,

                    method: 'post',

                    data: { 'num' : num},

                    dataType : 'json',

                    success : function(res) {

                        //alert(res.ename);

                        $('#ename').html(res.ename);

                        $('#empno').html(res.empno);

                        $('#dname').html(res.dname);

                        $('#sal').html(res.sal);

                        $('#pic').html('<img src=\'images/'+res.pic+'\'>');

                    },

                    error: function(xhr, status, error){

                        console.log(xhr.status + ', '+xhr.responseText +', '+status+', '+error);

                        alert(status);

                    }

                });

            }

        </script>

        <style>

            img { width: 200px; height:200px; }

        </style>

    </head>

    <body>

        <h3>사원정보</h3>

        <div>이름 : <span id="ename"></span></div>

        <div>번호 : <span id="empno"></span></div>

        <div>부서 : <span id="dname"></span></div>

        <div>급여 : <span id="sal"></span></div>

        <div>사진 : <span id="pic"></span></div>

        <div>

            [<a href="javascript:requestEmpInfo(11);">11</a>]

            [<a href="javascript:requestEmpInfo(12);">12</a>]

            [<a href="javascript:requestEmpInfo(13);">13</a>]

            [<a href="javascript:requestEmpInfo(14);">14</a>]

            [<a href="javascript:requestEmpInfo(15);">15</a>]

        </div>

    </body>

</html>




JSON 문자열을 웹브라우저에 출력하는 뷰 콤포넌트

ciBoard/application/views/emp_json.php

<?php

echo json_encode($row);


Posted by cwisky
Phaser/jQuery AJAX2019. 1. 19. 19:08

jQuery의 AJAX 를 사용한 서버측 PHP 간의 비동기 요청과 응답처리 예


jQuery 설정 및 테스트

<!doctype html>

<html>

<head>

<meta charset="utf-8">

<title>HTML Example</title>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

<script>

$(document).ready(function(){

  $("p").click(function(){

    $(this).hide();

  });

});

</script>

</head>

<body>

    <p>여기를 클릭하세요

</body>

</html>



위의 방법보다 더 간단하게 jQuery 의 ready 함수를 실행하는 방법

<script>

$().ready(function(){

  $("p").click(function(){

    $(this).hide();

  });

});

</script>

가장 간단한 jQuery ready 함수 호출 방법

$(function(){

  $("p").click(function(){

    $(this).hide();

  });

});


jQuery의 AJAX 기능을 사용한 서버측(PHP)과의 비동기 통신

<!doctype html>

<html>

<head>

<meta charset="utf-8">

<title>AJAX Example</title>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

<script>

$().ready(function(){

  $("p").click(function(){

    $(this).hide();

    $.ajax(

        {

            url : 'server.php',

            data : {name:'smith', phone:'010-9578-2154'},

            dataType : 'json',               /*html, text, json, xml, script*/

            method : 'post',

            success : function(res){

                alert(res.name + ', '+res.phone);

                $('p').show();

            },

            error : function(xhr, status, error){

                alert(xhr.status);           // 에러코드(404, 500 등)

                alert(xhr.responseText); // html 포맷의 에러 메시지

                alert(status);                // 'error'

                alert(error);                 // 'Not Found'

            }

        }

    );

  });

});

</script>

</head>

<body>

    <p>AJAX Request Test

</body>

</html>



위의 요청에 응답하는 서버측 PHP 스크립트(server.php)

<?php

    $name = $_REQUEST['name'];

    $phone = $_REQUEST['phone'];

    

    $arr['name'] = $name;

    $arr['phone'] = $phone;

    

    $json_str = json_encode($arr);

    echo $json_str;

?>




Posted by cwisky
PHP/CI Session2019. 1. 14. 16:07

CodeIgniter 에서 Session 사용하기


원문참조 : https://www.codeigniter.com/user_guide/libraries/sessions.html



PHP 표준 방법으로 세션을 다루는 예

session_start();


$_SESSION['id'] = 'Smith'; // 세션에 'id' 란 키를 이용해서 값을 저장한다

$_SESSION['id'] = 'Andy';  // 세션에 'id' 란 키로 저장된 값을 업데이트한다

unset ( $_SESSION['id'] );  // 세션에 'id' 란 키로 저장된 값을 제거한다


if ( empty ( $_SESSION['id] ) ) // 세션에 'id' 란 키로 저장된 값이 있는지 확인한다

{

     echo "로그인 후에 이용해 주세요";

)



콘트롤러 클래스 작성

application/controllers/ 아래에 다음과 같은 테스트용 클래스를 작성한다

<?php

defined('BASEPATH') OR exit('No direct script access allowed');


class SampleController extends CI_Controller 

{

    public function __construct() 

    {

        parent::__construct();

        $this->load->library('session'); // 생성자에서 세션을 초기화하면 이후 모든 페이지에서 사용가능

    }

    public function index()

    {

            $this->load->view('sample.php');

    }

}

?>


위와 같이 콘트롤러에서 세션을 초기화하지 않고 뷰에서 초기화하는 경우 오류가 발생한다

콘트롤러에서 세션의 초기화 뿐만 아니라 세션 관련 다른 모든 작업이 가능하다


콘트롤러에서 연결할 뷰 작성

application/views/ 아래에 다음과 같은 뷰(sample.php)를 생성한다

<?php


// 콘트롤러에서 초기화된 세션은 아래처럼 사용가능

//$this->session;


// PHP에서 세션에 값을 저장하려면

$_SESSION['key'] = "value";


// CodeIgniter에서는 ...

$this->session->set_userdata('key', 'value'); 


// 세션에 배열을 저장하려면..

$data = array(

    'id'=>'userid',

    'name'=>'username',

    'phone'=>'010-2547-9541'

);


$this->session->set_userdata($data);


foreach ($data as $key => $value) {

    echo $key. " : ". $value."<br>";

}


//PHP에서 세션에 저장된 값을 제거하려면..

unset($_SESSION['key']);


//CI에서 키를 이용하여 세션의 값 제거

$this->session->unset_userdata('key');


// CI에서 세션에 저장된 배열을 제거

$this->session->unset_userdata($data);


// CI에서 세션에 저장된 값을 가져와서 사용하는 예

$data = array(

    'id'=>'userid',

    'name'=>'username',

    'phone'=>'010-2547-9541'

);


$id = $this->session->userdata('id');


echo "이용자의 ID :".$id . "<br>";

?>



다른 페이지에서 로그인을 거쳤는지 확인하는 예

아래에서 사용된 base_url()함수는 url 헬퍼에 있으므로 콘트롤러에서 $this->load->helper('url'); 부분이 추가되어야 한다

<?php

    if( empty($_SESSION['id']) ) {?>

    <script>

        alert('로그인 후에 이용할 수 있습니다');

        location.href = '<?php echo base_url();?>logincontroller';

    </script>

<?php    

        return;

    }

?>



CodeIgniter 에서 실행 확인하기

웹브라우저에서 다음과 같이 요청한다

http://localhost/index.php/SampleController

http://localhost/index.php/sampleController

http://localhost/index.php/samplecontroller



요청 URL에서 콘트롤러 이름을 간결하게 사용하기

application/config/routes.php 파일의 끝에 다음과 같은 내용을 추가한다

route['sample'] = 'SampleController';

위와 같이 설정하면 웹브라우저에서 요청 URL은 다음과 같이 할 수 있다

http://localhost/index.php/sample

물론 원래의 URL도 사용할 수가 있다

Posted by cwisky
PHP/CI Model2019. 1. 14. 15:13

CodeIgniter 의 모델 콤포넌트에서 DB 다루기



application/config/database.php 에서 데이터베이스 정보 입력

$db['default'] = array(

'dsn' => '',

'hostname' => 'localhost',

'username' => 'root',

'password' => '',

'database' => 'sample_db',

'dbdriver' => 'mysqli',

        .........



Model  생성자 안에서 DB 초기화

$this->load->database();



데이터베이스 자동 연결

혹은 application/config/database.php에 자동로드 설정하면 위의 코드는 필요 없다

$autoload['libraries'] = array('database', 'session');



다수개의 행을 가져와서 배열로 변환

$result = $this->db->get('테이블명');

$data['rows'] = $result->result_array(); //각행이 연관배열로 표현된 2차원 배열 리턴

$data['title'] = 'Members List';



한행을 검색하여 가져오기

$result = $this->db->get_where('테이블명', array('id'=>11));

//위의 문장은 아래처럼 2개문장으로 표현할 수 있다

$this->db->where('id', 11);

$result = $this->db->get('테이블명');

$data = $result->row_array(); // 한행을 연관배열로 리턴, NULL



get_where() 함수

get_where([$table = ''[, $where = NULL[, $limit = NULL[, $offset = NULL]]]])

Parameters:

$table (mixed) – The table(s) to fetch data from; string or array

$where (string) – The WHERE clause

$limit (int) – The LIMIT clause

$offset (int) – The OFFSET clause



웹브라우저 파라미터를 추출하여 DB에 저장하기

$this->load->helper('url');

$title = $this->input->post('title');

$content = $this->input->post('content');

$data = array(

'title'=>$title,

'content'=>$content

);

$this->db->insert('테이블명', $data); //성공시 TRUE, 실패시 FALSE



특정 행의 컬럼 값을 변경하기

$data = array(

'title'=>$title,

'content'=>$content

);

$this->db->where('id', $id);

$this->db->update('테이블명', $data);  //성공시 TRUE, 실패시 FALSE

$this->db->update('테이블명', $data, $where);



특정 행 삭제

$this->db->where('id', $id);

$this->db->delete('테이블명'); // 실패시 FALSE

$this->db->delete('테이블명', $where);



SQL 문장을 직접 사용할 때

$this->db->query($sql); 

$this->db->query($sql, sql문장에 전달될 파라미터배열);


//$sql이 read type(SELECT)이라면 결과값 리턴

//$sql이 write type(INSERT,UPDATE,DELETE)이라면 TRUE|FALSE 리턴




Posted by cwisky
PHP/WAMP2019. 1. 12. 20:37

WAMP 다운로드 및 설치

https://sourceforge.net/projects/wampserver/files/

설치 중에 웹브라우저, 에디터를 선택하는 창에서 [아니오]를 선택하고 설치를 진행한다


WAMP 서버 실행

윈도우 시작 > wampserver64 선택

작업표시줄 우측에 local server 아이콘이 보이는지 확인

아이콘 클릭 > Start All Services 선택 > 아이콘이 초록색으로 변하면 실행완료

웹브라우저 주소창에 http://localhost 입력 > 엔터

웹브라우화면에 wamp 서버 초기화면이 표시되는지 확인한다

phpMyAdmin 링크를 클릭하여 데이터베이스 로그인 화면에서 다음과 같이 입력한다

아이디 :  root,      암호: 비어둔다

데이터베이스 관리화면에서 데이터베이스나 테이블을 생성하고 관리할 수 있다

참고로, wamp의  MySQL root 계정 암호는 설정되지 않은 상태로 설치된다


MySQL Workbench 설치 및 실행

MySQL Workbench 를 설치하고 실행하면 자동으로 로컬 MySQL root 계정 접속을 생성하여 첫화면에서 제시하므로 해당 접속을 클릭하여 바로 데이터베이스 관련 작업을 할 수 있다


PHP 에디터로 Apache NetBeans 설치하기

http://netbeans.apache.org/

Download

Downloading 항목 아래에서 

Binaries: incubating-netbeans-10.0-bin.zip ( SHA-512, PGP ASC)


넷빈즈 설치 후 실행/프로젝트 생성

화면 왼쪽 컬럼에서 [Projects] 탭에서 마우스 우측 > New Project >

 PHP > PHP Application > Project Name > 

Sources Folder : wamp/www

PHP Version : 5.6

Run As : Local Web Site

Project URL : http://localhost

PHP Frameworks:지정안함

Finish

프로젝트가 생성되면 왼쪽 [Projects] 탭에 소스폴더의 모든 내용이 표시된다


넷빈즈에서 웹브라우저 지정하기

넷빈즈 툴바에서 지구본 모양의 웹브라우저 아이콘을 선택하면 웹브라우저를 선택할 수 있다

여기서는 [Chrome] 을 선택한다

프로젝트 Source Files 안에 마우스 우측 > New > PHP File 선택 > 파일이름 지정 > Finish

생성된 파일명을 더블클릭하여 에디터를 열고 다음과 같이 내용을 입력한다


hello.php

<html>

    <head>

        <title>PHP로 나만의 웹사이트 만들기</title>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

    </head>

    <body>

    <?php

        echo "Hello World<br>";

        echo "안녕하세요?<br>"

    ?>

    </body>

</html>




넷빈즈에서 PHP 파일을 웹브라우저로 확인하기

PHP파일은 아파치 웹서버에서 실행되므로 먼저 wamp 서버를 실행한다

넷빈즈 프로젝트에 작성된 파일명 위에서 마우스 우측 > Run 선택

지정된 웹브라우저가 실행되고 웹서버에 해당파일을 요청하여 웹브라우저에 표시되는지 확인한다



Sublime Text 3 다운로드/설치/실행 (필요하다면 설치)

File > Open Folder > 를 누르고 WAMP/www 폴더를 선택

에디더 왼쪽에 루트폴더가 표시되면 마우스 우측을 누르고 New File, New Folder등을 선택하여 작업을 할 수 있다



CodeIgniter 다운로드/압축해제/ 복사

Download https://codeigniter.com/download

WAMP/www/ 아래에 ciBoard 폴더를 생성한다 (임의의 폴더명)

CodeIgniter 압축해제 후 application폴더와 함께 있는 다른 폴더와 파일들을 모두 복사하여 WAMP/www/ciBoard/ 폴더 안에 붙여넣는다

웹브라우저에서 http://localhost/ciBoard 으로 접속하여 화면에 아래와 같은 환영 메시지가 출력되는지 확인한다

Welcome to CodeIgniter!


Posted by cwisky
PHP/CI Login2019. 1. 12. 13:00

CodeIgniter를 이용한 로그인 예제


https://www.webslesson.info/2016/10/codeigniter-simple-login-form-with-sessions.html


application/config/autoload.php 파일에서 다음과 같은 부분을 편집한다


autoload.php

$autoload['libraries'] = array('database', 'session');



.htaccess 파일 생성 및 편집

요청 URL 중간에 들어가는 index.php를 제거하기 위함

base_url 이 http://localhost/ciBoard 라면...


wamp/www/ciBoard/.htaccess

RewriteEngine On

RewriteBase /ciBoard/

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ index.php?/$1 [L]


데이터베이스 테이블 생성


users ( id, pass, name )



서버측 폼검증 참조 : https://codeigniter-kr.org/user_guide_2.1.0/libraries/form_validation.html



Login 콘트롤러 클래스 생성


application/controllers/Login.php 생성 (클래스이름과 파일이름은 동일해야 함)

콘트롤러 이름은 첫자를 대문자로 한다.

요청 URL에 콘트롤러 이름이 들어갈 때는 첫자를 소문자로 표시한다


예를 들어, 콘트롤러 이름이 Login 이라면 파일명은 Login.php 이어야 하고 요청 URL을 사용하여 Login 콘트롤러를 실행하려면, http://localhost/ciBoard/login/함수명/param1/param2/.... 와 같이 하면 된다 



콘트롤러 이름을 URL에서 사용할 때 전혀 다르게 지정하려는 경우

우선, 콘트롤러 클래스 이름과 파일명은 항상 동일해야 한다


application/config/routes.php 파일에 등록하고 사용하면 된다

$route['sample'] = 'SampleController';

위와 같이 지정한 경우, URL은 http://localhost/ciBoard/sample 으로 하여 SampleController를 요청할 수 있다



Login.php

<?php  

 defined('BASEPATH') OR exit('No direct script access allowed');


 class Login extends CI_Controller 

 {  

    public function __construct() {

         parent::__construct();

         $this->load->helper('url'); // redirect(), base_url() 사용하기 위함

    }


    function login_form()  

    {  

        $data['title'] = '로그인 폼';  

        $this->load->view("login_form", $data);  

    }  


    function login_validation()  

    {  

        $this->load->library('form_validation');  

        $this->form_validation->set_rules('id', '아이디', 'required');  // 필드명, 이용자가 읽기쉬운 이름, 검사규칙

        $this->form_validation->set_rules('pass', '암호', 'required');  

        if($this->form_validation->run())  //입력 데이터 유효성검사 통과

        {  

            $id = $this->input->post('id');  

            $pass = $this->input->post('pass');  

            //model function  

            $this->load->model('LoginModel');  

            if($this->LoginModel->login($id, $pass))  //로그인 성공

            {  

                $session_data = array(  

                          'id'=>$id

                );  

                $this->session->set_userdata($session_data);  

                redirect(base_url() . 'login/enter');  //enter 함수로 이동

            }  

            else  //로그인 실패

            {  

                 $this->session->set_flashdata('error', '로그인에 실패했습니다');  

                 redirect(base_url() . 'login/login_form');  

            }  

        }  

        else  //입력데이터 유효성검사 실패한 경우

        {  

            $this->session->set_flashdata('validation_error', '아이디와 암호를 입력해주세요');

            $this->login_form();    // 폼으로 이동

        }  

    }  


    function enter() {  // 로그인 성공시

        if($this->session->userdata('id') != '')  

        {  

            echo '<h2>환영합니다 - '.$this->session->userdata('id').'님</h2>';  

            echo '<label><a href="' .base_url(). 'login/logout">로그아웃</a></label>';  //로그아웃

        }  

        else  

        {  

            redirect(base_url() . 'login/login_form');   // 폼으로 이동

        }  

    }  


    function logout()  

    {  

        $this->session->unset_userdata('id');  

        redirect(base_url() . 'login/login_form');  // 폼으로 이동

    }  

 }



application/models/ 안에 LoginModel 클래스 생성

<?php  

 class LoginModel extends CI_Model  

 {  

     public function __construct() {

         parent::__construct();

         // autoload.php에 $autoload['libraries'] = array('database') 설정이 없다면 아래 행이 필요함

         $this->load->database(); // application/config/database.php에서 설정된 DB정보 사용됨

     }


    function login($id, $pass)  

    {  

        $this->db->where('id', $id);  

        $this->db->where('pass', $pass);  

        $result = $this->db->get('users');  //Query 실행

        //SELECT * FROM users WHERE id = '$id' AND pass = '$pass'  

        if ( $result->num_rows() > 0)  { return true; }

        else  { return false; }

        

        /* SQL문장을 직접 사용하는 예

        $sql = "SELECT id FROM users WHERE id=? AND pass=?";

        $result = $this->db->query($sql, array($id, $pass)); //Query 실행

        if ( $result->num_rows() > 0)  { return true; }

        else  { return false; }

         */

    }  

}  



application/views/  안에 뷰 생성

login_form.php

<!DOCTYPE html>  

 <html>  

 <head>  

      <title><?php echo $title; ?></title>  

 </head>  

 <body>  

      <div>  

        <h3>로그인 폼</h3>

           <br />

           <?php  

                echo '<label>'.$this->session->flashdata("validation_error").'</label>';  

           ?>  

           <form method="post" action="<?php echo base_url();?>/login/login_validation">  

                <div>  

                     <label>아이디</label>  

                     <input type="text" name="id"/>

                </div>  

                <div>  

                     <label>암 호</label>  

                     <input type="password" name="pass"/>

                </div>  

                <div>  

                     <button type="submit">로그인</button>  

                     <?php  

                          echo '<label>'.$this->session->flashdata("error").'</label>';  

                     ?>  

                </div>  

           </form>  

      </div>  

 </body>  

 </html>


Posted by cwisky
MySQL/Transaction2019. 1. 5. 18:57

MySQL Transaction


MySQL의 저장엔진

MyISAM : MySQL 5.1까지의 기본 저장엔진. Transaction 지원안됨

InnoDB : MySQL 5.5까지의 기본 저장엔진. Transaction 지원함. 

MySQL 5.5 이후에는 테이블을 디폴트로 설정으로 생성하면 저장엔진은 InnoDB 으로 설정된다



지원하는 모든 저장엔진 확인하기 ( MySQL8 )

SHOW ENGINES;




현재 생성된 테이블에 적용된 저장엔진의 종류 확인하기

SHOW CREATE TABLE [테이블 이름];


'emp', 

'CREATE TABLE `emp` (\n

  `id` int(11) NOT NULL AUTO_INCREMENT,\n

  `ename` varchar(255) COLLATE utf8_bin DEFAULT NULL,\n

  `parent_id` int(11) DEFAULT \'0\',\n

  PRIMARY KEY (`id`)\n

) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_bin'



자동커밋 설정

SET AUtOCOMMIT=1;   // 각 SQL 문장 실행시 마다 즉시 영구반영

SET AUTOCOMMIT=0;   // 각 SQL 문장 실행시에 즉시 반영 안되고 COMMIT, ROLLBACK 명령시에 영구반영됨



MySQL은 디폴트로 SET AUTOCOMMIT=1; 상태이기 때문에 모든 문장마다 자동으로 COMMIT 이 실행되어 영구적으로 반영이 되고 있는 상태이다



현재의 AUTOCOMMIT 설정 상태 확인

SELECT @@AUTOCOMMIT;



MySQL에서 다수개의 SQL 문장으로 한개의 트랜잭션을 설정하려면 다음과 같이 할 수 있다


START TRANSACTION; 명령을 사용하는 경우

SET AUTOCOMMIT=1; 으로 설정된 디폴트 상태에서 아래처럼 특정 구간에서만 일시적인 트랜잭션을 설정할 수 있다

START TRANSACTION 이 실행되면,  SET AUTOCOMMIT=0 으로 자동설정되고 마지막에 COMMIT이나, ROLLBACK을 호출하면 다시 자동으로 SET AUTOCOMMIT=1 으로 설정된다

START TRANSACTION;      // 첫번째 트랜잭션

sql1, sql2, ...                    // 다수개의 SQL문장을 실행한다

COMMIT;                       // 혹은 ROLLBACK;                         // 첫번째 트랜잭션 완료


START TRANSACTION;     // 두번째 트랜잭션

sql1, sql2, ...                   // 다수개의 SQL문장을 실행한다

COMMIT;                       // 혹은 ROLLBACK;                         // 두번째 트랜잭션 완료

위의 방법을 사용하여 트랜잭선을 실행하고 나면(COMMIT, ROLLBACK 할 때) 다시 자동커밋으로 복원된다

그러므로 이후에는 SQL 한문장마다 커밋이 실행되어 실수로 COMMIT 깜박 잊더라도 모든 SQL 문장이 영구적으로 반영된다


START TRANSACTOIN; 명령을 사용하지 않는 경우

SETAUTOCOMMIT=0; 으로 설정을 변경하여 각 문장이 반영되지 않도록 한 경우

SET AUTOCOMMIT=0;     // 자동커밋을 해제한다

sql1 실행, sql2 실행, ...

COMMIT;                      // 혹은 ROLLBACK;                    // 첫번째 트랜잭션 완료

sql1 실행, sql2 실행, ...

COMMIT;                      // 혹은 ROLLBACK;                    // 두번째 트랜잭션 완료

SET AUTOCOMMIT=1;    // 원래의 자동커밋으로 복원한다

위의 방법을 사용할 때 주의할 점은 마지막에 SET AUTOCOMMIT=1; 부분을 설정하지 않으면 이후의 SQL문장이 반영되지 않기 때문에 작업을 끝내기 전에 반드시 COMMIT; 명령을 해야만 실수로 작업내용을 잃지 않게 된다

 


ROLLBACK으로 회복할 수 없는 작업(DDL)

ROLLBACK명령은 DML에 해당하는 SQL 문장을 취소하므로 아래와 같은 DDL에는 효과가 없으므로 주의해야 한다

CREATE ~

ALTER ~

DROP ~



START TRANSACTION 명령으로 트랜잭션을 설정하는 예

START TRANSACTION;                        // 트랜잭션 시작

DELETE FROM emp WHERE id != -100;   // 아이디가 음수가 아니면 모두 삭제, 다른 세션에서 emp 수정 불가

SELECT * FROM emp;                          // 모든 내용이 삭제된 것을 확인함

ROLLBACK;                                       // 트랜잭션 취소

SELECT * FROM emp;                          // 데이터가 다시 복원된 것을 확인함 



START TRANSACTION을 사용하지 않고 트랜잭션을 설정하는 예

SET AUTOCOMMIT=0;

DELETE FROM emp WHERE id != -100;

SELECT * FROM emp;

ROLLBACK;

SET  AUTOCOMMIT=1;

SELECT * FROM emp;



MySQL Workbench에서 트랜잭션 테스트

START TRANSACTION;


SELECT @num:=num, @title:=title, @regdate:=regdate, @hit:=hit

FROM bbs3 WHERE num=4;


DELETE FROM bbs3 WHERE num=4;


INSERT INTO delete_history VALUES(@num,@title,@regdate,NOW(),@hit);


SELECT * FROM bbs3;


ROLLBACK; # COMMIT;




PHP에서 MySQL 트랜잭션을 사용하는 예


MySQL에서 테이블 생성

CREATE TABLE bbs3 

(

   num INT AUTO_INCREMENT PRIMARY KEY,

   title VARCHAR(50) NOT NULL,

   regdate DATETIME,

   hit INT DEFAULT 0

);

INSERT INTO bbs3 VALUES

(NULL, '1번째 글', '2005-10-24 12:25:12', 0),

(NULL, '2번째 글', '2006-11-23 13:24:24', 0),

(NULL, '3번째 글', '2007-12-22 14:23:36', 0),

(NULL, '4번째 글', '2008-01-21 15:22:45', 0),

(NULL, '5번째 글', '2009-02-20 16:21:58', 0);


SELECT * FROM bbs3;


CREATE TABLE delete_history 

(

   num INT AUTO_INCREMENT PRIMARY KEY,

   title VARCHAR(50) NOT NULL,

   regdate DATETIME,

   deldate DATETIME,

   hit INT DEFAULT 0

);



transaction01.php

<!doctype html>

<html>

<head>

<meta charset="utf-8">

<title>트랜잭션 테스트</title>

</head>

<body>

<?php


  $host='fdb24.awardspace.net';

  $user='2915954_sampledb';

  $pass='2915954_sampledb_pwd';

  $db='2915954_sampledb';


  $conn = mysqli_connect($host,$user,$pass,$db);

  if ($conn->connect_error) {

    die("Connection failed: " . $conn->connect_error);

  }

  if($conn) {

    echo "Connection successful"; 

    echo "<br>";

  }

   

  mysqli_query($conn, "set session character_set_connection=utf8;");

  mysqli_query($conn, "set session character_set_results=utf8;");

  mysqli_query($conn, "set session character_set_client=utf8;");

  

  

  // 트랜잭션 시작 (MYSQLI_TRANS_START_READ_ONLY 도 가능)

  // 데이터 읽기만 할 때와 수정, 삭제할 때를 구분하여 지정함

  mysqli_begin_transaction($conn, MYSQLI_TRANS_START_READ_WRITE);

  

  $sql1 = "SELECT @num:=num, @title:=title, @regdate:=regdate, @hit:=hit

        FROM bbs3 WHERE num=4";

  $sql2 = "DELETE FROM bbs3 WHERE num=4";

  $sql3 = "INSERT INTO delete_history VALUES(@num,@title,@regdate,NOW(),@hit)";

  

  $result1 = mysqli_query($conn, $sql1);

  $result2 = mysqli_query($conn, $sql2);

  $result3 = mysqli_query($conn, $sql3);

  

  if( mysqli_num_rows($result1) > 0 && $result2 && $result3

  {

       mysqli_commit($conn);

       echo "트랜잭션 성공<br>";

  }else

  {       

       mysqli_rollback($conn);

       echo "트랜잭션 실패<br>";

  }

  

  // 위에서 실행된 트랜잭션이 정상적으로 작동했는지 확인해본다

  

  $result_set = mysqli_query($conn, "SELECT * FROM bbs3");

  

  if (mysqli_num_rows($result_set) > 0) {

      while($row = mysqli_fetch_assoc($result_set)) { 

        echo $row["num"]."<br>"; 

      }

  } else {

    echo "0 results";

  }


  mysqli_close($conn);

?>

</body>

</html>



트랜잭션과 LOCK IN SHARE MODE, FOR UPDATE

# SELECT 문장 오른쪽에 LOCK IN SHARE MODE, FOR UPDATE 를 사용할 수 있다

# LOCK IN SHARE MODE : 트랜잭션이 종료할 때까지 다른 세션에서 현재 행을 변경하지 못하고 읽기만 가능

# FOR UPDATE : 트랜잭션이 종료할 때까지 현재행을 다른 세션에서는 읽을 수도 없고 변경할 수도 없다


SELECT col FROM player WHERE id=1 LOCK IN SHARE MODE

SELECT col FROM player WHERE id=1 FOR UPDATE;


Posted by cwisky
MySQL/Hierarchical Query2019. 1. 5. 08:58

WITH RECURSIVE, UNION을 사용하여 계층구조 질의를 작성하기 위한 기초 정리

원문참조 : https://dev.mysql.com/doc/refman/8.0/en/with.html


MySQL의 WITH 절은 WITH절에 포함된 서브커리의 결과집합을 일단 이름을 붙여서 메모리에 저장하고 WITH 절 외부의 메인 커리에서 그 이름을 이용하여 마치 테이블처럼 사용할 수 있다. 그런 점에서 가상의 테이블이라고 설명할 수도 있다


# UNION 결과 : 1,2 출력

SELECT 1 AS num

UNION

SELECT 2 AS num;


# WITH, UNION 결과 : 1,2 출력

WITH test AS

(

SELECT 1 AS num

UNION ALL

SELECT 2 AS num

)

SELECT * FROM test;


# WITH, UNION 결과 : 1,2,2,2,... (emp테이블의 행수만큼 2가 반복됨)

WITH test AS

(

SELECT 1 AS num

UNION ALL

SELECT 2 AS num FROM emp # 이 문장은 emp 테이블의 행수만큼 반복된다

)

SELECT * FROM test;


# 서브커리에서 바깥 WITH 테이블 참조. 결과: RECURSIVE 가 없으므로 오류발생

WITH test AS

(

    SELECT 1 AS num

    UNION

    SELECT num+1 AS num FROM test WHERE num<10

)

SELECT * FROM test;


# 내부에 UNION 이 없으므로 오류

# Recursive Common Table Expression은 UNION을 포함해야 함

WITH RECURSIVE test AS

(

SELECT num+1 AS num FROM test WHERE num<10

)

SELECT * FROM test;


# RECURSIVE 문장에 정지조건이 없으므로 오류

WITH RECURSIVE test AS

(

    SELECT 1 AS num

    UNION

    SELECT num+1 AS num FROM test

)

SELECT * FROM test;


# 오류가 없는 정상적인 문장

# Recursive 내부에 UNION을 사용하고 정지조건도 사용한 경우

WITH RECURSIVE test AS

(

    SELECT 1 AS num

    UNION

    SELECT num+1 AS num FROM test WHERE num<10

)

SELECT * FROM test;


# Recursive 내부에는 한개 이상의 Non-Recursive문장이 포함되어야 하므로 아래의 문장은 오류

# Non-Recursive 문장이란 내부에서 바깥 WITH RECURSIVE 테이블을 참조하지 않는 문장을 의미함

WITH RECURSIVE test AS

(

    SELECT 1 AS num FROM test WHERE num<10

    UNION

    SELECT num+1 AS num FROM test WHERE num<10

)

SELECT * FROM test;


WITH RECURSIVE test AS

(    # Non-Recursive 문장( 첫번째 루프에서만 실행됨 )

    SELECT 1 AS num FROM emp # 첫 루프에서 emp 테이블의 행 수만큼 자체 반복됨

    UNION ALL

    # Recursive 문장(읽어 올 때마다 행의 위치가 기억되어 다음번 읽어 올 때 다음 행으로 이동함)

    SELECT num+1 AS num FROM test WHERE num<10

)

SELECT * FROM test;



다수개의 결과집합에 대한 이름을 붙일 때는 다음과 같은 형식을 사용한다

WITH name1 AS

(

   SELECT ~ (Sub Query)

),

name2 AS

(

   SELECT ~ (Sub Query)

)

SELECT ~ (Main Query)



WITH RECURSIVE 관련 정리

  • 메모리 상에 가상의 테이블을 저장
  • 반드시 UNION 사용
  • 반드시 비반복문도 최소한 1개 요구됨
  • 서브커리에서 바깥의 가상의 테이블을 참조하는 문장(반복문)이 반드시 필요함
  • 반복되는 문장은 반드시 정지조건이 요구됨
  • 가상의 테이블을 구성하면서 그 자신(가상의 테이블)을 참조하여 값을 결정할 때 유용함



상세한 해석 연습

아래의 문장에서 사용된 emp 테이블에는 총 10개의 행이 저장되어 있다고 가정한다

아래의 문장을 MySQL8에서 실행하면 1~10 사이의 숫자가 순서대로 10개씩 총 100개 출력된다

왜 그러한 결과가 나오는지 논리적으로 설명하고 그림으로 그릴 수 있는 정도까지 연습해보기 바란다


WITH RECURSIVE test AS

(    # Non-Recursive 문장( 첫번째 루프에서만 실행됨 )

    SELECT 1 AS num FROM emp # 첫 루프에서 emp 테이블의 행 수만큼 자체 반복됨

    UNION ALL

    # Recursive 문장(읽어 올 때마다 행의 위치가 기억되어 다음번 읽어 올 때 다음 행으로 이동함)

    SELECT num+1 AS num FROM test WHERE num<10

)

SELECT * FROM test;



아래의 계층구조 질의에서 사용할 함수

REPEAT(str, count)

 - str 문자열을 count 수만큼 반복하여 결합한다

 - SELECT REPEAT(' * ', 10);


CONCAT(exp1, exp2, ......)

 - 2개 이상의 파라미터를 문자열로 결합한다

 - 첫번째 파라미터는 필수이다


SELECT CONCAT ( REPEAT( ' ', 10 ), title ) AS title FROM bbs;



CREATE TABLE emp

(

    id INT AUTO_INCREMENT PRIMARY KEY, 

    ename VARCHAR(255), 

    parent_id INT DEFAULT 0 REFERENCES id

);


INSERT INTO emp VALUES

(1, 'name1', 0),

(2, 'name2', 1),

(3, 'name3', 1),

(4, 'name4', 3),

(5, 'name5', 0),

(6, 'name6', 3),

(7, 'name7', 4),

(8, 'name8', 5),

(9, 'name9', 8),

(10, 'name10',8);


WITH RECURSIVE tmp1 AS

(

    SELECT id, ename, parent_id, 

    ename AS path, 1 AS lvl

    FROM emp WHERE parent_id=0

    UNION ALL

    SELECT e.id, e.ename, e.parent_id, 

    CONCAT(t.path,',',e.ename) AS path, t.lvl+1 AS lvl 

    FROM tmp1 t JOIN emp e ON t.id=e.parent_id

)

SELECT id, CONCAT(REPEAT(' ', lvl*4), ename) ename, parent_id, path, lvl 

FROM tmp1 

ORDER BY path# path 임시컬럼을 사용하여 정렬하면 계층구조로 정렬된다



Workbench에서 위의 문장을 실행한 결과



Posted by cwisky
Networking/Insert Game Data2018. 12. 26. 22:21

VaRest 플러그인을 이용하여 게임 데이터를 웹서버로 전달하고 MySQL 에 저장하는 예


무료 PHP 웹호스팅 준비

awardspace.com 사이트에 회원가입하고 무로 PHP 계정을 얻는다

awardspace.com 사이트의 Hosting Tools > Database Manager / MySQL Database를 클릭하여 데이터베이스를 생성한다

Hosting Tools > Database Manager / phpMyAdmin4를 선택하여 테이블(game_history)을 생성한다

game_history 테이블에 컬럼 5개를 생성하고 아래처럼 각 컬럼의 자료형을 지정한다



위에서 생성한 game_history 테이블의 game_start_date, game_end_date 컬럼의 자료형인 datetime은 날짜와 시간을 동시에 저장할 수 있으며 그 데이터 포맷은 '2018-12-03 12:31:45' 와 같이 년-월-일 시:분:초 를 표현한다


위에서 생성한 game_history 테이블에 게임 데이터를 저장하는 기능을 할 PHP를 작성한다


insert_game_data.php

<?php

  $game_title = $_REQUEST['game_title'];

  $player_id =  $_REQUEST['player_id'];

  $score =       $_REQUEST['score'];

  $start_time = $_REQUEST['game_start_date'];

  $end_time =  $_REQUEST['game_end_date'];

  

  $host='fdb24.awardspace.net';

  $user='2915954_user';

  $pass='2915954Database';

  $db='2915954_user';


  $arr = array();

  

  $conn = mysqli_connect($host,$user,$pass,$db);

  if ($conn->connect_error) {

    $arr["connection"] = false;

    return;

  }

  if($conn) {

    $arr["connection"] = true;

  }

 

  $sql = "INSERT INTO game_history (game_title, player_id, score, game_start_date, game_end_date) ".

          "VALUES ('$game_title', '$player_id', $score, '$start_time','$end_time')";


  $inserted = mysqli_query($conn, $sql);


  if ($inserted) {

    $arr['inserted'] = true;

  } else {

    $arr['inserted'] = false;

  }

  mysqli_close($conn);

  $json = json_encode($arr);

  echo $json;

?>


언리얼 런처를 실행하여 마켓플레이스를 클릭하고 VaRest 플러그인을 찾아 언리얼 엔진에 설치한다

언리얼 엔진의 기본 프로젝트 템플릿을 이용하여 프로젝트를 생성한다


언리얼 에디터의 블루프린트 버튼을 누르고 레벨블루프린트를 열고 다음과 같이 시간 문자열을 생성하여 리턴하는 함수를 생성한다


GetDateTime함수(MySQL의 datetime 형 데이터를 생성하는 기능)



레벨블루프린트에 다음과 같이 VaRest 플러그인을 이용하여 서버측 스크립트(insert_game_data.php)에 데이터를 전달하는 기능을 작성한다(웹서버에 요청을 전달하는 기능)

Posted by cwisky
Networking/VaRest Plugin2018. 12. 22. 17:59

http 요청 및 응답처리를 위한 VaRest 플러그인 사용법


언리얼 엔진 4.11 ~ 현재 최신버전(4.21)까지 지원하는 플러그인


설치 및 테스트


언리얼 마켓플레이스에서 VaRest 를 검색하고 엔진에 설치하기를 누른다

언리얼 엔진을 이용하여 프로젝트를 생성한다

편집 > 플러그인 > VaRest 활성화 체크 > 화면 우측하단 [지금 재시작] 버튼 누름

언리얼 에디터가 다시 실행되면 블루프린트 입력 가능한 곳에 아래의 내용을 입력한다



VaRest 플러그인 작동 테스트 (https://alyamkin.com/ 사이트에 대한 GET 방식 요청)



게임을 실행하고 키보드에서 T 키를 누르면 아래와 같이 게임화면에 http 응답문자열이 표시된다




PHP 무료 호스팅 서비스 이용하기

언리얼 프로젝트에서 접속할 웹서버가 필요할 때 PHP 무료계정을 이용하면 빠르고 쉽게 웹서버를 구축할 수 있다


무료 PHP 계정으로 유명한 infinityfree.net 은 다른 곳에 비해서 더 좋은 프로그래밍 환경을 제공하고 있지만 언리얼 엔진에서 접속하면 자바스크립트가 지원되지 않는 클라이언트라는 오류 메시지가 표시되면서 PHP가 실행되지 않는 문제가 있어서 아쉽지만 사용할 수가 없다


아래 예제에서 사용한 웹사이트 무료계정

awardspace.com


PHP에서 단순한 문자열을 응답하는 경우

<?php

  echo "Hello";

?>



여러개의 문자열을 쉼표로 구분하여 한개의 문자열로 응답하는 경우

<?php

  echo "Hello, World, Bye";

?>

위와 같은 경우에는 블루프린트에서 수신된 응답 문자열을 Parse into Array 노드를 이용하여 배열을 생성하고 ForEachLoop노드를 이용하여 모든 문자열을 화면에 표시할 수 있다



PHP 에서 간단한 JSON 문자열을 응답으로 출력하는 경우

<?php

  header('Content-type: application/json; charset:utf-8');

  $arr = array();

  $arr["login"] = true;

  $arr["error"] = false;

  $json = json_encode($arr);

  echo urldecode($json);

?>




POST 방식 요청으로 웹서버에 파라미터 전송하기


블루프린트에서 POST 요청을 전달하기 위해서는 위의 블루프린트에서도 생성한 적이 있는 RequestObject 객체의 Set String Field 노드를 이용하여 파라미터 이름, 값을 웹서버로 전송할 수 있다.



웹서버측에서 파라미터를 수신하는 예

<?php

  $ParamValue = $_REQUEST["Param Name"];

  echo ParamValue;

?>



PHP에서 JSON 문자열을 생성하여 클라이언트에게 응답하는 예

<?php

  header('Content-type: application/json; charset=UTF-8');

  $arr = array();

  $arr["login"] = true;

  $arr["error"] = false;

  $jsonStr = json_encode($arr);

  echo $jsonStr;

?>



PHP에서 MySQL 데이터베이스에 접속하는 예 ( http://unrealengine.epizy.com/mysql_test.php )


<?php


  $host='sql113.epizy.com';

  $user='epiz_23187906';

  $pass='HQpyNNHkYp';

  $db='epiz_23187906_User';


  $conn = mysqli_connect($host,$user,$pass,$db);

  if ($conn->connect_error) {

    die("Connection failed: " . $conn->connect_error);

  }

  if($conn) {

    echo "Connection successful"; 

    echo "<br>";

  }

 

  $select_query = "SELECT * FROM Player ";


  $result_set = mysqli_query($conn, $select_query);


  if (mysqli_num_rows($result_set) > 0) {

    while($row = mysqli_fetch_assoc($result_set)) {

        echo "Player ID: " . $row["PlayerID"]. " - Email: " . $row["Email"]. " - Game: " . $row["Game"]. "<br>";

    }

  } else {

    echo "0 results";

  }

  mysqli_close($conn);

?>



로그인 결과를 JSON 문자열로 응답하는 PHP(http://unreal.mywebcommunity.org/unreal_login.php?id=smith&pwd=smithpwd)

<?php

header('Content-type: application/json; charset=UTF-8');

  $id = $_REQUEST["id"];

  $pwd = $_REQUEST["pwd"];


  $result = "";

  

  // DB 코드 시작

  $host='fdb24.awardspace.net';

  $user='2915954_user';

  $pass='2915954Database';

  $db='2915954_user';


  $arr = array();

  

  $conn = mysqli_connect($host,$user,$pass,$db);

  if ($conn->connect_error) {

$arr["error"] = true;

        echo json_encode($arr);

return;

  }

  if($conn) {

    $arr["connection"] = true;

  }

 

  $select_query = "SELECT * FROM Member WHERE memid='$id' AND mempwd='$pwd' ";


  $result_set = mysqli_query($conn, $select_query);


  if (mysqli_num_rows($result_set) > 0) {

$arr["login"] = true;

  } else {

$arr["login"] = false;

  }

  mysqli_close($conn);

  $json = json_encode($arr);

  echo $json;

?>




VaRest 플러그인을 이용하여 GET 방식으로 위의 PHP에 로그인 요청하는 예

 - 서버측에서 JSON 문자열을 송신하여 언리얼 측에서 JSON 오브젝트를 수신





VaRest 플러그인을 이용하여 POST 방식으로 위의 PHP에 로그인 요청하는 예




localhost:3000/login 요청으로 실행되는 Node.js 웹서버 코드


var express = require('express');
var app = express();
var qs = require('querystring');

app.get('/login', function(req, res){
console.log("id=%s, pass=%s", req.query.id, req.query.pass);
login(req.query.id, req.query.pass, res);
});

app.post('/login', function(req, res){
var body = '';

req.on('data', function (data) {
body += data;
if (body.length > 1e6)
req.connection.destroy();
});

req.on('end', function () {
var post = qs.parse(body);
console.log("수신된 파라미터:"+post.id+", "+post.pass);
login(post.id, post.pass, res);
});
});

app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});


function login(empID, empName, res)
{
const oracledb = require('oracledb');
oracledb.getConnection(
{
user : "hr",
password : "hr",
connectString : "127.0.0.1/XEPDB1"
},
function(err, connection)
{
if(err){
console.error(err.message);
return;
}
console.log("오라클 접속 성공");
connection.execute(
"SELECT * FROM employees WHERE employee_id=:a AND first_name=:b",
[empID, empName],
{
maxRows:1
},
function(err, result)
{
var jsonObj = {};

if(err) {
console.error(err.message);
connection.close();
jsonObj.result = "Login Failed!";
} else{
//console.log(result.metaData); // Column names etc
console.log(result.rows);
if(result.rows.length>0) {
console.log("검색 성공")
jsonObj.result = "Login OK";
}else{
console.log("검색 실패")
jsonObj.result = "Login Failed!";
}
connection.close();
}
res.end(JSON.stringify(jsonObj));
}
); // end of execute()
}
); // end of getConnection()
}


Posted by cwisky