티스토리 뷰

에세이

private war

June 2013. 4. 15. 23:01

1

 

신입사원 시절의 일이었어요. 보통 일을 할때는 자기가 하는 일의 사이즈를 잘 측정하는 것이 무엇보다도 중요한데, 여태까지 해온 일들과 남은 일들의 사이즈를 잘 파악하고 자신의 속도를 잘 알고 있어야 내가 하고 있는 일이 얼마나 지연되고 있는지, 이것이 이슈화 될 정도로 늦어지고 있는 것인지 내가 조금 더 속도를 내면 어떻게든 맞춰볼 수 있는지 파악할 수 있기 때문이고 관리 측면에서는 이것보다 중요한 일이 없기 때문이죠. 불이 나더라도 초기진화가 중요한 법이고 암에 걸려도 초기에 발견하면 완치율이 올라가듯이 이슈도 초기에 파악하면 적절하게 조치를 취할 수 있는 것이잖아요.

 

날씨가 더워지는 정도를 넘어서서 짜증스러워지는 정도에 도달했던 한 여름, 일이 많아서라기 보다는 에어컨 때문에 벗어날 수 없었던 사무실에서 (어떤 기준을 적용해도) 신입사원이었던  저는 사이즈 측정의 중요성을 뼈저리게 느끼고 있었어요. 당시 제가 처음으로 투입되었던 프로젝트는 개발기간이 3개월로 정해져 있었는데, 저는 제가 맡았던 일이 많은 건지 적은 건지 도저히 파악할 수가 없었고 모르겠다 일단 달리고 보자 라고 생각하고 SI업계가 가지고 있던 전가의 보도, 야근과 주말출근을 남용한 덕분에 2개월 만에 맡은 일을 다 끝내버렸어요. '왜 아직도 퇴근하지 않느냐'라는 질문에 '당신 눈치가 보여서요'라는 말 대신 '일이 많아서요'라는 말을 거짓없이 할 수 없게 되었고, 더 이상 SNS에 불쌍한 개발자 캐릭터를 고수할 수도 없었어요. 여러가지로 곤란한 상황이었지요. 물론 진짜 곤란한 상황은 시작도 되지 않았던 것이지만, 이 바닥에 쉽게 끝나는 일은 하나도 없는 법이었지만 그 때는 잘 몰랐어요.

 

2

 

원래 개발이라는게 마감이 가까워질 수록 퍼포먼스가 올라가는 작업이잖아요. 그런 의미에서 개발 마감이 1달 앞으로 다가온 8월은 더위도 피크였고 사무실 내의 열기도 피크였어요. 저도 이전에 만들었던 프로그램들에서 빼먹었던 것들이나 마음에 걸렸던 것들을 다시 살펴보면서 - 일 없는 티를 내지 않으려고 - 열심히 노력하고 있었어요. 그런데 조금 곤란한 상황이 발생했어요. 사실 많이 곤란했어요. 협력업체 과장님이 하기로 했던 일의 진척이 조금 느렸던 것 같아요. 사실 많이 느렸어요. 그 과장님이 개발하기로 했던 시스템은 웹 어플리케이션의 컴포넌트 간의 재사용율을 계산하여 기능 점수를 산출해주는 프로그램이었는데, 그 과장님은 2달 동안 코드의 LOC (Line Of Code)를 계산해주는 자바 함수 하나만 개발했어요. (이건 신입사원이 아니라 대학생이라고 해도 2달이 아니라 2시간이면 개발 할 수 있었어요.)

 

회의가 여러 번 열렸고, 진척을 복구하기 위하여 다양한 방법이 논의 되었어요. 가장 많이 나왔던 의견은 오픈소스를 활용하자는 의견이었고 컴포넌트 간의 재사용율을 측정해주는 오픈소스 라이브러리가 그렇게 없는지는 저도 그 때 처음 알았어요. 그리고 과장님이 필사적으로 조사해왔던 각종 오픈소스들은 대부분 부적격 판정을 내릴 수 밖에 없었어요. 그리고 그 과장님도 프로젝트에서 부적격 판정이 내려져서 결국 맡은 일을 내려놓게 되었지요.

 

남은 개발 기간은 20일 남짓이었어요. 테스트 기간등을 최대한 축소시켜서 기간을 최대한 확보하면 약 한 달 정도의 시간이 남아있었어요. 짧다면 한없이 짧은 기간이었지만 최소한 무언가 시도해볼 정도의 시간은 되었지요. 문제는 누가 시도하느냐 였어요. 저는 그 때까지도 '여전히 일이 많은' 티를 내고 있었다고 생각했는데 그건 어린애가 자기 인생이 힘들다고 징징거리는 것과 비슷한 정도의 느낌이었나봐요. 얼마 남지 않았던 제 일은 다 주변의 누군가에게 분배되었고 저에게는 새로운 일이 주어졌지요. 갑자기 사무실이 열섬현상 때문인지 아니면 기분 탓인지 견디기 어려울 정도로 더워지는 것 같았어요.

 

3

 

결국 모든 문제는 하나로 귀결될 수 있었어요. '어떻게 소스코드와 소스코드 사이의 재사용율을 측정할 것인가?' 라는 것이지요. A라는 소스코드가 있고 B라는 소스코드가 있으면 B가 A를 혹은 A가 B를 얼마나 많이 가져다 썼느냐, 그러니까 결국엔 소스코드간의 유사도를 비교하는 로직이 관건이었어요. 이러한 비교방식에 대해서 연구가 되지 않았던 것은 아니었는데 그렇게 활발하게 연구되고 있었던 것 같지도 않아요. 몇 몇 라이브러리들이 있기는 있었지만 그렇게 유용하지도 앟았고 목적에도 잘 맞지 않았어요. 결국엔 뭔가 직접 만들어야 된다는 생각이 들었어요. 어떤 함수에 파일 2개를 던지면 두 코드가 얼마나 유사한지 백분율로 나타내는 메소드를 만들면 되겠다는 생각이 들었어요.

 

문제는 방법이었지요. 정말 단순하게 비교한다면 두 코드를 바이트 단위로 비교해서 유사성을 파악할 수도 있었을 거에요. 그런데 이렇게 측정하면 엔터 하나만 쳐도 두 코드는 완전 다른 코드가 되어버리지요. 엔터와 공백을 다 제거한다면 어떨까요? 하지만 그렇게 된 경우에도 메소드를 그대로 가져온 다음 순서만 적절히 뒤바꾸면 서로 다른 코드가 되어버려요. 그건 좀 아닌 것 같았어요.

 

답이 안 나오는 것 같아서 인터넷에서 몇 가지 논문을 살펴봤는데, 역시나 논문은 논문이고 현장에 적용하기는 쉽지 않다는 사실만 알게 되었어요, 귀중한 시간이 이틀이나 허비되었어요. 그 주 주말은 프로젝트 워크샵이었는데, 일이 많아서 당일에만 참석하고 돌아와서 주말엔 출근하겠다는 의견이 기각되어 버리고 (신입사원은 워크샵때 할 일이 많더라구요.) 결국 귀중한 주말도 허비되었어요. 그리고 월요일 아침, 뭔가 들여다보고만 있으면 안되겠다 싶어서 일단 무작정 무언가 만들기 시작했어요. 제발 시작이 반이라는 격언이 맞는 말이기를 바라면서요.

 

4

 

처음 시작할 때 가지고 있던 아이디어는 매우 단순했어요. 소스 파일은 클래스 단위와 거의 일치하니까 제가 집중해야 될 것은 클래스와 클래스 간의 재사용율 비교였고, 이는 그 클래스가 가지고 있는 메소드와 메소드간의 재사용율의 평균이든 합계든 뭔가 그 비슷한 것이라고 생각했어요. 그리고 메소드와 메소드간의 재사용율은 그 메소드가 가지고 있는 구문들 간의 재사용율의 평균이든 합계든 뭔가 그 비슷한 것이 아닐까 생각했구요.

 

그렇다면 시야를 확 좁혀서 어떤 문장과 문장 사이의 유사성은 어떻게 판단하는 것이 좋을까요? 일단 문장을 정규화할 필요가 있었어요. 공백을 제거하고 기호도 제거하고 주석도 제거하고 전부 소문자로 바꿔버리면 최소한 뭔가 비교하기 좋은 상태가 될 것이라는 것은 자명하지요. 문학이 아니라 자바 구문이라면 공백 같은 건 몇 개가 있던간에 그 문장이 하고자 하는 의도와는 전혀 관계가 없으니까요.

 

 

문제는 그렇게 정규화 된 문장과 문장 사이의 유사성을 어떤 방식으로 계산하느냐 였어요. 여기서 뭔가 막히는 것이 느껴졌어요. 머리 속에 몇 가지 아이디어가 떠오르기는 했는데 별로 신뢰성 없는 방법들이라는 생각도 같이 떠올랐어요. 하지만 저는 개발자였고, 뭔가 막힐 때 제가 도움을 요청해야 될 것이 뭔지는 잘 알고 있었어요. 소스코드 간의 비교에는 별다른 답을 내주지 않던 구글은 문장 간의 비교에는 신뢰성 넘치는 아이디어를 꽤 많이 제시해주더라구요. 그 중에 마음에 들었던 것은 Levenshtein Distance 라는 알고리즘 이었어요. 편집거리(Edit Distance)라고도 불리는 이 알고리즘은 어떤 단어가 다른 단어로 변환되기 위해 필요한 동작을 추가, 삭제, 교체 3가지로 정의하였고, 한 단어가 다른 단어로 바뀌기 위한 최소값을 구할 수 있도록 도와주었어요. 그리고 이 알고리즘의 효율성을 높이기 위하여 Damerau - Levenshtein Distance라는 알고리즘이 등장하였고, 이 알고리즘은 추가, 삭제, 교체 이외에 글자의 이동까지도 고려하여 단어간의 변경에 필요한 최소노력을 측정해주었어요.

 

 

이 알고리즘들은 효율적이었고, 그리 어렵지 않았고 무엇보다도 그 이름이 마음에 들었어요. 뭔가 어려운 기술 구현한 것 처럼 보이잖아요. (이 모든게 다 끝나고 나서 느낀건데, 고객들이나 감리들도 이 알고리즘의 이름에 많은 감명을 받은 것 처럼 보였어요.) 그런데 막상 구현하려고 하니까 알고리즘 자체는 쉬운데 그 알고리즘의 구현은 쉽지 않다는 것을 느끼게 되었어요. 한 단어가 다른 단어로 바뀌는 방식은 굉장히 여러가지 방법이 있을 수 있었고 이 하나를 구하는 것은 전혀 어렵지 않았지만, 최소의 노력으로 다른 단어로 바뀌는 방식을 찾는 것은 손으로 하면 쉬울 지 몰라도 컴퓨터가 하기에는 어려운 문제였어요. 세일즈맨의 여행문제 처럼 NP-Hard 문제의 일종이라는 생각이 들었어요. CMI에서 문제 당 백만 달러씩 걸고 풀어보라고 했던 밀레니엄 세계 7대 수학난제 중 하나인 P-NP 문제 말이에요. 쉽게 말해서 컴퓨터로는 계산하기 어려운 문제였지요.

 

결국 최소한의 노력이 몇 번인지 알아내기 위해서는 모든 노력들을 다 해본 다음에 그 중에서 가장 적게 걸린 횟수를 찾아서 최소값으로 결정해야 되는데, 이게 소스코드의 수많은 구문들에 모두 적용되면 시간이 어마어마하게 많이 걸릴 것 같다는 생각이 들었어요. 하지만 다행히도 이럴 때 활용하라고 Dynamic Programming이라는 기법이 있었지요. 이미 해본 삽질은 다시 반복하지 않는 그 프로그래밍 기법이요. 이 기법은 이렇게 시도할 경우 어차피 결과가 최소값 이상으로 나올 것이라는 사실을 경험적으로 예측해서 쓸데없는 노력은 시도조차 안하고 건너뛰고 정말 의미있는 시도만 하도록 하여 효율성을 높여줘요. 컴퓨터는 계산만 하는 기계가 아니라 기억도 할 수 있는 기계라는 사실에 착안한 멋진 기법이고, 사실 이것도 구현하기는 좀 어려워요. 맘 먹고 만들면 만들 수야 있겠지만 저는 구문간의 비교가 메소드간의 비교로 이어지고 이걸 평균인지 합계인지 모를 뭔가 계산을 통해서 제대로 된 소스코드간의 재사용율이 측정될 것이라는 확신이 없었어요. 일단 해보고 되는 것 같으면 그 때 검증하려고 생각했는데, 확실하지도 않은 알고리즘의 구현에 시간을 많이 들이고 싶지 않았어요. 그래서 구글에서 조금만 검색해보니까 이 알고리즘을 자바로 구현한 사람이 있더라구요. 라이선스 문제가 없는지 확인해보고 바로 가져다 썼지요. 20줄도 되지 않는 이 코드는 두 문자열 사이의 편집거리를 완벽하게 측정해줬어요. 그리고 월요일 오전 업무가 종료되었어요. 참 기쁜 마음으로 점심을 먹으러 갈 수 있었어요.

 

5

 

이제 다시 공은 저한테로 넘어왔어요. 구문과 구문사이의 유사성은 나름 신뢰성 있게 나오는 것 처럼 보였어요. 그렇다면 이제 이 구문과 구문 사이의 유사성을 어떻게 메소드와 메소드 사이의 유사성으로 발전시켜 나가느냐가 문제였어요. 일단 억울한 경우는 최대한 피하는 것이 좋을 것 같다는 생각이 들었어요. 나는 가져다 쓴게 아닌데 가져다 썼다고 프로그램 따위가 지적질 하면 참 억울할 것 같았어요. 하지만 선언문 위치만 바꾼다거나 하는 등의 약삭빠른 변경은 잡아내고 싶었어요. 그래서 생각한 것이, 특정 메소드와 메소드를 비교한다면, 어떤 메소드의 첫번째 구문과 다른 메소드의 모든 구문을 비교하여 가장 유사한 구문을 하나 찾아내서 그 유사성을 비교하면 어떨까 하는 생각이 들었어요.

 

 

그러니까 A 메소드에 구문이 5개가 있고 B 메소드에 구문이 7개가 있다고 하면, A 메소드의 첫번째 구문은 B 메소드의 7개 구문과 모두 편집거리를 측정하고, 이 중 가장 편집거리가 작은 메소드가 자신과 가장 유사한 구문이라고 판단하는 것이지요. 그러면 두 구문은 서로 없어지게 되요. 일종의 동귀어진(同歸於盡) 같은 느낌으로요. 그리고 A 메소드의 2번째 구문이 같은 식으로 반복해요. 그렇게 되면 A 메소드와 B 메소드간의 유사성의 최소값이 나오게 되지요. 물론 전혀 같지 않은 메소드들도 약간의 유사성이 나타난다는 단점이 있기는 하지만 전체 크기에 비하면 무시할 정도로 작은 수치라고 생각했어요. 그리고 3년이 지나서 이 글을 쓰는 입장에서 생각해보니 정말 더 최소값을 구하려면 서로 없어지는 것이 아니라 모든 경우의 수를 다 따져보고 최소값을 (마찬가지로 Dynamic 하게) 구하는 것이 맞지 않았나 하는 생각이 드네요. 당시에는 급하긴 급했나봐요. 어쨌든 두 메소드간의 유사성을 수치적으로 구할 수 있었지요. 문제는 몇 퍼센트나 바뀌었나 백분율로 측정이 되어야 된다는 것이었어요.

 

일단 구한 것은 두 메소드간의 편집거리 라고 할 수 있어요. 구문간의 편집거리의 최소값의 합이었으니까 이건 일종의 문장간의 편집거리의 최소값이 되겠지요. 그렇다면 이걸 뭘로 나눠야 제대로 된 백분율이 나올까요? 프로그래밍의 영역에서 수학의 영역으로 바뀌자 머리가 아파지는 것을 알 수 있었어요. 이런건 구글 검색해봐야 나오지도 않구요. 더운 사무실에서 잘 식지도 않는 머리를 식히려고 무던히 노력한 끝에 간신히 최대값으로 나누면 된다는 생각을 했어요. 편집거리가 최대가 되는 지점은 두 메소드가 완전히 다른 경우이겠지요. 그렇다면 더 큰 메소드의 크기로 나누면 될까? 라고 잠시 생각했는데 뭔가 숫자가 이상했어요. 조금 더 고민해본 끝에, 두 구문이 서로 자폭할 때 더 큰 구문의 크기의 총 합이 최대값이 된다는 사실을 유추해낼 수 있었지요. 그래서 메소드의 유사도는 각각의 구문간의 유사도의 평균값에 구문의 크기를 고려한 일종의 가중평균값이 되었어요.

 

 

 

그렇게 수치를 구해보고 이리저리 테스트 해보자 대충 맞는 듯한 숫자가 나왔어요. 아이디어는 간단했지만 구현하는데 시간이 좀 걸려서 어느덧 시간이 많이 늦어서 일단은 퇴근을 했어요. 그 날도 결국 집이 아닌 집에 가는 길 어딘가에서 날짜가 바뀌었어요.

 

6

 

다음날 아침이 되었고 출근을 했지만 여전히 이렇게 구해진 수치가 정확한 값을 가지고 있다는 확신은 없었어요. 하지만 메소드 간의 유사도를 구하자 클래스 간의 유사도를 구하는 것은 쉬었어요. 같은 방법을 확대하면 되었거든요. 한 메소드가 가장 덜 유사한 메소드와 같이 자폭하는 방식으로 클래스의 유사도를 구할 수 있었죠. 문득 비교 횟수가 지나치게 많아진다는 느낌을 지울수가 없었어요. 메소드와 메소드만 비교해도 구문과 구문이 서로 계속 비교되고 있는데, 클래스와 클래스를 비교하면 그 비교가 또 메소드와 메소드끼리 서로 전부 비교해야 되었으니까요. 제발 그리 오래 걸리지 않기를 바라면서 클래스와 클래스 간의 비교를 테스트로 돌렸어요.

 

 

 

 

현대 컴퓨터 엔지니어링의 발전은 눈부셨고, 다행히도 수 백에서 수 천 번 시도했을 그 비교는 높은 컴퓨터의 성능에 힘입어서 밀리세컨드 단위의 시간안에 끝났어요. 그리고 콘솔창에 뭔가 적당한 숫자가 떨어졌지요. 이론 상으로는 맞는 숫자일텐데 그 숫자가 정말 맞는 숫자인지 확신이 없었어요. 이론적으로 검증을 해볼까 실험적으로 검증 해볼까 고민하다가 일단 좀 많이 가져다 쓴 클래스를 한 번 비교해봤어요. 매우 높은 유사도가 나왔어요. 이번엔 새로 만든 클래스를 한 번 비교해봤어요. 매우 낮은 유사도가 나왔어요. 몇 번 더 해봤는데 대충 맞는 것 같았어요. 소스코드 간의 재사용율을 측정하는 알고리즘이 대충 완성된 것 같았어요. 점심시간이 되기 전이었어요.

  

그리고 알고리즘을 대충 정리해서 논리적으로도 어느 정도 합당한 방식이라는 것을 설득할 준비를 했어요. 예상했던 것 처럼 오픈소스를 가져다 쓰지 않고 새로 구상해서 만들어낸 재사용율 측정 방식에 대해서 선배, 고객, 감리, 교수들의 의심이 꽤 있었고 다행히도 큰 반발 없이 지나갈 수 있었어요. 사실 재사용율 측정하는 거야 하루 반 나절이면 끝났지만 그걸 어플리케이션으로 만들고 UI를 만들고 부수적인 로직을 추가하는 것은 한 달이 넘게 걸렸지요. 그건 별로 재미 없는 작업이었어요.

 

7

 

그리고 이 이야기는 한여름 밤의 디버깅으로 이어지게 되요.

 

8

 

프로젝트는 잘 끝났어요. 그리고 그걸로 정말 끝났어요. 처음에 이슈화 되었던 이 프로그램은 이슈가 사라지자 프로젝트에서 개발되던 다른 모든 프로그램들과 마찬가지로 평범하게 달성된 목표 중 하나가 되었지요. 사실은 다 만들고 고객들에게 보여주니까, 왜 비교대상이 되는 소스코드와 비교할 소스코드를 하나씩 찍어야 되냐고, 프로젝트 압축파일을 던지면 알아서 비교하게 해달라고 하더라구요. 시험삼아서 전수검사를 돌려봤더니 80일이 넘게 걸린다는 계산결과가 나와서 기겁을 하고 또 한바탕 고민과 고민과 고민을 더해서 자동으로 비교되게 만들었어요. 그러니까 프로젝트가 끝나더라구요.

 

공익적인 목적을 가진 영상, 음악, 광고, 글에서는 보통 결과보다는 과정이 중요하다고들 말해요. 결과가 안 좋더라도 과정이 좋았다면 절대 나쁜 것이 아니라는 것이죠. 그래서 우리는 스포츠 경기를 볼 때 지저분하게 이긴 팀 보다는 정정당당하고 열심히 하다가 진 팀에게 박수를 치잖아요.

 

그런데 컴퓨터 공학에서는, 그리고 SI 프로젝트에서는 보통 과정 보다는 결과가 중요하다고들 생각하고 있는 것 같아요. 일단 잘 돌아가고 있는 소스코드는 그것이 문제를 일으키기 전에는 절대로 다시 볼 일이 없어요. PM님께 '만들라고 하신거 결국 못 만들기는 했는데요, 저 나름대로는 좋은 연습이 되었던 것 같습니다.' 라고 해봐야 좋은 소리 들을리도 없구요. 우리는 지저분하게 완성된 프로그램을 깨끗하게 못 만든 프로그램 보다 높게 평가하지요. 당장 후배가 완벽한 디자인 패턴과 객체지향적 설계를 가진 프로그램을 만드려고 시도하다가 일정을 쭉쭉 연기시키는 것을 보면 한숨이 날지도 모르겠어요. 이 바닥에서 무시못할 저명성을 가진 사람들도 '일단 돌아가게 만든 다음에 고쳐라.'라고 가이드 하는 걸요.

 

저는 지난 3달 동안 개발 업무 보다는 후배 개발자들을 관리하는 일을 더 많이 했었어요. 후배들에게 목표를 주고, 제대로 하고 있나 지켜보고, 문제가 발생하면 해결해주고 주로 그런 일들이 대부분이었지요. 가끔은 일정에 비해 너무 많은 일이 떨어져서 후배들에게 감당하기 어려운 일감들을 던져줄 때도 있었어요. 그리고 거의 대부분 경우에 후배들은 그걸 감당해내고 마감을 지켜냈구요. 저는 어려운 일을 해낸 후배들에게 수고했다는 말을 하기보다도 서둘러서 '만들라고 했던 것 다 만들었습니다.'라고 위에 보고하느라 바빴구요.

 

소스코드 재사용율 측정 프로그램을 만들라는 지시를 받았을 때 제가 느꼈던 막막함과 끝냈을 때 느낀 성취감은 정말 제 개인적인 것이었고, 프로젝트 입장에서는 어쨌든 목표 중 하나가 완료된 것에 불과했을지도 몰라요. 그 중간에 있었던 좌절이나 고민이나 성취감 등등은 누가 알아주지 않더라구요. 지난 3달 동안 제가 후배들에게 만들어야 된다고 나눠준 프로그램은 200개가 넘었고 거기에는 200번이 넘는 개인적인 좌절과 고민과 성취감 들이 있었을지도 몰라요.

 

우리가 하는 일은 정말 많은 사람이 팀을 이루어서 각자의 위치에서 공통의 목표를 향해 노력하는 일이에요. 수백 명이 넘는 사람들이 수천 개가 넘는 목표를 완료하고 그 목표가 모여서 프로젝트를 마무리하게 되죠. 우리가 야근을 하고 주말에 출근을 하고 쉴틈 없이 일하는 것은 결국 아웃이 되려는 공을 향해 몸을 날리는 운동선수의 마음과 크게 다르지 않을 거에요. 팀을 위해서 개인을 희생하는 행동이요. 다만 우리가 희생하는 행동들은 너무 흔한탓에 별다른 주목을 받지 못하는 경우가 많아요.

 

결국엔 우리가 하는 일은 결과가 너무나 중요해서 그 과정은 너무 쉽게 잊혀져요. 하지만 그게 그렇게 쉽게 잊혀질만큼 무가치한 일이던가요. 언제나 목표는 미친듯이 높고 그 목표에 도달하기 위해서 우리도 자신의 능력과 시간을 미친듯이 소모하는 걸요. 그 결과야 성공 혹은 실패 한 단어로 정의될 지 몰라도 그 과정은 거의 전쟁이에요. 정말 지극히 개인적인 전쟁이요. 그것들이 그렇게 무가치한 일은 아니었을꺼에요. 아무도 알아주지 않더라도.

'에세이' 카테고리의 다른 글

Last man standing  (0) 2013.11.16
근로자의 날  (0) 2013.05.20
private war  (0) 2013.04.15
失速 (Stall)  (0) 2013.03.09
양보  (0) 2013.03.08
지나치다  (2) 2012.02.22
댓글
댓글쓰기 폼