우아한 테크코스 레벨1 돌아보기

레벨1 기간 동안 미션을 진행하면서 많은 것들을 처음 알고 새롭게 배웠습니다.
그 중에서도 매 미션마다 크게 다가왔던 부분들에 대해 정리해보고자 합니다.


난수 테스트 - 자동차 경주 게임

자동차 경주 게임의 조건 중, 전진 조건은 다음과 같습니다.
0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다.
이를 간단하게 구현한다면 다음과 같습니다.

public class Car {
    private int position = 0;

    public void moveCar() {
        if (movable()) {
        	position++;
        }
        
    private boolean movable() {
    	int randomValue = random.nextInt(10);
        return randomValue >= 4;
    } 
}

random.nextInt() 값이 4이상이면 position을 이동하는 로직입니다.
만약 위 코드에서 position이 변하는 것을 테스트 하려고 한다면 어떻게 될까요?
난수 값은 계속해서 바뀌기 때문에 테스트 코드는 성공 할수도 있고 실패 할수도 있습니다.
그렇다면, position이 변하는 것을 테스트 하려면 어떻게 해야 할까요?
인터페이스를 활용하면 가능 합니다.

인터페이스의 활용

인터페이스는 관계가 없는 클래스들 간의 관계를 맺어줄수 있습니다.
이를 활용하면 전진 조건을 테스트 할 수 있습니다.
다음의 예시를 살펴보겠습니다.

public class Car {
    private int position = 0;

    public void moveCar(final MoveStrategy moveStrategy) {
        if (moveStrategy.movable()) {
        	position++;
        }
}
public interface Movable {

    boolean movable();
}

위 예시는 moveCar()에게 파라미터로 moveStrategy 인터페이스를 전달합니다.
그리고 moveCar()의 내부 로직에서 moveStrategy의 메서드를 호출합니다.
인터페이스를 파라미터로 전달하는 것입니다. 이를 통해 moveCar()메서드에 유연성을 부여 할 수 있습니다.
왜냐하면, moveStrategy.movable()에 대한 클래스를 두개 구현하고 비지니스로직과 테스트 코드에서 다른 클래스를 전달해주면 되기 때문입니다.
즉, 자동차 경주의 비지니스 로직에서는 실제 전진 조건을 구현하고 테스트 코드에서는 반드시 true를 반환하는 경우를 구현하는 것입니다.

위와 같이 인터페이스를 잘 활용한다면 테스트가 까다로운 부분을 테스트 할 수 있고 동시에, 객체에 유연성을 부여 할 수 있습니다.

인스턴스 변수의 선언 기준

객체간의 협력관계를 만들 때, 아래와 같이 객체 안에 인스턴스 변수를 선언하곤 합니다.

public class LottoMachine {

    private TotalLottoCount totalLottoCount;
    private Lottos lottos;
	
    ...
    
}

인스턴스 변수 선언을 통해 두가지 장점을 얻을 수 있습니다.

  1. 코드 구현을 편리하게 한다.
    객체에 인스턴스 변수를 선언 함으로써, 협력이 필요한 객체에게 요청을 보내기가 편리해집니다. 왜냐하면 메서드 안에서 협력을 요청할 객체의 인스턴스를 선언하는 과정을 줄 일 수 있기 때문입니다.

  2. 가독성을 높힌다.
    인스턴스 변수 선언을 통해 메서드 내부에서 인스턴스를 생성 하거나 파라미터로 필요한 인스턴스를 받아오는 코드를 줄여 객체의 전체적인 가독성을 높힐 수 있습니다.

그래서 인스턴스 변수를 선언한다면, 구현의 편의성과 가독성이라는 장점을 얻을 수 있습니다.
그렇다면 협력을 요청 할 모든 객체를 인스턴스 변수로 선언하는 것이 좋을까요?
상태를 가지지 않는 객체라면 괜찮지만, 상태를 가지는 객체는 인스턴스 변수로 선언하는 것을 신중히 고려해야합니다.
왜냐하면 프로그램이 동작 할 때, 우리가 정해놓은 순서대로 반드시 동작한다는 보장이 없기 때문입니다.
그래서 프로그램 동작 과정에서 인스턴스 변수의 상태가 기대 값과 다르게 변할 수 있고, 이는 프로그램 오류의 원인이 될 수 있습니다.
그래서 인스턴스 변수의 선언은 신중히 고려해야 합니다. 또한, 반드시 필요한 경우가 아니라면 인스턴스 변수 선언을 자제해야합니다.
제가 생각한 반드시 인스턴스 변수가 필요한 경우는 다음과 같습니다.

고유한 상태를 반드시 유지해야하는 경우

객체 내부에서 독립적인 상태를 유지하고 요청을 처리해야하는 경우 입니다.
비지니스 로직을 처리하기 위해서 필요한 상태 유지해야 경우에는 프로그램의 올바른 동작을 위해 인스턴스 변수로 선언하여 상태를 유지하게끔 해야합니다.

TDD

레벨1의 미션들을 진행하면서 TDD를 처음 알게 되었습니다. 또한, TDD로 미션을 진행해오면서 TDD의 사용 이유에 대해서 생각해보았습니다.

  1. 프로그램의 기능을 테스트하기 편하게 해줍니다.
    만약 테스트 코드가 없다면 기능을 구현 할 때마다, 매번 프로그램을 실행 시키고 해당 부분이 오류가 없이 정상적으로 작동하는지 확인해야합니다.
    하지만, 테스트 코드를 작성한다면 프로그램을 실행하지 않아도 기능에 대해서 테스트 할 수 있습니다.

  2. 오류를 발견하기 쉽게 해줍니다.
    전체 프로그램을 구현한 이후에 상황에 따라서 로직을 변경하거나 수정 해야 할 수 있습니다. 이때, 테스트 코드가 없다면 로직을 변경하는 과정에서 이전에 없던 오류나 문제가 발생해도 쉽게 파악하고 해결하기가 어렵습니다. 하지만 TDD를 통해서 구현한 테스트 코드와 로직을 가지고 점진적으로 리팩터링을 하면서 테스트 코드를 수정하고 로직을 변경 한다면 발생하는 오류나 문제를 쉽게 파악하고 해결 할 수 있습니다. 왜냐하면 점진적으로 리팩터링을 하는 과정에서 기존의 테스트코드가 깨지지 않도록 수정을 해나가기 때문입니다.

  3. 불안한 요소를 제거 할 수 있습니다.
    만약 코드의 양이 많아졌을때 테스트 코드가 없다면 모든 코드들이 문제 없이 잘 작동하는지 파악하기가 어렵습니다. 하지만 TDD를 바탕으로 개발을 진행해왔다면 구현한 기능들에 문제가 없다는 안정감을 얻을 수 있습니다.
    왜냐하면 객체의 메서드가 잘 작동하는지를 하나씩 테스트 하면서 코드를 구현하기 때문입니다.

좋은 웹페이지 즐겨찾기