2018년 9월 2일 일요일

[The Art of Readable Code, 읽기 좋은 코드가 좋은 코드다] 2. 이름에 정보 담기

표면적인 수준에서의 개선



"표면적 수준이란 좋은 이름을 짓고, 좋은 설명을 달고, 코드를 보기 좋게 정렬하는 따위를 의미한다."

책의 첫 단락은 표면적인 수준에서의 개선부터 시작합니다. 이런 수정은 코드를 통째로 바꾸거나 동작하는 방식을 변화시키지 않고 '그 자리에서' 곧바로 만들 수 있기에 첫 시작으로 매우 적절하다 생각합니다.

물론 가독성에 관련된 논의는 이 수준보다 더 나아가 많은 내용을 담고 있겠으나 이는 차차 살펴갈 것이며 먼저 1부에서는 폭넓게 적용할 수 있고, 그다지 많은 노력을 요구하지 않는 내용을 우선적으로 다룹니다.

이름에 정보 담기


변수, 함수, 혹은 클래스 등의 이름을 결정할 때는 항상 같은 원리가 적용합니다. 
"이름을 일종의 설명문으로 간주해야 한다."
충분한 공간은 아니지만, 좋은 이름을 선택하면 생각보다 많은 정보를 전달할 수 있다는 것이죠. 구체적으로는 아래의 여섯 가지 방법을 제안합니다.
  • 특정한 단어 고르기
  • 보편적인 이름 피하기 (혹은 언제 그런 이름을 사용해야 하는지 깨닫기)
  • 추상적인 이름 대식 구체적인 이름 사용하기
  • 접두사 혹은 접미사로 이름에 추가적인 정보 덧붙이기
  • 이름이 얼마나 길어져도 좋은지 결정하기
  • 추가적인 정보를 담을 수 있게 이름 구성하기
앞으로는 책에 나온 내용을 모두 다 소개하기 보다는 개중 제가 재미있었던 내용들을 좀 골라서 예시와 함께 알아보겠습니다. 

특정한 단어 고르기


매우 구체적인 단어를 선택하여 "무의미한" 단어를 피하자. 

예를 들어 "get"은 지나치게 보편적입니다.

def GetPage(url):
    ...

여기서 "get"보다는 메소드가 어디에서 페이지를 가져오는 지 알려줄 수 있게 FetchPage() 혹은 DownloadPage()와 같이 구체적으로 명명하는 것이 더 좋습니다.

사실 위 예시보다 다음 예시가 더 좋았는데요. 다음과 같이 BinaryTree 클래스에서

class BinaryTree {
    int Size();
    ...
}

우리는 Size() 메소드가 반환하는 것이 무엇일 지 이름만 봐서는 알 수 없습니다. 트리의 높이, 노드의 개수, 혹은 트리의 메모리 사용량이 될 수도 있겠죠.  따라서 Height(), NumNodes(), 혹은 MemoryBytes() 등이 더 의미 있는 이름이라는 것에는 모두 동의하리라 생각합니다. 

같은 맥락에서 저자들은 thesaurus를 뒤져보고 더 나은 이름을 생각하기를 권합니다. 다만 너무 "재치" 있는 이름보다는 명확하고 간결한 이름이 더 좋습니다. 다음에 이어지는 내용들도 사실 같은 내용인데 예제들과 소소한 팁 위주로 살펴보곘습니다.

tmp나 retval 같은 표편적인 이름 피하기


"변수값을 설명하는 이름을 사용하라"

예를 들어, 다음과 같이 Euclidean norm을 계산하는 자바스크립트 코드에서

var euclidean_norm = function (v) {
    var retval = 0.0;
    for (var = i = 0; i<v.length; i+=1)
        retval += v[i];
    return Math.sqrt(retval);
};

retval보다는 sum_squares라고 이름을 붙여준다면 변수의 목적을 바로 이해할 수 있으며 나중에 버그를 잡을 때도 용의합니다.

retval += v[i]; 부분이 sum_squares += v[i]; 였다면 훨씬 눈에 잘 띄었겠죠.

물론 아래와 같이 정말로 대상이 짧게 임시적으로만 존재하고, 임시적 존재 자체가 변수의 가장 중요한 용도일 때는 tmp와 같은 변수를 사용할 수 있겠습니다.

두 변수를 서로 교환하는 알고리즘 예:

if (right<left) {
    tmp = right;
    right = left;
    left = tmp;
}

같은 맥락으로 i, j, iter, it 같은 이름이 인덱스나 루프 반복자로 사용되는 것은 충분히 괜찮습니다. 다만 이 역시도 디버깅의 용이성을 위해서 아래와 같이 소속을 표현해준다면 더 좋겠죠.

(i, j, k) -> (club_i, members_j, users_i) or (ci, mj, ui)

활용 예:
if (clubs[ci].members[ui] == users[mi]) # 버그! 처음 문자가 일치 하지 않는다.

따라서 표편적인 이름이 항상 나쁜 것은 아니지만, 이를 사용하려면 꼭 그렇게 해야하는 이유가 있어야 합니다. 


추가적인 정보를 이름에 추가하기 


단위(sec, millisecond, kg 등)를 포함하거나 다른 중요한 속성(unsafe, utf_8 등)이 있을 때는 변수에 그런 내용을 추가해주면 좋습니다. 

start -> start_ms, elapsed -> elapsed_ms
html -> html_utf-8 # html의 바이트가 UTF-8으로 변환되었다. 

이름은 얼마나 길어야 하는가?


만일 변수가 좁은 scope (예: 끽해야 몇 줄 안의 함수 scope)에서 사용된다면 멤버 변수가 "m"과 같이 매우 짧은 이름을 사용해도 별 문제가 없으나 이 변수의 scope이 클래스나 전역으로 넓어지면 가독성이 매우 떨어지게 되므로 상황에 따라 잘 사용하라고 하는군요. 

게다가 요즘은 긴 이름을 입력하는 것이 자동완성 기능으로 매우 편해져서 그리 주저할 일이 아닙니다. 그렇기 때문에 약어와 축약형은 매우 보편적인 경우(string-> str과 같이)를 제외하고는 지양하는 편이 좋겠습니다. 

이에 좀 더 더한다면 ConvertToString()에서 ToString()과 같이 불필요한 단어를 제거해서 간결하게 만드는 등의 팁이 있으나 앞의 내용들이 더 핵심에 가까운 것으로 보입니다. 

이로써 이름에 정보를 넣는 방법에 대해 요약해보았습니다. 

책에서 다음 장은 의미를 오해하기 쉬운 이름들에 대한 팁입니다만 사실 오늘 소개한 내용에 어느 정도 포함되는 것 같습니다. 다음 글에서는 미학(Aesthetics) 즉 "눈을 편하게" 하는 코드에 대해 정리하겠습니다. 





댓글 2개:

  1. 안녕하세요. Jaejun님이 올려주신 여러가지 게시글에서 큰 도움을 얻고있는 학부 1학년 학생입니다. 특히 GAN 시리즈에 관심이 많은데 이론적인 측면에서 통계과 convex function에 대한 수학적인 지식이 필요한 것 같습니다. 제가 수학을 잘하는 편이 아니지만, 지금부터라도 공부해보고 싶은데 이것에 대해 공부하기 적합한 방법이 있을까요? 책도 좋고 웹도 좋습니다.

    그냥 읽었을 때 이해가 안가는 논문들에 대해서 본 블로그에서 정말 큰 도움을 많이 얻었습니다. 감사합니다.

    답글삭제
    답글
    1. 제 블로그 글들이 도움이 되고 있다니 보람있네요. 학부 1학년이시라면 아직 학교에 계실 날이 한참 많으니, 수학과 수업을 찾아들으셔요. 기초 과목들이 나중에 큰 도움이 됩니다. 선형대수학 기초를 잘 다지고 확률과 통계 해석학 쪽 공부를 한다면 나중에 혼자 공부할 힘이 생길겁니다. 홀로 공부하는 것이 좋지만 더 좋은 것은 물어볼 전문가가 있는 환경입니다. 수업과 교수님을 최대한 이용하셔요.

      삭제