From e1d4c6802a4c6e10f7827c113cedcd5d4a627a08 Mon Sep 17 00:00:00 2001 From: us2xince Date: Sat, 15 Nov 2025 02:17:46 +0900 Subject: [PATCH 1/9] =?UTF-8?q?<1=EB=8B=A8=EA=B3=84:=ED=99=88=ED=99=94?= =?UTF-8?q?=EB=A9=B4>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/HomeController.java | 12 ++++++++++++ src/test/java/roomescape/MissionStepTest.java | 8 ++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/main/java/roomescape/HomeController.java diff --git a/src/main/java/roomescape/HomeController.java b/src/main/java/roomescape/HomeController.java new file mode 100644 index 000000000..f293db616 --- /dev/null +++ b/src/main/java/roomescape/HomeController.java @@ -0,0 +1,12 @@ +package roomescape; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class HomeController { + @GetMapping("/") + public String home() { + return "home"; + } +} diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index cf4efbe91..4ad1744bc 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -1,10 +1,16 @@ package roomescape; import io.restassured.RestAssured; +import io.restassured.http.ContentType; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.Matchers.is; + @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) public class MissionStepTest { @@ -16,4 +22,6 @@ public class MissionStepTest { .then().log().all() .statusCode(200); } + + } From b5b6908a16a049b8ba391505b117567d6bfe2c9f Mon Sep 17 00:00:00 2001 From: us2xince Date: Sat, 15 Nov 2025 02:18:19 +0900 Subject: [PATCH 2/9] =?UTF-8?q?<2=EB=8B=A8=EA=B3=84:=ED=99=88=ED=99=94?= =?UTF-8?q?=EB=A9=B4>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ src/main/java/roomescape/Reservation.java | 27 +++++++++++++++++++ .../roomescape/ReservationAPIController.java | 20 ++++++++++++++ .../roomescape/ReservationController.java | 17 ++++++++++++ src/test/java/roomescape/MissionStepTest.java | 13 +++++++++ 5 files changed, 79 insertions(+) create mode 100644 src/main/java/roomescape/Reservation.java create mode 100644 src/main/java/roomescape/ReservationAPIController.java create mode 100644 src/main/java/roomescape/ReservationController.java diff --git a/build.gradle b/build.gradle index 57267157c..e9541e4de 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' } test { diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java new file mode 100644 index 000000000..bd93e8e6f --- /dev/null +++ b/src/main/java/roomescape/Reservation.java @@ -0,0 +1,27 @@ +package roomescape; + +public class Reservation { + private Long id; + private String name; + private String date; + private String time; + + public Reservation(Long id, String name, String date, String time) { + this.id = id; + this.name = name; + this.date = date; + this.time = time; + } + public Long getId() { return id;} + public void setId(Long id) { this.id = id;} + + public String getName() { return name;} + public void setName(String name) { this.name = name;} + + public String getDate() { return date; } + public void setDate(String date) { this.date = date; } + + public String getTime() { return time; } + public void setTime(String time) { this.time = time; } +} + diff --git a/src/main/java/roomescape/ReservationAPIController.java b/src/main/java/roomescape/ReservationAPIController.java new file mode 100644 index 000000000..ed7ab2cf1 --- /dev/null +++ b/src/main/java/roomescape/ReservationAPIController.java @@ -0,0 +1,20 @@ +package roomescape; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; + + +@RestController +public class ReservationAPIController { + @GetMapping("/reservations") + public List getReservations() { + return Arrays.asList( + new Reservation(1L,"브라운", "2023-10-15","10:00"), + new Reservation(2L,"브라운","2025-11-01","19:00") + ); + } +} diff --git a/src/main/java/roomescape/ReservationController.java b/src/main/java/roomescape/ReservationController.java new file mode 100644 index 000000000..913283a81 --- /dev/null +++ b/src/main/java/roomescape/ReservationController.java @@ -0,0 +1,17 @@ +package roomescape; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.ui.Model; + +import java.util.Arrays; +import java.util.List; + +@Controller +public class ReservationController { + @GetMapping("/reservation") + public String reservation() { + return "reservation"; + } + +} diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 4ad1744bc..672e8a3a0 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -23,5 +23,18 @@ public class MissionStepTest { .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(2)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수 만큼 검증하거나 0개임을 확인하세요. + } } From d28776fb050893a0fe457668bce9447467f71a2f Mon Sep 17 00:00:00 2001 From: us2xince Date: Sat, 15 Nov 2025 02:42:01 +0900 Subject: [PATCH 3/9] =?UTF-8?q?<3=EB=8B=A8=EA=B3=84:=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80/=EC=B7=A8=EC=86=8C>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/ReservationAPIController.java | 37 +++++++++++++++---- .../roomescape/ReservationController.java | 8 ++-- src/test/java/roomescape/MissionStepTest.java | 35 +++++++++++++++++- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/main/java/roomescape/ReservationAPIController.java b/src/main/java/roomescape/ReservationAPIController.java index ed7ab2cf1..7e6712024 100644 --- a/src/main/java/roomescape/ReservationAPIController.java +++ b/src/main/java/roomescape/ReservationAPIController.java @@ -1,20 +1,43 @@ package roomescape; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; @RestController +@RequestMapping("/reservations") public class ReservationAPIController { - @GetMapping("/reservations") + private final List reservations=new ArrayList<>(); + private final AtomicLong idCounter=new AtomicLong(1); + + @GetMapping public List getReservations() { - return Arrays.asList( - new Reservation(1L,"브라운", "2023-10-15","10:00"), - new Reservation(2L,"브라운","2025-11-01","19:00") + return reservations; + } + @PostMapping + public ResponseEntity addReservation(@RequestBody Map params) { + Reservation reservation = new Reservation( + idCounter.getAndIncrement(), + params.get("name"), + params.get("date"), + params.get("time") ); + reservations.add(reservation); + return ResponseEntity.status(HttpStatus.CREATED) + .header("Location", "/reservations/" + reservation.getId()) + .body(reservation); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteReservation(@PathVariable Long id) { + reservations.removeIf(r -> r.getId().equals(id)); + return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/roomescape/ReservationController.java b/src/main/java/roomescape/ReservationController.java index 913283a81..f3a67ab4d 100644 --- a/src/main/java/roomescape/ReservationController.java +++ b/src/main/java/roomescape/ReservationController.java @@ -1,14 +1,16 @@ package roomescape; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; + @Controller public class ReservationController { + private final List reservations = new ArrayList<>(); + @GetMapping("/reservation") public String reservation() { return "reservation"; diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 672e8a3a0..a295dd4e8 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -34,7 +34,40 @@ public class MissionStepTest { .when().get("/reservations") .then().log().all() .statusCode(200) - .body("size()", is(2)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수 만큼 검증하거나 0개임을 확인하세요. + .body("size()", is(0)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수 만큼 검증하거나 0개임을 확인하세요. + } + @Test + void 삼단계() { + Map params = new HashMap<>(); + params.put("name", "브라운"); + params.put("date", "2023-08-05"); + params.put("time", "15:40"); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(params) + .when().post("/reservations") + .then().log().all() + .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)); + + RestAssured.given().log().all() + .when().delete("/reservations/1") + .then().log().all() + .statusCode(204); + + RestAssured.given().log().all() + .when().get("/reservations") + .then().log().all() + .statusCode(200) + .body("size()", is(0)); } } From 6262105f2db44ae6807d2c1a70e77859f1d15aff Mon Sep 17 00:00:00 2001 From: us2xince Date: Sat, 15 Nov 2025 02:56:46 +0900 Subject: [PATCH 4/9] =?UTF-8?q?<4=EB=8B=A8=EA=B3=84:=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/GlobalExceptionHandler.java | 13 ++++++++++++ .../InvalidReservationException.java | 7 +++++++ .../roomescape/ReservationAPIController.java | 12 ++++++++++- src/test/java/roomescape/MissionStepTest.java | 21 +++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/main/java/roomescape/GlobalExceptionHandler.java create mode 100644 src/main/java/roomescape/InvalidReservationException.java diff --git a/src/main/java/roomescape/GlobalExceptionHandler.java b/src/main/java/roomescape/GlobalExceptionHandler.java new file mode 100644 index 000000000..2c7fa41b8 --- /dev/null +++ b/src/main/java/roomescape/GlobalExceptionHandler.java @@ -0,0 +1,13 @@ +package roomescape; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(InvalidReservationException.class) + public ResponseEntity handleInvalidReservationException(InvalidReservationException ex) { + return ResponseEntity.badRequest().body(ex.getMessage()); + } +} diff --git a/src/main/java/roomescape/InvalidReservationException.java b/src/main/java/roomescape/InvalidReservationException.java new file mode 100644 index 000000000..f9a471674 --- /dev/null +++ b/src/main/java/roomescape/InvalidReservationException.java @@ -0,0 +1,7 @@ +package roomescape; + +public class InvalidReservationException extends RuntimeException { + public InvalidReservationException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/ReservationAPIController.java b/src/main/java/roomescape/ReservationAPIController.java index 7e6712024..1d8905b40 100644 --- a/src/main/java/roomescape/ReservationAPIController.java +++ b/src/main/java/roomescape/ReservationAPIController.java @@ -29,6 +29,13 @@ public ResponseEntity addReservation(@RequestBody Map addReservation(@RequestBody Map deleteReservation(@PathVariable Long id) { - reservations.removeIf(r -> r.getId().equals(id)); + boolean removed= reservations.removeIf(r -> r.getId().equals(id)); + if (!removed) { + throw new InvalidReservationException("삭제할 예약이 없습니다."); + } return ResponseEntity.noContent().build(); } } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index a295dd4e8..020b5c683 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -69,5 +69,26 @@ public class MissionStepTest { .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 01be3170dd335aed1562f23d728c5e3eca2513a0 Mon Sep 17 00:00:00 2001 From: us2xince Date: Sat, 15 Nov 2025 03:21:13 +0900 Subject: [PATCH 5/9] =?UTF-8?q?<5=EB=8B=A8=EA=B3=84:=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EA=B8=B0>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ .../{ => controller}/HomeController.java | 2 +- .../ReservationAPIController.java | 5 +++-- .../ReservationController.java | 3 ++- .../roomescape/{ => model}/Reservation.java | 2 +- src/main/resources/application.properties | 2 ++ src/main/resources/schema.sql | 6 ++++++ src/test/java/roomescape/MissionStepTest.java | 19 +++++++++++++++++++ 8 files changed, 37 insertions(+), 5 deletions(-) rename src/main/java/roomescape/{ => controller}/HomeController.java (89%) rename src/main/java/roomescape/{ => controller}/ReservationAPIController.java (93%) rename src/main/java/roomescape/{ => controller}/ReservationController.java (84%) rename src/main/java/roomescape/{ => model}/Reservation.java (96%) create mode 100644 src/main/resources/schema.sql diff --git a/build.gradle b/build.gradle index e9541e4de..6cfc05623 100644 --- a/build.gradle +++ b/build.gradle @@ -16,8 +16,11 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' + testImplementation 'org.assertj:assertj-core:3.24.2' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'com.h2database:h2' } test { diff --git a/src/main/java/roomescape/HomeController.java b/src/main/java/roomescape/controller/HomeController.java similarity index 89% rename from src/main/java/roomescape/HomeController.java rename to src/main/java/roomescape/controller/HomeController.java index f293db616..34549a2af 100644 --- a/src/main/java/roomescape/HomeController.java +++ b/src/main/java/roomescape/controller/HomeController.java @@ -1,4 +1,4 @@ -package roomescape; +package roomescape.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; diff --git a/src/main/java/roomescape/ReservationAPIController.java b/src/main/java/roomescape/controller/ReservationAPIController.java similarity index 93% rename from src/main/java/roomescape/ReservationAPIController.java rename to src/main/java/roomescape/controller/ReservationAPIController.java index 1d8905b40..093355f93 100644 --- a/src/main/java/roomescape/ReservationAPIController.java +++ b/src/main/java/roomescape/controller/ReservationAPIController.java @@ -1,11 +1,12 @@ -package roomescape; +package roomescape.controller; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import roomescape.InvalidReservationException; +import roomescape.model.Reservation; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; diff --git a/src/main/java/roomescape/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java similarity index 84% rename from src/main/java/roomescape/ReservationController.java rename to src/main/java/roomescape/controller/ReservationController.java index f3a67ab4d..1f79d337a 100644 --- a/src/main/java/roomescape/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -1,7 +1,8 @@ -package roomescape; +package roomescape.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; +import roomescape.model.Reservation; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/model/Reservation.java similarity index 96% rename from src/main/java/roomescape/Reservation.java rename to src/main/java/roomescape/model/Reservation.java index bd93e8e6f..b90f24512 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 { private Long id; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29bb..bd8bae115 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1,2 @@ +spring.datasource.url=jdbc:h2:mem:database +spring.h2.console.enabled=true \ No newline at end of file diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 000000000..c3907a0e9 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE reservation( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(50) NOT NULL, + date DATE NOT NULL, + time TIME NOT NULL +); \ No newline at end of file diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 020b5c683..830d501ff 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -3,12 +3,19 @@ import io.restassured.RestAssured; import io.restassured.http.ContentType; 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 org.springframework.jdbc.core.JdbcTemplate; +import java.sql.Connection; +import java.sql.SQLException; import java.util.HashMap; import java.util.Map; +import java.sql.Connection; +import java.sql.SQLException; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @@ -90,5 +97,17 @@ public class MissionStepTest { .then().log().all() .statusCode(400); } + @Autowired + private JdbcTemplate jdbcTemplate; + @Test + void 오단계() { + try (Connection connection = jdbcTemplate.getDataSource().getConnection()) { + assertThat(connection).isNotNull(); + assertThat(connection.getCatalog()).isEqualTo("DATABASE"); + assertThat(connection.getMetaData().getTables(null, null, "RESERVATION", null).next()).isTrue(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } } From 28e23e78ce81f107364a9857bac5f1c35c311933 Mon Sep 17 00:00:00 2001 From: us2xince Date: Sat, 29 Nov 2025 14:41:23 +0900 Subject: [PATCH 6/9] =?UTF-8?q?<6=EB=8B=A8=EA=B3=84:=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A1=B0=ED=9A=8C=ED=95=98=EA=B8=B0>=20<7=EB=8B=A8?= =?UTF-8?q?=EA=B3=84:=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80/?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=ED=95=98=EA=B8=B0>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationAPIController.java | 35 +++++++------ .../controller/ReservationController.java | 7 ++- .../repository/ReservationRepository.java | 51 +++++++++++++++++++ src/main/resources/application.properties | 5 +- src/main/resources/schema.sql | 2 +- src/test/java/roomescape/MissionStepTest.java | 43 ++++++++++++++++ 6 files changed, 123 insertions(+), 20 deletions(-) create mode 100644 src/main/java/roomescape/repository/ReservationRepository.java diff --git a/src/main/java/roomescape/controller/ReservationAPIController.java b/src/main/java/roomescape/controller/ReservationAPIController.java index 093355f93..4199ce447 100644 --- a/src/main/java/roomescape/controller/ReservationAPIController.java +++ b/src/main/java/roomescape/controller/ReservationAPIController.java @@ -5,7 +5,9 @@ import org.springframework.web.bind.annotation.*; import roomescape.InvalidReservationException; import roomescape.model.Reservation; +import roomescape.repository.ReservationRepository; +import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -15,21 +17,17 @@ @RestController @RequestMapping("/reservations") public class ReservationAPIController { - private final List reservations=new ArrayList<>(); - private final AtomicLong idCounter=new AtomicLong(1); + private final ReservationRepository reservationRepository; + public ReservationAPIController(ReservationRepository reservationRepository) { + this.reservationRepository = reservationRepository; + } @GetMapping public List getReservations() { - return reservations; + return reservationRepository.findAll(); } @PostMapping public ResponseEntity addReservation(@RequestBody Map params) { - Reservation reservation = new Reservation( - idCounter.getAndIncrement(), - params.get("name"), - params.get("date"), - params.get("time") - ); String name=params.get("name"); String date=params.get("date"); String time=params.get("time"); @@ -37,18 +35,23 @@ public ResponseEntity addReservation(@RequestBody Map deleteReservation(@PathVariable Long id) { - boolean removed= reservations.removeIf(r -> r.getId().equals(id)); - if (!removed) { - throw new InvalidReservationException("삭제할 예약이 없습니다."); + boolean deleted = reservationRepository.deleteById(id); + + if (!deleted) { + throw new InvalidReservationException("삭제 중 오류 발생"); } + return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 1f79d337a..ab4ab7ca4 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import roomescape.model.Reservation; +import roomescape.repository.ReservationRepository; import java.util.ArrayList; import java.util.List; @@ -10,7 +11,11 @@ @Controller public class ReservationController { - private final List reservations = new ArrayList<>(); + private final ReservationRepository reservationRepository; + + public ReservationController(ReservationRepository reservationRepository) { + this.reservationRepository = reservationRepository; + } @GetMapping("/reservation") public String reservation() { diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java new file mode 100644 index 000000000..5d98ddc47 --- /dev/null +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -0,0 +1,51 @@ +package roomescape.repository; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; +import org.springframework.stereotype.Repository; +import roomescape.model.Reservation; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Repository +public class ReservationRepository { + + private final JdbcTemplate jdbcTemplate; + private final SimpleJdbcInsert jdbcInsert; + + public ReservationRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + this.jdbcInsert = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("reservation") + .usingGeneratedKeyColumns("id"); + } + + public List findAll() { + String sql = "SELECT id, name, date, time FROM reservation"; + return jdbcTemplate.query(sql, (rs, rowNum) -> new Reservation( + rs.getLong("id"), + rs.getString("name"), + rs.getString("date"), + rs.getString("time") + )); + } + + public Reservation save(Reservation reservation) { + Map params = new HashMap<>(); + params.put("name", reservation.getName()); + params.put("date", reservation.getDate()); + params.put("time", reservation.getTime()); + + Number key = jdbcInsert.executeAndReturnKey(params); + reservation.setId(key.longValue()); + return reservation; + + } + public boolean deleteById(Long id){ + String sql= "DELETE From reservation WHERE id = ?"; + int deleteRows = jdbcTemplate.update(sql, id); + return deleteRows > 0; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index bd8bae115..79ff90392 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,2 +1,3 @@ -spring.datasource.url=jdbc:h2:mem:database -spring.h2.console.enabled=true \ No newline at end of file +spring.datasource.url=jdbc:h2:mem:database;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.h2.console.enabled=true + diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index c3907a0e9..4af87ce0f 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,4 +1,4 @@ -CREATE TABLE reservation( +CREATE TABLE IF NOT EXISTS reservation( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, date DATE NOT NULL, diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 830d501ff..3f1e45a97 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -7,10 +7,12 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; import org.springframework.jdbc.core.JdbcTemplate; +import roomescape.model.Reservation; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.sql.Connection; import java.sql.SQLException; @@ -110,4 +112,45 @@ public class MissionStepTest { throw new RuntimeException(e); } } + @Test + void 육단계() { + jdbcTemplate.update("INSERT INTO reservation (name, date, time) VALUES (?, ?, ?)", "브라운", "2023-08-05", "15:40"); + + List reservations = RestAssured.given().log().all() + .when().get("/reservations") + .then().log().all() + .statusCode(200).extract() + .jsonPath().getList(".", Reservation.class); + + Integer count = jdbcTemplate.queryForObject("SELECT count(1) from reservation", Integer.class); + + assertThat(reservations.size()).isEqualTo(count); + } + + @Test + void 칠단계() { + Map params = new HashMap<>(); + params.put("name", "브라운"); + params.put("date", "2023-08-05"); + params.put("time", "10:00"); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(params) + .when().post("/reservations") + .then().log().all() + .statusCode(201) + .header("Location", "/reservations/1"); + + Integer count = jdbcTemplate.queryForObject("SELECT count(1) from reservation", Integer.class); + assertThat(count).isEqualTo(1); + + RestAssured.given().log().all() + .when().delete("/reservations/1") + .then().log().all() + .statusCode(204); + + Integer countAfterDelete = jdbcTemplate.queryForObject("SELECT count(1) from reservation", Integer.class); + assertThat(countAfterDelete).isEqualTo(0); + } } From be01cdd29a499202c4de51ee4ce43edf49651e30 Mon Sep 17 00:00:00 2001 From: us2xince Date: Sat, 29 Nov 2025 15:26:55 +0900 Subject: [PATCH 7/9] =?UTF-8?q?<8=EB=8B=A8=EA=B3=84:=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=EA=B8=B0=EB=8A=A5>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/InvalidTimeException.java | 7 +++ .../controller/ReservationAPIController.java | 2 - .../controller/TimeAPIController.java | 49 +++++++++++++++++++ .../roomescape/controller/TimeController.java | 25 ++++++++++ src/main/java/roomescape/model/Time.java | 20 ++++++++ .../roomescape/repository/TimeRepository.java | 47 ++++++++++++++++++ src/main/resources/schema.sql | 4 ++ src/test/java/roomescape/MissionStepTest.java | 25 ++++++++++ 8 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 src/main/java/roomescape/InvalidTimeException.java create mode 100644 src/main/java/roomescape/controller/TimeAPIController.java create mode 100644 src/main/java/roomescape/controller/TimeController.java create mode 100644 src/main/java/roomescape/model/Time.java create mode 100644 src/main/java/roomescape/repository/TimeRepository.java diff --git a/src/main/java/roomescape/InvalidTimeException.java b/src/main/java/roomescape/InvalidTimeException.java new file mode 100644 index 000000000..c01db2728 --- /dev/null +++ b/src/main/java/roomescape/InvalidTimeException.java @@ -0,0 +1,7 @@ +package roomescape; + +public class InvalidTimeException extends RuntimeException { + public InvalidTimeException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/controller/ReservationAPIController.java b/src/main/java/roomescape/controller/ReservationAPIController.java index 4199ce447..906050a04 100644 --- a/src/main/java/roomescape/controller/ReservationAPIController.java +++ b/src/main/java/roomescape/controller/ReservationAPIController.java @@ -8,10 +8,8 @@ import roomescape.repository.ReservationRepository; import java.net.URI; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; @RestController diff --git a/src/main/java/roomescape/controller/TimeAPIController.java b/src/main/java/roomescape/controller/TimeAPIController.java new file mode 100644 index 000000000..e7b8d390e --- /dev/null +++ b/src/main/java/roomescape/controller/TimeAPIController.java @@ -0,0 +1,49 @@ +package roomescape.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import roomescape.InvalidTimeException; +import roomescape.model.Time; +import roomescape.repository.ReservationRepository; +import roomescape.repository.TimeRepository; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/times") +public class TimeAPIController { + private final TimeRepository timeRepository; + + public TimeAPIController(TimeRepository timeRepository) { + this.timeRepository = timeRepository; + } + @GetMapping + public List