IT/Android

πŸ”„ Kotlin Flowμ—μ„œ μ€‘λ³΅λ˜λŠ” DTO-Domain λ§€ν•‘ 쀄이기

μ™•κ·€ 2025. 5. 24. 20:59
728x90
λ°˜μ‘ν˜•

 

πŸ”„ Kotlin Flowμ—μ„œ μ€‘λ³΅λ˜λŠ” DTO-Domain λ§€ν•‘ 쀄이기

Kotlin Flowλ₯Ό μ‚¬μš©ν•  λ•Œ, APIλ‘œλΆ€ν„° 받은 데이터λ₯Ό DTO둜 νŒŒμ‹±ν•œ ν›„ 이λ₯Ό Domain 객체둜 λ³€ν™˜ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 것이 일반적인 νŒ¨ν„΄μž…λ‹ˆλ‹€. μ΄λŸ¬ν•œ 계측 λΆ„λ¦¬λŠ” μ½”λ“œμ˜ ν’ˆμ§ˆμ„ 높이고 예기치 μ•Šμ€ λ™μž‘μ„ λ°©μ§€ν•˜λŠ” 데 도움이 λ©λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ Flow λ‚΄μ—μ„œ 맀번 map μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•˜μ—¬ DTOλ₯Ό Domain으둜 λ³€ν™˜ν•˜λŠ” 것은 반볡적이고 μ€‘λ³΅λœ μž‘μ—…μ΄ 될 수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό κ°œμ„ ν•˜κΈ° μœ„ν•΄ λ‹€μ–‘ν•œ 방법을 μ‹œλ„ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.


πŸ§ͺ μ‹œλ„ #1: μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•œ μΌλ°˜ν™”

λ¨Όμ €, DTO ν΄λž˜μŠ€μ— asDomain ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•˜κ³  이λ₯Ό μΈν„°νŽ˜μ΄μŠ€λ‘œ μΆ”μƒν™”ν•©λ‹ˆλ‹€.

interface DomainConvertible<Domain> {
    fun asDomain(): Domain
}

그런 λ‹€μŒ, Flow에 λŒ€ν•œ ν™•μž₯ ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄ λͺ¨λ“  DTO-Domain λ³€ν™˜μ— μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

fun <T : DomainConvertible<R>, R> Flow<T>.mapToDomain(): Flow<R> = transform { emit(it.asDomain()) }

μž₯점:

  • λͺ¨λ“  DTO ν΄λž˜μŠ€μ— asDomain κ΅¬ν˜„μ„ κ°•μ œν•˜μ—¬ 일관성을 μœ μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • μΌλ°˜ν™”λœ μ—°μ‚°μžλ₯Ό 톡해 μ½”λ“œ 쀑볡을 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.

단점:

  • λͺ¨λ“  DTO ν΄λž˜μŠ€μ— μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•˜λ―€λ‘œ μ½”λ“œκ°€ λ³΅μž‘ν•΄μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ§€ν•‘ ν•¨μˆ˜κ°€ DTO 클래슀 내뢀에 μœ„μΉ˜ν•˜μ—¬ 가독성이 λ–¨μ–΄μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ§ͺ μ‹œλ„ #2: ν™•μž₯ ν•¨μˆ˜λ₯Ό ν™œμš©ν•œ 뢄리

μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ , DTO와 Flow에 λŒ€ν•œ ν™•μž₯ ν•¨μˆ˜λ₯Ό λ³„λ„μ˜ νŒŒμΌμ— μ •μ˜ν•˜μ—¬ λ§€ν•‘ λ‘œμ§μ„ 뢄리할 수 μžˆμŠ΅λ‹ˆλ‹€.

fun UserDto.asDomain(): User {
    return User(id = id ?: 0, name = name ?: "")
}

fun Flow<UserDto>.mapToDomain(): Flow<User> = map { it.asDomain() }

μž₯점:

  • λ§€ν•‘ λ‘œμ§μ„ DTO 클래슀 외뢀에 μ •μ˜ν•˜μ—¬ 가독성이 ν–₯μƒλ©λ‹ˆλ‹€.
  • ν•„μš”ν•œ κ²½μš°μ—λ§Œ ν™•μž₯ ν•¨μˆ˜λ₯Ό μ •μ˜ν•˜μ—¬ μœ μ—°ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

단점:

  • λͺ¨λ“  DTO에 λŒ€ν•΄ λ³„λ„μ˜ ν™•μž₯ ν•¨μˆ˜λ₯Ό μž‘μ„±ν•΄μ•Ό ν•˜λ―€λ‘œ 관리가 번거둜울 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ§ͺ μ‹œλ„ #3: μ œλ„€λ¦­μ„ ν™œμš©ν•œ 단일 ν™•μž₯ ν•¨μˆ˜

λͺ¨λ“  DTO-Domain λ³€ν™˜μ„ ν•˜λ‚˜μ˜ μ œλ„€λ¦­ ν™•μž₯ ν•¨μˆ˜λ‘œ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

inline fun <reified T, R> Flow<T>.mapToDomain(): Flow<R> = map {
    when (T::class) {
        UserDto::class -> (it as UserDto).asDomain() as R
        // λ‹€λ₯Έ DTO-Domain λ§€ν•‘ μΆ”κ°€
        else -> throw IllegalArgumentException("Unknown type")
    }
}

μž₯점:

  • 단일 ν™•μž₯ ν•¨μˆ˜λ‘œ λͺ¨λ“  DTO-Domain λ³€ν™˜μ„ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ½”λ“œ 쀑볡을 μ΅œμ†Œν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

단점:

  • 클래슀 νƒ€μž…μ„ λͺ…μ‹œμ μœΌλ‘œ 확인해야 ν•˜λ―€λ‘œ μœ μ§€λ³΄μˆ˜κ°€ μ–΄λ €μšΈ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μƒˆλ‘œμš΄ DTO-Domain 맀핑을 μΆ”κ°€ν•  λ•Œλ§ˆλ‹€ when μ ˆμ„ μˆ˜μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.

βœ… κ²°λ‘ 

각 μ ‘κ·Ό 방식은 μž₯단점이 있으며, ν”„λ‘œμ νŠΈμ˜ 규λͺ¨μ™€ νŒ€μ˜ μ„ ν˜Έλ„μ— 따라 μ μ ˆν•œ 방법을 μ„ νƒν•˜λŠ” 것이 μ€‘μš”ν•©λ‹ˆλ‹€. 우리 νŒ€μ€ κ²°κ΅­ κ°€μž₯ λ‹¨μˆœν•œ map μ—°μ‚°μžμ™€ DTO에 λŒ€ν•œ ν™•μž₯ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” 방법을 μ„ νƒν–ˆμŠ΅λ‹ˆλ‹€. 이 방법은 λͺ…μ‹œμ μ΄κ³  μ΄ν•΄ν•˜κΈ° μ‰¬μš°λ©°, μœ μ§€λ³΄μˆ˜ μΈ‘λ©΄μ—μ„œλ„ μœ λ¦¬ν•˜λ‹€κ³  νŒλ‹¨ν–ˆμŠ΅λ‹ˆλ‹€.

πŸ“š 좜처

Florent Blot, Redundant DTO-Domain Mapping in Kotlin Flow (Medium)

728x90
λ°˜μ‘ν˜•