Skip to content

Commit 11649be

Browse files
authored
고생하셨습니다.
🎉 PR 머지 완료! 🎉
1 parent 18331a3 commit 11649be

18 files changed

+517
-79
lines changed

README.MD

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,144 @@
88
- /reservation: 예약 관리 페이지 (reservation.html) 응답
99
- /reservations: 예약 데이터 JSON 응답
1010

11+
## 3단계 - 예약 등록
12+
- `POST /reservations`: 새로운 예약 정보를 등록합니다.
13+
- 요청 본문에 예약자의 이름, 날짜, 시간이 포함됩니다.
14+
- 성공 시 `201 Created` 응답 및 Location 헤더 반환
15+
16+
## 4단계 - 예외 처리
17+
- 클라이언트 요청의 문제로 예외가 발생할 경우 `400 Bad Request`를 반환합니다.
18+
- 처리되는 예외 예시:
19+
- 예약 입력값 누락 (name, date, time 중 하나라도 누락)
20+
- 존재하지 않는 예약 ID로 삭제 요청
21+
- JSON 파싱 오류 (날짜/시간 형식이 올바르지 않음)
22+
23+
# 📌 API 명세서
24+
25+
---
26+
27+
## GET /reservations
28+
29+
- 설명: 모든 예약 목록 조회
30+
- 응답: `200 OK`
31+
- 응답 본문 예시:
32+
33+
```json
34+
[
35+
{
36+
"id": 1,
37+
"name": "브라운",
38+
"date": "2023-08-05",
39+
"time": "15:40"
40+
},
41+
{
42+
"id": 2,
43+
"name": "죠르디",
44+
"date": "2023-08-05",
45+
"time": "17:00"
46+
}
47+
// ... 더 많은 예약
48+
]
49+
50+
```
51+
| 필드 | 타입 | 설명 |
52+
| ------ | --------------------- | ------ |
53+
| `id` | `number` | 예약 식별자 |
54+
| `name` | `string` | 예약자 이름 |
55+
| `date` | `string (YYYY-MM-DD)` | 예약 날짜 |
56+
| `time` | `string (HH:mm)` | 예약 시간 |
57+
58+
---
59+
## POST /reservations
60+
- 설명: 예약 추가
61+
- 응답: `201 Created`
62+
- 응답 본문 예시:
63+
``` json
64+
{
65+
"id": 1,
66+
"name": "브라운",
67+
"date": "2023-08-05",
68+
"time": "15:40"
69+
}
70+
```
71+
| 필드 | 타입 | 필수 여부 | 설명 |
72+
| ------ | --------------------- | ----- | ------ |
73+
| `name` | `string` || 예약자 이름 |
74+
| `date` | `string (YYYY-MM-DD)` || 예약 날짜 |
75+
| `time` | `string (HH:mm)` || 예약 시간 |
76+
---
77+
## DELETE /reservations/{id}
78+
- 설명: 예약 삭제
79+
- 응답: `204 No Content`
80+
- 응답 본문 예시: 없음
81+
82+
---
83+
84+
## ❗ 예외 응답 명세
85+
86+
### 잘못된 요청 (필드 누락 등)
87+
88+
- 상태 코드: `400 Bad Request`
89+
- 발생 조건:
90+
- 예약 요청에서 name, date, time 중 하나라도 누락되거나
91+
- 빈 문자열 또는 null 값이 들어온 경우
92+
93+
응답 예시:
94+
```json
95+
{
96+
"code": "BAD_REQUEST",
97+
"message": "이름은 필수입니다."
98+
}
99+
```
100+
### 존재하지 않는 예약 ID
101+
- 상태 코드: `400 Bad Request`
102+
- 발생 조건: 존재하지 않는 ID를 가진 예약을 삭제할 때
103+
104+
응답 예시:
105+
```json
106+
{
107+
"code": "NOT_FOUND_RESERVATION",
108+
"message": "해당 예약이 존재하지 않습니다."
109+
}
110+
```
111+
112+
### JSON 파싱 오류
113+
- 상태 코드: `400 Bad Request`
114+
- 발생 조건: 요청 JSON이 잘못된 형식일 경우
115+
116+
응답 예시:
117+
```json
118+
{
119+
"code": "INVALID_JSON",
120+
"message": "형식이 옳바르지 않습니다"
121+
}
122+
```
123+
124+
### 존재하는 ID 중복 예약
125+
- 상태 코드: `409 CONFLICT`
126+
- 발생 조건: 중복된 ID의 Reservation을 예약할 경우
127+
128+
응답 예시:
129+
```json
130+
{
131+
"code": "RESERVATION_ALREADY_EXISTS",
132+
"message": "이미 ID가 존재하는 예약은 저장할 수 없습니다.
133+
}
134+
```
135+
136+
### 예상하지 못한 오류
137+
- 상태 코드: `500 Internal Server Error`
138+
- 발생 조건: 서버 내부에서 예상하지 못한 예외가 발생한 경우
139+
140+
응답 예시:
141+
```json
142+
{
143+
"code": "INTERNAL_SERVER_ERROR",
144+
"message": "예상치 못한 오류 발생"
145+
}
146+
```
147+
148+
149+
150+
151+

src/main/java/roomescape/controller/HomeController.java renamed to src/main/java/roomescape/controller/PageController.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44
import org.springframework.web.bind.annotation.GetMapping;
55

66
@Controller
7-
public class HomeController {
7+
public class PageController {
88

99
@GetMapping("/")
1010
public String home() {
1111
return "home";
1212
}
13+
14+
@GetMapping("/reservation")
15+
public String reservation() {
16+
return "reservation";
17+
}
1318
}
1419

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package roomescape.controller;
2+
3+
import org.springframework.http.MediaType;
4+
import org.springframework.http.ResponseEntity;
5+
import org.springframework.web.bind.annotation.*;
6+
import roomescape.controller.dto.ReservationRequest;
7+
import roomescape.controller.dto.ReservationResponse;
8+
import roomescape.service.ReservationService;
9+
import roomescape.service.dto.ReservationResult;
10+
11+
import java.net.URI;
12+
import java.util.List;
13+
14+
@RestController
15+
public class ReservationApiController {
16+
17+
private final ReservationService reservationService;
18+
19+
public ReservationApiController(ReservationService reservationService) {
20+
this.reservationService = reservationService;
21+
}
22+
23+
@GetMapping("/reservations")
24+
public ResponseEntity<List<ReservationResponse>> findAll() {
25+
List<ReservationResult> all = reservationService.findAll();
26+
List<ReservationResponse> reservationResponses = all.stream()
27+
.map(ReservationResponse::from)
28+
.toList();
29+
return ResponseEntity.ok(reservationResponses);
30+
}
31+
32+
@PostMapping("/reservations")
33+
public ResponseEntity<ReservationResponse> save(@RequestBody ReservationRequest reservationRequest) {
34+
ReservationResult result = reservationService.save(reservationRequest.toCommand());
35+
return ResponseEntity.created(URI.create("/reservations/" + result.id()))
36+
.contentType(MediaType.APPLICATION_JSON)
37+
.body(ReservationResponse.from(result));
38+
}
39+
40+
@DeleteMapping("/reservations/{id}")
41+
public ResponseEntity<Void> delete(@PathVariable Long id) {
42+
reservationService.delete(id);
43+
return ResponseEntity.noContent().build();
44+
}
45+
}

src/main/java/roomescape/controller/ReservationController.java

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package roomescape.controller.dto;
2+
3+
import roomescape.service.dto.SaveReservationCommand;
4+
5+
import java.time.LocalDate;
6+
import java.time.LocalTime;
7+
8+
public class ReservationRequest {
9+
private final String name;
10+
private final LocalDate date;
11+
private final LocalTime time;
12+
13+
public ReservationRequest(String name, LocalDate date, LocalTime time) {
14+
validate(name, date, time);
15+
this.name = name;
16+
this.date = date;
17+
this.time = time;
18+
}
19+
20+
public SaveReservationCommand toCommand() {
21+
return new SaveReservationCommand(name, date, time);
22+
}
23+
24+
public String getName() {
25+
return name;
26+
}
27+
28+
public LocalDate getDate() {
29+
return date;
30+
}
31+
32+
public LocalTime getTime() {
33+
return time;
34+
}
35+
36+
private static void validate(String name, LocalDate date, LocalTime time) {
37+
if (name == null || name.isBlank()) {
38+
throw new IllegalArgumentException("이름은 필수입니다.");
39+
}
40+
if (date == null) {
41+
throw new IllegalArgumentException("날짜는 필수입니다.");
42+
}
43+
if (time == null) {
44+
throw new IllegalArgumentException("시간은 필수입니다.");
45+
}
46+
}
47+
}
48+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package roomescape.controller.dto;
2+
3+
import roomescape.service.dto.ReservationResult;
4+
5+
import java.time.LocalDate;
6+
import java.time.LocalTime;
7+
8+
public record ReservationResponse(
9+
Long id,
10+
String name,
11+
LocalDate date,
12+
LocalTime time
13+
) {
14+
15+
public static ReservationResponse from(ReservationResult result) {
16+
return new ReservationResponse(
17+
result.id(),
18+
result.name(),
19+
result.date(),
20+
result.time()
21+
);
22+
}}

src/main/java/roomescape/domain/Reservation.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ public class Reservation {
1111
private LocalTime time;
1212

1313
public Reservation(Long id, String name, LocalDate date, LocalTime time) {
14+
validateName(name);
15+
validateDate(date);
16+
validateTime(time);
1417
this.id = id;
1518
this.name = name;
1619
this.date = date;
1720
this.time = time;
1821
}
1922

2023
public Reservation(String name, LocalDate date, LocalTime time) {
21-
this.id = null;
22-
this.name = name;
23-
this.date = date;
24-
this.time = time;
24+
this(null, name, date, time);
2525
}
2626

2727
public Reservation withId(Long id) {
@@ -31,7 +31,6 @@ public Reservation withId(Long id) {
3131
public Long getId() {
3232
return id;
3333
}
34-
3534
public String getName() {
3635
return name;
3736
}
@@ -43,4 +42,25 @@ public LocalDate getDate() {
4342
public LocalTime getTime() {
4443
return time;
4544
}
45+
46+
private void validateName(String name) {
47+
if (name == null || name.isBlank()) {
48+
throw new IllegalArgumentException("이름은 필수입니다.");
49+
}
50+
}
51+
52+
private void validateDate(LocalDate date) {
53+
if (date == null) {
54+
throw new IllegalArgumentException("날짜는 필수입니다.");
55+
}
56+
if (date.isBefore(LocalDate.now())) {
57+
throw new IllegalArgumentException("예약 날짜는 오늘 이후여야 합니다.");
58+
}
59+
}
60+
61+
private void validateTime(LocalTime time) {
62+
if (time == null) {
63+
throw new IllegalArgumentException("시간은 필수입니다.");
64+
}
65+
}
4666
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package roomescape.exception;
2+
3+
public record ErrorResult(
4+
String code,
5+
String message
6+
) {}

0 commit comments

Comments
 (0)