좋은 코드에 대한 개인적인 생각 – 1

사람들과 코드 리뷰를 하거나 면접을 보거나 하면서 다양한 코드를 접한다. 좋은 코드도 많이 봤다.  하지만 그보다 더 많은 나쁜 코드들도 봤다.  더구나 그런 코드들을 작성하는 분들이 경력 10년차 이상이라는 사실이 더 사람을 참담하게 만들었다.

경력이 비래해서 공통적으로 IT, 개발 사상에 대한 나름의 기준을 정립한 분들이다.  아마도 다른 곳에서는 본인이 다른 사람을 리딩하는 역할도 하고, 멘토링도 할 것이다.  하지만 코드가 그 모양인데 이 분들이 하는 리딩, 멘토링이 과연 맞는 것일까?  QCon 컨퍼런스에서 들은 말 가운데 “코드를 작성할 줄 모르는 아키텍트의 말은 사기다!” 라는 말이 정말 와 닿았다.

그러므로 코딩을 잘 해야한다.  하지만 어떻게 작성하는 코딩이 좋은 것인지 나름의 생각이 있을 것이다.

코드도 넓은 의미에서 글쓰기의 연장선이라고 생각한다.  그만큼 코드에는 작가(프로그래머)의 심미성이 반영될 수 있다.  그리고 기호에 따라 그 맛이 달라진다.  하지만 모든 글쓰기에 기본이 있듯이 코딩에서 기본이라는 것이 있다.  읽을 수 없는 글, 읽어도 뭔 이야기인지 알아들을 수 없는 글이 있는 것처럼 코딩에도 그런 쓰레기들이 존재한다.  쓰레기 코드라는 이야기를 듣지 않으려면 어떻게 해야할까?  글쓰기와 마찬가지로 코드를 읽고 작성해봐야 한다.

좋은 코드에 대한 예를 내 기준으로 기록해보고자 한다.  물론 이견이 있을 수 있고, 더 좋은 의견이 있을 수도 있다.  좋은 코딩에 대한 토론이 이어진다면 더할 나위 없을 것 같다.  하지만 “좋은 코드”에 대해 굳이 동의를 바리지 않는다.  다만 내가 기록해두고 앞으로 그 이상의 코딩을 할 수 있길 바랄 뿐이다.

몇 번까지 번호를 붙힐 수 있을지는 모르겠다.  생각나고 짬이 나는대로 써 볼 뿐이다.


먼저 간단히 다음의 코드를 살펴보자.

if (val < 13) { // for kids
  ...
} else if (13 <= val && val <= 18) { // for youth
  ...
} else { // for adult
  ...
}

잘못된 점을 탓하기전에 잘된점을 짚어보자.  얼핏보니 코드에 적절한 코멘트를 잘 사용한 것 같다?  확신이 들지는 않지만 그게 다인 것 같다.

그럼 잘못된 점들을 까보자!

  • 변수명이 개떡같다. 코멘트로 봐서 val 이라는 변수는 나이를 뜻한다. 그럼 당연히 변수 이름이 age가 되어야 한다.
  • 13, 18 이라는 숫자는 마찬가지로 나이를 뜻한다.  근데 뭘 의미하는 나이지? 의미를 알 수 없다.
  • if ~ else if ~ else 를 통해 하나의 코드 흐름에 3가지 분기를 치고 있다.

앞 선 두개의 지적은 초보적인 문제다.  바로 고칠 수 있다.

이 가운데 가장 잘못된 부분은 하나의 코드 흐름을 3개의 서로 다른 블럭으로 나뉜 부분이다.  다른 것들은 적절히 수정하면 금방 바로잡을 수 있지만 이 3가지 분기는 확실히 코드 읽기를 방해한다.  방해할 뿐만 아니라 이 코드를 그대로 방치하면 추가적으로 적용될 코드들도 마찬가지로 오염시킬 것이다.  그렇기 때문에 가장 큰 문제점이다.

디자인 패턴을 아는 사람이라면 제대로 고치는건 간단하다.

 

이 구조를 채택하면 위의 코드 구조를 아래와 같은 방식으로 변경할 수 있다.

...
Builder builder = new Builder();
ActionExecutor executor = builder.executor(age);
executor.execute();
...

앞에서 봤던 것과 같은 한 코드 영역에서 if .. else if .. else 와 같은 복잡 다단한 구조를 없앴다.  이에 대한 복잡도를 Builder -> Factory -> Executor 이어지는 연계 구조를 활용해서 깔끔하게 정리했다.  이런 방식이 주는 이점은 단순히 코드를 깔끔하게 만드는 것 이상의 의미가 있다.

  • 코드 읽기를 하나의 흐름으로 맞췄다.  읽는 과정에서 여기저기 눈을 움직일 필요없이 위에서 아래로 쭉 읽으면 된다.
  • 단위 테스트가 쉬워졌다.  이전 코드 구조에서 단위 테스트를 할려면 앞 전에 대한 조건등을 다 맞춰줘야 각 if .. else if .. else 사이 블럭에 대한 동작을 테스트할 수 있었다.  변경된 체계에서는 Executor 위주로 각 Executor가 정의된 동작을 하는지만 살펴보면 된다.  깔끔한 테스트를 만들어서 오류 자체를 효과적으로 제거할 수 있다.
  • 원래의 코드를 Mocking을 이용해 보다 다양한 상황에 대한 테스트 케이스를 보완할 수 있다.  실제 객체를 Injection하는 것보다는 Factory, Executor 인터페이스들을 Mocking으로 Injection하면 다양한 경우에 대한 테스트 케이스를 보다 쉽게 만들 수 있다.

혹자는 별것도 아닌 코드에 Factory니 Builder니 하는 계층을 이야기하는건 배보다 배꼽이 더 큰 이야기가 아니냐고 이야기할 지도 모른다.  하지만 한번 쓰고 버릴 코드가 아니라면 해야한다.  개인적으로는 한번 쓰고 버릴 코드도 해야한다고 주장한다.  코드를 작성하는 것도 습관이다.  그리고 잘못된 코딩 습관만큼 고치기 힘든 버릇도 없는 것 같다.

경우에 따라 빠르게 진행해야하는 코딩의 경우 Technical debt(s)를 감수하고 진행해야한다. 하지만 반드시 뒤따라 Refactoring이 이어져야한다.  하지만 한번 돌고 나면 만족하게 마련이다.  인간이라는게 이기적인 동물이기 때문이다. 목표를 달성하면 그 이후의 뒤정리는 그 이기심을 넘어서는 열정이 있어야 하는데 말이다. 그런 사람이 대부분이라고 생각하지 않는다.

따라서 대부분의 경우에도 이런 식으로 코딩해야한다.

– 끝 –