From eac669202adc673db111a579cf3f0aabff07368f Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Wed, 25 Dec 2024 01:59:09 +0900 Subject: [PATCH 01/30] =?UTF-8?q?[FEAT]=20=EB=A6=AC=EB=93=9C=EB=AF=B8=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 --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..c9355301 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# ๐ŸŒ€ Spring MVC (์ธ์ฆ) + +## ๋กœ๊ทธ์ธ +#### ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ + + ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ +#### ๋กœ๊ทธ์ธ ์š”์ฒญ + + ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ -> ๋ฉค๋ฒ„ ์กฐํšŒ + + ์กฐํšŒํ•œ ๋ฉค๋ฒ„๋กœ ํ† ํฐ ๋ฐœ๊ธ‰ + + Cookie ๋ฅผ ๋งŒ๋“ค์–ด ์‘๋‹ต +#### ์ธ์ฆ ์ •๋ณด ์กฐํšŒ + + Cookie -> ํ† ํฐ ์ •๋ณด ์ถ”์ถœ + + ๋ฉค๋ฒ„๋ฅผ ์ฐพ์•„์„œ ์‘๋‹ต + +๋กœ๊ทธ์ธ ๊ด€๋ จ API ++ GET/login : ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ ++ POST/login : ๋กœ๊ทธ์ธ ์š”์ฒญ ++ GET/login/check : ์ธ์ฆ ์ •๋ณด ์กฐํšŒ + From e1b4ce3178ea0c831ef085b1c6ae77113ff6f299 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 26 Dec 2024 01:22:26 +0900 Subject: [PATCH 02/30] =?UTF-8?q?[FEAT]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?(1=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++ src/main/java/roomescape/auth/JWTUtils.java | 36 +++++++++++++++++++ src/main/java/roomescape/auth/UserClaims.java | 6 ++++ src/main/java/roomescape/auth/UserToken.java | 6 ++++ .../java/roomescape/member/LoginRequest.java | 7 ++++ .../roomescape/member/MemberController.java | 18 ++++++++++ .../java/roomescape/member/MemberService.java | 34 +++++++++++++++--- src/main/resources/application.properties | 2 +- src/test/java/roomescape/MissionStepTest.java | 13 +++++-- 9 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 src/main/java/roomescape/auth/JWTUtils.java create mode 100644 src/main/java/roomescape/auth/UserClaims.java create mode 100644 src/main/java/roomescape/auth/UserToken.java create mode 100644 src/main/java/roomescape/member/LoginRequest.java diff --git a/build.gradle b/build.gradle index 8d52aebc..21a9445b 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,11 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-gson:0.11.2' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' diff --git a/src/main/java/roomescape/auth/JWTUtils.java b/src/main/java/roomescape/auth/JWTUtils.java new file mode 100644 index 00000000..9f710ab5 --- /dev/null +++ b/src/main/java/roomescape/auth/JWTUtils.java @@ -0,0 +1,36 @@ +package roomescape.auth; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import roomescape.member.Member; + +@Component +public class JWTUtils { + + @Value("${roomescape.auth.jwt.secret}") + private String secretKey; + + public UserToken createToken(Member member){ + String accessToken = Jwts.builder() + .setSubject(member.getId().toString()) + .claim("name", member.getName()) + .claim("role", member.getRole()) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) + .compact(); + + return new UserToken(accessToken); + } + + public UserClaims getClaimsFromToken(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody(); + + return new UserClaims(claims.get("name", String.class)); + } +} diff --git a/src/main/java/roomescape/auth/UserClaims.java b/src/main/java/roomescape/auth/UserClaims.java new file mode 100644 index 00000000..c2070c65 --- /dev/null +++ b/src/main/java/roomescape/auth/UserClaims.java @@ -0,0 +1,6 @@ +package roomescape.auth; + +public record UserClaims( + String name +) { +} diff --git a/src/main/java/roomescape/auth/UserToken.java b/src/main/java/roomescape/auth/UserToken.java new file mode 100644 index 00000000..ce2beb5b --- /dev/null +++ b/src/main/java/roomescape/auth/UserToken.java @@ -0,0 +1,6 @@ +package roomescape.auth; + +public record UserToken( + String token +) { +} diff --git a/src/main/java/roomescape/member/LoginRequest.java b/src/main/java/roomescape/member/LoginRequest.java new file mode 100644 index 00000000..fe3676fd --- /dev/null +++ b/src/main/java/roomescape/member/LoginRequest.java @@ -0,0 +1,7 @@ +package roomescape.member; + +public record LoginRequest( + String email, + String password +) { +} diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 881ae5e0..19dee8e6 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -1,13 +1,17 @@ package roomescape.member; import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import roomescape.auth.UserClaims; +import roomescape.auth.UserToken; import java.net.URI; @@ -25,6 +29,20 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); } + @PostMapping("/login") + public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse response){ + String token = memberService.getToken(loginRequest).token(); + Cookie cookie = new Cookie("token", token); + cookie.setHttpOnly(true); + cookie.setPath("/"); + response.addCookie(cookie); + } + + @GetMapping("/login/check") + public UserClaims checkLogin(@CookieValue("token") String token){ + return memberService.checkLogin(new UserToken(token)); + } + @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 ccaa8cba..089b6784 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -1,17 +1,41 @@ package roomescape.member; +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import roomescape.auth.JWTUtils; +import roomescape.auth.UserClaims; +import roomescape.auth.UserToken; @Service +@RequiredArgsConstructor public class MemberService { - private MemberDao memberDao; - - public MemberService(MemberDao memberDao) { - this.memberDao = memberDao; - } + private final MemberDao memberDao; + private final JWTUtils jwtUtils; 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 UserToken getToken(LoginRequest loginRequest) { + try { + Member member = memberDao.findByEmailAndPassword(loginRequest.email(), loginRequest.password()); + return jwtUtils.createToken(member); + } catch (EmptyResultDataAccessException e) { + throw new IllegalArgumentException("๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); + } + } + + public UserClaims checkLogin(UserToken userToken) { + try { + UserClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); + Member member = memberDao.findByName(userClaims.name()); + return new UserClaims(member.getName()); + } catch (EmptyResultDataAccessException exception) { + throw new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."); + } + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a0f33bba..0e21f4ec 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= diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 6add784b..510711ae 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -32,7 +32,16 @@ public class MissionStepTest { .extract(); String token = response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; - assertThat(token).isNotBlank(); + + ExtractableResponse checkResponse = RestAssured.given().log().all() + .contentType(ContentType.JSON) + .cookie("token", token) + .when().get("/login/check") + .then().log().all() + .statusCode(200) + .extract(); + + assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("์–ด๋“œ๋ฏผ"); } -} \ No newline at end of file +} From 0186fcf56814ca90dc6feb26e5bf2f9dc233f9d2 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 26 Dec 2024 17:27:26 +0900 Subject: [PATCH 03/30] =?UTF-8?q?[FEAT]=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(2=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++- src/test/java/roomescape/MissionStepTest.java | 60 +++++++++++++++---- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c9355301..c5c53b12 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ #### ๋กœ๊ทธ์ธ ์š”์ฒญ + ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ -> ๋ฉค๋ฒ„ ์กฐํšŒ + ์กฐํšŒํ•œ ๋ฉค๋ฒ„๋กœ ํ† ํฐ ๋ฐœ๊ธ‰ - + Cookie ๋ฅผ ๋งŒ๋“ค์–ด ์‘๋‹ต + + Cookie๋ฅผ ๋งŒ๋“ค์–ด ์‘๋‹ต #### ์ธ์ฆ ์ •๋ณด ์กฐํšŒ + Cookie -> ํ† ํฐ ์ •๋ณด ์ถ”์ถœ + ๋ฉค๋ฒ„๋ฅผ ์ฐพ์•„์„œ ์‘๋‹ต @@ -16,3 +16,11 @@ + POST/login : ๋กœ๊ทธ์ธ ์š”์ฒญ + GET/login/check : ์ธ์ฆ ์ •๋ณด ์กฐํšŒ +## ๋กœ๊ทธ์ธ ๋ฆฌํŒฉํ„ฐ๋ง +#### HandlerMethodArgumentResolver ++ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž๋™ ์ฃผ์ž… + +## ์˜ˆ์•ฝ ์ƒ์„ฑ ๊ธฐ๋Šฅ ๋ณ€๊ฒฝ ++ ์˜ˆ์•ฝ : ReservationRequest(์š”์ฒญ DTO) + -> name์ด ์žˆ์œผ๋ฉด name์œผ๋กœ Member ์ฐพ๊ธฐ + -> name์ด ์—†์œผ๋ฉด Cookie์— ๋‹ด๊ธด ์ •๋ณด ํ™œ์šฉ diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 510711ae..4288e25b 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import roomescape.reservation.ReservationResponse; import java.util.HashMap; import java.util.Map; @@ -19,29 +20,68 @@ public class MissionStepTest { @Test void ์ผ๋‹จ๊ณ„() { + String token = createToken("admin@email.com", "password"); + assertThat(token).isNotBlank(); + + ExtractableResponse checkResponse = RestAssured.given().log().all() + .contentType(ContentType.JSON) + .cookie("token", token) + .when().get("/login/check") + .then().log().all() + .statusCode(200) + .extract(); + + assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("์–ด๋“œ๋ฏผ"); + } + + @Test + void ์ด๋‹จ๊ณ„() { + String token = createToken("admin@email.com", "password"); + Map params = new HashMap<>(); - params.put("email", "admin@email.com"); - params.put("password", "password"); + 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) - .when().post("/login") + .cookie("token", token) + .contentType(ContentType.JSON) + .post("/reservations") .then().log().all() - .statusCode(200) .extract(); - String token = response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; - assertThat(token).isNotBlank(); + assertThat(adminResponse.statusCode()).isEqualTo(201); + assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("๋ธŒ๋ผ์šด"); + } - ExtractableResponse checkResponse = RestAssured.given().log().all() + private String createToken(String email, String password) { + Map params = new HashMap<>(); + params.put("email", email); + params.put("password", password); + + ExtractableResponse response = RestAssured.given().log().all() .contentType(ContentType.JSON) - .cookie("token", token) - .when().get("/login/check") + .body(params) + .when().post("/login") .then().log().all() .statusCode(200) .extract(); - assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("์–ด๋“œ๋ฏผ"); + String token = response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; + return token; } } From 769b6bf0d2f7c49af5c58c4c7d1a159c9307120a Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:27:20 +0900 Subject: [PATCH 04/30] =?UTF-8?q?[FEAT]=20ArgumentResolver=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20+=20=EC=98=88=EC=95=BD=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20(2=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/auth/JWTUtils.java | 2 +- .../auth/LoginMemberArgumentResolver.java | 45 +++++++++++++++++++ src/main/java/roomescape/auth/UserClaims.java | 3 +- src/main/java/roomescape/auth/WebConfig.java | 22 +++++++++ .../java/roomescape/member/MemberService.java | 6 +-- .../reservation/ReservationController.java | 29 +++++++----- .../reservation/ReservationDao.java | 16 +++---- .../reservation/ReservationRequest.java | 27 +++-------- .../reservation/ReservationResponse.java | 41 +++-------------- .../reservation/ReservationService.java | 2 +- src/test/java/roomescape/MissionStepTest.java | 4 +- 11 files changed, 114 insertions(+), 83 deletions(-) create mode 100644 src/main/java/roomescape/auth/LoginMemberArgumentResolver.java create mode 100644 src/main/java/roomescape/auth/WebConfig.java diff --git a/src/main/java/roomescape/auth/JWTUtils.java b/src/main/java/roomescape/auth/JWTUtils.java index 9f710ab5..4a7ed24e 100644 --- a/src/main/java/roomescape/auth/JWTUtils.java +++ b/src/main/java/roomescape/auth/JWTUtils.java @@ -31,6 +31,6 @@ public UserClaims getClaimsFromToken(String token) { .parseClaimsJws(token) .getBody(); - return new UserClaims(claims.get("name", String.class)); + return new UserClaims(claims.get("name", String.class),claims.get("role", String.class)); } } diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java new file mode 100644 index 00000000..163f4443 --- /dev/null +++ b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java @@ -0,0 +1,45 @@ +package roomescape.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import roomescape.member.MemberService; + +import java.util.Arrays; + +@Component +@RequiredArgsConstructor +public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { + + private final MemberService memberService; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().equals(UserClaims.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest(); + String token = extractToken(request); + + return memberService.checkLogin(new UserToken(token)); + } + + private String extractToken(HttpServletRequest request) { + return Arrays.stream(request.getCookies()) + .filter(cookie -> "token".equals(cookie.getName())) + .findFirst() + .map(Cookie::getValue) + .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + } + +} + diff --git a/src/main/java/roomescape/auth/UserClaims.java b/src/main/java/roomescape/auth/UserClaims.java index c2070c65..81270ee1 100644 --- a/src/main/java/roomescape/auth/UserClaims.java +++ b/src/main/java/roomescape/auth/UserClaims.java @@ -1,6 +1,7 @@ package roomescape.auth; public record UserClaims( - String name + String name, + String role ) { } diff --git a/src/main/java/roomescape/auth/WebConfig.java b/src/main/java/roomescape/auth/WebConfig.java new file mode 100644 index 00000000..8ee3d4fa --- /dev/null +++ b/src/main/java/roomescape/auth/WebConfig.java @@ -0,0 +1,22 @@ +package roomescape.auth; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private final LoginMemberArgumentResolver loginMemberArgumentResolver; + + public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) { + this.loginMemberArgumentResolver = loginMemberArgumentResolver; + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(loginMemberArgumentResolver); + } +} diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 089b6784..5270c51a 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -1,9 +1,7 @@ package roomescape.member; import lombok.RequiredArgsConstructor; -import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import roomescape.auth.JWTUtils; import roomescape.auth.UserClaims; @@ -32,8 +30,8 @@ public UserToken getToken(LoginRequest loginRequest) { public UserClaims checkLogin(UserToken userToken) { try { UserClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); - Member member = memberDao.findByName(userClaims.name()); - return new UserClaims(member.getName()); + memberDao.findByName(userClaims.name()); + return userClaims; } catch (EmptyResultDataAccessException exception) { throw new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."); } diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index b3bef399..ca4e87b1 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -1,5 +1,6 @@ package roomescape.reservation; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -7,35 +8,41 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import roomescape.auth.UserClaims; import java.net.URI; import java.util.List; @RestController +@RequiredArgsConstructor public class ReservationController { private final ReservationService reservationService; - public ReservationController(ReservationService reservationService) { - this.reservationService = reservationService; - } - @GetMapping("/reservations") public List list() { return reservationService.findAll(); } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest) { - if (reservationRequest.getName() == null - || reservationRequest.getDate() == null - || reservationRequest.getTheme() == null - || reservationRequest.getTime() == null) { + public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, UserClaims userClaims) { + if (reservationRequest.date() == null + || reservationRequest.theme() == null + || reservationRequest.time() == null) { return ResponseEntity.badRequest().build(); } - ReservationResponse reservation = reservationService.save(reservationRequest); - return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); + if (reservationRequest.name() == null) { + reservationRequest = new ReservationRequest( + userClaims.name(), + reservationRequest.date(), + reservationRequest.theme(), + reservationRequest.time() + ); + } + + ReservationResponse reservation = reservationService.save(reservationRequest); + return ResponseEntity.created(URI.create("/reservations/" + reservation.id())).body(reservation); } @DeleteMapping("/reservations/{id}") diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java index a4972430..d75201e3 100644 --- a/src/main/java/roomescape/reservation/ReservationDao.java +++ b/src/main/java/roomescape/reservation/ReservationDao.java @@ -47,25 +47,25 @@ public Reservation save(ReservationRequest reservationRequest) { KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(connection -> { PreparedStatement ps = connection.prepareStatement("INSERT INTO reservation(date, name, theme_id, time_id) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, reservationRequest.getDate()); - ps.setString(2, reservationRequest.getName()); - ps.setLong(3, reservationRequest.getTheme()); - ps.setLong(4, reservationRequest.getTime()); + ps.setString(1, reservationRequest.date()); + ps.setString(2, reservationRequest.name()); + ps.setLong(3, reservationRequest.theme()); + ps.setLong(4, reservationRequest.time()); return ps; }, keyHolder); Time time = jdbcTemplate.queryForObject("SELECT * FROM time WHERE id = ?", (rs, rowNum) -> new Time(rs.getLong("id"), rs.getString("time_value")), - reservationRequest.getTime()); + reservationRequest.time()); Theme theme = jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", (rs, rowNum) -> new Theme(rs.getLong("id"), rs.getString("name"), rs.getString("description")), - reservationRequest.getTheme()); + reservationRequest.theme()); return new Reservation( keyHolder.getKey().longValue(), - reservationRequest.getName(), - reservationRequest.getDate(), + reservationRequest.name(), + reservationRequest.date(), time, theme ); diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index 19f44124..ed0d50b0 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -1,24 +1,9 @@ package roomescape.reservation; -public class ReservationRequest { - private String name; - private String date; - private Long theme; - private Long time; - - public String getName() { - return name; - } - - public String getDate() { - return date; - } - - public Long getTheme() { - return theme; - } - - public Long getTime() { - return time; - } +public record ReservationRequest ( + String name, + String date, + Long theme, + Long time +){ } diff --git a/src/main/java/roomescape/reservation/ReservationResponse.java b/src/main/java/roomescape/reservation/ReservationResponse.java index 41360a36..be7cb163 100644 --- a/src/main/java/roomescape/reservation/ReservationResponse.java +++ b/src/main/java/roomescape/reservation/ReservationResponse.java @@ -1,37 +1,10 @@ package roomescape.reservation; -public class ReservationResponse { - private Long id; - private String name; - private String theme; - private String date; - private String time; - - public ReservationResponse(Long id, String name, String theme, String date, String time) { - this.id = id; - this.name = name; - this.theme = theme; - this.date = date; - this.time = time; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getTheme() { - return theme; - } - - public String getDate() { - return date; - } - - public String getTime() { - return time; - } +public record ReservationResponse( + Long id, + String name, + String date, + String theme, + String time +){ } diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index bd331332..0350dc30 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -15,7 +15,7 @@ public ReservationService(ReservationDao reservationDao) { public ReservationResponse save(ReservationRequest reservationRequest) { Reservation reservation = reservationDao.save(reservationRequest); - return new ReservationResponse(reservation.getId(), reservationRequest.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + return new ReservationResponse(reservation.getId(), reservationRequest.name(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); } public void deleteById(Long id) { diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 4288e25b..98eebea1 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -52,7 +52,7 @@ public class MissionStepTest { .extract(); assertThat(response.statusCode()).isEqualTo(201); - assertThat(response.as(ReservationResponse.class).getName()).isEqualTo("์–ด๋“œ๋ฏผ"); + assertThat(response.as(ReservationResponse.class).name()).isEqualTo("์–ด๋“œ๋ฏผ"); params.put("name", "๋ธŒ๋ผ์šด"); @@ -65,7 +65,7 @@ public class MissionStepTest { .extract(); assertThat(adminResponse.statusCode()).isEqualTo(201); - assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("๋ธŒ๋ผ์šด"); + assertThat(adminResponse.as(ReservationResponse.class).name()).isEqualTo("๋ธŒ๋ผ์šด"); } private String createToken(String email, String password) { From 9b180fb325e7b373c8e5aeefcbeb579e71680d99 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:34:54 +0900 Subject: [PATCH 05/30] =?UTF-8?q?[FEAT]=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(3=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +++++++++++ src/test/java/roomescape/MissionStepTest.java | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/README.md b/README.md index c5c53b12..eef270e3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # ๐ŸŒ€ Spring MVC (์ธ์ฆ) +# 1๋‹จ๊ณ„ +___ ## ๋กœ๊ทธ์ธ #### ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ + ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ @@ -16,6 +18,8 @@ + POST/login : ๋กœ๊ทธ์ธ ์š”์ฒญ + GET/login/check : ์ธ์ฆ ์ •๋ณด ์กฐํšŒ +# 2๋‹จ๊ณ„ +___ ## ๋กœ๊ทธ์ธ ๋ฆฌํŒฉํ„ฐ๋ง #### HandlerMethodArgumentResolver + ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž๋™ ์ฃผ์ž… @@ -24,3 +28,10 @@ + ์˜ˆ์•ฝ : ReservationRequest(์š”์ฒญ DTO) -> name์ด ์žˆ์œผ๋ฉด name์œผ๋กœ Member ์ฐพ๊ธฐ -> name์ด ์—†์œผ๋ฉด Cookie์— ๋‹ด๊ธด ์ •๋ณด ํ™œ์šฉ + +# 3๋‹จ๊ณ„ +___ +## ๊ด€๋ฆฌ์ž ๊ธฐ๋Šฅ ++ admin ํŽ˜์ด์ง€ ์ง„์ž… (HandlerInterceptor ์ด์šฉ) + -> ๊ด€๋ฆฌ์ž : ์ง„์ž… ๊ฐ€๋Šฅ + -> ๊ด€๋ฆฌ์ž X : 401 ์ฝ”๋“œ ์‘๋‹ต diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 98eebea1..d2444c6c 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -68,6 +68,25 @@ public class MissionStepTest { assertThat(adminResponse.as(ReservationResponse.class).name()).isEqualTo("๋ธŒ๋ผ์šด"); } + @Test + void ์‚ผ๋‹จ๊ณ„() { + String brownToken = createToken("brown@email.com", "password"); + + RestAssured.given().log().all() + .cookie("token", brownToken) + .get("/admin") + .then().log().all() + .statusCode(401); + + String adminToken = createToken("admin@email.com", "password"); + + RestAssured.given().log().all() + .cookie("token", adminToken) + .get("/admin") + .then().log().all() + .statusCode(200); + } + private String createToken(String email, String password) { Map params = new HashMap<>(); params.put("email", email); From 2ebef371f7eef15f2e7a88e39300e9f022e0f56e Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Fri, 27 Dec 2024 00:56:47 +0900 Subject: [PATCH 06/30] =?UTF-8?q?[FEAT]=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B6=8C=ED=95=9C=20(3=EB=8B=A8?= =?UTF-8?q?=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/LoginMemberArgumentResolver.java | 11 ++----- .../roomescape/auth/RoleCheckInterceptor.java | 33 +++++++++++++++++++ src/main/java/roomescape/auth/WebConfig.java | 13 +++++--- 3 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 src/main/java/roomescape/auth/RoleCheckInterceptor.java diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java index 163f4443..77d78d78 100644 --- a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java +++ b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java @@ -28,18 +28,13 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest(); - String token = extractToken(request); - - return memberService.checkLogin(new UserToken(token)); - } - - private String extractToken(HttpServletRequest request) { - return Arrays.stream(request.getCookies()) + String token = Arrays.stream(request.getCookies()) .filter(cookie -> "token".equals(cookie.getName())) .findFirst() .map(Cookie::getValue) .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - } + return memberService.checkLogin(new UserToken(token)); + } } diff --git a/src/main/java/roomescape/auth/RoleCheckInterceptor.java b/src/main/java/roomescape/auth/RoleCheckInterceptor.java new file mode 100644 index 00000000..5a75ccac --- /dev/null +++ b/src/main/java/roomescape/auth/RoleCheckInterceptor.java @@ -0,0 +1,33 @@ +package roomescape.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import roomescape.member.MemberService; + +import java.util.Arrays; + +@Component +@RequiredArgsConstructor +public class RoleCheckInterceptor implements HandlerInterceptor { + private final MemberService memberService; + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String token = Arrays.stream(request.getCookies()) + .filter(cookie -> "token".equals(cookie.getName())) + .findFirst() + .map(Cookie::getValue) + .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + + UserClaims userClaims = memberService.checkLogin(new UserToken(token)); + if (!userClaims.role().equals("ADMIN")) { + response.setStatus(401); + return false; + } + + return true; + } +} diff --git a/src/main/java/roomescape/auth/WebConfig.java b/src/main/java/roomescape/auth/WebConfig.java index 8ee3d4fa..09d76a1a 100644 --- a/src/main/java/roomescape/auth/WebConfig.java +++ b/src/main/java/roomescape/auth/WebConfig.java @@ -1,22 +1,27 @@ package roomescape.auth; +import lombok.RequiredArgsConstructor; 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 java.util.List; @Configuration +@RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { private final LoginMemberArgumentResolver loginMemberArgumentResolver; - - public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) { - this.loginMemberArgumentResolver = loginMemberArgumentResolver; - } + private final RoleCheckInterceptor roleCheckInterceptor; @Override public void addArgumentResolvers(List resolvers) { resolvers.add(loginMemberArgumentResolver); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(roleCheckInterceptor).addPathPatterns("/admin/**"); + } } From f9c1a7dcf57a908a602149f4c9bfa61cddb353d8 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:30:23 +0900 Subject: [PATCH 07/30] =?UTF-8?q?[REFACTOR]=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/{UserClaims.java => AuthClaims.java} | 2 +- ...r.java => AuthClaimsArgumentResolver.java} | 8 +++--- ...erceptor.java => AuthRoleInterceptor.java} | 7 ++--- .../auth/{UserToken.java => AuthToken.java} | 2 +- .../{WebConfig.java => AuthWebConfig.java} | 6 ++--- src/main/java/roomescape/auth/JWTUtils.java | 8 +++--- .../roomescape/member/MemberController.java | 22 +++++++-------- .../java/roomescape/member/MemberRequest.java | 21 +++++---------- .../roomescape/member/MemberResponse.java | 27 ++++--------------- .../java/roomescape/member/MemberService.java | 12 ++++----- .../reservation/ReservationController.java | 4 +-- 11 files changed, 45 insertions(+), 74 deletions(-) rename src/main/java/roomescape/auth/{UserClaims.java => AuthClaims.java} (73%) rename src/main/java/roomescape/auth/{LoginMemberArgumentResolver.java => AuthClaimsArgumentResolver.java} (80%) rename src/main/java/roomescape/auth/{RoleCheckInterceptor.java => AuthRoleInterceptor.java} (79%) rename src/main/java/roomescape/auth/{UserToken.java => AuthToken.java} (67%) rename src/main/java/roomescape/auth/{WebConfig.java => AuthWebConfig.java} (79%) diff --git a/src/main/java/roomescape/auth/UserClaims.java b/src/main/java/roomescape/auth/AuthClaims.java similarity index 73% rename from src/main/java/roomescape/auth/UserClaims.java rename to src/main/java/roomescape/auth/AuthClaims.java index 81270ee1..2c98324e 100644 --- a/src/main/java/roomescape/auth/UserClaims.java +++ b/src/main/java/roomescape/auth/AuthClaims.java @@ -1,6 +1,6 @@ package roomescape.auth; -public record UserClaims( +public record AuthClaims( String name, String role ) { diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java similarity index 80% rename from src/main/java/roomescape/auth/LoginMemberArgumentResolver.java rename to src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java index 77d78d78..7026488e 100644 --- a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java +++ b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java @@ -16,13 +16,13 @@ @Component @RequiredArgsConstructor -public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { +public class AuthClaimsArgumentResolver implements HandlerMethodArgumentResolver { private final MemberService memberService; @Override public boolean supportsParameter(MethodParameter parameter) { - return parameter.getParameterType().equals(UserClaims.class); + return parameter.getParameterType().equals(AuthClaims.class); } @Override @@ -32,9 +32,9 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m .filter(cookie -> "token".equals(cookie.getName())) .findFirst() .map(Cookie::getValue) - .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - return memberService.checkLogin(new UserToken(token)); + return memberService.checkLogin(new AuthToken(token)); } } diff --git a/src/main/java/roomescape/auth/RoleCheckInterceptor.java b/src/main/java/roomescape/auth/AuthRoleInterceptor.java similarity index 79% rename from src/main/java/roomescape/auth/RoleCheckInterceptor.java rename to src/main/java/roomescape/auth/AuthRoleInterceptor.java index 5a75ccac..7c963d7e 100644 --- a/src/main/java/roomescape/auth/RoleCheckInterceptor.java +++ b/src/main/java/roomescape/auth/AuthRoleInterceptor.java @@ -12,17 +12,18 @@ @Component @RequiredArgsConstructor -public class RoleCheckInterceptor implements HandlerInterceptor { +public class AuthRoleInterceptor implements HandlerInterceptor { private final MemberService memberService; + @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = Arrays.stream(request.getCookies()) .filter(cookie -> "token".equals(cookie.getName())) .findFirst() .map(Cookie::getValue) - .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - UserClaims userClaims = memberService.checkLogin(new UserToken(token)); + AuthClaims userClaims = memberService.checkLogin(new AuthToken(token)); if (!userClaims.role().equals("ADMIN")) { response.setStatus(401); return false; diff --git a/src/main/java/roomescape/auth/UserToken.java b/src/main/java/roomescape/auth/AuthToken.java similarity index 67% rename from src/main/java/roomescape/auth/UserToken.java rename to src/main/java/roomescape/auth/AuthToken.java index ce2beb5b..183c507b 100644 --- a/src/main/java/roomescape/auth/UserToken.java +++ b/src/main/java/roomescape/auth/AuthToken.java @@ -1,6 +1,6 @@ package roomescape.auth; -public record UserToken( +public record AuthToken( String token ) { } diff --git a/src/main/java/roomescape/auth/WebConfig.java b/src/main/java/roomescape/auth/AuthWebConfig.java similarity index 79% rename from src/main/java/roomescape/auth/WebConfig.java rename to src/main/java/roomescape/auth/AuthWebConfig.java index 09d76a1a..7c35c920 100644 --- a/src/main/java/roomescape/auth/WebConfig.java +++ b/src/main/java/roomescape/auth/AuthWebConfig.java @@ -10,10 +10,10 @@ @Configuration @RequiredArgsConstructor -public class WebConfig implements WebMvcConfigurer { +public class AuthWebConfig implements WebMvcConfigurer { - private final LoginMemberArgumentResolver loginMemberArgumentResolver; - private final RoleCheckInterceptor roleCheckInterceptor; + private final AuthClaimsArgumentResolver loginMemberArgumentResolver; + private final AuthRoleInterceptor roleCheckInterceptor; @Override public void addArgumentResolvers(List resolvers) { diff --git a/src/main/java/roomescape/auth/JWTUtils.java b/src/main/java/roomescape/auth/JWTUtils.java index 4a7ed24e..60b92f1e 100644 --- a/src/main/java/roomescape/auth/JWTUtils.java +++ b/src/main/java/roomescape/auth/JWTUtils.java @@ -13,7 +13,7 @@ public class JWTUtils { @Value("${roomescape.auth.jwt.secret}") private String secretKey; - public UserToken createToken(Member member){ + public AuthToken createToken(Member member) { String accessToken = Jwts.builder() .setSubject(member.getId().toString()) .claim("name", member.getName()) @@ -21,16 +21,16 @@ public UserToken createToken(Member member){ .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) .compact(); - return new UserToken(accessToken); + return new AuthToken(accessToken); } - public UserClaims getClaimsFromToken(String token) { + public AuthClaims getClaimsFromToken(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) .build() .parseClaimsJws(token) .getBody(); - return new UserClaims(claims.get("name", String.class),claims.get("role", String.class)); + return new AuthClaims(claims.get("name", String.class), claims.get("role", String.class)); } } diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 19dee8e6..d05ec3ca 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -1,36 +1,32 @@ package roomescape.member; import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import roomescape.auth.UserClaims; -import roomescape.auth.UserToken; +import roomescape.auth.AuthClaims; +import roomescape.auth.AuthToken; import java.net.URI; @RestController +@RequiredArgsConstructor public class MemberController { - private MemberService memberService; - - public MemberController(MemberService memberService) { - this.memberService = memberService; - } + private final MemberService memberService; @PostMapping("/members") public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { MemberResponse member = memberService.createMember(memberRequest); - return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); + return ResponseEntity.created(URI.create("/members/" + member.id())).body(member); } @PostMapping("/login") - public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse response){ + public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse response) { String token = memberService.getToken(loginRequest).token(); Cookie cookie = new Cookie("token", token); cookie.setHttpOnly(true); @@ -39,8 +35,8 @@ public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse re } @GetMapping("/login/check") - public UserClaims checkLogin(@CookieValue("token") String token){ - return memberService.checkLogin(new UserToken(token)); + public AuthClaims checkLogin(@CookieValue("token") String token) { + return memberService.checkLogin(new AuthToken(token)); } @PostMapping("/logout") diff --git a/src/main/java/roomescape/member/MemberRequest.java b/src/main/java/roomescape/member/MemberRequest.java index cafb79f1..8d892b82 100644 --- a/src/main/java/roomescape/member/MemberRequest.java +++ b/src/main/java/roomescape/member/MemberRequest.java @@ -1,19 +1,10 @@ package roomescape.member; -public class MemberRequest { - private String name; - private String email; - private String password; - public String getName() { - return name; - } - - public String getEmail() { - return email; - } - - public String getPassword() { - return password; - } +public record MemberRequest( + String name, + String email, + String password +) { } + diff --git a/src/main/java/roomescape/member/MemberResponse.java b/src/main/java/roomescape/member/MemberResponse.java index b9fa3b97..f2408ba3 100644 --- a/src/main/java/roomescape/member/MemberResponse.java +++ b/src/main/java/roomescape/member/MemberResponse.java @@ -1,25 +1,8 @@ package roomescape.member; -public class MemberResponse { - private Long id; - private String name; - private String email; - - public MemberResponse(Long id, String name, String email) { - this.id = id; - this.name = name; - this.email = email; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getEmail() { - return email; - } +public record MemberResponse( + Long id, + String name, + String email +) { } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 5270c51a..48b899b9 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -3,9 +3,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.stereotype.Service; +import roomescape.auth.AuthClaims; +import roomescape.auth.AuthToken; import roomescape.auth.JWTUtils; -import roomescape.auth.UserClaims; -import roomescape.auth.UserToken; @Service @RequiredArgsConstructor @@ -14,11 +14,11 @@ public class MemberService { private final JWTUtils jwtUtils; public MemberResponse createMember(MemberRequest memberRequest) { - Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), memberRequest.getPassword(), "USER")); + Member member = memberDao.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), "USER")); return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } - public UserToken getToken(LoginRequest loginRequest) { + public AuthToken getToken(LoginRequest loginRequest) { try { Member member = memberDao.findByEmailAndPassword(loginRequest.email(), loginRequest.password()); return jwtUtils.createToken(member); @@ -27,9 +27,9 @@ public UserToken getToken(LoginRequest loginRequest) { } } - public UserClaims checkLogin(UserToken userToken) { + public AuthClaims checkLogin(AuthToken userToken) { try { - UserClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); + AuthClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); memberDao.findByName(userClaims.name()); return userClaims; } catch (EmptyResultDataAccessException exception) { diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index ca4e87b1..e5ccecf2 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import roomescape.auth.UserClaims; +import roomescape.auth.AuthClaims; import java.net.URI; import java.util.List; @@ -25,7 +25,7 @@ public List list() { } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, UserClaims userClaims) { + public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, AuthClaims userClaims) { if (reservationRequest.date() == null || reservationRequest.theme() == null || reservationRequest.time() == null) { From c83b84b9d580c8f3920216ced5d87d52774129fe Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Tue, 7 Jan 2025 22:33:25 +0900 Subject: [PATCH 08/30] =?UTF-8?q?[FEAT]=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(4=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++++ src/main/java/roomescape/ExceptionController.java | 14 -------------- .../java/roomescape/auth/AuthRoleInterceptor.java | 3 ++- 3 files changed, 10 insertions(+), 15 deletions(-) delete mode 100644 src/main/java/roomescape/ExceptionController.java diff --git a/README.md b/README.md index eef270e3..5947d175 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,11 @@ ___ + admin ํŽ˜์ด์ง€ ์ง„์ž… (HandlerInterceptor ์ด์šฉ) -> ๊ด€๋ฆฌ์ž : ์ง„์ž… ๊ฐ€๋Šฅ -> ๊ด€๋ฆฌ์ž X : 401 ์ฝ”๋“œ ์‘๋‹ต + +# ๐ŸŒ€ Spring JPA (์ธ์ฆ) + +# 4๋‹จ๊ณ„ +___ +## JPA ์ „ํ™˜ ++ ์—”ํ‹ฐํ‹ฐ & ์—ฐ๊ด€ ๊ด€๊ณ„ ๋งคํ•‘ ++ DAP -> JpaRepository๋ฅผ ์ƒ์†๋ฐ›๋Š” Repository๋กœ ๋Œ€์ฒด diff --git a/src/main/java/roomescape/ExceptionController.java b/src/main/java/roomescape/ExceptionController.java deleted file mode 100644 index 4e2450f9..00000000 --- a/src/main/java/roomescape/ExceptionController.java +++ /dev/null @@ -1,14 +0,0 @@ -package roomescape; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; - -@ControllerAdvice -public class ExceptionController { - @ExceptionHandler(Exception.class) - public ResponseEntity handleRuntimeException(Exception e) { - e.printStackTrace(); - return ResponseEntity.badRequest().build(); - } -} diff --git a/src/main/java/roomescape/auth/AuthRoleInterceptor.java b/src/main/java/roomescape/auth/AuthRoleInterceptor.java index 7c963d7e..29f5ab36 100644 --- a/src/main/java/roomescape/auth/AuthRoleInterceptor.java +++ b/src/main/java/roomescape/auth/AuthRoleInterceptor.java @@ -13,6 +13,7 @@ @Component @RequiredArgsConstructor public class AuthRoleInterceptor implements HandlerInterceptor { + private final MemberService memberService; @Override @@ -24,7 +25,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); AuthClaims userClaims = memberService.checkLogin(new AuthToken(token)); - if (!userClaims.role().equals("ADMIN")) { + if (!"ADMIN".equals(userClaims.role())) { // ์ƒ์ˆ˜ ๊ธฐ์ค€์œผ๋กœ ๋น„๊ตํ•˜๊ธฐ response.setStatus(401); return false; } From 36b7dd3ad488856316cc7805d366e9269b0ddd61 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:46:02 +0900 Subject: [PATCH 09/30] =?UTF-8?q?[FEAT]=20Dao=20->=20Repository=20(4?= =?UTF-8?q?=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +- src/main/java/roomescape/member/Member.java | 13 ++ .../java/roomescape/member/MemberDao.java | 55 -------- .../roomescape/member/MemberRepository.java | 11 ++ .../java/roomescape/member/MemberService.java | 9 +- .../roomescape/reservation/Reservation.java | 12 ++ .../reservation/ReservationDao.java | 127 ------------------ .../reservation/ReservationRepository.java | 11 ++ .../reservation/ReservationRequest.java | 4 +- .../reservation/ReservationService.java | 20 +-- src/main/java/roomescape/theme/Theme.java | 9 ++ .../roomescape/theme/ThemeController.java | 13 +- src/main/java/roomescape/theme/ThemeDao.java | 41 ------ .../roomescape/theme/ThemeRepository.java | 8 ++ src/main/java/roomescape/time/Time.java | 23 +++- .../java/roomescape/time/TimeController.java | 4 +- src/main/java/roomescape/time/TimeDao.java | 41 ------ .../java/roomescape/time/TimeRepository.java | 8 ++ .../java/roomescape/time/TimeService.java | 24 ++-- src/main/resources/application.properties | 8 +- src/main/resources/schema.sql | 60 --------- src/test/java/roomescape/JpaTest.java | 30 +++++ 22 files changed, 161 insertions(+), 373 deletions(-) delete mode 100644 src/main/java/roomescape/member/MemberDao.java create mode 100644 src/main/java/roomescape/member/MemberRepository.java delete mode 100644 src/main/java/roomescape/reservation/ReservationDao.java create mode 100644 src/main/java/roomescape/reservation/ReservationRepository.java delete mode 100644 src/main/java/roomescape/theme/ThemeDao.java create mode 100644 src/main/java/roomescape/theme/ThemeRepository.java delete mode 100644 src/main/java/roomescape/time/TimeDao.java create mode 100644 src/main/java/roomescape/time/TimeRepository.java delete mode 100644 src/main/resources/schema.sql create mode 100644 src/test/java/roomescape/JpaTest.java diff --git a/build.gradle b/build.gradle index 21a9445b..18548121 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,8 @@ repositories { 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-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0' diff --git a/src/main/java/roomescape/member/Member.java b/src/main/java/roomescape/member/Member.java index 903aaa9b..33cee369 100644 --- a/src/main/java/roomescape/member/Member.java +++ b/src/main/java/roomescape/member/Member.java @@ -1,6 +1,15 @@ package roomescape.member; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; @@ -21,6 +30,10 @@ public Member(String name, String email, String password, String role) { this.role = role; } + public Member() { + + } + public Long getId() { return id; } diff --git a/src/main/java/roomescape/member/MemberDao.java b/src/main/java/roomescape/member/MemberDao.java deleted file mode 100644 index 81f77f4c..00000000 --- a/src/main/java/roomescape/member/MemberDao.java +++ /dev/null @@ -1,55 +0,0 @@ -package roomescape.member; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -@Repository -public class MemberDao { - private JdbcTemplate jdbcTemplate; - - public MemberDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public Member save(Member member) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - var ps = connection.prepareStatement("INSERT INTO member(name, email, password, role) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, member.getName()); - ps.setString(2, member.getEmail()); - ps.setString(3, member.getPassword()); - ps.setString(4, member.getRole()); - return ps; - }, keyHolder); - - return new Member(keyHolder.getKey().longValue(), member.getName(), member.getEmail(), "USER"); - } - - public Member findByEmailAndPassword(String email, String password) { - return jdbcTemplate.queryForObject( - "SELECT id, name, email, role FROM member WHERE email = ? AND password = ?", - (rs, rowNum) -> new Member( - rs.getLong("id"), - rs.getString("name"), - rs.getString("email"), - rs.getString("role") - ), - email, password - ); - } - - public Member findByName(String name) { - return jdbcTemplate.queryForObject( - "SELECT id, name, email, role FROM member WHERE name = ?", - (rs, rowNum) -> new Member( - rs.getLong("id"), - rs.getString("name"), - rs.getString("email"), - rs.getString("role") - ), - name - ); - } -} diff --git a/src/main/java/roomescape/member/MemberRepository.java b/src/main/java/roomescape/member/MemberRepository.java new file mode 100644 index 00000000..ab97d651 --- /dev/null +++ b/src/main/java/roomescape/member/MemberRepository.java @@ -0,0 +1,11 @@ +package roomescape.member; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +interface MemberRepository extends JpaRepository { + Member findByEmailAndPassword(String email, String password); + + void findByName(String name); +} diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 48b899b9..77cfe1cc 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -10,17 +10,18 @@ @Service @RequiredArgsConstructor public class MemberService { - private final MemberDao memberDao; + + private final MemberRepository memberRepository; private final JWTUtils jwtUtils; public MemberResponse createMember(MemberRequest memberRequest) { - Member member = memberDao.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), "USER")); + Member member = memberRepository.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), "USER")); return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } public AuthToken getToken(LoginRequest loginRequest) { try { - Member member = memberDao.findByEmailAndPassword(loginRequest.email(), loginRequest.password()); + Member member = memberRepository.findByEmailAndPassword(loginRequest.email(), loginRequest.password()); return jwtUtils.createToken(member); } catch (EmptyResultDataAccessException e) { throw new IllegalArgumentException("๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); @@ -30,7 +31,7 @@ public AuthToken getToken(LoginRequest loginRequest) { public AuthClaims checkLogin(AuthToken userToken) { try { AuthClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); - memberDao.findByName(userClaims.name()); + memberRepository.findByName(userClaims.name()); return userClaims; } catch (EmptyResultDataAccessException exception) { throw new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."); diff --git a/src/main/java/roomescape/reservation/Reservation.java b/src/main/java/roomescape/reservation/Reservation.java index 83a7edf1..5511053d 100644 --- a/src/main/java/roomescape/reservation/Reservation.java +++ b/src/main/java/roomescape/reservation/Reservation.java @@ -1,13 +1,25 @@ package roomescape.reservation; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; import roomescape.theme.Theme; import roomescape.time.Time; +@Entity public class Reservation { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String date; + + @ManyToOne private Time time; + @ManyToOne private Theme theme; public Reservation(Long id, String name, String date, Time time, Theme theme) { diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java deleted file mode 100644 index d75201e3..00000000 --- a/src/main/java/roomescape/reservation/ReservationDao.java +++ /dev/null @@ -1,127 +0,0 @@ -package roomescape.reservation; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import roomescape.theme.Theme; -import roomescape.time.Time; - -import java.sql.PreparedStatement; -import java.util.List; - -@Repository -public class ReservationDao { - - private final JdbcTemplate jdbcTemplate; - - public ReservationDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id", - - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public Reservation save(ReservationRequest reservationRequest) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement ps = connection.prepareStatement("INSERT INTO reservation(date, name, theme_id, time_id) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, reservationRequest.date()); - ps.setString(2, reservationRequest.name()); - ps.setLong(3, reservationRequest.theme()); - ps.setLong(4, reservationRequest.time()); - return ps; - }, keyHolder); - - Time time = jdbcTemplate.queryForObject("SELECT * FROM time WHERE id = ?", - (rs, rowNum) -> new Time(rs.getLong("id"), rs.getString("time_value")), - reservationRequest.time()); - - Theme theme = jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", - (rs, rowNum) -> new Theme(rs.getLong("id"), rs.getString("name"), rs.getString("description")), - reservationRequest.theme()); - - return new Reservation( - keyHolder.getKey().longValue(), - reservationRequest.name(), - reservationRequest.date(), - time, - theme - ); - } - - public void deleteById(Long id) { - jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); - } - - public List findReservationsByDateAndTheme(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id" + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public List findByDateAndThemeId(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id " + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } -} diff --git a/src/main/java/roomescape/reservation/ReservationRepository.java b/src/main/java/roomescape/reservation/ReservationRepository.java new file mode 100644 index 00000000..bd7f1789 --- /dev/null +++ b/src/main/java/roomescape/reservation/ReservationRepository.java @@ -0,0 +1,11 @@ +package roomescape.reservation; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ReservationRepository extends JpaRepository { + List findByDateAndThemeId(String date, Long themeId); +} diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index ed0d50b0..fb6eeeb3 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -3,7 +3,7 @@ public record ReservationRequest ( String name, String date, - Long theme, - Long time + String theme, + String time ){ } diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index 0350dc30..0fba65e7 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -1,30 +1,32 @@ package roomescape.reservation; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import roomescape.theme.Theme; +import roomescape.time.Time; import java.util.List; @Service +@RequiredArgsConstructor public class ReservationService { - private ReservationDao reservationDao; - public ReservationService(ReservationDao reservationDao) { - this.reservationDao = reservationDao; - } + private final ReservationRepository reservationRepository; public ReservationResponse save(ReservationRequest reservationRequest) { - Reservation reservation = reservationDao.save(reservationRequest); + Reservation reservation = new Reservation(reservationRequest.name(), reservationRequest.date(), new Time(reservationRequest.time()), new Theme(reservationRequest.theme(), "")); + reservationRepository.save(reservation); - return new ReservationResponse(reservation.getId(), reservationRequest.name(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + return new ReservationResponse(reservation.getId(), reservationRequest.name(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getTime()); } public void deleteById(Long id) { - reservationDao.deleteById(id); + reservationRepository.deleteById(id); } public List findAll() { - return reservationDao.findAll().stream() - .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())) + return reservationRepository.findAll().stream() + .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getTime())) .toList(); } } diff --git a/src/main/java/roomescape/theme/Theme.java b/src/main/java/roomescape/theme/Theme.java index 430a6239..8be4abd9 100644 --- a/src/main/java/roomescape/theme/Theme.java +++ b/src/main/java/roomescape/theme/Theme.java @@ -1,6 +1,15 @@ package roomescape.theme; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Theme { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; diff --git a/src/main/java/roomescape/theme/ThemeController.java b/src/main/java/roomescape/theme/ThemeController.java index 03bca41a..9f69c1e9 100644 --- a/src/main/java/roomescape/theme/ThemeController.java +++ b/src/main/java/roomescape/theme/ThemeController.java @@ -1,5 +1,6 @@ package roomescape.theme; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -12,27 +13,25 @@ import java.util.List; @RestController +@RequiredArgsConstructor public class ThemeController { - private ThemeDao themeDao; - public ThemeController(ThemeDao themeDao) { - this.themeDao = themeDao; - } + private final ThemeRepository themeRepository; @PostMapping("/themes") public ResponseEntity createTheme(@RequestBody Theme theme) { - Theme newTheme = themeDao.save(theme); + Theme newTheme = themeRepository.save(theme); return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())).body(newTheme); } @GetMapping("/themes") public ResponseEntity> list() { - return ResponseEntity.ok(themeDao.findAll()); + return ResponseEntity.ok(themeRepository.findAll()); } @DeleteMapping("/themes/{id}") public ResponseEntity deleteTheme(@PathVariable Long id) { - themeDao.deleteById(id); + themeRepository.deleteById(id); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/roomescape/theme/ThemeDao.java b/src/main/java/roomescape/theme/ThemeDao.java deleted file mode 100644 index 945341d8..00000000 --- a/src/main/java/roomescape/theme/ThemeDao.java +++ /dev/null @@ -1,41 +0,0 @@ -package roomescape.theme; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public class ThemeDao { - private JdbcTemplate jdbcTemplate; - - public ThemeDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query("SELECT * FROM theme where deleted = false", (rs, rowNum) -> new Theme( - rs.getLong("id"), - rs.getString("name"), - rs.getString("description") - )); - } - - public Theme save(Theme theme) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - var ps = connection.prepareStatement("INSERT INTO theme(name, description) VALUES (?, ?)", new String[]{"id"}); - ps.setString(1, theme.getName()); - ps.setString(2, theme.getDescription()); - return ps; - }, keyHolder); - - return new Theme(keyHolder.getKey().longValue(), theme.getName(), theme.getDescription()); - } - - public void deleteById(Long id) { - jdbcTemplate.update("UPDATE theme SET deleted = true WHERE id = ?", id); - } -} diff --git a/src/main/java/roomescape/theme/ThemeRepository.java b/src/main/java/roomescape/theme/ThemeRepository.java new file mode 100644 index 00000000..304cb0ef --- /dev/null +++ b/src/main/java/roomescape/theme/ThemeRepository.java @@ -0,0 +1,8 @@ +package roomescape.theme; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ThemeRepository extends JpaRepository { +} diff --git a/src/main/java/roomescape/time/Time.java b/src/main/java/roomescape/time/Time.java index 008ed93c..3d528bcf 100644 --- a/src/main/java/roomescape/time/Time.java +++ b/src/main/java/roomescape/time/Time.java @@ -1,16 +1,25 @@ package roomescape.time; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Time { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String value; + private String time; - public Time(Long id, String value) { + public Time(Long id, String time) { this.id = id; - this.value = value; + this.time = time; } - public Time(String value) { - this.value = value; + public Time(String time) { + this.time = time; } public Time() { @@ -21,7 +30,7 @@ public Long getId() { return id; } - public String getValue() { - return value; + public String getTime() { + return time; } } diff --git a/src/main/java/roomescape/time/TimeController.java b/src/main/java/roomescape/time/TimeController.java index 2343114d..abf63172 100644 --- a/src/main/java/roomescape/time/TimeController.java +++ b/src/main/java/roomescape/time/TimeController.java @@ -27,7 +27,7 @@ public List