프로젝트

Clean Architecture에 대한 고찰 [2]

roder 2025. 5. 29. 22:38

# Mapper 존재에 대한 의문

 

회사에서 단독으로 클린 아키텍처[로 프로젝트를 진행할때 

제일 많이 했더 실수가 고귀한 Domain에 Serialzable 즉 

DTO를 적용했던 것이였다.

 

import com.google.gson.annotations.SerializedName
import java.io.Serializable

data class UserResponse(
    @SerializedName("id")
    val id: Int,

    @SerializedName("name")
    val name: String,

    @SerializedName("email")
    val email: String
) : Serializable

 

이걸 Domain에 넣고, 클린 아키텍쳐라고 구현하고 있으니 

나중에 알고서 얼마나 어이가 없었는지.....


클린 아키텍처 즉 Domain은 어떤 곳에서도 의존성을 지녀서는 안된다.

Repository Interface , UseCase도 아닌 가장 의존성을 지녀서는 안되는

이곳에 Serializable를 라이브러리를 의존하게 하였으니 얼마나 치명적인

실수인지 되돌아보게 된다.

 

우리는 API 통신을 할때 DTO라는 데이터 클래스를 만들어야 한다.

 

DTO는 API 문서 즉 자주 바뀌는 부분이기 때문에 이 부분은 절대적으로

Domain에 들어가서는 안된다. 

 

그럼 어디에 들어가야 할까 바로 API와 통신하는 부분인 Data 모듈에 들어가야 적절하다.

 

우리가 보통 API와 통신한다고 했을때, parameter로 넣은 데이터 클래스와 

Response로 받는 데이터 클래스가 있다.

 

Parameter로 넣는 데이터 클래스는 Domain에 넣어야 한다.

 

그럼 여기서 문제가 한가지 있다. parameter로 넣는 Data 클래스도 API 통신하기 위해서는

Serialzable에 의존해야 하는데 이걸 Domain에 넣는다면 도대체 어떻게 통신하라는 말인가??

 

그 해결책은 바로 Mapper에 있다.

 

간단하게 API로 로그인을 한다고 구현해보자

보통 Domain에는 이런 클래스들이 있을 것이다.

package data.model.request

import com.google.gson.annotations.SerializedName
import java.io.Serializable

data class LoginRequestDto(
    @SerializedName("email")
    val email: String,

    @SerializedName("password")
    val password: String
) : Serializable

 

-> 요청하는 부분

package data.model.response

import com.google.gson.annotations.SerializedName
import java.io.Serializable

data class LoginResponseDto(
    @SerializedName("token")
    val token: String,

    @SerializedName("userName")
    val userName: String
) : Serializable

 

-> 받는 부분

 

이 코드들은 서버와 통신하는 API DTO이다. Domain이 아니기 때문에

serializble 라이브러리에 의존해도 된다.

package domain.model

data class LoginRequestEntity(
    val email: String,
    val password: String
)
package domain.model

data class LoginResponseEntity(
    val token: String,
    val userName: String
)

 

이 코드들은 순수하게 어느곳도 의존하고 있지 않아야 한다.

그것이 클린 아키텍처의 규칙이기 때문이다.

 

자 그렇다면 겉보기에 똑같은 Entity와 DTO를 어떻게 연결해야 할까? 

 

package data.mapper

import data.model.request.LoginRequestDto
import data.model.response.LoginResponseDto
import domain.model.LoginRequestEntity
import domain.model.LoginResponseEntity

object AuthMapper {
    fun toDto(entity: LoginRequestEntity): LoginRequestDto =
        LoginRequestDto(email = entity.email, password = entity.password)

    fun toEntity(dto: LoginResponseDto): LoginResponseEntity =
        LoginResponseEntity(token = dto.token, userName = dto.userName)
}

 

바로 Data클래스에 있는 Mapper에서 Entity와 Data를 매핑해준다.

 

이렇게 Mapper를 통해 클린 아키텍처의 원칙을 준수하면서 순수 Data 클래스를

DTO로 변환하여 서버와 통신 할 수 있게 해준다.