응답 모델을 모두 Nullable로 하면 좋은 이유 (feat.Gson 사용 시 null 문제)

송규빈·6일 전
0
post-thumbnail

개요

응답 모델의 프로퍼티를 모두 Nullable로 하면 안전한(좋은) 이유에 대해 설명해보고자 한다.
Gson을 사용하여 프로젝트를 진행하며 생겼던 이슈에 대해서도 함께 말할 것이다.

이슈

API 통신 중 아래와 같은 에러를 발견했다.

java. lang-NullPointerException: Parameter specified as non-null is null
non-null 타입인 positionId 매개변수에 null이 들어왔다는 것이다.
모델 구조는 대충 아래와 같았다.

// DTO
data class UserDTO(
val number:String?,
val positionId:String
...
)

// Domain Model
data class User(
val number:String,
val positionId:String
)

오류를 보니 DTO -> Domain 모델로의 Mapping과정에서 생겼다.

DTO는 서버 스펙을 따랐지만, 실수로 nullablepositionIdnon-null로 선언했다.

궁금증

non-null 타입인데 서버에서 null이 내려왔으니 에러가 발생하는 건 당연한 것이었다.
하지만 에러가 발생한 위치가 이상했다.
위와 같은 상황이면 DTO로 역직렬화를 할 때 에러가 생겼어야 하는건데, Domain model로의 Mapper 함수에서 에러가 발생했다는 것이다.

아래와 같이 DTO 객체가 생성되었다는 것이다.
UserDTO(number:String = "1234", positionId:String = null)
분명히 non-null 타입임에도 positionId의 값은 null이 들어와있다.
테스트 코드로 재현해보겠다.

private val json = """
            {
                "id": 1,
                "name": null,
                "email": "alice@example.com"
            }
        """
        
    @Test
    fun `Gson 역직렬화 테스트`() {
        val user = gson.fromJson(json, GsonUser::class.java)

        println(user)
    }

    data class GsonUser(
        val id: Int,
        val name: String,
        val email: String,
    )
// 결과
GsonUser(id=1, name=null, email=alice@example.com)
        

namenon-null 타입인데 null이 들어온다.

kotlinx.serialization

kotlinx.serialization의 경우는 다르다.

    @Test
    fun `Json 역직렬화 테스트`() {
        val user = jsonFormatter.decodeFromString<JsonUser>(json)
        println(user)
    }

    @Serializable
    data class JsonUser(
        val id: Int,
        val name: String,
        val email: String,
    )


Gson과 달리 역직렬화 과정에서 에러가 발생한다.

원인

왜 Gson은 non-null 타입에 null 값이 할당될 수 있는 것일까?
왜 kotlinx.serialization은 null값이 할당되기 전에 에러가 일어날 수 있는걸까?
둘의 가장 큰 차이는 Gson은 내부 코드가 자바로 되어있고, kotlinx.serialization은 코틀린으로 되어있기 때문이다.

그렇기에 Gson은 코틀린의 non-null 타입을 이해하지 못한다.
이러한 이유로 Gson은 null 값을 non-null타입에 역직렬화 할 때 에러없이 수행하는 것이다.
Gson은 기본적으로 리플렉션을 사용한다.
(kotlinx.serialization은 리플렉션을 사용하지 않고, 컴파일 플러그인을 사용한다고 합니다. 그 이유는 멀티플랫폼을 고려하여 설계가 되었기 때문이고, Reflection Kotlin/JVM 대상만 가능하기 때문이다.)


DTO의 프로퍼티는 nullable로 하자

이번 상황은 null에 대한 타입 선언을 실수한 제 탓이기도 하지만, 개발자 입장에서 이런 것들을 신경쓰기란 꽤나 번거로운 일이다.
개발하다보면 몇십개가 넘는 필드들을 마주할 텐데 하나하나 이 필드들의 Null 여부를 판단하기에는 쉽지 않은 일이다.
또한, 실수가 나올 수도 있고 서버측에서 공지없이 타입이 변경될 수도 있는 것이다.
그러므로 우리 모두 DTO 프로퍼티는 nullable로 안전하게 다루도록 하자!

profile
🚀 상상을 좋아하는 개발자
post-custom-banner

0개의 댓글