diff --git a/README.md b/README.md
new file mode 100644
index 000000000..130319fac
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+# π λ°©νμΆ μμ½ μλΉμ€
+
+
+
+
+
+
+## π μ£Όμ κΈ°λ₯
+
+1. **λ‘κ·ΈμΈ**: κ°μ
ν μ΄λ©μΌκ³Ό λΉλ°λ²νΈλ‘ λ‘κ·ΈμΈν μ μμ΅λλ€.
+
+2. **μμ½**: 'μμ½' νμ΄μ§μμ μνλ λ μ§, ν
λ§, μκ°μ μ ννκ³ 'μμ½νκΈ°' λ²νΌμ λλ₯΄λ©΄ μμ½μ΄ μλ£λ©λλ€.
+
+3. **μμ½ νμΈ λ° μ·¨μ**: 'λ΄ μμ½' νμ΄μ§μμ μμ½ λ΄μμ νμΈνκ±°λ, μμΉ μλ μμ½μ μ·¨μν μ μμ΅λλ€.
+
+4. **μμ½ λκΈ° μ μ²**: μμ½ λκΈ°λ₯Ό μ μ²νλ©΄, μ·¨μ μλ¦¬κ° μκ²Όμ λ μ°μ μ μΌλ‘ μμ½ν κΈ°νλ₯Ό λ립λλ€.
diff --git a/src/main/java/auth/JwtUtils.java b/src/main/java/auth/JwtUtils.java
new file mode 100644
index 000000000..45ed6d471
--- /dev/null
+++ b/src/main/java/auth/JwtUtils.java
@@ -0,0 +1,69 @@
+package auth;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+
+import javax.crypto.spec.SecretKeySpec;
+import java.security.Key;
+import java.util.Arrays;
+import java.util.Date;
+
+public class JwtUtils {
+
+ private final Key key;
+
+ public JwtUtils(String secretKey) {
+ this.key = new SecretKeySpec(secretKey.getBytes(), SignatureAlgorithm.HS256.getJcaName());
+ }
+
+ public String createToken(String id, String role) {
+ Claims claims = Jwts.claims().setSubject(id);
+ claims.put("role", role);
+ Date now = new Date();
+ Date validity = new Date(now.getTime() + 3600000);
+
+ return Jwts.builder()
+ .setClaims(claims)
+ .setIssuedAt(now)
+ .setExpiration(validity)
+ .signWith(key, SignatureAlgorithm.HS256)
+ .compact();
+ }
+
+ private Claims getClaims(String token) {
+ try {
+ Jws claims = Jwts.parserBuilder()
+ .setSigningKey(key)
+ .build()
+ .parseClaimsJws(token);
+ return claims.getBody();
+ } catch (JwtException | IllegalArgumentException e) {
+ throw new IllegalArgumentException("μ ν¨νμ§ μμ ν ν°μ
λλ€.");
+ }
+ }
+
+ public String getSubject(String token) {
+ return getClaims(token).getSubject();
+ }
+
+ public String getRole(String token) {
+ return getClaims(token).get("role", String.class);
+ }
+
+ public String getTokenFromCookie(HttpServletRequest request) {
+ Cookie[] cookies = request.getCookies();
+ if (cookies == null) {
+ return null;
+ }
+ return Arrays.stream(cookies)
+ .filter(cookie -> "token".equals(cookie.getName()))
+ .findFirst()
+ .map(Cookie::getValue)
+ .orElse(null);
+ }
+}
diff --git a/src/main/java/auth/annotation/Login.java b/src/main/java/auth/annotation/Login.java
new file mode 100644
index 000000000..d6ee3111d
--- /dev/null
+++ b/src/main/java/auth/annotation/Login.java
@@ -0,0 +1,11 @@
+package auth.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Login {
+}
diff --git a/src/main/java/roomescape/ExceptionController.java b/src/main/java/roomescape/ExceptionController.java
index 4e2450f9e..8c0a692d3 100644
--- a/src/main/java/roomescape/ExceptionController.java
+++ b/src/main/java/roomescape/ExceptionController.java
@@ -1,14 +1,33 @@
package roomescape;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
+import roomescape.auth.exception.UnauthenticatedException;
@ControllerAdvice
public class ExceptionController {
+
+ private static final Logger log = LoggerFactory.getLogger(ExceptionController.class);
+
@ExceptionHandler(Exception.class)
- public ResponseEntity handleRuntimeException(Exception e) {
- e.printStackTrace();
- return ResponseEntity.badRequest().build();
+ public ResponseEntity handleRuntimeException(Exception e) {
+ log.error("μμμΉ λͺ»ν μμΈκ° λ°μνμ΅λλ€.", e);
+ return ResponseEntity.internalServerError().body("μλ² λ΄λΆ μ€λ₯κ° λ°μνμ΅λλ€.");
+ }
+
+ @ExceptionHandler(UnauthenticatedException.class)
+ public ResponseEntity handleUnauthenticatedException(UnauthenticatedException e) {
+ log.error("μΈμ¦λμ§ μμ μμ²μ
λλ€.", e);
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
+ }
+
+ @ExceptionHandler(IllegalArgumentException.class)
+ public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) {
+ log.error("μλͺ»λ μμ²: {}", e.getMessage(), e);
+ return ResponseEntity.badRequest().body(e.getMessage());
}
}
diff --git a/src/main/java/roomescape/RoomescapeApplication.java b/src/main/java/roomescape/RoomescapeApplication.java
index 2ca0f743f..f63d4ca0f 100644
--- a/src/main/java/roomescape/RoomescapeApplication.java
+++ b/src/main/java/roomescape/RoomescapeApplication.java
@@ -3,7 +3,7 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-@SpringBootApplication
+@SpringBootApplication(scanBasePackages = {"roomescape", "auth"})
public class RoomescapeApplication {
public static void main(String[] args) {
SpringApplication.run(RoomescapeApplication.class, args);
diff --git a/src/main/java/roomescape/auth/AdminInterceptor.java b/src/main/java/roomescape/auth/AdminInterceptor.java
index 289db20c5..5c64339d2 100644
--- a/src/main/java/roomescape/auth/AdminInterceptor.java
+++ b/src/main/java/roomescape/auth/AdminInterceptor.java
@@ -1,35 +1,33 @@
package roomescape.auth;
-import jakarta.servlet.http.Cookie;
+import auth.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
-import java.util.Arrays;
-import java.util.Optional;
-
@Component
public class AdminInterceptor implements HandlerInterceptor {
- private final JwtTokenProvider jwtTokenProvider;
+ private final JwtUtils jwtUtils;
- public AdminInterceptor(JwtTokenProvider jwtTokenProvider) {
- this.jwtTokenProvider = jwtTokenProvider;
+ public AdminInterceptor(JwtUtils jwtUtils) {
+ this.jwtUtils = jwtUtils;
}
@Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- Optional tokenCookie = findTokenCookie(request.getCookies());
+ public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
+ String token = jwtUtils.getTokenFromCookie(request);
- if (tokenCookie.isEmpty()) {
+ if (token == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
try {
- String token = tokenCookie.get().getValue();
- String role = jwtTokenProvider.getRole(token);
+ String role = jwtUtils.getRole(token);
+
if (!"ADMIN".equals(role)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
@@ -41,13 +39,4 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
return true;
}
-
- private Optional findTokenCookie(Cookie[] cookies) {
- if (cookies == null) {
- return Optional.empty();
- }
- return Arrays.stream(cookies)
- .filter(cookie -> "token".equals(cookie.getName()))
- .findFirst();
- }
}
diff --git a/src/main/java/roomescape/auth/JwtTokenProvider.java b/src/main/java/roomescape/auth/JwtTokenProvider.java
deleted file mode 100644
index 58ec0a070..000000000
--- a/src/main/java/roomescape/auth/JwtTokenProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package roomescape.auth;
-
-import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.security.Keys;
-import jakarta.annotation.PostConstruct;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import roomescape.member.Member;
-
-import javax.crypto.SecretKey;
-
-@Component
-public class JwtTokenProvider {
-
- private SecretKey secretKey;
-
- @Value("${roomescape.auth.jwt.secret}")
- private String secret;
-
- @PostConstruct
- public void init() {
- this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
- }
-
- public String createToken(Member member) {
- return Jwts.builder()
- .setSubject(member.getId().toString())
- .claim("name", member.getName())
- .claim("role", member.getRole())
- .signWith(secretKey)
- .compact();
- }
-
- public String getSubject(String token) {
- return Jwts.parserBuilder()
- .setSigningKey(secretKey)
- .build()
- .parseClaimsJws(token)
- .getBody()
- .getSubject();
- }
-
- public String getRole(String token) {
- return Jwts.parserBuilder()
- .setSigningKey(secretKey)
- .build()
- .parseClaimsJws(token)
- .getBody()
- .get("role", String.class);
- }
-}
diff --git a/src/main/java/roomescape/auth/LoginController.java b/src/main/java/roomescape/auth/LoginController.java
index c3c67fc3b..b726bb082 100644
--- a/src/main/java/roomescape/auth/LoginController.java
+++ b/src/main/java/roomescape/auth/LoginController.java
@@ -1,5 +1,8 @@
package roomescape.auth;
+import auth.JwtUtils;
+import auth.annotation.Login;
+import roomescape.auth.dto.LoginMember;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.ResponseEntity;
@@ -8,22 +11,26 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import roomescape.auth.dto.LoginCheckResponse;
-import roomescape.auth.dto.LoginMember;
import roomescape.auth.dto.LoginRequest;
+import roomescape.member.Member;
import roomescape.member.MemberService;
@RestController
public class LoginController {
private final MemberService memberService;
+ private final JwtUtils jwtUtils;
- public LoginController(MemberService memberService) {
+ public LoginController(MemberService memberService, JwtUtils jwtUtils) {
this.memberService = memberService;
+ this.jwtUtils = jwtUtils;
}
@PostMapping("/login")
public ResponseEntity login(@RequestBody LoginRequest request, HttpServletResponse response) {
- String token = memberService.login(request);
+ Member member = memberService.login(request);
+
+ String token = jwtUtils.createToken(String.valueOf(member.getId()), member.getRole());
Cookie cookie = new Cookie("token", token);
cookie.setHttpOnly(true);
@@ -45,10 +52,7 @@ public ResponseEntity logout(HttpServletResponse response) {
}
@GetMapping("/login/check")
- public ResponseEntity checkLogin(LoginMember loginMember) {
- if (loginMember == null) {
- return ResponseEntity.status(401).build();
- }
+ public ResponseEntity checkLogin(@Login LoginMember loginMember) {
return ResponseEntity.ok(new LoginCheckResponse(loginMember.getName()));
}
}
diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java
deleted file mode 100644
index adefee7c2..000000000
--- a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java
+++ /dev/null
@@ -1,67 +0,0 @@
-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.auth.dto.LoginMember;
-import roomescape.auth.exception.UnauthenticatedException;
-import roomescape.member.Member;
-import roomescape.member.MemberService;
-
-import java.util.Arrays;
-
-@Component
-public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
-
- private final JwtTokenProvider jwtTokenProvider;
- private final MemberService memberService;
-
- public LoginMemberArgumentResolver(JwtTokenProvider jwtTokenProvider, MemberService memberService) {
- this.jwtTokenProvider = jwtTokenProvider;
- this.memberService = memberService;
- }
-
- @Override
- public boolean supportsParameter(MethodParameter parameter) {
- // 컨νΈλ‘€λ¬ λ©μλμ νλΌλ―Έν°κ° LoginMember νμ
μΌ λ μ΄ λ¦¬μ‘Έλ²λ₯Ό μ¬μ©νλλ‘ μ€μ
- return parameter.getParameterType().equals(LoginMember.class);
- }
-
- @Override
- public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
- NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
- HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
- if (request == null) {
- throw new UnauthenticatedException("μμ² μ 보λ₯Ό μ°Ύμ μ μμ΅λλ€.");
- }
-
- Cookie[] cookies = request.getCookies();
- if (cookies == null) {
- return null;
- }
-
- String token = Arrays.stream(cookies)
- .filter(cookie -> cookie.getName().equals("token"))
- .map(Cookie::getValue)
- .findFirst()
- .orElse(null);
-
- if (token == null) {
- return null; // ν ν°μ΄ μμΌλ©΄ null λ°ν
- }
-
- try {
- Long memberId = Long.valueOf(jwtTokenProvider.getSubject(token));
- Member member = memberService.findById(memberId);
- return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole());
- } catch (Exception e) {
- // ν ν°μ΄ μ ν¨νμ§ μμ κ²½μ° λ±
- throw new UnauthenticatedException("μ ν¨νμ§ μμ ν ν°μ
λλ€.", e);
- }
- }
-}
diff --git a/src/main/java/roomescape/auth/LoginUserArgumentResolver.java b/src/main/java/roomescape/auth/LoginUserArgumentResolver.java
new file mode 100644
index 000000000..aa488dc36
--- /dev/null
+++ b/src/main/java/roomescape/auth/LoginUserArgumentResolver.java
@@ -0,0 +1,56 @@
+package roomescape.auth;
+
+import auth.JwtUtils;
+import auth.annotation.Login;
+import roomescape.auth.dto.LoginMember;
+import io.jsonwebtoken.JwtException;
+import jakarta.servlet.http.HttpServletRequest;
+import org.jetbrains.annotations.NotNull;
+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.auth.exception.UnauthenticatedException;
+import roomescape.member.Member;
+import roomescape.member.MemberService;
+
+@Component
+public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
+
+ private final JwtUtils jwtUtils;
+ private final MemberService memberService;
+
+ public LoginUserArgumentResolver(JwtUtils jwtUtils, MemberService memberService) {
+ this.jwtUtils = jwtUtils;
+ this.memberService = memberService;
+ }
+
+ @Override
+ public boolean supportsParameter(MethodParameter parameter) {
+ return parameter.hasParameterAnnotation(Login.class)
+ && parameter.getParameterType().equals(LoginMember.class);
+ }
+
+ @Override
+ public Object resolveArgument(@NotNull MethodParameter parameter, ModelAndViewContainer mavContainer,
+ NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+ HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
+
+ String token = jwtUtils.getTokenFromCookie(request);
+
+ if (token == null) {
+ throw new UnauthenticatedException("λ‘κ·ΈμΈ ν ν°μ΄ μ‘΄μ¬νμ§ μμ΅λλ€.");
+ }
+
+ try {
+ Long memberId = Long.parseLong(jwtUtils.getSubject(token));
+ Member member = memberService.findById(memberId);
+
+ return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole());
+ } catch (JwtException| IllegalArgumentException e) {
+ throw new UnauthenticatedException("μ ν¨νμ§ μμ λ‘κ·ΈμΈ ν ν°μ
λλ€.", e);
+ }
+ }
+}
diff --git a/src/main/java/roomescape/auth/dto/LoginMember.java b/src/main/java/roomescape/auth/dto/LoginMember.java
index 2b4b10eab..28c838fa1 100644
--- a/src/main/java/roomescape/auth/dto/LoginMember.java
+++ b/src/main/java/roomescape/auth/dto/LoginMember.java
@@ -5,7 +5,7 @@
@Getter
@AllArgsConstructor
-public class LoginMember { // λ‘κ·ΈμΈν μ¬μ©μ μ 보λ₯Ό λ΄λ DTO
+public class LoginMember {
private Long id;
private String name;
diff --git a/src/main/java/roomescape/config/AuthConfig.java b/src/main/java/roomescape/config/AuthConfig.java
new file mode 100644
index 000000000..09abb6b06
--- /dev/null
+++ b/src/main/java/roomescape/config/AuthConfig.java
@@ -0,0 +1,18 @@
+package roomescape.config;
+
+import auth.JwtUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AuthConfig {
+
+ @Value("${roomescape.auth.jwt.secret}")
+ private String secretKey;
+
+ @Bean
+ public JwtUtils jwtUtils() {
+ return new JwtUtils(secretKey);
+ }
+}
diff --git a/src/main/java/roomescape/config/DataLoader.java b/src/main/java/roomescape/config/DataLoader.java
new file mode 100644
index 000000000..c817df913
--- /dev/null
+++ b/src/main/java/roomescape/config/DataLoader.java
@@ -0,0 +1,30 @@
+package roomescape.config;
+
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.annotation.Profile;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+@Profile("!test")
+@Component
+public class DataLoader implements CommandLineRunner {
+
+ private final JdbcTemplate jdbcTemplate;
+
+ public DataLoader(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
+ @Override
+ public void run(String... args) throws Exception {
+ jdbcTemplate.update("INSERT INTO member (name, email, password, role) VALUES ('μ΄λλ―Ό', 'admin@email.com', 'password', 'ADMIN')");
+
+ jdbcTemplate.update("INSERT INTO theme (name, description, thumbnail) VALUES ('곡ν¬', 'λ§€μ° λ¬΄μμ΄ ν
λ§', 'https://i.imgur.com/1.jpg')");
+ jdbcTemplate.update("INSERT INTO theme (name, description, thumbnail) VALUES ('μ½λ―Ή', 'λ§€μ° μκΈ΄ ν
λ§', 'https://i.imgur.com/2.jpg')");
+ jdbcTemplate.update("INSERT INTO theme (name, description, thumbnail) VALUES ('μ΄λλ²€μ²', 'μ λλ λͺ¨ν ν
λ§', 'https://i.imgur.com/3.jpg')");
+
+ jdbcTemplate.update("INSERT INTO time (time) VALUES ('10:00')");
+ jdbcTemplate.update("INSERT INTO time (time) VALUES ('13:00')");
+ jdbcTemplate.update("INSERT INTO time (time) VALUES ('15:00')");
+ }
+}
diff --git a/src/main/java/roomescape/config/TestDataLoader.java b/src/main/java/roomescape/config/TestDataLoader.java
new file mode 100644
index 000000000..34a2121e6
--- /dev/null
+++ b/src/main/java/roomescape/config/TestDataLoader.java
@@ -0,0 +1,40 @@
+package roomescape.config;
+
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.annotation.Profile;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+@Profile("test")
+@Component
+public class TestDataLoader implements CommandLineRunner {
+
+ private final JdbcTemplate jdbcTemplate;
+
+ public TestDataLoader(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
+ @Override
+ public void run(String... args) throws Exception {
+ // Member (ID: 1=μ΄λλ―Ό, 2=λΈλΌμ΄, 3=ν΄λ‘μ΄)
+ jdbcTemplate.update("INSERT INTO member (name, email, password, role) VALUES ('μ΄λλ―Ό', 'admin@email.com', 'password', 'ADMIN')");
+ jdbcTemplate.update("INSERT INTO member (name, email, password, role) VALUES ('λΈλΌμ΄', 'brown@email.com', 'password', 'USER')");
+ jdbcTemplate.update("INSERT INTO member (name, email, password, role) VALUES ('ν΄λ‘μ΄', 'chloe@email.com', 'password', 'USER')");
+
+ // Theme
+ jdbcTemplate.update("INSERT INTO theme (name, description, thumbnail) VALUES ('곡ν¬', 'λ§€μ° λ¬΄μμ΄ ν
λ§', 'https://i.imgur.com/1.jpg')");
+ jdbcTemplate.update("INSERT INTO theme (name, description, thumbnail) VALUES ('μ½λ―Ή', 'λ§€μ° μκΈ΄ ν
λ§', 'https://i.imgur.com/2.jpg')");
+ jdbcTemplate.update("INSERT INTO theme (name, description, thumbnail) VALUES ('μ΄λλ²€μ²', 'μ λλ λͺ¨ν ν
λ§', 'https://i.imgur.com/3.jpg')");
+
+ // Time
+ jdbcTemplate.update("INSERT INTO time (time) VALUES ('10:00')");
+ jdbcTemplate.update("INSERT INTO time (time) VALUES ('13:00')");
+ jdbcTemplate.update("INSERT INTO time (time) VALUES ('15:00')");
+
+ // Reservation
+ jdbcTemplate.update("INSERT INTO reservation (date, member_id, theme_id, time_id) VALUES ('2024-03-01', 1, 1, 1)");
+ jdbcTemplate.update("INSERT INTO reservation (date, member_id, theme_id, time_id) VALUES ('2024-03-01', 1, 1, 1)");
+ jdbcTemplate.update("INSERT INTO reservation (date, member_id, theme_id, time_id) VALUES ('2024-03-01', 1, 1, 1)");
+ }
+}
diff --git a/src/main/java/roomescape/config/WebConfig.java b/src/main/java/roomescape/config/WebConfig.java
index 91c824905..7949d0afb 100644
--- a/src/main/java/roomescape/config/WebConfig.java
+++ b/src/main/java/roomescape/config/WebConfig.java
@@ -1,34 +1,33 @@
package roomescape.config;
+import roomescape.auth.LoginUserArgumentResolver;
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.AdminInterceptor;
-import roomescape.auth.LoginMemberArgumentResolver;
import java.util.List;
@Configuration
public class WebConfig implements WebMvcConfigurer {
- private final LoginMemberArgumentResolver loginMemberArgumentResolver;
+ private final LoginUserArgumentResolver loginUserArgumentResolver;
private final AdminInterceptor adminInterceptor;
- public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver,
- AdminInterceptor adminInterceptor) {
- this.loginMemberArgumentResolver = loginMemberArgumentResolver;
+ public WebConfig(LoginUserArgumentResolver loginUserArgumentResolver, AdminInterceptor adminInterceptor) {
+ this.loginUserArgumentResolver = loginUserArgumentResolver;
this.adminInterceptor = adminInterceptor;
}
@Override
public void addArgumentResolvers(List resolvers) {
- resolvers.add(loginMemberArgumentResolver);
+ resolvers.add(loginUserArgumentResolver);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(adminInterceptor) // λ±λ‘ν μΈν°μ
ν° μ§μ
- .addPathPatterns("/admin/**"); // μΈν°μ
ν°λ₯Ό μ μ©ν URL ν¨ν΄ μ§μ
+ registry.addInterceptor(adminInterceptor)
+ .addPathPatterns("/admin/**");
}
}
diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java
index 9244e5ac1..1a45f3e71 100644
--- a/src/main/java/roomescape/member/MemberService.java
+++ b/src/main/java/roomescape/member/MemberService.java
@@ -2,7 +2,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import roomescape.auth.JwtTokenProvider;
import roomescape.auth.dto.LoginRequest;
import roomescape.member.dto.MemberRequest;
import roomescape.member.dto.MemberResponse;
@@ -11,11 +10,9 @@
public class MemberService {
private final MemberRepository memberRepository;
- private final JwtTokenProvider jwtTokenProvider;
- public MemberService(MemberRepository memberRepository, JwtTokenProvider jwtTokenProvider) {
+ public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
- this.jwtTokenProvider = jwtTokenProvider;
}
@Transactional
@@ -27,7 +24,7 @@ public MemberResponse createMember(MemberRequest memberRequest) {
}
@Transactional(readOnly = true)
- public String login(LoginRequest request) {
+ public Member login(LoginRequest request) { // λ°ν νμ
μ String(ν ν°)μμ Memberλ‘ λ³κ²½
Member member = memberRepository.findByEmail(request.getEmail())
.orElseThrow(() -> new IllegalArgumentException("μ΄λ©μΌ λλ λΉλ°λ²νΈκ° μΌμΉνμ§ μμ΅λλ€."));
@@ -35,7 +32,7 @@ public String login(LoginRequest request) {
throw new IllegalArgumentException("μ΄λ©μΌ λλ λΉλ°λ²νΈκ° μΌμΉνμ§ μμ΅λλ€.");
}
- return jwtTokenProvider.createToken(member);
+ return member;
}
public Member findById(Long id) {
diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java
index 9fa4cf0c6..9ce38ffce 100644
--- a/src/main/java/roomescape/reservation/ReservationController.java
+++ b/src/main/java/roomescape/reservation/ReservationController.java
@@ -1,5 +1,7 @@
package roomescape.reservation;
+import auth.annotation.Login;
+import roomescape.auth.dto.LoginMember;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -7,7 +9,6 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
-import roomescape.auth.dto.LoginMember;
import roomescape.reservation.dto.MyReservationResponse;
import roomescape.reservation.dto.ReservationRequest;
import roomescape.reservation.dto.ReservationResponse;
@@ -30,7 +31,7 @@ public ResponseEntity> list() {
}
@PostMapping("/reservations")
- public ResponseEntity create(@RequestBody ReservationRequest request, LoginMember loginMember) {
+ public ResponseEntity create(@RequestBody ReservationRequest request, @Login LoginMember loginMember) {
ReservationResponse reservation = reservationService.create(request, loginMember);
return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation);
}
@@ -48,8 +49,8 @@ public ResponseEntity delete(@PathVariable Long id) {
}
@GetMapping("/reservations-mine")
- public ResponseEntity> findMyReservations(LoginMember loginMember) {
- List myReservations = reservationService.findMyReservations(loginMember);
+ public ResponseEntity> findMyReservations(@Login LoginMember loginMember) {
+ List myReservations = reservationService.findMyReservations(loginMember.getId());
return ResponseEntity.ok(myReservations);
}
}
diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java
index 4aedc852c..7d15c6256 100644
--- a/src/main/java/roomescape/reservation/ReservationService.java
+++ b/src/main/java/roomescape/reservation/ReservationService.java
@@ -1,8 +1,8 @@
package roomescape.reservation;
+import roomescape.auth.dto.LoginMember;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import roomescape.auth.dto.LoginMember;
import roomescape.member.Member;
import roomescape.member.MemberRepository;
import roomescape.reservation.dto.MyReservationResponse;
@@ -80,9 +80,9 @@ private Member determineReservationHolder(ReservationRequest request, Member log
return loggedInUser;
}
- public List findMyReservations(LoginMember loginMember) {
- List reservations = reservationRepository.findWithDetailsByMemberId(loginMember.getId());
- List waitings = waitingRepository.findWaitingsWithRankByMemberId(loginMember.getId());
+ public List findMyReservations(Long memberId) {
+ List reservations = reservationRepository.findWithDetailsByMemberId(memberId);
+ List waitings = waitingRepository.findWaitingsWithRankByMemberId(memberId);
Stream reservationResponses = reservations.stream()
.map(r -> MyReservationResponse.from(r, r.getTheme(), r.getTime()));
diff --git a/src/main/java/roomescape/waiting/WaitingController.java b/src/main/java/roomescape/waiting/WaitingController.java
index d03329722..f5373f61e 100644
--- a/src/main/java/roomescape/waiting/WaitingController.java
+++ b/src/main/java/roomescape/waiting/WaitingController.java
@@ -1,5 +1,7 @@
package roomescape.waiting;
+import auth.annotation.Login;
+import roomescape.auth.dto.LoginMember;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -7,7 +9,6 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import roomescape.auth.dto.LoginMember;
import roomescape.waiting.dto.WaitingRequest;
import roomescape.waiting.dto.WaitingResponse;
@@ -24,7 +25,7 @@ public WaitingController(WaitingService waitingService) {
}
@PostMapping
- public ResponseEntity createWaiting(@RequestBody WaitingRequest request, LoginMember loginMember) {
+ public ResponseEntity createWaiting(@RequestBody WaitingRequest request, @Login LoginMember loginMember) {
WaitingResponse response = waitingService.create(request, loginMember);
return ResponseEntity.created(URI.create("/waitings/" + response.getId())).body(response);
}
diff --git a/src/main/java/roomescape/waiting/WaitingService.java b/src/main/java/roomescape/waiting/WaitingService.java
index cefaf5fa7..8e91d74cd 100644
--- a/src/main/java/roomescape/waiting/WaitingService.java
+++ b/src/main/java/roomescape/waiting/WaitingService.java
@@ -1,8 +1,9 @@
package roomescape.waiting;
+import auth.annotation.Login;
+import roomescape.auth.dto.LoginMember;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import roomescape.auth.dto.LoginMember;
import roomescape.auth.exception.UnauthenticatedException;
import roomescape.member.Member;
import roomescape.member.MemberRepository;
@@ -34,7 +35,7 @@ public WaitingService(WaitingRepository waitingRepository, ReservationRepository
}
@Transactional
- public WaitingResponse create(WaitingRequest request, LoginMember loginMember) {
+ public WaitingResponse create(WaitingRequest request, @Login LoginMember loginMember) {
Member member = memberRepository.findById(loginMember.getId())
.orElseThrow(() -> new IllegalArgumentException("μ¬μ©μ μ 보λ₯Ό μ°Ύμ μ μμ΅λλ€."));
diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql
deleted file mode 100644
index de8f21cf9..000000000
--- a/src/main/resources/data.sql
+++ /dev/null
@@ -1,15 +0,0 @@
-INSERT INTO member (name, email, password, role) VALUES ('μ΄λλ―Ό', 'admin@email.com', 'password', 'ADMIN');
-INSERT INTO member (name, email, password, role) VALUES ('ν΄λ‘μ΄', 'chloe@email.com', 'password', 'USER');
-INSERT INTO member (name, email, password, role) VALUES ('λΈλΌμ΄', 'brown@email.com', 'password', 'USER');
-
-INSERT INTO theme (name, description, thumbnail) VALUES ('곡ν¬', 'λ§€μ° λ¬΄μμ΄ ν
λ§', 'https://i.imgur.com/1.jpg');
-INSERT INTO theme (name, description, thumbnail) VALUES ('μ½λ―Ή', 'λ§€μ° μκΈ΄ ν
λ§', 'https://i.imgur.com/2.jpg');
-INSERT INTO theme (name, description, thumbnail) VALUES ('μ΄λλ²€μ²', 'μ λλ λͺ¨ν ν
λ§', 'https://i.imgur.com/3.jpg');
-
-INSERT INTO time (time) VALUES ('10:00');
-INSERT INTO time (time) VALUES ('13:00');
-INSERT INTO time (time) VALUES ('15:00');
-
-INSERT INTO reservation (member_id, date, time_id, theme_id) VALUES (1, '2024-03-01', 1, 1);
-INSERT INTO reservation (member_id, date, time_id, theme_id) VALUES (1, '2024-03-01', 2, 2);
-INSERT INTO reservation (member_id, date, time_id, theme_id) VALUES (1, '2024-03-01', 3, 3);
diff --git a/src/main/resources/static/js/reservation.js b/src/main/resources/static/js/reservation.js
index 8eaf9e47e..0f73eebef 100644
--- a/src/main/resources/static/js/reservation.js
+++ b/src/main/resources/static/js/reservation.js
@@ -131,9 +131,9 @@ function saveRow(event) {
const row = event.target.parentNode.parentNode;
const nameInput = row.querySelector('input[type="text"]');
- const themeSelect = row.querySelector('select');
+ const themeSelect = row.querySelector('#theme-select');
const dateInput = row.querySelector('input[type="date"]');
- const timeSelect = row.querySelector('select');
+ const timeSelect = row.querySelector('#time-select');
const reservation = {
name: nameInput.value,
diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java
index 8011fe97b..898b640e8 100644
--- a/src/test/java/roomescape/MissionStepTest.java
+++ b/src/test/java/roomescape/MissionStepTest.java
@@ -1,12 +1,17 @@
package roomescape;
+import auth.JwtUtils;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
+import org.assertj.core.api.AssertionsForClassTypes;
import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ActiveProfiles;
import roomescape.reservation.dto.MyReservationResponse;
import roomescape.reservation.dto.ReservationResponse;
import roomescape.waiting.dto.WaitingResponse;
@@ -17,6 +22,7 @@
import static org.assertj.core.api.Assertions.assertThat;
+@ActiveProfiles("test")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class MissionStepTest {
@@ -155,4 +161,18 @@ private String createToken(String email, String password) {
assertThat(status).isEqualTo("1λ²μ§Έ μμ½λκΈ°");
}
+
+ @Test
+ void μΉ λ¨κ³() {
+ Component componentAnnotation = JwtUtils.class.getAnnotation(Component.class);
+ assertThat(componentAnnotation).isNull();
+ }
+
+ @Value("${roomescape.auth.jwt.secret}")
+ private String secretKey;
+
+ @Test
+ void νλ¨κ³() {
+ AssertionsForClassTypes.assertThat(secretKey).isNotBlank();
+ }
}