StandardAnalyzer in Lucene, and Korean

Lucene에서 Analyzer의 역할은 Field의 텍스트를 TokenStream 즉, 토큰들의 스트림으로 만들어주는 역할입니다. 이 역할을 하는 abstract 메서드가 바로 Analyzer.tokenStream() 메서드인데, signature는 다음과 같습니다.

public abstract TokenStream tokenStream(String fieldName, Reader reader);

Lucene이 제공하는 Analyzer들에서 이 메서드의 구현은 하나의 Tokenizer와 다수의 TokenFilter들을 사용합니다. TokenizerField의 텍스트를 토큰들로 만들어주고, 이 토큰들이 TokenFilter들에 의해 변형되거나 걸러집니다. StandardAnalyzer.tokenStream() 메서드의 구현은 다음과 같습니다.

public TokenStream tokenStream(String fieldName, Reader reader) {

TokenStream result = new StandardTokenizer(reader);
result = new StandardFilter(result);
result = new LowerCaseFilter(result);
result = new StopFilter(result, stopSet);
return result;

}

일단 TokenFilter들에 대해서 설명하면, StandardFilter는 영어에서 소유격을 나타내는 apostrophe 또는 apostrophe s를 제거해주거나 acronym에서 period(.)를 제거해주는 역할을 합니다. LowerCaseFilter는 이름이 의미하듯이 토큰들을 소문자로 만들어주는 TokenFilter고, StopFilter는 지정된 stop word들을 토큰에서 걸러내는 역할을 합니다. StandardAnalyzer가 지정하는 stop word들은 StopAnalyzer.ENGLISH_STOP_WORDS로, 영어에서의 관사나 전치사, 대명사에 해당하는 단어들입니다.

결국 StandardAnalyzer의 한국어 처리에서 중요한 것은 바로 StandardTokenizer인데, 일종의 parser generator에서 생성된 것으로 보입니다. Lucene 소스에서 lucene-2.0.0/src/java/org/apache/lucene/analysis/standard/StandardTokenizer.jj를 보면, 토큰 정의들을 볼 수 있는데, 한국어와 관련해서 다음과 같은 부분이 있습니다.

// basic word: a sequence of digits & letters
<ALPHANUM: (<LETTER>|<DIGIT>|<KOREAN>)+ >

<중략 />

| < CJ: // Chinese, Japanese
[
“u3040”-“u318f”,
“u3300”-“u337f”,
“u3400”-“u3d2d”,
“u4e00”-“u9fff”,
“uf900”-“ufaff”
]
>

| < KOREAN: // Korean
[
“uac00”-“ud7af”
]
>

이것을 보면 한국어 문자들을 유니코드 범위로 표현하고, ALPHANUM이라는 토큰이 한국어 문자를 포함하도록 처리하고 있는데, 따라서, StandardAnalyzer를 사용하면, “911사태”, “Bush대통령”과 같은 것들이 하나의 토큰으로 처리됨을 알 수 있습니다. 중국어와 일본어 문자들은 CJ 토큰으로 따로 분리해놓은 반면 KOREAN은 왜 ALPHANUM으로 들어갔는지는 모르겠지만, 궁극적으로는 모든 언어의 문자들이 ALPHANUM에 들어가는 것이 정확하겠죠.

이렇게 해서, Lucene 2.0의 StandardAnalyzer에서 한국어 문자들로 이루어진 단어들이 제대로 인덱싱된다는 것은 알았지만, 이들을 이용해서 잘 검색할 수 있느냐는 다른 문제입니다. Analyzer.tokenStream() 메서드에서 돌려준 TokenStream에 담긴 토큰들을 인덱스에 그대로 저장하기 때문에, (이들을 다른 식으로 가공하기 위한 별다른 레이어를 발견하지 못했습니다.) 쿼리의 토큰들이 문서의 토큰들과 정확히 match되는 문서만 검색 결과에 포함됩니다. 즉, “Bush대통령”이라는 토큰으로 인덱스에 들어간다면, “Bush”나 “대통령”, “대통” 등으로는 이 토큰이 들어간 Document를 검색할 수 없습니다.

이 문제를 해결하기 위해서는, n-gram 분석이나 형태소 분석을 통해 토큰들을 적절하게 분리한 후, 인덱싱해야할 것입니다. lucene-2.0.0/contrib/analyzers/src/java/org/apache/lucene/analysis/cjk/에 있는 CJKAnalyzerCJKTokenizer2문자 단위로 토큰들을 생성하는 극히 단순한 Bigram 분석을 통해 이 문제를 해결하고 있는 것 같습니다. 이 경우의 문제점은 한국어에 존재하는 조사나 용언 어미등이 들어가는 토큰들을 전혀 배제하지 못하는 것인데, 검색의 품질을 상당히 낮출 가능성이 있습니다.

홍태희님은 루씬과 한글이라는 페이지(via 루씬에 대한 한국개발자의 고민들)에서 이 문제를 조사에 해당하는 부분을 토큰에서 잘라내는 TokenFilter를 도입해서 해결하고 계시긴 하지만, 조사에 해당하는 문자로 끝나는 단어들을 검색하지 못하는 문제가 있습니다.

제대로 된 오픈소스 한국어 Analyzer의 수요가 많음에도 불구하고 없는 것이 아쉽습니다. 짧은 생각으로는 단순한 휴리스틱으로는 힘들 것 같고, 충분히 많은 데이터를 이용한 분석이 필요할 듯 한데, 아무래도 오픈소스 활동에 전념하기 힘든 개인의 수준에서는 힘들지 않을까 싶군요. 좀 더 조사해 봐야겠습니다.

StandardAnalyzer in Lucene, and Korean 더 읽기"

Lucene: First Impression

Lucene 또는 Lucene Java는 매우 유명한 오픈 소스 검색 엔진 소프트웨어입니다. Lucene은 Apache Lucene 프로젝트에 속해있는데, 이 프로젝트는 Lucene 외에도 NutchHadoop 등을 포함하는 Apache의 검색 소프트웨어 프로젝트입니다.

팀에서 웹크롤한 결과의 질을 가시적으로 만들기 위한 방법의 하나로 Lucene을 사용해보기로 했습니다. Lucene이 사용하기 쉽다는 평은 처로군으로부터 자주 들었지만, 이 정도 일 줄은 몰랐습니다. 팀이 가지고 있는 데이터베이스와 연동해서 콘솔을 통해 검색 가능하게 만드는데, 한시간 밖에 안걸렸습니다. Lucene에 대한 사전지식은 거의 없었다고 봐도 무방합니다. Lucene을 사용하기 위해 참고한 것은 Lucene 소스 코드 내에 들어있는 데모와 데모를 간단하게 설명하고 있는 문서 뿐이었습니다. Lucene에 관한 유일한 책인 ‘루씬 인 액션‘을 살짝 보긴 했으나 책에 등장하는 코드들이 out-of-date 더군요. Lucene을 연동하는 코드를 짜는 데에는 별로 도움이 안되었습니다.

1시간 동안 만든 것은 데모의 코드를 Copy and Paste하면서 만든 Indexer 프로그램과 Searcher 프로그램인데, 각각 인덱싱을 수행하는 프로그램과 생성된 인덱스로부터 검색을 수행하는 프로그램입니다.

Indexer 코드의 중요한 부분만을 보자면 대충 다음과 같습니다.

IndexWriter writer = new IndexWriter(INDEX_DIR, new StandardAnalyzer(), true);

// make lucene document
Document doc = new Document();
doc.add(new Field(“url”, uri, Store.YES, Index.UN_TOKENIZED));
doc.add(new Field(“title”, title, Store.YES, Index.TOKENIZED));
doc.add(new Field(“content”, cpmtemt, Store.YES, Index.TOKENIZED));

// add it to IndexWriter
writer.addDocument(doc);

writer.optimize();
writer.close();

간단히 설명하면, IndexWriter는 인덱스를 생성하는 역할을 하고, 인덱스의 단위는 Document입니다. Document는 복수의 Field를 가질 수 있고, Field 별로 tokenize할 것인가, 인덱스에 Field 내용을 저장할 것인가를 지정할 수 있습니다. Analyzer가 Field를 tokenize하는 역할을 하는데, IndexWriter에 명시한 것 외에 Field 별로 Analyzer를 지정할 수 있습니다.

디자인이 도메인을 그대로 반영하고 있고, 단순하면서도 확장성이 높은, 좋은 API입니다.

Searcher 코드도 한번 들여다 보죠.

IndexReader reader = IndexReader.open(“index”);
Searcher searcher = new IndexSearcher(reader);
Analyzer analyzer = new StandardAnalyzer();

QueryParser parser = new QueryParser(“content”, analyzer);
Query query = parser.parse(queryString);

Hits hits = searcher.search(query);

for (int i = 0; i < hits.length(); i++) {

Document doc = hits.doc(i);
String urlString = doc.get(“url”);
String title = doc.get(“title”);
String content = doc.get(“content”);
// …

}

역시 마찬가지로 코드만으로도 이해하기 쉽습니다. 별로 설명할 것이 없네요.

인덱싱 하는데에는 시간이 좀 걸리지만, 한번 인덱싱하고난 후에는 검색 속도는 대단히 빠릅니다. 10만개 정도까지밖에 안해봐서 실제 크기의 데이터에서는 어떨런지는 모르겠습니다.

한편, 한글 관련한 Analyzer 이슈에 대해서는 아직 잘 모르겠습니다만, 한글 검색도 그럭저럭 되는 것 같군요. 좀 더 자세히 알아봐야할 것 같습니다.

현재의 검색 솔루션이 맘에 안든다면, 엔터프라이즈급의 간단한 검색 솔루션으로는 부담없이 연동해서 쓸 수 있는 수준인 것 같습니다.

지금 구글 검색해보니, 좀 더 좋은 튜터리얼 들이 있군요. 참고하세요.

Lucene: First Impression 더 읽기"

How To Code

‘How To Code’는 팀 내에서 발표할까 생각했다가 취소한 발표자료입니다. 내용은 소프트웨어 개발에서 프로그래머가 지켜야하는, 또는 지키도록 기대되는 기본적인 원칙들 입니다. (빠진 것들도 많겠지만, 생각나는 중요한 것들만 적었습니다.)

발표 자료에 관해 잠시 언급하자면, 이 발표 자료만으로 발표하는 것은 졸린 설교가 될 가능성이 높기 때문에, 충분한 예들이 있어야 합니다. 가능하다면, 팀에서 쓴 코드를 직접 사용하는 것도 괜찮다고 생각이 됩니다.

최근에, 팀 내에서 잘 지켜지지 않는 개발 원칙들을 어떻게 바른 방향으로 유도할 것인가에 관해서 대단히 관심이 많아졌는데, 이런 원칙들만을 나열하는 것은 ‘당신이 잘못 했으니까 고치라’라는 식으로 들릴 가능성이 높고, 그렇다면 절대 변화는 일어나지 않을 것이라는 생각이 듭니다.

다른 사람들을 변화시키려면, 원칙 이전의 가치에 대한 공감대가 필요한데, 우선 그 공감대가 형성되어있는가, 또는 어떻게 형성될 수 있는가를 따져야할 것 같습니다. 공감대를 어떻게 형성할 수 있는가에 대해서는, 이러한 원칙들이 실제로 적용되는 경우를 보여주면서 그것이 좋다는 것을 ‘느낄’ 수 있도록 해주는 것이 가장 좋은 방법인 것 같습니다. 이를 위한 실제적인 방법으로 ‘코드 리뷰’를 생각 중입니다만… 여러모로 아직 고민 중입니다.

How To Code 더 읽기"

2007년 공약: 출근 시간 지키기와 운동

제가 현재 가지고 있는 나쁜 습관 중 하나는 출근 시간을 잘 지키지 않는 것입니다. 제가 현재 잘하지 못하고 있는 것 중 하나는 운동을 소홀히 하는 것입니다. 따라서, 2007년에는 이 두가지 점을 개선해보기로 했습니다. 따라서, 다음의 두가지를 2007년 공약으로 지정합니다.

  • 출근 시간 지키기
    • 30분 이상 늦는 경우를 실패로 봅니다.
  • 운동하기
    • 운동을 주기적으로 그리고, 충분히 합니다.
    • 1주일에 평균 30분 이상씩, 3일간 달성하지 못하는 경우를 실패로 봅니다.

Kent Beck의 변화 유지 비결에서 설명하는 Accountability를 위하여, 매일의 출근 시간운동 내역을 기록하고, 한달마다 요약을 이 블로그에 포스팅 하도록 하겠습니다.

‘출근 시간 지키기’ 공약의 구체적인 계획

최근에 오전 7시 30분에 일어나는 것을 실천하고 있지만, 자는 시각은 그대로이기 때문에 수면 시간이 부족합니다. 평균적으로 7시간, 적어도 6시간 수면을 취하기 위해서는, 0시 30분에서 1시 30분에는 자야합니다. 자는 것을 방해하는 모든 요소들을 제거한 후에도 자는 시각이 늦어지는 이유 중에 하나가 자기 위한 준비 때문인데, 자기 위한 준비에 30분에서 1시간 정도가 소요되므로, 적어도 자정부터는 잘 준비를 해야합니다.  잘 준비를 마친 후부터 잠이 들 때까지는 TV 시청이나 책을 볼 수 있으나, TV 시청은 자제하는 것이 바람직 해 보입니다.

‘운동하기’ 공약의 구체적인 계획

제가 좋아하는 운동이 별로 없고, 기껏해야 수영을 좋아하지만, 수영장을 다닐만한 여건이 좋지 않으므로 Fitness center가 대안으로 떠오릅니다. 하지만, Fitness center를 흥미를 유지하면서 다니는 것은 어렵고, 제게는 대단히 시간 낭비로 느껴지기 때문에, 집에다 운동 기구를 사두고 운동을 하는 방법을 생각하고 있습니다. 일주일 정도 시간을 두고 조사를 한 후, 구입할 예정입니다. 집에서 휴식을 취할 때 미국 드라마를 보는 것을 대단히 좋아하기 때문에, 운동과 함께 드라마를 본다면 좋을 것 같습니다. 운동을 매일 할 수 있으면 좋겠지만, 약속, 야근, 회식 등의 비정기적인 사건 때문에 현실적으로 불가능하고, 일주일에 3일 정도만 30분 이상 할 수 있으면 좋을 것 같습니다. 장기적으로는 일주일에 3일 이상, 하루에 1시간 이상을 목표로 하고 있습니다.

2007년 공약: 출근 시간 지키기와 운동 더 읽기"

Expression Builder

Martin FowlerEffective Java Reloaded에서 언급했던 ‘Builder Pattern with Builder’s setter methods that return Builder’를 Expression Builder라고 이름 짓고 있다. Expression Builder는, CommandQuerySeparation을 어기는 Fluent Interface의 단점을 보강한 패턴이다.  Django Model의 filter 메서드를 봤을 때 다들 신기해했던 기억이 난다.

Expression Builder 더 읽기"

Effective Java Reloaded

Effective Java Reloaded는 구독하고 있는 서민구 님의 블로그 글 ‘Effective Java Reloaded‘에서 발견한 것인데, Effective Java 의 저자인  Joshua Bloch 가 JavaOne 2006 에서 발표한 내용이다.

이 material의 도입부에서 Joshua는 “Effective Java Hasn’t Yet Been Reloaded, but I Have Plenty of Ammunition” 이라는 얘기를 하고 있다. 조만간 Effective Java  다음 판이 나오리라고 기대할 수 있을 듯 하다. 몇가지 인상적인 내용을 적어보면,

Builder Pattern with Builder’s setter methods that return Builder

NutritionFacts twoLiterDietCoke = new NutritionFacts.Builder("Diet Coke").sodium(1).build();

 위와 같은  Builder Pattern이 새로운 것은 아니지만, Joshua Bloch가 얘기하고 있는 것은, 이러한 패턴을 많은 수의 optional parameter를 가진  constructor의 대안이라는 것이다. 내가 직접 디자인하는 경우에는 constructor에 parameter가 많아야하는 경우를 가능한 한 피하려고 하기 때문에, 이 정도의 대안이 필요할 정도의 클래스를 디자인하게 되는 건 드문 일이지만, 간혹 수많은 parameter들을 가진  constructor들의 overloading으로 점철된 클래스를 맞닥뜨리게 되기도 한다. 이 경우 난 setter method와 Factory Pattern을 조합하는 형태(Dependency Injection의 Setter Injection)로 리팩토링 방법을 권장하는데, 아무래도 invariants를 벗어나는 instance를 생산한다는 것은 개운하지는 않은 일이다. 이런 경우에 위와 같은 Builder Pattern이 약간이나마 더 우아한 해결책이 될 수 있다.

한편, 이렇게, 간단한 개념을 표현하기 위해서 복잡한 방법을 동원해야한다는 것은 자주 프로그래밍 언어의 한계를 느끼게 한다. 언제쯤 Factory Pattern이나  Builder Pattern을 프로그래밍 언어 자체에서 지원할런지.

Erasure/Wildcard 때문에 복잡한 Generic

Generic이 명시하는 타입은 컴파일 단계에서만 유효할 뿐, 실행 단계에서는 타입 정보는 모두 사라지고 Erasure 만이 남는다. 아마 기존의  VM을 어느 정도 유지하면서도 Generic의 이점을 얻기 위해서 이러한 방법을 선택했으리라고 생각된다. 하지만, 동시에 Generic을 복잡하게 만드는 원인이 되기도 한다. (Joshua Bloch도 아마도 미래의 Java에서는 없어질 것이라고 언급하고 있다.)

Wildcard의 존재도 Generic을 복잡하게 만드는 개념 중의 하나인데 이를 가장 극명하게 드러나는 것이 바로 이 material의  Pop Quiz다.

public static void rotate(List<?> list) {
    list.add(list.remove(0));
}

언뜻 보기에 정상적으로 보이는 이 코드는  컴파일이 되지 않는 코드다. 자세히 들여다보면, list.add() 메서드와 list.remove()  메서드에서의 list의 타입은  wildcard type이고, List<?>와 List<?>라고 볼 수 있다. 그렇다면, list.add(List<?>) 에는  List<?> 를 넣을 수 있을까?  List<?>는, Object 클래스 또는 Object 클래스의 subclass의 List임이 보장될 뿐이기 때문에, List<?>와 List<?>를 같은 타입인지는 비교할 수는 없다.

Joshua는 같은 material에서 type parameter 자체가 사용되는 일이 없다면, wildcard를 사용하라고 조언하고 있지만, 과연 그렇게 해서 얻는 것이 무엇인지 의문이 든다. parameterized type 대신 wildcard type을 써야만 하는 이유가 뭘까? wildcard 관련 JSR을 살펴봐야겠다.

Erasure와  Wildcard 를 이해하지 못하면 Java Generic의 반만 이해한 것에 불과하다.

private final is the norm

필요하지 않은 자유도를 가능한 한 제약해야한다는 원칙을 따르고 있다면, 필드(멤버 변수)들은 물론, 파라미터 변수, 로컬 변수까지 final을 붙여야 하는가 하는 고민을 한번쯤 해봤을 것이다. 이는 C++ 프로그래밍 언어의 const에 관해서도 비슷한 이슈가 발생하한다. 결론부터 얘기하자면, 여기서 우리는 타협이 필요하다. 모든 타입 선언에 final (또는 const)을 붙이는 것은 프로그래머에게 상당한 비용을 요구하는 일이지만, 그로 인해 얻는 것이 그 비용을 상회하리라는 보장은 없기 때문에 그렇다. 따라서, 우리가 final을 사용함으로써 무엇을 얻는가를 따져보아야 한다.

이를테면, 상대적으로 좁은 메서드 scope로 격리되어있어서 그 영향이 적은 파라미터 변수, 로컬 변수 값의 변경은 제한된 코드 블럭에서 일어나기 때문에, 상대적으로 변수를 제어하거나 변수로 인해 발생한 문제를 수정하기도 쉬운 편이다. (이것이 메서드를 짧게 쓰라는 이유 중의 하나다.) 하지만, 클래스의 필드들의 경우에는 파라미터 변수나 로컬 변수들과는 접근 범위가 상당히 넓은 편이다. (클래스의 모든 메서드들 뿐만 아니라, 접근 권한에 따라 외부의 불특정 다수가 접근할 수 있으므로). 따라서, 필드를 가능한 한 final로 사용하는 것은 얻을 수 있는 이익이 크다.

비용-효율적(cost-effective)한 지점에서 타협을 해야한다는 얘기는, static typing과  dynamic typing 논쟁(아마도 이 외에도 많은 프로그래밍 언어 논쟁)에도 적용시킬 수 있다. 변수의 파라미터 타입을 명시하지 않아도 되는 언어를 사용하다보면, 주의를 기울인다고 하더라도 실수할 가능성이 있다. 설령 이러한 가능성이 매우 낮더라도, 한번 실수를 하게되면, 드러나지 않은 문제를 찾아내기란 결코 쉽지 않다. (strong typing 특유의 편안함은 여기서 온다.) 그렇다고 해서  static typing만을 극단적으로 추구한다면, 불필요한 수준의 타입 명시로 인해 개발  비용은 높아지고, 충분히 유연하지 못한 언어상의 한계로 인해 디자인에서 어려움을 겪게 될 것이다. 결국 우리가  static/dynamic typing 사이에서 취해야할 행동은 마찬가지로 cost-effective 한 어떤 지점에서의 타협이 될 것이다. C# 3.0의 ‘Static Typing Where Possible, Dynamic Typing When Needed‘를 참고해보길 바란다.

Effective Java Reloaded 더 읽기"

Effective Java Programming Language Guide

Effective Java Programming Language GuideEffective Java Programming Language Guide, by Joshua Bloch

Effective Java Programming Language Guide (이하 Effective Java)는 Java 프로그래밍 언어를 잘 쓰기 위한 책에 속한다. 이 책은 Effective C++ 시리즈나 Exceptional C++ 시리즈처럼 하나의 조언과 그것에 대한 설명을 담은 57개의 항목들로 구성되어있다. 형식 뿐 만 아니라 내용을 보더라도 Java의 Effective C++과 같다고 봐도 무방할 것 같다.

Effective Java의 각 항목들은 반드시 Java 언어에만 국한되는 것이 아닌 일반적인 프로그래밍 원칙들 또는 객체 지향 프로그래밍에서 일반적으로 통용되는 프로그래밍 원칙들도 포함하고 있다. 그래서, Effective/Exceptional C++ 시리즈와 중복되는 항목들도 있다. (예를 들어, ‘Item 14: Favor composition over inheritance’ 와 같은 경우)

이런 종류의 책들은 한번만 읽는다고 해서 그 책들의 조언들을 완전하게 응용하기는 어렵다. 그래서, 단지 각 항목들의 리스트만이라도 주기적으로 보면서 조언들을 상기시켜주는 것이 좋다. (Effective Java의 항목 리스트) 특히, 거의 기계적으로 적용할 수 있는 항목들은 항상 기계의 도움을 받을 수 있도록 자동화해두는 것도 좋은 방법일 것이다. (지난 글에서 언급했던 FindBugs와 같은 이클립스 플러그인들이 도움을 줄 수 있을 것이다.) 한편, 각 항목들로부터 도출되는 보다 일반적인 프로그래밍 원칙들은 마음에 잘 새겨두는 수 밖에는 없을 것이다.

이 책이 출판된지 (2001년) 꽤 오래되었기 때문에, 현재 Java (특히 Java 5.0)의 상황을 반영하지 못하는 항목들이나 언급들이 약간씩 보인다. (예를 들어, type-safe enum 항목이나 doug lea의 concurrency 라이브러리에 대한 언급)  하지만, 반영하지 못하는 점들은 대체로 Java가 개선되었기 때문에 발생하는 것이어서 여전히 그 충고들 자체는 유효하다고도 볼 수 있다.

C++ 프로그래밍 언어에 관한 유명한 책들의 저자들이 해당 언어의 커뮤너티에서 상당히 유명하고 중요한 역할을 수행하고 있는 것을 보면, 분명히 이 책의 저자에 대해서도 알아두는 것이 도움이 될 것이다. Effective Java의 저자인 Joshua Bloch는 Sun Microsystems의 Distinguished Engineer 였고, Java 5.0 명세의 개발과정에도 중요한 역할을 했으며, 현재는 Google의 Chief Java Architect이다. 그는 이 책 이외에 Java Puzzlers도 썼는데, 국내에는 번역되지 않았기 때문에, 아마존에서 주문해서 읽어볼 예정이다.

Effective Java의 내용은 상당히 기본적인 내용에 해당하기 때문에, 실제로 Java 프로그래밍 언어를 사용하면서 깨닫게 되는 문제들을 고려하면 부족한 것이 사실이다. 최근에, 이 책과 같은 스타일의 또다른 책을 찾아보고 있는데, 아직은 없는 것 같다. Effective C++ 시리즈의 Scott Meyers나 Exceptional C++ 시리즈의 Herb Sutter와 같은 훌륭한 저자들이 Java 쪽에는 없는 것일까. Java 쪽의 유명인들이 부족한 것도 아닌데 말이다.

Effective Java Programming Language Guide 더 읽기"

The Java Programming Language, 4th Edition

The Java Programming Language, 4th Edition, by Ken Arnold, James Gosling, David Holmes

Java를 처음 쓰기 시작한 것은 대학교 시절의 숙제나 프로젝트들을 통해서였다. 기억이 나지 않는 과목에서 applet을 만든 (아마도 지금까지도, 앞으로도 applet에 대한 유일한) 경험, Software Engineering 과목에서의 UML로 모델링하고 그것을 구현하는 프로젝트 (도메인은 Parking Lot이었던 걸로 기억한다), Software Project 과목에서의 웹 애플리케이션 경험을 제외하고는 Java를 사용해 본 것이 전무하다.  사실, Java 부문이 워낙 넓고 배울 내용이 방대하기 때문에, 그동안 습득해야할 기술 목록에서 고의적으로 빼놓은 상태였으며, 그동안 Java에 대한 나의 인상은 성능에 대한 미신과 높은 수준의 추상화에 대한 부러움 같은 것들이 섞여있었다고 볼 수 있다. 그러던 중, 작년 2월 경 입사한 회사 ‘첫눈’에서 시작한 프로젝트에 참여하게 되었고, 초기 셋업 과정에서 Java를 사용하기로 결정되었기 때문에 (당시까지는 나는 C++ 프로그래머였다), Java 프로그래머로서의 이력을 시작하게 되었다.

Java에 대한 경험은 있긴 하나 일천한, 어설픈 초보의 상태에서 가장 필요한 것은 언어와 표준 라이브러리에 대한 정확한 지식들이다. Head First Java가 Java 입문서로는 상당히 유명하지만, 내게 ‘입문’이 필요한 상태는 아니라고 생각했고, 입문서는 표준 명세를 대체할만한 엄격한 언어 정의를 포함하고 있지 않기 때문에 선택 목록에서 제외되었고, 역시 유명한 Thinking in Java 4th Edition의 경우에는 Java 5.0을 반영하는 새 판이 나올거라는 소식은 알고 있었으나, 당시에 출판되지 않았던 상태라고 생각하고 있었기 때문에 (지금 보면, 2006년 2월 10일에 출판된 것으로 보인다.), Java 프로그래밍 언어에 관한 가장 권위있는 저자들이 쓴 이 책을 선택하게 되었다.

책의 내용은 크게 문법과 표준 라이브러리의 두가지 부분으로 나뉘는데, Java 5.0의 문법을 대부분 커버하고 있으나, 표준 라이브러리는 핵심적인 부분(Collection과 Stream)에 집중하고 있다. 따라서, Java의 표준 라이브러리에 대해서 책을 통해서 공부하고 싶다면, 다른 책을 보아야할 것이다.

평이하게 쓰여졌으며, 중요한 부분에는 항상 예제를 덧붙여서 설명하고 있어서, 그 문법이나 라이브러리가 어떠한 용법을 가지고 있는지도 알 수 있기 때문에, 어느 정도는 잘 쓰여진 책이라고 평할 수 있을 것 같다. 어느 정도 Java에 대해 알고 있는 상태에서 그 예제를 설명하는 부분을 보면 상당히 지루하기 때문에, skimming할 것을 권장한다. 책의 성격상 상대적으로 덜 중요한 내용들에 대한 설명들도 많이 들어가기 때문에 입문서로서는 이 책은 약간 무거울 것 같다. (살짝 본 것 뿐이지만, 입문서로는 Java 언어의 중요한 사항들만 쉽게 설명하고 있는 Head First Java를 추천한다.) 특히, Stream과 Collection을 다 읽고 난 후 ‘기타’ 성격에 해당하는 라이브러리 부분을 읽을 때는 너무 지루해져서, 다른 책 (Effective Java)을 읽고 나서야, 다시 이 책을 잡을 수 있었다.

한편, 기대했던대로 문법의 각 요소들에 대한 정확한 semantic과 이 책이 커버하고 있는 라이브러리 부분에 대해서는 완전하게 설명하고 있기 때문에, Java 프로그래밍 언어에 대한 지식에 어느 정도 자신감이 생겼다. 당연히 옆에다 두고 Java 문법 레퍼런스로도 사용할 수 있다. 라이브러리를 레퍼런스 해야할 경우에는, (Java 개발자라면 누구나 알다시피) 사실 Sun의 문서화가 너무나 잘되어있으므로, 그것을 참조하는 것이 효율적이고 더 정확할 것이다.

전체적으로 Java의 The C++ Programming Language라고 해도 손색은 없을 것 같다. The Java Programming Language는 serious한 Java 개발자라면 반드시 읽고 넘어가야할 책이다.

The Java Programming Language, 4th Edition 더 읽기"