Pharo Json PHP Example 01

From 흡혈양파의 인터넷工房
Revision as of 14:14, 12 May 2017 by Onionmixer (talk | contribs) (link 추가)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Pharo with PHP Example(1)

배경

Pharo 5.0 이 릴리즈되고, 64bit native 로 Pharo 를 사용할 수 있는 2017년 5월. 기존에도 나는 Pharo 또는 Smalltalk 을 사용하기 위해 여러가지 방안을 생각했다. 기본적으로 Standalone 에서 의미가 있는 프로젝트는 다른 사람에게 소개하기 어렵다는 생각 하에, 프로젝트 주제를 정했다. 결과물을 보다 여러사람이 쓸 수 있으며 Pharo 의 사용법을 익힐 수 있고, Pharo 하나만으로 모든것을 해결하는 광범위한 내용이 아니라, Pharo 는 그에 대한 최종 목적으로서 개인의 가벼운 프로젝트를 진행하는 방법을 염두에 두기로 했다.


개요

Pharo 를 client 로 두고 http 를 통해서 JSON 을 서버로 전송하고 이를 서버에서 받아서 처리.


Requirement

  • Pharo 5.0 이상
  • nginx(or apache)
  • PHP 5 이상(tested in PHP 7)


물론 이상의 코드 또는 환경을 처리할 수 있는 정도는 되어야 하겠다. (pharo 는 예외)


Lesson 1 : 프로젝트의 준비

기존에 운영하고 있는 homepage system 에 대한 contents 를 일일히 web 으로 login 하기 귀찮았다. Client 프로그램이 있었으면 좋겠다고 생각했으며 JSON 으로 데이터를 주고 받기로 결정하였다. 이에 다음의 내용을 정의했다.

  1. Server 와 Client 간의 데이터 규격은 JSON 으로 한다
  2. 프로토콜은 http 를 사용한다
  3. Server 는 PHP 를 사용한다
  4. 최신트렌드(?)에 맞게 PHP 에서도 Object 를 사용하기로 한다
  5. 전송방식은 POST 를 사용하기로 한다


이렇게 정리한 내용으로 다음과 같은 프로젝트 단계를 수립했다

  1. Phase 1
    • JSON 의 데이터 규격을 정한다.
  2. Phase 2
    • PHP 로 서버에서 처리할 module 을 만든다
    • module 은 일단 본인도 JSON 에 익숙하지 않기때문에 POST 를 stream 으로 받아서 처리하는 부분부터 작업
    • 이 시점에서 post 데이터는 array 로 취급
    • client 는 curl(libcurl 이 아니다) 로 처리한다
  3. Phase 3
    • PHP 에서 로 받은 데이터를 JSON 으로 처리하는 부분을 작업한다
    • JSON 을 단순히 array 가 아니라 class 로 mapping 해서 처리
    • PHP 에서의 class 생성 및 취급에 대한 이해
  4. Phase 4
    • 두번째에 작업했던 POST 테스트를 기준으로 Pharo 에서 POST 전송에 대한 예제 작성
    • Pharo 에서 작업하는 방법에 대한 학습
  5. Phase 5
    • Pharo 에서 JSON 형식으로 데이터를 전송하는 코드를 작성
    • http 를 디버깅하는 방법에 대한 학습


위의 과정을 통해 JSON 과 PHP 를 연동하는 초 소규모 프로젝트를 진행하였다.


Lesson 2 : 주고받을 데이터 규격 정하기

JSON 은 이미 많은곳에서 쓰이고 있는 데이터 포맷으로서 그 표현방법이 간단한 것이 특징이다. 또한 일반 String 이기 때문에 사용하는 쪽에서 원하는대로 변형해서 사용할 수 있는 편한 포맷이기도 하다.

대부분의 언어에서 배열(array) 또는 Dictionary 타입으로 처리하고 있다. 그 규칙은 여기에서 일일히 설명할 필요는 없을듯 하고... 본인은 다음과 같은 기본 JSON 예제를 정했다.

{"newstitle":"AAA","newstext":"BBB","newsurl":"한글"}


위의 JSON 에제에서 field 는 3개이며 각 필드에 대응하는 값도 각각 1개씩이다. 배열로 따지자면 2차원 배열로 생각하면 정확하다.


Lesson 3 : PHP post stream 예제 작성

PHP 에서는 서버와 network client 간 데이터를 받기 위해 http 에서 허용하는 방법에 의거 다음과 같은 방법을 지원한다

  • $_GET
  • $_POST


하지만 http protocol 에서 사용하는 POST 를 응용하는 방법으로서 raw 로 데이터를 날리는 방법이 있는데 curl 에서는 data-ascii 로 명명되는 방법이다. 여튼 이 방법을 사용하면 $_POST 변수에서 값을 얻을 수 없는 대신 다른 방법으로 POST 전송값을 받을 수 있다.

file_get_contents('php://input')


PHP 에서 이렇게 POST 값을 얻을 수 있게 curl 을 이용해서 데이터를 전송하는 방법은 다음의 코드를 참고한다. 물론 linux 의 shell 상에서 사용하는 방법이니, 다른 OS 와 다른 도구를 사용하는 경우에는 적합한 방법을 사용하면 된다.

curl -H "Content-Type: application/json" -X POST -d '{"newstitle":"AAA","newstext":"BBB","newsurl":"한글"}' http://192.168.1.17/sample_01.php


기본적으로 위의 방법을 통해 값을 받는다면 php 코드는 다음과 같이 구성될 것이다.(sample_01.php)

echo file_get_contents('php://input');


curl을 실행하면 터미널에서 사용자가 curl 을 통해 전송한 문자열 그대로를 다시 확인할 수 있을 것이다.


Lesson 4 : PHP post stream 값을 JSON 으로 처리하기

일단 앞의 단계에서 봤던건 json 으로 받은 값을 단순히 일반 PHP 변수로 사용한 경우이다. 이제 이걸 단계적으로 array 로 처리하고, 이것을 다시 PHP 의 class 로 처리하는 방법을 고민해 보도록 하자. 일단 post stream 으로 받은 값을 일반 변수가 아닌 array 로 처리하기 위해서는 다음의 과정을 사용하면 된다.

$data = json_decode(file_get_contents('php://input'));


이것을 PHP 에서 gettype() 함수를 사용하면 array 라는것을 알 수 있다. 물론 PHP 는 스크립트 언어의 정도를 걷기 때문에 data 가 mapping 되기 전에는 type 이 정해지지 않은 상태이므로, 대입한 값에 따라 이것이 array 가 된다는것을 알 수 있다. 그렇다면 이 값을 출력하는 전체 코드는 어떻게 될까?(sample_02.php)

$data = json_decode(file_get_contents('php://input'));

echo $data["newstitle"]."<\ br>";
echo $data["newstext"]."<\ br>";
echo $data["newsurl"]."<\ br>";


물론 web 으로 출력했을때를 기준으로 하고 있기 때문에 curl 로 값을 보내는 경우 보기가 힘들수도 있다. 터미널에서 확인하려면 다음과 같은 코드를 사용하면 보다 편리하게 확인할 수 있다.

$data = json_decode(file_get_contents('php://input'));

echo $data["newstitle"]."\r\n";
echo $data["newstext"]."\r\n";
echo $data["newsurl"]."\r\n";


curl 을 통해 PHP 로 전송된 값을 array 형태로 확인할 수 있었다면, 이제 최신 트렌드(?)를 위해 이 코드를 PHP 의 class 를 사용하는 형태로 바꿔 보도록 하자. 윗부분에서는 이러한 형태가 되어야 할 것이다.(sample_03.php)

$myObj = new stdClass;

$myObj = json_decode(file_get_contents('php://input'));


어떤 부분이 달라졌는지 알아보기란 그리 어렵지 않다. 앞의 예제에서는 $data 라는 변수를 바로 확인했다면 여기서는 $myObj 라는것이 Class 라는 것을 미리 정의해주고 있다. 제대로된 객체지향 언어라면 instance 등을 일일히 설명해야 하겠지만 PHP 에서는 큰 의미가 없기 때문에 이 부분에 대한 설명은 생락한다. 여기서 보이는 stdClass 는 PHP 의 표준 class 로서 특정한 타입이 아닌 비어있는 Class 를 사용하는데 가장 기본적인 명령이 된다. 이 class 를 사용하지 않는다면, 인터넷에서는 다음의 방법을 알려주기도 한다.

class stdObject {
    public function __construct(array $arguments = array()) {
        if (!empty($arguments)) {
            foreach ($arguments as $property => $argument) {
                $this->{$property} = $argument;
            }
        }
    }

    public function __call($method, $arguments) {
        $arguments = array_merge(array("stdObject" => $this), $arguments); // Note: method argument 0 will always referred to the main class ($this).
        if (isset($this->{$method}) && is_callable($this->{$method})) {
            return call_user_func_array($this->{$method}, $arguments);
        } else {
            throw new Exception("Fatal error: Call to undefined method stdObject::{$method}()");
        }
    }
}

$myObj = new stdObject();


다만 이런식으로 myObj 의 상세명세가 들어가는 경우라면, 분명 정상적인 객체처럼 동작하기는 하지만 편하게 사용하는데는 신경쓸 것이 많으므로, 일단은 stdClass 를 사용하는 것으로 한다.. stdClass 를 사용하는 경우 PHP 코드는 다음과 같이 작성될 수 있다.(sample_04.php)

$myObj = new stdClass;
$myObj = json_decode(file_get_contents('php://input'));

echo ($myObj->newstitle)."\r\n";
echo ($myObj->newstext)."\r\n";
echo ($myObj->newsurl)."\r\n";


기본적으로 내용 자체가 틀려지지는 않았다. 다만 array 를 사용하는 방법에서 class 의 property 를 사용하는 방법으로 바뀌었으나 문법상 접근하기 그리 어렵지는 않다. $data 라는 변수가 $myObj 라는 class 로 바뀌었으니 그 부분은 주의해야 한다. 이러한 방법을 통해서 array 로 데이터를 처리하는 부분은 class 를 이용해서 데이터를 처리하는 코드로 개선되었다.


사실 굳이... 이런 번거로운 방법을 쓸 필요는 없지만, 적어도 객체지향의 종가로 인정받을 수 있는 smalltalk 을 사용하는 일련의 예제에 대한 일종의 깔맞춤? 정도의 무언가라고 생각해도 될것 같다. 참고로 본인은 코드를 매우 단순하게 적었지만, php 를 통해서 반환되는 값을 표준적인 상태로 디버깅 또는 모니터링 할 수 없는 경우라면 다음의 코드등으로 전송되는 값을 파일로 써서 코드를 수정하는데에 참고할 수 있다.

$data = file_get_contents('php://input');

$file = 'trace_log.txt';
$log_file = fopen($file, "a");
fwrite($log_file, $data."\r\n");
fclose($log_file);


저장되는 파일명은 trace_log.txt 가 되며 PHP 의 post stream 으로 들어오는 값을 일반적인 text 파일로 output 을 하게 된다. 이때 주의할 점은 다음과 같다

  1. 생성되는 파일의 인코딩이 utf8 이 아니라면 ASCII 영역을 벗어나는 문자가 들어왔을때, 올바르게 파일에 쓰여진다는 보장은 없다.
  2. shell 의 환경변수(LANG, LC_ALL)가 UTF-8 로 맞춰져 있지 않다면 생성되는 txt 파일의 인코딩을 그 누구도 보장하지 못한다.
  3. txt 파일의 생성은, 물론 구축된 web 환경을 따라가게 된다. permission 도 마찬가지. 본인의 경우는 php-fpm 을 사용하기 때문에 nobody 라는 uid,gid 에 대해 해당되는 web base directory 의 권한을 조정해 주었다. 이 부분은 익숙하지 않다면 인터넷을 통해서 검색하기 바란다.


Lesson 5 : Pharo 에서 POST 값을 전송하는 코드의 작성

일단 앞의 과정에서 PHP 는 일반적인 POST 값을 처리할 수 있으며, post stream 의 값을 정상적으로 처리할 수 있는 코드를 작성하고 이를 확인할 수 있었다. 이제 Pharo 에서 작성되는 코드를 위해서 일반적인 POST 값을 모두 표준출력하는 PHP 코드를 작성해 보도록 하자.(post_test.php)

echo var_dump($_POST);


와우.. 매우 간단하다. 어차피 길게 사용할 것도 아니니, 굳이 긴 코드를 만들 필요는 없다. 그럼 이 코드를 기반으로 Pharo 에서 POST 기본 전송을 테스트 해보기로 하자.


일단 POST 기본전송을 테스트하기 전에 Pharo 5.0 에 대해 간단한 설명을 하고 이후 과정을 진행하기로 한다. 전통적인 Smalltalk 은 3개의 창[1]을 가지고 있다. 어떤게 Smalltalk 의 전통적인 구성요소일까?

  1. workspace
  2. transcript
  3. class browser(aka, System Browser)


위의 3가지를 일일히 설명하는건 Smalltalk system 을 통째로 설명하는게 되니 여기서는 자세한 설명은 생략하겠다. 하지만 적어도 2가지는 알고 있어야 이후의 설명이 편하니 조금은.. 더 알아보도록 하자.


전통적인 smalltalk System 은 "workspace"[2] 라는 작업공간을 가지고 있다. 이 기능은 코드를 작성하고, 테스트하며 직접 실행할 수 있는 기능이다. 단순한 메모장의 용도로 사용할 수도 있지만, 대부분의 경우 이 창은 테스트 코드 작성용으로 쓰인다. 최신버전의 Pharo(5.0) 에서는 workspace 가 "Playground" 라는 이름으로 제공되고 있으며 기능은 동일하다.[3] 이 글을 쓰는 시점(2017/05/12)에서 Squeak 등은 아직 workspace 라는 이름으로 제공되고 있지만 smalltalk 문법의 강조등의 기능이 없기때문에 내장 기능의 차이는 있는것으로 판단하는것이 좋다.


transcript[4] 는 일종의 "표준 출력" 이라고 이해하면 된다. 기본적으로 GUI 자체가 태생인 Smalltalk 은 터미널 또는 text 환경같은 별도의 표준출력을 지원하지 않는다(ex: printf(C언어), echo(PHP)). 때문에 출력값을 확인하기 위해서 별도의 표준출력을 사용하는데 그게 바로 "transcript" 이다. 대부분의 Smalltalk 구현체에서 "Transcript" 라는 전역 Object instance 로서 취급되기 때문에, 여러개의 transcript 를 띄운다고 해서 큰 의미는 없다.[5]


이러한 playground 와 transcript 에 대한 기본 지식을 바탕으로 아래의 코드를 실행해보자. 물론 입력은 playground 에서 해야함을 기억하길 바란다.

"basic post test"
| response content headers |

response := ZnClient new
    url: 'http://192.168.1.17/post_test.php';
    formAt: 'search-field' put: 'Pharo Smalltalk';
    post;
    response.

content := response contents.


위의 코드에 대해, 간단한 사실 몇가지만 알아보자.

  1. 큰 따옴표(") 로 묶이는 경우는 주석을 의미한다
  2. pipe(|) 문자로 묶이는 경우는 변수를 의미한다
  3. Smalltalk 에서 http 를 보다 단순하게 이용하기 위해서 "ZINC" 패키지를 이용한다
  4. ZnClient 는 ZINC 의 client side 에서 사용하기에 편한 class 이다
  5. php 서버의 주소는 "192.168.1.17" 이다
  6. php 프로그램의 파일 이름은 "post_text.php" 이다
  7. POST(http) 를 기준으로 첫번째 key 값은 "search-field" 가 된다.
  8. POST(http) 를 기준으로 첫번째 value 값은 "Pharo Smalltalk" 이 된다.
  9. 맨 아래줄의 의미는 다음과 같다
    • response 는 ZnClient 를 이용해서 http 로 서버에 접속된 network discriptor
    • "response contents" 는 response 클래스(network discriptor) 안의 http 본체내용(전송 및 결과 수신 buffer)
    • content 는 위에서 선언된 null object 로서, 일반 변수(variable container)이다.


위의 코드를 마우스등을 이용해서 전체로 선택한뒤 출력(PrintIt / Ctrl + P)를 이용해서 실행하면 아래의 화면과 같은 결과를 확인할 수 있다. (물론 서버 주소 및 파일명은 개발 환경에 맞춰 바꾸면 된다)

코드 01 Print It 출력


스크린샷의 노란 부분이 보이는가? PHP 에서 "var_dump()" 함수를 이용해서 출력된 내용을 그대로 볼 수 있다. 한번더 "출력" 을 진행하면 workspace 에 해당되는 text 가 삽입된다. 하지만 굳이 그럴 필요가 없다면 "ESC" 키를 이용해서 노란부부을 닫을 수 있다. 굳이 그런걸 확인하고 싶지 않으면 실행(DoIt/ Ctrl + D)를 이용해서 실행만 하면 된다. 다만 이 경우 제대로 값을 확인할 수 없기 때문에 2개의 방법은 번갈아가며 사용하는것이 좋다.


여튼 위의 코드를 실행해서 스크린샷과 비슷한 화면을 확인할 수 있었다면, ZnClient 의 사용법을 조금 더 복잡하게 해서 동일한 작업을 하는 코드를 작성해 보도록 하자. 물론 지금 사용하는 모든 코드를 이후에 사용하지는 않겠지만 ZnClient 의 구조를 파악하는데는 더없이 좋다고 생각한다.

"expert post test"
| response content headers |

response := ZnClient new
	systemPolicy;
	http;
	host: '192.168.1.17';
	path: 'post_test.php';
	headerAt: 'Accept' put: 'text/html; charset=UTF-8';
	accept: ZnMimeType textHtml;
	formAt: 'search-field' put: 'Pharo Smalltalk';
	ifFail: [ :exception | self inform: 'I am sorry: ' , exception printString ];
	post;
	response.
	
content := response contents.


코드 02 Print It 출력


노란색 박스를 보면 앞의 코드와 동일한 결과가 나온다는걸 확인할 수 있다. 이제 당신의 Pharo 는 정상적으로 코드가 동작하며 network 통신 역시 제대로 되고 있다는것을 간단하게 확인할 수 있다. 이제 아까 만든 JSON PHP 예제와 통신하기 위해 다음 단계로 진입해 보도록 하자.


Lesson 6 : Pharo 에서 JSON 데이터를 post stream 방식으로 전송

지금까지 과정을 착실하게 습득했다면 이후의 내용을 이해하는 것은 그리 어렵지 않을것이라 생각한다. 일단 예제코드를 보고 이후의 내용을 진행하도록 한다. 코드를 진행하기 전에 Pharo 에서 JSON 을 사용하기 위한 NeoJSON[6] 패키지를 설치해보자. "Playground" 에서 아래의 코드를 실행(DoIt)하기만 하면 된다.

Gofer it
   smalltalkhubUser: 'SvenVanCaekenberghe' project: 'Neo';
   configurationOf: 'NeoJSON';
   loadStable.


화면 한 구석에 몇개의 진행창이 지나간다음 더이상 아무것도 보이지 않을때 "transcript" 를 열어서 아래와 같은 text 를 확인할 수 있다면 설치가 종료된 것이다. "Shift + Enter" 키를 눌러서 "Spotter" 를 호출한 다음에, "NeoJSON" 을 입력해서 패키지가 당신의 Pharo 에 설치되었는지 확인해보자.

NeoJSON 설치 확인


위와 같은 화면을 볼 수 있다면 Pharo 에 NeoJSON 설치가 정상적으로 진행된 것이다. 이제 아래의 코드를 입력해서 PrintIt 을 진행하면 당신의 서버에 있는 PHP 프로그램으로 json 값을 보내며, 이것이 온전한 json 값인지는 PHP 프로그램에서 처리과정을 통해 검증하면 된다. Smalltalk 코드만이 아니라 PHP 코드도 함께 개선해 보도록 하자. php 에서는 지금까지처럼 PHP object 의 element 를 하나씩 보내는것이 아니라, $myObj 객체를 json_encode() 함수를 통해 다시 json 으로 만들어서 보내는 것으로 한다.

"Final Example"
| response content resultfromweb dataorig jsonorig |

dataorig := '{"newstitle":"AAA","newstext":"BBB","newsurl":"한글"}'.

jsonorig := NeoJSONReader fromString: dataorig.

response := ZnClient new
	url: 'http://192.168.1.17/sample_05.php';
	accept: ZnMimeType applicationJson;
	contents: ( ( NeoJSONWriter toString: jsonorig) asString );
	ifFail: [ :exception | self inform: 'I am sorry: ' , exception printString ];
	post;
	response.
	
resultfromweb := (response contents).


$myObj = new stdClass;
$myObj = json_decode(file_get_contents('php://input'));

echo (json_encode($myObj))."\r\n";


위의 Smalltalk 코드중에서 생소한 부분 또는 흐름을 간단하게 분석해 보자.

  • 맨 처음에 dataorig 에 값을 대입하는 건 dataorig 객체를 "String" 클래스로 취급하게 된다.
  • NeoJSONReader 를 이용해서 dataorig 객체를 jsonorig 에 대입하는 경우, jsonorig 는 "Dictionary" 클래스가 된다.
  • post stream 전송을 위해서 http 의 header 를 JSON 으로 맞춘다.
  • 중간의 ifFail 은 Znclient 를 이용해서 작업을 진행할떼 실패하는 경우에 대한 exception 처리를 의미한다.


나머지 부분은 설명이 굳이 필요하지 않을듯 하다. 지금까지처럼 json 값을 PrintIt 으로 확인할 수 있으면 성공이다. 물론 이후에 PHP 프로그램에 어떠한 server-side 의 동작을 추가하더라도 정상적으로 동작할 것을 보장하며, 지금의 상태로서 당신의 PHP 프로그램은 내부의 error 까지 json 의 형식으로 Pharo 시스템에 전달할 수 있다.


Extra Lesson : 읽으면 도움되지만 딱히 읽을 필요는 없는...

Advance Mission

가장 마지막의 |sample_05.php" 와 통신하는 Smalltalk 코드에 다음의 코드를 요령껏 적용해보자.

headerAt: 'Accept' put: 'text/html; charset=UTF-8';


이 코드와 "contents:" 에 대한 코드가 어떤식으로 다르게 작용하는지 ZnClient 의 header 를 변경하는 method 등을 바꿔가며 테스트해보면 보다 재미있는것들을 볼 수 있다.


Smalltalk Tip : Smalltalk 프로그램 개발과 Debug 문화

이 부분은 기존의 다른 개발툴에서도 적용되는 부분이 있지만 Smalltalk 에서의 디버깅은 그 특징이 조금 더 독특하다. 살아있는 객체(Live Object) 라는 Smalltalk 시스템의 성격상 좀 더 독특한 부분에 대해 살짝 알아보자

  • Do It : Smalltalk 코드를 실행한다
  • Debug It : 코드를 디버거와 함께 실행하며 한줄 한줄 step trace 를 해가며 디버깅을 할 수 있다. 이 상황에서 해당되는 코드가 실행되는 도중에 대한 모든 변수와 값을 즉각적으로 확인할 수 있다. 또한 사용한 Class 의 sequence 를 따라 Debugger 가 진행되기 때문에 Class 의 step hierarchy 를 확인하고 이를 학습할 수 있다
  • Print It : 코드의 실행 결과를 알 수 있다. 가장 마지막으로 access 되는 변수의 내용을 표시해준다. 물론 경우에 따라서는 "transcript" 를 이용해서 디버깅 하는 사람들도 있다
  • Inspect It : 코드를 실행하고, Inspector 를 띄운다. Inspector 에서 Debugger 보다는 좀 더 간단한 정보를 표시함으로서 해당되는 코드의 실행 과정을 추적할 수 있다


Smalltalk 코드에서 자동 완성을 사용하는 경우도 있지만 Pharo 의 "Playground" 에서 Class 이름으로 추정되는 부분은 마우스로 더블클릭해서 해당되는 text 를 선택하고, "Ctrl + B" 단축키를 통해서 해당되는 Class 의 내용을 "System Browser" 에서 확인할 수 있다. 어떤 method 등을 사용할 수 있는지등의 해당 Class 에 대한 정보를 실제 Smalltalk 코드와 함께 확인할 수 있기 때문에, Smalltalk 시스템 내에서라면 이런 방법은 매우 유용하게 쓰인다.


이 외에 Pharo 5 만의 특징인 "Spotter(Shift + Enter)" 를 이용하면 보다 편한 Pharo 프로그래밍을 할 수 있다.


Debug tip : tcpdupmp

중간에 이런저런 내용들을 찾아보면서 Pharo 의 ZnClient 의 post stream 에 대해 명확한 사용법이 나와있는 것을 찾을 수가 없었기 때문에 결과적으로는 tcpdump 를 통해서 Pharo 에서 어떤 http stream 을 보내는지를 확인해야할 경우가 있었다. 예를 들자면 아래와 같은 dump 내용이 된다.

13:27:09.048025 IP 192.168.1.16.42688 > 192.168.1.17.http: Flags [P.], seq 1:249, ack 1, win 229, options [nop,nop,TS val 873547273 ecr 3422016210], length 248: HTTP: POST /json_sample_04.php HTTP/1.1
E.., j@.@..............P..fa...0...........
4.F	....POST /json_sample_04.php HTTP/1.1
Host: 192.168.1.17
Content-Type: text/plain;charset=utf-8
Accept: application/json
Content-Length: 55
User-Agent: Zinc HTTP Components 1.0 (Pharo/6.0)

{"newstext":"BBB","newstitle":"AAA","newsurl":"......"}
13:27:09.048108 IP 192.168.1.17.http > 192.168.1.16.42688: Flags [.], ack 249, win 235, options [nop,nop,TS val 3422016212 ecr 873547273], length 0
E..4..@.@............P.....0..gY...........
....4.F	
13:27:09.049201 IP 192.168.1.17.http > 192.168.1.16.42688: Flags [P.], seq 1:432, ack 249, win 235, options [nop,nop,TS val 3422016213 ecr 873547273], length 431: HTTP: HTTP/1.1 200 OK
E.....@.@............P.....0..gY.....G.....
....4.F	HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Fri, 12 May 2017 13:27:09 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
X-Powered-By: PHP/7.0.15

c8
{"newstext":"BBB","newstitle":"AAA","newsurl":"......"}<br />


http header 가 제대로 보내지지 않으면 web daemon 및 PHP 시스템은 정상적으로 동작하지 않으며, Pharo 시스템에서 건네지는 값을 받을 수가 없다. 물론 사람에 딸 tcpdump 를 사용하지 않고 "wireshark" 등의 도구를 사용해도 좋다. tcpdump 의 경우는 다음과 같이 간단하게 사용할 수 있다. 물론 당신의 client(개발PC) 에서 이걸 실행한다면... 당신은 80 port 로 오가는 모든 데이터를 구경하게 될 것이기 때문에 그건 그리 권장하고 싶은 방법이 아니다. 가능하다면 데이터의 전송을 제어할 수 있는 개발서버에서 tcpdump 를 실행하기 바란다.

tcpdump -A -i 랜카드이름 tcp port 80


현대의 linux 에서 당신 서버의 랜카드 이름정도는 터미널에서 "dmesg | grep" 명령등을 사용해서 쉽게 알아낼 수 있다. 이 부분은 인터넷을 통해 참고하기 바란다.


Smalltalk 과 charactor Encoding

smalltalk 의 인코딩 체계를 알면 상대적으로 외부 환경과 문자를 다루는데 있어 보다 도움이 될거라고 생각해 정리해봅니다.

  • linux : 설정에 따라 다르지만 UTF-8(NFC)
  • mac os : classic 및 OS X 모두 UTF-8(NFD)
  • MS-windows : UTF-16(widechar)
    • FAT32 : UTF-8(NFC)
    • NTFS : UTF-16
  • Pharo(Squeak) : UTF-16


기본적으로 Pharo 내부에서는 UTF-16 을 사용하고 있습니다만, ZINC 등을 사용해서 http header 를 기본값인 UTF-8 로 사용하는 경우라면, UTF-8(NFC) 로 인코딩해서 내보내게 되어 있습니다. ZINC 는 내부의 "ZnCharacterEncoder" 를 통해서 이런 작업을 자동으로 처리하고 있죠. 하지만 tcpdump 등을 통해서 해당되는 인코딩의 결과를 하나하나 추적하고 싶다면, Pharo 에서 넘어온 값(get,post,post stream)을 PHP 에서 file 로 저장한 다음에 hexdump 등을 통해서 HEX 값을 확인하면 됩니다. 기 값이 Pharo 에서 어떤식으로 생성되는지 알아보기 위해서는, Pharo 에서 다음의 코드를 PrintIt 으로 추적할 수 있습니다.

'가' utf8Encoded.


이 코드를 통해서 다음과 같은 값을 얻을 수 있습니다.

#[234 176 128]

보이는 이 값은 Pharo 내부에서 "ByteArray" 타입으로 처리되어 반환됩니다. 제대로 인코딩이 전달되는지를 알아보려면 이러한 방법등을 이용해서 당신의 데이터를 디버깅할 수 있습니다.


참고자료

Notes

  1. 물론 하나하나가 Smalltalk 의 instance object 인데 그걸 설명하는것은 이 문서의 기본 취지에서 어긋나니 생략하기로 한다.
  2. http://wiki.squeak.org/squeak/1934
  3. Ctrl + O + W
  4. http://squeak.org/documentation/terse_guide/
  5. Ctrl + O + T
  6. https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/NeoJSON/NeoJSON.html