Computer Science./Books.

[Books.] Clean Code - Robert O. Martin

Chuuu_DevCamp:) 2022. 10. 25. 01:21
반응형

회사에서 코드 리뷰를 하고 받을 수록, 현재 담당하는 업무를 진행할 수록 "좋은 코드"에 대한 생각을 점점 더 많이 하게된다. 한 줄을 수정 하더라도 '어떻게 수정해야 가독성이 좋아질까', '어떻게 해야 다른사람이 쉽게 코드를 이해할 수 있을까' 같은 고민이 들어 다시 클린 코드 책을 읽게 되었다.

 

두 번째 이 책을 읽으면서 처음 읽었을 때와 느껴지는 점이 조금 달라진 것 같다. 이전에 제대로 현업에 적용하지 못했던 부분이나 부족했던 부분을 정리해서 앞으로의 업무에 적용해 보려고 한다.

 

좋은 코드란 무엇일까?

좋은 코드란, 가독성이 뛰어나고 원활한 유지보수성을 가진 코드이다.

대표적으로 다음과 같은 조건을 충족하는 코드라고 생각한다.

 - 처음 코드를 보더라도 쉽게 읽을 수 있어야 한다

 - 5년 뒤에 내가 다시 보더라도 금세 이해할 수 있어야 한다

 - 새로운 기능을 추가 / 변경 하더라도 큰 구조 변경을 하지 않아야 한다

위와 같은 코드를 만들기 위해 변수명을 정할 때도 가독성과 유지보수성을 생각 하며 프로 의식을 가지고 코드를 작성 해야겠다고 생각했다.

나쁜 코드로 치르는 대가

프로그램을 막 짜다보면 나중에 내가 쓴 코드를 읽기 힘들어지는 스파게티 코드 현상이 나타난다. 초반엔 개발 속도가 빠르지만 나중엔 코드를 읽는 것이 힘들어지고, 결국 읽는데 힘을 다 써 개발 속도가 느려진다.

나쁜 코드가 쌓일 수록 생산성이 떨어지고, 리펙토링에 걸리는 시간과 노력이 기하급수적으로 높아진다. 

이를 극복하기 위해 프로젝트에 새 인력을 추가 투입 하지만, 새 인력은 시스템 설계에 대한 조예가 깊지 않다. 그로 인해 설계 의도에 맞는 변경과 맞지 않는 변경을 구분하지 못하고 생산성을 높여야 한다는 압박감에 나쁜 코드를 더 많이 양산해내는 악순환이 반복된다.

 

결국 기한을 맞추고 생산성을 높이는 유일한 방법은 항상 코드를 깨끗이 유지하는 습관이다. 

 

그렇다면 좋은 코드를 만들고 깨끗하게 유지하기 위해서 어떻게 해야 할까?

좋은 코드를 위한 규칙 - 기억나는 것들 위주

1. 유의미한 이름을 짓자

이 책에서는 서술적인 네이밍을 사용하는 것이 이상적이라고 말한다. 충분히 의도를 표현하는 긴 이름이 의도를 다 표현하지 못하는 이름보다는 훨씬 좋다.

본인이 공감하는 유의미한 이름을 짓기 위해 생각해봐야 할 사항은 다음과 같다.

 1) 분명한 의미를 담자

 2) 발음하기 쉬운 이름을 쓰자

 3) 검색하기 쉬운 이름을 쓰자

 

2. 함수는 하나의 역할만 하도록 하자

함수 이름을 보고 짐작했던 기능을 그대로 수행 하는 코드가 좋은 코드라고 말한다.

만약 함수가 한 가지 일만 수행 하지 않고 부수적인 효과를 발생 시킨다면, 사용자의 의도와는 다른 기능이 자신도 모르는 사이에 함께 실행될 수 있어 좋지 않다.

이름이 길어도 짧고 의미파악이 어려운 이름이나 길고 서술적인 주석보다 좋다. 

 

3. 명령(Command)과 조회(Query)를 분리하자

2번에서 설명했다시피, 함수는 하나의 역할만을 해야 한다. 만약 두 개의 역할을 동시에 하면 이상한 함수가 탄생하게 된다. 

bool set(std::string attribute, std::string value);

이 함수는 이름이 attribute인 속성을 찾아 값을 value로 설정한 후 성공하면 true, 실패하면 false를 반환하는 함수라고 하자. 위 함수를 사용하면 다음과 같은 코드가 나오게 된다.

if(set("username", "chuuu"))
{
	// ...
}

독자 입장에선 이 함수가 key가 존재하는 경우 overwrite 하는지, 존재하지 않은 경우에만 업데이트 하는지 한눈에 알수 없을 것이다. 왜냐 하면 위 함수가 명령과 조회를 한번에 처리 하기 때문이다. 위와 같이 한눈에 파악하기 힘든 코드는 다음과 같이 명령과 조회를 분리해서 작성해주는 것이 좋다.

bool attributeExists(std::string attribute);
bool setAttribute(std::string attribute, std::string value);

if (false == attributeExists("username")) 
{
	setAttribute("username", "chuuu");
}

 

4. 주석은 의도를 표시하기 위해서만 사용해야 한다.

본인은 독자가 이해하기 어려울 것으로 보이는 코드에 주석을 달곤 했다. 가령 이해할 수 있는 코드더라도, 부연 설명을 위해 주석을 달았다. 하지만 이 책에서는 주석을 남용하지 말라고 말한다. 

주석은 꼭 필요한 경우에 꼭 필요한 위치에만 사용해야 한다. 만약 주석이 너무 많으면 주석을 읽는 데 코드를 읽는 것보다 더 긴 시간이 드는 기현상이 일어난다.

또한, 주석이 많이 필요하다는 것은 그만큼 코드가 정리되지 않았다는 의미이기도 하다. 

만약 부연 설명이 필요한 부분이 많다면 주석을 달아 놓을 것이 아니라 리펙토링이 필요하다는 시그널을 전달하는 것이다.

 

5. 테스트 주도 개발(Test-Driven Development)을 하자

실제 코드를 짜기 전 단위 테스트를 먼저 작성하는 기법으로, 이를 통해 유연성, 유지보수성, 재사용성을 제공한다.

이 책에서 말하는 테스트 주도 개발의 핵심 규칙 3가지는 다음과 같다.

 1) 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.

 2) 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.

 3) 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

위 규칙을 따라 테스트 코드를 작성하고 실제 코드를 짠 뒤, 실제 코드가 조금씩 개선 될 때마다 이전에 작성 했던 테스트 코드들이 모두 돌아가는 상태를 유지하라는 내용이다.

 

6. 변경하기 쉬운 클래스

요구사항은 수시로 바뀌고 새로 전달 되기 때문에 변경하기 쉬운 클래스를 만드는 것이 중요하다고 한다.

그렇기 때문에 하나의 클래스가 하나의 책임만을 가지도록 해야 하고, 다형성을 유지해야한다.