[Clean Code] 클린 코드 법칙

개발자 누구라면 깔끔한 코드를 원할 것이다. 나는 평소에 리팩토링, 클린 코드등에 괜히 관심히 많았다. (그렇다고 내 코드가 깨끗하다고는 못하겠다.) 처음으로 HOWTO 같은 종류가 아닌 개발 서적을 처음으로 산 책이 로버트 마틴의 ‘클린 코드’이다. 몇주 전 사내에 로버트 아저씨(실재로 백발의 할아버지이다.)가 같은 주제로 자신이 만든 동영상 시리즈 링크가 돌길래 하루에 한개씩 1.5배 속도로 시청하고 있다. 책에 나온 이야기를 재미있게 설명하고 있어서 귀에 속속 들어왔다. 한번 보고 스쳐 지나간다면 또 까먹을지 모른다는 두려움으로 노트에 핵심 포인트만 작성하기 시작했고, 노트도 나중되서 없어지만 안되겠다는 생각에 블로그에 정리를 한번 해보려고 한다. 이 글을 읽고 클린 코드에 관심이 생긴다면 책도 한번 사보길 권한다. 팀 단위로 일하는 소프트웨어 엔진이어라면 꼭 권하고 싶다. 링크

네이밍 (Naming)

  • 좋은 고드란? 사람들이 예상한 대로 작동하는 코드이다.
  • 좋은 네이밍은 타인과 소통할 수 있는 기본적이고 가장 중요한 도구이다.
    • 네이밍에서 의도가 나타나야 한다.
    • 해결하고자 하는 문제가 묘사되어야 한다.
    • 잘못된 정보는 절대 넣지 마라.
    • 클래스명, 변수명은 명사를 사용하여 작명하라.
    • 함수명은 동사로 시작해야 한다.
    • enum은 형용사로 시작한다.
    • 발음이 가능한 단어를 사용하자.
    • 변수의 타입을 나타내는 Prefix은 이제 그만 사용하자. 발달한 IDE가 해결해준다.
  • 이름이 사용되는 범위에 맞게 작명해야한다.
    • 변수는 범위가 광범위 할 수록 의도와 정확한 정보로 비교적 길게 작명하여 어디에서든지 이해가기 쉽게 만드는 것이 좋다.
    • 변수의 범위가 작을수록 간단하고 짧게 만드는 것이 가독성을 높이는 것에 좋다.
    • 클래스와 함수는 변수와 반대 되는 법칙을 따른다. 사용되어지는 범위가 넓을 수록 이름을 짧게 짓고, 범위가 좁을 수록 길게 지어서 정확히 무슨 일을 하는 묘사하자.

함수 (Function)

  • 함수의 크기가 작으면 작을 수록 좋다. 얼마나? (4줄~6줄)
  • 작명을 잘해야 한다.
  • 한가지 일만을 하자. 한가지 일만 하는지 알기 위해서 코드를 추출 (extract)이 안될 때 까지 추출한다.
  • 작은 크기의 함수들이 많아지면 함수 호출 오버해드나 가독성이 떨어진다고 생각한다면 그것은 오해이다.
    • 작은 크기의 좋은 작명이 된 함수가 많다는 것은 복잡한 길에 가고자 하는 길을 안내해줄 표시판이 많은 것 처럼 좋은 일이다.
    • 함수 호출 오버해드 (function call overhead)가 걱정되는가? 현대시대의 개발장비 성능은 옛 장비와 다르게 빨라졌기 때문에 나노 초도 걸리지 않으며, 여러 사람과 함께 용이 작업에 있어서 높은 가독성이 가져다 주는 혜택이 더 크게 보여진다.
  • 큰 함수의 구현을 클래스로 추출하여 크기를 줄여보자. 호출하는 곳에서는 새 클래스의 invoke()를 호출하고 중복 코드를 하나로 묶고, 더 이상 줄일 수 없을 때 까지 줄여 작은 단위의 함수로 구현을 분배하자.

함수 구조 (Function Structure)

  • 함수의 인자(Argument)의 갯수는 적을 수록 좋다. 1~2개가 적당하다. 3개 이상이 되면 오프젝트로 묶어서 전달하자.
  • output 인자는 절대 사용하지 말자. 전달된 인수의 필드나 프로퍼티를 변경하여 함수 밖에서의 사용하지 말자.
  • 함수 인자로 Boolean 타입의 인자는 사용하지 말자. Boolean 타입의 인자를 사용한다는 것은 함수 내에서 2가지 이상을 하겠다는 것을 공식적으로 선언한 것이기 때문이다. 함수는 한가지 일만 하는 것을 원칙으로 하자.
  • Nullable 인자는 지향하자. 물론 Open Source과 같이 불특정 인원이 어떤 것을 전달할지 모를 때면 사용하여 방어적 코드를 짜야겠지만, 팀내에서는 합의하에 절Nullable 인자를 사용하지 않고 공격적인 코딩 스타일을 고수하자.
  • Step Down 법칙: Public 변수나 메소드를 위에 넣고, private 메서드를 아래에 넣는 코드 컨밴션은 유행이 한참 지난 스타일이다. 잡지나 신문을 보면 제목이 나오고 자세한 내용은 그 후에 따라 나온다. 이와 같이 하면, 비록 Public 메서드들이 한눈에 안들어 올 것이다. 하지만 코드의 가독성을 위해서 Step Down 법칙을 추천한다.

Step Down Rule

 

  • Switch-case 문: 사용을 지향한다. Switch 문을 사용하게 되면 코드가 의존도가 높아진다. 대신하여 Polymorphism 방식을 사용하자.

No Switch statement

  • Command & Query: 함수의 특성을 광범위적으로 보면 크게 두가지 종류로 구분하여 사용하는 것을 추천한다.
    • Command : 반환 값은 없어야 하며 어떤 행동, 혹은 명령을 실행하는 함수라고 할 수 있다. 예를 들어 authenticator.login()라는 함수는 로그인을 하는 행동을 하는 함수이다. 꽤 자주 많은 곳에서 login과 같은 메서드에 User 오브젝트를 반환하는 경우를 볼 수 있다. 명령 성질의 함수에 반환을 하게 되면 Query 종류의 함수도 아니고 Command 종류의 함수도 아닌 두 가지 일을 하는 함수가 되어 버린다. 물론 Nil을 반환하여 로그인 실패를 체크하려는 의도도 있을지 모른다. 하지만 이런 박쥐 같은 함수는 타 개발자에게 혼란을 가중시킨다.
    • Query: 값을 반환하는 함수를 말한다.
  • Null를 반환하지 말자. 만약 Null이 될 것 같으면 Exception을 던지자.
  • try문은 함수의 가장 위에 사용하자. 여기에 언급되는 함수 구조의 룰은 전부 함수가 한가지 일만을 명확하게 하고 짧은 함수들을 전재를 한다.

 

형식 (Form)

  • 모든 파일의 형식은 일관성이 중요하다. (인던테이션, 줄 바꿈, 주석 등)
  • 주석: 오래된 레가시 코드에는 업데이트를 하지 않은 주석이 많다. 주석은 Public API 문서에서는 불특정 다수에게 유용함을 주지만 코드 자체가 주석을 대신해야 좋은 코드이다.
  • 파일 크기는 최대한 작을 수록 좋다. 500 줄 이상이 되지 않게 조심하자.
  • 적절한 코드 가로 길이는 화면에 보이는 곳 까지이다. 줄 바꿈 없이 한 눈에 코드가 읽혀지는 것이 좋다.
  • 실제 구현에서 직접적으로 구현체 클래스 (concrete class)를 호출하기 보다는 추상 클래스를 호출함으로써 확장성 확보하자.

TDD (Test Driven Development)

  • 개발자는 현존하는 코드에 손을 대길 꺼려 한다. 그 이유는 잘 돌아가던 것이 깨질까 봐 그렇다. 하지만 코드가 깨지는 지 안깨 지는 보장 받을 수 있으면, 지저분한 코드를 더 깔끔한 코드로 바꾸려고 노력할 가능성이 높다. 그래서 테스트가 필요하다.
  • 그런데 왜 TDD인가? 이름에서 느낄 수 있는 것 처럼 Test code의 중요성은 아무리 강조해도 부족하다. 중요한 것을 먼저 짜는 것이 좋은 습관이다. Production code를 먼저 짜게 되면 Test case를 소흘하게 여길 것이다. 뿐만 아니라 좋은 설계를 갖게 될 것이다. 좋은 설계는 유연하고, 유지보수 가능해야하며, 확장 가능해야 한다. 그러기 위해서 코드가 깨지는 지 안깨지는 확신을 해야한다.
  • TDD의 세가지 법칙
    • 실패한 Test case를 패스하기 위해 만든 것 외에는 절대 Production code를 짜서는 안된다.
    • 실패할 만큼만 Test case를 짠다.
    • 실패한 Test case가 패스 할 만큼만 Production 코드를 짜라
  • 순서 (Red -> Green -> Refactor)
    • Red: 실패할 만큼만 Test case를 짠다.
    • Green: 실패한 Test case가 패스 할 만큼만 Production 코드를 짜라.
    • Refactor: Test case를 포함해서 코드를 정리하라.

설계 (Architecture)

  • 소프트웨어 개발에 있어 훌륭한 설계는 최대한 개발환경에 대한 결정을 안 할수 있는 설계이다. 개발을 시작하기 전에 우리는 Use case 보다 사용할 Tool과 Framework에 얽매이는 경우가 많다. 또한 이렇게 결정된 사항에 맞추어 설계가 진행되는 경우가 대부분이다. 하지만 이렇게 되면 각 tool이나 framework에 의존도가 높아져 유연한 설계를 할 수 없게 만든다.
  • 좋은 설계자란 tool과 framework와 같은 개발 환경에 대한 결정 사항들을 최대한 유연하게 하고, 미루고 미룰 수 있는 자이다. 예를 들어 데이터 저장을 어떤 방식으로 할지 결정을 나중에 해도 될 만큼 유연한 설계를 짜는 사람이 진정 좋은 설계자라고 할 수 있다. Sql를 사용할지 file system을 사용할지 웹 API를 사용할 지 등을 설계 초입에 안하고 설계할 수 있어야 한다.
  • 개발 환경 보단 Use case에 더 집중하도록 하자.
  • UI 작업을 Use case의 플러그인 정도로 생각을 하고 설계를 하자. 보통 UI 작업은 꽤 비싼 작업이다. UX/UI design/UI dev등 상당히 많은 노력과 시간이 필요한 부분이다. 하지만 Use case 입장에서 나중에 UI이가 Web page에서 Command line으로 바뀌거나 stand alone 어플리케이션으로 바뀐다 해도 큰 지장 없는 설계, 즉 UI 레이어를 Use case의 플로그인 정도로 생각하고 설계를 해야한다.
  • Use case 란?
    • 사용자와 시스템이 특정 목적을 달성하기 위한 설명서 이상, 이하도 아니다.
    • 유스케이스 주도적 개발 (Use case driven development)은 보다 더 나은 설계를 줄 것이다.
    • Use case와 전달 방식 (delivery mechanism)와 분리되어야 한다.