수요일, 11월 20

Mobile seamless Handover on WIFI with hostapd, freeradius and SDN #3 SDN handover flow




Mobile seamless Handover on WIFI


 이번 글에서는 SDN을 통해 핸드오버, interworking을 구현해보고자 한다.
 전 글과 망 구성 및 이론은 동일하다.




 전 글에서는 동일 IP를 할당해줌을 통해서 접속 지점인 AP는 바꼈지만 실제 네트워크의 구성은 바뀌지 않은 상태였기 때문에 추가적인 처리 없이도 인터넷 스트리밍이 잠시의 끊길뿐 연결이 지속되는 것을 볼 수 있었다. 만약 다른 IP를 할당받더라도 같은 네트워크에 속해있다면 라우터 혹은 스위치가 RARP프로토콜을 통해 등록된 MAC을 보고 IP를 연결해줄 수 있기 때문에 동일한 효과를 볼 수 있다. 


 그러므로, SDN을 통한 handover를 구현 및 테스트해보고자 한다면 
 1. 서로 다른 네트워크로 구성된 AP 
 2. 이 둘을 컨트롤 할 수 있는 SDN Controller 

 두 가지 조건이 갖춰져아 한다는 점을 기억해야 한다. 
 미리 밝혔듯 앞선 조건을 미리 인지하지 못했기에 아직까지 이론만 갖춰져 있을뿐, 실제로 테스트를 하여 결과를 확인한 것은 아니다. 


 x86 base switch에는 내장 랜카드 외에 두 개의 추가 랜카드를 설치하고 Raspberry AP들을 연결시킨 후에 openvswitch를 통해 하나의 스위치처럼 묶어야 한다. 

ovs-vsctl add-br my-switch 
ovs-vsctl add-port my-switch eth1 
ovs-vsctl add-port my-switch eth2

 위와 같은 명령을 치면 openvswitch의 가상 스위치인  my-switch에 eth1과 eth2가 등록된다.

 ovs-vsctl show 

 위의 명령을 통해 이를 확인할 수 있다. 

 이제 SDN controller에 이 스위치를 연결해주는데, 필자는 floodlight를 사용했다. 

 ovs-vsctl set-controller my-switch tcp:<controller-ip>:6633

 6633은 floodlight의 기본 설정 포트이다. 제대로 등록됐다면 ovs-vsctl show 명령을 통해 컨트롤러가 연결되었음을 확인할 수 있을 것이고 floodlight web-ui를 통해 관련 정보를 쉽게 파악할 수 있을 것이다. 


 SDN 을 통해 handover를 구현하기 위한 시나리오를 생각해보면,  Raspberry AP 1 에서 Raspberry AP 2로 기기가 연결 지점을 바꾸는 것이 SDN Controller의 입장에서 보면  Raspberry AP1이 연결된 포트에서 Raspberry AP2가 연결된 포트로 패킷을 보내주게 바뀌는 것이다. 

 디바이스가 AP1 --> AP2 로 연결 변경
 Flow는   AP1 Port --> AP2 Port 로 변경

 여기서 필요한 것은 디바이스에게 오는 패킷을 식별할 값인데 MAC을 사용하면 된다. 이제 시나리오는 MAC 값을 기반으로 디바이스가 연결 지점을 바꾸면 들어오는 패킷을 바뀐 연결 지점으로 보내주는 것이다. 

 MAC값을 기반으로 패킷의 output을 변경. 


 추가적으로 더 처리해줄 것이 있는데, IP값이다. 디바이스가 연결지점을 바꾸며 동시에 IP를 재 할당받기 때문에(DHCP) 이에 관련된 제어가 필요하게 된다. 그러므로 패킷의 output 변경 외에 Destination IP 또한 연결을 바꾼 기기의 현재 IP에 맞게끔 바꿔줘야 한다. 


 MAC값을 기반으로 패킷의 output과 디바이스의 현재 IP로 Dst.IP를 변경


 이것이 handover의 최종 시나리오이다. 


 이에 따른 코드는 github를 통해 찾을 수 있다. 

 https://github.com/Bhb2011/floodlight-for-study/blob/master/src/main/java/net/floodlightcontroller/mactracker/MACTracker.java


 테스트


 이렇게 작성한 코드로 테스트를 해보면 디바이스가 AP를 이동할 시에 floodlight UI에 아래 그림과 같은 flow가 자동으로 등록된다.






 아래의 flow가 handover시에 등록되는 플로우이다.
설명하자면, MAC이 84:38:35:5f:04:94이면 3번 포트로 내보내고 dst.IP를 -1408211540으로 바꾸라는 뜻이고, 대충 172.16.101.XXX를 뜻한다. 그 뒤에 해당 플로우와 일치하는 패킷 수, 바이트 등의 정보가 표시되는데 업데이트가 일정 시점 이후에는 되지 않는 버그가 있다. 




 이론 상으로는 동적 Flow할당을 통해 AP를 변경한 디바이스의 맥 주소를 기준으로 디바이스의 현 IP로 바꾼 패킷을 그대로 전달해주므로 핸드오버의 구현이 되었다고 보이나, 실제 망에서 테스트하기 위해서는 빠른 IP할당 등의 여러가지 갖춰져야할 조건들이 있고 이들이 실제 handover의 테스트에 미치는 영향이 크기 때문에 실제 테스트 결과는 보지 못했다.










































화요일, 11월 5

Mobile seamless Handover on WIFI with hostapd, freeradius and SDN #2 802.11u interworking



Mobile seamless Handover on WIFI


1편에서는 잡담과 함께 기존의 이통사의 망을 살펴보고 이를 통해 핸드오버가 가능하게 하는 요소들에 대해서 정리를 했었다. 이를 다시 한번 살펴보면,

1. 핸드오버가 될 때 이를 중계할 수 있는 S-GW와 같은 장치가 있어야 하고
2. 같은 클라이언트 기기라는 것을 알 수 있는 어떤 인증 혹은 장치 관리 기법이 있어야하고
3. 클라이언트를 연결시켜줄 AP가 필요하다.

이를 와이파이 환경에서 구축하기 위해 필요한 툴 들을 정리해보면 다음과 같다.

1. S-GW = SDN
2. 인증장치 = Freeradius
3. AP = hostapd
4. Client = hotspot2 && 802.11u interworking

Client & Handover

 전 글에서는 다루지 않았지만 Client가 추가되었는데 이유는 이통사들의 핸드오버 기술은 3G이상의 망에서 다룬 것으로 Client에는 당연히 해당 통신망에 따른 장치와 제어 기술이 들어 있는것이 당연하기 때문이다.

 부연설명을 조금 더 하자면, 클라이언트가 아무런 알림 없이 기지국을 변경한다면 어떻게 될까? 이런 상황에서도 끊김없는 핸드오버가 가능할 것인가? 그렇지 않다고 본다. 왜냐하면 기존 wifi 처럼 연결을 끊고 새로 연결을 맺는데 몇 초씩 소모되지 않더라도 분명히 연결이 끊겨있는 시간이 있기 때문이다.

 때문에 클라이언트는 이런 특징을 가지고 있어야 한다.
 현재 AP에서 다른 AP를 검색 후 적합한 곳인지 판단되어 핸드오버를 할 시에 이를 미리 알리는 것인데 이에 대해 정해진 확정 프로토콜이나 기법은 무선랜에는 아직 존재하지 않는다.

 대신 802.11u spec은 핸드오버를 위한 표준을 제공을 제공해준다.
 (이제부터는 핸드오버 대신 네트웍 간 이동이라는 의미로 interworking이라는 용어를 사용)

 interworking을 위하여 클라이언트는 이동할 AP를 탐색해야 하고, 해당 AP를 이용할 수 있는지 등 여러 정보가 필요한데 이를 GAS/ANQP 라는 이름으로 정립해놨다. 또한 802.11u에 따르면 preauth기능등 여러가지 측면에서 핸드오버시 지연 요소들을 최소한으로 하기 위한 요소들을 만들었음을 알 수 있다.

  필자는 interworking을 구현하기 위해 wpa_supplicant 개발 버전 2.1-devel을 사용했는데 interworking을 지원하는 어떤 다른 방법이 있다면 사용해도 괜찮을 것 같다. 참고로 현재 Samsung Galaxy S3가 hotspot 2.0이란 이름으로 지원하므로 interworking 관련 기능을 사용할 수 있을 것이지만 지원하는 인증수단이 EAP-TLS 뿐이므로 인증서 관련 작업을 해줘야하는 번거로움이 있다.





네트워크 구성도

 interworking(=핸드오버)를 위한 네트워크 구성은 아래와 같다.
 RaspberryPi ( hostapd, Freeradis, openvswitch ) 2대
 x86 Linux Desktop ( SDN-Controller Floodlight, INTEL Lan-card x2 ) 1대

 2대의 RaspberryPi를 AP로 두고 디바이스가 AP를 이동 시에 SDN Controller가 디바이스의 MAC을 식별값으로 삼아 패킷을 전달해주는 방식이다.






Raspberry AP


 필자는 seamless handover를 구현해보기 위해 Raspberry pi 2대와 hostapd를 지원하는 무선랜카드 2종을 구입하였다.


 hostapd.handover.conf
====================================
# Lose the comment after its line

ssid=RaspberryAP_2
interface=wlan0
# The interface name of the card
driver=nl80211
bridge=br0
macaddr_acl=0
hw_mode=g
channel=1
#accept_mac_file=/etc/hostapd.accept
#deny_mac_file=/etc/hostapd.deny
ieee8021x=1    # Use 802.1X authentication
#wep_key_len_broadcast=13
#wep_key_len_unicast=13
#wep_rekey_period=300   # WEP rekeying interval
own_ip_addr=127.0.0.1  # The interface IP
nas_identifier=localhost
auth_server_addr=127.0.0.1 # Where is the radius server
auth_server_port=1812      # The port the radius server runs on
auth_server_shared_secret=password
# This is used to authenticate the hostapd to the radius server so none can use your raduis server
#acct_server_addr=127.0.0.1 # Where is the radius server
#acct_server_port=1813      # Where the accounting port is (I think)
#acct_server_shared_secret="password" # Same as above
wpa=2
wpa_key_mgmt=WPA-EAP
wpa_pairwise=CCMP
wpa_pairwise=CCMP

interworking=1
access_network_type=14
internet=1
asra=0
esr=0
uesa=0
venue_group=2
venue_type=0
roaming_consortium=2233445566
venue_name=P"eng:Example\nvenue"
domain_name=example.com
nai_realm=0,example.com,13[5:6],21[2:4][5:7]

hs20=1
rsn_preauth=1
#rsn_preauth_interfaces=wlan0
==========================================

 interworking 스펙상WPA-PSK 인증은 지원하지 않고, 반드시 EAP(=Extended Authorize Protocol  : 확장인증규약 )을 사용해야 한다. 왜냐하면 interworking은 단순히 한 AP에서 다른 AP로 넘어가는 것을 의미하는 것이 아니기 때문이다.

 interworking은 사용자가 거대한 네트워크 집합체에서 현재 접근 지점을 바꾸는 것을 의미하는 것이며, 더 이상 디바이스가 인증했떤 AP들의 인증 정보를 저장하고 있는 것이 아닌, 하나의 인증 정보를 가지고 이를 활용할 수 있는 불특정 다수의 AP를 찾아 접속지점을 바꾼다는 개념이다. 이를 Hotspot 2.0이라고 부르는데 검색해보면 마케팅의 선두주자 Apple이 얼마나 입을 털고 있는지 알 수 있다.

 어쨌든 EAP 인증 수단을 사용하기 위해서는 인증 서버를 만들어야 한다.
 만약 전 글에서 통신사들이 구축하고 있는 통신망의 핸드오버 정보를 자세히 보고 왔다면 AAA서버라는 것을 알게 되었을텐데 이는 Authorization, Accounting, Authentication의 약자로 통합 인증서버를 뜻한다. 통신사 망에서는 중계기들이 접속되는 디바이스들의 정보를 AAA서버를 통해 등록, 관리하고 인증시켜주게 된다.

 EAP 인증 수단은 TLS, TTLS, PEAP 등 엄청 다양하게 존재하고 Hotspot 2.0 에서는 TLS만을 지원하는데 이는 인증서 기반의 인증 수단이고 미리 발급받아야 한다. 참고로 현재 이통사들의 WIFI망은 sim값을 기반으로 인증하는데 이는 EAP-AKA라고 부른다. 어쨌든 AAA서버를 구축해야 되므로 Freeradius 서버를 설치하고 PEAP/MSCHAPV2 를 통해 인증할 수 있도록 설정을 한다.  ( PEAP/MSCHAPV2를 사용하는 이유는 id/password 인증이라 매우 간단하기 때문이다! )


 꼭 하나 위의 설정에서 집고 넘어가야 할 것이 바로

 roaming_consortium

 으로, 이 값이 동일하면 interworking 할 수 있는 AP로 인식하게 된다.


 AP의 hostapd+freeradius 설정이 끝났다면 간단히 핸드폰 등을 이용해서 접속이 원활이 되는지 테스트 해보도록 한다. 설정이 제대로 되었다면 AP 접속시 우측과 같은 화면이 보이며 인증 정보를 넣도록 되어 있다.

 우측과 같은 화면이 보이는지, 인증이 되는지, IP를 제대로 할당받아 오는지를 모두 확인해서 이상없이 네트워크를 사용할 수 있다면 다음 단계로 넘어가면 된다.

 만약, Galaxy S3 이상 Hotspot 2.0 지원 기종이라면 Freeradius 설정에서 TLS를 사용하도록 하고 인증서를 만들어서 핸드폰에 넣은 후, Hotspot 옵션을 활성화하고 EAP-TLS 설정에 들어가 인증서를 등록해주면 된다.

 참고로 필자는 안해봤다.









 테스트

 테스트 환경
 VirtualBox on mac
 linux 3.X kernel, ubuntu with Gnome-GUI
 Chrome Browser for linux
 wpa_supplicant 2.1-devel
 Ralink 3072 WLAN


 테스트를 위해 wpa_supplicant를 사용한 이유는 Galaxy S3가 없기도 하고 자유롭게 클라이언트를 제어할 수 있기 때문이다. 만약 Galaxy S3와 같은 디바이스를 사용한다면 interworking을 강제할 수 없기 때문에 AP간 이동이 가능한 환경을 구축해야 할 것이다.


( 주의 : 설정은 유동적이므로 자신의 환경에 맞게끔 실행해야 한다. )


AP
==========================================
brctl로 bridge를 등록하고 이를 기본 네트워크로 삼는다.


==========================================
ifconfig eth0 0
brctl addbr br0
brctl addif eth0
dhclient br0




==========================================
hostapd를 구동한다. 

==========================================
hostapd <path>/hostapd.conf


Client

==========================================
wpa_supplicant를 구동시키고 wpa_cli를 통해 콘솔에서 무선랜을 제어할 수 있다.


==========================================
wpa_supplicant -Dnl80211 -iwlan0 -C/var/wpa

==========================================
wpa_cli 를 실행한다.
==========================================
wpa_cli -p /var/wpa

==========================================
wpa_cli에서 다음의 명령을 통해 credential을 동록한다.
==========================================
add_cred
set_cred 0 username "user"
set_cred 0 password "password"
set_cred 0 eap PEAP
set_cred 0 phase2 "auth=MSCHAPV2"
set_cred 0 roaming_consortium 2233445566


앞서 말했듯 클라이언트가 interworking여부를 결정짓는 값은 roaming_consortium 이다.
============================
interworking 가능한 AP 지점을 찾는 명령
============================
interworking_select


============================
interworking 명령
============================
interworking_connect <bssid>


============================
TEST동영상
============================




 위의 테스트는 SDN은 아직 적용되지 않은, DHCP를 통해 강제로 동일 IP를 할당받게끔 설정한 환경에서의 테스트이다. twitch.tv 라는 온라인 스트리밍 사이트를 열어놓고 AP 간 이동 테스트를 했으며 결과는 보이는 바와 같다.


 다음 글에서는 SDN을 활용하는 방법에 대해서 알아보도록 하겠다...




















목요일, 10월 10

Mobile seamless Handover on WIFI with hostapd, freeradius and SDN #1 Overview



Mobile seamless Handover on WIFI

(주의: SDN을 통한 Handover의 구현은 아직 실제 테스트되지 않았습니다. )

 구현에 앞서...

 세상에 모든 기술이 등장하는 데는 이유와 시기가 있다.
 기술을 배우는 사람으로서 이와 같은 배경지식을 갖지 못하고 새로이 배울 기술을 선택하는 것은 어리석고 위험한 행위라고 생각한다. 왜냐면, 이 기술이 왜 등장했는지를 모르니까 배우고도 제대로 사용할 수 있을지를 장담할 수 없고 적합하지 않은 상황에 성급히 도입해 손해를 볼 수도 있기 때문이다.

 또한 당장 기술을 배우는 데도 앞서 말한 배경 지식 없이는 자신이 무엇을 배워야 하는지 혹은 무엇을 해야 하는지를 생각해내기 어렵다. 주입식으로 배우는 교육은 그것이 탄탄하고 이론적이며 그 자체로 바탕이 되어 사고를 확장시켜줄 수 있는 것에 한정해야 한다.


 본론으로 들어가, 모바일 심리스 핸드오버는 어떤 기술인가?

 이는 본래 3G이상의 모바일 네트워크에서는 이미 구현되어 있던 기술이다.
 사용자가 모바일 기기를 사용 중에 기지국이 변경되더라도 느끼지 못할 정도로 부드럽게 기지국 사이를 이동시켜주는 역할을 하는 기술인데, 이 기술이 어째서 Wifi와 엮이게 된 것일까?

 기술적인 구현에 앞서 이동통신사와 와이파이의 관계에 대해서 생각을 해봤다.

 "왜 이통사들은 3g, 4g, lte, wibro, lte-a, wimax 등의 대규모 통신망을 구축하고도 당장 수익원이 되지 않은 와이파이 망을 설치하고 있는가?"

 이 의문에 대해서 생각할 수 있는 답은 두 가지가 있다.
 1. 처음에는 데이터 네트워크가 수용량의 한계를 보여서 부하 분산을 위해 와이파이 망을 급히 도입하게 되었다.
 2. 어떤 목적성을 가지고 와이파이 망을 도입하게 되었다.

 어쨌건, 보급된 네트워크를 보다 효율적으로 활용하기 위해 통신망과 와이파이 망을 상호 호환하고자 하는 움직임이 있으며, 더 나아가 IEEE에서는 802.21 mih를 통해 media에 상관없이 네트워크를 이동할 수 있는 표준을 책정했다.

 와이파이만 놓고 보면 조금 애매한 것이, 현재 와이파이 망이 ac버전에서 최대 733mbps의 속도를 낼 수 있지만 보급이 늦고, 통신사들 LTE-A에서 100mbps의 전송속도를 보여주고 있다. 추가로 대부분의 이더넷이 100mbps 광랜이기 때문에 LTE-A이상의 무선통신망이 상용화된 현 시점에 와이파이 망을 굳이 더 사용해야 할 필요가 있을지 의문이다.

 하지만 이통사들은 와이파이 망을 어떻게든 활용도를 높히기 위해 여러가지 움직임을 보이고 있고 mobile seamless handover는 그 일환이라고 생각해도 될 것 같다.


그렇다면 기존의 이통사 망에서는 핸드오버를 어떻게 구현하고 있는가?
이것을 알게되면 보다 쉽게 접근할 수 있지 않을까?

========================================
이통사 들의 핸드오버 구현.
========================================

lte망의 핸드오버 방법
출처 - netmanias LTE 핸드오버 (X2 Handover & S1 Handover)( http://www.netmanias.com/bbs/view.php?id=blog&no=281 )


보다 상세한 내용은 링크를 참고하시고, 구현은 다음 단계를 따른다.

X2 Handover
클라이언트가 AP를 이동할 시에
현재 AP와 이동 대상 AP간에 터널을 뚫어 AP를 바꾸기 이전 클라이언트의 통신이 유지될 수 있도록 한다. 핸드오버가 종료된 시점에는 터널을 제거한다.

S1 Handover
클라이언트가 AP를 이동할 시에
현재 AP와 게이트웨이 간에 터널이 생겨 이를 통해 핸드오버 중 데이터를 이동 대상 AP에게 전달하여 클라이언트에 송신한다. 핸드오버 후 이동 전 AP 와의 터널을 제거한다.



이제 핸드오버를 구현하기 위해 우리에게 필요한게 뭔지 대충 알 것도 같다.
1. 핸드오버가 될 때 이를 중계할 수 있는 S-GW와 같은 장치가 있어야 하고
2. 같은 클라이언트 기기라는 것을 알 수 있는 어떤 인증 혹은 장치 관리 기법이 있어야하고
3. 클라이언트를 연결시켜줄 AP가 필요하다.

다음 편부터 step-by-step으로 이를 연구해보도록 하자.







일요일, 8월 11

ubuntu_12_LTS] Redmine 설치 + git 연동 사용법


Redmine 설치 방법은 이 분 블로그를 따라함.
http://booiljoung.blogspot.kr/2013/01/ubuntu-1204-lts-redmine.html

*** Passenger 가 설치되지 않았을 때 ***
1, apt-get install libapache2-mod-passenger
2,a2enmod passenger
3,service apache2 restart

다만 설치를 따라하기 앞서
Mysql client 와 Server를 설치해야한다.

apt-get install mysql-server mysql-client

아니면 중간에 DB 때문에 설정을 물어봄.
하지만 DB를 먼저 설치하고 진행하면 mysql로 선택만 하면 끝~

Redmine과 apache 연동 설정이 끝나 웹으로 접속이 되면, (관리자 계정 admin:admin)
Git 연동을 시도할 차례.

먼저 git을 설치하자.


=== git  bare repository ==============

apt-get install git

( git을 설치하고나면 왼쪽 상단 관리 - 설정 - 저장소 에서 Git 체크박스가 활성화된다 )

git을 설치하면 아무 경로에나 git의 서버 repository 용 디렉토리를 만든다.
본인은 /git에 만듬

루트에 만들면 owner가 루트이기 때문에 바꿔줘야한다.
chown -R [user]:[group] git

이제 본격적으로
git upstream repository ( git 원격 저장소를 만든다. )

git init --bare test.git

test.git이 생성된 것을 확인할 수 있다.
로컬에서 해당 저장소를 테스트 해보도록 한다.

git clone /git/test.git

test 에 clone 이 됐다는 메시지가 보이면 성공이다.
이번에는 push를 해볼 차례.

cd test
touch README
git status

git status를 치면 메시지 중에 README가 보일 것이다.
README를 add 하고 커밋하고 푸시해보자.

git add README
git commit -m "test"

커밋이 문제없이 되면 push 한다.

git push

아마 push가 안될것이다.
왜냐면 branch를 정하지 않았기 때문인데,

git status

*master
만 달랑 출력될 것이다.

git push origin master

명령을 통해 branch를 설정하고, push가 되는 것을 확인했으면
다시 

git status

를 통해 branch를 확인한다. 



=== local git repository 와 remote 연동 ==================

이제 작업하던 소스를 git repository로 만들고 이를 원격 repo에 push 해보자.
프로젝트 루트로 가서 먼저 local git repo를 만든다.

git init

그리고 파일들을 추가한다.

git add *
git commit -m "test-commit"
git push

push 는 되지 않는게 정상이다. 원격 저장소를 추가해줘야하기 때문.

git remote add origin ssh://[레드마인서버주소]/git/test.git 

이렇게 원격 저장소를 추가한 후에도 push는 되지 않는데 
push를 하기 전에 먼저 pull을 해서 변경사항을 맞춰줘야하기 때문이다.

git pull origin master 

명령을 실행하면 원격저장소의 내용을 다운받아 변경사항을 맞춰주는데
우리는 처음하기 때문에 별 다르게 할 일은 없다.
이후 push하면 정상적으로 작업이 진행된다.

git push


=== redmine 프로젝트 설정 =============

레드마인에서는 프로젝트를 만든 뒤에 
설정- 저장소 에서 만들어둔 git 저장소를 추가하면 된다.

우리의 저장소 경로는
/git/test.git 

설정이 마무리 된 후 
프로젝트 - 저장소 로 가면 git 리포지터리의 내역들이 보이게 된다. 














금요일, 8월 9

최근 웹 개발환경 이해 - 진보된 웹 환경 공부 #3 MVC on Client-side & advanced



개요

 지난 글 진보된 웹 환경 공부 #2 에서는 REST를 쉽게 구현해 줄 수 있는 RESTlet Framework를 사용해 HelloWorld 서비스를 구현해 봤습니다. REST를 통해 서버를 구현하면서 가장 많이 바뀐 면은 Client-side 입니다.

 지난번에 보았듯이, REST를 구현한 서버는 URI를 통해 호출된 서버자원에 매칭되는 작업을 할 시에 ( 비지니스 로직, 디비, 이미지나 html에 대한 리소스 전송 ) 이를 클라이언트에 전송해주게 되는데, 결과를 JSP나 PHP등의 서버사이트스크립트 페이지를 통해 데이터가 넣어진 형태로 전송해주지 않고, JSON과 같은 데이터 형으로 보내주게 됩니다. 

 이와 같은 구현은, 전에 말씀드렸듯이 서버를 개발하는 사람이 stand-alone하게 개발할 수 있는 여건을 마련해줍니다. 내가 서버를 구현하는데 반환해줄 데이터를 View에 어떻게 넣어줄지 등에 대한 고민을 할 필요가 전혀 없어졌기 때문입니다. 하지만 동시에 Interface 설계가 중요해졌습니다. 

 클라이언트 측면에서는 전송받은 데이터를 View에 넣어줄 필요가 생겼기 때문에 javascript를 통해 서버로부터 데이터를 받아오고 받아온 데이터를 View에 넣어주는 라이브러리들이 등장하게 됩니다.

 이번 글에서는, REST가 구현된 RESTful 서버 구현의 마지막 단계인 클라이언트 측면에서 서버의 자원 호출을 통해 받은 데이터(JSON)을 통해 View를 구성 방법을 다루고자 합니다. 

또한, Javascript 라이브러리인 Backbone.js(MVC패턴), knockout.js(MVVM패턴) 두 가지를 동시에 다뤄서 MVC 패턴과 MVVM 의 차이점 (MVVM이 MVC 보다 뭐가 나은가?)에 대해서도 다루고자 합니다. 


본문

먼저 지난 번의 Helloworld 예제를 조금 수정해서 그럴듯한 사용자 정보가 나오는 REST 서비스를 만듭니다.

public class HelloWorldRESTResource extends ServerResource {
@Get("json")
    public 
    Representation HelloWorld(Representation entity) 
    {
JsonRepresentation response = null;
        try
        {
        LinkedHashMap<String, Object> list = new LinkedHashMap<String, Object>();
            list.put("user_name", "Hanbum Bak");
            list.put("email", "HanbumBak@whatyougonnado.com");
            response = new JsonRepresentation(list);
        } catch(Exception ex) {
        
        }
return response;
    }

}

이제 아래 주소로 접속하면 
http://localhost:8182/helloworld

요런 JSON이 날라옵니다 .
{"user_name":"Hanbum Bak","email":"HanbumBak@whatyougonnado.com"}


OK! 이제 JSON을 받아와 View를 만들어 볼 차례입니다.
연장자 우대해서 MVC 패턴을 사용한 Backbone을 먼저 사용해볼까요?

아! 먼저 RESTlet에서 일반 파일들을 서빙하도록 서비스를 등록해야 하는데, 이 글에서 다룰 범주는 아니므로 간단히 소스만 공개하고 넘어가겠습니다.


========== 파일서빙용 서버리소스 ============
public class StaticPathWebRoutable implements IRestletRoutable {
    
    @Override
    public Router getRestlet(Context context) {
    final String WEB_ROOT = 
"file:///"+
System.getProperty("user.dir") + 
System.getProperty("file.separator") +
"resources" +
System.getProperty("file.separator") +
"web";
    
        Router router = new Router(context);
        router.attach("", new Directory(context, WEB_ROOT));
        context.setClientDispatcher(new Client(context, Protocol.FILE));
        return router;
    }

    /**
     * Set the base path for the Topology
     */
    @Override
    public String basePath() {
        return "/";
    }

}

=========== 서비스 등록용 인터페이스 ==============

public class StaticWebRoutable implements IRestletRoutable {
    
/*
* classloader path = $(ProjectHome)/bin
*/
    @Override
    public Router getRestlet(Context context) {
        Router router = new Router(context);
        router.attach("", new Directory(context, "clap://classloader/web/"));
        context.setClientDispatcher(new Client(context, Protocol.CLAP));
        return router;
    }

    /**
     * Set the base path for the Topology
     */
    @Override
    public String basePath() {
        return "/";
    }
}

===== Router에 서비스 등록 =============
public class RootRouter extends Application {
protected List<IRestletRoutable> restlets;
@Override
    public Restlet createInboundRoot() {
Context context = getContext();
        Router baseRouter = new Router(context);
        baseRouter.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
        
        /* TODO 
         * 나중에 모듈 로드 방식으로 고도화, 지금은 일단 하드코딩 
         */
        
        restlets = new ArrayList<IRestletRoutable>();
        restlets.add(new HelloWorldWebRoutable());
        restlets.add(new StaticPathWebRoutable());
        
        // attach 
        for (IRestletRoutable rr : restlets) {
            baseRouter.attach(rr.basePath(), rr.getRestlet(context));
        }
        
        return baseRouter;
    }
}


=== 그리고 HelloWorld 용 Routable도 추가합니다. =============

package com.swmaestro.sdn.HelloWorld;

import org.restlet.Context;
import org.restlet.routing.Router;

import com.swmaestro.sdn.core.IRestletRoutable;


public class HelloWorldWebRoutable implements IRestletRoutable {

@Override
    public Router getRestlet(Context context) {
        Router router = new Router(context);
        router.attach("/helloworld", HelloWorldRESTResource.class);
        return router;
    }

    /**
     * Set the base path for the Topology
     */
    @Override
    public String basePath() {
        return "/sm";
    }
}


여기까지 됐으면 본격적으로 Client 측면에서 아래의 목표 출력물을
Backbone과 Knockout 으로 각기 구현해보겠습니다.

목표 출력물
user-name : Hanbum Bak
email : HanbumBak@whatyougonnado.com


=== Backbone ================================

<필요한 라이브러리>
"https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"
"https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"
"https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"
</필요한 라이브러리>

HelloBackboneModel = Backbone.Model.extend({
url:"/sm/helloworld",
default: {
user_name: 'test',
email: 'test'
}
});

HelloBackboneView = Backbone.View.extend({
initialize: function(){
this.model.fetch({
success: function(data) {
HelloBackboneView.render();
}
});
},
render: function(){
var temp = "user-name : " + this.model.get("user_name");
temp += "<br>email : " + this.model.get("email");
        this.$el.html(temp);
    }
});

var HelloBackboneModel = new HelloBackboneModel();  
var HelloBackboneView = 

new HelloBackboneView({ model:HelloBackboneModel, el: $("#HelloBackbone") });


=== Knockout ================================

<필요한 라이브러리>
"http://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"
"https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"
</필요한 라이브러리>


<p>userName: <strong data-bind="text: userName"></strong></p>
<p>email: <strong data-bind="text: email"></strong></p>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"></script>

<script>
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
var self = this;
this.userName = ko.observable();
    this.email = ko.observable();
    
    this.update = function() { 
    $.getJSON("/sm/helloworld", function(data) {
        self.userName(data.user_name);
        self.email(data.email);
    });
    };    
}

// Activates knockout.js
var temp = new AppViewModel();
ko.applyBindings(temp);
temp.update();
</script>


 MVC를 구현한 Backbone과 MVVM을 구현한 Knockout의 가장 큰 차이점은 눈에 보이다싶이 Data를 View에 Binding하는 형태입니다. Backbone 예제에서는 직접 html 을 입력해줬는데 실제로 Backbone에서 제공하는 Template기능을 사용하더라도 소스 코드만 다를뿐 기본적인 동작원리는 똑같이 html을 입력해주는 형태입니다.

 눈에 보이지 않는 차이점은 Observable로 표현되는 Observer에 따른 View의 갱신 방법인데... Backbone예제에는 이를 넣지 않았는데... 이유는 이 소스를 보면 기분이 아주 안좋아지기 때문입니다. (-_-; )

 MVC 에서는 View가 정말 아무 기능이 없습니다.
 그냥 model 을 사용자에게 보여주기 위한 수단일 뿐이죠.
 하지만 대부분의 언어(??)가 View를 통해 Event를 전달받을 수 있었습니다.
 제가 MVC를 이해한 순간부터 사람들이 왜 이 패턴을 사용하는지 의아했던 이유입니다.

 우리는 View와의 상호작용을 통해 Data를 편집합니다.
 만약 우리가 View의 Data를 수정했다면 처리 로직은 어떻게 되는 걸까요?

 MVC 에서는
 Controller가 입력장치의 Data를 받아 -> Model을 갱신하고 -> View에 반영

 MVVM 에서는
 View가 Data를 갱신하고 -> 데이터로 Model을 갱신할지를 프로그래밍.


 MVC패턴은 View에서 Event를 처리할 수 있게 된 이후부터는 비효율적인 패턴이며, View의 역할은 단순히 Model을 사용자에게 보여주는 것에서 사용자와의 상호작용을 처리하고 이를 통해 모델을 갱신할지 여부를 결정할 수 있는 선까지 확장되었습니다. 그리고 MVVM은 이와같이 바뀐 패러다임을 잘 적용했을 뿐 아니라, 이를 통해 View와 Model의 의존성을 보다 느슨하게 만든 GUI 구현 패턴입니다 


  

마치며

MVC is nothing. or everything.

http://tomyrhymond.wordpress.com/2011/09/16/mvc-mvp-and-mvvm/



MVC is nothing.
MVC 패턴은 예전에 제가 다룬 글에서 이야기했듯, 1970년대의 OS에서나 사용했던 GUI 구현 패턴이죠. 아무것도 아닙니다. 구시대의 잔재일 뿐이죠.

or everything.
하지만 동시에 MVC는 model 2로 발전하면서 Struts와 같은 Framework를 통해 개발자들에게 웹의 시대를 열어줬습니다. 발전된 MVC model 2 패턴은 Client와 Server 개발을 분리하고 Html 등을 담당하는 퍼블리셔 혹은 디자이너와 개발자에게 훌륭한 개발 환경을 제공해줬습니다.


 객체지향에서 100번을 강조해도 지나침이 없는 중요한 키워드는 바로 De-Coupling입니다. 이것은 쉽게 이야기해서, Server-Client 와 같이 긴밀하게 연결될 수 밖에 없다고 생각되는 경우에도 변경과 수정이 상호 간에 미치는 영향을 최소화 하는 것 입니다.

 이를 지켜야 하는 이유는 OOP에서 말하는 "단일 책임의 법칙" (SRP : Single Responsibility Principle)을 준수함을 통해 OOP의 장점인 "개발의 병렬화" 그리고 "재활용성" 을 얻을 수 있기 때문입니다. 보다 나은 "의존성 없는 협업체계"는 MVP를 거쳐 MVVM으로 발전되었습니다. MVVM으로 오면서 MVC model 2에서 해결할 수 없었던 부분들이 해결되어 ( GUI의 갱신 등에 대한... ) 전보다 더 원활한 협업이 가능할 것으로 기대됩니다.


-fin




목요일, 7월 18

최근 웹 개발환경 이해 - 진보된 웹 환경 공부 #2 Restlet Framework



개요

지난 글 진보된 웹 환경 공부 #1 에서는 REST라는 설계 철학이 등장한 이유와 함께 그와 같은 설계와 함께 사용해야 하는 URI, JSON 등에 대한 개괄적인 설명을 다뤘습니다. 이번 글에서는 REST를 구현한 RESTful 서비스를 Restlet이라는 Framework를 사용하여 간단히 구현해보겠습니다.

그 전에 REST는 설계에 철학이 담겨있습니다.
제가 철학이라고 말하는 것은, 뭔가 거대 담론을 지칭하는 것은 아니고 단지 "이와 같은 설계를 통해 세상이 이렇게 되었으면 좋겠다." 라는 설계자의 바람이 느껴지는 것을 말합니다.

그렇다면 기존에는 어떤 설계를 사용했는지 그리고 이와 같은 설계의 한계점은 무엇인지 살펴보고 REST를 통해 어떻게 보다 더 좋은 방향으로 프로그래밍을 할 수 있는지 알아보는게 우선이라고 생각해서 이 부분을 추가했습니다.

해서 이 글은 이와 같은 순서로 작성됐으며, 이런 생각을 하시면서 읽으면 되실 것 같습니다.
1. 기존에 범용적으로 사용되던 Web Framework 개괄 설명
2. 1에서 다룬 Framework의 설계
3. 2의 문제점과 한계
4. REST의 개괄 설명은 생략 #1을 참고하세요
5. REST가 지향하는 설계철학
6. Code review
7. 결론 및 내용 요약

[ "기존에 사용되던 Framework의 한계점은 무엇이고 REST는 어떤 장점이 있으며, 어떻게 사용해야 하는가?" ]

Structs Framework

Structs는 2000년도 이전에 크레이그 맥클라나한(Craig McClanahan)에 의해 탄생했으며 MVC 패턴을 구현하기 위해 Java Servlet API를 사용했습니다. Structs외 이 당시 대부분의 Web-Framework는 거의 MVC 패턴을 사용했습니다.

Structs Framework가 사용하는 MVC 패턴의 문제

structs MVC model 2

 Structs에서는 MVC 패턴을 사용하는데 이는 Model-View 간의 결합도를 낮추기 위해서입니다. 객체지향에서 결합도를 낮추는 일은 매우 중요합니다.

 MVC model 2 는 비록 model 과 View 사이에 Bean을 두었지만 단순히 데이터를 주고 받는 매개자 역할을 했기 때문에 Model이나 View의 변화에서 자유롭지 못했습니다.

 그 외에 Server에서 최종적으로 반환하는 것이 View로 UI를 구성하기 위한 부가적인 데이터가 추가되어 있고, 이를 해석하는 과정에 비용이 많이 들었으므로 이 시기에 웹 서비스 간에 유려한 연동은 소원한 일이었습니다.

 만약 상기의 Structs구조에서 Javabean을 View에 binding시키고 View를 보내주는 것이 아닌, 서술가능한 형태의 DataStructure 형태로 Bean을 제공했다면 어땠을까요? 개발자들은 보다 View와 Model간에 연결성이 떨어져 병렬개발(?)이 가능했을 것이고, 외부에서 해당 서비스를 이용하기는 쉬웠을 것입니다. 요것이 현재 REST기반 아키텍쳐의 장점이죠.


Structs Framework의 Http session 문제

 또한 기존 웹 Framework들은 사용자의 로그인 정보등을 유지하기 위해서 세션과 쿠기 등의 부수요소를 사용했는데, 이와 같은 값들은 로그인과 같은 특정 과정을 거친 후에 생성되므로 웹 서비스 간에 연결하고자 할 때에 한 번 이상의 트랜잭션이 추가로 필요로 하게 되었습니다.


Restlet Framework

 다시 말하자면 기존에 사용하던 설계 상의 문제점은, 웹 서비스들 간에 연동도 어렵고, 웹 서비스를 다른 개발자들에게 공개하기도 어려운 상황 속이었습니다. 그러던 중에 REST를 통한 open-api 의 공개와 성공은 많은 개발자들에게 수 많은 웹 서비스들을 API처럼 사용할 수 있는 가능성을 열었고, 결과적으로 개발자와 서비스 모두에게 큰 도움이 되었습니다.


Restlet Framework가 지향하는 설계와 기능


 Restlet Framework는 #1 에서 살펴본 내용처럼 Restlet은 공개한 REST API 들을 URI로 접근할 수 있도록 도와줍니다. 또한 많은 REST API들을 사용할 수 있도록 URI을 각각의 메소드 등에 연결하기 편하게 도와주는 Routing이란 기능을 제공합니다.

 그리고 기존의 설계와는 다르게 Model의 데이터들을 View에 기입한 뒤에 View를 반환하는 것이 필수 구조가 아니고, View와 Model간에 Data를 설명해주는 서술자를 둠으로서 View와 Data간에 결합도는 최소화 할 수 있게 됐습니다.

 이것은 흔히 Front-End, Middle-End, Back-End 라고 부르는 구조처럼, 각 단계마다의 결합도와 의존성을 최소화하고 대신 정해진 규약과 인터페이스에 의한 분업 체계로 바뀌었음을 의미합니다. 이렇게 빠뀐 구조 덕분에 Front-End 에서 python, java, javascript 아무거나 내키는대로 사용할 수 있게 된 것입니다. Front-End Freedom!


Restlet Framework Sample service code review

Restlet Tutorial
Restlet의 기본적인 튜토리얼은 위의 주소를 참고하세요.

Restlet의 1단계는 서버를 만드는 작업입니다.
Restlet에서 서버는 하나의 Component 단위이며,

Component는 VirtualHost-Application의 구조로 이루어집니다.


그러므로 자연스럽게 2단계는 Virtual-Host 설정이고 3단계는 Application 설정입니다. 물론 정해진 순서는 없고 코드를 짜기에 따라서 어플리케이션에서 직접 Component를 올리고 Virtual-Host와 Application을 설정할 수도 있지만, 이번에는, Restlet Framework에 기본적으로 탑재하고 있는 서버 컴포넌트를 올리고 이 위에 어플리케이션을 올리는 작업을 해보도록 하겠습니다.

//HeloWorld
public class HelloWorldRESTMain {
public static void main(String[] args) throws Exception {
   // Create a new Component.
   Component component = new Component();
   // Add a new HTTP server listening on port 8182.
   component.getServers().add(Protocol.HTTP, 8182);
   // Attach the sample application.
    // component.getDefaultHost().attach(new HelloWorldRESTApplication());
   // Start the component.
   component.start();
}
}


컴포넌트를 만들고 8182번 포트를 사용하는 HTTP서버를 올리고, HelloWorldRESTApplication 을 attach 해줍니다.

//HelloWorldRESTApplication
public class HelloWorldRESTApplication extends Application {
    @Override
    public synchronized Restlet createInboundRoot()
    {
        Router router = new Router();
        router.attach("/helloworld",HelloWorldRESTResource.class );
        return router;
    }
}

HelloWorldRESTApplication에서는 helloworld URI에 Resource를 attach하는 역할을 합니다. 여기에 /helloworld 라는 URI는 이제부턴 HelloWorldRESTResource 클래스가 되는 거죠.
Resource는 어떤 역할을 할까요?


Resource는 실질적인 동작을 수행하는 곳 입니다. Router는 URI를 각 Resource로 연결해주는 식별자 역할만을 수행합니다.

REST Framework는 HTTP의 Get, Post, Put, Delete 를 지원하며 Restlet에서는 해당 Resource에 어노테이션을 확인하고 함수를 호출합니다. Get이라면 @Get 어노테이션에 따르는 함수를 호출하죠. 우리는 HelloWorld를 통해 Client에게 JSON을 반환해줄 것입니다.

// HellowWorldRESTResource
public class HelloWorldRESTResource extends ServerResource {
@Get
    public Representation HelloWorld(Representation entity)
    {
JsonRepresentation response = null;
        try
        {
        LinkedHashMap<String, Object> list = new LinkedHashMap<String, Object>();
            list.put("WELCOME_REST", "Hello World");
            response = new JsonRepresentation(list);
         
        } catch(Exception ex)
        {
       
        }
return response;
    }
     
}

JSON을 반환하기 위해 JsonRepresentation 클래스를 사용했습니다.
이제 브라우저로 http://localhost:8182/helloworld 에 접속하게 되면,

{"WELCOME_REST":"Hello World"} 

위와 같은 JSON을 반환받게 됩니다.



마치며

 REST는 매우 강력한 서버 모델이자 동시에 협업 모델이기도 합니다. MVC모델이 그토록 오랜 시간 GUI구현 패턴을 주도해온 이유는 View와 Model의 분리가 디자이너와 개발자에게 좋은 협업 환경을 만들어 주었기 때문입니다.

일보 더 나아가 REST는 개발에 있어 GUI와 서버 구성에 완벽한 독립성을 부여했습니다. GUI 개발자와 서버 개발자는 같은 프로그래밍 언어를 사용할 이유가 전혀 없게 되었다고 봐도 무방합니다. JSON이라는 훌륭한 매개자가 있으니까요. 또한 이를 통해 서비스 간에도 자유로운 협업이 가능해지게 되었습니다. RPC 같은 것과는 비교도 안되죠.

 다음 3부에서는 JSON을 통한 GUI 구성에 있어 MVVM모델을 사용하고 있는 knockout.js라이브러리를 통해 개발자와 퍼블리셔라고 불리는 Html 코더 간에 독립성을 확보하는 예제를 작업해 볼 것입니다.



화요일, 7월 16

최근 웹 개발환경 이해 - 진보된 웹 환경 공부 #1 REST




개요


 Framework는 개발자들에게 보다 편하고 쉽게 좋은 코드를 만들 수 있게 해주는 역할을 합니다. 그렇다면, 무엇이 좋은 코드이고 무엇이 좋은 코드일까요? 이것은 누구도 쉽게 대답하기 어려운 질문입니다.

 최근 프로그래밍 기법이나 언어는 지속적으로 OOP를 지향하며 발전되어 왔습니다. OOP하면 우리는 지겹도록 들어온 말이 재사용성이나 캡슐화 같은 이야기인데 잘 와닿지는 않을 것입니다. 왜냐면 그건 결과이기 때문이죠. 우리가 OOP언어를 사용하기 때문에 재사용성이 높아지고 하는 것이 아니라, OOP 적으로 설계하고 코드를 짤 수 있어야 그 결과물로 수업시간에 배운 OOP의 장점들을 얻어낼 수 있게 되는 것입니다.

 이번 최근 웹 개발환경의 이해에서는 REST와 함께 Knockout.js 라이브러리를 통해 요즘 OOP 기법에서 특히 중요하게 생각하는 "결합도 낮추기"를 선배님들이 어떻게 고민하였고 해결 방안을 제시했는지에 대해서 공부하고자 합니다.

 { "결합도 낮추기 (Decoupling) " = 코드간 의존성을 없애는 것을 뜻하며, 최근에 애자일 혹은 Lean 방식의 개발방법론이 대세를 이루며, 가장 핵심 기능을 우선 구현하여 단계적으로 향상시켜나가는 방식에 가장 중요한 OOP 요소입니다. " }



REST 란?


 먼저 살펴볼 것은 REST 입니다.
 REST는 Representational State Transfer 의 약자로 우리나라 말로는 "상태 재현적 전송" 으로 표현할 수 있겠습니다. "상태 재현적 전송"이란 REST의 설계 원칙 중에 하나로 (그리고 WWW의 설계 원칙에도 동일한 ) Stateless = 비연결성을 뜻합니다.

 원칙적으로 HTTP는 비연결성을 지향합니다만, 현존하는 거의 모든 웹사이트들은 세션이나 쿠키등을 통해 상태 정보를 저장하고 이를 통해 일종의 연결성을 임의로 만들어서 운용하고 있습니다. REST는 로이필딩이 2000년도 박사 논문으로 발표했고, HTTP의 주요 저자라고 합니다. 그러니 HTTP가 오용되는 현실을 보고 이를 바로잡기 위해서 REST를 발표했다고 생각해도 무리는 아닌 것 같습니다. ( 2000년 발표된 설계 기법이 퍼지는데 걸린 시간을 생각해보면 IT라고 해도 변화가 그렇게 빠른건 아닌 것 같습니다. )

 어쨌든, WWW 는 말 그대로 Web과 같은 네트워크 시스템을 만들고자 한 것이 목표였고 REST는 이와 같은 목표를 이루기 위한 방법론이라고 할 수 있겠습니다.

 REST의 장점을 대표하는 케이스를 하나 찝어보자면,
 웹 서비스들이 폭발적으로 성장하게 된 계기를 마련해준 Open-API 가 바로 REST의 대표적인 케이스입니다. 이쯤에서 REST 서비스를 웹 개발이나 서비스에 전혀 조예가 없는 초심자 분들에게도 쉽게 설명해드리고자 합니다.


예를 들어, 프로그래밍을 짤 때를 생각해보시길 바랍니다.
우리가 만드는 함수는 어떤 짜여진 규격이 있습니다.
다음의 pseudo 코드를 한번 보시죠.

[반환값] function1 ( Arg1, Arg2 ) {
        ... work work work ...
        return result;
}

어떠세요? 위의 코드는 거의 모든 분이 이해하실 수 있을 겁니다.
그럼 이제 질문을 하나 드리겠습니다.

REST는 아래와 같은 질문을 통해 이해해야 합니다.
< 위와 같은 코드를 Remote에서 호출하려면 어떻게 해야 할까요? >
- 1. 어떤 머신도 쉽게 접근할 수 있는 식별 가능한 주소.
- 2. 서로 다른 머신끼리 통신할 수 있는 공통의 인터페이스.
- 3. 함수를 호출하고자 하는 머신을 식별할 수 있는 stateless한 인터페이스.
- 4. 해당 함수에 인자를 줄 수 있는 공통의 인터페이스.

 다시 한번 강조드리는 것은, REST는 WWW, WEB처럼 거미줄로 엮인 네트워크 시스템을 고민하면서 나온 설계 철학이라는 것 입니다. REST의 저자인 로이 필딩은 이와 같은 문제들을 풀 수 있는 환경 = RESTful 을 구축하면 여러가지 장점이 있다고 생각한 것 입니다. ^^

 그리고 각 필요한 요소들이 시간이 지나면서 구축이 되기 시작한 것이죠 ~~


1번의 답 URI
URL은 많이 들어보셨을텐데 URI는 뭔가 하시는 분 혹시 계신가요?
URL은 Uniform Resource Location의 약자로, 말 그대로 Resource의 Location에 대한 Uniform 입니다. 예를들면 http://{host}/image.jpg 와 같은 형식이 URL입니다. 하지만 이것으론 충분하지 않게 됐습니다.

왜냐구요? 위를 보세요, 우리는 이제 함수를 호출할 수 있는 어떤 주소값도 필요하게 되었습니다. 이를 URL처럼 http://{host}/fucntion1 으로 접근하면 어때? 오, 그거 좀 괜찮은데? 그래서 나온게 URI입니다. URI는 Uniform Resource Identifier의 약자로 식별자로서 리소스를 구분하겠다는 뜻입니다. 앞의 예처럼 http://{host}/function1이라고 하면 이것은 함수와 연결되는 리소스의 식별자라고 생각하면 되겠죠.

대신 자신만 사용하는 것이 아니기 때문이 이름은 신중이 지을 필요가 있습니다~


2번의 답 JSON
JSON은 아직 표준이 아닌 것으로 알고 있어서 의견이 다를 수 있습니다만, 어쨌든 JSON도 서로 다른 머신끼리 통신할 수 있는 공통의 인터페이스를 목표하는 것은 맞습니다.

JSON이 뭔지 간단히 집고 넘어가자면, 위에서 예를 든 함수에서 반환값이 ArrayList와 같은 형식이라고 생각해봅시다. 이것을 Remote에서 호출한 컴퓨터에게 어떻게 전달하면 좋을까요? 이런 의문에서 만들어진 코드입니다.

JSON은 JavaScript Object Notation의 약자로, 말 그대로 Javascript에서 Object를 표기하는 방법입니다. 이는 Hashmap과 같이 key-value 짝 형식으로 되어 있는데, 예를 들자면
{
    "이름": "테스트",
    "나이": 25,
    "성별": "여",
    "기혼": true,
    "주소": "서울특별시 양천구 목동",
    "특기": ["농구", "도술"],
    "가족관계": {"#": 2, "아버지": "홍판서", "어머니": "춘섬"},
    "회사": "경기 안양시 만안구 안양7동"
 }
이런 형식입니다. Object들에 대한 Descriptor를 함께 가지고 있다고 생각하면 될 것 같습니다. 그렇기에 Client에서는 JSON을 통해 Return값을 받고 이를 적절히~ 파싱해서 화면에 보여주면 되는 것이죠.

3번의 답 Stateless.
Stateless는 단순히 비연결지향만을 뜻하는 것은 아닙니다.
비연결을 지향하기 위해 서버/클라이언트 간에 혹은 타 머신간에 서로를 인식하기 위한 값을 커뮤니케이션 시에 함께 보냄을 뜻하는 것 입니다. 이것은 마치 TCP와 UDP의 통신 방식의 차이와 유사합니다.


4번의 답 HTTP Protocol
앞선 단계를 따라 Remote의 함수를 URI를 통해 호출하고 결과를 HTTP JSON으로 받는 것까지를 설계적으로 구현했다면, Argument를 주는 것을 어떻게 할 것인가? 이것은 HTTP Protocol을 사용합니다. HTTP 스펙에 따르면 HTTP 프로토콜은 4가지 메소드를 지원하는데 Get, Post, Put, Delete 가 그것입니다.

굳이 이런 Argument를 방식을 사용하는 이유는 HTTP 프로토콜보다 범용적으로 사용되는 것이 없기 때문이기도 하겠고, 애초에 설계 방향이 같기도 하기 때문인 것 같습니다. 어쨌든 Argument로 값을 넣어주고자 한다면, Post로 JSON을 보내면 되고, 단순히 값을 넣고자 한다면 Put을 사용하면 되겠죠.





마치며...


 이번 글에서는 REST에 대해서 간략히 다뤄봤습니다.

 REST라는 설계, 구현 방법을 배우고 습득하는 것도 중요하지만 그만큼 REST라는 것을 만들기 위해 했던 선배들의 고민과 문제의식 그리고 해결방안을 생각하기 까지의 과정을 생각하는 것도 중요하다고 생각합니다.

 다음 글에서는 REST를 실질적으로 구현할 수 있는 Framework를 보면서 REST 서비스를 구현하기 위해 필요한 요소들이 무엇인지에 대해 살펴보도록 하겠는데, 아마 분량이 너무 많을 것이므로 서버 측면과 클라이언트 측면으로 나누어 설명하고 그 후에 서버와 클라이언트를 통합해 기존의 방식과 설계/디자인패턴의 변화에 대해 다루겠습니다.

 3편이 남았네요.











월요일, 4월 22

Base64 인코딩 디코딩시 주의점


http://support.microsoft.com/kb/323489/ko

Base64 주의사항.

"캐리지 리턴/줄 바꿈(CRLF) 문자가 출력 스트림에 삽입되어 줄 길이를 76자 이하로 유지합니다."



인코딩시 인코딩 된 문자열이 76자가 넘으려고 하면 "\n" 개행문자를 삽입합니다.

장문의 Text 를 다룰 시에는 문제가 될 것이 아니지만, 개행문자를 기반으로 한 인자값 전달 등의 경우에 문제가 될 여지가 있습니다.

Android에서 Base64 인코딩 시에 개행문자를 삽입하지 않는 옵션은 NO_WRAP입니다.


byte[] data = txt.getBytes("UTF-8");
return Base64.encodeToString(data, Base64.NO_WRAP );

이런 형식으로 사용하시면 됩니다.



목요일, 1월 24

Java 동기화 관련 키워드 정리




 Multi-Threading 에 동기화는 중요한 이슈입니다.

 이번 글은 Java에서 동기화를 사용할 때, 자주 등장하는 키워드를 정리해보고자 합니다.


 0. 키워드 정리 전에 앞서.
 이 글은 멀티 쓰레딩이나 동기화를 정리하려는게 아니고 키워드의 의미만 집고 넘어가려는 글입니다. 그러므로 어느 정도 기반 지식이 있으셔야 합니다.

 0-1. Atomic
 그런 측면에서 Atomic = 원자 연산에 대해서는 기본적으로 이해하고 계셔야 됩니다. 이 개념을 모르고 멀티쓰레딩이나 동기화를 논한다는 것 자체가 어이없는 일이라고 생각이 드니까요. 비유를 하자면 한글도 모르는 사람이 문법 따지는 격입니다.

 Atomic 연산이란 무엇이냐면, 1clock에 처리될 것을 보장하는 연산입니다.
 예를 들면,

 int x = 1;
 과 같은 연산은 cpu에서 보면

 xor [x], 1
 과 같은 명령어로 번역될 수 있고, 이는 1clock에 처리됩니다.
 만약 위의 코드가

 mov eax, 1
 mov [x], eax
 와 같은 식으로 번역된다면 mov eax,1 까지 처리된 상태에서 다른 쓰레드가 eax에 다른 값을 세팅해놓을 경우 [x]에는 엉뚱한 값이 들어가게 됩니다. 그래서 동기화 이슈에서 atomic 연산은 가장 기초적인 개념입니다.



 1. Volatile

 가장 많은 분이 헤깔려하고 이해하지 못하면서 대충 이해하는 키워드가 이 Volatile이 아닌가 싶습니다.

 Volatile 키워드의 의미는 "컴파일러나 옵티마이저가 특정 메모리에 대해서 최적화나 캐쉬등의 작업을 하지 말아라." 라고 주문해 놓는 것입니다.

 0.1 Atomic편에서 말씀드렸듯 원자 연산은 필수적인 요소 입니다.

 만약 Volatile 을 지시하지 않는다면, Java의 컴파일러 혹은 runtime optimizer는 해당 변수의 대입등을 최적화 하기 위해서 여러가지 수를 쓰게 됩니다. 이 과정에서 만약 xor [x], 1 과 같은 int x = 1. x에 1을 넣는 연산이 일단 레지스터나 캐쉬 같은 보다 속도가 빠른 공간에 1을 두고 추가 연산 후에 x에 값을 대입하려고 할 수 있습니다.

 이럴 경우 마찬가지로 동기화 이슈가 발생할 수 있습니다. 최적화 된 코드가 x에 1이 대입될 것을 보장해주지 않기 때문입니다. volatile 키워드로 지정된 변수와 관련된 대입, 연산 등의 동작은 반드시 해당 메모리 공간을 가지고 작업을 하게 됩니다.




 2. Synchronized


 Synchronized는 임계영역입니다.
 이는 말 그대로, 두 개 이상의 쓰레드가 동시에 접근하는 것을 막는다는 뜻입니다.














webview - viewport 사용하여 다해상도에서 동일한 웹페이지 보여주기






안드로이드 기기들의 해상도 파편화는 웹뷰를 사용할 때도 똑같이 문제가 됩니다. 만약 고정된 사이즈를 갖는 콘텐츠를 웹페이지 형태로 보여주고자 하면, 해당 문제로 골치를 꽤나 썩게 됩니다.


이런 문제를 해결하기 위해서는 webkit 엔진에서 사용하는 viewport meta-tag를 사용해야 합니다. 같은 webkit 이므로 ios에서도 동일하게 적용됩니다.




webview.getSettings().setUseWideViewPort(true);
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);

Format df = new DecimalFormat("##.##");

int contentWidth = 720; //content web page width
int deviceWidth = metrics.widthPixels;
String viewport = null;
if ( contentWidth > deviceWidth ){
 String scale = "1.0";
 scale = df.format( (float)deviceWidth / (float)contentWidth );
 contentWidth = deviceWidth;
 viewport = "";
} else {

 viewport = "";
}

Log.i("TEST", viewport);
data += viewport;
wv.loadDataWithBaseURL("file:///android_asset/", data , "text/html", "UTF-8", "");

        
해당 코드는 android_asset 에 넣어놓은 html 파일을 로드해오는 코드입니다.

자세한 사항은 여기 참고하시고 -

Targeting Screens from Web Apps


왜 이런 일이 발생하는지를 간단히 요약해드리겠습니다.


안드로이드에서 default dpi 값은 160 입니다. medium-dpi 라고 말하는 이 값의 뜻은
dpi :dots per inch 약자 입니다.

인치당 얼마의 dots 가 존재하느냐? 이런 의미지요.

안드로이드의 해상도는 이 dpi 값이 160일 때를 기본형, 즉 1로 처리합니다.

그러므로 만약 해상도가 480 x 800 인 기기가 있다고 했을때, 이는 실제로 화면 픽셀이 480x800인 것이 아니고, 기기 해상상도에 dpi 값이 곱해진 값입니다.

즉, 기기의 dpi 가 240 이라고 한다면. 이는 240/160 = 1.5 가 곱해진 해상도입니다.
이에 따른 실제 기기의 해상도는  320x533 인 것 입니다.

이런 상황에서 480px 800px 의 웹페이지를 그냥 로드하려고 하면 당연히 화면이 넘쳐나게 됩니다. 때문에 viewport를 통해 실제 웹페이지를 그려놓을 곳의 size를 정해주는 것입니다.