From 4ea6d0e7469b396ddb398221094bb25128986c6b Mon Sep 17 00:00:00 2001 From: nonactress Date: Thu, 6 Nov 2025 02:16:47 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat=20:=201,2=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EB=AF=B8=EC=85=98=20=EC=A0=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 16 +++++++++- src/main/java/roomescape/Reservation.java | 30 +++++++++++++++++++ .../controller/AdminController.java | 18 +++++++++++ .../controller/ReservationController.java | 26 ++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/main/java/roomescape/Reservation.java create mode 100644 src/main/java/roomescape/controller/AdminController.java create mode 100644 src/main/java/roomescape/controller/ReservationController.java diff --git a/build.gradle b/build.gradle index 57267157c..cf9ed3241 100644 --- a/build.gradle +++ b/build.gradle @@ -8,12 +8,26 @@ group = 'nextstep' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' +// build.gradle 파일 + +// ... (plugins, group, version 등 상단 정보) ... + repositories { mavenCentral() } +// 이 'dependencies' 블록 안에 모든 의존성을 관리하세요. dependencies { - implementation 'org.springframework.boot:spring-boot-starter' + // [필수 1] 웹 서버 엔진 + implementation 'org.springframework.boot:spring-boot-starter-web' + + // [필수 2] 웹 페이지 조립 엔진 (타임리프) + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + + // (참고) 'web' 스타터가 'starter'를 이미 포함하고 있으므로, + // 'spring-boot-starter'는 생략해도 괜찮습니다. + + // 테스트에 필요한 의존성 testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' } diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java new file mode 100644 index 000000000..c812e2214 --- /dev/null +++ b/src/main/java/roomescape/Reservation.java @@ -0,0 +1,30 @@ +package roomescape; + +public class Reservation { + + private String name; + private String date; + private String time; + + public Reservation() { + } + + public Reservation(String name, String date, String time) { + this.name = name; + this.date = date; + this.time = time; + } + + public String getName() { + return name; + } + + public String getDate() { + return date; + } + + public String getTime() { + return time; + } + +} diff --git a/src/main/java/roomescape/controller/AdminController.java b/src/main/java/roomescape/controller/AdminController.java new file mode 100644 index 000000000..cc711abd2 --- /dev/null +++ b/src/main/java/roomescape/controller/AdminController.java @@ -0,0 +1,18 @@ +package roomescape.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class AdminController { + + @GetMapping("/") + public String home() { + return "home"; + } + @GetMapping("/reservation") + public String reservation() + { + return "reservation"; + } +} diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java new file mode 100644 index 000000000..ebca9ad63 --- /dev/null +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -0,0 +1,26 @@ +package roomescape.controller; + +import org.springframework.web.bind.annotation.*; +import roomescape.Reservation; + +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping("/reservations") // 이 클래스의 모든 API는 /reservations 경로로 요청됩니다. +public class ReservationController { + + + public ReservationController() { + reservations.add(new Reservation( "브라운", "2025-01-01", "10:00")); + reservations.add(new Reservation("코니", "2025-01-02", "11:00")); + } + + private final List reservations = new ArrayList<>(); + + @GetMapping + public List getAllReservations() { + // ... + return reservations; + } +} From c71326081c27df3734d23d168e9374ddf652a02d Mon Sep 17 00:00:00 2001 From: nonactress Date: Thu, 6 Nov 2025 17:54:42 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat=20:=201,2=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EB=AF=B8=EC=85=98=20=EC=A0=9C=EC=B6=9C=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/controller/AdminController.java | 1 + .../controller/ReservationController.java | 9 +++------ .../java/roomescape/{ => model}/Reservation.java | 2 +- src/test/java/roomescape/MissionStepTest.java | 14 ++++++++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) rename src/main/java/roomescape/{ => model}/Reservation.java (94%) diff --git a/src/main/java/roomescape/controller/AdminController.java b/src/main/java/roomescape/controller/AdminController.java index cc711abd2..5ff1dc3fe 100644 --- a/src/main/java/roomescape/controller/AdminController.java +++ b/src/main/java/roomescape/controller/AdminController.java @@ -10,6 +10,7 @@ public class AdminController { public String home() { return "home"; } + @GetMapping("/reservation") public String reservation() { diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index ebca9ad63..325795149 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -1,26 +1,23 @@ package roomescape.controller; import org.springframework.web.bind.annotation.*; -import roomescape.Reservation; +import roomescape.model.Reservation; import java.util.ArrayList; import java.util.List; @RestController -@RequestMapping("/reservations") // 이 클래스의 모든 API는 /reservations 경로로 요청됩니다. +@RequestMapping("/reservations") public class ReservationController { - + private final List reservations = new ArrayList<>(); public ReservationController() { reservations.add(new Reservation( "브라운", "2025-01-01", "10:00")); reservations.add(new Reservation("코니", "2025-01-02", "11:00")); } - private final List reservations = new ArrayList<>(); - @GetMapping public List getAllReservations() { - // ... return reservations; } } diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/model/Reservation.java similarity index 94% rename from src/main/java/roomescape/Reservation.java rename to src/main/java/roomescape/model/Reservation.java index c812e2214..3267703c6 100644 --- a/src/main/java/roomescape/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -1,4 +1,4 @@ -package roomescape; +package roomescape.model; public class Reservation { diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index cf4efbe91..4c6138924 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -16,4 +16,18 @@ public class MissionStepTest { .then().log().all() .statusCode(200); } + + @Test + void 이단계() { + RestAssured.given().log().all() + .when().get("/reservation") + .then().log().all() + .statusCode(200); + + RestAssured.given().log().all() + .when().get("/reservations") + .then().log().all() + .statusCode(200) + .body("size()", is(3)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수 만큼 검증하거나 0개임을 확인하세요. + } } From 460e4941d8d2a34045f87ab8ac7db44667a3cc74 Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 12 Nov 2025 09:04:25 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat=20:=20is()=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20import=EB=AC=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/roomescape/MissionStepTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 4c6138924..5145d01ca 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -1,4 +1,5 @@ package roomescape; +import static org.hamcrest.Matchers.is; import io.restassured.RestAssured; import org.junit.jupiter.api.Test; From c97ea81c6f1406bfcb29fd7cd5ac4cbaa5e76029 Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 12 Nov 2025 15:36:35 +0900 Subject: [PATCH 04/18] =?UTF-8?q?refactor=20:=20record=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 11 +++++-- .../roomescape/dto/ReservationResponse.java | 18 +++++++++++ .../java/roomescape/model/Reservation.java | 32 +++---------------- 3 files changed, 31 insertions(+), 30 deletions(-) create mode 100644 src/main/java/roomescape/dto/ReservationResponse.java diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 325795149..b745d08cb 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -1,8 +1,8 @@ package roomescape.controller; import org.springframework.web.bind.annotation.*; +import roomescape.dto.ReservationResponse; import roomescape.model.Reservation; - import java.util.ArrayList; import java.util.List; @@ -16,8 +16,13 @@ public ReservationController() { reservations.add(new Reservation("코니", "2025-01-02", "11:00")); } + @ResponseBody @GetMapping - public List getAllReservations() { - return reservations; + public List getAllReservations() { + return reservations.stream() + .map((ReservationResponse::from)) + .toList(); } + + } diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java new file mode 100644 index 000000000..1684dddd9 --- /dev/null +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -0,0 +1,18 @@ +package roomescape.dto; + +import roomescape.model.Reservation; +public record ReservationResponse( + String name, + String date, + String time + ) + { + public static ReservationResponse from(Reservation reservation) { + return new ReservationResponse( + reservation.name(), + reservation.date(), + reservation.time() + ); + } + } + diff --git a/src/main/java/roomescape/model/Reservation.java b/src/main/java/roomescape/model/Reservation.java index 3267703c6..5e03e6309 100644 --- a/src/main/java/roomescape/model/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -1,30 +1,8 @@ package roomescape.model; -public class Reservation { +public record Reservation +( String name, + String date, + String time - private String name; - private String date; - private String time; - - public Reservation() { - } - - public Reservation(String name, String date, String time) { - this.name = name; - this.date = date; - this.time = time; - } - - public String getName() { - return name; - } - - public String getDate() { - return date; - } - - public String getTime() { - return time; - } - -} +){ } From d6c310ca82aafa624b433645f3095f7b03e00aa6 Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 12 Nov 2025 15:41:40 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat=20:=20service=20=EA=B3=84=EC=B8=B5?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 5 +++-- .../roomescape/service/ReservationService.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/main/java/roomescape/service/ReservationService.java diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index b745d08cb..1cae7179e 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -3,6 +3,8 @@ import org.springframework.web.bind.annotation.*; import roomescape.dto.ReservationResponse; import roomescape.model.Reservation; +import roomescape.service.ReservationService; + import java.util.ArrayList; import java.util.List; @@ -12,8 +14,7 @@ public class ReservationController { private final List reservations = new ArrayList<>(); public ReservationController() { - reservations.add(new Reservation( "브라운", "2025-01-01", "10:00")); - reservations.add(new Reservation("코니", "2025-01-02", "11:00")); + ReservationService.setReservations(reservations); } @ResponseBody diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java new file mode 100644 index 000000000..7ec8417e8 --- /dev/null +++ b/src/main/java/roomescape/service/ReservationService.java @@ -0,0 +1,14 @@ +package roomescape.service; + +import roomescape.model.Reservation; + +import java.util.ArrayList; +import java.util.List; + +public class ReservationService { + + public static void setReservations(List reservations) { + reservations.add(new Reservation( "브라운", "2025-01-01", "10:00")); + reservations.add(new Reservation("코니", "2025-01-02", "11:00")); + } +} From dcdb9f1a749a0d54717ab81171bb482e25f1a363 Mon Sep 17 00:00:00 2001 From: nonactress Date: Thu, 13 Nov 2025 09:37:49 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat=20:=203=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 54 +++++++++++++-- .../dto/ReservationCreatequest.java | 9 +++ .../roomescape/dto/ReservationDelete.java | 11 +++ .../roomescape/dto/ReservationResponse.java | 8 ++- .../java/roomescape/model/Reservation.java | 39 +++++++++-- .../service/ReservationService.java | 46 ++++++++++++- src/test/java/roomescape/MissionStepTest.java | 68 ++++++++++++++++--- 7 files changed, 208 insertions(+), 27 deletions(-) create mode 100644 src/main/java/roomescape/dto/ReservationCreatequest.java create mode 100644 src/main/java/roomescape/dto/ReservationDelete.java diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 1cae7179e..3b6a48647 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -1,29 +1,69 @@ package roomescape.controller; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import roomescape.dto.ReservationCreatequest; import roomescape.dto.ReservationResponse; import roomescape.model.Reservation; import roomescape.service.ReservationService; -import java.util.ArrayList; +import java.net.URI; import java.util.List; @RestController @RequestMapping("/reservations") public class ReservationController { - private final List reservations = new ArrayList<>(); - public ReservationController() { - ReservationService.setReservations(reservations); + // 1. final 서비스 선언 + private final ReservationService service; + + // 2. 생성자 주입 (Spring이 '진짜' 서비스 빈을 넣어줌) + @Autowired + public ReservationController(ReservationService service) { + this.service = service; } - @ResponseBody @GetMapping public List getAllReservations() { - return reservations.stream() - .map((ReservationResponse::from)) + return service.getAllReservations().stream() + .map(ReservationResponse::from) // (::from은 ReservationResponse::from과 동일) .toList(); } + @PostMapping + public ResponseEntity createReservation( + @RequestBody ReservationCreatequest requestDto + ) { + + Reservation reservationToCreate = new Reservation( // (변수명 변경) + requestDto.name(), + requestDto.date(), + requestDto.time() + ); + + // 서비스가 'id'가 발급된 객체를 반환 + Reservation savedReservation = service.addReservation(reservationToCreate); + + // 'savedReservation' (id 있음)을 사용하여 응답 생성 + ReservationResponse responseDto = ReservationResponse.from(savedReservation); + URI location = URI.create("/reservations/" + savedReservation.getId()); + + return ResponseEntity.created(location).body(responseDto); + } + + + @DeleteMapping("/{id}") + public ResponseEntity deleteReservation( + @PathVariable Long id + ) { + boolean removed = service.deleteReservation(id); + + if (removed) { + return ResponseEntity.noContent().build(); + } else { + return ResponseEntity.notFound().build(); + } + } } diff --git a/src/main/java/roomescape/dto/ReservationCreatequest.java b/src/main/java/roomescape/dto/ReservationCreatequest.java new file mode 100644 index 000000000..c66c574d0 --- /dev/null +++ b/src/main/java/roomescape/dto/ReservationCreatequest.java @@ -0,0 +1,9 @@ +package roomescape.dto; + +public record ReservationCreatequest( + String name, + String date, + String time +) +{ +} diff --git a/src/main/java/roomescape/dto/ReservationDelete.java b/src/main/java/roomescape/dto/ReservationDelete.java new file mode 100644 index 000000000..ca28d10a6 --- /dev/null +++ b/src/main/java/roomescape/dto/ReservationDelete.java @@ -0,0 +1,11 @@ +package roomescape.dto; + +public record ReservationDelete ( + int id, + String name, + String date, + String time +){ + + +} diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java index 1684dddd9..c745fca75 100644 --- a/src/main/java/roomescape/dto/ReservationResponse.java +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -2,6 +2,7 @@ import roomescape.model.Reservation; public record ReservationResponse( + long id, String name, String date, String time @@ -9,9 +10,10 @@ public record ReservationResponse( { public static ReservationResponse from(Reservation reservation) { return new ReservationResponse( - reservation.name(), - reservation.date(), - reservation.time() + reservation.getId(), + reservation.getName(), + reservation.getDate(), + reservation.getTime() ); } } diff --git a/src/main/java/roomescape/model/Reservation.java b/src/main/java/roomescape/model/Reservation.java index 5e03e6309..767743d98 100644 --- a/src/main/java/roomescape/model/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -1,8 +1,37 @@ package roomescape.model; -public record Reservation -( String name, - String date, - String time +public class Reservation +{ + Long id; + String name; + String date; + String time; -){ } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDate() { + return date; + } + + public String getTime() { + return time; + } + + public Reservation(String name, String date, String time) { + this.name = name; + this.date = date; + this.time = time; + } + + public Reservation(Long id, String name, String date, String time) { + this(name, date, time); + this.id = id; + } +} diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java index 7ec8417e8..bf476d9fd 100644 --- a/src/main/java/roomescape/service/ReservationService.java +++ b/src/main/java/roomescape/service/ReservationService.java @@ -1,14 +1,54 @@ package roomescape.service; +import org.springframework.stereotype.Service; import roomescape.model.Reservation; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; // 1. ID 생성을 위해 추가 +@Service // 2. @Service를 클래스 위에 붙입니다! public class ReservationService { - public static void setReservations(List reservations) { - reservations.add(new Reservation( "브라운", "2025-01-01", "10:00")); - reservations.add(new Reservation("코니", "2025-01-02", "11:00")); + // 3. 서비스가 내부에 데이터 목록을 '소유'하고 관리합니다. (final로 불변성) + private final List reservations = new ArrayList<>(); + + // 4. DB의 Auto-Increment 대신 사용할 ID 카운터 (AtomicLong은 동시성 보장) + private final AtomicLong counter = new AtomicLong(); + + // 5. 생성자: 이 Service 클래스의 '객체'가 처음 생성될 때 호출됩니다. + // (DB가 없으니) 여기서 초기 데이터를 세팅합니다. + public ReservationService() { + // "setReservations" 메서드 대신 생성자에서 초기 데이터를 직접 추가합니다. + reservations.add(new Reservation(counter.incrementAndGet(), "브라운", "2025-01-01", "10:00")); + reservations.add(new Reservation(counter.incrementAndGet(), "코니", "2025-01-02", "11:00")); + } + + // --- 이제부터 이 Service가 제공할 기능(메서드)들 --- + + + public List getAllReservations() { + return reservations; + } + + public Reservation addReservation(Reservation newReservation) { + Reservation savedReservation = new Reservation( + counter.incrementAndGet(), // 새 ID 발급 + newReservation.getName(), + newReservation.getDate(), + newReservation.getTime() + ); + reservations.add(savedReservation); + return savedReservation; + } + + public boolean deleteReservation(Long id) { + // 리스트에서 ID가 일치하는 예약을 찾아서 제거합니다. + return reservations.removeIf(reservation -> reservation.getId().equals(id)); + } + + public void clear() { + reservations.clear(); // 리스트를 비웁니다. + counter.set(0L); // 카운터를 0으로 리셋합니다. } } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 5145d01ca..fd72b289b 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -2,33 +2,83 @@ import static org.hamcrest.Matchers.is; import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import roomescape.service.ReservationService; + +import java.util.HashMap; +import java.util.Map; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) public class MissionStepTest { +// @Test +// void 일단계() { +// RestAssured.given().log().all() +// .when().get("/") +// .then().log().all() +// .statusCode(200); +// } + +// @Test +// void 이단계() { +// RestAssured.given().log().all() +// .when().get("/reservation") +// .then().log().all() +// .statusCode(200); +// +// RestAssured.given().log().all() +// .when().get("/reservations") +// .then().log().all() +// .statusCode(200) +// .body("size()", is(3)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수 만큼 검증하거나 0개임을 확인하세요. +// } +// + + + @Autowired + private ReservationService reservationService; + + @BeforeEach + void setUp() { + reservationService.clear(); + } @Test - void 일단계() { + void 삼단계() { + Map params = new HashMap<>(); + params.put("name", "브라운"); + params.put("date", "2023-08-05"); + params.put("time", "15:40"); + RestAssured.given().log().all() - .when().get("/") + .contentType(ContentType.JSON) + .body(params) + .when().post("/reservations") .then().log().all() - .statusCode(200); - } + .statusCode(201) + .header("Location", "/reservations/1") + .body("id", is(1)); + + RestAssured.given().log().all() + .when().get("/reservations") + .then().log().all() + .statusCode(200) + .body("size()", is(1)); - @Test - void 이단계() { RestAssured.given().log().all() - .when().get("/reservation") + .when().delete("/reservations/1") .then().log().all() - .statusCode(200); + .statusCode(204); RestAssured.given().log().all() .when().get("/reservations") .then().log().all() .statusCode(200) - .body("size()", is(3)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수 만큼 검증하거나 0개임을 확인하세요. + .body("size()", is(0)); } } From a493121e9fd9ee7d9d6f9a72b4bff42cc2411a01 Mon Sep 17 00:00:00 2001 From: nonactress Date: Thu, 13 Nov 2025 16:03:24 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat=20:=204=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 ++ .../controller/GlobalExceptionHandler.java | 37 +++++++++++++++++++ .../controller/ReservationController.java | 30 +++++---------- .../dto/ReservationCreateRequest.java | 18 +++++++++ .../dto/ReservationCreatequest.java | 9 ----- .../service/ReservationService.java | 28 +++++++------- src/test/java/roomescape/MissionStepTest.java | 24 ++++++++++++ 7 files changed, 105 insertions(+), 44 deletions(-) create mode 100644 src/main/java/roomescape/controller/GlobalExceptionHandler.java create mode 100644 src/main/java/roomescape/dto/ReservationCreateRequest.java delete mode 100644 src/main/java/roomescape/dto/ReservationCreatequest.java diff --git a/build.gradle b/build.gradle index cf9ed3241..95911afdc 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,9 @@ dependencies { // (참고) 'web' 스타터가 'starter'를 이미 포함하고 있으므로, // 'spring-boot-starter'는 생략해도 괜찮습니다. + + implementation 'org.springframework.boot:spring-boot-starter-validation' + // 테스트에 필요한 의존성 testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' diff --git a/src/main/java/roomescape/controller/GlobalExceptionHandler.java b/src/main/java/roomescape/controller/GlobalExceptionHandler.java new file mode 100644 index 000000000..5a70e2333 --- /dev/null +++ b/src/main/java/roomescape/controller/GlobalExceptionHandler.java @@ -0,0 +1,37 @@ +package roomescape.controller; // (또는 roomescape.exception) + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.HashMap; +import java.util.Map; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidationExceptions( + MethodArgumentNotValidException ex) { + + Map errors = new HashMap<>(); + ex.getBindingResult() + .getFieldErrors() + .forEach(error -> + errors.put(error.getField(), error.getDefaultMessage()) + ); + return ResponseEntity.badRequest().body(errors); + } + + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity> handleIllegalArgumentException( + IllegalArgumentException ex) { + + Map error = new HashMap<>(); + error.put("message", ex.getMessage()); + + return ResponseEntity.badRequest().body(error); + } +} diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 3b6a48647..6788c3327 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -2,8 +2,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; -import roomescape.dto.ReservationCreatequest; +import roomescape.dto.ReservationCreateRequest; import roomescape.dto.ReservationResponse; import roomescape.model.Reservation; import roomescape.service.ReservationService; @@ -15,10 +16,8 @@ @RequestMapping("/reservations") public class ReservationController { - // 1. final 서비스 선언 private final ReservationService service; - // 2. 생성자 주입 (Spring이 '진짜' 서비스 빈을 넣어줌) @Autowired public ReservationController(ReservationService service) { this.service = service; @@ -31,39 +30,28 @@ public List getAllReservations() { .toList(); } + @DeleteMapping("/{id}") + public ResponseEntity deleteReservation(@PathVariable Long id) { + service.deleteReservation(id); + return ResponseEntity.noContent().build(); + } @PostMapping public ResponseEntity createReservation( - @RequestBody ReservationCreatequest requestDto + @Valid @RequestBody ReservationCreateRequest requestDto ) { - Reservation reservationToCreate = new Reservation( // (변수명 변경) + Reservation reservationToCreate = new Reservation( requestDto.name(), requestDto.date(), requestDto.time() ); - // 서비스가 'id'가 발급된 객체를 반환 Reservation savedReservation = service.addReservation(reservationToCreate); - // 'savedReservation' (id 있음)을 사용하여 응답 생성 ReservationResponse responseDto = ReservationResponse.from(savedReservation); URI location = URI.create("/reservations/" + savedReservation.getId()); return ResponseEntity.created(location).body(responseDto); } - - - @DeleteMapping("/{id}") - public ResponseEntity deleteReservation( - @PathVariable Long id - ) { - boolean removed = service.deleteReservation(id); - - if (removed) { - return ResponseEntity.noContent().build(); - } else { - return ResponseEntity.notFound().build(); - } - } } diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java new file mode 100644 index 000000000..454f2b5d8 --- /dev/null +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -0,0 +1,18 @@ +package roomescape.dto; + +import jakarta.validation.constraints.NotBlank; + + +public record ReservationCreateRequest( + + @NotBlank(message = "이름은 필수 항목입니다.") + String name, + + @NotBlank(message = "날짜는 필수 항목입니다.") + String date, + + @NotBlank(message = "시간은 필수 항목입니다.") + String time +) { + +} diff --git a/src/main/java/roomescape/dto/ReservationCreatequest.java b/src/main/java/roomescape/dto/ReservationCreatequest.java deleted file mode 100644 index c66c574d0..000000000 --- a/src/main/java/roomescape/dto/ReservationCreatequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package roomescape.dto; - -public record ReservationCreatequest( - String name, - String date, - String time -) -{ -} diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java index bf476d9fd..8f60e5af5 100644 --- a/src/main/java/roomescape/service/ReservationService.java +++ b/src/main/java/roomescape/service/ReservationService.java @@ -7,26 +7,18 @@ import java.util.List; import java.util.concurrent.atomic.AtomicLong; // 1. ID 생성을 위해 추가 -@Service // 2. @Service를 클래스 위에 붙입니다! +@Service public class ReservationService { - // 3. 서비스가 내부에 데이터 목록을 '소유'하고 관리합니다. (final로 불변성) private final List reservations = new ArrayList<>(); - // 4. DB의 Auto-Increment 대신 사용할 ID 카운터 (AtomicLong은 동시성 보장) private final AtomicLong counter = new AtomicLong(); - // 5. 생성자: 이 Service 클래스의 '객체'가 처음 생성될 때 호출됩니다. - // (DB가 없으니) 여기서 초기 데이터를 세팅합니다. public ReservationService() { - // "setReservations" 메서드 대신 생성자에서 초기 데이터를 직접 추가합니다. reservations.add(new Reservation(counter.incrementAndGet(), "브라운", "2025-01-01", "10:00")); reservations.add(new Reservation(counter.incrementAndGet(), "코니", "2025-01-02", "11:00")); } - // --- 이제부터 이 Service가 제공할 기능(메서드)들 --- - - public List getAllReservations() { return reservations; } @@ -42,13 +34,21 @@ public Reservation addReservation(Reservation newReservation) { return savedReservation; } - public boolean deleteReservation(Long id) { - // 리스트에서 ID가 일치하는 예약을 찾아서 제거합니다. - return reservations.removeIf(reservation -> reservation.getId().equals(id)); + + + public void deleteReservation(Long id) { + boolean exists = reservations.stream() + .anyMatch(reservation -> reservation.getId().equals(id)); + + if (!exists) { + throw new IllegalArgumentException("존재하지 않는 예약 ID입니다: " + id); + } + + reservations.removeIf(reservation -> reservation.getId().equals(id)); } public void clear() { - reservations.clear(); // 리스트를 비웁니다. - counter.set(0L); // 카운터를 0으로 리셋합니다. + reservations.clear(); + counter.set(0L); //테스트를 위한 코드 입니다. } } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index fd72b289b..fea08045b 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -81,4 +81,28 @@ void setUp() { .statusCode(200) .body("size()", is(0)); } + + + @Test + void 사단계() { + Map params = new HashMap<>(); + params.put("name", "브라운"); + params.put("date", ""); + params.put("time", ""); + + // 필요한 인자가 없는 경우 + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(params) + .when().post("/reservations") + .then().log().all() + .statusCode(400); + + // 삭제할 예약이 없는 경우 + RestAssured.given().log().all() + .when().delete("/reservations/1") + .then().log().all() + .statusCode(400); + } + } From f9935e5d0d2464113eeee69a10b0ece25fd5b4b4 Mon Sep 17 00:00:00 2001 From: nonactress Date: Sun, 16 Nov 2025 14:18:31 +0900 Subject: [PATCH 08/18] =?UTF-8?q?fix(dto)=20:=20local=20date,=20local=20ti?= =?UTF-8?q?me=20=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/dto/ReservationCreateRequest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java index 454f2b5d8..545dfd0ef 100644 --- a/src/main/java/roomescape/dto/ReservationCreateRequest.java +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -2,6 +2,9 @@ import jakarta.validation.constraints.NotBlank; +import java.time.LocalDate; +import java.time.LocalTime; + public record ReservationCreateRequest( @@ -9,10 +12,10 @@ public record ReservationCreateRequest( String name, @NotBlank(message = "날짜는 필수 항목입니다.") - String date, + LocalDate date, @NotBlank(message = "시간은 필수 항목입니다.") - String time + LocalTime time ) { } From 08145b961084a8285946ec29e1a589e1123189e9 Mon Sep 17 00:00:00 2001 From: nonactress Date: Sun, 16 Nov 2025 22:06:40 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat(repository)=20:=20=EB=A0=88=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B3=84=EC=B8=B5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 9 +--- .../dto/ReservationCreateRequest.java | 4 +- .../roomescape/dto/ReservationDelete.java | 5 +- .../roomescape/dto/ReservationResponse.java | 4 ++ .../java/roomescape/model/Reservation.java | 3 ++ .../repository/ReservationRepository.java | 51 +++++++++++++++++++ .../service/ReservationService.java | 43 +++++++--------- 7 files changed, 83 insertions(+), 36 deletions(-) create mode 100644 src/main/java/roomescape/repository/ReservationRepository.java diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 6788c3327..9ab0ed24f 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -40,14 +40,7 @@ public ResponseEntity deleteReservation(@PathVariable Long id) { public ResponseEntity createReservation( @Valid @RequestBody ReservationCreateRequest requestDto ) { - - Reservation reservationToCreate = new Reservation( - requestDto.name(), - requestDto.date(), - requestDto.time() - ); - - Reservation savedReservation = service.addReservation(reservationToCreate); + Reservation savedReservation = service.addReservation(requestDto); ReservationResponse responseDto = ReservationResponse.from(savedReservation); URI location = URI.create("/reservations/" + savedReservation.getId()); diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java index 545dfd0ef..1778a78fe 100644 --- a/src/main/java/roomescape/dto/ReservationCreateRequest.java +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -12,10 +12,10 @@ public record ReservationCreateRequest( String name, @NotBlank(message = "날짜는 필수 항목입니다.") - LocalDate date, + String date, @NotBlank(message = "시간은 필수 항목입니다.") - LocalTime time + String time ) { } diff --git a/src/main/java/roomescape/dto/ReservationDelete.java b/src/main/java/roomescape/dto/ReservationDelete.java index ca28d10a6..c9f5639d5 100644 --- a/src/main/java/roomescape/dto/ReservationDelete.java +++ b/src/main/java/roomescape/dto/ReservationDelete.java @@ -1,11 +1,12 @@ package roomescape.dto; +import java.time.LocalDate; +import java.time.LocalTime; + public record ReservationDelete ( int id, String name, String date, String time ){ - - } diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java index c745fca75..6f33e2967 100644 --- a/src/main/java/roomescape/dto/ReservationResponse.java +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -1,6 +1,10 @@ package roomescape.dto; import roomescape.model.Reservation; + +import java.time.LocalDate; +import java.time.LocalTime; + public record ReservationResponse( long id, String name, diff --git a/src/main/java/roomescape/model/Reservation.java b/src/main/java/roomescape/model/Reservation.java index 767743d98..610abbc52 100644 --- a/src/main/java/roomescape/model/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -1,5 +1,8 @@ package roomescape.model; +import java.time.LocalDate; +import java.time.LocalTime; + public class Reservation { Long id; diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java new file mode 100644 index 000000000..cf24f7086 --- /dev/null +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -0,0 +1,51 @@ +package roomescape.repository; // (새 패키지) + +import org.springframework.stereotype.Repository; +import roomescape.model.Reservation; +import roomescape.dto.ReservationCreateRequest; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +@Repository +public class ReservationRepository { + + private final List reservations = new ArrayList<>(); + private final AtomicLong counter = new AtomicLong(); + + public ReservationRepository() { + this.save(new Reservation(null, "브라운", "2025-01-01", "12:0")); + this.save(new Reservation(null, "코니", "2025-01-02", "11:00")); + } + + public List findAll() { + return new ArrayList<>(reservations); + } + + public Reservation save(Reservation reservation) { + Reservation savedReservation = new Reservation( + counter.incrementAndGet(), + reservation.getName(), + reservation.getDate(), + reservation.getTime() + ); + reservations.add(savedReservation); + return savedReservation; + } + + public boolean existsById(Long id) { + return reservations.stream() + .anyMatch(reservation -> reservation.getId().equals(id)); + } + + public void deleteById(Long id) { + reservations.removeIf(reservation -> reservation.getId().equals(id)); + } + + public void clear() { + reservations.clear(); + counter.set(0L); + }} diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java index 8f60e5af5..51188133a 100644 --- a/src/main/java/roomescape/service/ReservationService.java +++ b/src/main/java/roomescape/service/ReservationService.java @@ -1,54 +1,49 @@ package roomescape.service; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import roomescape.dto.ReservationCreateRequest; import roomescape.model.Reservation; +import roomescape.repository.ReservationRepository; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicLong; // 1. ID 생성을 위해 추가 @Service public class ReservationService { - private final List reservations = new ArrayList<>(); + private final ReservationRepository reservationRepository; - private final AtomicLong counter = new AtomicLong(); - - public ReservationService() { - reservations.add(new Reservation(counter.incrementAndGet(), "브라운", "2025-01-01", "10:00")); - reservations.add(new Reservation(counter.incrementAndGet(), "코니", "2025-01-02", "11:00")); + @Autowired + public ReservationService(ReservationRepository reservationRepository) { + this.reservationRepository = reservationRepository; } public List getAllReservations() { - return reservations; + return reservationRepository.findAll(); } - public Reservation addReservation(Reservation newReservation) { - Reservation savedReservation = new Reservation( - counter.incrementAndGet(), // 새 ID 발급 - newReservation.getName(), - newReservation.getDate(), - newReservation.getTime() + + public Reservation addReservation(ReservationCreateRequest requestDto) { + Reservation reservationToSave = new Reservation( + null, + requestDto.name(), + requestDto.date(), + requestDto.time() ); - reservations.add(savedReservation); - return savedReservation; + return reservationRepository.save(reservationToSave); } - - public void deleteReservation(Long id) { - boolean exists = reservations.stream() - .anyMatch(reservation -> reservation.getId().equals(id)); + boolean exists = reservationRepository.existsById(id); if (!exists) { throw new IllegalArgumentException("존재하지 않는 예약 ID입니다: " + id); } - reservations.removeIf(reservation -> reservation.getId().equals(id)); + reservationRepository.deleteById(id); } public void clear() { - reservations.clear(); - counter.set(0L); //테스트를 위한 코드 입니다. + reservationRepository.clear(); } } From b3d2c0b09f4b9be5769ca8cb92cbe07c25a31ae2 Mon Sep 17 00:00:00 2001 From: nonactress Date: Sun, 16 Nov 2025 22:47:35 +0900 Subject: [PATCH 10/18] =?UTF-8?q?fix=20:=20=EA=B0=9C=ED=96=89=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/RoomescapeApplication.java | 1 - src/main/java/roomescape/controller/AdminController.java | 3 +-- .../java/roomescape/controller/GlobalExceptionHandler.java | 6 +++--- src/main/java/roomescape/dto/ReservationCreateRequest.java | 4 ---- src/main/java/roomescape/dto/ReservationDelete.java | 3 --- src/main/java/roomescape/dto/ReservationResponse.java | 3 --- src/main/java/roomescape/model/Reservation.java | 5 +---- .../java/roomescape/repository/ReservationRepository.java | 7 ++----- 8 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/main/java/roomescape/RoomescapeApplication.java b/src/main/java/roomescape/RoomescapeApplication.java index 702706791..2ca0f743f 100644 --- a/src/main/java/roomescape/RoomescapeApplication.java +++ b/src/main/java/roomescape/RoomescapeApplication.java @@ -8,5 +8,4 @@ public class RoomescapeApplication { public static void main(String[] args) { SpringApplication.run(RoomescapeApplication.class, args); } - } diff --git a/src/main/java/roomescape/controller/AdminController.java b/src/main/java/roomescape/controller/AdminController.java index 5ff1dc3fe..dcd686ea4 100644 --- a/src/main/java/roomescape/controller/AdminController.java +++ b/src/main/java/roomescape/controller/AdminController.java @@ -12,8 +12,7 @@ public String home() { } @GetMapping("/reservation") - public String reservation() - { + public String reservation() { return "reservation"; } } diff --git a/src/main/java/roomescape/controller/GlobalExceptionHandler.java b/src/main/java/roomescape/controller/GlobalExceptionHandler.java index 5a70e2333..f8e55ba52 100644 --- a/src/main/java/roomescape/controller/GlobalExceptionHandler.java +++ b/src/main/java/roomescape/controller/GlobalExceptionHandler.java @@ -1,4 +1,4 @@ -package roomescape.controller; // (또는 roomescape.exception) +package roomescape.controller; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -19,8 +19,8 @@ public ResponseEntity> handleValidationExceptions( ex.getBindingResult() .getFieldErrors() .forEach(error -> - errors.put(error.getField(), error.getDefaultMessage()) - ); + errors.put(error.getField(), error.getDefaultMessage()) + ); return ResponseEntity.badRequest().body(errors); } diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java index 1778a78fe..34e63b42a 100644 --- a/src/main/java/roomescape/dto/ReservationCreateRequest.java +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -2,10 +2,6 @@ import jakarta.validation.constraints.NotBlank; -import java.time.LocalDate; -import java.time.LocalTime; - - public record ReservationCreateRequest( @NotBlank(message = "이름은 필수 항목입니다.") diff --git a/src/main/java/roomescape/dto/ReservationDelete.java b/src/main/java/roomescape/dto/ReservationDelete.java index c9f5639d5..7bdc3a33e 100644 --- a/src/main/java/roomescape/dto/ReservationDelete.java +++ b/src/main/java/roomescape/dto/ReservationDelete.java @@ -1,8 +1,5 @@ package roomescape.dto; -import java.time.LocalDate; -import java.time.LocalTime; - public record ReservationDelete ( int id, String name, diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java index 6f33e2967..afb90dba7 100644 --- a/src/main/java/roomescape/dto/ReservationResponse.java +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -2,9 +2,6 @@ import roomescape.model.Reservation; -import java.time.LocalDate; -import java.time.LocalTime; - public record ReservationResponse( long id, String name, diff --git a/src/main/java/roomescape/model/Reservation.java b/src/main/java/roomescape/model/Reservation.java index 610abbc52..1c6e7284c 100644 --- a/src/main/java/roomescape/model/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -1,10 +1,7 @@ package roomescape.model; -import java.time.LocalDate; -import java.time.LocalTime; -public class Reservation -{ +public class Reservation { Long id; String name; String date; diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java index cf24f7086..cd423b883 100644 --- a/src/main/java/roomescape/repository/ReservationRepository.java +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -2,10 +2,6 @@ import org.springframework.stereotype.Repository; import roomescape.model.Reservation; -import roomescape.dto.ReservationCreateRequest; - -import java.time.LocalDate; -import java.time.LocalTime; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @@ -48,4 +44,5 @@ public void deleteById(Long id) { public void clear() { reservations.clear(); counter.set(0L); - }} + } +} From 2bc21d80e9fdbd9be6f1855b9346b5272e562714 Mon Sep 17 00:00:00 2001 From: nonactress Date: Sun, 16 Nov 2025 23:05:30 +0900 Subject: [PATCH 11/18] =?UTF-8?q?fix=20:=20=EA=B0=9C=ED=96=89=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 - src/main/java/roomescape/dto/ReservationCreateRequest.java | 3 --- src/main/java/roomescape/dto/ReservationDelete.java | 2 -- src/main/java/roomescape/dto/ReservationResponse.java | 3 --- src/main/java/roomescape/model/Reservation.java | 3 --- src/main/java/roomescape/repository/ReservationRepository.java | 1 + 6 files changed, 1 insertion(+), 12 deletions(-) diff --git a/README.md b/README.md index 61f2e5490..17704f9e4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - ### 컨트롤러 (Controller) - `AdminController`: `@Controller`를 사용하여 웹 페이지를 반환합니다. 사용자가 특정 경로로 접속했을 때, 해당하는 HTML 파일을 렌더링하여 보여줍니다. - `/`: `home.html` (홈 페이지) diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java index 1778a78fe..454f2b5d8 100644 --- a/src/main/java/roomescape/dto/ReservationCreateRequest.java +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -2,9 +2,6 @@ import jakarta.validation.constraints.NotBlank; -import java.time.LocalDate; -import java.time.LocalTime; - public record ReservationCreateRequest( diff --git a/src/main/java/roomescape/dto/ReservationDelete.java b/src/main/java/roomescape/dto/ReservationDelete.java index ca28d10a6..7bdc3a33e 100644 --- a/src/main/java/roomescape/dto/ReservationDelete.java +++ b/src/main/java/roomescape/dto/ReservationDelete.java @@ -6,6 +6,4 @@ public record ReservationDelete ( String date, String time ){ - - } diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java index 6f33e2967..afb90dba7 100644 --- a/src/main/java/roomescape/dto/ReservationResponse.java +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -2,9 +2,6 @@ import roomescape.model.Reservation; -import java.time.LocalDate; -import java.time.LocalTime; - public record ReservationResponse( long id, String name, diff --git a/src/main/java/roomescape/model/Reservation.java b/src/main/java/roomescape/model/Reservation.java index 610abbc52..767743d98 100644 --- a/src/main/java/roomescape/model/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -1,8 +1,5 @@ package roomescape.model; -import java.time.LocalDate; -import java.time.LocalTime; - public class Reservation { Long id; diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java index cd423b883..50ad32f75 100644 --- a/src/main/java/roomescape/repository/ReservationRepository.java +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -2,6 +2,7 @@ import org.springframework.stereotype.Repository; import roomescape.model.Reservation; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; From 2a5c8051380e1e7f25dfd51afa17e09080762716 Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 19 Nov 2025 22:08:21 +0900 Subject: [PATCH 12/18] =?UTF-8?q?refactor=20:=20=EB=A0=88=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC=EB=A5=BC=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=97=90=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ReservationRepository.java | 6 ++-- .../service/ReservationService.java | 32 +++++++------------ 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java index 50ad32f75..f3687b64f 100644 --- a/src/main/java/roomescape/repository/ReservationRepository.java +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -1,4 +1,4 @@ -package roomescape.repository; // (새 패키지) +package roomescape.repository; import org.springframework.stereotype.Repository; import roomescape.model.Reservation; @@ -38,8 +38,8 @@ public boolean existsById(Long id) { .anyMatch(reservation -> reservation.getId().equals(id)); } - public void deleteById(Long id) { - reservations.removeIf(reservation -> reservation.getId().equals(id)); + public Boolean deleteById(Long id) { + return reservations.removeIf(reservation -> reservation.getId().equals(id)); } public void clear() { diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java index 8f60e5af5..b59ee2f8a 100644 --- a/src/main/java/roomescape/service/ReservationService.java +++ b/src/main/java/roomescape/service/ReservationService.java @@ -1,7 +1,9 @@ package roomescape.service; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import roomescape.model.Reservation; +import roomescape.repository.ReservationRepository; import java.util.ArrayList; import java.util.List; @@ -10,45 +12,35 @@ @Service public class ReservationService { - private final List reservations = new ArrayList<>(); + private final ReservationRepository reservationRepository; - private final AtomicLong counter = new AtomicLong(); - - public ReservationService() { - reservations.add(new Reservation(counter.incrementAndGet(), "브라운", "2025-01-01", "10:00")); - reservations.add(new Reservation(counter.incrementAndGet(), "코니", "2025-01-02", "11:00")); + @Autowired + public ReservationService(ReservationRepository reservationRepository) { + this.reservationRepository = reservationRepository; } public List getAllReservations() { - return reservations; + return reservationRepository.findAll(); } public Reservation addReservation(Reservation newReservation) { - Reservation savedReservation = new Reservation( - counter.incrementAndGet(), // 새 ID 발급 - newReservation.getName(), - newReservation.getDate(), - newReservation.getTime() - ); - reservations.add(savedReservation); - return savedReservation; + + return reservationRepository.save(newReservation); } public void deleteReservation(Long id) { - boolean exists = reservations.stream() - .anyMatch(reservation -> reservation.getId().equals(id)); + boolean exists = reservationRepository.existsById(id); if (!exists) { throw new IllegalArgumentException("존재하지 않는 예약 ID입니다: " + id); } - reservations.removeIf(reservation -> reservation.getId().equals(id)); + reservationRepository.deleteById(id); } public void clear() { - reservations.clear(); - counter.set(0L); //테스트를 위한 코드 입니다. + reservationRepository.clear(); } } From 20d0881f0dc163844df6fd5771db467fbc90eea5 Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 19 Nov 2025 22:09:03 +0900 Subject: [PATCH 13/18] =?UTF-8?q?delete=20:=20=EC=95=88=EC=93=B0=EB=8A=94?= =?UTF-8?q?=20dto=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/dto/ReservationDelete.java | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/main/java/roomescape/dto/ReservationDelete.java diff --git a/src/main/java/roomescape/dto/ReservationDelete.java b/src/main/java/roomescape/dto/ReservationDelete.java deleted file mode 100644 index 7bdc3a33e..000000000 --- a/src/main/java/roomescape/dto/ReservationDelete.java +++ /dev/null @@ -1,9 +0,0 @@ -package roomescape.dto; - -public record ReservationDelete ( - int id, - String name, - String date, - String time -){ -} From b6887e07175e3947cf367a28e9d763f0e6ef9d5e Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 19 Nov 2025 22:18:26 +0900 Subject: [PATCH 14/18] =?UTF-8?q?refactor=20:=20localtime,date=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20dto=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/ReservationCreateRequest.java | 18 ++++++-- .../java/roomescape/model/Reservation.java | 43 ++++++++----------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java index 454f2b5d8..11cd82e32 100644 --- a/src/main/java/roomescape/dto/ReservationCreateRequest.java +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -1,6 +1,11 @@ package roomescape.dto; +import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.validation.constraints.NotBlank; +import roomescape.model.Reservation; + +import java.time.LocalDate; +import java.time.LocalTime; public record ReservationCreateRequest( @@ -9,10 +14,17 @@ public record ReservationCreateRequest( String name, @NotBlank(message = "날짜는 필수 항목입니다.") - String date, + @JsonFormat(pattern = "yyyy-MM-dd") + LocalDate date, @NotBlank(message = "시간은 필수 항목입니다.") - String time + @JsonFormat(pattern = "HH:mm") + LocalTime time ) { - + public Reservation toEntity() + { + return new Reservation(name,date,time); + } } + + diff --git a/src/main/java/roomescape/model/Reservation.java b/src/main/java/roomescape/model/Reservation.java index 767743d98..4cee58f0c 100644 --- a/src/main/java/roomescape/model/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -1,37 +1,28 @@ package roomescape.model; -public class Reservation -{ - Long id; - String name; - String date; - String time; +import java.time.LocalDate; +import java.time.LocalTime; +public class Reservation { + private Long id; + private String name; + private LocalDate date; + private LocalTime time; - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getDate() { - return date; - } - - public String getTime() { - return time; - } - - public Reservation(String name, String date, String time) { + // 생성자 + public Reservation(Long id, String name, LocalDate date, LocalTime time) { + this.id = id; this.name = name; this.date = date; this.time = time; } - public Reservation(Long id, String name, String date, String time) { - this(name, date, time); - this.id = id; + public Reservation(String name, LocalDate date, LocalTime time) { + this(null, name, date, time); } + + public Long getId() { return id; } + public String getName() { return name; } + public LocalDate getDate() { return date; } + public LocalTime getTime() { return time; } } From 675e1136d1073b198fc1e73718760bc2eac1fc39 Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 19 Nov 2025 22:20:42 +0900 Subject: [PATCH 15/18] =?UTF-8?q?refactor=20:=20localtime,date=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ReservationRepository.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java index f3687b64f..28cc9e504 100644 --- a/src/main/java/roomescape/repository/ReservationRepository.java +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -3,6 +3,8 @@ import org.springframework.stereotype.Repository; import roomescape.model.Reservation; +import java.time.LocalDate; +import java.time.LocalTime; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @@ -14,8 +16,19 @@ public class ReservationRepository { private final AtomicLong counter = new AtomicLong(); public ReservationRepository() { - this.save(new Reservation(null, "브라운", "2025-01-01", "12:0")); - this.save(new Reservation(null, "코니", "2025-01-02", "11:00")); + this.save(new Reservation( + null, + "브라운", + LocalDate.of(2025, 1, 1), + LocalTime.of(12, 0) + )); + + this.save(new Reservation( + null, + "코니", + LocalDate.of(2025, 1, 2), + LocalTime.of(11, 0) + )); } public List findAll() { @@ -38,8 +51,8 @@ public boolean existsById(Long id) { .anyMatch(reservation -> reservation.getId().equals(id)); } - public Boolean deleteById(Long id) { - return reservations.removeIf(reservation -> reservation.getId().equals(id)); + public void deleteById(Long id) { + reservations.removeIf(reservation -> reservation.getId().equals(id)); } public void clear() { From 9934f1107497324f51634a5312ea7ccde0fba697 Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 19 Nov 2025 22:26:11 +0900 Subject: [PATCH 16/18] =?UTF-8?q?refactor=20:=20localtime,date=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EB=B3=80=EA=B2=BD(notBlank->notNull)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/dto/ReservationCreateRequest.java | 5 +++-- src/main/java/roomescape/dto/ReservationResponse.java | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java index 11cd82e32..ac575e14b 100644 --- a/src/main/java/roomescape/dto/ReservationCreateRequest.java +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import roomescape.model.Reservation; import java.time.LocalDate; @@ -13,11 +14,11 @@ public record ReservationCreateRequest( @NotBlank(message = "이름은 필수 항목입니다.") String name, - @NotBlank(message = "날짜는 필수 항목입니다.") + @NotNull(message = "날짜는 필수 항목입니다.") @JsonFormat(pattern = "yyyy-MM-dd") LocalDate date, - @NotBlank(message = "시간은 필수 항목입니다.") + @NotNull(message = "시간은 필수 항목입니다.") @JsonFormat(pattern = "HH:mm") LocalTime time ) { diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java index afb90dba7..1699ebdf2 100644 --- a/src/main/java/roomescape/dto/ReservationResponse.java +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -2,11 +2,14 @@ import roomescape.model.Reservation; +import java.time.LocalDate; +import java.time.LocalTime; + public record ReservationResponse( long id, String name, - String date, - String time + LocalDate date, + LocalTime time ) { public static ReservationResponse from(Reservation reservation) { From 9b0add5e296b589bf328ddfb6bdfbb296677af6e Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 19 Nov 2025 22:27:35 +0900 Subject: [PATCH 17/18] =?UTF-8?q?refactor=20:=20=EA=B0=9C=ED=98=95?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 6 +---- .../dto/ReservationCreateRequest.java | 7 +++-- .../roomescape/dto/ReservationResponse.java | 27 +++++++++---------- .../java/roomescape/model/Reservation.java | 1 - 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 6788c3327..fda21bd72 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -41,11 +41,7 @@ public ResponseEntity createReservation( @Valid @RequestBody ReservationCreateRequest requestDto ) { - Reservation reservationToCreate = new Reservation( - requestDto.name(), - requestDto.date(), - requestDto.time() - ); + Reservation reservationToCreate = requestDto.toEntity(); Reservation savedReservation = service.addReservation(reservationToCreate); diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java index ac575e14b..e926981e1 100644 --- a/src/main/java/roomescape/dto/ReservationCreateRequest.java +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -22,10 +22,9 @@ public record ReservationCreateRequest( @JsonFormat(pattern = "HH:mm") LocalTime time ) { - public Reservation toEntity() - { - return new Reservation(name,date,time); - } + public Reservation toEntity() { + return new Reservation(name, date, time); + } } diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java index 1699ebdf2..599b2f589 100644 --- a/src/main/java/roomescape/dto/ReservationResponse.java +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -6,19 +6,18 @@ import java.time.LocalTime; public record ReservationResponse( - long id, - String name, - LocalDate date, - LocalTime time - ) - { - public static ReservationResponse from(Reservation reservation) { - return new ReservationResponse( - reservation.getId(), - reservation.getName(), - reservation.getDate(), - reservation.getTime() - ); - } + long id, + String name, + LocalDate date, + LocalTime time +) { + public static ReservationResponse from(Reservation reservation) { + return new ReservationResponse( + reservation.getId(), + reservation.getName(), + reservation.getDate(), + reservation.getTime() + ); } +} diff --git a/src/main/java/roomescape/model/Reservation.java b/src/main/java/roomescape/model/Reservation.java index 4cee58f0c..336082c9e 100644 --- a/src/main/java/roomescape/model/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -9,7 +9,6 @@ public class Reservation { private LocalDate date; private LocalTime time; - // 생성자 public Reservation(Long id, String name, LocalDate date, LocalTime time) { this.id = id; this.name = name; From 1422774b7426bef5192ae7a98300cb89b56f8e0f Mon Sep 17 00:00:00 2001 From: nonactress Date: Wed, 19 Nov 2025 22:28:33 +0900 Subject: [PATCH 18/18] =?UTF-8?q?refactor=20:=20=EA=B0=9C=ED=98=95?= =?UTF-8?q?=EC=A0=95=EB=A6=AC2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/controller/ReservationController.java | 2 +- src/main/java/roomescape/repository/ReservationRepository.java | 2 +- src/main/java/roomescape/service/ReservationService.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index fda21bd72..18ff306d7 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -25,7 +25,7 @@ public ReservationController(ReservationService service) { @GetMapping public List getAllReservations() { - return service.getAllReservations().stream() + return service.getAllReservations().stream() .map(ReservationResponse::from) // (::from은 ReservationResponse::from과 동일) .toList(); } diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java index 28cc9e504..79cf25d56 100644 --- a/src/main/java/roomescape/repository/ReservationRepository.java +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -52,7 +52,7 @@ public boolean existsById(Long id) { } public void deleteById(Long id) { - reservations.removeIf(reservation -> reservation.getId().equals(id)); + reservations.removeIf(reservation -> reservation.getId().equals(id)); } public void clear() { diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java index b59ee2f8a..b760fb189 100644 --- a/src/main/java/roomescape/service/ReservationService.java +++ b/src/main/java/roomescape/service/ReservationService.java @@ -29,7 +29,6 @@ public Reservation addReservation(Reservation newReservation) { } - public void deleteReservation(Long id) { boolean exists = reservationRepository.existsById(id);