Clean code 2장 - 의미 있는 이름
포스트
취소

Clean code 2장 - 의미 있는 이름

클린 코드 책을 읽고 정리한 내용입니다.

1. 의도를 분명히 밝혀라

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

  • 존재 이유, 수행 기능, 사용 방법이 잘 드러나도록 지어야 합니다.

  • 예시1

    1
    2
    3
    4
    5
    6
    7
    8
    
    // Bad
    int d; // elapsed time in days
    // Good
    int elapsedTimeInDays;
    int daysSinceCreation;
    int daysSinceModification;
    int fileAgeInDays;
        
    
  • 예시2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    // Bad
    public List<int[]> getThem() {
    	List<int[]> list1 = new ArrayList<int[]>();
    	for(int[] x: theList)
    		if (x[0] == 4)
    			list1.add(x);
    	return list1;
    }
        
    

    코드는 매우 단순하지만, 코드의 함축성 측면에서 좋지 않은 코드

    위 코드는 독자가 다음과 같은 정보들을 알고 있다고 가정하고 있습니다.

    • theList에 무엇이 들었는가?
    • theList에서 0번째 값이 어째서 중요한가?
    • 값 4는 무슨 의미인가?
    • 함수가 반환하는 리스트 list1을 어떻게 사용하는가?

    다음과 같이 수정해본다면?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    // Good
    public List<int[]> getFlaggedCells(){
    	List<int[]> flaggedCells = new ArrayList<int[]>();
    	for(int[] cell: gameBoard)
    		if(cell[STATUS_VALUE] == FLAGGED)
    			flaggedCells.add(cell);
    	return flaggedCells;
    }
        
    
    • 리스트 gameBoard는 말그대로 게임보드구나
    • gameBoard의 0번째 값은 상태를 뜻하는구나
    • 값 4는 깃발이 꽂힌 상태를 뜻하는구나
    • 깃발이 꽂힌 상태의 게임보드를 찾아내려고 하는구나

    코드의 단순성은 변하지 않으면서 함축성이 좋아집니다.

    한층 더 수정?

    1
    2
    3
    4
    5
    6
    7
    8
    
    public List<Cell> getFlaggedCells(){
    	List<Cell> flaggedCells = new ArrayList<Cell>();
    	for (Cell cell: gameBoard)
    		if (cell.isFlagged())
    			flaggedCells.add(cell);
    	return flaggedCells;
    }
        
    

2. 그릇된 정보를 피하라

  • 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용하지 말아야 합니다.

    • ex) 직각삼각형의 빗변(hypotenuse)을 구현할 때는 hp가 훌륭한 약어로 보일지라도 hp는 유닉스 플랫폼을 가르키는 이름이라 혼란을 가중시킵니다.
  • 사용하는 타입에 맞게 이름 지정합니다.

    • ex) 사용자의 계정 정보를 그룹핑하는 변수가 리스트 타입이 아닌데 accountList라고 지정 시 그릇된 정보 제공합니다.

      1
      2
      3
      4
      5
      6
      
      fun findAllById(pageable: Pageable): Page<String>
      Page: pageSize 30 
      val customInfoList = findAllById(....) // 4
      customInfoList.size  = 4 , 30 
      customInfoPage.numberOfElement = 4 
              
      
  • 서로 흡사한 이름 사용하지 말아야 합니다.

  • 유사한 개념은 유사한 표기법을 사용 하여 일관성이 떨어지지 않도록 해야 합니다.

3. 의미 있게 구분하라

  • 연속적인 숫자나 의미가 불분명한 불용어(noise word)를 추가하는 방식으로 이름을 구분하지 말 것
    • 이름이 달라지면 의미도 달라져야 함
    • 불용어 예시) info, data, variable, table, string, …
      • 같은 이름이 이미 존재한다는 이유로 Product, ProductInfo, ProductData 이런식으로 이름 짓지 말 것

        1
        2
        3
        
        val accountIdForUserNo: List<Account> = findAllByUserNo(userNo..)
        val accountIdForAccountNumber: List<Account> = findAllByAccountNumber(..)
                    
        

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

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

  • 문자 하나를 사용하는 이름과 상수는 텍스트 코드 내에서 쉽게 눈에 띄지 않습니다.

    • ex) 한 학생 당 수강할 수 있는 클래스가 7개라면, 7이라는 숫자를 MAX_CLASSES_PER_STUDENT라는 상수로 선언해놓고 쓰는게 바람직합니다.

      왜? MAX_CLASSES_PER_STUDENT는 grep으로 찾기 쉽지만, 숫자 7은 7이 들어가는 파일 이름이나 수식이 모두 검색되어 까다롭습니다.

  • 이름 길이는 범위 크기에 비례해야 합니다.

    • 여러 곳에서 사용될 수록 검색하기 쉬운 이름이 바람직합니다.
    • 간단한 메서드에서 로컬 변수정도는 한 문자를 사용해도 괜찮습니다.

6. 인코딩을 피하라

  • 인코딩은 개발자에게 불필요한 정신적 부담입니다.

  • 이름에 불필요한 정보를 추가하기 보다 IDE를 활용해야 합니다.

  • 헝가리식 표기법

  • 멤버 변수 접두어

    • 멤버변수에 m_이라는 접두어 붙일 필요 하지 않습니다.
    • 멤버 변수를 따로 표시해주는 IDE를 사용합니다.
  • 인터페이스 클래스와 구현 클래스

    • 인터페이스 클래스 이름과 구현 클래스 중 하나를 인코딩해야 한다면 구현 클래스를 인코딩 하는 것을 추천합니다. 2022-12-01-4-22-04
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    interface CustomerInfoCheckService{
    	fun checkPhoneNumberValidation(phoneNumber: String): Boolean
    	fun checkNameValidation(name: String): Boolean
    }
        
    class CustomerInfoCheckServiceImpl(): CustomerInfoCheckService{
    	override fun checkPhoneNumberValidation(phoneNumber: String): Boolean {
    		...
    	}
    	override fun checkNameValidation(name: String): Boolean {
    		...
    	}
    }
        
    

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

  • 코드를 읽으면서 독자가 자신이 아는 이름으로 변환해야 한다면 바람직하지 못한 이름입니다.
  • 문자 하나만 사용하는 변수 이름은 문제가 있습니다.
    • 루프에서 반복 횟수 세는 변수 제외 (i,, j, k) → 소문자 l 는 안됩니다.
      • 단, 루프 범위가 아주 작고 다른 이름과 충돌하지 않을 때만 사용 가능합니다.

8. 클래스 이름

  • 클래스/객체 이름은 명사나 명사구를 사용 합니다.
  • 동사는 사용 하지 않습니다.
  • Manager, Processor, Data, Info 등의 이름은 피합니다.

9. 메서드 이름

  • 메서드 이름은 동사나 동사구가 적합합니다.

  • 접근자, 변경자, 조건자는 javabean 표준에 따라 get, set, is 붙입니다.

  • 생성자를 overload할 때는 정적 팩토리 메서드 사용 합니다.

    1
    2
    3
    4
    
    // 첫번째 보다 두 번째 방법이 더 좋다.
    Complex fulcrumPoint = new Complex(23.0);
    Complex fulcrumPoint = Complex.FromRealNumber(23.0);
        
    

10. 기발한 이름은 피하라

  • 재미난 이름보다 명료한 이름을 선택하라
  • 의도를 분명하고 솔직하게 표현하라

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

  • 추상적인 개념 하나에 단어 하나를 선택합니다.
  • ex) 똑같은 메서드를 클래스마다 fetch, retrive, get으로 제각각 부르면 혼란스럽습니다.
  • 똑같은 메서드를 클래스마다 제각각 부르면 어느 클래스에서 어느 이름을 썼는지 기억하기 어렵습니다.
  • 메서드 이름은 독자적이고 일관적이어야 합니다.

12. 말장난을 하지 마라

  • 한 단어를 두 가지 목적으로 사용하지 말아야 합니다.
  • ex)
  • add 메서드 - 기존 값 두 개를 더하거나 이어서 새로운 값을 만들어야 합니다.
  • 집합에 새로운 값 하나 추가하는 메서드에는? → 기존 add와 맥락이 다르니 insert나 append 사용합니다.

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

  • 코드를 읽는 사람도 프로그래머!
  • 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등 사용 OK

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

  • 적절한 프로그래머 용어가 없으면 문제 영역에서 이름 가져옵니다!

15. 의미 있는 맥락을 추가하라

  • 스스로 의미가 분명한 이름이 있으나 그렇지 못하다면 마지막 수단으로 접두어를 붙입니다!

    ex) state 라는 이름의 변수가 단독으로 있으면 주소의 일부라는 사실을 알아차리기 어려우니, addrState라고, 주소를 나타내는 접두어를 붙여서 사용.

  • 또는 상위 개념의 클래스를 생성하면 더 좋습니다.

16. 불필요한 맥락을 없애라

  • 일반적으로 짧은 이름보다 긴 이름이 좋습니다.
  • 단, 의미가 분명한 경우에서만!
  • ex) Gas Station Deluxe라는 애플리케이션을 짠다고 가정할 때, 모든 클래스 이름을 GSD로 시작하겠다는 생각은 전혀 바람직하지 못합니다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.