Insight? Practice!

Road to myself. 자기자신에게로 이르는 길.

[책] Clean Code

필독서다.

예전에 한번 읽었었는데 자꾸 눈에 밟히기도 하고 위키에도 정리할 겸 한번 더 읽었다. 2번 읽을줄 알았으면 한번은 원서로 읽을껄 그랬나 싶다.

책을 통해 위안을 받고 싶었는지도 모르겠다.

빠듯한 일정, 코드에는 아무도 신경쓰지 않는 문화, 의지 부족, 클린이 뭔지 모르는 무지. 안좋은 코드가 나올 수 밖에 없는 현실을 경험하고 보니 클린 코드가 더 절실했으리라.

2번 읽어도 밥 형님은 최고다. 가슴을 울리는 말들이 이어진다. 모든 프로그래머가 봐야할 책임에 틀림없다. 그런데 번역하신 분의 블로그를 보니 많이 안팔렸다고 한다. 이럴수가. 이런 명서중의 명서가 안팔린다니. 좋은 책이 무조건 흥행을 보장하지는 않는구나. 이렇게 좋은 책을 사람들이 많이 안읽는다는 사실이 슬프게 느껴진다. 게다가 번역도 너무 멋진 책인데!

밥형님이 직접 보여주시는 리팩토링 before/after 는 어디에서도 얻지 못할 고급 정보임에 틀림없다.

왠만하면 작게 추려보려고 했는데 너무 좋은 말이 많아서 그나마 간추린 내용이 이정도다.

아직 안보셨다면 지금 당장 사보시라! 2번 보시라!

1장. 클린 코드

궁극적으로 코드는 요구사항을 표현하는 언어라는 사실을 명심한다.

우리 모두는 자신이 짠 쓰레기 코드를 쳐다보며 나중에 손보겠다고 생각한 경험이 있다. 다시 돌아와서 나중에 정리하겠다고 다짐했었다. 물론 그 때 그 시절 우리는 르블랑의 법칙을 몰랐다. “나중은 결코 오지 않는다”

시간을 들여서 클린 코드를 만들려는 노력이 비용을 절감하는 방법일 뿐 아니라 전문가로서 살아남는 길이라는 사실을 인정하리라.

일정에 쫓기더라도 대다수 관리자는 좋은 코드를 원한다. 그들이 일정과 요구사항을 강력하게 밀어붙이는 이유는 그것이 그들의 책임이기 때문이다. 좋은 코드를 사수하는 일은 바로 우리 프로그래머들의 책임이다.

기한을 맞추는 유일한 방법은, 그러니까 빨리 가는 유일한 방법은, 언제나 코드를 최대한 깨끗하게 유지하는 습관이다.

그림을 보면 좋은 그림인지 나쁜 그림인지 구분이 간다. 그렇지만 좋은 그림을 구분할 줄 안다고 좋은 화가라는 뜻은 아니다. 다시 말해, 클린 코드와 나쁜 코드를 구분할 줄 안다고 클린 코드를 작성할 줄 안다는 뜻은 아니다.

클린 코드는 ‘보기에 즐거운’ 코드다.

나쁜 코드는 너무 많이 하려고 애쓰다가 의도가 뒤섞이고 목적이 흐려진다. 클린 코드는 한가지에 ‘집중’한다.

테스트 케이스가 없는 코드는 클린 코드가 아니다. 아무리 코드가 우아해도, 아무리 가독성이 높아도, 테스트 케이스가 없으면 깨끗하지 못하다.

클린 코드는 주의깊게 작성한 코드다. 누군가 시간을 들여 깔끔하고 단정하게 정리한 코드다.

코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드라 말한다.

이 논리에서 빠져나갈 방법은 없다. 주변 코드를 읽지 않으면 새 코드를 짜지 못한다. 주변 코드를 읽기 쉬우면 새 코드를 짜기도 쉽다. 주변 코드를 읽기 어려우면 새 코드를 짜기도 어렵다. 그러므로 급하다면, 서둘러 끝내려면, 쉽게 짜려면, 읽기 쉽게 만들면 된다.

시간이 지날수록 코드가 좋아지는 프로젝트에서 작업한다고 상상해보라. 전문가라면 너무도 당연하지 않은가?

이 책을 읽는다고 우수한 프로그래머가 된다는 보장은 없다. 단지 우수한 프로그래머가 생각하는 방식과 그들이 사용하는 기술과 기교와 도구를 소개할 뿐이다.


2장. 의미있는 이름

좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다.

따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 소리다.

발음하기 쉬운 이름은 중요하다. 프로그래밍은 사회적 활동이기 때문이다.

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

프로그래머는 코드를 최대한 이해하기 쉽도록 짜야한다. 집중적인 탐구가 필요한 코드가 아니라 대충 훑어봐도 이해할 코드가 목표다. 의미를 해독할 책임이 독자에게 있는 논문 모델이 아니라 의도를 밝힐 책임이 저자에게 있는 잡지 모델이 바람직하다.


3장. 함수

함수를 만드는 첫 번째 규칙은 ‘작게!’다. 함수를 만드는 두 번때 규칙은 ‘더 작게!’다.

각 함수가 너무도 명백했다. 각 함수가 이야기 하나를 표현했다. 각 함수가 너무도 멋지게 다음 무대를 준비했다. 바로 이것이 답이다.

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

단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 가지 작업을 하는 셈이다.

근본 개념과 세부 사항을 뒤섞기 시작하면, 깨어진 창문처럼, 사람들이 함수에 세부 사항을 점점 더 많이 추가한다.

함수로 부울값을 넘기는 관례는 정말로 끔직하다. 왜냐고? 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 공표하는 셈이니까!

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

어쩌면 중복은 소프트웨어에서 모든 악의 근원이다.

소프트웨어를 짜는 행위는 여느 글짓기와 비슷하다. 논문이나 기사를 쓸 때는 먼저 생각을 기록한 후 읽기 좋게 다듬는다. 초안은 대개 서투르고 어수선하므로 원하는 대로 읽힐 때까지 말을 다듬고 문장을 고치고 문단을 정리한다. 처음부터 탁 짜내지 않는다. 그게 가능한 사람은 없으리라.


4장. 주석

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

부정확한 주석은 아예 없는 주석보다 훨씬 더 나쁘다.

진실은 한 곳에만 존재한다. 바로 코드다. 코드만이 자기가 하는 일을 진실하게 말한다. 코드만이 정확한 정보를 제공하는 유일한 출처다. 그러므로 우리는 (간혹 필요할지라도) 주석을 가능한 줄이도록 꾸준히 노력해야 한다.

주석으로 처리된 코드는 다른 사람들이 지우기를 주저한다. 이유가 있어 남겨놓았으리라고 중요하니까 지우면 안 된다고 생각한다. 그래서 질 나쁜 와인병 바닥에 앙금이 쌓이듯 쓸모없는 코드가 점차 쌓여간다.


6장. 객체와 자료 구조

변수를 private으로 선언하더라도 각 값마다 get과 set 함수를 제공한다면 구현을 외부로 노출하는 셈이다.

객체가 포함하는 자료를 표현할 가장 좋은 방법을 심각하게 고민해야 한다. 아무 생각 없이 조회/설정 함수를 추가하는 방법이 가장 나쁘다.

객체 지향 코드에서 어려운 변경은 절차적 코드에서 쉬우며, 절차적 코드에서 어려운 변경은 객체 지향 코드에서 쉽다!


8장. 경계

외부 코드를 익히기는 어렵다. 외부 코드를 통합하기도 어렵다. 두 가지를 동시에 하기는 두 배나 어렵다. 다르게 접근하면 어떨까? 곧바로 우리 쪽 코드를 작성해 외부 코드를 호출하는 대신 먼저 간단한 테스트 케이스를 작성해 외부 코드를 익히면 어떨까? 이를 학습 테스트라 부른다.

학습 테스트는 이해를 높여주는 정확한 실험이다. 공짜 이상이다. 투자하는 노력보다 얻어지는 성과가 더 크다.

외부 패키지를 호출하는 코드를 가능한 줄여 경계를 관리하자.


9장. 단위 테스트

애자일과 TDD 덕택에 단위 테스트를 자동화하는 프로그래머들이 이미 많아졌으며 점점 더 늘어나는 추세다. 하지만 우리 분야에 테스트를 추가하려고 급하게 서두르는 와중에 많은 프로그래머들이 제대로 된 테스트 케이스를 작성해야 한다는 좀더 미묘하고 더 중요한 사실을 놓쳐버렸다.

코드에 유연성, 유지 보수성, 재사용성을 제공하는 버팀목이 바로 단위 테스트다. 이유는 단순하다. 테스트 케이스가 있으면 변경이 두렵지 않으니까! 테스트 케이스가 없다면 모든 변경이 잠정적인 버그다. 아키텍처가 아무리 유연하더라도, 설계를 아무리 잘 나누었더라도, 테스트 케이스가 없으면 개발자는 변경을 주저한다. 버그가 숨어들까 두렵기 때문이다.

클린 테스트 코드를 만들려면? 세가지가 필요하다. 가독성, 가독성, 가독성.

테스트 함수마다 assert 하나만 사용하기 보다는 개념 하나만 테스트하라.

클린 테스트 5가지 규칙(FIRST). 빠르게, 독립적으로, 반복 가능하게, 자가 검증하는, 적시에.
Fast, Independent, Repeatable, Self-validation, Timely.

테스트 코드가 방치되어 망가지면 실제 코드도 망가진다. 테스트 코드를 깨끗하게 유지하자.


10장. 클래스

클래스를 만들 때 첫 번째 규칙은 크기다. 클래스는 작아야 한다. 두 번째 규칙도 크기다. 더 작아야 한다.

함수는 물리적인 행 수로 크기를 측정했었다. 클래스는 다른 척도를 사용한다. 클래스가 맡은 책임을 센다.

간결한 이름이 떠오르지 않는다면 분명 클래스 크기가 너무 커서 그렇다.

도구 상자를 어떻게 관리하고 싶은가? 작은 서랍을 많이 두고서 기능과 이름이 명확한 컴포넌트를 나눠 넣고 싶은가? 아니면 큰 서랍 몇 개를 두고서 모두를 던져 넣고 싶은가?
큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더 바람직하다. 작은 클래스는 각각 책임이 하나이며, 변경할 이유가 하나이며, 다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행한다.

“함수를 작게, 매개변수 목록을 짧게”라는 전략을 따르다 보면 때때로 몇몇 메소드만이 사용하는 인스턴스 변수가 아주 많아진다. 이때가 십중팔구 새로운 클래스로 쪼개야 한다는 신호다.


11장. 시스템

‘처음부터 올바르게’ 시스템을 만들 수 있다는 믿음은 미신이다. 대신에 우리는 오늘 주어진 사용자 스토리에 맞춰 시스템을 구현해야 한다. 내일은 새로운 스토리에 맞춰 시스템을 조정하고 확장하면 된다. 이것이 반복적이고 점진적인 애자일 방식의 핵심이다.


12장. 창발성

켄트 백이 제시한 간단한 설계 규칙 4가지.
모든 테스트를 실행한다. 중복은 없앤다. 프로그래머 의도를 표현한다. 클래스와 메소드 수를 최소로 줄인다.

코드를 정리하면서 시스템이 깨질까 걱정할 필요가 없다. 테스트 케이스가 있으니까!

작은 재사용을 제대로 익혀야 큰 재사용이 가능하다.

자신의 작품을 조금 더 자랑하자. 함수와 클래스에 조금 더 시간을 투자하자. 더 나은 이름을 선택하고, 큰 함수를 작은 함수 여럿으로 나누고, 자신의 작품에 조금만 더 주의를 기울이자. 주의는 대단한 재능이다.

경험을 대신할 단순한 개발 기법이 있을까? 당연히 없다.


13장. 동시성

동시성은 결합을 없애는 전략이다. 즉, 무엇과 언제를 분리하는 전략이다.

독립적인 스레드로, 가능하면 다른 프로세서로 돌려도 괘찮도록 자료를 독립적인 단위로 분할하라.

각 알고리즘을 공부하고 해법을 직접 구현해보라. 그러면 나중에 실전 문제에 부닥쳤을 때 해결이 쉬워지리라.

임계영역 개수를 줄인답시고 거대한 임계영역 하나로 구현하는 순진한 프로그래머도 있다.

‘일회성’ 문제를 계속 무시한다면 잘못된 코드 위에 코드가 계속 쌓인다.


14장. 점진적인 개선

여러분이 깨끗하고 우아한 프로그램을 한 방에 뚝딱 내놓으리라 기대하지 않는다. 지난 수십 여 년 동안 쌓아온 경험에서 얻은 교훈이라면, 프로그래밍은 과학보다 공계에 가깝다는 사실이다. 클린 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다는 의미다.

프로그램을 망치는 가장 좋은 방법 중 하나가 개선이라는 이름 아래 구조를 크게 뒤집는 행위다.

TDD는 시스템을 망가뜨리는 변경을 허용하지 않는다. 변경을 가한 후에도 시스템이 변경 전과 똑같이 돌아가야 한다는 소리다.

리팩토링은 루빅 큐브 맞추기와 비슷하다. 큰 목표 하나를 이루기 위해 자잘한 단계를 수없이 거친다. 각 단계를 거쳐야 다음 단계가 가능하다.

단순히 돌아가는 코드에 만족하는 프로그래머는 전문가 정신이 부족하다. 나쁜 코드보다 더 오랫동안 더 심각하게 개발 프로젝트에 악영향을 미치는 요인도 없다. 나쁜 일정은 다시 짜면 된다. 나쁜 요구사항은 다시 정의하면 된다. 나쁜 팀 역학은 복구하면 된다. 하지만 나쁜 코드는 썩어 문드러진다.

걸린시간

뽀모도로 14번. (25분 x 14 = 약 7시간)

Comments