작성
·
505
1
문제 3번 풀던 중 생긴 오류를 잡으면서 생긴 의문입니다.
DTO에 List를 만들어서 JSON 배열을 POST형식의 body로 받는 문제였습니다.
아래는 변수를 final로 선언했을 때 생기는 오류입니다.
ERROR 8512 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error
선언하는 방식에 문제가 없다고 여겨 수정해보다가 final과 constructor를 지우고 다시 요청을 날려보니 정상적인 값의 출력을 확인했습니다.
이 final과 constructor 이 무슨 역할을 하고 있는지, 왜 final로 선언되었을 때는 오류가 났는지, 언제 사용되어야 하는지 등 검색을 해보지만 잘 이해가 되지 않아서 질문 남깁니다.
답변 2
1
안녕하세요 오쩜오님! 🙂 좋은 질문 감사합니다.
아래 인프런 AI 인턴이 잘 언급해주기도 했는데요!
결론부터 말씀드리면 "명확히 설명드리기는 어렵지만, DTO의 조건이 분명 존재하긴 합니다"로 정리드릴 수 있을 것 같습니다.
명확히 설명해드리기 어려운 이유는 "스프링의 버전마다, 그리고 상황 마다 조금씩 다르게 적용되기" 때문입니다. 🥲
예를 들어 스프링 내부에서는 DTO를 인스턴스화 하기 위해
생성자의 개수를 확인하고 생성자의 여부에 따라, 기본생성자를 사용할지, 생성자를 사용할지
또 DTO 구조와 설정한 옵션에 따라 필드에 바로 값을 집어 넣을지 setter를 활용할지 등을 결정하는 부분이 있습니다.
아마 추측하기로는, final + constructor를 사용했을 때는 다음과 같은 이유로 오류가 난듯 합니다. (정확한 이유는 아닐 수 있고 추측입니다! 정확한 건 해당 버전의 스프링 부트 내부 코드를 디버거로 따라가 보아야 할 것 같아요!)
DTO를 인스턴스화 하기 위해 기본 생성자를 찾습니다.
기본 생성자를 찾지 못해, 필드에 직접 접근해 값을 바꾸려고 합니다.
필드가 private이라 찾지 못했거나, 찾았다고 하더라도 final이라 값을 임의로 집어 넣을 수 없어 에러가 발생했습니다.
대략 이런 느낌으로 봐주시면 될 것 같습니다!
추가로 final
키워드는 잘 아시겠지만, 변수에 final
키워드를 사용하면 해당 값을 한 번 설정한 이후에는 다시 바꿀 수 없게 하는 역할을 합니다!
따라서 기본 생성자로 클래스를 인스턴스화 하게 되면, final
키워드의 변수에 값을 재지정할 수 없게 되는것이죠.
이런 부분이 tricky하다 보니 GET, POST 별로 어떤 조합이면 DTO 인스턴스화가 잘 되더라~를 확인한 다음 해당 조합을 계속해서 사용하는 경향이 있습니다. 🙂
답변이 도움이 되었으면 좋겠습니다.
감사합니다! 🙏
0
안녕하세요, 인프런 AI 인턴입니다. 오쩜오님의 질문에 대해, final 키워드와 생성자(constructor)의 역할, 그리고 왜 final로 선언했을 때 오류가 나타났을지에 대해 해결할 수 있는 관련 내용을 찾았습니다.
주신 질문의 핵심은 final
키워드 사용과 관련된 문제와 private
및 private final
선언의 차이점에 대한 이해입니다. final
키워드는 변수가 한 번 초기화되면 (인스턴스 생성 시 혹은 선언 시) 그 이후로는 값을 변경할 수 없도록 하는 “불변성”을 부여합니다. 따라서, final
로 선언된 객체의 경우, 객체 내부 상태(필드)가 변경되지 않아야 하며, 이는 특히 쓰레드 안전성(thread safety)과 객체의 안정성을 높이는데 기여합니다.
DTO (Data Transfer Object)에 List
를 final
로 선언하신 경우, 해당 리스트는 생성자를 통해 한 번만 초기화되어야 합니다. 오류 메시지와 상황으로 미루어 볼 때, final
로 선언된 리스트가 적절하게 초기화되지 않았거나, Spring Framework의 데이터 바인딩 과정에서 final
필드와 관련된 문제가 발생한 것으로 보입니다.
이와 유사한 문제 해결 방법을 제공하는 아래 질문을 참고하시면 도움이 될 것 같습니다:
final
필드와 생성자 없이 스프링이 어떻게 HTTP 요청의 바디를 객체에 바인딩하는지의 과정을 설명합니다. 특히, Spring의 @RequestBody
어노테이션과 관련하여 final
필드의 처리 방식에 대한 통찰을 줄 수 있습니다.기본적으로 final
필드는 생성자에서만 초기화할 수 있고, 이후에는 값을 변경할 수 없습니다. 따라서, DTO를 설계할 때는 객체의 불변성이 필요한 경우에만 final
을 사용해야 합니다. 만약 Spring Framework를 사용하여 데이터 바인딩을 수행하는 경우, final
필드는 적절한 생성자나 Setter 메소드를 통해 초기화되어야 하며, 불변 객체를 설계할 목적이 아니라면 final
사용을 피하는 것이 좋습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.