ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 리팩토링을 통한 로젝트 망치기(?)
    Programming Info 2008. 12. 27. 03:29
    728x90
    주제의 특성상 많은 경험(최소한 몇 차례의 실무 프로젝트 경력)을 쌓고 평소 프로젝트 진행에 대해 많이 생각해보던 사람일수록 잘 이해되고 감흥이 일 것이라 생각됩니다. 특히 필자와 비슷한 일들을 겪어 본 독자라면 더욱 쉽게 이해할 수 있겠지요. 그렇지 못한 이들에게는 ‘이게 무슨 얘긴가? 추상적인 얘기들만 하고 있군!’하는 생각만 들게 할 지도 모르겠습니다(하지만 어쩔 수 없는 한계가 아닐까요. 아무리 훌륭한 가르침도 직접 겪어보지 않으면 깨닫지 못하는 것이 우리 인간 아닙니까? 하물며 필자의 어설픈 글이야).
    그래서 가능한 읽는 이들의 경험적 지식을 이끌어내기 위해 이 글의 모든 내용은 필자의 경험을 바탕으로 쓰여졌습니다. 처음 기획시는 소스 수준의 구체적 예를 많이 제시하려 하였으나 여의치 않네요. 본 글이 단순히 리팩토링 기법을 소개하는 것이 아닌 리팩토링을 수행하였을 때 잘못될 수 있는 상황을 이야기하고 있어 짧은 코드로 충분히 설명할 재간이 없군요. 대신 필자가 그간 쌓아 놓은 자료들과 기타 참조 문서의 링크를 별도의 박스(참고 자료)로 제공하고 있으니, 이들을 먼저 찾아보는 것이 글을 읽는데 도움이 될 겁니다. 글 중간 중간에 불쑥 불쑥 튀어나오는 낯선 이름들이 당황스럽다고 하는군요.

    리팩토링(refactoring)이란 단어 자체는 생소할 지라도 대부분의 개발자들은 알게 모르게 이미 수 차례 리팩토링을 수행해 왔고, 지금도 그리고 앞으로도 계속하게 될 것입니다(다만 얼마나 체계적으로 수행하는가는 개인에 따라 현격한 차이가 나타납니다). 리팩토링은 사실 별 게 아닙니다. 현 구조를 더욱 유연하고 재활용 가능한 형태로 재구성하는 일련의 과정(참고자료 ?, ?)을 말합니다. 즉, 기능상으로는 동일하지만 더 깔끔하고 세련된 형태로 구조를 뜯어고치는 작업입니다. 넓게 보면 클래스나 변수명 등을 바꾸거나 주석 추가 등 코드를 깔끔하고 일관성 있게 정리하는 작업 역시 리팩토링 행위로 포함시킬 수 있습니다.
    이런 리팩토링은 개념적 명확성과 유용성, 개발툴의 지원이 함께 하면서 최근 몇 년 사이에 급부상하고 있습니다. 초보 개발자들은 물론 숙련자들에 이르기까지 리팩토링은 세련된 코드와 유연한 아키텍처를 만들어 내는데 아주 효율적인 수단을 제공해 줍니다.
    그럼 이제부터 필자가 리팩토링을 좋아하는 이유를 간략히 짚어본 후 리팩토링을 통해 프로젝트를 망치는 비결(?)에 대해 본격적으로 논의해 보도록 합시다.

    나는 왜 리팩토링을 하는가?
    수없이 회자되던 리팩토링의 이점(참고자료 ?, ?) 외에 필자가 리팩토링을 좋아하는 이유 두 가지를 말씀드리죠. 최소한 필자의 경우 리팩토링이 가져다주는 가장 큰 선물은 바로 ‘자기만족’입니다. 필자가 아키텍처에 특히 심혈을 기울이는 이유와 마찬가지이지요. 겉으로만 뻔지르르하고 그저 돌아가기만 잘하는 프로그램이 아닌 누구 앞에서건 그 설계와 소스까지도 자랑스럽게 내보일 수 있는 기본이 충실한 프로그램을 만드는데 리팩토링은 매력적인 도우미 역할을 충실히 수행해줍니다.
    다음으로는 ‘아키텍처에 대한 이해도 증진’을 뽑아보았습니다. 리팩토링을 하고자 한다는 것은 다시 말해 현재의 구조(아키텍처)가 어딘지 못내 거슬린다는 뜻이죠. 그리고 리팩토링을 했다는 것은 최소한 이전보다는 덜 거슬리는 구조를 생각해 냈고 적용해 보았음을 뜻합니다. 이 과정에서 우리는 자연스럽게 다양한 아키텍처를 비교/분석해보며 서로의 장단점들을 놓고 고민해보게 되죠. 여러 후보 아키텍처들 중 마지막 하나를 남겨놓기까지의 사고 과정, 그리고 구현/검증하며 깨닫는 과정으로부터 아키텍처를 바라보는 시각은 점점 커져가게 됩니다. 돌아가면 돌아가는 데로 만족하고 더 이상 칼을 델 생각도 않는 사람들로서는 쉽게 얻기 힘든 리팩토링의 선물이 아닐까요?

    그럼 나도 리팩토링 해볼까?
    “자 그럼 옆집 고양이도 꼬리 치며(?) 반길만한 이 엄청난 보물을 눈앞에 두고 어찌 구경만 할 수 있겠는가? 나도 어서 리팩토링을 익혀 다음 프로젝트부터 당장 써먹어야지!”
    이렇게 되면 글 제목 그대로 프로젝트를 망치는 지름길로 들어서게 됩니다. 리팩토링은 상황에 맞게 그리고 많은 경험과 노하우를 바탕으로 이루어지지 않으면 자칫 단순 ‘노가다’가 될 뿐 아니라 프로젝트 전체를 망쳐버릴 수도 있는 커다란 위험 요소로 작용하기도 합니다. 상사가 내던진 키보드 파편에 피눈물을 흘리고 싶지 않다면 이제부터 언급하게 될 여러 사항들을 잘 숙지하고 리팩토링 적용에 신중을 기하기 바랍니다.
    그렇다면 리팩토링이 안고 있는 위험성에는 어떤 것들이 있을까요? 어느 경우에 리팩토링을 적용해야 하고 또 어떤 경우에 피해야 할까요? 리팩토링이란 개념도 모르던 시절부터 습관적으로 리팩토링을 행하며 깨달은 나름대로의 노하우들을 감히 여러분께 설파해 보고자 합니다.

    리팩토링은 버그를 만들어 낸다
    리팩토링은 기존 코드에 상당한 구조적 변경을 가져옵니다. 개념 자체가 그런 것이니 더 이상 반론의 여지가 없지요. 그리고 이 과정에서 얘기치 못한 미세한 버그들이 기어 나와 야금야금 대들보를 갉아먹게 될지 모릅니다.
    사실 이 주제는 ‘Refactoring - Improving the Design of Existing Code(참고자료 ?)’에서부터 익히 강조되던 내용이나 다른 문제들의 근간이 되는 만큼 역시 처음에 언급하지 않을 수 없군요. 만약 애플리케이션의 정상 동작 여부를 검증할 수 있는 테스트 킷이 없다면 여러분은 일일이 기능을 테스트해 봐야 합니다. 하지만 대부분의 개발자들은 몇몇 주요 기능만 실행해보고 ‘당연히 잘 돌아가겠지’하는 안이한 태도를 취하는 것이 현실입니다. 개발 경력이 조금만 된다면 사소한 수정 하나로 전혀 생각지도 못한 곳에서 문제가 발생하는 경우를 분명 접해봤을 테지요. 수정 당시에는 당면한 문제에만 집착하게 되기 때문에 매번 그것이 전체 프로젝트에 미칠 영향을 파악한다는 것은 이미 인간의 경지를 넘어서는 바람일 뿐입니다. 그렇다고 기도만 하고 넘어가자는 얘기는 물론 아닙니다. 최소한 뜯어고치기 전에 세 번(다른 각도에서) 생각하는 습관을 길러야 합니다.
    문제가 표면으로 쉽게 떠오르면 다행이지만 최악의 경우 구동 상에는 전혀 이상이 없고 특정한 시나리오에서만 발생하여 주요 데이터를 쉽게 구분할 수 없는 엉뚱한 값으로 변경해 버릴 수도 있습니다. 보통 이런 경우는 문제를 발견했을 당시 이미 원인이 되는 변경 후에도 수차례의 다른 변경이 가해진 상태이기 때문에 문제의 원흉을 찾기도 여간 까다롭지 않습니다. 리팩토링 툴을 사용하더라도 이러한 위험성으로부터 완벽히 자유로울 순 없습니다. 지역 변수를 필드로 옮기거나 슈퍼 클래스(또는 인터페이스) 추출, 중첩된(nested) 클래스를 일반 클래스로 변경하는 등 그 접근 범위가 확장될 때에는 특히 신경을 써야 합니다.
    접근 범위 확장이란 다시 말해 지금까지와는 다른 시나리오를 통해 해당 로직에 접근할 수 있다는 뜻이 됩니다. 이는 애플리케이션이 수행되는 동적 구조로 당연히 정적인 클래스 다이어그램이나 소스 코드만 들여다봐서는 전혀 도움이 되지 않습니다. 하지만 불행히도 필자가 마주했던 대부분의 개발자들은 동적인 것보다는 정적 구조에 익숙하며, 이를 더 중요시 여기더군요.

    툴 지원 없이 섣불리 리팩토링하지 말라
    단순 노가다를 통해 수많은 버그들을 만들어 보고 결국 원상 복구도 못하는 최악의 시나리오에 도전하는 일이 될지 모릅니다. 어쩔 수 없는 것이 우리 인간은 문제가 저수준으로 내려갈수록 시야가 좁아집니다. 툴이 없다면 우리는 코드 수준에서 잔뜩 신경을 곤두세워야만 합니다. 하지만 툴과 함께라면 최소한 넓은 시야를 가지고 구조(structure)적 관점에서 바라볼 수 있어 버그를 줄일 수 있는 간접적인 이점이 됩니다.
    단순한 이름 변경만 해도 그렇습니다. 텍스트 에디터로 작업하던 시절, 즐겨 쓰던 에디터의 ‘Replace in Files’ 기능은 양날의 칼이었죠. 수많은 파일들에 흩어져 있는 메쏘드, 변수명, 문자열 등을 빠짐없이 단번에 바꿔주는 것은 좋은데, 자칫 실수로 이미 존재하던 다른 용도의 이름과 겹쳐지기도 합니다. 생각하기도 싫은 경험이지만 일일이 바꿀 생각을 하면 역시 ‘Replace in Files’의 유혹을 뿌리칠 수 없었죠. 하지만 전문 리팩토링 툴들은 실제 리팩토링 하기에 앞서 충돌 검사를 수행함으로써 예상되는 버그들을 미연에 방지해줍니다.
    그렇지만 툴이라 해서 만능은 아닙니다. 툴 차원의 충돌 검사는 어차피 정적 구조에 제한되어 있고, ‘리팩토링은 단순한 작업’이란 잘못된 인식을 심어 주어, 검증 작업 없이 너무 많은 변경을 단번에 가하게 되는 상황을 발생시킬 수 있습니다.

    테스트 킷을 만들라
    ‘웃기는 소리, 말이야 쉽지!’ 많은 사람들이 이렇게 생각하지 않았을까 합니다. 당연한 반응이고 현실입니다. 대부분의 경우 테스트 킷을 만드는 일 자체가 귀찮기도 하거니와 어떤 경우는 별도의 프로젝트로 진행해도 될 만큼 커져버리기도 합니다. 기능 구현하기도 바쁜데 테스트 킷이라니.
    그럼에도 불구하고 대부분의 경우는 테스트 킷을 제작하는 것이 훨씬 경제적입니다. 프레임워크와 같이 고가용성을 요구하거나 다양한 플랫폼으로 이식 또는 커스터마이징을 해야 할 애플리케이션이라면 더 이상 언급할 필요가 없겠죠?
    개인적인 경험에 비추어 볼 때 일일이 하나씩 손으로 테스트하는 것은 오히려 더 많은 시간을 요구하는 것은 물론이요, 급기야는 ‘이제껏 잘 돌아가던 건데’하는 안이한 생각으로 테스트를 게을리 하게 만듭니다. 여러분도 그렇지 않나요? 안이함은 결국 상사의 핍박 어린 눈총과 때늦은 날밤 디버깅으로 되돌아오기 마련이죠. ‘잘 돌아가겠지’하는 믿음(?) 만으로 고객 앞에 선다면 망신만 당할 뿐입니다. 두세 번만 되풀이되면 고객은 더 이상 우리의 말을 신뢰하지 않게 될 겁니다. 양치기 소년이 되는 것이죠.
    한 가지 행복한 사실은 손만 뻗으면 테스트 킷을 쉽게 제작할 수 있는 훌륭한 솔루션들을 쉽게 얻을 수 있다는 것입니다(JUnit, 참고자료 ? 등). 덧붙여 자주 만들다 보면 나중에 가서는 기계적으로 만들어낼 수도 있을 테죠.
    사실 필자도 다른 프로젝트를 하면서 이에 대한 제대로 된 테스트 킷을 만들어본 경험은 없습니다. 온라인 도서 관리 시스템인 SuperCroc 프로젝트(참고자료 ?)에서 하부 계층의 정상 동작 여부를 검증하기 위한 테스트 기능을 추가하였지만, 테스트 자체를 위한 목적보다는 ‘봐라! 잘 돌아가지 않느냐!’하는 목적이 강했지요. 또한 범용적인 Stress 테스트 툴을 만들기 위한 SMS 프로젝트(참고자료 ?)는 실패로 끝이 났고, 디지털 TV에 임베디드된 PersonalJava의 Robustne ss(견고성) 테스트 킷(pJava RTK) 제작 프로젝트를 최근 마무리한 바 있지요. 단, 이전부터 주요한 기능을 수행하는 모듈들은 중첩(nested) 클래스 형태의 테스트 클래스를 제작해 두기는 합니다.

    리팩토링은 API 호환성을 파괴할 수 있다
    리팩토링을 하기 앞서 내가 지금 무엇을 하고 있는가 심사숙고 해본 후 일을 저지릅시다. 내부 구조만 변경하는 것이 가장 이상적이긴 하지만 사실 인터페이스 자체를 바꾸는 경우도 허다하기 때문이죠(인터페이스 설계를 소홀히 했다면 변경해 주는 것이 적절하며, 클래스 추상화, 파라미터 변경 등을 행하게 되면 자연히 인터페이스도 바뀌게 됩니다).
    남이 만든 소스를 살펴보다 보면 정말 ‘이 사람이 무슨 생각으로 이렇게 만들어 놨을까?’하는 생각과 함께 모두 사라지게 만들고 새 세상을 만들고픈 천지개벽의 욕구(?)가 용솟음칠 때가 한두 번이 아닙니다. 하지만 아무리 전임자가 엉터리로 짜놓은 프로그램이라도 만약 이 코드가 다른 프로젝트에서도 사용되는 라이브러리나 기반 플랫폼의 성격을 갖는다면 절대 자기 혼자 결정할 문제가 아닙니다. 물론 솟구치는 열정이 생계유지에 대한 압박보다 강렬하다면 누가 감히 가로막겠습니까?
    필자의 경우 개인 라이브러리(wegra’s Library, 참고자료 ?) 제작에 공을 쏟는 편입니다. 이런 저런 프로젝트를 하면서 프로젝트 독립적인 모듈들을 분리하여 개인 라이브러리에 추가하여 다른 프로젝트에서 이용하곤 하지요. 그래도 한참이 지나 다시 살펴보면 ‘이런 한심한 것들’하는 부분이 꼭 나오더군요. 메쏘드나 클래스 심지어 패키지 전체가 한심해 보이기도 합니다. 이런 것은 가차 없이 수정/제거되고 마는데, 어디까지나 개인 라이브러리이기 때문에 책임을 물을 사람도 손해를 보는 사람도 나밖에 없기 때문에 가능한 일이지요.
    자바를 하는 사람이라면 독수리 키보드 쪼던 시절인 JDK 1.0의 Deprecated API들이 아직까지도 살랑거리며 손을 흔드는지 잠시 고민해보길. 그만큼 호환성 유지에 대한 압박은 크다는 얘기입니다. 자칫 잘못된 설계를 끝까지 짊어지고 가야 할지도 모릅니다. 리팩토링이 항상 구세주가 되어줄 수는 없는 노릇 아니겠습니까?

    디자인에 신경을 써라
    “리팩토링이 잘못된 디자인을 손쉽게 뜯어 고칠 수 있는 수단인 것은 분명하지만, 그렇다고 디자인 자체를 소홀히 한다면 큰 낭패를 보게 되리라.”
    리팩토링에 ‘의존’한다는 것은 일단 돌아가게 만들어 놓고 뒤에 가서 디자인하겠다는 ‘선구현 후설계’ 정책을 사용하겠단 뜻입니다. ‘뭐가 문제인가? 오히려 더 빠를 수도 있지 않은가?’ 물론 그럴 수도 있고 대다수의 개발자들이 이 정책에 익숙한 것이 사실이죠. 하지만 문제가 있네요.
    리팩토링에 지나치게 의존하게 되면 지금까지 필자가 열변을 토한 리팩토링의 위험성에 그대로 노출되고 맙니다. 리팩토링으로 인한 버그 발생 확률은 얼마나 많은 리팩토링을 가하는가에 달려있지요. 그냥 가다듬는 수준의 리팩토링에서는 대부분 문제가 발생한다 해도 어렵지 않게 대처할 수 있지만 완전히 구조를 뒤엎는 리팩토링이라면?
    필자의 경우 지나치게 최적화된 코드를 리팩토링하려다 수차례 실패한 바 있습니다. 아주 단순한 JVM 메모리 모니터링 프로그램을 만들어 본 적이 있는데, 당시는 지금처럼 체계적인 코딩 스타일을 갖추지 못했을 뿐 아니라 최소한의 메모리 사용량에 초점을 맞추어 필드명까지 가능한 압축해 버렸습니다(참고 자료 ?은 이의 초기 버전의 변종 중 하나입니다). 그리고 몇 년 후 기능을 확장하려 시도했을 당시 정말 악몽 같은 코드가 되어 필자를 괴롭혔습니다. 열심히 뜯어고치고 나면 어딘가에서 원인 모를 오동작을 일으키곤 하여 결국 포기하곤 하였죠. 몇 번의 시도 끝에 결국 기존 코드를 완전히 무시하고, 처음부터 새로 구현하고 나서야 2.0 버전이 탄생하게 되었습니다. 그렇게 한번 체계가 잡히니 그 후부터는 비교적 어렵지 않게 리팩토링이 되더군요(현재는 3.7 버전(참고자료 ?)까지 개선되었고, AspectJ(자바의 AOP 확장 버전)를 이용해 한번 더 개선하려 합니다).

    단계적으로 리팩토링하라
    아키텍처의 구조적 문제 발생으로 어쩔 수 없이 전반적으로 다 손을 봐야만 합니다. 아예 처음부터 새로 구현하는 방법과 어떻게든 리팩토링을 시도해보는 방법 등이 떠오르는군요. 물론 어떤 방식을 택하는가는 프로젝트 진행 상황과 구조의 복잡도, 개발자의 역량 등에 따라 결정될 문제지만, 어찌되었건 우리는 리팩토링이란 방법을 선택했다 치고 이야기를 진행해 봅시다.
    우선은 메인 구조와 관련이 없는 부분들부터 정리하도록 합니다. 중요한 부분에서 헛갈리는 일이 없도록 길을 정비하는 단계라고 생각하길. 다음으론 일부 구조만 리팩토링해도 동작에 문제가 없는 부분들을 찾아봅니다. 그런 부분이 발견된다면 간단한 것들부터 하나씩 고쳐나갑니다. 단, 어느 정도 고쳐졌다면 반드시 의도한 대로 동작하는지 확인해 보아야 합니다. 중간 검증 과정이 없다면 마지막 수정 후 문제가 발생했을 때 원인 파악에 애를 먹게 되고, 자칫 처음부터 다시 시작하는 상황이 발생합니다(설마 하는 안이함과 성급함으로 귀중한 시간만 허비한 것이죠). 이러한 과정을 끝까지 진행하고 나면 우리가 원하는 모습이 되어 있을 겁니다.

     
    프로젝트 초기부터 꾸준히 아키텍처 디자인에 신경을 써야 합니다. 안정화되지 않은 아키텍처는 필연적으로 리팩토링을 해야 하는 구조를 만들어내기 때문이죠.
    데모에서 고객이 원하는 것은 안정적으로 동작하는 애플리케이션입니다. 오동작과 오류 메시지가 난무하는 시스템을 가지고 어찌 고객에게 신뢰감과 안도감을 심어줄 수 있을까요? 이 시점에서는 일단 요구 조건에 만족되면 건들지 말 것을 당부합니다. 버그 패치를 위한 목적 말고는 더 이상의 구조 변경은 금물. 지금은 그보다 현 상태에서의 디버깅과 안정화를 꾀할 시점입니다. 기능이 약간 부족하고 수행 성능이 약간 떨어진다 하더라도 안정적으로 동작만 한다면 프로젝트 기획시 목표로 한 시점에 제품을 릴리즈하는 데는 큰 지장이 없습니다.
    또한 안정화된 기반 플랫폼 위에는 손쉽게 새 기능을 덧붙일 수 있습니다. 픽스(fix)된 API와 수차례의 테스트를 거친 플랫폼이라면, 혹 버그가 발견되었다 해도 ‘숨어 있던 버그까지 잡아냈어! 이제 우리 플랫폼은 더욱 튼튼해진 거야!’하며 기뻐하겠지만, 마지막까지 언제 변경될지 모르는 API와 버그 투성이의 불안정한 플랫폼은 절망감을 안겨주곤 합니다. 나름대로 머리를 쓴다고 플랫폼의 버그 회피 방안을 구상해 돌아가는 경우도 많고, 플랫폼의 문제냐 응용 파트 문제로 고민하고 갈등하기도 합니다. 문제가 잘 안 풀리면 플랫폼부터 의심하다가 사실로 확인되면 ‘어떻게 작업하라는 거야?’라는 불만만 쌓여갑니다.

    늦은 리팩토링은 다음 릴리즈로 넘기자
    앞서 얘기한 대로 프로젝트가 어느 정도 마무리 단계에 들어가면 더 이상의 리팩토링은 지양합니다. 그리고 릴리즈가 되고 나면 소스를 두 개 이상의 버전으로 나누어 관리하는 것이죠. 이들은 물론 물리적으로 구분되어야 하며 각각을 관리하는 것은 별개의 프로젝트가 됩니다. 하나는 릴리즈된 버전의 유지보수용으로, 구조는 변경하지 말고 가능한 현 상태에서 문제들을 해결합니다. 구조 변경에서 오는 예기치 못한 문제들을 피하기 위함이죠. 다른 하나는 물론 다음 릴리즈를 위해 대규모 변형이 가해질 녀석입니다. 구조가 영 맘에 들지 않았다면 지금이 바로 리팩토링 타임이죠.
    초기에 변경된 구조가 최종 버전까지 유지되진 않더라도 잘 다듬어진 아키텍처는 다음 프로젝트 진행에 여러 모로 도움이 됩니다. 일단 구조뿐 아니라 코드 자체도 깔끔해지므로 다음 프로젝트를 위해 보강된 새 팀원 교육에도 효과적이고, 시장 요구 시 이를 기반으로 한 마이너(minor) 업그레이드 버전을 조기 투입할 수도 있겠죠.

    아키텍처 연구를 꾸준히 하라
    어떠한 아키텍처가 더 훌륭한 것인지 분간할 수 없다면 대체 무슨 이유로 리팩토링을 하는 걸까요? 좋은 아키텍처라 해서 누구나 보고 ‘음 좋군!’하는 것이 아닙니다. 경험 없고 아키텍처에 별다른 관심이 없는 사람의 눈으로는 그저 자신의 방식이 최고로 보이기 쉽습니다. 범용성을 위해 추가한 계층도 그 의도를 파악하지 못하면 쓸모없는 오버헤드로 보이기도 합니다.
    필자가 만든 기본 State(참고자료 ?)는 세 번의 대대적 변경 끝에 현재와 같은 모습을 갖추게 되었습니다. GoF(참고자료 ?)의 State 패턴은 책으로만 보았을 당시는 그저 ‘옳은 이야기군’하는 정도. 하지만 프로토콜 스택을 구현하면서 실제 State를 사용할 일이 생겼습니다. State 패턴을 적용해 보고자 구현에 들어갔고, 하지만 아무리 생각해도 GoF의 State는 너무 추상화되어 있다는 생각밖에 들지 않더군요. 나름대로 디테일한 부분까지 추가하고 구조도 바꿔 무사히 구현했더랍니다. 내 딴에는 잘 만들었단 생각으로 필자의 라이브러리(참고자료 ?)에 추가해 두었죠.
    그리곤 다른 프로젝트에서 또 State를 써먹을 일이 생겼을 때 나의 State는 칼질 없이는 도저히 그냥 사용할 수 없을 만큼 기존 프로젝트에 종속적인 구조였음을 깨닫게 되었죠. 이런 식으로 두 번의 큰 추상화를 거친 후에야 현재와 거의 유사한 모습이 되었습니다(SMS 프로젝트(참고자료 ?)의 일부로 Action Script(참고자료 ?)용 어휘 분석기 제작시 현 구조의 초기 구조 확립).
    그리곤 또 자만심이 생겼습니다. ‘내가 만든 것이 GoF만 못하겠냐?’ 당당히 GoF의 State를 살펴보았죠. 그런데 내가 만든 State와 GoF의 그것이 거의 흡사한 모습을 취하고 있지 않겠습니까? 결국 필자는 GoF를 부정하고 자신만의 길을 걷다가 무언가 깨닫고 보니 다시 GoF에 서 있던 것입니다.
    사실 이 패키지는 아직도 만족스럽지는 못합니다만, 현재의 자바로서는 마땅히 해결할 방법이 없어 Boxing/Unboxing 기능이 도입된 J2SE 1.5를 기다리고 있습니다. C#으로는 좀더 그럴싸한 State를 만들어 사용한 바 있습니다(AIX 용 nmon Parser(참고자료 ?).
    한 번은 회사 프로젝트를 진행하면서 동료가 만들어 놓은 MessageStream을 보고 ‘왜 이렇게 했을까? 내가 깔끔하게 고쳐줘야지’하고 수정했다가 낭패를 본 바 있습니다. 이때는 구조 자체를 이해하지 못했다기보다는 의도를 파악하지 못했기 때문에 벌어진 해프닝이었죠. 앞의 Stream들은 많은 이들이 익히 알고 있는 명저 자바 네트워크 프로그래밍(참고자료 ?)에서 소개하고 있는 구조이지요.
    패턴을 공부해 머리로 이해하는 것과 실제 프로젝트를 진행하며 적재적소에 응용하고 적용할 수 있는 것과는 하늘과 땅 만큼의 차이가 있습니다. 조작된 예제 수준에서만 구현해 보고 패턴을 안다고 얘기하는 사람들은 아직 햇병아리들에 지나지 않습니다. 실무에서는 오히려 패턴을 사용해 프로젝트를 망치는 경우도 그리 희귀하지 않으니 말입니다.
    이처럼 아키텍처는 책 한두 권 읽는다고 익혀지는 것이 아닙니다. 꾸준히 관심을 가지고 이것저것 시도해보고 시행착오를 거치면서 서서히 깨닫게 되는 것이죠. 아키텍트라 함은 최소 10년에서 15년 이상의 경력을 갖춘 사람임을 잊지 마시길!

    코딩 표준안을 이용하라
    표준안은 여러 사람이 함께 쓸수록 좋습니다. 자기가 만든 프로그램도 그날 기분에 따라 스타일이 달라지는 호떡이 된다면 그야말로 불행한 일이 아닐 수 없습니다. 팀 프로젝트일 경우 팀원들의 코딩 스타일을 가능한 일치시켜주는 것이 좋고, 나아가 회사 전체에 일정한 권장 표준안이 있어야 하며, 그 표준안이 해당 언어(기술 등)의 커뮤니티가 널리 공유하는 것이라면 더할 나위가 없겠죠.
    리팩토링을 해야 할 대상이 자신에게 익숙한 스타일로 작성되어 있다면, 흐름을 분석하고 의도를 파악하는 데서 오는 부담이 경감될 뿐 아니라 오해의 소지도 줄게 되어 잘못된 리팩토링이나 버그 발생 여지를 최소화합니다. 안정적으로 리팩토링을 할 수 있는 기반을 마련한다는 데에서 단계적 리팩토링과 유사한 이점이 있겠군요.
    필자는 자바 언어의 경우 나름대로의 표준안(wegra’s Java Naming & Coding Conventions, 참고자료 ?)을 가지고 있습니다. 개인 프로젝트의 경우 항시 이 표준안에 따르며, 팀 프로젝트에서도 해당 팀에 표준이 없다면 본인의 것을 제시하며 따라 주기를 요구합니다. 결과적으로 많이 참조하진 않은 듯 하지만 필자의 표준안이 모 대기업의 표준안 작성 시 참고 자료로 제출된 바 있습니다.
    개인적으로 가장 못마땅한 스타일은 자기가 익숙한 한 가지 스타일을 모든 플랫폼, 모든 언어에서 고수하는 사람들인데, 의외로 실력 있는 개발자 중에 꾀 존재하더군요. 아직 마인드가 부족하거나 귀차니즘(?) 또는 자아도취 등의 증상에 빠져 있는 개발자들이 아닐까 합니다. 개인 취향보다는 팀워크가 중요하지 않을까요? 융통성 있는 개발 습관을 기르도록 합시다.

    유지보수가 필요 없어, 그래도 리팩토링 해보자
    ‘드디어 프로젝트가 끝났다. 이제 이 코드들은 꼴도 보기 싫어! 지긋지긋해! 나의 기억 속에서 사라져줘!’
    잠도 못자고 고생하며 기껏 만들어 놓고 꼴도 보기 싫다 하면 왠지 손해란 생각 안드나요? 꼴도 보기 싫은 녀석을 잘 어르고 달래 어여쁜 녀석으로 탈바꿈해 봅시다. 더 낳은 구조를 떠올려 보고 정말 가능한 구조인가, 정말 더 좋은 구조인가를 확인해 봅시다. 확실해 보이던 것도 몇 가지 생각지 못한 문제들로 인해 구현 자체가 불가능할 수도 있지요. 실패한 것만으로도 값진 경험입니다. 최소한 유사 프로젝트에서 검증되지 않은 잘못된 구조로 직행하는 일은 줄어들지 않을까요. 아키텍처라는 분야는 많이 접해보고 고쳐봐야만 조금씩 감을 키워갈 수 있습니다.
    개인 프로젝트라면 마루타(?)로 이용하기 적격이죠. 뭐라 할 사람도 없고 스케쥴에 쫓길 이유도 없습니다. 생각날 때 조금 조금씩 개선해 보는 것도 나름대로 재밌습니다. 예전 자신의 코드를 다시 보면서 그 한심함을 느껴보기 바랍니다. 한심해 보일수록 내가 그만큼 성장했다는 증거이니 나름대로 스릴(?)과 재미가 있지 않을까요?

    좋은 구조를 익혔다면 재활용을 시도해보라
    자신에게 익숙한 구조는 기회만 생기면 가차 없이 다시 사용하려 하는 것이 인지상정이죠. 이런 식으로 계속 반복하며 갈리고 닦여져 나온 것 중 하나가 바로 그 유명한 디자인 패턴(참고자료 ?)입니다. 패턴화된 구조는 최소한 몇 차례씩 검증된 구조이고 익숙하기 때문에 실수할 확률도 적습니다. 결과적으로 개발 기간 단축, 나아가 경쟁력 향상이라는 것으로 치켜세울 수도 있습니다.
    잘 알려진 패턴은 아니더라도 ‘이 구조 정말 맘에 들어!’하는 때가 종종 있지요. 좋긴 좋은데 딱 한번 써보고 정말 좋은 것인지, 그냥 어쩌다 좋게 보인 것인지 확인할 길이 없군요. 그렇담 이를 잘 정리해 두었다가 비슷한 상황에 닥쳤을 때 다시 한번 시도해 보는 겁니다. 더 좋은 방법은 타인이 활용할 수 있는가를 확인해 보는 것이죠. 수정 없이 성공적으로 적용되었다면 정말로 제대로 된 구조일 가능성이 한층 높아진 것이고, 반대로 많은 칼질을 가해야 하거나 적용에 실패한다면 특정 경우에만 쓰일 수 있는 제한된 구조일 지도 모릅니다. 재사용성이 떨어지는 구조는 더욱 유연한 구조가 있을 지 고심해보고, 거기에 확장성까지 추가한다면 금상첨화가 따로 없죠. 이런 과정을 몇 번씩 반복하다 보면 멋들어진 패턴이 만들어질 겁니다.
    필자의 취미(?) 중 가장 재미난 것은 바로 라이브러리 구축이란 놀이입니다(wegra’s Library, 참고자료 ?). 라이브러리는 패턴처럼 구조적이진 않지만 커스터마이징 없이 반복 사용할 수 있을 만큼 높은 수준의 재사용성이 요구됩니다. 패키지(또는 네임스페이스) 구분에서부터 클래스명, 메쏘드 이름/시그니처, 쓰레드 안정성에 이르기까지 세세히 신경을 써야만 한답니다. 하지만 말처럼 쉽지는 않더군요. 생각해보니 나만의 라이브러리를 구축하리라 마음을 먹고 작업을 시작한 4년여 전의 소스들은 지금 하나도 남아 있지 않네요. 라이브러리 덩치가 항상 그대로인 가장 큰 이유가 아닐까 합니다.
    라이브러리를 리팩토링하려면 가장 먼저 고려해야 할 사항이 얼마나 많은 프로젝트가 본 라이브러리에 종속되어 있는가 입니다. 리팩토링이 미치는 영향을 충분히 파악했다면 피해를 감수하며 리팩토링을 할 것인가, 기존 인터페이스를 유지(deprecated)하면서 새로운 인터페이스를 추가할 것인가, 완전 분리된 별도의 라이브러리를 구축할 것인가 등을 결정하면 됩니다.

    리팩토링에 앞서 기존 소스를 백업해 둘 것
    리팩토링하다가 복구 불능 상태에 빠졌던 경험이 있나요? 불행인지 다행인지 필자는 몇 번을 겪어봤는지 잘 기억나지도 않는군요. 당시는 개발툴들이 리팩토링을 지원해 주기도 훨씬 전이었고, 버전 관리 툴(CVS, MS Visual Source Safe, Rational ClearCase 등)도 기껏 이름만 알았던 탓에 많은 고생을 했지요. 그래서인지 원본은 놔두고 복사본으로 작업하는 습관이 일찍부터 몸에 베어 대부분의 경우 ‘개선 실패’ 선에서 끝나고 말았습니다. 이런 원인은 요즘 툴 자체에서 히스토리 기능을 기본 지원하고 훌륭한 버전 관리 툴들이 많기 때문에, 거의 발생하지 않는답니다. 많이 좋아지긴 했는데, 그래도 수십 개의 파일들에 업데이트를 가한 상황이라면 일일이 찾아서 복구하기도 여간 귀찮은 작업이 아니겠죠. 실수하기도 쉽고 역시 가장 좋은 방식은 이정표가 되는 버전들을 물리적으로 격리시키는 것입니다.
    양날검(劍) 리팩토링, 도(刀)로 승화시키자!
    리팩토링은 꾸준한 아키텍처 연구와 많은 실무 경험을 바탕으로 행해져야 합니다. 그리고 나쁜 구조를 좋은 구조로 재창조하는 것보다, 좋은 구조를 좀더 나은 구조로 가다듬는 것이 진정 리팩토링 제대로 활용하는 것임을 감히 주장해 봅니다.
    이상으로 짧은 경력이지만 지금껏 개발하고 연구해 오면서 느낀 노하우들을 정리해 보았습니다. 아무쪼록 본 글이 많은 독자에게 깨우침을 주고, 바른 길로 인도하는데 조금이나마 힘이 되었으면 합니다. 나아가 여러분의 피드백이 필자를 각성시켜 함께 발전할 수 있는 계기가 되었으면 하는 바람입니다.

    정리 | 조규형 | jokyu@korea.cnet.com

    소프트웨어 개발 효율성을 향상시키는 다양한 개념들 중 비교적 최근에 구체화된 리팩토링은 그 개념적 명확성과 더불어 툴 차원의 지원이 함께하면서 급속히 퍼져 나가고 있습니다. 하지만 리팩토링의 이상적인 순기능만이 너무 부각되어 오히려 해가 될 수 있는 현실적인 문제들이 간과되고 있는 듯 하여, 비록 길지 않은 경력이지만 필자의 경험으로부터 얻은 리팩토링의 양면성을 여러 분께 알리고자 합니다.
     
    출처 : http://www.imaso.co.kr/?doc=bbs/gnuboard_pdf.php&bo_table=article&page=2&wr_id=660&publishdate=20030901

    댓글

Designed by black7375.