From 873824231ce246f9aeeebb1aa00bc5cad0afeb11 Mon Sep 17 00:00:00 2001 From: kimYejin Date: Tue, 1 Jul 2025 17:43:08 +0900 Subject: [PATCH 01/12] =?UTF-8?q?1=EB=8B=A8=EA=B3=84=20:=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/AuthenticatedMemberResponse.java | 26 +++++++++++++ .../java/roomescape/member/LoginRequest.java | 14 +++++++ .../roomescape/member/MemberController.java | 24 ++++++++++++ .../java/roomescape/member/MemberService.java | 35 ++++++++++++++++- .../java/roomescape/token/TokenParser.java | 34 +++++++++++++++++ .../java/roomescape/token/TokenProvider.java | 38 +++++++++++++++++++ .../java/roomescape/token/TokenResponse.java | 13 +++++++ src/main/resources/application.properties | 4 +- src/test/java/roomescape/MissionStepTest.java | 13 ++++++- 9 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 src/main/java/roomescape/member/AuthenticatedMemberResponse.java create mode 100644 src/main/java/roomescape/member/LoginRequest.java create mode 100644 src/main/java/roomescape/token/TokenParser.java create mode 100644 src/main/java/roomescape/token/TokenProvider.java create mode 100644 src/main/java/roomescape/token/TokenResponse.java diff --git a/src/main/java/roomescape/member/AuthenticatedMemberResponse.java b/src/main/java/roomescape/member/AuthenticatedMemberResponse.java new file mode 100644 index 000000000..09d6122aa --- /dev/null +++ b/src/main/java/roomescape/member/AuthenticatedMemberResponse.java @@ -0,0 +1,26 @@ +package roomescape.member; + +public class AuthenticatedMemberResponse { + + private Long id; + private String name; + private String role; + + public AuthenticatedMemberResponse(final Long id, final String name, final String role) { + this.id = id; + this.name = name; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } +} diff --git a/src/main/java/roomescape/member/LoginRequest.java b/src/main/java/roomescape/member/LoginRequest.java new file mode 100644 index 000000000..c91e6326e --- /dev/null +++ b/src/main/java/roomescape/member/LoginRequest.java @@ -0,0 +1,14 @@ +package roomescape.member; + +public class LoginRequest { + private String email; + private String password; + + public String getEmail() { + return email; + } + + public String getPassword() { + return password; + } +} diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 881ae5e0d..ff2160ec7 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -3,11 +3,14 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; 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.token.TokenResponse; import java.net.URI; @@ -19,6 +22,27 @@ public MemberController(MemberService memberService) { this.memberService = memberService; } + @PostMapping("/login") + public ResponseEntity login(@RequestBody LoginRequest loginRequest) { + TokenResponse token = memberService.getAccessToken(loginRequest.getEmail(), loginRequest.getPassword()); + ResponseCookie cookie = ResponseCookie.from("token", token.getAccessToken()) + .httpOnly(true) + .path("/") + .build(); + + return ResponseEntity.ok() + .header(HttpHeaders.SET_COOKIE, cookie.toString()) + .build(); + } + + @GetMapping("/login/check") + public ResponseEntity loingCheck(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + AuthenticatedMemberResponse response = memberService.parseTokenAndGetMemberInfo(cookies); + return ResponseEntity.ok().body(response); + } + + @PostMapping("/members") public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { MemberResponse member = memberService.createMember(memberRequest); diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index ccaa8cba5..6339bbec4 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -1,17 +1,48 @@ package roomescape.member; +import jakarta.servlet.http.Cookie; import org.springframework.stereotype.Service; +import roomescape.token.TokenParser; +import roomescape.token.TokenProvider; +import roomescape.token.TokenResponse; @Service public class MemberService { - private MemberDao memberDao; + private final MemberDao memberDao; + private final TokenProvider tokenProvider; + private final TokenParser tokenParser; - public MemberService(MemberDao memberDao) { + public MemberService(final MemberDao memberDao, final TokenProvider tokenProvider, final TokenParser tokenParser) { this.memberDao = memberDao; + this.tokenProvider = tokenProvider; + this.tokenParser = tokenParser; } 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 TokenResponse getAccessToken(String email, String password) { + Member member = getMember(email, password); + String token = tokenProvider.createAccessToken(member); + return new TokenResponse(token); + } + + public AuthenticatedMemberResponse parseTokenAndGetMemberInfo(Cookie[] cookies) { + + String token = ""; + for (Cookie cookie : cookies) { + if (cookie.getName().equals("token")) + token = cookie.getValue(); + } + + return tokenParser.paseMemberInfo(token); + } + + private Member getMember(String email, String password) { + Member member = memberDao.findByEmailAndPassword(email, password); + if (member == null) throw new IllegalArgumentException("등록되지 않은 사용자입니다."); + return member; + } } diff --git a/src/main/java/roomescape/token/TokenParser.java b/src/main/java/roomescape/token/TokenParser.java new file mode 100644 index 000000000..81d876013 --- /dev/null +++ b/src/main/java/roomescape/token/TokenParser.java @@ -0,0 +1,34 @@ +package roomescape.token; + +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.AuthenticatedMemberResponse; + +import java.util.Base64; + +@Component +public class TokenParser { + + @Value("${roomescape.auth.jwt.secret}") + private String secretKey; + + public AuthenticatedMemberResponse paseMemberInfo(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(Base64.getDecoder().decode(secretKey))) + .build() + .parseClaimsJws(token) + .getBody(); + + String name = claims.get("name", String.class); + String role = claims.get("role", String.class); + long id = Long.parseLong(claims.getSubject()); + + return new AuthenticatedMemberResponse(id, name, role); + + + } + +} diff --git a/src/main/java/roomescape/token/TokenProvider.java b/src/main/java/roomescape/token/TokenProvider.java new file mode 100644 index 000000000..ac319fca0 --- /dev/null +++ b/src/main/java/roomescape/token/TokenProvider.java @@ -0,0 +1,38 @@ +package roomescape.token; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import roomescape.member.Member; + +import java.util.Base64; +import java.util.Date; + + +@Component +public class TokenProvider { + + @Value("${roomescape.auth.jwt.secret}") + private String secretKey; + + @Value("${roomescape.auth.jwt.token.expire-length}") + private long expire; + + public String createAccessToken(Member member) { + Date now = new Date(); + Date validity = new Date(now.getTime() + expire); + return Jwts.builder() + .setSubject(Long.toString(member.getId())) + .claim("name", member.getName()) + .claim("role", member.getRole()) + .setIssuedAt(now) + .setExpiration(validity) + .signWith( + Keys.hmacShaKeyFor(Base64.getDecoder().decode(secretKey)), + SignatureAlgorithm.HS256 + ) + .compact(); + } +} diff --git a/src/main/java/roomescape/token/TokenResponse.java b/src/main/java/roomescape/token/TokenResponse.java new file mode 100644 index 000000000..7f586c338 --- /dev/null +++ b/src/main/java/roomescape/token/TokenResponse.java @@ -0,0 +1,13 @@ +package roomescape.token; + +public class TokenResponse { + private final String accessToken; + + public TokenResponse(final String accessToken) { + this.accessToken = accessToken; + } + + public String getAccessToken() { + return accessToken; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a0f33bbab..df2d5c887 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,9 +3,11 @@ spring.h2.console.enabled=true spring.h2.console.path=/h2-console spring.datasource.url=jdbc:h2:mem:database +roomescape.auth.jwt.secret= Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= +roomescape.auth.jwt.token.expire-length=3600000 + #spring.jpa.show-sql=true #spring.jpa.properties.hibernate.format_sql=true #spring.jpa.ddl-auto=create-drop #spring.jpa.defer-datasource-initialization=true -#roomescape.auth.jwt.secret= Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= \ No newline at end of file diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 6add784bd..510711ae5 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 11e2ea3cc94763d0fefc5f6cfffd2d040d6c6e88 Mon Sep 17 00:00:00 2001 From: kimYejin Date: Tue, 1 Jul 2025 19:55:03 +0900 Subject: [PATCH 02/12] =?UTF-8?q?2,3=EB=8B=A8=EA=B3=84=20:=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81,=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/AdminRoleCheckInterceptor.java | 27 +++++++ .../LoginMember.java} | 12 +++- .../auth/LoginMemberArgumentResolver.java | 35 +++++++++ .../{member => auth}/LoginRequest.java | 2 +- .../java/roomescape/config/WebConfig.java | 33 +++++++++ .../roomescape/member/MemberController.java | 8 ++- .../java/roomescape/member/MemberService.java | 5 +- .../reservation/ReservationController.java | 9 +-- .../reservation/ReservationRequest.java | 7 ++ .../reservation/ReservationService.java | 12 +++- .../java/roomescape/token/TokenParser.java | 7 +- .../java/roomescape/token/TokenProvider.java | 2 +- src/test/java/roomescape/MissionStepTest.java | 72 +++++++++++++++++++ 13 files changed, 213 insertions(+), 18 deletions(-) create mode 100644 src/main/java/roomescape/auth/AdminRoleCheckInterceptor.java rename src/main/java/roomescape/{member/AuthenticatedMemberResponse.java => auth/LoginMember.java} (55%) create mode 100644 src/main/java/roomescape/auth/LoginMemberArgumentResolver.java rename src/main/java/roomescape/{member => auth}/LoginRequest.java (88%) create mode 100644 src/main/java/roomescape/config/WebConfig.java diff --git a/src/main/java/roomescape/auth/AdminRoleCheckInterceptor.java b/src/main/java/roomescape/auth/AdminRoleCheckInterceptor.java new file mode 100644 index 000000000..9d4939369 --- /dev/null +++ b/src/main/java/roomescape/auth/AdminRoleCheckInterceptor.java @@ -0,0 +1,27 @@ +package roomescape.auth; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import roomescape.member.MemberService; + +@Component +public class AdminRoleCheckInterceptor implements HandlerInterceptor { + + private final MemberService memberService; + + public AdminRoleCheckInterceptor(final MemberService memberService) { + this.memberService = memberService; + } + + @Override + public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { + LoginMember member = memberService.parseTokenAndGetMemberInfo(request.getCookies()); + if (member == null || !member.getRole().equals("ADMIN")) { + response.setStatus(401); + return false; + } + return true; + } +} diff --git a/src/main/java/roomescape/member/AuthenticatedMemberResponse.java b/src/main/java/roomescape/auth/LoginMember.java similarity index 55% rename from src/main/java/roomescape/member/AuthenticatedMemberResponse.java rename to src/main/java/roomescape/auth/LoginMember.java index 09d6122aa..65281ec50 100644 --- a/src/main/java/roomescape/member/AuthenticatedMemberResponse.java +++ b/src/main/java/roomescape/auth/LoginMember.java @@ -1,14 +1,16 @@ -package roomescape.member; +package roomescape.auth; -public class AuthenticatedMemberResponse { +public class LoginMember { private Long id; private String name; + private String email; private String role; - public AuthenticatedMemberResponse(final Long id, final String name, final String role) { + public LoginMember(final Long id, final String name, final String email, final String role) { this.id = id; this.name = name; + this.email = email; this.role = role; } @@ -20,6 +22,10 @@ public String getName() { return name; } + public String getEmail() { + return email; + } + public String getRole() { return role; } diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java new file mode 100644 index 000000000..4203a6c82 --- /dev/null +++ b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java @@ -0,0 +1,35 @@ +package roomescape.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +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.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import roomescape.member.MemberService; + +@Component +public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { + + private final MemberService memberService; + + public LoginMemberArgumentResolver(final MemberService memberService) { + this.memberService = memberService; + } + + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return parameter.getParameterType().equals(LoginMember.class); + } + + @Override + public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) throws Exception { + HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + Cookie[] cookies = request.getCookies(); + return memberService.parseTokenAndGetMemberInfo(cookies); + } + + +} diff --git a/src/main/java/roomescape/member/LoginRequest.java b/src/main/java/roomescape/auth/LoginRequest.java similarity index 88% rename from src/main/java/roomescape/member/LoginRequest.java rename to src/main/java/roomescape/auth/LoginRequest.java index c91e6326e..89a69922e 100644 --- a/src/main/java/roomescape/member/LoginRequest.java +++ b/src/main/java/roomescape/auth/LoginRequest.java @@ -1,4 +1,4 @@ -package roomescape.member; +package roomescape.auth; public class LoginRequest { private String email; diff --git a/src/main/java/roomescape/config/WebConfig.java b/src/main/java/roomescape/config/WebConfig.java new file mode 100644 index 000000000..619c4c2c0 --- /dev/null +++ b/src/main/java/roomescape/config/WebConfig.java @@ -0,0 +1,33 @@ +package roomescape.config; + +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.AdminRoleCheckInterceptor; +import roomescape.auth.LoginMemberArgumentResolver; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private final LoginMemberArgumentResolver loginMemberArgumentResolver; + private final AdminRoleCheckInterceptor adminRoleCheckInterceptor; + + public WebConfig(final LoginMemberArgumentResolver loginMemberArgumentResolver, final AdminRoleCheckInterceptor adminRoleCheckInterceptor) { + this.loginMemberArgumentResolver = loginMemberArgumentResolver; + this.adminRoleCheckInterceptor = adminRoleCheckInterceptor; + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(loginMemberArgumentResolver); + } + + @Override + public void addInterceptors(final InterceptorRegistry registry) { + registry.addInterceptor(adminRoleCheckInterceptor) + .addPathPatterns("/admin/**"); + } +} diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index ff2160ec7..501dc2bb1 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -10,6 +10,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import roomescape.auth.LoginMember; +import roomescape.auth.LoginRequest; import roomescape.token.TokenResponse; import java.net.URI; @@ -36,13 +38,13 @@ public ResponseEntity login(@RequestBody LoginRequest loginRequest) { } @GetMapping("/login/check") - public ResponseEntity loingCheck(HttpServletRequest request) { + public ResponseEntity loingCheck(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); - AuthenticatedMemberResponse response = memberService.parseTokenAndGetMemberInfo(cookies); + LoginMember loginMember = memberService.parseTokenAndGetMemberInfo(cookies); + MemberResponse response = new MemberResponse(loginMember.getId(), loginMember.getName(), loginMember.getEmail()); return ResponseEntity.ok().body(response); } - @PostMapping("/members") public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { MemberResponse member = memberService.createMember(memberRequest); diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 6339bbec4..b83366cb8 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -2,6 +2,7 @@ import jakarta.servlet.http.Cookie; import org.springframework.stereotype.Service; +import roomescape.auth.LoginMember; import roomescape.token.TokenParser; import roomescape.token.TokenProvider; import roomescape.token.TokenResponse; @@ -29,7 +30,7 @@ public TokenResponse getAccessToken(String email, String password) { return new TokenResponse(token); } - public AuthenticatedMemberResponse parseTokenAndGetMemberInfo(Cookie[] cookies) { + public LoginMember parseTokenAndGetMemberInfo(Cookie[] cookies) { String token = ""; for (Cookie cookie : cookies) { @@ -37,6 +38,8 @@ public AuthenticatedMemberResponse parseTokenAndGetMemberInfo(Cookie[] cookies) token = cookie.getValue(); } + if (token.isEmpty()) throw new IllegalArgumentException("로그인하지 않은 사용자입니다."); + return tokenParser.paseMemberInfo(token); } diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index b3bef3990..66a7a89de 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.auth.LoginMember; import java.net.URI; import java.util.List; @@ -26,15 +27,15 @@ 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 loginMember) { + + if (reservationRequest.getDate() == null || reservationRequest.getTheme() == null || reservationRequest.getTime() == null) { return ResponseEntity.badRequest().build(); } - ReservationResponse reservation = reservationService.save(reservationRequest); + ReservationResponse reservation = reservationService.save(reservationRequest, loginMember); return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); } diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index 19f441246..41f424af7 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -6,6 +6,13 @@ public class ReservationRequest { private Long theme; private Long time; + public ReservationRequest(final String name, final String date, final Long theme, final Long time) { + this.name = name; + this.date = date; + this.theme = theme; + this.time = time; + } + public String getName() { return name; } diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index bd3313328..1456e29d4 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -1,6 +1,7 @@ package roomescape.reservation; import org.springframework.stereotype.Service; +import roomescape.auth.LoginMember; import java.util.List; @@ -12,12 +13,19 @@ public ReservationService(ReservationDao reservationDao) { this.reservationDao = reservationDao; } - public ReservationResponse save(ReservationRequest reservationRequest) { + public ReservationResponse save(ReservationRequest reservationRequest, LoginMember loginMember) { + reservationRequest = replaceNameIfEmpty(reservationRequest, loginMember); Reservation reservation = reservationDao.save(reservationRequest); - return new ReservationResponse(reservation.getId(), reservationRequest.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); } + private ReservationRequest replaceNameIfEmpty(ReservationRequest request, LoginMember loginMember) { + if (request.getName() == null || request.getName().isBlank()) { + return new ReservationRequest(loginMember.getName(), request.getDate(), request.getTheme(), request.getTime()); + } + return request; + } + public void deleteById(Long id) { reservationDao.deleteById(id); } diff --git a/src/main/java/roomescape/token/TokenParser.java b/src/main/java/roomescape/token/TokenParser.java index 81d876013..2b99442b6 100644 --- a/src/main/java/roomescape/token/TokenParser.java +++ b/src/main/java/roomescape/token/TokenParser.java @@ -5,7 +5,7 @@ import io.jsonwebtoken.security.Keys; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import roomescape.member.AuthenticatedMemberResponse; +import roomescape.auth.LoginMember; import java.util.Base64; @@ -15,7 +15,7 @@ public class TokenParser { @Value("${roomescape.auth.jwt.secret}") private String secretKey; - public AuthenticatedMemberResponse paseMemberInfo(String token) { + public LoginMember paseMemberInfo(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(Base64.getDecoder().decode(secretKey))) .build() @@ -23,10 +23,11 @@ public AuthenticatedMemberResponse paseMemberInfo(String token) { .getBody(); String name = claims.get("name", String.class); + String email = claims.get("email", String.class); String role = claims.get("role", String.class); long id = Long.parseLong(claims.getSubject()); - return new AuthenticatedMemberResponse(id, name, role); + return new LoginMember(id, name, email, role); } diff --git a/src/main/java/roomescape/token/TokenProvider.java b/src/main/java/roomescape/token/TokenProvider.java index ac319fca0..bf3a8a37d 100644 --- a/src/main/java/roomescape/token/TokenProvider.java +++ b/src/main/java/roomescape/token/TokenProvider.java @@ -10,7 +10,6 @@ import java.util.Base64; import java.util.Date; - @Component public class TokenProvider { @@ -27,6 +26,7 @@ public String createAccessToken(Member member) { .setSubject(Long.toString(member.getId())) .claim("name", member.getName()) .claim("role", member.getRole()) + .claim("email", member.getEmail()) .setIssuedAt(now) .setExpiration(validity) .signWith( diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 510711ae5..0d8cdfdc2 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; @@ -44,4 +45,75 @@ public class MissionStepTest { assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("어드민"); } + + @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("브라운"); + } + + 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) + .body(params) + .when().post("/login") + .then().log().all() + .statusCode(200) + .extract(); + + return response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; + } + + @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); + } + + } From 7a6895ec90011edfe45dc3064a12406dca4910a1 Mon Sep 17 00:00:00 2001 From: kimYejin Date: Tue, 1 Jul 2025 20:14:41 +0900 Subject: [PATCH 03/12] =?UTF-8?q?refact=20:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/roomescape/MissionStepTest.java | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 0d8cdfdc2..2c9914cb2 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -4,6 +4,7 @@ import io.restassured.http.ContentType; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; @@ -19,7 +20,8 @@ public class MissionStepTest { @Test - void 일단계() { + @DisplayName("로그인시 토큰 정상 발급 테스트") + void login_shouldReturnAccessToken() { Map params = new HashMap<>(); params.put("email", "admin@email.com"); params.put("password", "password"); @@ -47,7 +49,8 @@ public class MissionStepTest { } @Test - void 이단계() { + @DisplayName("예약 생성시 name이 비어있으면 로그인한 회원 이름을 사용하는지 테스트") + void createReservation_shouldUseLoginMemberName_whenNameIsNotProvided() { String token = createToken("admin@email.com", "password"); // 일단계에서 토큰을 추출하는 로직을 메서드로 따로 만들어서 활용하세요. Map params = new HashMap<>(); @@ -80,24 +83,9 @@ public class MissionStepTest { assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("브라운"); } - 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) - .body(params) - .when().post("/login") - .then().log().all() - .statusCode(200) - .extract(); - - return response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; - } - @Test - void 삼단계() { + @DisplayName("Admin 권한이 있는 사용자만 '/admin' 엔드포인트에 접근할 수 있는지 테스트") + void cannotAccessAdminEndpoint_whenNonAdminUser() { String brownToken = createToken("brown@email.com", "password"); RestAssured.given().log().all() @@ -115,5 +103,21 @@ private String createToken(String email, String password) { .statusCode(200); } + 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) + .body(params) + .when().post("/login") + .then().log().all() + .statusCode(200) + .extract(); + + return response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; + } + } From 36ca88a8a80f87b75560225a9d8d7776ce50ed4d Mon Sep 17 00:00:00 2001 From: kimYejin Date: Fri, 25 Jul 2025 15:54:58 +0900 Subject: [PATCH 04/12] =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/AdminRoleCheckInterceptor.java | 28 ++++- .../java/roomescape/auth/AuthController.java | 56 ++++++++++ .../java/roomescape/auth/AuthService.java | 37 +++++++ .../auth/LoginMemberArgumentResolver.java | 12 +- .../java/roomescape/auth/LoginRequest.java | 12 +- .../roomescape/auth/TokenInterceptor.java | 39 +++++++ .../java/roomescape/config/WebConfig.java | 11 +- src/main/java/roomescape/member/Member.java | 8 +- .../roomescape/member/MemberController.java | 41 +------ .../java/roomescape/member/MemberDao.java | 8 +- .../java/roomescape/member/MemberRequest.java | 17 +-- .../roomescape/member/MemberResponse.java | 23 +--- .../java/roomescape/member/MemberService.java | 37 +------ src/main/java/roomescape/member/Role.java | 6 + .../reservation/ReservationService.java | 3 +- .../java/roomescape/token/TokenParser.java | 3 +- .../java/roomescape/token/TokenProvider.java | 1 - src/main/resources/templates/signup.html | 103 +++++++++--------- 18 files changed, 241 insertions(+), 204 deletions(-) create mode 100644 src/main/java/roomescape/auth/AuthController.java create mode 100644 src/main/java/roomescape/auth/AuthService.java create mode 100644 src/main/java/roomescape/auth/TokenInterceptor.java create mode 100644 src/main/java/roomescape/member/Role.java diff --git a/src/main/java/roomescape/auth/AdminRoleCheckInterceptor.java b/src/main/java/roomescape/auth/AdminRoleCheckInterceptor.java index 9d4939369..851259d65 100644 --- a/src/main/java/roomescape/auth/AdminRoleCheckInterceptor.java +++ b/src/main/java/roomescape/auth/AdminRoleCheckInterceptor.java @@ -1,27 +1,43 @@ package roomescape.auth; +import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; -import roomescape.member.MemberService; + +import java.util.Arrays; @Component public class AdminRoleCheckInterceptor implements HandlerInterceptor { + private final AuthService authService; - private final MemberService memberService; - - public AdminRoleCheckInterceptor(final MemberService memberService) { - this.memberService = memberService; + public AdminRoleCheckInterceptor(AuthService authService) { + this.authService = authService; } @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { - LoginMember member = memberService.parseTokenAndGetMemberInfo(request.getCookies()); + Cookie[] cookies = request.getCookies(); + + if (cookies == null) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return false; + } + + String token = Arrays.stream(cookies) + .filter(cookie -> "token".equals(cookie.getName())) + .map(Cookie::getValue) + .findFirst() + .orElse(null); + + LoginMember member = authService.parseMemberInfo(token); + if (member == null || !member.getRole().equals("ADMIN")) { response.setStatus(401); return false; } return true; } + } diff --git a/src/main/java/roomescape/auth/AuthController.java b/src/main/java/roomescape/auth/AuthController.java new file mode 100644 index 000000000..123c1d144 --- /dev/null +++ b/src/main/java/roomescape/auth/AuthController.java @@ -0,0 +1,56 @@ +package roomescape.auth; + + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseCookie; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import roomescape.member.MemberResponse; +import roomescape.token.TokenResponse; + +@Controller +public class AuthController { + + private final AuthService authService; + + public AuthController(AuthService authService) { + this.authService = authService; + } + + @PostMapping("/logout") + public ResponseEntity logout(HttpServletResponse response) { + Cookie cookie = new Cookie("token", ""); + cookie.setHttpOnly(true); + cookie.setPath("/"); + cookie.setMaxAge(0); + response.addCookie(cookie); + return ResponseEntity.ok().build(); + } + + @GetMapping("/login/check") + public ResponseEntity loginCheck(LoginMember loginMember) { + if (loginMember == null) { + return ResponseEntity.status(401).build(); + } + MemberResponse response = new MemberResponse(loginMember.getId(), loginMember.getName(), loginMember.getEmail()); + return ResponseEntity.ok().body(response); + } + + @PostMapping("/login") + public ResponseEntity login(@RequestBody LoginRequest loginRequest) { + TokenResponse token = authService.createToken(loginRequest.email(), loginRequest.password()); + ResponseCookie cookie = ResponseCookie.from("token", token.getAccessToken()) + .httpOnly(true) + .path("/") + .build(); + + return ResponseEntity.ok() + .header(HttpHeaders.SET_COOKIE, cookie.toString()) + .build(); + } +} diff --git a/src/main/java/roomescape/auth/AuthService.java b/src/main/java/roomescape/auth/AuthService.java new file mode 100644 index 000000000..d52ec093f --- /dev/null +++ b/src/main/java/roomescape/auth/AuthService.java @@ -0,0 +1,37 @@ +package roomescape.auth; + +import org.springframework.stereotype.Service; +import roomescape.member.Member; +import roomescape.member.MemberDao; +import roomescape.token.TokenParser; +import roomescape.token.TokenProvider; +import roomescape.token.TokenResponse; + +@Service +public class AuthService { + private final MemberDao memberDao; + private final TokenProvider tokenProvider; + private final TokenParser tokenParser; + + public AuthService(MemberDao memberDao, TokenProvider tokenProvider, TokenParser tokenParser) { + this.memberDao = memberDao; + this.tokenProvider = tokenProvider; + this.tokenParser = tokenParser; + } + + public TokenResponse createToken(String email, String password) { + Member member = memberDao.findByEmailAndPassword(email, password); + if (member == null) { + throw new IllegalArgumentException("등록 되지 않은 사용자입니다."); + } + String token = tokenProvider.createAccessToken(member); + return new TokenResponse(token); + } + + public LoginMember parseMemberInfo(String token) { + if (token == null || token.isBlank()) { + throw new IllegalArgumentException("로그인 하지 않은 사용자입니다."); + } + return tokenParser.parseMemberInfo(token); + } +} diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java index 4203a6c82..08e057252 100644 --- a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java +++ b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java @@ -1,6 +1,5 @@ package roomescape.auth; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; @@ -8,17 +7,10 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import roomescape.member.MemberService; @Component public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { - private final MemberService memberService; - - public LoginMemberArgumentResolver(final MemberService memberService) { - this.memberService = memberService; - } - @Override public boolean supportsParameter(final MethodParameter parameter) { return parameter.getParameterType().equals(LoginMember.class); @@ -27,8 +19,8 @@ public boolean supportsParameter(final MethodParameter parameter) { @Override public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); - Cookie[] cookies = request.getCookies(); - return memberService.parseTokenAndGetMemberInfo(cookies); + LoginMember loginMember = (LoginMember) request.getAttribute("loginMember"); + return loginMember; } diff --git a/src/main/java/roomescape/auth/LoginRequest.java b/src/main/java/roomescape/auth/LoginRequest.java index 89a69922e..218f318e3 100644 --- a/src/main/java/roomescape/auth/LoginRequest.java +++ b/src/main/java/roomescape/auth/LoginRequest.java @@ -1,14 +1,4 @@ package roomescape.auth; -public class LoginRequest { - private String email; - private String password; - - public String getEmail() { - return email; - } - - public String getPassword() { - return password; - } +public record LoginRequest(String email, String password) { } diff --git a/src/main/java/roomescape/auth/TokenInterceptor.java b/src/main/java/roomescape/auth/TokenInterceptor.java new file mode 100644 index 000000000..0e394e443 --- /dev/null +++ b/src/main/java/roomescape/auth/TokenInterceptor.java @@ -0,0 +1,39 @@ +package roomescape.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.Arrays; + +@Component +public class TokenInterceptor implements HandlerInterceptor { + + private final AuthService authService; + + public TokenInterceptor(AuthService authService) { + this.authService = authService; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + Cookie[] cookies = request.getCookies(); + + if (cookies == null) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return false; + } + + String token = Arrays.stream(cookies) + .filter(cookie -> "token".equals(cookie.getName())) + .map(Cookie::getValue) + .findFirst() + .orElse(null); + + LoginMember loginMember = authService.parseMemberInfo(token); + request.setAttribute("loginMember", loginMember); + return HandlerInterceptor.super.preHandle(request, response, handler); + } +} diff --git a/src/main/java/roomescape/config/WebConfig.java b/src/main/java/roomescape/config/WebConfig.java index 619c4c2c0..cbee87fe2 100644 --- a/src/main/java/roomescape/config/WebConfig.java +++ b/src/main/java/roomescape/config/WebConfig.java @@ -6,6 +6,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import roomescape.auth.AdminRoleCheckInterceptor; import roomescape.auth.LoginMemberArgumentResolver; +import roomescape.auth.TokenInterceptor; import java.util.List; @@ -14,10 +15,12 @@ public class WebConfig implements WebMvcConfigurer { private final LoginMemberArgumentResolver loginMemberArgumentResolver; private final AdminRoleCheckInterceptor adminRoleCheckInterceptor; + private final TokenInterceptor tokenInterceptor; - public WebConfig(final LoginMemberArgumentResolver loginMemberArgumentResolver, final AdminRoleCheckInterceptor adminRoleCheckInterceptor) { + public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver, AdminRoleCheckInterceptor adminRoleCheckInterceptor, TokenInterceptor tokenInterceptor) { this.loginMemberArgumentResolver = loginMemberArgumentResolver; this.adminRoleCheckInterceptor = adminRoleCheckInterceptor; + this.tokenInterceptor = tokenInterceptor; } @Override @@ -29,5 +32,11 @@ public void addArgumentResolvers(List resolvers) public void addInterceptors(final InterceptorRegistry registry) { registry.addInterceptor(adminRoleCheckInterceptor) .addPathPatterns("/admin/**"); + + registry.addInterceptor(tokenInterceptor) + .addPathPatterns("/**") + .excludePathPatterns("/", "/signup", "/signup/**", "/login", "/login/**", "/logout", "/error"); + + } } diff --git a/src/main/java/roomescape/member/Member.java b/src/main/java/roomescape/member/Member.java index 903aaa9b0..9ec10f04a 100644 --- a/src/main/java/roomescape/member/Member.java +++ b/src/main/java/roomescape/member/Member.java @@ -5,16 +5,16 @@ public class Member { private String name; private String email; private String password; - private String role; + private Role role; - public Member(Long id, String name, String email, String role) { + public Member(Long id, String name, String email, Role role) { this.id = id; this.name = name; this.email = email; this.role = role; } - public Member(String name, String email, String password, String role) { + public Member(String name, String email, String password, Role role) { this.name = name; this.email = email; this.password = password; @@ -37,7 +37,7 @@ public String getPassword() { return password; } - public String getRole() { + public Role getRole() { return role; } } diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 501dc2bb1..e9c0dddb4 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -1,18 +1,9 @@ package roomescape.member; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; -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.LoginMember; -import roomescape.auth.LoginRequest; -import roomescape.token.TokenResponse; import java.net.URI; @@ -24,40 +15,10 @@ public MemberController(MemberService memberService) { this.memberService = memberService; } - @PostMapping("/login") - public ResponseEntity login(@RequestBody LoginRequest loginRequest) { - TokenResponse token = memberService.getAccessToken(loginRequest.getEmail(), loginRequest.getPassword()); - ResponseCookie cookie = ResponseCookie.from("token", token.getAccessToken()) - .httpOnly(true) - .path("/") - .build(); - - return ResponseEntity.ok() - .header(HttpHeaders.SET_COOKIE, cookie.toString()) - .build(); - } - - @GetMapping("/login/check") - public ResponseEntity loingCheck(HttpServletRequest request) { - Cookie[] cookies = request.getCookies(); - LoginMember loginMember = memberService.parseTokenAndGetMemberInfo(cookies); - MemberResponse response = new MemberResponse(loginMember.getId(), loginMember.getName(), loginMember.getEmail()); - return ResponseEntity.ok().body(response); - } - @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("/logout") - public ResponseEntity logout(HttpServletResponse response) { - Cookie cookie = new Cookie("token", ""); - cookie.setHttpOnly(true); - cookie.setPath("/"); - cookie.setMaxAge(0); - response.addCookie(cookie); - return ResponseEntity.ok().build(); - } } diff --git a/src/main/java/roomescape/member/MemberDao.java b/src/main/java/roomescape/member/MemberDao.java index 81f77f4cd..63898117c 100644 --- a/src/main/java/roomescape/member/MemberDao.java +++ b/src/main/java/roomescape/member/MemberDao.java @@ -20,11 +20,11 @@ public Member save(Member member) { ps.setString(1, member.getName()); ps.setString(2, member.getEmail()); ps.setString(3, member.getPassword()); - ps.setString(4, member.getRole()); + ps.setString(4, member.getRole().name()); return ps; }, keyHolder); - return new Member(keyHolder.getKey().longValue(), member.getName(), member.getEmail(), "USER"); + return new Member(keyHolder.getKey().longValue(), member.getName(), member.getEmail(), member.getRole()); } public Member findByEmailAndPassword(String email, String password) { @@ -34,7 +34,7 @@ public Member findByEmailAndPassword(String email, String password) { rs.getLong("id"), rs.getString("name"), rs.getString("email"), - rs.getString("role") + Role.valueOf(rs.getString("role")) ), email, password ); @@ -47,7 +47,7 @@ public Member findByName(String name) { rs.getLong("id"), rs.getString("name"), rs.getString("email"), - rs.getString("role") + Role.valueOf(rs.getString("role")) ), name ); diff --git a/src/main/java/roomescape/member/MemberRequest.java b/src/main/java/roomescape/member/MemberRequest.java index cafb79f14..50285d59f 100644 --- a/src/main/java/roomescape/member/MemberRequest.java +++ b/src/main/java/roomescape/member/MemberRequest.java @@ -1,19 +1,4 @@ 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 b9fa3b97a..c1babc0c9 100644 --- a/src/main/java/roomescape/member/MemberResponse.java +++ b/src/main/java/roomescape/member/MemberResponse.java @@ -1,25 +1,4 @@ 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 b83366cb8..d9808f127 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -1,51 +1,18 @@ package roomescape.member; -import jakarta.servlet.http.Cookie; import org.springframework.stereotype.Service; -import roomescape.auth.LoginMember; -import roomescape.token.TokenParser; -import roomescape.token.TokenProvider; -import roomescape.token.TokenResponse; @Service public class MemberService { private final MemberDao memberDao; - private final TokenProvider tokenProvider; - private final TokenParser tokenParser; - public MemberService(final MemberDao memberDao, final TokenProvider tokenProvider, final TokenParser tokenParser) { + public MemberService(final MemberDao memberDao) { this.memberDao = memberDao; - this.tokenProvider = tokenProvider; - this.tokenParser = tokenParser; } 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(), Role.USER)); return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } - public TokenResponse getAccessToken(String email, String password) { - Member member = getMember(email, password); - String token = tokenProvider.createAccessToken(member); - return new TokenResponse(token); - } - - public LoginMember parseTokenAndGetMemberInfo(Cookie[] cookies) { - - String token = ""; - for (Cookie cookie : cookies) { - if (cookie.getName().equals("token")) - token = cookie.getValue(); - } - - if (token.isEmpty()) throw new IllegalArgumentException("로그인하지 않은 사용자입니다."); - - return tokenParser.paseMemberInfo(token); - } - - private Member getMember(String email, String password) { - Member member = memberDao.findByEmailAndPassword(email, password); - if (member == null) throw new IllegalArgumentException("등록되지 않은 사용자입니다."); - return member; - } } diff --git a/src/main/java/roomescape/member/Role.java b/src/main/java/roomescape/member/Role.java new file mode 100644 index 000000000..599d59e6d --- /dev/null +++ b/src/main/java/roomescape/member/Role.java @@ -0,0 +1,6 @@ +package roomescape.member; + +public enum Role { + USER, + ADMIN +} diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index 1456e29d4..5d1757e59 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -20,7 +20,8 @@ public ReservationResponse save(ReservationRequest reservationRequest, LoginMemb } private ReservationRequest replaceNameIfEmpty(ReservationRequest request, LoginMember loginMember) { - if (request.getName() == null || request.getName().isBlank()) { + String requestName = request.getName(); + if (requestName == null || requestName.isBlank()) { return new ReservationRequest(loginMember.getName(), request.getDate(), request.getTheme(), request.getTime()); } return request; diff --git a/src/main/java/roomescape/token/TokenParser.java b/src/main/java/roomescape/token/TokenParser.java index 2b99442b6..4122ee2f6 100644 --- a/src/main/java/roomescape/token/TokenParser.java +++ b/src/main/java/roomescape/token/TokenParser.java @@ -15,7 +15,7 @@ public class TokenParser { @Value("${roomescape.auth.jwt.secret}") private String secretKey; - public LoginMember paseMemberInfo(String token) { + public LoginMember parseMemberInfo(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(Base64.getDecoder().decode(secretKey))) .build() @@ -29,7 +29,6 @@ public LoginMember paseMemberInfo(String token) { return new LoginMember(id, name, email, role); - } } diff --git a/src/main/java/roomescape/token/TokenProvider.java b/src/main/java/roomescape/token/TokenProvider.java index bf3a8a37d..2ab8e2ab0 100644 --- a/src/main/java/roomescape/token/TokenProvider.java +++ b/src/main/java/roomescape/token/TokenProvider.java @@ -26,7 +26,6 @@ public String createAccessToken(Member member) { .setSubject(Long.toString(member.getId())) .claim("name", member.getName()) .claim("role", member.getRole()) - .claim("email", member.getEmail()) .setIssuedAt(now) .setExpiration(validity) .signWith( diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html index 8965f2ddc..23ad77a3c 100644 --- a/src/main/resources/templates/signup.html +++ b/src/main/resources/templates/signup.html @@ -1,65 +1,66 @@ - - - Signup - - - + + + Signup + + + +;
-

Signup

-
-
- - -
-
- - -
-
- - -
- -
+

Signup

+
+
+ + +
+
+ + +
+
+ + +
+ +
From 86293d3e91c92ec46634166f4fb38b236b7dc07a Mon Sep 17 00:00:00 2001 From: kimYejin Date: Fri, 25 Jul 2025 16:09:56 +0900 Subject: [PATCH 05/12] =?UTF-8?q?fix=20:=20web=20config=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/config/WebConfig.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/roomescape/config/WebConfig.java b/src/main/java/roomescape/config/WebConfig.java index cbee87fe2..8a6c76c44 100644 --- a/src/main/java/roomescape/config/WebConfig.java +++ b/src/main/java/roomescape/config/WebConfig.java @@ -35,7 +35,14 @@ public void addInterceptors(final InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor) .addPathPatterns("/**") - .excludePathPatterns("/", "/signup", "/signup/**", "/login", "/login/**", "/logout", "/error"); + .excludePathPatterns( + "/", + "/signup", + "/signup/**", + "/login", + "/logout", + "/error" + ); } From 52ccca621e7c8e962e6abf6571b9794724eddd5e Mon Sep 17 00:00:00 2001 From: kimYejin Date: Tue, 5 Aug 2025 09:35:24 +0900 Subject: [PATCH 06/12] =?UTF-8?q?refact,fix=20:=20jwt=5Fsecret=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=20=EB=B3=80=EC=88=98=20=EB=93=B1=EB=A1=9D=20=EB=B0=8F?= =?UTF-8?q?=20WebConfig=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 1 + .gitignore | 4 ++++ .../java/roomescape/auth/AuthController.java | 18 +++++++++++------- src/main/java/roomescape/config/WebConfig.java | 11 +++-------- src/main/resources/application.properties | 5 ++--- 5 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 000000000..9e30472eb --- /dev/null +++ b/.env @@ -0,0 +1 @@ +JWT_SECRET=Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= diff --git a/.gitignore b/.gitignore index c2065bc26..22665389e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ out/ ### VS Code ### .vscode/ + + +### env ### +.env diff --git a/src/main/java/roomescape/auth/AuthController.java b/src/main/java/roomescape/auth/AuthController.java index 123c1d144..a2bf828f2 100644 --- a/src/main/java/roomescape/auth/AuthController.java +++ b/src/main/java/roomescape/auth/AuthController.java @@ -1,7 +1,6 @@ package roomescape.auth; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseCookie; @@ -24,12 +23,16 @@ public AuthController(AuthService authService) { @PostMapping("/logout") public ResponseEntity logout(HttpServletResponse response) { - Cookie cookie = new Cookie("token", ""); - cookie.setHttpOnly(true); - cookie.setPath("/"); - cookie.setMaxAge(0); - response.addCookie(cookie); - return ResponseEntity.ok().build(); + + ResponseCookie responseCookie = ResponseCookie.from("token", "") + .httpOnly(true) + .path("/") + .maxAge(0) + .build(); + + return ResponseEntity.ok() + .header(HttpHeaders.SET_COOKIE, responseCookie.toString()) + .build(); } @GetMapping("/login/check") @@ -43,6 +46,7 @@ public ResponseEntity loginCheck(LoginMember loginMember) { @PostMapping("/login") public ResponseEntity login(@RequestBody LoginRequest loginRequest) { + TokenResponse token = authService.createToken(loginRequest.email(), loginRequest.password()); ResponseCookie cookie = ResponseCookie.from("token", token.getAccessToken()) .httpOnly(true) diff --git a/src/main/java/roomescape/config/WebConfig.java b/src/main/java/roomescape/config/WebConfig.java index 8a6c76c44..6edccb4b0 100644 --- a/src/main/java/roomescape/config/WebConfig.java +++ b/src/main/java/roomescape/config/WebConfig.java @@ -30,20 +30,15 @@ public void addArgumentResolvers(List resolvers) @Override public void addInterceptors(final InterceptorRegistry registry) { + registry.addInterceptor(adminRoleCheckInterceptor) .addPathPatterns("/admin/**"); registry.addInterceptor(tokenInterceptor) .addPathPatterns("/**") .excludePathPatterns( - "/", - "/signup", - "/signup/**", - "/login", - "/logout", - "/error" + "/", "/signup", "/signup/**", "/login", "/logout", "/error", + "/css/**", "/js/**", "/images/**", "/favicon.ico", "/image/**", "/h2-console/**" ); - - } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index df2d5c887..31e4f4f7b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -2,10 +2,9 @@ spring.sql.init.encoding=utf-8 spring.h2.console.enabled=true spring.h2.console.path=/h2-console spring.datasource.url=jdbc:h2:mem:database - -roomescape.auth.jwt.secret= Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= +spring.config.import=optional:file:.env[.properties] +roomescape.auth.jwt.secret=${JWT_SECRET} roomescape.auth.jwt.token.expire-length=3600000 - #spring.jpa.show-sql=true #spring.jpa.properties.hibernate.format_sql=true #spring.jpa.ddl-auto=create-drop From 476dee3d932bd50ab39d0d85b13c96136c695799 Mon Sep 17 00:00:00 2001 From: kimYejin Date: Tue, 12 Aug 2025 10:01:02 +0900 Subject: [PATCH 07/12] =?UTF-8?q?refact=20:=20jpa=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- src/main/java/roomescape/member/Member.java | 20 +++ .../roomescape/reservation/Reservation.java | 31 +++-- .../reservation/ReservationDao.java | 14 +- .../reservation/ReservationService.java | 4 +- src/main/java/roomescape/theme/Theme.java | 9 ++ .../roomescape/time/ParticipationTime.java | 37 ++++++ .../time/ParticipationTimeController.java | 42 ++++++ ...TimeDao.java => ParticipationTimeDao.java} | 14 +- .../time/ParticipationTimeRepository.java | 11 ++ ...ice.java => ParticipationTimeService.java} | 24 ++-- src/main/java/roomescape/time/Time.java | 27 ---- .../java/roomescape/time/TimeController.java | 48 ------- src/main/resources/application.properties | 9 +- src/main/resources/data.sql | 21 +++ src/main/resources/schema.sql | 60 --------- src/main/resources/templates/admin/index.html | 84 ++++++------ .../templates/admin/reservation.html | 120 +++++++++--------- src/main/resources/templates/admin/theme.html | 116 ++++++++--------- src/main/resources/templates/admin/time.html | 116 ++++++++--------- src/main/resources/templates/reservation.html | 10 +- src/test/java/roomescape/JpaTest.java | 35 +++++ 22 files changed, 455 insertions(+), 399 deletions(-) create mode 100644 src/main/java/roomescape/time/ParticipationTime.java create mode 100644 src/main/java/roomescape/time/ParticipationTimeController.java rename src/main/java/roomescape/time/{TimeDao.java => ParticipationTimeDao.java} (70%) create mode 100644 src/main/java/roomescape/time/ParticipationTimeRepository.java rename src/main/java/roomescape/time/{TimeService.java => ParticipationTimeService.java} (53%) delete mode 100644 src/main/java/roomescape/time/Time.java delete mode 100644 src/main/java/roomescape/time/TimeController.java create mode 100644 src/main/resources/data.sql 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 8d52aebc6..9bc129a0e 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ 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-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 9ec10f04a..bf9c63d41 100644 --- a/src/main/java/roomescape/member/Member.java +++ b/src/main/java/roomescape/member/Member.java @@ -1,10 +1,26 @@ package roomescape.member; + +import jakarta.persistence.*; + +@Entity public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Column(nullable = false) private String name; + + @Column(nullable = false) private String email; + + @Column(nullable = false) private String password; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) private Role role; public Member(Long id, String name, String email, Role role) { @@ -21,6 +37,10 @@ public Member(String name, String email, String password, Role role) { this.role = role; } + public Member() { + + } + public Long getId() { return id; } diff --git a/src/main/java/roomescape/reservation/Reservation.java b/src/main/java/roomescape/reservation/Reservation.java index 83a7edf1b..60520eb0f 100644 --- a/src/main/java/roomescape/reservation/Reservation.java +++ b/src/main/java/roomescape/reservation/Reservation.java @@ -1,27 +1,42 @@ package roomescape.reservation; +import jakarta.persistence.*; import roomescape.theme.Theme; -import roomescape.time.Time; +import roomescape.time.ParticipationTime; +@Entity public class Reservation { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Column(nullable = false) private String name; + + @Column(nullable = false) private String date; - private Time time; + + @OneToOne + @JoinColumn(name = "time_id") + private ParticipationTime participationTime; + + @ManyToOne + @JoinColumn(name = "theme_id") private Theme theme; - public Reservation(Long id, String name, String date, Time time, Theme theme) { + public Reservation(Long id, String name, String date, ParticipationTime participationTime, Theme theme) { this.id = id; this.name = name; this.date = date; - this.time = time; + this.participationTime = participationTime; this.theme = theme; } - public Reservation(String name, String date, Time time, Theme theme) { + public Reservation(String name, String date, ParticipationTime participationTime, Theme theme) { this.name = name; this.date = date; - this.time = time; + this.participationTime = participationTime; this.theme = theme; } @@ -41,8 +56,8 @@ public String getDate() { return date; } - public Time getTime() { - return time; + public ParticipationTime getTime() { + return participationTime; } public Theme getTheme() { diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java index a4972430c..346e40871 100644 --- a/src/main/java/roomescape/reservation/ReservationDao.java +++ b/src/main/java/roomescape/reservation/ReservationDao.java @@ -5,7 +5,7 @@ import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; import roomescape.theme.Theme; -import roomescape.time.Time; +import roomescape.time.ParticipationTime; import java.sql.PreparedStatement; import java.util.List; @@ -32,7 +32,7 @@ public List findAll() { rs.getLong("reservation_id"), rs.getString("reservation_name"), rs.getString("reservation_date"), - new Time( + new ParticipationTime( rs.getLong("time_id"), rs.getString("time_value") ), @@ -54,8 +54,8 @@ public Reservation save(ReservationRequest reservationRequest) { return ps; }, keyHolder); - Time time = jdbcTemplate.queryForObject("SELECT * FROM time WHERE id = ?", - (rs, rowNum) -> new Time(rs.getLong("id"), rs.getString("time_value")), + ParticipationTime participationTime = jdbcTemplate.queryForObject("SELECT * FROM time WHERE id = ?", + (rs, rowNum) -> new ParticipationTime(rs.getLong("id"), rs.getString("time_value")), reservationRequest.getTime()); Theme theme = jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", @@ -66,7 +66,7 @@ public Reservation save(ReservationRequest reservationRequest) { keyHolder.getKey().longValue(), reservationRequest.getName(), reservationRequest.getDate(), - time, + participationTime, theme ); } @@ -89,7 +89,7 @@ public List findReservationsByDateAndTheme(String date, Long themeI rs.getLong("reservation_id"), rs.getString("reservation_name"), rs.getString("reservation_date"), - new Time( + new ParticipationTime( rs.getLong("time_id"), rs.getString("time_value") ), @@ -114,7 +114,7 @@ public List findByDateAndThemeId(String date, Long themeId) { rs.getLong("reservation_id"), rs.getString("reservation_name"), rs.getString("reservation_date"), - new Time( + new ParticipationTime( rs.getLong("time_id"), rs.getString("time_value") ), diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index 5d1757e59..7e9aacf07 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -16,7 +16,7 @@ public ReservationService(ReservationDao reservationDao) { public ReservationResponse save(ReservationRequest reservationRequest, LoginMember loginMember) { reservationRequest = replaceNameIfEmpty(reservationRequest, loginMember); 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.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getTime()); } private ReservationRequest replaceNameIfEmpty(ReservationRequest request, LoginMember loginMember) { @@ -33,7 +33,7 @@ public void deleteById(Long id) { public List findAll() { return reservationDao.findAll().stream() - .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())) + .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 430a6239c..8be4abd92 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/time/ParticipationTime.java b/src/main/java/roomescape/time/ParticipationTime.java new file mode 100644 index 000000000..d5e1dd151 --- /dev/null +++ b/src/main/java/roomescape/time/ParticipationTime.java @@ -0,0 +1,37 @@ +package roomescape.time; + +import jakarta.persistence.*; + +@Entity +public class ParticipationTime { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String time; + + public ParticipationTime(Long id, String time) { + this.id = id; + this.time = time; + } + + public ParticipationTime(String time) { + this.time = time; + } + + public ParticipationTime() { + + } + + public Long getId() { + return id; + } + + public String getTime() { + return time; + } + + +} diff --git a/src/main/java/roomescape/time/ParticipationTimeController.java b/src/main/java/roomescape/time/ParticipationTimeController.java new file mode 100644 index 000000000..a22dc2278 --- /dev/null +++ b/src/main/java/roomescape/time/ParticipationTimeController.java @@ -0,0 +1,42 @@ +package roomescape.time; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.net.URI; +import java.util.List; + +@RestController +public class ParticipationTimeController { + private ParticipationTimeService participationTimeService; + + public ParticipationTimeController(ParticipationTimeService participationTimeService) { + this.participationTimeService = participationTimeService; + } + + @GetMapping("/times") + public List list() { + return participationTimeService.findAll(); + } + + @PostMapping("/times") + public ResponseEntity create(@RequestBody ParticipationTime participationTime) { + if (participationTime.getTime() == null || participationTime.getTime().isEmpty()) { + throw new RuntimeException(); + } + + ParticipationTime newParticipationTime = participationTimeService.save(participationTime); + return ResponseEntity.created(URI.create("/times/" + newParticipationTime.getId())).body(newParticipationTime); + } + + @DeleteMapping("/times/{id}") + public ResponseEntity delete(@PathVariable Long id) { + participationTimeService.deleteById(id); + return ResponseEntity.noContent().build(); + } + + @GetMapping("/available-times") + public ResponseEntity> availableTimes(@RequestParam String date, @RequestParam Long themeId) { + return ResponseEntity.ok(participationTimeService.getAvailableTime(date, themeId)); + } +} diff --git a/src/main/java/roomescape/time/TimeDao.java b/src/main/java/roomescape/time/ParticipationTimeDao.java similarity index 70% rename from src/main/java/roomescape/time/TimeDao.java rename to src/main/java/roomescape/time/ParticipationTimeDao.java index f39a9a328..d3be61274 100644 --- a/src/main/java/roomescape/time/TimeDao.java +++ b/src/main/java/roomescape/time/ParticipationTimeDao.java @@ -9,30 +9,30 @@ import java.util.List; @Repository -public class TimeDao { +public class ParticipationTimeDao { private final JdbcTemplate jdbcTemplate; - public TimeDao(JdbcTemplate jdbcTemplate) { + public ParticipationTimeDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - public List