『클린 코드의 기술: 단순함의 노하우』 읽어보기

minjeong·2023년 7월 22일
0
post-thumbnail

클린 코드의 기술

책을 읽으면서 소화하지 못해 다시 읽어야 하는, 재밌거나 인상깊어서 다시 읽고 싶어지는 텍스트를 요약한 글.

로버트 C. 마틴이 기술한 『클린 코드』 는 실제 개발 산출물로써의 코드를 어떻게 클린하게 작성할 수 있는가에 대한 개발 방법론이라면, 이 책은 어떻게 프로그래밍의 생산성을 높일 수 있는가에 대한 더욱 넓은 주제의 책이었다. (물론 소스 코드를 클린하게 작성하는 방법에 대한 이야기가 주된 컨텐츠이긴 하다.)

정식 번역 서적 출간이 23년 1월인데 이 책이 회사에 있다니 반가운 마음에 냉큼 주말 동안 읽었다.


1장, 복잡성은 어떻게 생산성을 해치는가

복잡성(Complexity)

부분들로 이루어진 전체에 대한 것을 분석, 이해하기 어려운 정도.

컴퓨터 공학에서는 주어진 코드를 분석하여 계산 복잡도를 계산하는 등 엄격한 정의를 사용하고, 다른 경우에는 시스템 컴포넌트 사이의 상호 작용의 양 또는 구조의 정도로 느슨하게 정의하여 사용하기도 한다. 현재 이 책에서는 범용적으로, 그리고 전체 시스템 혹은 엔티티를 대상으로 사용하고자 한다.

프로그래머가 직면할 수 있는 복잡성의 원천은 다음과 같다.

  • 프로젝트 생명주기의 복잡성
    • 계획, 설계, 구현 및 테스트에 이르는 과정 속 이해 관계 충돌, 한정된 자원 등에 의한 복잡성을 말한다.
  • 소프트웨어와 알고리즘 이론의 복합성
    • algorithmic complexity -> 성능에 직결된다.
    • 계산 복잡도를 낮춰 자원 활용의 효율을 높이고 비용을 줄이고자 한다.
  • 학습의 복잡성
    • 개발을 위해 요구되는 컴퓨터 공학적 배경 및 도메인에 요구되는 사전 배경, 사용할 언어 및 프레임워크의 필요 전문성 등에 의한 복잡성을 말한다.
  • 프로세스의 복잡성
    • 소프트웨어 개발 과정의 프로세스는 개발, 테스트(QA), 출시라는 사이클을 가지지만 세부 프로세스가 다양해지고 커지면서 가지는 복잡성을 말한다.
  • ...

결론

"개발자는 복잡성을 극복해야 한다!"

2장, 80:20 원칙

80:20 원칙

대부분의 결과(80%)가 소수의 원인(20%)에서 온다.

이 원칙(이하 '파레토 원칙')은 소프트웨어 프로젝트에도 적용 가능하며, 실제로 연구에서 밝혀진 바가 있다. 어떤 소프트웨어의 소스 코드 실행 시간 전체 중 대다수의 실행 시간을 차지하는 것은 소수 코드이다. 따라서, 사소한 다수를 최적화하는 데에 오랜 시간을 소비하더라도 전반적인 실행 시간 향상에는 거의 영향이 없게 된다. 20%의 코드를 반복적으로 최적화하는 것이 중요하다.

(여기서부턴 왜 이런 논점으로 이야기가 흘러가는지 이해하진 못했다.)

그리고 이 원칙을 소프트웨어 프로젝트의 생산성, 즉 참여 개발진의 성과를 지표로 계산한 생산성에 적용할 수도 있다? 그리고 이것은 고소득으로 이어지므로, 어떻게 개발자는 자신의 성과를 높여 고소득자가 될 수 있을까? 저자는 이런 속담을 인용한다. "열두 가지 재주 있는 사람이 밥 굶는다" 라고. 다양한 활동, 학습 하지 말고 중요한 영역의 전문 지식을 쌓으라고 말한다. '위대한 프로그래머'는 평균적인 프로그래머가 만드는 생산성의 10,000배 가치가 있다고 한다. 따라서 그렇게 되기 위해서는 20%의 활동에 집중해야 하고, 그 중 하나의 방법은 더 많은 코드를 쓰는 것이라고 한다.

(흥... 100명이 들으면 100명 모두 동의할 말이다. 그리고 범용적으로 쓰여도 맞는 말일 것이다. 굉장히 자기 계발 서적 같은 챕터다...)

3장, 최소 기능 제품 만들기

최소 기능 제품(minimum viable product, MVP)

가장 필요한 기능들 외에는 모두 제거한 제품의 버전.

배포할 때마다 단지 하나의 기능을 구현(=MVP)하여 시장을 분석하고 전략을 설정하는 빠른 프로토타이핑 형태의 사이클을 가지면 좋다. 자동적으로 최소 기능들의 조합으로 이루어진 아키텍처 형태로 서비스가 구성될 것이고, 분리된 단위 테스트가 함께 확장될 것이다.

4장, 클린하고 단순한 코드 작성하기

클린 코드

읽고 이해하고 고치기 쉬운 코드.

작성한 코드를 단 한 번도 개선하지 않은 클린 코드는 없다. 코드 기반을 개선하고 복잡성을 줄이는 리팩토링도 주기적으로 수행해야 한다. 코드를 작성하고 리팩토링 하는 데에는 여러 원칙이 있다.

1. 큰 그림을 생각하라

좋은 소프트웨어 아키텍처가 곧 코드를 클린하게 한다. 소프트웨어 아키텍처는 다수의 파일, 모듈, 라이브러리 등의 구성 요소가 어떻게 상호 작용하는지 정의한 것이다. 좋은 아케틱처는 성능, 유지보수성, 사용성 개선에 큰 영향을 끼친다.

그렇다면 어떻게 좋은 소프트웨어 아키텍처를 만들 수 있을까? 기능을 정의하고 디자인해야 한다.

2. 거인들의 어깨 위에 서라

거인들, 즉 세계 최고의 프로그래머들은 잘 튜닝되고 검증된 알고리즘, 코드와 같은 유산을 남겼다. 자동차를 만드는데 바퀴를 재발명할 필요가 있는가? 프로그래밍도 그렇다. 라이브러리 코드를 적극적으로 사용한다!

3. 기계가 아닌 사람을 위한 코드

high-level language로 프로그래밍을 한다면, 궁극적으로 그 사용 언어의 목적은 프로그래머가 코드를 작성하는 일을 돕는 것이다.

4. 올바른 이름을 사용하라

  • 서술적인 이름을 고른다.
  • 모호하지 않은 이름을 고른다.
  • 발음하기 쉬운 이름을 고른다.
    • 잠재의식적으로 프로그래머는 코드를 "읽기" 때문에 코드 해독은 발음하기 쉬울 수록 더욱 용이하다.
  • 상수에 이름을 붙인다.

이 외에도 여러 클린 코드를 위한 네이밍 규칙은 많으니 구글링을 하여 학습하고 정해두는 것이 좋다.

5. 표준을 지키고 일관성을 유지하라

IDE와 린터를 사용해 스타일을 일관되게 유지하여 가독성을 높인다.

6. 주석을 사용하라

주석을 이용해 명명 규칙으로도 해결되지 않는 이해를 돕는다. 주석은 곧 코드의 불록을 추상화한다.

7. 불필요한 주석을 피하라

모든 주석이 코드 이해에 도움을 주는 것은 아니다. 오히려 명료성을 낮추고 코드를 읽는 사람을 혼란스럽게 할 수 있다.

  • 인라인 주석을 사용하지 않는다.
    • 의미있는 네이밍을 하면 전적으로 사용하지 않을 수 있게 된다.
  • 옛날 코드를 주석 처리하지 않는다.
    • 코드의 가독성을 매우매우 해친다.
    • 옛날 코드는 형상 관리 도구를 사용하여 관리하면 된다.
  • 문서화 기능을 사용한다.

8. 놀람 최소화 원칙

어떤 시스템의 컴포넌트는 사용자가 예상하는 대로 동작해야 한다.

9. 반복하지 않는다

10. 단일 책임 원칙

모든 클래스와 함수는 한 개의 책임(=주요 목적)을 가진다.

동시에 모든 것을 해내는 하나의 거대 컴포넌트가 아닌, 다수의 작은 컴포넌트를 사용하도록 개발해야 한다. 기능성을 캡슐화하면, 전체적인 코드 복잡도가 낮아진다.

11. 테스트

12. 작은 것이 아름답다

13. 디미터의 법칙

코드 요소들의 상호 의존성을 최소화해야 한다.

14. 필요하지 않아요

당장 필요한 코드만 작성한다. (미래의 요구사항 및 이해 관계를 위한 코드를 작성하지 않는다.) 과도한 엔지니어링을 피해야 한다.

15. 과도한 들여쓰기 금지

수없이 중첩되어 들여쓰기의 depth가 높으면 코드의 가독성이 떨어지게 된다.

16. 지표를 사용하라

코드의 복잡성을 추적할 수 있는 코드 품질 지표를 사용해야 한다. 비공식적이지만 궁극적인 지표는 WTF(Wath the Fuck), 코드를 읽는 동안 몇 번의 욕을 하는지 ㅋㅋ 이다. IDE에는 많은 도구, 플러그인들이 복잡도를 계산해 준다고 하니 적극적으로 사용해보는 것이 좋다.

17. 보이 스카웃 법칙과 리팩토링

야영장을 떠날 때는 들어올 때보다 깨끗하게 하라.

5장, 성급한 최적화는 모든 악의 근원

성급한 최적화의 유형들

코드 함수의 최적화

함수가 얼마나 쓰일지 알 수 없다면 최적화를 함부로 하지 않는다.

기능 최적화

반드시 필요로 하지 않는 기능을 추가하거나 최적화를 하느라 시간을 낭비하지 않는다.

계획 최적화

"세상에 가치 있는 것을 배포하려면 불완전함을 수용해야 한다."

소프트웨어 프로젝트 생명주기의 계획 단계에서 최적화를 도입할 경우, 오히려 지연을 발생시킨다.

확장성 최적화

실제 사용자를 만나기 전에 확장성을 최적화하는 것은 주의가 분산되고, 개발자와 서버 비용을 낭비할 수 있다. (수백만 명의 사용자가 방문할 것을 기대하고 예상한 시나리오 대로 설계한 분산 아키텍처를 구현하는 사례와 같다. 이럴수가. 완전 흔한 사례다.)

테스트 설계 최적화

테스트 주도 개발을 위해 "완벽한 단위 테스트"를 설계하는 일은 불필요한 복잡성을 늘리고 소프트웨어 개발 주기를 느리게 만들 수 있다. (어떤 기능이나 상황은 100%의 테스트 커버리지를 만들기가 어려운데, 그를 도달하기 위해 노력하는 상황을 말하는 것 같다.)

객체지향 최적화

성급한 "개념적" 최적화는 불필요한 복잡성을 만든다. 객체지향적 설계를 위해 불필요한 계층 구조를 만들고 모델링하게 되는 경우를 말한다.

성능 튜닝을 위한 팁

측정을 먼저, 개선은 다음

소프트웨어의 성능을 측정한 후에 최적화를 진행해야 한다. 최적화 목표 및 성공 여부를 판단할 수 있는 기준점(benchmark)도 이 측정으로부터 만들어진다.

파레토

파레토 원칙(80:20 원칙)을 성능 최적화에도 적용한다. 실제 많은 자원을 사용하거나 지연을 발생시키는 코드를 측정을 통해 발견한 후 해당 부분만 최적화한다.

알고리즘 최적화

대부분의 병목 현상은 알고리즘과 자료구조를 튜닝하여 해결 가능하다.

  • 더 나은 알고리즘이 있는가?
  • 기존의 알고리즘이 수정 가능한가?
  • 자료구조를 개선할 수 있는가?

캐시

캐싱 정책을 세우고 활용한다.

7장, 한 개의 일을 잘하기와 다른 유닉스 원칙들

"한 개의 일을 잘 하자(Do one thing and do it well)"

유닉스 철학의 기본적 생각은 확장하고 유지보수하기 쉽도록 단순하고 분명하고 정확하고 모듈화된 코드를 작성하는 것이다.

각 함수는 한 개의 일을 잘한다

작게 유지한 함수는 기존 기능성을 기반하는 새로운 코드를 쉽게 만들 수 있게 하고, 중복성도 발생하지 않게 만든다. 이는 코드의 모듈화로 이어지고 재사용성, 유지보수성과 확장성이 좋아진다.

단순함이 복잡함보다 좋다

작은 것이 아름답다

코드 블럭을 작게 만든다.

프로토타입을 가능한 빠르게 만든다

효율성보다는 이식성을 선택한다

어떤 시스템 혹은 프로그램이 한 환경에서 다른 환경으로 이동했을 때 여전히 정상적으로 동작하는 능력

이식성과 효율성의 트레이드-오프 관계를 이해해야 한다. 이식성이 좋다는 것은 효율성의 희생이 따른 결과다. 한 환경에 최적화되어 매우 효율적이라는 것은 이식성이 그만큼 희생된 결과인 것과 같다.

데이터는 플랫 텍스트 파일에 저장한다

플랫 텍스트 파일에 저장하면 파일 내용을 접근하는 고도의 메커니즘을 갖지 않는 이진 파일이므로, 쉽게 데이터이 CRUD가 가능하다. 더 적은 메모리를 차지하기도 한다. 최적화된 데이터 표현 형식을 사용하고자 할 경우 확실한 검증을 통해 결정해야 한다.

소프트웨어를 레버리지로 사용한다

캡티브 사용자 인터페이스를 피한다

프로그램의 주요 실행 흐름이 시작되기 전에 사용자의 상호작용을 요구하는 기능, 인터페이스

모든 프로그램을 필터로 만든다

더 나쁜 것이 좋다

시간과 자원은 한정적이므로, 완벽한 제품을 구현하여 배포하는 것보다는 부족하고 나쁜 제품을 먼저 배포한 뒤 완성해나가는 것이 효율적일 수 있다는 뜻이다.

클린 코드가 영리한 코드보다 좋다

다른 프로그램과 연결되는 프로그램을 설계한다

코드를 견고하게 만든다

  • 형상 관리 시스템을 사용해 이전 버전의 코드를 복원할 수 있도록 한다.
  • 데이터를 정기적으로 백업해 복구 가능하도록 만든다.
  • 분산 시스템을 사용하여 단일 장애점 문제를 회피한다.

여러분이 할 수 있는 것은 고친다, 하지만 실패는 빠르고 시끄럽게

오류는 축적될 수 있다. 프로그래머는 사용성 개선을 위해 오류를 감추기를 원하지만 이것이 가장 합리적인 접근법은 아니다.

손 해킹(hand-hacking)을 피한다: 할 수 있다면 프로그램을 생성하는 프로그램을 만든다

profile
신입 개발자 👩‍💻

0개의 댓글