728x90
발단
최근 작업중인 프로젝트에서 등록 기능에 파일이 포함되어야 할 수 있기 때문에 @RequestBody 가 아닌 @ModelAttribute 로 mutlipart/form-data 타입의 요청을 다루어야 하였다. 이 때, dto 가 2-depth 가 되며 생긴 문제이다.
예시 코드
@RestController
@RequestMapping("")
class TestController {
@PostMapping("")
fun hiPost(
@ModelAttribute body: TestDto,
){
...
}
}
data class TestDto(
var name: String = "",
var testInnerDto: TestInnerDto
)
data class TestInnerDto(
var age: Int
)
오류 발생
1차 시도
java.lang.NullPointerException: Parameter specified as non-null is null: method com.test.TestDto.<init>, parameter testInnerDto
요약 : non-null 로 선언한 파라미터가 null 이기 때문에 NullPointerException 발생
즉, TestDto 를 deserialize 하는 과정에서 testInnerDto 의 값을 입력해주지 못하였고 입력해주지 못하니 non-null type 이 null 이 되었음으로 인해 오류가 발생한듯 보인다.
2차 시도 - testInnerDto 를 nullable 한 변수로 수정
java.lang.NoSuchMethodException: com.test.TestInnerDto.<init>()
요약 : TestInnerDto 의 비어있는 생성자가 없으니 NoSuchMethodException 발생
생성자가 없다고 하는 것을 보니 reflection 을 통해 TestInnerDto 를 구성하려 시도하였지만 비어있는 생성자가 없어 오류가 발생한 듯 보인다.
3차 시도 - TestInnerDto class 에 임의로 비어있는 cosntrcutor 제공
성공
반만 해결
해결책1
@RestController
@RequestMapping("")
class TestController {
@PostMapping("")
fun hiPost(
@ModelAttribute body: TestDto,
){
...
}
}
data class TestDto(
var name: String = "",
var testInnerDto: TestInnerDto?
)
class TestInnerDto{
var age: Int = 0
constructor(){
}
constructor(age: Int){
this.age = age
}
}
- testInnerDto 를 nullable type 으로 수정
- TestInnerDto 에 대해 비어있는 생성자 구현
해결책2
...
data class TestDto(
var name: String = "",
var testInnerDto: TestInnerDto = TestInnerDto()
)
...
- testInnerDto 를 not-null type 으로 구성하고 비어있는 생성자로 임의의 값을 입력
반을 해결했다는 이유
- `해결책1`에서 TestDto 의 testInnerDto 필드가 로직상 반드시 요청되는 경우 nullable 하지 않음으로 sementic 적으로 잘못된 방법이다.
- TestInnerDto 의 필드인 age 는 현재 primitive type 이기 때문에 기본값을 입력해주어야 하지만, String 으로 변경한 후 lateinit 구문을 통해 늦은 초기화로 not-null type 을 보장하려 할 때 비어있는 생성자는 age 를 초기화 해주지 못하기 때문에 잘못 사용할 가능성이 높다.
- `해결책2`에서 testInnerDto 에 대해 not-null type 을 보장하려 할 때 기본값으로 TestInnerDto() 인스턴스를 입력하게 되면 `2번`문제를 위반한다.
결론
2-depth 이상을 @ModelAttribute 로써 다루고자 할 때 kotlin 에서는 방법이 위와 같이 반만 해결된 방식을 사용할 수 밖에 없는 것으로 보인다.
혹여나 제가 모르는 해결책이 있다면 댓글로 링크나 설명부탁드립니다!
728x90