REST API란?
REST API는 REST(representational state transfer)를 기반으로 하여 HTTP 요청을 사용하여 데이터에 액세스하고 사용하는 API의 아키텍처 스타일입니다.
CRUD란?
API를 구축할 때 모델이 제공하는 4가지 기본 유형의 기능, Create(생성), Read(읽기), Update(수정), Delete(삭제)를 CRUD라고 합니다.
CRUD 구현 (Entity, Repository, Service, Controller)
JPA와 H2 데이터베이스를 사용하여 간단하게 구현했습니다.
User.java (Entity)
package com.example.practice.impl;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@Getter
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private int age;
private String address;
@Builder
public User(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void update(String address) {
this.address = address;
}
}
UserRepository.java (Repository)
package com.example.practice.impl;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
UserService.java (Service)
package com.example.practice.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다."));
}
@Transactional
public Long save(User user) {
return userRepository.save(user).getId();
}
@Transactional
public Long update(Long id, String address) {
User user = findById(id);
user.update(address);
return id;
}
@Transactional
public void delete(Long id) {
User user = findById(id);
userRepository.delete(user);
}
}
UserController.java (Controller)
package com.example.practice.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RequiredArgsConstructor
@RestController
public class UserController {
private final UserService userService;
@PostMapping("/user")
public Long create(@RequestBody User user) {
return userService.save(user);
}
@GetMapping("/user/{id}")
public User read(@PathVariable Long id) {
return userService.findById(id);
}
@PostMapping("/user/{id}/update")
public Long update(@PathVariable Long id, @RequestBody String address) {
return userService.update(id, address);
}
@PostMapping("/user/{id}/delete")
public Long delete(@PathVariable Long id) {
userService.delete(id);
return id;
}
}
JUnit5로 CRUD 테스트
- Create Test
@Test
void create_test() {
// given
String name = "John Doe";
int age = 27;
String address = "Seoul";
User user = User.builder()
.name(name)
.age(age)
.address(address)
.build();
String url = "http://localhost:" + port + "/user";
// when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, user, Long.class);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
User savedUser = userService.findById(responseEntity.getBody());
assertThat(savedUser.getName()).isEqualTo(name);
assertThat(savedUser.getAge()).isEqualTo(age);
assertThat(savedUser.getAddress()).isEqualTo(address);
}
TestRestTemplate을 이용해 POST 요청으로 유저의 정보를 넘겨 DB에 저장하고, DB에서 해당 유저의 정보를 조회하여 입력한 정보와 같은지 검증
- Read Test
@Test
void read_test() {
// given
String name = "John Doe";
int age = 27;
String address = "Seoul";
Long savedId = userRepository.save(User.builder()
.name(name)
.age(age)
.address(address)
.build()).getId();
String url = "http://localhost:" + port + "/user/" + savedId;
// when
ResponseEntity<User> responseEntity = restTemplate.getForEntity(url, User.class);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
User readUser = responseEntity.getBody();
assertThat(readUser.getName()).isEqualTo(name);
assertThat(readUser.getAge()).isEqualTo(age);
assertThat(readUser.getAddress()).isEqualTo(address);
}
DB에 유저 정보를 하나 저장한 후 GET 요청으로 해당 유저를 조회하여 정보가 같은지 비교합니다.
- Update Test
@Test
void update_test() {
// given
String name = "John Doe";
int age = 27;
String address = "Seoul";
Long savedId = userRepository.save(User.builder()
.name(name)
.age(age)
.address(address)
.build()).getId();
String url = "http://localhost:" + port + "/user/" + savedId + "/update";
String address2 = "Busan";
// when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, address2, Long.class);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
User updatedUser = userService.findById(savedId);
assertThat(updatedUser.getAddress()).isEqualTo(address2);
}
DB에 유저 정보를 하나 저장한 후 /user/id/update의 경로로 POST 요청, 요청할 때 수정할 정보인 address2를 전달합니다. 요청이 종료된 후 DB에서 유저 정보를 조회하여 update 요청 시 전달한 정보와 같은지 조회합니다.
- Delete Test
@Test
void delete_test() {
// given
String name = "John Doe";
int age = 27;
String address = "Seoul";
Long savedId = userRepository.save(User.builder()
.name(name)
.age(age)
.address(address)
.build()).getId();
String url = "http://localhost:" + port + "/user/" + savedId + "/delete";
// when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, null, Long.class);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThatThrownBy(() -> userService.findById(savedId)).isInstanceOf(IllegalArgumentException.class);
}
DB에 유저 정보를 하나 저장한 후, /user/id/delete의 경로로 POST 요청을 통해 해당 유저 정보를 삭제합니다. 요청이 완료된 후 DB에서 해당 유저 정보를 조회하여 IllegalArgumentException이 발생하는지 확인합니다. UserService.java에서 유저 정보를 조회할 때 해당 유저가 존재하지 않는다면 해당 예외가 발생하도록 구현했습니다.
전체 테스트를 실행하면 잘 통과되는 것을 확인하실 수 있습니다.
UserControllerTest.java
package com.example.practice.web;
import com.example.practice.impl.User;
import com.example.practice.impl.UserRepository;
import com.example.practice.impl.UserService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.*;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {
@LocalServerPort
int port;
@Autowired
TestRestTemplate restTemplate;
@Autowired
UserRepository userRepository;
@Autowired
UserService userService;
@AfterEach
public void afterEach() {
userRepository.deleteAll();
}
@Test
void create_test() {
// given
String name = "John Doe";
int age = 27;
String address = "Seoul";
User user = User.builder()
.name(name)
.age(age)
.address(address)
.build();
String url = "http://localhost:" + port + "/user";
// when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, user, Long.class);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
User savedUser = userService.findById(responseEntity.getBody());
assertThat(savedUser.getName()).isEqualTo(name);
assertThat(savedUser.getAge()).isEqualTo(age);
assertThat(savedUser.getAddress()).isEqualTo(address);
}
@Test
void read_test() {
// given
String name = "John Doe";
int age = 27;
String address = "Seoul";
Long savedId = userRepository.save(User.builder()
.name(name)
.age(age)
.address(address)
.build()).getId();
String url = "http://localhost:" + port + "/user/" + savedId;
// when
ResponseEntity<User> responseEntity = restTemplate.getForEntity(url, User.class);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
User readUser = responseEntity.getBody();
assertThat(readUser.getName()).isEqualTo(name);
assertThat(readUser.getAge()).isEqualTo(age);
assertThat(readUser.getAddress()).isEqualTo(address);
}
@Test
void update_test() {
// given
String name = "John Doe";
int age = 27;
String address = "Seoul";
Long savedId = userRepository.save(User.builder()
.name(name)
.age(age)
.address(address)
.build()).getId();
String url = "http://localhost:" + port + "/user/" + savedId + "/update";
String address2 = "Busan";
// when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, address2, Long.class);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
User updatedUser = userService.findById(savedId);
assertThat(updatedUser.getAddress()).isEqualTo(address2);
}
@Test
void delete_test() {
// given
String name = "John Doe";
int age = 27;
String address = "Seoul";
Long savedId = userRepository.save(User.builder()
.name(name)
.age(age)
.address(address)
.build()).getId();
String url = "http://localhost:" + port + "/user/" + savedId + "/delete";
// when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, null, Long.class);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThatThrownBy(() -> userService.findById(savedId)).isInstanceOf(IllegalArgumentException.class);
}
}
'Spring' 카테고리의 다른 글
[Spring][Error] Spring Security 적용할 때 circular reference, dependency가 cycle 형성하는 것 해결하기 (2) | 2022.03.14 |
---|---|
AWS RDS(MariaDB)와 IntelliJ Database 연동하기 - [스프링 부트와 AWS로 혼자 구현하는 웹 서비스] (0) | 2022.03.05 |
JPA Auditing으로 Entity의 createdDate, modifiedDate 관리 (0) | 2022.02.26 |
Spring Data JPA & H2 데이터베이스 연결하기(인메모리, TCP) (0) | 2022.02.24 |
Spring Initializr로 Spring Boot 프로젝트 생성하고 IntelliJ에서 GitHub 연동하기 (0) | 2022.02.22 |
댓글