# Five Lines of Code - 2. 리팩토링 깊게 들여다보기

> 주제: 가독성을 통한 의미 전달, 불변속성 지역화, 추가를 통해 변경 가능하게 만들기

\\

## 🔖 2.1 가독성 및 유지보수성

### 책의 내용

가독성 - 의도를 전달하기 위한 코드의 성질이며, 가독성이 있다는 말은 코드가 무슨 일을 하는지 파악하기가 매우 쉽다는 뜻입니다.\\

유지보수성 - 얼마나 많은 후보를 조사해야 하는지를 나타내는 표현입니다. 조사 단계에 시간이 오래걸린다는 것은 유지보수성이 나쁘다는 징후입니다. 만약 무언가를 수정했을 때 관련이 없는 다른 곳에서 문제가 발생한다면, 이를 취약하다고 말합니다. 취약성의 근원은 일반적으로 '전역 상태' 입니다. 여기서 전역은 우리가 고려한 범위(scope)를 벗어난 것을 의미합니다.

### 나의 생각

돌이켜보면 전역 설정으로 인한 디버깅으로 은근 시간을 많이 쓴 것 같다. 예를 들면, 특정 스타일 전역 설정을 해두었는데 이를 까먹고 지역 설정이 왜 적용이 안되는지 계속 뒤져보았던 적이 있었다. 혹은 라이브러리 설정으로 인해 지역 설정이 적용되지 않았던 적도 있고.. 많은 후보를 조사하느라 (디버깅) 그때의 내 코드는 취약했다는 의미겠지..?

\\

## 🔖 2.1 불변속성

### 책의 내용

불변속성 - 데이터가 전역적일 경우, 데이터가 연결된 다른 변수를 통해 누군가가 읽거나 변경할 수 있어 실수로 데이터가 손상될 수 있습니다. 이때, 코드에서 상태(조건)를 명시적으로 확인하지 않는 속성을 불변속성(invariant)이라고 합니다. (e.g. 이 숫자는 절대 음수일 수 없습니다.) 하지만 불변속성이 유효한 상태로 유지되기란 거의 불가능합니다. 이런 경우의 리팩토링은 불변속성을 더욱 쉽게 볼 수 있도록 서로 가깝게 이동시켜 '불변속성의 범위 제한 (localizing invariants)' 이라고 합니다.

### 나의 생각

불변속성이 유효한 상태로 유지되기가 왜 어려운걸까? 변수가 재할당되지 않도록 설정하면 불변하지 않나? 물론 가깝게 놓으면 불변할 가능성이 높아지긴 하겠으나, 원천적으로 변경을 차단할 수 있는 방법은 없는지 스터디원들에게 이 예시를 물어보아야겠다.

\\

## 🔖 2.2 상속보다는 컴포지션 사용

### 책의 내용

상속보다는 컴포지션을 사용합니다. 대부분의 리팩토링 패턴과 규칙은 구체적으로 객체 컴포지션을 돕기 위한 것들입니다. 즉, 객체가 내부에 다른 객체의 참조를 가지는 것입니다.

```typescript
interface Bird {
  hasBeak(): boolean;
  canFly(): boolean;
}
class CommonBird implements Bird {
  hasBeak() { return true; }
  canFly() { return true; }
}
class penguin extends CommonBird { // 상속
  canFly() { return false; }
}
```

```typescript
interface Bird {
  hasBeak(): boolean;
  canFly(): boolean;
}
class CommonBird implements Bird {
  hasBeak() { return true; }
  canFly() { return true; }
}
class penguin implements Bird {
  private bird = new CommonBird(); // 컴포지션
  hasBeak() { return bird.hasBeak(); } // 호출을 직접 명시적으로 전달해야 함
  canFly() { return false; }
  canSwim() { return false; } // 만약 메서드 하나가 더 추가된다면?
}
```

위의 예시처럼, 컴포지션의 가장 큰 장점은 추가(addition)로 손쉬운 변경이 가능하다는 점입니다. 소프트웨어 구성 요소들은 확장에 대해 열려 있어야 하고, 수정에 대해 닫혀 있어야 한다는 의미입니다. 이를 개방-폐쇄(open-closed) 원칙이라고 합니다.

### 나의 생각

JavaScript를 학습하다보면, 자연스레 상속 이라는 개념을 배우기에 이게 리팩토링의 대상이 되어야 하는지 생각을 못했다. 그래서 처음엔 왜 상속보다는 다른 무언가를 사용할까? 했지만 예시를 보니 이해가 되었다. 만약 메서드를 하나 더 추가했을 때 `penguin`이 새로운 메서드 `canSwim`을 구현하지 않기 때문에 컴파일 오류가 발생한다. 이때, 컴포지션을 사용한다면 `hasBeak()`처럼 `canSwim()`도 간단하게 클래스 내에 정의하기만 하면 된다. 하지만 반대로 상속의 상황에서는, `canSwim()`을 재정의(override)해야 한다는 것을 작업자가 기억해야 하므로 이러한 의존 관계를 기억하기 어려울 수 있을 것 같다.\\

이렇듯, 상속보다 컴포지션을 사용한다면 더 자유자재로 코드를 레고 블럭을 조립하고 교체하듯이 코딩할 수 있을 것 같다.
