[https://spring.io/guides/tutorials/rest/] 를 처음부터 따라하며 실습해보는 것을 정리하는 글입니다. 의역이나 오역이 있을 수 있으니 정확한 것은 원문을 참고해주세요.
왜 RESTful API를 사용할까?
그렇다면 웹의 이점은?
GET
, POST
, PUT
, DELTE
, ...)하지만 깨달아야 할 중요한 점은, REST가 표준 그 자체가 아니라 웹 기반 시스템을 구축하는 데 도움이 되는 아키텍처의 접근 방식, 스타일, 제약 조건이라는 것이다.
Spring Initializer를 이용해 dependencies로 Web
, JPA
, H2
를 넣어주고, 프로젝트 명을 Payroll로 하여 프로젝트를 생성하자.
Gradle로 사용해도 되지만 해당 문서에서는 maven 기반의 프로젝트로 실행한다.
난 Gradle이 익숙하기에 Gradle로 생성
회사의 직원을 관리하는 간단한 급여 시스템을 만들어 볼 것이다.
처음에는 REST를 사용하지 않고 개발할 것이며, 결과적으로 차이를 보기 위해 점차 RESTful 하게 코드를 수정해나갈 것이다.
package com.example.Payroll;
import lombok.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
@Getter @Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class Employee {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String role;
}
package com.example.Payroll;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
@Configuration
에 의해 application context가 load 될 때 자동 실행된다. log를 통해 저장된 결과를 출력한다.package com.example.Payroll;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LoadDatabase {
private static final Logger log = LoggerFactory.getLogger(LoadDatabase.class);
@Bean
CommandLineRunner initDatabase(EmployeeRepository repository) {
return args -> {
log.info("Preloading " + repository.save(new Employee("Kim", "Student")));
log.info("Preloading " + repository.save(new Employee("Lee", "Teacher")));
};
}
}
package com.example.Payroll;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequiredArgsConstructor
public class EmployeeController {
private final EmployeeRepository repository;
@GetMapping("/employees")
public List<Employee> getAll() {
return repository.findAll();
}
@PostMapping("/employees/{id}")
public Employee enrollEmployee(@RequestBody Employee newEmployee) {
return repository.save(newEmployee);
}
@GetMapping("/employees/{id}")
public Employee getOne(@PathVariable Long id) {
Optional<Employee> employee = repository.findById(id);
return employee.orElseThrow(() -> new EmployeeNotFoundException(id));
}
@PutMapping("/employees/{id}")
public Employee replaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) {
Optional<Employee> findEmployee = repository.findById(id);
return findEmployee.map(employee -> {
employee.setName(newEmployee.getName());
employee.setRole(newEmployee.getRole());
return repository.save(employee);
}).orElseGet(() -> {
newEmployee.setId(id);
return repository.save(newEmployee);
});
}
@DeleteMapping("/employees/{id}")
public void deleteEmployee(@PathVariable Long id) {
repository.deleteById(id);
}
}
@RestController
: 모든 응답은 HTTP Response Body에 담아 응답.
@RequestBody
: body에 있는 정보들을 읽음
@PathVariable
: 경로에 있는 정보를 읽음
EmployeeNotFoundException(Long id)
: 커스텀 exception
EmployeeNotFoundException.java
package com.example.Payroll;
public class EmployeeNotFoundException extends RuntimeException {
EmployeeNotFoundException(Long id) {
super("" + id + "번의 직원을 찾을 수 없습니다.");
}
}
위의 예외가 터지면, HTTP 404 에러를 렌더링하는 @ControllerAdvice
:
package com.example.Payroll;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@ControllerAdvice
public class EmployeeNotFoundAdvice {
@ResponseBody
@ExceptionHandler(EmployeeNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String employeeNotFoundHandler(EmployeeNotFoundException exception) {
return exception.getMessage();
}
}
이제 main 함수를 실행시키고 http://localhost:8080/employees
로 접근하면,
[{"id":1,"name":"Kim","role":"Student"},{"id":2,"name":"Lee","role":"Teacher"}]
이 뜰 것이다.
이제 저장되어 있지 않은 id 값을 통해 접근해보자.
http://localhost:8080/employees/3
으로 접근하면, 3번의 직원을 찾을 수 없습니다. 라고 뜰 것이다. 정상적으로 예외처리가 된 것을 확인할 수 있다.
그렇다면 이제는 POSTMAN을 사용하여 직원 등록을 해보자.
이제는 방금 등록한 직원의 role을 수정해보자.
마지막으로 3번 직원을 delete 해보자.
이상 무!!
여기까지 Spring MVC를 이용하여 직원을 GET
, POST
, PUT
, DELETE
해보았다.
저장되어 있지 않는 직원을 조회할 때에는 적절하게 예외 처리로 오류 페이지까지 띄어보았다.
다음 시간부터는 이 코드들을 수정하여 RESTful스럽게 수정해보겠다.