엔티티에는 보통 해당 데이터의 생성,수정 시간이 포함한다. 예를 들어 새로운 회원이 언제 가입했는지, 그리고 로그인을 마지막으로 언제 했는지를 알기 위해서도 필요한 부분이다. 그럴 때 이제 엔티티마다 날짜 데이터를 등록/수정 하는 코드를 추가하게 되는데, 이럴 때마다 코드는 반복되고, 결국엔 지저분 해지는 결과를 초래한다. 이러한 문제를 해결하는 방법은 뭐가 있을까? 바로 "JPA Auditing" 이다.
1. BaseTimeEntity 만들고, @EntityListeners 을 통해 BaseTimeEntity 클래스가 Auditing 기능을 쓸 수 있도록 만든다.
package com.incheol.app.domain.post;
import java.time.LocalDateTime;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import lombok.Getter;
@Getter
@MappedSuperclass // 엔티티들이 BaseTimeEntity을 상속할 경우 BaseTimeEntity에 존재하는 필드들을컬럼으로 인식하도록 하는 어노테이션.
@EntityListeners(AuditingEntityListener.class) // BaseTimeEntity 추상클래서에 Auditing 기능을 포함시키는 어노테이
public abstract class BaseTimeEntity {
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime modifiedDate;
}
2. SpringBootAppApplication 클래스 위에 JPA Auditing 을 활성화 할 수 있도록 어노테이션을 추가한다.
@EnableJpaAuditing //JPA Auditing 어노테이션을 활성화 시키도록 하는 어노테이션.
@SpringBootApplication
public class SpringBootAppApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAppApplication.class, args);
}
}
3. Jpa Auditing을 쓸 Entity에 BaseTimeEntity을 상속시켜준다.
BaseTimeEntity을 상속 시킴으로써 Post 엔티티에는 아래와 같이 createdDate, modifiedDate 를 쓸수 있다고 보면 된다.
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime modifiedDate;
@Getter
@NoArgsConstructor // 기본 생성자 자동 추가 된다.
@Entity // 테이블과 매핑 될 클래스인 것을 나타낸다.
public class Post extends BaseTimeEntity {
@Id //PK를 나타낸다.
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increament 을 나타내기위한 전략
private Long id;
@Column(nullable = false , length = 500) // 컬럼에 대한 옵션 즉, not null과 컬럼의 길이를 정의할 수 있다.
private String title;
@Column(columnDefinition= "TEXT", nullable = false) // 컬럼의 길이를 500자 이상으로 나타내고 싶을 때 columnDefinition = "TEXT" 의 옵션을 적어준다.
private String content;
private String author; // 변수 위에 @Column을 붙이지 않아도, 클래스 위에 선언 되어있는 @Entity 어노테이션 때문에 변수 명을 기준으로 테이블의 컬럼이 디폴드로 생겨난다.
/*
* BaseTimeEntity을 상속 시킴으로써 Post 엔티티에는 아래와 같이 createdDate, modifiedDate 를 쓸수 있다고 보면 된다.
*
* @CreatedDate
* private LocalDateTime createdDate;
*
* @LastModifiedDate
* private LocalDateTime modifiedDate;
*/
@Builder // 해당 클래스의 빌더 패턴 클래스를 생성한다.
public Post(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
public void update(String title, String content ) {
this.content=content;
this.title=title;
}
}
package com.incheol.app.web.dto;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
private final String name;
private final int age;
}
HelloResponseDtoTest.java 을 생성한다.
테스트를 할땐 Given, When, Then 세 가지를 기억하면 된다.
Given : 시나리오 진행에 필요한 값을 설정한다.
When : 시나리오를 진행하는데 필요한 조건을 명시한다.
Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시한다.
package com.incheol.app.web.dto;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
public class HelloResponseDtoTest {
@Test
public void lombokTest() {
//Given : 시나리오 진행에 필요한 값을 설정한다.
String name = "incheol";
int age = 28;
//When : 시나리오를 진행하는데 필요한 조건을 명시한다.
HelloResponseDto dto = new HelloResponseDto(name, age);
//Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시한다.
assertThat(dto.getName()).isEqualTo(name);
assertThat(dto.getAge()).isEqualTo(age);
}
}
Junit 실행시켜서 테스트가 제대로 수행 되었는지 확인한다.
HelloController.java 에 Get /hello/dto API을 추가한다.
package com.incheol.app.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.incheol.app.web.dto.HelloResponseDto;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
@GetMapping("/hello/dto")
public HelloResponseDto helloDto(@RequestParam("name") String name, @RequestParam("age") int age) {
return new HelloResponseDto(name, age);
}
}
4번에서 추가한 기능을 테스트 코드로 작성해서 테스트를 해본다.
package com.incheol.app.web;
import static org.hamcrest.CoreMatchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
@RunWith(SpringRunner.class)
@WebMvcTest // Web(Spring MVC)에 집중할 수 있는 어노테이션
public class HelloControllerTest {
@Autowired
private MockMvc mvc; //웹 API을 테스트 할때 사용하는 가짜 데이터 , GET, POST 등에 대한 테스트를 할 수 있음.
@Test
public void returnHello() throws Exception {
String hello = "hello";
//테스트를 하는 행위(perform) => 하나씩 기대 값을 넣고 테스트를 한다.
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
@Test
public void returnHelloResponseDto() throws Exception {
//Given : 시나리오 진행에 필요한 값을 설정한다.
String name = "kabby";
int age = 28;
//When : 시나리오를 진행하는데 필요한 조건을 명시한다.
//Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시한다.
mvc.perform(get("/hello/dto")
.param("name", name) // name => kabby
.param("mount", String.valueOf(age))) // age => 28
.andExpect(status().isOk()) // 파라미터 2개의 기대 값에 대하여 상태 값 200을 받는지 비교한다.
.andExpect(jsonPath("$.name", is(name))) // {name: "kabby"} => jsonPath을 통해 값을 비교를 한다.
.andExpect(jsonPath("$.mount", is(age))); // {age: 28} => jsonPath을 통해 값을 비교를 한다.
}
}
Junit 실행시켜서 테스트가 제대로 수행 되었는지 확인한다.
실제로 화면에서 {"name":"kabby","age":28} 이 나왔는지 확인한다.
오늘은 웹 API을 테스트 할 때 Get 기준 파라미터가 존재 할때 어떻게 테스트를 하는지 알아봤다.