본문 바로가기

개발자 강화/개발 독서

[Clean Code 2판] 1-4: Meaningful Names

 


Part1. Code

Chapter4. Meaningful Names

 

Write for your audience, not for yourself.

 
나를 위해서가 아닌, 청중을 위해 작성해라.
 
앞으로 내 코드를 읽을 사람들의 경험에 비해서,
내가 코드를 쓰는 지금 당장의 경험은 굉장히 일시적이다.
 
좋은 이름을 선택하는 데 시간이 꽤 걸리지만,
그 가치는 들인 시간보다 훨씬 높을 것이다!
 
Everyone who reads your code (including you) will be happier if you do.
 


Use Intention-Revealing Names

의도를 드러내는 이름을 사용하라!
 
1. 이름을 설명하기 위해 주석이 필요하다면 잘못된 것이다.
 
예를 들어, int d; 라는 변수를 보고 이 의도를 바로 알 수 있을까?
 
사실 이 의도가 days를 의미한다고 했던 걸 알기 위해서는 주석이 필요할 것이다.
차라리 elapsedDays처럼 자세하게 적는 것이 나을수도 있다!
 
만약, int d = getElapsedTimeInDays();
이렇게 정의한다면, d의 의미를 주석 없이 명확히 알 수 있다.
만약 이 d를 사용하는 범위가 작다면 이런 식으로 선언하는 것도 괜찮을 수 있다!
 


 

Build a System of Names

 
이름 체계를 구축하라!
 

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

 
이것만 보면 theList가 뭐지?
0번째 원소를 왜 갑자기 4랑 비교하지?
list1을 반환하는 이유가 뭐지?
 
이런 의문이 든다.
 

// 맥락 있음
public List<Cell> getFlaggedCells() {
  List<Cell> flaggedCells = new ArrayList<Cell>();
  for (Cell cell : gameBoard)
    if (cell.isFlagged())
      flaggedCells.add(cell);
  return flaggedCells;
}

 
하지만 같은 구조에서 이름으로 맥락을 부여한다면?
아하 깃발이 꽂힌 위치를 찾아서 반환하는 거구나~ 라고 알 수 있다.
 


Avoid Disinformation

잘못된 정보를 피하라
 
1. 코드의 의미를 흐리는 거짓 단서를 남기는 것을 피해라!
 
계정이 묶인 덩어리를 지칭하고 싶어서 accountList라고 쓰자.
그런데, 실제로 이 컨테이너가 List가 아니라 Array나 JSON document라면...? 문제가 된다.
 
차라리 accountGroup이나 accounts로 쓰는 게 낫다!
 
2. 미묘한 차이를 가진 두 변수명을 사용하는 것도 조심해야 한다!
 
XYZControllerForEfficientHandlingOfStrings
XYZControllerForEfficientStorageOfStrings
XXZControllerForEfficientHandlingOfStrings
 
놀랍게도 이 셋은 다른 변수임
이 셋 사이 다른 점을 3초 안에 찾아내신 분?
 
솔직히 코딩하다보면 그냥 휙휙 넘길 가능성이 높음
 
3. 비슷한 모양의 철자를 함께 사용하는 것을 지양한다!
 
l과 1과 O와 0는 굉장히 비슷하게 생김
 


Make Meaningful Distinctions

의미 있는 구분을 하라
 
1. 역할에 따라 변수명을 지어 의미있는 구분을 하자

// 숫자 시리즈 - 의미 없음
copyChars(char a1[], char a2[])

// 역할에 따라 변수명 짓기 - 의미 있는 구분
copyChars(char source[], char destination[])

 
a1, a2로 이름을 붙임 -> 이름에 어떠한 정보도 포함되지 않음
source, destination -> 역할에 따라 변수명을 지어 의미있는 구분을 하자
 
2. noise words 금지!
ProductInfo, ProductData
-> 구분할 수 없는 noise words
-> 이름은 다른데 의미는 다르지 않음
 
getActiveAccount();
getActiveAccounts();
getActiveAccountInfo();
대체 진짜 활성 계정 가져오는 함수는 뭘까?
뭐가 다른거지?
 
3. type 특성을 내포하는 단어를 이름에 쓰면 안된다
variable을 변수 이름에 쓰면 안된다.
table을 테이블 이름에 쓰면 안된다
NameString -> Name이 number일 수 있나? 차라리 그냥 Name이 낫다
 


Use Pronounceable Names

발음할 수 있는 이름을 사용하라
 

논의의 편리함이 중요한 이유는, 프로그래밍이 사회적 활동이기 때문이다

 
genymdhms (생성 날짜, 년, 월, 일, 시 분, 초)
의도는 좋다. 근데 이걸 읽어보자.
젠 와이 엠 디 에이치 엠 에스... -> 젠야무다힘스
젠야무다힘스가 뭔데;
 
ㅇㅇ님 이 젠야무다힘스에 내려오는 서버값이 이상해요
님 왜 웃어요 저 지금 진지하다니까요;
 

// 변경 전
class DtaRcrd102 {
  private Date genymdhms;
  private Date modymdhms;
  private final String pszqint = "102";
  
// 변경 후
 class Customer {
  private Date generated;
  private Date modified;
  private final String recordId = "102";

 
 
이렇게 바꾸고 나니, 아 이게 생성날짜군! 아 이게 변경 날짜군!
굉장히 clear해짐
 
발음하기 편해서 논의하기도 편해짐
 


Use Searchable Names

 
검색할 수 있는 이름을 사용하라
 
1. 한 글자 이름과 숫자 상수는 텍스트 본문 전체에서 찾기 쉽지 않다.
 
검색: MAX_CLASSED_PER_STUDENT
결과: 10건
 
검색: 7
결과: 300건
 
ㄹㅇ 이보다 심하면 심했지 덜하진 않을듯
 
const MAX_CLASSED_PER_STUDENT = 7;
이렇게 선언해두면, 추후 기획 수정으로 max 값을 변경해야할 때 쉽게 찾아서 고칠 수 있다!

이거 예전에 리팩토링할 때 종근님이 제안하셨던 테스크와 비슷한 내용이다!
역시 종근님은 인간클린코드셨구나...
이번에 다른 레포 리팩토링할 때도 그 방법을 따라가고 있다
감사합니다!
 


Use Names of Appropriate Length

적절한 길이의 이름을 사용하라
 
1. 변수 이름의 길이는 그것을 포함하는 scope의 길이에 비례해야 한다
 

// 스코프 2줄 → 짧은 이름 OK
for (int n=0; n<20; n++)
  System.out.println(n + " " + n*n);

 
만약 n이 아니라 theNumberToBeSquared라고 썼다면?
오히려 번거로웠을 것! n은 scope가 2줄 뿐이라 간단하게 쓰는 게 낫다.

// 인스턴스 변수 → 긴 이름 필요
private int numberOfStudentsEnrolled;

 
인스턴스 변수는 전체 클래스에 영향을 미친다.
따라서 아까 scope 2줄짜리보단 길어야 한다.
 
2. 하지만! 함수 이름의 길이는 scope 길이에 반비례 해야 한다.
 
1) 함수의 scope가 클수록 자주 사용될텐데, 함수 이름의 길이가 길다면 불편할 것이다.
open 대신 openFiledAndThrowExceptionIfNotFound -> 와 정말 번거로워요.
 
2) 함수의 scope가 클수록 그 용도가 일반적이다.
open: 굉장히 일반적인 함수. 이 단어 하나만 봐도 용도 짐작 가능.
하지만, 클래스 내 private method는 작은 scope에서 작게 사용되므로, 일반적이지 않음.
일반적이지 않으면 이를 설명하기 위해 더 많은 단어가 필요함!
 


Avoid Encodings

인코딩을 피하라
 
1. 헝가리안 표기법 쓰지 않기
예전에는 C 컴파일러가 타입을 확인하지 않았기 때문에, 변수에 타입을 붙여 명명했다.
phoneNumber phoneString;
이런 표기 방식을 HN 헝가리안 표기법이라고 한다.
 
하지만, 현대 프로그래머들은 컴파일러와 IDE가 발달했기 때문에 이럴 필요가 없다.
또, 이런 식의 표기법은 변수, 함수, 클래스의 이름이나 타입을 바꾸는데 장애물이 된다.
 
그냥 phoneNumber phone;으로 표기하면 된다.
 
2. 인터페이스에 I 붙이지 않기
 
인터페이스나 구현 중 하나를 인코딩 해야 한다면, 구현에 접미사를 추가하는 것이 낫다.
shapeFactory // 인터페이스
shapeFactoryImp // 구현
 


Use Appropriate Parts of Speech

적절한 품사를 사용하라
 
품사 규칙

요소품사예시
클래스명사Customer, Account
메서드동사postPayment, deletePage
boolean 반환is/hasisPosted, hasPermission

 
클래스 이름
클래스와 객체는 일반적으로 명사, 명사구 이름을 가져야 한다.
명사는 복수가 아닌 단수여야 한다.
고객을 나타내는 클래스는 Customers (x), Customer (o)
 
클래스 이름에 Manager, Processor, Data, Info 같은 noise word를 피해라
클래스가 데이터와 정보를 관리하고 처리하는 건 이미 자명한 사실이니까!
 
메서드 이름
메서드는 일반적으로 동사, 동사구 이름을 가져야 한다.
변경자는 변경 형태의 이름을 따는 것이 좋다 -> add, remove, sort, delete 등


Consider Keyword Parameters

키워드 매개변수를 고려하라
 
Python, C#, Ruby를 예제로 들어서 좀 헷갈렸는데,
TS에서 위치 매개변수 대신 객체를 사용하는 것을 예시로 드니까 이해됐다
 
위치 매개변수는 아래처럼 위치를 맞춰서 param을 넘겨야 한다

function fetchProducts(category: string, limit: number, sortBy: string, ascending: boolean) {
    ...
}

// 호출할 때 헷갈림
fetchProducts("electronics", 10, "price", true);

 
하지만, 객체로 넘기면 전달 순서는 상관 없다.
key에 값을 지정해서 넘기므로 가독성도 좋고, 일부만 전달하기도 쉽다.

interface FetchProductsOptions {
    category: string;
    limit?: number;
    sortBy?: string;
    ascending?: boolean;
}

function fetchProducts({ category, limit = 20, sortBy = "name", ascending = true }: FetchProductsOptions) {
    ...
}

// 호출할 때 명확함
fetchProducts({
    category: "electronics",
    limit: 10,
    sortBy: "price",
    ascending: true
});

 
 
그리고,
이 문제와 관련한 문제를 실제로 겪은 적이 있다...ㅠㅠㅋㅋ
https://developer-dreamer.tistory.com/186

[매일메일+개발] 타입스크립트 사용하는 이유? + 실무 경험담

타입스크립트 사용하는 이유[1] 정적 타이핑을 통해 코드의 안정성 크게 향상개발 시 타입 오류를 런타임으로 실행하기 이전에 발견할 수 있음 [2] 개발자의 생산성을 높여줌IDE 자동 완성 기능과

developer-dreamer.tistory.com

맨 아래쪽에, ts인데도 런타임 에러가 나는 경우? 챕터 참고
 


Don't Be Cute

예?;;
https://www.youtube.com/watch?v=x_RYZsOfpKY

I'm not cute anymore - 아일릿

이거 제목 보자마자 아일릿 신곡(I'm not cute anymore) 생각나서 어이없었음
 
아무튼 변수명에 유머 넣지 말고 명확하게 쓰라고 합니다
변수명 장난은 개인 프로젝트에서만...^^
 
HolyHandGrenade라는 이름보다 DeleteItems라는 직관적인 이름이 나은 건 당연히...
 
근데 이 작가가 too clever라는 단어를 참 좋아하는듯
claude는 이걸 '영악한'으로 번역하던데
한국 문화권에서 영악한..?이 자주 쓰이는 단어가 아니여서 어색하게 느껴짐
 


Pick One Word Per Concept

개념당 한 단어를 선택하라
 
한 모듈에서 amount, fee, price, cost를 동시에 사용하지 말 것!
각 개념에 대해 한 단어를 선택하고, 팀의 모든 사람과 공유하기!
 


Use Solution Domain Names

솔루션 도메인 이름을 사용하라
 

// 프로그래머라면 다 아는 용어들
class JobQueue { ... }
class AccountVisitor { ... }  // Visitor 패턴
class UserFactory { ... }     // Factory 패턴
const fifoBuffer = [];        // FIFO 자료구조
const observerList = [];      // Observer 패턴

 
이런 이름을 지어도, 프로그래머에게 의미가 바로 전달 됨
 

// 억지로 비즈니스 용어로 바꾸면 오히려 이상함
class JobWaitingLine { ... }      // Queue를 왜 이렇게?
class AccountInspector { ... }    // Visitor 패턴인데?
class UserCreationHelper { ... }  // Factory인데?

 
굳이 온몸비틀기 해서 비즈니스 용어로 꺾을 필요 없음.

 


Use Problem Domain Names

문제 도메인 이름을 사용하라
 
비즈니스 로직을 다룰 때는 해당 도메인 용어를 사용해라!
억지로 기술 용어 사용하면 헷갈림
 
기획자와 소통하기도 쉽고, 팀의 개발자들과 함께 맥락을 만들어 나가는데도 도움이 될 것!
 

// 커머스에서 쓰는 비즈니스 개념들
class Product { ... }       // 상품
class Cart { ... }          // 장바구니
class Order { ... }         // 주문
class Payment { ... }       // 결제
class Refund { ... }        // 환불
class Coupon { ... }        // 쿠폰
class Shipping { ... }      // 배송
class Review { ... }        // 리뷰

// 비즈니스 로직에 기술 용어 억지로 쓰지 않아도 됨
class OrderDataObject { ... }      // 그냥 Order면 됨
class ProductProcessor { ... }     // 뭘 process 한다는 건지?
class CartManager { ... }          // Manager가 뭔데?

 


 

Add Meaningful Context

의미있는 맥락을 추가하라
 
변수가 혼자서 의미를 전달하지 못하면, 클래스/객체로 묶어서 맥락을 제공하라
 

firstName
lastName
street
houseNumber
city
state
postcode

 
이것들을 함께 보면 아 주소구나! 하고 바로 이해됨
하지만 state만 따로 있다면? 정확히 뭘 의미하는지 모름
 

// 맥락이 명확해짐
class Address {
    firstName: string;
    lastName: string;
    street: string;
    houseNumber: string;
    city: string;
    state: string;      // 아, 주소의 state구나!
    postcode: string;
}

class Order {
    status: string;     // 이건 주문 상태구나!
    shippingAddress: Address;
}

 
이런식으로 객체로 묶어서 제공하면 좀 더 명확해짐
 


Don't Add Gratuitous Context

불필요한 맥락을 추가하지 마라
 
Gas Station Deluxe라는 앱을 만든다고 해서 모든 클래스에 GSD를 붙인다면 어떨까?
IDE 자동완성이 클래스 이름을 지을 때 항상 GSD를 붙이게 될 것...
그럼 IDE 검색창에 GSD라고 치면... 100개의 클래스가 뜰텐데, 그게 유의미한가?
 

// 이미 네임스페이스/폴더로 구분되어 있을 때
// src/gasStationDeluxe/Product.ts

class GSDProduct { ... }  // GSD가 왜 필요해?
                          // 이미 폴더가 말해주고 있잖아

 
음... 내일 코드 리팩토링 해야겠다;ㅋㅋ
나 이런식으로 썼던 내 코딩 패턴이 자꾸 스멀스멀 떠오름
 

// 구분이 진짜 필요할 때
class PostalAddress { ... }    // 우편 주소
class MacAddress { ... }       // MAC 주소
class EmailAddress { ... }     // 이메일 주소

// 각각 다른 개념이니까 구분 필요!

 
구분이 진짜 필요한 경우는 Address 같은 단어이지만, 의미가 다른 경우!
 


마치며...

 
왜 이름을 짓기 어려울까?
 
1. 청중이 계속 바뀜
프로젝트 A에서 프로젝트 B로 옮김
- 새로운 팀원, 새로운 도메인 용어, 새로운 코딩 컨벤션
- 이름 짓기 기준도 다시 배워야 함!
 
2. 내 취항!==팀의 필요

// 내가 좋아하는 스타일
const usr = getUser();
const prod = getProduct();

// 팀이 원하는 스타일
const user = getUser();
const product = getProduct();

- 청중(팀)에 맞춰야 한다!
 
3. 이름 바꾸는 걸 두려워하지 마라!

// 일단 대충 지음
const temp = calculateSomething();
const data = fetchData();
const result = process();

// 나중에 더 나은 이름이 떠오르면 바꿈
const discountedPrice = calculateDiscountedPrice();
const userOrders = fetchUserOrders();
const validatedInput = validateInput();

 
처음부터 완벽하게 지을 수는 없다!
임시로 나쁜 이름을 쓰다가, 나중에 좋은 이름이 생각나면 다시 바꾸면 됨!
 
'다른 개발자가 반대하면 어쩌지?' '괜히 건드렸다 문제 터지면?'
이런 두려움을 느끼지 말고, 일단 바꿔보자!
좋은 쪽으로 이름이 바뀌면 개발자들도 좋아할 것
 
더군다나, 지금은 IDE가 잘 되어있어서 수동으로 바꾸지 않고, 한 번에 바꿀 수 있음!
 

All good writing is based on revision
- Josh Kerievesky

 
좋은 글쓰기는 수정에 기반한다!
 


 
이름짓기 하나로 한 챕터가 나오더니...
대박적...ㅋㅋㅋ
 
근데 읽으면서 공감가는 부분이 정말 많아서 좋았다!
역시 북 스터디는 좋구만!
 
작성 시작: 2025.12.18. 22:08
작성 종료: 2025.12.19. 00:53
 

728x90