본문 바로가기

General

[클린 코드] 2장 의미있는 이름

변수, 함수, 인수, 클래스, 소스 파일, 디렉터리. 우리는 모든 곳에 이름을 붙인다.

좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다. 이름을 주의 깊게 살펴 더 나은 이름으로 개선하면 코드를 읽는 사람이 좀 더 행복해질 수 있다.

좋은 이름을 선택하려면 설명 능력이 뛰어나고, 문화적인 배경이 같아야한다.

 

1. 의도를 분명히 밝혀라. Use Intention-Revealing Names

변수, 함수, 클래스 이름은 다음의 질문에 모두 답하는 이름이어야 한다.

  • 존재 이유는?
  • 수행 기능은?
  • 사용 방법은?
// ❌
let d; // 경과 시간 (단위: 날짜)

// ✅
let elapsedTimeInDays;
let fileAgeInDays;
let daysSinceCreation;
let daysSinceModification;

 

문제는 코드의 단축성이 아니라 함축성이다. 코드 맥락이 코드 자체에 명시적으로 드러나야 한다. 

 

// ❌
const getThem = (list1) => list1.filter(x => x[0] === 4)

// ✅ 
const getFlaggedCells = (cells) => cells.filter(x => x.isFlagged())

 

2. 그릇된 정보를 피하라. Avoid Disinformation

나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용하면 안된다. 

예를 들어 hp, aix, sco는 유닉스 플랫폼이나 유닉스 변종을 가리키는 이름이기 때문에 직각삼각형의 빗변을 hp라고 하면 그릇된 정보를 제공하게 된다.

// ❌
let hp;

// ✅
let hypotenues;

실제 List 자료구조가 아니라 단순 목록일 경우에도 list라고 명명하면 그릇된 정보를 제공하는 셈이다.

// ❌
let list = [];

// ✅
let accounts = [];
let bunchOfAccounts = [];
let accountGroup = [];

 

일관성이 떨어지는 표기법도 그릇된 정보다. 같은 역할에 서로 다른 이름을 사용하지 않도록 주의한다. 같은 개념은 같은 표기법을 사용한다.

// ❌

// libs/a.js
let XYZControllerForEfficientHandlingOfStrings; // 효율적인 문자열 핸들링을 위한 XYZ 컨트롤러

// libs/b.js
let XYZControllerForEfficientStorageOfStrings; // 효율적인 문자열 저장을 위한 XYZ 컨트롤러

알파벳 중 소문자 엘(l)과 대문자 오(O)는 숫자와 비슷해보여, 그릇된 정보를 제공하는 끔찍한 예에 속한다. 글꼴에 따라 소문자 엘(l)는 숫자 일(1)과 구분이 안되고, 대문자 오(O)는 숫자 영(0)과 구분이 어렵다.

// ❌

let a = l;
if (O === l) {
  a = l;
} else {
  l = 1;
}

 

3. 의미있게 구분하라. Make Meaningful Distinctions

연속된 숫자를 덧붙이거나 불용어(noise word)를 추가하는 방식은 적절하지 못하다.

// ❌

let a1, a2;
let productData, productInfo;
let className, klassName; // 😅
let zork, theZork;

불용어는 중복이다. 읽는 사람이 차이를 알도록 이름을 지어야 한다.

// ❌

let variable;
let name, nameString;
let customer, customerObj, customerInfo, customerData;
let money, moneyAmount;

const getActiveAccount = () => {};
const getActiveAccounts = () => {};
const getActiveAccountInfo = () => {};

 

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

사람들은 '단어' 단위에 익숙한데, 이 '단어'라는 것은 발음이 가능해야 한다.

발음하기 쉬운 이름은 커뮤니케이션에도 도움이 된다. 알고있겠지만 프로그래밍은 커뮤니케이션을 동반하는 사회활동이다. 발음하기 쉬운 이름은 '흠, 여기 비.씨.알.쓰리.씨.엔.티 말씀인가요?' 와 같은 상황을 방지해준다.

// ❌
class DtaRcrd102 {
  private genymdhms;
  private modymdhms;
  private pszqint;
}

// ✅
class Customer {
  private generationTimeStamp;
  private modificationTimeStamp;
  private recordId;
}

 

 

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

리터럴 값보다 이름을 지어 검색에 잘 잡히도록 하는 것이 좋다. 

// ❌
current = Math.min(value, 7);

// ✅
const MAX_CLASSES_PER_STUDENT = 7;
current = Math.min(value, MAX_CLASSES_PER_STUDENT);

이름을 의미있게 지으면 함수가 길어진다. 그러나 그냥 7을 사용한다면, 7이 들어가는 모든 값의 의미를 분석해 원하는 상수를 가려내야 할 것이다.

마찬가지로 너무 짧은 이름보다는, 검색에 걸릴 수 있도록 적당히 긴 이름이 좋다. 예를 들어, e는 거의 모든 프로그램, 거의 모든 문장에 등장한다.

// ❌
let e;

// ✅
let event;

 

이름의 길이는 범위 크기에 비례해야 한다. 간단한 메서드나 로컬 변수에는 한 문자 사용도 괜찮다. 

 

6. 인코딩을 피하라. Avoid Encodings

현대 IDE는 헝가리식 표기에 의존하지 않고도, 즉 변수이름에 타입을 인코딩하지 않고도 타입 오류를 감지할 만큼 발전했다. 이제는 헝가리식 표기법이나 기타 인코딩 방식이 오히려 방해가 된다. (특히 Java 기준)

// ❌

class Part {
  private m_description; // 멤버 변수를 나타내는 접두어
}

인터페이스 클래스와 구현 클래스의 구분 중 인터페이스 이름에는 접두어를 붙이지 않는 편이 좋다고 생각한다. 내가 다루는 클래스가 인터페이스라는 사실을 남에게 알리고 싶지 않고, 클래스 사용자가 그냥 ShapeFactory 라고만 생각했으면 좋겠다는 바람에 IShapeFactory, ShapeFactory 보다는 ShapeFactory, ShapeFactoryImp로 택하겠다.

 

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

코드를 읽는 중에 변수 이름을 자신이 아는 다른 이름으로 변환해야한다면 그 변수 이름은 바람직하지 못한 이름이다. 보통 문제 영역이나 해법 영역에서 사용하지 않는 다른 이름을 사용한 것이 이 문제의 원인이 된다.

문자 하나만 사용하는 변수이름은, 루프에서 반복 횟수를 세는 i, j, k 외에는 지양하는 것이 좋다. 그렇지 않으면 독자가 실제 개념을 변환해야하기 때문이다.

 

8. 클래스 이름 Class Names

클래스 이름과 객체 이름은 명사가 적합하다.

// ❌
Manager, Processor, Data, Info

// ✅
Customer, WikiPage, Account, AddressParser

 

9. 메서드 이름 Method Names 

메서드 이름은 동사가 적합하다.

employee.getName();
customer.setName('365kim');
if (paycheck.isPosted()) { ... }

정적 팩토리 메서드를 사용해서 생성자를 오버로드(중복 정의)할 때, 메서드에 인수를 설명하는 이름을 사용하는 것이 좋다.

// ❌
const fulcremPoint = new Complex(23.0)

// ✅
const fulcremPoint = Complex.fromRealNumber(23.0)

 

10. 기발한 이름은 피하라. Don’t Be Cute 

특정 문화에서만 사용하는 농담이나 표현은 피하는 편이 좋다. 의도를 분명하고 명확하게 표현하라.

// ❌
const 알잘딱깔센 = (list) => list.filter(isValid);

// ✅
const validList = (list) => list.filter(isValid);

 

11. 한 개념에 한 단어를 사용하라. Pick One Word per Concept

똑같은 메서드를 클래스마다 fetch, retrieve, get으로 제각각 부르면 혼란스럽다. 

마찬가지로 controller, manager, driver를 섞어 쓰면 혼란스럽다. DeviceManager와 ProtocolController는 근본적으로 어떻게 다를까? 이름이 다르면 코드를 읽는 사람은 당연히 클래스도 다르고 타입도 다를 것이라 예상한다.

일관성 있는 어휘는 코드를 사용할 프로그래머가 반갑게 여길 선물이다. 🎁

💬 이 규칙을 지키기 위해서는 프로젝트 단위로 사용할 Dictionary 가 필요해보인다. 위키 문서를 만들어 관리를 시도할 수 있겠으나 100% 지켜지기는 어려울 듯하다. 알아서 관리해주는 도구가 있다면 좋겠다.

 

12. 말장난을 하지 마라. Don’t Pun 

기존 값 두 개를 더하거나 이어서 새로운 값을 만들 때 add 를 사용한 상황을 가정해보자. 이 때 집합에 값을 하나 추가하는 메서드를 새로 만든다면 add라고 불러도 될까? 기존 add와는 맥락이 다르니, insert 또는 append가 적합하다. 이를 add라고 부른다면 말장난이다. 

 

13. 해법 영역에서 가져온 이름을 사용하라. Use Solution Domain Names

기술 개념에는 기술 용어가 가장 적합한 선택이다. 예를 들어 비지터 패턴에 친숙한 프로그래머는 AccountVisitor라는 이름을 금방 이해한다. 모든 이름을 문제 영역에서 가져오는 것은 현명하지 못하다.

 

14. 문제 영역에서 가져온 이름을 사용하라. Use Problem Domain Names 

적절한 '기술 용어'가 없다면, 그리고 문제 영역 개념과 관련이 깊은 코드라면, 문제 영역에서 이름을 가져온다. 그러면 코드를 보수하는 프로그래머가 PM 등 다른 전문가에게 의미를 물어 파악할 수 있게 된다.

훌륭한 프로그래머, 훌륭한 설계자는 해법 영역과 문제 영역을 구분할 줄 안다.

 

15. 의미있는 맥락 추가하라. Add Meaningful Context 

'state'가 사용되는 두 가지 상황을 생각해보자. 첫 번째 상황에서 street, houseNumber, city, state, zipcode 라는 변수가 있다면 주소의 state를 가리킨다는 것을 금방 알아챌 수 있다. 그런데 두 번째 상황에서는 어느 메서드가 state라는 변수 하나만 사용한다면, 변수 state가 주소의 일부라는 사실을 알아채기 어려울 것이다.

addrState 접두어를 추가해 맥락을 조금 더 분명하게할 수도 있고, address라는 클래스를 생성해서 맥락을 추가할 수 있다.

 

16. 불필요한 맥락을 없애라. Don’t Add Gratuitous Context 

Gas Station Deluxe 라는 애플리케이션을 작성한다고 가정했을 때, 모든 클래스 이름을 GSD로 시작하는 전략은 전혀 바람직하지 못하다. 

 

 

출처: 도서 클린코드 애자일 소프트웨어 장인 정신, 로버트 C.마틴 지음 | 박재호, 이해영 옮김 | 인사이트

'General' 카테고리의 다른 글

[클린 코드] 3장 함수  (0) 2023.04.23
[단위 테스트] 1장~5장 요약  (1) 2023.04.18
[클린 코드] 1장 깨끗한 코드  (1) 2023.04.16
[클린 코드] 서문, 0장 들어가면서  (0) 2023.04.16
[오브젝트] 12장 다형성  (0) 2022.08.14