Software Development

RBSE Spider

Eichmann D. 1994. The RBSE Spider – Balancing Effective Search Against Web Load

1993년은 여러가지 목적을 가진 웹 크롤러들이 출현하기 시작한 해였다. 예를 들어, 최초의 크롤러로 기록되고 있는 World Wide Web Wanderer는 웹의 크기와 성장를 측정하기 위한 용도로 만들어졌다. 한편, RBSE Spider는 웹 검색엔진을 위한 크롤러로 만들어졌으며, 제목과 헤더들만 인덱싱하던 JumpStation과 달리 Full-text 인덱싱하는 검색 엔진의 일부분이었다. (이 때는 아직 검색 엔진과 크롤러의 구분이 모호했다.) 따라서, RBSE Spider는 웹 크롤러를 사용하는 최초의 Full-Text 검색 엔진이라고 볼 수 있다.

크롤러와 인덱서의 분리

크롤러의 architecture면에서 RBSE Spider는 크롤러를 인덱서로부터 분리했다. 크롤러와 인덱서를 분리함으로써 separation of concern의 효과를 얻을 뿐만 아니라, 각각의 컴포넌트를 다른 용도로도 사용할 수 있게 되었다. 하지만, RBSE Spider는 크롤러가 웹 페이지의 발견(discovery)만을 수행할 뿐, 저장하지 않았기 때문에, 크롤러가 발견한 웹 페이지를 받아오기 위해서 인덱서가 다시 방문해야 했다.

Incremental Crawler

크롤러와 인덱서를 분리하지 않을 수 없었던 또다른 이유는 incremental한 크롤러를 만들기 위한 것이었고, 크롤러가 incremental해야만 했던 이유는 robust한 (그러니까 잘 죽지 않는) 크롤러를 만들기가 어려웠기 때문이라고 한다. 그들의 이유는 조금 우습지만, 현재는 웹의 크기가 커지면서 incremental한 크롤러는 필수적인 것이 되었다.

Politeness

당시에 여러 목적의 크롤러들이 늘어나면서 크롤러가 유발하는 웹서버의 로드에 대한 우려가 생기기 시작했고, 이는 Koster의 Guidelines for Robot Writers라는 문서의 제안으로 이어졌다. RBSE Spider는 User-Agent 필드를 이용한 identification 등 Robot exclusion protocol을 따르며, 웹 서버에 로드를 주지 않기 위해 주의하고 있다.

상세한 구현

RBSE Spider의 크롤러 부분은 두개의 프로그램으로 이루어져 있다. 그 하나는 mite라는 프로그램인데, 주어진 URL의 문서를 다운로드한 후, 문서 내에 포함된 URL들을 출력해주는 것이다. 다른 프로그램은 특정 패턴을 가진 URL들에 대한 링크를 mite를 이용해서 추출한 다음 source-target pair들을 Oracle 데이터베이스에 저장하는 역할을 한다. 데이터베이스에는 다시 방문하는 것을 방지하기 위해, 다운로드에 실패하거나 링크가 없는 경우의 로그가 있다. Robot exclusion protocol에 따라 받아서는 안되는 URL들의 패턴들의 리스트도 있다.

페이지의 텍스트를 저장하지 않는 것을 제외하고는 기초적인 크롤러의 기능들을 갖추고 있다고 볼 수 있다.

인덱서 부분은 좀 더 단순하다. 데이터베이스로부터 URL들을 가져온다음 이들을 waisindexer라는 프로그램에 집어넣으면, waisindexer는 다시 mite를 사용해서 URL에 해당하는 페이지를 다운로드한 다음 이들을 인덱싱한다. 인덱싱 부분은 거의 WAIS의 인프라를 활용함으로써 relevance feedback도 활용할 수 있다고 한다.

검색 엔진의 역할

이 paper는 크롤러에 대한 Rationale로서 크롤러가 마치 지도와 같은 역할로, 사용자가 원하는 페이지에 바로 찾아갈 수 있도록 함으로써, 네트워크 트래픽을 감소시킬 수 있다고 주장하고 있다. 이는 당시에 클라이언트 기반의 검색엔진 등이 있었다는 것을 감안하면 옳은 주장이라고 생각되나 사실 현재에는 별로 의미가 없다.

당시에 증가하고 있던, 데이터베이스로부터  동적으로 생성된 페이지들을 인덱싱하지 말아야 한다는 주장은 지금 생각하면 완전히 잘못된 것이다. 당시엔 아마도 각 사이트에서 제대로 인덱싱 되고 있는 페이지들을 따로 인덱싱하는 것은 낭비라고 생각했을 것이다.

한편, 크롤러의 역할로서 내용 유사도(semantic similarity)에 따른 가상적인 이웃 (virtual neighborhoods) 정보를 생성할 수 있다고 얘기하고 있다. 당시에는 아마도 하나의 사이트는 특정한 주제를 취급하는 사이트였을테고 그런 사이트 몇개만 알면 특정 주제에 대한 검색이 가능했겠지만, 그 때까지 그 사이트들 사이의 내용 유사성에 기초한 어떤 서비스는 없었을 것이다. 현재의 일반적인 검색 엔진에서 제공하는 유사 페이지 검색이라든가 버티컬 검색 엔진 등을 생각하면 일리가 있다. 이제는 특정 사이트 몇몇이 특정 주제에 대한 포탈이 되기 힘들고, 검색 엔진의 쿼리를 통해서 또는 버티컬 검색 엔진을 통해서 그러한 서비스를 하고 있다.

RBSE Spider 더 읽기"

ALIWEB

Koster, M. 1994. ALIWEB – Archie-Like Indexing in the WEB.

첫번째 WWW 컨퍼런스인 WWW ’94에 발표된 paper.

1990년 11월에 첫번째 웹서버가 생긴 이후로, 웹의 사용자와 정보의 크기는 점점 늘어나고 있었고, 1993년에는 200개 이상의 웹서버가 있다고 알려지게 되었다. 이렇게 되자, 웹에서 어떤 정보를 찾는 것은 점점 힘들어지게 되었고, 이 문제를 해결하기 위한 여러 노력들이 1993년부터 나타나기 시작했다. 그 결과로 나타난 첫번째 웹 검색엔진이 바로 ALIWEB이었다.

도입 부분에서, 웹의 resource discovery 방법이 진화한 과정을 Browsing – Listing – Searching (Catalogue) – Automatic Collection (from Catalogue) – Automatic Discovery의 단계로 설명하고 있는데 흥미롭다. 현재의 검색엔진에서 Automatic Discovery를 당연시하고 있는 것을 생각하면, 마치 FORTRAN Compiler가 탄생했을 때, 이를 Automatic Programming이라고 부르던 느낌이랄까.

한편, Archie란 여러 Anonymous FTP 사이트가 가지고 있는 파일들을 인덱싱하여 검색할 수 있도록 해주는 Anonymous FTP의 검색 엔진이었다. Archie의 동작 방식은 각 Anonymous FTP 사이트에서 파일 리스팅에 해당하는 파일을 받아서 사용자가 검색 가능하도록 해주는 것이었다. 이 paper에서 Archie-Like라고 부르는 것도 그러한 동작 방식을 사용했다는 것을 의미한다.

ALIWEB의 동작 방식은 다음과 같다.

  1. 웹사이트 관리자가 정해진 포맷으로 각 사이트에 대한 인덱스 파일을 만들고 이를 ALIWEB의 웹 인터페이스를 통해 등록한다.
  2. ALIWEB은 등록된 웹사이트들의 인덱스 파일들을 자동으로 받아와서 이를 검색 가능하도록 인덱싱한다.
  3. 검색을 원하는 사용자는 오늘날의 검색엔진 인터페이스와 거의 같은 모습의 ALIWEB의 웹 인터페이스를 통해서 쿼리를 보내고 결과를 볼 수 있다.

이 paper에서 밝히고 있는 ALIWEB의 문제점은 역시

  • 사이트별 인덱스 생성 과정의 어려움, 그리고
  • scalability

이다.

사실, 사이트 관리자에 의한 사이트별 인덱스 생성은, 사이트 관리자가 그 사이트에서 어떤 것이 인덱싱 되어야하는가를 가장 잘 알고 있을 것이라는 합리적인 의도를 가지고 있고, Koster는 이러한 방식이 적어도 medium term에서는 잘 동작할 것이라고 예상했지만, 실제로 이는 실패했다. 물론, 사이트 관리자에 의한 사이트별 인덱스 생성이라는 개념은 현재에도 Google Sitemap과 같은 형태로 존재하지만, 적어도 이러한 메커니즘이 Automatic Discovery의 주요한 역할을 맡고 있는 것은 아니다.

어쩌면, 텍스트를 위주로 한 현재의 웹에 대한 Automatic Discovery가 정점에 이르면, 오히려 장기적인 관점에서, 사이트 관리자에 의한 사이트별 인덱스 생성이라는 개념이 유효해질 가능성도 있다. Semantic Web이나 Web Services와 같은 웹을 구조화된 형태로 만드려는 노력이 어떤 방향으로 진행될지는 아직 미지수다.

Scalability의 문제는 프로토타입의 성격을 지닌 ALIWEB에서 해결되리라고 기대할만한 문제는 아니지만, 사이트별 인덱스를 사이트 내의 상위 레벨 서비스에 대해서만 생성한다든가, 특정 분류나 도메인에 한정된  ALIWEB 사이트를 만들어서 해결한다는 생각은 현재 시점에서 바라볼 때는 실망스럽다.

현재의 웹을 바라보면, 상위 레벨 서비스만 검색되면 되는 것이 아니라, 하위 레벨 페이지들이 실제로 사용자들에게 가장 흥미로운 내용을 포함하고 있고, 검색의 결과로서 적합한 경우도 많다. 한편, 특정 분류나 도메인에 한정된 검색도 물론 유용하지만, 이것이 Scalability 부족의 대안으로 나오는 것은 현재 웹의 크기를 고려할 때 바람직하지 못하다.

ALIWEB 더 읽기"

Bad Smell of Code: Too many top-level classes in a file of Java program

소스 파일(이하, 파일)도 모듈의 일종이다.

어떤 프로그래밍 언어에서는 파일을 언어 자체가 정의하는 개념(예를 들어, C++의 ‘컴파일 단위’)으로 본다. 어떤 프레임워크에서는 특정한 파일은 어떤 특별한 역할을 하는 코드의 집합으로 본다. (예를 들어, RoR의 파일 구성) 마찬가지로, 어떤 프로젝트에서는 정해진 독특한 규칙에 따라 특정 파일에 특별한 역할을 부여할 수 있다.

이런 면에서, 소스 파일의 구성(File Organization)은 소프트웨어 품질에 영향을 미치는 디자인 결정 중의 하나다.

파일이라는 OS라는 낡고 거추장스러운 곳으로부터 나온 (어떤 면에서) 언어 외적인 요소를, 고매하고 청명한 정신으로부터 태어난 자신의 코드와 결부시키는 것이 불편하게 느껴질지도 모르겠다. 언어 자체가 파일 개념에 의존하지 않는다면, 한 프로젝트의 모든 코드가 한 파일에 들어있든, 1억개의 파일에 들어있든 무슨 상관이 있겠는가.

하지만, 아직은 프로그래밍 세계는 파일이라는 요소에 의존하는 편이다. 그럴 듯한 IDE를 사용하지 않는 프로그래머들도 너무나 많다. (당신이 vim을 IDE라고 주장하더라도 notepad는 어떤가?!) 그들에게 파일은 Readability에 직접 영향을 미치는 단위가 된다. 너무 긴 파일은 너무 긴 클래스나 너무 긴 함수의 문제점과 동일한 문제를 유발한다. 읽기 힘들다. 파악하기 힘들다. 소위, 스크롤의 압박이란거다.

한편, 현재의 IDE들은 언어가 파일에 의존하지만 않는다면, 파일이라는 요소를 배제할 수 있는 포텐셜을 가지고 있으나, 단순하게도, 그렇게 하지 않는다! 그나마 우리를 행복하게 해주기 위해 IDE를 제작하는 분들에게 뭔가를 추궁하지는 않겠다. 아직은 우리는 파일이라는 개념에 너무나 익숙하다. 정말로 파일이 싫은가 하면 그렇지도 않단 말이다.

어쨌든, 파일은 모듈이고 다시 말해, decomposition의 단위다. 이것은 파일이라는 모듈에도 높은 cohesion과 낮은 coupling이 요구된다는 것이다.

Java와 같은 언어에서는 (Top-level) 클래스 각각은 그 클래스의 이름을 가진 독립된 하나의 파일을 점유하는 것이 일종의 컨벤션이다.

얼마전에 코드 리뷰를 한 적이 있는데, Top-level 클래스 5-7개 정도가 하나의 파일에 들어가 있는 경우를 발견했다. 이것을 발견하자 내 직관은 뭔가 잘못된 것이라는 알려주었다. 도대체 이 클래스의 범위는 어디까지인거지? 왜 이렇게 길어? 이 클래스로 이동하려면? (난 이클립스를 사용하고 있고, 코드 navigation 기능은 숙지하고 있으니, 친절하게 알려주지 않아도 된다.) 혼자만의 생각이지만, 다른 개발자가 이런 코드를 보더라도 분명 나와 비슷한 느낌을 받으리라고 생각한다. (무슨 소리?)

자, 그럼 직관이 아니라, 뭔가 납득할만한 설명을 필요한 것 같다. 대체 왜 그  클래스들은 한 파일에 있어야만 할까? 클래스들을 자세히 들여다보면, 1) A와 B는 Use 관계에 있으나, 그 역할은 충분히 다르고, 특히 A와 B는 그 파일 밖에 있는 클래스 P와 Q에 의해서 사용되고 있었다. 2) D는 C의 구현이 되어야할 부분을 다시 모듈화한 것이었다.

1) A와 B는 충분하게 cohesive하지 않을 뿐만 아니라 파일 외부의 클래스에 의해서도 사용되므로 다른 클래스들과 동등한 자격으로 – 즉, 파일로 노출될 필요가 있다. 2) D는 C의 구현에 해당하고 외부로 노출될 필요가 없으므로 Top-level 클래스가 될 필요가 없다. 즉, D는 C의 임베디드 클래스가 되면 훌륭하다.

이런 식으로 리팩토링을 하고 나니, 하나의 파일에는 하나의 Top-level 클래스만이 남아있었다.

물론 하나의 파일에 무조건 하나의 Top-level 클래스만 써야만 하는 법은 없을 것이다. 내가 알지 못하는 문맥에서의 필요성이 있을 수도 있고, Top-level 클래스를 하나의 파일에 모아두는 것에 페티쉬를 가진 경우도 있을 것이다. 물론, 파일 개념의 철폐를 부르짖는 해방주의자도 빼놓을 수는 없다. 하지만, 대부분의 경우에는 별 생각 없이 한 파일에는 하나의 Top-level 클래스를 두는 것으로 생각하면 별 탈 없을 것이고, 어쩌면 칭찬을 들을지도 모른다.

오늘의 Rule of Thumb: Java 프로그래밍에 있어서 하나의 파일에는 하나의 Top-level class를 두자. 땅땅땅.

Bad Smell of Code: Too many top-level classes in a file of Java program 더 읽기"

Unnecessary Software Complexity

소프트웨어가 해결하려는 문제가 복잡하다면 그 해결책 즉, 소프트웨어도 복잡해진다. 즉, 소프트웨어의 복잡성은 부정적인 효과를 가지지만, 항상 잘못된 것이고 거절할 수 있는 것은 아니다.

따라서, 소프트웨어의 복잡성 문제를 해결하려고 할 때는 불필요한 소프트웨어의 복잡성(Unnecessary Software Complexity)라는 개념을 가지고 얘기해야한다.

한편, 소프트웨어의 복잡성이 문제의 복잡성에 의존하기 때문에, 문제가 달라지면 불필요한 소프트웨어의 복잡성도 달라진다. 다르게 말하면, 문제가 달라지면, 소프트웨어를 평가하는 기준도 달라질 수 있다는 것이다.

이야기를 하나 해보자. 어떤 소프트웨어 팀은 실제로 많은 문제가 있다고 생각해서, 또는 의욕적이어서 많은 문제를 해결하는 소프트웨어를 만든다. 시간이 지나고, 그런 문제를 해결할 필요가 없어졌고, 사람들은 그런 사실을 잊어버렸다고 해보자. 남아있는 소프트웨어는 그 시점에서 불필요한 소프트웨어의 복잡성이 높은 소프트웨어일테고, 그 소프트웨어의 개발자는 비난을 받는다. 이것은 공평하지 못하다. 소프트웨어 엔지니어링에서 얘기하는 것처럼 문제의 합의에 대한 계약서라도 써놓는 것이 이러한 문제를 해결할까? 그렇지는 않다고 생각한다. 사람들은 ‘문제가 변화한다’는 사실에 대한 명확한 인식을 가지고 있어야할 뿐만 아니라 ‘문제를 알고있다’라는 생각에 대해 좀 더 보수적일 필요가 있다.

문제가 언제 변화하냐고? 문제는 항상 변화한다.

Unnecessary Software Complexity 더 읽기"

Software Complexity

생물학자 줄리언 헉슬리(Julian Huxley)는 1912년에 복잡성을 ‘부분들의 이질성’이라고 정의했는데, 이는 일종의 기능적 불가분성을 뜻한 것이었다. 만들어진 신, 리처드 도킨스

어떤 일을 할 때는 항상 그 일에 맞는 ‘생각의 틀’이 필요하다. 최근 4개월 가량 소프트웨어 개발에서 완전히 손을 떼고 있었고, 그 생각의 틀을 조금씩 회복하는 중이다. 그러던 중 읽은 복잡성에 관한 정의는 내가 지금 겪고 있는 소프트웨어 복잡성의 문제를 떠올리게 해주었다.

복잡성에 관한 얘기를 할 때, 소프트웨어의 복잡성은 흔히 생물의 복잡성과 함께 취급되곤 한다. 물론, 생물의 복잡성은 소프트웨어의 복잡성에 비교할 수 없을 정도지만, 소프트웨어의 복잡성에 대한 일반인들의 관용은 생물의 복잡성에 대한 관용보다 높지는 않을 듯하다. 하지만, 소프트웨어 엔지니어 입장에서 복잡한 소프트웨어를 바라보았을 때 ‘손을 댈 수도 없다’는 그 심정은 생물을 다루는 연구자나 중환자의 몸을 다루는 의사의 심정과 크게 다르지 않으리라고 생각한다.

소프트웨어 복잡성의 문제는 다른 과학이나 공학과 마찬가지로, 환원, 특히 기능적 환원으로 해결하는 것이 기본이다. 복잡한 소프트웨어는 하나의 부분을 이해하거나 고치기 위해서는 그 부분과 기능적 불가분의 관계에 있는 여러 부분들을 동시에 이해하고 고쳐야한다. 환원 과정을 통해 프로그래머가 신경써야할 거리를 분리함(separation of concern)으로써, 프로그래머는 한번에 하나의 부분만을 다른 부분과 관계없이 신경쓸 수 있다. 소프트웨어에 있어서는 이러한 과정을 decomposition또는 모듈화(modulization)이라고 부른다. 소프트웨어가 만들어지고 나서 수행하는 decomposition을 특히 리팩토링(refactoring)이라고 부르기도 한다.

생물과 소프트웨어의 또다른 유사성은 바로 계속 변화한다는 것이다. 그리고, 생물이 진화할 수록 그 복잡성이 높아지는 것처럼 (진화의 방향이 항상 복잡도가 높아지는 방향인 것은 아닐테지만, 적어도 현재까지의 생물사에서는 그러했던 것 같다.) 소프트웨어도 성장할될 때마다 복잡성이 높아진다. 생물의 진화는 인위적인 것이 아니므로 복잡성을 ‘해결’할 필요가 없지만, 소프트웨어의 변화에는 (아직은) 인간의 지적인 능력이 필수적이므로, 복잡성은 소프트웨어가 더이상 성장하는 것을 막는 요인이 되고, 심지어는 소프트웨어의 생명을 짧게 만드는 요인이 되기도 한다. 따라서, 소프트웨어가 오랫동안 성장하기 위해서는 복잡성을 일정 수준 이하로 항상 유지시켜야 하고, 이를 위해서는  decomposition에 더욱 많은 시간을 들이고 자주 해야한다.

소프트웨어 복잡성을 해결하는 과정 못지않게 그것을 인지하는 과정도 중요하다. 복잡성을 인지하지 못하면, 해결하려는 노력을 할 수도 없다. 또한, 복잡성을 인지하지 않고 decomposition을 하려는 노력은 무의미하거나 적어도 비용-효율적이지 못할 수 있다.

소프트웨어 복잡성은 소프트웨어를 만드는 과정 뿐만 아니라 소프트웨어의 결함들을 해결하는 과정 또는 그 원인을 분석하는 과정에서도 인지될 수 있다. 그러한 과정에서 프로그래머가 ‘너무 복잡하다’고 느끼는 것은 복잡성의 첫번째 징표다.

  • 결함 해결 과정에서 너무 많은 부분들이 결함 가능성을 안고 있고 어느 부분에 결함이 있는지 가늠하기 어렵다면 그 소프트웨어는 복잡한 것이다.
  • 결함의 원인이 한 부분만의 결함이 아니라 많은 부분들이 서로 영향을 주면서 발생한 결함이라면 그 소프트웨어는 복잡한 것이다.

복잡성을 훌륭하게 해결한 시스템들을 보면 항상 기능적 분화 뿐만 아니라 발생 가능한 결함 자체를 격리한다. 복잡한 시스템들은 그 부분들간에 너무 많은 기능을 서로 의존하고 있을 뿐만 아니라, 설령 기능은 분리되어있다고 하더라도, 발생가능한 결함들은 분리되어있지 않다.

가만히 생각해보면 소프트웨어 디자인이 해결하고자 하는 주요 문제는 결국 복잡성의 해결이다. 성능과 같은 비기능적 요소들은 제약조건에 불과하다. 이를테면, ‘성능 요건을 xxx만큼 만족시키면서 복잡성을 yyy만큼 해결하는 것’의 문제란 것이다. 대규모 소프트웨어를 높은 수준에서 바라보는 사람은 복잡성에 관한 생각이 반드시 그의 ‘생각의 틀’에 담겨있어야한다.

Software Complexity 더 읽기"

Web Crawler

web_crawler_20070223.pdf

올해 초에 신입 팀원들을 대상으로 발표한 자료로, Web Crawler의 기본적인 설계와 구현시 신경써야할 기초적인 사항들을 언급하고 있습니다. 대단한 내용이 있는 것은 아니고 단순히 Mining the Web의 Web Crawler chapter를 정리한 내용입니다.

Web Crawler 개발에 있어서 가장 중요한 것은 무엇이냐고 제게 묻는다면 저는 이 발표자료의 Closing에 적혀있는 한 줄로 대신 답하겠습니다.

Understand status quo of Web

사실 어느 정도 숙련된 개발자라면 Web Crawler의 기초적인 기능들은 대단히 단순하기 때문에 별로 어렵지 않다고 느끼실 겁니다. 저도 물론 처음엔 그렇게 생각했구요. 하지만 현재는 약간 생각이 달라졌습니다. 훌륭한 Web Crawler를 만들 수 있는 능력은 바로 웹의 현재 상태에 관한 지식을 얼마나 가지고 있는가 또는 얻을 수 있는가에 달려있다고 생각합니다. 이 발표자료를 쓸 때보다 이러한 생각이 더욱 강하게 드는 요즈음입니다.

Web Crawler 더 읽기"

재설계

사람들이 미술을 처음 시작하면 잘못된 부분을 다시 그리는 것을 꺼려한다. 일단 그 정도로 그린 것에 만족하며, 다시 손을 대다가 혹시 더 망치지나 않을까 염려한다. 그리하여 방금 그린 것이 그렇게 나쁘지는 않다고, 어쩌면 최선의 작품일지도 모른다고 생각하여 스스로를 설득한다. 폴 그레이엄, 해커와 화가

재설계 더 읽기"

Naming Objects

이 글은 2004년 12월 14일, 네오위즈에서 일하던 시절에, 팀 내부에 공유한 메일입니다. (영어 단어나 어법 오류 등은 수정했습니다.) 현재의 제 생각과는 약간 다른 부분도 있지만,

  • 문제 영역의 언어로 이름짓기
  • 적절한 비유의 사용
  • 최소화 인터페이스

등은 저 뿐만 아니라 많은 사람들이 동의하는 프로그래밍의 원칙들입니다.

이름 짓기

바쁜 일정과 많은 업무로 인해서 팀 내에서는 ‘이름 짓기’가 중요하게 여겨지지 않고 있지만, 저는 이름 짓기에 대해서 강한 의견을 가지고 있는 사람입니다. ;-)

객체와 메서드의 이름을 지을 때는 그것이 하는 역할에 맞게 이름을 짓는 것이 좋습니다. 당연히 이 역할은 객체가 해결하려는 문제의 도메인 안에서, 그 도메인의 언어로 정의됩니다. 따라서, 객체와 메서드의 이름은 문제 도메인의 언어로 기술하는 것이 가장 바람직합니다. (일반화된 이름은 원래 문제 도메인의 이름이 아닌 경우가 있지만, ‘일반화’ 역시 일종의 해결하려는 문제입니다)

예를 들어, 6살짜리 어린이가 가게에서 사탕을 사먹는 문제에 대해서 해결 방법을 기술하려면, 6살짜리 어린이가 이해할 수 있어야 하고, 사탕이라는 단어가 거의 반드시 들어가야겠죠.  또한, 사탕을 사 먹어야 하는데, 다른 도메인의 언어, 즉 음표라든가, 양자역학 같은 단어가 나오면 곤란합니다.

junkmemocheck를 희생양으로 삼아 예를 들어보죠. (junkmemocheck는 쪽지를 너무 많이 보내는 사람을 차단하기 위해 사용자별로 최근에 쪽지를 보낸 수를 세어주는 컴포넌트입니다.)

메서드 이름 짓기

제가 이해한 바로는, junkmemocheck(이하, 줄여서 JMC)에 관련된 시나리오는 다음과 같습니다.

  1. 사용자 A가 쪽지를 보낼 때, A가 쪽지를 보냈음을 JMC에 알려줍니다.
  2. 사용자 A가 미리 정해진 시간 내에, 쪽지를 보낸 수를 얻거나, 너무 많이 보냈다는 정보를 얻어옵니다.

이를 좀 더 단순한 말로 적어보죠.

  1. 특정 사용자가 쪽지를 보냈다.
  2. 특정 사용자가 최근에 쪽지를 보낸 수를 주세요.

메서드 이름으로 바꿔보죠.

쪽지는 Memo라는 용어로 대치하고, ‘미리 정해진 시간 내에’라는 requirement에 대해서 ‘Recently’라는 단어를 도입했습니다.

  1. sentMemo(user)
  2. getCountOfMemoSentRecently(user)

좀 더 줄이면,

  1. sentMemo(user)
  2. getRecentMemoSent(user) 또는 getMemoSent(user)

클래스 이름 짓기

이러한 역할에 비추어보면, junkmemocheck는 (Sent)MemoCounter 같은 이름이 더욱 적당하지 않을까 싶습니다. (사실 ‘정해진 시간 내’라는 제약 조건이 있어서, “Counter”라는 비유가 약간 부적절해 보이므로, 좀 더 나은 비유가 있으면 좋을 듯 하네요.)

MemoCounter
+ increaseCount(user)
+ getRecentCount(user)

일반화된 이름 짓기

나중에 다른 요구사항이 생겨서, 쪽지만이 아니라 다른 곳에도 이 어플리케이션이 사용되기도 결정되었다고 합시다. 우리는 “Counter”라는 비유를 이미 사용했으므로, 이를 그대로 사용하면 될 것 같습니다. 사용자 대신 슬롯(slot)이라는 개념을 사용합니다.

Counter 클래스의 역할(Role)은 다음과 같습니다.

  1. k 슬롯의 카운터를 하나 증가시킨다.
  2. 일정 시간 내에 k 슬롯에 증가된 카운터 값을 가져온다.

이를 바탕으로 다음과 같이 이름을 지을 수 있겠네요.

Counter
+ increaseCount(k)
+ getRecentCount(k)

개선된 디자인

사실 Counter 안에 k라는 슬롯을 찾는 것보다는, k라는 이름의 Counter가 있는 것이 더욱 직관적입니다. Counter의 메서드에 k를 명시하기 보다는, k에 해당하는 Counter 객체가 있는 것이 낫겠죠.

kCounter = CounterCollection.getCounter(k)
kCounter.increaseCount()
kCounter.getRecentCount()

좀 더 단순하게 만들어볼까요? CounterCollection에 Count가 있는 것은 당연하고, Count를 increase하는 것은 당연하므로,

kCounter = CounterCollection.get(k)
kCounter.increase()
kCounter.getRecent()

성능 고려하기

MemoCounter 든, Counter든 역할에 따라 두개의 operation으로 분리했습니다. 하지만, 내부적인 구현에 따르면, 각각이 server로 메시지를 따로 보내야 한다면, 그리고 거의 항상 두 operation이 같이 쓰인다면, 이러한 분리는 비효율적입니다. 한번에 물어볼 수 있는 방법이 필요합니다.

Counter
+increase(k)
+getRecent(k)
+increaseAndGetRecent(k)

가능한 한 원래의 인터페이스가 직관적이므로 유지하는 것이 좋다고 생각하지만, 이를 유지하면서 성능을 개선할 수 있는 더 나은 방법이 있을지도 모르겠습니다.

인터페이스 최소화

거의 항상 단순한 인터페이스가 좋습니다. 필요없는 인터페이스는 사용자를 혼란스럽게 합니다. 예를 들어, increaseAndGet(k)만이 사용되고, 다른 메서드는 사용될 가능성이 별로 없다면,

Counter
+increaseAndGetRecent(k)

만으로도 충분합니다.

결론

저는 메서드 이름부터 새로 짓기 시작했지만, 좀 더 일반적인 방법은,

  1. 문제 해결에 필요한 객체의 이름과 역할을 정합니다. 객체를 정할 때는 문제를 해결하기 위한 비유를 생각해보면 좀 더 쉽게 지을 수 있습니다. 문제는 얼마나 적절한 비유인가인데, 어떤 비유가 객체의 역할 즉, 문제의 해결방법에 얼마나 근접하느냐를 따져야할 것입니다. 객체의 이름은 항상 문제 도메인의 언어나, 비유의 이름으로 짓습니다.
  2. 객체 역할에 따라 메서드를 추가합니다. 하나의 동작에 하나의 메서드를 추가합니다. 메서드 이름 역시 항상 문제 도메인의 언어로 짓습니다.
  3. 이 후에도 얼마든지 성능 고려나 인터페이스 최소화를 할 수 있습니다.

Naming Objects 더 읽기"

Dynamics of Software Development

Dynamics of Software Development by Jim McCarthy

요즈음 널리 유행하는 방법론들처럼 체계화 되어있는 것은 아니지만, 이 책은 소프트웨어 개발의 여러 부분들에 걸친 조언을 담고 있는 책이다. 그 범위는 소프트웨어 개발에서 어떤 것이 바람직한 리더쉽인가, 어떻게 팀을 만들 것인가, 고객이나 비즈니스 부문 등의 팀 외부와 어떻게 소통할 것인가, 경쟁 시장에서 어떻게 포지셔닝할 것인가,빌드와 출시, 개발 일정과 주기는 어떻게 할 것인가, 사람은 어떻게 뽑을 것인가와 같이 소프트웨어 개발 팀에 관련된 거의 모든 부문을 다루고 있다고 해도 과언이 아니다.

저자는 소프트웨어 개발팀을 생태계와 같은 것으로 보고 있다. 팀원들은 팀장의 지시에만 따르고, 미리 정해진 역할을 수행하는 것이 아니라, 팀장은 팀원에게 권한을 위임해야하며 (지식노동에 있어서 이것은 이미 황금률이다.) 팀원들은 자신의 능력이 다른 팀원들과 조화를 이루도록 역할을 진화시켜 나간다. 이러한 관점은 소프트웨어가 지식 노동의 산출물이기 때문에 무엇보다도 팀원들부터 아이디어가 자유롭게 나오고 그 중에서 최상의 아이디어가 선택되어야한다는 이유에 비롯한다.

애자일 소프트웨어 개발 프랙티스 (이를테면 XP와 같은)에서와 비슷한 주장들을 1995년에 쓰여진 이 책에서 하고 있는 것도 재미있는 점이다. (참고로, C3 프로젝트는 1996년에 시작되었고, Kent Beck이 유명한 Extreme Programming Explained를 내놓은 것은 1997년의 일이다.) 신속하게 개발주기를 반복한다든가, 알려진 상태를 유지하는 것, 고객과의 관계의 강조 등에서 그러한 것들을 엿볼 수 있다. Scrum과 같은 방법론은 1993년에 적용되기 시작했고, Iterative and Incremental Development: A Brief History와 같은 문서를 보면, 1980년대에 이미 점진적인 개발 방법이 소프트웨어 개발계에 떠들썩하게 떠오르고 있음을 할 수 있다.

설령 책이 제시하는 세부적인 방법들에 동의를 하지 않는다고 하더라도 한번이라도 팀에서 소프트웨어 개발을 해본 경험이 있다면, 지난 날의 경험을 회상하며 이 책의 조언에 대해 깊히 생각해보는 것은 매우 유익한 일일 것이다. 다만, 저자는 인문학적인 배경이 있는지 몰라도 문학이나 철학에 기원한 개념들을 자주 빌려쓰고 있을 뿐만 아니라 전체적인 문체도 간명한 편은 아니어서, 읽기가 쉽지 않은 면이 있다. 단지 번역의 탓만은 아닌 듯 보인다. 반대로 이러한 점 때문에, 한문장 한문장을 곱씹어볼 경우 여러가지 경우에 여러 의미로 다가올 수도 있을 것이고, 비단 저자의 조언을 곧이곧대로 받아들이기보다는 풍부한 고민의 원천이 될 수 있는 면도 있을 것이다.

Dynamics of Software Development 더 읽기"