Clean_Code
13 posts
13장 동시성

Intro 동시성과 깔끔한 코드는 양립하기 어렵다. 스레드를 하나만 실행하는 코드는 짜기가 쉽다. 간략히 알아보자. 동시성이 필요한 이유? 동시성은 결합(coupling) 을 없애는 전략이다. 즉, 무엇(what) 과 언제(when) 를 분리하는 전략이다. 그래서 디버깅을 할 때 **정지점(breakpoint)**을 정한 후 어느 정지점에 걸렸는지 살펴본다. 서블릿은 EJB컨테이너 안에서 돌아가는데, 이들은 동시성을 부분적으로 관리한다. 각 서블릿 스레드는 다른 서블릿과 무관하게 돌아간다. 서블릿 모델이 제공하는 구조적 이점이 아주 크다. 하지만 작업 처리량(throughput) 개선이라는 요구로 인해 직접적인 동시성을 구현해야 한다. 미신과 오해 동시성은 향상 성능을 높여준다. 동시성은 때로 성능을 높여준다. 동시성을 구현해도 설계는 변하지 않는다. 무엇 과 언제 를 분리하면 시스템 구조가 크게 달라진다. 웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다. 어떻게 …

12장 창발성

Intro 창발적? 이라는 단어를 처음 들었다. 구글에 검색해보니, 뜻은 다음과 같다. 창발적 설계로 깔끔한 코드를 구현하자 켄트 벡이 제시한 를 적용하면 SRP나 DIP와 같은 원칙을 적용하기 쉬워진다. 켄트 벡은 다음 규칙을 따르면 설계는 ‘단순하다’고 말한다. 모든 테스트를 실행한다. 중복을 없앤다. 프로그래머 의도를 표현한다. 클래스와 메서드 수를 최소로 줄인다. 단순한 설계 규칙 1: 모든 테스트를 실행하라 어떠한 프로그램을 만들었을 때, 검증되지 않으면 무용지물이다. 검증되지 않은 프로그램은 절대 출시해서는 안 된다. 그렇기에 모든 테스트를 만들어야 하는데, SRP를 준수하면 단순해진다. 결합도가 높으면 테스트 케이스를 작성하기 어렵다. DIP를 적용하고 DI, 인터페이스, 추상화 등과 같은 도구로 결합도를 낮춘다. 즉, 테스트 케이스를 작성하면 설계 품질이 높아진다. 단순한 설계 규칙 2 ~ 4: 리팩터링 리팩터링 단계에서는 소프트웨어 설계 품질을 높이는 방법이라면 뭐…

11장 시스템

Intro 도시를 세운다면? 도시는 적절한 추상화와 묘율화로 돌아간다. 그래서 큰 그림을 이해하지 않아도 개인과 개인이 관리하는 는 효율적으로 돌아간다. 소프트웨어도 똑같다. 깨끗한 코드는 낮은 추상화 수준에서 관심사를 분리하기 쉬워진다. 이번엔 높은 추상화 수준, 즉 시스템 수준에서도 꺠끗함을 유지하는 방법을 알아보자! 시스템 제작과 시스템 사용을 분리하라 를 분리해야한다. 위와 같은 방법이 초기화 지연(Lazy Initialization) 혹은 계산 지연(Lazy Evaluation) 기법이다. 장점 실제로 필요할 때까지 객체를 생성하지 않으므로 불필요한 부하가 걸리지 않는다. 따라서 애플리케이션을 시작하는 시간이 빨라진다. 어떤 경우에도 null 포인터를 반환하지 않는다. 단점 getService 메서드가 MyServiceImpl과 생성자 인수에 명시적으로 의존한다. 의존성을 해결하지 않으면 컴파일이 안된다. MyServiceImpl이 무거운 객체라면 테스트 전용 객체(TES…

10장 클래스

Intro 코드의 표현력과 그 코드로 이루어진 함수에 아무리 신경 쓸지라도 좀 더 차원 높은 단계까지 신경쓰지 않으면 깨끗한 코드를 얻기는 어렵다. 클래스 체계 표준 자바 관례에 따르면, 변수 목록이 가장 먼저 나온다. : , 상수 -> -> 비공개 인스턴스 변수 -> -> 즉, 추상화 단계가 순차적으로 내려간다. 그래서 신문처럼 읽힌다. 캡슐화 같은 패키지 안에서 테스트 코드가 함수를 호출하거나 변수를 사용해야 한다면 그 함수나 변수를 로 선언하거나 패키지 전체로 공개한다. 캡슐화를 풀어주는 결정은 언제나 최후의 수단이다. 클래스는 작아야 한다! 함수는 로 크기를 측정했다면, 클래스는 을 센다. 클래스 이름에 , , 등과 같이 모호한 단어가 있다면 클래스에다 여러 책임을 떠 안겼다는 증거다. 또한 클래스 설명을 , , , 을 사용하지 않고서 25단어 내외로 가능해야 한다. 단일 책임 원칙 은 클래스나 모듈을 변경할 이유가 하나, 단 하나뿐이어야 한다는 원칙이다. 큰 …

9장 단위 테스트

Intro 이 장에서는 단위 테스트에 대해 다룬다. 과거엔 드라이버 코드를 급조해 결과물이 나오는 것을 팀원들에게 보여줘야 했다. 그리고 버렸다. 하지만 현재는 애자일과 TDD 덕택에 단위 테스트를 자동화하는 프로그래머들이 많아졌다. 그러던 중 많은 프로그래머들이 놓친 미묘한 사실을 알아가보자. TDD 법칙 세 가지 첫째 법칙: 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다. 둘째 법칙: 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다. 셋째 법칙: 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다. 위 세 가지 법칙을 따르면 개발과 테스트가 대략 30초 주기로 묶인다. 또한 매일 많은 양에 달하는 테스트 케이스가 나온다. 사실상 전부 테스트하는 테스트 케이스가 나온다. 하지만 실제 코드와 맞먹을 정도로 방대한 테스트 코드는 심각한 관리 문제를 유발하기도 한다. 깨끗한 테스트 코드 유지하기 저자는 테스트 코드에 팀원들간 규칙…

8장 경계

Intro 우리가 시스템을 개발할 때 모두 직접 개발하지 않고, 패키지를 사던지 오픈소스를 이용한다. 또는 사내 다른 팀이 제공하는 컴포넌트를 사용한다. 이렇게 외부 환경과의 상호작용 방식을 정의하는 것이 라고 생각한다. 외부 코드 사용하기 인터페이스 제공자는 적용성을 최대한 넓히려 애쓴다. 인터페이스 사용자는 자신의 요구에 집중하는 인터페이스를 바란다. 여기서는 예시로 을 들고 있다. Sensor라는 객체를 담는 Map을 생성하고, Seonsor 객체가 필요한 코드는 Sensor 객체를 가져온다. 이 코드는 깨끗한 코드가 아니고, 의도도 분명히 드러나지 않는다. 를 사용하면 코드 가독성이 크게 높아진다. 하지만 이 방법도 ”Map가 사용자에게 필요하지 않은 기능까지 제공한다.“는 문제를 해결하지 못한다. 이렇게 하면 사용자는 제네릭스가 사용되었는지 여부를 신경 쓸 필요가 없다. Sensor 클래스 안에서 객체 유형을 관리하고 변환하기 때문이다. 이…

7장 오류 처리

Intro 깨끗한 코드와 오류 처리는 확실히 연관성이 있다. 여기저기 흩어진 오류 처리 코드 때문에 실제 코드가 하는 일을 파악하기가 거의 불가능하다. 오류 코드보다 예외를 사용하라 예전에는 오류 플래그를 설정하거나 호출자에게 오류 코드를 반환하는 방법이 전부였다. 이런 방법을 사용하면 호출자 코드가 복잡해진다. 그래서 오류가 발생하면 예외를 던지는 편이 낫다. 논리가 오류 처리 코드와 뒤섞이지 않으니까 말이다. 뒤섞였던 개념을 알고리즘에 따라 분리함으로써 각 개념을 독립적으로 살펴보고 이해할 수 있다. Try-Catch-Finally 문부터 작성하라 try 블록에서 무슨 일이 생기든지 호출자가 기대하는 상태를 정의하기 쉬워진다. 여기서 생성자가 던지는 을 잡아낼 수 있다. 강제로 예외를 일으키는 테스트 케이스 작성 테스트를 통과하게 코드 작성 -> try 블록의 트랜잭션 범위부터 구현하게 되므로 범위 내에서 트랜잭션 본질을 유지하기 쉬워진다. 미확인(Unchecked) 예외를 …

6장 객체와 자료 구조

Intro 변수를 으로 정의하는 이유가 있다. 남들이 변수에 의존하지 않게 만들기 위해서다. 그렇다면 왜 많은 개발자들이 함수와 함수를 당연하게 해 변수를 외부에 노출할까? 자료 추상화 더 나은 예시 변수를 private으로 선언하더라도 각 값마다 get함수와 set함수를 제공한다면 구현을 외부로 노출하는 셈이다. 구현을 감추려면 추상화가 필요하다. 더 나은 예시 인터페이스나 조회/설정 함수만으로는 추상화가 이뤄지지 않는다. 객체가 포함하는 자료를 표현할 가장 좋은 방법을 심각하게 고민해야 한다. 자료/객체 비대칭 객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 제공한다. 자료 구조는 자료를 그대로 공개하며 별다른 함수는 제공하지 않는다. 절차 지향 코드의 장단점 객체 지향 코드의 장단점 객체 지향 코드에서 어려운 변경은 절차 지향 코드에서 쉬우며, 절차 지향 코드에서 어려운 변경은 객체 지향 코드에서 쉽다! 디미터 법칙 디미터 법칙은 잘 알려진 휴리스틱으로, 모듈은 자신…

5장 형식 맞추기

Intro 깔끔하게 형식을 맞춰 코드를 짜야한다. 간단한 규칙을 정하고, 그 규칙을 착실히 따라야 한다. 팀으로 일한다면 팀 규칙을 따라야 한다. 필요하다면 도구도 활용하자. 형식을 맞추는 목적 오늘 구현한 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다. 맨 처음 잡아놓는 구현 스타일과 가독성 수준은 유지보수 용이성과 확장성에 계속 영향을 미친다. 원래 코드는 사라질지라도 개발자의 스타일과 규율은 사라지지 않는다. 적절한 행 길이를 유지하라 소스 코드는 얼마나 길어야 적당할까? 라는 질문에 이렇게 답한다. 500줄을 넘지 않고 대부분 200줄 정도인 파일로도 커다란 시스템을 구축할 수 있다. 신문 기사처럼 작성하라 이름은 간단하면서도 설명이 가능하게 짓는다. 첫 부분은 고차원 개념과 알고리즘을 설명한다. 아래로 내려갈수록 세세하게 묘사한다. 마지막에는 가장 저차원 함수와 세부 내역이 나온다. 개념은 빈 행으로 분리하라 생각 사이는 빈 행을 넣어 분리하자. 행 묶음…

4장 주석

Intro 코드로 의도를 표현하지 못했을 때, 즉 실패했을 때 우리는 주석을 사용한다. 하지만 주석은 프로그래머들이 유지보수하기는 현실적으로 불가능하다. 따라서 우리는 주석이(간혹 필요할지라도) 필요 없는 방향으로 애너지를 쏟아야 한다. 주석은 나쁜 코드를 보완하지 못한다 코드로 의도를 표현하라! 주석으로 의도를 표현한 경우 코드로 의도를 표현한 경우 주석으로 달려는 설명을 함수로 만들어 표현해도 충분하다. 좋은 주석 1) 법적인 주석 2) 정보를 제공하는 주석 이왕이면 시각과 날짜를 변환하는 클래스를 만들어 코드를 옮겨주면 더 좋고 더 깔끔하겠다. 3) 의도를 설명하는 주석 구현을 이해하게 도와주는 선을 넘어 결정에 깔린 의도까지 설명할 수 있다. 4) 의미를 명료하게 밝히는 주석 테스트 코드에서 사용할 수 있다. 5) 결과를 경고하는 주석 프로그램 효율을 높이기 위해 정적 초기화 함수를 사용하려던 열성적인 프로그래머가 주석 때문에 실수를 면한다. 6) TODO 주석 앞으로 할 …

3장 함수

Intro 추상화 수준이 너무 다양하고 코드가 길다. 두 겹으로 중첩된 if문은 이상한 플래그를 확인하고, 이상한 문자열을 사용하며, 이상한 함수를 호출한다. 코드를 이해하기가 어렵다. 하지만 매서드 몇 개를 추출하고, 이름 몇 개를 변경하고, 구조를 조금 변경하면 파악하기 어려웠던 정보가 쉽게 드러난다. 작게 만들어라 100줄 ~ 300줄 함수도 있고 20줄 ~ 30줄 함수도 있다. 최대한 작으면 작을 수록 좋다. 심지어 2줄 ~ 4줄인 함수도 있다. 줄일 수 있다면 줄이는게 좋다. 블록과 들여쓰기 if문 / else문 / while 문 등에 들어가는 블록은 한 줄이어야 한다. -> 대개 여기서 함수를 호출한다 함수에서 들여쓰기 수준은 1단이나 2단을 넘어서면 안 된다. 한 가지만 해라! 우리가 함수를 만드는 이유는 큰 개념을 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해서이다. 그렇기 때문에 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면, …

2장 의미있는 이름

Intro 나는 평소에 네이밍을 하는데 어려움이 있었다. 여러 컨벤션들이 있었지만, 내가 옆에 두고 하지 않는 이상 좋은 네이밍을 하기엔 어려웠다. 아직은 경험이 없고 익숙하지 않아서 일까?… 그래서 더더욱 이번 챕터를 재미있게 봤다. 의도를 분명히 밝혀라 따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다. 여기서 d는 아무 의미도 드러나지 않는다. 의도가 드러나는 이름을 사용하면 코드 이해와 변경이 쉬워진다. 그러면서도 영어 동사의 표현을 잘 알아야겠다는 생각이 든다. 다음 코드가 무엇을 하는지 살펴보자. 복잡한 문장은 없다. 공백과 들여쓰기도 적당하다. 변수는 세개, 상수는 두 개뿐이다. 문제는 코드의 단순성이 아니라 코드의 함축성이다. 즉, 코드 맥락이 코드 자체에 명시적으로 드러나지 않는다. theList에 무엇이 들었는가? theList에서 0번째 값이 어째서 중요한가? 값 4는 무슨 의미인가? 함수가 반환하는 리스트 list1을 어떻게 사용하는가? 그렇다면 …

1장 깨끗한 코드

Intro 회사에서 유지보수를 하다가 끔찍한 프로젝트를 보게 되었다. 변수명과 클래스 명은 모두 1,2,3 혹은 A, B, C로 구분되어 있었다. 이런 이유 때문에 유지보수 하는 일이 너무 힘들었다. 그래서 어떻게 하면 나는 이런 코드를 짜지 않을 수 있을까? 어떻게 하면 이 코드를 좋은 코드로 바꿀 수 있을까?라는 의문점이 생겼다. 그래서 미루고 미뤄왔던 클린코드를 읽어보려고 한다. 코드가 존재하리라 요즘 기술이 발전함에 따라 코드를 자동으로 생성해주는 기술들이 생겨나기 시작했다. 그럼에도 저자는 코드가 사라질 가망이 없다고 한다. 나쁜 코드 80년대 후반 킬러 앱 하나를 구현한 회사를 이야기하면서 시작한다. 회사가 망했는데, 그 원인은 바로 나쁜 코드 탓이었다. 나쁜 코드에 발목이 잡혀 고생하는 것을 고행(Wading)이라 부른다. 나쁜 코드로 치르는 대가 나쁜 코드가 쌓일수록 팀 생산성은 떨어진다. 그러다 팀은 재설계를 요구한다. 새로운 프로젝트를 시작하기 때문에 모두가 합류…