본문 바로가기

Clean Code

[Clean Code] Ch.1 깨끗한 코드 & Ch.2 의미 있는 이름

반응형

 

  1. 깨끗한 코드에 대한 유명한 프로그래머들의 의견

 

1. 비야네 스트롭스트룹 (Bjarne Stroustrup)

나는 우아하고 효율적인 코드를 좋아한다. 논리가 간단해야 버그가 숨어들지 못한다. 의존성을 최대한 줄여야 유지보수가 쉬워진다. 오류는 명백한 전략에 의거해 철저히 처리한다. 성능을 최적으로 유지해야 사람들이 원칙 없는 최적화로 코드를 망치려는 유혹에 빠지지 않는다. 깨끗한 코드는 한 가지를 제대로 한다. 

 

  • 깨끗한 코드는 보기에 즐거운 코드
  • 세세한 사항까지 꼼꼼하게 철저한 오류 처리
  • 깨끗한 코드는 한 가지에 집중

2. 그래디 부치 (Grady Booch)

깨끗한 코드는 단순하고 직접적이다. 깨끗한 코드는 잘 쓴 문장처럼 읽힌다. 깨끗한 코드는 결코 설계자의 의도를 숨기지 않는다. 오히려 명쾌한 추상화와 단순한 제어문으로 가득하다. 

 

  • 가독성 강조
  • 코드는 추측이 아니라 사실에 기반해야 함. 반드시 필요한 내용만 담아야 함.

3. 데이브 토마스 (Dave Thomas)

깨끗한 코드는 작성자가 아닌 사람도 읽기 쉽고 고치기 쉽다. 단위 테스트 케이스와 인수 테스트 케이스가 존재한다. 깨끗한 코드에는 의미 있는 이름이 붙는다. 특정 목적을 달성하는 방법은 (여러 가지가 아니라) 하나만 제공한다. 의존성은 최소이며 각 의존성을 명확히 정의한다. API는 명확하며 최소로 줄였다. 언어에 따라 필요한 모든 정보를 코드만으로 정확히 표현할 수 없기 때문에 코드는 문학적으로 표현해야 마땅하다. 

 

  • 깨끗한 코드는 다른 사람이 고치기 쉬운 코드, 읽기 좋은 코드
  • 테스트 케이스가 없는 코드는 깨끗한 코드가 아님
  • 큰 코드보다 작은 코드에 가치를 둠

4. 마이클 페더스 (Michael Feathers)

깨끗한 코드의 특징은 많지만 그중에서도 모두를 아우르는 특징이 하나 있다. 깨끗한 코드는 언제나 누군가 주의 깊게 짰다는 느낌을 준다. 고치려고 살펴봐도 딱히 손댈 곳이 없다. 작성자가 이미 모든 사항을 고려했으므로. 고칠 궁리를 하다 보면 언제나 제자리로 돌아온다. 그리고는 누군가 남겨준 코드, 누군가 주의 깊게 짜 놓은 작품에 감사를 느낀다. 

 

  • 깨끗한 코드는 주의 깊게 작성한 코드

5. 론 제프리스 (Ron Jeffries)

중요한 순으로 나열하자면 간단한 코드는 
- 모든 테스트를 통과한다. 
- 중복이 없다.
- 시스템 내 모든 설계 아이디어를 표현한다. 
- 클래스, 메서드, 함수 등을 최대한 줄인다. 
(중략)

 

  • 같은 작업을 여러 차례 반복한다면 코드가 아이디어를 제대로 표현하지 못한다는 증거이다. 
  • 명확하게 표현하자. 한 메서드가 하나의 기능을 수행하도록. 
  • 실제 기능을 구현하기 전에 먼저 추상화를 하자. 

6. 워드 커닝햄 (Ward Cunningham)

코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다. 코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드라 불러도 되겠다. 

 

  • 코드를 독해하는데 어려움이 없어야 한다. 

개인적인 요약)

  • 읽는 사람이 쉽게 읽고 고칠 수 있어야 한다.
  • 코드에 설계자의 의도가 모두 드러나있어야 한다.
  • 하나의 메서드는 하나의 기능을 수행하도록 분리해야 한다.
  • 꼼꼼하게 오류처리를 해주어야 한다. 

 

  2. 의미 있는 이름

 

1. 의도를 분명히 밝혀라

 

  • 변수나 함수, 클래스에 대한 주석이 별도로 필요하지 않도록 의도를 분명히 드러내는 이름을 설정해야 한다.
// 잘못된 예시

int d; // 경과 시간(단위: 날짜)

 

  • 코드 맥락이 코드 자체에 명시적으로 드러나야 한다. 지나치게 함축하면 안 된다. 

아래의 코드는 단순성은 만족하지만, 코드 맥락이 코드 자체에 명시적으로 드러나지 않는 코드이다. 

public List<int[]> getThem{
	List<int[]> list1 = new ArrayList<int[]>();
	for (int[] x : theList)
		if (x[0] == 4)
			list1.add(x);
	return list1;
}

 

list1이 어떤 역할을 하는지, x[0]이 담는 정보는 무엇인지, theList는 어떤 값을 가지고 있는지 등을 알 수 없다. 

이 코드가 지뢰 찾기 게임이라고 가정한다면, theList는 게임판이 될 것이다. 그리고, 게임판의 각 칸은 배열의 형태로 표현되고, 배열의 0번째 값은 칸 상태를 의미한다. 4는 깃발이 꽂힌 상태이다. 

 

더 나아가, int 배열을 사용하는 대신에 칸을 간단한 클래스로 만든다면 더 의미를 잘 표현할 수 있다. 

그리고, 깃발을 상수로 표현하기보단 isFlagged라는 좀 더 명시적인 함수를 이용할 수 있다. 

따라서, 각 개념에 이름을 붙이면 아래와 같이 코드를 작성할 수 있다. 

public List<int[]> getFlaggedCells(){
	List<Cell> flaggedCells = new ArrayList<Cell>();
	for (Cell cell : gameBoard)
		if (cell.isFlagged())
			flaggedCells.add(Cell);
	return flaggedCells;
}

 

2. 그릇된 정보를 피하라

 

  • 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용하면 안 된다.
  • ex) 여러 계정을 그룹으로 묶을 때, 실제 List가 아님에도 accountList로 명명하는 경우. (List는 특수한 의미)
  • 유사한 개념은 유사한 표기법을 사용하므로, 다른 개념에 서로 흡사한 이름을 사용하지 않도록 주의한다. 

3. 의미 있게 구분하라

 

  • 읽는 사람이 차이를 알도록 이름을 지어야 한다. 
  • 연속된 숫자를 덧붙이는 방식은 적절하지 못하다. 
    ex) a1, a2, ... aN
  • 불용어 (Stop word)를 사용하는 방식 또한 적절하지 못하다. 
    ex) Product라는 클래스가 있다고 가정. 다른 클래스를 ProductInfo, ProductData라고 부른다면, 개념을 구분하지 않고 이름만 다르게 한 경우
  • 이미 아는 정보를 변수 명에 담을 필요가 없다. 
    ex) 표 이름에 table을 붙이는 경우, NameString (이름은 당연히 문자열)

4. 발음하기 쉬운 이름을 사용하라

 

5. 검색하기 쉬운 이름을 사용하라

 

  • 문자 하나를 사용하는 이름이나, 상수는 검색하기가 어렵다. 
  • 이름 길이는 범위 크기에 비례해야 한다.
    ex) 간단한 메서드에서의 로컬 변수만 한 문자를 사용
  • 상수도 이름을 붙여서 사용하는 것이 좋다. 
for (int j = 0; j < 34; j++){
	s += (t[j] * 4) / 5;
}
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j = 0; j< NUMBER_OF_TASKS; j++){
	int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
	int realTaskWeeks = (realTaskDays / WORK_DAYS_PER_WEEK);
	sum += realTaskWeeks;
}

아래의 코드가 길이는 훨씬 길지만, 검색하는데 매우 유용하다. 위의 코드에서 s를 찾기 위해서는 s가 포함된 모든 단어들이 검색될 것이다. 

 

6. 인코딩을 피하라

 

  • 변수명에 유형이나 범위 정보까지 추가하면 이름을 해독하기가 어려움
  • 헝가리식 표기법 (변수 앞에 데이터의 형식을 붙여서 저장하는 방식)을 지양하자. 

7. 자신의 기억력을 자랑하지 마라

 

  • 문자 하나만 사용하는 변수 이름은 루프에서 반복 횟수를 세는 i, j, k에서만 사용하자. 

8. 클래스/메서드 이름

 

  • 클래스 이름과 객체 이름은 명사나 명사구가 적합하고, 동사는 사용하지 않는다.
    Good) Customer, WikiPage, Account, AddressParser
    Bad) Manager, Processor, Data, Info 
  • 메서드 이름은 동사나 동사구가 적합하다. 
    Good) postPayment, deletePage, save
  • 접근자(Accessor), 변경자(Mutator), 조건자(Predicate)는 앞에 get, set, is를 붙인다. 
  • 기발한 이름은 피하는 것이 좋다. (비유적인 표현, 특정 문화에서만 사용하는 농담 x)

9. 한 개념에 한 단어를 사용하라. 한 단어를 두 가지 목적으로 사용하지 마라. 

 

  • 추상적인 개념 하나에 단어 하나를 선택하여 이를 고수한다. 
    ex) fetch, retrieve, get (X)  controller, manager, driver (X)
  • 메서드 이름은 독자적이고 일관적이어야 한다. 
  • 지나치게 일관성을 고려하면 안 된다. 
    ex) 기존에 add가 두 개를 더하거나 이어서 새로운 값을 만드는 메서드. 집합에 값 하나를 추가하는 것도 add로 표현하는 경우 -> insert나 append를 사용해야 한다. 

10. 의미 있는 맥락을 추가하고, 불필요한 맥락은 제거하라. 

 

  • 클래스, 함수, 이름 공간에 넣어서 맥락을 부여하거나, 모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다. 
  • ex) firstName, lastName, street, houseNumber, city, state, zipcode 라는 변수가 있다고 가정하면 주소라는 사실을 금방 알 수 있음. 
    특정 메서드에서 state 변수 하나만 사용한다면? state가 주소의 일부라는 정보를 쉽게 알기 어려움
    -> addrFirstName, addrLastName, addrState, ... 
    -> Address라는 클래스를 생성하는 방법도 있음
  • 만약 주소에 대한 정보밖에 없다면 addr이 굳이 필요하지 않다. 
  • 의미가 분명한 경우에 한해서는 짧은 이름이 긴 이름보다 좋다. 

 

출처) Clean Code (Robert C. Martin)

 

반응형

'Clean Code' 카테고리의 다른 글

[Clean Code] Ch.4 주석  (0) 2023.02.05
[Clean Code] Ch.3 함수  (0) 2022.07.24