본문 바로가기
Java/[Clean Code]

[Clean Code] 1. 의미 있는 이름

2021. 8. 5.
반응형

개발자 대다수는 자신이 짠 클래스 이름과 메서드 이름을 모두 기억하지 못한다. 문장처럼 읽히는 코드 또는 표나 자료구조처럼 읽히는 코드를 짜는 데만 집중해야 마땅하다. 의미 있는 이름을 짓는 규칙을 적용하면 가독성이 높아지며, 단기적인 효과는 물론 장기적인 이익도 보장한다.

 

소프트웨어에서 의미 있는 이름을 지어야 하는 이유

변수, 함수, 인수, 클래스, 패키지 등 우리는 모든 곳에 이름을 붙인다. 따라서 이름을 잘 지으면 편리하다. 좋은 이름을 지으려면 시간이 걸릴지 모르지만 좋은 이름으로 절약하는 시간이 훨씬 크다. 이름을 주의하여 지으면 자신은 물론이고 코드를 읽는 사람이 좀 더 행복해질 것이다. 의미 있는 이름을 짓는 규칙들을 알아보자.

1. 의도가 분명한 이름을 지어라
2. 그릇된 정보를 남기지 말아라
3. 의미 있게 구분하라
4. 발음하기 쉬운 이름을 사용하라
5. 검색하기 쉬운 이름을 사용하라
6. 인코딩을 피하라
7. 자신의 기억력을 자랑하지 마라
8. 클래스 이름과 메서드 이름
9. 기발한 이름은 피하라
10. 한 개념에 한 단어를 사용하라. 그러나 말장난하지 마라
11. 해법 영역 또는 문제 영역에서 가져온 이름을 사용하라
12. 의미 있는 맥락을 추가하고, 불필요한 맥락을 없애라

 

1. 의도가 분명한 이름을 지어라

이름을 지을 때는 다음과 같은 질문에 대답을 할 수 있어야 한다. 이것의 존재 이유는? 수행하는 기능은? 사용 방법은? 이 질문들을 대답하기 위해 따로 주석이 필요하다면 의도를 분명히 드러내지 못한 것이다.

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

d라는 이름은 아무런 의미도 드러나지 않는다. 따라서 측정값인 경과 시간과 단위인 날짜를 표현하는 이름이 필요하다. d보다는 daysSinceCreation 또는 fileAgeInDays라는 이름이 의도를 알아내기 쉽다. 이렇듯 의도가 드러나는 이름을 사용하면 코드의 이해와 변경이 쉬워진다. 아래의 두 코드를 살펴보면, 같은 코드지만 이름이 달라진 것만으로도 코드가 하는 일과 의도를 알아내기 쉬워진다.

public List<int[]> getThem(){
	List<int[]> list1 = new ArrayList<int[]>();
	for (int[] x: theList)
		if (x[0] == 4)
			list1.add(x);
	return list1;
}
public List<int[]> getFlaggedCells(){
	List<int[]> flaggedCells = new ArrayList<int[]>();
	for (int[] cell: gameBoard)
		if (cell[STATUS_VALUE] == FLAGGED)
			flaggedCells.add(cell);
	return flaggedCells;
}

위의 코드에서 변화한 것은 코드의 단순성이 아니라 함축성이다. 코드 맥락이 코드 자체에 드러남으로써 코드가 더욱 명확해졌다. 이름만 고쳤음에도 함수가 하는 일을 이해하기 쉬워진 것을 알 수 있다.

 

2. 그릇된 정보를 남기지 말아라

그릇된 단서는 코드의 의미를 흐린다. 나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해도 안 된다. 예를 들어 여러 계정을 그룹으로 묶을 때, 실제로 List가 아니라면 accountList라고 명명하면 안 된다. 사실 실제 List라 할지라도 accountList와 같이 컨테이너 유형을 이름에 넣는 것은 바람직하지 않다. 대신 accountGroup 또는 단순하게 Accounts라고 명명하는 것이 좋다.

 

또한 서로 유사한 이름을 사용하지 않도록 주의해야 한다. 한 모듈에서 XYZControllerForEfficientHandlingOfStrings라는 이름을 사용하고 조금 떨어진 모듈에서 XYZControllerForEfficientStorageOfStrings라는 이름을 사용하면 읽는 사람은 헷갈릴 수밖에 없다.

 

유사한 개념은 유사한 표기법을 사용하여 일관성이 떨어지지 않도록 해야 하지만, 서로의 기능에 대해 구분이 가능한 이름을 명명해야 한다. 가장 최악의 경우가 소문자 L이나 대문자 O로 명명한 변수다. l은 1과, O는 0과 구분하기 힘들기 때문이다.

3. 의미 있게 구분하라

동일한 범위 안에서는 다른 두 개념에 같은 이름을 사용할 수 없다. 이때 연속된 숫자를 덧붙이거나 불용어를 추가하는 방식은 적절하지 못하다. 이름이 달라야 한다면 의미도 달라져야 한다. a1, a2,... aN은 그릇된 정보를 제공하는 이름도 아니고 아무런 정보를 제공하지 못한다.

 

불용어를 추가한 이름도 마찬가지다. Product라는 클래스가 있다고 해서 다른 클래스를 ProductInfo 또는 ProductData라고 명명한다면 개념을 구분하지 않고 이름만 달리 한 것이다. Customer와 CustomerObject, Name과 NameString 등 타입을 붙인 이름도 둘의 차이를 구분할 수 없으므로 좋지 않은 이름이다. 따라서 읽는 사람이 차이를 알도록 이름을 지어야 한다.

 

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

발음하기 어려운 이름은 토론하기도 어렵다. generate date, year, month, day, hour, minute, second를 줄여서 genymdhms라는 단어를 사용했다고 하자. 누군가는 "젠 와이 엠 디 에이치 엠 에스"라고 발음할 것이고 누군가는 쓰인 대로 "젠 야무다 함즈"라고 발음할 수도 있다. 새로운 개발자가 온다면 변수를 설명해주고 발음도 알려줘야 할 것이다. 이보다는 generationTimestamp 같은 이름이 훨씬 얘기하기 쉬울 것이다.

 

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

문자 하나만을 사용하는 이름 또는 상수는 코드에서 쉽게 눈에 띄지 않는다. MAX_CLASS_PER_STUDENT는 검색하기 쉽지만 숫자 7은 까다롭다. 7이 들어가는 수식이나 파일 등이 모두 검색되기 때문이다. e라는 문자도 영어에서 매우 많이 사용되는 문자이기 때문에 변수 이름으로 적합하지 못하다.

 

이름의 길이는 범위 크기에 비례해야 한다. 간단한 메서드의 로컬 변수는 한 문자를 사용해도 좋다. 그러나 변수나 상수를 여러 곳에서 사용한다면 검색하기 쉬운 이름이 좋을 것이다. 이름을 의미 있게 지으면 함수가 길어지지만 검색이 쉬워진다. 상수나 짧은 문자를 사용한다면 검색 결과를 모두 읽어보고 의미를 분석해서 가려내야 한다.

 

6. 인코딩을 피하라

이름에 인코딩할 정보는 아주 많으므로 유형이나 범위까지 인코딩에 넣으면 그만큼 이름을 해독하기 어려워진다. 인코딩한 이름은 거의 발음하기 어렵고 오타가 생기기도 한다.

 

옛날에는 컴파일러가 타입을 점검하지 않아서 프로그래머가 타입을 기억할 단서가 필요했지만 요즘은 컴파일러가 타입을 기억하고 강제하기 때문에 변수 이름에 타입을 인코딩할 필요가 없다. 멤버 변수에 m_이라는 접두어를 붙일 필요도 없어졌다. 클래스와 함수는 접두어가 필요 없을 정도로 작아야 한다.

public class Part {
	private String m_dsc;
	void setName(String name){
		m_dsc = name;
	}
}
public class Part {
	private String description;
	void setDescription(String description){
		this.description = description;
	}
}

때로는 인코딩이 필요한 경우도 있다. 도형을 생성하는 Abstract Factory를 구현한다고 가정하면 인터페이스 클래스와 구현 클래스를 어떻게 구분 지어 이름을 짓는 것이 좋을까? IShapeFactory와 ShapeFactory라고 지을 수도 있겠다. 그러나 접두어 I는 주의를 흩트리고 과도한 정보를 제공한다. 두 이름 중 하나를 인코딩해야 한다면 구현 클래스를 택할 것이다. ShapeFactory - ShapeFactoryImp 같은 이름이 훨씬 좋다.

 

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

문자 하나만 사용하는 변수 이름은 루프에서 반복 횟수를 세는 i, j, k 외에는 문제가 있다. 독자가 실제 개념으로 변환해야 하기 때문이다. 일반적으로 프로그래머들은 아주 똑똑하기 때문에 r이라는 변수가 어떤 의미인지 언제나 기억할 수도 있다. 그러나 전문가 프로그래머는 명료함이 최고라는 사실을 이해한다. 전문가 프로그래머는 남이 이해하는 코드를 내놓는다.

 

8. 클래스 이름과 메서드 이름

클래스 이름과 객체 이름명사나 명사구가 적합하다.

Customer, WikiPage, Account, AddressParser (O)
Manager, Processor, Data, Info (X)

메서드 이름동사나 동사구가 적합하다. postPayment, deletePage 등이 좋은 예시다. 접근자, 변경자, 조건자는 javabean 표준에 따라 get, set, is를 붙인다.

string name = exployee.getName();
customer.setName("jung");
if (paycheck.isPosted()) ...

생성자를 중복 정의(overload)할 때는 정적 팩토리 메서드를 사용하며 이름은 인수를 설명하는 이름을 사용한다. 아래의 첫 번째 코드가 두 번째 코드보다 좋다. 생성자 사용을 제한하려면 해당 생성자를 private로 선언한다.

Complex fulcrumPoint = Complex.FromRealNumber(23.0);
Complex fulcrumPoint = new Complex(23.0);

 

9. 기발한 이름은 피하라

이름이 너무 기발하면 작성자와 유머 감각이 비슷하고 그마저도 농담을 기억하는 동안만 이름을 기억할 것이다. 재미난 이름보다는 명료한 이름을 사용해야 한다. kill() 대신 whack()을 사용하는 등 구어체나 속어를 이름으로 사용해서는 안된다.

 

10. 한 개념에 한 단어를 사용하라. 그러나 말장난하지 마라

똑같은 메서드를 클래스마다 다르게 사용하지 말고 추상적인 개념 하나에 단어 하나를 선택해서 이를 고수하라. 어느 클래스에서 어느 이름을 썼는지 기억하기 어렵기 때문이다. 메서드 이름은 독자적이고 일관적이어야 주석을 뒤져보지 않고도 프로그래머가 올바른 메서드를 선택할 수 있다.

 

그러나 한 단어를 두 가지 목적으로 사용해서 다른 개념에 같은 단어를 사용한다면 그것은 말장난에 불과하다. 같은 맥락이 아닌데도 일관성을 고려해서 같은 단어를 사용하게 될 수 있다. 예를 들어 여러 클래스에 add라는 메서드가 생겼다. 지금까지 구현한 add는 두 값을 더하거나 이어서 새로운 값을 만드는데, 새로 작성하는 메서드는 집합에 값을 하나 추가한다. 새로운 add 메서드는 기존에 구현한 add 메서드와 맥락이 다르기 때문에 다른 단어(insert나 append)를 사용해야 한다.

 

11. 해법 영역 또는 문제 영역에서 가져온 이름을 사용하라

코드를 작성하는 사람도 읽는 사람도 프로그래머다. 따라서 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 좋다. 모든 이름을 문제 영역(domain)에서 가져오는 것은 바람직하지 못하다. 그러나 기술 용어가 없다면 문제 영역에서 이름을 가져온다. 그러면 보수하는 프로그래머가 분야 전문가에게 의미를 물어 파악할 수 있다.

 

12. 의미 있는 맥락을 추가하고, 불필요한 맥락을 없애라

스스로 의미가 분명한 이름이 없지는 않지만, 대다수 이름은 그러지 못한다. 그래서 클래스, 함수, 이름 공간에 맥락을 부여한다. 지금까지의 모든 방법이 실패한다면 마지막 수단으로 접두어를 붙인다.

firstName, lastName, street, houseNumber, city, state, zipcode

위 변수들을 훑어보면 주소라는 사실을 금방 알아챌 수 있다. 그렇지만 어떤 메서드가 state라는 변수 하나만 사용한다면 변수 state가 주소의 일부라는 사실을 금방 알아채기 어려울 것이다. 이때 addr라는 접두어를 추가하면 맥락이 좀 더 분명해진다. 물론 Address라는 클래스를 생성하면 더 좋다. 맥락을 개선하면 함수를 쪼개기 쉬워지고 알고리즘도 더 명확해진다.

 

Gas Station Deluxe라는 애플리케이션을 짠다고 할 때, 모든 클래스 이름을 GSD로 시작하겠다는 생각은 좋지 못하다. IDE에서 G를 입력하고 자동 완성 키를 누르면 모든 클래스를 열거할 것이다. 이렇게 IDE를 방해할 이유는 없다.

 

일반적으로는 짧은 이름이 긴 이름보다 좋지만 의미가 분명한 경우에 한해서다. 이름에 불필요한 맥락을 추가하지 않도록 주의한다. Address는 클래스 이름으로 적합하다. 만약 포트 주소, MAC 주소, 웹 주소를 구분해야 한다면 PostalAddress, MAC, URI라는 이름도 의미가 분명해지기 때문에 괜찮다.

 

 

 

참고 서적: <Clean Code(클린 코드):애자일 소프트웨어 장인 정신>

 

Clean Code(클린 코드):애자일 소프트웨어 장인 정신

COUPANG

www.coupang.com

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

728x90
반응형

'Java > [Clean Code]' 카테고리의 다른 글

[Clean Code] 3. 주석  (0) 2021.09.02
[Clean Code] 2. 함수  (0) 2021.08.24

댓글