좋은 이름 짓기

    좋은 이름 짓기
    Jane Developer
    Woosol Kim
    Full-stack Developer
    Ilsan, Goyang
    5 min read
    2023. 09. 13

    좋은 이름이란

    개발을 하다보면 좋은 변수/함수 이름 짓기에 꽤 많은 시간을 쏟는다.

    좋은 이름은 팀원과의 의사소통 비용을 줄일 뿐 아니라, 추후 유지보수와 확장에도 큰 역할을 한다.

    함수 이름이 무언가 이상한데, 명확히 설명을 못했었던 이유는 나 스스로도 "좋은 이름" 에 대한 기준이 없어서 였으리라.

    좋은 이름에 대한 명확한 답이 있는 것이 아니나,

    이름이 왜 중요한지, 어떤 이름이 왜 나쁜지, 왜 좋은지 스스로 참고할 기준은 갖추는건 분명 도움이 될 것이다.


    이름은 추상화 계층을 만든다

    이름은 가장 간단한 추상화 계층이다.

    좋은 이름은 해당하는 코드가 무엇을 하는지 단번에 이해할 수 있고,

    추가적인 설명이 필요없으며, 오해가 적어진다.

    또한, 함수나 변수의 목적이 분명히 드러나므로, 기능 확장, 변경시에도 "이 코드가 담당하는 역할" 을 빠르게 파악할 수 있다

    네이밍 핵심 포인트

    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 등으로 명시적으로 구분, 단 구분으로 인한 추가 비용은 감수 해야할 수 있음.

    요약

    • 이름은 너무 일반적이지도, 구체적이지도 않게 핵심 목적을 드러내며 내부 구현 세부 사항은 감추되 정체성은 확실히 표현해야 한다.

    • 같은 의미에는 같은 이름을, 다른 의미에는 다른 이름을 쓴다.

    최소 이 두 가지만 지켜도, 다른 사람에게 내 코드를 이해시키는 것이 더 쉬워지고 기능을 변경하거나 확장할 때도 유연성을 가질 수 있다.

    무엇보다 팀 내에서 의사소통 비용을 줄이는 일은 전반적으로 너무 너무 너무 좋은일이다.

    참고

    woosol

    Written by Woosol Kim

    똑똑하게 일하는게 꿈