From d00ac48303b80f7463dd3ffc8fcb955d87066046 Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Sat, 4 Oct 2025 17:52:25 +0900 Subject: [PATCH 01/11] =?UTF-8?q?[Feat]=20=ED=99=88=20=ED=99=94=EB=A9=B4,?= =?UTF-8?q?=20=EC=98=88=EC=95=BD=20=EC=A1=B0=ED=9A=8C=20=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ .../controller/ReservationController.java | 30 +++++++++++++++++++ .../controller/StartController.java | 18 +++++++++++ src/main/java/roomescape/dto/Reservation.java | 20 +++++++++++++ src/test/java/roomescape/MissionStepTest.java | 16 ++++++++++ 5 files changed, 86 insertions(+) create mode 100644 src/main/java/roomescape/controller/ReservationController.java create mode 100644 src/main/java/roomescape/controller/StartController.java create mode 100644 src/main/java/roomescape/dto/Reservation.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/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java new file mode 100644 index 000000000..d46c55b85 --- /dev/null +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -0,0 +1,30 @@ +package roomescape.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import roomescape.dto.Reservation; + +import java.util.ArrayList; +import java.util.List; + +@Controller +public class ReservationController { + + private List reservations = new ArrayList<>(); + + @GetMapping("/reservation") + public String reservation(){ + return "/reservation.html"; + } + + @GetMapping("/reservations") + public ResponseEntity> reservations(){ + + Reservation reservation1=new Reservation(1L,"브라운","2023-01-01","10:00"); + reservations.add(reservation1); + + return ResponseEntity.ok(reservations); + + } +} diff --git a/src/main/java/roomescape/controller/StartController.java b/src/main/java/roomescape/controller/StartController.java new file mode 100644 index 000000000..5292abcce --- /dev/null +++ b/src/main/java/roomescape/controller/StartController.java @@ -0,0 +1,18 @@ +package roomescape.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class StartController { + + @GetMapping("/") + public String home(){ + return "/home.html"; + } + + + + +} diff --git a/src/main/java/roomescape/dto/Reservation.java b/src/main/java/roomescape/dto/Reservation.java new file mode 100644 index 000000000..555048f3e --- /dev/null +++ b/src/main/java/roomescape/dto/Reservation.java @@ -0,0 +1,20 @@ +package roomescape.dto; + +import java.sql.Date; +import java.sql.Time; +import java.time.LocalDate; +import java.time.LocalTime; + +public class Reservation { + public Long id; + public String name; + public String date; //우선 String으로.. 차후 Date로 형변환 필요하면 교체.. + public String time; //동일!! + + public Reservation(Long id,String name,String date,String time){ + this.id=id; + this.name=name; + this.date=date; + this.time=time; + } +} diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index cf4efbe91..313857a93 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -5,6 +5,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +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 +18,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(1)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수(=1) 만큼 검증. + } } From 5fddb601a0e9cac88d33a6e2d0f92bfb38a60606 Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Sat, 4 Oct 2025 21:27:50 +0900 Subject: [PATCH 02/11] =?UTF-8?q?[Feat]=20return=EA=B0=92=EC=97=90?= =?UTF-8?q?=EC=84=9C=20.html=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/controller/ReservationController.java | 2 +- src/main/java/roomescape/controller/StartController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index d46c55b85..3c9eee57f 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -15,7 +15,7 @@ public class ReservationController { @GetMapping("/reservation") public String reservation(){ - return "/reservation.html"; + return "reservation"; } @GetMapping("/reservations") diff --git a/src/main/java/roomescape/controller/StartController.java b/src/main/java/roomescape/controller/StartController.java index 5292abcce..73f23f94b 100644 --- a/src/main/java/roomescape/controller/StartController.java +++ b/src/main/java/roomescape/controller/StartController.java @@ -9,7 +9,7 @@ public class StartController { @GetMapping("/") public String home(){ - return "/home.html"; + return "home"; } From a120bd321f73071cf4a236186c6bf046d18ac0b0 Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Sat, 4 Oct 2025 21:44:15 +0900 Subject: [PATCH 03/11] =?UTF-8?q?[Feat]=20reservation()=20Model=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=EC=98=88=EC=95=BD=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 3c9eee57f..c1c2cdbfb 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -2,29 +2,32 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; import roomescape.dto.Reservation; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; @Controller public class ReservationController { - private List reservations = new ArrayList<>(); + Reservation reservation1=new Reservation(1L,"브라운","2023-01-01","10:00"); + Reservation reservation2=new Reservation(2L,"브라운","2023-01-02","11:00"); + private List reservations = new ArrayList<>(Arrays.asList(reservation1,reservation2)); @GetMapping("/reservation") - public String reservation(){ + public String reservation(Model model){ + + model.addAttribute(reservations); return "reservation"; } @GetMapping("/reservations") public ResponseEntity> reservations(){ - Reservation reservation1=new Reservation(1L,"브라운","2023-01-01","10:00"); - reservations.add(reservation1); - return ResponseEntity.ok(reservations); - } } From b3cf99409bb3d81bd28d3538837dcfc6d3d497bd Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Sat, 4 Oct 2025 21:49:34 +0900 Subject: [PATCH 04/11] =?UTF-8?q?[Feat]=20reservations()=20@ResponseBody?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/controller/ReservationController.java | 5 +++-- src/test/java/roomescape/MissionStepTest.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index c1c2cdbfb..976734f36 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -26,8 +26,9 @@ public String reservation(Model model){ } @GetMapping("/reservations") - public ResponseEntity> reservations(){ + @ResponseBody + public List reservations(){ - return ResponseEntity.ok(reservations); + return reservations; } } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 313857a93..a1cc39c6e 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -30,6 +30,6 @@ public class MissionStepTest { .when().get("/reservations") .then().log().all() .statusCode(200) - .body("size()", is(1)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수(=1) 만큼 검증. + .body("size()", is(2)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수(=1) 만큼 검증. } } From 81259f3e5d80c368a7402bc0a7077bbfcd4c4fc6 Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Tue, 7 Oct 2025 18:57:10 +0900 Subject: [PATCH 05/11] =?UTF-8?q?[Feat]=20dto=20=EB=A9=A4=EB=B2=84?= =?UTF-8?q?=EB=B3=80=EC=88=98=20private=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/dto/Reservation.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/roomescape/dto/Reservation.java b/src/main/java/roomescape/dto/Reservation.java index 555048f3e..58e425107 100644 --- a/src/main/java/roomescape/dto/Reservation.java +++ b/src/main/java/roomescape/dto/Reservation.java @@ -6,10 +6,10 @@ import java.time.LocalTime; public class Reservation { - public Long id; - public String name; - public String date; //우선 String으로.. 차후 Date로 형변환 필요하면 교체.. - public String time; //동일!! + private Long id; + private String name; + private String date; //우선 String으로.. 차후 Date로 형변환 필요하면 교체.. + private String time; //동일!! public Reservation(Long id,String name,String date,String time){ this.id=id; From 0ea5bfe3c41c1a2c66dac294200327a7301f4984 Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Thu, 23 Oct 2025 15:40:56 +0900 Subject: [PATCH 06/11] =?UTF-8?q?[Fix]=20dto=20=EB=A9=A4=EB=B2=84=EB=B3=80?= =?UTF-8?q?=EC=88=98=20private=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=96=88=EC=9D=84=20=EC=8B=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(@Getter=20=EC=82=AC=EC=9A=A9),=20@AllArgsConstruct?= =?UTF-8?q?or=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ src/main/java/roomescape/dto/Reservation.java | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index e9541e4de..1bd5ad96f 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,8 @@ dependencies { 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' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' } test { diff --git a/src/main/java/roomescape/dto/Reservation.java b/src/main/java/roomescape/dto/Reservation.java index 58e425107..23c7196a2 100644 --- a/src/main/java/roomescape/dto/Reservation.java +++ b/src/main/java/roomescape/dto/Reservation.java @@ -1,20 +1,22 @@ package roomescape.dto; +import lombok.AllArgsConstructor; +import lombok.Getter; + import java.sql.Date; import java.sql.Time; import java.time.LocalDate; import java.time.LocalTime; +@Getter +@AllArgsConstructor //기본 생성자 자동 생성 public class Reservation { + //캡슐화를 위해서는 private으로 해야 하지만, step12의 코드에서 private으로 하면 JSON이 접근을 못 해서 직렬화 못 하는 에러 발생 + //해결을 위해 lombok의 getter 추가 private Long id; private String name; private String date; //우선 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; - } + } From 41ff18eedb8fa43f29c6b43c724f1ce53db4c833 Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Thu, 23 Oct 2025 16:31:24 +0900 Subject: [PATCH 07/11] =?UTF-8?q?[Feat]=203=EB=8B=A8=EA=B3=84=20-=20?= =?UTF-8?q?=EC=98=88=EC=95=BD=20=EC=B6=94=EA=B0=80=20/=20=EC=B7=A8?= =?UTF-8?q?=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 38 ++++++++++++++---- .../roomescape/dto/ReservationAddReq.java | 12 ++++++ .../{Reservation.java => ReservationReq.java} | 7 +--- src/test/java/roomescape/MissionStepTest.java | 40 ++++++++++++++++++- 4 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 src/main/java/roomescape/dto/ReservationAddReq.java rename src/main/java/roomescape/dto/{Reservation.java => ReservationReq.java} (80%) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 976734f36..877acb02d 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -1,22 +1,24 @@ package roomescape.controller; -import org.springframework.http.ResponseEntity; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import roomescape.dto.Reservation; +import org.springframework.web.bind.annotation.*; +import roomescape.dto.ReservationAddReq; +import roomescape.dto.ReservationReq; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; @Controller public class ReservationController { - Reservation reservation1=new Reservation(1L,"브라운","2023-01-01","10:00"); - Reservation reservation2=new Reservation(2L,"브라운","2023-01-02","11:00"); - private List reservations = new ArrayList<>(Arrays.asList(reservation1,reservation2)); + //AtomicLong: Long 자료형 가지는 Wrapping 클래스. incrementAndGet(): ++x + private AtomicLong index = new AtomicLong(0); + private List reservations = new ArrayList<>(); @GetMapping("/reservation") public String reservation(Model model){ @@ -27,8 +29,28 @@ public String reservation(Model model){ @GetMapping("/reservations") @ResponseBody - public List reservations(){ + public List reservations(){ return reservations; } + + @PostMapping("/reservations") + @ResponseBody + @ResponseStatus(HttpStatus.CREATED) + public ReservationReq addReservation(@RequestBody ReservationAddReq reservation, HttpServletResponse response){ + ReservationReq newReservation =new ReservationReq(index.incrementAndGet(),reservation.getName(),reservation.getDate(),reservation.getTime()); + reservations.add(newReservation); + + // ResponseEntity 사용하면 header 명시적으로 지정하지 않아도 된다고 한다. + response.setHeader("Location", "/reservations/" + newReservation.getId()); + return newReservation; + } + + @DeleteMapping("/reservations/{id}") + @ResponseBody + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteReservation(@PathVariable Long id){ + ReservationReq delete=reservations.stream().filter(ReservationReq -> id.equals(ReservationReq.getId())).findAny().orElse(null); + reservations.remove(delete); + } } diff --git a/src/main/java/roomescape/dto/ReservationAddReq.java b/src/main/java/roomescape/dto/ReservationAddReq.java new file mode 100644 index 000000000..a86225fbd --- /dev/null +++ b/src/main/java/roomescape/dto/ReservationAddReq.java @@ -0,0 +1,12 @@ +package roomescape.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class ReservationAddReq { + private String name; + private String date; //우선 String으로.. 차후 Date로 형변환 필요하면 교체.. + private String time; //동일!! +} diff --git a/src/main/java/roomescape/dto/Reservation.java b/src/main/java/roomescape/dto/ReservationReq.java similarity index 80% rename from src/main/java/roomescape/dto/Reservation.java rename to src/main/java/roomescape/dto/ReservationReq.java index 23c7196a2..542565146 100644 --- a/src/main/java/roomescape/dto/Reservation.java +++ b/src/main/java/roomescape/dto/ReservationReq.java @@ -3,14 +3,9 @@ import lombok.AllArgsConstructor; import lombok.Getter; -import java.sql.Date; -import java.sql.Time; -import java.time.LocalDate; -import java.time.LocalTime; - @Getter @AllArgsConstructor //기본 생성자 자동 생성 -public class Reservation { +public class ReservationReq { //캡슐화를 위해서는 private으로 해야 하지만, step12의 코드에서 private으로 하면 JSON이 접근을 못 해서 직렬화 못 하는 에러 발생 //해결을 위해 lombok의 getter 추가 private Long id; diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index a1cc39c6e..784e98f07 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -1,10 +1,14 @@ 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) @@ -30,6 +34,40 @@ public class MissionStepTest { .when().get("/reservations") .then().log().all() .statusCode(200) - .body("size()", is(2)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수(=1) 만큼 검증. + .body("size()", is(0)); // 아직 생성 요청이 없으니 Controller에서 임의로 넣어준 Reservation 갯수(=1) 만큼 검증. + } + + @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 a4a9f6bcb8482269bfb2a232a217ec42a94855bd Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Thu, 23 Oct 2025 17:20:42 +0900 Subject: [PATCH 08/11] =?UTF-8?q?[Feat]=204=EB=8B=A8=EA=B3=84=20-=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 17 ++++++++++++-- .../exception/ExceptionHandlers.java | 21 ++++++++++++++++++ .../InvalidRequestReservationException.java | 7 ++++++ .../NotFoundReservationException.java | 7 ++++++ src/test/java/roomescape/MissionStepTest.java | 22 +++++++++++++++++++ 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/main/java/roomescape/exception/ExceptionHandlers.java create mode 100644 src/main/java/roomescape/exception/InvalidRequestReservationException.java create mode 100644 src/main/java/roomescape/exception/NotFoundReservationException.java diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 877acb02d..2a1bf2b0f 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -2,14 +2,16 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import roomescape.dto.ReservationAddReq; import roomescape.dto.ReservationReq; +import roomescape.exception.InvalidRequestReservationException; +import roomescape.exception.NotFoundReservationException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @@ -19,7 +21,7 @@ public class ReservationController { //AtomicLong: Long 자료형 가지는 Wrapping 클래스. incrementAndGet(): ++x private AtomicLong index = new AtomicLong(0); private List reservations = new ArrayList<>(); - + @GetMapping("/reservation") public String reservation(Model model){ @@ -27,6 +29,7 @@ public String reservation(Model model){ return "reservation"; } + //예약 조회 @GetMapping("/reservations") @ResponseBody public List reservations(){ @@ -34,10 +37,14 @@ public List reservations(){ return reservations; } + //예약 추가 @PostMapping("/reservations") @ResponseBody @ResponseStatus(HttpStatus.CREATED) public ReservationReq addReservation(@RequestBody ReservationAddReq reservation, HttpServletResponse response){ + if(reservation.getName().isEmpty()||reservation.getDate().isEmpty()||reservation.getTime().isEmpty()){ + throw new InvalidRequestReservationException("필요한 인자가 없습니다."); + } ReservationReq newReservation =new ReservationReq(index.incrementAndGet(),reservation.getName(),reservation.getDate(),reservation.getTime()); reservations.add(newReservation); @@ -46,11 +53,17 @@ public ReservationReq addReservation(@RequestBody ReservationAddReq reservation, return newReservation; } + //예약 삭제 @DeleteMapping("/reservations/{id}") @ResponseBody @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteReservation(@PathVariable Long id){ ReservationReq delete=reservations.stream().filter(ReservationReq -> id.equals(ReservationReq.getId())).findAny().orElse(null); + if(delete==null){ + throw new NotFoundReservationException("삭제할 예약이 없습니다"); + } reservations.remove(delete); } + + } diff --git a/src/main/java/roomescape/exception/ExceptionHandlers.java b/src/main/java/roomescape/exception/ExceptionHandlers.java new file mode 100644 index 000000000..c9bed34e1 --- /dev/null +++ b/src/main/java/roomescape/exception/ExceptionHandlers.java @@ -0,0 +1,21 @@ +package roomescape.exception; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + + +@ControllerAdvice +public class ExceptionHandlers { + + @ExceptionHandler(NotFoundReservationException.class) + public ResponseEntity NotFoundReservationException() { //딱히 메시지에 id 등 추가할 필요를 못 느껴서 매개변수 없앰. + return ResponseEntity.badRequest().build(); + } + + @ExceptionHandler(InvalidRequestReservationException.class) + public ResponseEntity InvalidRequestReservationException(){ + return ResponseEntity.badRequest().build(); + } + +} diff --git a/src/main/java/roomescape/exception/InvalidRequestReservationException.java b/src/main/java/roomescape/exception/InvalidRequestReservationException.java new file mode 100644 index 000000000..b34365062 --- /dev/null +++ b/src/main/java/roomescape/exception/InvalidRequestReservationException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class InvalidRequestReservationException extends RuntimeException { + public InvalidRequestReservationException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/exception/NotFoundReservationException.java b/src/main/java/roomescape/exception/NotFoundReservationException.java new file mode 100644 index 000000000..9aa1022b2 --- /dev/null +++ b/src/main/java/roomescape/exception/NotFoundReservationException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class NotFoundReservationException extends RuntimeException { + public NotFoundReservationException(String message) { + super(message); + } +} diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 784e98f07..1c2666c27 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -70,4 +70,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 56b2a8ed544ab76c2238bbab9d5689fa1e5e34df Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Wed, 12 Nov 2025 18:16:33 +0900 Subject: [PATCH 09/11] =?UTF-8?q?[Feat]=205=EB=8B=A8=EA=B3=84=20-=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=B2=A0=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ src/main/resources/application.properties | 3 +++ src/main/resources/schema.sql | 8 ++++++++ src/test/java/roomescape/MissionStepTest.java | 20 +++++++++++++++++++ 4 files changed, 33 insertions(+) create mode 100644 src/main/resources/schema.sql diff --git a/build.gradle b/build.gradle index 1bd5ad96f..458a76b48 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + runtimeOnly 'com.h2database:h2' } test { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29bb..b63619d73 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console +spring.datasource.url=jdbc:h2:mem:database diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 000000000..8d9ab2754 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,8 @@ +CREATE TABLE reservation +( + id BIGINT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + date VARCHAR(255) NOT NULL, + time VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +); diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 1c2666c27..acbc2bc2f 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -3,12 +3,17 @@ 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.jdbc.core.JdbcTemplate; import org.springframework.test.annotation.DirtiesContext; +import java.sql.Connection; +import java.sql.SQLException; import java.util.HashMap; import java.util.Map; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.hamcrest.Matchers.is; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @@ -92,4 +97,19 @@ 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 d364a3c74a0a8ededb5b243f2b41182863a475e7 Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Wed, 12 Nov 2025 18:36:18 +0900 Subject: [PATCH 10/11] =?UTF-8?q?[Feat]=206=EB=8B=A8=EA=B3=84=20-=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 29 +++++++++++++++---- src/test/java/roomescape/MissionStepTest.java | 18 ++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 2a1bf2b0f..7453690bd 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -3,6 +3,7 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @@ -20,12 +21,17 @@ public class ReservationController { //AtomicLong: Long 자료형 가지는 Wrapping 클래스. incrementAndGet(): ++x private AtomicLong index = new AtomicLong(0); - private List reservations = new ArrayList<>(); + private List reservations1 = new ArrayList<>(); + private JdbcTemplate jdbcTemplate; + + public ReservationController(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } @GetMapping("/reservation") public String reservation(Model model){ - model.addAttribute(reservations); + model.addAttribute(reservations1); return "reservation"; } @@ -33,7 +39,18 @@ public String reservation(Model model){ @GetMapping("/reservations") @ResponseBody public List reservations(){ - + String sql="SELECT * from reservation"; + List reservations=jdbcTemplate.query( + sql, + (resultSet,rowNum)->{ + ReservationReq reservation = new ReservationReq( + resultSet.getLong("id"), + resultSet.getString("name"), + resultSet.getString("date"), + resultSet.getString("time") + ); + return reservation; + }); return reservations; } @@ -46,7 +63,7 @@ public ReservationReq addReservation(@RequestBody ReservationAddReq reservation, throw new InvalidRequestReservationException("필요한 인자가 없습니다."); } ReservationReq newReservation =new ReservationReq(index.incrementAndGet(),reservation.getName(),reservation.getDate(),reservation.getTime()); - reservations.add(newReservation); + reservations1.add(newReservation); // ResponseEntity 사용하면 header 명시적으로 지정하지 않아도 된다고 한다. response.setHeader("Location", "/reservations/" + newReservation.getId()); @@ -58,11 +75,11 @@ public ReservationReq addReservation(@RequestBody ReservationAddReq reservation, @ResponseBody @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteReservation(@PathVariable Long id){ - ReservationReq delete=reservations.stream().filter(ReservationReq -> id.equals(ReservationReq.getId())).findAny().orElse(null); + ReservationReq delete=reservations1.stream().filter(ReservationReq -> id.equals(ReservationReq.getId())).findAny().orElse(null); if(delete==null){ throw new NotFoundReservationException("삭제할 예약이 없습니다"); } - reservations.remove(delete); + reservations1.remove(delete); } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index acbc2bc2f..6d83a3672 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.jdbc.core.JdbcTemplate; import org.springframework.test.annotation.DirtiesContext; +import roomescape.dto.ReservationReq; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -112,4 +114,20 @@ public class MissionStepTest { } } + @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(".", ReservationReq.class); + + Integer count = jdbcTemplate.queryForObject("SELECT count(1) from reservation", Integer.class); + + assertThat(reservations.size()).isEqualTo(count); + } + + } From a7cb4191dc82dcd65267557617d2bdb2aaaa3df5 Mon Sep 17 00:00:00 2001 From: jeein <23minjjj@ewha.ac.kr> Date: Thu, 13 Nov 2025 09:50:52 +0900 Subject: [PATCH 11/11] =?UTF-8?q?[Feat]=207=EB=8B=A8=EA=B3=84=20-=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80/=EC=82=AD?= =?UTF-8?q?=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/ReservationController.java | 21 +++++++++------ src/main/resources/application.properties | 2 +- src/test/java/roomescape/MissionStepTest.java | 27 +++++++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 7453690bd..78690a40d 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -62,11 +62,15 @@ public ReservationReq addReservation(@RequestBody ReservationAddReq reservation, if(reservation.getName().isEmpty()||reservation.getDate().isEmpty()||reservation.getTime().isEmpty()){ throw new InvalidRequestReservationException("필요한 인자가 없습니다."); } - ReservationReq newReservation =new ReservationReq(index.incrementAndGet(),reservation.getName(),reservation.getDate(),reservation.getTime()); - reservations1.add(newReservation); + + String sql="INSERT INTO reservation(name, date, time) VALUES (?,?,?)"; + jdbcTemplate.update(sql,reservation.getName(),reservation.getDate(),reservation.getTime()); + String getIdSql="SELECT LAST_INSERT_ID()"; //해당 구문 사용 위해 application.properties에 MODE=MySQL 추가함 + Long id=jdbcTemplate.queryForObject(getIdSql, Long.class); + ReservationReq newReservation =new ReservationReq(id,reservation.getName(),reservation.getDate(),reservation.getTime()); // ResponseEntity 사용하면 header 명시적으로 지정하지 않아도 된다고 한다. - response.setHeader("Location", "/reservations/" + newReservation.getId()); + response.setHeader("Location", "/reservations/" + id); return newReservation; } @@ -75,12 +79,13 @@ public ReservationReq addReservation(@RequestBody ReservationAddReq reservation, @ResponseBody @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteReservation(@PathVariable Long id){ - ReservationReq delete=reservations1.stream().filter(ReservationReq -> id.equals(ReservationReq.getId())).findAny().orElse(null); - if(delete==null){ + + String sql="DELETE FROM reservation WHERE id=?"; + int rowCount=jdbcTemplate.update(sql,id); + if(rowCount==0){ throw new NotFoundReservationException("삭제할 예약이 없습니다"); } - reservations1.remove(delete); - } + } -} +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b63619d73..d7553a520 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,3 @@ spring.h2.console.enabled=true spring.h2.console.path=/h2-console -spring.datasource.url=jdbc:h2:mem:database +spring.datasource.url=jdbc:h2:mem:database;MODE=MySQL diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 6d83a3672..a11fd1925 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -129,5 +129,32 @@ public class MissionStepTest { 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); + } + }