본문으로 건너뛰기

클린 코드(Clean Code)를 읽고 느낀점(feat. 코딩 책 한권만 읽으면 이렇게 됩니다.)

· 약 16분
Braun Park
Curious Developer

이전에 nuxt로 만들었던 개인 블로그에 같은 제목의 포스팅을 작성했었습니다. 회사에서 클린 코드(Clean Code) 책을 읽고 토론하는 시간을 갖기로 해서 읽고 포스팅으로 정리했었습니다. 그렇게 잊고 있었는데, 이 블로그를 구글 웹마스터도구에 등록하고 노출 키워드를 봤을 때 관련 키워드로 블로그가 노출이 된 경우들이 있었습니다. 많지는 않았지만요. 그래서 그 때 작성한 포스팅을 살짝씩 수정해서 재 포스팅합니다.

Clean Code가 좋은 책이지만, 비판적으로 읽으려고 노력하고 느낀점을 공유합니다.

제목에 feat라고 붙인 '코딩 책 한권만 읽으면 이렇게 됩니다.' 타이틀은 클린 코드 책을 읽으면서 '이게 맞나?' 라고 생각할 때쯤 동명의 유튜브 영상 유튜버 코딩애플님의 코딩 책 한권만 읽으면 이렇게 됩니다.(링크)에서 따왔습니다.

이전에 토론용으로 작성한 내용이고 책 내용에 제 의견을 섞어서 작성한 글이기 때문에 편의상 독백체로 작성되었습니다.

깨끗한 코드란 ?

코드란 요구사항을 표현하는 언어. 일정에 밀려 어쩔 수 없이 나쁜 코드를 사용하여 개발한 후 나중에 손보겠다고 생각한 경험이 있지만, 나중은 결코 오지 않는다. 이 나쁜 코드가 누적되면 추후에는 기한을 맞추지 못하게 된다. 기한을 맞추는 유일한 방법, 빨리 가는 유일한 방법은 언제나 코드를 최대한 깨끗하게 유지하는 습관이다.

여러 전문가들이 깨끗한 코드에 대해 내린 정의들을 소개하고 있는데, 내가 공감이 가장 많이 되었던 문구는 이 두 가지이다.

깨끗한 코드는 잘 쓴 문장처럼 읽힌다.
깨끗한 코드는 언제나 누군가 주의 깊게 짰다는 느낌을 준다.

함수

함수를 깨끗하게 작성하는 방법에 대해서 저자가 소개하고 있다. 다만 내가 읽으면서 어떤 부분은 공감을 하지만 공감이 되지 않는 부분도 있다. 이 부분에 대해서는 개발자들도 의견이 나뉘는 것 같다.

작게 만들어라

함수를 작게 만들어야 한다는 것에는 공감한다. 너무 긴 함수는 가독성이 떨어진다. 물론 그렇다고 해서 저자가 소개 하는 '한 가지만 해라!' 는 공감하기 어렵다. 한 가지만 하는 함수를 만들다보면 함수의 수가 워낙 많아질 수 있기 때문에 오히려 함수를 찾아다니는게 가독성을 더 떨어뜨릴 수 있다고 생각한다.

물론 함수를 쪼갤 수 있는 것은 쪼개는 게 좋다고 생각한다. 중복으로 발생하는 것들도.

Try ~ Catch

이 장에서 가장 스스로가 부족하다고 생각했던 부분이 바로 예외처리이다. 오류 코드 대신에 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다. 하지만 내가 지금까지 정확하게 사용하지 못해 지저분하기만 했던 코드들은 아래와 같다.

try {  
// 기존에 들어 있는 데이터 확인(중복체크)
String paramDateStr = searchDate + " " + searchTime + ":00:00";
Date paramDate = StringUtility.convertStrToDate(paramDateStr, ORDER_TIME_PATTERN);

List<Sales> salesList = salesRepo.findAll();
} catch (ParseException e) {
throw new RestApiException(CommonErrorCode.INVALID_PARAMETER);
}

위와 같은 코드가 이 책에서 설명하는 안 좋은 코드의 예시이며, 내가 짠 코드이다. convertStrToDate() 라는 함수는 String형태와 변환하고자 하는 Date Format을 인자로 받아 해당 포맷의 형태로 Date 형식으로 변경해주는 함수이다. 해당 함수에서는 Date.parse()를 사용하여 ParseException이 발생할 수 있는데 해당 처리를 전체 동작에다가 표현했다. 6번째 줄과 try 문이 끝나기 까지는 사실 수 많은 코드가 숨겨져 있다. 하지만 길이가 길어 생략했다.

이러한 try/catch 블록은 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다. 그러므로 try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.

주석

저자는 기본적으로 주석에 대해 거부감을 갖고 있는 것처럼 보인다. 물론 주석을 아예 쓰지 말자는 건 아니지만 썩 좋아하지 않는다. 물론 나도 주석에 대해서는 최소화하는 게 좋다고 생각하기 때문에 크게는 공감을 한다.

뼈를 때리는 말들이 있다.

'나쁜 코드에 주석을 달지 마라. 새로 짜라.'

누군가가 나의 코드를 볼 때, 혹은 내가 나중에 이 코드를 돌아봤을 때 이해를 한 번에 하지 못할 것 같아 작성하는 주석이 많다. 이러한 주석을 단다는 것은 결국 깨끗한 코드가 아닌 것이다. 이런 주석을 작성할 필요가 없도록 깨끗한 코드를 짜는 습관을 들여야 한다.

// 그 동안 달아놓았던 나쁜 주석들

// 매장별 이상한 특수기호들 예외처리

/**
* @packageName : com.example.domain
* @fileName : category
* @author : bokyun
* @date : 2023/06/16
* @description : 블라블라
*/

/** <code>name</code> : 카테고리 명 */

또한 아무리 좋은 뜻의 주석이라고 해도, 그 주석에 달린 코드들이 언젠가는 바뀔 수 있는 여지들이 있는데, 주석까지 꼼꼼하게 관리할 수 없다면 안 다는 것이 낫다. 그 당시에는 옳았지만, 언젠가는 계속해서 남아 있는 그 주석은 거짓말쟁이가 될 수 있다.

오류 처리

기본적으로 오류라는 단어가 주는 이미지가 부정적이어서 기피하게 되지만, 깨끗한 코드와 오류 처리는 확실히 연관성이 있다. 오류가 발생하면 예외를 던지는 편이 낫다. 그러면 호출자 코드가 더 깔끔해진다.

Try ~ Catch ~ Finally 문부터 작성하라

확실히 지금까지는 이렇게 코드를 해본 적도, 들은 적도 없다. 하지만 이런 습관은 매우 중요할 것 같다. 이렇게 코드를 작성하고 예외를 일으키는 테스트 케이스를 작성한 후, 테스트를 통과하게 코드를 작성하는 방법을 사용한다면 보다 더 테스트 단계에서 단단한 코드를 짤 수 있을 것 같다.

예외를 던지는 것에서 끝나는 게 아니라 오류 메시지에 정보를 담아 예외와 함께 던진다. 실패한 연산 이름과 실패 유형도 언급한다.

// 그동안의 return null을 사용했던 코드 중.
if(id == 1) {
if(receiptData.startsWith("빵.")) {
return new class();
}
Category category = categoryRepo.findById(id).get();
}
return class();

//
a = method.check();
if(a.message.equals("")) {
//nullable
}

그리고 Null을 반환하지 마라. 차라리 빈 값을 넘기는 것이 NullPointerException이 발생할 가능성이 줄어서 안정성이 높다. 깨끗한 코드는 읽기도 좋아야 하지만 안정성도 높아야 한다.

단위 테스트

책에서 소개하는 TDD 법칙 세 가지

  1. 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
  2. 컴파일은 실행하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
  3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

테스트 코드도 실제 코드 못지 않게 중요하다. 깨끗한 테스트 코드를 만들기 위해서는 가독성이 제일 중요.

깨끗한 테스트를 위한 다섯가지 규칙(FIRST)

  1. Fast(빠르게) : 테스트는 빨리 돌아야 한다. 자주 돌리지 않으면 초반에 문제를 찾아내 고치지 못한다.
  2. Independent(독립적으로) : 각 테스트는 서로 의존하면 안된다.
  3. Repeatable(반복가능하게) : 테스트는 어떤 환경에서도 반복 가능해야 한다.
  4. Self-Validating(자가검증하는) : 테스트는 bool 값으로 결과를 내야 한다.
  5. Timely(적시에) : 테스트는 적시에 작성해야 한다. 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다.

클래스

클래스를 정의하는 표준 자바 관례

  1. 가장 먼저 변수 목록이 나온다.
  2. 정적(static) 공개(public) 상수가 있다면 맨 처음에 나온다.
  3. 다음으로 정적 비공개(private) 변수가 나오며
  4. 이어서 비공개 인스턴스 변수가 나온다.

클래스는 작아야 한다.클래스 이름은 해당 클래스 책임을 기술해야 한다. 실제로 작명은 클래스 크기를 줄이는 첫 번째 관문이다.

큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더 바람직하다.

창발성

창발성이라는 챕터 제목이 사실 무엇을 의미하는지, 이 챕터 내용을 포괄할 수 있는 단어인지는 사실 잘 모르겠다.

켄트 벡이 제시한 단순한 설계 규칙 네 가지

  1. 모든 테스트를 실행한다.
  2. 중복을 없앤다.
  3. 프로그래머 의도를 표현한다.
  4. 클래스와 메서드 수를 최소로 줄인다.

설계는 의도한 대로 돌아가는 시스템을 내놓아야 한다. 테스트 케이스가 많을수록 개발자는 테스트가 쉽게 코드를 작성한다. 그리고 철저한 테스트가 가능한 시스템을 만들면 더 나은 설계가 얻어진다.

테스트 케이스를 모두 작성했다면 이제 코드와 클래스를 정리해도 괜찮다.

코드는 개발자의 의도를 분명히 표현해야 한다.

  1. 좋은 이름을 선택한다.
  2. 함수와 클래스 크기를 가능한 줄인다.
  3. 표준 명칭을 사용한다.
  4. 단위 테스트 케이스를 꼼곰히 작성한다.
const bookList = this.state.bookList;
// bookList => author, name, price, category 책의 목록

const content = bookList.map((product) => {

})

느낀점

이 책은 그동안의 코딩을 스스로 돌아보게 하는 책이었습니다. 물론 책의 내용이 무조건적으로 옳다고는 생각하지 않지만, 그래도 대부분은 옳은 내용이었습니다. 스타트업의 특성상이라는 핑계를 대면서 일단은 일정이 빠듯하니 우선 만들어놓고 리팩토리을 하자는 게 쌓이고 쌓여서 엄청난 부채로 남아있는 상태입니다. 그러다보니 특정 클래스 파일은 몇 천 줄이 넘어가는 경우도 생겼습니다.

이 책을 팀원들과 읽고 나서 우선은 앞으로 작성하는 코드에 대해서는 급하더라도 나중에 또 기술부채를 넘기지 않도록 신경써서 개발을 하자고 있습니다. 물론 아직 적응 과정에 있어서 기존에 코딩할 때보다는 더 많은 시간이 소요되는 것은 사실이지만, 지금의 이 작업이 나중에는 더 빠른 작업 방식이 될 것이라고 생각합니다.

그리고 시간을 내서라도 기존 코드들을 리팩토링을 틈틈이 해야겠다는 생각을 했습니다. 다른 팀원들과 동떨어져서 혼자 코딩을 하고 있는데, 추후 다른 팀원들이 이 코드들을 이해하는 데 많은 시간이 소요될 것 같다는 생각이 들었습니다.