Skip to content

Commit

Permalink
[1 - 3단계 방탈출 사용자 예약] 이든(최승준) 미션 제출합니다. (#57)
Browse files Browse the repository at this point in the history
* init: 이전 미션 코드 반영

* style: 줄바꿈 수정

* docs: 예외 상황 정의

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 유효하지 않은 시간 생성 검증 기능 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 유효하지 않은 시간 생성 검증 기능 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 예약이 존재하는 시간 삭제를 방어하는 기능 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* style: 줄바꿈 정리

* feat: 유효하지 않은 예약 등록 요청 검증 로직 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 중복된 날짜 및 시간 예약 등록 검증 로직 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* chore: gradle 설정 변경

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 지나간 날짜와 시간에 대한 예약 생성 방지 기능 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* docs: 테마 API 명세 추가

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* chore: 테마 관련 DDL 추가

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 테마 관리 페이지 요청 API 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* fix: 예약 관리 페이지 파일 변경

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* docs: 테마 관련 REST API 명세 추가

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 테마 조회 기능 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 테마 추가 기능 구현

* feat: 테마 삭제 기능 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 예약 도메인과 테마 도메인 연동

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 사용자 예약 페이지 요청 API 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* chore: 더미 데이터 삽입 SQL 작성

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 특정 날짜의 특정 테마 예약 정보 조회 기능 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* docs: 인기 테마 API 명세 추가

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* feat: 인기 테마 조회 기능 구현

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* refactor: 테스트 성능 개선

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* refactor: 메서드 분리

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* style: 완료된 TODO 제거

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* style: 테스트 given, when, then 분리

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* style: TODO 제거

Co-authored-by: PgmJun <chltmdwns96@gmail.com>
Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>

* docs: 테마 인기순 조회 API 문서 작성

* refactor: 컨트롤러 공통 Endpoint 매핑 해제

* style: 메서드 인자 개행 수정

* style: 코드라인 정리

* refactor: HTTP Response 형식 수정(List -> Object)

* style: 컨트롤러 메서드 네이밍 변경

* style: DAO 메서드 네이밍 변경

* style: Service 메서드 네이밍 변경

* refactor: 메서드 인자 final로 불변 처리

* refactor: 예약 시간 정보 조회 API Endpoint 수정

* refactor: 테스트에서 컴포넌트 객체 사용을 위해 Import 애노테이션 적용

* refactor: 예외 발생 시 Response도 Object로 처리하도록, 응답 객체 ApiResponse 구현

그저 String을 return하는 방식은 이후 응답 데이터 확장 설계를 할 때
클라이언트의 응답값 흭득 방식에 변경이 발생해야하기 때문에 break change가 발생
때문에 요청 정보/에러 정보를 담은 응답 객체(ApiResponse)를 두어 사용

* test: MVC Controller 테스트 작성

* feat: 입력부 검증 로직 구현

* feat: 도메인 검증 로직 구현

* refactor: 예약 시간 정보 조회 로직 - 서브 쿼리 사용하여 하나의 SQL로 조회하도록 수정

* refactor: 테스트 코드 `@BeforeEach` 제거하고 필요한 곳에서만 데이터 삽입해주도록 변경

* fix: 00:30분 일때 1시간 전은 23:30분 이기 때문에 다른 날짜로 판별되어 테스트코드 실패하는 문제 해결

* style: Repository 네이밍 용도에 맞도록 Dao로 변경

* refactor: 특정 기간 사이 인기테마 조회 API 쿼리스트링으로 날짜값 받도록 변경

* refactor: 의미가 불분명한 ConflictException 네이밍 변경 (DataConflictException)

* refactor: 에러 메시지 클라이언트 친화적으로 개선

* refactor: Location 헤더 상수 사용하도록 변경

* refactor: 로깅 예외 메시지 구체화

* refactor: 인기테마 조회 API에 defaultValue 적용

* refactor: 복잡성 제거를 위해 ApiResponse isSuccess 필드 제거

* refactor: 예약 시간 정보 조회 쿼리에서 의미없는 조인 쿼리 제거

* refactor: 조건식 가독성 향상을 위한 조건문 분리

* fix: 잘못된 예외 객체 사용 로직 수정 (DataConflict -> Validate)

* fix: 불필요한 포맷팅 애노테이션 제거

---------

Co-authored-by: J-I-H-O <jeongjiho0731@gmail.com>
  • Loading branch information
PgmJun and J-I-H-O authored May 7, 2024
1 parent 30a2fc7 commit bbaf858
Show file tree
Hide file tree
Showing 63 changed files with 3,250 additions and 343 deletions.
305 changes: 305 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
# 방탈출 예약 관리

## API 명세

| Method | Endpoint | Description | File Path | Controller Type |
|--------|---------------------------------------------|-----------------------|----------------------------------------|-------------------|
| GET | `/` | 인기 테마 페이지 요청 | `templates/index.html` | `@Controller` |
| GET | `/reservation` | 사용자 예약 페이지 요청 | `templates/reservation.html` | `@Controller` |
| GET | `/admin` | 어드민 페이지 요청 | `templates/admin/index.html` | `@Controller` |
| GET | `/admin/reservation` | 예약 관리 페이지 요청 | `templates/admin/reservation-new.html` | `@Controller` |
| GET | `/admin/time` | 예약 시간 관리 페이지 요청 | `templates/admin/time.html` | `@Controller` |
| GET | `/admin/theme` | 테마 관리 페이지 요청 | `templates/admin/theme.html` | `@Controller` |
| GET | `/reservations` | 예약 정보 조회 | | `@RestController` |
| GET | `/reservations/themes/{themeId}/times?date` | 특정 날짜의 특정 테마 예약 정보 조회 | | `@RestController` |
| POST | `/reservations` | 예약 추가 | | `@RestController` |
| DELETE | `/reservations/{id}` | 예약 취소 | | `@RestController` |
| GET | `/times` | 예약 시간 조회 | | `@RestController` |
| DELETE | `/times/{id}` | 예약 시간 추가 | | `@RestController` |
| POST | `/times` | 예약 시간 삭제 | | `@RestController` |
| GET | `/themes` | 테마 정보 조회 | | `@RestController` |
| GET | `/themes/top?count&startAt&endAt` | 특정 기간의 인기 테마 조회 | | `@RestController` |
| POST | `/themes` | 테마 추가 | | `@RestController` |
| DELETE | `/themes/{id}` | 테마 삭제 | | `@RestController` |

---

### 예약 정보 조회 API

- Request

```
GET /reservations HTTP/1.1
```

- Response

```
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 1,
"name": "브라운",
"date": "2023-08-05",
"time": {
"id": 1,
"startAt": "10:00"
}
}
]
```

---

### 특정 날짜의 특정 테마 예약 정보 조회

- Request

```
GET /reservations/themes/1/times?date=2024-12-31 HTTP/1.1
```

---

- Response

```
[
{
"timeId": 1,
"startAt": "17:00",
"alreadyBooked": false
},
{
"timeId": 2,
"startAt": "20:00",
"alreadyBooked": false
}
]
```

---

### 예약 추가 API

- Request

```
POST /reservations HTTP/1.1
content-type: application/json
{
"date": "2023-08-05",
"name": "브라운",
"timeId": 1
}
```

- Response

```
HTTP/1.1 201
Content-Type: application/json
{
"id": 1,
"name": "브라운",
"date": "2023-08-05",
"time" : {
"id": 1,
"startAt" : "10:00"
}
}
```

---

### 예약 취소 API

- Request

```
DELETE /reservations/1 HTTP/1.1
```

- Response

```
HTTP/1.1 204
```

---

### 예약 시간 조회 API

- Request

```
GET /times HTTP/1.1
```

- Response

```
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 1,
"startAt": "10:00"
}
]
```

---

### 예약 시간 추가 API

- Request

```
POST /times HTTP/1.1
content-type: application/json
{
"startAt": "10:00"
}
```

- Response

```
HTTP/1.1 201
Content-Type: application/json
{
"id": 1,
"startAt": "10:00"
}
```

---

### 예약 시간 삭제 API

- Request

```
DELETE /times/1 HTTP/1.1
```

- Response

```
HTTP/1.1 204
```

---

### 테마 정보 조회 API

- Request

```
GET /themes HTTP/1.1
```

- response

```
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 1,
"name": "레벨2 탈출",
"description": "우테코 레벨2를 탈출하는 내용입니다.",
"thumbnail": "https://i.pinimg.com/236x/6e/bc/46/6ebc461a94a49f9ea3b8bbe2204145d4.jpg"
}
]
```

---

### 특정 기간의 인기 테마 조회 API

- Request

```
GET /themes/top?count=10&startAt=2024-01-01&endAt=2024-01-07 HTTP/1.1
```

- response

```
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 1,
"name": "레벨2 탈출",
"description": "우테코 레벨2를 탈출하는 내용입니다.",
"thumbnail": "https://i.pinimg.com/236x/6e/bc/46/6ebc461a94a49f9ea3b8bbe2204145d4.jpg"
},
...8개 생략
{
"id": 10,
"name": "레벨10 탈출",
"description": "우테코 레벨10를 탈출하는 내용입니다.",
"thumbnail": "https://i.pinimg.com/236x/6e/bc/46/6ebc461a94a49f9ea3b8bbe2204145d4.jpg"
}
]
```

---

### 테마 추가 API

- Request

```
POST /themes HTTP/1.1
content-type: application/json
{
"name": "레벨2 탈출",
"description": "우테코 레벨2를 탈출하는 내용입니다.",
"thumbnail": "https://i.pinimg.com/236x/6e/bc/46/6ebc461a94a49f9ea3b8bbe2204145d4.jpg"
}
```

- response

```
HTTP/1.1 201
Location: /themes/1
Content-Type: application/json
{
"id": 1,
"name": "레벨2 탈출",
"description": "우테코 레벨2를 탈출하는 내용입니다.",
"thumbnail": "https://i.pinimg.com/236x/6e/bc/46/6ebc461a94a49f9ea3b8bbe2204145d4.jpg"
}
```

---

### 테마 삭제 API

- Request

```
DELETE /themes/1 HTTP/1.1
```

- response

```
HTTP/1.1 204
```
19 changes: 19 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## 예외 상황

### 예약

- [x] 예약 생성 시 예약자명, 날짜, 시간에 유효하지 않은 값이 입력할 수 없다.
- null, 빈 값, 날짜 / 시간 포맷
- [x] 중복 예약 추가는 불가능하다.
- [x] 지나간 날짜와 시간에 대한 예약 생성은 불가능하다.

### 시간

- [x] 시간 생성 시 시작 시간에 유효하지 않은 값이 입력할 수 없다.
- null, 빈 값, 날짜 / 시간 포맷
- [x] 중복 시간 추가는 불가능하다.
- [x] 특정 시간에 대한 예약이 존재하면, 그 시간을 삭제할 수 없다.

### 공통

- [ ] 존재하지 않는 API로 요청을 보낼 수 없다.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 1 addition & 1 deletion src/main/java/roomescape/RoomescapeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
@SpringBootApplication
public class RoomescapeApplication {

public static void main(String[] args) {
public static void main(final String[] args) {
SpringApplication.run(RoomescapeApplication.class, args);
}

Expand Down
28 changes: 28 additions & 0 deletions src/main/java/roomescape/controller/AdminController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package roomescape.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class AdminController {

@GetMapping("/admin")
public String showAdminPage() {
return "admin/index";
}

@GetMapping("/admin/reservation")
public String showAdminReservationPage() {
return "admin/reservation-new";
}

@GetMapping("/admin/time")
public String showAdminTimePage() {
return "admin/time";
}

@GetMapping("/admin/theme")
public String showAdminThemePage() {
return "admin/theme";
}
}
18 changes: 18 additions & 0 deletions src/main/java/roomescape/controller/ClientController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package roomescape.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ClientController {

@GetMapping("/")
public String showPopularThemePage() {
return "index";
}

@GetMapping("/reservation")
public String showReservationPage() {
return "reservation";
}
}
Loading

0 comments on commit bbaf858

Please sign in to comment.