좋은 이름이란
개발을 하다보면 좋은 변수/함수 이름 짓기에 꽤 많은 시간을 쏟는다.
좋은 이름은 팀원과의 의사소통 비용을 줄일 뿐 아니라, 추후 유지보수와 확장에도 큰 역할을 한다.
함수 이름이 무언가 이상한데, 명확히 설명을 못했었던 이유는 나 스스로도 "좋은 이름" 에 대한 기준이 없어서 였으리라.
좋은 이름에 대한 명확한 답이 있는 것이 아니나,
이름이 왜 중요한지, 어떤 이름이 왜 나쁜지, 왜 좋은지 스스로 참고할 기준은 갖추는건 분명 도움이 될 것이다.
이름은 추상화 계층을 만든다
이름은 가장 간단한 추상화 계층이다.
좋은 이름은 해당하는 코드가 무엇을 하는지 단번에 이해할 수 있고,
추가적인 설명이 필요없으며, 오해가 적어진다.
또한, 함수나 변수의 목적이 분명히 드러나므로, 기능 확장, 변경시에도 "이 코드가 담당하는 역할" 을 빠르게 파악할 수 있다
네이밍 핵심 포인트
1. 협의성 (narrowness 狹義)
정의: 이름이 대상의 ‘핵심 특성’을 충분히 드러내되, 그 대상이 어떻게 구현되어 있는지까지 지나치게 노출하지 않는 것
너무 일반적일 때의 문제
- 예: data, value, item, obj
- 이 이름들을 보면 **“도대체 이 데이터가 무엇을 의미하나?”**를 알기 어렵다.
- 코드 독자는 이름의 용도나 맥락을 파악하기 위해 결국 내부 구현(또는 문서)을 직접 찾아봐야 하므로, 간단하게 추론할 수 있는 장점을 잃게 된다.
너무 구체적일 때의 문제
- 예: sha256HashedPasswordForAdminUserOnMobileApp
- 이름에 구현 세부 사항(sha256, adminUser, MobileApp 등)이 그대로 노출되면, 나중에 변경하기 힘들다.
- 비밀번호 해싱 방식을 Bcrypt로 바꾸거나, 모바일이 아닌 웹 기반에서 사용할 때도 이름을 갈아엎어야 하므로, 유지보수 비용이 커진다.
적절히 협의성 있는 이름의 예시
- hashedPassword, encryptedMessage, calculateTax, fetchWeatherData
- 이들은 “무엇을 하는가?”나 “어떤 데이터인가?”는 명확히 표현하지만, 내부 구조나 세부 구현까지는 드러내지 않는다.
- 핵심 특성(해싱, 암호화, 계산, 날씨 데이터 가져오기 등)이 이름에 담겨 있지만, ‘어떤 해시 알고리즘이 쓰이는지’, ‘어떤 방식으로 데이터가 날아오는지’ 같은 구체적인 구현은 필요하면 내부 구현에서만 표현한다.
// BAD
// (1) 구현이 드러난 함수명
function hashPasswordWithSHA256AndSalt(user, saltValue) {
// 내부에서 SHA256 + saltValue를 사용해 해시 계산
return doHash(user.password + saltValue);
}
// (2) 너무 모호한 변수명
function calcData(data) {
// ...실제로는 사용자 비밀번호를 해시하지만...
// 이 함수의 목적이 전혀 드러나지 않는다.
return someHashFunction(data);
}
- (1) 함수 이름에 구현 방식을 그대로 노출했다.
- 나중에 해시 방식을 바꾸려면 함수명까지 몽땅 수정해야 하므로 변경 비용이 높아짐.
- (2) calcData 같은 이름은 무엇을 계산하는지 목적이 모호하다.
- 처음 보는 사람은 “이 함수가 도대체 무슨 계산을 하는가?”를 전혀 알 수 없다.
// GOOD
// (1) 목적을 표현한 함수명
function hashUserPassword(user) {
// 내부 구현은 감춰둔다. (SHA256, Bcrypt, etc.)
return someHashFunction(user.password);
}
// (2) 역할이 명확한 변수명
const hashedPassword = hashUserPassword(currentUser);
saveHashedPassword(hashedPassword);
- (1) 함수 이름이 **‘사용자의 패스워드를 해시한다’ **는 목적에 초점을 맞춰 작성되었다.
- 내부적으로 어떤 해시 알고리즘을 쓰는지는 감춰져 있으므로, 변경 시 함수명은 그대로 둬도 된다.
- (2) 변수명 hashedPassword는 “해시된 비밀번호”라는 의미를 바로 전달한다.
- 이름만 봐도 무엇을 담고 있는 값인지 직관적으로 알 수 있다.
2. 일관성 (consistency)
정의: 같은 개념(의미)에 대해 언제나 같은 이름을 쓰고, 이름이 달라질 때는 의미도 달라진다는 것을 분명히 하는 것
문맥마다 이름의 의미가 달라질 때의 문제
- 예: map이라는 이름을 어떤 곳에서는 “배열 변환함수(Array.prototype.map)”, 어떤 곳에서는 “지도를 저장하는 객체”로 사용
- 코드를 읽는 사람이 “이 map이 어떤 map이지?”를 매번 문맥으로 파악해야 하므로 인지적 부담이 커진다.
- 문서화·주석 등에 “이 모듈의 map은 배열변환이 아니라 지리정보용 map이다”라고 계속 설명해야 하며, 협업 시에도 혼동이 늘어난다.
일관성을 지키지 못하는 또 다른 예시
- 같은 Student라는 이름이 부서별로 다른 뜻을 가짐:
- 입학부서: Student = “이번 학기에 지원하여 합격한 사람”
- 회계부서: Student = “이번 학기에 등록금을 낸 사람”
- 학과부서: Student = “특정 학과 수업을 등록한 사람”
- 하나의 코드베이스 안에서 섞여 있으면, 어느 지점에서 Student가 어떤 의미로 쓰였는지 모두 기억·확인해야 한다.
일관성을 지키는 방법
- 하나의 개념을 지칭할 때는 하나의 이름만 쓴다.
- 문맥마다 의미가 확연히 다르다면, 아예 다른 이름을 쓰는 편이 낫다. 예:
- applicantStudent, registeredStudent, enrolledStudent 등으로 명시적으로 구분, 단 구분으로 인한 추가 비용은 감수 해야할 수 있음.
요약
-
이름은 너무 일반적이지도, 구체적이지도 않게 핵심 목적을 드러내며 내부 구현 세부 사항은 감추되 정체성은 확실히 표현해야 한다.
-
같은 의미에는 같은 이름을, 다른 의미에는 다른 이름을 쓴다.
최소 이 두 가지만 지켜도, 다른 사람에게 내 코드를 이해시키는 것이 더 쉬워지고 기능을 변경하거나 확장할 때도 유연성을 가질 수 있다.
무엇보다 팀 내에서 의사소통 비용을 줄이는 일은 전반적으로 너무 너무 너무 좋은일이다.
참고