From 3d3151e9678be51691d22322411fbc9f3bf29976 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 01:52:16 +0900 Subject: [PATCH 01/54] =?UTF-8?q?refactor:=20Id=EB=A1=9C=20=EB=A7=B4?= =?UTF-8?q?=EB=B2=84=20=EC=A1=B0=ED=9A=8C=20dao=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/member/MemberDao.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/roomescape/member/MemberDao.java b/src/main/java/roomescape/member/MemberDao.java index 81f77f4cd..3ad19b4b8 100644 --- a/src/main/java/roomescape/member/MemberDao.java +++ b/src/main/java/roomescape/member/MemberDao.java @@ -40,6 +40,19 @@ public Member findByEmailAndPassword(String email, String password) { ); } + public Member findById(Long id) { + return jdbcTemplate.queryForObject( + "SELECT id, name, email, role FROM member WHERE id = ?", + (rs, rowNum) -> new Member( + rs.getLong("id"), + rs.getString("name"), + rs.getString("email"), + rs.getString("role") + ), + id + ); + } + public Member findByName(String name) { return jdbcTemplate.queryForObject( "SELECT id, name, email, role FROM member WHERE name = ?", From 0c3e685e0023776c93a4e443934aeeb0b59deb68 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 09:09:36 +0900 Subject: [PATCH 02/54] =?UTF-8?q?feat:=20POST/login=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/member/MemberController.java | 22 +++++++++++++++++++ .../java/roomescape/member/MemberService.java | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 881ae5e0d..ea9393029 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -1,5 +1,7 @@ package roomescape.member; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -25,6 +27,26 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); } + @PostMapping("/login") + public ResponseEntity login(@RequestBody MemberRequest memberRequest, HttpServletResponse response) { + Member member = memberService.login(memberRequest.getEmail(), memberRequest.getPassword()); + + String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; + String accessToken = Jwts.builder() + .setSubject(member.getId().toString()) + .claim("name", member.getName()) + .claim("role", member.getRole()) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) + .compact(); + + Cookie cookie = new Cookie("token", accessToken); + cookie.setHttpOnly(true); + cookie.setPath("/"); + response.addCookie(cookie); + + return ResponseEntity.ok().build(); + } + @PostMapping("/logout") public ResponseEntity logout(HttpServletResponse response) { Cookie cookie = new Cookie("token", ""); diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index ccaa8cba5..2fa1acc10 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -14,4 +14,8 @@ public MemberResponse createMember(MemberRequest memberRequest) { Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), memberRequest.getPassword(), "USER")); return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } + + public Member login(String email, String password) { + return memberDao.findByEmailAndPassword(email, password); + } } From 8b26e96e1369c41624037ecbd5df8c4899b7af16 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 09:26:44 +0900 Subject: [PATCH 03/54] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/member/MemberController.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index ea9393029..051e0db67 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -47,6 +47,34 @@ public ResponseEntity login(@RequestBody MemberRequest memberRequest, HttpServle return ResponseEntity.ok().build(); } + @GetMapping("/login/check") + public ResponseEntity checkLogin(HttpServletRequest request) { + String token = extractTokenFromCookie(request.getCookies()); + + String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; + String name = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody() + .get("name", String.class); + + MemberResponse body = new MemberResponse(null, name, null); + return ResponseEntity.ok(body); + } + + private String extractTokenFromCookie(Cookie[] cookies) { + if (cookies == null || cookies.length == 0) { + return ""; + } + for (Cookie cookie : cookies) { + if ("token".equals(cookie.getName())) { + return cookie.getValue(); + } + } + return ""; + } + @PostMapping("/logout") public ResponseEntity logout(HttpServletResponse response) { Cookie cookie = new Cookie("token", ""); From 378e7c58c279fb538ea29b203f17402733a4f677 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 09:41:50 +0900 Subject: [PATCH 04/54] =?UTF-8?q?refactor:=20=ED=82=A4=20=EC=A0=9C?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=EA=B0=80=EB=8A=A5=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=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/member/MemberController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 051e0db67..fca57533f 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -16,6 +16,7 @@ @RestController public class MemberController { private MemberService memberService; + private final String secretKey = "temporary-secret-key"; public MemberController(MemberService memberService) { this.memberService = memberService; @@ -31,7 +32,6 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { public ResponseEntity login(@RequestBody MemberRequest memberRequest, HttpServletResponse response) { Member member = memberService.login(memberRequest.getEmail(), memberRequest.getPassword()); - String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; String accessToken = Jwts.builder() .setSubject(member.getId().toString()) .claim("name", member.getName()) @@ -51,7 +51,6 @@ public ResponseEntity login(@RequestBody MemberRequest memberRequest, HttpServle public ResponseEntity checkLogin(HttpServletRequest request) { String token = extractTokenFromCookie(request.getCookies()); - String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; String name = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) .build() From cfd15f67d53be91ddfc4a611c14059adb7776899 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 10:12:48 +0900 Subject: [PATCH 05/54] =?UTF-8?q?test:=20=EC=9D=B4=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=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 | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 6add784bd..80a0520fb 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -35,4 +35,38 @@ public class MissionStepTest { assertThat(token).isNotBlank(); } -} \ No newline at end of file + + @Test + void 이단계() { + String token = createToken("admin@email.com", "password"); // 일단계에서 토큰을 추출하는 로직을 메서드로 따로 만들어서 활용하세요. + + Map params = new HashMap<>(); + params.put("date", "2024-03-01"); + params.put("time", "1"); + params.put("theme", "1"); + + ExtractableResponse response = RestAssured.given().log().all() + .body(params) + .cookie("token", token) + .contentType(ContentType.JSON) + .post("/reservations") + .then().log().all() + .extract(); + + assertThat(response.statusCode()).isEqualTo(201); + assertThat(response.as(ReservationResponse.class).getName()).isEqualTo("어드민"); + + params.put("name", "브라운"); + + ExtractableResponse adminResponse = RestAssured.given().log().all() + .body(params) + .cookie("token", token) + .contentType(ContentType.JSON) + .post("/reservations") + .then().log().all() + .extract(); + + assertThat(adminResponse.statusCode()).isEqualTo(201); + assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("브라운"); + } +} From b456b89df9b9339ef841f686db2c3ed295cbb661 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 10:13:01 +0900 Subject: [PATCH 06/54] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=EB=A7=B4?= =?UTF-8?q?=EB=B2=84=20dto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/member/LoginMember.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/roomescape/member/LoginMember.java diff --git a/src/main/java/roomescape/member/LoginMember.java b/src/main/java/roomescape/member/LoginMember.java new file mode 100644 index 000000000..b4eeb64cc --- /dev/null +++ b/src/main/java/roomescape/member/LoginMember.java @@ -0,0 +1,33 @@ +package roomescape.member; + +public class LoginMember { + private Long id; + private String name; + private String email; + private String role; + + public LoginMember(Long id, String name, String email, String role) { + this.id = id; + this.name = name; + this.email = email; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + + public String getRole() { + return role; + } +} + + From 97cf2335177a30abf1a2622207f8d0a5fd4e2ec1 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 10:31:54 +0900 Subject: [PATCH 07/54] =?UTF-8?q?feat:=20resolver,config=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/main/java/roomescape/WebConfig.java | 20 ++++++ .../auth/LoginMemberArgumentResolver.java | 62 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/main/java/roomescape/WebConfig.java create mode 100644 src/main/java/roomescape/auth/LoginMemberArgumentResolver.java diff --git a/src/main/java/roomescape/WebConfig.java b/src/main/java/roomescape/WebConfig.java new file mode 100644 index 000000000..c8ad77f81 --- /dev/null +++ b/src/main/java/roomescape/WebConfig.java @@ -0,0 +1,20 @@ +package roomescape; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import roomescape.auth.LoginMemberArgumentResolver; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + private final String secretKey = "temporary-secret-key"; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new LoginMemberArgumentResolver(secretKey)); + } +} + + diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java new file mode 100644 index 000000000..bb66c0138 --- /dev/null +++ b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java @@ -0,0 +1,62 @@ +package roomescape.auth; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import roomescape.member.LoginMember; + +public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { + + private final String secretKey; + + public LoginMemberArgumentResolver(String secretKey) { + this.secretKey = secretKey; + } + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().equals(LoginMember.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + String token = extractTokenFromCookie(request.getCookies()); + + if (token.isEmpty()) { + return null; + } + + var claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody(); + + Long id = Long.valueOf(claims.getSubject()); + String name = claims.get("name", String.class); + String role = claims.get("role", String.class); + + return new LoginMember(id, name, null, role); + } + + private String extractTokenFromCookie(Cookie[] cookies) { + if (cookies == null || cookies.length == 0) { + return ""; + } + for (Cookie cookie : cookies) { + if ("token".equals(cookie.getName())) { + return cookie.getValue(); + } + } + return ""; + } +} + + From 451beff3c0471a70a6412fa004290e2136b6d2a7 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 10:32:17 +0900 Subject: [PATCH 08/54] =?UTF-8?q?refactor:=20reservationRequest=EC=97=90?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/reservation/ReservationRequest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index 19f441246..c41db6eca 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -6,6 +6,16 @@ public class ReservationRequest { private Long theme; private Long time; + public ReservationRequest() { + } + + public ReservationRequest(String name, String date, Long theme, Long time) { + this.name = name; + this.date = date; + this.theme = theme; + this.time = time; + } + public String getName() { return name; } From f1e328804d6ffc4ba053675d824e081acdf9e8f0 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 10:45:19 +0900 Subject: [PATCH 09/54] =?UTF-8?q?=20refactor:=20createToken=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/member/MemberController.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index fca57533f..33aefc6fa 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -32,12 +32,7 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { public ResponseEntity login(@RequestBody MemberRequest memberRequest, HttpServletResponse response) { Member member = memberService.login(memberRequest.getEmail(), memberRequest.getPassword()); - String accessToken = Jwts.builder() - .setSubject(member.getId().toString()) - .claim("name", member.getName()) - .claim("role", member.getRole()) - .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) - .compact(); + String accessToken = createToken(member); Cookie cookie = new Cookie("token", accessToken); cookie.setHttpOnly(true); @@ -62,6 +57,15 @@ public ResponseEntity checkLogin(HttpServletRequest request) { return ResponseEntity.ok(body); } + private String createToken(Member member) { + return Jwts.builder() + .setSubject(member.getId().toString()) + .claim("name", member.getName()) + .claim("role", member.getRole()) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) + .compact(); + } + private String extractTokenFromCookie(Cookie[] cookies) { if (cookies == null || cookies.length == 0) { return ""; From 0d5c991797d5b10b928aeb4d78e8e522f0b3f11f Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 11:12:27 +0900 Subject: [PATCH 10/54] =?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=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/WebConfig.java | 2 +- src/main/java/roomescape/member/MemberController.java | 11 +++++++++-- src/test/java/roomescape/MissionStepTest.java | 8 +++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/roomescape/WebConfig.java b/src/main/java/roomescape/WebConfig.java index c8ad77f81..8f803c497 100644 --- a/src/main/java/roomescape/WebConfig.java +++ b/src/main/java/roomescape/WebConfig.java @@ -9,7 +9,7 @@ @Configuration public class WebConfig implements WebMvcConfigurer { - private final String secretKey = "temporary-secret-key"; + private final String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; @Override public void addArgumentResolvers(List resolvers) { diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 33aefc6fa..af7fd3992 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -16,7 +16,7 @@ @RestController public class MemberController { private MemberService memberService; - private final String secretKey = "temporary-secret-key"; + private final String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; public MemberController(MemberService memberService) { this.memberService = memberService; @@ -57,7 +57,7 @@ public ResponseEntity checkLogin(HttpServletRequest request) { return ResponseEntity.ok(body); } - private String createToken(Member member) { + public String createToken(Member member) { return Jwts.builder() .setSubject(member.getId().toString()) .claim("name", member.getName()) @@ -65,6 +65,13 @@ private String createToken(Member member) { .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) .compact(); } + + + public String createTokenFromEmailAndPassword(String email, String password) { + Member member = memberService.login(email, password); + return createToken(member); + } + private String extractTokenFromCookie(Cookie[] cookies) { if (cookies == null || cookies.length == 0) { diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 80a0520fb..5ca2a2ee4 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -7,6 +7,9 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.beans.factory.annotation.Autowired; +import roomescape.member.MemberController; +import roomescape.reservation.ReservationResponse; import java.util.HashMap; import java.util.Map; @@ -17,6 +20,9 @@ @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) public class MissionStepTest { + @Autowired + private MemberController memberController; + @Test void 일단계() { Map params = new HashMap<>(); @@ -38,7 +44,7 @@ public class MissionStepTest { @Test void 이단계() { - String token = createToken("admin@email.com", "password"); // 일단계에서 토큰을 추출하는 로직을 메서드로 따로 만들어서 활용하세요. + String token = memberController.createTokenFromEmailAndPassword("admin@email.com", "password"); Map params = new HashMap<>(); params.put("date", "2024-03-01"); From f6a7db0580b7d01416122142d539811f6d1f5c9c Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 11:12:57 +0900 Subject: [PATCH 11/54] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=8C=80=EB=A1=9C=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservation/ReservationController.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index b3bef3990..5edb16124 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -7,6 +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.member.LoginMember; import java.net.URI; import java.util.List; @@ -26,14 +27,22 @@ public List list() { } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest) { - if (reservationRequest.getName() == null - || reservationRequest.getDate() == null + public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, LoginMember member) { + if (reservationRequest.getDate() == null || reservationRequest.getTheme() == null || reservationRequest.getTime() == null) { return ResponseEntity.badRequest().build(); } - ReservationResponse reservation = reservationService.save(reservationRequest); + + String effectiveName = reservationRequest.getName() != null ? reservationRequest.getName() : member.getName(); + ReservationRequest requestWithName = new ReservationRequest( + effectiveName, + reservationRequest.getDate(), + reservationRequest.getTheme(), + reservationRequest.getTime() + ); + + ReservationResponse reservation = reservationService.save(requestWithName); return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); } From 20d75260cafe466f75170bd37c70adf0a82ae3eb Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 11:20:47 +0900 Subject: [PATCH 12/54] =?UTF-8?q?test:=20=EC=82=BC=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=9E=91?= =?UTF-8?q?=EC=84=B1?= 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 5ca2a2ee4..25a655e0b 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -75,4 +75,23 @@ public class MissionStepTest { assertThat(adminResponse.statusCode()).isEqualTo(201); assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("브라운"); } + + @Test + void 삼단계() { + String brownToken = memberController.createTokenFromEmailAndPassword("brown@email.com", "password"); + + RestAssured.given().log().all() + .cookie("token", brownToken) + .get("/admin") + .then().log().all() + .statusCode(401); + + String adminToken = memberController.createTokenFromEmailAndPassword("admin@email.com", "password"); + + RestAssured.given().log().all() + .cookie("token", adminToken) + .get("/admin") + .then().log().all() + .statusCode(200); + } } From 1357d073354ab0f5e133a45c6e1f6ce38adad597 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Sat, 27 Dec 2025 11:37:10 +0900 Subject: [PATCH 13/54] =?UTF-8?q?feat:=20interceptor=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/WebConfig.java | 8 +++ .../roomescape/auth/AdminAuthInterceptor.java | 58 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/java/roomescape/auth/AdminAuthInterceptor.java diff --git a/src/main/java/roomescape/WebConfig.java b/src/main/java/roomescape/WebConfig.java index 8f803c497..75c8261e1 100644 --- a/src/main/java/roomescape/WebConfig.java +++ b/src/main/java/roomescape/WebConfig.java @@ -2,7 +2,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import roomescape.auth.AdminAuthInterceptor; import roomescape.auth.LoginMemberArgumentResolver; import java.util.List; @@ -15,6 +17,12 @@ public class WebConfig implements WebMvcConfigurer { public void addArgumentResolvers(List resolvers) { resolvers.add(new LoginMemberArgumentResolver(secretKey)); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new AdminAuthInterceptor(secretKey)) + .addPathPatterns("/admin", "/admin/**"); + } } diff --git a/src/main/java/roomescape/auth/AdminAuthInterceptor.java b/src/main/java/roomescape/auth/AdminAuthInterceptor.java new file mode 100644 index 000000000..07415db60 --- /dev/null +++ b/src/main/java/roomescape/auth/AdminAuthInterceptor.java @@ -0,0 +1,58 @@ +package roomescape.auth; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.servlet.HandlerInterceptor; + +public class AdminAuthInterceptor implements HandlerInterceptor { + + private final String secretKey; + + public AdminAuthInterceptor(String secretKey) { + this.secretKey = secretKey; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String token = extractTokenFromCookie(request.getCookies()); + if (token.isEmpty()) { + response.setStatus(401); + return false; + } + + try { + var claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody(); + + String role = claims.get("role", String.class); + if (!"ADMIN".equals(role)) { + response.setStatus(401); + return false; + } + return true; + } catch (Exception e) { + response.setStatus(401); + return false; + } + } + + private String extractTokenFromCookie(Cookie[] cookies) { + if (cookies == null || cookies.length == 0) { + return ""; + } + for (Cookie cookie : cookies) { + if ("token".equals(cookie.getName())) { + return cookie.getValue(); + } + } + return ""; + } +} + + From 12171bb34fd97aaf771d4208a571c463fddbdc1a Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 31 Dec 2025 19:58:50 +0900 Subject: [PATCH 14/54] =?UTF-8?q?refactor:=20auth=EC=9D=98=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/auth/AdminAuthInterceptor.java | 25 +++----------- .../auth/LoginMemberArgumentResolver.java | 28 +++------------- .../java/roomescape/util/auth/JwtUtil.java | 33 +++++++++++++++++++ 3 files changed, 42 insertions(+), 44 deletions(-) create mode 100644 src/main/java/roomescape/util/auth/JwtUtil.java diff --git a/src/main/java/roomescape/auth/AdminAuthInterceptor.java b/src/main/java/roomescape/auth/AdminAuthInterceptor.java index 07415db60..a26395239 100644 --- a/src/main/java/roomescape/auth/AdminAuthInterceptor.java +++ b/src/main/java/roomescape/auth/AdminAuthInterceptor.java @@ -1,11 +1,11 @@ package roomescape.auth; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.Claims; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; +import roomescape.util.JwtUtil; public class AdminAuthInterceptor implements HandlerInterceptor { @@ -17,19 +17,14 @@ public AdminAuthInterceptor(String secretKey) { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - String token = extractTokenFromCookie(request.getCookies()); + String token = JwtUtil.extractTokenFromCookies(request.getCookies()); if (token.isEmpty()) { response.setStatus(401); return false; } try { - var claims = Jwts.parserBuilder() - .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) - .build() - .parseClaimsJws(token) - .getBody(); - + Claims claims = JwtUtil.parseClaims(token, secretKey); String role = claims.get("role", String.class); if (!"ADMIN".equals(role)) { response.setStatus(401); @@ -41,18 +36,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return false; } } - - private String extractTokenFromCookie(Cookie[] cookies) { - if (cookies == null || cookies.length == 0) { - return ""; - } - for (Cookie cookie : cookies) { - if ("token".equals(cookie.getName())) { - return cookie.getValue(); - } - } - return ""; - } } diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java index bb66c0138..a7ec378e3 100644 --- a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java +++ b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java @@ -1,7 +1,6 @@ package roomescape.auth; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.Claims; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; @@ -10,6 +9,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import roomescape.member.LoginMember; +import roomescape.util.JwtUtil; public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { @@ -27,17 +27,13 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); - String token = extractTokenFromCookie(request.getCookies()); + String token = JwtUtil.extractTokenFromCookies(request.getCookies()); if (token.isEmpty()) { return null; } - var claims = Jwts.parserBuilder() - .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) - .build() - .parseClaimsJws(token) - .getBody(); + Claims claims = JwtUtil.parseClaims(token, secretKey); Long id = Long.valueOf(claims.getSubject()); String name = claims.get("name", String.class); @@ -45,18 +41,4 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m return new LoginMember(id, name, null, role); } - - private String extractTokenFromCookie(Cookie[] cookies) { - if (cookies == null || cookies.length == 0) { - return ""; - } - for (Cookie cookie : cookies) { - if ("token".equals(cookie.getName())) { - return cookie.getValue(); - } - } - return ""; - } -} - - +} \ No newline at end of file diff --git a/src/main/java/roomescape/util/auth/JwtUtil.java b/src/main/java/roomescape/util/auth/JwtUtil.java new file mode 100644 index 000000000..05b16758d --- /dev/null +++ b/src/main/java/roomescape/util/auth/JwtUtil.java @@ -0,0 +1,33 @@ +package roomescape.util; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; + +public final class JwtUtil { + private JwtUtil() { + } + + public static String extractTokenFromCookies(Cookie[] cookies) { + if (cookies == null || cookies.length == 0) { + return ""; + } + for (Cookie cookie : cookies) { + if ("token".equals(cookie.getName())) { + return cookie.getValue(); + } + } + return ""; + } + + public static Claims parseClaims(String token, String secretKey) { + return Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody(); + } +} + + From 1eb651367a144d41be1a2b6eed05723a6a27e3e1 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 31 Dec 2025 20:27:19 +0900 Subject: [PATCH 15/54] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=8D=BC?= =?UTF-8?q?=ED=8B=B0=20=EC=A3=BC=EC=9E=85=EC=9C=BC=EB=A1=9C=20=EC=8B=9C?= =?UTF-8?q?=ED=81=AC=EB=A6=BF=20=ED=82=A4=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ src/main/java/roomescape/WebConfig.java | 4 +++- src/main/java/roomescape/member/MemberController.java | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c2065bc26..6f0ca134f 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ out/ ### VS Code ### .vscode/ + + +application-local.properties diff --git a/src/main/java/roomescape/WebConfig.java b/src/main/java/roomescape/WebConfig.java index 75c8261e1..00e16aca6 100644 --- a/src/main/java/roomescape/WebConfig.java +++ b/src/main/java/roomescape/WebConfig.java @@ -1,5 +1,6 @@ package roomescape; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; @@ -11,7 +12,8 @@ @Configuration public class WebConfig implements WebMvcConfigurer { - private final String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; + @Value("${roomescape.auth.jwt.secret}") + private String secretKey; @Override public void addArgumentResolvers(List resolvers) { diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index af7fd3992..a0b276030 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -5,6 +5,7 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -16,7 +17,8 @@ @RestController public class MemberController { private MemberService memberService; - private final String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; + @Value("${roomescape.auth.jwt.secret}") + private String secretKey; public MemberController(MemberService memberService) { this.memberService = memberService; From a108bc6dcd39494a632599989bdf3c7a3632b712 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 31 Dec 2025 22:08:50 +0900 Subject: [PATCH 16/54] =?UTF-8?q?refactor:=20application.properties=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/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a0f33bbab..612d9890e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,4 +8,4 @@ spring.datasource.url=jdbc:h2:mem:database #spring.jpa.ddl-auto=create-drop #spring.jpa.defer-datasource-initialization=true -#roomescape.auth.jwt.secret= Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= \ No newline at end of file +roomescape.auth.jwt.secret= Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= \ No newline at end of file From 069e91f18c091fce302909eb3bc2ba2d3eb04424 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 31 Dec 2025 22:09:08 +0900 Subject: [PATCH 17/54] =?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=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/test/java/roomescape/MissionStepTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 25a655e0b..9953a060c 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -6,8 +6,8 @@ import io.restassured.response.Response; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.jdbc.Sql; import roomescape.member.MemberController; import roomescape.reservation.ReservationResponse; @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) -@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +@Sql(scripts = "/sql/truncate.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) public class MissionStepTest { @Autowired From f7a7485f3756090e84463784166dc1b40fd75a95 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 31 Dec 2025 22:15:09 +0900 Subject: [PATCH 18/54] =?UTF-8?q?refactor:=20=EC=83=9D=EC=84=B1=EC=9E=90?= =?UTF-8?q?=20=EC=A3=BC=EC=9E=85=EC=9C=BC=EB=A1=9C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=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 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 9953a060c..7c6f906a2 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -6,7 +6,6 @@ import io.restassured.response.Response; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.jdbc.Sql; import roomescape.member.MemberController; import roomescape.reservation.ReservationResponse; @@ -20,8 +19,11 @@ @Sql(scripts = "/sql/truncate.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) public class MissionStepTest { - @Autowired - private MemberController memberController; + private final MemberController memberController; + + public MissionStepTest(MemberController memberController) { + this.memberController = memberController; + } @Test void 일단계() { From b4869c1d33890614fa630ab10a8d993770651bf1 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 31 Dec 2025 22:15:24 +0900 Subject: [PATCH 19/54] =?UTF-8?q?test:=20truncate=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B5=AC=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/sql/truncate.sql | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test/resources/sql/truncate.sql diff --git a/src/test/resources/sql/truncate.sql b/src/test/resources/sql/truncate.sql new file mode 100644 index 000000000..ae0bec068 --- /dev/null +++ b/src/test/resources/sql/truncate.sql @@ -0,0 +1,25 @@ +SET REFERENTIAL_INTEGRITY FALSE; +TRUNCATE TABLE reservation; +TRUNCATE TABLE member; +TRUNCATE TABLE theme; +TRUNCATE TABLE time; +SET REFERENTIAL_INTEGRITY TRUE; + +INSERT INTO member (name, email, password, role) +VALUES ('어드민', 'admin@email.com', 'password', 'ADMIN'), + ('브라운', 'brown@email.com', 'password', 'USER'); + +INSERT INTO theme (name, description) +VALUES ('테마1', '테마1입니다.'), + ('테마2', '테마2입니다.'), + ('테마3', '테마3입니다.'); + +INSERT INTO time (time_value) +VALUES ('10:00'), + ('12:00'), + ('14:00'), + ('16:00'), + ('18:00'), + ('20:00'); + + From 97e01060cc14584d360fd5d3da3af5e6b78eb613 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 31 Dec 2025 22:31:43 +0900 Subject: [PATCH 20/54] =?UTF-8?q?refactor:=20=EA=B2=80=EC=A6=9D=EC=9D=84?= =?UTF-8?q?=20requestDto=EB=8B=A8=EC=97=90=EC=84=9C=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=ED=95=98=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 --- build.gradle | 1 + .../roomescape/reservation/ReservationController.java | 8 ++------ .../java/roomescape/reservation/ReservationRequest.java | 5 +++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 8d52aebc6..1d884b864 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0' diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index 5edb16124..c025f5797 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -1,6 +1,7 @@ package roomescape.reservation; import org.springframework.http.ResponseEntity; +import jakarta.validation.Valid; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -27,12 +28,7 @@ public List list() { } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, LoginMember member) { - if (reservationRequest.getDate() == null - || reservationRequest.getTheme() == null - || reservationRequest.getTime() == null) { - return ResponseEntity.badRequest().build(); - } + public ResponseEntity create(@RequestBody @Valid ReservationRequest reservationRequest, LoginMember member) { String effectiveName = reservationRequest.getName() != null ? reservationRequest.getName() : member.getName(); ReservationRequest requestWithName = new ReservationRequest( diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index c41db6eca..150a2d708 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -1,9 +1,14 @@ package roomescape.reservation; +import jakarta.validation.constraints.NotNull; + public class ReservationRequest { private String name; + @NotNull private String date; + @NotNull private Long theme; + @NotNull private Long time; public ReservationRequest() { From 4b90a361702675f187a27af8f9092c3281178a70 Mon Sep 17 00:00:00 2001 From: mintcoke123 <115970383+mintcoke123@users.noreply.github.com> Date: Wed, 31 Dec 2025 23:46:04 +0900 Subject: [PATCH 21/54] =?UTF-8?q?refactor:=20=EC=96=B4=EB=93=9C=EB=AF=BC?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservation/ReservationController.java | 27 ++++- .../roomescape/theme/ThemeController.java | 17 +++ .../java/roomescape/time/TimeController.java | 20 ++++ src/main/resources/static/js/reservation.js | 6 +- src/main/resources/static/js/theme.js | 2 +- src/main/resources/static/js/time.js | 104 +++++++++--------- 6 files changed, 118 insertions(+), 58 deletions(-) diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index c025f5797..41ee6938b 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -27,10 +27,20 @@ public List list() { return reservationService.findAll(); } + @GetMapping("/admin/reservations") + public List adminList() { + return reservationService.findAll(); + } + @PostMapping("/reservations") public ResponseEntity create(@RequestBody @Valid ReservationRequest reservationRequest, LoginMember member) { - String effectiveName = reservationRequest.getName() != null ? reservationRequest.getName() : member.getName(); + String effectiveName = reservationRequest.getName() != null && !reservationRequest.getName().isBlank() + ? reservationRequest.getName() + : (member != null ? member.getName() : null); + if (effectiveName == null || effectiveName.isBlank()) { + return ResponseEntity.badRequest().build(); + } ReservationRequest requestWithName = new ReservationRequest( effectiveName, reservationRequest.getDate(), @@ -43,9 +53,24 @@ public ResponseEntity create(@RequestBody @Valid ReservationRequest reservationR return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); } + @PostMapping("/admin/reservations") + public ResponseEntity adminCreate(@RequestBody @Valid ReservationRequest reservationRequest) { + if (reservationRequest.getName() == null || reservationRequest.getName().isBlank()) { + return ResponseEntity.badRequest().build(); + } + ReservationResponse reservation = reservationService.save(reservationRequest); + return ResponseEntity.created(URI.create("/admin/reservations/" + reservation.getId())).body(reservation); + } + @DeleteMapping("/reservations/{id}") public ResponseEntity delete(@PathVariable Long id) { reservationService.deleteById(id); return ResponseEntity.noContent().build(); } + + @DeleteMapping("/admin/reservations/{id}") + public ResponseEntity adminDelete(@PathVariable Long id) { + reservationService.deleteById(id); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/roomescape/theme/ThemeController.java b/src/main/java/roomescape/theme/ThemeController.java index 03bca41a6..1bf2811b3 100644 --- a/src/main/java/roomescape/theme/ThemeController.java +++ b/src/main/java/roomescape/theme/ThemeController.java @@ -25,14 +25,31 @@ public ResponseEntity createTheme(@RequestBody Theme theme) { return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())).body(newTheme); } + @PostMapping("/admin/themes") + public ResponseEntity adminCreateTheme(@RequestBody Theme theme) { + Theme newTheme = themeDao.save(theme); + return ResponseEntity.created(URI.create("/admin/themes/" + newTheme.getId())).body(newTheme); + } + @GetMapping("/themes") public ResponseEntity> list() { return ResponseEntity.ok(themeDao.findAll()); } + @GetMapping("/admin/themes") + public ResponseEntity> adminList() { + return ResponseEntity.ok(themeDao.findAll()); + } + @DeleteMapping("/themes/{id}") public ResponseEntity deleteTheme(@PathVariable Long id) { themeDao.deleteById(id); return ResponseEntity.noContent().build(); } + + @DeleteMapping("/admin/themes/{id}") + public ResponseEntity adminDeleteTheme(@PathVariable Long id) { + themeDao.deleteById(id); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/roomescape/time/TimeController.java b/src/main/java/roomescape/time/TimeController.java index 2343114d1..04accfc07 100644 --- a/src/main/java/roomescape/time/TimeController.java +++ b/src/main/java/roomescape/time/TimeController.java @@ -25,6 +25,11 @@ public List