본문 바로가기
Blog/TIL

2025-01-14 (화)

by 코젼 2025. 1. 14.
728x90
반응형

sealed class

sealed class 를 사용하여 하위 클래스의 유형을 제한하고, 안전하고 명확하게 관리하려는 목적.
상속 가능한 클래스 계층을 제한할 때 사용함.

  • 특징
    • 제한된 상속: sealed class 를 상속할 수 있는 클래스는 같은 파일 내에서만 정의될 수 있다.
    • when 문에서 모든 하위 클래스를 컴파일 시점에 확인할 수 있어 안전한 타입 검사가 가능하다.

@Hidden

  • OpenAPI(Swagger) 에서 해당 필드를 문서화하지 않고 숨기기 위해 사용함.
  • 비즈니스 로직에서 사용되지만 API 사용자에게 노출할 필요가 없는 내부 구현 세부 사항인 경우 어노테이션을 사용해 숨김.

@JsonIgnore

  • Jackson 라이브러리가 해당 필드를 JSON 직렬화/역직렬화 과정에서 무시하도록 함.
  • API 요청 또는 응답에 포함되지 않기 위해서 사용함.
  • 내부적으로 동작을 제어하기 위한 값일 뿐, 외부 클라이언트가 제공하거나 볼 필요가 없는 데이터일 경우 제외함.

필드를 숨긴 이유는 이 값이 내부 구현을 위한 로직일 뿐, 외부 API 사용자에게 노출할 필요가 없기 때문임.

class DTO {  

    sealed class Request(  
        @Hidden  
        @JsonIgnore        
        val type: PlatformType,  
    )  

    data class ARequest(  
        val code: String,  
        val aId: String,
    ) : Request(PlatformType.A)  

    data class MetaRequest(  
        val token: String,  
        val bId: String,  
    ) : Request(PlatformType.B)  
}

enum class PlatformType(  
    val description: String,  
) {  
    A("---") {  
        override fun existsObjectiveType(objectiveType: ObjectiveType) =  
            listOf(ObjectiveType.TRAFFIC).contains(objectiveType)  
    },  
    B("+++") {  
        override fun existsObjectiveType(objectiveType: ObjectiveType) =  
            listOf(ObjectiveType.TRAFFIC).contains(objectiveType)  
    }  
    ;  

// 공통 인터페이스 강제, 추상화 및 다형성
// - abstract 는 서브클래스 또는 Enum 상수에서 반드시 특정 메서드를 구현하도록 강제 해야함.
    abstract fun existsObjectiveType(objectiveType: ObjectiveType): Boolean  
}
  • 사용 예시
    PlatformType.A.existsObjectiveType(ObjectiveType.TRAFFIC) // true
    PlatformType.B.existsObjectiveType(ObjectiveType.OTHER) // false

mockk

relaxed = true

mockk<ObjectMapper>() != mockk<ObjectMapper>(relaxed = true)
  • 기본 동작 및 반환 타입과 관련이 있음.
  • relaxed = true 로 설정하는 경우, mockk 에서 모든 메서드 호출에 대해 기본 값을 반환하도록 설정함
    • 예상치 못한 타입 캐스팅 문제 발생 가능성 있음.
  • java.lang.ClassCastException: class java.lang.Object cannot be cast to class...

every 제네릭 타입 처리 방식

every { objectMapper.readValue(redis, any<TypeReference<AdvertiserAuthDTO.Response>>()) } returns user  
  • any: 객체의 어떤 인스턴스도 허용한다는 의미.
  • TypeReference: Jackson이 제네릭 타입의 데이터를 매핑할 때 사용하는 클래스.
  • 런타임에 구체적인 제네릭 타입 정보를 유지함.
    every { objectMapper.readValue<AdvertiserAuthDTO.Response>(redis) } returns user
  • Kotlin 의 제네릭을 직접 사용하는 방식.
  • Kotlin 은 기본적으로 제네릭 타입 정보를 컴파일 시점에만 유지하고, 런타임에는 해당 정보를 제거함.
  • Jackson 은 런타임 시점에 제네릭 정보를 필요로 하는데, 이 방식에서는 TypeReference 가 없으므로 Jackson 이 런타임 타입을 정확히 알 수 없음.
  • readValue<AdvertiserAuthDTO.Response> 는 리플렉션을 사용한 내부 호출을 포함할 수 있는데, mockk 는 리플렉션 기반의 호출을 제대로 mock 하지 못할 수 있음.
    • mockk 가 호출을 인식하지 못해 MockException 발생.
728x90
반응형

'Blog > TIL' 카테고리의 다른 글

2025-01-16 (목)  (0) 2025.01.16
2025-01-15 (수)  (0) 2025.01.15
2025-01-13 (월)  (1) 2025.01.13
2025-01-10 (금)  (1) 2025.01.10
2025-01-09 (목)  (0) 2025.01.09

댓글