From 766511b21efaa3fd18b29496aa5d531e66f5793b Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:23:39 +0900 Subject: [PATCH 01/55] =?UTF-8?q?[chore]=20=EC=9D=B8=EC=A6=9D=20=EC=9D=B8?= =?UTF-8?q?=EA=B0=80=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=A3=BC=EC=9E=85=20(#30)=20-=20JWT=20-=20OAuth2?= =?UTF-8?q?=20-=20Spring=20Security?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 49 ++++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/build.gradle b/build.gradle index 6dd1f49ee..312bc961e 100644 --- a/build.gradle +++ b/build.gradle @@ -24,53 +24,32 @@ repositories { } dependencies { - // Spring Boot 스타터 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' -// implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' - // Lombok compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' - // Runtime DB 드라이버 + //Security + implementation 'org.springframework.boot:spring-boot-starter-security' + + // JWT + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + + // OAuth2 + implementation 'org.springframework.security:spring-security-oauth2-client' + runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' - - // Test + annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.assertj:assertj-core:3.24.2' testImplementation 'org.springframework.boot:spring-boot-starter-test' -// testImplementation 'org.springframework.security:spring-security-test' + testImplementation 'org.springframework.security:spring-security-test' testImplementation 'com.h2database:h2:2.1.214' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - - // Querydsl - implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' - annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" - annotationProcessor "jakarta.persistence:jakarta.persistence-api" -} - -def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile - -// QueryDSL Q-클래스는 compileJava 단계에서만 생성 -tasks.named('compileJava', org.gradle.api.tasks.compile.JavaCompile) { - options.generatedSourceOutputDirectory.set(querydslDir) -} - -// test 컴파일 단계에서는 어노테이션 프로세서를 비워서 Q-클래스 중복 생성 방지 -tasks.named('compileTestJava', org.gradle.api.tasks.compile.JavaCompile) { - options.annotationProcessorPath = files() -} - -sourceSets { - main.java.srcDirs += [ querydslDir ] -} - -clean.doLast { - file(querydslDir).deleteDir() } tasks.named('test') { From 6e6f6eed188fcaadd4e030b5b99bbe45e63a316b Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:26:43 +0900 Subject: [PATCH 02/55] =?UTF-8?q?[feat]=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EC=A0=95=EC=9D=98=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/constant/JwtAuthParameters.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/constant/JwtAuthParameters.java diff --git a/src/main/java/konkuk/thip/common/security/constant/JwtAuthParameters.java b/src/main/java/konkuk/thip/common/security/constant/JwtAuthParameters.java new file mode 100644 index 000000000..ee2b49649 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/constant/JwtAuthParameters.java @@ -0,0 +1,18 @@ +package konkuk.thip.common.security.constant; + +import lombok.Getter; + +@Getter +public enum JwtAuthParameters { + JWT_HEADER_KEY("Authorization"), + JWT_PREFIX("Bearer "), + + ; + + private final String value; + + JwtAuthParameters(String value) { + this.value = value; + } +} + From 775093a5fa7b7d4e57d18024e758e2133afbf1f6 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:27:23 +0900 Subject: [PATCH 03/55] =?UTF-8?q?[feat]=20Oauth2=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=EC=9D=84=20=ED=8C=8C=EC=8B=B1=ED=95=98=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20UserDetails=20=ED=81=B4=EB=9E=98=EC=8A=A4=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/oauth2/GoogleUserDetails.java | 31 +++++++++++++++++++ .../security/oauth2/KakaoUserDetails.java | 30 ++++++++++++++++++ .../security/oauth2/OAuth2UserDetails.java | 8 +++++ 3 files changed, 69 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java create mode 100644 src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java create mode 100644 src/main/java/konkuk/thip/common/security/oauth2/OAuth2UserDetails.java diff --git a/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java new file mode 100644 index 000000000..00b8d35ea --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java @@ -0,0 +1,31 @@ +package konkuk.thip.common.security.oauth2; + +import java.util.Map; + +public class GoogleUserDetails implements OAuth2UserDetails{ + + private final Map attribute; + + public GoogleUserDetails(Map attribute) { + + this.attribute = attribute; + } + + @Override + public String getProvider() { + + return "google"; + } + + @Override + public String getProviderId() { + + return attribute.get("sub").toString(); + } + + @Override + public String getEmail() { + + return attribute.get("email").toString(); + } +} diff --git a/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java new file mode 100644 index 000000000..9d16e84d1 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java @@ -0,0 +1,30 @@ +package konkuk.thip.common.security.oauth2; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class KakaoUserDetails implements OAuth2UserDetails { + + private Map attributes; + + public KakaoUserDetails(Map attributes) { + this.attributes = attributes; + } + + @Override + public String getProvider() { + return "kakao"; + } + + @Override + public String getProviderId() { + return attributes.get("id").toString(); + } + + @Override + public String getEmail() { + Object object = attributes.get("kakao_account"); + LinkedHashMap accountMap = (LinkedHashMap) object; + return accountMap.get("email").toString(); + } +} diff --git a/src/main/java/konkuk/thip/common/security/oauth2/OAuth2UserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/OAuth2UserDetails.java new file mode 100644 index 000000000..836b3a19a --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/oauth2/OAuth2UserDetails.java @@ -0,0 +1,8 @@ +package konkuk.thip.common.security.oauth2; + +// 구글, 카카오 소셜 로그인 통합을 위한 인터페이스 +public interface OAuth2UserDetails { + String getProvider(); // (e.g., "kakao", "google", etc.) + String getProviderId(); + String getEmail(); +} From c1adbeb1dbe67b2c0b02f2868248381f30ec02d5 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:27:43 +0900 Subject: [PATCH 04/55] =?UTF-8?q?[feat]=20=EC=9D=B8=EC=A6=9D.=EC=9D=B8?= =?UTF-8?q?=EA=B0=80=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=20=EB=B0=8F=20=ED=95=B8=EB=93=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/common/exception/AuthException.java | 18 +++++++++++++++ .../thip/common/exception/code/ErrorCode.java | 6 +++++ .../handler/GlobalExceptionHandler.java | 23 +++++++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/main/java/konkuk/thip/common/exception/AuthException.java diff --git a/src/main/java/konkuk/thip/common/exception/AuthException.java b/src/main/java/konkuk/thip/common/exception/AuthException.java new file mode 100644 index 000000000..8c2709f1f --- /dev/null +++ b/src/main/java/konkuk/thip/common/exception/AuthException.java @@ -0,0 +1,18 @@ +package konkuk.thip.common.exception; + +import konkuk.thip.common.exception.code.ErrorCode; +import lombok.Getter; + +@Getter +public class AuthException extends RuntimeException { + private final ErrorCode errorCode; + + public AuthException(ErrorCode errorCode) { + this.errorCode = errorCode; + } + + public AuthException(ErrorCode errorCode, Exception e) { + super(e); + this.errorCode = errorCode; + } +} diff --git a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java index b538f025d..26d33d38c 100644 --- a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java +++ b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java @@ -16,6 +16,12 @@ public enum ErrorCode implements ResponseCode { API_INVALID_PARAM(HttpStatus.BAD_REQUEST, 40002, "파라미터 값 중 유효하지 않은 값이 있습니다."), API_INVALID_TYPE(HttpStatus.BAD_REQUEST, 40003, "파라미터 타입이 잘못되었습니다."), + AUTH_INVALID_TOKEN(HttpStatus.UNAUTHORIZED, 40100, "유효하지 않은 토큰입니다."), + AUTH_EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, 40101, "만료된 토큰입니다."), + AUTH_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 40102, "인증되지 않은 사용자입니다."), + AUTH_TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, 40103, "토큰을 찾을 수 없습니다."), + AUTH_LOGIN_FAILED(HttpStatus.UNAUTHORIZED, 40104, "로그인에 실패했습니다."), + /* 60000부터 비즈니스 예외 */ /** * 60000 : alias error diff --git a/src/main/java/konkuk/thip/common/exception/handler/GlobalExceptionHandler.java b/src/main/java/konkuk/thip/common/exception/handler/GlobalExceptionHandler.java index e6d0671d7..58ac9d5f2 100644 --- a/src/main/java/konkuk/thip/common/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/konkuk/thip/common/exception/handler/GlobalExceptionHandler.java @@ -1,6 +1,7 @@ package konkuk.thip.common.exception.handler; import konkuk.thip.common.dto.ErrorResponse; +import konkuk.thip.common.exception.AuthException; import konkuk.thip.common.exception.BusinessException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; @@ -71,6 +72,15 @@ public ResponseEntity missingServletRequestParameterExceptionHand .body(ErrorResponse.of(API_MISSING_PARAM, e.getParameterName() + "를 추가해서 요청해주세요.")); } + // 인증, 인가 권한 관련 예외 처리 + @ExceptionHandler(AuthException.class) + public ResponseEntity authExceptionHandler(AuthException e) { + log.error("[AuthExceptionHandler] {}", e.getMessage()); + return ResponseEntity + .status(e.getErrorCode().getHttpStatus()) + .body(ErrorResponse.of(e.getErrorCode())); + } + // 비즈니스 로직에서 발생한 예외 처리 @ExceptionHandler(BusinessException.class) public ResponseEntity businessExceptionHandler(BusinessException e) { @@ -81,12 +91,21 @@ public ResponseEntity businessExceptionHandler(BusinessException } // 서버 내부 오류 예외 처리 - @ExceptionHandler({RuntimeException.class, IllegalStateException.class}) - public ResponseEntity runtimeExceptionHandler(Exception e) { + @ExceptionHandler(RuntimeException.class) + public ResponseEntity runtimeExceptionHandler(RuntimeException e) { log.error("[RuntimeExceptionHandler] {}", e.getMessage()); return ResponseEntity .status(API_SERVER_ERROR.getHttpStatus()) .body(ErrorResponse.of(API_SERVER_ERROR)); } + // IllegalStateException 예외 처리 + @ExceptionHandler(IllegalStateException.class) + public ResponseEntity illegalStateExceptionHandler(IllegalStateException e) { + log.error("[IllegalStateExceptionHandler] {}", e.getMessage()); + return ResponseEntity + .status(API_SERVER_ERROR.getHttpStatus()) + .body(ErrorResponse.of(API_SERVER_ERROR)); + } + } From 681c5d87bb102b69e622c9108e3afdd830ba72e0 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:28:21 +0900 Subject: [PATCH 05/55] =?UTF-8?q?[feat]=20User=EC=97=90=20Oauth2Id=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java | 3 +++ .../java/konkuk/thip/user/adapter/out/mapper/UserMapper.java | 2 ++ .../thip/user/adapter/out/persistence/UserJpaRepository.java | 2 +- src/main/java/konkuk/thip/user/domain/User.java | 5 ++++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java b/src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java index 6258411ae..b03486e1d 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java +++ b/src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java @@ -27,6 +27,9 @@ public class UserJpaEntity extends BaseJpaEntity { @Column(name = "image_url", columnDefinition = "TEXT", nullable = false) private String imageUrl; + @Column(name = "oauth2_id", length = 50, nullable = false) + private String oauth2Id; + @Enumerated(EnumType.STRING) @Column(nullable = false) private UserRole role; diff --git a/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java b/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java index 38780e9ef..9471dbf72 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java +++ b/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java @@ -15,6 +15,7 @@ public UserJpaEntity toJpaEntity(User user, AliasJpaEntity aliasJpaEntity) { .nickname(user.getNickname()) .imageUrl(user.getImageUrl()) .role(UserRole.from(user.getUserRole())) + .oauth2Id(user.getOauth2Id()) .aliasForUserJpaEntity(aliasJpaEntity) .build(); } @@ -27,6 +28,7 @@ public User toDomainEntity(UserJpaEntity userJpaEntity) { .imageUrl(userJpaEntity.getImageUrl()) .userRole(userJpaEntity.getRole().getType()) .aliasId(userJpaEntity.getAliasForUserJpaEntity().getAliasId()) + .oauth2Id(userJpaEntity.getOauth2Id()) .createdAt(userJpaEntity.getCreatedAt()) .modifiedAt(userJpaEntity.getModifiedAt()) .status(userJpaEntity.getStatus()) diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java index 39798fe6a..83674ef98 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java @@ -4,6 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface UserJpaRepository extends JpaRepository, UserQueryRepository { - + UserJpaEntity findByOauth2Id(String oauth2Id); boolean existsByNickname(String nickname); } diff --git a/src/main/java/konkuk/thip/user/domain/User.java b/src/main/java/konkuk/thip/user/domain/User.java index b7d1e1ebf..b26558c4e 100644 --- a/src/main/java/konkuk/thip/user/domain/User.java +++ b/src/main/java/konkuk/thip/user/domain/User.java @@ -20,7 +20,9 @@ public class User extends BaseDomainEntity { private Long aliasId; - public static User withoutId(String email, String nickname, String imageUrl, String userRole, Long aliasId) { + private String oauth2Id; + + public static User withoutId(String email, String nickname, String imageUrl, String userRole, Long aliasId, String oauth2Id) { return User.builder() .id(null) .email(email) @@ -28,6 +30,7 @@ public static User withoutId(String email, String nickname, String imageUrl, Str .imageUrl(imageUrl) .userRole(userRole) .aliasId(aliasId) + .oauth2Id(oauth2Id) .build(); } From e59c1a8d3c3d64e3e5ee28136b0a49a0f652f39d Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:28:51 +0900 Subject: [PATCH 06/55] =?UTF-8?q?[feat]=20JwtAuthenticationFilter(?= =?UTF-8?q?=EC=9D=B8=EA=B0=80=EC=9A=A9=20=ED=95=84=ED=84=B0)=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/JwtAuthenticationFilter.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java diff --git a/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java b/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java new file mode 100644 index 000000000..1680a82e6 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java @@ -0,0 +1,96 @@ +package konkuk.thip.common.security.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import konkuk.thip.common.exception.AuthException; +import konkuk.thip.common.security.oauth2.CustomOAuth2User; +import konkuk.thip.common.security.oauth2.LoginUser; +import konkuk.thip.common.security.util.JwtUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +import static konkuk.thip.common.exception.code.ErrorCode.*; +import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_HEADER_KEY; +import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_PREFIX; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + try { + String token = extractToken(request); + if (token == null) { + throw new AuthException(AUTH_TOKEN_NOT_FOUND); + } + + if (!jwtUtil.validateToken(token)) { + throw new AuthException(AUTH_INVALID_TOKEN); + } + + if (jwtUtil.isExpired(token)) { + throw new AuthException(AUTH_EXPIRED_TOKEN); + } + + LoginUser loginUser = jwtUtil.getLoginUser(token); + + if (loginUser.userId() != null) { + request.setAttribute("userId", loginUser.userId()); + } + else { + request.setAttribute("oauth2Id", loginUser.oauth2Id()); + } + + CustomOAuth2User customOAuth2User = new CustomOAuth2User(loginUser); + + Authentication authToken = new UsernamePasswordAuthenticationToken(customOAuth2User, null, customOAuth2User.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + } catch (Exception e) { + log.error("JWT 필터에서 오류 발생: {}", e.getMessage()); + request.setAttribute("exception", e); + } finally { + filterChain.doFilter(request, response); + } + } + + private String extractToken(HttpServletRequest request) { + String authorization = request.getHeader(JWT_HEADER_KEY.getValue()); + if (authorization != null && authorization.startsWith(JWT_PREFIX.getValue())) { + return authorization.split(" ")[1]; + } + log.info("토큰이 없습니다."); + return null; + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + String path = request.getRequestURI(); + return path.startsWith("/oauth2/authorization/") + || path.startsWith("/login/oauth2/code/") + || path.startsWith("/swagger-ui/") + || path.startsWith("/api-docs/") + || path.startsWith("/v3/api-docs/") + || path.startsWith("/kakao-login-test.html") + || path.startsWith("/index.html") + || path.startsWith("/auth/kakao/") + || path.startsWith("/api/test/public") + || path.startsWith("/api/test/auth-status") + || path.startsWith("/api/test/protected"); + } + +} From e78d5b54927b69e2bee76e69297eede3d34f78c3 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:29:12 +0900 Subject: [PATCH 07/55] =?UTF-8?q?[feat]=20=ED=95=84=ED=84=B0=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?AuthenticationEntryPoint=20=EC=A0=95=EC=9D=98=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/JwtAuthenticationEntryPoint.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationEntryPoint.java diff --git a/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationEntryPoint.java b/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationEntryPoint.java new file mode 100644 index 000000000..b52dfa329 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationEntryPoint.java @@ -0,0 +1,30 @@ +package konkuk.thip.common.security.filter; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import java.io.IOException; + +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + private final HandlerExceptionResolver resolver; + + public JwtAuthenticationEntryPoint(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver){ + this.resolver = resolver; + } + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + Exception e = (Exception) request.getAttribute("exception"); + if(e == null){ + e = authException; + } + resolver.resolveException(request, response, null, e); + } +} From f7687741f52bc5af2de19e4c7050b7458df6d012 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:29:39 +0900 Subject: [PATCH 08/55] =?UTF-8?q?[feat]=20Oauth2User=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=98=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=84=A0=EC=96=B8=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/oauth2/CustomOAuth2User.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2User.java diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2User.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2User.java new file mode 100644 index 000000000..dd41e4e45 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2User.java @@ -0,0 +1,52 @@ +package konkuk.thip.common.security.oauth2; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +@Getter +@RequiredArgsConstructor +public class CustomOAuth2User implements OAuth2User { + + private final LoginUser loginUser; + + @Override + public Map getAttributes() { + return null; + } + + @Override + public String getName() { + return loginUser.oauth2Id(); + } + + @Override + public Collection getAuthorities() { + Collection authorities = new ArrayList<>(); + authorities.add(new GrantedAuthority() { + @Override + public String getAuthority() { + return "NORMAL_USER"; // 임시 사용자 권한 + } + }); + return authorities; + } + + public String getOauth2Id() { + return loginUser.oauth2Id(); + } + + public String getEmail() { + return loginUser.email(); + } + + public boolean isNewUser() { + return loginUser.isNewUser(); + } + +} From 291f7c9643d46a7aad2ab6baff2fab8f5bf2746f Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:29:53 +0900 Subject: [PATCH 09/55] =?UTF-8?q?[feat]=20=EC=86=8C=EC=85=9C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/CustomOAuth2UserService.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java new file mode 100644 index 000000000..1aae12563 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java @@ -0,0 +1,52 @@ +package konkuk.thip.common.security.oauth2; + +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.UserJpaRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class CustomOAuth2UserService extends DefaultOAuth2UserService { + + private final UserJpaRepository userJpaRepository; + + @Override + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + + OAuth2User oAuth2User = super.loadUser(userRequest); + + log.info("OAuth2User: {}", oAuth2User.getAttributes()); + + String registrationId = userRequest.getClientRegistration().getRegistrationId(); + OAuth2UserDetails oAuth2UserDetails = null; + if (registrationId.equals("kakao")) { + oAuth2UserDetails = new KakaoUserDetails(oAuth2User.getAttributes()); + } + else if (registrationId.equals("google")) { + oAuth2UserDetails = new GoogleUserDetails(oAuth2User.getAttributes()); + } + else { + log.warn("카카오 또는 구글 소셜 로그인만 지원합니다."); + return null; + } + + String oauth2Id = oAuth2UserDetails.getProvider() + "_" + oAuth2UserDetails.getProviderId(); //kakao_1234567890 + String email = oAuth2UserDetails.getEmail(); + UserJpaEntity existingUser = userJpaRepository.findByOauth2Id(oauth2Id); + if(existingUser == null) { + LoginUser newUser = LoginUser.createNewUser(oauth2Id, email); + return new CustomOAuth2User(newUser); + } + + LoginUser loginUser = LoginUser.createExistingUser(oauth2Id, existingUser.getUserId(), email); + return new CustomOAuth2User(loginUser); + } +} + From 42d3d95915cda844e97f877bdc427d66dca76e46 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:30:04 +0900 Subject: [PATCH 10/55] =?UTF-8?q?[feat]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=84=B1=EA=B3=B5=EC=8B=9C=20=ED=95=B8=EB=93=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/oauth2/CustomSuccessHandler.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java new file mode 100644 index 000000000..06131fbc2 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java @@ -0,0 +1,53 @@ +package konkuk.thip.common.security.oauth2; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import konkuk.thip.common.dto.BaseResponse; +import konkuk.thip.common.security.util.JwtUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_HEADER_KEY; +import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_PREFIX; + +@Component +@RequiredArgsConstructor +public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + private static final String CONTENT_TYPE = "application/json"; + private static final String ENCODING = "UTF-8"; + private final JwtUtil jwtUtil; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal(); + LoginUser loginUser = oAuth2User.getLoginUser(); + + if(oAuth2User.isNewUser()) { + // 최초 로그인 : 회원가입을 위한 임시 토큰 발급 + String tempToken = jwtUtil.createSignupToken(loginUser.oauth2Id(), loginUser.email()); + response.setHeader(JWT_HEADER_KEY.getValue(), JWT_PREFIX.getValue() + tempToken); + writeResponse(response, BaseResponse.ok(oAuth2User.getLoginUser())); + return; + } + + // 기존 회원 : Access Token 발급 + String accessToken = jwtUtil.createAccessToken(loginUser.userId(), loginUser.email()); + response.setHeader(JWT_HEADER_KEY.getValue(), JWT_PREFIX.getValue() + accessToken); + writeResponse(response, BaseResponse.ok(oAuth2User.getLoginUser())); + } + + private void writeResponse(HttpServletResponse response, Object value) throws IOException { + response.setStatus(HttpServletResponse.SC_OK); + response.setContentType(CONTENT_TYPE); + response.setCharacterEncoding(ENCODING); + ObjectMapper objectMapper = new ObjectMapper(); + String body = objectMapper.writeValueAsString(value); + response.getWriter().write(body); + } +} From 2b676afe713d51a91e10af30627e4dfdedfeefe5 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:30:31 +0900 Subject: [PATCH 11/55] =?UTF-8?q?[feat]=20Jwt=20=EC=83=9D=EC=84=B1,=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=93=B1=EC=9D=84=20=EB=8B=B4=EB=8B=B9?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/common/security/util/JwtUtil.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/util/JwtUtil.java diff --git a/src/main/java/konkuk/thip/common/security/util/JwtUtil.java b/src/main/java/konkuk/thip/common/security/util/JwtUtil.java new file mode 100644 index 000000000..4790e2a23 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/util/JwtUtil.java @@ -0,0 +1,93 @@ +package konkuk.thip.common.security.util; + +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.UnsupportedJwtException; +import konkuk.thip.common.security.oauth2.LoginUser; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +@Slf4j +@Component +public class JwtUtil { + + private final SecretKey secretKey; + + //todo 확정 후 환경변수로 변경 + private final long tokenExpiredMs = 1000 * 60 * 60 * 24; // 24시간 + private final long signupTokenExpiredMs = 5 * 60 * 1000; // 5분 + + public JwtUtil(@Value("${jwt.secret}") String secret) { + secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); + } + + public String createSignupToken(String oauth2Id, String email) { + return Jwts.builder() + .claim("oauth2Id", oauth2Id) + .claim("email", email) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + signupTokenExpiredMs)) + .signWith(secretKey) + .compact(); + } + + public String createAccessToken(Long userId, String email) { + return Jwts.builder() + .claim("userId", userId) + .claim("email", email) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + tokenExpiredMs)) + .signWith(secretKey) + .compact(); + } + + public boolean validateToken(String token) { + try { + Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token); + return true; + } catch (MalformedJwtException e) { + log.info("Invalid JWT Token", e); + } catch (ExpiredJwtException e) { + log.info("Expired JWT Token", e); + } catch (UnsupportedJwtException e) { + log.info("Unsupported JWT Token", e); + } catch (IllegalArgumentException e) { + log.info("JWT claims string is empty.", e); + } + return false; + } + + public Boolean isExpired(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); + } + + private String getOauth2Id(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("oauth2Id", String.class); + } + + private Long getUserId(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("userId", Long.class); + } + + private String getEmail(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("email", String.class); + } + + public LoginUser getLoginUser(String token) { + String oauth2Id = getOauth2Id(token); + Long userId = getUserId(token); + String email = getEmail(token); + + if (userId == null) { + return LoginUser.createNewUser(oauth2Id, email); + } + return LoginUser.createExistingUser(oauth2Id, userId, email); + } +} From 8f9f88ce6e8bf456d020056454165c7aefb70ac2 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:30:43 +0900 Subject: [PATCH 12/55] =?UTF-8?q?[feat]=20SecurityConfig=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/config/SecurityConfig.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/main/java/konkuk/thip/config/SecurityConfig.java diff --git a/src/main/java/konkuk/thip/config/SecurityConfig.java b/src/main/java/konkuk/thip/config/SecurityConfig.java new file mode 100644 index 000000000..a0752df9f --- /dev/null +++ b/src/main/java/konkuk/thip/config/SecurityConfig.java @@ -0,0 +1,106 @@ +package konkuk.thip.config; + +import konkuk.thip.common.security.filter.JwtAuthenticationEntryPoint; +import konkuk.thip.common.security.filter.JwtAuthenticationFilter; +import konkuk.thip.common.security.oauth2.CustomOAuth2UserService; +import konkuk.thip.common.security.oauth2.CustomSuccessHandler; +import konkuk.thip.common.security.util.JwtUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Collections; +import java.util.List; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtUtil jwtUtil; + private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final CustomOAuth2UserService customOAuth2UserService; + private final CustomSuccessHandler customSuccessHandler; + + private static final String[] WHITELIST = { + "/swagger-ui/**", "/api-docs/**", "/swagger-ui.html", + "/v3/api-docs/**","/oauth2/authorization/**", + "/login/oauth2/code/**", + +// //테스트를 위한 url +// "/api/test/public", +// "/api/test/auth-status", +// "/api/test/protected", +// "/auth/kakao/**", +// "/kakao-login-test.html", +// "/google-login-test.html", +// "/index.html", + }; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + + http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .csrf(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .addFilterBefore(new JwtAuthenticationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class) + .oauth2Login((oauth2) -> oauth2 + .userInfoEndpoint(userInfoEndpointConfig -> userInfoEndpointConfig + .userService(customOAuth2UserService) + ) + .successHandler(customSuccessHandler) // OAuth2 로그인 성공 시 처리 + ) + .authorizeHttpRequests(auth -> auth + .requestMatchers(WHITELIST).permitAll() + .anyRequest().authenticated() + ) + .exceptionHandling(handler -> handler.authenticationEntryPoint(jwtAuthenticationEntryPoint)) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + ; + + return http.build(); + } + + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { + return configuration.getAuthenticationManager(); + } + + // CORS 설정 + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(Collections.singletonList("*")); // 배포 시 도메인 명시 + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); + config.setAllowedHeaders(Collections.singletonList("*")); + config.setAllowCredentials(true); + config.setMaxAge(3600L); + + config.setExposedHeaders(Collections.singletonList("Authorization")); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + + return source; + } + +} \ No newline at end of file From 2c49059b397c177304e9c5c4550953b850546161 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:31:00 +0900 Subject: [PATCH 13/55] =?UTF-8?q?[feat]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=ED=95=9C=20user=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20=EB=8B=B4?= =?UTF-8?q?=EB=8A=94=20LoginUser=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/common/security/oauth2/LoginUser.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/oauth2/LoginUser.java diff --git a/src/main/java/konkuk/thip/common/security/oauth2/LoginUser.java b/src/main/java/konkuk/thip/common/security/oauth2/LoginUser.java new file mode 100644 index 000000000..6cfe71458 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/oauth2/LoginUser.java @@ -0,0 +1,16 @@ +package konkuk.thip.common.security.oauth2; + +public record LoginUser( + String oauth2Id, + Long userId, + String email, + boolean isNewUser +) { + public static LoginUser createNewUser(String oauth2Id, String email) { + return new LoginUser(oauth2Id, null, email,true); + } + + public static LoginUser createExistingUser(String oauth2Id, Long userId, String email) { + return new LoginUser(oauth2Id, userId, email, false); + } +} From b23a6fa8fa240b6ecab12c721df060b49918d7ac Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:31:19 +0900 Subject: [PATCH 14/55] =?UTF-8?q?[feat]=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EB=B0=8F=20arg?= =?UTF-8?q?ument=20resolver=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/security/annotation/Oauth2Id.java | 11 ++++++ .../common/security/annotation/UserId.java | 11 ++++++ .../Oauth2IdArgumentResolver.java | 38 +++++++++++++++++++ .../UserIdArgumentResolver.java | 38 +++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 src/main/java/konkuk/thip/common/security/annotation/Oauth2Id.java create mode 100644 src/main/java/konkuk/thip/common/security/annotation/UserId.java create mode 100644 src/main/java/konkuk/thip/common/security/argument_resolver/Oauth2IdArgumentResolver.java create mode 100644 src/main/java/konkuk/thip/common/security/argument_resolver/UserIdArgumentResolver.java diff --git a/src/main/java/konkuk/thip/common/security/annotation/Oauth2Id.java b/src/main/java/konkuk/thip/common/security/annotation/Oauth2Id.java new file mode 100644 index 000000000..65ac29d59 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/annotation/Oauth2Id.java @@ -0,0 +1,11 @@ +package konkuk.thip.common.security.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 Oauth2Id { +} diff --git a/src/main/java/konkuk/thip/common/security/annotation/UserId.java b/src/main/java/konkuk/thip/common/security/annotation/UserId.java new file mode 100644 index 000000000..c8ef09c18 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/annotation/UserId.java @@ -0,0 +1,11 @@ +package konkuk.thip.common.security.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 UserId { +} diff --git a/src/main/java/konkuk/thip/common/security/argument_resolver/Oauth2IdArgumentResolver.java b/src/main/java/konkuk/thip/common/security/argument_resolver/Oauth2IdArgumentResolver.java new file mode 100644 index 000000000..9891ac26f --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/argument_resolver/Oauth2IdArgumentResolver.java @@ -0,0 +1,38 @@ +package konkuk.thip.common.security.argument_resolver; + +import jakarta.servlet.http.HttpServletRequest; +import konkuk.thip.common.exception.AuthException; +import konkuk.thip.common.security.annotation.Oauth2Id; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import static konkuk.thip.common.exception.code.ErrorCode.AUTH_TOKEN_NOT_FOUND; + +@Component +@RequiredArgsConstructor +public class Oauth2IdArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(Oauth2Id.class) + && parameter.getParameterType().equals(String.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + + Object oauth2Id = ((HttpServletRequest) webRequest.getNativeRequest()).getAttribute("oauth2Id"); + if (oauth2Id == null) { + throw new AuthException(AUTH_TOKEN_NOT_FOUND); + } + return oauth2Id; + } +} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/common/security/argument_resolver/UserIdArgumentResolver.java b/src/main/java/konkuk/thip/common/security/argument_resolver/UserIdArgumentResolver.java new file mode 100644 index 000000000..cfbb4d75e --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/argument_resolver/UserIdArgumentResolver.java @@ -0,0 +1,38 @@ +package konkuk.thip.common.security.argument_resolver; + +import jakarta.servlet.http.HttpServletRequest; +import konkuk.thip.common.exception.AuthException; +import konkuk.thip.common.security.annotation.UserId; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import static konkuk.thip.common.exception.code.ErrorCode.AUTH_TOKEN_NOT_FOUND; + +@Component +@RequiredArgsConstructor +public class UserIdArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(UserId.class) + && parameter.getParameterType().equals(Long.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + + Object userId = ((HttpServletRequest) webRequest.getNativeRequest()).getAttribute("userId"); + if (userId == null) { + throw new AuthException(AUTH_TOKEN_NOT_FOUND); + } + return userId; + } +} From c776f43b237119b133580e1188634579a4da17f3 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:31:28 +0900 Subject: [PATCH 15/55] =?UTF-8?q?[feat]=20argument=20resolver=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/konkuk/thip/config/WebMvcConfig.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/konkuk/thip/config/WebMvcConfig.java diff --git a/src/main/java/konkuk/thip/config/WebMvcConfig.java b/src/main/java/konkuk/thip/config/WebMvcConfig.java new file mode 100644 index 000000000..12d47b9a4 --- /dev/null +++ b/src/main/java/konkuk/thip/config/WebMvcConfig.java @@ -0,0 +1,24 @@ +package konkuk.thip.config; + +import konkuk.thip.common.security.argument_resolver.Oauth2IdArgumentResolver; +import konkuk.thip.common.security.argument_resolver.UserIdArgumentResolver; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +@RequiredArgsConstructor +public class WebMvcConfig implements WebMvcConfigurer { + + private final UserIdArgumentResolver userIdArgumentResolver; + private final Oauth2IdArgumentResolver oauth2IdArgumentResolver; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(userIdArgumentResolver); + resolvers.add(oauth2IdArgumentResolver); + } +} From 3a7a50581644894fe88440972d8fdac861e865a4 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:31:50 +0900 Subject: [PATCH 16/55] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20dto=EC=97=90=20oauth2Id=20=EC=B6=94=EA=B0=80(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/adapter/in/web/request/PostUserSignupRequest.java | 3 ++- .../thip/user/application/port/in/dto/UserSignupCommand.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java b/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java index 99e45578c..b865ce4bc 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java @@ -15,11 +15,12 @@ public record PostUserSignupRequest( @Email(message = "이메일 형식이 올바르지 않습니다.") String email ) { - public UserSignupCommand toCommand() { + public UserSignupCommand toCommand(String oAuth2Id) { return UserSignupCommand.builder() .aliasId(aliasId) .nickname(nickname) .email(email) + .oauth2Id(oAuth2Id) .build(); } } diff --git a/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java b/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java index 38aef5a36..9af8c2e74 100644 --- a/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java +++ b/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java @@ -6,5 +6,6 @@ public record UserSignupCommand( Long aliasId, String nickname, - String email + String email, + String oauth2Id ) {} From 4a16efbae76320026ea913b3c57b11c4b6972c9a Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:32:07 +0900 Subject: [PATCH 17/55] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=A1=9C=EC=A7=81=EC=97=90=EC=84=9C=20oauth2Id?= =?UTF-8?q?=EB=8F=84=20=EC=A0=80=EC=9E=A5=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/user/application/service/UserSignupService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/user/application/service/UserSignupService.java b/src/main/java/konkuk/thip/user/application/service/UserSignupService.java index b32c2028e..66ae01841 100644 --- a/src/main/java/konkuk/thip/user/application/service/UserSignupService.java +++ b/src/main/java/konkuk/thip/user/application/service/UserSignupService.java @@ -25,7 +25,7 @@ public class UserSignupService implements UserSignupUseCase { public Long signup(UserSignupCommand command) { Alias alias = aliasCommandPort.findById(command.aliasId()); User user = User.withoutId( - command.email(), command.nickname(), alias.getImageUrl(), USER.getType(), alias.getId() + command.email(), command.nickname(), alias.getImageUrl(), USER.getType(), alias.getId(), command.oauth2Id() ); return userCommandPort.save(user); From f3e16cf4a44b4f10da592187d9375591f95f171e Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:32:38 +0900 Subject: [PATCH 18/55] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=9C=20=EC=9E=84=EC=8B=9C=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=20=EC=B6=94=EC=B6=9C=20=EB=B0=8F=20=EC=95=A1=EC=84=B8=EC=8A=A4?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EB=B0=9C=EA=B8=89=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/UserCommandController.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java index 8fcfa2206..bbc4c8c78 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java @@ -1,6 +1,11 @@ package konkuk.thip.user.adapter.in.web; +import jakarta.servlet.http.HttpServletResponse; import konkuk.thip.common.dto.BaseResponse; +import konkuk.thip.common.security.annotation.Oauth2Id; +import konkuk.thip.common.security.util.JwtUtil; +import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; +import konkuk.thip.user.adapter.in.web.response.UserSignupResponse; import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; import konkuk.thip.user.adapter.in.web.request.PostUserVerifyNicknameRequest; import konkuk.thip.user.adapter.in.web.response.PostUserSignupResponse; @@ -13,18 +18,25 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_HEADER_KEY; +import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_PREFIX; + @RestController @RequiredArgsConstructor public class UserCommandController { private final UserSignupUseCase userSignupUseCase; private final VerifyNicknameUseCase verifyNicknameUseCase; + private final JwtUtil jwtUtil; @PostMapping("/users/signup") - public BaseResponse signup(@Validated @RequestBody PostUserSignupRequest request) { - return BaseResponse.ok(PostUserSignupResponse.of( - userSignupUseCase.signup(request.toCommand())) - ); + public BaseResponse signup(@Validated @RequestBody UserSignupRequest request, + @Oauth2Id String oauth2Id, + HttpServletResponse response) { + Long userId = userSignupUseCase.signup(request.toCommand(oauth2Id)); + String accessToken = jwtUtil.createAccessToken(userId, request.email()); + response.setHeader(JWT_HEADER_KEY.getValue(), JWT_PREFIX.getValue() + accessToken); + return BaseResponse.ok(UserSignupResponse.of(userId)); } @PostMapping("/users/nickname") From b8f1e466dc23adbda1c4b7cf51ca5c685001bcd3 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 14:33:26 +0900 Subject: [PATCH 19/55] =?UTF-8?q?[refactor]=20email=20=EC=BB=AC=EB=9F=BC?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/security/oauth2/CustomOAuth2User.java | 4 ---- .../security/oauth2/CustomOAuth2UserService.java | 5 ++--- .../security/oauth2/CustomSuccessHandler.java | 4 ++-- .../common/security/oauth2/GoogleUserDetails.java | 10 +++++----- .../common/security/oauth2/KakaoUserDetails.java | 12 ++++++------ .../thip/common/security/oauth2/LoginUser.java | 9 ++++----- .../common/security/oauth2/OAuth2UserDetails.java | 1 - .../konkuk/thip/common/security/util/JwtUtil.java | 15 ++++----------- .../java/konkuk/thip/config/SecurityConfig.java | 14 +++++++------- .../adapter/in/web/UserCommandController.java | 2 +- .../in/web/request/PostUserSignupRequest.java | 7 +------ .../thip/user/adapter/out/jpa/UserJpaEntity.java | 3 --- .../thip/user/adapter/out/mapper/UserMapper.java | 2 -- .../port/in/dto/UserSignupCommand.java | 1 - .../application/service/UserSignupService.java | 2 +- src/main/java/konkuk/thip/user/domain/User.java | 5 +---- 16 files changed, 34 insertions(+), 62 deletions(-) diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2User.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2User.java index dd41e4e45..7b675a92a 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2User.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2User.java @@ -41,10 +41,6 @@ public String getOauth2Id() { return loginUser.oauth2Id(); } - public String getEmail() { - return loginUser.email(); - } - public boolean isNewUser() { return loginUser.isNewUser(); } diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java index 1aae12563..fb97d8b9a 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java @@ -38,14 +38,13 @@ else if (registrationId.equals("google")) { } String oauth2Id = oAuth2UserDetails.getProvider() + "_" + oAuth2UserDetails.getProviderId(); //kakao_1234567890 - String email = oAuth2UserDetails.getEmail(); UserJpaEntity existingUser = userJpaRepository.findByOauth2Id(oauth2Id); if(existingUser == null) { - LoginUser newUser = LoginUser.createNewUser(oauth2Id, email); + LoginUser newUser = LoginUser.createNewUser(oauth2Id); return new CustomOAuth2User(newUser); } - LoginUser loginUser = LoginUser.createExistingUser(oauth2Id, existingUser.getUserId(), email); + LoginUser loginUser = LoginUser.createExistingUser(oauth2Id, existingUser.getUserId()); return new CustomOAuth2User(loginUser); } } diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java index 06131fbc2..19fc0fda9 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java @@ -30,14 +30,14 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo if(oAuth2User.isNewUser()) { // 최초 로그인 : 회원가입을 위한 임시 토큰 발급 - String tempToken = jwtUtil.createSignupToken(loginUser.oauth2Id(), loginUser.email()); + String tempToken = jwtUtil.createSignupToken(loginUser.oauth2Id()); response.setHeader(JWT_HEADER_KEY.getValue(), JWT_PREFIX.getValue() + tempToken); writeResponse(response, BaseResponse.ok(oAuth2User.getLoginUser())); return; } // 기존 회원 : Access Token 발급 - String accessToken = jwtUtil.createAccessToken(loginUser.userId(), loginUser.email()); + String accessToken = jwtUtil.createAccessToken(loginUser.userId()); response.setHeader(JWT_HEADER_KEY.getValue(), JWT_PREFIX.getValue() + accessToken); writeResponse(response, BaseResponse.ok(oAuth2User.getLoginUser())); } diff --git a/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java index 00b8d35ea..db9f9a0aa 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java @@ -23,9 +23,9 @@ public String getProviderId() { return attribute.get("sub").toString(); } - @Override - public String getEmail() { - - return attribute.get("email").toString(); - } +// @Override +// public String getEmail() { +// +// return attribute.get("email").toString(); +// } } diff --git a/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java index 9d16e84d1..e533fa7c5 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java @@ -21,10 +21,10 @@ public String getProviderId() { return attributes.get("id").toString(); } - @Override - public String getEmail() { - Object object = attributes.get("kakao_account"); - LinkedHashMap accountMap = (LinkedHashMap) object; - return accountMap.get("email").toString(); - } +// @Override +// public String getEmail() { +// Object object = attributes.get("kakao_account"); +// LinkedHashMap accountMap = (LinkedHashMap) object; +// return accountMap.get("email").toString(); +// } } diff --git a/src/main/java/konkuk/thip/common/security/oauth2/LoginUser.java b/src/main/java/konkuk/thip/common/security/oauth2/LoginUser.java index 6cfe71458..3b5dd37b2 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/LoginUser.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/LoginUser.java @@ -3,14 +3,13 @@ public record LoginUser( String oauth2Id, Long userId, - String email, boolean isNewUser ) { - public static LoginUser createNewUser(String oauth2Id, String email) { - return new LoginUser(oauth2Id, null, email,true); + public static LoginUser createNewUser(String oauth2Id) { + return new LoginUser(oauth2Id, null, true); } - public static LoginUser createExistingUser(String oauth2Id, Long userId, String email) { - return new LoginUser(oauth2Id, userId, email, false); + public static LoginUser createExistingUser(String oauth2Id, Long userId) { + return new LoginUser(oauth2Id, userId, false); } } diff --git a/src/main/java/konkuk/thip/common/security/oauth2/OAuth2UserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/OAuth2UserDetails.java index 836b3a19a..8e4b07468 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/OAuth2UserDetails.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/OAuth2UserDetails.java @@ -4,5 +4,4 @@ public interface OAuth2UserDetails { String getProvider(); // (e.g., "kakao", "google", etc.) String getProviderId(); - String getEmail(); } diff --git a/src/main/java/konkuk/thip/common/security/util/JwtUtil.java b/src/main/java/konkuk/thip/common/security/util/JwtUtil.java index 4790e2a23..08d734ebd 100644 --- a/src/main/java/konkuk/thip/common/security/util/JwtUtil.java +++ b/src/main/java/konkuk/thip/common/security/util/JwtUtil.java @@ -28,20 +28,18 @@ public JwtUtil(@Value("${jwt.secret}") String secret) { secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); } - public String createSignupToken(String oauth2Id, String email) { + public String createSignupToken(String oauth2Id) { return Jwts.builder() .claim("oauth2Id", oauth2Id) - .claim("email", email) .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() + signupTokenExpiredMs)) .signWith(secretKey) .compact(); } - public String createAccessToken(Long userId, String email) { + public String createAccessToken(Long userId) { return Jwts.builder() .claim("userId", userId) - .claim("email", email) .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() + tokenExpiredMs)) .signWith(secretKey) @@ -76,18 +74,13 @@ private Long getUserId(String token) { return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("userId", Long.class); } - private String getEmail(String token) { - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("email", String.class); - } - public LoginUser getLoginUser(String token) { String oauth2Id = getOauth2Id(token); Long userId = getUserId(token); - String email = getEmail(token); if (userId == null) { - return LoginUser.createNewUser(oauth2Id, email); + return LoginUser.createNewUser(oauth2Id); } - return LoginUser.createExistingUser(oauth2Id, userId, email); + return LoginUser.createExistingUser(oauth2Id, userId); } } diff --git a/src/main/java/konkuk/thip/config/SecurityConfig.java b/src/main/java/konkuk/thip/config/SecurityConfig.java index a0752df9f..c75e34818 100644 --- a/src/main/java/konkuk/thip/config/SecurityConfig.java +++ b/src/main/java/konkuk/thip/config/SecurityConfig.java @@ -40,13 +40,13 @@ public class SecurityConfig { "/login/oauth2/code/**", // //테스트를 위한 url -// "/api/test/public", -// "/api/test/auth-status", -// "/api/test/protected", -// "/auth/kakao/**", -// "/kakao-login-test.html", -// "/google-login-test.html", -// "/index.html", + "/api/test/public", + "/api/test/auth-status", + "/api/test/protected", + "/auth/kakao/**", + "/kakao-login-test.html", + "/google-login-test.html", + "/index.html", }; @Bean diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java index bbc4c8c78..4f4f710f9 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java @@ -34,7 +34,7 @@ public BaseResponse signup(@Validated @RequestBody UserSignu @Oauth2Id String oauth2Id, HttpServletResponse response) { Long userId = userSignupUseCase.signup(request.toCommand(oauth2Id)); - String accessToken = jwtUtil.createAccessToken(userId, request.email()); + String accessToken = jwtUtil.createAccessToken(userId); response.setHeader(JWT_HEADER_KEY.getValue(), JWT_PREFIX.getValue() + accessToken); return BaseResponse.ok(UserSignupResponse.of(userId)); } diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java b/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java index b865ce4bc..2a786372b 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java @@ -9,17 +9,12 @@ public record PostUserSignupRequest( @Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)") @Size(max = 10, message = "닉네임은 최대 10자 입니다.") - String nickname, - - @NotBlank(message = "이메일은 공백일 수 없습니다.") - @Email(message = "이메일 형식이 올바르지 않습니다.") - String email + String nickname ) { public UserSignupCommand toCommand(String oAuth2Id) { return UserSignupCommand.builder() .aliasId(aliasId) .nickname(nickname) - .email(email) .oauth2Id(oAuth2Id) .build(); } diff --git a/src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java b/src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java index b03486e1d..e7556dd11 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java +++ b/src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java @@ -18,9 +18,6 @@ public class UserJpaEntity extends BaseJpaEntity { @Column(name = "user_id") private Long userId; - @Column(name = "email", length = 100, nullable = false) - private String email; - @Column(length = 60, nullable = false) private String nickname; diff --git a/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java b/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java index 9471dbf72..40ce8d0c3 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java +++ b/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java @@ -11,7 +11,6 @@ public class UserMapper { public UserJpaEntity toJpaEntity(User user, AliasJpaEntity aliasJpaEntity) { return UserJpaEntity.builder() - .email(user.getEmail()) .nickname(user.getNickname()) .imageUrl(user.getImageUrl()) .role(UserRole.from(user.getUserRole())) @@ -23,7 +22,6 @@ public UserJpaEntity toJpaEntity(User user, AliasJpaEntity aliasJpaEntity) { public User toDomainEntity(UserJpaEntity userJpaEntity) { return User.builder() .id(userJpaEntity.getUserId()) - .email(userJpaEntity.getEmail()) .nickname(userJpaEntity.getNickname()) .imageUrl(userJpaEntity.getImageUrl()) .userRole(userJpaEntity.getRole().getType()) diff --git a/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java b/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java index 9af8c2e74..0b23769e8 100644 --- a/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java +++ b/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java @@ -6,6 +6,5 @@ public record UserSignupCommand( Long aliasId, String nickname, - String email, String oauth2Id ) {} diff --git a/src/main/java/konkuk/thip/user/application/service/UserSignupService.java b/src/main/java/konkuk/thip/user/application/service/UserSignupService.java index 66ae01841..478ed31e4 100644 --- a/src/main/java/konkuk/thip/user/application/service/UserSignupService.java +++ b/src/main/java/konkuk/thip/user/application/service/UserSignupService.java @@ -25,7 +25,7 @@ public class UserSignupService implements UserSignupUseCase { public Long signup(UserSignupCommand command) { Alias alias = aliasCommandPort.findById(command.aliasId()); User user = User.withoutId( - command.email(), command.nickname(), alias.getImageUrl(), USER.getType(), alias.getId(), command.oauth2Id() + command.nickname(), alias.getImageUrl(), USER.getType(), alias.getId(), command.oauth2Id() ); return userCommandPort.save(user); diff --git a/src/main/java/konkuk/thip/user/domain/User.java b/src/main/java/konkuk/thip/user/domain/User.java index b26558c4e..b19513dd7 100644 --- a/src/main/java/konkuk/thip/user/domain/User.java +++ b/src/main/java/konkuk/thip/user/domain/User.java @@ -10,8 +10,6 @@ public class User extends BaseDomainEntity { private Long id; - private String email; - private String nickname; private String imageUrl; @@ -22,10 +20,9 @@ public class User extends BaseDomainEntity { private String oauth2Id; - public static User withoutId(String email, String nickname, String imageUrl, String userRole, Long aliasId, String oauth2Id) { + public static User withoutId(String nickname, String imageUrl, String userRole, Long aliasId, String oauth2Id) { return User.builder() .id(null) - .email(email) .nickname(nickname) .imageUrl(imageUrl) .userRole(userRole) From caa7452c4e438a8301d17d1f8394a6a75e6c492e Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 14:33:40 +0900 Subject: [PATCH 20/55] =?UTF-8?q?[refactor]=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20filter=20=EC=84=A4=EC=A0=95=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/filter/JwtAuthenticationFilter.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java b/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java index 1680a82e6..7cde061af 100644 --- a/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java +++ b/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java @@ -77,20 +77,4 @@ private String extractToken(HttpServletRequest request) { return null; } - @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - String path = request.getRequestURI(); - return path.startsWith("/oauth2/authorization/") - || path.startsWith("/login/oauth2/code/") - || path.startsWith("/swagger-ui/") - || path.startsWith("/api-docs/") - || path.startsWith("/v3/api-docs/") - || path.startsWith("/kakao-login-test.html") - || path.startsWith("/index.html") - || path.startsWith("/auth/kakao/") - || path.startsWith("/api/test/public") - || path.startsWith("/api/test/auth-status") - || path.startsWith("/api/test/protected"); - } - } From 2da22292093d28bda99ade134a2824adfc87033b Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 18:07:32 +0900 Subject: [PATCH 21/55] =?UTF-8?q?[test]=20=ED=8C=A9=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=8F=84=EC=9E=85=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/common/util/TestEntityFactory.java | 75 ++++++++++++++++ .../adapter/out/jpa/FeedJpaEntityTest.java | 52 +++-------- .../adapter/out/jpa/RecordJpaEntityTest.java | 86 +++---------------- .../adapter/out/jpa/RoomJpaEntityTest.java | 74 ++++------------ .../adapter/out/jpa/VoteJpaEntityTest.java | 86 ++++--------------- .../adapter/out/jpa/UserJpaEntityTest.java | 18 +--- 6 files changed, 136 insertions(+), 255 deletions(-) create mode 100644 src/test/java/konkuk/thip/common/util/TestEntityFactory.java diff --git a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java new file mode 100644 index 000000000..69b9895c4 --- /dev/null +++ b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java @@ -0,0 +1,75 @@ +package konkuk.thip.common.util; + +import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; +import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; +import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserRole; + +import java.time.LocalDate; + +public class TestEntityFactory { + + public static AliasJpaEntity createAlias() { + return AliasJpaEntity.builder() + .value("칭호") + .imageUrl("test-image-url") + .color("red") + .build(); + } + + public static UserJpaEntity createUser(AliasJpaEntity alias) { + return UserJpaEntity.builder() + .nickname("테스터") + .imageUrl("https://test.img") + .oauth2Id("kakao_12345678") + .aliasForUserJpaEntity(alias) + .role(UserRole.USER) + .build(); + } + + public static BookJpaEntity createBook() { + return BookJpaEntity.builder() + .title("책제목") + .authorName("저자") + .isbn("isbn") + .bestSeller(false) + .publisher("출판사") + .imageUrl("img") + .pageCount(100) + .description("설명") + .build(); + } + + public static CategoryJpaEntity createCategory(AliasJpaEntity alias) { + return CategoryJpaEntity.builder() + .value("카테고리1") + .aliasForCategoryJpaEntity(alias) + .build(); + } + + public static RoomJpaEntity createRoom(BookJpaEntity book, CategoryJpaEntity category) { + return RoomJpaEntity.builder() + .title("방이름") + .description("설명") + .isPublic(true) + .startDate(LocalDate.now()) + .endDate(LocalDate.now().plusDays(5)) + .recruitCount(3) + .bookJpaEntity(book) + .categoryJpaEntity(category) + .build(); + } + + public static RecordJpaEntity createRecord(UserJpaEntity user, RoomJpaEntity room) { + return RecordJpaEntity.builder() + .content("기록 내용") + .userJpaEntity(user) + .page(22) + .isOverview(false) + .roomJpaEntity(room) + .build(); + } +} \ No newline at end of file diff --git a/src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java b/src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java index 3eef4a2bf..d956cf296 100644 --- a/src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java @@ -1,13 +1,12 @@ package konkuk.thip.feed.adapter.out.jpa; import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.feed.adapter.out.persistence.FeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import konkuk.thip.user.adapter.out.jpa.UserRole; import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; import konkuk.thip.user.adapter.out.persistence.UserJpaRepository; import org.junit.jupiter.api.DisplayName; @@ -24,7 +23,7 @@ @Import(konkuk.thip.config.TestQuerydslConfig.class) // DataJpaTest 이므로 JPA 제외 빈 추가로 import class FeedJpaEntityTest { - @PersistenceContext + @Autowired private EntityManager em; @Autowired @@ -39,58 +38,31 @@ class FeedJpaEntityTest { @Autowired private FeedJpaRepository feedRepository; - private UserJpaEntity createUser() { - AliasJpaEntity alias = AliasJpaEntity.builder() - .value("칭호") - .imageUrl("test-image-url") - .color("red") - .build(); - - aliasRepository.save(alias); - - UserJpaEntity user = UserJpaEntity.builder() - .email("test@test.com") - .nickname("테스터") - .imageUrl("https://test.img") - .aliasForUserJpaEntity(alias) - .role(UserRole.USER) - .build(); - return userRepository.save(user); - } - - private BookJpaEntity createBook() { - return bookRepository.save(BookJpaEntity.builder() - .title("책제목") - .authorName("저자") - .isbn("isbn") - .bestSeller(false) - .publisher("출판사") - .imageUrl("img") - .pageCount(100) - .description("설명") - .build()); - } - @Test @DisplayName("FeedJpaEntity 저장 및 조회 테스트") void saveAndFindFeed() { - UserJpaEntity user = createUser(); - BookJpaEntity book = createBook(); + // given + AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createAlias()); + UserJpaEntity user = userRepository.save(TestEntityFactory.createUser(alias)); + BookJpaEntity book = bookRepository.save(TestEntityFactory.createBook()); - FeedJpaEntity feed = FeedJpaEntity.builder() + FeedJpaEntity feed = feedRepository.save(FeedJpaEntity.builder() .content("피드 내용") .userJpaEntity(user) .isPublic(true) .bookJpaEntity(book) - .build(); + .build()); - feedRepository.save(feed); em.flush(); em.clear(); + // when FeedJpaEntity found = feedRepository.findById(feed.getPostId()).orElseThrow(); + + // then assertThat(found).isNotNull(); assertThat(found.getContent()).isEqualTo("피드 내용"); + assertThat(found.getUserJpaEntity().getNickname()).isEqualTo("테스터"); assertThat(found.getBookJpaEntity().getTitle()).isEqualTo("책제목"); } } \ No newline at end of file diff --git a/src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java b/src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java index cdfee771e..4a807fe95 100644 --- a/src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java +++ b/src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java @@ -3,13 +3,13 @@ import jakarta.persistence.EntityManager; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; import konkuk.thip.record.adapter.out.persistence.RecordJpaRepository; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; import konkuk.thip.room.adapter.out.persistence.RoomJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import konkuk.thip.user.adapter.out.jpa.UserRole; import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; import konkuk.thip.user.adapter.out.persistence.UserJpaRepository; import org.junit.jupiter.api.DisplayName; @@ -19,8 +19,6 @@ import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; -import java.time.LocalDate; - import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest @@ -49,87 +47,27 @@ class RecordJpaEntityTest { @Autowired private CategoryJpaRepository categoryRepository; - private UserJpaEntity createUser() { - AliasJpaEntity alias = AliasJpaEntity.builder() - .value("칭호") - .imageUrl("test-image-url") - .color("red") - .build(); - - aliasRepository.save(alias); - - UserJpaEntity user = UserJpaEntity.builder() - .email("test@test.com") - .nickname("테스터") - .imageUrl("https://test.img") - .aliasForUserJpaEntity(alias) - .role(UserRole.USER) - .build(); - return userRepository.save(user); - } - - private BookJpaEntity createBook() { - return bookRepository.save(BookJpaEntity.builder() - .title("책제목") - .authorName("저자") - .isbn("isbn") - .bestSeller(false) - .publisher("출판사") - .imageUrl("img") - .pageCount(100) - .description("설명") - .build()); - } - - private RoomJpaEntity createRoom(BookJpaEntity book, CategoryJpaEntity category) { - return roomRepository.save(RoomJpaEntity.builder() - .title("방이름") - .description("설명") - .isPublic(true) - .startDate(LocalDate.now()) - .endDate(LocalDate.now().plusDays(5)) - .recruitCount(3) - .bookJpaEntity(book) - .categoryJpaEntity(category) - .build()); - } - - private CategoryJpaEntity createCategory() { - AliasJpaEntity alias = AliasJpaEntity.builder() - .value("칭호") - .imageUrl("test-image-url") - .color("red") - .build(); - - aliasRepository.save(alias); - - return categoryRepository.save(CategoryJpaEntity.builder() - .value("카테고리1") - .aliasForCategoryJpaEntity(alias) - .build()); - } - @Test @DisplayName("RecordJpaEntity 저장 및 조회 테스트") void saveAndFindRecord() { - UserJpaEntity user = createUser(); - RoomJpaEntity room = createRoom(createBook(), createCategory()); - - RecordJpaEntity record = RecordJpaEntity.builder() - .content("기록 내용") - .userJpaEntity(user) - .page(22) - .isOverview(false) - .roomJpaEntity(room) - .build(); + // given + AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createAlias()); + UserJpaEntity user = userRepository.save(TestEntityFactory.createUser(alias)); + BookJpaEntity book = bookRepository.save(TestEntityFactory.createBook()); + CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createCategory(alias)); + RoomJpaEntity room = roomRepository.save(TestEntityFactory.createRoom(book, category)); + RecordJpaEntity record = recordRepository.save(TestEntityFactory.createRecord(user, room)); - recordRepository.save(record); em.flush(); em.clear(); + // when RecordJpaEntity found = recordRepository.findById(record.getPostId()).orElseThrow(); + + // then assertThat(found).isNotNull(); assertThat(found.getPage()).isEqualTo(22); assertThat(found.getRoomJpaEntity().getTitle()).isEqualTo("방이름"); + assertThat(found.getUserJpaEntity().getNickname()).isEqualTo("테스터"); } } \ No newline at end of file diff --git a/src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java b/src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java index ecb06c6a3..97610553e 100644 --- a/src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java +++ b/src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java @@ -3,10 +3,11 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; -import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; import konkuk.thip.room.adapter.out.persistence.RoomJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; +import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -15,12 +16,13 @@ import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; -import java.time.LocalDate; +import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest @ActiveProfiles("test") @Import(konkuk.thip.config.TestQuerydslConfig.class) // DataJpaTest 이므로 JPA 제외 빈 추가로 import class RoomJpaEntityTest { + @PersistenceContext private EntityManager em; @@ -39,65 +41,25 @@ class RoomJpaEntityTest { @Test @DisplayName("RoomJpaEntity 저장 및 조회 테스트") void saveAndFindRoom() { - //given - BookJpaEntity book = BookJpaEntity.builder() - .title("테스트 책") - .authorName("테스트 저자") - .isbn("1234567890") - .bestSeller(false) - .publisher("테스트 출판사") - .imageUrl("http://test.image.url") - .pageCount(300) - .description("테스트 책 설명") - .build(); - - bookRepository.save(book); - - AliasJpaEntity alias = AliasJpaEntity.builder() - .value("칭호") - .imageUrl("test-image-url") - .color("red") - .build(); - - aliasRepository.save(alias); + // given + BookJpaEntity book = bookRepository.save(TestEntityFactory.createBook()); + AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createAlias()); + CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createCategory(alias)); + RoomJpaEntity room = roomRepository.save(TestEntityFactory.createRoom(book, category)); - CategoryJpaEntity category = CategoryJpaEntity.builder() - .value("카테고리1") - .aliasForCategoryJpaEntity(alias) - .build(); - - categoryRepository.save(category); - - RoomJpaEntity room = RoomJpaEntity.builder() - .title("테스트 방") - .description("테스트 방 설명") - .isPublic(true) - .startDate(LocalDate.of(2025, 6, 20)) - .endDate(LocalDate.of(2025, 6, 30)) - .recruitCount(5) - .bookJpaEntity(book) - .categoryJpaEntity(category) - .build(); - - //when - roomRepository.save(room); + // when em.flush(); em.clear(); RoomJpaEntity foundRoom = roomRepository.findById(room.getRoomId()).orElseThrow(); - //then - assert foundRoom.getTitle().equals("테스트 방"); - assert foundRoom.getDescription().equals("테스트 방 설명"); - assert foundRoom.isPublic(); - assert foundRoom.getStartDate().equals(LocalDate.of(2025, 6, 20)); - assert foundRoom.getEndDate().equals(LocalDate.of(2025, 6, 30)); - assert foundRoom.getRecruitCount() == 5; - assert foundRoom.getBookJpaEntity().getTitle().equals("테스트 책"); - assert foundRoom.getBookJpaEntity().getAuthorName().equals("테스트 저자"); - assert foundRoom.getBookJpaEntity().getIsbn().equals("1234567890"); + // then + assertThat(foundRoom.getTitle()).isEqualTo("방이름"); + assertThat(foundRoom.getDescription()).isEqualTo("설명"); + assertThat(foundRoom.isPublic()).isTrue(); + assertThat(foundRoom.getRecruitCount()).isEqualTo(3); + assertThat(foundRoom.getBookJpaEntity().getTitle()).isEqualTo("책제목"); + assertThat(foundRoom.getBookJpaEntity().getAuthorName()).isEqualTo("저자"); + assertThat(foundRoom.getBookJpaEntity().getIsbn()).isEqualTo("isbn"); } - - - } \ No newline at end of file diff --git a/src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java b/src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java index e28fee521..692dfe4ef 100644 --- a/src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java +++ b/src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java @@ -1,14 +1,13 @@ package konkuk.thip.room.adapter.out.jpa; import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; import konkuk.thip.room.adapter.out.persistence.RoomJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import konkuk.thip.user.adapter.out.jpa.UserRole; import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; import konkuk.thip.user.adapter.out.persistence.UserJpaRepository; import konkuk.thip.vote.adapter.out.jpa.VoteJpaEntity; @@ -20,8 +19,6 @@ import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; -import java.time.LocalDate; - import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest @@ -29,7 +26,7 @@ @Import(konkuk.thip.config.TestQuerydslConfig.class) // DataJpaTest 이므로 JPA 제외 빈 추가로 import class VoteJpaEntityTest { - @PersistenceContext + @Autowired private EntityManager em; @Autowired @@ -50,86 +47,35 @@ class VoteJpaEntityTest { @Autowired private CategoryJpaRepository categoryRepository; - private UserJpaEntity createUser() { - AliasJpaEntity alias = AliasJpaEntity.builder() - .value("칭호") - .imageUrl("test-image-url") - .color("red") - .build(); - aliasRepository.save(alias); - - UserJpaEntity user = UserJpaEntity.builder() - .email("test@test.com") - .nickname("테스터") - .imageUrl("https://test.img") - .aliasForUserJpaEntity(alias) - .role(UserRole.USER) - .build(); - return userRepository.save(user); - } - - private BookJpaEntity createBook() { - return bookRepository.save(BookJpaEntity.builder() - .title("책제목") - .authorName("저자") - .isbn("isbn") - .bestSeller(false) - .publisher("출판사") - .imageUrl("img") - .pageCount(100) - .description("설명") - .build()); - } - - private RoomJpaEntity createRoom(BookJpaEntity book, CategoryJpaEntity category) { - return roomRepository.save(RoomJpaEntity.builder() - .title("방이름") - .description("설명") - .isPublic(true) - .startDate(LocalDate.now()) - .endDate(LocalDate.now().plusDays(5)) - .recruitCount(3) - .bookJpaEntity(book) - .categoryJpaEntity(category) - .build()); - } - - private CategoryJpaEntity createCategory() { - AliasJpaEntity alias = AliasJpaEntity.builder() - .value("칭호") - .imageUrl("test-image-url") - .color("red") - .build(); - - aliasRepository.save(alias); - - return categoryRepository.save(CategoryJpaEntity.builder() - .value("카테고리1") - .aliasForCategoryJpaEntity(alias) - .build()); - } - @Test @DisplayName("VoteJpaEntity 저장 및 조회 테스트") void saveAndFindVote() { - UserJpaEntity user = createUser(); - RoomJpaEntity room = createRoom(createBook(), createCategory()); - - VoteJpaEntity vote = VoteJpaEntity.builder() + // given + AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createAlias()); + UserJpaEntity user = userRepository.save(TestEntityFactory.createUser(alias)); + BookJpaEntity book = bookRepository.save(TestEntityFactory.createBook()); + CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createCategory(alias)); + RoomJpaEntity room = roomRepository.save(TestEntityFactory.createRoom(book, category)); + + VoteJpaEntity vote = voteRepository.save(VoteJpaEntity.builder() .content("투표 내용") .userJpaEntity(user) .page(10) .isOverview(true) .roomJpaEntity(room) - .build(); + .build()); - voteRepository.save(vote); em.flush(); em.clear(); + // when VoteJpaEntity found = voteRepository.findById(vote.getPostId()).orElseThrow(); + + // then assertThat(found).isNotNull(); assertThat(found.getPage()).isEqualTo(10); + assertThat(found.getContent()).isEqualTo("투표 내용"); + assertThat(found.getUserJpaEntity().getNickname()).isEqualTo("테스터"); assertThat(found.getRoomJpaEntity().getTitle()).isEqualTo("방이름"); } } \ No newline at end of file diff --git a/src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java b/src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java index fc2056ac3..f303de831 100644 --- a/src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java +++ b/src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java @@ -2,6 +2,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; import konkuk.thip.user.adapter.out.persistence.UserJpaRepository; import org.junit.jupiter.api.DisplayName; @@ -31,23 +32,10 @@ class UserJpaEntityTest { @DisplayName("UserJpaEntity 저장 및 조회 테스트") void saveAndFindUser() { // given - AliasJpaEntity alias = AliasJpaEntity.builder() - .value("칭호") - .imageUrl("test-image-url") - .color("red") - .build(); - aliasJpaRepository.save(alias); - - UserJpaEntity user = UserJpaEntity.builder() - .email("test@test.com") - .nickname("테스터") - .imageUrl("https://test.img") - .aliasForUserJpaEntity(alias) - .role(UserRole.USER) - .build(); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createAlias()); + UserJpaEntity user = userRepository.save(TestEntityFactory.createUser(alias)); // when - userRepository.save(user); em.flush(); em.clear(); From 54ab951f68fcf10603c371c539fbc32d0e02ac96 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 18:08:17 +0900 Subject: [PATCH 22/55] =?UTF-8?q?[test]=20=EC=9D=B8=EC=A6=9D=EC=9D=B8?= =?UTF-8?q?=EA=B0=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/web/UserSignupControllerTest.java | 119 +++++++++++------- 1 file changed, 77 insertions(+), 42 deletions(-) diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java index cc7d92e83..24258a536 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import konkuk.thip.common.security.util.JwtUtil; +import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; @@ -19,6 +21,7 @@ import org.springframework.test.web.servlet.ResultActions; import static konkuk.thip.common.exception.code.ErrorCode.API_INVALID_PARAM; +import static konkuk.thip.common.exception.code.ErrorCode.AUTH_TOKEN_NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -42,6 +45,9 @@ class UserSignupControllerTest { @Autowired private UserJpaRepository userJpaRepository; + @Autowired + private JwtUtil jwtUtil; + @AfterEach void tearDown() { userJpaRepository.deleteAll(); @@ -49,7 +55,7 @@ void tearDown() { } @Test - @DisplayName("[칭호id, 닉네임, 이메일] 정보를 바탕으로 회원가입을 진행한다.") + @DisplayName("[칭호id, 닉네임] 정보를 바탕으로 회원가입을 진행한다.") void signup_success() throws Exception { //given : alias 생성, 회원가입 request 생성 AliasJpaEntity aliasJpaEntity = AliasJpaEntity.builder() @@ -63,10 +69,13 @@ void signup_success() throws Exception { aliasJpaEntity.getAliasId(), "테스트유저", "test@test.com" + "테스트 유저" ); - //when : 회원가입 api 호출 + //when : 회원가입 api 호출 + 임시 토큰 발급 + String testToken = jwtUtil.createSignupToken("kakao_12345678"); ResultActions result = mockMvc.perform(post("/users/signup") + .header("Authorization", "Bearer " + testToken) //헤더 추가 .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))); @@ -91,14 +100,15 @@ void signup_alias_id_null() throws Exception { //given: aliasId null PostUserSignupRequest request = new PostUserSignupRequest( null, - "테스트유저", - "test@test.com" + "테스트유저" ); //when //then - mockMvc.perform(post("/users/signup") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) + String testToken = jwtUtil.createSignupToken("kakao_12345678"); + ResultActions result = mockMvc.perform(post("/users/signup") + .header("Authorization", "Bearer " + testToken) //헤더 추가 + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value(API_INVALID_PARAM.getCode())) .andExpect(jsonPath("$.message", containsString("aliasId는 필수입니다."))); @@ -110,14 +120,15 @@ void signup_nickname_blank() throws Exception { //given: nickname blank PostUserSignupRequest request = new PostUserSignupRequest( 1L, - "", - "test@test.com" + "" ); //when //then - mockMvc.perform(post("/users/signup") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) + String testToken = jwtUtil.createSignupToken("kakao_12345678"); + ResultActions result = mockMvc.perform(post("/users/signup") + .header("Authorization", "Bearer " + testToken) //헤더 추가 + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value(API_INVALID_PARAM.getCode())) .andExpect(jsonPath("$.message", containsString("닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)"))); @@ -129,8 +140,7 @@ void signup_nickname_invalid_pattern() throws Exception { //given: nickname with invalid characters PostUserSignupRequest request = new PostUserSignupRequest( 1L, - "닉네임!!", - "test@test.com" + "닉네임!!" ); //when //then @@ -148,54 +158,79 @@ void signup_nickname_too_long() throws Exception { //given: 11글자 nickname PostUserSignupRequest request = new PostUserSignupRequest( 1L, - "11글자닉네임입니다아", - "test@test.com" + "11글자닉네임입니다아" ); //when //then - mockMvc.perform(post("/users/signup") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) + String testToken = jwtUtil.createSignupToken("kakao_12345678"); + ResultActions result = mockMvc.perform(post("/users/signup") + .header("Authorization", "Bearer " + testToken) //헤더 추가 + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value(API_INVALID_PARAM.getCode())) .andExpect(jsonPath("$.message", containsString("닉네임은 최대 10자 입니다."))); } @Test - @DisplayName("[이메일]값이 공백일 경우, 400 error가 발생한다.") - void signup_email_blank() throws Exception { - //given - PostUserSignupRequest request = new PostUserSignupRequest( - 1L, - "테스트유저", - "" + @DisplayName("임시 토큰을 통해 @Oauth2Id로 oauth2Id를 정확히 추출하여 회원가입에 성공한다.") + void signup_whenValidSignupToken_thenExtractOauth2IdCorrectly() throws Exception { + //given : alias 데이터 저장 + AliasJpaEntity aliasJpaEntity = AliasJpaEntity.builder() + .value("테스트칭호") + .color("green") + .imageUrl("http://image.url") + .build(); + aliasJpaRepository.save(aliasJpaEntity); + + //회원가입 request 생성 + UserSignupRequest request = new UserSignupRequest( + aliasJpaEntity.getAliasId(), + "테스트유저" ); - //when //then - mockMvc.perform(post("/users/signup") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.code").value(API_INVALID_PARAM.getCode())) - .andExpect(jsonPath("$.message", containsString("이메일은 공백일 수 없습니다."))); + //when : 임시 토큰 생성 + String expectedOauth2Id = "kakao_12345678"; + String testToken = jwtUtil.createSignupToken(expectedOauth2Id); + + //when : 회원가입 API 호출 + ResultActions result = mockMvc.perform(post("/users/signup") + .header("Authorization", "Bearer " + testToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + //then : 응답 검증 + result.andExpect(status().isOk()) + .andExpect(jsonPath("$.data.userId").exists()); + + //등록된 사용자 oauth2Id 검증 + String responseBody = result.andReturn().getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + Long savedUserId = jsonNode.path("data").path("userId").asLong(); + + UserJpaEntity savedUser = userJpaRepository.findById(savedUserId).orElseThrow(); + + assertThat(savedUser.getOauth2Id()).isEqualTo(expectedOauth2Id); + assertThat(savedUser.getNickname()).isEqualTo("테스트유저"); } @Test - @DisplayName("[이메일]값이 유효한 이메일 형식이 아닐 경우, 400 error가 발생한다.") - void signup_email_invalid_format() throws Exception { - //given - PostUserSignupRequest request = new PostUserSignupRequest( + @DisplayName("헤더에 토큰을 넣지 않고 요청시에 401 error가 발생한다.") + void signup_whenNoToken_thenUnauthorized() throws Exception { + //given: aliasId null + UserSignupRequest request = new UserSignupRequest( 1L, - "테스트유저", - "invalid-email-format" + "테스트유저" ); //when //then mockMvc.perform(post("/users/signup") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.code").value("40002")) - .andExpect(jsonPath("$.message", containsString("이메일 형식이 올바르지 않습니다."))); + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.code").value(AUTH_TOKEN_NOT_FOUND.getCode())) + .andExpect(jsonPath("$.message", containsString(AUTH_TOKEN_NOT_FOUND.getMessage()))); } + + } From 800bdb092239ccc52c9a231eedcc2c204df9f208 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 18:27:43 +0900 Subject: [PATCH 23/55] =?UTF-8?q?[refactor]=20=EC=83=81=EC=88=98=20enum=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/constant/AuthParameters.java | 23 +++++++++++++++++++ .../security/constant/JwtAuthParameters.java | 18 --------------- .../filter/JwtAuthenticationFilter.java | 7 +++--- .../oauth2/CustomOAuth2UserService.java | 7 ++++-- .../security/oauth2/CustomSuccessHandler.java | 4 ++-- .../security/oauth2/GoogleUserDetails.java | 7 ++++-- .../security/oauth2/KakaoUserDetails.java | 8 ++++--- .../thip/common/security/util/JwtUtil.java | 11 +++++---- .../adapter/in/web/UserCommandController.java | 4 ++-- 9 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 src/main/java/konkuk/thip/common/security/constant/AuthParameters.java delete mode 100644 src/main/java/konkuk/thip/common/security/constant/JwtAuthParameters.java diff --git a/src/main/java/konkuk/thip/common/security/constant/AuthParameters.java b/src/main/java/konkuk/thip/common/security/constant/AuthParameters.java new file mode 100644 index 000000000..3349db319 --- /dev/null +++ b/src/main/java/konkuk/thip/common/security/constant/AuthParameters.java @@ -0,0 +1,23 @@ +package konkuk.thip.common.security.constant; + +import lombok.Getter; + +@Getter +public enum AuthParameters { + JWT_HEADER_KEY("Authorization"), + JWT_PREFIX("Bearer "), + KAKAO("kakao"), + GOOGLE("google"), + KAKAO_PROVIDER_ID_KEY("id"), + GOOGLE_PROVIDER_ID_KEY("sub"), + JWT_ACCESS_TOKEN_KEY("userId"), + JWT_SIGNUP_TOKEN_KEY("oauth2Id"), + ; + + private final String value; + + AuthParameters(String value) { + this.value = value; + } +} + diff --git a/src/main/java/konkuk/thip/common/security/constant/JwtAuthParameters.java b/src/main/java/konkuk/thip/common/security/constant/JwtAuthParameters.java deleted file mode 100644 index ee2b49649..000000000 --- a/src/main/java/konkuk/thip/common/security/constant/JwtAuthParameters.java +++ /dev/null @@ -1,18 +0,0 @@ -package konkuk.thip.common.security.constant; - -import lombok.Getter; - -@Getter -public enum JwtAuthParameters { - JWT_HEADER_KEY("Authorization"), - JWT_PREFIX("Bearer "), - - ; - - private final String value; - - JwtAuthParameters(String value) { - this.value = value; - } -} - diff --git a/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java b/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java index 7cde061af..abcb45aa5 100644 --- a/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java +++ b/src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java @@ -19,8 +19,7 @@ import java.io.IOException; import static konkuk.thip.common.exception.code.ErrorCode.*; -import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_HEADER_KEY; -import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_PREFIX; +import static konkuk.thip.common.security.constant.AuthParameters.*; @Slf4j @Component @@ -50,10 +49,10 @@ protected void doFilterInternal(HttpServletRequest request, LoginUser loginUser = jwtUtil.getLoginUser(token); if (loginUser.userId() != null) { - request.setAttribute("userId", loginUser.userId()); + request.setAttribute(JWT_ACCESS_TOKEN_KEY.getValue(), loginUser.userId()); } else { - request.setAttribute("oauth2Id", loginUser.oauth2Id()); + request.setAttribute(JWT_SIGNUP_TOKEN_KEY.getValue(), loginUser.oauth2Id()); } CustomOAuth2User customOAuth2User = new CustomOAuth2User(loginUser); diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java index fb97d8b9a..36760e194 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java @@ -10,6 +10,9 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; +import static konkuk.thip.common.security.constant.AuthParameters.GOOGLE; +import static konkuk.thip.common.security.constant.AuthParameters.KAKAO; + @Slf4j @Service @RequiredArgsConstructor @@ -26,10 +29,10 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic String registrationId = userRequest.getClientRegistration().getRegistrationId(); OAuth2UserDetails oAuth2UserDetails = null; - if (registrationId.equals("kakao")) { + if (registrationId.equals(KAKAO.getValue())) { oAuth2UserDetails = new KakaoUserDetails(oAuth2User.getAttributes()); } - else if (registrationId.equals("google")) { + else if (registrationId.equals(GOOGLE.getValue())) { oAuth2UserDetails = new GoogleUserDetails(oAuth2User.getAttributes()); } else { diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java index 19fc0fda9..778b9671c 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java @@ -13,8 +13,8 @@ import java.io.IOException; -import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_HEADER_KEY; -import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_PREFIX; +import static konkuk.thip.common.security.constant.AuthParameters.JWT_HEADER_KEY; +import static konkuk.thip.common.security.constant.AuthParameters.JWT_PREFIX; @Component @RequiredArgsConstructor diff --git a/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java index db9f9a0aa..5e7dc790c 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java @@ -2,6 +2,9 @@ import java.util.Map; +import static konkuk.thip.common.security.constant.AuthParameters.GOOGLE; +import static konkuk.thip.common.security.constant.AuthParameters.GOOGLE_PROVIDER_ID_KEY; + public class GoogleUserDetails implements OAuth2UserDetails{ private final Map attribute; @@ -14,13 +17,13 @@ public GoogleUserDetails(Map attribute) { @Override public String getProvider() { - return "google"; + return GOOGLE.getValue(); } @Override public String getProviderId() { - return attribute.get("sub").toString(); + return attribute.get(GOOGLE_PROVIDER_ID_KEY.getValue()).toString(); } // @Override diff --git a/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java index e533fa7c5..c37d52247 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java @@ -1,8 +1,10 @@ package konkuk.thip.common.security.oauth2; -import java.util.LinkedHashMap; import java.util.Map; +import static konkuk.thip.common.security.constant.AuthParameters.KAKAO; +import static konkuk.thip.common.security.constant.AuthParameters.KAKAO_PROVIDER_ID_KEY; + public class KakaoUserDetails implements OAuth2UserDetails { private Map attributes; @@ -13,12 +15,12 @@ public KakaoUserDetails(Map attributes) { @Override public String getProvider() { - return "kakao"; + return KAKAO.getValue(); } @Override public String getProviderId() { - return attributes.get("id").toString(); + return attributes.get(KAKAO_PROVIDER_ID_KEY.getValue()).toString(); } // @Override diff --git a/src/main/java/konkuk/thip/common/security/util/JwtUtil.java b/src/main/java/konkuk/thip/common/security/util/JwtUtil.java index 08d734ebd..48ef9068d 100644 --- a/src/main/java/konkuk/thip/common/security/util/JwtUtil.java +++ b/src/main/java/konkuk/thip/common/security/util/JwtUtil.java @@ -14,6 +14,9 @@ import java.nio.charset.StandardCharsets; import java.util.Date; +import static konkuk.thip.common.security.constant.AuthParameters.JWT_ACCESS_TOKEN_KEY; +import static konkuk.thip.common.security.constant.AuthParameters.JWT_SIGNUP_TOKEN_KEY; + @Slf4j @Component public class JwtUtil { @@ -30,7 +33,7 @@ public JwtUtil(@Value("${jwt.secret}") String secret) { public String createSignupToken(String oauth2Id) { return Jwts.builder() - .claim("oauth2Id", oauth2Id) + .claim(JWT_SIGNUP_TOKEN_KEY.getValue(), oauth2Id) .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() + signupTokenExpiredMs)) .signWith(secretKey) @@ -39,7 +42,7 @@ public String createSignupToken(String oauth2Id) { public String createAccessToken(Long userId) { return Jwts.builder() - .claim("userId", userId) + .claim(JWT_ACCESS_TOKEN_KEY.getValue(), userId) .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() + tokenExpiredMs)) .signWith(secretKey) @@ -67,11 +70,11 @@ public Boolean isExpired(String token) { } private String getOauth2Id(String token) { - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("oauth2Id", String.class); + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get(JWT_SIGNUP_TOKEN_KEY.getValue(), String.class); } private Long getUserId(String token) { - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("userId", Long.class); + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get(JWT_ACCESS_TOKEN_KEY.getValue(), Long.class); } public LoginUser getLoginUser(String token) { diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java index 4f4f710f9..b2a98124b 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java @@ -18,8 +18,8 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_HEADER_KEY; -import static konkuk.thip.common.security.constant.JwtAuthParameters.JWT_PREFIX; +import static konkuk.thip.common.security.constant.AuthParameters.JWT_HEADER_KEY; +import static konkuk.thip.common.security.constant.AuthParameters.JWT_PREFIX; @RestController @RequiredArgsConstructor From f0003a9da1e937a96fde171b6e602b8edb962c0e Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 19:42:53 +0900 Subject: [PATCH 24/55] =?UTF-8?q?[refactor]=20=EA=B3=B1=ED=95=98=EA=B8=B0?= =?UTF-8?q?=20=EC=97=B0=EC=82=B0=20=EC=82=AD=EC=A0=9C=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/common/security/util/JwtUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/common/security/util/JwtUtil.java b/src/main/java/konkuk/thip/common/security/util/JwtUtil.java index 48ef9068d..947cd9b27 100644 --- a/src/main/java/konkuk/thip/common/security/util/JwtUtil.java +++ b/src/main/java/konkuk/thip/common/security/util/JwtUtil.java @@ -24,8 +24,8 @@ public class JwtUtil { private final SecretKey secretKey; //todo 확정 후 환경변수로 변경 - private final long tokenExpiredMs = 1000 * 60 * 60 * 24; // 24시간 - private final long signupTokenExpiredMs = 5 * 60 * 1000; // 5분 + private final long tokenExpiredMs = 86400000; // 24시간 + private final long signupTokenExpiredMs = 300000; // 5분 public JwtUtil(@Value("${jwt.secret}") String secret) { secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); From 85102677cda7776714c809d02b5316fbd2a1d836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sat, 28 Jun 2025 01:37:52 +0900 Subject: [PATCH 25/55] =?UTF-8?q?[feat]=20BookDto=20=EC=9E=91=EC=84=B1=20(?= =?UTF-8?q?#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/book/adapter/in/web/response/BookDto.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/konkuk/thip/book/adapter/in/web/response/BookDto.java diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDto.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDto.java new file mode 100644 index 000000000..2750096a4 --- /dev/null +++ b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDto.java @@ -0,0 +1,9 @@ +package konkuk.thip.book.adapter.in.web.response; + +public record BookDto( + String title, + String imageUrl, + String authorName, + String publisher, + String isbn +) {} From ecdc972ff97df9ddabb2514ba6630bba3cd80416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sat, 28 Jun 2025 01:38:07 +0900 Subject: [PATCH 26/55] =?UTF-8?q?[feat]=20BookQueryController=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/book/adapter/in/web/BookQueryController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java index 3bb6113f3..a9892eb4c 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java @@ -1,5 +1,8 @@ package konkuk.thip.book.adapter.in.web; +import konkuk.thip.book.adapter.in.web.response.GetBookSearchListResponse; +import konkuk.thip.book.application.port.in.BookSearchUseCase; +import konkuk.thip.common.dto.BaseResponse; import konkuk.thip.book.adapter.in.web.response.GetBookSearchListResponse; import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; import konkuk.thip.book.application.port.in.BookSearchUseCase; From 7b1ae2a87250901cd733e374c87e6b333f78b8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sat, 28 Jun 2025 01:40:27 +0900 Subject: [PATCH 27/55] =?UTF-8?q?[feat]=20NaverApiUtil=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/konkuk/thip/util/NaverApiUtil.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/main/java/konkuk/thip/util/NaverApiUtil.java diff --git a/src/main/java/konkuk/thip/util/NaverApiUtil.java b/src/main/java/konkuk/thip/util/NaverApiUtil.java new file mode 100644 index 000000000..b897be658 --- /dev/null +++ b/src/main/java/konkuk/thip/util/NaverApiUtil.java @@ -0,0 +1,107 @@ +package konkuk.thip.util; + +import konkuk.thip.common.exception.BusinessException; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +import static konkuk.thip.common.exception.code.ErrorCode.*; + +@RequiredArgsConstructor +@Component +public class NaverApiUtil { + + @Value("${naver.clientId}") + private String clientId; + @Value("${naver.clientSecret}") + private String clientSecret; + + private final String NAVER_BOOK_SEARCH_URL = "https://openapi.naver.com/v1/search/book.xml?query="; //책 검색 결과 조회 + + + public String searchBook(String keyword, int start){ + String query = keywordToEncoding(keyword); + String url = buildSearchApiUrl(query, start); + + Map requestHeaders = new HashMap<>(); + requestHeaders.put("X-Naver-Client-Id", clientId); + requestHeaders.put("X-Naver-Client-Secret", clientSecret); + + return get(url,requestHeaders); + } + + private String buildSearchApiUrl(String query,Integer start) { + return NAVER_BOOK_SEARCH_URL+query+"&start="+start; + } + + private String keywordToEncoding(String keyword) { + String text = null; + try { + text = URLEncoder.encode(keyword, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new BusinessException(BOOK_KEYWORD_ENCODING_FAILED); + } + return text; + } + + + String get(String apiUrl, Map requestHeaders){ + HttpURLConnection con = connect(apiUrl); + try { + con.setRequestMethod("GET"); + for(Map.Entry header :requestHeaders.entrySet()) { + con.setRequestProperty(header.getKey(), header.getValue()); + } + + int responseCode = con.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 호출 + return readBody(con.getInputStream()); + } else { // 오류 발생 + return readBody(con.getErrorStream()); + } + } catch (IOException e) { + throw new BusinessException(BOOK_NAVER_API_REQUEST_ERROR); + } finally { + con.disconnect(); + } + } + + + private HttpURLConnection connect(String apiUrl){ + try { + URL url = new URL(apiUrl); + return (HttpURLConnection)url.openConnection(); + } catch (MalformedURLException e) { + throw new BusinessException(BOOK_NAVER_API_URL_ERROR); + } catch (IOException e) { + throw new BusinessException(BOOK_NAVER_API_URL_HTTP_CONNECT_FAILED); + } + } + + + private String readBody(InputStream body){ + InputStreamReader streamReader = new InputStreamReader(body); + + try (BufferedReader lineReader = new BufferedReader(streamReader)) { + StringBuilder responseBody = new StringBuilder(); + + String line; + while ((line = lineReader.readLine()) != null) { + responseBody.append(line); + } + + return responseBody.toString(); + } catch (IOException e) { + throw new BusinessException(BOOK_NAVER_API_RESPONSE_ERROR); + } + } + +} From 31b36349c1833fbb37e8b8a9e20efb8aba9a8aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sat, 28 Jun 2025 01:40:56 +0900 Subject: [PATCH 28/55] =?UTF-8?q?[feat]=20NaverBookXmlParser=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/util/NaverBookXmlParser.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/main/java/konkuk/thip/util/NaverBookXmlParser.java diff --git a/src/main/java/konkuk/thip/util/NaverBookXmlParser.java b/src/main/java/konkuk/thip/util/NaverBookXmlParser.java new file mode 100644 index 000000000..30099821c --- /dev/null +++ b/src/main/java/konkuk/thip/util/NaverBookXmlParser.java @@ -0,0 +1,64 @@ +package konkuk.thip.util; + +import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; +import konkuk.thip.book.domain.Book; +import konkuk.thip.common.exception.BusinessException; +import konkuk.thip.common.exception.code.ErrorCode; +import org.w3c.dom.*; +import javax.xml.parsers.*; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import org.xml.sax.InputSource; + +public class NaverBookXmlParser { + + public static NaverBookParseResult parse(String xml) { + List books = new ArrayList<>(); + int total = -1; + int start = -1; + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + InputSource is = new InputSource(new StringReader(xml)); + Document doc = builder.parse(is); + + NodeList channelNodes = doc.getElementsByTagName("channel"); + if (channelNodes.getLength() > 0) { + Element channel = (Element) channelNodes.item(0); + total = Integer.parseInt(getTagValue(channel, "total")); + start = Integer.parseInt(getTagValue(channel, "start")); + + NodeList itemNodes = channel.getElementsByTagName("item"); + for (int i = 0; i < itemNodes.getLength(); i++) { + Element item = (Element) itemNodes.item(i); + String title = getTagValue(item, "title"); + String imageUrl = getTagValue(item, "image"); + String authorName = getTagValue(item, "author"); + String publisher = getTagValue(item, "publisher"); + String isbn = getTagValue(item, "isbn"); + Book book = Book.builder() + .title(title) + .imageUrl(imageUrl) + .authorName(authorName) + .publisher(publisher) + .isbn(isbn) + .build(); + books.add(book); + } + } + } catch (Exception e) { + throw new BusinessException(ErrorCode.BOOK_NAVER_API_PARSING_ERROR); + } + return NaverBookParseResult.of(books, total, start); + } + + private static String getTagValue(Element element, String tag) { + NodeList nodeList = element.getElementsByTagName(tag); + if (nodeList.getLength() > 0 && nodeList.item(0).getFirstChild() != null) { + return nodeList.item(0).getFirstChild().getNodeValue(); + } + return ""; + } + +} From ddf9647356cef9afd86aa9a3c12620eb736f966f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sat, 28 Jun 2025 01:42:08 +0900 Subject: [PATCH 29/55] =?UTF-8?q?[test]=20NaverApiUtilTest=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/util/NaverApiUtilTest.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/test/java/konkuk/thip/util/NaverApiUtilTest.java diff --git a/src/test/java/konkuk/thip/util/NaverApiUtilTest.java b/src/test/java/konkuk/thip/util/NaverApiUtilTest.java new file mode 100644 index 000000000..1bdbe63be --- /dev/null +++ b/src/test/java/konkuk/thip/util/NaverApiUtilTest.java @@ -0,0 +1,49 @@ +package konkuk.thip.util; + +import konkuk.thip.common.exception.BusinessException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static konkuk.thip.common.exception.code.ErrorCode.BOOK_NAVER_API_REQUEST_ERROR; +import static org.assertj.core.api.Assertions.*; + +class NaverApiUtilTest { + + @Test + @DisplayName("정상 응답을 모킹하여 반환") + void searchBook_success_mocking() { + // given + NaverApiUtil naverApiUtil = Mockito.spy(new NaverApiUtil()); + + // get 메서드를 spy로 모킹 (실제 네트워크 호출 없이 원하는 응답 반환) + String expectedXml = "11"; + Mockito.doReturn(expectedXml) + .when(naverApiUtil) + .get(Mockito.anyString(), Mockito.anyMap()); + + // when + String result = naverApiUtil.searchBook("테스트", 1); + + // then + assertThat(result).contains("1"); + assertThat(result).contains("1"); + } + + @Test + @DisplayName("get 메서드에서 예외가 발생하면 BusinessException이 발생") + void searchBook_ioException() { + // given + NaverApiUtil naverApiUtil = Mockito.spy(new NaverApiUtil()); + + // get 메서드가 예외를 던지도록 설정 + Mockito.doThrow(new BusinessException(BOOK_NAVER_API_REQUEST_ERROR)) + .when(naverApiUtil) + .get(Mockito.anyString(), Mockito.anyMap()); + + // when & then + assertThatThrownBy(() -> naverApiUtil.searchBook("테스트", 1)) + .isInstanceOf(BusinessException.class) + .hasMessageContaining(BOOK_NAVER_API_REQUEST_ERROR.getMessage()); + } +} From 943e45a22fe7017e0fc8cd3afae5f5d919dc53b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 29 Jun 2025 17:42:33 +0900 Subject: [PATCH 30/55] =?UTF-8?q?[refactor]=20NaverApiUtil=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=20url=20=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/konkuk/thip/util/NaverApiUtil.java | 107 ------------------ 1 file changed, 107 deletions(-) delete mode 100644 src/main/java/konkuk/thip/util/NaverApiUtil.java diff --git a/src/main/java/konkuk/thip/util/NaverApiUtil.java b/src/main/java/konkuk/thip/util/NaverApiUtil.java deleted file mode 100644 index b897be658..000000000 --- a/src/main/java/konkuk/thip/util/NaverApiUtil.java +++ /dev/null @@ -1,107 +0,0 @@ -package konkuk.thip.util; - -import konkuk.thip.common.exception.BusinessException; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.io.*; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLEncoder; -import java.util.HashMap; -import java.util.Map; - -import static konkuk.thip.common.exception.code.ErrorCode.*; - -@RequiredArgsConstructor -@Component -public class NaverApiUtil { - - @Value("${naver.clientId}") - private String clientId; - @Value("${naver.clientSecret}") - private String clientSecret; - - private final String NAVER_BOOK_SEARCH_URL = "https://openapi.naver.com/v1/search/book.xml?query="; //책 검색 결과 조회 - - - public String searchBook(String keyword, int start){ - String query = keywordToEncoding(keyword); - String url = buildSearchApiUrl(query, start); - - Map requestHeaders = new HashMap<>(); - requestHeaders.put("X-Naver-Client-Id", clientId); - requestHeaders.put("X-Naver-Client-Secret", clientSecret); - - return get(url,requestHeaders); - } - - private String buildSearchApiUrl(String query,Integer start) { - return NAVER_BOOK_SEARCH_URL+query+"&start="+start; - } - - private String keywordToEncoding(String keyword) { - String text = null; - try { - text = URLEncoder.encode(keyword, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new BusinessException(BOOK_KEYWORD_ENCODING_FAILED); - } - return text; - } - - - String get(String apiUrl, Map requestHeaders){ - HttpURLConnection con = connect(apiUrl); - try { - con.setRequestMethod("GET"); - for(Map.Entry header :requestHeaders.entrySet()) { - con.setRequestProperty(header.getKey(), header.getValue()); - } - - int responseCode = con.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 호출 - return readBody(con.getInputStream()); - } else { // 오류 발생 - return readBody(con.getErrorStream()); - } - } catch (IOException e) { - throw new BusinessException(BOOK_NAVER_API_REQUEST_ERROR); - } finally { - con.disconnect(); - } - } - - - private HttpURLConnection connect(String apiUrl){ - try { - URL url = new URL(apiUrl); - return (HttpURLConnection)url.openConnection(); - } catch (MalformedURLException e) { - throw new BusinessException(BOOK_NAVER_API_URL_ERROR); - } catch (IOException e) { - throw new BusinessException(BOOK_NAVER_API_URL_HTTP_CONNECT_FAILED); - } - } - - - private String readBody(InputStream body){ - InputStreamReader streamReader = new InputStreamReader(body); - - try (BufferedReader lineReader = new BufferedReader(streamReader)) { - StringBuilder responseBody = new StringBuilder(); - - String line; - while ((line = lineReader.readLine()) != null) { - responseBody.append(line); - } - - return responseBody.toString(); - } catch (IOException e) { - throw new BusinessException(BOOK_NAVER_API_RESPONSE_ERROR); - } - } - -} From 40c1e2af9077b5ac3012c1449779d1e08642625e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 29 Jun 2025 17:42:46 +0900 Subject: [PATCH 31/55] =?UTF-8?q?[refactor]=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/util/NaverApiUtilTest.java | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 src/test/java/konkuk/thip/util/NaverApiUtilTest.java diff --git a/src/test/java/konkuk/thip/util/NaverApiUtilTest.java b/src/test/java/konkuk/thip/util/NaverApiUtilTest.java deleted file mode 100644 index 1bdbe63be..000000000 --- a/src/test/java/konkuk/thip/util/NaverApiUtilTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package konkuk.thip.util; - -import konkuk.thip.common.exception.BusinessException; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static konkuk.thip.common.exception.code.ErrorCode.BOOK_NAVER_API_REQUEST_ERROR; -import static org.assertj.core.api.Assertions.*; - -class NaverApiUtilTest { - - @Test - @DisplayName("정상 응답을 모킹하여 반환") - void searchBook_success_mocking() { - // given - NaverApiUtil naverApiUtil = Mockito.spy(new NaverApiUtil()); - - // get 메서드를 spy로 모킹 (실제 네트워크 호출 없이 원하는 응답 반환) - String expectedXml = "11"; - Mockito.doReturn(expectedXml) - .when(naverApiUtil) - .get(Mockito.anyString(), Mockito.anyMap()); - - // when - String result = naverApiUtil.searchBook("테스트", 1); - - // then - assertThat(result).contains("1"); - assertThat(result).contains("1"); - } - - @Test - @DisplayName("get 메서드에서 예외가 발생하면 BusinessException이 발생") - void searchBook_ioException() { - // given - NaverApiUtil naverApiUtil = Mockito.spy(new NaverApiUtil()); - - // get 메서드가 예외를 던지도록 설정 - Mockito.doThrow(new BusinessException(BOOK_NAVER_API_REQUEST_ERROR)) - .when(naverApiUtil) - .get(Mockito.anyString(), Mockito.anyMap()); - - // when & then - assertThatThrownBy(() -> naverApiUtil.searchBook("테스트", 1)) - .isInstanceOf(BusinessException.class) - .hasMessageContaining(BOOK_NAVER_API_REQUEST_ERROR.getMessage()); - } -} From 91872961ea119771579be8e68e4f0a50b37fa4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 29 Jun 2025 17:43:32 +0900 Subject: [PATCH 32/55] =?UTF-8?q?[refactor]=20BookDto=EC=9D=B4=EB=84=88?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EB=B0=8F=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC,=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20response=EA=B0=92=20=EB=B6=84=EB=A6=AC=20(?= =?UTF-8?q?#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/book/adapter/in/web/response/BookDto.java | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/main/java/konkuk/thip/book/adapter/in/web/response/BookDto.java diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDto.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDto.java deleted file mode 100644 index 2750096a4..000000000 --- a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDto.java +++ /dev/null @@ -1,9 +0,0 @@ -package konkuk.thip.book.adapter.in.web.response; - -public record BookDto( - String title, - String imageUrl, - String authorName, - String publisher, - String isbn -) {} From 781cc4dfe814c48f11127ff20c83786591efd543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 29 Jun 2025 17:44:06 +0900 Subject: [PATCH 33/55] =?UTF-8?q?[refactor]=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/book/adapter/in/web/BookQueryController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java index a9892eb4c..34a932b65 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java @@ -1,6 +1,7 @@ package konkuk.thip.book.adapter.in.web; import konkuk.thip.book.adapter.in.web.response.GetBookSearchListResponse; +import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; import konkuk.thip.book.application.port.in.BookSearchUseCase; import konkuk.thip.common.dto.BaseResponse; import konkuk.thip.book.adapter.in.web.response.GetBookSearchListResponse; From ad4eab1ec211568766d2e9ad445561f28ad94716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 29 Jun 2025 17:44:54 +0900 Subject: [PATCH 34/55] =?UTF-8?q?[refactor]=20NaverBookParseResult=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=EA=B0=92=20NaverBook=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/util/NaverBookXmlParser.java | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 src/main/java/konkuk/thip/util/NaverBookXmlParser.java diff --git a/src/main/java/konkuk/thip/util/NaverBookXmlParser.java b/src/main/java/konkuk/thip/util/NaverBookXmlParser.java deleted file mode 100644 index 30099821c..000000000 --- a/src/main/java/konkuk/thip/util/NaverBookXmlParser.java +++ /dev/null @@ -1,64 +0,0 @@ -package konkuk.thip.util; - -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; -import konkuk.thip.book.domain.Book; -import konkuk.thip.common.exception.BusinessException; -import konkuk.thip.common.exception.code.ErrorCode; -import org.w3c.dom.*; -import javax.xml.parsers.*; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; -import org.xml.sax.InputSource; - -public class NaverBookXmlParser { - - public static NaverBookParseResult parse(String xml) { - List books = new ArrayList<>(); - int total = -1; - int start = -1; - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - InputSource is = new InputSource(new StringReader(xml)); - Document doc = builder.parse(is); - - NodeList channelNodes = doc.getElementsByTagName("channel"); - if (channelNodes.getLength() > 0) { - Element channel = (Element) channelNodes.item(0); - total = Integer.parseInt(getTagValue(channel, "total")); - start = Integer.parseInt(getTagValue(channel, "start")); - - NodeList itemNodes = channel.getElementsByTagName("item"); - for (int i = 0; i < itemNodes.getLength(); i++) { - Element item = (Element) itemNodes.item(i); - String title = getTagValue(item, "title"); - String imageUrl = getTagValue(item, "image"); - String authorName = getTagValue(item, "author"); - String publisher = getTagValue(item, "publisher"); - String isbn = getTagValue(item, "isbn"); - Book book = Book.builder() - .title(title) - .imageUrl(imageUrl) - .authorName(authorName) - .publisher(publisher) - .isbn(isbn) - .build(); - books.add(book); - } - } - } catch (Exception e) { - throw new BusinessException(ErrorCode.BOOK_NAVER_API_PARSING_ERROR); - } - return NaverBookParseResult.of(books, total, start); - } - - private static String getTagValue(Element element, String tag) { - NodeList nodeList = element.getElementsByTagName(tag); - if (nodeList.getLength() > 0 && nodeList.item(0).getFirstChild() != null) { - return nodeList.item(0).getFirstChild().getNodeValue(); - } - return ""; - } - -} From 12c17d0e65956b9adb6702a0d802f8bc39b59e7d Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 29 Jun 2025 19:49:51 +0900 Subject: [PATCH 35/55] =?UTF-8?q?[refactor]=20findByOauth2Id=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20=ED=83=80=EC=9E=85=20Optional=EB=A1=9C=20=EA=B0=90?= =?UTF-8?q?=EC=8B=B8=EA=B8=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/security/oauth2/CustomOAuth2UserService.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java index 36760e194..6d1c452ad 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java @@ -10,6 +10,8 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; +import java.util.Optional; + import static konkuk.thip.common.security.constant.AuthParameters.GOOGLE; import static konkuk.thip.common.security.constant.AuthParameters.KAKAO; @@ -41,13 +43,13 @@ else if (registrationId.equals(GOOGLE.getValue())) { } String oauth2Id = oAuth2UserDetails.getProvider() + "_" + oAuth2UserDetails.getProviderId(); //kakao_1234567890 - UserJpaEntity existingUser = userJpaRepository.findByOauth2Id(oauth2Id); - if(existingUser == null) { + Optional existingUser = userJpaRepository.findByOauth2Id(oauth2Id); + if(existingUser.isEmpty()) { LoginUser newUser = LoginUser.createNewUser(oauth2Id); return new CustomOAuth2User(newUser); } - LoginUser loginUser = LoginUser.createExistingUser(oauth2Id, existingUser.getUserId()); + LoginUser loginUser = LoginUser.createExistingUser(oauth2Id, existingUser.get().getUserId()); return new CustomOAuth2User(loginUser); } } From adcd74a023f8cd3a24b35373335e9cae48fb04ac Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 29 Jun 2025 19:54:46 +0900 Subject: [PATCH 36/55] =?UTF-8?q?[refactor]=20ArgumentResolver=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=95=88=EC=A0=95=EC=84=B1=20=EB=B3=B4=EC=9E=A5=20?= =?UTF-8?q?(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/argument_resolver/Oauth2IdArgumentResolver.java | 4 ++-- .../security/argument_resolver/UserIdArgumentResolver.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/konkuk/thip/common/security/argument_resolver/Oauth2IdArgumentResolver.java b/src/main/java/konkuk/thip/common/security/argument_resolver/Oauth2IdArgumentResolver.java index 9891ac26f..34d590e14 100644 --- a/src/main/java/konkuk/thip/common/security/argument_resolver/Oauth2IdArgumentResolver.java +++ b/src/main/java/konkuk/thip/common/security/argument_resolver/Oauth2IdArgumentResolver.java @@ -24,7 +24,7 @@ public boolean supportsParameter(MethodParameter parameter) { } @Override - public Object resolveArgument(MethodParameter parameter, + public String resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { @@ -33,6 +33,6 @@ public Object resolveArgument(MethodParameter parameter, if (oauth2Id == null) { throw new AuthException(AUTH_TOKEN_NOT_FOUND); } - return oauth2Id; + return (String) oauth2Id; } } \ No newline at end of file diff --git a/src/main/java/konkuk/thip/common/security/argument_resolver/UserIdArgumentResolver.java b/src/main/java/konkuk/thip/common/security/argument_resolver/UserIdArgumentResolver.java index cfbb4d75e..f2b8a7d06 100644 --- a/src/main/java/konkuk/thip/common/security/argument_resolver/UserIdArgumentResolver.java +++ b/src/main/java/konkuk/thip/common/security/argument_resolver/UserIdArgumentResolver.java @@ -24,7 +24,7 @@ public boolean supportsParameter(MethodParameter parameter) { } @Override - public Object resolveArgument(MethodParameter parameter, + public Long resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { @@ -33,6 +33,6 @@ public Object resolveArgument(MethodParameter parameter, if (userId == null) { throw new AuthException(AUTH_TOKEN_NOT_FOUND); } - return userId; + return (Long) userId; } } From ca20c73fb1f5a37e923c8bd32998c53c3e48c0ee Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 29 Jun 2025 19:56:09 +0900 Subject: [PATCH 37/55] =?UTF-8?q?[refactor]=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EB=B3=B4?= =?UTF-8?q?=EC=9E=A5=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/common/security/oauth2/GoogleUserDetails.java | 8 ++++---- .../thip/common/security/oauth2/KakaoUserDetails.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java index 5e7dc790c..da43c50e4 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/GoogleUserDetails.java @@ -7,11 +7,11 @@ public class GoogleUserDetails implements OAuth2UserDetails{ - private final Map attribute; + private final Map attributes; - public GoogleUserDetails(Map attribute) { + public GoogleUserDetails(Map attributes) { - this.attribute = attribute; + this.attributes = attributes; } @Override @@ -23,7 +23,7 @@ public String getProvider() { @Override public String getProviderId() { - return attribute.get(GOOGLE_PROVIDER_ID_KEY.getValue()).toString(); + return attributes.get(GOOGLE_PROVIDER_ID_KEY.getValue()).toString(); } // @Override diff --git a/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java b/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java index c37d52247..86a754664 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/KakaoUserDetails.java @@ -7,7 +7,7 @@ public class KakaoUserDetails implements OAuth2UserDetails { - private Map attributes; + private final Map attributes; public KakaoUserDetails(Map attributes) { this.attributes = attributes; From 59b7bdec96f1ff5e43347084cc5115241fee2671 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 29 Jun 2025 20:00:15 +0900 Subject: [PATCH 38/55] =?UTF-8?q?[refactor]=20=EC=A7=80=EC=9B=90=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=86=8C=EC=85=9C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/konkuk/thip/common/exception/code/ErrorCode.java | 1 + .../thip/common/security/oauth2/CustomOAuth2UserService.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java index 26d33d38c..5fadd59cd 100644 --- a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java +++ b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java @@ -21,6 +21,7 @@ public enum ErrorCode implements ResponseCode { AUTH_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 40102, "인증되지 않은 사용자입니다."), AUTH_TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, 40103, "토큰을 찾을 수 없습니다."), AUTH_LOGIN_FAILED(HttpStatus.UNAUTHORIZED, 40104, "로그인에 실패했습니다."), + AUTH_UNSUPPORTED_SOCIAL_LOGIN(HttpStatus.UNAUTHORIZED, 40105, "지원하지 않는 소셜 로그인입니다."), /* 60000부터 비즈니스 예외 */ /** diff --git a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java index 6d1c452ad..e6deabd7a 100644 --- a/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java +++ b/src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java @@ -1,5 +1,6 @@ package konkuk.thip.common.security.oauth2; +import konkuk.thip.common.exception.AuthException; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.UserJpaRepository; import lombok.RequiredArgsConstructor; @@ -12,6 +13,7 @@ import java.util.Optional; +import static konkuk.thip.common.exception.code.ErrorCode.AUTH_UNSUPPORTED_SOCIAL_LOGIN; import static konkuk.thip.common.security.constant.AuthParameters.GOOGLE; import static konkuk.thip.common.security.constant.AuthParameters.KAKAO; @@ -39,7 +41,7 @@ else if (registrationId.equals(GOOGLE.getValue())) { } else { log.warn("카카오 또는 구글 소셜 로그인만 지원합니다."); - return null; + throw new AuthException(AUTH_UNSUPPORTED_SOCIAL_LOGIN); } String oauth2Id = oAuth2UserDetails.getProvider() + "_" + oAuth2UserDetails.getProviderId(); //kakao_1234567890 From 0402c6444652b41f0492b9a9774f98c8efcbcb10 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 29 Jun 2025 20:03:10 +0900 Subject: [PATCH 39/55] =?UTF-8?q?[refactor]=20JwtAuthenticationFilter=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/config/SecurityConfig.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/konkuk/thip/config/SecurityConfig.java b/src/main/java/konkuk/thip/config/SecurityConfig.java index c75e34818..c31804c1b 100644 --- a/src/main/java/konkuk/thip/config/SecurityConfig.java +++ b/src/main/java/konkuk/thip/config/SecurityConfig.java @@ -4,7 +4,6 @@ import konkuk.thip.common.security.filter.JwtAuthenticationFilter; import konkuk.thip.common.security.oauth2.CustomOAuth2UserService; import konkuk.thip.common.security.oauth2.CustomSuccessHandler; -import konkuk.thip.common.security.util.JwtUtil; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -29,8 +28,8 @@ @RequiredArgsConstructor public class SecurityConfig { - private final JwtUtil jwtUtil; private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final JwtAuthenticationFilter jwtAuthenticationFilter; private final CustomOAuth2UserService customOAuth2UserService; private final CustomSuccessHandler customSuccessHandler; @@ -57,7 +56,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .csrf(AbstractHttpConfigurer::disable) .formLogin(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) - .addFilterBefore(new JwtAuthenticationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .oauth2Login((oauth2) -> oauth2 .userInfoEndpoint(userInfoEndpointConfig -> userInfoEndpointConfig .userService(customOAuth2UserService) From dba86f48d030ba2dcf04fe2ec73e3cd556339531 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 29 Jun 2025 20:26:25 +0900 Subject: [PATCH 40/55] =?UTF-8?q?[test]=20BookQueryControllerTest=20filter?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/book/adapter/in/web/BookQueryControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java index 20a1e4938..b85119545 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java @@ -15,7 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @SpringBootTest -@AutoConfigureMockMvc +@AutoConfigureMockMvc(addFilters = false) @ActiveProfiles("test") class BookQueryControllerTest { From 22f3edfaa2bfdb09e17c96cce1a12579a845864c Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Fri, 27 Jun 2025 11:08:00 +0900 Subject: [PATCH 41/55] =?UTF-8?q?[refactor]=20:=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20api=20request=20dto=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - nickname 필드값 bean validation 수정 - 테스트 코드의 메서드 네이밍 간결하게 수정 --- .../thip/user/adapter/in/web/UserSignupControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java index 24258a536..78896a585 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java @@ -163,7 +163,7 @@ void signup_nickname_too_long() throws Exception { //when //then String testToken = jwtUtil.createSignupToken("kakao_12345678"); - ResultActions result = mockMvc.perform(post("/users/signup") + mockMvc.perform(post("/users/signup") .header("Authorization", "Bearer " + testToken) //헤더 추가 .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) From 5b9ea9c6d803e2c023b64bb3b49901f5781ce896 Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Fri, 27 Jun 2025 11:18:15 +0900 Subject: [PATCH 42/55] =?UTF-8?q?[refactor]=20:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=20DB=20clear?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각각 테스트 메서드 종료 후 DB를 clear 해주는 @BeforeEach 메서드 추가 - 테스트 클래스에 @Transactional 을 명시하는 방법도 있지만, 명시적으로 DB 클리어를 보여주기 위해 @BeforeEach 메서드 사용을 채택하였음 --- .../thip/user/adapter/in/web/UserSignupControllerTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java index 78896a585..ac88cd0c7 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java @@ -2,8 +2,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import konkuk.thip.common.security.util.JwtUtil; -import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; @@ -21,7 +19,6 @@ import org.springframework.test.web.servlet.ResultActions; import static konkuk.thip.common.exception.code.ErrorCode.API_INVALID_PARAM; -import static konkuk.thip.common.exception.code.ErrorCode.AUTH_TOKEN_NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; From d01a344def45711e40e79c41f503dadba15f9082 Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Fri, 27 Jun 2025 11:19:09 +0900 Subject: [PATCH 43/55] =?UTF-8?q?[feat]=20:=20=EB=8B=89=EB=84=A4=EC=9E=84?= =?UTF-8?q?=20=EC=A4=91=EB=B3=B5=20=EA=B2=80=EC=82=AC=20api=20controller?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/request/VerifyNicknameRequest.java | 11 +++++++++++ .../in/web/response/VerifyNicknameResponse.java | 7 +++++++ 2 files changed, 18 insertions(+) create mode 100644 src/main/java/konkuk/thip/user/adapter/in/web/request/VerifyNicknameRequest.java create mode 100644 src/main/java/konkuk/thip/user/adapter/in/web/response/VerifyNicknameResponse.java diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/request/VerifyNicknameRequest.java b/src/main/java/konkuk/thip/user/adapter/in/web/request/VerifyNicknameRequest.java new file mode 100644 index 000000000..b552614d9 --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/in/web/request/VerifyNicknameRequest.java @@ -0,0 +1,11 @@ +package konkuk.thip.user.adapter.in.web.request; + +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +public record VerifyNicknameRequest( + @Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)") + @Size(max = 10, message = "닉네임은 최대 10자 입니다.") + String nickname +) { +} diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/response/VerifyNicknameResponse.java b/src/main/java/konkuk/thip/user/adapter/in/web/response/VerifyNicknameResponse.java new file mode 100644 index 000000000..b6ef18cb5 --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/in/web/response/VerifyNicknameResponse.java @@ -0,0 +1,7 @@ +package konkuk.thip.user.adapter.in.web.response; + +public record VerifyNicknameResponse(boolean isVerified) { + public static VerifyNicknameResponse of(boolean isVerified) { + return new VerifyNicknameResponse(isVerified); + } +} From df2b46c1604121a32565ccb0a0971779b188885e Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Fri, 27 Jun 2025 11:19:45 +0900 Subject: [PATCH 44/55] =?UTF-8?q?[feat]=20:=20=EB=8B=89=EB=84=A4=EC=9E=84?= =?UTF-8?q?=20=EC=A4=91=EB=B3=B5=20=EA=B2=80=EC=82=AC=20=EC=98=81=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=96=B4=EB=8C=91=ED=84=B0=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/out/persistence/UserJpaRepository.java | 6 ++++-- .../adapter/out/persistence/UserRepositoryCustom.java | 5 +++++ .../out/persistence/UserRepositoryCustomImpl.java | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustom.java create mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustomImpl.java diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java index 83674ef98..6eb57bcfc 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java @@ -3,7 +3,9 @@ import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; -public interface UserJpaRepository extends JpaRepository, UserQueryRepository { - UserJpaEntity findByOauth2Id(String oauth2Id); +import java.util.Optional; + +public interface UserJpaRepository extends JpaRepository, UserQueryJpaRepository { + Optional findByOauth2Id(String oauth2Id); boolean existsByNickname(String nickname); } diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustom.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustom.java new file mode 100644 index 000000000..11c30268a --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustom.java @@ -0,0 +1,5 @@ +package konkuk.thip.user.adapter.out.persistence; + +public interface UserRepositoryCustom { + +} diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustomImpl.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustomImpl.java new file mode 100644 index 000000000..7991e25e5 --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustomImpl.java @@ -0,0 +1,11 @@ +package konkuk.thip.user.adapter.out.persistence; + +import org.springframework.stereotype.Repository; + +@Repository +public class UserRepositoryCustomImpl implements UserRepositoryCustom { + + /** + * QueryDsl 을 활용한 복잡한 조회 로직 구현 + */ +} From 1af9a7b1134e006bdfc7e01760aeff5ea8f76325 Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Sat, 28 Jun 2025 02:36:01 +0900 Subject: [PATCH 45/55] =?UTF-8?q?[refactor]=20:=20tearDown=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=A5=BC=20@AfterEach=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/user/adapter/in/web/UserSignupControllerTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java index ac88cd0c7..7c139120b 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java @@ -64,9 +64,7 @@ void signup_success() throws Exception { PostUserSignupRequest request = new PostUserSignupRequest( aliasJpaEntity.getAliasId(), - "테스트유저", - "test@test.com" - "테스트 유저" + "테스트유저" ); //when : 회원가입 api 호출 + 임시 토큰 발급 @@ -88,7 +86,6 @@ void signup_success() throws Exception { assertThat(userJpaEntity.getAliasForUserJpaEntity().getAliasId()).isEqualTo(request.aliasId()); assertThat(userJpaEntity.getNickname()).isEqualTo(request.nickname()); - assertThat(userJpaEntity.getEmail()).isEqualTo(request.email()); } @Test From 11ef7ac22786308a2483c65649375781364a5ccb Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Sat, 28 Jun 2025 02:37:46 +0900 Subject: [PATCH 46/55] =?UTF-8?q?[feat]=20:=20querydsl=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 312bc961e..2faa397bc 100644 --- a/build.gradle +++ b/build.gradle @@ -24,12 +24,16 @@ repositories { } dependencies { + // Spring Boot 스타터 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-data-redis' + + // Lombok compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' //Security implementation 'org.springframework.boot:spring-boot-starter-security' @@ -42,14 +46,36 @@ dependencies { // OAuth2 implementation 'org.springframework.security:spring-security-oauth2-client' + // Runtime DB 드라이버 runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' - annotationProcessor 'org.projectlombok:lombok' + + // Test testImplementation 'org.assertj:assertj-core:3.24.2' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testImplementation 'com.h2database:h2:2.1.214' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // Querydsl + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" +} + +def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile + +tasks.withType(JavaCompile).configureEach { + options.generatedSourceOutputDirectory.set(querydslDir) +} + +sourceSets { + main.java.srcDirs += [ querydslDir ] +} + +clean.doLast { + file(querydslDir).deleteDir() } tasks.named('test') { From b8c58f5cf96fd3795cd4602f7cc2d84a34b58563 Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Sun, 29 Jun 2025 21:38:40 +0900 Subject: [PATCH 47/55] =?UTF-8?q?[refactor]=20:=20Querydsl=20=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=20repository=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - interface : xxxQueryRepository - 구현체 : xxxQueryRepositoryImpl --- .../adapter/out/persistence/UserRepositoryCustom.java | 5 ----- .../out/persistence/UserRepositoryCustomImpl.java | 11 ----------- 2 files changed, 16 deletions(-) delete mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustom.java delete mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustomImpl.java diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustom.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustom.java deleted file mode 100644 index 11c30268a..000000000 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustom.java +++ /dev/null @@ -1,5 +0,0 @@ -package konkuk.thip.user.adapter.out.persistence; - -public interface UserRepositoryCustom { - -} diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustomImpl.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustomImpl.java deleted file mode 100644 index 7991e25e5..000000000 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserRepositoryCustomImpl.java +++ /dev/null @@ -1,11 +0,0 @@ -package konkuk.thip.user.adapter.out.persistence; - -import org.springframework.stereotype.Repository; - -@Repository -public class UserRepositoryCustomImpl implements UserRepositoryCustom { - - /** - * QueryDsl 을 활용한 복잡한 조회 로직 구현 - */ -} From c2b00eaf92d9533cf43e26c87b06d249e98bbb81 Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Sun, 29 Jun 2025 21:45:16 +0900 Subject: [PATCH 48/55] =?UTF-8?q?[refactor]=20:=20request,=20response=20dt?= =?UTF-8?q?o=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 팀 컨벤션에 맞추어 수정 --- .../adapter/in/web/request/VerifyNicknameRequest.java | 11 ----------- .../in/web/response/VerifyNicknameResponse.java | 7 ------- 2 files changed, 18 deletions(-) delete mode 100644 src/main/java/konkuk/thip/user/adapter/in/web/request/VerifyNicknameRequest.java delete mode 100644 src/main/java/konkuk/thip/user/adapter/in/web/response/VerifyNicknameResponse.java diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/request/VerifyNicknameRequest.java b/src/main/java/konkuk/thip/user/adapter/in/web/request/VerifyNicknameRequest.java deleted file mode 100644 index b552614d9..000000000 --- a/src/main/java/konkuk/thip/user/adapter/in/web/request/VerifyNicknameRequest.java +++ /dev/null @@ -1,11 +0,0 @@ -package konkuk.thip.user.adapter.in.web.request; - -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; - -public record VerifyNicknameRequest( - @Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)") - @Size(max = 10, message = "닉네임은 최대 10자 입니다.") - String nickname -) { -} diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/response/VerifyNicknameResponse.java b/src/main/java/konkuk/thip/user/adapter/in/web/response/VerifyNicknameResponse.java deleted file mode 100644 index b6ef18cb5..000000000 --- a/src/main/java/konkuk/thip/user/adapter/in/web/response/VerifyNicknameResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.user.adapter.in.web.response; - -public record VerifyNicknameResponse(boolean isVerified) { - public static VerifyNicknameResponse of(boolean isVerified) { - return new VerifyNicknameResponse(isVerified); - } -} From 9271fd45a95a31732ce810f8fa4ff70e4164ccc0 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 03:32:38 +0900 Subject: [PATCH 49/55] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=9C=20=EC=9E=84=EC=8B=9C=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=20=EC=B6=94=EC=B6=9C=20=EB=B0=8F=20=EC=95=A1=EC=84=B8=EC=8A=A4?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EB=B0=9C=EA=B8=89=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/user/adapter/in/web/UserCommandController.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java index b2a98124b..eec85e647 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java @@ -10,6 +10,14 @@ import konkuk.thip.user.adapter.in.web.request.PostUserVerifyNicknameRequest; import konkuk.thip.user.adapter.in.web.response.PostUserSignupResponse; import konkuk.thip.user.adapter.in.web.response.PostUserVerifyNicknameResponse; +import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; +import konkuk.thip.user.adapter.in.web.request.PostUserVerifyNicknameRequest; +import konkuk.thip.user.adapter.in.web.response.PostUserSignupResponse; +import konkuk.thip.user.adapter.in.web.response.PostUserVerifyNicknameResponse; +import konkuk.thip.common.security.annotation.Oauth2Id; +import konkuk.thip.common.security.util.JwtUtil; +import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; +import konkuk.thip.user.adapter.in.web.response.UserSignupResponse; import konkuk.thip.user.application.port.in.UserSignupUseCase; import konkuk.thip.user.application.port.in.VerifyNicknameUseCase; import lombok.RequiredArgsConstructor; From 50a5f32563411ff43dafbfb8665450917660921a Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 28 Jun 2025 14:33:26 +0900 Subject: [PATCH 50/55] =?UTF-8?q?[refactor]=20email=20=EC=BB=AC=EB=9F=BC?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/user/adapter/in/web/request/PostUserSignupRequest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java b/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java index 2a786372b..f8ab8a775 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java @@ -1,6 +1,8 @@ package konkuk.thip.user.adapter.in.web.request; import jakarta.validation.constraints.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import konkuk.thip.user.application.port.in.dto.UserSignupCommand; public record PostUserSignupRequest( From 873144a89cb83abe0b65d1606b1f83a644b27609 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Mon, 30 Jun 2025 03:04:00 +0900 Subject: [PATCH 51/55] =?UTF-8?q?[refactor]=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 - .../adapter/in/web/ShowAliasChoiceViewControllerTest.java | 2 +- .../user/adapter/in/web/VerifyNicknameControllerTest.java | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 2faa397bc..3c0aeb7bb 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-data-redis' - // Lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/ShowAliasChoiceViewControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/ShowAliasChoiceViewControllerTest.java index 892e45555..f458c7203 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/ShowAliasChoiceViewControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/ShowAliasChoiceViewControllerTest.java @@ -28,7 +28,7 @@ @SpringBootTest @ActiveProfiles("test") -@AutoConfigureMockMvc +@AutoConfigureMockMvc(addFilters = false) class ShowAliasChoiceViewControllerTest { @Autowired diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/VerifyNicknameControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/VerifyNicknameControllerTest.java index a2c04a7f3..fa4daffa3 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/VerifyNicknameControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/VerifyNicknameControllerTest.java @@ -28,7 +28,7 @@ @SpringBootTest @ActiveProfiles("test") -@AutoConfigureMockMvc +@AutoConfigureMockMvc(addFilters = false) class VerifyNicknameControllerTest { @Autowired @@ -83,10 +83,10 @@ void verify_nickname_false() throws Exception { aliasJpaRepository.save(aliasJpaEntity); UserJpaEntity userJpaEntity = UserJpaEntity.builder() - .email("test@test.com") .nickname("테스트유저") .imageUrl("http://image.url") .role(USER) + .oauth2Id("kakao_12345678") .aliasForUserJpaEntity(aliasJpaEntity) .build(); userJpaRepository.save(userJpaEntity); From 1c630055d5e6e62566a36c22c6d50a5fde1e75b6 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Mon, 30 Jun 2025 03:09:12 +0900 Subject: [PATCH 52/55] =?UTF-8?q?[refactor]=20dto=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=EC=97=90=20=EB=A7=9E=EC=B6=B0=EC=84=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/adapter/in/web/UserCommandController.java | 13 +++++++++---- .../user/adapter/in/web/UserQueryController.java | 12 ++++++------ ...erSignupRequest.java => UserSignupRequest.java} | 3 +-- ...Request.java => UserVerifyNicknameRequest.java} | 2 +- .../in/web/response/PostUserSignupResponse.java | 7 ------- .../response/PostUserVerifyNicknameResponse.java | 7 ------- .../in/web/response/UserSignupResponse.java | 7 +++++++ .../web/response/UserVerifyNicknameResponse.java | 7 +++++++ ...ponse.java => UserViewAliasChoiceResponse.java} | 8 ++++---- .../persistence/AliasQueryPersistenceAdapter.java | 4 ++-- .../out/persistence/AliasQueryRepository.java | 4 ++-- .../out/persistence/AliasQueryRepositoryImpl.java | 10 +++++----- .../port/in/ShowAliasChoiceViewUseCase.java | 8 -------- ...UseCase.java => UserVerifyNicknameUseCase.java} | 2 +- .../port/in/UserViewAliasChoiceUseCase.java | 8 ++++++++ .../user/application/port/in/dto/DummyCommand.java | 10 ---------- .../user/application/port/in/dto/DummyQuery.java | 9 --------- ...wResult.java => UserViewAliasChoiceResult.java} | 2 +- .../user/application/port/out/AliasQueryPort.java | 4 ++-- ...Service.java => UserVerifyNicknameService.java} | 4 ++-- ...ervice.java => UserViewAliasChoiceService.java} | 8 ++++---- .../adapter/in/web/UserSignupControllerTest.java | 2 ++ ....java => UserVerifyNicknameControllerTest.java} | 14 +++++++------- ...java => UserViewAliasChoiceControllerTest.java} | 8 ++++---- 24 files changed, 75 insertions(+), 88 deletions(-) rename src/main/java/konkuk/thip/user/adapter/in/web/request/{PostUserSignupRequest.java => UserSignupRequest.java} (90%) rename src/main/java/konkuk/thip/user/adapter/in/web/request/{PostUserVerifyNicknameRequest.java => UserVerifyNicknameRequest.java} (89%) delete mode 100644 src/main/java/konkuk/thip/user/adapter/in/web/response/PostUserSignupResponse.java delete mode 100644 src/main/java/konkuk/thip/user/adapter/in/web/response/PostUserVerifyNicknameResponse.java create mode 100644 src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java create mode 100644 src/main/java/konkuk/thip/user/adapter/in/web/response/UserVerifyNicknameResponse.java rename src/main/java/konkuk/thip/user/adapter/in/web/response/{GetUserShowAliasChoiceResponse.java => UserViewAliasChoiceResponse.java} (67%) delete mode 100644 src/main/java/konkuk/thip/user/application/port/in/ShowAliasChoiceViewUseCase.java rename src/main/java/konkuk/thip/user/application/port/in/{VerifyNicknameUseCase.java => UserVerifyNicknameUseCase.java} (68%) create mode 100644 src/main/java/konkuk/thip/user/application/port/in/UserViewAliasChoiceUseCase.java delete mode 100644 src/main/java/konkuk/thip/user/application/port/in/dto/DummyCommand.java delete mode 100644 src/main/java/konkuk/thip/user/application/port/in/dto/DummyQuery.java rename src/main/java/konkuk/thip/user/application/port/in/dto/{AliasChoiceViewResult.java => UserViewAliasChoiceResult.java} (77%) rename src/main/java/konkuk/thip/user/application/service/{VerifyNicknameService.java => UserVerifyNicknameService.java} (73%) rename src/main/java/konkuk/thip/user/application/service/{ShowAliasChoiceViewService.java => UserViewAliasChoiceService.java} (56%) rename src/test/java/konkuk/thip/user/adapter/in/web/{VerifyNicknameControllerTest.java => UserVerifyNicknameControllerTest.java} (90%) rename src/test/java/konkuk/thip/user/adapter/in/web/{ShowAliasChoiceViewControllerTest.java => UserViewAliasChoiceControllerTest.java} (91%) diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java index eec85e647..4542a2808 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java @@ -18,7 +18,12 @@ import konkuk.thip.common.security.util.JwtUtil; import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; import konkuk.thip.user.adapter.in.web.response.UserSignupResponse; +import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; +import konkuk.thip.user.adapter.in.web.request.UserVerifyNicknameRequest; +import konkuk.thip.user.adapter.in.web.response.UserSignupResponse; +import konkuk.thip.user.adapter.in.web.response.UserVerifyNicknameResponse; import konkuk.thip.user.application.port.in.UserSignupUseCase; +import konkuk.thip.user.application.port.in.UserVerifyNicknameUseCase; import konkuk.thip.user.application.port.in.VerifyNicknameUseCase; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; @@ -34,7 +39,7 @@ public class UserCommandController { private final UserSignupUseCase userSignupUseCase; - private final VerifyNicknameUseCase verifyNicknameUseCase; + private final UserVerifyNicknameUseCase userVerifyNicknameUseCase; private final JwtUtil jwtUtil; @PostMapping("/users/signup") @@ -48,9 +53,9 @@ public BaseResponse signup(@Validated @RequestBody UserSignu } @PostMapping("/users/nickname") - public BaseResponse verifyNickname(@Validated @RequestBody PostUserVerifyNicknameRequest request) { - return BaseResponse.ok(PostUserVerifyNicknameResponse.of( - verifyNicknameUseCase.isNicknameUnique(request.nickname())) + public BaseResponse verifyNickname(@Validated @RequestBody UserVerifyNicknameRequest request) { + return BaseResponse.ok(UserVerifyNicknameResponse.of( + userVerifyNicknameUseCase.isNicknameUnique(request.nickname())) ); } } diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java b/src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java index 01ca9f6b0..3a4436721 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java @@ -1,8 +1,8 @@ package konkuk.thip.user.adapter.in.web; import konkuk.thip.common.dto.BaseResponse; -import konkuk.thip.user.adapter.in.web.response.GetUserShowAliasChoiceResponse; -import konkuk.thip.user.application.port.in.ShowAliasChoiceViewUseCase; +import konkuk.thip.user.adapter.in.web.response.UserViewAliasChoiceResponse; +import konkuk.thip.user.application.port.in.UserViewAliasChoiceUseCase; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -11,12 +11,12 @@ @RequiredArgsConstructor public class UserQueryController { - private final ShowAliasChoiceViewUseCase showAliasChoiceViewUseCase; + private final UserViewAliasChoiceUseCase userViewAliasChoiceUseCase; @GetMapping("/users/alias") - public BaseResponse showAliasChoiceView() { - return BaseResponse.ok(GetUserShowAliasChoiceResponse.of( - showAliasChoiceViewUseCase.getAllAliasesAndCategories() + public BaseResponse showAliasChoiceView() { + return BaseResponse.ok(UserViewAliasChoiceResponse.of( + userViewAliasChoiceUseCase.getAllAliasesAndCategories() )); } } diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java b/src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java similarity index 90% rename from src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java rename to src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java index f8ab8a775..fe13a0804 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserSignupRequest.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java @@ -1,11 +1,10 @@ package konkuk.thip.user.adapter.in.web.request; import jakarta.validation.constraints.*; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import konkuk.thip.user.application.port.in.dto.UserSignupCommand; -public record PostUserSignupRequest( +public record UserSignupRequest( @NotNull(message = "aliasId는 필수입니다.") Long aliasId, diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserVerifyNicknameRequest.java b/src/main/java/konkuk/thip/user/adapter/in/web/request/UserVerifyNicknameRequest.java similarity index 89% rename from src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserVerifyNicknameRequest.java rename to src/main/java/konkuk/thip/user/adapter/in/web/request/UserVerifyNicknameRequest.java index 3a8734d9f..54050a79c 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/request/PostUserVerifyNicknameRequest.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/request/UserVerifyNicknameRequest.java @@ -3,7 +3,7 @@ import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; -public record PostUserVerifyNicknameRequest( +public record UserVerifyNicknameRequest( @Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)") @Size(max = 10, message = "닉네임은 최대 10자 입니다.") String nickname diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/response/PostUserSignupResponse.java b/src/main/java/konkuk/thip/user/adapter/in/web/response/PostUserSignupResponse.java deleted file mode 100644 index c3cbb993c..000000000 --- a/src/main/java/konkuk/thip/user/adapter/in/web/response/PostUserSignupResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.user.adapter.in.web.response; - -public record PostUserSignupResponse(Long userId) { - public static PostUserSignupResponse of(Long userId) { - return new PostUserSignupResponse(userId); - } -} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/response/PostUserVerifyNicknameResponse.java b/src/main/java/konkuk/thip/user/adapter/in/web/response/PostUserVerifyNicknameResponse.java deleted file mode 100644 index c254d052e..000000000 --- a/src/main/java/konkuk/thip/user/adapter/in/web/response/PostUserVerifyNicknameResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.user.adapter.in.web.response; - -public record PostUserVerifyNicknameResponse(boolean isVerified) { - public static PostUserVerifyNicknameResponse of(boolean isVerified) { - return new PostUserVerifyNicknameResponse(isVerified); - } -} diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java b/src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java new file mode 100644 index 000000000..f5a490e6e --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java @@ -0,0 +1,7 @@ +package konkuk.thip.user.adapter.in.web.response; + +public record UserSignupResponse(Long userId) { + public static UserSignupResponse of(Long userId) { + return new UserSignupResponse(userId); + } +} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/response/UserVerifyNicknameResponse.java b/src/main/java/konkuk/thip/user/adapter/in/web/response/UserVerifyNicknameResponse.java new file mode 100644 index 000000000..449dd9c20 --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/in/web/response/UserVerifyNicknameResponse.java @@ -0,0 +1,7 @@ +package konkuk.thip.user.adapter.in.web.response; + +public record UserVerifyNicknameResponse(boolean isVerified) { + public static UserVerifyNicknameResponse of(boolean isVerified) { + return new UserVerifyNicknameResponse(isVerified); + } +} diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/response/GetUserShowAliasChoiceResponse.java b/src/main/java/konkuk/thip/user/adapter/in/web/response/UserViewAliasChoiceResponse.java similarity index 67% rename from src/main/java/konkuk/thip/user/adapter/in/web/response/GetUserShowAliasChoiceResponse.java rename to src/main/java/konkuk/thip/user/adapter/in/web/response/UserViewAliasChoiceResponse.java index b9f3116af..8af48cfef 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/response/GetUserShowAliasChoiceResponse.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/response/UserViewAliasChoiceResponse.java @@ -1,12 +1,12 @@ package konkuk.thip.user.adapter.in.web.response; -import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; import java.util.List; -public record GetUserShowAliasChoiceResponse(List aliasChoices) { +public record UserViewAliasChoiceResponse(List aliasChoices) { - public static GetUserShowAliasChoiceResponse of(AliasChoiceViewResult result) { + public static UserViewAliasChoiceResponse of(UserViewAliasChoiceResult result) { List choices = result.aliasChoices().stream() .map(ac -> new AliasChoice( ac.aliasId(), @@ -16,7 +16,7 @@ public static GetUserShowAliasChoiceResponse of(AliasChoiceViewResult result) { ac.color() )) .toList(); - return new GetUserShowAliasChoiceResponse(choices); + return new UserViewAliasChoiceResponse(choices); } public record AliasChoice( diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryPersistenceAdapter.java index 5f4e01dd4..0fe88291d 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryPersistenceAdapter.java @@ -1,6 +1,6 @@ package konkuk.thip.user.adapter.out.persistence; -import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; import konkuk.thip.user.application.port.out.AliasQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -12,7 +12,7 @@ public class AliasQueryPersistenceAdapter implements AliasQueryPort { private final AliasJpaRepository aliasJpaRepository; @Override - public AliasChoiceViewResult getAllAliasesAndCategories() { + public UserViewAliasChoiceResult getAllAliasesAndCategories() { return aliasJpaRepository.getAllAliasesAndCategories(); } } diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepository.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepository.java index 25ce90252..43cf50e37 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepository.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepository.java @@ -1,8 +1,8 @@ package konkuk.thip.user.adapter.out.persistence; -import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; public interface AliasQueryRepository { - AliasChoiceViewResult getAllAliasesAndCategories(); + UserViewAliasChoiceResult getAllAliasesAndCategories(); } diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepositoryImpl.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepositoryImpl.java index 9cabe24cf..a3791cd11 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepositoryImpl.java @@ -4,7 +4,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import konkuk.thip.room.adapter.out.jpa.QCategoryJpaEntity; import konkuk.thip.user.adapter.out.jpa.QAliasJpaEntity; -import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -17,13 +17,13 @@ public class AliasQueryRepositoryImpl implements AliasQueryRepository { private final JPAQueryFactory jpaQueryFactory; @Override - public AliasChoiceViewResult getAllAliasesAndCategories() { + public UserViewAliasChoiceResult getAllAliasesAndCategories() { QAliasJpaEntity alias = QAliasJpaEntity.aliasJpaEntity; QCategoryJpaEntity category = QCategoryJpaEntity.categoryJpaEntity; - List aliasChoices = jpaQueryFactory + List aliasChoices = jpaQueryFactory .select(Projections.constructor( - AliasChoiceViewResult.AliasChoice.class, + UserViewAliasChoiceResult.AliasChoice.class, alias.aliasId, alias.value, category.value, @@ -36,6 +36,6 @@ public AliasChoiceViewResult getAllAliasesAndCategories() { .orderBy(alias.aliasId.asc()) .fetch(); - return new AliasChoiceViewResult(aliasChoices); + return new UserViewAliasChoiceResult(aliasChoices); } } diff --git a/src/main/java/konkuk/thip/user/application/port/in/ShowAliasChoiceViewUseCase.java b/src/main/java/konkuk/thip/user/application/port/in/ShowAliasChoiceViewUseCase.java deleted file mode 100644 index 994a6cf31..000000000 --- a/src/main/java/konkuk/thip/user/application/port/in/ShowAliasChoiceViewUseCase.java +++ /dev/null @@ -1,8 +0,0 @@ -package konkuk.thip.user.application.port.in; - -import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; - -public interface ShowAliasChoiceViewUseCase { - - AliasChoiceViewResult getAllAliasesAndCategories(); -} diff --git a/src/main/java/konkuk/thip/user/application/port/in/VerifyNicknameUseCase.java b/src/main/java/konkuk/thip/user/application/port/in/UserVerifyNicknameUseCase.java similarity index 68% rename from src/main/java/konkuk/thip/user/application/port/in/VerifyNicknameUseCase.java rename to src/main/java/konkuk/thip/user/application/port/in/UserVerifyNicknameUseCase.java index 3cca5a9f3..d3af2661d 100644 --- a/src/main/java/konkuk/thip/user/application/port/in/VerifyNicknameUseCase.java +++ b/src/main/java/konkuk/thip/user/application/port/in/UserVerifyNicknameUseCase.java @@ -1,6 +1,6 @@ package konkuk.thip.user.application.port.in; -public interface VerifyNicknameUseCase { +public interface UserVerifyNicknameUseCase { boolean isNicknameUnique(String nickname); } diff --git a/src/main/java/konkuk/thip/user/application/port/in/UserViewAliasChoiceUseCase.java b/src/main/java/konkuk/thip/user/application/port/in/UserViewAliasChoiceUseCase.java new file mode 100644 index 000000000..2f925145f --- /dev/null +++ b/src/main/java/konkuk/thip/user/application/port/in/UserViewAliasChoiceUseCase.java @@ -0,0 +1,8 @@ +package konkuk.thip.user.application.port.in; + +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; + +public interface UserViewAliasChoiceUseCase { + + UserViewAliasChoiceResult getAllAliasesAndCategories(); +} diff --git a/src/main/java/konkuk/thip/user/application/port/in/dto/DummyCommand.java b/src/main/java/konkuk/thip/user/application/port/in/dto/DummyCommand.java deleted file mode 100644 index c15059ace..000000000 --- a/src/main/java/konkuk/thip/user/application/port/in/dto/DummyCommand.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.user.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DummyCommand { - -} diff --git a/src/main/java/konkuk/thip/user/application/port/in/dto/DummyQuery.java b/src/main/java/konkuk/thip/user/application/port/in/dto/DummyQuery.java deleted file mode 100644 index 39c0fb70a..000000000 --- a/src/main/java/konkuk/thip/user/application/port/in/dto/DummyQuery.java +++ /dev/null @@ -1,9 +0,0 @@ -package konkuk.thip.user.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DummyQuery { -} diff --git a/src/main/java/konkuk/thip/user/application/port/in/dto/AliasChoiceViewResult.java b/src/main/java/konkuk/thip/user/application/port/in/dto/UserViewAliasChoiceResult.java similarity index 77% rename from src/main/java/konkuk/thip/user/application/port/in/dto/AliasChoiceViewResult.java rename to src/main/java/konkuk/thip/user/application/port/in/dto/UserViewAliasChoiceResult.java index 3df6ca89e..b60820376 100644 --- a/src/main/java/konkuk/thip/user/application/port/in/dto/AliasChoiceViewResult.java +++ b/src/main/java/konkuk/thip/user/application/port/in/dto/UserViewAliasChoiceResult.java @@ -2,7 +2,7 @@ import java.util.List; -public record AliasChoiceViewResult(List aliasChoices) { +public record UserViewAliasChoiceResult(List aliasChoices) { public record AliasChoice( Long aliasId, diff --git a/src/main/java/konkuk/thip/user/application/port/out/AliasQueryPort.java b/src/main/java/konkuk/thip/user/application/port/out/AliasQueryPort.java index c832e863a..064428123 100644 --- a/src/main/java/konkuk/thip/user/application/port/out/AliasQueryPort.java +++ b/src/main/java/konkuk/thip/user/application/port/out/AliasQueryPort.java @@ -1,8 +1,8 @@ package konkuk.thip.user.application.port.out; -import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; public interface AliasQueryPort { - AliasChoiceViewResult getAllAliasesAndCategories(); + UserViewAliasChoiceResult getAllAliasesAndCategories(); } diff --git a/src/main/java/konkuk/thip/user/application/service/VerifyNicknameService.java b/src/main/java/konkuk/thip/user/application/service/UserVerifyNicknameService.java similarity index 73% rename from src/main/java/konkuk/thip/user/application/service/VerifyNicknameService.java rename to src/main/java/konkuk/thip/user/application/service/UserVerifyNicknameService.java index f8cae39a1..8650171c2 100644 --- a/src/main/java/konkuk/thip/user/application/service/VerifyNicknameService.java +++ b/src/main/java/konkuk/thip/user/application/service/UserVerifyNicknameService.java @@ -1,13 +1,13 @@ package konkuk.thip.user.application.service; -import konkuk.thip.user.application.port.in.VerifyNicknameUseCase; +import konkuk.thip.user.application.port.in.UserVerifyNicknameUseCase; import konkuk.thip.user.application.port.out.UserQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor -public class VerifyNicknameService implements VerifyNicknameUseCase { +public class UserVerifyNicknameService implements UserVerifyNicknameUseCase { private final UserQueryPort userQueryPort; diff --git a/src/main/java/konkuk/thip/user/application/service/ShowAliasChoiceViewService.java b/src/main/java/konkuk/thip/user/application/service/UserViewAliasChoiceService.java similarity index 56% rename from src/main/java/konkuk/thip/user/application/service/ShowAliasChoiceViewService.java rename to src/main/java/konkuk/thip/user/application/service/UserViewAliasChoiceService.java index bc96d2749..b04dd2df6 100644 --- a/src/main/java/konkuk/thip/user/application/service/ShowAliasChoiceViewService.java +++ b/src/main/java/konkuk/thip/user/application/service/UserViewAliasChoiceService.java @@ -1,19 +1,19 @@ package konkuk.thip.user.application.service; -import konkuk.thip.user.application.port.in.ShowAliasChoiceViewUseCase; -import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; +import konkuk.thip.user.application.port.in.UserViewAliasChoiceUseCase; +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; import konkuk.thip.user.application.port.out.AliasQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor -public class ShowAliasChoiceViewService implements ShowAliasChoiceViewUseCase { +public class UserViewAliasChoiceService implements UserViewAliasChoiceUseCase { private final AliasQueryPort aliasQueryPort; @Override - public AliasChoiceViewResult getAllAliasesAndCategories() { + public UserViewAliasChoiceResult getAllAliasesAndCategories() { return aliasQueryPort.getAllAliasesAndCategories(); } } diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java index 7c139120b..dfca94543 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import konkuk.thip.common.security.util.JwtUtil; +import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/VerifyNicknameControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java similarity index 90% rename from src/test/java/konkuk/thip/user/adapter/in/web/VerifyNicknameControllerTest.java rename to src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java index fa4daffa3..e56c806cc 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/VerifyNicknameControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import konkuk.thip.user.adapter.in.web.request.PostUserVerifyNicknameRequest; +import konkuk.thip.user.adapter.in.web.request.UserVerifyNicknameRequest; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; @@ -29,7 +29,7 @@ @SpringBootTest @ActiveProfiles("test") @AutoConfigureMockMvc(addFilters = false) -class VerifyNicknameControllerTest { +class UserVerifyNicknameControllerTest { @Autowired private MockMvc mockMvc; @@ -53,7 +53,7 @@ void tearDown() { @DisplayName("[닉네임]값이 unique 할 경우, true를 반환한다.") void verify_nickname_true() throws Exception { //given - PostUserVerifyNicknameRequest request = new PostUserVerifyNicknameRequest("테스트유저"); + UserVerifyNicknameRequest request = new UserVerifyNicknameRequest("테스트유저"); //when ResultActions result = mockMvc.perform(post("/users/nickname") @@ -91,7 +91,7 @@ void verify_nickname_false() throws Exception { .build(); userJpaRepository.save(userJpaEntity); - PostUserVerifyNicknameRequest request = new PostUserVerifyNicknameRequest("테스트유저"); + UserVerifyNicknameRequest request = new UserVerifyNicknameRequest("테스트유저"); //when ResultActions result = mockMvc.perform(post("/users/nickname") @@ -113,7 +113,7 @@ void verify_nickname_false() throws Exception { @DisplayName("[닉네임]값이 공백일 경우, 400 error가 발생한다.") void nickname_blank() throws Exception { //given: nickname blank - PostUserVerifyNicknameRequest request = new PostUserVerifyNicknameRequest(""); + UserVerifyNicknameRequest request = new UserVerifyNicknameRequest(""); //when //then mockMvc.perform(post("/users/nickname") @@ -128,7 +128,7 @@ void nickname_blank() throws Exception { @DisplayName("[닉네임]값이 한글, 영어, 숫자 외의 문자를 포함할 경우, 400 error가 발생한다.") void nickname_invalid_pattern() throws Exception { //given: nickname with invalid characters - PostUserVerifyNicknameRequest request = new PostUserVerifyNicknameRequest("닉네임!!"); + UserVerifyNicknameRequest request = new UserVerifyNicknameRequest("닉네임!!"); //when //then mockMvc.perform(post("/users/nickname") @@ -143,7 +143,7 @@ void nickname_invalid_pattern() throws Exception { @DisplayName("[닉네임]값이 11자 이상일 경우, 400 error가 발생한다.") void nickname_too_long() throws Exception { //given: 11글자 nickname - PostUserVerifyNicknameRequest request = new PostUserVerifyNicknameRequest("11글자닉네임입니다아"); + UserVerifyNicknameRequest request = new UserVerifyNicknameRequest("11글자닉네임입니다아"); //when //then mockMvc.perform(post("/users/nickname") diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/ShowAliasChoiceViewControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserViewAliasChoiceControllerTest.java similarity index 91% rename from src/test/java/konkuk/thip/user/adapter/in/web/ShowAliasChoiceViewControllerTest.java rename to src/test/java/konkuk/thip/user/adapter/in/web/UserViewAliasChoiceControllerTest.java index f458c7203..2514b10d7 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/ShowAliasChoiceViewControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserViewAliasChoiceControllerTest.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; -import konkuk.thip.user.adapter.in.web.response.GetUserShowAliasChoiceResponse; +import konkuk.thip.user.adapter.in.web.response.UserViewAliasChoiceResponse; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; import org.junit.jupiter.api.AfterEach; @@ -29,7 +29,7 @@ @SpringBootTest @ActiveProfiles("test") @AutoConfigureMockMvc(addFilters = false) -class ShowAliasChoiceViewControllerTest { +class UserViewAliasChoiceControllerTest { @Autowired private MockMvc mockMvc; @@ -65,8 +65,8 @@ void show_alias_choice_view() throws Exception { String json = result.andReturn().getResponse().getContentAsString(); JsonNode jsonNode = objectMapper.readTree(json); - GetUserShowAliasChoiceResponse showResponse = objectMapper.treeToValue(jsonNode.get("data"), GetUserShowAliasChoiceResponse.class); - List choices = showResponse.aliasChoices(); + UserViewAliasChoiceResponse showResponse = objectMapper.treeToValue(jsonNode.get("data"), UserViewAliasChoiceResponse.class); + List choices = showResponse.aliasChoices(); assertThat(choices).hasSize(2); assertThat(choices) From 95eb446e2e1f73a76a7c2439e81554bfa26f16b8 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Mon, 30 Jun 2025 03:28:46 +0900 Subject: [PATCH 53/55] =?UTF-8?q?[refactor]=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/user/adapter/out/persistence/UserJpaRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java index 6eb57bcfc..691be41af 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java @@ -5,7 +5,7 @@ import java.util.Optional; -public interface UserJpaRepository extends JpaRepository, UserQueryJpaRepository { +public interface UserJpaRepository extends JpaRepository, UserQueryRepository { Optional findByOauth2Id(String oauth2Id); boolean existsByNickname(String nickname); } From 533775d4ffc532e8e05452d29f4365c82c54d90c Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Mon, 30 Jun 2025 03:48:15 +0900 Subject: [PATCH 54/55] =?UTF-8?q?[refactor]=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/UserCommandController.java | 15 --------------- .../in/web/UserSignupControllerTest.java | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java index 4542a2808..e95ab982f 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java @@ -5,26 +5,11 @@ import konkuk.thip.common.security.annotation.Oauth2Id; import konkuk.thip.common.security.util.JwtUtil; import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; -import konkuk.thip.user.adapter.in.web.response.UserSignupResponse; -import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; -import konkuk.thip.user.adapter.in.web.request.PostUserVerifyNicknameRequest; -import konkuk.thip.user.adapter.in.web.response.PostUserSignupResponse; -import konkuk.thip.user.adapter.in.web.response.PostUserVerifyNicknameResponse; -import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; -import konkuk.thip.user.adapter.in.web.request.PostUserVerifyNicknameRequest; -import konkuk.thip.user.adapter.in.web.response.PostUserSignupResponse; -import konkuk.thip.user.adapter.in.web.response.PostUserVerifyNicknameResponse; -import konkuk.thip.common.security.annotation.Oauth2Id; -import konkuk.thip.common.security.util.JwtUtil; -import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; -import konkuk.thip.user.adapter.in.web.response.UserSignupResponse; -import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; import konkuk.thip.user.adapter.in.web.request.UserVerifyNicknameRequest; import konkuk.thip.user.adapter.in.web.response.UserSignupResponse; import konkuk.thip.user.adapter.in.web.response.UserVerifyNicknameResponse; import konkuk.thip.user.application.port.in.UserSignupUseCase; import konkuk.thip.user.application.port.in.UserVerifyNicknameUseCase; -import konkuk.thip.user.application.port.in.VerifyNicknameUseCase; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java index dfca94543..6f890abd9 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java @@ -3,8 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import konkuk.thip.common.security.util.JwtUtil; -import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; -import konkuk.thip.user.adapter.in.web.request.PostUserSignupRequest; +import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; @@ -21,6 +20,7 @@ import org.springframework.test.web.servlet.ResultActions; import static konkuk.thip.common.exception.code.ErrorCode.API_INVALID_PARAM; +import static konkuk.thip.common.exception.code.ErrorCode.AUTH_TOKEN_NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -64,7 +64,7 @@ void signup_success() throws Exception { .build(); aliasJpaRepository.save(aliasJpaEntity); - PostUserSignupRequest request = new PostUserSignupRequest( + UserSignupRequest request = new UserSignupRequest( aliasJpaEntity.getAliasId(), "테스트유저" ); @@ -94,7 +94,7 @@ void signup_success() throws Exception { @DisplayName("[칭호id]값이 null일 경우, 400 error가 발생한다.") void signup_alias_id_null() throws Exception { //given: aliasId null - PostUserSignupRequest request = new PostUserSignupRequest( + UserSignupRequest request = new UserSignupRequest( null, "테스트유저" ); @@ -114,7 +114,7 @@ void signup_alias_id_null() throws Exception { @DisplayName("[닉네임]값이 공백일 경우, 400 error가 발생한다.") void signup_nickname_blank() throws Exception { //given: nickname blank - PostUserSignupRequest request = new PostUserSignupRequest( + UserSignupRequest request = new UserSignupRequest( 1L, "" ); @@ -134,13 +134,15 @@ void signup_nickname_blank() throws Exception { @DisplayName("[닉네임]값이 한글, 영어, 숫자 외의 문자를 포함할 경우, 400 error가 발생한다.") void signup_nickname_invalid_pattern() throws Exception { //given: nickname with invalid characters - PostUserSignupRequest request = new PostUserSignupRequest( + UserSignupRequest request = new UserSignupRequest( 1L, "닉네임!!" ); //when //then - mockMvc.perform(post("/users/signup") + String testToken = jwtUtil.createSignupToken("kakao_12345678"); + ResultActions result = mockMvc.perform(post("/users/signup") + .header("Authorization", "Bearer " + testToken) //헤더 추가 .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andExpect(status().isBadRequest()) @@ -152,7 +154,7 @@ void signup_nickname_invalid_pattern() throws Exception { @DisplayName("[닉네임]값이 11자 이상일 경우, 400 error가 발생한다.") void signup_nickname_too_long() throws Exception { //given: 11글자 nickname - PostUserSignupRequest request = new PostUserSignupRequest( + UserSignupRequest request = new UserSignupRequest( 1L, "11글자닉네임입니다아" ); From 458a06dd974e27c83a96cef933dde590fbf3074a Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Mon, 30 Jun 2025 03:54:58 +0900 Subject: [PATCH 55/55] =?UTF-8?q?[refactor]=20=EC=93=B8=EB=8D=B0=EC=97=86?= =?UTF-8?q?=EB=8A=94=20import=20=EB=B0=8F=20=EB=B0=98=ED=99=98=ED=98=95?= =?UTF-8?q?=ED=83=9C=20=EC=A0=9C=EA=B1=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/book/adapter/in/web/BookQueryController.java | 4 ---- .../thip/user/adapter/in/web/UserSignupControllerTest.java | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java index 34a932b65..3bb6113f3 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java @@ -1,9 +1,5 @@ package konkuk.thip.book.adapter.in.web; -import konkuk.thip.book.adapter.in.web.response.GetBookSearchListResponse; -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; -import konkuk.thip.book.application.port.in.BookSearchUseCase; -import konkuk.thip.common.dto.BaseResponse; import konkuk.thip.book.adapter.in.web.response.GetBookSearchListResponse; import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; import konkuk.thip.book.application.port.in.BookSearchUseCase; diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java index 6f890abd9..9947800a1 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java @@ -101,7 +101,7 @@ void signup_alias_id_null() throws Exception { //when //then String testToken = jwtUtil.createSignupToken("kakao_12345678"); - ResultActions result = mockMvc.perform(post("/users/signup") + mockMvc.perform(post("/users/signup") .header("Authorization", "Bearer " + testToken) //헤더 추가 .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) @@ -121,7 +121,7 @@ void signup_nickname_blank() throws Exception { //when //then String testToken = jwtUtil.createSignupToken("kakao_12345678"); - ResultActions result = mockMvc.perform(post("/users/signup") + mockMvc.perform(post("/users/signup") .header("Authorization", "Bearer " + testToken) //헤더 추가 .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) @@ -141,7 +141,7 @@ void signup_nickname_invalid_pattern() throws Exception { //when //then String testToken = jwtUtil.createSignupToken("kakao_12345678"); - ResultActions result = mockMvc.perform(post("/users/signup") + mockMvc.perform(post("/users/signup") .header("Authorization", "Bearer " + testToken) //헤더 추가 .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)))