Clean Code - 의미 있는 이름

의도를 분명히 밝혀라

"의도가 분명한 이름이 정말로 중요하다. 좋은 이름을 통해 절약하는 시간이 매우 많다."

이름은 의도가 분명하게 지어야 한다. 더 좋은 이름이 생각난다면 개선해야 한다. 또한, 이름을 지었을 때 다음과 같은 질문들에 모두 답할 수 있어야 한다.

  • 존재 이유는?
  • 수행하는 기능은?
  • 사용 방법은?

추가적으로, 주석이 따로 필요한 경우라면 그 의도를 정확하고 분명하게 드러내지 못했다고 봐야 한다.

int d; // 경과 시간(단위 : 날짜)

이름 d는 아무 의미도 드러나지 않는다. 경과 시간이나 날짜라는 느낌을 받을 수 없다. 측정하려는 값과 단위를 표현하는 이름이 필요하다.

int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

의도가 드러나는 이름을 사용하면 코드 이해와 변경이 쉬워질 수 있다.
이름만 고쳐도 각 변수, 클래스, 함수 등이 하는 일을 명확히 이해할 수 있고, 이는 자연스럽게 유지보수성 증가로 이어질 수 있다.

그릇된 정보를 피해라

"프로그래머는 코드에 그릇된 단서를 남겨서는 안 된다. 코드의 의미를 흐리거나 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해서는 안 된다."

약어를 사용하거나, 단어를 사용하는 경우 그 단어가 일반적으로 다른 의미로 사용되고 있는 경우 그 단어를 사용하는 것을 피해야 한다.

만약 여러 사용자 계정을 여러 그룹으로 묶는 다면, 실제 List가 아닌 경우 accountList로 명명하지 않는다. List는 개발자에게 특수한 의미이다. 따라서, 계정을 담는 컨테이너가 List가 아닌 경우 개발자에게 잘못된 의미를 전달하게 될 수 있다.
이 경우 accountGroup, bunchOfAccounts, Accounts 와 같이 명명하는 것이 좋다.

유사한 이름을 사용하지 않는 것도 중요하다.

의미 있게 구분하라

"이름을 명명할 때 이름이 달라야 한다면, 의미도 달라져야 한다. 예를 들어, 연속적인 숫자를 붙인 이름(a1, a2 ... aN)은 의도적인 이름이 아니다."

위와 같은 이름은 잘못된 정보를 제공하는 이름은 아니다. 다만, 아무런 정보를 제공하지 못한다. 이름만 보고서는 의도를 전혀 알 수 없다.

다음 코드를 보도록 하자.

fun copyChars(a1 : CharArray, a2 : CharArray) {
        for(i in 0..a1.size)
            a2[i] = a1[i]
}

위 코드에서 source, destination을 사용했다면 코드가 훨씬 읽기 더 쉬워질 것이다.

그리고, 중복된 이름을 반드시 피해야 한다. 코드를 읽던 도중, Customer 클래스와 Customer Object 클래스를 발견했다면 그 차이를 바로 알 수 있을까? 불가능하다.

읽는 사람이 그 차이를 바로 알 수 있도록 이름을 지어야 한다.

발음하기 쉬운 이름을 사용하라

"사람들은 단어에 능숙하다. 발음하기 쉬운 단어를 사용해야 얘기하기도 편하다. 협업에 있어 필수적이다."

간단하게 두 개의 예시를 비교해 본다.

class DtaRcrd102 {
    private Date genymdhms;
    private Date modymdhms;
    private final String pszqint = "102";
};

class Customer {
    private Date generationTimestamp;
    private Date modificationTimestamp;
    private final String recordId = "102";
};

아래 코드는 위 코드와 비교했을 때 대화에 있어 훨씬 용이하다.

검색하기 쉬운 이름을 사용하라

"코드에서 필요한 이름을 검색했을 때, 쉽게 찾을 수 있는 이름이어야 한다. 숫자가 들어가거나, 알파벳 e를 검색한다면 너무 많이 검색될 것이다. 검색하기 쉬운 이름을 사용해라."

검색하기 쉬운 이름을 명명하기 위한 관점에서 긴 단어가 짧은 단어보다 좋다. 또한, 이름 길이는 범위 크기에 비례해야 한다. 길이가 짧은 코드라면 전통적으로 반복문 현재 횟수를 의미하는 i, j, k 등과 같은 변수는 괜찮다.

다만, 길이가 길어진다면 그 목적에 맞게 길게 명명하는 것이 바람직하다.

코드의 길이가 조금 길어질 수 있다고 하더라도 검색할 때 유용할 수 있도록 상수 지정을 해두는 것이 좋다. 가령, 상수 5를 명시적으로 사용했을 때 검색한 경우 수 없이 많은 검색 결과가 나올 것이다. 하지만, 상수로 WORK_DAYS_PER_WEEK와 같이 명명한다면 금방 찾아낼 수 있다.

인코딩을 피하라

"개발자가 데이터 타입을 이름에 표현할 필요가 없다. 이 같은 방식은 읽기도 어려우며 잘 못 이해할 수 있게 만들어 버린다."

"멤버 변수에 m_ 과 같은 접두어를 붙일 필요도 없다. 클래스와 함수는 접두어가 필요 없을 정도로 작아야 한다."

"인코딩이 필요한 경우도 분명 존재한다. 가령, 인터페이스와 구현체가 있을 경우 구현체 이름 접미사로 Impl 등의 키워드를 붙인다."

자신의 기억력을 자랑하지 마라

"명료함이 최선이다. 쉽게 바로 이해할 수 있는 명명 규칙을 따르자."

독자가 코드를 읽으면서 변수 이름을 독자가 아는 이름으로 바꿔야 할 필요성이 느껴진다면 변수 이름은 바람직하지 못한 것이다.

독자가 한번 더 생각해야 할 이름으로 명명하지 말자.

클래스 이름

"클래스 이름은 명사나 명사구가 적합하다. Customer, WikiPage, Account, AddressParser 등이 좋은 예이고, Manager, Processor, Data, Info 등과 같은 단어는 피한다. 동사는 사용하지 않는다."

메서드 이름

"메서드 이름은 동사나 동사구가 적합하다. postPayment, deletePage, save 등이 좋은 예이다. 접근자(Accessor), 변경자(Mutator), 조건자(Predicate)는 값 앞에 get, set, is를 붙인다."

생성자를 오버로드 하는 경우 static 하게 사용한다.
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
Complex fulcrumPoint = new Complex(23.0)
위 두 코드를 비교했을 때, 위 코드가 좋은 코드이다.

기발한 이름은 피하라

"일반적으로 사용되는 이름을 선택하자. HolyHandGrenade 함수는 무슨 역할인지 봐서는 알 수 없다. DeleteItems가 더 좋은 이름이다."

한 개념에 한 단어를 사용하라

"추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다. 가령, 같은 역할의 메서드를 클래스마다 fetch, retrieve, get으로 부른다면 혼란스럽다. 일관성 있는 어휘는 코드를 사용할 개발자가 반갑게 여길 것이다."

메서드 이름은 독자적이고 일관적이어야 한다.

말장난을 하지 마라

"한 단어를 두 가지 목적으로 사용하지 마라."

한 개념에 한 단어를 사용하라 라는 규칙을 따랐다는 뜻에서, 여러 클래스에 add 메서드가 있다고 가정하자.

그런데 지금까지 구현한 add 메서드는 두 개를 더하거나 이어서 새로운 값을 만든 것이다. 새로 작성하는 메서드는 집합에 값 하나를 추가한다.

이 경우, insertappend 라는 이름이 적당하다. 개발자는 코드를 대충 훑어 봐도 이해할 수 있는 수준의 코드를 작성할 수 있어야 한다.

해법 영역에서 가져온 이름을 사용하라

"코드를 읽는 사람 또한 개발자다. 따라서 알고리즘 이름, 디자인 패턴 이름, 수학 용어, 전산 용어를 사용해도 괜찮다. 모든 이름을 문제 영역에서 가져오는 것은 현명하지 못하다. 기술 개념에는 기술 이름이 가장 적합하다."

문제 영역에서 가져온 이름을 사용하라

"적절한 프로그래밍 용어가 없는 경우, 문제 영역에서 이름을 가져 온다. 다만, 해법 영역(Solution Domain)과 문제 영역(Problem Domain) 영역을 명확히 구분해야 한다. 문제 영역 개념과 관련이 깊다면, 문제 영역에서 이름을 가져 온다."

의미 있는 맥락을 추가하라

"대부분의 이름은 스스로 의미가 불명확하다. 따라서, 맥락을 부여하여 의미를 분명하게 만들자."

예를 들어, firstName, lastName, street, houseNumber, city, state, zipcode 라는 변수가 있을 때, 변수를 훑어 본다면 주소라는 사실을 금방 알아낼 수 있다.

그런데 만약, 어떤 메소드에서는 state 라는 변수만 사용한다면 이것이 주소의 일부라는 사실을 깨닫기 오랜 시간이 걸릴 수 있다.

그래서 addr이라는 접두어를 추가한다. addrFirstName, addrLastName, addrState와 같이 사용한다면 맥락이 좀 더 분명해질 수 있다.

맥락을 좀 더 분명하게 만드는 방법으로 클래스를 따로 생성하는 방법이 있다.

불필요한 맥락을 없애라

"일반적으로, 짧은 이름이 긴 이름보다 좋다. 단, 의미가 분명한 경우에 한해서이다. 이름에 불필요한 맥락을 추가하지 않도록 주의해야 한다."

예를 들어, accountAddress와 customerAddress는 Address 클래스 인스턴스로는 좋은 이름이다. 하지만, 클래스 이름으로는 적합하지 못하다.

포트 주소, MAC 주소, 웹 주소를 구분해야 한다면 PostalAddress, MAC, URI 라는 이름도 괜찮다. 의미가 좀 더 분명해질 수 있기 때문이다.

결론

이름을 잘 짓는 것 하나만으로도 엄청난 이득을 불러올 수 있다. 대체적으로 서비스를 개발할 때 개발 기간보다 유지보수 기간이 훨씬 더 길다. 따라서, 테스트 용이성에 목적을 둔 코드를 작성하는 것이 훨씬 경제적이다.

이름만 잘 짓더라도 코드 가독성이 매우 높아질 수 있다. 단기적인 효과는 물론이고, 장기적 이익도 보장될 수 있을 것이다.

좋은 웹페이지 즐겨찾기