2) 의미있는 이름
소프트웨어에서 이름은 어디나 쓰인다. 정말 도처에서 이름을 사용한다.
이렇듯 많이 사용하므로 이름을 잘 지으면 여러모로 편한다.
(시간이 없으신 분들은 1~3까지만 보셔도 많은 도움이 될 것 같습니다.)
1. 의도를 분명히 밝혀라
"의도가 분명하게 이름을 지으라"고 말하기 쉽다. (의도가 분명한 이름이 정말로 중요하다는 사실을 거듭 강조하고싶다)
좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다.
그러므로 이름을 주의 깊게 살펴 더 나은 이름이 떠오르면 개선하는게 훨씬 좋다.
변수(혹은 함수나 클래스)의 존재 이유는? 수행 기능은? 사용방법은? 따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.
int d; // 경과 시간(단위: 날짜)
이름 d는 아무 의미도 드러나지 않는다. 또한 무엇을 표현하고자 했는지 예측조차 할 수 없다. 경과 시간이나 날짜라는 느낌이 전혀 들지 않는다.
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
이처럼 측정하려는 단위를 표현하는 이름이 필요하다.
의도가 드러나는 이름을 사용하려면 코드 이해와 변경이 쉬워진다.
다음 코드는 무엇을 할까?
public List<int[]>getThem(){
List<int[]>list1 = new ArrayList<int[]>();
for (int[] x : theList)
if(x[0] ===4)
list1.add(X);
return list1;
}
코드가 하는 일을 짐작하기 어렵다. 복잡하지도 않고 공백과 들여쓰기도 적당하고 변수는 세 개, 상수는 두 개뿐이고 화려한 클래스같은 것도 없다.
문제는 코드의 단순성이 아니라 코드의 함축성이다.
제3자가 이 코드를 본다면 아래의 사항을 모를 것이다.
- theList에 무엇이 들었는지
- theList에서 0번째 값이 어째서 중요한지?
- 값 4는 무슨 의미인지?
- 함수가 반환하는 리스트 list1을 어떻게 사용하는지?
이 코드는 지뢰찾기 게임을 만드는 과정의 일부이다.
이제 제대로 이름을 지어보자
- theList가 게임판이기에 더 잘 표현하는 이름인 theList를 gameBoard로 바꾼다.
- 게임판에서 각 칸은 단순 배열로 표현한다.
- 배열에서 0번째 값은 칸 상태를 뜻한다.
- 값 4는 깃발이 꽂힌 상태를 가리킨다.
위와같은 정보를 인지하고 다시 작명을 해보자
public List<int[]> getFlaggedCells(){
List<int[]> flaggedCells = new ArrayList<int[]>;
for (int[] cell : gameBoard)
if(cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
위에서 보듯, 코드의 단순성은 변하지 않았다. 연산자 수와 상수 수는 앞의 예와 똑같으며, 들여쓰기 단계도 동일하다. 그런데도 코드는 더욱 명확해졌다.
한 걸음 더 나아가, int 배열을 사용하는 대신, 칸을 간단한 클래스로 만들어도 된다. isFlagged라는 좀 더 명시적인 함수를 사용해 FLAGGED라는 상수를 감추면 더 좋다.
public List<int[]> getFlaggedCells(){
List<int[]> flaggedCells = new ArrayList<int[]>;
for (int[] cell : gameBoard)
if(cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
단순히 이름만 고쳤는데도 함수가 하는 일을 이해하기 쉬워졌다. 바로 이것이 좋은 이름이 주는 위력이다.
2. 그릇된 정보를 피하라
프로그래머는 코드에 그릇된 단서를 남겨서는 안 된다.
그릇된 단서는 코드 의미를 흐린다. 나름대로 널리 쓰이는 의미 있는 단어를 다른 의미로 사용해도 안 된다.
예를 들어, hp,aix,sco는 변수 이름으로 부적절하다. 유닉스 플랫폼이나 유닉스 변종을 가리키는 이름이기 때문이다.
직각삼각형의 빗변(hypotenuse)를 구현할 때는 hp가 훌륭한 약어로 보일지라도 hp라는 변수는 독자에게 그릇된 정보를 제공한다.
- don't use type information in name
- 여러 계정을 그룹으로 묶을 때, 실제 List가 아니라면, accountList라 명명하지 않는다. 프로그래머에게 List라는 단어는 특수한 의미다. 계정을 담는 컴테이너가 실제 LIst가 아니라면 프로그래머에게 그릇된 정보를 제공하는 셈이다. 그러므로 accountGroup, bunchOfAcoounts, 아니면 단순히 Accounts라 명명하는 게 더 좋다.
- Beware of using names which vary in small ways
- 한 모듈에서 XYZControllerForEfficientHandlingOfStrings라는 이름을 사용하고, 조금 떨어진 모듈에서 XYZControllerForEfficientStorageOfStrings라는 이름을 사용한다면? 차이를 알아채기 어렵다 두 단어는 너무나도 비슷하다.
- 쉽게 말해 작은 차이만을 가지고 서로 다르게 명명하는 것은 피해야한다.
- Spelling similar concepts similarly is information. Using inconsistent spellings is disinformation.
- If names must be different, then they should also mean something different.
- 예를들어 자바스크립트에는 기존에 array.flat(), array.map() 이라는 메소드가 있다 그리고 몇년뒤 이 두 기능을 차례로 한꺼번에 실행하는 메소드를 만들었는데 다음과 같이 명명하였다. array.flatMap() 딱 이름만 봐도 대충예상이 가지 않는가? 만약 flat,map을 포함하지 않고 명명하였다면 이것은 함수명이 불필요한 정보가 되는 것이다.
- 한글판은 번역을 정말 이상하게 해서 더 이해가 가지 않았다. 쉽게 표현하자면 "서로 유사한 개념들은 유사한 표기법을 사용해야 제 3자가 봐도 표기법을 보고 쉽게 이해할 수 있지만 서로 유사한 개념들끼리 부합하지 않는 표기법을 사용한다면 그 표기법은 전혀 도움이 되지 않는 표기법이다. 따라서 오히려 혼란만 가중시킨다.
- 소문자 L이나 대문자 O변수는 그릇된 정보를 전달 할 수 있다.
int a = 1;
if( O == 1)
a = O1;
else
l = 01;
3. 의미있게 구분하라
단순 컴파일러나 인터프리터를 통과하려는 생각으로 한 네이밍은 문제를 일으킨다.
- 연속적인 숫자를 덧붙이지 않는다
- a1, a2, ....aN
- 이런 이름은 의도적인 이름과 정반대이며 아무런 정보를 제공하지 못하는 이름이다
- 저자의 의도가 드러나지 않는다
- a1, a2, ....aN
- 불용어를 추가한 이름은 아무런 정보도 제공하지 못한다. 따라서 읽는 사람이 차이를 알도록 이름을 지어야 한다.
- Info, Data는 a, an, the와 마찬가지로 의미가 불분명한 불용어이다
- a,the와 같은 접두어를 사용하지 말라는 소리가 아니다. 의미가 분명히 다르다면 사용해도 무방하다.
- 예를들어, 모든 지역 변수는 a를 사용하고 모든 함수 인수는 the를 사용해도 되겠다.
- Name, NameString에서 Name은 String일 확률이 대부분 이므로 불용어다. 따라서 그냥 Name으로 명명하는 게 좋다.
- customerInfo - customer, accountData - account, TheMessage - message와 구분이 안되기 때문에 차이를 알 수 있도록 한다
- a,the와 같은 접두어를 사용하지 말라는 소리가 아니다. 의미가 분명히 다르다면 사용해도 무방하다.
- Info, Data는 a, an, the와 마찬가지로 의미가 불분명한 불용어이다
4. 발음하기 쉬운 이름을 사용하라
- 발음하기 어려운 이름은 토론하기도 어렵다.
- ex) genymdhms(generate date, year, month, day ,hour, minute, second) 발음이 힘들다.
5. 검색하기 쉬운 이름을 사용하라
- 문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다는 문제점이 있다.
- MAX_CLASSES_PER_STUDENT는 grep으로 찾기가 쉽지만, 숫자 7은 은근히 까다롭다. 7이 들어가는 파일 이름이나 수식이 모두 검색되기 때문이다. 검색은 되었지만, 7을 사용한 의도가 다른 경우도 있다. 상수가 여러 자리 숫자이고 누군가 상수 내 숫자 위치를 바꿨다면 문제는 더욱 심각해진다. 상수에 버그가 있으나 검색으로 찾아내지 못한다.
- 변수나 상수를 코드 여러 곳에서 사용한다면 검색하기 쉬운 이름이 바람직하다.
for (int j=0; j<34; j++{
s += (t[j]*4)/5;
}
//or
int realDaysPerIdealDay = 4
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j< NUMBER_OF_TASKS; j++{
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = (realTaskDays / WORK_DAYS_RER_WEEK);
sum += realTaskWeeks;
}
위 코드에서 sum이 별로 유용하지 않으나 최소한 검색이 가능하다.
이름을 의미있게 지으면 함수가 길어진다. 하지만 WORK_DAYS_PER_WEEK를 찾기가 얼마나 쉬운지 생각해보라.
그냥 5를 사용한다면 5가 들어가는 이름을 모두 찾은 후 의미를 분석해 원하는 상수를 가려내야 한다.
6. 자신의 기억력을 자랑하지 말고 기발한 이름은 피해야한다.
독자가 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 그 변수 이름은 바람직하지 못하다.
- 문자 하나만 사용하는 변수 이름은 문제가 있다.
- 루프에서 반복 횟수를 세는 변수 i,j,k는 괜찮다 (단, 루프 범위가 아주 작고 다른 이름과 충돌하지 않을 때)
- 그 외에는 대부분 적절하지 못하다. 최악은 이미 a와 b를 사용하므로 c를 선택한다는 논리다.
이름이 너무 기발하면 저자와 유머 감각이 비슷한 사람만, 그리고 농담을 기억하는 동안만, 이름을 기억한다. 재미난 이름보다 명료한 이름을 선택하는게 좋다.
- 특정 문화에서만 사용하는 농담은 피하는 편이 좋다.
7. 한 개념에 한 단어를 사용하라
- 추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다.
- 예를 들어, 똑같은 메소드를 클래스마다 fetch, retrieve.get으로 제각각 부르면 혼란스럽다. 어느 클래스에서 어느 이름을 썼는지 기억하기 어렵다.
- 동일 코드 기반에 controller, manager, driver를 섞어 쓰면 혼란스럽다.
- DeviceManager와 ProtocolController는 근본적으로 어떻게 다른가? 어째서 둘 다 controller가 아닌가?
- 이름이 다르면 독자는 당연히 클래스도 다르고 타입도 다르리라 생가한다.
- 일관성 있는 어휘는 코드를 사용할 프로그래머가 반갑게 여길 선물이다.
8. 말장난을 하지 마라
한 단어를 두 가지 목적으로 사용하면 안된다. 다른 개념에 같은 단어를 사용한다면 그것은 말장난에 불과하다.
- "한 개념에 한 단어를 사용하라"는 규칙을 따랐더니, 예를 들어, 여러 클래스에 add라는 메서드가 생겼다. 모든 add 메서드의 매개변수와 반환값이 의미적으로 똑같다면 문제가 없다. 하지만 때로는 프로그래머가 같은 맥락이 아닌데도 '일관성'을 고려해 add라는 단어를 선택한다.
- 예를 들어, 지금까지 구현한 add메서드는 모두가 기존 값 두 개를 더하거나 이어서 새로운 값을 만든다고 가정하자. 새로 작성하는 메서드는 집합에 값 하나를 추가한다. 이 메서드를 add라 불러도 괜찮을까? 하지만 새 메서드는 기존 add 메서드와 맥락이 다르다. 그러므로 insert나 append라는 이름이 적당하다.
- 프로그래머는 코드를 최대한 이해하기 쉽게 짜야한다.
- 집중적인 탐구가 필요한 코드가 아니라 대충 훑어봐도 이해할 코드 작성이 목표다.
- 의미를 해독할 책임이 독자에게 있는 논문 모델이 아니라 의도를 밝힐 책임이 저자에게 있는 잡지 모델이 바람직하다.
9. 불필요한 맥락을 없애라
애플리케이션 네임을 모든 클래스의 이름의 시작에 넣는 것은 바람직하지 않다.
- '고급 휘발유 충전소'(Gas Station Deluxe)라는 애플리케이션을 짠다고 가정하자 모든 클래스 이름을 GSD로 시작하겠다는 생각은 전혀 바람직하지 못하다. IDE에서 G를 입력하고 자동 완성 키를 누르면 IDE는 모든 클래스를 열거한다. 현명하지 못하다.
- 비슷한 예로, GSD 회계 모듈에 MailingAddress 클래스를 추가하면서 GSDAccountAddress로 이름을 바꿨다. 나중에 다른 고객 관리 프로그램에서 고객 주소가 필요하다 .GSDAccountAddress 클래스를 사용할까? 이름이 적절한가? 이름 17자중 10자는 중복이거나 부적절하다. 일반적으로는 짧은 이름이 긴 이름보다 좋다, 단 의미가 분명한 경우에 한해서다.
- accountAddress와 customerAddress는 Address 클래스 인스턴스로는 좋은 이름이나 클래스 이름으로는 적합하지 못하다. Address는 클래스 이름으로 적합하다. 포트 주소, MAC 주소, 웹 주소를 구분해야 한다면 PostalAddress, Mac, URI라는 이름도 괜찮다. 그러면 의미가 좀 더 분명해 진다.
마치며
암기는 요즘 나오는 도구에게 맡기고, 우리는 문장이나 문단처럼 읽히는 코드 아니면 (정보를 표시하는 최선의 방법이 항상 문장만은 아니므로) 적어도 표나 자료 구조처럼 읽히는 코드를 짜는 데만 집중해야 마땅하다.
여느 코드 개선 노력과 마찬가지로 이름 역시 나름대로 바꿨다가 누군가 질책할지도 모른다. 그렇다고 코드를 개선하려는 노력을 중단해서는 안 된다.
ps
중간에 번역이 이해가 가지 않는 부분이 있어서 글을 작성하는 데 꽤 오랜시간이 걸렸다.
클린코드 원서와 해외 기술 블로그도 참고하니 이해가 잘 되어 일부는 이 글에 잘 녹였다.
일전에 클린코드를 읽었지만 이렇게 다시보니 많이 까먹었다는 것을 세삼 느꼈다. 오늘 이렇게 정리하고 빠른 시일에 다시 봐야겠다.
정말 기능을 구현하는 것 보다. 이렇게 변수/함수/클래스 명을 고민하는 게 참 어렵다는 것을 다시 한번 느꼈다.
같이 보면 좋은 컨테츠
source
클린코드 도서
클린코드 원서 : https://thixalongmy.haugiang.gov.vn/media/1175/clean_code.pdf
https://github.com/JuanCrg90/Clean-Code-Notes#chapter2
'정보 > The 공부' 카테고리의 다른 글
[JS - 심화] Differences whith Throw and Return (0) | 2022.11.05 |
---|---|
[고찰] 상처 받지 않으면서 피드백 주고 받기 (1) | 2022.11.04 |
[JS - 심화] 왜 else , else if문을 지양해야 할까 ? (0) | 2022.10.28 |
[우테콘2022] 우리 팀의 코드리뷰 문화, 이렇게 조금씩 발전했어요 - (3) 코드리뷰 문화, 이렇게 개선했어요 (0) | 2022.10.21 |
[우테콘2022] 우리 팀의 코드리뷰 문화, 이렇게 조금씩 발전했어요 - (2) 코드리뷰, 우리팀은 이렇게 (0) | 2022.10.21 |