글
이클립스를 통해서 본 Refactoring (Bad Smell & JUnit)
프로그램 개발
2008/07/06 19:16
리팩토링은 어떤 코드에 하는 것일까? 여기에 대해서 리팩토링이라는 책을 지은 Martin Fowler 아저씨는 나쁜냄새라는 개념을 소개했다. (코드에서 냄새가 난다는 이분의 센스도 좀 재미있긴하다.;;; 유명한 개발자들의 책을 보면 은근히 농담처럼 소개하는 이야기에 뼈가 있는 경우가 많은 것 같다.)
나쁜냄새는 보통 아래의 경우들을 말하는데 정리해서 보는 것이 처음일 뿐이지 사실 프로그램을 작성하다 보면 정말 안좋다고 생각했던 것들이 상당히 많다.
자 그러면 리팩토링을 하면서 항상 해주어야할 테스팅에 대한 이야기를 해보자, 흔히 TDD 라는 단어를 들어보았을 것이다. (지인들과 이 단어를 바꾸어서 MDD 라는 말로 농담처럼 말하면 놀았다. Mouth-Driven-Development ;;;)
리팩토링이라는 것이 이미 기존에 존재하는 잘돌아가는 프로그램을 소스코드를 바꾸면서 외견상 아무른 변화가 없게 만드는 작업이 우선이다. 바뀌는 것은 바로 소스 코드에 한정되어야 한다는 점이 매우 중요하다. 이를 위해서 한개의 리팩토링을 하면 해당 부분의 기능을 테스트하는 것이 매우 중요하다. 이를 해결하는 가장 좋은 방식을 테스트 자체를 자동화해서 테스트 프로그램 자체가 스스로 결과를 확인하게 만드는 것이다. 무언가 엄청 복잡해 보이고 귀찮아 보이지만.... 우리의 강력한 후원자 이클립스는 이 과정을 단순화해준다.
상기의 예제는 리팩토링 책에 제시된 예제일뿐이고, 실재로는 테스트 하려는 클래스에대해서 JUnit 프로젝트를 생성하는 자동화된 기능을 이클립스가 제공하며, 사용자가 해줄 일은 셋업코드와 종결코드를 작성하고 해당 모듈에 대한 테스트 내역만 작성해주고, JUnit 프로젝트만 실행하면 이클립스가 알아서 테스트하고 결과를 알려준다. 이런 식으로 테스트 셋을 만들어두면 리팩토링을 할때마다 일일이 프로그래머가 직접 테스트할 필요없이 테스트가 가능하도록 만드는 것이 가능하다.
나쁜냄새는 보통 아래의 경우들을 말하는데 정리해서 보는 것이 처음일 뿐이지 사실 프로그램을 작성하다 보면 정말 안좋다고 생각했던 것들이 상당히 많다.
중복된 코드(Deplicated Code)
이 경우에 가장 먼저 생각할 수 있는 중복된 코드 부분을 Extract Method 를 이용해서 메소드로 분리해 낸뒤에 중복된 부분에서 해당 메소드를 호출하는 방식을 사용할 수 있다. 그리고 이런 중복 코드가 만약 공통의 부모 클래스를 갖는 자식 클래스에서 발생했다면 추출된 메소드를 Pull up method 를 이용해서 부모 클래스로 올리는 방법을 사용할 수 있다. 책에는 몇가지 다른 경우를 소개하지만 대충 이정도만 기억해도 크게 문제는 없으리라고 생각된다.
긴 메소드(Long Method)
초창기 컴파일러의 경우 함수 호출에서 발생하는 오버헤드 자체가 어느정도 존재했기 때문에 짧은 메소드를 지양하여 프로그램을 개발한 경우가 많았다고 한다. 하지만, 당시의 프로그래머들도 긴 메소드는 분명히 가독성이 떨어진다는 사실을 알고 있었다고 한다. 중요한건 최근의 컴파일러들은 이런 호출에서 발생하는 오버헤드를 기술적인 면에서 상당 부분 제거했기 때문에 최대한 짧은 메소드를 여러개 만들어서 프로그램의 메소드 내부를 간결하게 유지하는게 좋은 코드라는 설명이다. 여기서 주의할 점은 여러개의 메소드를 사용하다 보니 메소드의 이름을 명확하게 짓지 못하면 오히려 코드를 알아보기 더 힘들다는 단점이 있다는 점이다.
이런 긴 메소드를 리팩토링하면서 사용할 수 있는 방법은 Extract Method 가 기본이며, 해당 메소드 콜에서 전달돼는 파라메티가 너무 많아서 문제가 돼는 경우에 파라미터를 최소화 하기 위해서 Replace temp with query, introduce parameter object, Preserve whole object, Replace method with method object 같은 방법을 사용한다고 한다. (-_-;; 그냥 메소드 추출한다고 기억하자 ㅋㅋ)
거대한 클래스(Large Class)
보통 단일 클래스가 커다란 경우의 공통점이라면 굉장히 많은 멤버 변수와 중복되는 코드가 많다라는 점이라고 할 수 있다. 의도하지 않았지면 결과적으로 나온 코드가 중복이 많다는 점인데 이런 경우 비슷한 용도로 사용되는 멤버 변수를 모에서 해당 변수와 연관된 메소드를 한개의 클래스로 묶어서 Extract class 하는 방법을 사용할 수 잇고, 만약 이렇게 추출된 클래스가 기존의 클래스에 자식 클래스의 관계로 묶을 수 있다면 Extract subclass 를 이용해서 나쁜 냄새를 없앨 수 있다.
긴 파라미터 리스트(Long Parameter List)
OOP 언어에서는 보통 객체에 필요한 파라미터를 물을 수 있다라는 장점때문에, 메소드 자체에 파라미터를 넘겨주는 숫자는 상당히 적은 편이다. 하지만, 분명히 프로그램을 작성하다보면 필요이상으로 파라미터 리스트가 길어지는 경우가 있고, 이런 냄새를 없애기 위해서는 Replace parameter with method, Preserve Whole Object, Introduce Parameter Object 로 파라미터를 단순화시키는 방법을 사용해야한다.
확산적 변경(Divergent Change)
보통 좋은 소프트웨어를 OOP에서 말할때는 수정이 용이한 소프트웨어를 말한다. 확산적 변경이란 기능에 대한 변경이 한개의 클래스에 잣은 수정이 많이 발생하는 다수의 메소드가 존재하는 경우를 말한다. 이런 경우 기능변경에 대해서 수정이 일어나는 부분의 코드를 모아서 한개의 클래스로 묶고서 해당 기능의 변경이 발생할때 한개의 단일 클래스를 변경하도록 만들어주는 것이 현명할 것이다. 이경우 Extract class 를 이용할 수 있다.
산탄총 수술(Shotgun Surgery)
이는 확산적 변경과 유사한 냄새이지만, 약간 다른 점이라면 확산적 변경이 보통 변경사항에 대해서 한개의 클래스에 변경이 잣은 경우를 말한다면 산탄총 수술의 경우라면 변경사항이 발생할 경우 프로그램의 다양한 부분에서 많은 수정이 필요한 경우를 말한다. 이런 경우에는 Move method, Move field 를 사용하여 한개의 클래스로 변경이 필요한 부분을 몰아넣거나 적당한 후보 클래스가 없을 경우 새로운 클래스를 만들어서 해당 변경 부분을 한곳으로 몰아 넣는 것이 좋다. 확산적 변경이나, 산탄총 수술이나 현상은 약간 씩 다를지 모르겠지만 결론적으로 요구사항의 변경에 대한 소스 코드의 수정을 최대한 근접한 곳에서 해결할 수 있도록 만드는 것이 중요하다.
기능에 대한 욕심(Feture Envy)
기능에 대한 욕심이란 것은 클래스 내의 메소드가 자신이 속한 클래스의 데이터 보다는 다른 클래스에 속한 데이터를 다루는데 더 관심이 많은 경우를 말한다. 처음 클래스를 배울때는 분명 클래스라는 것이 객체를 표현한 것이고, 이런 객체의 속성을 변경하는 등 필요한 작업을 하는 것을 메소드라고 공부는 하지만 정작 프로그램을 작성하다보면 메소드가 속한 클래스보다 다른 클래스의 속성을 다루는 일을 하는 메소드를 작성하는 경우가 많다. 이런 경우 욕심이 많은 메소드에 대해서 Move method 를 이용해서 해당 메소드가 관심이 많은 클래스로 위치를 변경하는 작업을 해주는 것이 좋다. (만약 그 욕심의 정도에 대한 경계가 애매모호한 경우라면 책에서는 메소드에서 사용하는 필드의 양을 가지고 판단하라고 추천한다.)
데이터 덩어리(Data Clump)
데이터 덩어리라는 것은 서로 다른 클래스에 존재하는 데이터들이 자주 같이 사용되는 경우를 보통 말한다. (혹은 파라미터 리스트에서 자주 같이 등장하는 녀석들?) 이런 녀석들은 같이 한데 묶어서 한개의 클래스로 만들어주는 것이 좋은 경우이다. 주로 Extract class 를 우선적으로 적용해 볼 수 있을 것이고, Introduce parameter object, Preserve whole object 와 같은 방법을 사용해서 파라미터 리스트를 단순하게 만들어주는 방법을 통해서 좋은 냄새를 풍기게 만들 수 있을 것이다.
기본 타입에 대한 강박관념(Primitive Obssession)
이 경우는 프로그램에서 기본 타입을 써서 데이터를 표현해야한다는 강박관념에서 벗어나라는 이야기이다. 만약 돈을 표현해야하는 데이터라면 이를 Money 클래스로 만들어서 표현하라는 것이다. 여기서 발생할 수 있는 조건문과 같은 상황에서의 문제는 Replace type code with subclass, Replace type code with state/strategy 같은 것을 이용해서 해결하라고 말한다.
Switch 문(Switch Statement)
OOP프로그램에서는 확실히 switch-case 문이 적게 사용돼긴 하지만 사용하는 곳이 종종 있고 이런 구문이 여러곳에서 사용될 경우에는 한개의 조건 추가를 위해서 소스의 여러부분을 수정할 필요가 있기 때문에 가능하면 polymorphism 을 이용해서 이런 상황에서의 switch-case 사용을 지양하라는 이야기이다.
평행 상속 구조 (Parallel Inheritance Hierarchies)
한 클래스의 서브 클래스르 작성할때, 다른 곳에서 서브 클래스를 모두 작성해야하는 경우를 말한다. 일반적인 해결방법은 한 쪽 클래스의 상속구조에서 다른 쪽의 상속 클래스를 참조하게 하는 방법으로 해결하라고 말한다. (정확한 경우를 모르겠다;;;-_-;; 일단 소개만;)
게으른 클래스 (Lazy Class)
최초의 설계에서는 반드시 필요했으나 작성을 하다보면 분명히 생각보다 적은 사용율을 가지는 클래스들이 존재할 것이고 이런 클래스는 계층에서 없애는 것이 도움이 되는 경우가 많다. Collapse Hierarchy 를 이용해서 과감하게 클래스를 삭제하는 것이 냄새를 없애는데 도움이 된다.
추측성 일반화(Speculative Generality)
미래의 언제가 발생할 지도 모르는 상황을 처리하기 위해서 많은 코드, 클래스를 추가해서 코드의 가독성이나 유지보수를 힘들게 하는 경우를 말한다. 이 경우 해당 코드를 삭제하는 것이 올바른 개발이라고 할 수 있다.
임시 필드(Temporary Field)
객체안의 필드가 특정한 상황에서만 사용되는 경우를 말한다. 보통 알고리즘의 구현 코드에서 사용되는 임시변수가 이런 냄새를 풍기는 경우가 많고, 이를 없애기 위해서는 해당 변수를 Extract class 를 이용해서 묶어주는 것이 좋다.
메시지 체인 (Message Chains)
메시지 체인은 특정 객체를 얻기위해서 다양한 참조의 단계를 거쳐야 하는 경우에 나는 냄새를 말한다. 이런 경우 해당 참조의 단계 부분을 한개의 메소드를 묶어버리는 Extract method를 이용해서 정리하는 것이 좋다.
미들 맨(Middle Man)
메소드 호출시 캡슐화라는 특징으로 인해서 위임이 지나치는 경우에 발생하는 냄새를 말한다. 이런 경우 Remove middle man 을 이용해서 객체에서 실재로 발생하는 작업을 알 필요가 있다. 그리고 호출이 많이 되는 메소드의 작업이 많지 않은 경우라는 Inline method 를 이용해서 해당 메소드의 호출 부분을 전개하는 방법을 취할 수 잇다.
주석 (Comments)
주석 자체의 냄새라기 보다는 좋지 못한 코드를 숨기기 위한 주석을 지양하자는 말로 이해하자. 구문 구문의 주석을 달아야하는 경우라면 해당 구문을 묶어서 적당한 이름을 가진 메소드로 구성할 수는 없는지를 고민하는 것이 더욱 좋은 소스를 만들 수 있도록 해줄 것이다.
이 밖에도 부적절한 친밀 (Inappropriate Intimacy), 다른 인터페이스를 가진 데체 클래스 (Alternative Classes with Different Interface), 불완전한 라이브러리 클래스 (Incomplete Library Class), 데이터 클래스 (Data Class), 거부된 유산(Refused Bequest) 같은 내용이 책에 소개돼지만... 책보기도 짜증나고... -_-;; 대충 이정도 소개만 하고 그칠까 한다. 사실 내용이 어렵게 보이는 기는 해도 많은 경우가 간단한 게임정도만 작성해도 흔하게 마주치는 상황인 경우가 많아서 그때 그때 찾아보는 것이 좋지 않을까 생각한다. 리팩토링의 가장 좋은 샘플은 기존에 본인이 작성한 프로그램이라고 생각한다. -_-;;;
이 경우에 가장 먼저 생각할 수 있는 중복된 코드 부분을 Extract Method 를 이용해서 메소드로 분리해 낸뒤에 중복된 부분에서 해당 메소드를 호출하는 방식을 사용할 수 있다. 그리고 이런 중복 코드가 만약 공통의 부모 클래스를 갖는 자식 클래스에서 발생했다면 추출된 메소드를 Pull up method 를 이용해서 부모 클래스로 올리는 방법을 사용할 수 있다. 책에는 몇가지 다른 경우를 소개하지만 대충 이정도만 기억해도 크게 문제는 없으리라고 생각된다.
긴 메소드(Long Method)
초창기 컴파일러의 경우 함수 호출에서 발생하는 오버헤드 자체가 어느정도 존재했기 때문에 짧은 메소드를 지양하여 프로그램을 개발한 경우가 많았다고 한다. 하지만, 당시의 프로그래머들도 긴 메소드는 분명히 가독성이 떨어진다는 사실을 알고 있었다고 한다. 중요한건 최근의 컴파일러들은 이런 호출에서 발생하는 오버헤드를 기술적인 면에서 상당 부분 제거했기 때문에 최대한 짧은 메소드를 여러개 만들어서 프로그램의 메소드 내부를 간결하게 유지하는게 좋은 코드라는 설명이다. 여기서 주의할 점은 여러개의 메소드를 사용하다 보니 메소드의 이름을 명확하게 짓지 못하면 오히려 코드를 알아보기 더 힘들다는 단점이 있다는 점이다.
이런 긴 메소드를 리팩토링하면서 사용할 수 있는 방법은 Extract Method 가 기본이며, 해당 메소드 콜에서 전달돼는 파라메티가 너무 많아서 문제가 돼는 경우에 파라미터를 최소화 하기 위해서 Replace temp with query, introduce parameter object, Preserve whole object, Replace method with method object 같은 방법을 사용한다고 한다. (-_-;; 그냥 메소드 추출한다고 기억하자 ㅋㅋ)
거대한 클래스(Large Class)
보통 단일 클래스가 커다란 경우의 공통점이라면 굉장히 많은 멤버 변수와 중복되는 코드가 많다라는 점이라고 할 수 있다. 의도하지 않았지면 결과적으로 나온 코드가 중복이 많다는 점인데 이런 경우 비슷한 용도로 사용되는 멤버 변수를 모에서 해당 변수와 연관된 메소드를 한개의 클래스로 묶어서 Extract class 하는 방법을 사용할 수 잇고, 만약 이렇게 추출된 클래스가 기존의 클래스에 자식 클래스의 관계로 묶을 수 있다면 Extract subclass 를 이용해서 나쁜 냄새를 없앨 수 있다.
긴 파라미터 리스트(Long Parameter List)
OOP 언어에서는 보통 객체에 필요한 파라미터를 물을 수 있다라는 장점때문에, 메소드 자체에 파라미터를 넘겨주는 숫자는 상당히 적은 편이다. 하지만, 분명히 프로그램을 작성하다보면 필요이상으로 파라미터 리스트가 길어지는 경우가 있고, 이런 냄새를 없애기 위해서는 Replace parameter with method, Preserve Whole Object, Introduce Parameter Object 로 파라미터를 단순화시키는 방법을 사용해야한다.
확산적 변경(Divergent Change)
보통 좋은 소프트웨어를 OOP에서 말할때는 수정이 용이한 소프트웨어를 말한다. 확산적 변경이란 기능에 대한 변경이 한개의 클래스에 잣은 수정이 많이 발생하는 다수의 메소드가 존재하는 경우를 말한다. 이런 경우 기능변경에 대해서 수정이 일어나는 부분의 코드를 모아서 한개의 클래스로 묶고서 해당 기능의 변경이 발생할때 한개의 단일 클래스를 변경하도록 만들어주는 것이 현명할 것이다. 이경우 Extract class 를 이용할 수 있다.
산탄총 수술(Shotgun Surgery)
이는 확산적 변경과 유사한 냄새이지만, 약간 다른 점이라면 확산적 변경이 보통 변경사항에 대해서 한개의 클래스에 변경이 잣은 경우를 말한다면 산탄총 수술의 경우라면 변경사항이 발생할 경우 프로그램의 다양한 부분에서 많은 수정이 필요한 경우를 말한다. 이런 경우에는 Move method, Move field 를 사용하여 한개의 클래스로 변경이 필요한 부분을 몰아넣거나 적당한 후보 클래스가 없을 경우 새로운 클래스를 만들어서 해당 변경 부분을 한곳으로 몰아 넣는 것이 좋다. 확산적 변경이나, 산탄총 수술이나 현상은 약간 씩 다를지 모르겠지만 결론적으로 요구사항의 변경에 대한 소스 코드의 수정을 최대한 근접한 곳에서 해결할 수 있도록 만드는 것이 중요하다.
기능에 대한 욕심(Feture Envy)
기능에 대한 욕심이란 것은 클래스 내의 메소드가 자신이 속한 클래스의 데이터 보다는 다른 클래스에 속한 데이터를 다루는데 더 관심이 많은 경우를 말한다. 처음 클래스를 배울때는 분명 클래스라는 것이 객체를 표현한 것이고, 이런 객체의 속성을 변경하는 등 필요한 작업을 하는 것을 메소드라고 공부는 하지만 정작 프로그램을 작성하다보면 메소드가 속한 클래스보다 다른 클래스의 속성을 다루는 일을 하는 메소드를 작성하는 경우가 많다. 이런 경우 욕심이 많은 메소드에 대해서 Move method 를 이용해서 해당 메소드가 관심이 많은 클래스로 위치를 변경하는 작업을 해주는 것이 좋다. (만약 그 욕심의 정도에 대한 경계가 애매모호한 경우라면 책에서는 메소드에서 사용하는 필드의 양을 가지고 판단하라고 추천한다.)
데이터 덩어리(Data Clump)
데이터 덩어리라는 것은 서로 다른 클래스에 존재하는 데이터들이 자주 같이 사용되는 경우를 보통 말한다. (혹은 파라미터 리스트에서 자주 같이 등장하는 녀석들?) 이런 녀석들은 같이 한데 묶어서 한개의 클래스로 만들어주는 것이 좋은 경우이다. 주로 Extract class 를 우선적으로 적용해 볼 수 있을 것이고, Introduce parameter object, Preserve whole object 와 같은 방법을 사용해서 파라미터 리스트를 단순하게 만들어주는 방법을 통해서 좋은 냄새를 풍기게 만들 수 있을 것이다.
기본 타입에 대한 강박관념(Primitive Obssession)
이 경우는 프로그램에서 기본 타입을 써서 데이터를 표현해야한다는 강박관념에서 벗어나라는 이야기이다. 만약 돈을 표현해야하는 데이터라면 이를 Money 클래스로 만들어서 표현하라는 것이다. 여기서 발생할 수 있는 조건문과 같은 상황에서의 문제는 Replace type code with subclass, Replace type code with state/strategy 같은 것을 이용해서 해결하라고 말한다.
Switch 문(Switch Statement)
OOP프로그램에서는 확실히 switch-case 문이 적게 사용돼긴 하지만 사용하는 곳이 종종 있고 이런 구문이 여러곳에서 사용될 경우에는 한개의 조건 추가를 위해서 소스의 여러부분을 수정할 필요가 있기 때문에 가능하면 polymorphism 을 이용해서 이런 상황에서의 switch-case 사용을 지양하라는 이야기이다.
평행 상속 구조 (Parallel Inheritance Hierarchies)
한 클래스의 서브 클래스르 작성할때, 다른 곳에서 서브 클래스를 모두 작성해야하는 경우를 말한다. 일반적인 해결방법은 한 쪽 클래스의 상속구조에서 다른 쪽의 상속 클래스를 참조하게 하는 방법으로 해결하라고 말한다. (정확한 경우를 모르겠다;;;-_-;; 일단 소개만;)
게으른 클래스 (Lazy Class)
최초의 설계에서는 반드시 필요했으나 작성을 하다보면 분명히 생각보다 적은 사용율을 가지는 클래스들이 존재할 것이고 이런 클래스는 계층에서 없애는 것이 도움이 되는 경우가 많다. Collapse Hierarchy 를 이용해서 과감하게 클래스를 삭제하는 것이 냄새를 없애는데 도움이 된다.
추측성 일반화(Speculative Generality)
미래의 언제가 발생할 지도 모르는 상황을 처리하기 위해서 많은 코드, 클래스를 추가해서 코드의 가독성이나 유지보수를 힘들게 하는 경우를 말한다. 이 경우 해당 코드를 삭제하는 것이 올바른 개발이라고 할 수 있다.
임시 필드(Temporary Field)
객체안의 필드가 특정한 상황에서만 사용되는 경우를 말한다. 보통 알고리즘의 구현 코드에서 사용되는 임시변수가 이런 냄새를 풍기는 경우가 많고, 이를 없애기 위해서는 해당 변수를 Extract class 를 이용해서 묶어주는 것이 좋다.
메시지 체인 (Message Chains)
메시지 체인은 특정 객체를 얻기위해서 다양한 참조의 단계를 거쳐야 하는 경우에 나는 냄새를 말한다. 이런 경우 해당 참조의 단계 부분을 한개의 메소드를 묶어버리는 Extract method를 이용해서 정리하는 것이 좋다.
미들 맨(Middle Man)
메소드 호출시 캡슐화라는 특징으로 인해서 위임이 지나치는 경우에 발생하는 냄새를 말한다. 이런 경우 Remove middle man 을 이용해서 객체에서 실재로 발생하는 작업을 알 필요가 있다. 그리고 호출이 많이 되는 메소드의 작업이 많지 않은 경우라는 Inline method 를 이용해서 해당 메소드의 호출 부분을 전개하는 방법을 취할 수 잇다.
주석 (Comments)
주석 자체의 냄새라기 보다는 좋지 못한 코드를 숨기기 위한 주석을 지양하자는 말로 이해하자. 구문 구문의 주석을 달아야하는 경우라면 해당 구문을 묶어서 적당한 이름을 가진 메소드로 구성할 수는 없는지를 고민하는 것이 더욱 좋은 소스를 만들 수 있도록 해줄 것이다.
이 밖에도 부적절한 친밀 (Inappropriate Intimacy), 다른 인터페이스를 가진 데체 클래스 (Alternative Classes with Different Interface), 불완전한 라이브러리 클래스 (Incomplete Library Class), 데이터 클래스 (Data Class), 거부된 유산(Refused Bequest) 같은 내용이 책에 소개돼지만... 책보기도 짜증나고... -_-;; 대충 이정도 소개만 하고 그칠까 한다. 사실 내용이 어렵게 보이는 기는 해도 많은 경우가 간단한 게임정도만 작성해도 흔하게 마주치는 상황인 경우가 많아서 그때 그때 찾아보는 것이 좋지 않을까 생각한다. 리팩토링의 가장 좋은 샘플은 기존에 본인이 작성한 프로그램이라고 생각한다. -_-;;;
자 그러면 리팩토링을 하면서 항상 해주어야할 테스팅에 대한 이야기를 해보자, 흔히 TDD 라는 단어를 들어보았을 것이다. (지인들과 이 단어를 바꾸어서 MDD 라는 말로 농담처럼 말하면 놀았다. Mouth-Driven-Development ;;;)
리팩토링이라는 것이 이미 기존에 존재하는 잘돌아가는 프로그램을 소스코드를 바꾸면서 외견상 아무른 변화가 없게 만드는 작업이 우선이다. 바뀌는 것은 바로 소스 코드에 한정되어야 한다는 점이 매우 중요하다. 이를 위해서 한개의 리팩토링을 하면 해당 부분의 기능을 테스트하는 것이 매우 중요하다. 이를 해결하는 가장 좋은 방식을 테스트 자체를 자동화해서 테스트 프로그램 자체가 스스로 결과를 확인하게 만드는 것이다. 무언가 엄청 복잡해 보이고 귀찮아 보이지만.... 우리의 강력한 후원자 이클립스는 이 과정을 단순화해준다.
JUnit Frmk가 알아서 결과를 테스트하고 이를 확인해서 프로그래머에게 알려준다. (녹색 막대가 인상적) | JUnit Frmk가 알아서 결과를 테스트하고 이를 확인해서 프로그래머에게 알려준다. (붉은 막대가 인상적) |
상기의 예제는 리팩토링 책에 제시된 예제일뿐이고, 실재로는 테스트 하려는 클래스에대해서 JUnit 프로젝트를 생성하는 자동화된 기능을 이클립스가 제공하며, 사용자가 해줄 일은 셋업코드와 종결코드를 작성하고 해당 모듈에 대한 테스트 내역만 작성해주고, JUnit 프로젝트만 실행하면 이클립스가 알아서 테스트하고 결과를 알려준다. 이런 식으로 테스트 셋을 만들어두면 리팩토링을 할때마다 일일이 프로그래머가 직접 테스트할 필요없이 테스트가 가능하도록 만드는 것이 가능하다.
Refactoring.rar