menu

클린 코드 책을 읽고

프롤로그

사무실 구석에 놓여있던 클린 코드 책이 갑자기 눈에 들어와 가져와 읽기 시작했다. 몇 개월전에 선배가 읽어보라고 추천해줬던 기억이 있는데, 이제서야 읽게 되었다.

2. 의미있는 이름

프로그래머는 매 순간 이름을 짓는다.

의도를 분명히 밝혀라

  • 의도가 분명한 이름은 짓는 것은 정말로 중요하다.

그릇된 정보를 피하라

  • list가 아닌 것에는 list를 붙이지 마라(타입을 뒤에 붙이지 않는 것을 추천)

의미있게 구분하라

  • zork라는 이름을 쓰고 있다고 새 변수를 thezork라고 지으면 안된다.

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

  • genymdhms 이런 변수는 쓰지마라

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

  • WORK_DAYS_PER_WEEK와 같은 단어는 찾기 쉽지만, 5같은 숫자는 찾기 어렵다.

인코딩을 피하라

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

  • 루프의 반복변수(i,j) 정도 외에 한글자로 이름짓지 말라
  • 명료함이 최고다

클래스이름은 명사구로 지어라

메서드이름은 동사구가 적합하다

기발한 이름은 피하라

  • kill()대신 whack()라고 짓지 마라

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

  • 똑같은 메서드를 클래스마다 fetch, get, retrieve라고 짓는 일은 하지 말라

말장난을 하지마라

  • 전부 add를 쓰고 있다고 다른 동작을 하는 새 메서드에 add라고 붙여선 안된다. 그것을 말장난이다.

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

  • 코드를 읽는 사람도 프로그래머이므로, 전산 용어, 알고리즘 용어 등을 사용해도 괜찮다.

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

의미있는 맥락을 추가하라

  • firstName, state, housdeNumber 같은 변수를 사용한다면, 변수에 Addr 접두어를 붙이거나, Address클래스를 만들어 맥락을 표현하는 것이 좋다.

불필요한 맥락을 없애라

  • GSD라는 이름의 애플리케이션을 만든다고, 모든 모듈이나 클래스에 GSD 접두어를 붙이지 마라.
3. 함수

어떤 프로그램이든 가장 기본적인 단위는 함수이다.

작게 만들어라!

  • 함수는 작게 만들어야한다.

한 가지만 해라!

  • 함수는 한 가지를 해야 한다. 그 한 가지를 잘해야 한다. 그 한 가지만을 해야 한다.

함수 당 추상화 수준은 하나로!

  • 함수가 한 가지를 하기 위해서는 추상화 수준이 동일해야 한다.
  • getHTML()과 append(“\n”)은 추상화 수준이 다르다.
  • 내려가기 규칙 : 위에서 아래로 내려갈 수록 추상화 수준이 낮아지도록 작성

서술적인 이름을 사용하라!

  • 이름이 길어도 괜찮다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.

함수 인수

  • 함수에서 이상적인 인수의 개수는 0개 이고, 3개 이상은 피하는 게 좋다.
  • 인수가 많아질수록 이해하기 어렵다.
  • 플래그 인수는 추하다.

부수효과를 일으키지 마라!

  • 함수 내부에서 남몰래 전역 변수를 수정하거나 하는 등의 일은 하면 안된다.

명령과 조회를 분리하라!

  • 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야한다.

오류코드 보다 예외를 사용하라!

  • 오류코드를 리턴하는 방식은 이후 if문을 만들어낸다.
  • try/catch블록은 원래 추하다. 따라서 try/catch 블록만 따로 함수로 뽑아내는 것이 좋다.

반복하지 마라!

  • 중복은 소프트웨어에서 만악의 근원이다.

함수를 어떻게 짜죠?

  • 소프트웨어를 짜는 행위는 글짓기와 비슷하다.
  • 어설픈 초안을 만들고, 테스트 케이스를 짠 뒤, 함수를 다듬는다.
4. 주석

우리는 의도를 코드로 표현하지 못해, 그러니까 실패를 만해하기 위해 주석을 사용한다. 진실은 한 곳에만 존재한다. 바로 코드다.

주석은 나쁜 코드를 보완하지 못한다

  • 자신이 저지른 난장판을 주석으로 설명하려 애쓰는 대신, 그 난장판을 깨끗이 치우는 데 시간을 써라
5. 형식 맞추기

코드 형식은 중요하다! 프로그래머라면 형식을 깔끔하게 맞춰 코드를 짜야한다.

적절한 행 길이를 유지하라

  • 200줄 정도의 파일들로도 커다란 시스템을 구축할 수 있다. 일반적으로 큰 파일 보다 작은 파일이 이해하기 쉽다.

개념은 빈 행으로 분리하라

세로 밀집도

  • 서로 밀접한 코드 행은 세로로 놓여야 한다.

수직 거리

  • 변수 선언 : 변수는 사용하는 위치에 최대한 가까이 선언한다.
  • 인스턴스 변수 : 인스턴스 변수는 클래스 맨 처음에 선언한다. 위치에 대한 이견이 있지만, 맨 처음이던 맨 뒤던, 모은다는 것이 중요하다.
  • 종속 함수 : 한 함수가 다른 함수를 호출한다면, 두 함수는 세로로 가까이 배치한다.
  • 개념적 유사성 : 같은 변수를 사용하거나, 하는 동작이 비슷한 함수는 가까이 배치한다.

세로 순서

  • 함수 호출의 종속성은 아래 방향으로 유지한다. 호출되는 함수를 호출하는 함수보다 나중에 배치한다.

가로 공백과 밀집도

  • 가로로는 공백을 사용해 밀접한 개념과 느슨한 개념을 표현한다. 할당문에 공백을 주어 왼쪽/오른쪽 요소를 확실히 나눌 수 있고, 함수이름과 괄호는 밀접하기에 공백을 넣지 않는다.

들여 쓰기

  • 간단한 if문, 함수에서 들여쓰기를 무시하고 싶은 충동을 이겨내라.

팀 규칙

  • 팀은 한 가지 규칙에 합의해야 한다. 그리고 모든 팀원이 그 규칙을 따라야 한다. 소프트웨어의 스타일은 일관적이고 매끄러워야 한다.
6. 객체와 자료구조

자료 추상화

  • 변수 사이에 함수라는 계층을 넣는다고 구현이 저절로 감춰지지는 않는다. 구현을 감추려면 추상화가 필요하다!
  • 의식적으로 getter setter 만들었다고 땡 치는 게 아니다.

객체와 자료구조

  • 객체는 추상화 뒤로 자료를 숨기고 자료를 다루는 함수를 제공
  • 자료구조는 자료를 그대로 공개하고, 함수를 제공하지 않음

객체지향 코드와 절차적인 코드

  • 절차적인 코드는 함수를 추가하기 쉽지만, 새로운 자료구조를 추가하기 어렵다.
  • 객체지향 코드는 새 클래스를 추가하기 쉽지만, 함수를 추가하기 어렵다.

자료 전달 객체(DTO)

  • 자료 전달 객체(DTO)는 자료구조체의 전형적인 형태로, 공개 변수만 있고 함수가 없는 클래스다.
  • Save나 find같은 탐색 함수를 넣기도 한다.
7. 오류 처리

난잡한 오류 처리 때문에 실제 코드가 하는 일을 파악 못함!

오류 코드보다 예외를 사용하라

  • 오류코드를 리턴 하는 방식은 이후 if문을 만들어낸다.
  • If(deletePage(page) == E_OK)

미확인 예외를 사용하라

  • 피호출 함수에 오류를 추가하면 호출함수로 돌아가 함수 선언부에 throws 절을 추가해야 한다.
  • RuntimeException 사용하라

호출자를 고려해 예외 클래스를 정의하라

  • 오류를 정의할 때, 가장 중요한 관심사는 오류를 잡아내는 방법이다.
  • 외부 API를 쓰는 코드를 감싸게 되면 의존성이 크게 줄어듦

Null을 반환하지 마라

  • Null 체크는 빼먹기 쉽다. 애초에 null을 반환하지 않도록 짜라!
  • 대신 특수사례 객체를 만들어 return하라

Null을 전달하지 마라

  • 메서드로 null을 전달하는 방식은 매우 나쁘다!
  • NullPointerException 터져나오게됨
8. 경계

개발 중 다양한 외부 라이브러리나 오픈 소스를 이용한다.

외부 코드 사용하기

  • 외부에서 가져온 인터페이스를 여기저기 넘기지 마라.
  • 이를 이용하는 클래스 밖으로 노출되지 않도록 주의한다.

학습 테스트

  • 외부 코드는 익히기 어렵다.
  • 바로 외부 코드를 호출하는 대신 간단한 테스트 케이스를 작성
  • 가성비가 좋다
  • 버전 올리기에도 좋다. TC가 있으므로
9. 단위 테스트

TDD시, 방대한 테스트 코드가 생성됨. 관리 문제를 유발

깨끗한 테스트 코드 유지하기

  • 테스트 코드가 깨끗하지 않으면, 케이스 추가 어렵게되고, 실패율이 올라간다.
  • 테스트 코드는 실제 코드 못지 않게 중요하다!

깨끗한 테스트 코드

  • 가독성, 가독성, 가독성
  • 테스트에서만 사용하는 특수 API를 만든다
  • 실제 코드만큼 효율적일 필요는 없다
  • 앞선 규칙들을 약간 위반하더라도 가독성을 최우선으로 하라!

F.I.R.S.T

  • Fast : 테스트는 빨라야 한다.
  • Independent : 각 테스트는 서로 의존하면 안 된다.
  • Repeatable : 어떤 환경에서도 반복 가능해야 한다.
  • Self-Validating : 테스트는 Bool값으로 결과를 내야 한다.
  • Timely : 단위 테스트는 실제 코드 구현 직전에 작성한다.
10. 클래스

클래스 체계

  • 자바 관례에서 가장 먼저 변수 목록이 나온다
  • Static public -> Static private -> Private 인스턴스 순서
  • 변수목록 다음 Public 함수
  • Private 함수는 자기 호출한 Public 함수 직후

클래스는 작아야 한다!

  • 작아야 한다. 작아야 한다.
  • 클래스 이름은 클래스 책임을 기술
  • 이름이 안 떠오른다? 책임이 너무 많은 것

단일 책임 원칙(SRP)

  • 클래스나 모듈을 변경할 이유가 단 하나뿐이어야 한다.
  • 돌아가는 프로그램만 생각하면 SRP가 잘 깨진다.

응집도

  • 몇몇 함수가 몇몇 변수만 사용한다면 쪼개라
11. 시스템

도시를 세운다면?

  • 혼자서 도시를 관리할 수 있는 가? 없다!
  • 그럼에도 잘 돌아간다
  • 수도 관리 팀, 전력 관리 팀 등 각 분야의 관리 팀이 있기에…
  • 도시가 잘 돌아가는 이유는 적절한 추상화와 모듈화 때문이다.

시스템

  • 시스템을 개발할 때 비슷한 수준의 관심사를 분리해 모듈화해야 깨끗한 시스템을 유지할 수 있다

생성과 사용을 분리하라

  • 소프트웨어 시스템은
  • 객체를 생성하고 의존성을 서로 연결하는 준비과정과
  • 준비 과정 이후에 이어지는 런타임 로직을 분리해야 한다.

의존 주입(DI)

  • 객체는 의존성을 인스턴스로 만드는 책임을 지지 않음
  • 전담 매커니즘(main / 특수 컨테이너)에 책임을 넘김
  • 클래스는 완전히 수동적
  • Spring Framework

확장

  • 처음부터 올바르게 시스템을 만들 수 있다는 믿음은 미신
  • 관심사를 적절히 분리해 관리한다면, 소프트웨어 아키텍처는 점진적으로 발전할 수 있다!

관심사를 적절히 분리하기 위한 노력

  • EJB1/EJB2
  • AOP : 횡단 관심사를 분리하여 모듈성을 증가
    • 횡단 관심사 : Logging, Security, Transaction과 같이 객체 경계를 넘나드는 관심사
  • Spring, Jboss AOP, AspectJ

POJO

  • Plain Old Java Object : 단순한 자바 객체
  • POJO는 프레임워크에 의존하지 않는다. 즉, 코드 수준에서 아키텍처 관심사가 분리되어 있다.
  • 애플리케이션 도메인 논리를 POJO로 구현한다.
  • 무엇을 얻는가?
    • 쉬운 테스트 주도 기법 적용
    • 빠른 개발
    • 코드의 단순성
12. 창발성

착실하게 따르기만 하면 우수한 설계가 나오는 간단한 규칙이 있다.

규칙1. 모든 테스트를 실행하라

  • 테스트가 가능한 시스템을 만들려고 애쓰면 설계 품질이 높아진다.

규칙2. 리팩토링:중복을 없애라

  • 우수한 설계에서 중복은 커다란 적이다.

규칙3. 리팩토링:표현하라

  • 코드는 개발자의 의도를 분명히 표현해야 한다.
  • 표현력을 높이는 최고의 방법은 노력이다. 자신의 작품을 좀 더 사랑하라.

규칙4. 리팩토링:클래스와 메서드 수를 최소로 줄여라

  • 4가지 규칙 중 우선순위가 제일 낮다.
  • 규칙2,3이 극단으로 가면 클래스와 메서드가 수없이 많아진다.
13. 동시성

동시성과 깔끔한 코드는 양립하기 매우 어렵다.

난관

  • 동시성을 구현하는 것은 어렵다.

동시성 방어 원칙

  • 동시성 관련 코드는 다른 코드와 분리하라.
  • 자료를 캡슐화하라. 공유 자료를 최대한 줄여라.
  • 스레드는 독립적으로 구현하라. 다른 스레드와 자료를 공유하지 않도록.

라이브러리를 이해하라

  • 언어에서 제공하는 동시성 관련 클래스를 익히고 사용하라.

공유 객체 하나에는 메서드 하나만 사용하라

동기화하는 부분을 최대한 작게 만들어라

올바른 종료 코드는 구현하기 어렵다

  • 깔끔하게 종료하는 코드는 올바로 구현하기 어렵다.
  • 종료 코드를 개발 초기부터 고민하라.

스레드 코드 테스트하기

  • 시스템 설정과 부하를 바꿔 자주 테스트하라.

시스템 실패를 ‘일회성’이라 치부하지 마라

순차 코드부터 제대로 돌게 만들어라

  • 스레드 환경 밖에서 코드가 제대로 도는지 반드시 확인한다.

프로세스 수 보다 많은 스레드를 돌려보라

다른 플랫폼에서 돌려보라

14. 점진적인 개선

단순히 돌아가는 코드에 만족하는 프로그래머는 전문가 정신이 부족하다. 코드는 언제나 최대한 깔끔하게 정리하라.

15. JUnit 들여다보기
16. SerialDate 리팩토링
17. 냄새와 휴리스틱

이 목록은 완벽하지 않다. 다만 가치 체계를 피력할 뿐이다. 이러한 규칙을 따른다고 깨끗한 코드가 얻어지지 않는다. 휴리스틱 목록을 익힌다고 소프트웨어 장인이 되지는 못한다. 전문가 정신과 장인 정신은 가치에서 나온다. 그 가치에 기반한 규율과 절제가 필요하다.

소감

두꺼운 책이었고, 읽는 데 꽤 많은 시간이 소요되었다. 전체적으로 잘 읽히는 책이라고 느꼈다. 재밌게 읽었다. 전체 내용이 모두 유익했지만, 앞 부분이 더 좋았다. 특히, 클린 코드란 무엇인가에 대해 이야기하는 서두 부분이 아주 기억에 남는다. 조금 오래되어 옛날 기술 기준으로 적혔다거나, 이젠 이미 당연히 받아들이는 내용을 설명한 부분이 있다. 이런 부분은 알아서 가볍게 읽고 지나가면 좋을 것 같다.

당연히 이 책의 규칙들이 정답은 아니겠지만, 개발 꼬꼬마로서 지침은 충분히 될 수 있을 것 같다. 책에서 말한 것 처럼, 클린 코드를 알아 볼 수 있다고, 그 코드를 작성할 수 있는 건 아니므로… 열심히 코딩해보고, 뜯어보는 노력이 제일 필요할 것 같다.