개발 공부 기록

[독서] Clean Code 내용정리 본문

개발/독서

[독서] Clean Code 내용정리

_김도연 2020. 8. 26. 14:42

Clean Code

https://book.naver.com/bookdb/book_detail.nhn?bid=7390287

 

Clean Code

『CLEAN CODE(클린 코드)』은 오브젝트 멘토(OBJECT MENTOR)의 동료들과 힘을 모아 ‘개발하며’ 클린 코드를 만드는 최상의 애자일 기법을 소개하고 있다. 소프트웨어 장인 정신의 가치를 심어 주며 ��

book.naver.com

책을 읽기 전에 짓던 변수명과 책을 읽고 난 후 짓는 변수명이 엄청 달라졌다. 또한 이전에는 주석을 통해 나의 코드를 설명했었다. 하지만 이제 나는 주석을 쓰지 않는다. 변수명과 코드 구조를 잘 활용하여 글을 읽는 것 같은 코드를 작성하기 위해 노력 중이다.

좋은 개발자, 협업하고 싶은 개발자가 되고 싶다면 이 책을 읽을 것을 강력 추천한다.

1장. 깨끗한 코드

- 깨끗한 코드를 만드는 비결: 중복 줄이기, 표현력 높이기, 초반부터 간단한 추상화 고려하기

2장. 의미 있는 이름

1. 의도를 분명히 밝혀라.

2. 그릇된 정보를 피하라. 

3. 의미 있게 구분하라.

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

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

6. 인코딩을 피하라.

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

8. 기발한 이름은 피하라.

9. 한 개념에 한 단어를 사용해라.

10. 말장난을 하지 마라.

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

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

13. 의미 있는 맥락을 추가하라.

14. 불필요한 맥락을 없애라.

 

클래스 이름 - 명사나 명사구가 적합

메서드 이름 - 동사나 동사구가 적합

3장. 함수

1. 작게 만들어라.

블록과 들여 쓰기 - 중첩 구조가 생길 만큼 함수가 커져서는 안 된다.

2. 한 가지만 해라.

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

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

함수가 확실히 '한 가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.

4. Switch 문

switch문은 작게 만들기 어렵다. 다형성 객체를 생성하는 코드를 제외하면 되도록 쓰지 않도록 하자.

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

길고 서술적인 이름이 짧고 어려운 이름보다 좋다. 길고 서술적인 이름이 길고 서술적인 주석보다 좋다.

함수 인수 - 함수에서 이상적인 인수 개수는 0개(무항)이다. 그다음 1개(단항)이고, 다음은 2개(이항)이다. 3개(삼항)는 가능한 피하는 편이 좋다. 4개 이상(다항)은 특별한 이유가 필요하다. 특별한 이유가 있어도 사용하면 안 된다. 최선 입력은 인수가 없는 경우이며, 차선은 입력 인수가 1개뿐인 경우이다.

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

7. 명령과 조회를 분리하라!

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

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

if-else를 통해 사용하는 것보다 try-catch 블록을 별도 함수로 뽑아내는 편이 좋다.

오류 처리도 한 가지 작업이다. 함수는 '한 가지' 작업만 해야 한다. 오류 처리도 '한 가지' 작업에 속한다. 그러므로 오류를 처리하는 함수는 오류만 처리해야 마땅하다.

9. 반복하지 마라

중복은 코드 길이가 늘어나며 알고리즘이 변하면 모두 고쳐야 하므로 없애거나 제거한다.

10. 구조적 프로그래밍

모든 함수와 함수 내 모든 불록에 입구와 출구가 하나만 존재해야 한다. 즉, 함수는 return문이 하나여야 한다.

4장. 주석

주석은 반겨 맞을 손님이 아니다. 코드를 깔끔하게 정리하고 표현력을 강화하는 방향으로, 그래서 애초에 주석이 필요 없는 방향으로 코드를 짜자.

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

표현력이 풍부하고 깔끔하며 주석이 거의 없는 코드가, 복잡하고 어수선하며 주석이 많이 달린 코드보다 훨씬 좋다.

2. 코드로 의도를 표현하라

3. 좋은 주석

- 법적인 주석

- 정보를 제공하는 주석

- 의도를 설명하는 주석

- 의미를 명료하게 밝히는 주석

- 결과를 경고하는 주석

- TODO 주석

- 중요성을 강조하는 주석

- 공개 API에서 Javadocs

4. 나쁜 주석

- 주절거리는 주석

- 같은 이야기를 반복하는 주석(주석이 코드보다 더 많은 정보를 제공하지 못한다.)

- 오해할 여지가 있는 주석

- 의무적으로 다는 주석

- 이력을 기록하는 주석

- 있으나 마나 한 주석(주석을 지나치게 달게 되면 개발자가 주석을 무시하는 습관에 빠지게 만든다.)

- 무서운 잡음(때로는 Javadocs도 잡음이다)

- 함수나 변수로 표현할 수 있다면 주석을 달지 마라

- 위치를 표시하는 주석

- 닫는 괄호에 다는 주석(닫는 괄호에 주석을 달지 말고 함수를 줄이려고 해라)

- 공로를 돌리거나 저자를 표시하는 주석

- 주석으로 처리한 코드

- HTML 주석

- 전역 정보(전역 변수의 값이 변해도 아래 주석이 변한다는 보장이 없다.)

- 너무나 많은 정보

- 모호한 관계(주석 자체가 다시 설명을 요구한다.)

- 함수 헤더(짧고 한 가지만 수행하며 이름을 잘 붙인 함수가 주석으로 헤더를 추가한 함수보다 훨씬 좋다.)

- 비공개 코드에서 Javadocs

5장. 형식 맞추기

오늘 구현한 기능이 다음 버전에서 바뀔 확률은 아주 높다. 그런데 오늘 구현한 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다.

적절한 행 길이를 유지하라

적은 길이의 파일로도 커다란 시스템을 구축할 수 있다.

- 신문기사처럼 작성하라. 독자는 위에서 아래로 읽는다.

- 개념은 빈 행으로 분리하라. 빈 행은 새로운 개념을 시작한다는 시각적 단서이다.

- 세로 밀집도. 서로 밀접한 행은 세로로 가까이 놓아야 한다. 주석으로 떨어뜨려 놓으면 안 된다.

- 세로 거리. 시스템이 무엇을 하는지 이해하고 싶은데, 이 조각 저 조각이 어디에 있는지 찾고 기억하느라 시간과 노력을 소모하면 안 된다. 같은 파일에 속할 정도로 밀접한 개념은 세로 거리로 연관성을 표현한다.

- 변수 선언. 변수는 사용하는 위치에 최대한 가까이 선언한다.

- 인스턴스 변수. 반면, 인스턴스 변수는 클래스 맨 처음에 선언한다.

- 종속 함수. 한 함수가 다른 함수를 호출한다면 두 함수는 세로로 가까이 배치한다. 또한 가능하다면 호출하는 함수를 호출되는 함수보다 먼저 배치한다.

- 개념적 유사성. 친화도가 높을수록 코드를 가까이 배치한다.

- 세로 순서. 신문기사와 마찬가지로 가장 중요한 개념을 가장 먼저 표현한다.

 

가로 형식 맞추기

짧은 행이 바람직하다. 본 책에서는 120자 정도로 행 길이를 제한할 것을 권장한다.

- 가로 공백과 밀집도. 가로 공백을 사용해 밀접한 개념과 느슨한 개념을 표현한다. 예를 들면, 승수 사이에는 공백을 없앤다. (불행히도, 코드 형식을 자동으로 맞춰주는 도구는 대다수가 연산자 우선순위를 고려하지 못하므로, 수식에 똑같은 간격을 적용한다. 따라서 공백을 넣어줘도 나중에 도구에서 없애는 경우가 흔하다.)

- 가로 정렬. 사실 가로 정렬은 유용하지 못하다. 코드가 엉뚱한 부분을 강조해 진짜 의도가 가려진다.

- 들여쓰기. 들여쓰는 정도는 계층에서 코드가 자리 잡은 수준에 비례한다.

- 들여쓰기 무시하기. 간단한 if문, while문에서 들여쓰기 규칙을 무시하고 유혹이 생겨도 무시하자.

 

팀 규칙

팀에 속했을 때 자신이 가장 선호해야 할 규칙은 팀 규칙이다.

6장. 객체와 자료 구조

변수를 private으로 정의하는 이유: 남들이 변수에 의존하지 않게 만들기 위함

1. 자료 추상화

변수를 private으로 선언하더라도 각 값마다 get함수와 set함수를 제공한다면 구현을 외부로 노출하는 셈이다. 구현을 감추려면 추상화가 필요하다. 자료를 세세하게 공개하기보다는 추상적인 개념으로 표현하는 편이 좋다.

2. 자료/객체 비대칭

객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개한다.

- (자료 구조를 사용하는) 절차적인 코드는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다. 반면, 객체지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다.

- 절차적인 코드는 새로운 자료 구조를 추가하기 어렵다. 그러려면 모든 함수를 고쳐야 한다. 객체 지향 코드는 새로운 함수를 추가하기 어렵다. 그러려면 모든 클래스를 고쳐야 한다.

3. 디미터 법칙

디미터 법칙은 잘 알려진 휴리스틱으로, 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다. 객체는 get 함수로 내부 구조를 공개하면 안 된다.

4. 자료 전달 객체

자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스다. 이런 자료 구조체를 DTO(자료 전달 객체)라 한다.

DTO는 데이터베이스와 통신하거나 소켓에서 받은 메시지의 구문을 분석할 때 유용하다. 흔히 DTO는 데이터베이스에 저장된 가공되지 않은 정보를 애플리케이션 코드에서 사용할 객체로 변환하는 일련의 단계에서 가장 처음으로 사용하는 구조체이다.

 

결론

객체는 동작을 공개하고 자료를 숨긴다. 그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉬운 반면, 기존 객체에 새 동작을 추가하기는 어렵다.

자료 구조는 별다른 동작 없이 자료를 노출한다. 그래서 기존 자료 구조에 새 동작을 추가하기는 쉬우나, 기존 함수에 새 자료 구조를 추가하기는 어렵다.

7장. 오류 처리

예외를 사용하면 논리와 오류 처리 코드가 뒤섞이지 않아 깔끔하다. 각 개념을 독립적으로 살펴볼 수 있어 코드 품질이 높아진다.

 

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

2. Try-Catch-Finally 문부터 작성하라

3. 미확인 예외를 사용하라

4. 예외에 의미를 제공하라

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

6. 정상 흐름을 정의하라

7. null을 반환하지 마라

8. null을 전달하지 마라

8장. 경계

소프트웨어 경계를 깔끔하게 처리하는 기법과 기교

 

1. 외부 코드 사용하기

제공자는 적용성을 최대한 넓히려하고, 사용자는 자신의 요구에 집중하는 인터페이스를 바란다. 이러한 이유로 시스텀 경계에서 문제가 생기는 경우가 많다.

Map과 같은 경계 인터페이스를 이용할 때는 이를 이용하는 클래스나 클래스 계열 밖으로 노출되지 않도록 주의한다. Map 인스턴스를 공개 API의 인수로 넘기거나 반환값으로 사용하지 않는다.

2. 경계 살피고 익히기

외부 코드를 익히거나 통합하기는 어렵다. 이 때, 학습테스트로 접근해본다. 곧바로 우리쪽 코드를 작성해 외부 코드를 호출하는 대신 먼저 간단한 테스트 케이스를 작성해 외부 코드를 익힌다.

학습 테스트는 프로그램에서 사용하려는 방식대로 외부 API를 호출한다. 통제된 환경에서 API를 제대로 이해하는지를 확인한다. 학습 테스트는 API를 사용하려는 목적에 초점을 맞춘다.

3. 학습 테스트는 공짜 이상이다

(어차피 API를 배워야 하기 때문에)학습테스트에 드는 비용은 없다. 오히려 필요한 지식만 확보하는 손쉬운 방법이다. 학습 테스트는 이해도를 높여주는 정확한 실험이다.

학습 테스트는 패키지가 예상대로 도는지 검증한다. 학습 테스트를 사용하면 패키지에 변화(문제)가 생겨도 새 버전이 호환되지 않는 다는 것을 바로 밝혀낼 수 있다.

학습 테스트를 이용한 학습이 필요하든 그렇지 않든, 실제 코드와 동일한 방식으로 인터페이스를 사용하는 테스트 케이스가 필요하다. 이런 경계 테스트가 있다면 패키지의 새 버전으로 이전하기 쉬워진다.

4. 아직 존재하지 않는 코드를 사용하기

협업을 하다보면 아직 존재하지 않는 코드가 필요할 때가 있다. 이 경우 구현을 미루거나 자체적으로 인터페이스를 정의한다. 우리 팀에서 바라는 인터페이스를 구현하면 우리가 인터페이스를 전적을 ㅗ통제한다는 장점이 생긴다. 또한 코드 가독성도 높아지고 코드 의도도 분명해진다.

5. 깨끗한 경계

- 경계에 위치하는 코드는 깔끔히 분리한다. 기대치를 정의하는 테스트 케이스도 작성한다.

- 통제가 불가능한 외부 패키지에 의존하는 대신 통제가 가능한 우리 코드에 의존하는 편이 훨씬 좋다.

- 외부 패키지를 호출하는 코드를 가능한 줄여 경계를 관리하자. 새로운 클래스로 경계를 감싸거나 아니면 ADAPTER 패턴을 사용해 우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환하자. → 가독성이 높아지며, 경계 인터페이스를 사용하는 일관성도 높아진다. 외부 패키지가 변했을 때 변경할 코드도 줄어든다.

9장. 단위 테스트

TDD(Test Driven Developmnet) 법칙 세 가지

- 첫째 법칙: 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.

- 둘째 법칙: 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.

- 셋째 법칙: 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

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

테스트 코드는 실제 코드 못지 않게 깨끗하게 짜야한다.

문제는 실제 코드가 진화하면 테스트 코드도 변해야 한다는 데 있다. 테스트 코드가 복잡할수록 실제 코드를 짜는 시간보다 테스트 케이스를 추가하는 시간이 더 걸린다.

테스트 케이스를 유지보수하는 비용이 늘어나다보면 테스트 슈트를 폐기해야 하는 상황까지 온다. 그러나, 테스트 슈트를 폐기하면 코드의 작동을 확인할 방법이 없고 결함율이 높아지기 시작한다는 문제가 있다.

테스트는 유연성, 유지보수성, 재사용성을 제공한다.

코드에 유연성, 유지보수성, 재사용성을 제공하는 버팀목은 단위테스트이다. 실제 코드를 점검하는 자동화된 단위 테스트 슈트는 설계와 아키텍처를 최대한 깨끗하게 보존하는 열쇠다. 테스트 케이스가 있으면 변경이 쉬워지기 때문이다.

깨끗한 테스트 코드

깨끗한 테스트 코드를 만드는데 중요한 세가지는 가독성, 가독성, 가독성이다. 테스트 코드의 가독성을 높이기 위해서는 명료성, 단순성, 풍부한 표현력이 필요하다.

> BUILD-OPERATE-CHECK 패턴 : 테스트를 세 부분으로 나눈다. 첫 부분은 테스트 자료를 만든다. 두 번째 부분은 테스트 자료를 조작하고, 마지막으로 조작한 결과가 올바른지 확인한다.

잡다하고 세세한 코드를 거의 다 없애야 한다. 테스트 코드는 진짜 필요한 자료 유형과 함수만 사용한다. 코드를 읽는 사람은 온갖 잡다하고 세세한 코드에 주눅들고 헷갈릴 필요 없이 코드가 수행하는 기능을 재빨리 이해한다.

테스트 당 assert 하나

- 테스트 코드를 짤 때 함수마다 assert문을 단 하나만 사용해야 한다고 주장하는 학파도 있다. 이렇게 짜면 결론이 하나기 때문에 이해하기 쉽고 빠르다.

- given-when-then 이라는 관례를 사용하면 테스트 코드를 읽기 쉬워진다. 그러나, 테스트를 분리하면 중복되는 코드가 많아진다.

- TEMPLATE METHOD 패턴을 사용하면 중복을 제거할 수 있다. given/when 부분을 부모 클래스에 두고 then 부분을 자식 클래스에 두면 된다. 아니면 @Before 함수에 given/when 부분을 넣고 @Test 함수에 then 부분을 넣어도 된다. 하지만 이렇게 해도 배보다 배꼽이 더 크다.

- 단일 assert문은 훌륭한 규칙일 수 있지만 때로는 함수 하나에 여러 assert문이 필요할 때도 있다.

- 테스트당 개념 하나. 이것저것 잡다한 개념을 연속적으로 테스트하는 긴 함수는 피한다.

> 정리 : 개념 당 assert 문 수를 최소로 줄여라. 테스트 함수 하나는 개념 하나만 테스트하라.

F.I.R.S.T.

Fast(빠르게) : 테스트는 빨라야 한다.

Independent(독립적으로) : 각 테스트는 서로 의존하면 안된다. 각 테스트는 독립적으로 그리고 어떤 순서로 실행해도 괜찮아야 한다.

Repeatable(반복가능하게) : 테스트는 어떤 환경에서도 반복 가능해야 한다.

Self-Validating(자가 검증하는) : 테스트는 bool 값으로 결과를 내야 한다. 성공 아니면 실패다.

Timely(적시에) : 테스트는 적시에 작성해야 한다. 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다.

결론

테스트 코드는 지속적으로 깨끗하게 관리하자. 표현력을 높이고 간결하게 정리하자. 테스트 API를 구현해 도메인 특화 언어(DSL)를 만들자.

 

'개발 > 독서' 카테고리의 다른 글

[독서] 자바스크립트 완벽가이드 내용 정리  (0) 2021.04.16
Comments