관리 메뉴

KorSA

Clean Code - 로버트 C.마틴 본문

Books/Software Developments

Clean Code - 로버트 C.마틴

Praiv. 2020. 7. 7. 09:35
320x100

이 글은 로버트 C.마틴의 [Clean Code] 를 읽고 정리한 내용입니다.

 

 

[ 1장 - 깨끗한 코드 ]

#1. 보이 스카우트 규칙

아무리 잘 짠 코드도 시간이 지나면 엉망으로 되기 쉽다. 그래서 우리는 코드를 잘 짜는 것 이외에도 코드의 퇴보를 잘 막는 조치가 필요하다. 미국 보이스카우트의 규칙이 코드 관리에도 유용하다.

 

    캠프장에 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라.

 

체크아웃할때보다 체크인할때 코드를 더 깔끔하게 해놓자.

 

 

[ 2장 - 의미 있는 이름]

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

 

#2. 멤버 변수 접두어 (m_ 등) 를 붙일 필요가 없다

클래스와 함수는 접두어가 필요없을 정도로 작아야 마땅하고, 멤버 변수를 다른 색상으로 표시하거나 눈에 띄게 보여주는 IDE를 사용해야 마땅하다. 게다가 사람들은 접두어(또는 접미어)를 무시하고 이름을 해독하는 방식을 재빨리 익힌다.

코드를 읽을수록 접두어는 관심 밖으로 밀려난다.

 

[3장 - 함수]

#1. 함수는 한 가지 일만 해라

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

 

#2. 서술적인 이름을 사용하라

이름이 길어도 괜찮다. 겁먹을 필요없다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다. 길고 서술적인 이름이 길고 서술적인 주석보다 좋다. 함수 이름을 정할 때는 여러 단어가 쉽게 읽히는 명명법을 사용한다. 그런 다음, 여러 단어를 사용해 함수 기능을 잘 표현하는 이름을 선택한다.

 

#3. 부수 효과를 일으키지 마라

부수 효과는 거짓말이다. 함수에서 한 가지를 하겠다고 약속하고선 남몰래 다른 짓도 하니까. 떄로는 예상치 못하게 클래스 변수를 수정한다. 때로는 함수로 넘어온 인수나 시스템 전역 변수를 수정한다. 어느 쪽이든 교활하고 해로운 거짓말이다. 많은 경우 시간적인 결합(temporal coupling)이나 순서 종속성(order dependency)을 초래한다.

 

#4. 출력 인수를 되도록 피하라

일반적으로 우린은 인수를 함수 입력으로 해석하지, 출력으로 해석하지 않는다.

 

#5. 명령과 조회를 분리하라

함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다. 둘 다 하면 안 된다. 객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다. 둘 다 하면 혼란을 초래한다.

 

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

명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 미묘하게 위반한다. 자칫하면 if문에서 명령을 표현식으로 사용하기 쉬운 탓이다.

 

#7. 함수는 어떻게 짜는가?

  소프트웨어를 짜는 행위는 여느 글짓기와 비슷하다. 논문이나 기사를 작성할 때는 먼저 생각을 기록한 후 읽기 좋게 다듬는다. 초안은 대개 서투르고 어수선하므로 원하는 대로 읽힐 때까지 말을 다듬고 문장을 고치고 문단을 정리한다.

  내가 함수를 짤 떄도 마찬가지다. 처음에는 길고 복잡하다. 들여쓰기 단계도 많고 중복된 루프도 많다. 인수 목록도 아주 길다. 이름은 즉흥적이고 코드는 중복된다. 하지만 나는 그 서투른 코드를 빠짐없이 테스트하는 단위 테스트 케이스를 만든다.

  그런 다음 나는 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거한다. 메서드를 줄이고 순서를 바꾼다. 때로는 전체 클래스를 쪼개기도 한다. 이 와중에도 코드는 항상 단위 테스트를 통과한다.

 

[ 5장 - 형식 맞추기 ]

#1. 형식을 맞추는 목적

코드 형식은 의사소통의 일환이다. 의사소통은 전문 개발자의 일차적인 의무이다. 어쩌면 '돌아가는 코드'가 전문 개발자의 일차적인 의무라 여길지도 모르겠다. 하지만 이 책을 읽으면서 생각이 바뀌었기 바란다. 오늘 구현한 기능이 다음 버전에서 바뀔 확률은 아주 높다. 그런데 오늘 구현한 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다. 오랜 시간이 지나 원래 코드의 흔적을 더 이상 찾아보기 어려울 정도로 코드가 바뀌어도 맨 처음 잡아놓은 구현 스타일과 가독성 수준은 유지보수 용이성과 확장성에 계속 영향을 미친다. 원래 코드는 사라질지라도 개발자의 스타일과 규율은 사라지지 않는다.

 

#2. 적절한 행 길이를 유지하라

200줄 정도의 파일들로도 커다란 시스템을 구축할 수 있다. 반드시 지킬 엄격한 규칙은 아니지만 바람직한 규칙으로 삼으면 좋겠다. 일반적으로 큰 파일보다 작은 파일이 이해하기 쉽다.

 

[ 7장 - 오류 처리 ]

#1. null을 반환하지 마라

null을 반환하는 코드는 일거리를 늘릴 뿐만 아니라 호출자에게 문제를 떠넘긴다. 누구 하나라도 null 확인을 빼먹는다면 애플리케이션이 통제 불능에 빠질지도 모른다.

 

#2. null을 전달하지 마라

메서드에서 null을 반환하는 방식도 나쁘지만 메서드로 null을 전달하는 방식은 더 나쁘다. 정상적인 인수로 null을 기대하는 API가 아니라면 메서드로 null을 전달하는 코드는 최대한 피한다. 대다수 프로그래밍 언어는 호출자가 실수로 넘기는 null을 적절히 처리하는 방법이 없다. 그렇다면 애초에 null을 넘기지 못하도록 금지하는 정책이 합리적이다.

 

[ 8장 - 경계 ]

#1. 깨끗한 경계

경계에서는 흥미로운 일이 많이 벌어진다. 변경이 대표적인 예다. 소프트웨어 설계가 우수하다면 변경하는데 많은 투자와 재작업이 필요하지 않다. 

 

[ 9장 - 단위 테스트 ]

#1. 테스트 당 개념 하나

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

 

#2. FIRST

F(Fast): 테스트는 빨라야 한다. 테스트가 느리면 자주 돌릴 엄두를 못 낸다.

I(Independent): 각 테스트는 서로 의존하면 안된다.

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

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

T(Timely): 테스트는 적시에 작성해야 한다. 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다. 실제 코드를 구현한 다음에 테스트 코드를 만들면 실제 코드가 테스트하기 어렵다는 사실을 발견할지도 모른다. 어떤 실제 코드는 테스트하기 너무 어렵다고 판명날지 모른다. 테스트가 불가능하도록 실제 코드를 설계할지도 모른다.

 

[ 10장 - 클래스 ]

#1. 클래스는 작아야 한다

큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 위뤄진 시스템이 더 바람직하다. 큼직한 다목적 클래스 몇 개로 이뤄진 시스템은 (변경을 가할 때) 당장 알 필요가 없는 사실까지 들이밀어 독자를 방해한다.

 

#2. 응집도

응집도가 가장 높은 클래스는 가능하지도 바람직하지도 않다. 그렇지만 우리는 응집도가 높은 클래스를 선호한다. 응집도가 높다는 말은 클래스에 속한 메서드와 변수가 서로 의존하며 논리적인 단위로 묶인다는 의미이기 때문이다.

 

#3. 응집도를 유지하면 작은 클래스 여럿이 나온다

 

#4. OCP(Open-Closed Principle)

OCP란 클래스는 확장에 개방적이고 수정에 폐쇄적이어야 한다는 원칙이다. 새 기능을 수정하거나 기존 기능을 변경할 때 건드릴 코드가 최소인 시스템 구조가 바람직하다. 이상적인 시스템이라면 새 기능을 추가할 떄 시스템을 확장할 뿐 기존 코드를 변경하지는 않는다.

 

[ 11장 - 시스템 ]

#1. 추상화

깨끗한 코드를 구현하면 낮은 추상화 수준에서 관심사를 분리하기 쉬워진다. 이 장에서는 높은 추상화 수준, 즉 시스템 수준에서도 깨끗함을 유지하는 방법을 살펴본다.

 

#2. 관심사

시작 단계는 모든 어플리케이션이 풀어야 할 관심사다. 관심사 분리는 우리 분야에서 가장 오래되고 가장 중요한 설계 기법 중 하나다.

 

#3. 확장

  '처음부터 올바르게' 시스템을 만들 수 있다는 믿음은 미신이다. 대신에 우리는 오늘 주어진 사용자 스토리에 맞춰 시스템을 구현해야 한다. 내일은 새로운 스토리에 맞춰 시스템을 조정하고 확장하면 도니다. 이것이 반복적이고 점진적인 애자일 방식의 핵심이다. 테스트 주도 개발(TDD), 리팩터링, (TDD와 리팩터링으로 얻어지는) 깨끗한 코드는 코드 수준에서 시스템을 조정하고 확장하기 쉽게 만든다.

  하지만 시스템 수준에서는 어떨까? 시스템 아키텍처는 사전 계획이 필요하지 않을까? 단순한 아키텍처를 복잡한 아키텍처로 조금씩 키울 수 없다는 현실은 정확하다. 맞는 말 아닌가? 나중에 보겠지만, 소프트웨어 시스템은 '수명이 짧다'는 본질로 인해 아키텍처의 점진적인 발전이 가능하다. 다만 이 경우, 관심사를 적절히 분리해 관리해야 한다는 선행 조건이 붙는다.

 

 [ 12장 - 창발성 ]

#1. 착실하게 따르기만 하면 우수한 설계가 나오는 간단한 규칙 4가지

1) 모든 테스트를 실행한다.

2) 중복을 없앤다.

3) 프로그래머 의도를 표현한다.

4) 클래스와 메서드 수를 최소로 줄인다.

 

[ 13장 - 동시성 ]

#1. 동시성

동시성은 결합을 없애는 전략이다. 즉, 무엇과 언제를 분리하는 전략이다. 스레드가 하나인 프로그램은 무엇과 언제가 서로 밀접하다.

 

#2. 동시성의 미신

1) 동시성은 항상 성능을 높여준다.

2) 동시성을 구현해도 설계는 변하지 않는다.

3) 웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다.

 

#3. 동시성에 관한 타당한 생각

1) 동시성은 다소 부하를 유발한다.

2) 동시성은 복잡하다.

3) 일반적으로 동시성 버그는 재현하기 어렵다.

4) 동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야 한다.

 

#4. 스레드 코드를 테스트하기 위한 몇가지 지침

1) 말이 안 되는 실패는 잠정적인 스레드 문제로 취급하라.

2) 다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자.

3) 다중 스레드를 쓰는 코드 부분을 다양한 환경에 쉽게 끼워 넣을 수 있도록 스레드 코드를 구현하라.

4) 다중 스레드를 쓰는 코드 부분을 상황에 맞춰 조정할 수 있게 작성하라.

5) 프로세서 수보다 많은 스레드를 돌려보라.

6) 다른 플랫폼에서 돌려보라.

7) 코드에 보조 코드를 넣어 돌려라. 강제로 실패를 일으키게 해보라.

8) 흔들기 기법(jiggle)을 사용해 오류를 찾아내라.

 

[ 14장 - 점진적인 개선 ]

#1. 공예

지난 수십여 년 동안 쌓아온 경험에서 얻은 교훈이라면, 프로그래밍은 과학보다 공예(craft)에 가깝다는 사실이다. 깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다는 의미다.

728x90
728x90
Comments