작년 겨울부터 새로운 취미를 시작했다. 작년 겨울 휴가를 근처 스키 리조트인 Lake Tahoe에서 보냈는데, 그 때 내가 킨들에 담아 간 책은 소설 책이 아니라 ‘iOS 프로그래밍:The Big Nerd Ranch Guide‘ 였다. 원래 대학 졸업하고 처음 시작한 일이 게임을 만드는 일이었으니까 코딩은 전에도 많이 했었지만, MBA에 진학한 이후로는 거의 할 일이 없었는데 다시 해보기 시작한 것이다.
Friend Collage 코딩하기
전에 페이스북에서 Friend Collage라는 것을 보았다. 페이스북 친구들의 프로필 사진을 이용해서 자신의 프로필 자신을 모자이크로 재구성하는 것이다. 꼭 친구들의 프로필 사진들만이 아니라 내가 원하는 이미지들을 이용해서 자유자재로 만들어보고 싶어 그런 게 있는지 여기 저기에서 검색해 보았지만, 딱 내가 원하는 것은 없었다.
가만히 생각해 보니 그리 어렵지 않겠다는 생각이 들어 코딩을 시작했다. 내가 생각한 알고리즘은 이런 것이었다.
- 그림을 격자로 나눈다.
- 각 격자마다 빨간색(R), 초록색(G), 파란색(B)의 평균 값을 구한다. (컴퓨터는 Red, Green, Blue의 세 가지 색을 이용해서 화면에 색을 표현하고 있다)
- 타일로 쓰일 그림을 불러온 후, 마찬가지로 각각에 대해 빨간색, 초록색, 파란색의 평균 값을 구한다.
- (2)와 (3)의 결과를 비교한다. 즉 값의 차이, 또는 색의 거리를 계산한다.
- ‘값 차이’가 가장 적은 이미지를 찾아 이를 배치한다. 같은 그림이 계속 등장하면 재미가 없으니 한 번 쓰인 이미지는 다시 쓰이지 않도록 한다.
이걸 코드로 옮기면 어떻게 될까?
1. 먼저, 그림을 정해진 크기의 격자로 나누어서 저장해둔다.
위 코드에서 ‘bg‘는 배경 그림 이미지를 나타내고, getSubimage 는 그림의 특정 부분을 잘라내는 명령어이다. 즉, bg.getSubimage 라고 하면 ‘bg라는 이미지에서 특정 부분을 잘라내어라’라는 뜻이다. 그 ‘특정 부분’이 괄호 안에서 정의된다. 첫 번째는 가로 좌표, 두 번째는 세로 좌표, 그 다음은 격자 가로 크기, 그 다음은 격자 세로 크기이다. TILE_WIDTH와 TILE_HEIGHT 미리 정의되어 있다. 그림으로 나타내면 다음과 같다. 가운데 있는 등호는 ‘오른쪽 연산의 결과를 왼쪽에 저장하라’는 뜻이다. 예를 들어, A = 3 * 5 는, 3과 5를 곱한 결과, 즉 15를 A에 저장하라는 뜻이다. 위의 예에서는 잘라낸 그림을 tileToAnalyze라는 곳에 저장해두라는 뜻이다. 이렇게 일단 저장해 두면 저장된 그림을 가지고 뭐든 할 수 있게 된다. 이 경우에는, 그 그림마다 R, G, B값을 얻어내어 평균 값을 구할 것이다. 그림으로 표현하면 아래와 같다.
2. 잘라진 그림의 평균 R, G, B 값을 구한다.
복잡해 보이는데, 설명을 해보겠다. image.getRGB(k, l)은, k와 l에 해당하는 지점에서 있는 점의 R, G, B 값을 구하는 명령어이다. 그 결과 R, G, B값이 pixel이라는 숫자에 저장된다. 그런데 pixel에는 이 세 개의 값이 합쳐서 들어가 있으므로 R, G, B값을 따로 찾아내려면 분리를 해야 한다. 그래서 ((pixel >> 16) & 0xff)과 같은 코드가 필요하다 (이 코드는 설명이 길어지므로 생략한다.). 분리된 R, G, B 값은 각각 rgb[0], rgb[1], rgb[2]에 들어가는데, 그냥 들어가는 게 아니라 누적 합산해서 들어간다 (rgb[0] = rgb[0] + xxx). 값의 평균을 구하려면 값을 다 더한 상태에서 픽셀의 갯수로 나누면 된다. 픽셀의 총 수는 (이미지 가로 크기 X 이미지 세로 크기) 이므로 (width*height)라고 표현된다. rgb[0] = rgb[0] / (width*height)는, rgb[0]에 들어가 있는 값(각 픽셀마다의 R 값을 모두 누적 합산한 결과)을 픽셀의 총 수로 나눈 다음에 다시 rgb[0]에 저장하라는 뜻이다. k와 l값은 좌표를 나타내는데, for 명령문이 있기 때문에 (0, 0), (1, 0), (2, 0), …, (0, 1), (1, 1), (2, 1), …, (k, l), …, (이미지 가로 크기, 이미지 세로 크기) 까지 값이 변한다. for (int k=0; k<width; k++) 는 ‘k 값이 0부터 시작해서 width 값보다 작은 동안 k값을 1씩 증가시키면서 아래에 있는 명령을 반복 실행하라’는 뜻이다. 아래 그림을 보자.
3. 타일로 쓰일 그림에 대해 마찬가지로 R, G, B 평균 값을 구한다.
이는 바로 위와 완전히 동일한 과정을 통해 계산할 수 있다.
4. 배경 이미지를 자른 타일의 R, G, B 값과 타일로 쓰일 이미지의 R, G, B 값을 서로 비교한다.
Math.abs(a, b)는 a와 b의 absolute(절대적) 차이를 계산하는 명령어이다. 즉 Math.abs(5 – 3)의 결과는 2이고, 마찬가지로 Math.abs(3 – 5)의 결과도 2이다. 위 코드의 결과로 R, G, B 값의 차이는 deltaSum이라는 공간에 저장된다.
5. 그 값의 차이가 최소인 타일(즉, 배경 이미지를 자른 타일과 가장 R, G, B값이 유사한 타일)을 찾아내어 타일을 배치한다.
6. 이 작업을 모든 타일에 대해 반복한다. 아래는 그 결과이다.
전체 코드는 여기에서 설명한 것보다 더 길지만, 앞에서 설명한 것이 가장 핵심적인 알고리즘이다. 결국 ’코드’이란 머리속으로 생각한 논리를 영어 단어와 기호로 변환하여 표현한 것에 불과하다. 그런 면에서는 외국어를 배우는 것과 비슷하다고 볼 수도 있다. 특수한 사람들만 배울 수 있거나 이해할 수 있는 것이 결코 아니다. 누구나 ‘논리’를 생각해낼 수 있고, 그 논리를 코드로 그대로 옮기면 프로그램이 된다.
마이클 블룸버그 뉴욕 시장은, 올해 초에 트위터를 통해 새 해 결심이 코딩을 배우는 것이라며 코드 아카데미에 등록했다고 했다. 인기 아이폰 앱을 만든 회사, 인스타그램(Instagram) 창업자의 여자 친구는 발렌타인 데이를 맞아 Lovestagram이라는 앱을 만들어서 남자친구를 깜짝 놀라게 해주었다고 한다(결국 나중엔 남자친구의 도움을 받았지만). 인터뷰에서, 그녀는 “누구도 코딩하는 것을 두려워할 필요가 없다.”라고 이야기했다. 2012년, 코딩을 한 번 배워보면 어떨까?
API란?
아는 사람들로부터 “API란 무엇인가?“라는 질문을 참 자주 받았다. API는 Application Programming Interface(애플리케이션 프로그래밍 인터페이스)의 약자이다. 쉽게 설명하기 위해 자동차의 예를 들어 보겠다.
자동차는, 운전하는 사람 입장에서는 참 간단하게 보인다. 액셀러레이터를 밟으면 나가고, 브레이크를 밟으면 선다. 핸들을 돌리면 방향이 바뀐다. 기어를 사용해서 변속을 할 수 있다. 사실, 내부에서 일어나는 일은 훨씬 복잡하다. 휘발유가 공급되고, 공기와 섞이고, 이를 연료로 엔진 속에서 연소가 일어나고, 그 결과 엔진이 돌아간다. 브레이크를 밟으면 바퀴에 달린 브레이크 패드에 압력이 가해진다. 하지만 운전하는 사람은 내부에서 일어나는 일은 알 필요가 없다. 여기서 ‘액셀러레이터, 브레이크, 핸들, 기어’에 해당하는 것이 “자동차의 API”이다. 코드로 예를 들면 아래와 같다.
- putOnAccelerator (int pushLevel): 엑셀러레이터를 발로 밟는 정도(pushLevel)를 보내면, 그만큼 차가 추진력을 받을 것이다.
- putOnBreak (int pushLevel): 브레이크를 밟는 정도(pushLevel)를 보내면, 그만큼 차의 속력이 감소할 것이다.
- rotateSteeringWheel (float angle): 핸들의 회전 각(angle)을 보내면 차가 그만큼 왼쪽이나 오른쪽으로 돈다.
- changeGear (int newGear): 새로운 기어 값(newGear)을 보내면 그에 따라 차가 변속한다.
- getCurrentSpeed(): 현재 차의 속도를 알려준다.
페이스북 API, 트위터 API도 비슷한 개념이다. 페이스북 API를 익히면 페이스북에서 정보를 얻어오고, 친구들의 사진을 다운로드하고, 페이스북에 업데이트하는 프로그램을 만들 수 있다. 예를 들어 페이스북의 그래프 API 중에 이런 것이 있다.
https://graph.facebook.com/sungmoon.cho
‘sungmoon.cho’라는 페이스북 유저의 가장 기본적인 정보를 요청하는 API이다. 아래와 같은 결과를 얻는다 (브라우저에 이 주소를 직접 복사해서 붙여넣기하면 테스트할 수 있다).
{
“id”: “524334413”,
“name”: “Sungmoon Cho”,
“first_name”: “Sungmoon”,
“last_name”: “Cho”,
“link”: “https://www.facebook.com/sungmoon.cho”,
“username”: “sungmoon.cho”,
“gender”: “male”,
“locale”: “en_US”
}
즉, sungmoon.cho라는 아이디를 가진 유저는 페이스북 번호가 524334413이고, 이름은 Sungmoon, 성은 Cho이며, 남자이고, 설정 언어는 영어(en_US)이다. 주소에서 ‘sungmoon.cho’를 다른 아이디로 바꿔 보면 다른 결과를 얻는다. (이와 같이, 페이스북에서는 아이디만 알면 별도의 보안 절차 없이 성별은 무조건 알아낼 수 있도록 되어 있다.)
비슷하게, https://graph.facebook.com/me/friends 는 ‘내 친구들의 리스트’를 알아오는 API이다. 아래와 같이 친구 리스트를 얻는다 (물론, 다른 사람의 친구 리스트를 얻어오기 위해서는 그 사람의 사전 허락이 있어야만 하고, API 사용자는 허락 받았음을 증명할 수 있어야 한다. 이 때 사용되는 프로토콜이 OAuth이다.’).
{
“data”: [
{
“name”: “Andrew Danger Chung”,
“id”: “2493”
},
{
“name”: “Johanna Javadizadeh”,
“id”: “6110”
},
{
“name”: “John Luna”,
“id”: “7592”
},
{
“name”: “John Lai”,
“id”: “20158”
},
또한, https://graph.facebook.com/sungmoon.cho/picture 는 sungmoon.cho라는 유저의 프로필 사진을 얻어오는 API이다. https://graph.facebook.com/arjun/feed 라는 API를 이용하면 ‘arjun’이라는 사용자의 벽(wall)에 메시지를 남길 수 있다. 페이스북에 정의된 이러한 API는 무궁무진하고, 개발자들은 API를 이용하면 수많은 재미있는 응용 프로그램들을 만들어낼 수 있다. 이런 응용프로그램이 많아질수록 페이스북의 가치는 올라간다. 그래서 많은 회사들이 API를 정의하고, 잘 정리해서 사용법과 함께 이를 공개하는 것이다. 공개하는 API가 많아질수록 개발자들에게는 더 유용하지만, 프라이버시 침해 및 보안의 위험 또한 증가하기 때문에 무조건 많은 것을 공개하는 것이 정답은 아니다.
글 : 조성문
출처 : http://sungmooncho.com/2012/02/18/joy-of-coding/