블로그


Youngrok Pak at 10 years, 5 months ago.

블로그 태그를 붙인 글을 최신 순으로. 좀더 개인적인 글은 일기장에. 블로그 글 전체 목록은 내 글 모음에.


안드로이드를 하면서 다시 생각해본 OOP

 

창업하고 나서 GUI 프로그래밍을 많이 하게 된다. 원래 하던 웹에 더해서 플래시에 아이폰에 안드로이드까지. 그러면서 개발자들의 실력 차이가 정말 크게 드러나는 분야가 GUI라는 생각이 들곤 한다. 실력차가 크게 드러나는 이유 중 하나는 OOP다. GUI 프로그래밍은 OOP를 모른다면 제대로 돌아가게 만드는 것조차 힘든 분야다. 겉으로는 얼추 돌아가는 것처럼 보여도 버그가 산재해 있는 경우도 많다. 코드의 양도 엄청난 차이가 난다. 내가 본 경우에도 OOP를 아는 사람과 모르는 사람의 코드 라인수가 10배 가까이 차이가 나는 것을 본 적이 있다. 생산성의 차이는 그보다 더 컸으리라 짐작된다. 사실 난 개발이란 분야에서 전문가와 초보자간 생산성 차이가 최대 40배까지 난다는 이야기를 처음 들었을 때는 믿을 수가 없었다. 에이, 아무리 그래도 설마. 하지만 몇 차례의 경험 이후 조금 믿게 되었다.

이를테면 이런 식이다. 초보자에게만 개발을 맡기면 처음에 기능의 개수가 적고 단순할 때는 그럭저럭 생산성을 낸다. 전문가만큼은 아니라도 전문가 절반만큼은 되는 것도 같다. 그런데 시간이 지나면서 점점 생산성이 떨어지고 어느 순간 생산성이 0이 되는 순간을 맞이한다. 더욱 놀라운 것은 그 시점을 지나면 마이너스로 내려가기도 한다는 것이다. 되던 기능을 망가뜨리고, 다른 사람이 코드를 건드리는 것 자체를 불가능하게 만들어버린다. 결국 그 사람이 만든 소스 코드를 폐기하게 되면 마이너스의 크기는 더 극적으로 변한다. 이 단계에 이르면 생산성 차이는 40배 정도가 아닐 것이다.

써놓고 보니 굉장히 익숙한 이야기다. 개발자들이 자주 하는 푸념 아니던가? 코드에 더 이상 손을 댈 수가 없어. 다시 짜는 것 말고는 답이 없어. 이런 일이 자신(혹은 팀)에게도 자주 일어난다면 왜 자신의 실력이 발전하지 않는지 고민해야 한다.

그렇지만 그래도 대부분의 프로그래머들은 그럭저럭 헤쳐 나간다. 시스템의 복잡도가 늘 엄청나게 큰 것은 아니기 때문이다. 시스템 프로그래밍이 어렵다 어렵다 하지만 중요한 알고리즘 문제는 이미 다 해결되어 있기 때문에 공학적인 복잡도는 별로 높지 않다. 엔터프라이즈 쪽도 비즈니스 로직의 복잡도는 대부분 RDBMS가 커버하며, 아키텍처도 결국 요청을 받아서 응답을 내주는 것으로 어느 정도 단순화되어 있다. 하지만 GUI는 알고리즘 하나 잘 짠다고 해결되는 것도 아니고, RDBMS가 쿼리 로직을 다 감당해주는 것도 아니다. 거기에 사용자 인터액션이 다양하게 들어온다. 엔터프라이즈는 보통 stateless가 많고, 설령 기능적으로 stateful하다고 하더라도 거시적인 비즈니스 로직 관점에서만 stateful하다. 하지만 GUI는 비즈니스로직 뿐 아니라 화면에 뿌려지는 모든 컴포넌트가 다 stateful하다. 엔터프라이즈에서 stateful이 될 때 stateless보다 얼마나 복잡해지는지를 생각해보면 GUI가 얼마나 복잡할지 상상할 수 있을 것이다.

이것이 아마 제대로 동작하는 GUI 애플리케이션이 드물고, 또 그래서 몇 안되는 GUI 애플리케이션이 시장을 다 잡고 있는 이유일 것이다. 해당 분야에서 시장을 지배하지 못하는 애플리케이션은 기능도 기능이지만 대체로 제대로 동작하지 않는 상황을 흔하게 접한다. 프로그램 쓰다가 아니 이 기능도 얼마 안되는 프로그램이 왜 이렇게 느린거야하는 생각이 든 적 없는가? 아이폰은 저렇게 플릭이 매끄러운데 왜 옴니아는 손가락을 고문하는가? 도대체 MSN 메신저 live는 왜 이렇게 느린 거야. 어떻게 더 intelligent한 IDEA는 메모리 60메가 먹고 있는데 이클립스는 왜 600메가씩 먹고도 느린 거야. 왜 Anycall PC manager는 자꾸 에러를 띄우는 것인가. 왜 우리투자증권의 머그는 띄우는데 5분씩 걸리고 창도 안 닫아지는 거야. 왜 오픈오피스는 느려터진데다가 자꾸 죽는 거야.

GUI 개발이 쉬운 일이라면 이렇게 되지는 않았을 것이다.

그럼 어떻게 하면 GUI 개발을 좀더 잘할 수 있을까? 뭐 개발을 잘하는 방법에는 여러 가지 도가 있겠지만 오늘은 OOP에 초점을 맞춰보자. GUI 개발은 OOP가 없이는 불가능하다. 비 OOP 언어로 개발된 Gtk조차도 OOP적인 개념을 차용하고 있다. OOP에도 여러 가지 특성이 있겠지만 GUI 개발에서 특히 중요한 요소는 다형성이다. GUI에서 다형성이 중요한 이유는 뭘까. 간단한 예를 들어보자. 탐색기에서 파일을 선택하고 Ctrl-C를 누르면 파일이 클립보드로 복사된다. 반면, 웹브라우저에서 텍스트를 긁어서 Ctrl-C를 누르면 텍스트가 복사된다. 이건 어떻게 구현할까? 다음처럼 짜면 어떨까?

function copy(source) {
  if (source is file) {
     copy_file_to_clipboard()
  } else if {source is text) {
     copy_text_to_clipboard()
  }
}

돌아가는데는 문제가 없다. 그런데 이미지도 복사하고 싶으면 어떻게 될까? 저기에 else if가 한 줄 추가되어야 할 것이다. 복사하려는 객체의 종류가 늘어날 때마다 copy함수도 같이 늘어난다. copy만 있으면 뭐 그것만 고치면 되겠지. 그런데, 윈도우 시스템을 다 인스톨해놨는데 고칠 수는 없다. 누군가 새로운 종류의 객체를 copy&paste 가능하게 만들고 싶어도 할 수가 없는 것이다. 설령 고칠 수 있다 하더라도 문제는 남는다. paste는 어떻게 할 것인가? paste에도 똑같은 if else가 들어가야 할 것이다.

OOP에서는 이렇게 해결한다.

function copy(source) {
   source.copyTo(clipboard)
}

즉, 각 객체의 copy 동작은 각 객체에 위임하는 것이다. 다형성은 이런 식으로 if문을 제거한다.

이건 너무 전형적인 예라고 생각하는가? 그렇다면 이건 어떨까. 메일 클라이언트를 만든다고 해보자. 받은편지함에 들어가서 메일을 읽을 때는 답장, 전달, 삭제 등의 메뉴가 나와야 한다. 임시보관함에서 메일을 읽을 때는 보내기 메뉴가 나와야 하고 휴지통에서 읽을 때는 복원이나 완전히 삭제하기 등의 메뉴가 나와야 한다. 이 메뉴를 어떻게 그릴 것인가? 혹시 이렇게 메뉴를 그리려면 지금 어느 편지함에 있는지 알아야 한다라고 생각했다면 당신은 아직 절차적 패러다임으로 생각하고 있는 것이다. 그럼 아마 이렇게 짤 것이다.

class 메일클라이언트:
  def drawMenu(편지함):
    if 편지함 is 받은편지함:
      menu.add(답장)
      menu.add(전달)
      menu.add(삭제)
    elif 편지함 is 임시보관함:
      menu.add(보내기)
    elif 편지함 is 휴지통:
      menu.add(복원)
      menu.add(완전삭제)
    draw(menu)

이제 이걸 어떻게 짜야하는지 알 것이다. 어느 편지함에 있는지를 알려 하지 말고 편지함에 그 일을 맡기면 된다.

class 메일클라이언트:
  def drawtMenu(편지함):
    draw(편지함.getMenu())

class 받은편지함:
  def getMenu(self):
    menu.add(답장)
    menu.add(전달)
    menu.add(삭제)
    return menu

말하자면, 어떤 상태에 따라서 동작이 달라져야 하는 경우에 if else를 쓰지 말고 다형성으로 해결해야 한다는 것이다. if else를 쓰면 그 상태에 따라 달라지는 분기문이 코드 이곳 저곳에 산재하게 된다. 그리고 보통 그 분기 조건도 쓰는 곳마다 미세하게 다르다. 받은편지함의 목록 볼 때 나와야 할 필드와 보낸편지함의 목록을 볼 때 나와야 할 필드는 다르지만 또 사용자가 임의로 만든 폴더는 받은편지함과 비슷할 수 있다. 이런 것들이 다 if else로 처리되면 나중에 감당이 안되는 코드가 남는 것이다.

기능 차원의 거시적인 예를 들었지만 사실 미시적인 부분에서도 이런 다형성은 계속 필요하다. 이를테면 자식 뷰를 가질 수 있는 뷰 컨테이너를 그리는 코드는 어떻게 짤까? 컨테이너에 무슨 뷰가 들어와 있는지 if else로 확인하면서 그릴 수는 없을 것이다. 이 경우도 자식 뷰의 draw를 차례로 호출해서 그리는 로직을 위임한다. 그래서 화면 가득이 컴포넌트가 넘쳐나는 화면도 if else를 쓰지 않고 그리는 코드를 짤 수 있는 것이다.

사실 이런 것을 설명해야 한다는 사실 자체가 당혹스러웠던 적도 있다. 심지어 코드 리뷰할 때 type code를 쓰는 부분을 제거해야 된다는 이야기를 했더니 그럼 어떻게 해야 되느냐는 질문이 나왔는데 그 질문에 대답할 수 있는 사람이 아무도 없었던 적도 있다. 하지만 사실 생각해보면 대학에서 OOP를 가르치는 것도 아니고, 학원에서 OOP를 가르칠 리도 없고, 실무에서 가르쳐줄 수 있는 실력자가 많은 것도 아니니 당연한 현상인지도 모른다. 아마 다형성만 정확하게 이해하고 써도 전세계 개발자의 10% 안에는 충분히 들지 않을까.

굳이 전세계를 언급한 이유는 안드로이드의 소스코드들도 반 OOP적인 특성으로 가득하기 때문이다. 처음에 다른 개발자들이 짠 안드로이드 앱 코드를 보면서 뭐 이따위로 짜놨나 생각을 했었는데, 안드로이드 소스코드와 예제들에 다 그렇게 되어 있었다. 그 소스를 보고는 정말 이게 구글에서 짠 코드인가 하는 생각마저 했었으니까. 이를테면 이런 것들이다. 앞서 이야기한 type code, 거의 OOP의 적이다. 그런데 안드로이드는 내부 소스 뿐 아니라 API에서도 type code를 개발자가 선언하고 쓰게 만드는 API가 많다. 게다가 거의 최근 10년 간 본 적도 없던 out parameter까지 등장했다. OOP 뿐 아니라 절차적 프로그래밍의 관점에서도 아주 나쁜 관례다. 상태를 많이 만들고 기억하게 만드는 코드이기 때문이다. 객체를 생성하고 사용하는 코드를 봐도 일단 생성한 다음 이것저것 세팅을 하고 나서 실행해야 하는 API가 여럿 있다. 헝거리안 표기법은 앞서의 단점들에 비하면 그래도 참아줄 만한 단점인지도 모른다. 아마도 C 개발자들이 자바 문법만 배우고 개발한 게 아닌가 하는 생각이 든다.

안드로이드 공식 문서의 퍼포먼스 팁도 거의 반 OOP 가이드나 다름 없다. 객체를 만들지 말라고 하고 인터페이스 대신 버추얼을, 버추얼 대신 스태틱을 쓰라고 할 정도니 심각하다. internal getter/setter도 나쁘다고 하니 상속할 때는 멤버를 죄다 protected로 하란 말인가. 또 하나 심각한 것은 field lookup을 멤버로 캐시해두라는 것. 거의 써먹을 만한 팁이 몇 안된다. 이런 식으로 코딩했는데도 안드로이드의 화면 응답성은 아이폰만 못하다면 안드로이드 개발자들의 실력이 부족한 거라고 봐야 하지 않을까.

자바도 OOP의 전통이 상당히 깊은 언어지만 아직 다형성을 정확하게 이해하는 개발자는 별로 많이 만나지 못한 것 같다. 자바 계열은 프레임웍을 상당히 많이 쓰고 프레임웍은 다형성 없이 존재할 수 없는 것인데 왜 이런 현상이 발생할까? 내가 세운 가설 하나는, 프레임웍이 나무만 보고 숲을 보지 못하게 만드는 넛지를 주기 때문이 아닌가 싶다. 위의 메일 클라이언트로 예를 들면, 받은편지함.getMenu만 짜게 만들기 때문에 메일클라이언트.drawMenu를 보지 못하는 것이다. 사실 다형성은 편지함.getMenu를 implement하는 곳에 있는 게 아니고 메일클라이언트.drawMenu에 있다. 이걸 봐야 다형성이 왜 필요한지 아는데, implement만 하다보니 이런 것을 잃어버리는 것이다. DependencyInjection이나 InversionOfControl도 결국 다형성으로 설명하는 것들이다. 그런데 다형성을 모르고 이런 개념들을 쓰는 사례가 간간이 보인다. AOP로 나아가는 것도 좋겠지만 그 전에 OOP부터 마스터하고 넘어가야 한다. 언제 또 이야기할 기회가 있겠지만 AOP는 OOP 없이 제대로 활용할 수 있는 개념이 아니며, 또한 AOP가 필요하다고 생각하는 많은 부분이 OOP로 해결된다.

아뭏든, OOP 개발자로 거듭나기 위해서는 다형성을 이해하는 것이 그 첫걸음이다. if else가 코드 안에서 번식하기 시작한다면 다형성을 생각해보자.


 

블로그 / 소프트웨어 개발

Youngrok Pak , 10 years, 5 months ago

아이폰 개발 vs 안드로이드 개발

 

아이폰 개발은 내가 학습에서 가장 지진했던 분야다. Hello World를 완전히 이해하는데 무려 이틀이 걸렸고, ViewController 및 delegate 구조, nib와 outlet, 각종 UI 컴포넌트 사용법 등의 주요 개념을 파악하는데 2주 가량 걸렸다. 3주 째에 첫번째 아이폰 애플리케이션을 완성. HTML/CSS/JS로 만든다면 하루면 충분한 정도의 애플리케이션이었다. 한 달 가량이 지나서야 내 맘대로 개발할 수 있겠다는 생각이 들었다.

반면, 안드로이드는 Hello World는 할 것도 없었고 View, ViewGroup, Context, resource, Intent, Animation 등의 주요 개념을 학습하는데 1주가 안 걸렸다. 나흘 째에 이미 첫번째 업무로 주어진 애플리케이션의 기능을 대부분 완성하고 UI까지 다 입혔다. 아직 2주도 채 지나지 않았는데 다 파악했다는 느낌이 온다.

물론, 둘다 내 학습 스타일상, 책 펴놓고 공부하거나, 문서 열심히 읽고 시작하거나 하지는 않았고, 일단 개발 환경 실행시켜놓고 달려드는 식이었다. 하지만 아이폰 쪽은 문서를 안 읽고는 이해가 안가는 부분들이 너무 많아서 하면서 내내 문서를 뒤적거리면서 했던 반면, 안드로이드는 대부분 API 문서 찾아보는 것과 구글링으로 충분했다.

이쯤 되면 아이폰 개발을 안드로이드 개발보다 훨씬 후진 걸로 평가해야 정상일 텐데, 별로 그런 생각이 들지 않는다. 아무래도 직접 비교하기는 좀 그렇다. 사실 아이폰 개발은 다른 GUI 애플리케이션 개발과 구조가 완전히 다르다. 지금까지 GUI 애플리케이션 개발 경험을 보면 AWT에서 시작해서 비주얼 베이직, MFC, SWT, HTML/CSS/JavaScript, 플래시까지 많은 플랫폼을 써봤는데, 대부분 비슷비슷한 개념들을 쓰고 있고, 안드로이드도 그런 관례를 충실히 따르고 있다. 하지만 아이폰 UI 클래스들의 구조는 이런 것들과 완전히 다르다. 때문에, 나의 UI 개발 경험들이 안드로이드에는 거의 그대로 적용이 되었지만 아이폰에는 별 도움이 안되었다.

거기에 OS와 IDE에 익숙지 않은 것도 한 몫 한다. 코코아가 다른 GUI 툴킷과 다른 것처럼 맥도 리눅스, 윈도우와는 큰 차이가 있다. 특히 단축키 구조가 다른 것이 가장 고통스러웠다. 현실적으로 맥의 단축키 구조가 그 자체로 주는 장점이 거의 없는 이상, 윈도우의 관례를 따라가는 것이 좋지 않을까. 키보드도 다르게 생겼지만 UX적으로 아무런 장점도 주지 못하고 있다. 맥 애플리케이션의 멀티 윈도우 시스템도 고통스러운 점 중 하나. 파인더도 버그인지 기능을 몰라서 그런지 짜증스러운 점이 좀 있었다. Xcode는 나름의 장점이 있지만 편집 단축키가 한참 부족하고 리팩토링, 소스 스켈레톤 생성 등의 기능이 모자라다. 아뭏든, 이런 점들이 있기 때문에 익숙해지는 과정을 단순 평가하기는 힘들다. 물론, 안드로이드 개발이 기존의 경험을 잘 활용하게 해준다는 것은 그 자체로도 충분히 플러스 점수를 줄 수 있는 것이기는 하다.

그래서, 익숙해진 후의 관점을 비교한다면, 아이폰과 안드로이드는 장단점이 비슷하게 어우러지는 느낌이다. 완성도는 아이폰이 압도적으로 높다. 이미 코코아에서 탄탄하게 다져진 클래스들을 코코아 터치로 가져온 것이기 때문에 API의 완성도가 높고 되는 것 안되는 것이 명확하다. 하지만 안드로이드는 SWT나 스윙의 컴포넌트 상속 구조를 그대로 가져오지 않고 새로 만든 것이기 때문에 아직은 완성도가 낮다. 이벤트에 여러 개의 리스너를 지원하지도 않고 중첩 컴포넌트의 이벤트 전달도 좀 부실하다. View와 ViewGroup 등의 상속 구조, 난립하는 ViewGroup 클래스들 등이 UIView를 중심으로 하는 코코아 터치의 견고한 설계에 비하면 부족한 느낌이다.

인터페이스 디자이너의 완성도 차이는 그야말로 극과 극이다. 코코아 터치의 인터페이스 디자이너는 가히 UI 디자이너 중 최고라고 할 만한데 안드로이드의 레이아웃 에디터는 형편 없다. 스타일조차 에디터에서는 반영이 안되니 실행시켜봐야 한다. 여담이지만, 둘다 코드만으로 UI를 구성하는 것도 가능한데, 이건 정말 어리석은 일이다. 코드도 불필요하게 번잡해질 뿐더러, 직관성이 아주 떨어진다. UI를 구성할 때 눈으로 보면서 구성하는 것과, 실행해보고 고치고 하는 것의 생산성 차이는 엄청나다. 안드로이드에서는 레이아웃 에디터가 불편해서 xml 에디팅을 직접 하기는 하겠지만 그래도 자바 코드로 하는 것보다는 훨씬 낫다. 비유를 하자면, 웹 프로그래밍 하면서 HTML을 직접 쓰는 것과, 자바스크립트의 DOM으로 HTML을 그리는 것의 차이다. 많은 숙련된 자바스크립트 개발자들이 DOM을 쓰느니 차라리 innerHTML을 쓰라고 하는 것도 이게 더 직관적이기 때문이다. 심지어 플래시 개발할 때도 코드로 뷰를 그리는 개발자를 본 적이 있는데, 이래서는 안된다. 이 문제가 더 심각한 것은, 이렇게 코드로 뷰를 그리는 것을 선호하는 개발자들이 대개 뷰와 컨트롤러를 분리하지 않는다는 것이다. 그래서 뷰를 그리는 코드와 로직 코드들이 얽혀 있는 코드를 만들어내곤 한다. 그림은 툴로 그리고, 머리는 로직을 짜는데 쓰는 게 좋다.

다시 돌아와서, 에뮬레이터도 아이폰은 금방 뜨는데 안드로이드는 이미 악명이 높다. 안드로이드 에뮬레이터의 성능에 관한 최고의 조언은, 일단 띄워 놓으면 절대 닫지 말라는 것. 에뮬레이터 뜨고 나서의 실행 시간도 느리다.

하지만 안드로이드의 API들은 익숙하다. 설계는 조금 다르지만 기본 컨셉은 기존의 GUI 툴킷과 같기 때문에 경험을 쉽게 재활용할 수 있다. Objective C와 Java의 차이도 크다. 둘다 문법이 verbose한 편이기는 하나, Java가 그래도 조금 더 간결하다. 무엇보다 Objective C의 verbosity는 Xcode가 썩 잘 커버해주지 못한다. 캐스팅이라든지, protocol implement, property 등을 보면 만드는 과정의 중간 단계들은 자동 완성 지원이 되는데, 전체 과정이 자동화되지는 않는다. 반면 이클립스는 중간 단계들은 물론이고 전체 단계도 스무스하게 잘 완성시켜 준다. 사실 자바는 이클립스든 IDEA든, IDE가 워낙 좋아서 문법의 verbosity로 인한 context 끊김이 적다. 뭔 소리냐면, 예를 들어, 퀵 소트 알고리즘을 직접 구현한다고 할 때, 알고리즘을 생각하다가 리스트 사용법을 생각하느라고 알고리즘 생각이 끊기는 일이 없다는 것이다. 하지만 Objective C는 로직을 생각하다가 빠뜨린 괄호를 붙이러 맨 앞으로 간다든지, 프로퍼티를 바로 연결하고 싶은데 IBOutlet property로 선언하느라 .h와 .m을 왔다갔다 하는 등, 문법의 verbosity로 인한 끊김이 크다. 거기에 가비지 컬렉션이 안되는 건 정말 불편하다.

또 다른 중요한 차이는, 아이폰이 훨씬 이쁘다는 것이다. 계속 쳐다보고 싶은 생각이 든다. 하지만 안드로이드의 에뮬레이터는, 아니 어떻게 그렇게 못 생기게 만들 수가 있는지. 이건 진짜 내가 디자인해도 저거보다 잘하겠다. 기본 위젯들의 생김새도 차이가 크다.

물론, 아이폰이 안드로이드보다 더 견고한 아키텍처와 깔끔한 API를 가질 수 있는데는 아이폰이 더 단순하다는 것도 있다. 다양한 화면 크기와 입력 장치를 생각해야 하는 안드로이드와 달리 아이폰은 그런 고민이 필요 없다. 키 이벤트도 필요 없고 포커스 개념도 first responder 정도로 충분하다. 가변폭도 생각할 필요가 거의 없고, 위젯도 없고 멀티 윈도우도 없고 intent 같은 것도 없다. 물론 단순함이 주는 장점도 크지만 그것이 아이폰을 장난감에 머무르게 하는지도 모른다. 위젯이 안된다는 것은 정말 비즈니스 스마트폰으로는 실격이다.

어느 쪽이 개발이 즐거운지는 좀 애매하다. 일단 아무리 UI 개발이라도 개발의 대부분은 코드와 함께하는데, 코딩하기에는 역시 이클립스가 Xcode를 압도한다. 다양한 편집 기능과 단축키, 리팩토링, 프로젝트 관리, 소스 저장소 등등, 거의 모든 기능에 차이를 보인다. 딱 하나 좋은 것은 타이핑할 때 자동완성이 이클립스보다 좀더 기민하다는 것. 하지만 이것도 IDEA나 RubyMine 앞에서는 무릎을 꿇어야 한다. IDEA나 RubyMine에 90점을 준다면 이클립스는 80점, Xcode는 50점 정도다. 비주얼 스튜디오 2008은 60점 정도? RubyMine을 써보면서 꼭 이놈은 내가 뭘 하고 싶은지를 알고 있는 것 같다는 느낌을 많이 받았는데, Xcode는 그냥 내가 뭘 타이핑하고 싶은지를 아는 정도다. 그러다보니 코딩의 재미는 안드로이드 쪽이 더 크다. 반면, 아이폰은 에뮬레이터가 좋고 UI가 미려하고, UI 그리기가 수월하다보니 만들고 실행해보고 하는 과정은 아이폰이 훨씬 재미있다.

어쨋든 전부 플래시 개발이랑 비교하면 형편 없다. 개발 도구도 이클립스에 FDT를 쓸 수 있기 때문에 좋고, 플래시 툴 자체도 코코아의 인터페이스 디자이너처럼 쉽지는 않지만 편리함에서는 비슷하다. 무엇보다 큰 차이는 아키텍처다. UI 개발에서 가장 중요한 것은 View 상의 컴포넌트를 어떻게 조작하고, 이벤트를 어떻게 연결하는가이다. 코코아 터치에서는 코드로 일단 써놓고 인터페이스 디자이너에서 연결해야 해서 좀 번거로운 편이다. 반대로 인터페이스 디자이너 쪽에서 뭔가 지정하고 자동으로 코드를 생성하게 했다면 훨씬 나았을 텐데. 안드로이드는 리소스 클래스를 이용하는 방식이라 글로벌한 ID를 지정해야 하는데, 이게 캐스팅 문제도 좀 있고, 간혹 뷰를 넘겨야 하는지 ID를 넘겨야 하는지 혼동되는 점도 있는 등, 약간 불편하다. 반면 플래시는 뷰를 디자인하면서 속성 이름만 지정해주면 그대로 코드에서 프로퍼티로 쓸 수 있다.

이벤트 지정 방식도 IBAction을 선언하고 연결해줘야 하는 코코아 터치는 역시 순서가 바뀐 느낌이라 불편하다. 안드로이드는 예의 자바의 익명 클래스를 활용한 이벤트 리스너 등록이라 문법이 번잡하다. 플래시는 클로저가 가능하니 당연히 훨씬 좋다. 하지만 이벤트 전달 방식은 HTML 쪽이 더 좋은 듯.

UI 재활용 측면에서도 재활용하려면 별도의 nib로 분리하고 컨트롤러를 붙이거나, View에서 로드하거나 해야 하는 코코아 터치는 많이 번거롭다. 안드로이드도 layout xml 파일을 분리해야 하므로 불편. 사실 큰 부분을 재활용할 때는 분리하는 게 더 낫지만 테이블의 셀처럼 작은 부분을 컴포넌트화하고 싶을 때도 그 때마다 파일을 분리해야 되면 번거롭다. 반면 플래시는 심볼로 만들어주기만 하면 손쉽게 재활용 가능하다.

애니메이션은 뭐 비교 불허. 비디오, 오디오 등의 미디어를 다루는 것도 월등이 편리하다.

개발 생산성을 비교한다면, 똑같은 애플리케이션을 만들 때, 플래시로 일주일이 걸리는 일이라면, 안드로이드는 1개월, 아이폰은 2개월 가량 걸리지 않을까. 잡스는 고집 그만 부리고 그냥 플래시 탑재하자. 플래시는 이미 아이폰보다 오래 전부터 핸드폰에서 쓰였고, 성능에 아무 문제 없다.


 

블로그 / 소프트웨어 개발

Youngrok Pak , 10 years, 5 months ago

신들이_보고_있다

 

[프로페셔날의 조건]에 나온 이야기. 아테네의 파르테논 신전 위에는 페이디아스라는 조각가의 작품이 서 있는데 지금도 최고의 걸작으로 평가 받고 있다. 그런데 정작 아테네의 재무관은 작품료 지불을 거절했다. "조각들은 신전의 지붕 위에 있고 신전은 아테네의 가장 높은 언덕에 있기 때문에 사람들은 조각의 전면 밖에 볼 수 없다. 그런데 당신은 아무도 볼 수 없는 조각 뒷면의 비용까지 청구했으니 줄 수 없다."가 이유였다. 이에 대해 페이디아스는 이렇게 답했다. "아무도 볼 수 없다고? 당신은 틀렸어. 하늘의 신들이 볼 수 있지."

그리고 이어서 피터 드러커는 사람들로부터 "당신이 쓴 책 가운데 어느 책을 최고로 꼽습니까?"라고 물으면 "바로 다음에 나올 책이지요"라고 대답한다는 말을 남겼다.

프로 바둑 기사들도 비슷한 이야기를 한다. 바둑 해설을 보다보면 [정석에 대한 오해]와는 달리 프로 기사들의 유연한 사고 방식이 참 많이 느껴진다. 특히 포석 단계에서는 이렇게 두어도 한 판의 바둑, 저렇게 두어도 한 판의 바둑이라는 말처럼 유연성이 많이 강조된다. 하지만 그럼에도 불구하고 프로기사들은 포석 단계에서도 신의 한수를 추구한다. 언젠가 조훈현 9단이 그런 말을 한 적이 있다. 가볍게 이야기할 때는 이렇게 두어도, 저렇게 두어도 한 판의 바둑이라는 이야기를 하지만 사실은 분명히 최선의 한 수가 존재한다. 단지 찾지 못하고 있을 뿐이고 프로 기사들은 모두 그 한 수를 찾기 위해 끊임 없이 연구한다. 이런 자세가 아마도 젊은 기사가 득세하는 바둑계에서 환갑을 바라보는 조훈현 9단이 살아 남는 이유이기도 할 것이다.

나에게도 비슷한 욕망이 있다. 당장은 현재의 기술로도 한 판의 프로젝트를 제대로 해낼 수 있다. 하지만 늘 그 너머의 신의 한수를 찾고 싶은 욕망이 있다. 그건 프로그래밍 언어가 되기도 하고 프레임워크가 되기도 하고 OS가 되기도 한다. 이런 욕망들이 기술의 발전을 이끌고 있는 것이리라.


 

블로그

Youngrok Pak , 10 years, 5 months ago

스프링노트_기술보고서

 

스프링노트를 오픈하고 나서 그 동안 사용했던, 그리고 경험했던 기술들을 하나씩 평가해 본다.

Ruby on Rails

ActiveRecord

RoR의 가장 큰 장점은 ORM인 것 같다. 현재 Rails류의 프레임웍이 많지만 완성도 면에서 RoR의 ActiveRecord만큼 높은 건 별로 많지 않다. 하지만 디자인 측면에선 맘에 안드는 부분도 있다. 순수 OOP적인 모델 설계를 지향하고 있는 SQLObject나 Hibernate, GORM에 비해 ActiveRecord는 테이블을 기술하는 방식에서 SQL에 더 가깝다. 코드로 한 번 비교해보자. 다음은 Rails에서 User라는 모델이 name과 password라는 프로퍼티를 가지는 경우이다.

  class AddANewTable < ActiveRecord::Migration
    def self.up
      create_table :users do |table|
        table.column :name,  :string, :null => false
        table.column :password, :string, :limit => 32, :null => false
      end
    end

    def self.down
      drop_table :users
    end
  end

이렇게 migrate를 만든다. 그리고 migrate를 실행하면 name, password라는 컬럼이 있는 users 테이블을 만들고 이 테이블을 ActiveRecordPattern으로 User 객체와 매핑시킨다. User 객체는 그냥 다음처럼 쓰기만 하면 된다.

class User < ActiveRecord::Base
end

user = User.new
puts user.name
user.password = 'xxx'

즉, 메타 정보를 담는 migrate 코드와 User 클래스 코드가 합쳐져야 하나의 모델이 되는 것이다. 게다가 메타 정보를 기술하는 방식은 SQL에 아주 가까운 방식이다. 그러면서 User의 메소드는 또 User class에 기술한다. 그래서 User class를 봐도 무슨 프로퍼티가 있는지 알기가 어렵다. SQLObject랑 비교하면 이 차이는 더 드러난다.

class User(SQLObject):
    name = StringCol(length=100)
    password = PasswordCol(default='')

user = User('a')
print user.name
user.password = 'xxx'

User 클래스 하나에서 모든 정의를 한다. 그래서 조금 더 응집성이 높고 간결하다. GORM도 이런 방향을 지향하고 있다. Django도 DRY 원칙이나 OnceAndOnlyOnce의 입장에서 이런 방식을 채용했다.

하지만 Real World에서는 꼭 SQLObject 쪽이 좋다고 할 수 없다. SQLObject도, Hibernate도 지원이 부실한 부분이 바로 스키마의 migration이다. Rails는 앞서의 migrate 코드를 실행하면 self.up 코드를 실행하고 또 migrate back을 하면 self.down을 실행한다. 그래서 스키마 변경 코드도 migration으로 만들 수 있다. 이를테면 위의 예에서 User에 email 필드를 추가한다고 해보자. Rails는 다음과 같은 migration 코드를 만들고 rake db:migrate 명령을 내린다.

  class AddANewTable < ActiveRecord::Migration
    def self.up
      alter_table :users do |table|
        add_column :email,  :string, :null => false
      end
    end

    def self.down
      alter_table :users do |table|
        remove_column :email
      end
    end
  end

그러면 self.up 부분이 실행되서 스키마를 바꾼다. migrate는 역도 가능하다. 이게 꼭 매끄럽게 되는 건 아니지만 역 migrate도 가끔 필요하다. 그래서 이런 시점에 migrate에 필요한 작업들을 해줄 수 있다. 하지만 SQLObject나 GORM 등에서는 그냥 클래스 정의만 바꾸면 이게 테이블에 자동으로 반영된다. 이 자동이란 게 편하기도 하지만 가끔 데이터를 날려 먹는 수가 있다. 그래서 이런 건 조금 더 SQL에 가깝고 수동으로 할 여지를 많이 열어 놓은 Rails가 유리한 점이 있다.

하지만 어차피 migration 코드에 model 객체를 이용한 코드를 집어 넣으면 migration이 가끔 안되는 수가 있다. 이론적으로는 migration 파일이 1부터 10까지 있으면 1부터 10까지 쭉 실행하면 데이터가 다 자동으로 변환되면서 스키마가 변경되어야 하지만 1단계에는 있는 모델 클래스가 5단계에는 없을 수 있고 또 그 반대도 있기 때문에 모델이 들어간 migration 코드는 종종 class not found 에러를 낸다. 그래서 어차피 migration은 sql을 이용해서 하거나 수동으로 script/console을 열고 하는 경우가 많다. 팀에서 한 사람이 쭉 migrate를 쌓아 놓은 것을 다른 개발자들이 제때 제때 따라가지 못하면 migrate를 할 수 없어서 개발 DB를 다른 사람 껄 덤프 떠와야 하는 일도 간혹 있다. 이를 대비해서 schema:load 같은 명령도 있지만 잘 안된다. 이런 식이라면 SQLObject도 충분히 할 수 있기 때문에 큰 차이는 아닐 수 있다. 어쨋건 Rails의 ActiveRecord가 나름 실용성과 디자인의 중간을 찾으려고 애를 썼고 두 마리 토끼를 절반씩은 잡았다는 점은 Rails의 중요한 장점이다. 사실 익숙해지면 ActiveRecord의 응집성 문제는 그다지 annoying하지 않다.

성능은 조금 문제가 있다. Django 같은 경우는 QuerySet을 iterate할 때 쿼리가 날아가기 때문에 그 앞에서는 안심하고 여러 가지 조합을 해줄 수 있는데 Rails는 좀더 많은 종류의 코드들이 직접 쿼리로 날아간다. 그래서 모델 4, 5개 정도 사용하는 페이지에서 무려 80여개의 쿼리가 날아가기도 한다. 결국 다 만들어 놓고 캐싱하고 최적화 하는데만도 엄청난 시간을 투자했으니 ORM으로 인해 초반에 얻은 장점을 후반 가서 다 까먹는다고도 할 수 있을 것 같다. 하지만 이부분은 꾸준히 개선되고 있는 듯하니 지켜볼 일이다.

RHTML

JSP랑 똑같다. 스크립트렛 문법은 거의 완전 똑같다. JSP의 모든 장점과 단점을 다 그대로 계승하고 있다고 보면 된다. 한 가지 짚고 싶은 것은 RHTML식 태그 라이브러리(?). JSP에서 태그 라이브러리로 하는 것들을 RHTML은 모두 ruby 코드를 그대로 노출시켜서 하고 있다. 이걸 helper 메소드라고 부른다. 태그가 코드보다 낫다는 말을 하려는 것은 결코 아니다. 오히려 이 점은 RHTML의 장점이다. JSP에서 태그 만들려면 해야 되는 삽질이 좀 된다. JSP 2.0에서 좀 나아졌다고는 해도 여전히 Rails에 비하면 불편하다. Django에서 사용하는 템플릿도 태그를 따로 만들려면 일반 파이썬 메소드가 아니라 태그 인터페이스에 맞춰야 하기 때문에 불편한데 RHTML은 그대로 ruby 코드로 모든 것을 할 수 있어서 좋다. 물론 이걸 단점이라고 말하는 사람도 많지만 JSP의 태그 라이브러리가 스크립트렛보다 전혀 낫다고 생각하지 않는 나로선 이건 분명 장점이다. 문자열 길이 제한하는 코드 좀 써넣자고 태그 라이브러리를 만들어야 하는 것보다는 간단히 [..50] 같은 걸로 쓰는 게 훨씬 가독성도 높고 편리하다.

그렇지만 Rails 태그 라이브러리들 자체는 별로 옹호하고 싶지 않다. JSP 태그 라이브러리들의 단점 중 많은 부분을 그대로 계승하고 있기 때문이다. JSP에서 가장 싫은 부분이 form을 좀 편하게 써보자고 다 태그 라이브러로 만드는 것이다. 하지만 그래서 편해지기는 커녕 호환성 떨어지는 코드가 되고 form 태그와 많은 면에서 중복이 된다. 그래서 임의의 속성 달기도 어렵고 HTML만 아는 UI 개발자랑 협업하기도 어렵다. 둘다 도대체 form을 왜 그렇게 괴롭히는지 모르겠다. 여기서 Rails가 좀더 나쁜 부분은 JavaScript까지 헬퍼 메소드로 집어 넣고 있다는 것이다. 이걸 두고 루비 개발자들은 나대지 않는 자바스크립트니 어쩌니 하면서 좋아하지만 이것 역시 UI 개발자와의 협업을 어렵게 만들고 지저분한 HTML 소스 코드를 생성할 뿐더러 자유도까지 왕창 떨어뜨린다. 제발 HTML에서는 루비 코드가 나대지 말았으면 하는 게 내 소망이다. 루비가 DSL을 지향하는 언어라면 HTML 속에서는 HTML스러운 코드를 써야 한다. 근데 HTML 안에서도 루비스러운 코드를 써보자고 온갖 문제를 만들어내는 헬퍼를 남발하고 있다. 그래서 실제로 RHTML 코드들을 보면 몇 년 전 사람들이 JSP 처음 쓸 때 이상으로 지저분하다.

템플릿 재활용에 관한 부분은 좋은 편이다. Tiles나 SiteMesh 같은 자바 쪽 템플릿에 비해 구조화하기가 훨씬 수월하다. 자바는 반성하라! header와 body를 따로 넣을 수도 있고 부분만 분리하거나 상속 비슷한 개념을 활용할 수도 있다. 하지만 파이썬의 cheetah에 비하면 조금 부족한 느낌이다.

webrick & mongrel

Rails가 자바보다 나은 건 rails만 그냥 설치하면 자바처럼 톰캣 깔고 설정하고 할 필요 없이 바로 script/server로 띄워볼 수 있다는 것이다. 하지만 이것도 cherrypy 같은 프레임웍에서 훨씬 일찍 보여준 것이고 cherrypy는 그냥 그대로 띄워도 production scale이 나오는데 webrick은 절대 production에서 쓸 수 없다. 그래서 webrick에서 속도에 민감한 부분을 C로 바꾼 mongrel을 쓴다. 하지만 이것도 성능은 그닥 좋지 않아 보인다. 그리고 루비는 자체 쓰레드 모델이 없기 때문에 실제 서비스에서는 직접 프로세스를 여러 개 띄워야 한다. 그러다보니 자바의 서블릿 컨텍스트 같은 개념도 없고 start/stop도 실제 서비스에서는 간단치 않다. apache랑 연동하지 않으면 서비스할 수 없는 것은 물론이고. 차라리 mod_ruby 쪽으로 최적화시키는 게 성능 면에서나 관리 면에서나 더 낫지 않을까 싶다.

어쨋든 현재 스프링노트에는 초당 10~15 request가 들어오는데 CPU 사용률이 30~70%를 오락가락한다. tomcat에서 웬만큼 무거운 SQL과 로직을 돌려도 초당 40 request에 CPU 20%도 안 먹었었던 걸 생각하면 Rails does not scale이라고 해도 할 말 없을 것 같다. 물론 앞서 언급한 것처럼 ORM의 성능 문제가 더 크긴 하지만 Rails 자체도 우분투에서 제 성능이 안 나오는 등 아직 문제가 많다.

RSpec on Rails

RSpec은 BehaviorDrivenDesign의 생각에 따른 새로운 UnitTestFramework이다. BDD는 TDD와 같은 개념이지만 용어들을 좀더 인간적으로 말이 되게 하는데 초점을 맞췄다. 코드로 비교하면 이렇다. (이건 비교를 위한 샘플이고 실제로 이런 테스트를 만들진 않는다.)

  • TDD
    class UserTest < Test::Unit::TestCase
      def test_create
        user = User.create(:name => 'Karl')
        assertEquals user.name, 'Karl'
      end
    end
  • BDD
    context "user's behavior" do
      specify "should be invalid without a username" do
        user = User.create(:name => 'Karl')
        user.name.should_be_eql 'Karl'
      end
    end

용어들이 조금씩 다르다. 보다시피 rspec이 좀더 자유롭게 spec을 서술할 수 있고 테스트 코드도 뭔가 좀더 말이 되는 것 같아 보인다. 이외에도 user.should_be_valid 같은 메쏘드를 실행하면 user.valid? 메소드를 실행해서 그 결과로 assert를 해 주는 등의 장점이 좀 있다.

그러나, 사실 큰 차이는 아닌 것 같다. 그리고 TDD에 익숙해진 사람들에게 조금 거슬리게 다가오는 부분이 하나 있다. 보통 TDD에서는 assert를 할 때 expected를 먼저 쓰고 result를 뒤에 쓴다. 하지만 BDD에서는 result.should_be expected와 같은 형식이 된다. 호불호의 문제일 수도 있겠지만 나에겐 전자가 좀더 명확해 보인다. 괜히 메이저도 아닌 언어가 두 개의 테스팅 프레임웍을 가진다는 게 그닥 좋아보이지 않는다.

사실 더 큰 문제는 RSpec on Rails에 있다. 이건 RSpec에서 오는 문제보다 Rails에서 오는 문제가 더 크다. RSpec on Rails의 테스트 코드들을 보면 다음과 아주 유사한 코드가 아주 많다.

fixtures :users
controller :sessions

specify ... do
  get :login, id=>@first_user.id, :passwd => 'xxx'

  @first_user.should_be_logged_in
end

얼핏 보기엔 아주 깔끔하고 좋은 코드 같아 보인다. 근데 문제는 저 코드를 실행하면 실제 Rails의 Controller가 내부적으로 떠서 동작한다는 것이다. Java의 Cactus처럼 100% 다 뜨는 것은 아니지만 핵심 코드들은 다 로딩되기 때문에 엄청나게 느리다. 느린 것도 문제거니와 테스트 해야 하는 것은 controller의 로직인데 저 코드는 일종의 FunctionalTest처럼 되서 안에서 모델 사용하는 로직이 있으면 실제로 DB도 다 갔다 온다. 위에서 선언된 fixtures를 통해서 테스트에 필요한 데이터들을 미리 DB로 로드하고 테스트에서는 그 데이터에 실제로 쿼리를 날리는 것이다. 그리고 그 결과로 assert를 한다. 그래서 Test의 granuality가 좀 어중간하다. 아예 FunctionalTest라면 FIT 등으로 쉽게 만들 수도 있는 부분인데 나름 UnitTest 프레임웍이라서 그런 지원이 없다보니 위와 같은 코드가 주욱 늘어서는 중복 아닌 중복이 발생하게 되는 것이다. UnitTest의 입장에서도 테스트하려는 부분만 떼서 테스트할 수 없기 때문에 TDD의 가장 큰 효과, 좋은 디자인을 만들도록 유도하는 효과가 적다. 결과적으로 spec 하나만 놓고 보면 깔끔한 코드지만 spec들이 주욱 늘어서면서 중복이 되고 또 정작 실제 코드의 디자인에는 긍정적인 영향을 주지 못하는 것이다.

테스트 실행이 느린 것도 문제다. Java에서 보통 프레임웍의 Action 테스트 100개 정도 돌리면 거의 10초도 안 걸리는데 Rails의 controller 테스트를 돌리면 듀얼코어에서도 100개에 4,5분은 족히 걸리는 듯하다. 컨테이너 띄우고 DB까지 다 갔다 오니 그럴 수 밖에 없다. 그나마 TextMate에서는 spec 하나 단위로 실행할 수 있고 이상하게도 Mac에서는 상당히 빠르지만 다른 툴에서는 테스트 돌리기가 꺼려질 정도이다.

이 문제의 원죄는 RSpec on Rails가 아니라 Rails 자체에 있다. 일반적으로 Java나 Python 프레임웍에서 controller를 테스트할 땐 그냥 new로 객체 생성하고 메쏘드 실행하면서 mock object만 약간 넣어주면 땡이다. 대부분의 경우 mock object조차도 필요 없다. 하지만 Rails는 controller 코드들이 컨테이너 없이 동작하게 만들려면 상당히 많은 수고를 해야 한다. 그래서 위에서 보는 것처럼 get, post 같은 걸로 쉽게 요청을 때릴 수 있는 툴들을 제공하는 것이다. 하지만 그보다 좀더 가볍게 controller를 mock으로 만들 수 있게 했다면 controller의 디자인 자체도 더 좋아졌을 것이고 테스트하기도 더 쉬웠을 것이다. 테스트하기 좋은 코드가 좋은 코드라는 말이 이럴 때 보면 정말 맞는 것 같다.

이 문제는 controller만의 문제가 아니다. Rails의 모든 컴포넌트들이 다 독립적으로 테스트하기가 어렵다. 컨테이너가 뜨지 않으면 제대로 테스트할 수 있는 건 lib 정도일까. 머, 어쨋든 해결 불가능한 문제는 아니다. mock object를 미리 좀 만들어두고 재사용하면 그나마 편하게 테스트할 수 있다. 사실 그보다 더 큰 문제는 Rails 커뮤니티에서 위와 같은 방식을 권장하고 있을 뿐더러 이런 문제를 개선하려면 Rails 자체에 많은 변화가 가해져야 한다는 것이다. Convention over Configuration의 약점은 이럴 때 드러난다. Rails 전반에 걸쳐 너무 많은 Convention을 강요하고 있기 때문에 작은 변화 하나 일으키기도 쉽지 않다.

루비라는 언어

난 루비가 The Principle of least surprise에 의거해서 디자인되었다는 것에 절대 공감할 수 없다. 오히려 내가 아는 언어 중 가장 surprise가 많은 언어가 루비다. Functional Language에 대한 개념이 전혀 없던 상태에서 Haskell을 써봤을 때도 이렇지는 않았다. 루비스트들은 이렇게 하면 될 것 같다라는 추측으로 코드를 쓰면 되는 경우가 많아서 좋다고 이야기하는데 난 오히려 그게 불만이다. 이렇게 하면 될 것 같다라고 생각해서 코딩했는데 안 되는 경우도 적지 않기 때문이다. 또, 이렇게 하면 안될 것 같은데...라는 느낌으로 썼는데 되는 경우도 있고 코드를 보면서 이런 것도 된단 말야하고 놀랄 때도 있다. 다른 언어는 그렇지 않다. 되는 코드와 안 되는 코드는 한 번에 알 수 있다. 이런 모호함이 난 좀 싫었다.

또 순수 OOP 언어라고 주장하지만 실제로 그렇지 않은 것도 문제다. 실제로 루비에서 가장 중요하다는 루비 블럭은 객체가 아니다. 메쏘드도 객체가 아니고 함수도 객체가 아니다. 때문에 python이나 javascript류의 언어에 비해 mock을 만드는 것이 상당히 귀찮은 편이다. 사실 순수 OOP라는 것 자체가 별로 가치 있다고는 생각하지 않지만 루비는 여러가지 개념들로 OOP를 어지럽히고 있으면서도 순수 OOP라고 주장하는 것이 좀 거슬린다.

그럼에도 불구하고 루비는 매력이 있다. 내가 느끼는 가장 큰 매력은 괄호를 적게 쓸 수 있다는 것이다. 그래서 마치 원래 그런 문법이 있는 것처럼 DSL을 상당히 높은 자유도로 만들 수 있다. 루비 블럭도 그런 DSL을 만드는데 많은 도움을 준다. 블럭이 단점도 많지만 그래도 간결한 코드를 만드는데 도움이 되는 것은 사실이다. 잘 짠 파이썬 코드는 눈에 구조가 확 들어오면서 이해가 빠른 장점이 있다면 잘 짠 루비 코드는 술술 읽으면 말이 되고 보기에 미려하다는 장점이 있다.

하지만 나에게 또 루비로 프로젝트를 하고 싶냐고 묻는다면 대답은 No다. 난 아름다운 코드보다는 읽기 쉬운 코드가 좋다. 루비스트들은 그 아름다운 코드란 것에 많이 끌려서 루비를 선택하지만 실질적으로 많은 가치를 주는 것은 아닌 것 같다. 오히려 루비를 하면서 smalltalk를 깊이 배워보고 싶다는 생각이 든달까.

Third party

루비의 또 하나의 약점은 Third Party Library의 완성도가 낮다는 것이다. 웬만한 게 다 있긴 하지만 완성도가 떨어지는 게 많다. 그러다보니 다른 언어 라이브러리에 바인딩해서 쓰는 경우가 많고 또, 그러다보니 개발 과정에 설치해야 할 것이 많아서 여러 개발자가 같이 개발할 때 어려움이 좀 있다. 특히, 중요한 라이브러리들이 *Nix 플랫폼 기준으로 개발되어 있어서 윈도우에서 개발할 때는 에로사항이 꽃핀다. jar만 떨구면 되는 Java와 비교할 수는 없다 해도 같은 계열인 파이썬이나 펄에 비해 플랫폼 독립성이 많이 떨어진다.

Eclipse vs TextMate

루비 개발자들이 가장 선호하는 개발툴은 Mac 위에서 돌아가는 TextMate다. 근데 이넘을 뜯어보면 그닥 유쾌한 툴이 아니다. Eclipse 같은 경우는 컨텍스트 메뉴가 강력해서 단축키를 몰라도 오른쪽 버튼 눌러서 기능 쓰면서 단축키 하나하나 익힐 수 있다. 하지만 TextMate는 단축키 모르면 전체 메뉴를 헤매면서 기능을 찾아서 써야 한다. 단축키 매핑도 Mac이라 그런지 일반적인 에디터의 단축키와 너무 다르다. SCM 지원도 빈약하고 프로젝트 관리도 부실하다. 좋은 점은 Rails에 특화된 기능이 많다는 것. RSpec이나 Rails랑 바로 붙는 기능들이 많아서 Rails 개발할 때는 은근히 편리한 점이 많다. 하지만 좀처럼 익숙해지기 어려운 툴이라는 점에서 vi와 같은 한계를 갖고 있다.

이에 비해 Eclipse는 안정적인 플랫폼에 꽤 괜찮은 루비 지원을 해주지만 몇 가지 아쉬운 점이 있다. 하나는 spec 하나 단위로 RSpec을 돌릴 수 없다는 것. 그리고 pydev에 비하면 코드 어시스트가 없어서 모든 것을 외워서 써야 한다는 것. 어쨋든 난 Mac도 없고 Eclipse 밖에 대안이 없어서 쓰고 있는데 TextMate보다는 좀더 편한 것 같다.

bash, ant, perl vs rake

개발에서 또 하나 중요한 것이 빌드, 배치 등의 사이클을 관리하는 스크립트다. 루비에선 이걸 rake로 하고 있는데 나름 ant와 make의 장점을 흡수하려 애쓴 것 같긴 하지만 결과적으로는 그냥 another ant 정도인 것 같다. XML이라는 ant의 불편함을 제거하고 보다 가독성 높고 확장성 높은 루비 언어로 할 수 있다는 것은 좋으나 여전히 루비 코드들에서 공통적으로 나타나는 프레임웍성의 구조가 마음에 안든다. 차라리 groovy의 gant가 낫지 않나 싶다. 그리고 실상 의존성 체크가 별로 중요하지 않은 dynamic language에서는 그냥 bash 스크립트가 더 나은 것 같기도 하다. perl을 배워두면 bash 기반으로 쓰다가 약간 확장해야 할 때 perl을 쓸 수 있다. 이게 자동화 관련에서는 가장 편리한 조합이 아닐까 싶다. python도 몇 차례 스크립팅에 활용해봤는데 perl에 비하면 이런 류의 작업에는 좀 덜 기민한 것 같다.

Naming Convention

주로 자바 프로젝트들을 해왔기 때문에 CamelCase에 익숙한데 이번에 루비를 하면서 underscore를 구분자로 사용하는 걸 많이 썼다. CamelCase에 비해서 타이핑 부담이 클거라고 생각했는데 의외로 별 차이 없었다. 가독성 역시 비슷하다. 장단점이 약간 있는데 CamelCase에 비해 한 identifier 내의 단어 구분은 좀더 명확한 반면, 기호를 많이 쓰고 괄호가 적은 ruby 코드에서는 다른 identifier들과 혼동이 되는 경우가 종종 있었다. 이것 때문에 ruby 코드가 보기에 따라서 자연 언어에 더 가깝다고 하기도 하고 또 좀더 지저분해보인다고 하기도 하는 것 같다.

JavaScript

내가 JavaScript를 제대로 써본 건 이번 프로젝트가 거의 처음이다. 2004년 쯤 웹 표준에 관심을 가지면서 HTML, CSS는 상당히 많이 공부를 했었는데 JavaScript는 그냥 생각보다 괜찮은 언어 정도로 인식하고 있었다. 근데 이번에 정말 JavaScript를 제대로 써보면서 정말 많이 감탄했다. 핵심 문법을 단 몇 줄로 설명할 수 있을 정도로 간결함에도 표현력이 아주 높다. 자세한 건 이미 JavaScript 페이지에 정리를 많이 해놨으니 여기선 패스.

XHTML, microformat, WYSIWYM

웹 표준, 시맨틱 웹과 관련해서 XHTML, microformat이 많이 뜨고 있다. WYSIWYM(What You See Is What You Mean)이란 용어도 생겼다. 스프링노트도 이 문제로 많은 노력을 기울였다. 하지만 난 좀 회의적이다. 사실 XHTML microformat을 완벽하게 하더라도 WYSIWYM과도 무관한 얘기다. 그저 또 하나의 DocBook이 될 뿐. 이 문제에 관련해서 많은 사람들이 의미론과 형식론을 혼동하고 있다. 제목을 h1 태그에 넣고 문단은 p로 구분하고 리스트는 li 안에 넣으면 형식적으로 잘 꾸며진 문서는 될 수 있지만 의미론적으로는 아무 도움을 주지 못한다. 그런데 시맨틱 웹이니 WYSIWYM이니 하는 용어와 얽어서 마치 사용자에게 어떤 시맨틱한 장점을 줄 수 있는 것처럼 오도하고 있다. 실상은 표현력은 제약하면서 실질적인 이득은 거의 없다. 웹 브라우저의 기본 에디터가 microformat이 아닌데 굳이 microformat을 맞추려면 적지 않은 희생을 치러야 하기도 하고.

좀더 pragmatic하게 접근해야 하는 문제다. XHTML이 기술적으로 제공하는 장점은 어느 정도 있다. validation을 통해서 심각한 오류는 사전에 잡아낼 수 있는 경우가 많다는 것, XML과 섞어서 사용하기 쉽고 XSLT 등의 XML 관련 기술을 쉽게 활용할 수 있다는 것. 하지만 microformat이 줄 수 있는 장점은 그다지 많지 않다. microformat을 기반으로 뭔가 해주는 도구들이 많다면 어느 정도 자동화의 장점을 누릴 수 있기는 하다. 이를테면 검색 엔진에서 h1~5나 strong 태그 등에 중요도를 높인다든지, HTML 문서를 PT로 자동 변환 한다든지. 하지만 아직 microformat을 기준으로 동작하는 툴은 많지 않은 반면 microformat을 제대로 지키기 위한 비용은 HTML 에디터에서 쉽게 감당할 만한 것이 아니다. 호환성은 오히려 HTML 4.01 transitional보다 훨씬 떨어진다. 스프링노트로 외부 HTML 문서를 붙여 넣거나 반대의 동작을 할 때 뷰가 많이 깨지는 것도 microformat에 원죄가 있다. 사용자를 위한 HTML 에디터를 만들고 싶은 거였다면 microformat은 하지 말았어야 했다.

REST

REST는 HTTP method인 GET/POST/PUT/DELETE와 resource에 대한 URI의 결합으로 애플리케이션의 모든 동작을 정의하는 URL 스킴이다. 스프링노트에서 페이지를 예로 들면 ID가 150인 페이지를 읽을 땐 GET으로 /pages/150에, 내용을 업데이트할 때는 PUT으로 /pages/150에 요청한다. 예전 같으면 /pages/150?action=read 와 같은 식이거나 혹은 /pages/update?id=150 처럼 표현했을 것을 같은 URI에 HTTP 메소드로 CRUD를 구분하는 것이다. 이것도 요즘 유행인 듯 하다.

먼저 REST에 대한 비판 하나를 짚고 넘어가면, 애플리케이션의 모든 동작이 REST의 네 가지 동작으로 쉽게 정의되지 않는다는 비판이 있다. 우리가 개발하는 애플리케이션은 너무 복잡해서 CRUD로 단순하게 나눌 수 없어! 라는 이야기. 하지만 이것은 좀더 잘게 모델링을 해야 하는 문제일 뿐이다. 이를테면, 스프링노트에 페이지 잠금을 요청하는 URL이 있는데 이걸 단순하게 /pages/150?lock 과 같이 요청하는 것이 일반적인데 이런 건 lock이라는 resource를 새로 정의하면 쉽게 해결된다. 잠금 요청은 새 잠금을 만드는 것이니 /pages/150/lock을 POST로 요청하면 된다. 로그아웃은 /user?logout이 아니라 DELETE /session이 되는 것이다.

이런 식으로 REST를 사용하면 URL의 의미가 좀더 명확해지고 애플리케이션에서 모델만 잘 정의하면 컨트롤러는 거의 반자동으로 작성할 수 있게 된다. 외부에 좀더 hackable한 URL을 제공할 수 있기도 하고.

하지만, 이런 REST의 장점을 가로 막는 중요한 제약사항이 하나 있다. 웹 브라우저를 포함한 대부분의 HTTP 클라이언트들이 디폴트로 GET/POST만 지원하고 PUT/DELETE는 지원하지 않는다는 것이다. 그래서 실전에서는 PUT /pages/150은 /pages/150?_method=PUT 과 같은 방식으로 사용한다. 이 무슨 삽질인가! 이래서는 /pages/150?action=update와 별반 다를 바가 없다. 그리고 좀더 hackable한 URL이긴 하지만 실제로 더 hackable하진 않다. 대부분의 HTTP 라이브러리들이 PUT, DELETE를 기본으로 지원하지 않기 때문이다.

또 하나의 문제는 MVC에서 설계한 model을 좀더 작은 resource로 나눠야 한다는 것이다. 앞서의 비판이 가능/불가능의 이야기라면 부적절한 비판이지만 비용의 문제가 된다면 쉽게 무시할 수 없는 비판이 된다. model을 작게 나누는 것이 반드시 좋은 디자인이 되는 건 아니기 때문이다. 그래서 model은 그냥 내버려두고 controller에서만 따로 처리하는 경우가 많은데 이로서 REST는 기술적인 장점을 거의 상실하게 된다.

실질적으로 스프링노트에서 REST는 득보다 실이 더 많았다. REST 라이브러리가 어떻게 된 건지 자동화가 안되서 매 resource를 다 routes에 정의해줘야 했다. Rails의 중요한 장점 중 하나인 ConventionOverConfiguration을 잃은 것이다. 그러면서 간혹 REST가 아닌 URL들도 섞여 들었다. 그러다보니 점점 URL과 controller가 어떻게 매핑될지 예측하기가 힘들어졌고 그래서 View 단에서 작업하는 것도 더 힘들었다. 좀더 자동화된 개발 도구가 나오기 전까지는 REST 도입은 자제하는 것이 좋을 것 같다.

CSS

CSS는 예전에도 꽤 많이 했었기 때문에 그렇게 새롭게 익혀야 하는 것이 많진 않았다. 근데 하면서 UI 개발자들의 코딩 컨벤션에 몇 가지 불만이 있었다. 예를 들면 다음과 같은 코드가 있다.

div#main table tr td.data {
    background-color: yellow;
    ...
    ...
}

csskorea에서 권장하는 방식인지 어떤지 모르겠는데 내가 접한 UI 개발자들은 대개 다 이런 식으로 css selector를 사용한다. 즉, select해야 하는 대상 엘리먼트에 이르는 경로를 쭈욱 기술해주는 것이다. 이렇게 하면 CSS 코드만 봐도 HTML 구조가 보이기 때문에 찾아가기가 쉽다고 한다. 하지만 사실 난 그 점을 공감할 수 없었다. 어차피 css selector 문법으로 찾아갈 수 있다면 사람도 쉽게 찾아갈 수 있다. 나는 selector에 최소주의를 선호한다. 해당 엘리먼트를 선택할 수 있는 최소한의 경로만 써주는 것이다. 위와 같은 경우라면 .data만 써도 웬만하면 다 될 것이고 정 불안하면 #main td.data 정도를 쓰면 될 것이다. 중간 경로는 사실상 무의미하다.

경로를 다 써주는 방식의 문제는 크게 두 가지다. 하나는 중복 문제다. 스프링노트의 경우 dialog가 20여개 되는데 dialog 전체에 공통적인 css가 대부분인데도 모든 dialog의 CSS가 다 따로따로 있다. 그래서 스타일 바꾸려면 이걸 다 찾아서 바꿔야 하고 selector에서 실수하기도 쉽다. 파일 크기가 커지는 것도 문제다. 로딩 타임과 랜더링 타임이 느려지는 것도 문제거니와 관리해야 하는 파일 사이즈가 커지니까 유지보수하기도 어렵다. 그냥 클래스만 공통으로 주고 공통 속성들을 dialog 클래스에만 주면 이런 중복은 다 제거할 수 있다. 또 하나는 HTML 구조에 의존하기 때문에 구조를 쉽게 바꿀 수 없다는 것이다. 개발하다보면 HTML의 구조를 뜯어고칠 일이 적지 않게 있는데 그 때마다 CSS의 selector를 다 바꿔줘야 하기 때문에 구조 변경의 부담이 크다.

내 생각엔 selector에는 특수한 경우에만 태그를 명시해주고 그 외에는 최대한 id나 class만을 사용하려고 노력하는 것이 좋을 것 같다.

linux (RHEL vs ubuntu)

RHEL은 NHN에서부터 줄곧 써왔지만 사실 잘 모른다. 늘 소스 컴파일로 설치를 해서 썼기 때문에 패키지는 거의 사용하지 않았다. 반면 우분투를 쓸 때는 패키지가 있는 건 특별히 패키지 구성이 심하게 맘에 들지 않는 이상 패키지를 우선으로 썼다. 그래서, 사실 제목은 RHEL vs ubuntu지만 내용은 웹 서버에 애플리케이션을 설치할 때 소스 컴파일을 하는 것이 좋은가, 패키지로 설치하는 것이 좋은가에 대한 이야기다.

과거 자바를 주로 개발할 때는 아파치와 DoS 방지 모듈, jk 커넥터 정도만 컴파일해서 썼고 Java와 tomcat 등은 그냥 갖다 풀기만 하면 되서 별로 어려운 작업이 아니었다. 라이브러리는 죄다 jar로 떠 놓으면 되기 때문에 모두 CVS로 관리가 되고 변경될 때마다 소스와 같이 배포가 되기 때문에 라이브러리 관리 문제는 거의 없었다. 하지만 Rails와 PHP를 쓰게 되니 이야기가 많이 달랐다. 아파치에 설치해야 하는 모듈만 6,7개 정도에 memcache, mysql client 등 컴파일해야 하는 게 한두 개가 아니었다. 그래서 모든 설치를 다 마치는데 서버 한 대당 거의 1시간이 넘게 걸렸다. 시간도 시간이지만 이것들을 모두 설치하고 세팅하는 과정이 너무나 짜증스러웠다. 우분투를 썼다면 거의 5분 안에 완료할 수 있는 작업인데 컴파일하고 세팅하고 하느라 삽질하다보니 정말 시간이 아까웠다. RHEL을 선택한 이유는 Rails가 우분투에서 제 성능이 안 나와서인데 RHEL 라이센스 구매가 늦어져서 레드햇의 패키지 관리 시스템은 못 쓰고 소스 컴파일을 하게 된 상황인데 차라리 우분투에서 제 성능 나오게 하는 삽질을 하는 게 더 적은 비용이 드는 일이 아니었을까 싶다. 결과적으로는 이 삽질도 내가 Rails를 싫어하게 된 계기 중 하나가 되었다.

우분투에서 제공하는 아파치 패키지 구조가 다른 배포판과는 약간 다른데 알고보면 상당히 편리하다. 대부분의 모듈이 패키지로 있기 때문에 설치하기도 쉽고 설정 파일도 직관적으로 나눠져 있다. 특별한 이유가 없다면 우분투에서는 아파치 패키지를 그냥 쓰는 것이 제일 좋은 선택일 것이다. Rails도 그냥 패키지로 설치해도 쓰기에 별 무리가 없다. 어차피 EdgeRails를 쓰는 경우는 Rails 패키지가 아무 버전이나 깔려만 있으면 되기 때문에 상관 없기도 하고. mysql도 그냥 패키지로 설치해도 별 문제 없다.

실제로 개발 데스크탑으로 우분투를 쓰고 있다면 서버도 그냥 우분투를 쓰는 것이 더 나은 선택일 것이다. 로컬에서 실험해본 것을 거의 그대로 활용할 수 있으니까. 그래서 이번에 Django로 개발하는 프로젝트는 로컬에서 설정한 그대로 서버에 적용해서 비교적 쉽게 설정할 수 있었다. 우분투의 전략 중 하나가 데스크탑을 널리 퍼트려서 개발자들이 데스크탑에서 우분투를 쓰게 되면 자연히 서버도 우분투를 쓰려고 할 것이라는 것인데 내가 넘어간 걸 보면 꽤 괜찮은 전략인 것 같기도-_-

Subversion

아직 CVS를 쓰고 있다면 Subversion으로 옮겨 타지 말라. Subversion이 CVS보다 나은 점은 없다. 같은 개발진이 만들었다면서, 더 낫게 만들려고 했다면서 왜 이 모양인지. 가장 심각한 문제는 툴의 완성도가 낮다는 것이다. 이클립스에서 편리하게 쓸 수 있는 CVS에 비해 Subversion을 이클립스에서 쓰려면 대단한 인내심이 필요할 것이다. 버그가 너무 많다. 커밋이 잘 안되는 경우는 부지 기수고 merge도 잘 안되고 삭제나 이동도 일반적인 기대와 다르게 동작한다. subclipse는 정말 후졌고 subversive는 그나마 조금 쓸만하지만 여전히 CVS 플러그인에 비하면 너무 부족하다. 더 심각한 문제는 커맨드 라인에서 써도 버그가 발생한다는 것이다. 많은 파일과 디렉토리를 한 번에 커밋하면 락이 걸려서 안되기도 하고 엄청나게 느린 속도로 되는 경우도 있다.

버그가 좀 있어도 기능이 더 좋다면 이해해 줄 수도 있다. 그러나 실제로 기능을 체감하는 것은 도구를 통해서인데 실질적으로 이클립스의 CVS 플러그인보다 나은 클라이언트를 갖지 못한 Subversion은 기능적으로 더 떨어진다고도 할 수 있다. 그리고 Subversion이 내세우는 장점인 changeset 단위의 revision number도 장점보다는 단점인 것 같다. 실제로 commit은 파일 단위로 일어나는 경우가 훨씬 더 많다. 한 번 생각해보라. commit하는 단위가 하나의 의미적 작업 단위와 일치하는지를. 두 개 이상의 작업 단위를 한 번에 commit하는 경우도 있고 파일에 따라 다른 commit log를 남겨야 해서 한 작업 단위를 여러 번에 나눠서 commit하는 경우도 많을 것이다. 이런 상황에서 changeset 단위 rollback이 큰 의미가 있는 것은 아니다. 어차피 CVS도 시간과 태그로 changeset 단위와 비슷한 개념을 활용할 수 있기 때문에 파일별 revision이 더 나은 것 같다. changeset 때문에 한 저장소에 여러 프로젝트를 관리하기 어렵다는 것도 문제다. revision이 공유되기 때문에 다른 프로젝트의 변경 내용 때문에 내 프로젝트의 revision이 올라가는 현상이 생기는 것이다. 이것 때문에 내 프로젝트의 이전 버전을 찾는데 고생할 때도 있고 export하거나 할 때도 revision이 너무 많아서 오래 걸리는 문제가 있다. 태깅도 좀 문제가 있다. subversion 태깅은 단순 copy라서 더 쉽다고 하는데 그래서 오히려 더 오래 걸리고 무거운 동작이 되버려서 잘 활용 안하는 경향이 있다. 물론 changeset 개념이 있기 때문에 반대로 태깅이 별로 필요 없기도 하지만.

CVS는 오랫동안 많은 사람들이 써왔고 몇 가지 부족함이 있긴 하지만 그 부족함은 대부분 툴로 커버할 수 있다. 그리고 거의 모든 배포판에 기본으로 설치된다. 별다른 이유가 없다면 그냥 CVS 쓰기를 권한다. ClearCase 등 상용 SCM도 좋은 기능을 제공하긴 하지만 실제 사용할 때 CVS보다 월등히 나은 점을 발견하기는 쉽지 않을 것이다. 단순 기능 목록에 현혹되지 말고 자신이 실제로 사용하는 기능에 초점을 맞춰서 도구를 선택해야 한다.

총평

무수히 많은 신기술을 도입한 프로젝트였지만 불행히도 내 맘에 드는 건 별로 없었다. JavaScript에서 도입한 prototype 정도? 무비판적으로 유행을 쫓은 대가가 아닐까 싶다. 검증되지 않은 것을 도입하지 않는 게 좋다는 말을 하려는 건 아니다. 검증되지 않았으면 스스로 검증해보고 도입해야 한다는 것이다. 오픈마루가 전반적으로 누가 미는 기술이 있으면 별 말 않고 따라가는 경향이 있는데 프로젝트가 혼자 하는 게 아닌 이상 중요한 기술적 결정을 내릴 때는 기술 검토를 충분히 거쳤어야 했다. 그리고 기술적인 이슈에 사용자 가치를 어정쩡하게 개입시키는 것도 경계해야 한다. XHTML이나 REST 같은 것도 사용자 가치를 많이 이야기하지만 실제로 사용자에게 주는 가치는 거의 없다. 그럼에도 불구하고 표준의 논리가 개입되면서 기술적인 단점들을 묻고 지나가곤 한다. 좀더 실용적인 기술 평가가 필요하다.


블로그 / 소프트웨어 개발

Youngrok Pak , 10 years, 5 months ago

성폭력을_어떻게_정의할_것인가

 

나우누리 SCCR에서 있었던 논쟁으로 인해 교지 관악에서 제의를 받아 기고한 글. 당시 논쟁 상대자의 글도 함께 실렸는데, 논쟁 당시에는 이긴 논쟁이었지만 교지에 올릴 때는 상대자가 훨씬 보강된 논리를 준비해왔었던 기억이. 어쨋든 현재 널리 적용되고 있는 성폭력 관련 법률이나 학칙은 이 당시의 내 논리에 더 가까와져 있다.


최근 몇 년간 학내 여성운동이 결실을 맺어가고 있습니다. 성폭력 학칙 제정에 대한 논의로 성폭력에 대한 인식이 관악 전체에 확산되었고 구체적인 학칙안이 나오고 그게 공식적으로 학칙으로 인정되는 단계에 와 있죠? 그리고 이런 움직임이 다른 대학까지 파급되기에 이르렀습니다. 이런 시점에서 현재 여성 운동에서 '성폭력'이란 것을 어떻게 정의하고 있는지 조금 짚어볼 필요가 있는 것 같습니다. 전 현재 '성폭력 학칙안'이라고 나와있는 것을 비롯해 여성 운동권에서 주장하고 있는 성폭력의 개념에 문제가 있다고 봅니다. 그래서 저 나름대로 성폭력을 새롭게 정의해보고자 합니다. 시작하기에 앞서, 전 이제까지 어떤 종류의 여성 운동에도 참여해본 적이 없기 때문에 여성 운동을 잘못 이해하고 있을 수도 있고 글 중간 중간에 저의 무식함이 많이 드러날 수도 있습니다. 저 자신도 남자인만큼 남성중심적 사고에 빠져 있을 수도 있습니다. 이런 점들 양해해주시기 바랍니다. 그리고, 제 마음대로 몇 가지 용어를 재정의해서 사용할 것에 대해서도 양해를 구합니다.

먼저, 전 제가 생각하는 성폭력을 말하기 전에 자유의 개념을 재정의하고 들어가고자 합니다. 웬 뚱딴지 같이 갑자기 자유냐 하실지도 모르겠지만 이것은 제가 성폭력을 이해하는 사고의 구조에 있어서 대단히 중요한 부분이고, 앞으로 성폭력을 이야기할 때 이러한 자유의 정의를 전제하게 될 겁니다. 먼저 사람의 행동이라는 전체집합을 자유집합과 자유의 여집합, 두 부분으로 나누어봅시다. 자유집합은 말 그대로 여기까지는 '자유'에 해당하는 것이라고 볼 수 있는 것들의 집합이고 자유의 여집합은 '자유'라고 볼 수 없는 행동들의 집합이죠. 따라서, 어떤 행동이 성폭력인지 아닌지를 판단할 때 먼저 그것이 자유집합에 속하는지 자유의 여집합에 속하는지를 보자는 겁니다. 그래서 그것이 자유집합에 속한다면 성폭력이 아닌 것이고 자유의 여집합에 속하면서 성적인 행동이라면 성폭력일 가능성이 높다고 할 수 있겠죠.

그러면 자유는 어떻게 정의할 것이냐. 교과서적 정의에 따르면 자유는 '다른 사람에게 피해를 주지 않는 범위 내에서 자기 마음대로 하는 것'입니다. 저는 초등학교 때부터 그렇게 배워왔습니다. 이 이외의 다른 방식의 정의를 교과서에서 본 적은 없습니다. 그래서, 아마 이 개념을 그대로 받아들이고 있는 사람이 많을 것입니다. '누구 한 사람이라도 불쾌감을 갖는다면 그것은 성폭력이다.'라고 말하는 것도 이러한 사고 방식이 바탕에 깔려 있는 것이라고 할 수 있겠죠.

그러나, 전 이러한 자유의 정의에 이의를 제기하고 싶습니다. 남에게 피해를 준다면 그것은 무조건 자유가 아닌 게 되는 걸까요? 그렇지 않습니다. 남에게 피해를 주게 되더라도 그것은 자유일 수 있습니다. 예를 들어보겠습니다. 서울대의 시설노조 파업. 그것은 많은 서울대생들을 대단히 불편하게 만드는 피해를 주었습니다. 한 명도 아니고 많은 사람에게 상당한 피해를 주었죠. 그러나, 그렇다고해서 시설노조의 행동은 '남에게 피해를 주었으니 이것은 자유가 아니다.'라고 할 수 있을까요? 시설노조의 행동은 자유집합에 속할까요? 자유 여집합에 속할까요? 어떤 벤처기업이 크게 성공하여 원래 그 시장을 점유하고 있던 대기업이 매출에서 큰 손해를 입었다고 합시다. 그러면, 그 벤처기업은 대기업에 손해를 끼쳤으니 잘못한 걸까요? 성폭력과 좀 유사한 예를 봅시다. '요즘 젊은이들'의 옷차림에 불만이 많은 한 할아버지가 길거리에서 머리를 울긋불긋 염색하고 찢어진 청바지에 쫄티를 입은 청소년을 만나서 심한 불쾌감을 느꼈다고 합시다. 그러면 그 청소년은 그 할아버지에게 피해를 입힐지도 모르니까 그러한 차림으로 거리를 나돌아다녀서는 안되는 걸까요?

저런 것들이 자유가 아니라고 말할 수는 없겠죠. 그래서, 저는 자유를 재정의하고 싶습니다. 제가 정의하는 자유는 '이성적인 사회적 합의를 깨뜨리지 않는 범위 내에서 자기 마음대로 하는 것'입니다. 사회적 합의라는 것은 그 합의의 대상자 모두가 직접적이든 암묵적이든 동의를 한 것을 말하는 것이죠. 많은 사람들이 같은 잘못을 저지르고 있는 경우도 많기에 '이성적인'이라는 단서를 붙인 거고, 핵심은 '사회적 합의를 깨뜨리지 않는 것'이죠. 여기서 말하는 사회란 기본적으로 사회 전체를 가리키는 말이기도 하지만, 경우에 따라서 당사자간 관계에서의 합의를 말하는 것일수도 있고 그 둘을 모두 고려한 것일 수도 있습니다. 다만 관습적인 합의를 뜻하는 것은 아닙니다. 관습적인 사회적 합의라도 그것의 합리성이 보장된 경우에만 자유의 판단 근거로 삼을 수 있겠죠.

상당히 애매모하한 듯 하지만 자유라는 것 자체가 명확하게 선을 긋기 힘든, 이런 상호간의 관계를 중심으로 따져야하는 것이 아닌가 생각합니다. 그것이 제가 정의하는 자유입니다. 따라서 '사회적 합의가 이루어져 있고 그 합의의 합리성이 보장된다면 그 행동으로 인해 다른 사람이 피해를 입더라도 그것은 자유다.'라는 것이 제 주장입니다. 앞으로 성폭력에 대한 논의는 모두 이 자유에 대한 정의를 바탕으로 전개될 것입니다.

성폭력에 대한 제 정의를 보기 전에 먼저 여성 운동에서 주장하는 성폭력의 정의로서 '성폭력 학칙안'을 한 번 봅시다. 성폭력 학칙안에 따르면 성폭력은 법률로 정의되는 것을 말하는 거고 대신 성희롱에 대한 항목 중에 '상대방이 동의하지 않는 성적행동과 요구 등 언어적, 정신적, 물리적인 행위를 통하여 개인의 성적 자율권을 침해하는 행위'라고 되어 있습니다. 학칙안에서 지칭하는 법률이 어떤 내용인지는 잘 모르겠고 처벌 규정을 보면 성희롱과 성폭력을 특별히 분리해놓지 않고 대부분 성희롱이란 표현을 쓰고 있는 것으로 보아 통상적으로 성폭력이라고 말하는 것이 학칙안에서의 성폭력과 성희롱을 포괄하는 개념이라고 봐도 될 듯 합니다. 성적 자율권이 침해된다는 것은 자신이 하고 싶지 않는 성적 행동을 당하거나 하도록 강요받는다는 것이죠. 그러므로 저 정의대로라면 '피해자가 불쾌감을 느끼게 만드는 성적 행동 및 언어적 정신적 물리적 행위는 성폭력이다.'라고 이해할 수 있겠죠. 이것이 여성 운동에서 말하는 '피해자 중심주의'인 듯 합니다. 즉, 피해자가 불쾌감을 느낀다면 그건 두말할 필요도 없이 성폭력이 되버리는 거죠.

이런 현재의 성폭력 개념은 앞서 제가 정의한 '자유'의 관점에서 볼 때 문제가 많습니다. 앞서 말한 기존의 자유의 정의에서 생기는 것과 비슷한 문제가 발생할 우려가 많죠. 그래서 저는 앞의 자유의 개념을 바탕으로 '이성적인 사회적 합의를 깨뜨리는 성적 행동'을 성폭력으로 정의하자고 제안합니다. 몇 가지 적용 사례를 통해 학칙안 상의 성폭력과 비교해보겠습니다.

  • 사례 1. 여학생들과의 스킨쉽을 아무렇지 않게 생각하는 한 남자 선배가 있었습니다. 이를테면, 어깨동무를 한다든지, 손을 잡는다든지 등의 가벼운 스킨쉽이죠. 그런데, 몇몇 여학생들이 이런 스킨쉽을 불쾌하게 생각했습니다. 이런 종류의 스킨쉽이란 것은 집단에 따라 아무렇지도 않은 경우일 수도 있고, 대단한 수준으로 받아들여질 수도 있는 것입니다. 이 때 남자 선배의 행동은 성폭력일까요?
    • 학칙안 상의 정의에 따르면 여자 후배들의 성적 자율권이 침해되었음이 명백하므로 성폭력이 되겠죠? 그러나, 이것을 성폭력이라고 단정하기엔 무리가 있다고 봅니다. 제 정의에 따라 판단해본다면, 먼저 그 남자 선배의 행동에 대한 사회 전체의 '이성적인 사회적 합의'는 성립되어 있지 않습니다. 집단에 따라 다를 수 있으니까요. 따라서 이 문제는 당사자, 그리고 그 주변 사회에서 '이성적인 사회적 합의'가 있는지를 보아야겠죠. 그런데, 이 경우 여자 후배들이 그 남자 선배의 행동에 이의를 제기한 바가 없다면 역시 합의는 존재하지 않으므로 그 선배는 '이성적인 사회적 합의'를 깨뜨렸다고 말할 수 없습니다. 따라서 성폭력이라고 말할 수 없습니다.
    사례 2. A(여)와 B(남)가 있는데 둘이 CC라고 합시다. 그런데 아직 서로간의 성적인 접촉은 대단한 수준이 아닙니다. 점차 진전되어가는 단계죠. 그런데, 어느날 B가 술이 많이 취한 상태에서 갑자기 A에게 이제까지의 발전 속도를 넘어서는 성적 접촉을 시도했습니다. A는 '이거 다음엔 이거일 꺼다'라고 생각하고 있었는데 갑작스런 기습으로 상당한 불쾌감을 느꼈고 그 남자 친구에게 실망해서 다음날 아침 성폭력 위원회에 제소했습니다. 이 경우 어떻게 판단해야할까요?
    • 이 경우도 학칙안 상의 정의에 따르면 A가 성적 자율권을 침해당한 것이 명백하므로 B는 성폭력을 저지른 것이 되겠죠. 그러나 이것이 합리적인 판단일까요? 저의 정의에 따르면 이것 역시 '이성적인 사회적 합의'가 도출되지 않은 상태이므로 B의 행동이 자유집합에 속하는지 자유여집합에 속하는지 판단할 수 없기 때문에 성폭력이라고 할 수 없습니다. 그러나, 만약 A가 분명한 거부 의사를 밝혔는데도 B가 그 행동을 멈추지 않았다면 어떨까요? 이 때는 B의 행동은 상호간의 합의를 깨뜨린 것이 명백합니다. 따라서 이 경우는 성폭력으로 처벌대상이 되어야합니다. A가 이런 거부 의사를 전달하지 않았다면 B의 행동은 비록 A의 성적 자율권을 침해했더라도 성폭력이라고 단정할 수 없습니다.
    사례 3. A는 아주 유머가 풍부한 사람으로 종종 주변 사람을 웃기는 재주가 있습니다. 그리고 그의 유머에는 늘 성적인 비유가 포함되곤 하는데 대부분의 여성들도 그 유머에 별다른 이의를 제기하지 않고 폭소를 터트렸고요. 그런데 어느 날, B라는 여자가 거기에 기분이 상했고 그래서 바로 성폭력으로 제소했습니다. 어떻게 판단해야할까요?
    • 전 이것 역시 앞의 두 경와 마찬가지의 결론을 내리고 싶습니다. 어떤 방식으로든 이의 제기가 있어야합니다. 만약에 단지 A가 B에게 불쾌감을 주었기 때문에 그것이 성폭력으로 규정되어버린다면, A는 성적인 비유를 사용하는 농담은 전혀 하지 못하게 됩니다. '성적인 비유를 사용하는 것' 자체가 성폭력이라는 말도 들은 바 있는데 남녀 모두 같이 공감하는데도 단지 성적인 비유를 사용했다는 이유로 성폭력으로 규정되어버린다면 그것이야말로 정말 불합리한 일입니다.

여기까지만 본다면 이런 반론이 있을 수 있겠죠. '그렇다면 어떤 행동이든지 먼저 일단 해버리면 그 상황에서 합의가 되어 있지 않으니까 처벌할 수 없으니 일단 저질러놓고 보면 되지 않느냐.'하고요. 그런 점에 대해서는 다음과 같은 사례를 제시하겠습니다.

  • 사례 4. 어떤 술 취한 남자가 지나가던 여자에게 성추행을 했습니다. 이건 학칙안 상의 기준으로 분명한 성폭력입니다. 제가 정의한 기준으로는 어떨까요? 이런 행동은 이미 사회 전체에 성폭력이 맞다는 '이성적인 사회적 합의'가 이루어져 있다고 할 수 있습니다. 따라서 성폭력이 맞습니다. 사례 5. 종종 해방터 자보에 성폭력 가해자의 사과문이 공개되곤 하는데 보통 사건 유형이 비슷하더군요. 남녀 모두 술에 취한 상태에서 한 방에서 밤을 보내다가 남자가 성충동을 이기지 못해 성추행을 저지르는 식이죠. 일단 이런 경우는 그 행동의 정도와 두 사람의 관계에 따라서 성폭력일수도 있고 아닐 수도 있습니다만 피해자측이 거부 의사를 표현할 수 없는 상황이라는 점에서 '이성적인 사회적 합의'를 도출할 가능성이 아예 차단되어 있으므로 성폭력이라고 하는 것이 옳겠죠.

결국 제가 말하고 싶은 것은 상호간의 합의가 깨졌느냐 아니냐가 그 행동이 성폭력인지 아닌지를 결정하는데 가장 중요하다는 것입니다. 그리고 그 판단에는 피해자의 사전 '가부 의사 표시'가 대단히 중요하며 표시할 수 있는 상황임에도 하지 않았다면 그 행동은 피해자에게 불쾌감을 주었더라도 성폭력으로 규정될 수 없습니다. 이에 반해 '피해자 중심주의'적 시각에서는 '동의 의사 표시'가 기준이 됩니다. 즉, 합의가 먼저 보장된 행동, 즉 자유집합에 속하는 것이 명백한 행동만 가능하다는 결론이 나오겠죠. 제가 굳이 자유를 재정의하고 여러 가지 사례를 제시한 이유는 이러한 '피해자 중심주의'가 잘못이라고 주장하기 위해서입니다.

현실적으로 '동의 의사 표시'를 사전 합의의 기준으로 삼는 것은 문제가 많습니다. 매번 '나 키스해도 되'하고 물어보고 키스할 수는 없는 노릇이죠. 따라서 '거부 의사 표시'를 기준으로 삽는 것이 훨씬 합리적일 것입니다. 거부 의사 표시야말로 합의가 깨어졌음을 명백히 증명할 수 있는 것이니까요. 법적으로도 강간과 화간을 구분하는 가장 중요한 차이는 피해자의 저항 정도라고 알고 있습니다. 그것이 현실적으로 피해자의 입장을 무시하게 될 위험성이 많음에도 불구하고 법적인 판단 근거로 삼을 수 밖에 없는 이유도 그나마 이것이 더 합리적이기 때문입니다. 따라서 서로간에 충분한 합의가 도출되지 않은 상황에서의 선을 넘는 행위는 성폭력으로 규정하여 처벌할 수 없고, 합의가 깨어진 게 명백한 상황, 혹은 합의가 도출될 가능성이 차단된 상황에서만 성폭력이라고 규정할 수 있다고 봅니다.

속칭 연애박사들의 연애론을 들어보면 이런 얘기를 많이 합니다. '여자의 거부의 표현에서 그것이 진짜로 거부하는 표현인지 표현만 그렇고 마음은 동의하고 있는지를 잘 판단하라.' 불행히도 많은 남자들은 이런 판단 능력이 많이 부족합니다. 그래서, 본의 아니게 상대방에게 불쾌감을 주게 되는 경우가 생기는데, 이런 경우 여자의 입장에서도 상대방에게 자신의 의사가 진짜 거부라는 것이 전달되도록 충분한 의사 표시를 할 필요가 있다고 봅니다. 이것이 정말 피해자에게 책임을 전가하는 것일까요?

무엇보다도 '조금이라도 불쾌감을 느낀다면 그것은 성폭력이다.'라는 시각에서 벗어나는 것이 중요하다고 생각합니다. '조금이라도 불쾌감을 느낀다면'하는 생각이 피해자 중심적인 사고인 것처럼 보이지만 사실은 피해자만(!)을 고려하는 사고입니다. 당시의 상황이 어떤지, 당사자가 어떤 관계인지를 도외시한 채 성폭력을 일방적으로 규정해서 가해자를 처벌하는 일은 가해자를 또다른 '피해자'로 만드는 일입니다.


 

블로그

Youngrok Pak , 10 years, 5 months ago

 


Comments




Wiki at WikiNamu