아름다운 이름 푸르게 푸르게

코드 속의 이름들

코드 속에는 많은 이름이 있으며 그 중에는 이름을 닦는 자도 있다.

들어가기 전에

빛이 있으라

첫째 날, 신이 말했다, “빛이 있으라!” 우리는 이렇게 말할 수 있을 것이다, “코드가 있으라!”

아마도, 추측컨데, 창조의 광경을 직접 보며(어디까지나, 비유적 의미이다.) 성경을 작성한 사람들은 많은 고민을 했을 것이다.

“야, 그 밝은… 그걸 뭐라고 표현하면 적절할까?” “E=mc^2 ?” “아니다 이 악마야!”

우리도 많은 고민을 한다.

“뇌야, 잘 생각을 해봐라, 내가 방금 운동신경을 놀려 짠 이 메서드가 저기 디비에서 데이터를 가져와서 이렇게 이렇게 볶고 저렇게 지져서 가공품을 내놓는데, 뭐라고 이름을 지으면 좋지?” “그건 일단 메서드를 나눠야 할 것 같군.”

일단 이름이 길면 이해하기 좋다

대부분, 짧은 이름보다는 긴 이름이 이해에 도움이 된다.

어떤 안 좋은 예시

적절한 이름에 적절한 이해가 따라온다, 당신이 100줄이 넘어가는 메서드에서 (100줄이 넘어가는 메서드에 놀라기 이전, 또는 이후에) 아래와 같은 한 줄을 발견했다고 해보자.

String c = "";

읽는 사람은 당황스럽다. 문자열 c에는 코드 작성자의 심오한 세계관, 미학에 대한 독특한 고찰, 아름다운 속 뜻이 모두 함축되어 있다. 코드 작성자는 자신의 고차원적 사고를 범인이 따라오지 못할 것에 대비하여 친절한 주석을 추가하였다.

String c = ""; // c에는 색 이름이 들어간다

친절하기도 하셔라, 이제 코드 리뷰어는 코드를 읽을 때 자신이 어느 주석으로 돌아가야 할지 알게 되었다. 해피앤딩이냐고? 그럴 리가 있나.

String color = "";

리뷰어가 여전히 막히자 작성자는 c를 color이라고 바꾸며 자신의 배려심에 큰 감동을 느꼈다. 그러나 불행하게도, 메서드는 내부에서 각각 고유한 색을 가지는 다섯 가지의 객체(그것이 버튼이든 판넬이든, 뭐든지 상관 없다)를 만지고 있다. 그래, 이제 확률은 1/5로 줄긴 했다.

조금 더 일반적인 예시

위의 예시처럼 아주 더럽고 치사하고, 특수한 사례를 들고 오지 않아도 잘못된 이름은 도처에서 찾을 수 있다. 아래를 보자.

List <String> list = new ArrayList<>();

나는 이 코드를 생각하고 작성하는데 몇 초 걸리지 않았다. 생각 없이 아무 이름 대잔치 를 했다는 의미이다. 읽는 사람은, 아마 이 아래의 다섯 줄은 더 읽어봐야 list 의 뜻을 알게 될 것이다. 아마 이렇게 짰으면, 최소한 list 보다는 조금 더 많은 의미를 전달할 수 있었을 것이다.

List <String> userNameList;
List <String> messageList;

혼돈! 파괴! 망각!

혼란이 오는 이름은 피해야 한다.

줄임말의 위험성

줄임말은 사람마다 다르고, 집단마다 다르고, 잘못하면 아예 다른 두 개념이 동일한 줄임말로 표현되는 경우가 있다. 나는 이 문제의 대표적인 예시를 찾기 위해 구글링을 잠시 하였는데, 깊이 찾을 것도 없었다

r, re, rep, regexp, regex…

이, 변수명으로 쓰기로는 참담한 수준의 전달력을 가진 줄임말들은, 모두 정규표현식(regular expression)을 뜻한다(나는 구글에 정규표현식 약어 라고 검색했고, 단 1페이지에 이 마법같은 결과가 모두 나와 있었다.). r, re는 말할 것도 없거니와, 그나마 길게 느껴지는 regexp나 regex에도 문제점이 있다, reg는 윈도우 레지스트리 확장자(더 나아가면, 레지스트리 그 자체의 줄임말) 로도 쓰인다!

일단 굴러만 가면…

아래 코드는 내가 얼마 전에 작성한 JUnit 테스트 코드에 등장했던 코드이다.

UserDto user1 = new UserDto();
UserDto user2 = new UserDto();
UserDto user3 = new UserDto();
UserDto user4 = new UserDto();

1호기는 이벤트의 주인(개최자), 2호기와 3호기는 참가자, 4호기는 이미 서비스에서 탈퇴한 사용자이고, 모두 다른 특성을 나타내지만, 또한 그들 모두를 사용자, 즉 user 로 표현할 수 있었다. 이 테스트 코드는 아직 작성 중이었고, 나는 이것으로 잠시 괜찮을 것이라 생각했다. 안타깝게도, 나는 다음 날 또 하나의 나와 혈투를 벌였다. 혈투 끝, 위대한 통찰과 지성의 신의 가호를 받은 내가 변경한 코드는 아래와 같다.

UserDto hostUser = new UserDto();
UserDto guestUser = new UserDto();
UserDto guestUser = new UserDto();
UserDto removedUser = new UserDto();

창의력에 도전하지 마라

책에서는 안티오크의 성스러운 수류탄 을 예시로 들고 있다. 몬티 파이썬을 즐겨 본 사람은 이 성스럽고 파괴적인 이름을 가진 메서드가 어떤 일을 하는지 쉽게 알 수 있다( 붕붕펑 ). 그러나, 도버 해협을 건너는 순간 이 이름의 지명도는 반토막이 나고, 시베리아를 건너면서 나머지 반토막이 얼어서 쪼개질 것이다. 아쉽지만, DeleteAllXXX 라고 하는 것이 낫겠다.

이름은 Map이다.

한 이름에는 하나의 개념만 있어야 한다. 나는 한 무더기의 Dao들을 만들고, 코드를 리뷰했다.

class AaDao{
    findItem(int id);
}
class BbDao{
    searchItem(int id);
}
class CcDao{
    getItem(int id);
}
// ...

아, 참, 모두 DB에서 데이터를 찾는 메서드이다. 내가 만든 Dao를 써야 하는 협업자는 큰 혼란에 빠질 것이다.

찾기 쉽게 하는 것도 곧 이름일지어다.

나는 첫 메서드 실행 후 7초를 쉬고 다음 매서드를 실행 후 3초 정지 다시 첫 메서드를 실행해야 하는 스레드를 만들게 되었다, 어떻게 할까?

doFirst();
sleep(7000);
doNext();
sleep(3000);

코드가 예쁘게 돌아간다, 이것으로 괜찮을까? 몇—–달 뒤에, 몇 초를 쉬는지는 잘 모르겠는데, 여하간 중간 인터벌을 5초로 늘여야 할 것 같은, 그런 느낌을 받게 되었다. 그런데 문제가 생겼다, 몇 초를 쉬더라? 코드가 짧으니 sleep으로 찾으면 약간의 고민 끝에, 성공적으로 3000을 5000으로 변경할 수 있을 것이다.

하지만 코드가 더 길고 복잡해지고, 좀 더 일반적인 문법의 조건이라면 어떨까? 나는 내 손으로 지옥을 만들어보았다.

if(a.size > 4){
    if(b.type == 3){
    }
    if(a.size > 8){
        // ...
        // 더 이상의 자세한 조건문은 생략한다
    }
}

다음 주에, 나는 신을 찾을 것이다, 오 신이시여. 깊고 어두운 DEEP♂DARK♂FANTASY if문 자체가 OOP의 적이라는 것을 차치하고서, 첫 4는 무엇이고 다음 3은 무슨 의미를 가지고… 8에 가면 다음과 같은 생각이 든다, “a가 뭔데 4 이하일 때, 4 이상 8 이하일 때, 8 초과일 때로 갈리냐!” 상수화를 하면 나 자신을 고통에서 구할 수 있다.

private static final int 반차_근무_시간 = 4;
private static final int 오아시스_근무 = 3;
private static final int 법정_근무_시간 = 4;

그리고 고백하건데, 아무 생각 없이 짠 if문에 의미를 넣는 것보다 힘든 일은 없다.

마지막, 읽는 사람도 개발자다!

인생에서 코드 한 번 볼 일 없었던 사람이 우연히 나의 코드를 주의깊게 읽을 일은 거의 없다. 거의 대부분, 나와 협업하는 개발자가 그 코드를 읽을 것이고, 그들도 엇비슷한 배경 지식이 있을 것이다. 적어도, Factory를 클래스 이름의 마지막에 붙이면서, ‘그가 이것을 이해할 수 있을까?’ 를 걱정하진 않아도 될 것이다. 팩토리 (메서드) 패턴을 모른다고? 우리에겐 구글이 있다. 그것도 아니라면 나에게 물어보겠지.

“이거, 하는 일은 팩토리가 아닌데 왜 이름이 팩토리냐?”