본문 바로가기

리팩토링(마틴 파울러)

리팩토링 2장 : 리팩토링의 원리

1장은 예제라도 있어서 힘겹게 소화했지만, 2장은 저명한 개발자의 경험을 통해서 리팩토링의 효과와 한계를 들여다보는 내용이기에, 나같은 초보자는 더 이해가 어려웠다. 

 

구체적인 이야기들은 아니기에 아는 만큼 보이는 내용일거라고 생각한다. 나는 내가 이해하고 있는 것에 확신이 없어서 일단 책에 쓰인 내용 그대로를 옮겨 정리할 것이다.

 


 리팩토링의 정의 

  • 소프트웨어를

  • 쉽게 이해할 수 있고,

  • 적은 비용으로 수정할 수 있도록

  • 겉으로 보이는 동작의 변화 없이

  • 내부구조를 변경하는 것

리팩토링은 단순히 코드를 깔끔하게 하기 위해 보다 효율적이고 통제된 방법을 제공한다.

중요한 점은 리팩토링은 소프트웨어를 더 쉽게 이해할 수 있도록 바꾸기 때문에 그 과정만으로 코드에 대한 더 깊은 이해를 돕는다. 이와 대조되는 것은 퍼포먼스 최적화로 필요한 퍼포먼스를 위해 종종 코드를 더 이해하기 어렵게 만들기도 한다.

 

두 개의 모자

Kent Beck은 '두개의 모자' 라는 은유를 통해 프로그래밍 과정을 리팩토링과 기능 추가로 분리한다. 기능을 추가할 때는 기존 코드를 건드리지 않고, 새로운 기능 추가(테스트 추가도 동반된다.)에만 집중하며, 리팩토링을 할 때는 기능을 추가해선 안되고 코드의 구조에만 신경 써야한다. 물론 꼭 필요한 테스트 이외의 테스트 추가는 하지 않는다. 프로그래밍 과정은 이렇게 두 개의 모자를 바꿔 써가며 번갈아 진행되는 데, 적어도 자기가 프로그래밍 중에 어떤 모자를 쓰고 있는 지는 알고서 해야한다.

 

 

 


리팩토링을 해야하는 이유

1. 리팩토링은 소프트웨어의 디자인을 개선한다.

정기적인 리팩토링은 원래의 의도된 디자인이 망가지지 않고 유지되도록 도와준다.

디자인이 좋지 않으면 수정에 더 많은 비용이 들 수 밖에 없다. 같은 작업을 하는 코드가 여러 곳에 중복해서 있는 경우가 많기 때문이다. 물리적으로 코드가 늘어나면 이해하는 데 걸리는 시간이 늘어나고, 수정 과정도 복잡해진다. 리팩토링은 중복된 코드를 제거하고, 각 작업에 대한 코드가 오직 한 곳에만 있게 한다.

 

2. 리팩토링은 소프트웨어를 더 이해하기 쉽게 만든다.

프로그래밍은 우리가 원하는 것을 정확하게 말하는 것에 대한 모든 것이다. 이러한 형태의 소통을 하게 되는 대상은 컴퓨터 뿐만 아니라 코드를 보는 다른 개발자에게도 해당이 된다. 이 개발자들에게 이해가 쉽도록 코드를 짜지 않는다면 이들이 코드를 수정하는 데 더 많은 시간이 걸릴 수 밖에 없다. 리팩토링은 코드의 목적을 분명하게 함으로써 코드를 더 읽기 쉽게 만든다. 이는 다른 개발자 뿐만 아니라 개발하고 난 뒤에 오랜 시간이 지난 나 자신에게도 해당될 수 있는 이야기이다. 

 

3. 리팩토링은 버그를 찾도록 도와준다. 

코드를 잘 이해하게 되면 버그도 쉽게 찾을 수 있다. 코드를 리팩토링하면 코드가 무슨 작업을 하는 지 깊이 이해할 수 있게 되고 이 이해를 프로그램 구조에 곧바로 반영하게 되면 버그는 쉽게 눈에 보이는 것이다.

 

4. 리팩토링은 프로그램을 빨리 작성할 수 있게 도와준다.

언뜻 생각해보면 리팩토링은 품질 향상에 도움이 되지만 개발 속도에 도움이 되지 않는다고 느껴질 수 있으나, 오히려 리팩토링을 거치지 않으면 형편없는 디자인으로 인해 오히려 작업 속도가 느려지는 경우가 많다. 리팩토링은 디자인의 문제로 버그를 잡는 시간을 줄이고 기능을 추가하는 데에 집중할 시간을 제공한다. 

 

 


 리팩토링을 언제 해야하는 가? 

리팩토링을 위한 시간을 따로 정하지 말고, 틈틈히 계속 해야한다. 리팩토링은 그 자체를 목적으로 하는 것이 아니라. 다른 것을 하기 위해 하는 것이기 때문이다. 

 

* 삼진 규칙

첫번째 코딩을 할 때 아무 생각 없이 하다가, 두번째로 같은 코드를 작업하고 있을 때는 중복 때문에 주춤하지만 그냥 진행한다. 세번째로 같은 코드를 작업하면 그제서야 리팩토링을 하게 된다는 법칙이다.

 

기능을 추가할 때 리팩토링을 하라

  • 이 시점에 리팩토링을 하는 이유는 종종 수정해야할 코드에 대한 이해를 돕기 위해서이다. 해당 코드가 하는 일을 이해하기 위해서 코드를 리팩토링을 하는 것도 방법이란 것이다.
  • 또 다른 이유는 기존의 디자인이 기능 추가가 쉽지 않을 때이다. 쉬운 수정이 가능하게 리팩토링을 하는 것이 오히려 그냥 기존 디자인에 기능 추가를 하는 과정보다 더 빠를 수 있다.

버그를 수정할 때 리팩토링을 하라

버그가 있다는 것은 버그의 존재를 모를 정도로 코드가 명확하지 않았다는 것을 의미하므로 리팩토링의 필요하다는 신호이다. 

 

코드 검토를 할 때 리팩토링을 하라

코드 리뷰(code review)

  • 지식이 개발팀 전체로 확산되는 것을 돕는다.
  • 소프트웨어의 보다 많은 면을 보다 많은 사람이 이해하도록 돕는다. 
  • 팀원 전체가 명확한 코드를 작성하는 데도 매우 중요하다. 
  • 많은 사람들에게 유요한 아이디어를 제안할 기회를 준다. 

리팩토링이 코드 리뷰에 도움이 되는 이유

코드 리뷰 과정에서 리팩토링은 아이디어를 쉽게 구현할 수 있도록 도움으로써 코드 리뷰가 좀 더 구체적인 결과를 얻게 한다. 이 프로세스를 가능하게 하려면 검토 그룹이 한명의 검토자와 한명의 코드 원작자 정도로 인원이 작은 검토 그룹이어야 한다. 반면, 규모가 큰 디자인을 검토할 때는 큰 그룹에서 여러 의견을 얻는 것이 좋다. 이런 경우에는 코드 자체보다는 UML 다이어그램이나 CRC 카드로 시나리오를 검토하는 것을 선호한다. 

 

익스트림 프로그래밍(Extreme Programming)의 페어 프로그래밍(Pair Programming)

한 대의 컴퓨터에 두 명의 개발자가 작업한다. 그 효과로 코드 검토가 개발 프로세스에 포함되고 리팩토링 역시 프로세스에 포함된다.

 

 


 리팩토링을 할 때의 문제 

리팩토링이 제공하는 실익을 얻기 위해서는 리팩토링을 적용해야 한다고 확실하게 믿고 있으나 리팩토링에 어떤 한계가 있는지, 리팩토링이 야기할 문제점은 무엇인지 항상 감시하고 경계해야한다.

 

데이터 베이스

리팩토링이 문제가 되는 분야 중 하나는 데이터 베이스이다. 비즈니스 애플리케이션이 데이터 베이스 스키마*와 밀접하게 관련되어 있어 데이터 베이스의 변경을 어렵게 한다. 또한 스키마를 변경한 이후의 데이터 마이그레이션에 걸리는 시간과 위험성도 문제가 된다. 

더보기

*스키마란? 데이터베이스의 구조와 제약조건에 관해 전반적인 명세를 기술한 것

  • 개체의 특성을 나타내는 속성(Attribute)
  • 속성들의 집합으로 이루어진 개체(Entity)
  • 개체 사이에 존재하는 관계(Relation)에 대한 정의
  • 이들이 유지해야 할 제약조건

출처: https://jwprogramming.tistory.com/47 [개발자를 꿈꾸는 프로그래머]

*데이터 마이그레이션이란?

데이터 베이스 스키마의 버전을 관리하기 위한 하나의 방법(데이터 전환)이다. 

개발 시스템에는 데이터베이스 스키마가 변경되었지만, 운영시스템의 데이터베이스 스키마가 변경되지 않았을 경우 마이그레이션을 수행한다. 

 

출처: https://life-with-coding.tistory.com/68#:~:text=%EA%B0%9C%EB%B0%9C%20%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%97%90%EB%8A%94%20%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%20%EC%8A%A4%ED%82%A4%EB%A7%88,%EC%98%AE%EA%B8%B0%EB%8A%94%20%EA%B2%83%EA%B9%8C%EC%A7%80%20%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98%EC%9D%B4%EB%8B%A4.

객체지향 데이터 베이스가 아니라면 이 문제를 해결하기 위해 객체 모델과 데이터베이스 모델 사이에 별도의 소프트웨어 계층을 둔다. 이를 통해 변경을 각각의 모델에 한정할 수 있다. 이런 계층은 복잡성을 증가시키나 많은 융통성을 제공한다.

 

일부의 객체 데이터 베이스는 한 객체에서 다른 객체로의 자동 마이그레이션을 지원한다. 마이그레이션이 자동이 아닌 경우는 직접 해야하는데, 이 과정에서 클래스의 데이터 구조에 대한 변경에 주의해야한다. 객체의 동작보다 필드드를 옮기는 것에 특히 더 주의해야한다.

 

인터페이스 변경

객체지향에서는 인터페이스를 변경하지 않고, 객체의 내부 구현을 안전하게 바꿀 수 있지만, 인터페이스를 변경한다면 어떤 문제를 발생시킬지 알 수 없기에 인터페이스의 유지가 중요하다. 많은 리팩토링이 인터페이스를 변경하기 때문에 해당 사실은 리팩토링에 방해가 된다.

 

특히 객체의 캡슐화 개념과 밀접한 관련을 가지는 데, 변경하고자하는 메서드를 사용하는 모든 코드를 쉽게 찾을 수 없거나 변경할 수 없는 경우가 있어 리팩토링이 쉽지 않다는 것이다. 따라서 모든 곳에서 접근이 가능한 '공표된 인터페이스(published interface)'일수록 더이상 안전하게 인터페이스를 수정할 수 없다. 어디서 인터페이스를 사용하고 있을지 찾을 수 없기 떄문이다.

 

이런 경우에는 예전의 인터페이스와 변경된 인터페이스를 모두 유지해야한다. 예전의 인터페이스가 여전히 동작하여 새로운 인터페이스를 호출하도록 하면 된다. 대신 자바에서는 deprecation 기능을 사용해서 코드가 더이상 사용되어서는 안된다는 것을 표시한다. 이렇게 하면 예전 인터페이스를 사용하는 측에서 뭔가가 바뀌었다는 것을 알 수 있게 된다.

 

기존의 인터페이스를 유지하는 것은 고통스러운 일이기에, 꼭 필요한 것이 아니면 인터페이스를 공표하지 않는 것이 방법이 될 수 있다.

 

리팩토링이 어려운 디자인 변경 

리팩토링을 효율적으로 할 수 있는 상황도 있지만, 리팩토링이 어려운 상황도 종종 있다. 간단히 리팩토링을 할 수 있는 방법이 보이지 않는다면 디자인에 더 많은 노력을 기울일 수 밖에 없지만 이런 경우는 흔하지 않다.

 

언제 리팩토링을 하지 말아야하는가?

대표적인 예는 코드를 처음부터 다시 작성해야할 때이다. 기존 코드가 너무 엉망이라 리팩토링을 하는 것보다 처음부터 다시 시작하는 것이 더 쉬운 경우를 말한다.

 

또 현재의 코드가 애초에 작동조차 하지 않는 경우이다. 이 때도 리팩토링 보다는 코드의 재작성이 필요하며, 리팩토링을 하기 전에는 코드가 제대로 작동해야한다는 점을 기억해야한다.

 

처음부터 다시 코드를 짜야하는 이런 경우에는 하나의 절충법이 있는 데, 하나의 거대한 소프트웨어를 리팩토링해서 강력하게 캡슐화된 컴포넌트로 나눈 뒤에 각각의 것을 리팩토링할 것인지, 재작성을 할 것인지 결정하는 방법이다.

 

리팩토링을 피해야하는 또 다른 경우는 마감일에 가까울 때이다. 리팩토링을 통해 얻는 생산성 향상은 마감일이 지난 다음이기 떄문에 그때는 너무 늦는다. 그러나 마감일에 아주 가까울 때는 제외하고 항상 작업하는 데 시간이 빠듯하다고 느껴진다면 보통 리팩토링이 필요하다는 신호로 받아들여라.

 


리팩토링과 디자인

Upfront Design

프로그래밍을 하기 전에 미리 디자인에 대해 생각하는 방법론. 

디자인은 설계도를 그리는 것이고, 프로그래밍은 설계도대로 만드는 단순한 작업이라는 개념

 

 

리팩토링은 Upfront Design과 관점을 달리한다. 소프트웨어는 물리적 기계류가 아닌 생각에 대한 것이며 적응성이 있다. 디자인은 우리의 예상과는 달리 크고작은 헛점들이 존재한다. 

 

리팩토링이 Upfront Design을 완전히 대체할 수 있는지는 논란의 여지가 있지만, 사전의 디자인을 전혀 하지 않고, 일단 코딩한 뒤에 리팩토링을 한 성공적인 경우들도 많이 보았다. 그러나 리팩토링만을 사용하는 방법이 가장 효율적인 방법이 아닐 수 있다. 대부분의 개발자들은 아무리 리팩토링을 좋아하는 경우라도 최소한의 솔루션을 고안하고서야 코딩을 시작한다. 

 

중요한 점은 리팩토링은 사전 디자인의 역할을 바꾼다는 점이다. 리팩토링 없이는, 디자인은 처음부터 정확해야한다는 부담을 안는다. 추후의 변경을 견딜 수 있는 융통성 있는 솔루션을 찾아야만 하는 것이다. 그러나 융통성에 치중한 솔루션은 단순한 솔루션보다 복잡하고 유지보수가 힘들다. 또한 미래의 가능성을 위해서 융통성을 안고 가는 것이기에 필요한 만큼보다 더 많은 융통성을 갖는 경우가 많다. 리팩토링은 변경의 위험에 대한 접근을 달리한다. 단순한 솔루션을 융통성 있는 솔루션으로 리팩토링 하는 것은 쉽기 때문에 대부분의 경우 간단한 디자인으로 시작이 가능해지는 것이다. 리팩토링을 쉽게 할 수 있을 만큼 폭 넓은 감각을 가지면 융통성 있는 솔루션에 대해서 생각할 필요 조차 없는 것이다.

 


 리팩토링과 퍼포먼스 

리팩토링에 대한 공통의 관심사는 리팩토링이 프로그램의 퍼포먼스에 미치는 영향이다. 리팩토링은 종종 프로그램을 더 느리게 작동하도록 하는 원인이 되지만, 소프트웨어에 대한 튜닝을 더 쉽게할 수 있도록 한다.

 

리팩토링을 하면서 동시에 퍼포먼스를 최적화시킬 수 있는 프로세스는 다음과 같다.

  • 리팩토링을 통해 튜닝할 수 있는 소프트웨어를 만든다.
  • 그 후에 충분한 속도를 얻을 수 있도록 튜닝한다.

빠른 소프트웨어를 작성하기 위한 세가지 일반적인 접근 방법

  • 실시간 시스템에서 사용되는 시간 배분 방법 : 각 컴포넌트에 리소스(시간과 footprint)를 배분하고 그 할당된 배분을 초과하지 못하게 하는 방법. 이 기법은 심장박동 조정기와 같은 특별한 시스템에서는 매우 중요하나, 그 외에는 좀 과한 방법이라고 할 수 있다.
  • 퍼포먼스에 지속적으로 관심을 갖는 방법 : 가장 흔한 방법이며, 직관적인 장점이 있으나 실제로 잘 작동하지 않는 경우가 많다. 또한 개발을 할 때 퍼포먼스에 치중하면 프로그램을 작업하기가 더 어려워져 개발 속도를 느리게한다. 퍼포먼스 개선을 위한 코드는 프로그램 곳곳에 퍼져 각 코드들이 좁은 시각으로 짜여질 것이다. 또한 의외로 프로그램이 대부분의 시간을 코드의 작은 부분에서 보내기 때문에 별로 실행되지도 않는 코드를 최적화하는 경우가 대부분일 것이다. 
  • 프로그램을 잘 분해된 형태로 개발 후, 프로파일러로 퍼포먼스에 영향을 주는 일부에만 집중하는 방법 : 잘 분해된 형태로 프로그램을 개발하면 최적화 과정에서 퍼포먼스 튜닝에 보낼 수 있는 시간을 확보하게 되며, 퍼포먼스 분석을 할 때 더 세밀한 분석이 가능해진다. 따라서 프로파일러를 통해 큰 영향을 주는 작은 코드에만 집중을 할 수 있게 되어 앞의 방법보다 적은 노력으로 큰 효과를 얻을 수 있다. 

 


 리팩토링의 기원 

리팩토링의 중요성을 초기에 인식한 사람은 1980년 초부터 스몰토크*를 사용하고 있던 Ward Cunningham과 Kent Back이다. 스몰토크는 매우 동적인 환경으로 높은 수준의 기능을 가진 소프트웨어를 빠르게 작성할 수 있는 언어이다. 스몰토크에서는 컴파일-링크-실행 사이클이 아주 짧아 어떤 것을 빨리 변경하는 것이 아주 쉽다. 또한 객체지향 언어이기 대문에 추후의 수정에 대한 충격을 최소화할 수 있는 도구들을 갖고 있다. Ward와 Kent는 이런 환경에서 작업하는 데 최적화된 소트프웨어 개발 프로세스인 Extreme Programming을 개발했고, 이 프로세스의 생산성을 높이는 데 리팩토링이 중요한 역할을 한다는 것을 깨달았다. 그 이후로 프로세스  개량에 리팩토링을 적용해오고 있다. 

더보기

스몰토크는 객체지향 프로그래밍의 개념을 지원하기 위해 특별히 설계된 프로그래밍 언어로써 동적 형, 반영을 지원한다. 스몰토크는 제록스 파크(PARC)에서 앨런 케이, 댄 잉갤스, 테드 캘러, 에이들 골드버그 가 만들었다. 리스코, 로고, 스케치패드와 시뮬라 등의 영향을 받았으며, 스몰토크-80부터 외부에 공개되어 퍼지기 시작했다. 스몰토크는 루비, 오브젝티브-C 등의 프로그래밍 언어에 영향을 주었으며, 그 외에 매킨토시의 그래픽 사용자 인터페이스에도 영향을 주었다. 그러나 스몰토크는 C++과는 달리, 절차적 언어의 구문 상에 구현되지 않고, C언어의 절차적 구조를 일부 허용하는 C++ 보다 더욱 엄격하게 강화된 규칙이 적용된 순수한 객체 지향적 언어이다.

출처: https://gorm.tistory.com/305 [T.M.I]

스몰토크 계를 선도한 또 한 사람은 Gang of Four의 일원인 Ralph Johnson이다. Ralph는 효율적이고 융통성있는 프레임워크를 개발하는데 리팩토링이 어떻게 도움이 될 것인지를 탐구했다. 그의 박사과정 학생이었던 Bill Opdyke는 스몰토크뿐만 아니라 다른 많은 곳에 리팩토링을 적용할 수 있다는 것을 알았고, 툴빌더의 관점에서 리팩토링을 탐구했다. C++ 프레임 워크 개발에 도움이 될만한 리팩ㅌ룅을 검토하고, 의미 체계가 보존되는 리팩토링, 그리고 도구에서 이런 아이디어를 어떻게 구현할 것인지에 대한 연구를 진행했다. 

 

한편, John Brant와 Don Roberts는 리팩토링 도구에 대한 아이디어를 받아들여 스몰토크에서 사용할 수 있는 리팩토링 도구인 리팩토링 브라우저를 만들었다.