Spring에서 JSON 데이터를 Java 객체로 변환하기

Lee·2023년 4월 26일
0

스프링(Spring) 프레임워크를 사용하여 개발을 진행하다 보면, 클라이언트로부터 전송되는 JSON 데이터를 Java 객체로 변환하여 처리해야 하는 경우가 많습니다. 이번 글에서는 이러한 과정에서 발생할 수 있는 문제와 그 원인, 해결 방법에 대해 알아보겠습니다.

문제 상황

해당 예제는 다음과 같은 상황이 존재합니다:

  1. 클라이언트에서 JSON 형식의 데이터를 서버로 전송합니다.
  2. 서버에서는 JSON 데이터를 Java 객체로 변환하여 처리합니다.

예제 코드는 다음과 같습니다:

  • DTO 클래스
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);
    }
}
  • Entity 클래스
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;
        }
    }

}
  • ApiController 클래스
@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 클래스는 다음과 같습니다:

  • 수정 후 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 객체로 변환하려면 기본 생성자를 포함해야 합니다.

profile
잡다한 개발자

0개의 댓글