[코틀린을 다루는 기술] 더 안전한 프로그램 - 부수효과

timothy jeong·2022년 4월 1일
0

코틀린

목록 보기
19/20

용어

효과(effect)

코틀린의 함수는 특정한 블록 안에서 정의되지만 함수는 정의된 블록 바깥의 영역도 볼수가 있고 상호작용할 수 있다. 함수의 입장에서는 자신의 정의한 클래스도 바깥 영역이다. 만약 함수를 통해 스스로를 정의한 클래스의 프로퍼티를 변경한다면, 더 나아가 바깥 영역(외부 영역)과 상호작용하는 것을 '효과'라고 정의한다. 이는 네트워크, DB, 콘솔, 파일 등에 데이터를 쓰는 행위도 포함한다.

부수 효과(side effect)

어떤 함수는 값을 반환하고 어떤 함수는 상태를 변경한다. 일부 함수는 값을 반환하는 동시에 상태를 변경한다. 함수 외부와 상호작용하는 것을 '효과'라고 불렀다. 하지만 함수가 값을 반환하는 동시에 효과를 만들어낸다면 이 효과는 '부수효과(side effect)' 라고 불린다.

부수효과 지양하기

부수효과를 사용하는 프로그램은 잘못된 프로그램이다. 안전한 프로그램은 인자를 받아서 값을 반환하는 여러 함수를 합성해 만들어진다. 그리고 그러한 함수를 합성하는 것이 프로그램의 전부이다.

부수효과가 있으면 단일 책임 원칙에 위배될 뿐만 아니라 테스트하기 힘들어진다. Mock 을 통해 다른 클래스를 제어해야하기 때문이다.

예시

도넛을 구매하는 함수를 아래와 같이 정의했다고 하자.

fun buyDonut(creditCard: CreditCard): Donut {
	val donut = Donut()
    creditCard.charge(Donut.price) // 부수효과
    return donut
}

도넛을 주문하면 도넛을 생성하고, 결제를 하고 도넛을 반환하다는 것은 인지적으로는 굉장히 자연스러운 흐름이다. 하지만 하나의 함수에서 관리하고자하니 도넛을 반환하는 함수가 인자로 받은 CreditCard 클래스의 함수를 또 호출하는 부수효과가 발생하고 있음을 알 수 있다.

이를 해결하기 위해서는 부수효과 자체를 없애버리면 된다. buyDonut 함수를 호출하는 함수 내부에서 결제를 하지 않고, 결제 함수를 따로 만드는 방법이 있을 것이다. 하지만 함수를 나누고 싶지 않다면 buyDonut 함수가 지급 연산을 나타내는 표현을 반환 값에 덧붙이면 된다.

class Payment(
	val creditCard: CreditCard, // 결제에 사용될 신용카드
    val amount: Int // 결제 금액
)


Class Purchase(
	val donut: Donut, // 구매한 도넛
    val payment: Payment // 지급 연산 표현
)

fun buyDonut(creditCard: CreditCard): Purchase {
	val donut = Donut()
    val payment = Payment(creditCard, Donut.price)
    return Purchase(donut, payment)
}

buyDonut 함수는 Purchase 클래스를 반환하지만, 외부 세계에 영향을 주지 않는다. buyDonut 함수를 호출후, 반환값을 이용해서 결제를 한번에 몰아서 처리하는 가능성도 열려있다. 무엇보다 이제 Mock 을 사용하지 않아도 테스트할 수 있다.

profile
개발자

0개의 댓글