sealed interface를 이용한 타입 안정성과 확장성 있는 설계
Kotlin의 sealed interface를 활용한 DTO 객체의 타입 안정성과 확장성 설계
sealed interface class
를 이용한 확장성 설계
단순하게 사용자 로그인을 구현하여 ID와 Password로만 이루어진 요청을 보내고 있다가 갑작스럽게 요구 사항이 증가하여 일반 관리자와 슈퍼 관리자의 로그인을 갑작스럽게 처리해야 하는 상황이 생긴다면 당황스러울 것이다.
예를 들어서
interface class LoginService {
fun login(request: RequestLoginDTO): JwtToken
}
data class RequestUserLoginDTO (
val loginId: String,
val loginPw: String
)
위와 같이 인터페이스를 구현했을 경우에는 해당 인터페이스를 상속 받아 구현해야 하는 구현체 RequestLoginDTO 라는 객체만을
매개 변수로 받아야 한다. 하지만, 만약 슈퍼 관리자와 일반 관리자가 각각 별도의 추가 파라미터 값을 요구하고 있다면 확장성에
불리한 요구 사항이 될 것이다. 이를 해결하기 위해서 Kotlin에서 sealed interface
를 사용할 수 있다.
sealed interface LoginDTO
위와 같이 sealed interface
를 생성하고 인터페이스에 매개 변수로 생성해준다
그리고 DTO에 sealed interface
를 상속 받아 구현해주면 아래와 같이 사용할 수 있다.
data class RequestUserLoginDTO(
val loginId: String,
val loginPw: String
): LoginDTO
이렇게 되면
interface LoginService {
fun login(request: LoginDTO): JwtToken
}
위와 같이 interface
를 수정해주고, 위의 인터페이스를 상속 받은 서비스 구현체에 when
을 사용하여 exhaustive check
가 가능하다
그렇다면 일반 관리자와 슈퍼 관리자가 추가되었다고 가정 했을 경우,
data class RequestAdminLoginDTO(
val loginId: String,
val loginPw: String,
val adminKey: String
): LoginDTO
data class RequestSuperAdminLoginDTO(
val loginId: String,
val loginPw: String,
val loginIp: String
): LoginDTO
이렇게 LoginDTO
를 통해서 타입의 확장성과 안정성을 확보할 수 있게 됩니다.
그리고 interface
에 LoginDTO
를 강제할 수 있게 interface
에 매개 변수로
설정하게 되면
interface LoginService {
fun login(request: LoginDTO): JwtToken
}
과 같이 LoginDTO
를 상속해야만 가능한 구조로 만들 수 있고
class LoginServiceImpl: LoginService {
override fun login(request: LoginDTO): JwtToken {
when (request) {
is RequestUserLoginDTO -> { TODO("일반 사용자 로그인") }
is RequestAdminLoginDTO -> { TODO("일반 관리자 로그인") }
is RequestSuperAdminLoginDTO -> { TODO("슈퍼 관리자 로그인") }
}
}
}
이렇게 처리하여 타입 안정성을 확보할 수 있으며, 만약 구현되지 않은 케이스가 있다면
런타임에 오류를 발생하여 exhaustive check
가 가능한 설계가 됩니다.
마무리
서비스 초반에는 ID/PW만 받는 간단한 구조로 개발해도 괜찮습니다.
하지만 서비스가 커질수록 각 역할별로 인증 파라미터가 달라지는 요구 사항은 반드시 발생합니다.
이러한 추가 요구 사항이 생기거나, 제공하는 서비스가 점점 커질수록 sealed interface
를 활용한다면
구조를 깨지 않고, 확장성 있게 타입 안정성까지 챙길 수 있어 실무에서 강력한 무기가 될 것 같습니다.
추가 사항
sealed class
의 경우에는 다중 상속이 안 되기 때문에 기능을 확장하기에는 불편하지만
sealed interface
의 경우에는 역할 분리도 가능해서 전략 패턴, 로깅 기능 추가에도 유리할 수 있습니다.