Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1 - 3단계 방탈출 사용자 예약] 이든(최승준) 미션 제출합니다. #57

Merged
merged 61 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
bd5a99e
init: 이전 미션 코드 반영
J-I-H-O Apr 30, 2024
b0bd9bb
style: 줄바꿈 수정
PgmJun Apr 30, 2024
ba6b536
docs: 예외 상황 정의
J-I-H-O Apr 30, 2024
3592c1d
feat: 유효하지 않은 시간 생성 검증 기능 구현
J-I-H-O Apr 30, 2024
75c7ee4
feat: 유효하지 않은 시간 생성 검증 기능 구현
J-I-H-O Apr 30, 2024
ec2e3b0
feat: 예약이 존재하는 시간 삭제를 방어하는 기능 구현
J-I-H-O Apr 30, 2024
e37964d
Merge remote-tracking branch 'origin/step1' into step1
J-I-H-O Apr 30, 2024
a8a4a02
style: 줄바꿈 정리
J-I-H-O Apr 30, 2024
0fe2794
feat: 유효하지 않은 예약 등록 요청 검증 로직 구현
J-I-H-O Apr 30, 2024
540fdee
feat: 중복된 날짜 및 시간 예약 등록 검증 로직 구현
J-I-H-O Apr 30, 2024
5d6008e
chore: gradle 설정 변경
J-I-H-O May 1, 2024
7307152
feat: 지나간 날짜와 시간에 대한 예약 생성 방지 기능 구현
J-I-H-O May 1, 2024
4f592bd
docs: 테마 API 명세 추가
J-I-H-O May 1, 2024
7331554
chore: 테마 관련 DDL 추가
J-I-H-O May 1, 2024
17af3db
feat: 테마 관리 페이지 요청 API 구현
J-I-H-O May 1, 2024
9e8790e
fix: 예약 관리 페이지 파일 변경
J-I-H-O May 1, 2024
e951bbb
docs: 테마 관련 REST API 명세 추가
J-I-H-O May 1, 2024
3bbd400
feat: 테마 조회 기능 구현
J-I-H-O May 1, 2024
40d021b
feat: 테마 추가 기능 구현
J-I-H-O May 1, 2024
ff72e78
feat: 테마 삭제 기능 구현
J-I-H-O May 1, 2024
fe93600
feat: 예약 도메인과 테마 도메인 연동
J-I-H-O May 1, 2024
60d7ed3
feat: 사용자 예약 페이지 요청 API 구현
J-I-H-O May 1, 2024
478963c
chore: 더미 데이터 삽입 SQL 작성
J-I-H-O May 2, 2024
45fbf52
feat: 특정 날짜의 특정 테마 예약 정보 조회 기능 구현
J-I-H-O May 2, 2024
efb21f8
docs: 인기 테마 API 명세 추가
J-I-H-O May 2, 2024
f7177bd
feat: 인기 테마 조회 기능 구현
J-I-H-O May 2, 2024
1f76b8f
refactor: 테스트 성능 개선
J-I-H-O May 2, 2024
772631f
refactor: 메서드 분리
J-I-H-O May 2, 2024
ac95a28
style: 완료된 TODO 제거
J-I-H-O May 2, 2024
830df0c
style: 테스트 given, when, then 분리
J-I-H-O May 2, 2024
cd175dc
style: TODO 제거
J-I-H-O May 2, 2024
2d9aa37
docs: 테마 인기순 조회 API 문서 작성
PgmJun May 2, 2024
23b6a97
refactor: 컨트롤러 공통 Endpoint 매핑 해제
PgmJun May 4, 2024
35935dc
style: 메서드 인자 개행 수정
PgmJun May 4, 2024
5604c83
style: 코드라인 정리
PgmJun May 4, 2024
230ce75
refactor: HTTP Response 형식 수정(List -> Object)
PgmJun May 4, 2024
4cec0d8
style: 컨트롤러 메서드 네이밍 변경
PgmJun May 4, 2024
04131d6
style: DAO 메서드 네이밍 변경
PgmJun May 4, 2024
a0899cf
style: Service 메서드 네이밍 변경
PgmJun May 4, 2024
7b816fd
refactor: 메서드 인자 final로 불변 처리
PgmJun May 4, 2024
4a9a980
refactor: 예약 시간 정보 조회 API Endpoint 수정
PgmJun May 5, 2024
0f8ef2c
refactor: 테스트에서 컴포넌트 객체 사용을 위해 Import 애노테이션 적용
PgmJun May 5, 2024
0470138
refactor: 예외 발생 시 Response도 Object로 처리하도록, 응답 객체 ApiResponse 구현
PgmJun May 5, 2024
c983dfd
test: MVC Controller 테스트 작성
PgmJun May 5, 2024
41fa7a2
feat: 입력부 검증 로직 구현
PgmJun May 5, 2024
818b1eb
feat: 도메인 검증 로직 구현
PgmJun May 5, 2024
a2905fc
refactor: 예약 시간 정보 조회 로직 - 서브 쿼리 사용하여 하나의 SQL로 조회하도록 수정
PgmJun May 5, 2024
79de6f5
refactor: 테스트 코드 `@BeforeEach` 제거하고 필요한 곳에서만 데이터 삽입해주도록 변경
PgmJun May 5, 2024
604a0c0
fix: 00:30분 일때 1시간 전은 23:30분 이기 때문에 다른 날짜로 판별되어 테스트코드 실패하는 문제 해결
PgmJun May 5, 2024
a0f45d0
style: Repository 네이밍 용도에 맞도록 Dao로 변경
PgmJun May 5, 2024
15276bd
refactor: 특정 기간 사이 인기테마 조회 API 쿼리스트링으로 날짜값 받도록 변경
PgmJun May 5, 2024
b40b582
refactor: 의미가 불분명한 ConflictException 네이밍 변경 (DataConflictException)
PgmJun May 6, 2024
7620bc5
refactor: 에러 메시지 클라이언트 친화적으로 개선
PgmJun May 6, 2024
f6b6e37
refactor: Location 헤더 상수 사용하도록 변경
PgmJun May 6, 2024
4014865
refactor: 로깅 예외 메시지 구체화
PgmJun May 7, 2024
80690a0
refactor: 인기테마 조회 API에 defaultValue 적용
PgmJun May 7, 2024
272a414
refactor: 복잡성 제거를 위해 ApiResponse isSuccess 필드 제거
PgmJun May 7, 2024
3175c22
refactor: 예약 시간 정보 조회 쿼리에서 의미없는 조인 쿼리 제거
PgmJun May 7, 2024
81687e0
refactor: 조건식 가독성 향상을 위한 조건문 분리
PgmJun May 7, 2024
bc3aaf6
fix: 잘못된 예외 객체 사용 로직 수정 (DataConflict -> Validate)
PgmJun May 7, 2024
a80fcbd
fix: 불필요한 포맷팅 애노테이션 제거
PgmJun May 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 173 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
# 방탈출 예약 관리

## 응답 페이지

- `/` : 웰컴 페이지
- `/admin` : 어드민 메인 페이지
- `/admin/reservation` : 예약 관리 페이지
- `/admin/time` : 예약 시간 관리 페이지

## API 명세

### 예약 목록 조회 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}?date` | 특정 날짜의 특정 테마 예약 정보 조회 | | `@RestController` |
Copy link
Author

@PgmJun PgmJun May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API URI를 최대한 RESTful하게 설계해보려고 노력했는데요.
그 중에서도 /reservations 컬렉션 내부에 서브 컬렉션인 /themes를 넣어준 이 URI Endpoint는 어떻게 설계하면 좋을 지 고민이 많이 되었던 부분입니다.
자원을 적절하게 명시해주고 있는 지, 그리고 변경해야할 것 같다면 어떻게 변경하면 좋을 지 웨지의 의견이 궁금합니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reservation 와 theme는 종속관계로 생각하시는데 times는 별도로 다루는 이유가 있을까요?
리소스간 관계 설정에 통일성이 있었으면 좋겠습니다.

전 API 설계는 적당히 프론트랑 저희 팀원들만 알아볼 수 만 있으면 된다는 생각입니다.

그리고 전 요구사항 변경이 매우 빈번한 서비스를 관리하는 개발자기 때문에 서비스의 하위호환성을 중시하는 경항성이 있습니다. 그래서 API를 최초에 완전하게 설계하는 것에는 큰 관심이 없고 하위버전과 신규버전의 API를 호환해서 운영하는 것에 관심이 많습니다.

그래서 reservtions prefix가 있냐 마냐는 저에겐 이러든 저러든 큰 상관은 없다고 생각하나, 통일성 없는 규칙은 모든 사람(2주 후의 '나'를 포함한)에게 혼란을 주므로 통일감을 주는건 중요하다고 생각합니다.

Copy link
Author

@PgmJun PgmJun May 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

결론적으로 얻고자하는건 시간까지 포함된 예약 여부와 예약 시간들인데 times가 빠지니 어색하게 느껴지긴 하네요!
충분히 고려하지 못한 것 같네요 🥲 짚어주셔서 감사합니다!
/reservations/themes/{themeId}/times?date 이렇게 변경해보면 좋을까요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 해당 리뷰는 제 착각에서 비롯된 것도 있네요 죄송합니다 🙇‍♂️

잘못 생각한 부분 ->

  • /reservations/themes만 있고 /themes는 없음
  • /times는 있음
    -> 리소스 상 theme는 reservation의 하위 개념으로 명시되는데 times는 별도 리소스로 취급한 것으로 생각

그런데 말씀주신것처럼 해당 API의 응답 결과가 themes가 아닌 theme의 시간인거 같아서 말씀주신 방향으로 수정하는게 좋을거 같네요 (뒷걸음질 치다 쥐 잡았네요ㅎ)

| POST | `/reservations` | 예약 추가 | | `@RestController` |
| DELETE | `/reservations/{id}` | 예약 취소 | | `@RestController` |
| GET | `/times` | 예약 시간 조회 | | `@RestController` |
| DELETE | `/times/{id}` | 예약 시간 추가 | | `@RestController` |
| POST | `/times` | 예약 시간 삭제 | | `@RestController` |
| GET | `/themes` | 테마 정보 조회 | | `@RestController` |
| GET | `/themes/top?count` | 인기순 테마 조회 | | `@RestController` |
Copy link
Author

@PgmJun PgmJun May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 Endpoint를 설계할 때 너무 고민이 됐어요.
top으로 인기순 테마 조회 Endpoint를 설계해보았는데 괜찮을 지 의견 남겨주시면 감사하겠습니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 설계했다면 themes의 sort option (TOP을 추가) 과 페이지네이션 요청객체를 조합해서 응답하긴 했을거 같아요.
themes?sortOption=TOP&page=0&size=10
그런데 이건 상황마다 다른거라서요, (개발에 절대적인 규칙은 잘 없습니다)
작성해주신 것처럼 sort option마다 API를 하나 씩 따주는 것도 괜찮다고 생각합니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇게도 처리해줄 수 있겠네요:)
지금 상황에서 어떤 게 더 좋은 설계일지 한번 고민해보겠습니다. 감사합니다!

| POST | `/themes` | 테마 추가 | | `@RestController` |
| DELETE | `/themes/{id}` | 테마 삭제 | | `@RestController` |

---

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

꼼꼼한 API 문서네요 ㅎ 👍

### 예약 정보 조회 API

- Request

Expand All @@ -36,6 +51,37 @@ Content-Type: application/json
]
```

---

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

- Request

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

---

- Response

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

---

### 예약 추가 API

- Request
Expand Down Expand Up @@ -68,6 +114,8 @@ Content-Type: application/json
}
```

---

### 예약 취소 API

- Request
Expand All @@ -82,7 +130,9 @@ DELETE /reservations/1 HTTP/1.1
HTTP/1.1 204
```

### 시간 목록 조회 API
---

### 예약 시간 조회 API

- Request

Expand All @@ -104,7 +154,9 @@ Content-Type: application/json
]
```

### 시간 추가 API
---

### 예약 시간 추가 API

- Request

Expand All @@ -129,7 +181,9 @@ Content-Type: application/json
}
```

### 시간 삭제 API
---

### 예약 시간 삭제 API

- Request

Expand All @@ -141,4 +195,111 @@ DELETE /times/1 HTTP/1.1

```
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 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
7 changes: 6 additions & 1 deletion src/main/java/roomescape/controller/AdminController.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ public String readAdminPage() {

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

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

@GetMapping("/theme")
public String readAdminThemePage() {
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 readPopularThemePage() {
return "index";
}

@GetMapping("/reservation")
public String readReservationPage() {
return "reservation";
}
}
13 changes: 13 additions & 0 deletions src/main/java/roomescape/controller/ReservationController.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package roomescape.controller;

import java.net.URI;
import java.time.LocalDate;
import java.util.List;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import roomescape.dto.reservation.ReservationAvailableTimeResponse;
import roomescape.dto.reservation.ReservationRequest;
import roomescape.dto.reservation.ReservationResponse;
import roomescape.service.ReservationService;
Expand All @@ -31,6 +35,15 @@ public ResponseEntity<List<ReservationResponse>> readReservations() {
return ResponseEntity.ok(reservationResponses);
}

@GetMapping("/themes/{themeId}")
public ResponseEntity<List<ReservationAvailableTimeResponse>> readAvailableTimeReservations(
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 List로 return하는 것도 괜찮을까요?
아니면 List를 감싼 DTO 객체로 리턴해주는 것이 좋을까요?
개인적으로는 DTO로 리턴해야 Response하는 데이터의 변경에 유연해질 것 같아요
웨지의 생각은 어떤지 궁금해요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이전 크루의 리뷰를 복붙 한번 하겠습니당.

응답객체는 Collection이 아닌 Object 형식으로 내려주는게 유연한 설계입니다.
page 요구사항이 추가되어 응답객체에 data size만 추가되어도 API break change가 됩니다 (기존 API가 하위버전에 호환되지 않는 것을 break change라고 합니다. 이 경우 반드시 서버와 프론트엔드가 반드시 동시 배포해야 하고, 배포 과정 중 오류가 발생하는 것을 피할수 없으므로 동일한 역할을 하는 새로운 API를 제공하게 되는 안티패턴이 됩니다)
프론트 입장에서 생각해보시면 {} 와 []는 다루는 방법이 다르기 때문입니다

최근에 끄덕끄덕하면서 본 아티클인데, Don’t return arrays as top-level responses. 라는 조언이 이 리뷰에 해당합니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

만약 지금처럼 List로 전송하는 API가 있을 때,
다음 버전의 서버 애플리케이션에서 어떠한 데이터를 추가로 보내주게 되면 그때서야 객체로 response할텐데
이 시점에서 서버의 Response JSON이 List에서 객체로 되기 때문에 []에서 {}로 형태로 변경되기 때문에
클라이언트에서도 변경이 발생해야하는 문제를 말씀하신 거군요!

거기까지는 생각해보지 못했는데 정말 좋은 의견인 것 같습니다.
데이터 변경에 유연해진다는 장점만을 가진 줄 알았는데 훨씬 중요한 이유가 있었네요.
감사합니다:)

Copy link
Author

@PgmJun PgmJun May 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이때 Endpoint를 통해 버전을 관리하는 방식(ex. /api/v1/reservations/themes/1 -> /api/v2/reservations/themes/1) 으로 해결해볼 수도 있지 않을까 싶지만
그렇게 하더라도 Json 파싱 방식이 변경되는 건 클라이언트 측에도 귀찮은 문제가 되겠네요:)
바로 반영해보겠습니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 경우 반드시 서버와 프론트엔드가 반드시 동시 배포해야 하고, 배포 과정 중 오류가 발생하는 것을 피할수 없으므로 동일한 역할을 하는 새로운 API를 제공하게 되는 안티패턴이 됩니다

이 부분이 말씀해주시는 API 버저닝과 관계있습니다 ㅎ
버전이 하나씩 늘어날수록 클라이언트 뿐만 아니라 서버도 관리해야하는 코드가 늘어나고, 항상 하위버전을 신경써야합니다
그렇다고 하위버전을 Deprecated 하는 것도 매우 어렵습니다

외부사에 API를 제공하는 경우 API 버전을 올렸다고 맞춰달라고 하는것조차 쉽지 않기 때문입니다 (상대 회사의 개발력을 투입해야함)
그게 중대한 기능변경이라면 요청이 가능하겠지만 사소한 이유로 버전을 변경할 경우 설득 조차 쉽지 않겠죠 ㅎ

인하우스 기능(사내 서버만 활용하는 API)이라고 쉽냐고 물어보면 그렇지도 않습니다. 회사 전체 채널로 3개월씩 공지해도 각자의 일로 바쁘므로 하위버전 API를 계속 사용하는 팀이 나옵니다

손쉬운 해결책으로 보여도 매우 고난한 길이니 가급적 가시지 않기를 권합니다 ㅋㅋ

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실무의 세계란 정말 고려할 사항들이 많네요!
웨지 덕분에 넓은 관점을 배워가는 것 같아요 🙂

@PathVariable
Long themeId,
@RequestParam
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 개행이 많이 어색해서 변경해보겠습니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넹 ㅎ 가독성은 꼼꼼히 챙겨주세요.

return ResponseEntity.ok(reservationService.findReservationByDateAndThemeId(date, themeId));
}

@PostMapping
public ResponseEntity<ReservationResponse> createReservation(@RequestBody ReservationRequest reservationRequest) {
ReservationResponse reservationResponse = reservationService.createReservation(reservationRequest);
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/roomescape/controller/ThemeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package roomescape.controller;

import java.net.URI;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import roomescape.dto.theme.ThemeRequest;
import roomescape.dto.theme.ThemeResponse;
import roomescape.service.ThemeService;

@RestController
@RequestMapping("/themes")
Copy link
Author

@PgmJun PgmJun May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재는 모든 컨트롤러 코드들이 이렇게 공통 endpoint를 묶어 관리하고 있는 형태인데요.
이 코드를 제거하고 각각의 매핑 애노테이션에서 endpoint 전체를 가지고 있게 변경하려고 합니다.
그렇게 관리해야 엔드포인트로 컨트롤러 메서드를 탐색하는 과정이 훨씬 수월해져서 이후 유지보수에 이점이 있을 것 같아요!
웨지는 어떻게 생각하시나요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋ 동의합니다
디버깅할 때 유용한 습관입니다
짬에서 우러나오는 습관인데 벌써 고려하시는 게 인상적이네요

public class ThemeController {

private final ThemeService themeService;

public ThemeController(ThemeService themeService) {
this.themeService = themeService;
}

@GetMapping
public ResponseEntity<List<ThemeResponse>> readThemes() {
List<ThemeResponse> themeResponses = themeService.findAllThemes();

return ResponseEntity.ok(themeResponses);
}

@GetMapping("/top")
public ResponseEntity<List<ThemeResponse>> readTopNThemes(@RequestParam int count) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재는 거의 모든 메서드의 인자에 final 처리가 되어있지 않은 상태인데요.
메서드의 인자 값에 변경이 필요하지 않은 부분들은 final로 재할당을 막아
예상치 못한 인자 값 변경으로 인한 사이드 이펙트를 방지해 볼 생각인데
이런 처리를 해주는 것에 대해서 웨지는 어떻게 생각하시나요??

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전 모든 파라미터 / 지역 변수 final에 대해 부정적인 편입니다
final 이라는 제약이 주는 코드의 유지보수성 향상에 대해선 동의합니다만
java라는 언어가 자동 final을 지원하지 않는데서 주는 불편감이 더 큽니다

인자 final 규칙은 모든 코드에 적용해야만 비로소 효과가 생기는데 (final이 붙어 있지 않으면 가변임을 예상할 수 있음) 자동으로 처리가 안 되다 보니 팀원들에게 강요하기가 어렵습니다. 의식하고 있어도 자주 빠지기도 하구요.
지켜지지 못할 규칙이라면 없는게 낫다고 생각하기 때문에 굳이 final를 권장하진 않습니다.
하겠다는 사람 말리지도 않지만요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 다 처리해줄 게 아니면 오히려 의도가 불분명한 코드가 될 수도 있다는 생각이 있는데요

인텔리제이에 자동으로 final 키워드를 추가해주는 기능이 있기도하고 개인적으로 사이드이펙트를 방지하는데에 좋다는 생각이 들어서
저는 처리해보겠습니다:)

https://stackoverflow.com/questions/9579403/intellij-idea-automatically-add-final-keyword-to-the-generated-variables

List<ThemeResponse> themeResponses = themeService.findTopNThemes(count);

return ResponseEntity.ok(themeResponses);
}

@PostMapping
public ResponseEntity<ThemeResponse> createTheme(@RequestBody ThemeRequest request) {
ThemeResponse response = themeService.createTheme(request);

return ResponseEntity.created(URI.create("/themes/" + response.id()))
.body(response);
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteTheme(@PathVariable Long id) {
themeService.deleteTheme(id);

return ResponseEntity.noContent().build();
}
}
Loading