티스토리 뷰

서술형 명칭 사용

가독성은 본질적으로 주관적인 것이며, 그것이 정확히 무엇을 의미하는지 확실하게 정의하기는 어렵다. 가독성의 핵심은 개발자가 코드의 기능을 빠르고 정확하게 이해할 수 있도록 하는 것이다.


서술적이지 않은 이름은 코드를 읽기 어렵게 만든다

아래 예는 서술적인 이름을 사용하지 않은 예이다. 만약 아래 코드가 무엇을 하는지 설명하라고 요청을 받는다면 어떻게 답해야 할까?

class T {
    Set<String> pns = new Set();
    Int s = 0;

    Boolean f(Strinf n) {
        return pns.contains(n);
    }

    Int getS() {
        return s;
    }
}

Int? s(List<T> ts, String n) {
    for (T t in ts){
        if(t.f(n)) {
            return t.getS();
        }
    }
    return null;
}

주석문으로 서술적인 이름을 대체할 수 없다

이를 개선할 수 있는 한 가지 방법은 주석문과 문서를 추가하는 것이다. 코드 작성자가 이 작업을 수행했다면 코드는 아래와 같을 것이다. 조금은 이해하기 쉬워 보이지만, 아래와 같은 많은 문제가 있다.

  • 코드가 훨씬 더 복잡해보인다. 작성자와 다른 개발자는 코드뿐만 아니라 주석문과 문서도 유지보수해야 한다.
  • 개발자는 코드를 이해하기 위해 파일을 계속해서 위아래로 스크롤해야 한다. 코드를 파악할 때 getS() 함수를 보고 변수 s의 용도를 잊어버린 경우, s가 무엇인지 설명하는 주석을 찾기 위해 파일 맨 위로 스크롤 해야 한다. T 클래스가 수백 줄의 길이라면, 이것은 꽤 번거로운 일이다.
  • 함수 s()의 내용을 확인할 때, 클래스 T를 살펴보지 않는 한 t.f(n)과 같은 호출이 무엇을 하는지 또는 무엇이 변환되는지 알기 어렵다.
/** 팀을 나타낸다. */
class T {
    Set<String> pns = new Set(); // 팀에 속한 선수의 이름
    Int s = 0; // 팀의 함수

    /**
     * @param n 플레이어의 이름
     * @return true 플레이어가 팀에 속해 있는 경우
     */
    Boolean f(Strinf n) {
        return pns.contains(n);
    }

    /**
     * @return 팀의 점수
     */
    Boolean f(Strinf 
    Int getS() {
        return s;
    }
}

/**
 * @param ts 모든 팀의 리스트
 * @param n 플레이어의 이름
 * @return 플레이어가 속해 있는 팀의 점수
 */
Int? s(List<T> ts, String n) {
    for (T t in ts){
        if(t.f(n)) {
            return t.getS();
        }
    }
    return null;
}

매개변수 및 반환 유형을 주석문으로 설명하는 것은 다른 개발자가 코드 사용 방법을 이해하는 데 도움이 될 수 있다. 하지만 서술적인 이름을 붙이는 대신 주석문을 사용하면 안 된다.


해결책: 서술적인 이름 짓기

서술적인 이름을 사용하면 위에서 살펴본 이해하기 어려운 코드가 갑자기 이해하기 쉬운 코드로 바뀐다. 아래 예제는 이전 코드가 서술적인 이름을 사용했을 때 어떻게 되는지 보여준다.

class Team {
    Set<String> playerNames = new Set();
    Int score = 0;

    Boolean containsPlayer(Strinf playerName) {
        return playerNames.contains(playerName);
    }

    Int getScore() {
        return score;
    }
}

Int? getTeamScoreForPlayer(List<Team> teams, String playerName) {
    for (Tema team in teams){
        if(team.containsPlayer(playerName)) {
            return team.getScore();
        }
    }
    return null;
}

코드가 한결 더 이해하기 쉬워졌고, 주석문을 사용한 경우보다 덜 지저분하고 개발자가 주석문까지 관리할 필요 없이 코드에만 집중할 수 있다.

  • 이제 변수, 함수 및 클래스가 별도로 설명할 필요가 없이 자명하다.
  • 코드를 따로 떼어내 보면 더 의미가 있다. team.contaisPlayer(playerName)과 같은 호출이 무엇을 하는지 무슨 값을 반환하는지 Team 클래스를 확인하지 않더라도 분명하게 알 수 있다. 이전에는 이 함수 호출이 t.f(n)이었기 때문에 변경된 코드에서는 가독성이 크게 향상되었다.

주석문의 적절한 사용

코드 내에서 주석문이나 문서화는 아래와 같은 다양한 목적을 수행할 수 있다.

  • 코드가 무엇을 하는지 설명
  • 코드가 왜 그 일을 하는지 설명
  • 사용 지침 등 기타 정보 제공

클래스와 같이 큰 단위의 코드가 무엇을 하는지 요약하는 높은 수준에서의 주석문은 유용하다. 그러나 하위 수준에서 한 줄 한 줄 코드가 무엇을 하는지 설명하는 주석문은 가독성을 높이기 위한 효과적인 방법이 아니다.


코드의 기능을 설명하기 위해 낮은 층위의 주석문을 많이 추가해야 한다면, 이것은 코드 자체의 가독성이 떨어진다는 신호다.


중복된 주석문은 유해할 수 있다

코드 자체로도 하는 일이 설명된다면, 굳이 코드를 설명하기 위한 추가적인 주석문을 작성할 필요가 없다.

String generateId(String firstName, String lastName) {
    // "이름.성"의 형태로 ID를 생성한다.
    return firstName + "." + lastName;
}

이런 불필요한 주석문은 추후 코드가 변경된다면, 주석문도 유지보수해야 함을 의미한다.


주석문으로 가독성 높은 코드를 대체할 수 없다

아래 예제는 위의 예제와 동일하게 마침표를 사용하여 성과 이름을 결합함으로써 ID를 생성한다. 하지만 이번에는 이름과 성이 배열의 첫 번째와 두 번째 요소로 주어지기 때문에 코드 자체만 보면 이에 대한 것을 알 수 없다. 따라서 이 코드에는 이에 대해 설명하는 주석문이 존재하게 되는데, 코드 자체로 명확하지 않기 때문에 주석문이 유용한 것처럼 보이지만, 진짜 문제는 코드가 읽기 쉽도록 작성되지 않았다는 점이다.

String generateId(String[] data) {
    // data[0]는 유저의 이름이고 data[1]은 성이다.
    // "이름.성"의 형태로 ID를 생성한다.
    return data[0] + "." + data[1];
}

코드 자체가 가독성이 높지 않기 때문에 주석문이 필요하지만, 더 나은 접근법은 가독성이 좋은 코드를 작성하는 것이다. 아래 코드는 코드 자체만으로 설명이 되도록 작성한 예이다.

String generateId(String[] data) {
    return firstName(data) + "." + lastName(data);
}

String firstName(String[] data) {
    return data[0];
}

String lastName(String[] data) {
    return data[1];
}

코드 자체만으로 설명이 되도록 코드를 작성하면 유지 및 관리의 과도한 비용을 줄이고, 주석문의 내용이 업데이트되지 않거나 잘못된 가능성을 없애주기 때문에 주석문을 사용하는 것보다 더 선호될 때가 많다.


주석문은 코드의 이유를 설명하는 데 유용하다

코드가 그 일을 왜 수행하는지는 코드 자체만으로 설명하기 어렵다. 특정 코드가 존재하는 이유나 어떤 일을 수행하는 목적은 코드를 파악하고자 하는 다른 개발자가 알 수 없는 배경 지식과 관련 있을 수 있다. 이러한 배경 지식이 코드를 이해하거나 안전하게 수정하기 위해 중요한 경우 주석문은 매우 유용하다.


코드 줄 수를 고정하지 말라

일반적으로 코드베이스의 코드 줄 수는 적을수록 좋다. 코드는 일반적으로 어느 정도의 지속적인 유지보수를 필요로 하며 코드의 줄이 많다는 것은 코드가 지나치게 복잡하거나, 기존 코드를 재사용하지 않고 있다는 신호일 수 있다. 또한, 코드 줄이 많으면 읽어야 할 코드의 양이 늘어나기 때문에 개발자의 인지 부하가 증가할 수 있다.


그러나 코드 줄 수는 간접적으로 측정해줄 뿐이다. 대부분의 간접 측정이 그렇듯이 유용한 지침 원칙이긴 하지만 반드시 지켜야 할 염격한 규칙은 아니라는 점을 기억하자. 우릭사 정말로 신경 쓰는 것은 코드에 대해 아래와 같은 사항들을 확실하게 하는 것이다.

  • 이해하기 쉽다.
  • 오해하기 어렵다.
  • 실수로 작동이 안 되게 만들기가 어렵다.

간결하지만 이해하기 어려운 코드는 피하라

아래 예제는 16비트 ID가 유효한지 확인하는 함수를 보여준다.

Boolean isInvalid(UInt16 id) {
    return countSetBits(id & 0x7FFF) % 2 == ((id & 0x8000) >> 15);
}

위 코드는 간결하지만 거의 이해할 수 없다. 여러 개발자들이 이 코드가 무엇을 하는지 이해하기 위해 시간을 낭비할 가능성이 크다.


해결책: 더 많은 줄이 필요하더라도 가독성 높은 코드를 작성하라

아래 예제에서는 잘 명명된 헬퍼 함수와 상수를 정의한다. 이로 인해 코드가 훨씬 더 이해하기 쉽고 하위 문제에 대한 해결책을 재사용할 수 있지만, 코드의 양은 더 많아진다.

Boolean isInvalid(UInt16 id) {
    return extractEncodeParity(id) == calculateParity(getIdValue(id));
}

private const UInt16 PARITY_BIT_INDEX = 15;
private const UInt16 PARITY_BIT_MASK = (1 << PARITY_BIT_INDEX);
private const UInt16 VALUE_BIT_MASK = ~PARITY_BIT_MASK;

private UInt16 getIdValue(UInt16 id) {
    return id & VALUE_BIT_MASK;
}

// 패리티 비트는 1인 비트의 수가 짝수이면 0이고
// 홀수이면 1이다.
private UInt16 calculateParity(UInt16 value) {
    return constSetBits(value) % 2;
}

깊이 중첩된 코드를 피하라

일반적으로 코드는 아래와 같이 서로 중첩되는 블록으로 구성된다.

  • 함수가 호출되면 그 함수가 실행되는 코드가 하나의 블록이 된다.
  • if문의 조건이 참일 때 실행되는 코드는 하나의 블록이 된다.
  • for루프의 각 반복시 실행되는 코드는 하나의 블록이 된다.

인간의 눈은 각 코드 라인의 중첩 수준이 정확히 어느 정도인지 추적하는 데 능숙하지 않다. 이로 인해 코드를 읽을 때, 다른 논리가 실행되는 때를 정확히 이해하기 어렵다. 중첩이 깊어지면 가독성이 떨어지기 때문에 최소화하도록 코드를 구성하는 것이 바람직하다.


요약

  • 코드의 가독성이 떨어져서 이해하기 어려울 때 아래와 같은 문제가 발생할 수 있다.
    • 다른 개발자가 코드를 이해하느라 시간을 허비함
    • 버그를 유발하는 오해
    • 잘 작동하던 코드가 다른 개발자가 수정한 후에 작동하지 않음
  • 코드의 가독성을 높이다 보면 때로는 코드가 더 장황하게 되고, 더 많은 줄을 작성해야 할 수도 있다. 이것은 종종 가치 있는 절충이다.
  • 코드의 가독성을 높이려면 다른 개발자의 입장을 공감하고, 그들이 코드를 읽을 때 어떻게 혼란스러워할지 상상해보는 것이 필요하다.
  • 실제 시나리오는 다양하며 보통 그 상황에 해당하는 어려움이 있다. 가독성이 좋은 코드를 작성하려면 언제나 상식을 적용하고 판단력을 사용해야 한다.


Reference
톰 롱. 『좋은 코드, 나쁜 코드』. 제이펍, 2022.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함