본문 바로가기

Programing/Framework

fastcampus: 실무 프로젝트로 배우는 Kotlin & Spring - Part 4/Ch02/03 오류 발견

위치: https://fastcampus.app/courses/211160/clips/779520?position=1248&organizationProductId=13720
Part 4. 실제 예제를 기반으로 자바 프로젝트에 코틀린 도입해보기
ch02. 자바 프로젝트(ToDo프로젝트) 코틀린으로 리팩토링 하기
03. 컨트롤러 레이어 리팩토링 22:51

자바로 되어 있는 코드를 코틀린으로 옮기는 것을 예제로 보여 주는 강좌였다.
18:04경에 Assert.notNull 을 코틀린의 checkNotNull로 바꾸는 부분이 있었다.

이 부분을 보면서 저 변환은 계약 위반이라고 생각했다.

그 이유는 무엇일까?

기존에 Assert.notNull 는 스프링이 제공해주는 Assertion utility이다.
코드를 보면 다음과 같다.

package org.springframework.util;

public abstract class Assert {

	public static void notNull(@Nullable Object object, String message) {
		if (object == null) {
			throw new IllegalArgumentException(message);
		}
	}

다음은 코틀린 코드이다.

package kotlin

@kotlin.internal.InlineOnly
public inline fun <T : Any> checkNotNull(value: T?): T {
    contract {
        returns() implies (value != null)
    }
    return checkNotNull(value) { "Required value was null." }
}

@kotlin.internal.InlineOnly
public inline fun <T : Any> checkNotNull(value: T?, lazyMessage: () -> Any): T {
    contract {
        returns() implies (value != null)
    }

    if (value == null) {
        val message = lazyMessage()
        throw IllegalStateException(message.toString())
    } else {
        return value
    }
}


어떤 계약이 깨졌는지 보이는가?

이전에 Assert.notNull 로 단정하는 경우에는 todo가 null 일 경우에 IllegalArgumentException 가 발생한다.
그런데 코틀린으로 전환한 Preconditions 인라인 함수의 경우 IllegalStateException 이 발생하게 된다.

언어의 전환은 리팩터링

리팩터링 2판의 294쪽에 다음과 같은 내용이 있다.

수정 전과 수정 후의 겉보기 동작이 달라지면 더 이상 리팩터링이 아니다.

언어의 전환은 세부사항(기술)의 변경이지 비즈니스 로직이 변경이 되어서는 아니다. 따라서 동작은 동일하게 유지되어야 하며 이 글에서 이것을 계약(contract)라는 용어로 표현했다.

그렇다면 제대로 변환을 한다면 어떻게 해야 할까?

코틀린의 Preconditions 유틸리티에도 IllegalArgumentException 를 발생시키는 함수가 있다.
바로 requireNotNull 이다.

@kotlin.internal.InlineOnly
public inline fun <T : Any> requireNotNull(value: T?, lazyMessage: () -> Any): T {
    contract {
        returns() implies (value != null)
    }

    if (value == null) {
        val message = lazyMessage()
        throw IllegalArgumentException(message.toString())
    } else {
        return value
    }
}


만약 테스트 코드를 꼼꼼하게 작성했다면 전환하는 data class 의 테스트 코드는 예외의 변화에 대해서 제대로 알려주었을 것이다.

이상했던 것은 04. 서비스 레이어 리팩토링에서 checkNotNull의 구현을 이미 강사는 보여주고 있었다.

130라인에서 IllegalStateException 을 throw 하고 있다.