HTTP 메서드 GET, DELETE, POST, PUT을 스프링에서 REST-API 방식으로 처리하는 방법을 알아본다.
GET, DELETE 메서드
- 두 메서드는 HTTP 요청 메세지에 바디가 없다.
- 그러므로 서버에 데이터를 전달 할 때는, URI에 포함하거나 파라미터를 사용하거나 헤더에 포함하여 전송한다.
1. 호텔 정보 조회 API 명세서
- 기본적으로는 호텔 객실 정보를 조회하는 API 이다.
- 단순 리소스 조회이므로 GET 메서드를 사용하며, 호텔을 구부할 수있는 고유 아이디 {hotelId}와 객실을 구분하는 {roomNumber}를 사용한다.
- 하지만 요청을 보낼 때, 예약 시작일(fromDate)와 예약 종료일(toDate)를 같이 보내면 예약 정보를 포함하여 응답한다. 포함하지 않으면 응답하지 않는다.
# REST-API 요청
GET /hotels/{hotelId}/rooms/{roomNumber}?fromDate={yyyyMMdd}&toDtate={yyyyMMdd}
# REST-API 응답
{
"id" : 1201928183,
"roomNumber" : "West-Wing-3928",
"numberOfBeds" : 2,
"roomType" : "deluxe",
"originalPrice" : 150.00,
# 예약일을 지정했을 때 포함되는 응답
"reservations" : [
{
"id" : 129171201928183,
"reservedDate" : "yyyy-MM-dd"
},
{
"id" : 129171201928183,
"reservedDate" : "yyyy-MM-dd"
}
]
}
- 기본적으로 위와 같이 방 정보를 응답하며, 예약일을 지정해서 보내면 고유 예약 아이디와 함께 예약일을 리스트 형태로 응답하여 준다.
2. Controller 구현
- 명세서를 설계하였으면, 그에 맞게 API를 구현해야한다. 먼저 요청을 제일 먼저 받는 Controller부터 작성한다.
@RestController
public class HotelRoomController {
@GetMapping("/hotels/{hotelId}/rooms/{roomNumber}")
public HotelRoomResponse getHotelRoomByPerios(@PathVariable Long hotelId, @PathVariable String roomNumber,
@RequestParam(value = "fromDate", required = false) @DateTimeFormat(pattern = "yyyyMMdd") LocalDate fromDate,
@RequestParam(value = "toDate", required = false) @DateTimeFormat(pattern = "yyyyMMdd") LocalDate toDate) {
Long hotelRoomId = IdGenerator.create(); // 랜덤한 hotelRoomIdfmf 생성한다.
BigDecimal originalPrice = new BigDecimal("130.00"); // 가격을 130달러로 지정한다.
HotelRoomResponse response = HotelRoomResponse.of(hotelRoomId, originalPrice, HotelRoomType.DOUBLE, roomNumber); // 응답 객체를 생성한다.
// 만약 fromDate, toDate 파라미터가 모두 존재한다면 응답객체에 예약내용을 추가한다.
if(Objects.nonNull(fromDate) && Objects.nonNull(toDate)) {
fromDate.datesUntil(toDate.plusDays(1))
.forEach(date -> response.reservedAt(date));
}
return response;
}
@DeleteMapping("/hotels/{hotelId}/rooms/{roomNumber}")
public DeleteResultResponse deleteHotelRoom(@PathVariable Long hotelId, @PathVariable String roomNumber) {
log.info("Delete Request. hotelId={}, roomNumber={}", hotelId, roomNumber);
return new DeleteResultResponse(Boolean.TRUE, "success");
}
}
- @RestController는 @Controller와 @ResponseBody를 합친 어노테이션 이다. 스프링 빈으로 등록되어 JSON 메지시 형태로 리턴하게 된다.
- @GetMapping() : 명세에서 정해진 URL 형식의 요청이 GET 형식으로 들어왔을 때 처리하는 사용자 메서드를 매핑해준다.
1. @PathVariable : URL 중 특정한 위치에 있는 데이터를 추출한다.
2. @RequestParam() : HTTP 파라미터로 전달되는 데이터를 추출한다. value는 파라미터의 이름을 뜻한다.
3. @DateTimeFormat : 전달되는 날짜 인자 값을 LocalDate 객체로 파싱한다.
4. 사용자가 생성한 HotelRoomResponse 객체를 JSON 형태로 리턴한다.
- @DeleteMapping() : 명세에서 정해진 URL 형식의 요청이 DELETE 형식으로 들어왔을 때 처리하는 사용자 메서드를 매핑해준다.
1. @PathVariable을 통해 URL의 데이터를 추출한다.
2. 지금은 DB와 연결되지 않아 예약정보를 저장할 수 없기에, 간단히 해당 요청이 수행되었다는 로그만 서버에 남긴다.
3. 사용자가 생성한 DeleteResultREsponse 객체를 JSON 형태로 리턴한다.
3. DTO 구현
- DTO란 Data Transfer Object, 즉 데이터 전달을 위한 객체를 뜻한다. 위의 컨트롤러에서 사용한 반환 객체인 HotelRoomResponse와 DeleteResultREsponse 객체를 구현해본다.
@Data
public class HotelRoomResponse {
@JsonProperty("id") // Json 객체의 이름을 지정하는데 사용
@JsonSerialize(using = ToStringSerializer.class) // Json 객체로 변환 할 때 타입을 String 으로 변환한다.
private Long hotelRoomId;
@JsonSerialize(using = ToDollarStringSerializer.class) // Json 객체로 변환 할 때 타입을 커스텀한 ToDollarString 으로 변환한다.
private BigDecimal originalPrice;
private HotelRoomType hotelRoomType;
private String roomNumber;
private List<Reservation> reservations;
public HotelRoomResponse(Long hotelRoomId, BigDecimal originalPrice, HotelRoomType hotelRoomType, String roomNumber) {
this.hotelRoomId = hotelRoomId;
this.originalPrice = originalPrice;
this.hotelRoomType = hotelRoomType;
this.roomNumber = roomNumber;
reservations = new ArrayList<>();
}
public static HotelRoomResponse of(Long hotelRoomId, BigDecimal originalPrice, HotelRoomType hotelRoomType, String roomNumber) {
return new HotelRoomResponse(hotelRoomId,originalPrice, hotelRoomType, roomNumber);
}
public void reservedAt(LocalDate reservedAt) {
reservations.add(new Reservation(IdGenerator.create(), reservedAt));
}
@Data
private static class Reservation {
@JsonProperty("id")
@JsonSerialize(using = ToStringSerializer.class)
private Long reservationId;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate reserveDate;
@Builder
public Reservation(Long reservationId, LocalDate reserveDate) {
this.reservationId = reservationId;
this.reserveDate = reserveDate;
}
}
}
@Data
public class DeleteResultResponse {
private boolean success;
private String message;
public DeleteResultResponse(boolean success, String message) {
this.success = success;
this.message = message;
}
}
- InnerClass인 Reservation 또한 하나의 DTO이다.
- @JsonProperty는 json으로 클라이언트에게 반환할 때 key값을 괄호 안의 값으로 설정해준다.
- @JsonSerialize는 json으로 클라이언트에게 반환할 때 괄호안에 지정한 형태로 설정해준다.
- @JsonFormat은 객체를 사용자가 원하는 형태의 포맷으로 바꾸어준다.
4. 결과
http://localhost:8080/hotels/123/rooms/12345?fromDate=20220101&toDate=20220102
위와 같이 서버에 요청하게 되면, 다음과 같은 결과를 반환해준다.
{
"originalPrice": "130.00",
"hotelRoomType": "double",
"roomNumber": "12345",
"reservations": [
{
"reserveDate": "2022-01-01",
"id": "16711785287875610"
},
{
"reserveDate": "2022-01-02",
"id": "16711785287871245"
}
],
"id": "16711785287845223"
}
'공부 > Spring Boot' 카테고리의 다른 글
[스프링 부트 공부 일지] 3. 스프링에서 REST API 개발하기 (3) (0) | 2022.12.17 |
---|---|
[스프링 부트 공부 일지] 3. 스프링에서 REST API 개발하기 (2) (0) | 2022.12.16 |
[스프링 부트 공부 일지] 2. 스프링 웹 MVC (0) | 2022.12.15 |
[스프링 부트 공부 일지] 1. 스프링 애플리케이션 (0) | 2022.12.15 |
[스프링 부트 공부 일지] 0. 공부를 시작하며 (0) | 2022.12.13 |