예전에 쓴 들여쓰기 최소화라는 글에 많은 분들이 다양한 의견을 주셨습니다. 주신 의견들을 유심히 읽다 보니 해당 글에 대해 깊히 생각하는 기회를 가질 수 있었고, 자연스레 아래와 같은 세 가지 논제를 머리속에 떠올렸습니다.

첫 번째, 들여쓰기 최소화가 정말 가치있는 것인가? 만약 가치 있다면 논리적인 근거를 제시할 수 있는가?
두 번째, 진입점과 종료점을 각 하나씩만 유지해야 하는 것은 의미 있는 일인가?
세 번째, 긍정 조건과 부정 조건 중 어떤 것이 더 이해하기 좋은가? 만약 상황에 따라 적절히 사용해야 한다면 긍정 조건과 부정 조건을 선택하기 위해 사용하는 기준을 도출할 수 있을까?

최근 읽고 있는 켄트벡의 Implementation Patterns에는 위 논제에 대한 답변을 얻는 데 도움이 될만한 내용이 많이 담겨있었습니다. 저는 그동안의 저의 생각과, 최근 해당 책에서 얻은 지식들을 이용하여 위 세 가지 논제 중 첫번째 논제에 대한 저의 생각을 얘기해보고자 합니다.(참고로 Implementation Patterns의 Guard Clause와는 조금 다른 시점의 글입니다. 이 글은 이전 글에서 얘기했던 들여쓰기 최소화 자체에 집중한 글임을 밝힙니다)

들여쓰기를 한다는 것은 현재 들여쓰기 되고 있는 단락 위에 진입 조건이 존재한다는 것을 의미합니다. 조건문, 반복문, 메서드 등이 이러한 진입 조건이 될 수 있습니다.

만약 어떤 사람이 조건문에 속해 있는 하위 단락을 읽는다면, 하위 단락이 어떤 경우에 실행되는지를 '전제'로 인식하고 있어야 합니다. 그런데 이러한 전제가 많아지고 단락이 깊어지면 문제가 생깁니다. 아래 예제 코드는 이러한 문제를 잘 드러내고 있습니다.
void compute() {
    Server server = getServer();
    if (server != null) {
        Client client = server.getClient();
        if (client != null) {
            Request current = client.getRequest();
            if (current != null) {
                 processRequest(current);
            }
        }
    }
}

void compute() {
    Server server = getServer();
   if (server == null)
        return;
    Client client = server.getClient();
   if (client == null)
       return;
    Request current = client.getRequest();
   if (current == null)
       return;
   processRequest(current);
}

Implementation Patterns P71에서 인용

첫 번째 코드를 보면 읽는 사람이 전제로 유지해야 할 조건이 많다는 것을 볼 수 있습니다. 'server가 null이 아니고, client도 null이 아니며, current도 null이 아니면' 이라는 전제 조건이 processRequest에 이르기 전까지 단계적으로 머리속에 유지 되어야 합니다. 켄트백은 이러한 형태의 전제 조건들에 대해 '집중해야 할 것에 집중하지 못하게 방해하는 것들'이라고 얘기합니다. 실제로 위와 같은 코드는 집중해야 할 것에 집중하기 어렵게 하며 만약 else까지 존재한다면 더욱더 어려운 상황이 만들어집니다. 결국 코드를 이해하기가 어려워지는 것입니다.

반면 두 번째 코드는 동일한 일을 하면서도 보다 이해하기 쉬운 코드입니다. 코드에 윗 부분에 조건에 안 맞으면 바로 종료하는 코드가 있음을 볼 수 있습니다. processRequest에 이르렀을 때 첫번째 코드와는 달리 머리속에 전제 조건으로 유지해야 할 것이 없어집니다. 왜냐하면 'server가 null이면 종료','client가 null이면 종료','current가 null이면 종료' 이렇게 세 가지 조건 단락이 독립적으로 분리되어 이미 실행되었기에 더 이상 앞에 코드에 대해서 신경쓰지 않아도 되기 때문입니다.

만약 사람의 두뇌가 스택(Stack) 형태의 기억을 잘 할 수 있다면 첫번째 코드도 큰 문제는 아닐 것입니다. 하지만 저의 경우 스택 형태로 무엇인가를 기억하는 것은 쉽지 않았습니다. 따라서 몇단계의 전제 조건을 가지는 코드를 보면 한 세번째 전제 조건을 보는 중, 첫번째로 보았던 전제 조건을 잊어버려 허둥되곤 했습니다. 사실 이 문제는 저만의 문제는 아닌 것 같습니다. ANT의 depends 기능을 지나치게 활용하여 만들어진 빌드 스크립트를 분석하는 일은 저뿐만 아니라 다른 사람들도 무척 어려워 하더군요.

저는 첫 번째 논제인 '들여쓰기 최소화가 정말 가치있는 것인가?'라는 부분에 대해 '들여쓰기 최소화는 충분한 가치를 가지고 있다'고 주장하고 싶습니다. 왜냐하면 많은 경우 들여쓰기 최소화를 통해 더 이해하기 쉬운 코드를 만들 수 있기 때문입니다.

2008/01/15 ~ 2008/01/17

WRITTEN BY
차민창
르세상스 엔지니어가 사회를 이끌어나가는 상상을 하며!

트랙백  0 , 댓글  14개가 달렸습니다.
  1. 무플방지위원회에서 나왔다..ㅋㅋ

    if (server == null)
    return;

    이거보다 어차피 터미네이트가 목적이면..

    if (server == null) return;

    일케 한줄로 끝내는게 보기 좋지 않나?? @.@
    이건 뭐 내 취향..
    • 오 안녕하세요! 무플방지 감사합니다.

      저도 사실 그 취향이예요. 그런데 예제를 수정 없이 옮기려다보니 그렇게 되었네요.
  2. 소스 품질 체크해주는 플러그인 중에는 if안에 if가 또 있으면 경고메세지를 주더군요.
    • 안녕하세요. 처음 덧글 남겨 주시네요. 요즘 블로그를 시작하신 것 같은데 홍보차 들리신거죠? ㅎ
      앞으로 블로그 통해서 많은 소통 있으면 좋겠습니다.
  3. 전에 분기점을 만나면 증가하는 '순환 복잡도'라는 개념을 배웠던 적이 있는데요.
    두번째 코드처럼 로직을 단순화하니까 이해하기도 편하고,
    버그가 숨을 수 있는 여지도 적어서 유지보수하기 위해 필요하다고 생각해요.

    좋은 글 잘 읽었습니다. ^^
    • 예 아무래도 그렇죠.
      최근 작성하는 코드에 열심히 적용중인데 일단 감각적으론 무척 만족하고 있습니다.
  4. 저도 들여쓰기를 줄이는 것이 좋다는데 한표 거듭니다. 특정 조건을 판단하기 위해 많은 단계로 들여쓰기가 되어있는 경우 이해하기가 힘들더군요.

    if (server == null) return; <== 이렇게 쓰면 디버깅할 때 라인 잡기가 명확하지 않던데요. (VS2003 씁니다.)

    if (server == null)
    return;
    요런 스타일이 낫더군요 ^^;
    • 아 맞습니다. 디버깅을 할 때 저렇게 되어 있으면 좀 힘들죠. 개인적인 취향은 한 줄 취향이지만 디버깅을 고려한다면 밑으로 내리는게 좋겠네요. 좋은 의견 감사드려요.
  5. 음.. 저는 켄트 벡 형님이 안 좋은 예로 들어주신 것처럼 코딩을 하는 스타일인데요, 후자에도 문제가 좀 있긴 하죠.
    일단 생각나는 데로 짚어 보자면, 검사할 조건이 리소스와 관련된 것이라면, 리소스 반환 문제가 있겠습니다. 첫 번째 조건에서 a 리소스를 얻어서 실패했을 경우 return 한다고 치죠. 성공한다면 b 리소스를 얻어서 검사를 하고 실패하면 a 리소스는 반환하고 return 해야 합니다. 그렇지 않고 검사에 성공했다면 c 리소스를 얻어서 검사를 하고 실패하면 a와 b 리소스를 반환하고 .. 같은 코드가 중복이 되죠. 실수로 빼먹을 수도 있고요.

    또 하나는 검사가 실패할 경우에 어떤 동일한 처리를 하고 싶다고 합시다. 예를 들어 로그를 남긴다고 치면, return 할 때마다 같은 코드를 넣어야겠죠?

    .. 그런 등등의 이유로 저는 들여쓰기를 하고 있어요.
    • 예 말씀하신 대로 가비지 컬렉터가 있는 언어에서는 메모리 같은 자원 해제에 대한 부담이 없어 코드가 더 깔끔하게 나올 수 있는 것 같습니다.

      들여쓰기 최소화는 원칙까지는 아닌 것 같고 일반적인 경우에 좋다라는 권고 정도 될 것 같습니다. 따라서 들여쓰기 최소화가 힘든 곳에서 어렵게 들여쓰기 최소화를 할 필요는 없다고 생각하구요. :)

      마지막으로 자원 해제나 로깅을 한곳에서 하면서 들여쓰기 최소화도 할 수 있는 방법이 있을 것이라 생각되네요.
  6. 소트웍스 앤솔로지 객체지향생활체조편에서도 한메쏘드당 하나의 들여쓰기연습이 나옵니다.
    기존의 코딩습관으로는 매우 어려운 연습이죠..그래도 한번 해보고자 합니다.
    • 네, 들여쓰기 최소화를 하면 여러모로 좋은 점이 많은 것 같습니다. 하지만 너무 무리스럽게 하지는 않아도 되는 것 같아요. 제 생각에는 하면 좋다(권고) 정도가 딱 맞는 것 같습니다. 처음 뵙는 분인 것 같은데, 앞으로 자주 뵈어요! :)
  7. 코딩 스타일에 대해서 생각해 볼수 있는 계기가 되었습니다. 좋은글 감사드립니다. ^^
secret