> For the complete documentation index, see [llms.txt](https://koseungbin.gitbook.io/wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://koseungbin.gitbook.io/wiki/books/undefined-3/1/money.md).

# 다중 통화를 지원하는 Money 객체

## 테스트 목록

테스트 필요한 해야할 목록을 작성한다.

{% tabs %}
{% tab title="테스트 목록" %}

* $5 + 10CHF = $10(환율이 2:1 일 경우) &#x20;
* ***$5 \******&#x20;2 = 10$**&#x20;
  {% endtab %}
  {% endtabs %}

## 사용자 입장에서의 시나리오&#x20;

테스트를 작성할 때는 오퍼레이션의 완벽한 인터페이스에 대해 상상해보는 것이 좋다.&#x20;

즉, 오퍼레이션이 외부에서 어떤 식으로 보일 지에 대한 이야기를 테스트 코드로 적고 있는 것이다.

{% hint style="info" %}
**오퍼레이션**

객체가 수행할 수 있는 연산을 의미하며, 오퍼레이션에 대한 특정한 구현을 메서드라고 부른다.

다형성을 통해, 한 오퍼레이션은 여러 메서드를 가질 수 있다.
{% endhint %}

```java
    @Test
    void testMultiplication() {
        Dollar five = new Dollar(5);
        five.times(2);
        assertEquals(10, five.amount);

    }
```

## 작은 단계로 시작

위 테스트는 예기치 못한 부작용을 고려하지 않았고, 금액 계산도 정수형으로 하였다.&#x20;

하지만, 우선 이러한 ***문제 사항들은 목록에 추가***&#xD558;고, ***작은 단계로 시작해보는 것이 중요***&#xD558;다.&#x20;

{% tabs %}
{% tab title="테스트 목록" %}

* $5 + 10CHF = $10(환율이 2:1 일 경우) &#x20;
* ***$5 \******&#x20;2 = 10$**&#x20;
* *amount를 private으로 만들기*&#x20;
* Dollar 부작용(side effect)?&#x20;
* &#x20;Money 반올림?
  {% endtab %}
  {% endtabs %}

### 우선 컴파일 되게 만들자!

아직은 컴파일 조차 되지 않는다. 따라서 에러를 보고 하나씩 고쳐나가 보자.

![](https://1360705058-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LZ0r85EEFTHmLyxK8ag%2F-Ls_TI1Ys31aFiH44upg%2F-Ls_W3gTRvsLs-fGyKnJ%2Fimage.png?alt=media\&token=b347f37b-354f-41d4-8048-2fb934eabc7b)

#### 문제 상황을 적어보자.

* Dollar 클래스가 없음
* 생성자가 없음
* times 메서드가 없음
* amount 필드가 없음&#x20;

&#x20;하나씩 해결해보자. 우선 Dollar 클래스를 생성하여 첫 번째 에러를 제거한다.

```java
public class Dollar {
}
```

하지만 생성자가 없다는 에러와 times() 메서드가 없다는 에러, amount 필드가 없다는 에러 또 다시 발생한다.  순차적으로 하나씩 에러를 제거해보자.

![](https://1360705058-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LZ0r85EEFTHmLyxK8ag%2F-Ls_gh96cjpFQIbcSeXo%2F-Ls_hG8T-UbxXIUvE7u0%2Fimage.png?alt=media\&token=20f28cee-07f1-4952-9028-319c73d9d760)

따라서, Dollar 클래스에 생성자를 추가하여 첫 번째 에러를 제거한다.

```java
public class Dollar { 
    public Dollar(int mount) {
        
    }
}
```

다음으로 times() 메서드를 위해서는 스텁 구현이 필요하며, 이를 통해 에러를 제거한다.

{% hint style="info" %}
**스텁 구현**

메서드의 서명부와 반환 값을 적으며, 이 메서드를 호출하는 코드가 컴파일이 될 수 있도록 껍데기가 만들어 주는 것을 의미한다.
{% endhint %}

```java
public class Dollar {
    public Dollar(int mount) {

    }
    
    public void times(int multiplier) {
        
    }
    
}
```

�마지막으로 amount 필드를 추가하여 에러를 제거한다.

```java
public class Dollar {
    int amount;

    public Dollar(int amount) {

    }

    public void times(int multiplier) {

    }

}
```

�결과적으로 테스트는 실패하지만 컴파일이 되는 것을 확인할 수 있다.

### 테스트 통과시켜 보자!

![](https://1360705058-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LZ0r85EEFTHmLyxK8ag%2F-Ls_TI1Ys31aFiH44upg%2F-Ls_W_OrKdjB6X5w2QCL%2Fimage.png?alt=media\&token=9d38de14-ea26-40e7-afbe-6b094f748cd0)

원하는 결과는 10 이지만, 실제 결과가 0 이 나온 것을 확인할 수 있다. 테스트에서는 실패했지만, 나아진 것은 '다중 통화 구현'에서 '이 테스트를 통과 시킨 후 나머지 테스트도 통과시키기'로 변형되었다는 것이다.

해당 빨간 막대를 해결하기 위한 최소 작업은 아래와 같다.

```java
public class Dollar {
    int amount = 10;

    public Dollar(int amount) {

    }

    public void times(int multiplier) {

    }

}
```

![](https://1360705058-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LZ0r85EEFTHmLyxK8ag%2F-Ls_TI1Ys31aFiH44upg%2F-Ls_YL0U2vmrlxU2Png6%2Fimage.png?alt=media\&token=97eac612-a2fd-4b97-9562-83f1cbb2483f)

드디어 초록 막대를 볼 수 있게 되었다!&#x20;

### 테스트 주기&#x20;

하지만, 진정하고 **테스트 주기에서의 현재 자신의 단계를 생각**해보자. 테스트 주기는 아래와 같다.

{% hint style="warning" %}
테스트 주기&#x20;

1. 작은 테스트를 하나 추가한다.
2. 모든 테스트를 실행해서 테스트가 실패하는 것을 확인한다.
3. 조금 수정한다.
4. 모든 테스트를 실행해서 테스트가 성공하는 것을 확인한다.
5. 중복을 제거하기 위해 리팩토링을 한다.
   {% endhint %}

### 중복 제거 위한 리팩토링 &#x20;

{% hint style="danger" %}
**의존성과 중복**

테스트를 작성하게 되면, 자연스럽게 테스트와 코드 사이에 의존성이 발생하게 된다. 즉, 코드나 테스트 중 한쪽을 수정하면 반드시 다른 한쪽도 수정해야만 한다는 것이다.

의존성이 문제 그 자체라면 중복은 문제의 징후이다. 중복의 가장 흔한 예는 로직의 중복이며, 중복된 로직을 제거하는 가장 좋은 방법은 객체를 이용하는 것이다.

프로그램에서는 중복만 제거해주면 의존성도 제거된다. 이게 바로 TDD의 두 번째 규칙이 존재하는 이유이며, 다음 테스트로 진행하기 전에 중복을 제거하여, 오직 한 가지의 코드 수정을 통해 다음 테스트도 통과되게 만들 가능성을 최대화하는 것이다.
{% endhint %}

이제 5번을 실행할 단계이다. 하지만 어디에 중복이 존재한단 말인가? 이번 경우에는 코드가 아닌 데이터의 중복이 있는 것을 확인할 수 있다. 코드에서의  amount 필드가 5 \* 2 나누어 생각해보면, 테스트에서의 5, 2와 데이터가 중복되는 것을 확인할 수 있다. 하지만 5와 2 를 한번에 제거할 수 있는 방법은 없으나, amount 초기화 단계를 times() 메서드 안으로 옮겨 보자.&#x20;

```java
public class Dollar {
    int amount;

    public Dollar(int amount) {

    }

    public void times(int multiplier) {
        amount = 5 * 2;

    }

}
```

�테스트는 여전히 통과하며, 테스트 막대 역시 초록색이다. 하지만, 아직도 중복 데이터는 여전히 존재한다. 다음으로 5라는 값을 제거할 수 있을까?? 이건 생성자를 통해서 넘어오는 값이기 때문에 amount 초기화 단계를 변경해볼 수 있을 것이다.

```java
public class Dollar {
    int amount;

    public Dollar(int amount) {
        this.amount = amount;

    }

    public void times(int multiplier) {
        amount = amount * 2;

    }

}
```

또한, multiplier 의 값이 2이므로 이것도 중복이며, 상수를 multiplier 로 일반화할 수 있을 것이다.

```java
public class Dollar {
    int amount;

    public Dollar(int amount) {
        this.amount = amount;

    }

    public void times(int multiplier) {
        amount = amount * multiplier;

    }

}
```

�결과적으로 times 메서드 안에서의 amount 가 변수도 중복이므로 자바 \*= 문법을 통해 간결하게 수정할 수 있다.

```java
public class Dollar {
    int amount;

    public Dollar(int amount) {
        this.amount = amount;

    }

    public void times(int multiplier) {
        amount *= multiplier;

    }

}
```

아직까지 테스트가 성공하는 것을 확인할 수 있다.

�이제 드디어 첫 번째 테스트를 완료하며 완료 표시를 할 수  있게 되었다.&#x20;

{% tabs %}
{% tab title="테스트 목록" %}

* $5 + 10CHF = $10(환율이 2:1 일 경우) &#x20;
* ~~*$5 \** 2 = 10$~~&#x20;
* *amount를 private으로 만들기*&#x20;
* Dollar 부작용(side effect)?&#x20;
* &#x20;Money 반올림?
  {% endtab %}
  {% endtabs %}

## 요약&#x20;

요약해보면, 아래와 같이 정리할 수 있다.

* 자신이 알고 있는 작업해야 할 테스트 목록을 만든다.
* 오퍼레이션이 외부에서 어떻게 보이길 원하는 지 말해주는 이야기를 테스트 코드로 표현한다.
* 돌아가는 코드에서 상수를 변수로 변경하여 점진적으로 일반화한다.
* 새로운 할일을 한번에 처리하는 대신 할일 목록에 추가하고 넘어간다.
