본문 바로가기
공부정리

마틴 파울러 - 리팩터링의 절차 (리팩터링의 중요성)

by 에드박 2023. 1. 11.

아래 영상을 보고 내용을 정리한 글입니다.

https://www.youtube.com/watch?v=mNPpfB8JSIU 

내용에 대한 원문 : https://martinfowler.com/articles/workflowsOfRefactoring/

 

리팩터링이 자주 필요한 순간은 언제인가? -> TDD 

Red, Green, Refactor 그림

개발을 진행할 때, 새로운 기능을 추가하고 싶다면 실패할 테스트를 추가한다. 

테스트가 빨간색으로 보일 때(실패할 때), 이 그림이 시작됨 (Red)

애플리케이션이 새로운 기능을 동작하도록 코드를 만들어서, 테스트를 초록색(성공)으로 만든다. (Green)

 

이 단계에서 개발자는 기능을 구현하는 것, 기능이 정상 동작하는 것이 우선이다.

이 기능이 큰 시스템에 어떻게 적용될 지는 깊게 고민하지 않는다.

 

세번째 단계는 Refactor 새로운 코드들이 들어왔으니, 깔끔하게 만들어야한다. (Refactor)

중복 코드를 없애도 나머지 코드들에 잘 녹아들도록 변경한다.

 

위 과정을 크게 보면 코드 짜는 단계는 2단계로 나눠진다

a. 기능을 만드는 것

b. 코드를 깔끔하게 만드는 것


리팩터링이란 무엇을 하는것인가?

리팩터링은 2개의 모자를 쓰는것에서 시작한다.

개발을 할 때 2가지 모드로 진행하는걸 기억해야 한다. - 켄트 백

이 때, 2개의 모자를 쓰고 개발을 한다고 생각해보자

1. 새로운 기능을 추가 모자 (Add Function)

  - 테스트가 성공하도록 개발

  - 모든 상황의 개발

  - TDD에만 국한되는 것이 아님,

  - 코드가 이전과 다른 새로운 기능을 하는 모든 행위

2. 리팩터링 모자 (Refactoring)

  - 새로운 기능을 만드는게 아님

  - 기능이 이전과 동일하게 동작하면서 개발을 진행하는 것

  - 소프트웨어 내부 설계를 더 높은 수준으로 진화시키는 것

 

일반적으로 리팩터링을 진행할 때는 계속해서 테스트 코드를 실행하게 됨

-> 리팩터링에서 실수가 나지 않기 위함

-> 리팩터링 도중 버그를 발견하더라도 고치면 안된다 ( 버그 Fix도 소프트웨어의 기능을 변경하는 행위 )

 

두 모자는 언제든지 바꿔 착용할 수 있지만, 두 개의 모자를 동시에 착용하면 안된다.

-> 리팩터링 작업 중 버그를 고치고 싶다면, 새로운 기능 추가 모자로 바꿔쓰고 기능을 고쳐나가야 한다.

 

개발자는 어떤 모자를 쓰고 있는지 항상 의식해야한다.

 

개발자의 그 외 다른 모드들

- 성능을 올리는 작업은 리팩터링과 유사하지만, 코드를 깔끔하게 만드는 것이 아니라 더 빠르게 만드는 것이 목적이다.
   즉, 성능을 올리는 작업과 리팩터링은 다른 모드(작업)이다.
- 탐험(스케치) 모드 = 아이디어를 코드에 녹이기 위해 이것저것 적용하고 잘 안되면 코드를 없애버리는 모드.

 

TDD 싸이클에서 2개의 모자를 번갈아 쓰며 코드를 수정하는 것을 알 수 있다.

  1. 새로운 기능을 추가하는 것
  2. 리팩터링 하는 것

왜 이렇게 나눠야 했는가?

각 모드에서 개발자가 어떤 자세를 가지고 개발을 해야하는지 알아야 한다.

리팩터링은 소프트웨어의 기능을 절대 변경하지 않도록 해야한다.

리팩터링을 조금 하고나서 테스트를 돌려서 기능에 이상이 없는지 확인하고

만약 이슈가 있다면 되돌려야 합니다.

실수를 했다면 다시 되돌리고, 다시 조그만 스탭으로 돌아가는 것이다.

중요한 건 위와 같이 작은 단위로 개선하는 것

 

이런 리듬의 개발 방식은 일반적인 기능 개발때는 흔치않은 방식이다.

기능 개발과 리팩터링은 전혀 다른 스타일로 개발을 진행한다.

 

리팩터링하는 개발 스타일에서

이렇게 TDD 사이클에서 리팩터링을 넣는 방식은 리팩터링 절차 중 한 가지라고 본다.

이것을 TDD 리팩터링이라고 부른다.

 

TDD에 리팩터링이 포함되기 때문

 

가장 전형적인 방식이라 볼 수 있음

TDD에서 리팩터링을 진행할 때 가장 흔히 보이는 모습


그 외 리팩터링 방식들

위에서 리팩터링 방법 중 가장 전형적인 방법 중 하나인 TDD 리팩터링을 설명했다.

(TDD 사이클에 리팩터링을 넣는것을 TDD 리팩터링이라고 부른다.)

 

이제 그 외의 리팩터링 방식들을 살펴보자

  • 쓰레기 줍기 리팩터링
  • 이해를 위한 리팩터링
  • 준비를 위한 리팩터링
  • 계획된 리팩터링
  • 장기간 리팩터링

쓰레기 줍기 리팩터링

요약
- 개발 중 또는 코드를 살펴보던 중 나쁜 코드를 발견했을 때, 코드를 깔끔하게 수정하는 것
   모든 코드가 깔끔하게 유지될 수 있도록 발견한 나쁜 코드를 그냥 두는 것이 아닌 리팩터링 하는 것

 

이미 다른 사람들이 개발한 코드를 보다가 나쁜 코드를 발견했을 때,

코드가 돌아가기만 한다면 바꿀 필요는 없다고 생각할 수 있다. (수정하면 안돌아갈 수도 있으니까!)

일부는 나쁜 코드를 봤을 때, 이 코드를 깨끗하게 만들어야겠다 라는 생각을 한다.

리팩터링은 싸이클에서 중요한 역할을 한다.

코드를 천천히 살펴보고 이 코드는 잘못됐어 라고 생각하고 바로 코드를 수정하는 것

이것이 개발자가 코드와 싸우는 기본적인 상황이다.

 

우리는 의식적으로 코드를 비판적으로 대한다. 코드를 비판적으로 보다 보면, 제대로 돌아가지 않는 코드에 대해 참을 수 없게 된다.

2달전에 잘 돌아가던 코드라도 지금 시점에서 봤을 때는 고쳐야한다 생각할지도 모른다.

이 때가 리팩터링의 기점이라 볼 수 있다.

코드가 잘 돌아가더라도 리팩터링을 해야겠다 라는 생각을 할 수 있다.

 

이미 작성된 코드를 볼 때 동작하는 코드들을 분리하고, 이 코드는 이상하다고 생각해서 그대로 두는 것이 아니라 고치려고 할 것이다.

이 때 행하는 리팩터링 방식을 `쓰레기 줍기 리팩터링` 이라고 한다.

항상 이전처럼 깨끗한 상태가 되도록 하기 위해서다.

 

굳이 모든 것을 깨끗하게 만들 필요는 없다.

할수 있는 만큼 하는 것이 더 중요하다. 개발자는 적어도 이전보다 나은 코드를 생산해야 한다.

아주 작은 개선사항이라도 좋다.

조금씩 지속해서 코드를 개선하다 보면 작은 수정이 모여 대부분의 코드가 깨끗해진다.

 

이해하기 위한 리팩터링

요약
- 코드가 무슨 동작을 하는지, 로직이 이해가 안될 때 수행하는 리팩터링 방식.
   머리 속에 파악이 끝난 로직을 코드에 깔끔하게 녹여내는 것
   다음 사람이 또 코드에 대해 파악하는 시간을 절약해준다.

 

코드를 보고 이해가 안될 때 , 코드가 무슨 동작을 하는지 추측이 안될 때

코드를 이해했더라도, 그대로 둔다면 자신의 머리 속에서만 코드를 이해하고 끝났다는 것이다.

이런 상태는 정보가 파편화 된 것, 다음 사람은 또 다시 코드에 대한 정보를 이해하기 위해 시간을 써야한다.

나머지 팀원을 이해시키기 위해서는 내용을 공유해야 한다.

우리가 할 일은 리팩터링을 수행해서 머리속에 로직을 코드에 녹여내는 것

코드를 만들고 수정하다보면 다음으로 이 코드를 보는 사람들은 이 코드가 어떻게 동작하는지 짧은 시간에 알아낼 수 있다.

 

복잡한 코드를 읽는것은 추리소설을 읽는것과 같다.

-> 하지만 코드를 기반으로 추리를 하는 건데, 좋은 상황은 아니다. (추리 혹은 추측으로 실제 spec 과 다를 수 있음)

그렇기 때문에 코드를 수정하고 깔끔하게 만들어야 함

이런 리팩터링 방식을 `이해하기 위한 리팩터링` 이라 한다.

 

쓰레기 줍기 리팩터링이해하기 위한 리팩터링 똑같은거 아닌가?

어떤 코드든 이해하기 힘든 코드는 일반적으로 나쁜 코드라고 한다.

하지만 두 개를 구분하는 약간의 작은 차이점이 있다.

 

이상한 코드를 봤을 때이해하기 어려운 코드를 봤을 때로 나뉜다.

(하지만 둘 다 나쁜 코드를 고친다는 점은 똑같다)

 

 

리팩터링 할 때, 큰 두 가지 방법

아래의 표를 보자

우리가 이해하지 못할 코드를 보거나 또는 나쁜 코드를 봤을 때, 우선 판달해야할 것은 `이걸 지금 당장 고쳐야 하는가?` 이다

 

우리가 리팩터링을 결정해야하는 시점에 새로운 기능을 추가해야할지도 모른다.

우선 결정해야 하는 것지금 해야하는 일에 영향을 받지 않고 쉽고 빠르게 고칠 수 있을것인가? 또는 고치는데 시간이 오래걸리지 않는지 판단해야 함 (메서드 이름 변경이나 변수명 변경이라면 금방 끝나버릴 것)

시간이 오래걸리지 않는다면 당장 시작해도 된다.

 

만약 우리가 Red 의 시점에, 즉 TDD에서 실패한 테스트를 만들고 있을 때

지금 당장 리팩터링을 결정한다면, 우선 이미 작성한 Red(실패) 상태의 테스트를 Green(성공)으로 만들어야 한다.

 

테스트를 성공시키기 위해 다양한 작업을 할 수 있는데, 일부 테스트를 비활성화하고 나머지 테스트가 잘 동작한다면 리팩터링을 시작할 수 있다.

 

즉시 리팩터링을 할 수 없다면, 개발자가 할 수 있는것은 리팩터링 할 것을 메모해두는 것이다.

(Jira 티켓, 이메일에 남겨놓는 것 등등 나중에 즉시 보고 처리할 수 있도록)

메모 후 최대한 빨리 기능을 개발해야하는 것을 수행하고, 다시 정리한 것을 보고 리팩터링을 통해 코드를 정리해야한다.

 

개발 프로세스에서 리팩터링을 할 시점을 결정하는 것이 가장 중요하다.

리팩터링을 개별 태스크로 나누고 나서, 코드에 대해 논의하는 것

리팩터링 해야하는 시점을 정하는 가장 좋은 방법은 리팩터링의 적정 범위를 정하는 것

 

준비를 위한 리팩터링

요약
- `코드가 왜 이렇게 동작하는지 모르겠다` 라고 느낄 때 준비를 위한 리팩터링을 해야할 시점
   소프트웨어 설계는 계속 성장하므로, 새로운 코드 기존 코드가 다른 구조가 됐다면 리팩터링으로 통일된 구조로 정돈해야한다.
   리팩터링에 당장은 시간이 낭비된다고 생각할 수 있다
   하지만 앞으로 새로운 기능 추가에 있어서 코드 이해를 위해 빼앗길 시간에 비하면 리팩토링 비용이 싸게 먹힌다.

리팩터링을 해야할 또 다른 시점은

`이 코드가 왜이렇게 동작하는지 모르겠다` 이다. 보통 새로운 기능을 하는 코드를 작성하면 나타난다.

새로운 기능을 추가하기는 매우 쉽지만, 새로운 코드가 기존과 다른 구조가 될 수 있다.

 

새로운 코드의 변경된 구조는 앞으로 기능을 추가할 때 고민이 많아지게 한다.

소프트웨어가 계속 진화한다는 가정하에 이런 문제는 계속 발생할 수 있다.

(새 기능을 추가했더니 예전 코드가 이상한것 같아)

 

이런 상황을 `준비를 위한 리팩터링` 이라고 한다.

 

우리가 새로운 코드를 적용했을 때, 코드가 적절하지 않다면 개발자는 리팩터링을 시작해야하고, 리팩터링을 통해 코드를 정리해야한다.

리팩터링을 통해 잘 정돈되지 않은 코드를 정리하고, 테스트를 성공시킨 후, 리팩터링한 코드 위에서 새로운 기능을 추가해야 한다.

 

많은 개발자들이 리팩터링은 기능을 개발하는데 시간을 빼앗는다고 불평하곤 하는데

모든 상황에 적용할 수는 없겠지만, 일반적으로 리팩터링을 하고나면 중간단계에서는 이득을 본다.

`준비를 위한 리팩터링`을 수행하면 새로운 기능을 추가할 때, 더 빠른 속도로 개발할 수 있다.

우리는 이후에 리팩터링 하지 않아도 빠르게 기능을 추가할 수 있게 된다.

 

새로운 코드가 기존 코드에 잘 녹여져 있지 않다고 생각될 때는 신규 코드 작성을 멈추고 리팩터링을 해야한다.

그리고 다시 기능 개발을 해야한다.

계획된 리팩터링

요약
- 프로젝트 계획에 리팩터링을 추가하여 매번 코드를 깨끗하게 만드는 방법이다.
- 많은 양의 코드를 짤 때 사용됨. 
- 리팩터링 해야할 이유를 찾아야하는 단점이 있으므로 자연스러운 리팩터링 방식은 아니다.
- 단점이 있더라도 리팩터링을 안하는 것보단 나은 방식이다.

리팩터링을 프로젝트 계획 중간에 끼워넣어서 수행하는 것

많은 양의 코드를 짤 때 사용됨

프로젝트 계획에서 리팩터링을 추가함으로써 매번 코드를 깨끗하게 만드는 것

 

이런 상태를 `계획된 리팩터링` 이라고 한다.

왜냐하면 리팩터링 해야할 시점이 명확하고 개발자가 수행해야 하는것

 

계획된 리팩터링은 많은 개발팀들이 수행해야 겠지만 역량이 좋은 팀은 굳이 이걸 해야할 필요는 없다.

리팩터링을 하지 않는 팀에 비해 리팩터링을 매번 하는 팀은 작업 단위를 더 작게 쪼개야한다.

기능들을 작게 쪼개서 개발할 때마다 리팩터링을 실행하므로 따로 리팩터링 작업 계획이 필요없을 수도 있다.

 

계획된 리팩터링의 문제점은 리팩터링 해야할 이유를 찾아야하는것이다. 이 리팩터링은 지양하는게 좋다.

일반적인 상황에서 리팩터링을 하는것이 맞다고 생각한다. ( 코드를 살펴보다가 나쁜 코드를 찾아내는 ) 

개발자는 코드를 항상 깨끗하게 만드려는 노력을 한다.

하지만 계획된 리팩터링은 필요악이다. 이 방식대로 리팩터링을 수행하면 안하는것보단 낫기때문이다.

 

장기적인 리팩터링

요약
- 큰 덩어리의 코드를 수정해야할 때 사용하는 리팩터링 방법
- 모듈간 복잡한 의존관계 정리, 복잡한 코드 정리
- 장기간 리팩터링을 실행할 때는 명확한 목표를 정하고 목표에 도달하도록 조금씩 리팩터링을 수행해야 한다.

리팩터링에 많은 시간이 걸려서는 안된다.

하지만 코드를 가지고 계속 일을 하다 보면 큰 덩어리의 코드를 수정해야할 때가 있다.

 

이런 리팩터링 방법을 `장기적인 리팩터링`이라고 한다.

장기간 리팩터링을 진행할 때 앞서 말한 리팩터링 방법들을 사용하길 권장한다. (물론 계획된 리팩터링은 여기에 적용할 수 없다.)

-> 앞서 말한 리팩터링 ( 쓰레기 줍기 / 이해를 위한 / 준비를 위한 리팩터링)

 

중요한 것은 코드를 어떻게 고쳐야하는지 명확한 비젼을 가져야 한다는 점이다.

개발팀과 함께 논의해서 이번 장기적인 리팩터링이 끝나면 우리가 고친 코드가 어떤 방식으로 변경되어야 할지 명확한 목표를 정해야한다.

리팩터링을 하나의 작업으로 가져가기 보다는, 특정 부분의 코드를 작업 진행할 때, 우리가 정한 목표에 도달하도록 조금씩 리팩터링을 수행해야 한다.

시간이 얼마나 걸릴지 모르지만, 수정하다 보면 목표에 도달할 것이다.

마지막 리팩터링이 끝날 때 까지 계속해서 작은 단위로 리팩터링을 수행하는 것.

리팩터링을 계획에 조금씩 녹이는 것

코드가 잘 돌아가고 깨끗하고 언제든 배포 가능하게 만드는 것

이걸 수행하다 보면 꽤 큰 범위의 리팩터링이라도 작은 단위로 처리 하다보면 목표에 도달할 수 있다는걸 깨달을 수 있다.

 

큰 범위의 리팩터링을 마주했을 때 `어떻게 목표에 달성할 수 있을까` 라는 의문이 든다면, 작은 단위로 리팩터링 해야만 목표에 도달할 수 있다.

팀원들과 계속 목표에 도달하도록 소통해야 한다.

생각보다 시간이 오래걸릴 수 있지만, 지속적인 수정으로 복잡한 코드가 더 명확하게 바뀌는걸 볼 수 있다.

 


왜 리팩터링을 해야 하는가?

 

리팩터링하는 시간이 똑같은 일을 반복하느라 시간을 버리는게 아니냐고 묻기도 한다.

(코드를 한번 더 작업하는 것은 중복 작업이다?)

 

소프트웨어 설계에 신경 쓰지 않으면 시간이 지날수록 개발 속도가 현저히 떨어진다.

개발자가 새로운 기능을 추가할 때, 복잡한 코드와 씨름하느라 속도가 느려진다.

 

좋은 소프트웨어 설계를 가지면, 새로운 기능을 추가하는건 정말 쉬운 일이 된다.

변경해야할 모듈을 정하고, 코드를 추가하면 된다.

 

모든 사람들은 좋은 설계를 가진 코드를 원한다.

이때 리팩터링은 좋은 소프트웨어 설계를 만들기위해 중요한 역할을 한다.

개발자가 정말 멋진 소프트웨어 아키텍처를 원하지만, 모두가 성공하긴 어렵다.

하지만 리팩터링을 지속하면 코드를 항상 깔끔한 상태를 유지하고 이는 좋은 아키텍처로 가도록 유도한다.

리팩터링을 잘하는 개발자는 좋은 그래프 형태를 얻게 된다.

 

나쁜 설계를 가진 소프트웨어에 비해서 좋은 설계는 훨씬 더 많은 기능을 더 빠르게 구현할 수 있게 해준다.

이것이 바로 리팩터링이 중요한 이유다.

 

`개발자들이 왜 리팩터링 해야하나요?` 에 대한 대답으로 다음과 같은 것들이 있다.

  • 코드 퀄리티
  • 클린 코드
  • 전문성
  • 옳은 일

리팩터링에 대해 이야기할 때는 '경제성(Economics)'를 이야기하는 편이 좋다.

개발자가 하는 모든 것들이 더 많은 기능을 더 빠르게 적용하기 위함이다.

이것이 바로 유일한 리팩터링을 해야하는 이유다.

design stamina hypothesis

당장 리팩터링을 하면 결국 긴 기간을 봤을 때, 기능 추가는 더 빨라진다. 

만약 리팩터링을 하지 않으면? 이미 잘 돌아가는 코드가 있다면 개발자는 코드를 더 수정하고싶지 않을 수 있다. 지금은 개발 속도가 느리지 않으니까. 하지만 점점 속도는 느려질 것이다.

 

개발자가 작업을 할 수록 개발하는데 시간이 너무 오래걸린다고 생각돼서 코드를 깔끔하게 만들고 싶어한다면

그 때가 바로 리팩터링을 해야하는 코드다

 

개발자로써 전문성이 있다고 생각한다면 항상 코드를 깔끔하게 유지하도록 해야한다.

코드위에서 오직 개발자만이 `좋은 소프트웨어 설계를 가져갈지` `나쁜 소프트웨어를 가져가서 느린 개발을 할지` 선택할 수 있는 일이기 때문이다.

만약 개발자가 코드를 깨끗하게 만들지 않는다면 고객을 도둑질하는 것과 같다.

회사로 하여금 새로운 기능을 만들 때 더 많은 시간, 더 많은 돈을 사용하게 만든다.

그렇기 때문에 개발자가 하는 일이 경제적으로 어떤 영향을 미치는지 생각해야한다.

 

명심해야할 것은 리팩터링은 경제성때문에 하는 것이다

깔끔한 코드는 더 빠른 기능 개발을 할 수 있게한다.

 

 

 

 

 

댓글