들어가며
읽기 쉬운 코드, 이해하기 쉬운 코드를 위해 네이밍에 시간을 들이지만 쉽지 않았다.
이 변수/메서드/클래스의 존재이유나 역할을 생각해 단어를 찾아보지만 생소한 단어가 나오거나 이름이 길어질 뿐이었다.
클린 코드를 읽기 시작한지 얼마 되지 않았지만, 지금 가장 필요한 방법에 대해 알려주는 글이라 생각되어 내용을 정리한다.
의미 있는 이름 짓기
의도를 분명히 밝혀라
변수/메서드/클래스의 이름은 다음과 같은 세 가지 질문에 모두 답할 수 있도록 지어야 한다.
(1) 존재 이유
(2) 수행 기능
(3) 사용 방법
예시
상담원에게 담당자가 없는 문의 건들을 배분하는 메서드의 이름을 지으려고 한다.
(기능 상세)
- 매니저가 사용할 기능이다.
- 매니저는 자신이 담당하는 카테고리의 문의 건과 상담원을 관리한다.
- 배분하는 기준
-- 상담원이 현재 맡고 있는 문의 건의 개수가 적은 사람을 우선으로 한다.
-- 담당하는 문의 개수가 동일하다면 id를 비교하여 사전의 앞에 오는 사람을 우선으로 한다.
이름 지어보기
(1) 존재 이유 : 담당자가 없는 문의 건을 상담원에게 분배하기 위해
(2) 수행 기능 : 상담을 분배한다.
(3) 사용 방법 : 매니저가 사용한다.
결과
public void distibuteCounselsWithoutCounselor(String managerId) { }
의도
메서드에는 담당자가 없는 문의 건수를 배분한다는 의미를 담고, 매개변수로 매니저의 id를 받아 사용 방법을 표시했다.
(좋은 이름인지 혼자 봐서는 잘 모르겠으니 의견을 댓글로 알려주시면 감사하겠습니다.)
그릇된 정보를 피하라
위에서 이름 지은 distributeCounselsWithoutCounselor의 구현을 보자.
@Transactional
public void distributeCounselsWithoutCounselor(String managerId) {
List<Counsel> counselList = getCounselsWithoutCounselor(managerId);
List<Charger> counselorList = chargerMapper.selectAvailableCounselorList(managerId);
if (counselorList.size() == 0) {
throw new IllegalStateException("There are no Counselors available.");
}
PriorityQueue<Charger> counselorQueue = makePriorityQueue(counselorList);
if (counselorQueue.isEmpty()) {
throw new IllegalStateException("Cast Error : Cannot convert List to Queue");
}
for (Counsel currentCounsel : counselList) {
Charger currentCharger = counselorQueue.poll();
assignCounselor(currentCounsel, currentCharger.getUserId());
changeModifier(currentCounsel, managerId);
addOneMoreCounsel(currentCharger);
counselorQueue.add(currentCharger);
}
for (Counsel counsel : counselList) {
counselMapper.updateCounselCharger(counsel);
counselMapper.insertCounselHistory(counsel.getId());
}
}
컨테이너 유형
이 부분(그릇된 정보를 피하라)에서 다룬 내용은 아니지만,
실제 컨테이너가 List인 경우에도 컨테이너 유형은 이름에 넣지 않는 편이 바람직하다고 한다.
OOOList, OOOMap 같은 이름을 짓는 습관이 있는데 고쳐야겠다.
그 외에는 '그릇된 정보'를 주고 있는 메서드나 변수명은 보이지 않는다.
(이 부분도 다른 생각이 있을 경우 의견 주시면 감사하겠습니다.)
의미 있게 구분하라
이 말의 의미는 읽는 사람이 차이를 알도록 이름을 지어야 한다는 것이다.
이 원칙을 위반하는 부분도 보이지 않는다.
발음하기 쉬운 이름을 사용하라
코드를 보며 지적인 대화가 가능하도록 이름을 지으면 좋다는 것이다.
위의 코드에서는 불필요하게 사용한 줄임말이나 발음하기 어려운 부분은 보이지 않는다.
검색하기 쉬운 이름을 사용하라
짧은 이름이 좋지만, 이런 관점에서는 긴 이름이 짧은 이름보다 낫다.
이름 길이는 범위 크기에 비례해야 한다고 하는데, 이 역시 위반하는 부분은 없는 듯하다.
인코딩을 피하라
타입을 붙인 변수명/메서드명은 없다.
그릇된 정보를 피하라에서 컨테이너 유형을 넣지 말라는 원칙을 지키지 못한 부분을 제외한다면 그렇다.
자신의 기억력을 자랑하지 마라
의미를 담지 않은 변수명을 코드를 짤 때에는 기억했을지 몰라도,
다른 사람이 코드를 보게 되거나 작성자가 다시 코드를 보게 될 때에도 기억하리라는 보장이 없다.
언제나 이름에는 존재이유/기능/사용방법을 담자.
특히 사용하지 말아야 할 것은 한 글자짜리 이름이다. (예외 : 반복문에서 반복 횟수를 세는 변수)
기발한 이름은 피하라
재미난 이름보다는 명료한 이름을 사용하라는 원칙이다.
코드에서 곰인형을 푸라고 부르기 보다는 곰인형이라고 명확하게 의미를 드러내는 것이 더 도움된다.
한 개념에 한 단어를 사용하라
일관성 있는 어휘를 사용하라는 원칙이다.
List<Counsel> counselList = getCounselsWithoutCounselor(managerId);
List<Charger> counselorList = chargerMapper.selectAvailableCounselorList(managerId);
이 부분에서 '가져온다'는 동일한 개념에 get과 select라는 각각 다른 어휘를 사용했다.
둘 다 get으로 하고 싶었으나, 쿼리 id의 경우 쿼리 키워드를 그대로 사용하는 것이 좋겠다는 의견에 따라 변경한 것이다.
(이 부분도 의견이 궁금하다.)
말장난을 하지 마라
한 단어를 두 가지 목적으로 사용하지 말라는 원칙이다.
이것은 한 개념에 한 단어를 사용하라는 원칙과 맥락을 같이 한다.
여기 의미는 비슷하지만 기능이 다른 두 메서드가 있다.
(1) 두 값을 더하는 메서드
(2) 집합에 값 하나를 추가하는 메서드
만약 이 두 메서드를 모두 'add'로 시작하는 이름으로 한다면 어떨까?
이는 '사용 방법'이 명확히 드러나지 않은 이름이 될 것이다.
해법 영역에서 가져온 이름을 사용하라
전산 용어나 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮다는 의미이다.
코드를 읽는 우리는 비슷한 배경지식을 가진 프로그래머니까.
문제 영역에서 가져온 이름을 사용하라
적절한 용어가 없다면 도메인에서 자주 사용되는 이름을 사용하라는 의미이다.
그 이름의 의미를 모르는 프로그래머가 해당 분야 전문가에게 개념을 물어 얼마든지 파악할 수 있기 때문이다.
의미 있는 맥락을 추가하라
접두어를 추가하거나, 별도의 클래스를 생성해 멤버변수로 만들면 좋다.
맥락을 처음부터 알지 못하더라도 변수 하나만으로 맥락을 파악할 수 있게 된다.
불필요한 맥락을 없애라
accountAddress, customerAddress를 예로 들어보자.
이 이름은 인스턴스의 이름으로 적절할지 모르겠으나, 클래스의 이름으로는 부적절하다.
Address로 이름지으면 된다.
마치며
오래 생각해야 하지만 시간 들이기 쉽지 않은 이름 짓기에 대해 정리해보았다.
단편적인 메서드 예시 하나만으로는 바로 적용하기에 긴가민가한 부분도 있었지만,
이 조언들을 따르며 조금씩 습관으로 만들다보면 이해하기 쉬운 코드, 읽기 쉬운 코드를 작성할 수 있을 것이다.