스프링(Spring) 프레임워크를 사용하여 개발을 진행하다 보면, 클라이언트로부터 전송되는 JSON 데이터를 Java 객체로 변환하여 처리해야 하는 경우가 많습니다. 이번 글에서는 이러한 과정에서 발생할 수 있는 문제와 그 원인, 해결 방법에 대해 알아보겠습니다.
해당 예제는 다음과 같은 상황이 존재합니다:
예제 코드는 다음과 같습니다:
package com.example.testproject.dto;
import com.example.testproject.entity.Article;
import lombok.AllArgsConstructor;
import lombok.ToString;
@AllArgsConstructor
@ToString
public class ArticleDTO {
private Long id;
private String title;
private String content;
public Article toEntity() {
return new Article(id, title, content);
}
}
package com.example.testproject.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Entity
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Getter
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String title;
@Column
private String content;
public Long getId() {
return id;
}
public void patch(Article article) {
if (article.title != null) {
this.title = article.title;
}
if (article.content != null) {
this.content = article.content;
}
}
}
@RestController
@Slf4j
public class ArticleApiController {
/* 기존 코드 생략 */
// PATCH
@PatchMapping("/api/articles/{id}")
public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleDTO dto) {
Article article = dto.toEntity();
log.info("id: {}, article: {}", id, article.toString());
Article target = articleRepo.findById(id).orElse(null);
if (target == null || id != article.getId()) {
log.info("잘못된 요청! id: {}, article: {}", id, article.toString());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
target.patch(article);
Article updated = articleRepo.save(target);
return ResponseEntity.status(HttpStatus.OK).body(updated);
}
}
이 상황에서 다음과 같은 오류가 발생했습니다:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.example.testproject.dto.ArticleDTO]] with root cause
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.testproject.dto.ArticleDTO` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
위 오류의 원인은 JSON 데이터를 Java 객체로 변환하는 과정에서 발생한 것입니다. 이 과정은 스프링 내부에서 Jackson 라이브러리를 사용하여 수행됩니다. Jackson 라이브러리는 기본적으로 클래스에 기본 생성자가 필요합니다. 기본 생성자가 없을 경우, 객체를 생성하고 JSON 데이터를 해당 객체의 필드에 할당할 수 없기 때문입니다. 따라서, 기본 생성자가 없는 경우 Jackson 라이브러리는 객체를 생성할 수 없어 InvalidDefinitionException
예외가 발생합니다.
이 문제를 해결하기 위해 기본 생성자를 추가해야 합니다. 이 경우 Lombok 라이브러리의 @NoArgsConstructor
애너테이션을 사용하여 기본 생성자를 생성할 수 있습니다.또한 추가적으로 Patch 데이터를 받아오기 위해@Getter
, @Setter
애너테이션을 사용했습니다.
수정된 DTO 클래스는 다음과 같습니다:
package com.example.testproject.dto;
import com.example.testproject.entity.Article;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class ArticleDTO {
private Long id;
private String title;
private String content;
public Article toEntity() {
return new Article(id, title, content);
}
}
이렇게 기본 생성자를 추가하면, Jackson 라이브러리가 정상적으로 객체를 생성하고 JSON 데이터를 해당 객체의 필드에 할당할 수 있게 됩니다.
JSON 데이터를 Java 객체로 변환하는 과정에서 기본 생성자의 중요성을 알아보았습니다. 기본 생성자는 Jackson 라이브러리가 객체를 생성하고 필드에 값을 할당하기 위해 필요합니다. 기본 생성자가 없을 경우, 이 과정에서 오류가 발생할 수 있습니다. 따라서, JSON 데이터를 Java 객체로 변환하려면 기본 생성자를 포함해야 합니다.