좋은 코드를 위한 JAVA 변수 네이밍

Benjamin·2022년 10월 29일
0

JAVA

목록 보기
4/28

변수, 메소드, 클래스 등의 이름에 일관성이 없고, 쓰임을 분명히 나타내지 않는다면 어떻게 될까?

문제점

  • 코드의 유지보수가 어려워진다.
    이름만 보고도 그것의 쓰임을 알 수 있어야한다. 그렇지않다면 코드를 분석해야하는 불필요한 과정이 필요하다.
    좋은이름을 짓기위한 시간이 많이 들겠지만, 큰 그림을 봤을때 절약하는 시간이 더 많다.

🙌🏻 협업자에 대한 배려가 필요하다.

이름을 주의깊게 살펴, 더 좋은 이름이 떠오른다면 기꺼이 수정할 수 있어야한다.

프레임워크마다 코드 컨벤션이 달라지기도 한다!
하지만 자바는 보통 오라클의 자바 코드 컨벤션을 따른다.
이것에 대해 살펴보자!

기본적인 변수명 네이밍 컨벤션

컴파일러에서 제한하는 사항

  • 대소문자는 구분
  • 길이의 제한은 없다.
  • 예약어를 사용 불가
  • 숫자로 시작하면 안된다.
  • 특수문자는 '_' 와 '$' 만 허용

JE22에서 권장하는 변수명 규칙

  • 첫글자는 영어소문자로 시작
  • 명사형
  • 여러 단어가 합쳐질 경우, 카멜 표기법 : 첫 단어 제외한 각 단어의 첫 글자를 대문자로 작성

좋은 변수명 짓기

의도를 분명히 밝히기

  • 의도를 잘 밝혔다면, 주석이 필요하지않다.
  • 변수가 표현하고 있는 것을 완벽하고 정확하게 설명해야 한다.
  • 구체적으로 짓자 : 모호하거나 하나 이상의 목적으로 사용될 수 있는 일반적인 이름은 보통 좋지 않은 이름이다.

예를 살펴보자.

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을 어떻게 사용하는가?

지뢰 찾기 게임을 만든다고 가정하자, theList는 게임판이다. 게임판에서 각 칸은 단순 배열로 표현한다.
배열에서 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<Cell> flaggedCells = new ArrayList<Cell>();
    for (Cell cell : gameBoard)
        if (cell.isFlagged())
            flaggedCells.add(cell);
    return flaggedCells;
}

처음보다 코드를 이해하기가 굉장히 쉬워졌다.

최적의 이름길이

  • 변수 이름의 길이가 평균적으로 10~16일 때 프로그램을 디버깅하기 위해서 들이는 노력을 최소화 할 수 있고, 변수의 평균 길이가 8~20인 프로그램은 디버깅하기가 쉽다.

모든 변수의 이름을 10~16의 길이로 작성하기 위해 애쓸 필요는 없겠지만, 길다면 그 이름이 적당한지 확인할 필요가 있다.

[너무 긴 이름]
numberOfPeopleOnTheUsOlympicTeam
numberOfSeatsInTheStadium
maximunNumberOfPointsInMordernOlympics

[너무 짧은 이름]
n, np, ntm
n, ns, nsisd
m, mp, max, points

[적당한 이름]
numTeamMembers, teamMemberCount
numSeatsInStadium, seatCount
teamPointsMax, pointsRecord

협업을 염두해서 짓기

private void validateNumericPosition(String[] expressionAsArray) {
    for (int i = 0; i < expressionAsArray.length; i += 2) {
    	...
    }
}

위 코드에서 2가 가르키는 내용은 뭘까?
협업자가 이를 알기위해서는 코드를 분석하는 불필요한 시간을 가져야한다.
하지만 아래와 같이 작성한다면?

private void validateNumericPosition(String[] expressionAsArray) {
    int numberIndex = 2;
    for (int i = 0; i < expressionAsArray.length; i += numberIndex) {
    	...
    }
}

2를 numberIndex라고 변수처리하며 의도를 정확히 밝힐 수 있다.

맥락을 고려해서 짓기

이름이 지나치게 짧을 경우 변수의 용도를 알기 어렵다.

  • 의도가 불분명한 짧은 이름보다는 의미있는 맥락이 있는 긴 이름이 좋다.
  • 하지만 쓸데없이 구구절절한 이름은 오히려 독이다.
public class User{
String userName;
int userAge;
...
}

위 코드를 보면, 클래스의 이름에서 user정보를 담고 있다는 것을 알 수 있다.
변수명에 정확한 의미를 담으려는 의도는 좋지만, 클래스명에서 user정보임을 알 수 있기때문에 굳이 멤버변수의 이름을 userName, userAge로 지을 필요가 없다.

public class User{
    String name;
    int age;
    ...
}

멤버변수명은 이처럼 수정해도 좋다.

불린 변수의 네이밍

  • 전형적인 불린 변수의 이름을 사용
    done, error, found, success, ok ...
    성공했다는 것을 정확히 설명하는 구체적인 이름이 있다면 다른 이름으로 대체하는 것이 좋다. (found, processingComplete 등)
  • 참이나 거짓의 의미를 함축하는 불린 변수의 이름을 사용
    status, sourceFile 같은 변수들은 참이나 거짓이 명백하지 않으므로 좋지 못한 이름이다. statusOK, sourceFileAvailable 또는 sourceFileFound 와 같은 이름으로 대체한다.
  • 긍정적인 불린 변수 이름을 사용

접두어 is

자바에서는 접두어 is 를 붙여 불린 메소드나 변수명을 짓는다.
그러나, 접두어가 없는 이름이 읽고 이해하는데 더 쉬운 경우가 있다.

메소드가 여러번 호출된 한눈에 보기 힘든 코드

private static final String FORMAT_SYMBOL = "//(.*)\\\\n(.*)";

public static List<String> customSplit(String input) {
    Matcher matcher = Pattern.compile(FORMAT_SYMBOL).matcher(input);
    if (matcher.find()) {
        return Arrays.asList(matcher.group(2).split(Pattern.quote(matcher.group(1))));
    }
    ...
}

위 코드의 리턴 부분을 보았을 때, 한 줄에 많은 기능이 있어서 코드 읽기가 어렵다.

private static final String FORMAT_SYMBOL = "//(.*)\\\\n(.*)";

public static List<String> customSplit(String input) {
    Matcher matcher = Pattern.compile(FORMAT_SYMBOL).matcher(input);
    if (matcher.find()) {
        String splitSymbol = matcher.group(1);
        String content = matcher.group(2);
        return Arrays.asList(content.split(Pattern.quote(splitSymbol)));
    }
    ...
}

splitSymbol, content 변수로 선언해 줌으로써 이전보다 훨씬 읽기가 수월해진다!

변수 이름에 자료형이 들어간다면?

private List<Double> numberList = new ArrayList<>();
private List<String> operatorList = new ArrayList<>();

List 대신 다른 자료형(Set…)을 써야 하는 경우가 오면 어떻게 해야 할까?
기존 변수명이 적절한 의미를 나타내지 못하게 되므로 결국 변수명을 변경해야 하는 번거로움이 생긴다.
변수 이름에 자료형을 쓰지 않아도 타입을 통해 충분히 어떤 변수인지 파악이 가능하다.

private List<Double> numbers = new ArrayList<>();
private List<String> operators = new ArrayList<>();
  • List, Collection 등의 자료형은 복수형으로 표현하는 것이 좋다.

변수 이름에서의 계산값 한정자

  • 계산된 값(총계, 평균, 최대값 등)을 보관하는 변수에 Total, Sub, Average, Max, Min, Record, String, Pointer 등의 한정자를 사용해야 한다면, 이름의 끝에 입력하자.

[좋은 예]
revenueTotal
expenseTotal
revenueAverage
expenseAverage

[나쁜 예]
totalRevenue
expenseTotal
revenueAverage
averageExpense

  • Num 한정자의 관습적인 위치는 예외!
    변수의 이름 앞에 있는 Num : 총계 (numCustomer: 전체 고객의 수)
    변수의 이름 끝에 있는 Num : 인덱스 (customerNum: 특정 고객의 번호)
    이런 혼란을 피하기 위해 Num이라는 단어를 피하는게 좋다.
    customerCount(전체 고객의 수), customerIndex(특정 고객의 번호)로 대체가능하다.

좋은 루틴의 이름

  1. 루틴이 하는 모든것을 표현하라

  2. 의미가 없거나 모호하거나 뚜렷한 특징이 없는 동사들을 피하라.
    HandleCalculation(), PerformServices()과 같은 이름들은 그 루틴이 무엇을 하는지 알 수 없다.
    실제로 루틴은 잘 설계되었지만, 루틴의 이름에 뚜렷한 특징이 없기 때문에 문제가 되기도 한다. 만약 HandleOutput()이 FormatAndPrintOutput()으로 대체된다면, 루틴이 무엇을 하는지 상당히 정확하게 이해할 수 있다.
    ! 루틴이 처리하는 연산 자체가 모호하기 때문에 이름이 모호해지는 경우도 있다.
    루틴의 목적이 취약하다는 것이 문제이고, 서투른 이름은 그로 인한 증상이다.
    ⚡️이런 경우 해당 루틴을 적절하게 리팩토링하여 명확한 처리를 하도록 해야 한다.

  3. 루틴 이름을 숫자만으로 구분하지 말라.
    OutputUser1(), OutputUser2()은 잘못된 이름이다.

  4. 함수의 이름을 지을 때, 리턴 값에 대한 설명을 사용하라.
    pen.CurrentColor()는 함수가 리턴하는 것을 정확하게 보여주고 있기 때문에 좋은 이름이다.

  5. 프로시저의 이름을 지을 때, 확실한 의미를 갖는 동사 다음에 객체를 사용하라.
    프로시저의 이름은 프로시저가 무엇을 하는지를 반영해야 하기 때문에, 객체에 대한 연산은 동사+객체 형태의 이름을 갖는다
    CheckOrderInfo()는 좋은 이름이다.

  6. 공통적인 연산을 위한 규약을 만들어라.

employee.id.Get()
dependent.GetId()
supervisor()
candidate.id()

위 코드는 모두 특정 객체의 식별자를 얻기 위한 코드이다. Employee 클래스는 자신의 id 객체를 노출하고, id 객체는 Get() 루틴을 노출했으며, Dependent 클래스는 GetId() 루틴을 노출했다.
Supervisor 클래스는 id를 기본 리턴캆으로 만들었고, Candidate 클래스는 id 객체의 기본 리턴 값이 id라는 사실을 이용하여 id 객체를 노출시켰다.
id를 가져오는 이름 규칙이 있었다면 이러한 난잡한 코드가 생성되는 현상은 막을 수 있을 것이다.

데이터의 특정 타입에 대한 네이밍

  • 루프 인덱스
    루프에 있는 변수의 이름을 지을 때, i,j,k와 같은 이름들은 관습적으로 사용된다.
    하지만, 이 변수가 루프외부에서 사용된다면, 의미있는 이름으로 지어야한다.

루프가 길어질때에도 의미있는 이름이 좋다.
코드는 자주 변경되고 확장되고 다른 프로그램에 복사되기 때문에, 많은 숙련된 프로그래머들은 i와 같은 이름들은 피한다.
여러 개의 중첩된 루프가 있다면, 가독성을 향상시키기 위해서 좀 더 긴 이름으로 루프 변수들을 작성한다.

// 중첩된 루프에서 좋은 루프 이름을 갖는 예제
for ( teamIndex = 0; teamIndex < teamCount; teamIndex++ ) {
  for ( eventIndex = 0; eventIndex < eventCount[ teamIndex ]; eventIndex++ ) {
      score[ teamIndex ][ eventIndex ] = 0;
  }
}

임시 변수 또한 구체적으로

임시 변수는 계산의 중간 결과를 보관하기 위한 임시 저장소로 사용되고 보조 수단으로 사용되는 값을 보관하기 위해 사용된다.
일반적으로 temp, x 또는 그 밖의 모호하고 설명적이지 않은 이름으로 만들어진다.
일반적으로, 임시 변수는 프로그래머가 프로그램을 완벽하게 이해하지 못하고 있다는 신호이다.
게다가 변수가 공식적으로 ‘임시’상태이기 때문에, 프로그래머는 임시 변수를 다른 변수들보다 별 생각 없이 다루게 되어 오류가 발생할 가능성이 높아진다.

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

프로그래밍은 사회적 활동이다.
발음하기 어려운 이름은 본인의 사고에도 방해가 되고 사람들과 협업할 때는 더욱 그렇다.

말장난을 하지마라

add라는 의미가 기존 두 값을 더하거나 이러서 새로운 값을 만드는데 쓰였다고 하자.
새로 작성하는 메서드는 집합에 값 하나를 추가하는데, 일관성을 고려한답시고 이 메서드를 add라고 부르면 안된다.

// Bad
class DtaRcrd102 {
    private Date genymdhms;
    private Date modymdhms;
    private final String pszqint = "102";
    /* ... */
};

정리

네이밍에 대한 일반적인 고려사항

  • 변수가 표현하고자 하는 것을 이름이 완벽하고 정확하게 설명하는가?
  • 변수가 프로그래밍 언어의 해결책보다는 실세계의 문제를 참조하고 있는가?
  • 고민할 필요가 없을 만큼 긴가?
  • 계산 값 한정자가 이름의 마지막에 있는가?
  • Num 대신 Count나 Index를 사용하는가?

특정 종류의 데이터에 대한 네이밍

  • 루프 인덱스의 이름이 의미가 있는가(루프가 한 줄 이상이거나 중첩되어 있다면 i,j,k가 아닌 다른 것)?
  • 모든 ‘임시’ 변수가 보다 의미 있는 이름으로 다시 명명되었는가?
  • 불린 변수가 참일 때 그 의미가 분명하도록 명명되었는가?

코드는 작성의 편의성보다, 가독성에 더 중점을 두고 작성해야한다!

🔥정체성이 분명한 객체들끼리 모여서 자기 역할에 집중하며 서로 상호작용할때 제대로 된 OOP가 구현된다.

참고사이트
https://tecoble.techcourse.co.kr/post/2020-04-24-variable_naming/
https://remotty.github.io/blog/2014/03/01/hyogwajeogin-ireumjisgi/
https://his2070.tistory.com/6

0개의 댓글