티스토리 뷰

G1:한 소스파일에 여러 언어를 사용한다

이상적으로는 소스 파일 하나에 언어 하나만 사용하는 방식이 가장 좋다. 

현실적으로는 여러 언어가 불가피하지만 각별한 노력을 기울여 소스파일에서 언어 수와 범위를 최대한 줄이도록 애써야 한다.

 

G2: 당연한 동작을 구현하지 않는다

최소 놀람의 원칙에 의거해 함수나 클래스는 다른 프로그래머가 당연하게 여길 만한 동작과 기능을 제공해야 한다.

당연한 동작을 구현하지 않으면 코드를 읽거나 사용하는 사람이 더 이상 함수 이름만으로 함수 기능을 직관적으로 예상하기 어렵다.

 

G3: 경계를 올바로 처리하지 않는다

코드는 올바로 동작해야 한다. 스스로의 직관에 의존하지 마라. 모든 경계 조건을 찾아내고, 모든 경계 조건을 테스트하는 테스트 케이스를 작성하라.

 

G4: 안전 절차 무시

안전 절차를 무시하면 위험하다. 컴파일러 경고 일부를 꺼버리면 빌드가 쉬워질지 몰라도 자칫하면 끝없는 디버깅에 시달린다. 실패하는 테스트 케이스를 제껴두고 나중으로 미루는 태도는 신용카드가 공짜 돈이라는 생각만큼 위험하다.

 

G5: 중복

DRY원칙이라고도 부른다. 코드에서 중복을 발견할 때마다 추상화할 기회로 간주하라.

중복된 코드를 하위 루틴이나 다른 클래스로 분리하라. 추상화로 중복을 정리하면 설계의 어휘가 늘어난다. 

가장 뻔한 유형은 똑같은 코드가 여러 차례 나오는 중복인데, 이런 중복은 간단한 함수로 교체한다.

좀 더 미묘한 유형인 여러 모듈에서 일련의 switch/case나 if/else문으로 똑같은 조건을 거듭 확인하는 중복은 다형성으로 대체해야 한다.

더더욱 미묘한 유형은 알고리즘이 유사하나 코드가 서로 다른 중복이다. TEMPLATE METHOD 패턴이나 STRATEGY 패턴으로 중복을 제거한다.

 

G6: 추상화 수준이 올바르지 못하다

추상화는 저차원 상세 개념에서 고차원 일반 개념을 분리한다.

때로 우리는 고차원 개념을 표현하는 추상 클래스와 저차원 개념을 표현하는 파생 클래스를 생성해 추상화를 수행한다.

추상화로 개념을 분리할 때는 철저해야 하는데, 모든 저차원 개념은 파생 클래스에 넣고, 모든 고차원 개념은 기초 클래스(추상 클래스)에 넣는다.

예를 들어, 세부 구현과 관련한 상수, 변수, 유틸리티 함수는 기초 클래스(추상 클래스)에 넣으면 안된다. 

기초 클래스(추상 클래스)는 구현 정보에 무지해야 마땅하다.

 

G7: 기초 클래스가 파생 클래스에 의존한다

개념을 기초 클래스와 파생 클래스로 나누는 가장 흔한 이유는 고차원 기초 클래스 개념을 저차원 파생 클래스 개념으로 분리해 독립성을 보장하기 위해서다. 그러므로 기초 클래스가 파생 클래스를 사용한다면 뭔가 문제가 있다는 말이다. 일반적으로 기초 클래스는 파생 클래스를 아예 몰라야 마땅하다.

 

G8: 과도한 정보

잘 정의된 모듈은 인터페이스가 아주 작다. 작은 인터페이스로도 많은 동작이 가능하다.

잘 정의된 인터페이스는 많은 함수를 제공하지 않는다. 그래서 결합도가 낮다.

우수한 소프트웨어 개발자는 클래스나 모듈 인터페이스에 노출할 함수를 제한할 줄 알아야 한다. 

클래스가 제공하는 메서드 수는 작을수록 좋다.

함수가 아닌 변수 수도 작을수록 좋다. 클래스에 들어있는 인스턴스 변수도 작을 수록 좋다.

자료와 유틸리티, 상수와 임시변수를 숨겨라.메서드와 인스턴스 변수가 넘쳐나는 클래스는 피하라.

인터페이스를 매우 작게, 그리고 매우 깐깐하게 만들어라. 정보를 제한해 결합도를 낮춰라.

 

G9: 죽은 코드

죽은 코드란 실행되지 않는 코드를 말한다. 불가능한 조건을 확인하는 if문과 throw문이 없는 try문에서 catch 블록이 좋은 예다. 아무도 호출하지 않는 유틸리티 함수와 switch/case문에서 불가능한 case 조건도 또 다른 좋은 예다.

죽은 코드를 발견하면 적절한 장례식을 치뤄주라. 시스템에서 제거하라.

 

G10: 수직 분리

변수와 함수는 사용되는 위치에 가깝게 정의한다.

지역 변수는 처음으로 사용하기 직전에 선언하며 수직으로 가까운 곳에 위치해야 한다. 선언한 위치로 부터 몇백 줄 아래에서 사용하면 안 된다. 

 

G11: 일관성 부족

어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현한다.

앞서 언급한 '최소 놀람의 원칙'에도 부합한다.

표기법은 신중하게 택하고, 일단 선택한 표기법은 신중하게 따른다.

 

G12: 잡동사니

비어 있는 기본 생성자는 쓸데없이 코드만 복잡하게 만든다. 아무도 사용하지 않는 변수, 아무도 호출하지 않는 함수, 정보를 제공하지 못하는 주석 등이 좋은 예이다. 깔끔하게 정리하라. 제거하라.

 

G13: 인위적 결합

서로 무관한 개념을 인위적으로 결합하지 않는다. 일반적인 enum을 특정 클래스에 속하지 않고 범용 static 함수도 마찬가지로 특정 클래스에 속할 이유가 없으므로 속하지 않는다.

일반적으로 인위적인 결합은 직접적인 상호작용이 없는 두 모듈 사이에서 일어난다.

뚜렷한 목적 없이 변수, 상수, 함수를 당장 편한 위치에 넣어버린 결과다. 게으르고 부주의한 행동이다.

함수, 상수, 변수를 선언할 때는 시간을 들여 올바른 위치를 고민한다. 그저 당장 편한 곳에 선언하고 내버려두면 안된다.

 

G14: 기능욕심

클래스 메서드는 자기 클래스의 변수와 함수에 관심을 가져야지 다른 클래스의 변수와 함수에 관심을 가져서는 안된다.

메서드가 다른 객체의 내용을 조작한다면 메서드가 그 객체 클래스의 범위를 욕심내는 탓이다. 

기능 욕심은 한 클래스의 속사정을 다른 클래스에 노출하므로, 별다른 문제가 없다면 제거하는 편이 좋다.

 

G15: 선택자 인수

함수 호출 끝에 달리는 false 인수 만큼이나 밉살스런 코드도 없다. 선택자 인수는 목적을 기억하기 어려울 뿐 아니라 각 선택자 인수가 여러 함수를 하나로 조합한다. 선택자 인수는 큰 함수를 작은 함수 여럿으로 쪼개지 않으려는 게으름의 소산이다. 물론 boolean 인수만이 문제라는 말이 아니다. enum, int 등 함수 동작을 제어하려는 인수는 하나 같이 바람직하지 않다. 일반적으로, 인수를 넘겨 동작을 선택하는 대신 새로운 함수를 만드는 편이 좋다.

 

G16: 모호한 의도

코드를 짤 때는 의도를 최대한 분명히 밝힌다. 

 

G17: 잘못 지운 책임

소프트웨어 개발자가 내리는 가장 중요한 결정 중 하나가 코드를 배치하는 위치다.

여기서도 '최소 놀람의 원칙'을 적용해 코드는 독자가 자연스럽게 기대할 위치에 배치한다.

 

G18: 부적절한 static 함수

일반적으로 static 함수보다 인스턴스 함수가 더 좋다. 조금이라도 의심스럽다면 인스턴스 함수로 정의힌다.

반드시 static 함수로 정의해야겠다면 재정의할 가능성은 없는지 꼼꼼히 따져본다.

 

G19: 서술적 변수

프로그램 가독성을 높이는 가장 효과적인 방법 중 하나가 계산을 여러 단계로 나누고 중간 값으로 서술적인 변수 이름을 사용하는 방법이다. 서술적인 변수 이름은 많이 써도 괜찮다. 일반적으로는 많을수록 더 좋다.

 

G20: 이름과 기능이 일치하는 함수

이름만으로 분명하지 않기에 구현을 살피거나 문서를 뒤적여야 한다면 더 좋은 이름으로 바꾸거나 아니면 더 좋은 이름을 붙이기 쉽도록 기능을 정리해야 한다.

 

G21: 알고리즘을 이해하라

테스트 케이스를 모두 통과한다는 사실만으로 부족하다. 작성자가 알고리즘이 올바르다는 사실을 알아야 한다.

알고리즘이 올바르다는 사실을 확인하고 이해하려면 기능이 뻔히 보일 정도로 함수를 깔끔하고 명확하게 재구성하는 방법이 최고다.

 

G22: 논리적 의존성은 물리적으로 드러내라

한 모듈이 다른 모듈에 의존한다면 물리적인 의존성도 있어야 한다. 논리적인 의존성만으로는 부족하다. 의존하는 모듈이 상대 모듈에 대해 뭔가를 가정하면 안된다. 의존하는 모든 정보를 명시적으로 요청하는 편이 좋다.

 

G23: If/Else 혹은 Switch/Case 문보다 다형성을 사용하라

첫째, 대 다수 개발자가 switch문을 사용하는 이유는 그 상황에서 가장 올바른 선택이기보다는 가장 손쉬운 선택이기 때문이다.

그러므로 switch를 선택하기 전에 다형성을 먼저 고려하라는 의미다.

둘째, 유형보다 함수가 더 쉽게 변하는 경우는 극히 드물다. 그러므로 모든 switch 문을 의심해야 한다.

 

G24: 표준 표기법을 따르라

팀은 업계 표준에 기반한 구현 표준을 따라야 한다.

구현 표준은 인스턴스 변수 이름을 선언하는 위치, 클래스/메서드/변수 이름을 정하는 방법, 괄호를 넣는 위치 등을 명시해야 한다. 표준을 설명하는 문서는 코드 자체로 충분해야 하며 별도 문서를 만들 필요는 없어야 한다.

팀이 정한 표준은 팀원들 모두가 따라야 한다. 실제 괄호를 넣는 위치는 중요하지 않다. 모두가 동의한 위치에 넣는다는 사실이 중요하다.

 

G25: 매직 숫자는 명명된 상수로 교체하라

일반적으로 코드에서 숫자를 사용하지 말라는 규칙이다. 숫자는 명명된 상수 뒤로 숨기라는 의미다.

어떤 공식은 그냥 숫자를 쓰는 편이 훨씬 좋다. 예를 들어, 원 넓이를 구할 때 2는 TWO로 변환하는 것 보다 그냥 숫자를 쓰는 편이 좋다.

'매직 숫자'라는 용어는 단지 숫자만 의미하지 않는다. 의미가 분명하지 않은 토큰을 모두 가리킨다.

 

G26: 정확하라

코드에서 뭔가를 결정할 때는 정확히 결정한다. 결정을 내리는 이유와 예외 처리할 방법을 분명히 알아야 한다. 대충 결정해서는 안 된다. 호출하는 함수가 null을 반환할지도 모르면 null을 반드시 점검한다. 조회 결과가 하나뿐이라 짐작한다면 하나인지 확실히 확인한다. 통화를 다뤄야 한다면 정수를 사용하고 반올림을 올바로 처리한다.

병행 특성으로 인해 동시에 갱신할 가능성이 있다면 적절한 잠금 매커니즘을 구현한다.

코드에서 모호성과 부정확은 의견차나 게으름의 결과다. 

 

G27: 관례보다 구조를 사용하라

설계 결정을 강제할 때는 규칙보다 관례를 사용한다. 명명 관례도 좋지만 구조 자체로 강제하면 더 좋다. 예를 들어, enum 변수가 멋진 switch/case 문 보다 추상 메서드가 있는 기초 클래스가 더 좋다. 구현에 강제성을 부여할 수 있기 때문이다.

 

G28: 조건을 캡슐화 하라

bool 논리는 이해하기 어렵다. 조건의 의도를 분명히 밝히는 함수로 표현하라.

// Better
if (shouldBeDeleted(timer))

// Bad
if (timer.hasExpired() && !timer.isRecurrent())

 

G29: 부정 조건은 피하라

!를 통한 부정 조건은 긍정 조건보다 이해하기 어렵다. 가능하면 긍정 조건으로 표현한다

 

G30: 함수는 한 가지만 해야 한다

함수는 한 가지 임무만 수행 한다.

 

G31: 숨겨진 시간적인 결합

때로는 시간적인 결합이 필요하다. 하지만 시간적인 결합을 숨겨서는 안 된다. 

함수를 짤 때는 함수 인수를 적절히 배치해 함수가 호출되는 순서를 명백히 드러낸다.

 

G32: 일관성을 유지하라

코드 구조를 잡을 때는 이유를 고민하라. 그 이유를 코드 구조도 명확히 표현하라. 구조에 일관성이 없어 보인다면 남들이 맘대로 바꿔도 괜찮다고 생각한다.

 

G33: 경계 조건을 캡슐화하라

경계 조건은 빼먹거나 놓치기 십상이다. 경계 조건은 한 곳에서 별도로 처리한다. 코드 여기저기서 처리하지 않는다.

다시 말해, 코드 여기저기에 +1 이나 -1 을 흩어놓지 않는다.

 

G34: 함수는 추상화 수준을 한 단계만 내려가야 한다

함수 내 모든 문장은 추상화 수준이 동일해야 한다. 그리고 그 추상화 수준은 함수 이름이 의미하는 작업보다 한 단계만 낮아야 한다. 

추상화 수준 분리는 리팩터링을 수행하는 가장 중요한 이유 중 하나다. 제대로 하기에 가장 어려운 작업 중 하나이기도 하다. 

 

G35: 설정 정보는 최상위 단계에 둬라

추상화 최상위 단계에 둬야 할 기본값 상수나 설정 관련 상수를 저차원 함수에 숨겨서는 안 된다. 대신 고차원 함수에서 저차원 함수를 호출할 때 인수로 넘긴다.

설정 관련 상수는 최상위 단계에 둔다. 그래야 변경하기도 쉽다. 설정 관련 변수는 나머지 코드에 인수로 넘긴다. 저차원 함수에 상수 값을 정의하면 안 된다.

 

G36: 추이적 탐색을 피하라

일반적으로 한 모듈은 주변 모듈을 모를수록 좋다. 

좀 더 구체적으로, A가 B를 사용하고 B가 C를 사용한다 하더라도 A가 C를 알아야 할 필요는 없다는 뜻이다.

이를 디미터의 법칙이라 부른다. 요지는 자신이 직접 사용하는 모듈만 알아야 한다는 뜻이다.

 

Reference : 

클린 코드 애자일 소프트웨어 장인 정신 (인사이트)

최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday