로버트 C 마틴(이하 엉클 밥)의 “객체지향” 개발의 필독서라고 불리우는(..) clean code를 읽고 있습니다.

오래전에 사두고 첫 머리말이 너무 재미 없어서(..) 접어두었다가 신년 기획겸 해서 읽고 있습니다.

역시 대작은 첫 머리말이 너무 지루해요. 왕좌의 게임처럼 말이죠..

처음 읽으시는 분들이라면 인내심을 가지고 함수(3장)부분까지는 읽어보시는 것을 추천합니다.

저는 그 후에 책을 스키밍하시면 대략적인 책의 구조를 파악하니 재미가 붙어서 읽게 되었으니 참고하시면 좋으실 것 같습니다.

서장 들어가면서

clean code를 작성하는 것은 매우 어려우며 이론만으로는 습득할 수 없고, 이론을 바탕으로하는 실습만이 습득방법임을 자전거 타기와 물리학 실험과 같다고 비교하며 설명한다.

이외에 이 책은 3부분으로 나뉘어 있으며 2번째 부분이 사례연구인데 이부분의 습득이 없다면 이책의 이론만 공부한 샘이 된다고 설명한다.

1장 깨끗한 코드

1장에서는 우선 나쁜 코드의 대한 정의와 부작용 그리고 책의 재목이자 이 책에서 주장하는바인 clean code란 무엇인가에 대해서 여러 유명한 프로그래머의 말을 인용함과 동시에 왜 그렇게 이야기했는지를 설명한다.

나쁜코드

나쁜 코드는 좋지 못한 코드의 전부라고 생각한다(필자 의견). 저자는 나쁜 코드는 소프트웨어의 유지보수를 어렵게하여 생산성을 저하시키고, 결국 기술부채가 쌓여 그 기술이 망하게 되는 결과를 초래하고 재설계라는 결론에 오게 된다고 설명한다. 필자는 원초적 난제인 “나쁜 코드가 업무 속도를 늦추지만, 기한을 맞추기 위해서는 나쁜 코드를 양산해야한다고 느낀다”라는 것은 틀린 생각이라고 말하며 기한을 맞추는 유일한 방법은 코드를 짜는 개발자가 최대한 깨끗한 코드를 유지하는 것을 습관화해야 한다고 말한다.

좋은코드란?

프로그래밍으로 저명한 사람들의 의견을 듣고 그 말을 해석해본다.

비야네 스트롭스트룹(C++ 창시자)

나는 우아하고 효율적인 코드를 좋아한다. 논리가 간단해야 버그가 숨어들지 못한다. 의존성을 최대한 줄여야 유지보수가 쉬워진다. 오류는 명백한 전략에 의거해 철저히 처리한다. 성능을 최적으로 유지하야 사람들이 원칙 없는 최적화로 코드를 망치려는 유혹에 빠지지 않는다. 깨끗한 코드는 한가지를 재대로 한다.

  • 깨끗한 코드 === 우아한 코드 === 보기에 즐거운 코드
  • 효율적이지 못한 코드는 결국 다른사람이 손을 보게하도록 “유혹”한다. (깨진 유리창 이론)
  • 깨끗한 코드는 한가지를 잘한다.

그래디 부치(Object Oriented Analysis and Design with Application 저자)

깨긋한 코드는 단순하고 직접적이다. 깨끗한 코드는 잘 쓴 문장 처럼 읽힌다. 꺠긋한 코드는 결코 설계자의 의도를 숨기지 않는다. 오히려 명쾌한 추상화와 단순한 제어문으로 가득하다.

  • 가독성을 강조
  • 잘 짜여진 코드는 마치 잘 쓰여진 소설책처럼 잘 읽힌다.

큰 데이브 토마스(OTI의 창립자이자 이클립스 전략의 대부)

깨끗한 코드는 작성자가 아닌 사람도 읽기 쉽고 고치기 쉽다. 단위 테스트 케이스와 인수 테스트 케이스가 존재한다. 깨긋한 코드에는 의미 있는 이름이 붙는다. 특정 목적을 달성하는 방법은(여러가지가 아니라) 하나만 제공한다. 의존성은 최소이며 각 의존성을 명확히 정의한다. API는 명확하며 최소로 줄였다. 언어에 따라 필요한 모든 정보를 코드만으로 명확히 표현할 수 없기에 코드는 문학적으로 표현해야 마땅하다.

  • 가독성, 특히 고치기 쉬운 코드에 강조.
  • 깨끗한 코드 === 태스트 케이스가 존재하는 코드 === 검증된 코드
  • 코드는 작을수록 좋다.
  • 인간이 읽기 쉬운 코드를 작성하자.

마이클 페더스(Working Effectively with Legacy Code의 저자)

꺠긋한 코드의 특징은 많지만 그 중에서도 모두를 아우르는 특징이 하나 있다. 깨긋한 코드는 언제나 누군가 주의 깊게 짰다는 느낌을 준다. 고치려고 살펴봐도 딱히 손댈 곳이 없다. 작성자가 이미 모든 사항을 고려 했으므로. 고칠 궁리를 하다보면 언제나 제자리로 돌아온다. 그리고는 누군가 넘겨준 코드, 누군가 주의 깊게 짜놓은 작품에 감사를 느낀다.

  • 클린한 코드는 주의 깊게 짜여진 코드이다. 이 책의 부재를 붙인다면 “코드를 주의깊게 짜는 방법”이라고 할수 있다.

론 제프리스(Extreme Programming Installed와 Extreme Programming Adventure in C#의 저자:특징 모든 플랫폼에서 모든 언어로 코드를 구현해본사람.)

최근 들어 나는 켄드 백이 제안한 단순한 코드의 규칙으로 구현을 시작한다.(그리고 같은 규칙으로 구현을 거의 끝낸다.) 중요한 순으로 나열하자면 간단한 코드는

  • 모든 테스트를 통과해야한다.
  • 중복이 없다.
  • 시스템 내 모든 설계 아이디어를 표현한다.
  • 클래스, 메서드, 함수 등을 최대한 줄인다.

물론 나는 주로 중복에 집중한다. 같은 작업을 여러 차례 반복한다면 코드가 아이디어를 제대로 표현하지 못한다는 증거다. 나는 문제의 아이디어를 찾아내 좀더 명확하게 표현하려 애쓴다.

내게 있어 표현력은 의미 있는 이름을 포함한다. 보통 나는 확정하기 전에 이름을 여러 차례 바꾼다. 이클립스와 같은 최신 개발도구는 이름을 바꾸기가 상당히 쉽다. 그래서 별 고충 없이 이름을 바꾼다. 하지만 표현력은 이름에만 국한되지 않는다. 나는 여러 기능을 수행하는 객체나 메서드도 찾는다. 객체가 여러 기능을 수행한다면 여러 객체로 나눈다. 메서드가 여러 기능을 수행한다면 매서드 추출 리팩터링 기법을 적용해 기능을 명확히 기술하는 메서드 하나와 기능을 실제로 수행하는 메서드 여러 개로 나눈다.

중복과 표현력만 신경 써도 (내가 생각하는) 깨긋한 코드라는 목표에 성큼 다가선다. 지저분한 코드를 손볼 때 이 두 가지만 고려해도 코드가 크게 나아진다. 하지만 나는 한 가지를 더 고려한다. 이는 설명하기 조금 까다롭다.

오랜 경험 끝에 나는 모든 프로그램이 아주 유사한 요소로 이뤄진다는 사실을 깨달았다. 한 가지 예가 ‘집합에서 항목 찾기’다. 직원 정보가 저장된 데이터배이스든, 키/밸류 쌍이 저장된 해시 맵이던, 여러 값을 모아놓은 배열이든, 프로그램을 짜다 보면 어떤 집합에서 특정 항목을 찾아낼 필요가 자주 생긴다. 이런 상황이 발생하면 나는 추상 클래스나 추상 메서드를 만들어 실제 구현을 감싼다. 그러면 여러가지 장점이 생긴다.

이제 실제 기능은 아주 간단한 방식으로, 예를 들어 해시 맵으로, 구현해도 괜찮다. 다른 코드는 추상 클래스나 추상 매서드가 제공하는 기능을 사용하므로 실제 구현은 언제든지 바꿔도 괜찮다. 지금은 간단하게 재빨리 구현했다가 나중에 필요할 때 바꾸면 된다.

게다가 집합을 추상화하면 ‘진짜’문제에 신경 쓸 여유가 생긴다. 간단한 찾기 기능이 필요한데 온갖 집합 기능을 구현하느라 시간과 노력을 낭비할 필요가 없어진다.

중복 줄이기,표현력 높이기,초반부터 간단한 추상화 고려하기. 내게는 이 세가지가 깨끗한 코드를 만드는 비결이다.

  • 중복을 피하라. 한기능만 수행하라. 제대로 표현하라. 작게 추상화하라.

워드 커닝햄(위키 창시자, eXtreme Programming[xp] 공동 창시자, smalltalk와 객체지향의 정신적 지도자.)

코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다. 코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드라 불러도 되겠다.

  • 깨긋한 코드는 읽으면서 놀라는 일이 없어야 한다. 코드를 독해 해야할 일이 없어야 한다.

저자(로버트 C 마틴)

이 책은 오브젝트 멘토 진영이 생각하는 깨끗한 코드를 설명한다.

  • 개발자는 저자이다.
    • 개발자는 코드라는 글을 쓰는 저자이다. 글을 쓴다면 그 글을 읽는 “독자”가 존재한다.
    • 개발을 하면서 우리는 새로운 코드를 작성하는 시간보다 기존코드를 읽는 시간이 훨씬 더 많다. (자신이 코딩하는 화면을 녹화해서 돌려본다면 이해할 것이다.)
  • 보이스카우트 규칙

    “캠프장은 처음 왔을 때보다 더 깨긋하게 해놓고 떠나라.”

    • git 커밋 전이나 소스를 저장하고 자리를 뜨기전에라도 소스를 한번 더 살펴보는 습관을 들이자.
  • 프리퀄과 원칙
    • 이 책은 저자(엉클 밥)의 책 Agile Software Development: Principles, Patterns, and Practices(PPP)의 프리퀄이다.
    • 또한 SOLID 규칙을 산발 적으로 거론한다.
  • 결론
    • 예술의 책을 읽는다고 좋은 예술가가 되지 않는다. 연습하라!

2장 의미 있는 이름 (변수)

이 장에서 주로 전달하려는 정보는 이름을 잘 짓는 간단한 규칙이다.

의도를 분명히 밝혀라

변수가 어떤 정보를 저장하는지를 명확히 하는 이름을 짓는 것을 말한다. 코드의 예로 이해를 돕는다. 아래의 코드는 같은 기능을 수행하는 코드이다.

1번코드

public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<int[]>();
    for (int{] x : theList})
        if (x[0] == 4)
            list1.add(x);
    return list1;
}

2번코드

 class Cell { ... }

 public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<Cell>();
    for (Cell cell : gameBoard)
        if(cell.isFlagged())
            flaggedCells.add(cell);
    return flaggedCell;
 }

그릇된 정보를 피하라

변수를 작성할 때 오해나 혼란을 줄수 있는 단어나 약어들을 사용하지 말아야한다.

  • 직각삼각형의 빗변(hypotenuse)을 hp로 줄여서 쓰는 방식은 hp의 의미를 떠오르게 하는데 많은 시간을 들게 함으로 사용시 주의해야한다.
  • 변수의 자료형이 list가 아닌데 변수의 이름에 list를 붙이지 말것.
  • 서로 흡사한 이름을 사용하지 말것.(ForEffecientHandlingOfString / ForEffecientStorageOfString 두 변수의 차이를 보자마자 구분할 수 있겠는가?)
  • 변수 ‘l’과 ‘O’는 각각 1과 0을 연상하게 되므로 사용하지 말것을 권장한다.

의미있게 구분하라

읽는 사람이 각 변수의 차이를 알도록 계열의 변수들을 명명해야한다.

  • 불용어 (a1, a2등 아무 의미 없는 글자들)를 사용하지 말것.
  • 쓸 때 없이 Info, Data 접미어를 쓰지 말것.(ProductInfo와 ProductData를 구분할 수 있겠는가?)
  • 변수안에 예약어(class, string 등 언어가 지정한 키워드)를 사용하지 말 것.

발음하기 쉬운 이름을 사용하라

  • genymdhms를 어떻게 읽은 것인가?
  • 위의 변수를 generationTimestamp로 바꾸면 기억하거나 이해하기 더 쉽지 않겠는가?

검색하기 쉬운 이름을 사용하라

  • 기준이 되는 숫자는 상수를 지정해서 사용할 것.
  • 변수이름을 최소한 단어로 지정 할 것. (로컬변수라면 상관 없을 수 있음.)

인코딩을 피하라

피해야하는 인코딩 목록을 나열해본다.

  • 헝가리식 표기법 : 각 변수에 속성을 변수 앞 접두어로 표기하는 방법.
  • 맴버변수 접두어 : m_

    public class Part { private String m_dsc; // String description void setName(String name){ m_dsc = name; } } — public class Part { String description; void setName(string description){ this.description = description; } }

  • 인터페이스 클래스와 구현 클래스 : 인터페이스 클래스에 굳이 접두어를 붙일필요 없이 그냥 클래스이름 규칙으로 명명 구현 클레스에 -Impl을 붙여주는 것이 적합.

자신의 기억력을 자랑하지 마라

똑똑한 프로그래머와 전문가 프로그래머 사이에서 나타나는 차이점 하나만 들자면, 전문가 프로그래머는 명료함이 최고라는 사실을 이해한다.

  • 독자(코드를 보는사람)이 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 다시 생각해야한다면 바람직 하지 않은 변수 이름이다.
  • 문자 하나만 사용하는 변수는 for문의 i,j,k (l은 절대 안된다.)를 제외하고 문제갸 될 수 있다.

클래스 이름

  • 클래스 이름과 객체 이름은 명사나 명사구가 적합하다. Customer, WikiPage, Account, AddressParser 등이 좋은 예.
  • Manager, Processer, Data, Info 등과 같은 단어를 피하고 동사를 사용하지 않는다.

메서드 이름

  • 메서드 이름은 동사나 동사구가 적합하다.
  • 접근자, 변경자, 조건자는 javabean 표준에 따라 get, set, is를 붙인다.

기발한 이름은 피하라.

명확한 정보만 담을 것. 코드에 당신의 재치와 재미를 넣지 말 것.

한 개념에 한 단어를 사용하라

추상적인 개념 하나에 단어 하나를 선택해서 고수한다. 일관성 있는 어휘는 코드를 사용할 프로그래머가 반갑게 여길 선물이다.

  • 똑같은 메서드를 각각의 클래스에서 fetch, retrieve, get으로 제각각 부르면 혼동이 올 수 있다.
  • 동일 코드 기반에 controller, manager, driver를 섞어서 쓰면 혼동이 올 수 있다.

말장난을 하지 마라

한 단어를 두 가지 목적으로 사용하지 마라.

  • add를 두 가지 값을 받아 합쳐 하나의 값으로 변환하는 메서드라고 했을 떄 하나의 집합과 하나의 요소를 합쳐 하나의 값으로 변환하는 개념은 add 보다는 insert나 append가 더 정확한 명명이 될 수 있다.

해법 영역에서 가져운 이름을 사용하라

코드를 읽을 사람은 프로그래머이다. 프로그래머 영역에서 사용되는 단어가 더 정확한 뜻을 전달한다면 그 용어를 사용하는 것이 좀더 바람직 할 수 있다.

문제 영역에서 가저온 이름을 사용하라

적절한 ‘프로그래머 용어’가 없다면 문제 영역에서 이름을 가져온다. 문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야 한다.

의미 있는 맥락을 추가하라

스스로 의미가 분명한 이름이 없지 않지만 대다수 이름이 의미가 분명하지 않은 경우가 많다. 그래서 클래스,함수, 네임스페이스에 넣어(묶어) 맥락을 부여한다. 마지막 방법으로 접두어를 붙인다.

불필요한 맥락을 없애라

일반적으로는 짧은 이름이 긴 이름보다 좋다. 단, 의미가 분명한 경우에 한해서다. 할일(todo) 앱을 만든다고 해서 모든 클래스에 TODO라는 접두어를 붙일 필요는 없다.