From a26d3bd6b46dc5cbb3622cf4cfdbfbed9e5727c2 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 6 Nov 2025 17:49:34 +0900 Subject: [PATCH 01/38] =?UTF-8?q?gradle:=20gradle=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle b/build.gradle index 57267157c..cf8c895d6 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,11 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + + developmentOnly 'org.springframework.boot:spring-boot-devtools' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' } From 33475a6b03a7da814aed1912f2c623aac1715336 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 6 Nov 2025 17:49:58 +0900 Subject: [PATCH 02/38] =?UTF-8?q?feat:=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/HomeController.java | 12 ++++++++++++ 1 file changed, 12 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..bdb1dba7c --- /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"; + } +} \ No newline at end of file From 2759859df18966abbae882156e34575d274295ce Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 6 Nov 2025 17:59:27 +0900 Subject: [PATCH 03/38] =?UTF-8?q?feat:=20=EC=98=88=EC=95=BD=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20api=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/Reservation.java | 36 +++++++++++++++++++ .../roomescape/ReservationController.java | 33 +++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/main/java/roomescape/Reservation.java create mode 100644 src/main/java/roomescape/ReservationController.java diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java new file mode 100644 index 000000000..68e89a327 --- /dev/null +++ b/src/main/java/roomescape/Reservation.java @@ -0,0 +1,36 @@ +package roomescape; + +public class Reservation { + private int id; + private String name; + private String date; + private String time; + + public Reservation() { + } + + public Reservation(int id, String name, String date, String time) { + this.id = id; + this.name = name; + this.date = date; + this.time = time; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDate() { + return date; + } + + public String getTime() { + return time; + } +} + + diff --git a/src/main/java/roomescape/ReservationController.java b/src/main/java/roomescape/ReservationController.java new file mode 100644 index 000000000..7a0723463 --- /dev/null +++ b/src/main/java/roomescape/ReservationController.java @@ -0,0 +1,33 @@ +package roomescape; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.ArrayList; +import java.util.List; + +@Controller +public class ReservationController { + + private final List reservations = new ArrayList<>(); + + public ReservationController() { + reservations.add(new Reservation(1, "브라운", "2023-01-01", "10:00")); + reservations.add(new Reservation(2, "브라운", "2023-01-02", "11:00")); + reservations.add(new Reservation(3, "브라운", "2023-01-03", "12:00")); + } + + @GetMapping("/reservation") + public String reservationPage() { + return "reservation"; + } + + @GetMapping("/reservations") + @ResponseBody + public List findAll() { + return reservations; + } +} + + From c32009e4fba18ce50b987710a2dcca6827581a54 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 6 Nov 2025 19:29:01 +0900 Subject: [PATCH 04/38] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/Reservation.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java index 68e89a327..d67650dd9 100644 --- a/src/main/java/roomescape/Reservation.java +++ b/src/main/java/roomescape/Reservation.java @@ -6,9 +6,6 @@ public class Reservation { private String date; private String time; - public Reservation() { - } - public Reservation(int id, String name, String date, String time) { this.id = id; this.name = name; @@ -16,21 +13,7 @@ public Reservation(int id, String name, String date, String time) { this.time = time; } - public int getId() { - return id; - } - - public String getName() { - return name; - } - - public String getDate() { - return date; - } - public String getTime() { - return time; - } } From 7489cf53151f982f3fe6ced86882039a17b78ed9 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:53:14 +0900 Subject: [PATCH 05/38] =?UTF-8?q?refactor:=20Reservation=20dto=EB=A5=BC=20?= =?UTF-8?q?record=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/roomescape/MissionStepTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index cf4efbe91..4c62760e3 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; 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) @@ -16,4 +17,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)); + } } From 7543356a3cb1c17e5e81cb8c03c0998aae8c25d4 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:53:17 +0900 Subject: [PATCH 06/38] =?UTF-8?q?refactor:=20Reservation=20dto=EB=A5=BC=20?= =?UTF-8?q?record=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/Reservation.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java index d67650dd9..12143abfb 100644 --- a/src/main/java/roomescape/Reservation.java +++ b/src/main/java/roomescape/Reservation.java @@ -1,19 +1,3 @@ package roomescape; -public class Reservation { - private int id; - private String name; - private String date; - private String time; - - public Reservation(int id, String name, String date, String time) { - this.id = id; - this.name = name; - this.date = date; - this.time = time; - } - - -} - - +public record Reservation(int id, String name, String date, String time) { } \ No newline at end of file From 565d8f29186b78051dfe579750f65871f53e11ab Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sun, 9 Nov 2025 22:06:49 +0900 Subject: [PATCH 07/38] =?UTF-8?q?refactor:=20eol=20=EC=A4=80=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/HomeController.java | 2 +- src/main/java/roomescape/Reservation.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/roomescape/HomeController.java b/src/main/java/roomescape/HomeController.java index bdb1dba7c..c16dd77fa 100644 --- a/src/main/java/roomescape/HomeController.java +++ b/src/main/java/roomescape/HomeController.java @@ -9,4 +9,4 @@ public class HomeController { public String home() { return "home"; } -} \ No newline at end of file +} diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java index 12143abfb..303d8f4f7 100644 --- a/src/main/java/roomescape/Reservation.java +++ b/src/main/java/roomescape/Reservation.java @@ -1,3 +1,4 @@ package roomescape; -public record Reservation(int id, String name, String date, String time) { } \ No newline at end of file +public record Reservation(int id, String name, String date, String time) { +} \ No newline at end of file From be87cab37bd2836fb2c7c36561ae601ce4035088 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 12 Nov 2025 16:31:44 +0900 Subject: [PATCH 08/38] =?UTF-8?q?refactor:=20eol=20=EC=A4=80=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/Reservation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java index 303d8f4f7..73607a6b3 100644 --- a/src/main/java/roomescape/Reservation.java +++ b/src/main/java/roomescape/Reservation.java @@ -1,4 +1,4 @@ package roomescape; public record Reservation(int id, String name, String date, String time) { -} \ No newline at end of file +} From e1fcf2bf0c62f1fd3051084a4a52d1231f7a28a7 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 10:35:36 +0900 Subject: [PATCH 09/38] =?UTF-8?q?chore:=20=EA=B9=83=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 74 +-- LICENSE | 42 +- build.gradle | 56 +- gradle/wrapper/gradle-wrapper.properties | 10 +- gradlew | 490 +++++++++--------- gradlew.bat | 184 +++---- src/main/java/roomescape/HomeController.java | 24 +- src/main/java/roomescape/Reservation.java | 8 +- .../roomescape/ReservationController.java | 66 +-- src/main/resources/static/css/reservation.css | 32 +- src/main/resources/static/css/styles.css | 176 +++---- src/main/resources/static/css/time.css | 24 +- .../resources/static/js/new-reservation.js | 424 +++++++-------- src/main/resources/static/js/reservation.js | 320 ++++++------ src/main/resources/static/js/time.js | 312 +++++------ src/main/resources/templates/home.html | 94 ++-- .../resources/templates/new-reservation.html | 136 ++--- src/main/resources/templates/reservation.html | 136 ++--- src/main/resources/templates/time.html | 130 ++--- src/test/java/roomescape/MissionStepTest.java | 68 +-- 20 files changed, 1403 insertions(+), 1403 deletions(-) diff --git a/.gitignore b/.gitignore index c2065bc26..132b46915 100644 --- a/.gitignore +++ b/.gitignore @@ -1,37 +1,37 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/LICENSE b/LICENSE index 7754876dd..549255451 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2023 next-step - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2023 next-step + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/build.gradle b/build.gradle index cf8c895d6..1be9887e7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,28 +1,28 @@ -plugins { - id 'org.springframework.boot' version '3.1.0' - id 'io.spring.dependency-management' version '1.1.0' - id 'java' -} - -group = 'nextstep' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = '17' - -repositories { - mavenCentral() -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - - developmentOnly 'org.springframework.boot:spring-boot-devtools' - - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'io.rest-assured:rest-assured:5.3.1' -} - -test { - useJUnitPlatform() -} +plugins { + id 'org.springframework.boot' version '3.1.0' + id 'io.spring.dependency-management' version '1.1.0' + id 'java' +} + +group = 'nextstep' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '17' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + + developmentOnly 'org.springframework.boot:spring-boot-devtools' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.rest-assured:rest-assured:5.3.1' +} + +test { + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fcea..5ec4b8ed0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cbb4..6a7df8703 100755 --- a/gradlew +++ b/gradlew @@ -1,245 +1,245 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f1..6689b85be 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,92 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/roomescape/HomeController.java b/src/main/java/roomescape/HomeController.java index c16dd77fa..6c5a65293 100644 --- a/src/main/java/roomescape/HomeController.java +++ b/src/main/java/roomescape/HomeController.java @@ -1,12 +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"; - } -} +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/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java index 73607a6b3..bbc44073e 100644 --- a/src/main/java/roomescape/Reservation.java +++ b/src/main/java/roomescape/Reservation.java @@ -1,4 +1,4 @@ -package roomescape; - -public record Reservation(int id, String name, String date, String time) { -} +package roomescape; + +public record Reservation(int id, String name, String date, String time) { +} diff --git a/src/main/java/roomescape/ReservationController.java b/src/main/java/roomescape/ReservationController.java index 7a0723463..6586ba510 100644 --- a/src/main/java/roomescape/ReservationController.java +++ b/src/main/java/roomescape/ReservationController.java @@ -1,33 +1,33 @@ -package roomescape; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -import java.util.ArrayList; -import java.util.List; - -@Controller -public class ReservationController { - - private final List reservations = new ArrayList<>(); - - public ReservationController() { - reservations.add(new Reservation(1, "브라운", "2023-01-01", "10:00")); - reservations.add(new Reservation(2, "브라운", "2023-01-02", "11:00")); - reservations.add(new Reservation(3, "브라운", "2023-01-03", "12:00")); - } - - @GetMapping("/reservation") - public String reservationPage() { - return "reservation"; - } - - @GetMapping("/reservations") - @ResponseBody - public List findAll() { - return reservations; - } -} - - +package roomescape; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.ArrayList; +import java.util.List; + +@Controller +public class ReservationController { + + private final List reservations = new ArrayList<>(); + + public ReservationController() { + reservations.add(new Reservation(1, "브라운", "2023-01-01", "10:00")); + reservations.add(new Reservation(2, "브라운", "2023-01-02", "11:00")); + reservations.add(new Reservation(3, "브라운", "2023-01-03", "12:00")); + } + + @GetMapping("/reservation") + public String reservationPage() { + return "reservation"; + } + + @GetMapping("/reservations") + @ResponseBody + public List findAll() { + return reservations; + } +} + + diff --git a/src/main/resources/static/css/reservation.css b/src/main/resources/static/css/reservation.css index 925a18a9d..8861350c9 100644 --- a/src/main/resources/static/css/reservation.css +++ b/src/main/resources/static/css/reservation.css @@ -1,16 +1,16 @@ -.table-container table th:nth-child(1), -.table-container table td:nth-child(1), -.table-container table th:nth-child(2), -.table-container table td:nth-child(2), -.table-container table th:nth-child(3), -.table-container table td:nth-child(3), -.table-container table th:nth-child(4), -.table-container table td:nth-child(4), -.table-container table th:nth-child(5), -.table-container table td:nth-child(5) { - width: 20%; -} - -.table-container table td:nth-child(5) { - text-align: center; -} +.table-container table th:nth-child(1), +.table-container table td:nth-child(1), +.table-container table th:nth-child(2), +.table-container table td:nth-child(2), +.table-container table th:nth-child(3), +.table-container table td:nth-child(3), +.table-container table th:nth-child(4), +.table-container table td:nth-child(4), +.table-container table th:nth-child(5), +.table-container table td:nth-child(5) { + width: 20%; +} + +.table-container table td:nth-child(5) { + text-align: center; +} diff --git a/src/main/resources/static/css/styles.css b/src/main/resources/static/css/styles.css index cdcbac921..1525445e2 100644 --- a/src/main/resources/static/css/styles.css +++ b/src/main/resources/static/css/styles.css @@ -1,88 +1,88 @@ -body { - font-family: 'Noto Sans KR', sans-serif; -} - -/* 기본 레이아웃 */ -#sidebar-container { - height: 100vh; - background-color: #0a3711; -} - -/* 로고 관련 */ -.sidebar-logo { - text-align: center; - padding: 20px 0; -} - -.sidebar-logo img { - max-width: 80%; - display: block; - margin: 0 auto; -} - -/* 메뉴 및 아이콘 관련 */ -.menu-icon { - margin-right: 10px; -} - -#sidebar .nav-link { - border-radius: 0; - color: #333; -} - -#sidebar .nav-link.active { - background-color: #f8f9fa; - color: #007bff; -} - -#sidebar .nav-link:hover { - background-color: #f8f9fa; -} - -/* 버튼 관련 */ -.btn-primary { - background-color: #0a3711 !important; - border-color: #0a3711 !important; -} - -.btn-primary:hover, .btn-primary:focus, .btn-primary:active { - background-color: #08290f !important; - border-color: #08290f !important; -} - -#content { - padding: 20px; -} - -.page { - min-height: 100vh; - background-color: #f5f5f5; -} - -.table-header { - display: flex; - justify-content: right; - align-items: center; - margin-bottom: 20px; -} - -/* 테이블 관련 */ -.table-container { - background-color: #fff; - border: 1px solid #e0e0e0; - border-radius: 5px; - padding: 20px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); -} - -.table { - margin-bottom: 0; - width: 100%; - table-layout: fixed; -} - -@media (max-width: 767px) { /* Bootstrap의 md breakpoint 이전 (일반적으로 767px 이하) */ - #sidebar-container { - height: 100%; - } -} +body { + font-family: 'Noto Sans KR', sans-serif; +} + +/* 기본 레이아웃 */ +#sidebar-container { + height: 100vh; + background-color: #0a3711; +} + +/* 로고 관련 */ +.sidebar-logo { + text-align: center; + padding: 20px 0; +} + +.sidebar-logo img { + max-width: 80%; + display: block; + margin: 0 auto; +} + +/* 메뉴 및 아이콘 관련 */ +.menu-icon { + margin-right: 10px; +} + +#sidebar .nav-link { + border-radius: 0; + color: #333; +} + +#sidebar .nav-link.active { + background-color: #f8f9fa; + color: #007bff; +} + +#sidebar .nav-link:hover { + background-color: #f8f9fa; +} + +/* 버튼 관련 */ +.btn-primary { + background-color: #0a3711 !important; + border-color: #0a3711 !important; +} + +.btn-primary:hover, .btn-primary:focus, .btn-primary:active { + background-color: #08290f !important; + border-color: #08290f !important; +} + +#content { + padding: 20px; +} + +.page { + min-height: 100vh; + background-color: #f5f5f5; +} + +.table-header { + display: flex; + justify-content: right; + align-items: center; + margin-bottom: 20px; +} + +/* 테이블 관련 */ +.table-container { + background-color: #fff; + border: 1px solid #e0e0e0; + border-radius: 5px; + padding: 20px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +.table { + margin-bottom: 0; + width: 100%; + table-layout: fixed; +} + +@media (max-width: 767px) { /* Bootstrap의 md breakpoint 이전 (일반적으로 767px 이하) */ + #sidebar-container { + height: 100%; + } +} diff --git a/src/main/resources/static/css/time.css b/src/main/resources/static/css/time.css index 0fd92e385..d107922f0 100644 --- a/src/main/resources/static/css/time.css +++ b/src/main/resources/static/css/time.css @@ -1,12 +1,12 @@ -.table-container table th:nth-child(1), -.table-container table td:nth-child(1), -.table-container table th:nth-child(2), -.table-container table td:nth-child(2), -.table-container table th:nth-child(3), -.table-container table td:nth-child(3) { - width: 20%; -} - -.table-container table td:nth-child(3) { - text-align: center; -} +.table-container table th:nth-child(1), +.table-container table td:nth-child(1), +.table-container table th:nth-child(2), +.table-container table td:nth-child(2), +.table-container table th:nth-child(3), +.table-container table td:nth-child(3) { + width: 20%; +} + +.table-container table td:nth-child(3) { + text-align: center; +} diff --git a/src/main/resources/static/js/new-reservation.js b/src/main/resources/static/js/new-reservation.js index 520d0cf42..1eb91f6d3 100644 --- a/src/main/resources/static/js/new-reservation.js +++ b/src/main/resources/static/js/new-reservation.js @@ -1,212 +1,212 @@ -let isEditing = false; -const RESERVATION_API_ENDPOINT = '/reservations'; -const TIME_API_ENDPOINT = '/times'; - -document.addEventListener('DOMContentLoaded', () => { - document.getElementById('add-reservation').addEventListener('click', addEditableRow); - fetchReservations(); - fetchTimes(); -}); - -function fetchTimes() { - requestReadTimes() - .then(data => { - const timeSelectControl = createFormControl(data); - appendFormControlToDocument(timeSelectControl); - }) - .catch(error => console.error('Error fetching time:', error)); -} - -function createFormControl(timeData) { - const select = document.createElement('select'); - select.className = 'form-control'; - select.id = 'time-select'; - - const defaultOption = document.createElement('option'); - defaultOption.textContent = "시간 선택"; - select.appendChild(defaultOption); - - timeData.forEach(time => { - const option = document.createElement('option'); - option.value = time.id; - option.textContent = time.time; - select.appendChild(option); - }); - - return select; -} - -function appendFormControlToDocument(control) { - document.body.appendChild(control); -} - -function fetchReservations() { - requestRead() - .then(renderReservations) - .catch(error => console.error('Error fetching reservations:', error)); -} - -function renderReservations(data) { - const tableBody = document.getElementById('reservation-table-body'); - tableBody.innerHTML = ''; - - data.forEach(reservation => { - const row = tableBody.insertRow(); - insertReservationRow(row, reservation); - }); -} - -function insertReservationRow(row, reservation) { - ['id', 'name', 'date'].forEach((field, index) => { - row.insertCell(index).textContent = reservation[field]; - }); - - row.insertCell(3).textContent = reservation.time.time; - - const actionCell = row.insertCell(4); - actionCell.appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); -} - -function createActionButton(label, className, eventListener) { - const button = document.createElement('button'); - button.textContent = label; - button.classList.add('btn', className, 'mr-2'); - button.addEventListener('click', eventListener); - return button; -} - -function addEditableRow() { - - if (isEditing) return; // 이미 편집 중인 경우 추가하지 않음 - - const tableBody = document.getElementById('reservation-table-body'); - const row = tableBody.insertRow(); - isEditing = true; - - createEditableFieldsFor(row); - addSaveAndCancelButtonsToRow(row); -} - -function createEditableFieldsFor(row) { - const nameInput = createInput('text'); - const dateInput = createInput('date'); - const timeDropdown = document.getElementById('time-select').cloneNode(true); - - const fields = ['', nameInput, dateInput, timeDropdown]; - - fields.forEach((field, index) => { - const cell = row.insertCell(index); - if (typeof field === 'string') { - cell.textContent = field; - } else { - cell.appendChild(field); - } - }); -} - -function addSaveAndCancelButtonsToRow(row) { - const actionCell = row.insertCell(4); - actionCell.appendChild(createActionButton('확인', 'btn-primary', saveRow)); - actionCell.appendChild(createActionButton('취소', 'btn-secondary', () => { - row.remove(); - isEditing = false; - })); -} - -function createInput(type) { - const input = document.createElement('input'); - input.type = type; - input.className = 'form-control'; - return input; -} - -function saveRow(event) { - const row = event.target.parentNode.parentNode; - const nameInput = row.querySelector('input[type="text"]'); - const dateInput = row.querySelector('input[type="date"]'); - const timeSelect = row.querySelector('select'); - - const reservation = { - name: nameInput.value, - date: dateInput.value, - time: timeSelect.value - }; - - requestCreate(reservation) - .then(data => updateRowWithReservationData(row, data)) - .catch(error => console.error('Error:', error)); - - isEditing = false; // isEditing 값을 false로 설정 -} - -function updateRowWithReservationData(row, data) { - const cells = row.cells; - cells[0].textContent = data.id; - cells[1].textContent = data.name; - cells[2].textContent = data.date; - cells[3].textContent = data.time.time; - - // 버튼 변경: 삭제 버튼으로 변경 - cells[4].innerHTML = ''; - cells[4].appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); - - isEditing = false; - - // Remove the editable input fields and just show the saved data - for (let i = 1; i <= 3; i++) { - const inputElement = cells[i].querySelector('input'); - if (inputElement) { - inputElement.remove(); - } - } -} - -function deleteRow(event) { - const row = event.target.closest('tr'); - const reservationId = row.cells[0].textContent; - - requestDelete(reservationId) - .then(() => row.remove()) - .catch(error => console.error('Error:', error)); -} - -function requestCreate(reservation) { - const requestOptions = { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(reservation) - }; - - return fetch(RESERVATION_API_ENDPOINT, requestOptions) - .then(response => { - if (response.status === 201) return response.json(); - throw new Error('Create failed'); - }); -} - -function requestRead() { - return fetch(RESERVATION_API_ENDPOINT) - .then(response => { - if (response.status === 200) return response.json(); - throw new Error('Read failed'); - }); -} - -function requestDelete(id) { - const requestOptions = { - method: 'DELETE', - }; - - return fetch(`${RESERVATION_API_ENDPOINT}/${id}`, requestOptions) - .then(response => { - if (response.status !== 204) throw new Error('Delete failed'); - }); -} - -function requestReadTimes() { - return fetch(TIME_API_ENDPOINT) - .then(response => { - if (response.status === 200) return response.json(); - throw new Error('Read failed'); - }); -} +let isEditing = false; +const RESERVATION_API_ENDPOINT = '/reservations'; +const TIME_API_ENDPOINT = '/times'; + +document.addEventListener('DOMContentLoaded', () => { + document.getElementById('add-reservation').addEventListener('click', addEditableRow); + fetchReservations(); + fetchTimes(); +}); + +function fetchTimes() { + requestReadTimes() + .then(data => { + const timeSelectControl = createFormControl(data); + appendFormControlToDocument(timeSelectControl); + }) + .catch(error => console.error('Error fetching time:', error)); +} + +function createFormControl(timeData) { + const select = document.createElement('select'); + select.className = 'form-control'; + select.id = 'time-select'; + + const defaultOption = document.createElement('option'); + defaultOption.textContent = "시간 선택"; + select.appendChild(defaultOption); + + timeData.forEach(time => { + const option = document.createElement('option'); + option.value = time.id; + option.textContent = time.time; + select.appendChild(option); + }); + + return select; +} + +function appendFormControlToDocument(control) { + document.body.appendChild(control); +} + +function fetchReservations() { + requestRead() + .then(renderReservations) + .catch(error => console.error('Error fetching reservations:', error)); +} + +function renderReservations(data) { + const tableBody = document.getElementById('reservation-table-body'); + tableBody.innerHTML = ''; + + data.forEach(reservation => { + const row = tableBody.insertRow(); + insertReservationRow(row, reservation); + }); +} + +function insertReservationRow(row, reservation) { + ['id', 'name', 'date'].forEach((field, index) => { + row.insertCell(index).textContent = reservation[field]; + }); + + row.insertCell(3).textContent = reservation.time.time; + + const actionCell = row.insertCell(4); + actionCell.appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); +} + +function createActionButton(label, className, eventListener) { + const button = document.createElement('button'); + button.textContent = label; + button.classList.add('btn', className, 'mr-2'); + button.addEventListener('click', eventListener); + return button; +} + +function addEditableRow() { + + if (isEditing) return; // 이미 편집 중인 경우 추가하지 않음 + + const tableBody = document.getElementById('reservation-table-body'); + const row = tableBody.insertRow(); + isEditing = true; + + createEditableFieldsFor(row); + addSaveAndCancelButtonsToRow(row); +} + +function createEditableFieldsFor(row) { + const nameInput = createInput('text'); + const dateInput = createInput('date'); + const timeDropdown = document.getElementById('time-select').cloneNode(true); + + const fields = ['', nameInput, dateInput, timeDropdown]; + + fields.forEach((field, index) => { + const cell = row.insertCell(index); + if (typeof field === 'string') { + cell.textContent = field; + } else { + cell.appendChild(field); + } + }); +} + +function addSaveAndCancelButtonsToRow(row) { + const actionCell = row.insertCell(4); + actionCell.appendChild(createActionButton('확인', 'btn-primary', saveRow)); + actionCell.appendChild(createActionButton('취소', 'btn-secondary', () => { + row.remove(); + isEditing = false; + })); +} + +function createInput(type) { + const input = document.createElement('input'); + input.type = type; + input.className = 'form-control'; + return input; +} + +function saveRow(event) { + const row = event.target.parentNode.parentNode; + const nameInput = row.querySelector('input[type="text"]'); + const dateInput = row.querySelector('input[type="date"]'); + const timeSelect = row.querySelector('select'); + + const reservation = { + name: nameInput.value, + date: dateInput.value, + time: timeSelect.value + }; + + requestCreate(reservation) + .then(data => updateRowWithReservationData(row, data)) + .catch(error => console.error('Error:', error)); + + isEditing = false; // isEditing 값을 false로 설정 +} + +function updateRowWithReservationData(row, data) { + const cells = row.cells; + cells[0].textContent = data.id; + cells[1].textContent = data.name; + cells[2].textContent = data.date; + cells[3].textContent = data.time.time; + + // 버튼 변경: 삭제 버튼으로 변경 + cells[4].innerHTML = ''; + cells[4].appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); + + isEditing = false; + + // Remove the editable input fields and just show the saved data + for (let i = 1; i <= 3; i++) { + const inputElement = cells[i].querySelector('input'); + if (inputElement) { + inputElement.remove(); + } + } +} + +function deleteRow(event) { + const row = event.target.closest('tr'); + const reservationId = row.cells[0].textContent; + + requestDelete(reservationId) + .then(() => row.remove()) + .catch(error => console.error('Error:', error)); +} + +function requestCreate(reservation) { + const requestOptions = { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(reservation) + }; + + return fetch(RESERVATION_API_ENDPOINT, requestOptions) + .then(response => { + if (response.status === 201) return response.json(); + throw new Error('Create failed'); + }); +} + +function requestRead() { + return fetch(RESERVATION_API_ENDPOINT) + .then(response => { + if (response.status === 200) return response.json(); + throw new Error('Read failed'); + }); +} + +function requestDelete(id) { + const requestOptions = { + method: 'DELETE', + }; + + return fetch(`${RESERVATION_API_ENDPOINT}/${id}`, requestOptions) + .then(response => { + if (response.status !== 204) throw new Error('Delete failed'); + }); +} + +function requestReadTimes() { + return fetch(TIME_API_ENDPOINT) + .then(response => { + if (response.status === 200) return response.json(); + throw new Error('Read failed'); + }); +} diff --git a/src/main/resources/static/js/reservation.js b/src/main/resources/static/js/reservation.js index f8e6bf2b0..3e67853f7 100644 --- a/src/main/resources/static/js/reservation.js +++ b/src/main/resources/static/js/reservation.js @@ -1,161 +1,161 @@ -let isEditing = false; -const RESERVATION_API_ENDPOINT = '/reservations'; - -document.addEventListener('DOMContentLoaded', () => { - document.getElementById('add-reservation').addEventListener('click', addEditableRow); - fetchReservations(); -}); - -function fetchReservations() { - requestRead() - .then(renderReservations) - .catch(error => console.error('Error fetching reservations:', error)); -} - -function renderReservations(data) { - const tableBody = document.getElementById('reservation-table-body'); - tableBody.innerHTML = ''; - - data.forEach(reservation => { - const row = tableBody.insertRow(); - insertReservationRow(row, reservation); - }); -} - -function insertReservationRow(row, reservation) { - ['id', 'name', 'date', 'time'].forEach((field, index) => { - row.insertCell(index).textContent = reservation[field]; - }); - - const actionCell = row.insertCell(4); - actionCell.appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); -} - -function createActionButton(label, className, eventListener) { - const button = document.createElement('button'); - button.textContent = label; - button.classList.add('btn', className, 'mr-2'); - button.addEventListener('click', eventListener); - return button; -} - -function addEditableRow() { - - if (isEditing) return; // 이미 편집 중인 경우 추가하지 않음 - - const tableBody = document.getElementById('reservation-table-body'); - const row = tableBody.insertRow(); - isEditing = true; - - createEditableFieldsFor(row); - addSaveAndCancelButtonsToRow(row); -} - -function createEditableFieldsFor(row) { - const fields = ['', createInput('text'), createInput('date'), createInput('time')]; - fields.forEach((field, index) => { - const cell = row.insertCell(index); - if (typeof field === 'string') { - cell.textContent = field; - } else { - cell.appendChild(field); - } - }); -} - -function addSaveAndCancelButtonsToRow(row) { - const actionCell = row.insertCell(4); - actionCell.appendChild(createActionButton('확인', 'btn-primary', saveRow)); - actionCell.appendChild(createActionButton('취소', 'btn-secondary', () => { - row.remove(); - isEditing = false; - })); -} - -function createInput(type) { - const input = document.createElement('input'); - input.type = type; - input.className = 'form-control'; - return input; -} - -function saveRow(event) { - const row = event.target.parentNode.parentNode; - const inputs = row.querySelectorAll('input'); - - const reservation = { - name: inputs[0].value, - date: inputs[1].value, - time: inputs[2].value - }; - - requestCreate(reservation) - .then(data => updateRowWithReservationData(row, data)) - .catch(error => console.error('Error:', error)); - - isEditing = false; // isEditing 값을 false로 설정 -} - -function updateRowWithReservationData(row, data) { - const cells = row.cells; - cells[0].textContent = data.id; - cells[1].textContent = data.name; - cells[2].textContent = data.date; - cells[3].textContent = data.time; - - // 버튼 변경: 삭제 버튼으로 변경 - cells[4].innerHTML = ''; - cells[4].appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); - - isEditing = false; - - // Remove the editable input fields and just show the saved data - for (let i = 1; i <= 3; i++) { - const inputElement = cells[i].querySelector('input'); - if (inputElement) { - inputElement.remove(); - } - } -} - -function deleteRow(event) { - const row = event.target.closest('tr'); - const reservationId = row.cells[0].textContent; - - requestDelete(reservationId) - .then(() => row.remove()) - .catch(error => console.error('Error:', error)); -} - -function requestCreate(reservation) { - const requestOptions = { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(reservation) - }; - - return fetch(RESERVATION_API_ENDPOINT, requestOptions) - .then(response => { - if (response.status === 201) return response.json(); - throw new Error('Create failed'); - }); -} - -function requestRead() { - return fetch(RESERVATION_API_ENDPOINT) - .then(response => { - if (response.status === 200) return response.json(); - throw new Error('Read failed'); - }); -} - -function requestDelete(id) { - const requestOptions = { - method: 'DELETE', - }; - - return fetch(`${RESERVATION_API_ENDPOINT}/${id}`, requestOptions) - .then(response => { - if (response.status !== 204) throw new Error('Delete failed'); - }); +let isEditing = false; +const RESERVATION_API_ENDPOINT = '/reservations'; + +document.addEventListener('DOMContentLoaded', () => { + document.getElementById('add-reservation').addEventListener('click', addEditableRow); + fetchReservations(); +}); + +function fetchReservations() { + requestRead() + .then(renderReservations) + .catch(error => console.error('Error fetching reservations:', error)); +} + +function renderReservations(data) { + const tableBody = document.getElementById('reservation-table-body'); + tableBody.innerHTML = ''; + + data.forEach(reservation => { + const row = tableBody.insertRow(); + insertReservationRow(row, reservation); + }); +} + +function insertReservationRow(row, reservation) { + ['id', 'name', 'date', 'time'].forEach((field, index) => { + row.insertCell(index).textContent = reservation[field]; + }); + + const actionCell = row.insertCell(4); + actionCell.appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); +} + +function createActionButton(label, className, eventListener) { + const button = document.createElement('button'); + button.textContent = label; + button.classList.add('btn', className, 'mr-2'); + button.addEventListener('click', eventListener); + return button; +} + +function addEditableRow() { + + if (isEditing) return; // 이미 편집 중인 경우 추가하지 않음 + + const tableBody = document.getElementById('reservation-table-body'); + const row = tableBody.insertRow(); + isEditing = true; + + createEditableFieldsFor(row); + addSaveAndCancelButtonsToRow(row); +} + +function createEditableFieldsFor(row) { + const fields = ['', createInput('text'), createInput('date'), createInput('time')]; + fields.forEach((field, index) => { + const cell = row.insertCell(index); + if (typeof field === 'string') { + cell.textContent = field; + } else { + cell.appendChild(field); + } + }); +} + +function addSaveAndCancelButtonsToRow(row) { + const actionCell = row.insertCell(4); + actionCell.appendChild(createActionButton('확인', 'btn-primary', saveRow)); + actionCell.appendChild(createActionButton('취소', 'btn-secondary', () => { + row.remove(); + isEditing = false; + })); +} + +function createInput(type) { + const input = document.createElement('input'); + input.type = type; + input.className = 'form-control'; + return input; +} + +function saveRow(event) { + const row = event.target.parentNode.parentNode; + const inputs = row.querySelectorAll('input'); + + const reservation = { + name: inputs[0].value, + date: inputs[1].value, + time: inputs[2].value + }; + + requestCreate(reservation) + .then(data => updateRowWithReservationData(row, data)) + .catch(error => console.error('Error:', error)); + + isEditing = false; // isEditing 값을 false로 설정 +} + +function updateRowWithReservationData(row, data) { + const cells = row.cells; + cells[0].textContent = data.id; + cells[1].textContent = data.name; + cells[2].textContent = data.date; + cells[3].textContent = data.time; + + // 버튼 변경: 삭제 버튼으로 변경 + cells[4].innerHTML = ''; + cells[4].appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); + + isEditing = false; + + // Remove the editable input fields and just show the saved data + for (let i = 1; i <= 3; i++) { + const inputElement = cells[i].querySelector('input'); + if (inputElement) { + inputElement.remove(); + } + } +} + +function deleteRow(event) { + const row = event.target.closest('tr'); + const reservationId = row.cells[0].textContent; + + requestDelete(reservationId) + .then(() => row.remove()) + .catch(error => console.error('Error:', error)); +} + +function requestCreate(reservation) { + const requestOptions = { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(reservation) + }; + + return fetch(RESERVATION_API_ENDPOINT, requestOptions) + .then(response => { + if (response.status === 201) return response.json(); + throw new Error('Create failed'); + }); +} + +function requestRead() { + return fetch(RESERVATION_API_ENDPOINT) + .then(response => { + if (response.status === 200) return response.json(); + throw new Error('Read failed'); + }); +} + +function requestDelete(id) { + const requestOptions = { + method: 'DELETE', + }; + + return fetch(`${RESERVATION_API_ENDPOINT}/${id}`, requestOptions) + .then(response => { + if (response.status !== 204) throw new Error('Delete failed'); + }); } \ No newline at end of file diff --git a/src/main/resources/static/js/time.js b/src/main/resources/static/js/time.js index e794464ec..888df8eec 100644 --- a/src/main/resources/static/js/time.js +++ b/src/main/resources/static/js/time.js @@ -1,157 +1,157 @@ -let isEditing = false; -const TIME_API_ENDPOINT = '/times'; - -document.addEventListener('DOMContentLoaded', () => { - document.getElementById('add-time').addEventListener('click', addEditableRow); - fetchTimes(); -}); - -function addEditableRow() { - - if (isEditing) return; // 이미 편집 중인 경우 추가하지 않음 - - const tableBody = document.getElementById('time-table-body'); - const row = tableBody.insertRow(); - isEditing = true; - - createEditableFieldsFor(row); - addSaveAndCancelButtonsToRow(row); -} - -function createEditableFieldsFor(row) { - const fields = ['', createInput('time')]; - fields.forEach((field, index) => { - const cell = row.insertCell(index); - if (typeof field === 'string') { - cell.textContent = field; - } else { - cell.appendChild(field); - } - }); -} - -function createInput(type) { - const input = document.createElement('input'); - input.type = type; - input.className = 'form-control'; - return input; -} - -function addSaveAndCancelButtonsToRow(row) { - const actionCell = row.insertCell(2); - actionCell.appendChild(createActionButton('확인', 'btn-primary', saveRow)); - actionCell.appendChild(createActionButton('취소', 'btn-secondary', () => { - row.remove(); - isEditing = false; - })); -} - -function createActionButton(label, className, eventListener) { - const button = document.createElement('button'); - button.textContent = label; - button.classList.add('btn', className, 'mr-2'); - button.addEventListener('click', eventListener); - return button; -} - -function saveRow(event) { - const row = event.target.parentNode.parentNode; - const inputs = row.querySelectorAll('input'); - - const time = { - time: inputs[0].value, - }; - - requestCreate(time) - .then(data => updateRowWithTimeData(row, data)) - .catch(error => console.error('Error:', error)); - - isEditing = false; // isEditing 값을 false로 설정 -} - -function updateRowWithTimeData(row, data) { - const cells = row.cells; - cells[0].textContent = data.id; - cells[1].textContent = data.time; - - // 버튼 변경: 삭제 버튼으로 변경 - cells[2].innerHTML = ''; - cells[2].appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); - - isEditing = false; - - // Remove the editable input fields and just show the saved data - for (let i = 1; i <= 1; i++) { - const inputElement = cells[i].querySelector('input'); - if (inputElement) { - inputElement.remove(); - } - } -} - -function deleteRow(event) { - const row = event.target.closest('tr'); - const id = row.cells[0].textContent; - - requestDelete(id) - .then(() => row.remove()) - .catch(error => console.error('Error:', error)); -} - -function fetchTimes() { - requestRead() - .then(renderTimes) - .catch(error => console.error('Error fetching times:', error)); -} - -function renderTimes(data) { - const tableBody = document.getElementById('time-table-body'); - tableBody.innerHTML = ''; - - data.forEach(time => { - const row = tableBody.insertRow(); - insertTimeRow(row, time); - }); -} - -function insertTimeRow(row, time) { - ['id', 'time'].forEach((field, index) => { - row.insertCell(index).textContent = time[field]; - }); - - const actionCell = row.insertCell(2); - actionCell.appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); -} - -function requestCreate(time) { - const requestOptions = { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(time) - }; - - return fetch(TIME_API_ENDPOINT, requestOptions) - .then(response => { - if (response.status === 201) return response.json(); - throw new Error('Create failed'); - }); -} - -function requestRead() { - return fetch(TIME_API_ENDPOINT) - .then(response => { - if (response.status === 200) return response.json(); - throw new Error('Read failed'); - }); -} - -function requestDelete(id) { - const requestOptions = { - method: 'DELETE', - }; - - return fetch(`${TIME_API_ENDPOINT}/${id}`, requestOptions) - .then(response => { - if (response.status !== 204) throw new Error('Delete failed'); - }); +let isEditing = false; +const TIME_API_ENDPOINT = '/times'; + +document.addEventListener('DOMContentLoaded', () => { + document.getElementById('add-time').addEventListener('click', addEditableRow); + fetchTimes(); +}); + +function addEditableRow() { + + if (isEditing) return; // 이미 편집 중인 경우 추가하지 않음 + + const tableBody = document.getElementById('time-table-body'); + const row = tableBody.insertRow(); + isEditing = true; + + createEditableFieldsFor(row); + addSaveAndCancelButtonsToRow(row); +} + +function createEditableFieldsFor(row) { + const fields = ['', createInput('time')]; + fields.forEach((field, index) => { + const cell = row.insertCell(index); + if (typeof field === 'string') { + cell.textContent = field; + } else { + cell.appendChild(field); + } + }); +} + +function createInput(type) { + const input = document.createElement('input'); + input.type = type; + input.className = 'form-control'; + return input; +} + +function addSaveAndCancelButtonsToRow(row) { + const actionCell = row.insertCell(2); + actionCell.appendChild(createActionButton('확인', 'btn-primary', saveRow)); + actionCell.appendChild(createActionButton('취소', 'btn-secondary', () => { + row.remove(); + isEditing = false; + })); +} + +function createActionButton(label, className, eventListener) { + const button = document.createElement('button'); + button.textContent = label; + button.classList.add('btn', className, 'mr-2'); + button.addEventListener('click', eventListener); + return button; +} + +function saveRow(event) { + const row = event.target.parentNode.parentNode; + const inputs = row.querySelectorAll('input'); + + const time = { + time: inputs[0].value, + }; + + requestCreate(time) + .then(data => updateRowWithTimeData(row, data)) + .catch(error => console.error('Error:', error)); + + isEditing = false; // isEditing 값을 false로 설정 +} + +function updateRowWithTimeData(row, data) { + const cells = row.cells; + cells[0].textContent = data.id; + cells[1].textContent = data.time; + + // 버튼 변경: 삭제 버튼으로 변경 + cells[2].innerHTML = ''; + cells[2].appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); + + isEditing = false; + + // Remove the editable input fields and just show the saved data + for (let i = 1; i <= 1; i++) { + const inputElement = cells[i].querySelector('input'); + if (inputElement) { + inputElement.remove(); + } + } +} + +function deleteRow(event) { + const row = event.target.closest('tr'); + const id = row.cells[0].textContent; + + requestDelete(id) + .then(() => row.remove()) + .catch(error => console.error('Error:', error)); +} + +function fetchTimes() { + requestRead() + .then(renderTimes) + .catch(error => console.error('Error fetching times:', error)); +} + +function renderTimes(data) { + const tableBody = document.getElementById('time-table-body'); + tableBody.innerHTML = ''; + + data.forEach(time => { + const row = tableBody.insertRow(); + insertTimeRow(row, time); + }); +} + +function insertTimeRow(row, time) { + ['id', 'time'].forEach((field, index) => { + row.insertCell(index).textContent = time[field]; + }); + + const actionCell = row.insertCell(2); + actionCell.appendChild(createActionButton('삭제', 'btn-danger', deleteRow)); +} + +function requestCreate(time) { + const requestOptions = { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(time) + }; + + return fetch(TIME_API_ENDPOINT, requestOptions) + .then(response => { + if (response.status === 201) return response.json(); + throw new Error('Create failed'); + }); +} + +function requestRead() { + return fetch(TIME_API_ENDPOINT) + .then(response => { + if (response.status === 200) return response.json(); + throw new Error('Read failed'); + }); +} + +function requestDelete(id) { + const requestOptions = { + method: 'DELETE', + }; + + return fetch(`${TIME_API_ENDPOINT}/${id}`, requestOptions) + .then(response => { + if (response.status !== 204) throw new Error('Delete failed'); + }); } \ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 7ddf752a6..cc9b4eb19 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -1,48 +1,48 @@ - - - - - - - - 방탈출 어드민 페이지 - - - - - - - -
-
- - - -
-

어드민 페이지

-
-
-
- - - + + + + + + + + 방탈출 어드민 페이지 + + + + + + + +
+
+ + + +
+

어드민 페이지

+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/new-reservation.html b/src/main/resources/templates/new-reservation.html index ae49f5ad0..55acad8a7 100644 --- a/src/main/resources/templates/new-reservation.html +++ b/src/main/resources/templates/new-reservation.html @@ -1,69 +1,69 @@ - - - - - - - - 방탈출 어드민 페이지 - - - - - - - - -
-
- - - -
-

예약 관리

-
- -
-
- - - - - - - - - - - - -
예약번호예약자날짜시간
-
-
-
- - - - - + + + + + + + + 방탈출 어드민 페이지 + + + + + + + + +
+
+ + + +
+

예약 관리

+
+ +
+
+ + + + + + + + + + + + +
예약번호예약자날짜시간
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/reservation.html b/src/main/resources/templates/reservation.html index 7c0eb9fc4..be5d69a9b 100644 --- a/src/main/resources/templates/reservation.html +++ b/src/main/resources/templates/reservation.html @@ -1,69 +1,69 @@ - - - - - - - - 방탈출 어드민 페이지 - - - - - - - - -
-
- - - -
-

예약 관리

-
- -
-
- - - - - - - - - - - - -
예약번호예약자날짜시간
-
-
-
- - - - - + + + + + + + + 방탈출 어드민 페이지 + + + + + + + + +
+
+ + + +
+

예약 관리

+
+ +
+
+ + + + + + + + + + + + +
예약번호예약자날짜시간
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/time.html b/src/main/resources/templates/time.html index 4670aeb0f..fd0a4b3af 100644 --- a/src/main/resources/templates/time.html +++ b/src/main/resources/templates/time.html @@ -1,66 +1,66 @@ - - - - - - - - 방탈출 어드민 페이지 - - - - - - - - -
-
- - - -
-

시간 관리

-
- -
-
- - - - - - - - - - -
순서시간
-
-
-
- - - - - + + + + + + + + 방탈출 어드민 페이지 + + + + + + + + +
+
+ + + +
+

시간 관리

+
+ +
+
+ + + + + + + + + + +
순서시간
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 4c62760e3..bf7fb9265 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -1,34 +1,34 @@ -package roomescape; - -import io.restassured.RestAssured; -import org.junit.jupiter.api.Test; -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 { - - @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)); - } -} +package roomescape; + +import io.restassured.RestAssured; +import org.junit.jupiter.api.Test; +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 { + + @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)); + } +} From b1b4fb999f6813b9333d74b73b1f1df84c776189 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 10:53:53 +0900 Subject: [PATCH 10/38] =?UTF-8?q?chore:=20editorconfig=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 6 ++++++ src/main/java/roomescape/ReservationController.java | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..567d8a37d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*] +end_of_line = lf +charset = utf-8 +insert_final_newline = true +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/src/main/java/roomescape/ReservationController.java b/src/main/java/roomescape/ReservationController.java index 6586ba510..1b9b11c59 100644 --- a/src/main/java/roomescape/ReservationController.java +++ b/src/main/java/roomescape/ReservationController.java @@ -29,5 +29,3 @@ public List findAll() { return reservations; } } - - From f8655df5ca4249e54c4f7ce3b48bd82a568297d3 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 11:37:01 +0900 Subject: [PATCH 11/38] =?UTF-8?q?refactor:=20=ED=8F=B4=EB=8D=94=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/RoomescapeApplication.java | 1 + .../{ => controller}/HomeController.java | 24 +++---- .../ReservationController.java | 63 ++++++++++--------- .../roomescape/{ => dto}/Reservation.java | 8 +-- 4 files changed, 49 insertions(+), 47 deletions(-) rename src/main/java/roomescape/{ => controller}/HomeController.java (87%) rename src/main/java/roomescape/{ => controller}/ReservationController.java (92%) rename src/main/java/roomescape/{ => dto}/Reservation.java (76%) diff --git a/src/main/java/roomescape/RoomescapeApplication.java b/src/main/java/roomescape/RoomescapeApplication.java index 702706791..20faf2470 100644 --- a/src/main/java/roomescape/RoomescapeApplication.java +++ b/src/main/java/roomescape/RoomescapeApplication.java @@ -10,3 +10,4 @@ public static void main(String[] args) { } } + diff --git a/src/main/java/roomescape/HomeController.java b/src/main/java/roomescape/controller/HomeController.java similarity index 87% rename from src/main/java/roomescape/HomeController.java rename to src/main/java/roomescape/controller/HomeController.java index 6c5a65293..7ee555676 100644 --- a/src/main/java/roomescape/HomeController.java +++ b/src/main/java/roomescape/controller/HomeController.java @@ -1,12 +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"; - } -} +package roomescape.controller; + +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/main/java/roomescape/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java similarity index 92% rename from src/main/java/roomescape/ReservationController.java rename to src/main/java/roomescape/controller/ReservationController.java index 1b9b11c59..4d9c3c3a9 100644 --- a/src/main/java/roomescape/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -1,31 +1,32 @@ -package roomescape; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -import java.util.ArrayList; -import java.util.List; - -@Controller -public class ReservationController { - - private final List reservations = new ArrayList<>(); - - public ReservationController() { - reservations.add(new Reservation(1, "브라운", "2023-01-01", "10:00")); - reservations.add(new Reservation(2, "브라운", "2023-01-02", "11:00")); - reservations.add(new Reservation(3, "브라운", "2023-01-03", "12:00")); - } - - @GetMapping("/reservation") - public String reservationPage() { - return "reservation"; - } - - @GetMapping("/reservations") - @ResponseBody - public List findAll() { - return reservations; - } -} +package roomescape.controller; + +import org.springframework.stereotype.Controller; +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.List; + +@Controller +public class ReservationController { + + private final List reservations = new ArrayList<>(); + + public ReservationController() { + reservations.add(new Reservation(1, "브라운", "2023-01-01", "10:00")); + reservations.add(new Reservation(2, "브라운", "2023-01-02", "11:00")); + reservations.add(new Reservation(3, "브라운", "2023-01-03", "12:00")); + } + + @GetMapping("/reservation") + public String reservationPage() { + return "reservation"; + } + + @GetMapping("/reservations") + @ResponseBody + public List findAll() { + return reservations; + } +} diff --git a/src/main/java/roomescape/Reservation.java b/src/main/java/roomescape/dto/Reservation.java similarity index 76% rename from src/main/java/roomescape/Reservation.java rename to src/main/java/roomescape/dto/Reservation.java index bbc44073e..0a854da85 100644 --- a/src/main/java/roomescape/Reservation.java +++ b/src/main/java/roomescape/dto/Reservation.java @@ -1,4 +1,4 @@ -package roomescape; - -public record Reservation(int id, String name, String date, String time) { -} +package roomescape.dto; + +public record Reservation(int id, String name, String date, String time) { +} From d75c0dd63fed2e6bce29dfc924849c5a012e4890 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 12:47:09 +0900 Subject: [PATCH 12/38] =?UTF-8?q?refactor:=20ReservationList=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/domain/ReservationList.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/java/roomescape/domain/ReservationList.java diff --git a/src/main/java/roomescape/domain/ReservationList.java b/src/main/java/roomescape/domain/ReservationList.java new file mode 100644 index 000000000..3c3adcfd5 --- /dev/null +++ b/src/main/java/roomescape/domain/ReservationList.java @@ -0,0 +1,34 @@ +package roomescape.domain; + +import org.springframework.stereotype.Component; +import roomescape.dto.Reservation; +import roomescape.dto.ReservationRequest; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +@Component +public class ReservationList { + + private final Map idToReservation = new HashMap<>(); + private int index = 0; + + public List findAll() { + return new ArrayList<>(idToReservation.values()); + } + + public Reservation create(ReservationRequest request) { + int newId = ++index; + Reservation reservation = new Reservation(newId, request.name(), request.date(), request.time()); + idToReservation.put(newId, reservation ); + return reservation; + } + + public void delete(int id) { + idToReservation.remove(id); + } +} + + From 2426350750cfb28894e309aa07af9a4d98e3493d Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 12:57:58 +0900 Subject: [PATCH 13/38] =?UTF-8?q?refactor:=20ResponseEntity=EB=A5=BC=20?= =?UTF-8?q?=EB=8F=84=EC=9E=85=ED=95=98=EC=97=AC=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=EC=97=90=20=EB=A7=9E=EA=B2=8C=20controller?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 43 ++++++++++++------- src/test/java/roomescape/MissionStepTest.java | 41 +++++++++++++++++- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 4d9c3c3a9..0610efd0a 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -1,32 +1,43 @@ package roomescape.controller; -import org.springframework.stereotype.Controller; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import roomescape.domain.ReservationList; import roomescape.dto.Reservation; +import roomescape.dto.ReservationRequest; -import java.util.ArrayList; +import java.net.URI; import java.util.List; -@Controller +@RestController public class ReservationController { - private final List reservations = new ArrayList<>(); + private final ReservationList reservations; - public ReservationController() { - reservations.add(new Reservation(1, "브라운", "2023-01-01", "10:00")); - reservations.add(new Reservation(2, "브라운", "2023-01-02", "11:00")); - reservations.add(new Reservation(3, "브라운", "2023-01-03", "12:00")); - } - - @GetMapping("/reservation") - public String reservationPage() { - return "reservation"; + public ReservationController(ReservationList reservations) { + this.reservations = reservations; } @GetMapping("/reservations") - @ResponseBody public List findAll() { - return reservations; + return reservations.findAll(); + } + + @PostMapping("/reservations") + public ResponseEntity create(@RequestBody ReservationRequest request) { + Reservation created = reservations.create(request); + URI location = URI.create("/reservations/" + created.id()); + return ResponseEntity.created(location).body(created); + } + + @DeleteMapping("/reservations/{id}") + public ResponseEntity delete(@PathVariable int id) { + reservations.delete(id); + return ResponseEntity.noContent().build(); } } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index bf7fb9265..fc7529c2f 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -5,6 +5,10 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; import static org.hamcrest.Matchers.is; +import io.restassured.http.ContentType; +import java.util.Map; +import java.util.HashMap; + @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) @@ -29,6 +33,41 @@ public class MissionStepTest { .when().get("/reservations") .then().log().all() .statusCode(200) - .body("size()", is(3)); + .body("size()", is(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 59d2619f427347510d468697f2b5caac6e35a8e4 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:01:53 +0900 Subject: [PATCH 14/38] =?UTF-8?q?feat:=20=EB=82=B4=EB=B6=80=20dto=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/dto/ReservationRequest.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/roomescape/dto/ReservationRequest.java diff --git a/src/main/java/roomescape/dto/ReservationRequest.java b/src/main/java/roomescape/dto/ReservationRequest.java new file mode 100644 index 000000000..cf941b897 --- /dev/null +++ b/src/main/java/roomescape/dto/ReservationRequest.java @@ -0,0 +1,6 @@ +package roomescape.dto; + +public record ReservationRequest(String name, String date, String time) { +} + + From 34703113e01a504a9fa3992a418aed6aed8bcb7f Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:02:13 +0900 Subject: [PATCH 15/38] =?UTF-8?q?feat:=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationPageController.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/roomescape/controller/ReservationPageController.java diff --git a/src/main/java/roomescape/controller/ReservationPageController.java b/src/main/java/roomescape/controller/ReservationPageController.java new file mode 100644 index 000000000..26bb1772c --- /dev/null +++ b/src/main/java/roomescape/controller/ReservationPageController.java @@ -0,0 +1,17 @@ +package roomescape.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@Controller +public class ReservationPageController { + + @GetMapping("/reservation") + public String reservationPage() { + return "reservation"; + } +} + + From f385424208ac51598b8227e995d7d8cb7008922b Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:27:46 +0900 Subject: [PATCH 16/38] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1,=20Reservation?= =?UTF-8?q?List=EC=97=90=EC=84=9C=20exception=EC=9D=84=20throw=20=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InvalidReservationRequestException.java | 7 +++++++ .../exception/NotFoundReservationException.java | 7 +++++++ .../exception/ReservationExceptionHandler.java | 17 +++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 src/main/java/roomescape/exception/InvalidReservationRequestException.java create mode 100644 src/main/java/roomescape/exception/NotFoundReservationException.java create mode 100644 src/main/java/roomescape/exception/ReservationExceptionHandler.java diff --git a/src/main/java/roomescape/exception/InvalidReservationRequestException.java b/src/main/java/roomescape/exception/InvalidReservationRequestException.java new file mode 100644 index 000000000..8bccf3a54 --- /dev/null +++ b/src/main/java/roomescape/exception/InvalidReservationRequestException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class InvalidReservationRequestException extends RuntimeException { + public InvalidReservationRequestException(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..55990b696 --- /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/main/java/roomescape/exception/ReservationExceptionHandler.java b/src/main/java/roomescape/exception/ReservationExceptionHandler.java new file mode 100644 index 000000000..e1188960a --- /dev/null +++ b/src/main/java/roomescape/exception/ReservationExceptionHandler.java @@ -0,0 +1,17 @@ +package roomescape.exception; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ReservationExceptionHandler { + + @ExceptionHandler({ + InvalidReservationRequestException.class, + NotFoundReservationException.class + }) + public ResponseEntity handleBadRequest(RuntimeException e) { + return ResponseEntity.badRequest().build(); + } +} From 0343da914aacc6833ac89747409e4ece571a8555 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:28:14 +0900 Subject: [PATCH 17/38] =?UTF-8?q?chore:=20requestDto=EC=9D=98=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=AA=85=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/Reservation.java | 4 ---- src/main/java/roomescape/dto/ReservationRequest.java | 2 -- src/main/java/roomescape/dto/ReservationResponse.java | 4 ++++ 3 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 src/main/java/roomescape/dto/Reservation.java create mode 100644 src/main/java/roomescape/dto/ReservationResponse.java diff --git a/src/main/java/roomescape/dto/Reservation.java b/src/main/java/roomescape/dto/Reservation.java deleted file mode 100644 index 0a854da85..000000000 --- a/src/main/java/roomescape/dto/Reservation.java +++ /dev/null @@ -1,4 +0,0 @@ -package roomescape.dto; - -public record Reservation(int id, String name, String date, String time) { -} diff --git a/src/main/java/roomescape/dto/ReservationRequest.java b/src/main/java/roomescape/dto/ReservationRequest.java index cf941b897..e646e64be 100644 --- a/src/main/java/roomescape/dto/ReservationRequest.java +++ b/src/main/java/roomescape/dto/ReservationRequest.java @@ -2,5 +2,3 @@ public record ReservationRequest(String name, String date, String time) { } - - diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java new file mode 100644 index 000000000..72564ec82 --- /dev/null +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -0,0 +1,4 @@ +package roomescape.dto; + +public record ReservationResponse(int id, String name, String date, String time) { +} From afb174a5a703e95a05cc7750bb37003961f37c2d Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:28:48 +0900 Subject: [PATCH 18/38] =?UTF-8?q?test:=204=EB=8B=A8=EA=B3=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/roomescape/MissionStepTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index fc7529c2f..e5bed08f3 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 b6d22f25a15f42251d5cd431a8b96b3ecf94737f Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:29:23 +0900 Subject: [PATCH 19/38] =?UTF-8?q?chore:=20=EB=B3=80=EA=B2=BD=ED=95=9C=20dt?= =?UTF-8?q?o=EB=AA=85=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/RoomescapeApplication.java | 1 - .../controller/ReservationController.java | 8 +++---- .../controller/ReservationPageController.java | 2 -- .../roomescape/domain/ReservationList.java | 22 +++++++++++++------ 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main/java/roomescape/RoomescapeApplication.java b/src/main/java/roomescape/RoomescapeApplication.java index 20faf2470..702706791 100644 --- a/src/main/java/roomescape/RoomescapeApplication.java +++ b/src/main/java/roomescape/RoomescapeApplication.java @@ -10,4 +10,3 @@ public static void main(String[] args) { } } - diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 0610efd0a..08c0a8bdd 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import roomescape.domain.ReservationList; -import roomescape.dto.Reservation; +import roomescape.dto.ReservationResponse; import roomescape.dto.ReservationRequest; import java.net.URI; @@ -24,13 +24,13 @@ public ReservationController(ReservationList reservations) { } @GetMapping("/reservations") - public List findAll() { + public List findAll() { return reservations.findAll(); } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest request) { - Reservation created = reservations.create(request); + public ResponseEntity create(@RequestBody ReservationRequest request) { + ReservationResponse created = reservations.create(request); URI location = URI.create("/reservations/" + created.id()); return ResponseEntity.created(location).body(created); } diff --git a/src/main/java/roomescape/controller/ReservationPageController.java b/src/main/java/roomescape/controller/ReservationPageController.java index 26bb1772c..deceb1cf5 100644 --- a/src/main/java/roomescape/controller/ReservationPageController.java +++ b/src/main/java/roomescape/controller/ReservationPageController.java @@ -13,5 +13,3 @@ public String reservationPage() { return "reservation"; } } - - diff --git a/src/main/java/roomescape/domain/ReservationList.java b/src/main/java/roomescape/domain/ReservationList.java index 3c3adcfd5..f95c61281 100644 --- a/src/main/java/roomescape/domain/ReservationList.java +++ b/src/main/java/roomescape/domain/ReservationList.java @@ -1,8 +1,10 @@ package roomescape.domain; import org.springframework.stereotype.Component; -import roomescape.dto.Reservation; +import roomescape.dto.ReservationResponse; import roomescape.dto.ReservationRequest; +import roomescape.exception.InvalidReservationRequestException; +import roomescape.exception.NotFoundReservationException; import java.util.ArrayList; import java.util.List; @@ -12,23 +14,29 @@ @Component public class ReservationList { - private final Map idToReservation = new HashMap<>(); + private final Map idToReservation = new HashMap<>(); private int index = 0; - public List findAll() { + public List findAll() { return new ArrayList<>(idToReservation.values()); } - public Reservation create(ReservationRequest request) { + public ReservationResponse create(ReservationRequest request) { + if (request.name() == null || request.name().isBlank() + || request.date() == null || request.date().isBlank() + || request.time() == null || request.time().isBlank()) { + throw new InvalidReservationRequestException("이름,날짜,시간이 모두 입력되어야 합니다."); + } int newId = ++index; - Reservation reservation = new Reservation(newId, request.name(), request.date(), request.time()); + ReservationResponse reservation = new ReservationResponse(newId, request.name(), request.date(), request.time()); idToReservation.put(newId, reservation ); return reservation; } public void delete(int id) { + if (!idToReservation.containsKey(id)) { + throw new NotFoundReservationException("해당 분실물을 찾을 수 없습니다: " + id); + } idToReservation.remove(id); } } - - From 7931473645aeedcdcc750f1be3f17953c18143c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=ED=98=84?= Date: Thu, 13 Nov 2025 18:15:16 +0900 Subject: [PATCH 20/38] chore: remove editorconfig for clean diff --- .editorconfig | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 567d8a37d..000000000 --- a/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -[*] -end_of_line = lf -charset = utf-8 -insert_final_newline = true -indent_style = space -indent_size = 4 \ No newline at end of file From 46a253dd5ecef062e307f063dbe6be6a3bd6015d Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 15 Nov 2025 23:39:08 +0900 Subject: [PATCH 21/38] =?UTF-8?q?feat:=20ReservationList=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=EC=99=80=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 2 +- .../java/roomescape/domain/Reservation.java | 33 +++++++++++++++++++ .../{domain => service}/ReservationList.java | 23 +++++++++---- 3 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 src/main/java/roomescape/domain/Reservation.java rename src/main/java/roomescape/{domain => service}/ReservationList.java (60%) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 08c0a8bdd..20e7708ef 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import roomescape.domain.ReservationList; +import roomescape.service.ReservationList; import roomescape.dto.ReservationResponse; import roomescape.dto.ReservationRequest; diff --git a/src/main/java/roomescape/domain/Reservation.java b/src/main/java/roomescape/domain/Reservation.java new file mode 100644 index 000000000..43342d632 --- /dev/null +++ b/src/main/java/roomescape/domain/Reservation.java @@ -0,0 +1,33 @@ +package roomescape.domain; + +public class Reservation { + private final int id; + private final String name; + private final String date; + private final String time; + + public Reservation(int id, String name, String date, String time) { + this.id = id; + this.name = name; + this.date = date; + this.time = time; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDate() { + return date; + } + + public String getTime() { + return time; + } +} + + diff --git a/src/main/java/roomescape/domain/ReservationList.java b/src/main/java/roomescape/service/ReservationList.java similarity index 60% rename from src/main/java/roomescape/domain/ReservationList.java rename to src/main/java/roomescape/service/ReservationList.java index f95c61281..52c6139f9 100644 --- a/src/main/java/roomescape/domain/ReservationList.java +++ b/src/main/java/roomescape/service/ReservationList.java @@ -1,10 +1,11 @@ -package roomescape.domain; +package roomescape.service; import org.springframework.stereotype.Component; import roomescape.dto.ReservationResponse; import roomescape.dto.ReservationRequest; import roomescape.exception.InvalidReservationRequestException; import roomescape.exception.NotFoundReservationException; +import roomescape.domain.Reservation; import java.util.ArrayList; import java.util.List; @@ -14,11 +15,15 @@ @Component public class ReservationList { - private final Map idToReservation = new HashMap<>(); + private final Map idToReservation = new HashMap<>(); private int index = 0; public List findAll() { - return new ArrayList<>(idToReservation.values()); + List responses = new ArrayList<>(); + for (Reservation reservation : idToReservation.values()) { + responses.add(toResponse(reservation)); + } + return responses; } public ReservationResponse create(ReservationRequest request) { @@ -28,9 +33,9 @@ public ReservationResponse create(ReservationRequest request) { throw new InvalidReservationRequestException("이름,날짜,시간이 모두 입력되어야 합니다."); } int newId = ++index; - ReservationResponse reservation = new ReservationResponse(newId, request.name(), request.date(), request.time()); - idToReservation.put(newId, reservation ); - return reservation; + Reservation reservation = new Reservation(newId, request.name(), request.date(), request.time()); + idToReservation.put(newId, reservation); + return toResponse(reservation); } public void delete(int id) { @@ -39,4 +44,10 @@ public void delete(int id) { } idToReservation.remove(id); } + + private ReservationResponse toResponse(Reservation reservation) { + return new ReservationResponse(reservation.getId(), reservation.getName(), reservation.getDate(), reservation.getTime()); + } } + + From b92cdfb777cd17586ebae57cf31fa8142e0e5823 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 15 Nov 2025 23:52:50 +0900 Subject: [PATCH 22/38] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ReservationExceptionHandler.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/roomescape/exception/ReservationExceptionHandler.java b/src/main/java/roomescape/exception/ReservationExceptionHandler.java index e1188960a..8ce8aa3b5 100644 --- a/src/main/java/roomescape/exception/ReservationExceptionHandler.java +++ b/src/main/java/roomescape/exception/ReservationExceptionHandler.java @@ -1,17 +1,29 @@ package roomescape.exception; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; @RestControllerAdvice public class ReservationExceptionHandler { @ExceptionHandler({ InvalidReservationRequestException.class, - NotFoundReservationException.class + NotFoundReservationException.class, + IllegalArgumentException.class, + HttpMessageNotReadableException.class, + MethodArgumentTypeMismatchException.class, + MissingServletRequestParameterException.class }) public ResponseEntity handleBadRequest(RuntimeException e) { return ResponseEntity.badRequest().build(); } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleUnexpected(Exception e) { + return ResponseEntity.status(500).build(); + } } From 70a9b67aec21aa972976ac6455417f0dfc5280c0 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sun, 16 Nov 2025 00:04:20 +0900 Subject: [PATCH 23/38] =?UTF-8?q?refactor:=20404=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/exception/ReservationExceptionHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/roomescape/exception/ReservationExceptionHandler.java b/src/main/java/roomescape/exception/ReservationExceptionHandler.java index 8ce8aa3b5..e0440d7d1 100644 --- a/src/main/java/roomescape/exception/ReservationExceptionHandler.java +++ b/src/main/java/roomescape/exception/ReservationExceptionHandler.java @@ -12,7 +12,6 @@ public class ReservationExceptionHandler { @ExceptionHandler({ InvalidReservationRequestException.class, - NotFoundReservationException.class, IllegalArgumentException.class, HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class, @@ -22,6 +21,11 @@ public ResponseEntity handleBadRequest(RuntimeException e) { return ResponseEntity.badRequest().build(); } + @ExceptionHandler(NotFoundReservationException.class) + public ResponseEntity handleNotFound(NotFoundReservationException e) { + return ResponseEntity.status(404).build(); + } + @ExceptionHandler(Exception.class) public ResponseEntity handleUnexpected(Exception e) { return ResponseEntity.status(500).build(); From 102f026ca3cf78c71937594fc31b86596591a3e3 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sun, 16 Nov 2025 00:23:09 +0900 Subject: [PATCH 24/38] =?UTF-8?q?refactor:=20AtomicLong=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 2 +- .../java/roomescape/domain/Reservation.java | 6 ++--- .../roomescape/dto/ReservationResponse.java | 2 +- .../roomescape/service/ReservationList.java | 23 +++++++++++++------ 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 20e7708ef..e0d546292 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -36,7 +36,7 @@ public ResponseEntity create(@RequestBody ReservationReques } @DeleteMapping("/reservations/{id}") - public ResponseEntity delete(@PathVariable int id) { + public ResponseEntity delete(@PathVariable long id) { reservations.delete(id); return ResponseEntity.noContent().build(); } diff --git a/src/main/java/roomescape/domain/Reservation.java b/src/main/java/roomescape/domain/Reservation.java index 43342d632..dd2af4eb7 100644 --- a/src/main/java/roomescape/domain/Reservation.java +++ b/src/main/java/roomescape/domain/Reservation.java @@ -1,19 +1,19 @@ package roomescape.domain; public class Reservation { - private final int id; + private final long id; private final String name; private final String date; private final String time; - public Reservation(int id, String name, String date, 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 int getId() { + public long getId() { return id; } diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java index 72564ec82..7d131db81 100644 --- a/src/main/java/roomescape/dto/ReservationResponse.java +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -1,4 +1,4 @@ package roomescape.dto; -public record ReservationResponse(int id, String name, String date, String time) { +public record ReservationResponse(long id, String name, String date, String time) { } diff --git a/src/main/java/roomescape/service/ReservationList.java b/src/main/java/roomescape/service/ReservationList.java index 52c6139f9..e80fe6ea6 100644 --- a/src/main/java/roomescape/service/ReservationList.java +++ b/src/main/java/roomescape/service/ReservationList.java @@ -15,8 +15,11 @@ @Component public class ReservationList { - private final Map idToReservation = new HashMap<>(); - private int index = 0; + private final Map idToReservation = new HashMap<>(); + private AtomicLong index = 0; + public ReservationList() { + this.index = new AtomicLong(0); + } public List findAll() { List responses = new ArrayList<>(); @@ -28,17 +31,23 @@ public List findAll() { public ReservationResponse create(ReservationRequest request) { if (request.name() == null || request.name().isBlank() - || request.date() == null || request.date().isBlank() - || request.time() == null || request.time().isBlank()) { - throw new InvalidReservationRequestException("이름,날짜,시간이 모두 입력되어야 합니다."); + || request.date() == null || request.date().isBlank() + || request.time() == null || request.time().isBlank()) { + throw new InvalidReservationRequestException( + "잘못된 예약 요청입니다. " + + "name=" + request.name() + + ", date=" + request.date() + + ", time=" + request.time() + ); } - int newId = ++index; + + long newId = index.incrementAndGet(); Reservation reservation = new Reservation(newId, request.name(), request.date(), request.time()); idToReservation.put(newId, reservation); return toResponse(reservation); } - public void delete(int id) { + public void delete(long id) { if (!idToReservation.containsKey(id)) { throw new NotFoundReservationException("해당 분실물을 찾을 수 없습니다: " + id); } From 2d7ef51882528c986a460c6cfbb0168c3ff26093 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sun, 16 Nov 2025 00:30:07 +0900 Subject: [PATCH 25/38] =?UTF-8?q?refactor:=20IllegalArgumentException?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/InvalidReservationRequestException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/roomescape/exception/InvalidReservationRequestException.java b/src/main/java/roomescape/exception/InvalidReservationRequestException.java index 8bccf3a54..da1a3780e 100644 --- a/src/main/java/roomescape/exception/InvalidReservationRequestException.java +++ b/src/main/java/roomescape/exception/InvalidReservationRequestException.java @@ -1,6 +1,6 @@ package roomescape.exception; -public class InvalidReservationRequestException extends RuntimeException { +public class InvalidReservationRequestException extends IllegalArgumentException { public InvalidReservationRequestException(String message) { super(message); } From 4e374501ec6225670cb85f921ad6a47be3c8680d Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sun, 16 Nov 2025 00:34:44 +0900 Subject: [PATCH 26/38] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/service/ReservationList.java | 3 ++- src/test/java/roomescape/MissionStepTest.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/roomescape/service/ReservationList.java b/src/main/java/roomescape/service/ReservationList.java index e80fe6ea6..bd83592f5 100644 --- a/src/main/java/roomescape/service/ReservationList.java +++ b/src/main/java/roomescape/service/ReservationList.java @@ -11,12 +11,13 @@ import java.util.List; import java.util.Map; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicLong; @Component public class ReservationList { private final Map idToReservation = new HashMap<>(); - private AtomicLong index = 0; + private AtomicLong index; public ReservationList() { this.index = new AtomicLong(0); } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index e5bed08f3..110a734c5 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -90,6 +90,6 @@ public class MissionStepTest { RestAssured.given().log().all() .when().delete("/reservations/1") .then().log().all() - .statusCode(400); + .statusCode(404); } } From 31af36687d550daa5f12d3a11be5b87726bf6550 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Mon, 17 Nov 2025 05:18:30 +0900 Subject: [PATCH 27/38] =?UTF-8?q?refactor:=20long=20=EC=9E=90=EB=A3=8C?= =?UTF-8?q?=ED=98=95=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/domain/Reservation.java | 6 +++--- src/main/java/roomescape/service/ReservationList.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/roomescape/domain/Reservation.java b/src/main/java/roomescape/domain/Reservation.java index dd2af4eb7..81e6290b1 100644 --- a/src/main/java/roomescape/domain/Reservation.java +++ b/src/main/java/roomescape/domain/Reservation.java @@ -1,19 +1,19 @@ package roomescape.domain; public class Reservation { - private final long id; + private final Long id; private final String name; private final String date; private final String time; - public Reservation(long id, String name, String date, 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() { + public Long getId() { return id; } diff --git a/src/main/java/roomescape/service/ReservationList.java b/src/main/java/roomescape/service/ReservationList.java index bd83592f5..e88363881 100644 --- a/src/main/java/roomescape/service/ReservationList.java +++ b/src/main/java/roomescape/service/ReservationList.java @@ -42,13 +42,13 @@ public ReservationResponse create(ReservationRequest request) { ); } - long newId = index.incrementAndGet(); + Long newId = index.incrementAndGet(); Reservation reservation = new Reservation(newId, request.name(), request.date(), request.time()); idToReservation.put(newId, reservation); return toResponse(reservation); } - public void delete(long id) { + public void delete(Long id) { if (!idToReservation.containsKey(id)) { throw new NotFoundReservationException("해당 분실물을 찾을 수 없습니다: " + id); } From 1982c7673d2e05fad0167c7076fa074cbbe4a2c2 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Mon, 17 Nov 2025 05:49:35 +0900 Subject: [PATCH 28/38] =?UTF-8?q?refactor:=20logger=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ReservationExceptionHandler.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/roomescape/exception/ReservationExceptionHandler.java b/src/main/java/roomescape/exception/ReservationExceptionHandler.java index e0440d7d1..40a4c990f 100644 --- a/src/main/java/roomescape/exception/ReservationExceptionHandler.java +++ b/src/main/java/roomescape/exception/ReservationExceptionHandler.java @@ -7,9 +7,16 @@ import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + @RestControllerAdvice public class ReservationExceptionHandler { + private static final Logger logger = Logger.getLogger("테스트용"); + @ExceptionHandler({ InvalidReservationRequestException.class, IllegalArgumentException.class, @@ -23,6 +30,10 @@ public ResponseEntity handleBadRequest(RuntimeException e) { @ExceptionHandler(NotFoundReservationException.class) public ResponseEntity handleNotFound(NotFoundReservationException e) { + String stackTrace = Arrays.stream(e.getStackTrace()) + .map(StackTraceElement::toString) + .collect(Collectors.joining("\n")); + logger.log(Level.INFO, stackTrace); return ResponseEntity.status(404).build(); } From 9743e4a0a9c68ad08515e71f0b577e09d1f6fd38 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 20 Nov 2025 10:01:14 +0900 Subject: [PATCH 29/38] =?UTF-8?q?gradle:=20=EC=9D=98=EC=A1=B4=EC=84=B1=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 --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 1be9887e7..454d27036 100644 --- a/build.gradle +++ b/build.gradle @@ -16,11 +16,14 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' developmentOnly 'org.springframework.boot:spring-boot-devtools' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' + + runtimeOnly 'com.h2database:h2' } test { From 10a094a79878501f658c07f1de079930c70fda11 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:09:09 +0900 Subject: [PATCH 30/38] =?UTF-8?q?chore:=20=EC=9E=98=EB=AA=BB=20=EC=98=AE?= =?UTF-8?q?=EA=B8=B4=20=ED=8C=8C=EC=9D=BC=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/HomeController.java | 12 ------- src/main/java/roomescape/Reservation.java | 4 --- .../roomescape/ReservationController.java | 33 ------------------- 3 files changed, 49 deletions(-) delete mode 100644 src/main/java/roomescape/HomeController.java delete mode 100644 src/main/java/roomescape/Reservation.java delete mode 100644 src/main/java/roomescape/ReservationController.java diff --git a/src/main/java/roomescape/HomeController.java b/src/main/java/roomescape/HomeController.java deleted file mode 100644 index 6c5a65293..000000000 --- a/src/main/java/roomescape/HomeController.java +++ /dev/null @@ -1,12 +0,0 @@ -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/main/java/roomescape/Reservation.java b/src/main/java/roomescape/Reservation.java deleted file mode 100644 index bbc44073e..000000000 --- a/src/main/java/roomescape/Reservation.java +++ /dev/null @@ -1,4 +0,0 @@ -package roomescape; - -public record Reservation(int id, String name, String date, String time) { -} diff --git a/src/main/java/roomescape/ReservationController.java b/src/main/java/roomescape/ReservationController.java deleted file mode 100644 index 6586ba510..000000000 --- a/src/main/java/roomescape/ReservationController.java +++ /dev/null @@ -1,33 +0,0 @@ -package roomescape; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -import java.util.ArrayList; -import java.util.List; - -@Controller -public class ReservationController { - - private final List reservations = new ArrayList<>(); - - public ReservationController() { - reservations.add(new Reservation(1, "브라운", "2023-01-01", "10:00")); - reservations.add(new Reservation(2, "브라운", "2023-01-02", "11:00")); - reservations.add(new Reservation(3, "브라운", "2023-01-03", "12:00")); - } - - @GetMapping("/reservation") - public String reservationPage() { - return "reservation"; - } - - @GetMapping("/reservations") - @ResponseBody - public List findAll() { - return reservations; - } -} - - From 5c852cc17f3a43b38fa642009a5d1d0449796850 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:09:54 +0900 Subject: [PATCH 31/38] =?UTF-8?q?feat:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EC=8A=A4=ED=82=A4=EB=A7=88=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 5 +++++ src/main/resources/schema.sql | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 src/main/resources/schema.sql diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29bb..822f3ed0e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console + +spring.datasource.url=jdbc:h2:mem:database +spring.datasource.driver-class-name=org.h2.Driver \ 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..0a3d3a80e --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,9 @@ +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) +); + From 9aa4ca92119e5075d601fee5d0e69d796da62ddc Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:11:00 +0900 Subject: [PATCH 32/38] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/roomescape/MissionStepTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 110a734c5..9e9c44dc9 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -8,6 +8,11 @@ import io.restassured.http.ContentType; import java.util.Map; import java.util.HashMap; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import java.sql.Connection; +import java.sql.SQLException; +import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @@ -92,4 +97,18 @@ public class MissionStepTest { .then().log().all() .statusCode(404); } + + @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 75bd872e33032b4132463eb1b6446cb7f79b4fd6 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:23:58 +0900 Subject: [PATCH 33/38] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=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 | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 9e9c44dc9..333078f1f 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -99,16 +99,32 @@ public class MissionStepTest { } @Autowired -private JdbcTemplate jdbcTemplate; + 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); + 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); + } } + + @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); } } From 63c9eff59022f6661797a5100aeb2bfda03946e6 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:56:55 +0900 Subject: [PATCH 34/38] =?UTF-8?q?test:=20=EC=B9=A0=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/roomescape/MissionStepTest.java | 89 +++++++++++++------ 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 333078f1f..9cbb45f81 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -4,16 +4,25 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; + import static org.hamcrest.Matchers.is; + import io.restassured.http.ContentType; + import java.util.Map; import java.util.HashMap; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; + import java.sql.Connection; import java.sql.SQLException; + import static org.assertj.core.api.Assertions.assertThat; +import java.util.List; + +import roomescape.domain.Reservation; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) @@ -41,7 +50,6 @@ public class MissionStepTest { .body("size()", is(0)); } - @Test void 삼단계() { Map params = new HashMap<>(); @@ -101,30 +109,61 @@ public class MissionStepTest { @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); - } + @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); } + } - @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 육단계() { + 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 10c8b9f278256c05ad7e97216e44cb60cd22c61f Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:13:43 +0900 Subject: [PATCH 35/38] =?UTF-8?q?refactor:=20dao=EC=99=80=EC=9D=98=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/dao/ReservationDao.java | 57 +++++++++++++++++++ .../roomescape/service/ReservationList.java | 46 ++++++++------- 2 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 src/main/java/roomescape/dao/ReservationDao.java diff --git a/src/main/java/roomescape/dao/ReservationDao.java b/src/main/java/roomescape/dao/ReservationDao.java new file mode 100644 index 000000000..0be1e5b85 --- /dev/null +++ b/src/main/java/roomescape/dao/ReservationDao.java @@ -0,0 +1,57 @@ +package roomescape.dao; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Repository; +import roomescape.domain.Reservation; + +import java.sql.PreparedStatement; +import java.util.List; + +@Repository +public class ReservationDao { + + private final JdbcTemplate jdbcTemplate; + + public ReservationDao(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + private final RowMapper rowMapper = (resultSet, rowNum) -> + new Reservation( + resultSet.getLong("id"), + resultSet.getString("name"), + resultSet.getString("date"), + resultSet.getString("time") + ); + + public List findAll() { + return jdbcTemplate.query( + "SELECT id, name, date, time FROM reservation", + rowMapper + ); + } + + public long insert(String name, String date, String time) { + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update(con -> { + PreparedStatement ps = con.prepareStatement( + "INSERT INTO reservation (name, date, time) VALUES (?, ?, ?)", + new String[]{"id"} + ); + ps.setString(1, name); + ps.setString(2, date); + ps.setString(3, time); + return ps; + }, keyHolder); + return keyHolder.getKey().longValue(); + } + + public int deleteById(long id) { + return jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); + } +} + + diff --git a/src/main/java/roomescape/service/ReservationList.java b/src/main/java/roomescape/service/ReservationList.java index e88363881..1427b7961 100644 --- a/src/main/java/roomescape/service/ReservationList.java +++ b/src/main/java/roomescape/service/ReservationList.java @@ -1,6 +1,7 @@ package roomescape.service; import org.springframework.stereotype.Component; +import roomescape.dao.ReservationDao; import roomescape.dto.ReservationResponse; import roomescape.dto.ReservationRequest; import roomescape.exception.InvalidReservationRequestException; @@ -9,22 +10,20 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.HashMap; -import java.util.concurrent.atomic.AtomicLong; @Component public class ReservationList { - private final Map idToReservation = new HashMap<>(); - private AtomicLong index; - public ReservationList() { - this.index = new AtomicLong(0); + private final ReservationDao reservationDao; + + public ReservationList(ReservationDao reservationDao) { + this.reservationDao = reservationDao; } public List findAll() { + List results = reservationDao.findAll(); List responses = new ArrayList<>(); - for (Reservation reservation : idToReservation.values()) { + for (Reservation reservation : results) { responses.add(toResponse(reservation)); } return responses; @@ -32,31 +31,34 @@ public List findAll() { public ReservationResponse create(ReservationRequest request) { if (request.name() == null || request.name().isBlank() - || request.date() == null || request.date().isBlank() - || request.time() == null || request.time().isBlank()) { + || request.date() == null || request.date().isBlank() + || request.time() == null || request.time().isBlank()) { throw new InvalidReservationRequestException( "잘못된 예약 요청입니다. " + - "name=" + request.name() + - ", date=" + request.date() + - ", time=" + request.time() + "name=" + request.name() + + ", date=" + request.date() + + ", time=" + request.time() ); } - Long newId = index.incrementAndGet(); - Reservation reservation = new Reservation(newId, request.name(), request.date(), request.time()); - idToReservation.put(newId, reservation); - return toResponse(reservation); + long id = reservationDao.insert(request.name(), request.date(), request.time()); + return new ReservationResponse(id, request.name(), request.date(), request.time()); } - public void delete(Long id) { - if (!idToReservation.containsKey(id)) { - throw new NotFoundReservationException("해당 분실물을 찾을 수 없습니다: " + id); + public void delete(long id) { + int updated = reservationDao.deleteById(id); + if (updated == 0) { + throw new NotFoundReservationException("해당 예약을 찾을 수 없습니다: " + id); } - idToReservation.remove(id); } private ReservationResponse toResponse(Reservation reservation) { - return new ReservationResponse(reservation.getId(), reservation.getName(), reservation.getDate(), reservation.getTime()); + return new ReservationResponse( + reservation.getId(), + reservation.getName(), + reservation.getDate(), + reservation.getTime() + ); } } From 0b545da67cc6fbf41538d8654bfcd6eaf3ca9f2d Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:35:47 +0900 Subject: [PATCH 36/38] =?UTF-8?q?refactor:=20Simplejdbcinsert=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/dao/ReservationDao.java | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/main/java/roomescape/dao/ReservationDao.java b/src/main/java/roomescape/dao/ReservationDao.java index 0be1e5b85..a975e1407 100644 --- a/src/main/java/roomescape/dao/ReservationDao.java +++ b/src/main/java/roomescape/dao/ReservationDao.java @@ -2,21 +2,25 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import roomescape.domain.Reservation; -import java.sql.PreparedStatement; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Repository public class ReservationDao { private final JdbcTemplate jdbcTemplate; + private final SimpleJdbcInsert simpleInsert; public ReservationDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; + this.simpleInsert = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("reservation") + .usingGeneratedKeyColumns("id"); } private final RowMapper rowMapper = (resultSet, rowNum) -> @@ -35,23 +39,16 @@ public List findAll() { } public long insert(String name, String date, String time) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(con -> { - PreparedStatement ps = con.prepareStatement( - "INSERT INTO reservation (name, date, time) VALUES (?, ?, ?)", - new String[]{"id"} - ); - ps.setString(1, name); - ps.setString(2, date); - ps.setString(3, time); - return ps; - }, keyHolder); - return keyHolder.getKey().longValue(); + Map params = new HashMap<>(); + params.put("name", name); + params.put("date", date); + params.put("time", time); + + Number key = simpleInsert.executeAndReturnKey(params); + return key.longValue(); } public int deleteById(long id) { return jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); } } - - From 9759c2d4155408f745f40c1bae999705f3c5bbc8 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:39:33 +0900 Subject: [PATCH 37/38] =?UTF-8?q?refactor:=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=EB=AA=85=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/service/ReservationList.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/roomescape/service/ReservationList.java b/src/main/java/roomescape/service/ReservationList.java index 1427b7961..27723bc65 100644 --- a/src/main/java/roomescape/service/ReservationList.java +++ b/src/main/java/roomescape/service/ReservationList.java @@ -1,6 +1,7 @@ package roomescape.service; import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import roomescape.dao.ReservationDao; import roomescape.dto.ReservationResponse; import roomescape.dto.ReservationRequest; @@ -11,7 +12,7 @@ import java.util.ArrayList; import java.util.List; -@Component +@Service public class ReservationList { private final ReservationDao reservationDao; From 9a5996ab90b5cf8072f3d0e70a0da0a8bca6681d Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:38:12 +0900 Subject: [PATCH 38/38] =?UTF-8?q?refactor:=20logger=20=EC=A0=81=EC=9A=A9,?= =?UTF-8?q?=20dto=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/dao/ReservationDao.java | 8 ++++++++ .../java/roomescape/service/ReservationList.java | 14 ++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/roomescape/dao/ReservationDao.java b/src/main/java/roomescape/dao/ReservationDao.java index a975e1407..7e74dd4e0 100644 --- a/src/main/java/roomescape/dao/ReservationDao.java +++ b/src/main/java/roomescape/dao/ReservationDao.java @@ -48,6 +48,14 @@ public long insert(String name, String date, String time) { return key.longValue(); } + public Reservation findById(long id) { + return jdbcTemplate.queryForObject( + "SELECT id, name, date, time FROM reservation WHERE id = ?", + rowMapper, + id + ); + } + public int deleteById(long id) { return jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); } diff --git a/src/main/java/roomescape/service/ReservationList.java b/src/main/java/roomescape/service/ReservationList.java index 27723bc65..836ee5a32 100644 --- a/src/main/java/roomescape/service/ReservationList.java +++ b/src/main/java/roomescape/service/ReservationList.java @@ -1,6 +1,5 @@ package roomescape.service; -import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import roomescape.dao.ReservationDao; import roomescape.dto.ReservationResponse; @@ -11,11 +10,13 @@ import java.util.ArrayList; import java.util.List; +import java.util.logging.Logger; @Service public class ReservationList { private final ReservationDao reservationDao; + private static final Logger logger = Logger.getLogger(ReservationList.class.getName()); public ReservationList(ReservationDao reservationDao) { this.reservationDao = reservationDao; @@ -34,16 +35,13 @@ public ReservationResponse create(ReservationRequest request) { if (request.name() == null || request.name().isBlank() || request.date() == null || request.date().isBlank() || request.time() == null || request.time().isBlank()) { - throw new InvalidReservationRequestException( - "잘못된 예약 요청입니다. " + - "name=" + request.name() + - ", date=" + request.date() + - ", time=" + request.time() - ); + logger.warning("Invalid request: " + request); + throw new InvalidReservationRequestException("잘못된 예약 요청입니다."); } long id = reservationDao.insert(request.name(), request.date(), request.time()); - return new ReservationResponse(id, request.name(), request.date(), request.time()); + Reservation saved = reservationDao.findById(id); + return toResponse(saved); } public void delete(long id) {