February 2007 Archives

예전엔 매뉴얼에서 쉽게 찾아볼 수 있는 단편적인 사실을 물어볼 땐 RTFM이라고 대답하는 것이 일종의 농담처럼 여겨졌지만, 사실은 옳은 방식이다. RTFM을 알고 있어도 그것을 자신의 습관으로 굳히기에는 쉽지 않아보인다. 그것이 바로 단편적인 사실을 물어보는 것에 RTFM이라고 '대답해주어야' 하는 이유다.

같은 이유로 신입자의 문제를 도와줄 때는 모든 답을 제공해주어서는 안된다. 물론, 그렇다고 해서 영원히 시지프스의 노역을 하고 있는 것을 두고보라는 얘기도 아니다. 문제를 해결할 수 있는 길을 보여주고 조용히 지켜봐주는 것이 바로 멘터의 중요한 역할이다. 물론 그 첫걸음은 바로 RTFM이다. (요즘은 'Google it'이지만.) 또는, 키가 되는 중요한 사실 하나만을 알려줄 수도 있을 것이다. 이런 식으로, 신입자에게 문제를 해결하는 방법을 익히게 해줄 수 있을 뿐만 아니라, 신입자의 능력 또는 지식을 파악하고 무엇을 가르칠 것인가를 알 수 있다.

사람들이 배우는 방식은 사람들마다 차이가 있다. 어떤 사람들은 단편적인 사실을 배우는 것에만 집착하고, 그것을 어떻게 배워야하는가는 신경쓰지 않는다. 어떤 사람들은 어떻게 배워야하는가를 알려주면, 나머지는 스스로 알아서 배우려고 한다. 단순히 열정의 문제가 아니다.

일반적으로 인간은 Context Switching에 익숙하지 못하며, 그렇게 해야만 하는 경우, 실수를 하거나 비효율적이 되기 쉽다. 회사에서 있었던 일이다. (내게 회사 말고 달리 심각한 사회적 관계가 있겠는가) A와 업무를 대화를 하고 있던 중에 대뜸 B가 자신의 업무를 해결하기 위해 다가와서 A와 대화를 시도한다. 덕분에 나는 멀뚱멀뚱 가만히 있어야하는 상황이 되어버린다. 나는 다른 업무로 돌아갈 수도 없다. 아니, 이메일, 메신저와 같은 비동기적인 통신 방법은 두었다가 무엇하는가.

The Last Mind 블로그에, MovableType 3.2 이후로 함께 배포되는 OpenID comment 플러그인을 설치했습니다.

제 OpenID도 만들고 싶어서, MovableType을 개발하는 Six Apart에서 제공하는 TypeKey를 쓸까하다가, 그냥 myid.net에서 만들었습니다. 제 OpenID는 당연히 lastmind.net (alias of josephjang.myid.net)입니다.

Code Monkey

| | Comments (0) | TrackBacks (0)
Refactoring: Improving the Design of Existing CodeRefactoring: Improving the Design of Existing Code, by Martin Fowler

리팩토링이 없다면, 소프트웨어의 생명 주기가 진행될 수록 소프트웨어의 질은 점점 떨어질 수 밖에 없다. 현대적인 관점에서 소프트웨어 개발은 더이상 Rocket Science처럼 고정된 결과물을 산출하는 활동이 아니다. 뛰어난 소프트웨어 아키텍트라 하더라도 소프트웨어 개발 프로젝트의 초기에 그 소프트웨어를 바라보는 방식 (또는 디자인)이 후반에까지 변함없이 지속될 확률은 매우 낮다. 하물며 여러 사람이 함께 일하는 팀 소프트웨어 개발에서야 더이상 말할 것도 없다. 특히, 사용자의 요구사항이 다 떨어져서 더이상 변경할 것이 없어지지 않는 한, 소프트웨어 질의 하락은 점점 빨라질 뿐이다.

이 책은 리팩토링에 관한 바이블이다. Martin Fowler는 더이상 언급할 필요도 없는 유명한 저자다. 이 책은 리팩토링과정 예시를 통한 리팩토링에 관한 소개, 리팩토링의 정의와 중요성, 리팩토링을 언제, 어떻게 적용할 것인가에 관한 가이드, 리팩토링 패턴 카탈로그로 이루어져 있고, 리팩토링의 간략한 역사와 리팩토링의 도입 방법, 리팩토링도구에 관한 의미있는 에세이들으로 끝맺고 있다.

Martin Fowler는 리팩토링을 다음과 같이 정의하고 있다.

Refactoring: a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.

리팩토링의 목적은 소프트웨어를 좀 더 이해하게 쉽게 만들거나 그 과정에서 소프트웨어를 좀 더 이해함으로써 소프트웨어를 수정하는 비용을 줄이는 것이다. 반대로 말하면, 소프트웨어를 수정할 필요가 없다면, 리팩토링을 할 필요는 없다는 얘기가 된다. 또한 여기에는 한가지 가정이 있는데, 소프트웨어를 좀 더 이해하기 쉽다면, 수정하기도 쉽다는 것이다. 당연한 얘기같지만, 많은 개발자들은 어떤 이유에서건 이를 중요하게 생각하지 않는다.

리팩토링의 정의는 리팩토링의 가장 중요한 측면 또한 언급하고 있는데, 바로 겉으로 드러나는 소프트웨어의 동작 방식을 변경하지 않는다는 것이다. 리팩토링 패턴 카탈로그는 바로, 소프트웨어의 동작 방식을 변경하지 않는다는 제약을 위해서 어떤 방식으로 Refactoring을 수행해야하는가에 관한 패턴들을 모아둔 것이라고 볼 수 있다. 그리고, 이러한 제약을 보장하기 위해서 Unit Testing을 제안하고 있는 것이다.

패턴 카탈로그를 읽는 것은 (항상 그렇듯이) 매우 지루한 일이었다. 패턴 카탈로그는 패턴이 갖는 이점들을 제공하지만, 카탈로그를 처음부터 끝까지 읽어볼 정도로 가치가 있지는 않다. 특히, Mechanics 부분은 읽지 않아도 무방하다. 어느 정도 숙련된 개발자라면 리팩토링의 개념만 잘 알고 있다면, Mechanics를 직접 읽어볼 필요 없이 같은 것을 수행할 수 있을 것이다. 특히, 많은 수의 리팩토링 패턴들이 도구를 통해 자동화가 되어있는 현재 시점에서는 더더욱 그러하다. 내가 추천하는 방법은, 패턴의 이름(Name), 맥락(Context) 부분과 동기(Motivation) 부분만 읽고, 이해가 가지 않는 경우에만 예시(Example) 부분을 읽어보는 것이다. 내게는 패턴들을 통해 어휘를 확장시킬 수 있었던 것이 가장 큰 도움이 되었다. 예를 들어, 실제로 리팩토링을 수행할 때, 커밋 로그(Commit Log)에 리팩토링 패턴을 적어넣을 수 있어서 편리했다. 사실, 만약 이 책을 가장 효과적으로 읽고 싶다면, 패턴 카탈로그만 빼고 다 읽고 나서, 패턴 카탈로그들은 이름만 익숙해질 정도로만 훑어보라고 조언해주고 싶다.

패턴 카탈로그 읽는 것을 마치고 수십 페이지 남겨놓은 상태에서는, 마치 이미 이 책을 다 읽었다는 듯 생각을 하고 있었는데, 이 책의 마지막 장들에 들어있는 에세이들은 상당히 중요한 문제 제기와 인식을 제공해주었다.

사실상 리팩토링이라는 단어를 처음으로 만들고 초기부터 연구를 수행한 William Opdyke의 에세이는, 왜 실제 세계에서 프로그래머들은 리팩토링을 하지 않으려하는가를 따져보고, 어떻게 하면 그런 문제를 넘어 리팩토링을 적용할 수 있을 것인가에 관해서 얘기하고 있다. 자세히 설명하기 보다는 다음 문단을 인용하도록 하자.

Within Lucent/Bell Labs I found that encouraging application of reuse and platforms required reaching a variety of stakeholders. It required formulating strategy with executives, organizing leadership team meetings among middle managers, consulting with development projects, and publicizing the benefits of these technologies to broad research and development audiences through seminars and publications. Throughout it was important to train staff in the principles, address near-term benefits, provide ways to reduce overhead, and address how these techniques could be introduced safely. I had gained these insights from my refactoring research.

이 내용은 비단 리팩토링에만 해당되는 것은 아니다. 주로 단기적인 성과를 중요시하는 기업 환경에서, 장기적인 투자를 필요로 하는 무언가를 추구하기 위해서는 위에서 언급한 모든 일들을 할 수 있을 정도로 부단히 노력해야한다는 생각이 든다.

두번째 에세이는 리팩토링 도구에 관한 얘기를 하고 있는데, 이 책이 쓰여진 1999년에는 어느 정도 널리 퍼진 리팩토링 도구가 없었겠지만, 2007년 현재에는 주요 언어인 Java의 주요 IDE들이 리팩토링을 직접 지원하고 있기 때문에, 기술의 변화를 지면을 통해 느낄 수 있게 해준다.

이 책의 마무리에 해당하는 Kent Beck의 에세이는 리팩토링을 할 때 가져야할 마음가짐에 관해서 얘기하고 있는데, 중요한 항목들은 여기에 인용할 가치가 있을 것이다.

  • Get used to picking a goal
  • Stop when you are unsure
  • Backtrack
  • Duets

이 책을 읽기 시작하면서 바로 떠오른 생각은 패턴들로 리팩토링하는 리팩토링 패턴이 이 책에는 별로 없다는 것이다. 저자는 이 책에서 리팩토링 패턴의 가장 작은 단위들을 우선적으로 다루고 싶어했고, 그 목적은 의미가 있었던 것 같다. 그리고 이 책에서도 같은 아이디어를 여러번 언급하고 있다. 실제로 이 아이디어는 2004년에 출판된 Refactoring to Patterns라는 책으로 실현되었다. 시간이 되는대로 Refactoring to Patterns를 읽어볼 예정이다.

JRuby String 즉, JRuby 상에서의 Ruby String과 Java String이 서로 호환되지 않는 문제를 발견했다. ASCII의 경우에는 별 문제가 없지만, 한글의 경우에는 깨지는 문제가 발생한다. JRuby 사용자가 별로 없어서 이 문제가 내 환경에 국한된 문제인지 실제로 JRuby의 문제인지 확인은 할 수 없다. JRuby 코드를 약간 들여다보아야 할 것 같다.

이 문제를 테스트하기 위해서는 다음의 Ruby 코드를 사용하면 된다. Java String은 System.out.println으로 출력했을 때만, Ruby String은 puts로 출력했을 때만 정상적으로 출력된다. 서로 호환이 된다면, 모두 정상적으로 출력되어야할 것이다.

require 'java'

include_class 'Foo'
include_class 'java.lang.System'

java_string = Foo::getString # yields 'meme메롱' in java.lang.String form
puts java_string
System.out.println java_string

ruby_string = 'meme메롱'
puts ruby_string
System.out.println ruby_string

다음 코드는 위의 Ruby 코드에서 사용하는 Java 클래스인 Foo 클래스.

import java.lang.String;

public class Foo {
   public static String getString() {
      return new String("meme메롱");
}
}

이 문제를 피해가기 위한 한가지 workaround는 ASCII 문자로 된 intermediate form을 두고 Java String과 Ruby String을 서로 변환하는 것이다. intermediate form은 어떤 것을 사용해도 되지만 percent encoding을 사용하여, 다음과 같은 코드를 사용할 수 있다.

require 'java'
require 'cgi'

include_class 'java.net.URLEncoder'
include_class 'java.net.URLDecoder

class StringTranslator
   def self.to_ruby_string(java_str)
      CGI.unescape(URLEncoder.encode(java_str, "UTF-8"))
end

def self.to_java_string(ruby_str)
      URLDecoder.decode(CGI.escape(ruby_str), "UTF-8")
end
end

 

결론부터 얘기하자면, Java의 variable number of arguments(이하 varargs)를 가진 Java 메서드를 JRuby 코드에서 호출하려면, 여러 argument들을 나열하거나 Ruby Array를 사용하면 안되고, Java array 객체를 생성해주어야한다. Java의 varargs는 Java array와 동일하게 취급되므로 당연히 Java array의 경우에도 마찬가지다. 그렇다면, Ruby Array는 무엇이랑 대응될까? 바로 java.util.List다.

다음은 이러한 동작을 테스트하기 위한 간단한 Java 클래스. 위에서 언급한대로, Java array parameter를 varargs로 보면 되겠다.

import java.lang.String;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

public class Song {

   
public enum Category {
       
POP, ROCK, CLASSIC, JAZZ
   
}

   
private String title;
   
private List<Category> categories = new ArrayList<Category>();

   
public Song(String title) {
       
this.title = title;
   
}

   
public void setCategory(Category category) {
       
this.categories.clear();
       
this.categories.add(category);
   
}

   
public void setCategoryList(List<Category> categories) {
       
this.categories.clear();
       
this.categories.addAll(categories);
   
}

   
public void setCategories(Category[] categories) {
       
this.categories.clear();
       
this.categories.addAll(Arrays.asList(categories));
   
}
   
   
public String toString() {
       
return "Title: " + title + " Categories: " + categories;
   
}
       
}  

다음은 위의 Java 클래스를 사용하는 JRuby 코드.

require 'java'

include_class 'Song'

song = Song.new 'Waiting On The World To Change'
puts song

song.setCategory Song::Category::POP
puts song

categories = Array.new
categories.push Song::Category::POP
categories.push Song::Category::ROCK
puts categories.length
puts categories.size
song.setCategoryList categories
puts song

categories = Song::Category[].new 2
#categories.add Song::Category::POP # not working
categories[0] = Song::Category::POP
categories[1] = Song::Category::CLASSIC
puts categories.length
#puts categories.size # not working
song.setCategories categories
puts song
public class Song {
public enum Category {
POP, ROCK, CLASSIC, JAZZ
}

위와 같이 Java enum constants들이 정의되어있다면, JRuby에서 이들을 사용하는 방법은 다음과 같다.

require 'java'
include_class 'Song'

puts Song::Category::POP

좀 뒤늦은 소식이지만, Sun의 JDK 5.0 Update 10에 epoll 지원이 들어갔습니다. JDK 6.0에 epoll 지원이 들어가면서 JDK 5.0에도 반영된 것 같습니다. nio가 생길 때 당연히 epoll을 지원하겠거니 생각했는데, 의외로 대단히 늦게 지원하기 시작했군요. 그럼 이전에는 select()나 poll()로 구현되어있었다는 얘기군요. epoll을 사용했을 때의 performance, scalability상의 이점은 Blackdown JVM 1.4.2를 이용한 벤치마크에서도 알 수 있습니다. Sun JDK 6.0의 nio 벤치마크 자료는 모르겠지만, 이희승님의 벤치마크에 의하면 10% 정도 향상이 있었다고 합니다. 어쨌든 희소식입니다. C/C++의 성능 우위를 뒤따라잡기 위해서 Java도 열심히 노력하고 있군요.

Support for epoll

The Linux downloads of this update release include an implementation of java.nio.channels.spi.SelectorProvider that is based on the epoll I/O event notification facility. The epoll facility is available in the Linux 2.6 kernel, and is more scalable than the traditional poll system call. This epoll-based implementation may improve the performance of server applications that use the New I/O API and that register hundreds of channels with a selector. For more information, refer to the epoll(4) and poll(2) man pages.

The epoll-based implementation of SelectorProvider is not selected by default. To select it, specify a property value from the command line as follows:

java -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider ...

(Excerpted from JDK 5.0 Release Notes)

Author

Linko Apple Booth in the Coex Seoul Email address

Recent Comments

  • Nomota: Please turn yourself to http://cafe.naver.com/korlucene It's a cafe at Naver, read more
  • abdul muis: thanks it's cool read more
  • trustin.myid.net: 독일어의 압박이 흐흐.. 헤르만 헤세의 데미안!? read more
  • pcpenpal: 요새 주변이 무시무시합니다. ㅠㅠ read more
  • lastmind.net: 오! 반가워요~ read more
  • coolluck: 안녕하셔요. 저는 최민수라고 하는데 네오위즈 있을 때 잠시 인연이... 저도 read more
  • lastmind.net: 와, 정말이네요. 거기까지는 생각이 미치지 못했습니다. ^^ read more
  • 홍민희: 굳이 string concatenation 쓸 필요 없이 "\0012\0"이라고 써도 됩니다. 최대 read more
  • waitfor: mod_php 로 구글링중 우연히 방문했습니다. 좋은글이 많아 자주 들르게 될것같습니다. read more
  • 죠커: 스몰 릴리즈 오랜만에 듣는 단어이네요. 한동안 스몰 릴리즈를 잊고 있었던 read more

About this Archive

This page is an archive of entries from February 2007 listed from newest to oldest.

January 2007 is the previous archive.

March 2007 is the next archive.

Find recent content on the main index or look in the archives to find all content.