[클린코드 리뷰]이름을 잘 짓는 방법

Jarban·2022년 3월 2일
0

클린코드 리뷰

목록 보기
1/2
post-thumbnail

최근 프로그래밍을 하면서 느낀 것이, 조직적인 프로세스와 클린코드는 분명하게 필요하고 이를 처음부터 실천하지 않으면 나중에 처리해야 할 일들이 산더미 처럼 쌓인 다는 것을 몸소 느꼈습니다. 앞으로는 이를 방지하고 실천하기 위해서 다음과 같은 클린코드를 공부합니다.  리뷰는 2장부터 시작합니다.

"의도를 분명하게 이름을 지으라"

  └ 좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다.

변수나 함수 그리고 클래스 이름은 다음과 같은 굵직한 질문에 모두 답해야 한다.
Q ) 변수(혹은 함수나 클래스)의 존재 이유는? 수행 기능은? 사용 방법은?
- 따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.

그러면 나쁜 예시를 보자

예시 1)

public List<int[]> getThem(){
	List<int[]> list1=new ArrayList<int[]>();
    for(int[] x: theList)
    	if(x[0]==4)
        	list.add(x);
        return list1
}

위 코드는 복잡하지 않지만 직관적으로 추측하기 어렵다. 이는 코드 맥락이 코드 자체에 명시적으로 드러나지 않기 떄문이다.

만약 독자가 위 코드 중 다음과 같은 내용을 안다고 가정해보면,

  • theList에 무엇이 들어있는가?
  • theList에서 0번쨰 값이 어째서 중요한가?
  • 값 4는 무슨 의미인가?
  • 함수가 반환하는 리스트 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;
}

클래스화

public List<Cell> getFlaggedCells(){
	List<Cell> flaggedCells =new ArrayList<Cell>();
    for(Cell cell:gameBoard)
    	if(cell.isFlagged())
        	flaggedCells.add(cell);
    return flaggedCells;
}

단순히 이름만 고쳤는데도 함수가 하는 일을 이해하기 쉬워졌다. 이것이 좋은 이름이 주는 위력이다.

"그릇된 정보를 피하라게 이름을 지으라"

  └ 좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다.**

변수나 함수 그리고 클래스 이름은 다음과 같은 굵직한 질문에 모두 답해야 한다.

Q ) 변수(혹은 함수나 클래스)의 존재 이유는? 수행 기능은? 사용 방법은?

   - 따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.

그러면 나쁜 예시를 보자

예시 1) 

public List<int[]> getThem(){

 List<int[]> list1=new ArrayList<int[]>();

    for(int[] x: theList)

     if(x[0]==4)

         list.add(x);

        return list1

}

위 코드는 복잡하지 않지만 직관적으로 추측하기 어렵다. 이는 코드 맥락이 코드 자체에 명시적으로 드러나지 않기 떄문이다.

만약 독자가 위 코드 중 다음과 같은 내용을 안다고 가정해보면,

theList에 무엇이 들어있는가?

theList에서 0번쨰 값이 어째서 중요한가?

값 4는 무슨 의미인가?

함수가 반환하는 리스트 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;
}

클래스화

public List<Cell> getFlaggedCells(){
 List<Cell> flaggedCells =new ArrayList<Cell>();
    for(Cell cell:gameBoard)
     if(cell.isFlagged())
         flaggedCells.add(cell);
    return flaggedCells;
}

단순히 이름만 고쳤는데도 함수가 하는 일을 이해하기 쉬워졌다. 이것이 좋은 이름이 주는 위력이다.

"그릇된 정보를 피하라"

└ 그릇된 단서는 코드의 의미를 흐린다.

ex) hp,aix,sco는 유닉스 변종을 가르키는 이름인데 직각삼각형의 빗병(hypotenuse)을 구현할때 hp가 좋은 약어라고 생각할 수 있지만 hp라는 변수는 독자에게 그릇된 정보를 제공한다.

여러 계정으로 그룹을 묶을 때, 실제 List가 아니라면 accountList는 그릇된 정보이다. accountGroup,bunchOfAccounts가 맞다.

유사한 개념은 유사한 표기법을 사용한다. 이것도 정보다. 일관성이 떨어지는 표기법은 그릇된 정보이다.

ex) XControllerForEfficientHandlingOfStrings , XControllerForEffieientStorageOfStrings

끔찍한 코드

int a=1;
if( O==1 )
a = O1;
else
l = 01;

"의미 있게 구분하라"

컴파일러나 인터프리터만 통과하려는 생각으로 코드를 구현하는 프로그래머는 문제가 있음

컴파일러를 통과할지라도 연속된 숫자를 덧붙이거나 불용어를 추가하는 방식은 적절하기 못하다. 이름이 달라야 한다면 의미도 달라져야 한다.

ex ) Product클래스가 있다고 가정, 다른클래스를 ProductInfo, ProductData라 부른다면 개념을 구분하지 않은 채 이름만 달리한 경우임. Info나 Data는 a, an, the와 마찬가지로 의미가 불분명한 불용어다. 

불용어는 중복임. Customer클래스와 CustomerObject라는 클래스의 차이를 알겠나..?

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

 발음하기 어려운 이름은 토론하기도 어렵다. 프로그래밍은 사회활동이다.

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

if(manState==4)
 MatchingCoupleWithMan()

if(manState==SOLO_MAN_STATE_VALUE)
 MatchingCoupleWithMan()

위와 아래의 소스중 다음에 변경 시 편한 것은 무엇인가?

당연히 수정할 때 편한것은 후자이다. 이는 나중에 모든 STATE값을 변경하고자 한다면 4를 찾아야하는데 4라는 숫자는 중복될 가능성이 높다. 따라서 상수의 경우 매직넘버로 검색하기 쉬운 이름을 사용하는 것이 좋다.

"인코딩을 피하라"

굳이 부담을 더하지 않아도 이름에 인코딩할 정보는 많다. 유형이나 범위의 정보까지 인코딩에 넣으면 그만큼 이름을 해독하기 어려워진다. 

헝가리식 표기법과 멤버 변수 접두어 등은 이제는 옛날에 작성한 구닥다리 코드라는 징표가 됐다.

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

독자가 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 변수 이름은 바람직하지 못하다.

문자 하나만 사용하는 변수 이름은 문제가 있다. 루프에서 반복 횟수를 세는 (i,j,k)는 괜찮다.[단, 루프 범위가 아주 작고 다른 이름과 충돌하지 않을 때만] 최악은 a와 b를 사용하므로 c를 선택한다는 논리다.

전문가가 프로그래머는 명료함이 최고라는 사실을 이해한다. 전문가 프로그래머는 자신의 능력은 좋은 방향으로 사용해, 남들이 이해하는 코드를 내놓는다.

"클래스 이름"

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

Customer, WikiPage, Account, AddressParser이 좋은 예이다. Manager, Processor, Data, Info등과 같은 단어는 피하고 동사는 사용하지 않는다.

"메서드 이름"

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

생성자를 중복정의 할때는 정적 팩토리 메서드를 사용한다.

// 좋은예제
Complex fulcrumPoint=Complex.FromRealNumber(23.0);
// 별로
Complex fulcrumPoint=new Complex(23.0);

"기발한 이름은 피하라"

이름이 너무 기발하면 저자와 유머 감각이 비슷한 사람만 농담을 기억한다. 재미난 이름보다 명료한 이름을 선택하라.

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

추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다. 예를 들어 똑같은 메서드를 클래스마다 fetch, retreieve, get으로 제각각 부르면 혼란스럽다. 어느 클래스에서 어느 이름을 썼는지 기억하기 어렵다.

마찬가지로, controller, manager, driver를 섞어 쓰면 혼란스러움

이름이 다르면 독자는 당연히 클래스도 다르고 타입도 다르리라 생각함.

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

적절한 '프로그래머 용어'가 없다면, 문제 영역에서 이름을 가져온다. 우수한 프로그래머와 설계자라면 해법 영역과 문제 영역을 구분할 줄 알아야 한다. 문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야 한다.

"의미 있는 맥락을 추가하라"

스스로 의미가 분명한 이름이 없지 않다. 하지만 대다수 이름은 그렇지 못하다.
예를들어, firstName, lastName, street, houseNumber, city, state라는 변수가 있다고 했을 때 -> 어떤 메소드는 state만 사용한다면? -> 변수 이름을 쭉 훑어보면 주소와 관련된 변수이름이라고 알 수 있지만, state혼자만 보면 이게 주소와 관련된지 뭔지 모른다. -> 이럴때 addr라는 점두어를 추가해서 맥락을 분명하게 하라. 

"불필요한 맥락을 없애라"

고급 휘발유 충전소라는 어플리케이션을 짠다고 가정했을 때, 모든 클래스 명을 GSD로 시작하겠다는 생각은 전혀 바람직스럽지 않다. 솔직히 전봇대로 이쑤시는 격이다.  일반적으로 짧은 이름이 긴 이름보다 좋다. 단, 의미가 분명한 경우에 한해서다. 이름에 불필요한 맥락을 추가하지 않도록 주의한다.

"마치면서"

좋은 이름을 선택하려면 설명 능력이 뛰어나야 하고 문화적인 배경이 같아야 한다. 이는 가장 어렵다. 사람들이 이름을 바꾸지 않으려는 이유 하나는 다른 개발자가 반대할까봐 인데, 우리는 오히려 좋은 이름으로 바꿔주면 고맙고 우리 대다수는 자신이 짠 클래스 이름등을 기억하지 못한다. 암기는 요즘 나오는 도구에게 맡기고 우리는 문장이나 문단처럼 읽히는 코드에 집중해야 한다. 어느 코드 개선 노력과 마찬가지고 이름 역시 나름대로 바꿨다가 질책받을 수도 있지만 이 때문에  코드를 개선하려는 노력을 중단해서는 안된다.

profile
안녕하세요 자르반입니다.

0개의 댓글