User Domain 생성, Get
프로젝트 구조와 경로
package com.example.restfulwebservice.user;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private Date joinDate;
}
package com.example.restfulwebservice.user;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class UserDaoService {
private static List<User> users = new ArrayList<>();
private static int usersCount = 3;
static {
users.add(new User(1,"kenneth", new Date()));
users.add(new User(2,"elena", new Date()));
users.add(new User(3,"chris", new Date()));
}
public List<User> findAll() {
return users;
}
public User save(User user) {
if (user.getId() == null) {
user.setId(++usersCount);
}
users.add(user);
return user;
}
public User findOne(int id) {
for (User user : users) {
if (user.getId() == id ) {
return user;
}
}
return null;
}
}
package com.example.restfulwebservice.user;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@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 /users/10 ... -> String
@GetMapping("/users/{id}")
public User retrieveUser(@PathVariable int id) {
return service.findOne(id);
}
}
@AllArgsConstructor
@RestController
@PathVariable
Post
package com.example.restfulwebservice.user;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private UserDaoService service;
public UserController(UserDaoService service) {
this.service = service;
}
//사용자 추가
@PostMapping ("/users")
//Post 사용 시, 클라이언트로부터 xml, json이 아닌
//오브젝트 형 데이터를 받을 때에는 매개변수에 @RequestBody 사용)
public void createUser(@RequestBody User user) {
User savedUser = service.save(user);
}
}
4번째 유저가 추가 됐다.
Status Code, Exception Handling
create 메소드에 코드를 조금 더 추가했다.
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = service.save(user);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).build();
}
그리고 다시 한번 실행했을 때, 이번엔 다른 상태 코드를 받았다.
HTTP 상태 코드
200 : OK. 성공적으로 처리했을 때 쓰인다. 가장 일반적으로 볼 수 있는 HTTP 상태.
201 : Created. 요청이 성공적으로 처리되어서 리소스가 만들어졌음을 의미한다.
용도에 맞는 적당한 전달방식을 택해서
200번만이 아닌 적절한 상태 코드로 결과를 받는 것이 올바른 REST API 개발이다.
존재하지 않는 id를 호출할 경우에는 null 값을 받아 화면에 출력되는 것은 없지만
역시나 같은 상태 코드를 받는다.
데이터가 존재하지 않을 뿐, 서버의 요청이나 전송에는 문제가 없기 때문에 일어나는 현상이다.
그러므로 이 경우를 예외로 처리해본다.
기존의 개별 조회 메소드에 user 값이 null 일 경우 예외로 처리하는 메소드를 작성한다.
//개별 조회
//Get : /users/1 or /users/10 ... -> String
@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);
}
}
이번에는 200번 정상 응답이 아닌
500번 에러로 처리된다.
HTTP 상태 코드 500 Internal Server Error(내부 서버 오류):
서버에 오류가 발생해 작업을 수행할 수 없을 때 사용된다.
보통 설정이나 퍼미션 문제. 아니면 HTTP 요청을 통해 호출한 문서가 실제 HTML 문서가 아니라 JSP, PHP, 서블릿 등의 프로그램일 경우 그 프로그램이 동작하다 세미콜론을 빼먹는 등의 각종 에러로 비정상 종료를 하는 경우 이 응답코드를 보낸다.
이런 예외 화면을 클라이언트에게 그대로 노출하는 것은 지양해야 한다.
예외 코드를 그대로 노출하는 것은 미관상으로도 보안상으로도 적절하지 않은 일이라
프로그램을 완성할 때에는 에러 페이지를 따로 만들어 대체하는 것이 좋다.
그런데 사실 100번 사용자가 화면에 출력되지 않는 이유는 데이터가 없기 때문이지
내부 서버 오류 때문은 아니기 때문에
이것을 500번 오류가 아닌 400번대 Not Found 오류로 다시 처리해본다.
어노테이션으로 간단히 처리할 수 있는데,
예외처리를 위해 만든 클래스인 UserNotFOundException에
@ResponseStatus 어노테이션을 추가하는 것이다.
package com.example.restfulwebservice.user;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException (String message) {
super(message);
}
}
에러를 핸들링하기 위한 패키지를 따로 생성했고,
ExceptionResponse 라는 이름의 클래스를 생성했다.
package com.example.restfulwebservice.exception;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExceptionResponse {
private Date timestamp; // 예외 발생 일시
private String message; // 예외 메시지
private String details; // 예외 상세 내용
}
package com.example.restfulwebservice.exception;
import java.util.Date;
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;
@RestController
// 프로젝트 내의 모든 컨트롤러가 실행될 때, 반드시 이 어노테이션을 가진 빈이 반드시 실행된다.
@ControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExcptions(Exception ex, WebRequest request) {
ExceptionResponse exceptionResponse =
new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
에러코드 그대로 노출되는 것이 아니라
의도한 내용이 출력된다.
데이터가 존재하지 않는 100번 아이디에 대한 상태코드에 따른 에러 메시지로는 적절치 않기 때문에
400번 대를 처리할 예외 메소드를 다시 만들어 본다.
@ExceptionHandler(UserNotFoundException.class)
public final ResponseEntity<Object> handleUserNotFoundExcptions(Exception ex, WebRequest request) {
ExceptionResponse exceptionResponse =
new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND);
}
Delete
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;
}
//사용자 삭제 컨트롤러
@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));
}
}
200번 OK로 상태코드가 반환되고, 빈 내용이 뜬다면 정상 처리 된 것이다. (void 이기 때문에)
전체 유저 조회를 해본다.
'프로젝트 > 파이널 프로젝트' 카테고리의 다른 글
RESTful Service 기능 확장 (1) - 유효성 체크 (0) | 2022.11.23 |
---|---|
파이널 프로젝트 (1) | 2022.11.23 |
RESTful Service (1) - 기초 (0) | 2022.11.22 |
Web Service / Web Application / SOAP (0) | 2022.11.22 |
API / REST / REST API (0) | 2022.11.22 |