[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

2016년 9월 27일 화요일

2016년 9월 19일 월요일

[DEV] Sauce Labs : 크로스 브라우징 테스트 서비스..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

전에 크로스 브라우져 테스트를 할 수 있는 서비스로 BrowserStack을 소개한 적이 있었는데 오늘 소개할 Sauce Labs도 동일한 서비스로 볼 수 있다. 최근에 프론트앤드 작업을 하다보니 아무래도 크로스 브라우징 테스트가 필요해서 Sauce Labs를 사용하고 있는데 아직까지는 상당히 만족하고 있다.

크로스 브라우징 테스트

Sauce Labs는 가상 머신을 통해서 다양한 플랫폼과 브라우저의 조합을 쉽게 테스트해 볼 수 있게 해주는 서비스다. 서비스 자체의 사용방법은 아주 간단하다. 무료로도 이용할 수 있는데 가입을 하면 아래 화면에 나오듯이 수동으로 직접 실행하는 경우에 30분을 주고 자동화 테스트에 Win, Linux, 안드로이드등에 200분이 할당되고, Mac/iOS에 40분식 나누어서 할당된다. 처음에 기능을 확인해 보기에는 충분하고 회사에서는 결재를 해서 쓰고 있기 때문에 결재자에게 한정된 것인지 모르겠지만 테스트를 계속 수행하다보니 보너스라서 시간을 계속 준다. 왠만한 회사에서는 월 12불 정도로도 충분해 보인다.(무료는 동시에 실행할 수 있는 세션이 2개로 제한된다.)

브라우저 테스트 목록

로그인한 화면에서 우측상단의 아이콘을 클릭하면 접속할 URL과 사용할 플랫폼/브라우저 조합을 선택해서 실행할 수 있다. 보다시피 테스트 대상이 될만한 대부분의 플랫폼과 브라우저가 준비되어 있다.

Sauce Labs에서 크로스 브라우저 테스트를 하는 화면

실행하면 약간의 로딩시간(내부에서 가상 머신을 띄우는 시간) 후에 앞에서 입력한 URL로 접속한 화면이 나타나게 된다. 별도의 프로그램 없이 브라우저내에서 실행해 볼 수 있기 때문에 편하고 이는 원격조작을 하는 것이기 때문에 브라우저를 띄운 뒤에 (약간은 느리지만) 조작을 하거나 새로운 탭등을 띄우는 작업도 가능하다. 그냥 원격으로 붙어서 작업한다고 생각하면 된다. iOS나 안드로이드 같은 경우는 시뮬레이터로 실행이 된다. 실제 장비는 아닌 점은 아쉽지만 가상머신으로 다룰 수 있으려면 어쩔 수 없는 선택으로 보이고 브라우저를 테스트하기에는 무리가 없어 보인다. 상단의 메뉴바를 통해서 새로운 세션을 띄우거나(앞에서 처럼 다시 플랫폼과 브라우저를 실행해서 띄운다.) 해당 화면을 다름 사람한테 공유할 수도 있다.(공유 기능은 많이 쓸지는 잘...)

Sauce Labs의 대시보드 화면

사용자의 대시보드 화면해서는 테스트했던 기록이 위처럼 나오고 좌측에는 현재 실행되고 있는 상태나 남은 시간등이 나타난다. 실행했던 기록도 볼 수 있는 있는데 클릭을 하면 다음과 같이 테스트한 세션의 정보가 나타나고 실행한 액션과 마지막 스크린샷을 볼 수 있고 전체 실행과정을 녹화된 비디오로도 볼 수 있다.

기존에 실행한 테스트의 상세화면

터널링

일반적으로 개발하는 프로세스를 생각하면 개발자 PC의 로컬에 서버가 떠있거나 내부망에만 떠있는 경우가 대부분이다. 이는 외부에서 접속할 수 없는 경로 이므로 외부에서 테스트하기 어려운데 그러한 상황을 위해서 터널링 기능을 제공한다.(이는 BrowserStack도 제공하는 기능이다.) 터널링 안내 페이지에 가면 자세한 안내를 볼 수 있는데 jar파일로 터널링 프로그램을 제공하므로 플랫폼에 상관없이 모두 사용할 수 있고 당연히 80 혹은 443 포트는 외부로 열려있어야 가능하다.

터널링 프로그램을 실행한 화면

터널링 프로그램을 위처럼 사용자계정명과 API키(로그인하면 좌측에서 볼 수 있다.)로 실행하면 터널링 연결을 시도하고 "Connected! You may start your tests."라는 메시지가 나오면 성공한 것이다.

터널링 접속여부가 Sauce Labs 페이지에 표시된 화면

이제 Sauce Labs 사이트에 가면 우측 상단에 터널링이 연결되었다는 표시로 아이콘이 녹색으로 바뀐 것을 불 수 있다. 터널링이 연결된 후에는 개발자 PC 로컬에서 접속할 수 있는 localhost나 다른 내부 URL을 통해서 사이트에 접속해서 테스트를 할 수 있다.


2016년 9월 8일 목요일

[UFC] UFC 마감뉴스..

한 동안 UFC 뉴스를 안올린 듯 하다.. 다채로운 경기 결과는 항사 있었지만 어쩌다보니 그리 됬다.. ㅎㅎㅎㅎ..

항상 뉴스를 올리지는 못하고 올리더라도 나 스스로에게 관심이 되는 이벤트와 경기를 위주로 올리다보니 좀 종합된..?? 그런 뉴스를 올려볼까 생각을 했는데 SPOTV 에서 주기적으로 올리는 마감 뉴스가 있더라.. 그래서 해당 뉴스를 출처를 밝히면서 올리고자 한다.. 그곳에 내가 하고 싶은 얘기가 있다면 덧 붙여서 말이지.. 혹여라도 문제가 추후 생기면 삭제해야되겠지만 말이다.. ㅎㅎ

출처 : SPOTV NEWS

































벨라스케즈는 개인적으로 너무 좋아하는 선수인데 본인의 바람처럼 잘 되었으면 한다.. 꼭 타이틀을 되찾고 헤비급 방어전 신기록도 세웠으면 좋겠다.. ㅎㅎ








[Talk] 괜시리 아침부터 불을 뿜는 기사가..

상쾌한 아침..?? 아니 좀 꿀꿀한 아침이다.. 날씨도 그렇고 비가 와서 그런지 후덥지근하다.. 개인적으로 더운걸 엄청 싫어하는데 오히려 여름보다 딱 지금정도의 시기가 더 별루다.. 여름에서 가을 넘어가는 시기.. 왜냐면.. 날이 완벽하게 덥지 않다보니 단순한 온도 수치만 보고서 에어컨을 안틀어서 상대적으로 덥게 만들기 때문이다.. 모든 곳이 다 그런건 아니지만.. 무튼 그건 그거고..

오늘 웹 뉴스 속 IT 분야를 보다가 어이 없는 글이라고 해야되나.. 괜한 나의 노파심이자 자격지심이라고 해야되나 그것도 아니면.. 괜히 딴지를 걸고 싶은걸까..?? 그런 뉴스가 하나 보였다..

..구글러 되고프면 일단 도전해야죠..

위와 같은 제목이었다.. 난 혹시나 하는 마음에 클릭을 했다.. 혹시나 하는 마음이라고 언급한 것은 요새는 기자들도 낚시성 제목을 너무 많이 달기 때문이다..

글을 들어가보니 기본적인 내용은 이거였다.. 난 스펙도 없는 사람인데 어려서부터 프로그램이 좋아서 해당 분야 공부를 해왔고, 최연소로 구글에 입사했다는 것이다..

ㅋㅋㅋㅋ.. 글을 다 읽고나니 미친거 아닌가..?? 싶었다.. 아니 이건 마치 대기업들이 대중매체에 우리는 학력, 학벌 같은거 안봐요.. 영어점수..?? 이런거 필요없어요.. 와 같은 거 아닌가..?? 일단 도전하라고..?? 도전하면 서류 전형에서 다 떨어지는게 현실 아니던가..?? 누군 몰라서 도전을 안한다고 생각을 하는건가..?? 진짜 웃기다.. 내가 왜 이렇게 열을 내냐고..?? 내가 못들어가니까 괜히 그런다고..?? 배아퍼서..??

그래 배 아프다.. 구글이라면 자신의 실력을 떠나서 가고 싶은.. 들어가서 일 한번쯤 해보고 싶은 회사일테니까 말이다.. 그런데 나는 뉴스에서 사람들에게 올바른 정보를 제공해야될 뉴스에서 저런식으로 뉴스를 올리는게 진짜 싫다.. 아니 꼴보기도 싫을 뿐더러 저따위 뉴스로 인해서 취업 준비생 혹은 대학진학자들 고등학생들에게 쓸데없는 희망을 주기 때문에 그 부분이 더 싫다.. 너무 비관적이고 현실적이라고..?? 세상 살아보면 그런 부분 인정하고 사는것이 더 좋다.. 이상적인..?? 꿈같은..?? 그 따위것이 통하리라 보는가..??

실예로 과거 자격증만 있으면 무슨 일이건 할 수 있다고 했다.. 대학교..?? 나올 필요 없다.. 그리고 자신이 원하는 공부하고 싶은 특출난 분야가 있다면 대기업도 문제 없다고 했다.. 학력 학벌 언어점수 등등 다 파괴한다면서 말이지.. 진짜 속된말로 개똥같은 소리 하지말라고 해라.. 어떤 회사를 가건 어떤 회사에서 이력서를 보건 기본적인 학력 학벌 다 본다.. 학력과 학벌 때문에 연봉부터 차이가 난다.. 언어점수..?? 마찬가지다.. 그리고 자격증..?? 진짜.. 말같은 소리를 해야지.. 10개 정도 있는 주변 지인도 일반 소기업에서 일한다.. 연봉?? 겁니 짜다.. 물론!!! 진짜 그런 모든 스펙들을 파괴하고 잘되는 사람은 있다.. 그런데 그 사람들이 많어..?? 상위 1% 가지고 제발 좀 일반화 시키지 말란 얘기다.. 기자양반들아.. 정말이지.. 에휴.. 지인이 기자여서 가끔 쇠주 일잔에 이런 얘기를 해봤는데.. 본인도 그 부분은 인정하더라..

얘기가 너무 돌아왔는데 내가 해당 기사에 대해서 맘에 안드는 부분들이 있는데.. 그것들을 보면서 얘기를 다시금 이어가보자.. 어후.. 글 쓰다보니 또 열받네 진짜 ㅡㅡ.. 본문 내용중에 일반적인 애기와 구글 내부 분위기 등은 따로 발췌하지 않았다..

"구글에 입사하려면 어떻게 해야 하느냐고 묻는 친구가 많아요. 근데 실제로 지원서를 써 보는 사람은 거의 없죠. 일단 도전하세요." 지난해 10월 구글 본사에 한국인 가운데 최연소로 입사한 H 씨(22)의 말이다. 대학생들이 '꿈의 회사'로 꼽는 구글에 입사해 1년째 일하고 있는 한 씨를 전화로 인터뷰했다.
- 위 내용은 도입부 내용인데.. 일단 도전하라고..?? 아놔 진짜 말 같은 소릴 하라고 진짜.. 우선 그냥 그러려니 하고 넘어갔다..

H 씨는 어려서부터 컴퓨터 프로그래밍 하나에만 몰두했다. 수학과 과학을 좋아했던 H 씨는 초등학교 때부터 정보올림피아드를 공부했다. 주어진 문제를 컴퓨터 프로그램을 만들어 푸는 대회다. 중학교 때 정보올림피아드에서 금상을 받아 한성과학고에 입학했고 2년 만에 졸업했다.
- 이게 스펙이 없다고..?? 웃긴다.. 그럼 여기서 말하는 스펙은 머란 말인가..?? 요새는 초중고 교장이 추천서를 써줘도 그게 스펙으로 들어가는 마당에.. 올림피아드 금상이 스펙이 없는거라고..?? ㅋㅋ 과학고 출신..?? 그것도 2년만에 졸업..?? 이게 스펙이 없는거라고..?? 대체 그럼 당신들이 말하는 스펙이 머란 말이지..??

대부분 서울대나 카이스트를 택하는 친구들과 달리 H 씨는 프로그래밍을 깊이 있게 공부하고 싶어 성균관대 소프트웨어학과에 입학했다. 대학 친구들과 한국을 대표해 국제컴퓨터학회(ACM)가 주관하는 국제대학생프로그래밍대회(ICPC)에 2년 연속 나갔고, 특별상도 받았다.
- 대부분 서울대나 카이스트를 선택하는데 본인은 성대를 선택했고 거기서 국제대회나가서 특별상도 받았다..?? 여보세요들.. 이런것 자체가 스펙이라구요.. 스펙 없다구요..?? 본인들이 생각하는 스펙의 의미가 진짜 궁금해지네..

구글 입사 기회는 자연스럽게 찾아왔다. 2014년 11월 구글 본사로부터 "입사 지원서를 내 보라"는 e메일을 받았다. 한 씨는 "대회 측에 제공한 e메일 주소를 구글이 받아 입사 권유를 한 것 같다"고 말했다. H 씨는 "나도 거창한 스펙 없이 지원서에 학점과 들은 과목, 다룰 수 있는 프로그래밍 언어, 대회 경력, 인턴 경험을 간략하게 썼다"며 "한번 떨어진다고 이후 합격 못 하는 것도 아니니 구글에 오고 싶으면 일단 지원해 보면 좋겠다"고 말했다.
- 여기서도 웃긴 말들이 보이지 않는가..?? 서두에 그랬지만 제목도 그렇고 구글에 일단은 지원하라고..?? 당신은 지원한 것이 아니다.. 구글에서 당신의 스펙을 보고서 학교가 되었건 무엇이 되었건 당신의 그동안 활동이 있기 때문에 그것이 스펙이 되어서 지원서를 보내라고 한것이다.. 그건 본인이 지원한 것이 아닌 구글측에서 제의를 한것이다.. 그런 것을 가지고 나는 스펙이 없는 지원을 했다라고 떠들지 말란 것이다..

그리고 나는 별다른 스펙이 없는데 간략하게 이력서를 제출했다고..?? ㅎㅎ 올림피아드 입상한것도 한성고를 나온것도 성균관대에서 국제대회를 나간것도 입상한것도 인턴경험이 있는것도 모든 것이 다 본인의 스펙인 것이다.. 대체 무엇이 스펙이 없다는 것이지..??

그럼 일류 고등학교나와서 일류 대학교 나오고 해외 일류 대학교 나오고 방송 타고 논문과 업적이 엄청 많으면 그게 스펙이 된다고 보는건가..?? 본인의 기준과 실제 일반 회사원들 혹은 회사에 지원하는 지원자들은 당신과 같은 스펙이 없다.. 그 대단한 스펙이 말이지.. 정말 너무 웃긴다..

글을 쓰다보니 감정이입이 너무 되서 짜증나는 말들 그리고 약간은 험한 말들도 나왔는데 솔직한 내 심정이다.. 기사에서 멋대로 특별한 케이스를 일반화 시키고 하는 것들 자체가 너무 꼴볼견이다.. 진짜 스펙없이 어딘가를 입사했거나 그런 것을 적으려거던.. 특정 분야에 특출난 영재거나 특별한 재능이 있어서 그것을 스스로 공부하고 키워나가다가 입사한 케이스를 적어라.. 아니면 저런 글을 적어도 좋고 저런 케이스를 적어도 좋은데 제발 일반화를 시키지 말길 바란다..

다시 말하는데 괜한 자격지심이라고..?? 맞는 얘기다.. 하지만 분명한건 주변에서 이따위 뉴스 때문에 속된말로 개나소나 다 되나보다 하는 착각에 사는 혹은 착각을 하는 취준생 또는 학생들이 많다는 것이다.. 생각점 하고 써라.. 현실을 살면서 왜 현실을 안쓰고 이상을 쓰는지 모르겠다..


2016년 9월 6일 화요일

[JAVA] Baekjoon 각 자리수가 등차수열을 이루는 지 판별하는 함수를 구현해 문제를 해결해봅니다..

문제
어떤 양의 정수 X의 자리수가 등차수열을 이룬다면, 그 수를 한수라고 한다. 등차수열은 연속된 두 개의 수의 차이가 일정한 수열을 말한다. N이 주어졌을 때, 1보다 크거나 같고, N보다 작거나 같은 한수의 개수를 출력하는 프로그램을 작성하시오. 

입력
첫째 줄에 1,000보다 작거나 같은 자연수 N이 주어진다.

출력
첫째 줄에 1보다 크거나 같고, N보다 작거나 같은 한수의 개수를 출력한다.

이번에는 등차수열이란다.. 아흠.. 등차수열 너무 오래전 얘기라 먼소린지도 모르겠고, 우선 개념부터 잡아야지 하면서 검색하고 있었다.. 그런데 역시나 내 스탈상 정의만 봐서는 이해가 안가고.. 혹시 누군가 부연 설명을 좀 해주지 않았을까 하는 마음에 더 검색을 해봤다..

그런데 역시나 위대한 분들은 세상에 너무 많으시더란 말이지.. occidere 님의 블로그를 가보니 해당 알고리즘에 대한 문제 풀이와 실질적으로 필요한 내용들에 대해서 정리를 잘 해주셨다.. 그래서 문제를 보면서 이해를 할 수 있었다.. 다만, 단번에 이해가 안가서 여러번 읽어봤다능.. ㅠㅜ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package step5;

import java.util.Scanner;

public class FuncHanSu {

    @SuppressWarnings("resource")
    public static void main(String[] args) {

        Scanner scan = new Scanner(System.in);
        
        //int valA = 110;
        int valA = scan.nextInt();
        int valK = 0;
        int valHan = 0;
        int varArr[] = new int[3];
  
        for(int i = 1; i <= valA; i++) {
            if(0 < i && i < 100) {
                valHan = i;
            
            } else if(i == 1000) {
                break;
           
            } else {
                valK = 0;
                int valT = i;
                while(valT > 0) {
                    varArr[valK] = valT % 10;
                    valT /= 10; valK++;
                }
                if(varArr[0] - varArr[1] == varArr[1] - varArr[2]) {
                    valHan++;
                }
            }
        }
        System.out.println(valHan);
    }

}//





[JAVA] Baekjoon 자연수 n에 대해 d(n)의 값을 구하는 함수를 정의해 문제를 해결해봅니다..

오랫만에 코딩 테스트를 올려본다.. 이제 곧 있음 명절인데 모두 즐거운 명절을 맞이 하시길 바라며..

문제
셀프 넘버는 1949년 인도 수학자 D.R. Kaprekar가 이름 붙였다. 양의 정수 n에 대해서 d(n)을 n과 n의 각 자리수를 더하는 함수라고 정의하자. 예를 들어, d(75) = 75+7+5 = 87이다.

양의 정수 n이 주어졌을 때, 이 수를 시작해서 n, d(n), d(d(n)), d(d(d(n))), ...과 같은 무한 수열을 만들 수 있다. 

예를 들어, 33으로 시작한다면 다음 수는 33 + 3 + 3 = 39이고, 그 다음 수는 39 + 3 + 9 = 51, 다음 수는 51 + 5 + 1 = 57이다. 이런식으로 다음과 같은 수열을 만들 수 있다.

33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ...

n을 d(n)의 생성자라고 한다. 위의 수열에서 33은 39의 생성자이고, 39는 51의 생성자, 51은 57의 생성자이다. 생성자가 한 개보다 많은 경우도 있다. 예를 들어, 101은 생성자가 2개(91과 100) 있다. 

생성자가 없는 숫자를 셀프 넘버라고 한다. 100보다 작은 셀프 넘버는 총 13개가 있다. 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97

10000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 출력하는 프로그램을 작성하시오.

입력
입력은 없다

출력
10,000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 증가하는 순서로 출력한다.

해당 문제는 나 스스로 풀진 못했다.. 다른 분이 하신 것을 보면서 문제의 의도와 풀어나가는 과정을 익혔다.. 그런데 아마도 이제 그런 문제가 더 많아질 것으로 보인다.. 아쉽기는 하지만 나 스스로 생각하는 과정 등을 더 발전해보고자 시작했기에 그런 측면에서는 만족한다..

이 문제는 다른 부분 보다는 셀프넘버에 대한 개념과 이해가 가장 중요하다고 생각 된다.. 나두 처음 문제를 접했을 때 문제를 푸는게 문제가 아니라 셀프넘버 자체를 이해를 못하고 있다보니 다른분들이 풀어둔 문제를 봐도 소스 자체가 이해가 안갔다..

정의를 찾다보니 영문으로 된 글도 있었다.. 영문이어서 번역기 돌려서 봤는데 그래도 어느정도의 개념은 문제들과 더블어 이해하다보면 납득?? 은 가더란.. ㅎㅎ



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package step5;

//import java.util.Scanner;

public class FuncNum {

    //@SuppressWarnings("resource")
    public static void main(String[] args) {
  
        //Scanner scan = new Scanner(System.in);
        
        int valA = 0;
        int valB = 0;
        int valC = 0;
        int valD = 0;
        int valResult = 0;
  
        boolean[] generator = new boolean[10000];
        
        for(int i = 1; i < 10000; i++) {
            valA = i / 1000;
            valB = (i / 100) % 10;
            valC = (i / 10) % 10;
            valD = i % 10;
            
            //System.out.println("valA ######## > " + valA);
            //System.out.println("valB ######## > " + valB);
            //System.out.println("valC ######## > " + valC);
            //System.out.println("valD ######## > " + valD);
            //System.out.println("================================");
            
            valResult = valA + valB + valC + valD + i;
         
            if((valResult >= 1) && (valResult < 10000)) {
                generator[valResult] = true;
            }
        }
        
        for(int i = 1; i < 10000; i++) {
            if(generator[i] == false) {
                System.out.println(i);
            }
        }
    }

}//



[Book] 페르시아의 왕자 개발일지..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

페르시아의 왕자 : 개발일지 - 8점
조던 메크너
다이피아

이 책은 20대 후반이상이라면 어렸을때 대부분 해봤을 페르시아의 왕자의 개발일지로 게임을 만든 조던 메크너가 블로그에 틈틈이 올리던 당시의 일기를 묶어서 25주년을 기념해서 이북으로 발매한 책이다. 이 책을 장희재님이 작년에 클라우드 펀딩을 통해서 번역의사를 밝혔고 여기에 다수에 참여자가 모여 펀딩이 이뤄져서 이 책이 한글로도 나오게 되었다.
페르시아의 왕자는 어렸을 때 무척 재밌게 했던 게임이지만 어렸기 때문에 게임에 대해서는 잘 몰랐다. 심지어 이 게임을 만든 조던 메크너가 카라테카도 만든 지도 몰랐었다. 카라테카란 이름만 보는 순간은 무슨 게임인지 몰랐었지만 게임화면을 보는 순간 어렸을 때 마찬가지로 많이 했던 게임임을 바로 알 수 있었다. 이 개발일지는 조던 메크너가 카라테카를 만들어서 어느 정도 성공을 이룬 1985년부터 페르시아의 왕자를 기획해서 만들고 발표한 뒤 페르시아의 왕자 2를 준비하던 1993년까지의 개인 일기를 책으로 엮은 것이다.

개인 일기이기는 하지만 IT종사자라면 꽤나 재미있게 읽을 수 있으니라고 생각한다. 일기이므로 내용은 꽤 산만하고 이런저런 사적인 얘기나 고민들도 많이 나오기는 하지만 어쨌든 재밌다!! 그것도 상당히.. 그 당시에 게임을 어떻게 만들었는지 어떤식으로 진행이 되었는지를 알게 되는 것도 재미가 쏠쏠하다. 페르시아의 왕자를 만들기 위해서 동생을 달리게 하거나 점프하게 하는 걸 영상으로 찍어서 디지타이징하는데 당시에는 컴퓨터에 무지했기 때문에 이런 식으로 게임을 만든다는 것이 너무나 흥미로웠고 그 영상과 마지막 공주와 껴안는 영상이 실제 페르시아의 왕자의 화면과 너무 흡사해서(픽셀로 되어 있음에도!!!) 웃음이 나올 지경이었다. 또한 재미있는 점은 당시의 게임이나 IT 업계의 상황을 엿볼 수 있다는 점인데... 조던 메크너가 "게임 업계가 가라앉고 있다"고 하는 점이나 "젊은 프로그래머들이 치고 올라와서 연배있는 프로그래머들이 걱정한다"는 부분은 왠지 30년 가까이 지난 지금도 크게 달라지지 않은 것같아서 흥미롭다.

조던 메크너는 영화각본을 쓰는 쪽에도 관심이 많았는데 나에게는 페르시아의 왕자를 다 만들고 여러 나라를 돌아다니며 영화관련 일을 하면서 페르시아의 왕자 2 사이를 왔다갔다 하는 뒷부분 보다는 페르시아의 왕자를 한창 만드는 부분이 훨씬 재미가 있었다. 페르시아의 왕자에 나오는 새도우맨이나 적과 칼싸움을 하는 과정, 약물을 먹는 과정이 어떤 과정을 거쳐서 추가되고 만들게 되었는지가 자세히 나와있고 조던 메크너가 페르시아의 왕자를 디자인하면서 하던 고민들이 고스란히 담아 있어서 게임 개발자는 아니지만 개발자로써 무척 흥미롭게 보았다. 물론 이는 페르시아의 왕자가 어렸을때 많이 한 게임이라서 각 게임의 요소들이 기억속에 남아있기 때문일 것이다.

일기형식이라 내용이 부담스럽지도 않으니 더 많은 사람이 이 책을 읽었으면 하는 바램이다. 그리고 이 책은 이북으로만 출시되었는데(클라우드 펀딩에서 높은 금액을 펀딩한 사람들에게는 종이책도 준 걸로 알고 있는데 파는지는 모르겠다.) 이 책을 읽으려면 왠만하면 epub 3가 지원되는 책으로 읽기를 권한다. 내가 알기로는 아이튠즈다이피아에서 epub 3를 팔고 있는 걸로 알고 있다. epub 3를 추천하는 이유는(난 아이튠즈 버전만 봤다.) 책 중간에 나오는 조던 메크너의 실제 메모등의 사진을 확대해서 볼 수 있고 중간중간 나오는 동영상을 플레이해 볼 수 있기 때문이다. 페르시아의 왕자를 만들면서 찍었던 이 영상들을 함께 보는 것은 큰 즐거움으로 보이는데 리디북스등에서 파는 이북에서는 이 동영상을 볼 수 없는 것으로 보인다.(아마도..) 그리고 이는 epub 3가 아닌 알라딘등에서 파는 책도 비슷하리라고 생각한다.

어쨌든 이 자리를 빌어 이 책을 번역해서 발매해주신 장희재님께 감사를 드린다. 사실 클라우드 펀딩에 참여하고(난 클라우드 펀딩을 별로 좋아하지 않는다.) 10월에 나온 다는 책이 올 봄에나 나오게 되면서 트위터에서 좀 징징 거렸었는데(변명을 하자면 늦는걸 징징거렸다기 보다는 늦어지는 것에 대한 피드백이 잘 오지 않아서 그런거였지만...) 책을 읽고 나니 약간 죄송한 마음이다. 정말 엄청난 품질로 좋은 책이 나와버렸다.(번역 품질이 무척 좋다.) 블로그에 가보니 카라테카 개발일지도 번역하신다고 올라와 있던데 기대가 많이 된다.


[EP] FRENDS meetup에서 발표한 "초급 개발자 탈출"..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

지난 6일(토) FRENDS에서 올해는 내부 모임만 계속하다가 오랜만에 meetup을 열었다. 올해 해보기로 한 것중에 하나이기도 하고 이런 행사에 대한 경험도 쌓기 위해서 작은 규모로 진행을 했고 여기서 발표를 하나 맡게 되었다. 발표를 많이 하는 편은 아니지만 그동안은 기술세션만 발표했기 때문에 이번에는 좀 다른 얘기를 할 수 있지 않을까 해서 초심자들에게 혹 도움이 될만한 얘기를 해볼까 해서 "초급 개발자 탈출"이라는 제목을 선택했다. 준비하면서 주제선정을 잘못했나 싶을 정도로 스토리라인을 잡기 어려웠고 기술 세션이 아니다 보니 어떤 얘기를 할지 참 고민을 많이 했는데 그냥 내가 해온걸 중심으로 얘기하자는 생각으로 발표자료를 만들었고 그럭저럭 나쁘지 않았던 듯 하다.



이 발표자료는 슬라이드쉐어에도 올라가있다. 이번 meetup은 처음 기획할 때부터 프로그래밍을 많이 했고 잘하는 사람들 보다는 초심자들을 대상으로 하기로 했었다. 원래는 대학생들을 대상으로 하자는 얘기를 했었지만 대학생들과의 어떤 연결점을 가질 수 없었고 그러다보니 대학생들의 관심사를 추정하기도 어려웠기에 그냥 초심자들을 모았다. 장소는 NHN의 후원을 받았지만 큰 장소를 구할수 없어서(Toz에 의외로 큰 장소가 없더라는...) 30명까지만 신청받았다. 하지만 우리 인원수는 세지 않았기 때문에 우리는 계속 서있어야 했다. ㅠㅠ 장소가 협소하기도 하고 의도하고 있는 타게팅이 있었기 때문에 이번에는 역필터링(?)을 시도했다. 그러니까 이미 잘하는 사람은 왠만하면 받지 않으려는 의도였고 이 생각은 내가 낸 의견이었지만 막상 하고보니 그냥 아는 사람 필터링이 되어버려서 좋은 시도였는지는 애매하다. 다음번에 하게되면 고민이 좀 될듯...



아무튼 무료세미나이긴 하지만 너무 신청이 막 들어오고 당일날 안오면 주최하는 입장에서는 곤란하기 때문에 전에 KSUG에서도 한번 한적이 있었던 만원을 선입금받고 왔을 경우 돌려주는 방식을 취했다. 이는 무료세미나에서 일반적으로 발생하는 갈지 안갈지 모르지만 일단 신청해놓고 당일날 고민하는 것을 좀 막고 곰곰히 생각해보고 신청하길 바라는 의도였는데 생각외로 3일정도만에 30석이 다 찼다..(오오~~) 이렇게 신청하신 분중에 28명이나 오시는(2분은 미리 연락도 주신...) 높은 참여율에 여성개발자도 5분이나 오셨다. 이건 정말 흔치않은... 보통 여성개발자들이 가고싶어도 가면 혼자만 여자일까봐 잘 안온다는 얘기를 들었는데 5명이나 오신건 꽤 흥미로운 점이었다. 간만에 처음 시도하는 방식으로 진행한 것 치고는 큰 사건사고 없이 잘 진행된것 같다.

My Comment..
햄이 오래전에 발표한 내용들이다.. 위에 언급된 것처럼 초보 개발자 또는 개발자를 시작하려는 사람들을 대상으로 한 발표다..

하지만 내가 보기엔 나처럼 개발을 오래했거나 개발자라는 이름으로 오래 지난 사람도 참석했어도 좋았을 발표였다고 생각한다.. 비록 엄청 오래전이긴 하지만 말이지.. ㅎㅎㅎ 이유는 전체적인 내용이 개발을 어떻게 접근해야될지.. 개발을 위해서 어떻게 하는게 좋을지 등등의 개발자로써 살아가면서 취했으면 하는 행동들과 방법론 등에 대한 내용들이 많기 때문이다..

항상 발표내용처럼 할 순 없을 것이다.. 저마다 입장과 상황 차이가 있을테니 말이다.. 그렇지만 해당 내용을 보고 한 번 쯤은 고민해보고 내가 하고 있는 것들과 비교를 하면서 조금더 덧붙여볼만한 것은 덧붙여서 발전해나가는 것도 좋다고 생각한다.. 나 또한, 그렇게 해서 무엇인가를 하려고 노력하였으니 말이다.. ㅎㅎ


2016년 9월 5일 월요일

[JAVA] 20장 스프링을 사용한 원격작업(remoting) 및 웹 서비스 #2..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.

20.6 JMS

의존하는 통신 프로토콜로 JMS를 사용해서 서비스를 투명하게 노출할 수도 있다. 스프링 프레임워크의 JMS 원격지원은 아주 기본적이고(같은 스레드와 트랜잭션이 아닌 같은 Session에서 주고 받는다.) 이러한 스루풋은 아주 의존적인 구현체가 될 것이다. 이러한 단일 스레ㅇ드이면서 트랜잭션이 아닌 제약사항은 스프링의 JMS 원격 지원에만 적용된다. JMS에 기반한 메시징에 대한 스프링의 더 많은 지원에 대한 내용은 Chapter 22, JMS (Java Message Service)를 봐라.
다음 인터페이스는 서버와 클라이언트가 모두 사용한다.

1
2
3
4
5
6
7
8
Java

package com.foo;

public interface CheckingAccountService {

  public void cancelAccount(Long accountId);
}

다음은 앞의 인터페이스의 간단한 구현체로 서버측에서 사용한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Java

package com.foo;

public class SimpleCheckingAccountService implements CheckingAccountService {

  public void cancelAccount(Long accountId) {
    System.out.println("Cancelling account [" + accountId + "]");
  }
}

이 구성파일은 클라이언트와 서버가 모두 공유하는 JMS-인프라스트럭처 빈을 가진다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://ep-t43:61616"/>
  </bean>

  <bean id="queue" class="org.apache.activemq.command.ActiveMQQueue">
    <constructor-arg value="mmm"/>
  </bean>
</beans>


20.6.1 서버측 구성

서버에서는 JmsInvokerServiceExporter를 사용해서 서비스 객체를 노출해야 한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="checkingAccountService"
      class="org.springframework.jms.remoting.JmsInvokerServiceExporter">
    <property name="serviceInterface" value="com.foo.CheckingAccountService"/>
    <property name="service">
      <bean class="com.foo.SimpleCheckingAccountService"/>
    </property>
  </bean>

  <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="queue"/>
    <property name="concurrentConsumers" value="3"/>
    <property name="messageListener" ref="checkingAccountService"/>
  </bean>
</beans>


Java

package com.foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Server {

  public static void main(String[] args) throws Exception {
    new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"});
  }
}


20.6.2 클라이언트측 구성

클라이언트는 합의한 인터페이스 (CheckingAccountService)를 구현할 클라이언트측 프록시를 생성해야 한다. 다음 빈 정의로 생성한 최종 객체(The resulting object created off the back of the following bean definition)를 다른 클라이언트측 객체에 주입할 수 있고 JMS로 서버측 객체 호출에 대한 포워딩은 프록시가 담단한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="checkingAccountService"
      class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean">
    <property name="serviceInterface" value="com.foo.CheckingAccountService"/>
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="queue" ref="queue"/>
  </bean>
</beans>


Java

package com.foo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

  public static void main(String[] args) throws Exception {
    ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[] {"com/foo/client.xml", "com/foo/jms.xml"});
    CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService");
    service.cancelAccount(new Long(10));
  }
}

Lingo 프로젝트의 지원도 살펴보길 원할 것이다. “ Lingo는 스프링 프레임워크에 기반해서 스프링의 원격 라이브러리가 JMS를 지원하도록 확장한 경량 POJO 기반의 원격 메시징 라이브러리이다. ”

20.7 원격 인터페이스의 자동탐지는 구현되지 않았다

원격 인터페이스를 구현한 인터페이스를 자동으로 탐지하지 않는 주요 이유는 원격 호출자에 너무 많은 연결을 만들지 않도록 하기 위함이다. 대상 객체는 호출자에게 노출하고 싶지 않은 InitializingBean나 DisposableBean 같은 내부 콜백 인터페이스를 구현할 것이다.
대상 객체가 구현한 모든 인터페이스의 프록시를 제공하는 것을 로컬에서는 중요치 않지만 원격서비스를 익스포트하는 경우에는 원격에서 사용할 동작을 가진 특정 서비스 인터페이스를 노출해야 한다. 내부 콜백 인터페이스와 달리 대상 객체는 여러 비즈니스 인터페이스를 구현하고 그 중 하나는 원격으로 노출할 것이다. 그래서 서비스 인터페이스등을 지정해야 한다.
이는 설정의 편리함과 내부 메서드를 실수로 노출할 위험사이의 트레이드오프다. 서비스 인터페이스를 지정하는 것이 항상 많은 노력이 드는 것은 아니고 특정 메서드 노출을 안전하게 제어할 수 있게 해라.

20.8 기술 선택시 고려사항

여기서 언급한 모든 기술은 단점을 가진다. 기술을 선택할 때 요구사항과 노출할 서비스, 네트워크로 보낼 객체를 신중하게 고려해야 한다.
RMI를 사용할 때는 RMI 트래픽을 터널링하지 않고는 HTTP 프로토콜로 객체에 접근할 수 없다. RMI는 상당히 비용이 드는 프로토콜로 네트워크사이에 직렬화가 필요한 복잡한 데이터 모델을 사용할 때 중요한 전체 객체의 직렬화를 지원한다.하지만 RMI-JRMP는 자바 클라이언트에 의존성이 있는 Java-to-Java 원격 솔루션이다.
HTTP기반의 원격이 필요하다면 스프링의 HTTP 인보커가 괜찮지만 스프링 HTTP 인보커도 자바 직렬화에 의존하고 있다. 스프링 HTTP 인보커는 RMI 인보커와 기본 인프라스트럭처를 공유하면서 전송에 HTTP를 사용한다. 스프링 HTTP 인보커는 Java-to-Java 원격에만 제한될 뿐만 아니라 클라이언트와 서버측이 모두 스프링이어야 한다. (클라이언트와 서버가 모두 스프링인 경우 RMI가 아닌 인터페이스에는 스프링의 RMI도 적용한다.)
헤시안과 버랩은 명시적으로 자바가 아닌 클라이언트를 허용하므로 이기종간의 환경에서 작업할 때 중요한 가치를 준다. 하지만 비자바 지원은 아직 제한적이다. 알려진 이슈에는 지연 초기화 컬렉션이 있는 하이버네이트 객체의 직렬화 등이 있다. 이러한 데이터 모델이 있다면 헤시안 대신에 RMI나 HTTP 인코커를 고려해 봐라.
JMS는 서비스 클러스터를 제공하고 로드 밸런싱, 발견(discovery), 자동 장애복구(auto-failover)를 담당하는 JMS 중계자(broker)를 허용할 때 유용하다. 기본적으로 JMS 원격을 사용할 때 자바 직렬화를 사용하지만 JMS 제공자는 네트워크 형식에 따라 다른 기술로 구현한 서버를 사용할 수 있는 XStream같은 다른 메카니즘을 사용할 수 있다.
마지막으로 EJB는 표준 역할(role) 기반의 인증과 인가를 지원하고 원격 트랜잭션 전파(propagation)를 지원해서 RMI보다 뛰어난 이점들이 있다. 스프링 코어가 제공하지는 않지만 보안 컨텍스트 전파를 지원하는 RMI 인보커나 HTTP 인보커를 획득할 수 있다.(서드파티 플러그인이나 커스텀 솔루션을 위한 적절한 훅(hook)을 제공한다.)

20.9 클라이언트에서 RESTful 서비스 접근하기

RestTemplate이 클라이언트가 RESTful 서비스에 접근하는 핵심 클래스다. 개념적으로는 JdbcTemplate과 JmsTemplate이나 스프링 포트폴리오 프로젝트의 템플릿 클래스들과 유사하다. 콜백 메서드를 지정하고 객체를 HTTP 요청 바디로 마샬링하고 응답을 다시 객체로 언마샬링하는데 사용하는 HttpMessageConverter를 구성해서 RestTemplate의 동작을 커스터마이징 할 수 있다. 메시지 형식으로 XML을 사용하는 것이 일반적이므로 스프링은 org.springframework.oxm에 Object-to-XML 프레임워크를 사용하는 MarshallingHttpMessageConverter를 제공한다. MarshallingHttpMessageConverter는 XML을 객체로 매핑하는 기술의 넓은 범위의 선택권을 준다.
이번 섹션에서는 RestTemplate과 관련 HttpMessageConverters를 사용하는 방법을 설명한다.

20.9.1 RestTemplate

자바에서 RESTful 서비스 호출은 보통 Jakarta Commons HttpClient같은 헬퍼 클래스를 사용해서 호출한다. 일반적인 REST 작업에 이 접근은 다음에서 보듯이 너무 저수순이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Java

String uri = "http://example.com/hotels/1/bookings";

PostMethod post = new PostMethod(uri);
String request = // 예약 요청 내용 생성
post.setRequestEntity(new StringRequestEntity(request));

httpClient.executeMethod(post);

if (HttpStatus.SC_CREATED == post.getStatusCode()) {
  Header location = post.getRequestHeader("Location");
  if (location != null) {
    System.out.println("Created new booking at :" + location.getValue());
  }
}

RestTemplate은 6개의 주요 HTTP 메서드에 대응되는 고수준의 메서드를 제공하고 한줄로 다수의 RESTfult 서비스를 호출하고 REST 베스트 프렉틱스를 강제한다.

Table 20.1. RestTemplate 메서드 요약
HTTP 메서드RestTemplate 메서드
DELETEdelete
GETgetForObject
getForEntity
HEADheadForHeaders(String url, String… urlVariables)
OPTIONSoptionsForAllow(String url, String… urlVariables)
POSTpostForLocation(String url, Object request, String… urlVariables)
postForObject(String url, Object request, Class responseType, String… uriVariables)
PUTput(String url, Object request, String…urlVariables)







RestTemplate 메서드의 이름은 작명관례를 따라서 첫 부분은 어떤 HTTP 메서드가 호출되는 지를 나타내고 두번째 부분은 무엇을 반환하는 지를 나타낸다. 예를 들어 getForObject() 메서드는 GET을 수행하고 HTTP 응답을 원하는 객체 타입으로 변환해서 반환한다. postForLocation() 메서드는 POST를 수행하고 전달한 객체를 HTTP 요청으로 변환하고 새로 생성한 객체의 위치를 가리키는 응답 HTTP Location 헤더를 반환한다. HTTP 요청의 예외 처리에서는 RestClientException 타입의 예외가 던져질 것이다. 이 동작은 RestTemplate에 다른 ResponseErrorHandler를 연결해서 변경할 수 있다.
이러한 메서드에 전달하거나 메서드가 반환한 객체들은 HttpMessageConverter 인스턴스를 사용해서 HTTP 메세지로 변환한다. 주요 mime 타입에 대한 컨버터는 기본적으로 등록되어 있지만 messageConverters() 빈 프로퍼티로 자신만의 컨버터를 작성해서 등록할 수도 있다. 템플릿에 등록된 기본 컨버터 인스턴스는 ByteArrayHttpMessageConverter, StringHttpMessageConverter, FormHttpMessageConverter, SourceHttpMessageConverter다. messageConverters()를 사용해서 이 기본값을 덮어쓸 수 있고 MarshallingHttpMessageConverter나 MappingJacksonHttpMessageConverter를 사용한다면 덮어써야 한다.
각 메서드는 String 가변 인자나 Map<String,String> 두가지 형식의 URI 템플릿 인자를 받는다. 예를 들면

1
2
3
4
Java

String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}",
        String.class,"42", "21");

가변 인자를 사용하거나

1
2
3
4
Java

Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);

Map<String,String>를 사용한다.
RestTemplate 인스턴스를 생성하려면 인자없이 기본 생성자를 호출하면 된다. 이는 HTTP 요청을 생성하는 의존 구현체로 java.net에서 표준 자바 클래스를 사용할 것이다. 그리고 ClientHttpRequestFactory 구현체를 지정해서 오버라이드할 수 있다. 스프링은 요청 생성에 Jakarta Commons HttpClient를 사용하는 CommonsClientHttpRequestFactory 구현체를 제공한다. CommonsClientHttpRequestFactory는 org.apache.commons.httpclient.HttpClient 인스턴스를 사용해서 설정하고 org.apache.commons.httpclient.HttpClient는 자격(credentials) 정보나 연결 풀링 기능을 설정할 수 있다.
Jakarta Commons HttpClient를 직접 사용하는 앞의 예제를 RestTemplate을 사용해서 다음과 같이 다시 작성한다.

1
2
3
4
5
6
7
8
9
Java

uri = "http://example.com/hotels/{id}/bookings";

RestTemplate template = new RestTemplate();

Booking booking = // 예약 객체 생성

URI location = template.postForLocation(uri, booking, "1");

일반적인 콜백 인터페이스는 RequestCallback이고 실행(execute) 메서드가 호출되었을 때 호출한다.

1
2
3
4
5
6
7
Java

public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
         ResponseExtractor<T> responseExtractor,
         String... urlVariables)

// Map<String, String>처럼 urlVariables로 오버로드했다.

RequestCallback 인터페이스는 다음과 같이 정의했다.

1
2
3
4
5
Java

public interface RequestCallback {
  void doWithRequest(ClientHttpRequest request) throws IOException;
}

그리고 요청헤더를 조작하고 요청 바디를 작성할 수 있게 한다. 실행 메서드를 사용하는 경우 리소스 관리는 전혀 걱정한 필요가 없다. 템플릿이 항상 요청을 닫고 오류를 처리할 것이다. 실행메서드를 사용하는 방법과 다른 메서드 인자의 의미는 API 문서를 참고해라.

20.9.1.1 URI 사용하기

주요 HTTP 메서드에 대해 RestTemplate는 첫 인자로 문자열 URI나 java.net.URI를 받을 수 있다.
문자열 URI 방식은 가변 문자열 인자나 Map<String,String>를 템플릿 인자로 받고 URL 문자열이 인코딩되지 않아서 인코딩해야 한다고 가정한다. 예를 들면 다음과 같다.

1
2
3
Java

restTemplate.getForObject("http://example.com/hotel list", String.class);

이는 http://example.com/hotel%20list에 GET 요청을 보낼 것이다. 즉, 입력 URL 문자열이 이미 인코딩되었다면 한번 더 인코딩할 것이다. 예를 들어 http://example.com/hotel%20list는 http://example.com/hotel%2520list가 될 것이다. 두번 인코딩하는 것을 원치 않는다면 URL을 이미 인코딩했고 하나의 (완전히 확장된) URI를 여러번 재사용할 때 유용한 java.net.URI 메서드 방식을 사용해라.
URI 템플릿을 지원하는 URI를 생성하고 인코딩할 때 UriComponentsBuilder 클래스를 사용할 수 있다. 예를 들면 URL 문자열로 시작할 수 있다.

1
2
3
4
5
6
7
8
Java

UriComponents uriComponents =
    UriComponentsBuilder.fromUriString("http://example.com/hotels/{hotel}/bookings/{booking}").build()
        .expand("42", "21")
        .encode();

URI uri = uriComponents.toUri();

아니면 각 URI 컴포넌트를 각각 지정할 수 있다.

1
2
3
4
5
6
7
8
9
Java

UriComponents uriComponents =
    UriComponentsBuilder.newInstance()
        .scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build()
        .expand("42", "21")
        .encode();

URI uri = uriComponents.toUri();


20.9.1.2 요청 헤더와 응답 헤더 다루기

앞에서 설명한 메서드에 추가적으로 RestTemplate도 HttpEntity 클래스에 기반한 임의의 HTTP 메서드 실행에 사용할 수 있는 exchange() 메서드를 가진다.
아마 가장 중요한 것중에 하나는 요청 헤더를 추가하고 응답 헤더를 읽는데 exchange() 메서드를 사용할 수 있다는 것이다. 예를 들면 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Java

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity< ?> requestEntity = new HttpEntity(requestHeaders);

HttpEntity<String> response = template.exchange("http://example.com/hotels/{hotel}",
  HttpMethod.GET, requestEntity, String.class, "42");

String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();

앞의 예제에서 MyRequestHeader 헤더를 담고 있는 요청 엔티티를 먼저 준비한다. 그 다음 응답을 획득하고 MyResponseHeader와 바디를 읽는다.

20.9.2 HTTP 메시지 변환

getForObject(), postForLocation(), put() 메서드에 전달하거나 반환받은 객체는 HttpMessageConverters가 HTTP 요청이나 HTTP 응답으로 변환한다. HttpMessageConverter 인터페이스의 기능은 다음 소스를 보면 알 수 있을 것이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
Java

public interface HttpMessageConverter<T> {

  // 이 컨버터가 해당 클래스와 미디어타입을 읽을 수 있는지 여부를 나타낸다.
  boolean canRead(Class clazz, MediaType mediaType);

  // 이 컨버터가 해당 클래스와 미디어타입을 쓸(write) 수 있는지 여부를 나타낸다.
  boolean canWrite(Class clazz, MediaType mediaType);

  // 이 컨버터가 지원하는 MediaType 객체 목록을 반환한다.
  List<MediaType> getSupportedMediaTypes();

  // 주어진 입력 메시지에서 해당 타입의 객체를 읽어서 반환한다.
  T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException,
      HttpMessageNotReadableException;

  // 주어진 출력 메시지에 주어진 객체를 작성한다.
  void write(T t, HttpOutputMessage outputMessage) throws IOException,
      HttpMessageNotWritableException;

}

메인 미디어(mime) 타입에 대한 구현체는 프레임워크가 제공하고 있고 클라이언트측에서는 기본적으로 RestTemplate에 등록되어 있고 서버측에는 AnnotationMethodHandlerAdapter로 등록되어 있다.
HttpMessageConverter의 구현체는 다음 섹션에 나와있다. 모든 컨버터는 기본 미디어 타입을 사용하지만 supportedMediaTypes 빈 프로퍼티를 설정해서 덮어쓸 수 있다.

20.9.2.1 StringHttpMessageConverter

HTTP 요청과 응답에서 문자열을 읽고 쓸 수 있는 HttpMessageConverter 구현체다. 기본적으로 이 컨버터는 모든 텍스트 미디어 타입(text/*)을 지원하고 text/plain의 Content-Type으로 작성한다.

20.9.2.2 FormHttpMessageConverter

HTTP 요청과 응답에서 데이터를 읽고 쓸 수 있는 HttpMessageConverter 구현체다. 기본적으로 이 컨버터는 application/x-www-form-urlencoded 미디어 타입을 읽고 쓴다. 폼(form) 데이터를 읽어서 MultiValueMap<String, String>에 작성한다.

20.9.2.3 ByteArrayMessageConverter

HTTP 요청과 응답에서 바이트 배열을 읽고 쓸 수 있는 HttpMessageConverter 구현체다. 기본적으로 이 컨버터는 모든 미디어 타입(/)을 지원하고 application/octet-stream의 Content-Type으로 작성한다. 이는 supportedMediaTypes 프로퍼티를 설정하고 getContentType(byte[])를 오버라이딩해서 덮어쓸 수 있다.

20.9.2.4 MarshallingHttpMessageConverter

org.springframework.oxm 패키지의 Marshaller와 Unmarshaller 추상화를 사용해서 XML을 읽고 쓸 수 있는 HttpMessageConverter 구현체다. 이 컨버터를 사용하기 전에 Marshaller와 Unmarshaller가 필요하다. Marshaller와 Unmarshaller는 생성자나 빈 프로퍼티로 주입할 수 있다. 기본적으로 이 컨버터는 text/xml와 application/xml를 지원한다.

20.9.2.5 MappingJacksonHttpMessageConverter

Jackson의 ObjectMapper를 사용해서 JSON을 읽고 쓸 수 있는 HttpMessageConverter 구현체다. 필요할 때 Jackson이 제공하는 어노테이션을 사용해서 JSON 매핑을 커스터마이징할 수 있다. 더 세밀한 제어가 필요한 경우 해당 타입에 커스텀 JSON 직렬화/역직렬화(serializers/deserializers)가 필요한 곳에 ObjectMapper 프로퍼티로 커스텀 ObjectMapper를 주입할 수 있다. 기본적으로 이 컨버터는 application/json를 지원한다.

20.9.2.6 SourceHttpMessageConverter

HTTP 요청과 응답에서 javax.xml.transform.Source를 읽고 쓸 수 있는 HttpMessageConverter 구현체다. DOMSource, SAXSource, StreamSource를 지원한다. 기본적으로 이 컨버터는 text/xml과 application/xml를 지원한다.

20.9.2.7 BufferedImageHttpMessageConverter

HTTP 요청과 응답에서 java.awt.image.BufferedImage를 읽고 쓸 수 있는 HttpMessageConverter 구현체다. 이 컨버터는 Java I/O API가 지원하는 미디어타입을 읽고 쓴다.