# Refactoring

## 리팩토링이란

> 리팩터링은 '**기능을 변경하지 않고 코드의 가독성과 유지보수가 쉽도록 코드를 변경하는 것**'

* 리팩터링을 수행하려면 **리팩터링 대상을 식별하는 스킬**과 **리팩터링 단계를 명시적으로 가진 문화**, **리팩터링을 돕는 도구**의 **조합이 필요**하다.

### 리팩터링을 해야하는 이유

* 코드를 더 빠르게 만들기 위해
* 더 작은 코드를 만들기 위해
* 코드를 더 일반적이거나 재사용 가능하게 하기 위해
* <mark style="color:green;">**코드의 가독성을 높이고 유지보수를 용이하게 하기 위해**</mark>

{% hint style="info" %}
좋은 코드란 **사람이 읽기 쉽고, 유지보수가 용이하며, 의도한 대로 잘 동작하는 코드**
{% endhint %}

#### 더 자세히 풀어보자면

* 코드를 작성하는 시간보다 코드를 읽고 이해하는데 더 많은 시간을 보내게 된다.
  * 복잡한 분야에서 작업하기에 이해 하지 못하고 변경하게되면 치명적인 장애가 발생할 수 있기 때문
* 코드베이스의 가독성을 높이면 새로운 기능을 구현하기 위한 시간을 확보할 수 있게된다. (경제적인 이유)
* 유지보수가 용이해지면 버그 발생이 줄어들고 수정이 쉬워진다.
* 좋은 코드베이스는 생각하기 편하기 때문
  * 디버깅이 지옥인 이유는 우리가 코드를 읽을 때 코드가 하는 일에 대해 머릿속으로 해석하게되는데 머릿속에서 한 번에 많은 것을 기억해야 할수록 더 지치게 된다.

#### 개발 작업 절차

1. **탐색(Explore)**: 무언가 만들어야 할지 확신이 안서거나 또는 문제를 해결할 수 있을지조차 모르는 경우엔 항상 실험부터 시작해야 한다. 무언가를 신속하게 구현하면 고객이 무엇을 필요로하는지 함께 확인할 수 있게된다.
2. **명세화(Specify)**: 무엇을 만들지 알게 되면 그것을 명세화한다. 최적의 경우 이것은 자동화된 테스트의 형태가 된다.
3. **구현(Implement)**: 코드를 구현
4. **테스트(Test)**: 코드가 2단계의 명세(사양)를 따르는지 확인
5. **리팩터링(Refactor)**: 코드를 전달하기 전에 다음 사람이 쉽게 작업할 수 있는지 확인(다른 사람이 내가 될 수 있음)
6. **전달(Deliver)**: 가장 일반적으로 PR(pull request) 또는 특정 브랜치로 push하는 작업

#### 레거시 시스템에서의 리팩터링

* 대규모 레거시 시스템에서 리팩터링을 시작할 때 모든 것을 중단한 후 코드베이스 전체를 먼저 리팩터링할 필요없이 일상 업무에 결합할 수 있는 좋은 방법은 "<mark style="color:red;">**우선 변경하기 쉽게 만든 후 변경하라 - 켄트 백**</mark>"
* 새로운 것을 구현할 때마다 새 코드를 쉽게 추가할 수 있게 리팩터링을 먼저 한다.

### 규칙

> 일반적으로 코드 스멜은 리팩터링 대상을 설명하는데 쓰인다. 하지만 이것들은 모호해서 주니어들이 내면화하기 어렵기 때문에 코드 스멜을 대체할 구체적인 규칙을 제공한다.&#x20;

* **다섯 줄 제한**: 메서드는 사용하는 데이터 구조를 탐색하는데 필요한 것보다 더 많은 코드로 구성돼서는 안된다.
* **호출 또는 전달, 한가지만 할 것**: 함수 내에서 객체에 있는 메서드를 호출하거나 객체를 인자로 전달할 수 있지만 둘은 섞어 사용해서는 안된다.
* **if 문은 함수의 시작에만 배치**: if 문이 있는 경우 해당 if 문은 함수의 첫 번째 항목이어야 한다.
* **if 문에서 else를 사용하지 말 것**: 프로그램에서 이해하지 못하는 타입(형)인지를 검사하지 않는 한 if문에서 else를 사용하지 않기
* **switch를 사용하지 말 것**: default 케이스가 없고 모든 case에 반환 값이 있는 경우가 아니라면 switch를 사용하지 않기
* **인터페이스에서만 상속받을 것**: 상속은 클래스나 추상 클래스가 아닌 오직 인터페이스를 통해서만 받는다.
* **순수 조건 사용**: 조건문의 조건식에서는 변수에 값을 할당하거나 예외를 발생시키거나 I/O와 상호작용해서는 안된다.
* **구현체가 하나뿐인 인터페이스를 만들 지 말 것**: 구현체가 하나뿐인 인터페이스를 사용하지 말기
* **getter와 setter를 사용하지 말 것**: 부울이 아닌 필드에 setter나 getter를 사용하지 않기
* **공통 접사를 사용하지 않기**: 코드에는 공통 접두사나 접미사가 있는 메서드나 변수가 없어야 한다.

### 리팩토링 패턴

* **메서드 추출**: 한 메서드의 일부를 가져와 고유한 메서드로 추출&#x20;
* **클래스로 타입 코드 대체**: 열거형을 인터페이스로 변환하고 열거형의 값을 클래스로 만든다.
* **클래스로의 코드 이관**: 기능을 클래스로 옮기기 때문에 클래스로 타입 코드 대체는 클래스로의 코드 이관으로 자연스럽게 이어진다.
* **메서드 인라인화**: 프로그램의 가독성에 더 이상 도움을 주지 않는 메서드를 제거
* **메서드 전문화**: 메서드에 불필요하고 문제가 있는 일반성을 제거
* **삭제 후 컴파일하기**: 인터페이스와 클래스의 전체 사용 범위를 알고 있는 경우 사용하지 않는 메서드를 인터페이스와 클래스에서 제거
* **유사 클래스 통합**: 일련의 상수 메서드에서 서로 다른 두 개 이상의 클래스를 통합
* **if 문 결합**: 동일한 본문을 가진 if문이 이어진 경우 if 문의 분기들을 결합해서 중복을 줄임
* **전략 패턴의 도입**: if문을 사용한 분기를 클래스를 인스턴스화하는 것으로 대체
* **구현에서 인터페이스 추출**: 클래스에 대한 종속성을 인터페이스로 바꿈
* **getter와 setter 제거하기**: 기능을 데이터에 더 가깝게 옮겨 getter와 setter를 제거
* **데이터 캡슐화**: 변수와 관련된 불변속성을 지역화하고 응집력을 더 명확히 한다.
* **순서 강제화**: 특정 순서대로 작업을 수행하는 것을 컴파일러가 보장하게 한다.
