이름을 짓기 전 고려해야 할 것들

There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Karlton

컴퓨터 공학에서는 꽤 유명한 농담이다. 가끔씩 변수명을 지어주는 사이트나 챗봇이 있다는 얘기를 듣기도 하는 것을 보면 그만큼 개발자들이 이름 짓는 것을 어려워 한다는 것이 아닐까.

이 글에서는 클린 코드의 내용을 기반으로 이름을 지을 때 참고할 수 있는 규칙들을 정리한다.

객체는 명사, 메서드는 동사를 사용한다

  • 클래스 이름과 객체 이름은 명사나 명사구가 적합하다
    • Customer, WikiPage, AddressParser 등이 좋은 예
    • Manager, Processor, Data, Info 등과 같은 단어는 피하고, 동사는 사용하지 않는다
  • 메서드 이름은 동사구가 적합하다
    • postPayment, deletePage 등이 좋은 예
    • 접근자, 변경자, 조건자는 값 앞에 get, set, is를 붙이는 게 일반적

의도를 분명히 밝힌다

  • 의미가 정확하게 드러나는가?
  • 잘못된 의미로 전달되지는 않은가?
  • 다른 이름과 구분이 잘 되는가?
  • 맥락이 잘 드러나는가?
  • 불필요한 맥락이 들어가 있지는 않은가?

의미를 정확히 이해할 수 있는 이름을 사용한다. 대부분 IDE에서 자동완성이 지원되기 때문에 단어를 줄여쓰지 않는 것이 좋다.

let d // BAD
let daysSinceCreation // GOOD
js

value, item, info 등 의미를 정확히 알 수 없는 단어는 사용하지 않는다.

let items // BAD
let flaggedCells // GOOD
js

잘못된 의미로 이해될 수 있는 이름은 사용하지 않는다. 예를 들어, Set 자료구조에 List라는 이름은 적합하지 않다.

let accountList = new Set() // BAD
let accounts = new Set() // GOOD
js

구분이 되는 이름을 사용한다.

// BAD
function copyCharacters(a1, a2) {}

// GOOD
function copyCharacters(source, destination) {}
js

구분이 되는 이름을 사용한다(2). 의미없는 어휘를 붙여서 구분하지 않는다.

// BAD
let user
let userData
let userInfo
js

같은 맥락에서 사용되는 값들은 이름에 맥락을 추가하면 좋고, 맥락을 객체나 클래스로 만들어서 묶어주면 더욱 좋다.

// BAD
let name
let price

// GOOD
let beverageName
let beveragePrice

// BEST
let beverage = {
  name,
  price,
}
js

불필요한 맥락을 이름에 사용하지 않는다.

// BAD
let carTruck
let carSuv

// GOOD
let truck
let suv
js

쉽고 일관된 이름을 사용한다

  • 발음하기 쉬운가?
  • 검색하기 쉬운가?
  • 불필요한 접두어, 인코딩이 붙지는 않은가?
  • 하나의 개념에 여러가지 어휘가 사용되지는 않은가?
  • 여러 개념에 같은 어휘가 사용되지는 않은가?
  • 자신만 아는 어휘가 있지는 않은가?

발음하기 쉬운 이름을 사용해야 한다.

let ymdhms // BAD
let timeStamp // GOOD
js

검색하기 쉬운 이름을 사용해야 한다. 상수값은 따로 이름을 붙여준다.

// BAD
let s = t.reduce((i, j) => i + j) * 5

// GOOD
const WORK_DAYS_PER_WEEK = 5
let sumOfTasksInWeek = tasks.reduce((accumulator, task) => accumulator + task) * WORK_DAYS_PER_WEEK
js

예전에 IDE의 지원이 부실했을 때는 헝가리안 표기법과 같은 방법으로 데이터 타입이나 유형을 접두어/접미어로 붙이는 경우가 많았다. 이제는 사용할 필요가 없고, 오히려 파악하기 힘들고 유지보수를 어렵게 한다. (타입이 바뀌면 변수명도 변경해야 하는데 이 점을 잊어버린다면?)

불필요한 인코딩(타입정보)을 붙이지 않는다.

let phoneString // BAD
let phone // GOOD
js

마찬가지로 불필요한 접두어를 붙이지 않는다.

let m_dsc // BAD
let description // GOOD
js

추상 클래스나 인터페이스와 그것들을 구현하는 구체 클래스간의 이름을 짓는 컨벤션이 있는 경우도 있다.

  • C#에서는 인터페이스 앞에 I를 붙인다.
  • Java에서는 구현체 클래스 이름 뒤에 Impl을 붙인다.

클라이언트에 노출되는 것은 인터페이스 또는 추상 클래스이고, 클라이언트에게 인터페이스인지 알려줄 필요는 없다. 알려주는 것은 오히려 캡슐화를 약화시킨다. 그러므로 인터페이스 이름에 접두어를 붙이는 것은 피해야 한다.
구현체 클래스의 이름을 구체적인 이름으로 붙이는 것이 좋고, 마땅히 붙일 이름이 없을 때는 구현체 클래스의 이름에 추가정보를 붙이는 것이 차선책이다.
클린코드에서는 "나로서는 내가 다루는 클래스가 인터페이스라는 사실을 남에게 알리고 싶지 않다. 클래스 사용자는 그냥 ShapeFactory라고만 생각하면 좋겠다." 라고 했다.

// BAD
interface ICar {}
class Car implements ICar {}

// BETTER
interface Car {}
class CarImpl implements Car {}

// BEST
interface Car {}
class SportsCar implements Car {}
ts

하나의 개념에는 하나의 어휘만 사용해야 한다.
update, edit, modify 등, 같은 의미의 비슷한 단어들은 통일한다.

// BAD
function updateScore() {}
function editTitle() {}
function modifyAddress() {}

// GOOD
function updateScore() {}
function updateTitle() {}
function updateAddress() {}
js

위와 비슷하게, 여러 개념에 같은 어휘를 사용하지 않는다.
예를 들어 Map에 키-값을 설정하는 메서드에는 add보다 set을 사용한다.

// BAD
function addNum(num1, num2) {}
function addMap(map, key, val) {}

// GOOD
function addNum(num1, num2) {}
function setMap(arr1, arr2) {}
js

모두가 이해할 수 있는 어휘를 사용한다.
내가 작성한 코드는 내가 읽기도 하지만, 다른 사람들도 많이 읽게 된다. 그러므로 모든 사람이 쉽게 이해할 수 있도록 이름을 붙여야 한다.

나만 아는 이름을 쓰지 않는다.

array.whack() // BAD
array.pop() // GOOD
js

쉬운 단어를 사용한다.

function vacate() {} // BAD
function clear() {} // GOOD
js

멘탈 모델에 맞춰서 이름을 짓는 것도 중요하다.

개발자들은 문제를 프로그래밍을 이용해서 해결한다. 그리고 코드를 작성하는 사람도 개발자고, 코드를 읽는 다른 사람도 개발자일 것이다. 그러므로 프로그래밍 관련 용어로 이름을 짓는 것은 적합한 선택이다. 책에서는 "해법 영역에서 가져온 이름" 이라고 표현한다.

"문제 영역에서 가져온 이름" 을 사용하는 것도 좋다. 같이 일하는 개발자들은 같은 문제를 해결하기 위해서 개발을 할 것이고, 당연히 해당 도메인의 용어에 익숙할 것이다.

참조