• Versions available for this page:
  • Debug 1.5
  • Add new page
  • Edit page
  • Delete page
  • History view
Views 15252 |IP *.5.5.8

디버깅을 위한 기초함수

2013-04-19 7:05 PM | By 우진홈 Updated | 2013-04-30 21:04

"DEBUG 이야기"를 통해 프로그램 안에서 오류의 원인이 되는 소스 코드를 찾고, 추적하고, 제거하거나 수정하는 과정을 디버깅이라고 설명하였다. 디버깅은 반드시 프로그램의 결과물에 대해서만 한정하여 수행하는 작업은 아니다. 프로그램의 구상과 설계, 코딩과 배포 등 모든 제작 과정 전반에 걸쳐 활용될 수 있는 유용한 도구이며 시작 단계를 포함하여 각각의 과정에서 효과적으로 활용한다면 오류를 찾고 제거하는 노력과 시간을 최소한으로 단축할 수 있다. 우선 디버깅에 도움이 될 만한 PHP 명령어와 기초 함수에 대해 살펴보자


다음과 같이 debug.php 라는 문서를 만들고 변수 $string 에 임의의 문자열을 대입한다. 

<?php
   $string = 'Xpress Engine';
?> 

과연 변수 주머니에 문자열이 들어 있을까? echo 명령어를 이용해 변수 $string 의 값을 확인해 보자.

<?php
   $string = 'Xpress Engine';
   echo $string;
?>

결과> Xpress Engine


웹브라우저는 변수에 할당된 문자열 그대로 출력해 줄 것이다. 위에서 사용한 echo 명령어는 PHP 책을 열면 가장 먼저 등장하는 echo "Hello World!" 와 같은 일을 한다. echo는 문자 그대로 '메아리'라는 뜻이다. PHP는 echo 명령어를 만나면 입력 데이터 그대로 웹브라우저에게 돌려보낸다. 컴퓨터는 소리를 낼 수 없으니 터미널을 향해, 즉 가장 마지막 출력 장치인 모니터 상에 문자열을 표시하는 방법으로 메아리를 출력하는 것이다.


echo와 같은 방법으로 이번에는 print 명령어를 사용해 보자.

<?php
   $string = 'Xpress Engine';
   print $string;
?>

결과> Xpress Engine


print 역시 echo와 같은 방법으로 문자열을 출력한다. print가 echo와 다른 점은 언제나 1을 반환한다는 것이다. 반면 echo는 반환값이 없다. 가장 단순한 메아리라는 것. 그리고 echo와 print를 함수라고 부르지 않는 것은 두 명령어 모두 PHP의 기본 언어구조에 포함되어 있기 때문이다. 그밖의 차이점은 별도의 학습을 통해 참고하자. 이 글은 XE 코어 디버깅을 위한 이해이다.


이번에는 print를 닮은 printf() 함수와 sprintf() 함수에 대해 살펴보자 printf()는 문자 그대로 출력을 위한 함수로서 함수 이름 끝의 "f"는 "Format"을 의미한다. print와 다른점은 입력 받은 각각의 데이터를 어떠한 양식(format)에 맞추어 출력한다는 것으로 동적 페이지를 만들때 자주 사용된다. 이것을 PHP 매뉴얼에서는 "형식화한 문자열을 출력한다."라고 표현하는데 가장 쉽게 설명한다면 함수의 매개변수로 넘어온 입력 문자열을 다른 문자열과 함께 꾸며서 출력할 수 있다는 뜻이다.

<?php
   $string = 'Xpress Engine';
   $version = 1.5;
   printf('이 문서는 %s을 위한 도움말입니다. 참고 버전은 %.1f 입니다.', $string, $version);
?>

결과> 이 문서는 Xpress Engine을 위한 도움말입니다. 참고 버전은 1.5 입니다.


함수의 첫 매개변수는 포멧 문자열이다. 항상 이 양식으로 출력할 것인데 일반 문자열은 그대로 출력하고 포멧 문자열 속에 포함된 지시어 문자(%s 와 %.1f)는 뒤따라 입력되는 매개변수의 인수 데이터를 어떤 형으로 취급할지 결정하게 된다. 형지정어는 와일드카드(%, 다른 문자를 대신하는 문자)와 함께 사용되며 이것들은 입력된 데이터를 변환하기 위한 특정어라고도 부른다. 포멧 문자열 속에 등장하는 지시어 문자의 순서는 입력 매개변수와 같은 순서로 교체되는 것이 기본값이지만 다음과 같이 작성하면 인수의 순서를 바꾸는 것도 가능하다.

%2 + $ + .1f

(교체할 매개변수 순서) + $ + 형(type, 소수 n 자리까지)

<?php
   $string = 'Xpress Engine';
   $version = 1.5;
   $format = '이 문서는 XE %2$.1f 를 참고하여  %1$s 디버깅을 위한 도움말입니다.';
   printf($format, $string, $version);
?>

결과> 이 문서는 XE 1.5 를 참고하여 Xpress Engine 디버깅을 위한 도움말입니다.


디버깅은 printf() 함수의 놀이터다. echo처럼 단순히 입력 문자열만 출력하는 것이 아니라 프로그램의 처리 결과에 따른 현재 상태 정보를 동적으로 꾸며 생성할 수 있기 때문이다. printf() 함수가 프론트엔드 영역에서 사용된다면 알림 메시지로 활용될 수 있고 백엔드 영역에서 사용된다면 디버깅을 위한 모든 정보를 수집하여 인수로 전달할 수 있다.

<?php
   $string = 'Xpress Engine';
   //$version = 1.5;  // 한줄 주석으로 변수를 임시 제거

   $a = printf($string);
   echo $a; //  Xpress Engine13

   $b = printf($version);  
   echo $b; //  0

   $c = sprintf($string);
   echo $c; // Xpress Engine

   $d = sprintf($version);  
   echo $d; //  
?>

sprintf() 함수는 printf() 함수와 기능이 동일하면서 반환값(return)이 문자열이기 때문에 변수에 할당하기 좋다.(※ 함수 스스로가 출력해 주지 않는다. 항상 변수에 할당하고 사용해야 한다.) 만약 printf()를 변수에 할당한다면 성공시 반환 값은 공백을 포함한 문자열의 길이(length)를 반환한다.(The number of characters written on success.) 따라서 $a의 반환 값은 정수(13)이고 $c의 반환 값은 문자열이다. 반면 실패(check for error)했다면 $b는 0을 반환하고 $d는 반환된 문자열이 없다. 따라서 print를 사이에 두고 print, printf, sprintf를 다음과 같이 기억하자.


string + print + format


위 함수들의 특징은 변수에 담겨진 값이 문자열일때만 사용할 수 있다. 만약 변수가 배열 또는 객체를 담고 있을 때에는 사용할 수 없다. 아래와 같이 $phone이라는 변수가 종류, 버전, 색깔이라는 원소를 가지고 있는 배열이라고 생각해 보자.

<?php
   $phone = array('iphone', 5, 'white');
   echo $phone; // Array
   echo 'My cell phone is an ', $phone[0], $phone[1]; // My cell phone is an iphone5
   print('What is color?' . $phone[2]); // What is color?white
   printf('My cell phone color is %s.', $phone[2]); // My cell phone color is white. 
   echo sprintf($phone[2]); // white
?>

이때 echo를 이용하여 $phone 변수를 출력하려고 한다면 echo는 Array라고만 대답한다. 다른 함수들도 같은 대답을 하며 만약 객체라면 object(stdClass)로 대답할 것이다. 메아리를 듣고 싶다면 배열의 인덱스로 원소를 직접 가리켜야 한다.


그러면 배열이나 객체가 담고 있는 원소의 값은 어떻게 확인 할 수 있을까? 다음의 예제는 Phone이라는 객체를 만들고 속성으로 3개의 변수를 생성 하였다.

<?php
   class Phone {
      var $name, $version, $color;
   }
   
   $myPhone = new Phone();
   print_r($myPhone);
?>

결과> Object (

                [name] =>

                [version] =>

                [color] =>

            )


위와 같이 print_r() 함수를 이용하면 배열이나 객체가 담고 있는 속성의 값을 디버깅 할 수 있다. 지금쯤 눈치가 빠른 독자는 함수 이름 뒤에 붙어있는 '_r'이 무엇을 말하는지 쉽게 알 수 있을 것이다. 즉 Array(배열)라는 것이다. 객체도 인덱스(index)가 없는 배열이기 때문에 당연히 출력해 준다. print_r() 함수를 우리말로 고쳐 쓰면 "인쇄_배열()"이다.


print_r() 함수는 변수에 관한 정보를 사람이 읽기 편하게 출력해 주고 매개변수에 true를 던져주면 sprintf()처럼 출력하는 대신 임의의 변수에 반환 값을 저장할 수도 있다. print_r()과 유사한 var_dump() 함수는 변수에 대한 정보를 좀 더 자세히 덤프(dump)해 준다. 덤프라는 의미는 본래 불필요한 것들, 즉 쓰레기 같은 것들을 "~버리다"는 뜻이지만, 컴퓨터에서는 어떤 변수에 대한 정보들을 모두 쓸어 담아 다른 곳으로 복사해 가서 저장하는 것을 말한다. 이런 일을 가장 잘하는 자동차가 있는데 '덤프트럭'이다. 도로에서 덤프트럭을 본다면 덤프트럭에 실려 있는 흙이 변수(variable)라는 것을 기억하자. print_r() 대신 var_dump()를 사용하면 다음과 같은 결과를 출력한다.


object(Phone)#1 (3)

{

   ["name"]=> NULL

   ["version"]=> NULL

   ["color"]=> NULL

}


덤프트럭이 변수들에 대한 정보를 모아 버리기만 하는 것은 아니다. 혹시 필요한 곳에서 재활용할 수 있다면 수집한 정보를 좀 더 깔끔하게 다듬을 필요가 있다. 이러한 일을 하는 함수가 var_export() 이다. var_export()는 문자 그대로 변수를 내보내는 일을 하는데 다른 곳에서도 처리가 가능하도록 구조화된 문자열을 생성하여 출력하거나 반환한다.


Phone::__set_state

(

   array

   (

      'name' => NULL,

      'version' => NULL,

      'color' => NULL,

   )

)


PHP에서는 디버깅을 위한 전용 함수도 가지고 있다. debug_backtrace() 함수가 그것인데 문자 그대로 원인이 되는 요소를 따라 역추적하고 수집된 정보를 문자열로 생성하여 반환해 준다.  다음의 예제를 살펴보자.

<?php
   function one($str1, $str2) {
      two("C", "D"); // 함수 2를 호출한다.
   }
   function two($str1, $str2) {
      three("E", "F"); // 함수 3을 호출한다.
   }
   function three($str1, $str2) {
      print_r(debug_backtrace()); // 추적한 내용을 출력한다.
  }

   one("A", "B"); //  함수 1을 호출한다.
?>

결과>

Array
(

   [0] => Array
        (
            [file] => /xeschool/debug.php
            [line] => 6
            [function] => three
            [args] => Array
                            (
                                [0] => E
                                [1] => F
                            )
        )
    [1] => Array
        (
            [file] => /xeschool/debug.php
            [line] => 3
            [function] => two
            [args] => Array
                            (
                                [0] => C
                                [1] => D
                            )
        )
    [2] => Array
        (
            [file] => /xeschool/debug.php
            [line] => 12
            [function] => one
            [args] => Array
                            (
                                [0] => A
                                [1] => B
                            )
        )


추적한 결과의 내용은 배열에 순서대로 담겨지는데 php 파일과 라인 위치, 함수의 이름과 매개변수를 자세히 기술해 주고 있다. 함수가 호출된 위치를 간략히 확인하려면 debug_print_backtrace()를 이용할 수도 있다. debug_print_backtrace() 함수는 PHP5 부터 사용이 가능하다.


#0 three(E, F) called at [/xeschool/debug.php:6]
#1 two(C, D) called at [/xeschool/debug.php:3]
#2 one(A, B) called at [/xeschool/debug.php:12]



지금까지 살펴본 간단한 명령어와 함수들은 XE코어를 디버깅하는데 도움이 될만한 기초 내용으로 별도의 php파일을 만들어 예제와 같은 방법으로 실습을 통해 꼭 이해하고 넘어가자. 특별히 배열과 객체에 대한 자료 분해는 모듈을 만드는 과정 뿐만 아니라 템플릿 파일에서 스킨을 수정한다거나 혹은 제작을 위해서라도 반드시 이해가 필요한 내용이다.  


위 함수들은 모두 PHP로 작성된 php 확장자를 가진 문서와 <?php 로 시작하고 ?>로 끝나는 PHP 언어 안에서 사용되는 기초 명령어와 함수들이다. XE코어는 객체지향 프로그램이면서 동시에 MVC 패턴으로 동작한다. 따라서 XE코어의 모든 출력은 HTML 마크업 언어로 작성된 HTML 템플릿 문서 안에서 XE 템플릿 문법으로 데이터를 받아 처리하여 화면에 출력하는 구조이고 PHP 파일은 템플릿을 기준으로 본다면 모두 백엔드로 동작한다고도 볼 수 있다. 지금은 조금 어렵겠지만 다음 장에서 이 부분에 대한 이야기를 자세히 다룰 것이다.

 

XE코어에서 사용되는 대부분의 변수들은 객체 또는 배열이기 때문에 echo 또는 print와 같이 문자열만 다루는 명령어들은 당연히 사용할 기회가 그다지 많지 않다. 다만 XE 템플릿 문법은 HTML 템플릿 안에서 중괄호 안에 작성된 간단한 PHP 함수에 대해 PHP 언어로 변환하여 처리가 가능하도록 설계 되어 있다. 따라서 위 함수들 가운데 템플릿 파일에서 자주 사용되는 print_r()과 var_dump() 함수를 기본 레이아웃 템플릿으로 가져가 메뉴 변수에 대해 첫 디버깅을 시도해 보자.


XE코어의 기본 레이아웃 템플릿 파일의 경로는 /xe/layouts/xe_official/layout.html 이다. 초기 설치 시 기본 레이아웃에는 GNB 메뉴를 $main_menu 라는 변수에 객체로 담아 온다. 왜냐하면 레이아웃 정보 파일(conf/info.xml)에서 $main_menu 라는 변수에다가 관리자 화면에서 생성하는 임의의 메뉴를 받아쓰기로 미리 설정해 두었기 때문이다. 현재 운영 중인 사이트라면 이보다 더 많은 메뉴의 갯수를 가지고 있을 것이다.(※ 운영 중인 메뉴의 변수 이름이 다른 경우 참고하여 수정한다.) 


layout.html 템플릿 문서를 에디터에서 열고 맨 마지막 하단에 XE 템플릿 문법으로 다음과 같이 작성하고 업로드 한다.

 {print_r($main_menu)}
PHP의 print_r() 함수를 중괄호 안에 작성하고 확인할 객체 또는 배열 변수의 이름을 매개변수로 입력한 것이다. 아래와 같은 형식의 결과가 출력 되었다면 첫 번째 디버깅에 성공한 것이다. 같은 방법으로 var_dump() 함수로도 확인해 보자. var_dump()는 객체의 형(type)과 더불어 print_r() 보다 더욱 자세한 객체 정보를 제공할 것이다.

(※ XE Core Ver.1.5.4.3 초기 메뉴 객체)


stdClass Object
(
    [name] => main_menu
    [title] => 상단 메뉴
    [maxdepth] => 3
    [menu_srl] => 61
    [xml_file] => ./files/cache/menu/61.xml.php
    [php_file] => ./files/cache/menu/61.php
    [list] => Array
                (
                    [62] => Array
                                (
                                    [node_srl] => 62
                                    [parent_srl] => 0
                                    [text] => menu1
                                    [href] =>
                                    [url] =>
                                    [open_window] => N
                                    [normal_btn] =>
                                    [hover_btn] =>
                                    [active_btn] =>
                                    [selected] => 1
                                    [expand] => N
                                    [list] => Array
                                                (
                                                    [63] => Array
                                                                (
                                                                    [node_srl] => 63
                                                                    [parent_srl] => 62
                                                                    [text] => menu1-1
                                                                    [href] => /xe/index.php?mid=welcome_page
                                                                    [url] => welcome_page
                                                                    [open_window] => N
                                                                    [normal_btn] =>
                                                                    [hover_btn] =>
                                                                    [active_btn] =>
                                                                    [selected] => 1
                                                                    [expand] => N
                                                                    [list] => Array ( )
                                                                    [link] => menu1-1
                                                                )
                                                )
                                    [link] => menu1
                                )
                )
) 1


다음은 XE코어의 모든 상황정보를 담고 있는 Context 객체를 확인해 보자. 같은 방법으로 layout.html 템플릿 문서 하단에 다음과 같이 작성하고 업로드 한다.

<h1>Properties of the Context Object</h1>
{var_dump(new Context())}

이 코드의 결과는 XE코어 Context 객체가 가지고 있는 프로퍼티(properties) 즉, 속성 변수들을 보여줄 것이다. 객체의 인스턴스만 생성하고 속성 값은 할당하지 않았기 때문에 결과는 다음과 같다.Context 클래스가 가지고 있는 속성들의 설명은 /xe/classes/context/Context.class.php 파일에서 자세히 확인해 볼 수 있다.


object(Context)#96 (22)
{
    ["allow_rewrite"]=> bool(false)
    ["request_method"]=> string(3) "GET"
    ["response_method"]=> string(0) ""
    ["context"]=> NULL
    ["db_info"]=> NULL
    ["ftp_info"]=> NULL
    ["sslActionCacheFile"]=> string(30) "./files/cache/sslCacheFile.php"
    ["ssl_actions"]=> array(0) { }
    ["oFrontEndFileHandler"]=> object(FrontEndFileHandler)#97 (6)
                                                {
                                                    ["cssMap"]=> array(0) { }
                                                    ["jsHeadMap"]=> array(0) { }
                                                    ["jsBodyMap"]=> array(0) { }
                                                    ["cssMapIndex"]=> array(0) { }
                                                    ["jsHeadMapIndex"]=> array(0) { }
                                                    ["jsBodyMapIndex"]=> array(0) { }
                                                }
    ["html_header"]=> NULL
    ["body_class"]=> array(0) { }
    ["body_header"]=> NULL
    ["html_footer"]=> NULL
    ["path"]=> string(0) ""
    ["lang_type"]=> string(0) ""
    ["lang"]=> NULL
    ["loaded_lang_files"]=> array(0) { }
    ["site_title"]=> string(0) ""
    ["get_vars"]=> NULL
    ["is_uploaded"]=> bool(false)
    ["patterns"]=> array(3)
                            {
                                [0]=> string(9) "/<\?/iUsm"
                                [1]=> string(9) "/<\%/iUsm"
                                [2]=> string(56) "/<script\s*?language\s*?=\s*?("|\')?\s*?php\s*("|\')?/iUsm"

                             }

}


XE코어를 다루면서 Context::set()과 Context::get()을 활용해 본 사용자라면 "과연 Context 객체에는 무엇이 들어있을까?"라고 한 번씩은 의문을 가져보았을 것이다. 실제로 Context 객체를 열어본다는 것은 디버깅에 별로 도움이 되지는 않는다. 코어가 동작하기 위해 사용하는 상황정보를 열어본다는 것은 마치 자동차 본네트를 열고 복잡하게 얽혀있는 자동차 엔진을 쳐다보는 것과 같다. 혹여 냉각수와 엔진오일, 브레이크 오일을 넣는 입구의 위치만 알아도 자동차를 운전하는데는 전혀 지장이 없다. 좀 더 전문가답게 코어를 디버깅 해 본다는 것은 가장 작은 부품, 또는 문제가 있는 부품에 대해 조심스럽게 변수들을 분해, 조립하면서 고장의 원인을 찾거나 또는 가끔은 원하는 대로 조금씩 수정해 사용할 수 있는 방법을 안다는 것이 중요하다.

{print_r($__Context)}

OR

{print_r(Context::getAll())}
OR

{var_dump(Context::getInstance())}
스크롤의 압박을 감내하고 위와 같이 작성하면 자동차 본네트를 열어본 듯한 느낌을 받을 것이다. 직접 확인해 보자. 단, 해커들에게도 매우 유용한 정보임으로 확인 후에는 반드시 삭제하고 웹에서 접근 가능한 코드에 위 소스를 남겨 두어서는 않된다.



⇒ Comments have been disabled for this page.

Today268|Yesterday724|Total Visitors1,054,745

User ID
Password