728x90
반응형
[User Service API 구현]
1) Domain Class 생성
package com.example.restfulwebservice.user;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;
@Data
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private Date joinDate;
}
2) DAO + Service class 생성 (db연동을 하지 않을 것이기때문에 DAO 클래스가 딱히 필요없음)
package com.example.restfulwebservice.user;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
// 통합 클래스 이름 Dao + Service
@Service
public class UserDaoService {
private static List<User> users = new ArrayList<>();
private static int userCount = 3;
// static block
static{
users.add(new User(1, "Kenneth",new Date()));
users.add(new User(2, "Alice",new Date()));
users.add(new User(3, "Elena",new Date()));
}
// 모든 유저 리스트 반환
public List<User> findAll(){
return users;
}
// 유저 회원 가입 + 유저 count up
public User save(User user){
if(user.getId()== null){
user.setId(++userCount);
}
users.add(user);
return user;
}
// 한명의 유저 반환
public User findOne(int id){
for(User user : users){
if(user.getId()== id){
return user;
}
}
return null;
}
}
3) Controller class 생성 -> service를 파라미터로 받아 의존성 주입
package com.example.restfulwebservice.user;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private UserDaoService service;
// 생성자를 통한 의존성 주입
public UserController(UserDaoService service){
this.service=service;
}
4) 사용자 목록 조회를 위한 API 구현 -> Controller에 GET HTTP Method 구현
package com.example.restfulwebservice.user;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
private UserDaoService service;
// 생성자를 통한 의존성 주입
public UserController(UserDaoService service){
this.service=service;
}
// 사용자 리스트 리턴
@GetMapping("/users")
public List<User> retrieveAllUsers(){
return service.findAll();
}
// 사용자 한명 리턴
// GET /users/1 or /user/2
// --> controller로 전달되면 String 형태로 전달됨
// --> 자동으로 int 형 매핑
@GetMapping("/users/{id}")
public User retrieveUser(@PathVariable int id){
return service.findOne(id);
}
}
5) 사용자 등록을 위한 API 구현 -> Controller에 POST HTTP Method 구현
package com.example.restfulwebservice.user;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class UserController {
private UserDaoService service;
// 생성자를 통한 의존성 주입
public UserController(UserDaoService service){
this.service=service;
}
// 사용자 리스트 리턴
@GetMapping("/users")
public List<User> retrieveAllUsers(){
return service.findAll();
}
// 사용자 한명 리턴
// GET /users/1 or /user/2
// --> controller로 전달되면 String 형태로 전달됨
// --> 자동으로 int 형 매핑
@GetMapping("/users/{id}")
public User retrieveUser(@PathVariable int id){
return service.findOne(id);
}
// 새로운 사용자를 등록하는 method 구현
@PostMapping("/users")
public void createUser(@RequestBody User user){
User savedUser = service.save(user);
}
}
http://localhost:8088/users의 경우 GetMapping과 PostMapping으로 나뉘어짐 -> http method를 달리하면 전혀 다른 요청이 된다. GET -> 유저 리스트 요청 POST -> 새로운 유저 등록 |
6) HTTP Status Code 제어
// 새로운 사용자를 등록하는 method 구현
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user){
User savedUser = service.save(user);
// 사용자에게 요청 값을 변환해주기
// fromCurrentRequest() :현재 요청되어진 request값을 사용한다는 뜻
// path : 반환 시켜줄 값
// savedUser.getId() : {id} 가변변수에 새롭게 만들어진 id값 저장
// toUri() : URI형태로 변환
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).build();
}
한 가지의 http method를 사용하는게 아니라 적절한 status code의 반환 필요 -> 좋은 REST API를 설계하는 방법 -> 네트워크 트래픽 감소 -> 좀 더 효율적인 어플리케이션 개발 가능 클라이언트가 지금 생성된 사용자의 id(서버가 자동으로 생성)를 알기 위해서는 서버에 또 한번 요청을 해야한다. 그것을 피하기 위해서 ResponseEntity<>를 사용하여 요청한 정보에 대한 HTTP 상태코드를 리턴받는다. |
7) HTTP Status Code 제어를 위한 Exception handling
// UserDAOService
public User findOne(int id){
for(User user : users){
if(user.getId()== id){
return user;
}
}
return null;
}
// UserController
@GetMapping("/users/{id}")
public User retrieveUser(@PathVariable int id){
User user = service.findOne(id);
if(user==null){
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
return user;
}
// 예외 클래스 생성
package com.example.restfulwebservice.user;
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
8) HTTP Status Code 제어를 위한 Exception handling - 특정한 예외 지정
@ResponseStatus(HttpStatus.NOT_FOUND)
// UserNotFoundException
package com.example.restfulwebservice.user;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
// HTTP Status Code
// 2XX -> OK
// 4XX -> Client 오류
// 5XX -> Server 오류
// 데이터가 존재하지않는 오류기때문에
// '5XX 오류메시지'가 아니라 'NotFound' 오류메시지지 전달해주기
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
9) Spring AOP를 이용한 Exception Handling
// ExceptionResponse
package com.example.restfulwebservice.exception;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
// 예외처리를 하기위해 사용되는 자바 객체
// 모든 Controller에서 사용할 수 있는 일반화된 예외 클래스
// AOP 기능 사용
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExceptionResponse {
private Date timestamp; // 예외가 발생한 시간 정보
private String message; // 예외가 발생한 메시지
private String details; // 예외 상세 정보
}
package com.example.restfulwebservice.exception;
import com.example.restfulwebservice.user.UserNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.Date;
// ResponseEntityExceptionHandler를 상속받는 클래스
// 사용하는 시스템에서 에러가 발생하게되면,
// 에러를 핸들링하기 위해 스프링 부트에서 제공되는 클래스
@RestController
@ControllerAdvice
// 모든 컨트롤러가 실행이 될때 @ControllerAdvice 어노테이션을 가지고 있는 빈이 실행되게 되어있는데
// 만약 에러가 발생하면 예외 핸들러 클래스가 실행됨
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
// 모든 예외처리를 처리해주는 메소드
@ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllException(Exception ex, WebRequest request){
// handleAllException(Exception ex, WebRequest request)
// Exception ex : 에러객체
// WebRequest : 어디서 발생했는지에 대한 정보
ExceptionResponse exceptionResponse = new ExceptionResponse( new Date(), ex.getMessage(), request.getDescription(false));
// 서버에서 가장 일반화되어있는 오류 : 500번 / HttpStatus.INTERNAL_SERVER_ERROR
return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
package com.example.restfulwebservice.exception;
import com.example.restfulwebservice.user.UserNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.Date;
// ResponseEntityExceptionHandler를 상속받는 클래스
// 사용하는 시스템에서 에러가 발생하게되면,
// 에러를 핸들링하기 위해 스프링 부트에서 제공되는 클래스
@RestController
@ControllerAdvice
// 모든 컨트롤러가 실행이 될때 @ControllerAdvice 어노테이션을 가지고 있는 빈이 실행되게 되어있는데
// 만약 에러가 발생하면 예외 핸들러 클래스가 실행됨
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
// 사용자가 존재하지않았을 때 사용하는 UserNotFound Exception
@ExceptionHandler(UserNotFoundException.class)
public final ResponseEntity<Object> handleUserNotFoundException(Exception ex, WebRequest request){
// handleAllException(Exception ex, WebRequest request)
// Exception ex : 에러객체
// WebRequest : 어디서 발생했는지에 대한 정보
ExceptionResponse exceptionResponse = new ExceptionResponse( new Date(), ex.getMessage(), request.getDescription(false));
// NOT_FOUND
return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND);
}
}
10) 사용자 삭제를 위한 API 구현 - DELETE HTTP Method
String.format() 메소드는 C언어의 printf 함수와 같은 용도로 사용되며,
출력 포맷을 하나의 문자열로 만들수 있기 때문에 불필요한 문자열 결합도 필요없고, 가독성도 높아져서 자주 사용합니다.
// UserDAOService
// 사용자 삭제
public User deleteById(int id){
Iterator<User> iterator = users.iterator();
while (iterator.hasNext()){
User user = iterator.next();
if(user.getId()==id){
iterator.remove();
return user;
}
}
return null;
}
// UserController
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id){
User user = service.deleteById(id);
if(user==null){
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
}
728x90
반응형