본문 바로가기

Study/스프링 기반 REST API 개발

01) 이벤트 생성 API 개발

Inflearn : https://www.inflearn.com/course/spring_rest-api

GitHub : https://github.com/JunTaeINC/inflearn-rest-api

 

GitHub - JunTaeINC/inflearn-rest-api

Contribute to JunTaeINC/inflearn-rest-api development by creating an account on GitHub.

github.com

 

 


 

여러 채용공고를 보니 우대사항에 REST API에 대한 내용이 적지 않게 보였다. 간략하게 REST API를 알고 있지만 실제로 어떤 식으로 구현을 하는지 몰라서 학습하게 되었다. 다 학습하고 나면 기존 팀 프로젝트에 진행됐던 Grooveo에 적용해 볼 생각이다. 이미 알고 있던 어노테이션에 대해 조금 더 깊게 학습하고 강의는 한 5년 전 내용이라 최신버전에 맞는 어노테이션과 자바 17 버전으로 사용해서 구현할 예정이다.

 

 

 

 

 

 

1. @Enumerated

JPA에 제공하는 어노테이션이다. 엔티티의 필드가 Enum 타입일 경우 사용되며 Enum의 값을 어떠한 형태로 DB에 저장할지 결정한다. 
STRING을 권장한다. 이유는 만약 ORDINAL로 했을 경우 만약 순서가 바뀌거나 데이터를 추가를 할 경우 데이터의 정합성 문제를 야기시킬 수 있다. 또한 DB에서 조회할 경우에도 직관적이어서 이해하기 쉽다. 
그래서 장점만 있나 해서 단점을 찾아보니 ORDINAL에 비해 약간 더 많은 저장 공간을 차지하고, 문자열 비교가 숫자 비교보다는 느리다. 라는 단점이 있다.

 

1.1 ORDINAL

Enum의 순서를 DB에 저장한다. @Enumerated의 기본값이다.

public enum Animal {
    DOG,  // 순서: 0
    CAT,  // 순서: 1
    BIRD; // 순서: 2
}
public class Student {
	// ... 생략
    
	@Enumerated // @Enumerated(EnumType.ORDINAL) 같음
	private Animal animal;
}

Student student = new Student();

// DB에는 Animal에 0으로 저장됨
student.setAnimal(Animal.CAT);

 

 

 

1.2 STRING

각각의 이름이  DB에 저장된다.

public class Student {
	// ... 생략
    
	@Enumerated(EnumType.STRING)
	private Animal animal;
}

Student student = new Student();

// DB에는 Animal에 CAT으로 저장됨
student.setAnimal(Animal.CAT);

 

 

 

 

2. ObjectMapper

ObjectMapper는 리플렉션을 활용해 객체를 Json 형태의 문자열로 만들어준다. 이것을 직렬화라고 한다.

반대로 Json형태의 문자열을 객체로 만드는 것을 역질렬화라고 한다.

강의에서 테스트 코드를 통해 역직렬화를 통해 객체에 없는 필드를 발견할 경우 실패하도록 하기위해 ObjectMapper를 활용한다.

출처 : stackabuse.com

 

2.1 설정

강의는 스프링부트 2.2.5 버전을 사용하고 있다. 하지만 난 3.1.5 버전을 사용하고있다.

2.2.5 버전에서는 Jackson 직렬화를 application.properties에 추가하면 되지만

spring.jackson.deserialization.fail-on-unknown-properties=true

 

스프링 3.x 버전에서는 Jackson 라이브러리를 이용한 Json 직렬화/역직렬화 설정을 ObjectMapper 클래스를 사용해 직접 설정해야한다.

 

- @Configuration : 스프링에게 Bean을 정의하는 클래스라고 알려준다.

- @Bean : 이 어노테이션이 붙은 메소드가 반환하는 객체는 스프링 컨테이너에 의해 관리되는 빈으로 등록한다.

@Configuration
public class JacksonConfiguration {

   @Bean
   public ObjectMapper objectMapper() {
      ObjectMapper objectMapper = new ObjectMapper();

      return objectMapper;
   }
}

 

java.time.LocalDateTime not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: hello.study.restapi.event.Event["beginEnrollmentDateTime"])

 

⚠️ 하지만 에러가 발생했다. 지금 Event 엔티티에 LocalDateTime 타입 필드를 가지고 있는데 Jackson 라이브러리는 기본적으로 Java의 기본 데이터 타입과 몇 가지 추가적인 타입만을 지원하지만 Java 8 이후에 도입된 날짜/시간 API인 java.time 패키지의 클래스들은 기본적으로 지원하지 않는다. java.time 패키지가 Jackson이 만들어지고 이후에 나왔기 때문이라고 한다. 그래서 LocalDateTime를 직렬화/역직렬화를 하기위해선 jackson-datatype-jsr310 모듈을 추가해줘야 한다.

 

package hello.study.restapi.event;

import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Builder
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Event {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;
   private String name;
   private String description;
   private LocalDateTime beginEnrollmentDateTime;
   private LocalDateTime closeEnrollmentDateTime;
   private LocalDateTime beginEventDateTime;
   private LocalDateTime endEventDateTime;
   private String location;
   private int basePrice;
   private int maxPrice;
   private int limitOfEnrollment;
   private boolean offline;
   private boolean free;
   @Enumerated(EnumType.STRING)
   private EventStatus eventStatus = EventStatus.DRAFT;
}

 

- Maven

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.12.3</version>
</dependency>

- Gradle

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3'

 

나는 gradle을 사용하고 있으므로 위 의존성을 추가해준다.

 

@Configuration
public class JacksonConfiguration {

   @Bean
   public ObjectMapper objectMapper() {
      ObjectMapper objectMapper = new ObjectMapper();
      objectMapper.registerModule(new JavaTimeModule());

      return objectMapper;
   }
}

 

그 다음 ObjectMapper 모듈을 등록해준다.