티스토리 뷰

목차

    배경

    Java 17버전부터는 Sealed Class와 Sealed Ineface를 제공한다. Sealed Class와 Sealed Interface는 상속과 구현할 클래스를 제한하여 구현 가능한 타입을 명확히 지정할 수 있다. 또한 문법적으로 상속/구현 가능한 클래스를 제한하여 의도치않은 상속과 구현을 방지할 수 있다. 이러한 특징으로 컴파일 시점에 올바르게 클래스를 상속 또는 인터페이스를 구현했는지 검증할 수 있다.

     

    또한 Sealed Class를 활용하여 객체지향적 설계와 코드를 작성하고, Mocking을 통해 테스트 코드로 여러 케이스에 대한 검증을 할 수 있다.

     

    예시 - Sealed Class를 활용한 API 응답 처리

    예를 들어 API 요청과 응답에 따른 처리가 필요하다고 가정해보자. API 응답은 보통 두 개의 타입(성공과 실패)으로 제한된다. 이처럼 타입이 특정 케이스로 제한되는 경우 Sealed Class/Inteface를 적용하기 적합하다.

     

    이 경우 다음과 같이 코드를 작성할 수 있다. API 응답 상태를 나타내기 위해 ApiResponse라는 이름의 Sealed Interface를 구현한다. 이 인터페이스는 성공(Success)과 실패(Failure)로만 구현할 수 있다.

    public sealed interface ApiResponse permits Failure, Success {
    	default boolean isSuccess() {
    		return false;
    	}
    
    	default boolean isFailure() {
    		return false;
    	}
    }

     

    ApiResponse를 구현하는 클래스는 두 개의 메서드를 오버라이딩한다. 바로 API 요청의 성공과 실패 여부이다. 이 두 메서드(isSuccess, isFailure)는 default 메서드(Java 8부터 제공)로 구현하여 기본적으로 false를 반환하도록 한다.

     

    성공(Success)과 실패(Failure) 구체 클래스의 두 메서드를 오버라이디한다. 성공 클래스는 isSuccess를 true로, 실패 클래스는 isFailure를 true로 반환한다.

    public record Success<T>(T content) implements ApiResponse {
    	@Override
    	public boolean isSuccess() {
    		return true;
    	}
    }
    
    public record Failure(String message, int errorCode) implements ApiResponse {
    	@Override
    	public boolean isFailure() {
    		return true;
    	}
    }

     

    객체에게 메시지를 던지는 구조

    이처럼 Sealed Class로 타입을 제한하고 객체에게 isSuccess, isFailure로 메서드를 던지는 구조는 객체지향적 설계와 개발로 이어질 수 있다. 예를 들어 API 요청과 응답에 따른 처리가 각각 다른 상황이라고 가정하자.

     

    이 경우 API의 응답 코드나 응답 메시지를 직접 꺼내서 비교하는게 아니라 isSuccess, isFailure 메서드를 호출하여 객체에게 메시지를 던지는 구조로 설계할 수 있다. 이러한 구조는 명확한 의도로 가독성을 높일 수 있다.

    if (response.isFailure()) {
    	throw new ApiCallException("API 호출 실패");
    }

     

    Mocking으로 API 호출 응답에 따른 테스트 검증

    Sealed Class와 Inteface를 활용하면 Mocking을 통해 다양한 케이스에 대해 테스트 코드로 검증할 수 있다. 만약 API 호출 성공과 실패에 따른 로직을 테스트 코드로 검증하고 싶다면 다음과 같이 Mocking을 통해 검증할 수 있다.

    // API Mocking
    given(service.method(any(), any()))
    	.willReturn(
    		new Failure("API 호출 실패", HttpStatus.BAD_REQUEST.value())
    );

     

     API 호출 실패시 예외가 발생하도록 프로덕션 코드를 작성했다면, 다음과 같이 실패 케이스에 대해 모킹을 통해 테스트 코드로 검증을 할 수 있다.

    @Test
    void API_호출_실패_테스트() {
    	// given
    	given(apiCallService.method(any(), any()))
    		.willReturn(new Failure("API 호출 실패", HttpStatus.BAD_REQUEST.value()));
    
    	// when, then
    	final var request = new Request(...);
    	assertThatThrownBy(() -> service.run(request))
    		.isInstanceOf(ApiCallException.class);
    }
    Comments