Clean Code 1장 - 4장

2019-03-25

이번에 학교 동아리 스터디 하면서 다시 읽게 되었다. 몇 년전에 뭣도 모를때 이 책을 추천받아서 한 번 읽었지만, 처음 본 것 처럼 읽었다.. 오래되어서 내용을 완전히 까먹은 줄 알았는데, 그래도 과거에 한 번 봤던 책이라 그런가 생각보다 빨리 읽었다.

1장 깨끗한 코드

깨끗한 코드란 무엇인가?에 대해 여러 유명인들의 명언들이 있다. 아마 어디선가 다 들어본 내용들인듯.

하지만 다 들어본 내용일지라도 매번 잘 지키기가 힘들었다. 항상 그런 말을 들어도 ‘아 일정이 빠듯한데..’, ‘지금은 당장 만들기 바쁘니까, 유지보수는 담에 생각하자’ 등의 핑계로 미뤄놨던 과거들이 생각났다.

그렇게 꾸준히 미뤄놔서 언젠가 했냐? 안했다. p. 4에서 나중은 결코 오지 않는다를 보고 크게 뜨끔했다. 구우욷이 핑계를 대자면, 과제 혹은 그 이후에 굳이 손을 댈 이유가 없는 프로젝트에서 유지보수를 고려하기란 쉽지않다. 당장 눈에 보여줄 결과물의 규모가 작으면 스스로가 위축되기도 하고, 실제 평가에서도(…)

보이스카우트 규칙 - 체크아웃해 코드를 꺼낼 때보다 체크인해서 코드를 넣을 때 더 깨끗한 상태로 만들어야 할 의무가 있다

2장 의미있는 이름

변수명을 지나치게 줄여서 쓰지말고 의미없는 변수명을 쓰지 말라고 하면 그냥 짧은게 좋은거 아닌가? 어차피 저게 뭔 뜻인지 너도 알고 나도 아는데 굳이 왜?라는 질문이 들어온다. 난 여태 이 질문에 제대로 대답해 준적이 없다.

p. 23에 그 질문에 대한 답을 명쾌하게 제시해 준다. 문제는 코드의 단순성이 아니라 코드의 함축성이다. 코드 맥락이 코드 자체에 명시적으로 드러나지 않는다. 기간이 길어지면 내가 짠 코드일지라도 다시 돌아봤을 때 ‘내가 왜 이렇게 짰지?’ 싶다.

p. 31 인터페이스 클래스와 구현 클래스의 예시가 바로 와닿지 않았다. 이는 Map을 떠올려 보면 금방 와닿는다. Map이라는 인터페이스 아래에 HashMap, LinkedHashMap 등이 있다. 아래로 갈수록 구체적인 이름이 붙는것이 자연스럽다.
‘ShapeFactoryImpl 혹은 심지어 CShapeFactory가 IShapeFactory보다 좋다.’라는 말은, 굳이 구체적으로 붙일게 없으면 하다못해 class를 뜻하는 C라도 붙여라는 뜻이다.

사람은 계속 까먹는다. 나 혼자 짜는게 아니라 여러명이서 한 프로젝트를 만진다면, 팀원의 변동이 있을 예정이라면 반드시 지켜야 할 것이라고 생각한다. 커밋로그도 그렇게 관리하는데 코드는 당연할 것이다.

우리는 이미 인텔리제이와 같이 좋은 IDE를 쓰고 있다. IDE를 방해하지 말자. 편한건 좋은것이고 더 편한 방법이 있다면 당연히 그렇게 하는게 맞다.

3장 함수

p. 44 한 가지만 해라!

함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
이번 장을 요약하자면 딱 이거다. 뒤에 나와있는 설명 모두 ‘몸집을 키우지 말고 최대한 작은 단위로 만들어라. 가독성도 올라가고 고치기도 쉽다’로 귀결된다.

p. 47 Switch 문

Switch문이 갖고있는 문제는 새로운 case가 생기면 그 분기를 처리하는 모든 함수에 새로운 case를 추가 해 줘야 한다. 만약 실수로 빼먹으면 default로 가버려서 코드가 동작은 하는데 잘못 돌아가게 되고 문제를 인지하기가 쉽지 않다.

목록 3-4의 Payroll.java에는 calculatePay() 함수가 있다. 이는 고용형태에 따라서 급여를 계산하는 코드이다. 보여지는 코드는 이거 하나 뿐이지만, switch문이 이 함수에만 나오지 않을 것이라는 것을 유추할 수 있다.

예를 들면 급여일을 알려주는 함수, 보너스가 나왔을 때 급여를 계산하는 함수, 계약기간을 알려주는 함수 등..이 더 있을 것이고 그 함수 마다 switch문으로 고용형태에 따른 분기처리를 하고 있을 것이다.

만약 새로운 고용형태인 ‘주급’이 나왔다고 생각해보자. 그러면 모든 함수에 있는 switch문 마다 새로운 case WEEKLY: 를 달아줘야 할 것이다. 얼마나 끔찍한지.. 실수로 어떤 함수에는 새로운 case를 빼먹을 위험도 있다.

그러니까 목록 3-4의 코드는 하나 추가되거나 수정 할 일이 있으면 건드릴 코드가 많다. 그 수 많은 코드를 건드리면서 실수를 하게 된다.

목록 3-5 Employee and Factory는 팩토리 패턴을 사용해서 개선한 것이다. 디자인 패턴과 관련된 지식이 없으면 이게 왜 개선된 코드인지 받아들이기가 쉽지 않을 수 있으니, 팩토리 패턴이 무엇이고 왜 쓰는지를 먼저 찾아보면 도움이 될 것이다. 간단히 말하면 팩토리 패턴은 객체 생성을 위임하는 형태인데, 말 그대로 객체 찍어내는 공장이라는 뜻이다.

디자인 패턴이 가지고 있는 기본적인 내용은 아래와 같다. 이 세가지를 유념하면서 찾아보자.

  • 대부분의 패턴과 원칙은 소프트웨어의 변경 문제와 관련되어 있음.
  • 대부분의 패턴은 시스템의 일부분을 고쳤을 때 나머지 부분에 영향이 가지 않도록 변경하는 방법을 알려줌.
  • 보통 시스템에서 바뀌는 부분을 골라내서 캡슐화 함.

디자인 패턴을 공부하면서 결합도는 낮추고 응집도는 올리라는 말을 꽤 보게 될 것이다. 목록 3-5 에서 팩토리 메소드 패턴을 사용하는 이유는 클래스간의 결합도를 낮추기 위한것이다.

이 책을 읽다가 ‘바뀐 코드가 왜 개선된 코드지?’ 하는 생각이 들면 디자인 패턴 몇 가지를 먼저 찾아보는 것을 추천한다. 뒷 부분을 쭉 훑어보니까 패턴을 아예 모르면 어려운 부분들이 좀 있다. strategy, singleton, factory 이 정도만 봐도 받아들이는데 무리가 없을 것이다. 나머지 패턴들은 그때 그때 찾아보면 될 듯?…

p. 53 삼항 함수

삼항 짜리 assertEquals를 한번도 써보지 않아서 좀 찾아보았다. 이 책에 나온 assertEquals(message, expected, actual)은 부동소수점 비교에 대한 말이 나오는거 보면 문맥상 assertEquals(java.lang.String message, double expected, double actual)이걸 지칭하는듯 하다.

junit4 문서에서 이건 Deprecated 되어있다고 하고,

@Deprecated
assertEquals(String message, double expected, double actual) 
          Deprecated. Use assertEquals(String message, double expected, double actual, double delta) instead

junit5 문서에서는 인자 순서가 바뀌었는데, message가 제일 마지막 인자로 밀려있고 expected가 첫번째 인자로 바뀌었다.

public static void assertEquals(double expected, double actual, String message)
Assert that expected and actual are equal.
Equality imposed by this method is consistent with Double.equals(Object) and Double.compare(double, double).

Fails with the supplied failure message.

assertEquals(message, expected, actual)이랑 assertEquals(1.0, amount, .001)이거 두 개가 다른 함수라고 말하는 듯 하다. ‘앞에꺼는 별로고 뒤에꺼는 그나마 받아들일 수 있음’ 이런 느낌으로 설명해 놨다. 뒤에껀 assertEquals(double expected, double actual, double delta)라고 이해했다.

p. 55 부수 효과를 일으키지 말라! 코드 3-6

checkPasswordAndInitializeSesion이라는 이름으로 바꾸는게 좋다고 제안하는데 그냥 아래와 같이 쓰면 안되는지 궁금했다. Session.initialize();이걸 checkPassword()에서 지우고, password 확인 용도로만 쓰는게 더 나은거 같았기 때문이다.

if(checkPassword(userName, pasword))
    Session.initialize();

앞에 있는 추상화 수준과 이어지는 내용으로 저걸 따로 빼면 추상화 수준이 맞지 않기 때문이라고 필기를 해 뒀..는데.. 그 때 질문하고 설명들을땐 받아들여졌는데 며칠 지나서 글로 정리하니까 또 가물가물하다..

p. 56 출력 인수

출력인수 자체를 내가 써본적이 없는 것 같다. 내가 써봤으나 기억을 못하는건지 모르겠지만, 이 부분이 이해가 안되어서 좀 찾아보았다. ‘출력 인수’를 검색 해 보면 거진 매트랩 관련 글이 나온다. 나는 매트랩을 써본적이 없으나, 출력인수 자체가 뭔지 와닿지 않아서 매트랩 코드를 봤다.

말 그대로 출력용 인수(변수)구나~하고 이해했는데, 출력인수로 사용하라고 설계한 변수가 바로 this이기 때문이다에서 멈칫했다.

출력인수를 쓴다는 건 아래와 같은 경우를 말하는 것이다. 그래서 appendFooter(s)가 아니라 report.appendFooter()와 같이 쓰는것이 좋다고 적어 놓은 것이다.

public void appendFooter(StringBuffer report) {
    ...
    return report;
}

4장 주석

주석은 그냥 대부분의 경우에서 쓰지말라고 배웠었다. 아마 내가 과하게 써서 과거에 그런 피드백을 들었을지도 모른다는 생각을 이 파트를 읽으면서 느꼈다.

p. 74 TODO 주석

TODO 주석자체를 쓰지말라고 배워서 물어봤는데, 그건 내가 TODO 주석 자체를 과하게써서 그런것 같다. 당장 급하지는 않으나 있으면 좋겠고 개선되지 않아도 당장은 별 문제 없는 내용을 TODO 주석에 쓴다고 한다.

p. 84 위치를 표시하는 주석

위치를 표시한다는게 무슨말인지 이해가 안갔는데 HTML 코드 예시를 듣고 바로 이해했다. HTML에서 <!-- banner -->, <!-- container --> 와 같은 주석이 위치를 표시하는 주석이다.

극히 드물지만 유용한 경우가 HTML에서의 위치를 표시하는 주석일 것이다.