-
Notifications
You must be signed in to change notification settings - Fork 170
[Spring JDBC] 김태우 5,6,7 단계 미션 제출합니다. #530
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
base: tae-wooo
Are you sure you want to change the base?
Changes from all commits
4ee1510
84a59b5
0599c64
6cc4f11
db298e1
65a2b86
e813153
5e335e0
9f06209
f60f1ac
3f95a1c
bdd3ca1
f01d97b
11013d9
8f76975
3444ef2
cc0031a
1513c89
2dee21f
4b8a3bc
904a3ba
e817519
f44976c
8e01e11
abc6834
7d8306a
84f96c4
b2b9e5c
913fe55
624819d
fccc8ff
29d8ef3
2c3cca4
dc60143
1bc32dc
0764299
ed57a7a
c66dc1d
e718f80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| [33mcommit db4464b2ee2776fc1a7e2c33ac06db51dca35c96[m[33m ([m[1;36mHEAD[m[33m -> [m[1;32mroomescape-taewoo[m[33m, [m[1;31morigin/roomescape-taewoo[m[33m)[m | ||
| Author: 김태우 <[email protected]> | ||
| Date: Sun Nov 9 04:45:22 2025 +0900 | ||
|
|
||
| Reservation 클래스 record 타입으로 전환 | ||
|
|
||
| [33mcommit 5e335e0b40bec777c2c887a4361dd6d0be5d1f0d[m | ||
| Author: 김태우 <[email protected]> | ||
| Date: Sun Nov 9 04:29:07 2025 +0900 | ||
|
|
||
| dependencies scope 별 그룹 정리 | ||
|
|
||
| [33mcommit e81315342c391df2ce1b226f975f500dca48c502[m | ||
| Author: taewoo <[email protected]> | ||
| Date: Thu Nov 6 16:17:56 2025 +0900 | ||
|
|
||
| README 프로젝트 구조 추가 | ||
|
|
||
| [33mcommit 65a2b867e22ee4e1c972ae64fb560d3069e2d329[m | ||
| Author: taewoo <[email protected]> | ||
| Date: Thu Nov 6 15:54:16 2025 +0900 | ||
|
|
||
| README파일 추가 | ||
|
|
||
| [33mcommit db298e1d1f6dc6fbaa4eb3e5644e8671a1b76e1a[m | ||
| Author: taewoo <[email protected]> | ||
| Date: Thu Nov 6 06:35:18 2025 +0900 | ||
|
|
||
| 1,2단계 테스트 코드 | ||
|
|
||
| [33mcommit 6cc4f11270dbd5587e645d58012541d95b7e8e3a[m | ||
| Author: taewoo <[email protected]> | ||
| Date: Thu Nov 6 06:34:50 2025 +0900 | ||
|
|
||
| 예약 페이지 응답 및 예약 목록 조회 기능 구현 | ||
|
|
||
| [33mcommit 0599c6420f34983907795296d8e5bc82db75ea61[m | ||
| Author: taewoo <[email protected]> | ||
| Date: Thu Nov 6 06:33:49 2025 +0900 | ||
|
|
||
| 예약 조회 기능 테스트를 위한 기본 Reservation 데이터 정의 | ||
|
|
||
| [33mcommit 84a59b597b453e7c1b9768671b8f552aa0820eb2[m | ||
| Author: taewoo <[email protected]> | ||
| Date: Thu Nov 6 04:11:06 2025 +0900 | ||
|
|
||
| index.html 파일명 규칙 대신 / 매핑을 통해 home.html 을 초기화면으로 반환하도록 구현 | ||
|
|
||
| [33mcommit 4ee151079690731ed3b64b3d94a513ed8580dc8c[m | ||
| Author: taewoo <[email protected]> | ||
| Date: Thu Nov 6 04:09:41 2025 +0900 | ||
|
|
||
| web 기능 및 thymeleaf 템플릿 엔진 의존성 추가 | ||
|
|
||
| [33mcommit 13c25ffbfd35945a01ec7efd07f0e56638ca0082[m[33m ([m[1;31morigin/zinyan[m[33m, [m[1;31morigin/wzrabbit[m[33m, [m[1;31morigin/wfs0502[m[33m, [m[1;31morigin/wateralsie[m[33m, [m[1;31morigin/uoehisx[m[33m, [m[1;31morigin/taeyeonroyce[m[33m, [m[1;31morigin/tae-wooo[m[33m, [m[1;31morigin/stonecau[m[33m, [m[1;31morigin/sseung3424[m[33m, [m[1;31morigin/sohvun[m[33m, [m[1;31morigin/shin-mallang[m[33m, [m[1;31morigin/programming-alpaca[m[33m, [m[1;31morigin/nyeroni[m[33m, [m[1;31morigin/nova0128[m[33m, [m[1;31morigin/nonactress[m[33m, [m[1;31morigin/newvh[m[33m, [m[1;31morigin/mintcoke123[m[33m, [m[1;31morigin/marriedsenior[m[33m, [m[1;31morigin/main[m[33m, [m[1;31morigin/leeyoonjoo[m[33m, [m[1;31morigin/leegwichan[m[33m, [m[1;31morigin/kyy00n[m[33m, [m[1;31morigin/kyuwon-choi[m[33m, [m[1;31morigin/kwakminu[m[33m, [m[1;31morigin/kokodak[m[33m, [m[1;31morigin/kimsky247-coder[m[33m, [m[1;31morigin/ke-62[m[33m, [m[1;31morigin/kang1221[m[33m, [m[1;31morigin/jxxxxxn[m[33m, [m[1;31morigin/jihyeonanan[m[33m, [m[1;31morigin/jelee2555[m[33m, [m[1;31morigin/idle2534[m[33m, [m[1;31morigin/idealhyun[m[33m, [m[1;31morigin/hong-sile[m[33m, [m[1;31morigin/gy102912[m[33m, [m[1;31morigin/fanngineer[m[33m, [m[1;31morigin/corjqnrl[m[33m, [m[1;31morigin/chemistryx[m[33m, [m[1;31morigin/breadquokka[m[33m, [m[1;31morigin/boyekim[m[33m, [m[1;31morigin/bingle625[m[33m, [m[1;31morigin/bbggr1209[m[33m, [m[1;31morigin/HEAD[m[33m, [m[1;31morigin/8parks[m[33m, [m[1;32mmain[m[33m)[m | ||
| Author: boorownie <[email protected]> | ||
| Date: Tue Aug 15 01:12:26 2023 +0900 | ||
|
|
||
| default | ||
|
|
||
| [33mcommit 94aebe2cc437605d73f15fc851e9504a976c3c37[m | ||
| Author: boorownie <[email protected]> | ||
| Date: Tue Aug 15 01:12:02 2023 +0900 | ||
|
|
||
| init | ||
|
|
||
| [33mcommit ef8a7256668457b8b9be3685163a937c2b1e6070[m | ||
| Author: 류성현 <[email protected]> | ||
| Date: Tue Aug 15 01:16:06 2023 +0900 | ||
|
|
||
| Initial commit |
This file was deleted.
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,47 +11,46 @@ | |
| import roomescape.domain.Reservation; | ||
| import roomescape.dto.ReservationRequest; | ||
| import roomescape.dto.ReservationResponse; | ||
| import roomescape.exception.NotFoundReservationException; | ||
|
|
||
| import roomescape.service.ReservationService; | ||
|
|
||
| import java.net.URI; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.concurrent.atomic.AtomicLong; | ||
|
|
||
| @RestController | ||
| public class ReservationController { | ||
| private final List<Reservation> reservations = new ArrayList<>(); | ||
| private final AtomicLong index = new AtomicLong(1); | ||
|
|
||
| private final ReservationService reservationService; | ||
|
|
||
| public ReservationController(ReservationService reservationService) { | ||
| this.reservationService = reservationService; | ||
| } | ||
|
|
||
|
|
||
| @PostMapping("/reservations") | ||
| public ResponseEntity<ReservationResponse> createReservations(@Valid @RequestBody ReservationRequest request) { | ||
| Reservation newReservation = Reservation.createReservation(index.getAndIncrement(), request.name(), request.date(), request.time()); | ||
| reservations.add(newReservation); | ||
| Reservation newReservation = reservationService.registerReservation(request.name(), request.date(), request.time()); | ||
|
|
||
| return ResponseEntity.created(URI.create("/reservations/" + newReservation.getId())). | ||
| body(ReservationResponse.from(newReservation)); | ||
| } | ||
|
Comment on lines
30
to
36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요청이 들어오면 먼저 DispatcherServlet이 실행되고, 그 다음 HandlerAdapter가 선택된 Handler(Controller)를 실행할 수 있도록 준비하며, Controller가 호출되면 비즈니스 로직 처리를 위해 Service를 호출하고, 응답 단계에서는 |
||
|
|
||
|
|
||
| @GetMapping("/reservations") | ||
| public ResponseEntity<List<ReservationResponse>> readReservations() { | ||
| List<Reservation> reservations = reservationService.getReservations(); | ||
|
|
||
| List<ReservationResponse> responses = reservations.stream(). | ||
| map(ReservationResponse::from).toList(); | ||
|
|
||
| return ResponseEntity | ||
| .ok() | ||
| .body(responses); | ||
| } | ||
|
|
||
| @DeleteMapping("/reservations/{id}") | ||
| public ResponseEntity<Void> deleteReservations(@PathVariable Long id) { | ||
| Reservation reservation = reservations.stream() | ||
|
Comment on lines
46
to
-48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. POST에서는 @RequestBody를, DELETE에서는 @PathVariable을 사용하셨는데,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아래와 같이 POST 요청에서는 새로운 예약 정보를 생성해야 하기 때문에 POST /reservations HTTP/1.1
Content-Type: application/json
{
"date": "2023-08-05",
"name": "브라운",
"time": "15:40"
}이처럼 생성에 필요한 데이터가 요청 본문(JSON)에 담겨 전달되므로, 반대로 DELETE 요청은 이미 존재하는 특정 예약을 제거하는 용도이기 때문에 그래서 URI 템플릿( |
||
| .filter(it -> Objects.equals(it.getId(), id)) | ||
| .findFirst() | ||
| .orElseThrow(() -> new NotFoundReservationException("예약된 기록이 없습니다.")); | ||
|
|
||
| reservations.remove(reservation); | ||
|
|
||
| reservationService.delete(id); | ||
| return ResponseEntity.noContent().build(); | ||
|
|
||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package roomescape.dao; | ||
|
|
||
| import org.springframework.jdbc.core.JdbcTemplate; | ||
| import org.springframework.jdbc.core.simple.SimpleJdbcInsert; | ||
| import org.springframework.stereotype.Repository; | ||
| import roomescape.domain.Reservation; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @Repository | ||
| public class ReservationDao { | ||
|
Comment on lines
+11
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DAO 잘 만들어주셨습니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 처음에는 다른 DAO 구현 예시들을 참고하면서 DAO는 데이터베이스 접근 로직을 담당하는 계층이기 때문에, 물론 기능적으로 보면 하지만 첫 번째로, 해당 클래스가 "DB 접근을 담당하는 영속성 계층"이라는 의미를 코드 레벨에서 명확하게 드러내기 때문에 두 번째로, 정리하자면,
따라서 DAO와 같은 영속성 계층에서는
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DAO와 Repository는 모두 데이터 접근 계층에서 사용되는 개념이지만, 접근 관점과 역할에서 차이가 있습니다. DAO(Data Access Object)는 데이터베이스 기술에 맞춰 CRUD 작업을 수행하는 객체로, SQL 기반 접근 방식에 초점을 둡니다. 반면 Repository는 도메인 객체를 저장하고 조회하는 '저장소'라는 개념에 더 가깝습니다. 정리하면,
예를 들어, 아래와 같은 Repository 인터페이스는 구현체가 아닌 도메인 관점의 행동에 집중하고 있습니다. public interface UserRepository {
User getByUserId(int id);
void createByName(String name);
void updateByUserId(int id, User user);
void removeByUserId(int id);
} |
||
|
|
||
| private final JdbcTemplate jdbcTemplate; | ||
|
|
||
| public ReservationDao(JdbcTemplate jdbcTemplate) { | ||
| this.jdbcTemplate = jdbcTemplate; | ||
| } | ||
|
|
||
| public Long insert(Reservation reservation) { | ||
| SimpleJdbcInsert insertActor = new SimpleJdbcInsert(jdbcTemplate) | ||
| .withTableName("reservation") | ||
| .usingGeneratedKeyColumns("id"); | ||
| Map<String, Object> parameters = Map.of( | ||
| "name", reservation.getName(), | ||
| "date", reservation.getDate(), | ||
| "time", reservation.getTime() | ||
| ); | ||
| Number id = insertActor.executeAndReturnKey(parameters); | ||
| return id.longValue(); | ||
| } | ||
|
|
||
| public List<Reservation> findAll() { | ||
| String sql = "select id,name,date,time from reservation"; | ||
| return jdbcTemplate.query(sql, | ||
| (resultSet, rowNum) -> { | ||
| Reservation reservation = Reservation.newReservationFromDb( | ||
| resultSet.getLong("id"), | ||
| resultSet.getString("name"), | ||
| resultSet.getString("date"), | ||
| resultSet.getString("time") | ||
|
|
||
| ); | ||
| return reservation; | ||
| }); | ||
| } | ||
|
|
||
|
|
||
| public int delete(Long id) { | ||
| return jdbcTemplate.update("delete from reservation where id = ?", id); | ||
| } | ||
| } | ||

Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
단일 예약을 생성하는 API지만 /reservations라는 복수형 엔드포인트를 사용하신 이유가 궁금합니다.
컬렉션을 기준으로 복수형을 사용하는 컨벤션을 적용하신 건가요?
(단순 궁금)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음에는 단일 예약 생성 API라
/reservation이 더 적절하다고 생각했습니다.하지만 테스트 코드가
/reservations기준으로 작성되어 있었고,REST API 설계를 간단히 살펴보니 컬렉션 리소스에 POST 요청을 보내
새로운 항목을 추가할 때 복수형을 사용하는 패턴도 흔히 사용되는 것을 확인했습니다.
그래서 이번 구현에서는
/reservations를 선택하게 되었습니다.