http://gpgstudy.com/forum/viewtopic.php?topic=16661

자주 방문하는 프로그래머 커뮤니티 사이트에 흥미로운 (하지만 고통스럽고 힘든) 주제가 올라왔습니다.

현업에서 일하는 프로그래머들이라면 누구나 피해갈 수 없는 문제입니다. 많은 회사가 프로젝트를 오랫

동안 진행하면서 인력 교체를 겪곤 하게 되고, 새로 들어와서 적응해야 하는 프로그래머들에게는 힘든

도전과제이기도 합니다. 저희 회사라고 예외는 아닙니다.


프로젝트에 적응하기 위해서는 일단 프로그램이 돌아가고 있는 상태를 유지하는 것이 중요합니다.

프로그램이 실행되고 있는 상태에서는 그것을 관찰하고, 그 내부에 크게 행동을 바꾸지 않을 장치

(디버그용 로그를 추가하는 것 등)를 덧붙이는 것을 통해서 많은 정보를 얻어낼 수 있으며, 이것은

프로그램이 돌아가지 않는 상태에서 얻을 수 있는 정보에 비해 훨씬 정확하고 중요한 정보들입니다.

프로그램을 갈아엎어서 깔끔한 구조로 만들어야 겠다라는 충동이 들어 손을 대기 시작하고 프로그램이

실행안되기 시작하면 프로그래머가 얻을 수 있는 정보의 양이 훨씬 줄어들기 시작합니다.


또 하나, 리팩토링의 목적이 무엇인가에 대해서 스스로에게 생각해볼 필요가 있습니다. 단지 내 습관

에 부응하는 코드를 만드는 것이 리팩토링이 되어선 안되겠지요. 단순히 코드의 양을 줄이는 것이

목표가 되어서도 곤란합니다. 코드의 중복이 있다고 해서 그걸 줄이기 위해 서로 다른 모듈들을 대통합

하는 작업을 하다보면, 코드의 양이 줄어든 댓가로, 모듈간의 수평적, 수직적 의존도가 강해져버리는

훨씬 더 큰 댓가를 치루게 될 수도 있습니다.

예를 들어서 GE의 그래픽 엔진에는 여러가지 서로 다른 컴포넌트들이 제각각 섞여있었습니다. 화면에

모델을 찍는 데에는 emfx 에서 나온 메쉬들을 내부적인 옥트리를 이용해 관리했고, 나무를 찍을 때에는

스피드 트리 라이브러리를 통해 렌더링을 했고, 이펙트를 찍기 위해선 전 프로그래머가 어디선가 가져온

파티클 렌더링 소스를 사용했었는데, 이런 각각의 요소들은 통합적으로 관리되지 못하고, 제각각의

매니저와 씬 그래프 구조를 갖고 있었습니다 (모델=옥트리, 나무=쿼드트리, 이펙트=선형 검색)


이것을 하나로 통합해봐야겠다라고 생각해서, 대통합작업을 했습니다만, 그 결과는 더욱 복잡해진

의존관계라는 부작용이 생기게 되었습니다. 스피드트리나 emfx가 새버전이 나왔다고 해서 고치고

싶어도 다른 부분까지 더 얽혀들어가게 되어버린 것입니다.


그럼 리팩토링의 진짜 목적은, 나 혼자뿐만이 아니라, 여럿이 공감하고 실질적인 장점을 가져올 수 있는

목적은 무엇이 되어야 할까요? 지금의 저는 소프트웨어가 얼마나 테스트 가능(testable) 하게 되었는가

를 기준으로 해야 한다고 생각하고 있습니다. gpg의 쓰레드에 등장한 매우 복잡한 프로그램의 경우

예상컨데, 프로그램 전체를 실행시켜서 잘 돌아가나 테스트 하는 것 외에는 이 프로그램이 잘 돌아가는지

잘 안 돌아가는지 테스트할 방법이 없을 것입니다. 만약에 프로그램 전체가 아니라 일부분만 실행을

시켜서 잘 돌아가는지 아닌지를 알 수 있게 해줄 테스트가 다수 존재한다면 검증과 이해, 유지보수를

비롯한 작업은 훨씬 편해질 것입니다.


예를 들어 프로그램 전체 소스중 ui에 관련된 부분만 따로 테스트할 수 있는 테스트가 빌드 가능하고

실행 가능하다면, ui에 관련된 디버깅이나 최적화, 기능추가가 훨씬 간단해질 것입니다. 물론 그렇게

하기 위해선 ui쪽 코드를 다른쪽 메인로직 코드들과 분리해내는 작업이 필요하겠죠. 이 작업이 복잡해

보인다면, 좀 더 간단한 부분부터 접근해 봐야 할 것입니다.

예전에 한참 개발중이던 ge의 그래픽 엔진에는 실행하다보면 메모리가 새는 문제점이 있었습니다. 이걸

해결하기 위해서 방대한 소스를 들여다보고 고생을 많이 했었는데, 그러다 문득 떠오른 생각이 프로그램

메인루프 전체를 실행시키지 말고, 한번 프로그램을 초기화하자마자 종료시키는 시험을 해 보기로 했었

습니다. 작업은 간단히 메인 프로그램의 메인루프 진입부분을 주석처리 하는 것이었는데, 이렇게 하는

것 만으로도, 많은 문제점들이 한번에 노출된 것을 확인할 수 있었습니다. 초기화와 종료 처리가 제대로

짝이 맞지 않고 있었던 것을 시작으로, 초기화 처리가 메인루프와 제대로 분리가 안 되어있었다는 점과

메인 루프를 한번이라도 가지 않으면 종료처리에서 다운이 일어나는 등의 구조적인 문제점들이 한번에

드러나게 된 것이었습니다. 사실 이걸 고치는 것은 별로 어려운 작업이 아니었습니다만, 지금까지 왜

이런 식으로 살펴볼 생각을 안 했을까? 하는 생각이 들게 되었고, 테스트 중심의 프로그램 디자인 방식이

이런 문제를 초기부터, 혹은 중간에라도 예방하고 발견할 수 있는 방법이구나하고 느끼게 된 것입니다.

프로그램의 메인루프를 빼버린 순간, 프로그램은 '각종 라이브러리의 초기화/종료 테스트' 로 변화한 것

이었습니다.


다시 요약하자면

1. 돌아가는 프로그램은 일단 그대로 둔다
2. 다른 팀원들과 의논해서 어떤 테스트부터 만들면 좋을지 의논한다. (간단한 것부터)
3. 간단한 테스트를 만들 수 있도록 조금씩 돌아가는 프로그램에서 모듈간 분리를 시도한다
   - 이 과정에서 메인 프로그램을 조금이라도 변경한 후, 뭔가 망치지 않았는지 확인하려면 QA팀의
     도움을 받을 필요가 있다. 프로그램이 테스트 가능하게 만들어지지 않은 댓가.
4. 테스트를 어떻게 만들어야 할지 감이 안잡힌다면, 손대지 않는다.
   - 테스트를 전제로 하지 않은 리팩토링은 의미가 없다.
   - 재사용가능한 코드 = 부분만 떼어다 사용할 수 있는 코드 = 부분만 테스트 할 수 있는 코드

imcgames 의 김학규입니다