Skip to content

Conversation

@minhyuk2
Copy link
Member

@minhyuk2 minhyuk2 commented Jul 17, 2025

#️⃣ Issue Number

📝 요약(Summary)

로그인 구조 및 카카오 로그인을 구현하였습니다.
Spring security와 jwt 토큰을 사용해서 구현하였습니다.

📸 기능 스크린 샷 공유

스크린샷 2025-07-17 오전 12 58 15

💬 공유사항 to 리뷰어

✅ PR Checklist

PR이 다음 요구 사항을 충족하는지 확인하세요.

  • 커밋 메시지 컨벤션에 맞게 작성했습니다.
  • 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트).

closes : #1

Summary by CodeRabbit

  • New Features

    • Introduced user authentication and authorization using JWT, including login with Kakao OAuth, sign-up, logout, and account deletion endpoints.
    • Added support for user profile management, including updating user information.
    • Implemented Swagger/OpenAPI documentation with JWT bearer authentication.
    • Integrated QueryDSL for advanced database queries.
    • Added error handling with standardized error codes and custom exceptions.
  • Chores

    • Updated build and project configuration files to support new dependencies and modules.
    • Improved .gitignore to exclude sensitive configuration files.

@minhyuk2 minhyuk2 self-assigned this Jul 17, 2025
@minhyuk2 minhyuk2 requested a review from 7dngur7 as a code owner July 17, 2025 07:58
@coderabbitai
Copy link

coderabbitai bot commented Jul 17, 2025

Walkthrough

This change introduces a comprehensive user authentication and management system using JWT and OAuth2, specifically integrating Kakao login. It adds new entities, DTOs, repositories, services, controllers, and security configurations. The build system is updated for QueryDSL and JWT dependencies, and Swagger/OpenAPI configuration is enhanced for JWT security documentation. Project structure and IDE configuration files are also updated.

Changes

File(s) Change Summary
.idea/compiler.xml, .idea/gradle.xml, .idea/modules.xml, .idea/modules/*greatjourney.main.iml Updated module, annotation processor, and Gradle paths; added new module entries for backend.greatjourney.main.
greatjourney/.gitignore Added application.yml to ignored files.
greatjourney/build.gradle Added/updated dependencies: Spring WebMVC, JWT, Swagger, QueryDSL, OAuth2 client, etc.
greatjourney/src/main/java/backend/greatjourney/GreatjourneyApplication.java Enabled Spring Security auto-configuration by removing exclusion.
greatjourney/src/main/java/backend/greatjourney/config/QueryDslConfig.java Added QueryDSL configuration class.
greatjourney/src/main/java/backend/greatjourney/config/SwaggerConfig.java Refactored and enabled Swagger/OpenAPI configuration with JWT bearer security.
greatjourney/src/main/java/backend/greatjourney/domain/token/dto/TokenResponse.java Added DTO for access and refresh tokens.
greatjourney/src/main/java/backend/greatjourney/domain/token/entity/RefreshToken.java Added JPA entity for refresh tokens.
greatjourney/src/main/java/backend/greatjourney/domain/token/repository/RefreshTokenRepository.java Added repository interface for refresh tokens with custom methods.
greatjourney/src/main/java/backend/greatjourney/domain/token/service/JwtAuthenticationFilter.java Added JWT authentication filter for request processing.
greatjourney/src/main/java/backend/greatjourney/domain/token/service/JwtAuthenticationManager.java Added authentication manager for JWT tokens.
greatjourney/src/main/java/backend/greatjourney/domain/token/service/JwtTokenProvider.java Added provider for JWT creation, validation, and authentication.
greatjourney/src/main/java/backend/greatjourney/domain/user/controller/UserController.java Added REST controller for user sign-up, sign-out, login (Kakao), logout, and info change.
greatjourney/src/main/java/backend/greatjourney/domain/user/dto/properties/KakaoOAuthProperties.java Added configuration properties for Kakao OAuth.
greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/ChangeUserRequest.java Added DTO for changing user info (name).
greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/KakaoLoginRequest.java Added DTO for Kakao login requests.
greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/SignUpRequest.java Added DTO for user sign-up requests.
greatjourney/src/main/java/backend/greatjourney/domain/user/dto/response/KakaoUserResponse.java Added DTO for Kakao user API responses.
greatjourney/src/main/java/backend/greatjourney/domain/user/entity/Domain.java Added enum for user domain (KAKAO, GOOGLE, NAVER).
greatjourney/src/main/java/backend/greatjourney/domain/user/entity/Status.java Added enum for user status (PENDING, SUCCESS, DELETED).
greatjourney/src/main/java/backend/greatjourney/domain/user/entity/User.java Added User JPA entity with embedded Terms and associated fields.
greatjourney/src/main/java/backend/greatjourney/domain/user/entity/UserRole.java Added enum for user roles (ROLE_USER, ROLE_ADMIN).
greatjourney/src/main/java/backend/greatjourney/domain/user/repository/UserRepository.java Added repository interface for User entity, extending custom interface.
greatjourney/src/main/java/backend/greatjourney/domain/user/repository/UserRepositoryCustom.java Added custom repository interface for user queries.
greatjourney/src/main/java/backend/greatjourney/domain/user/repository/impl/UserRepositoryImplement.java Added QueryDSL-based implementation for custom user queries.
greatjourney/src/main/java/backend/greatjourney/domain/user/service/KakaoService.java Added service for Kakao OAuth login and user persistence.
greatjourney/src/main/java/backend/greatjourney/domain/user/service/SignService.java Added service for saving Kakao users.
greatjourney/src/main/java/backend/greatjourney/domain/user/service/UserService.java Added user service for sign-up, logout, sign-out, and info change.
greatjourney/src/main/java/backend/greatjourney/global/exception/BaseResponse.java Removed trailing blank line (no logic change).
greatjourney/src/main/java/backend/greatjourney/global/exception/CustomException.java Added custom runtime exception for error handling.
greatjourney/src/main/java/backend/greatjourney/global/exception/ErrorCode.java Added enum for standardized error codes and messages.
greatjourney/src/main/java/backend/greatjourney/global/security/config/SecurityConfig.java Added Spring Security configuration for JWT-based stateless authentication.
greatjourney/src/main/java/backend/greatjourney/global/security/entitiy/CustomOAuth2User.java Added custom OAuth2 user principal implementation.

Sequence Diagram(s)

User Sign-up and Kakao Login Flow

sequenceDiagram
    participant Client
    participant UserController
    participant UserService
    participant KakaoService
    participant SignService
    participant UserRepository
    participant JwtTokenProvider
    participant RefreshTokenRepository

    Client->>UserController: POST /api/v1/user/signup (SignUpRequest)
    UserController->>UserService: signupUser(request)
    UserService->>UserRepository: existsByEmail(email)
    UserRepository-->>UserService: boolean
    alt Email not exists
        UserService->>UserRepository: save(new User)
        UserRepository-->>UserService: User
        UserService-->>UserController: BaseResponse<User>
    else Email exists
        UserService-->>UserController: Error
    end
    UserController-->>Client: Response

    Client->>UserController: POST /api/v1/user/kakao (KakaoLoginRequest)
    UserController->>KakaoService: loginWithKakao(accessToken)
    KakaoService->>Kakao API: GET /v2/user/me
    Kakao API-->>KakaoService: KakaoUserResponse
    KakaoService->>SignService: saveUserKakao(userInfo, "kakao")
    SignService->>UserRepository: save(new User)
    UserRepository-->>SignService: User
    SignService-->>KakaoService: User
    KakaoService->>JwtTokenProvider: createToken(userId)
    JwtTokenProvider->>RefreshTokenRepository: save(refreshToken)
    JwtTokenProvider-->>KakaoService: TokenResponse
    KakaoService-->>UserController: TokenResponse
    UserController-->>Client: BaseResponse<TokenResponse>
Loading

JWT Authentication Filter Flow

sequenceDiagram
    participant Client
    participant JwtAuthenticationFilter
    participant JwtTokenProvider
    participant SecurityContext

    Client->>JwtAuthenticationFilter: HTTP request with Authorization header
    JwtAuthenticationFilter->>JwtTokenProvider: validateToken(token)
    JwtTokenProvider-->>JwtAuthenticationFilter: boolean
    alt Token valid
        JwtAuthenticationFilter->>JwtTokenProvider: getAuthentication(token)
        JwtTokenProvider-->>JwtAuthenticationFilter: Authentication
        JwtAuthenticationFilter->>SecurityContext: setAuthentication(auth)
    end
    JwtAuthenticationFilter-->>Client: Continue filter chain
Loading

Poem

Oh, what a hop in the backend warren,
With JWTs and Kakao, no sign of sorrow!
Users can sign up, log in, and roam,
Secure and swift in their digital home.
QueryDSL and Swagger now join the quest,
This bunny’s code garden is truly blessed!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 30

🔭 Outside diff range comments (1)
greatjourney/build.gradle (1)

65-71: Remove duplicate CSV and POI dependencies.

The CSV and POI dependencies are duplicated (lines 65-66 and 70-71). Keep only one declaration of each.

Apply this diff to remove the duplicates:

 	// CSV 파싱
 	implementation 'org.apache.commons:commons-csv:1.10.0'
 
 	// Excel(XLSX) 파싱
 	implementation 'org.apache.poi:poi-ooxml:5.2.5'
 
-	implementation 'org.apache.commons:commons-csv:1.10.0'
-	implementation 'org.apache.poi:poi-ooxml:5.2.5'
🧹 Nitpick comments (20)
greatjourney/.gitignore (1)

31-33: Consolidate duplicate ignore patterns and align wildcard usage

There is already a wildcard rule (**/application.properties) at line 28.
Keeping the root-level application.properties (line 31) duplicates this, and application.yml (line 32) is the only new entry but lacks the same wildcard coverage.
Recommend pruning the duplicate and applying the wildcard for consistency:

-application.properties
-application.yml
+**/application.yml

This continues to ignore application.properties everywhere (via line 28) and now does the same for application.yml.

greatjourney/src/main/java/backend/greatjourney/config/SwaggerConfig.java (1)

16-16: Consider using a constant for the security scheme name.

To improve maintainability and avoid potential typos, consider defining the security scheme name as a constant.

Add a constant at the class level:

 @Configuration
 public class SwaggerConfig {
+	private static final String BEARER_AUTH_SCHEME = "BearerAuth";
+
 	@Bean
 	public OpenAPI openAPI() {
-		SecurityRequirement securityRequirement = new SecurityRequirement().addList("BearerAuth");
+		SecurityRequirement securityRequirement = new SecurityRequirement().addList(BEARER_AUTH_SCHEME);

Then update the components registration to use the same constant.

greatjourney/src/main/java/backend/greatjourney/domain/token/dto/TokenResponse.java (1)

6-11: Prefer immutable DTO – declare fields final and remove setters

The DTO is only ever populated through the all-args constructor, so declaring the two fields final clarifies immutability and prevents accidental mutation through reflection libraries.

-@Getter
-@AllArgsConstructor
-public class TokenResponse {
-    private String accessToken;
-    private String refreshToken;
-}
+@Getter
+@AllArgsConstructor
+public class TokenResponse {
+    private final String accessToken;
+    private final String refreshToken;
+}

If you need Jackson deserialization, add @JsonCreator or a no-args constructor instead of sacrificing immutability.

greatjourney/src/main/java/backend/greatjourney/domain/user/dto/response/KakaoUserResponse.java (1)

16-21: Consider making DTOs records for concise, immutable representation

Since this class is a pure data holder, Java 17+ record syntax can cut boilerplate and enforce immutability:

public record KakaoUserResponse(
        Long id,
        KakaoAccount kakaoAccount,
        KakaoProfile profile) { }
greatjourney/src/main/java/backend/greatjourney/domain/user/entity/Status.java (2)

3-6: Clarify enum semantics & consider more conventional naming

SUCCESS is somewhat vague for a long-lived user state. In most auth domains the active state is named ACTIVE or ENABLED, which better contrasts with PENDING and DELETED. If the enum is persisted, changing names later becomes a breaking DB migration, so it’s worth double-checking the vocabulary now.

No code change required if you are confident, but please verify the terminology across the service layer and API docs.


3-6: Add minimal Javadoc

Even for small enums, a one-liner Javadoc describing when each status is assigned greatly improves maintainability for newcomers.

greatjourney/src/main/java/backend/greatjourney/domain/user/entity/UserRole.java (1)

3-5: Integrate with Spring-Security’s GrantedAuthority once, not everywhere

You’ll soon need a Collection<? extends GrantedAuthority> representation. Consider adding a helper inside the enum:

public GrantedAuthority asAuthority() {
    return new SimpleGrantedAuthority(name());
}

This eliminates scattered string concatenations in security code.

.idea/modules/1984157471/greatjourney.main.iml (1)

1-7: IDE-specific files should be excluded from VCS

.iml files are machine-generated and environment-specific; committing them clutters the repo and causes merge noise. Add .idea/** (or at least *.iml) to .gitignore unless the team has agreed otherwise.

.idea/gradle.xml (1)

6-11: Same concern: project-local IDE metadata in VCS

The change merely updates an IntelliJ Gradle linkage path. Unless you’re intentionally version-controlling workspace configs, consider ignoring .idea/ to avoid accidental breakage for other developers or CI images.

greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/SignUpRequest.java (1)

1-5: Add JavaDoc documentation for the record.

Consider adding JavaDoc to document the purpose and usage of this DTO.

+/**
+ * DTO for user sign-up requests containing email and authentication domain.
+ * 
+ * @param email user's email address
+ * @param domain authentication domain (e.g., KAKAO, GOOGLE, NAVER)
+ */
 public record SignUpRequest(
greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/KakaoLoginRequest.java (1)

1-4: Add JavaDoc documentation for the record.

Consider adding JavaDoc to document the purpose of this DTO in the Kakao OAuth flow.

+/**
+ * DTO for Kakao login requests containing the OAuth access token.
+ * 
+ * @param accessToken OAuth access token obtained from Kakao
+ */
 public record KakaoLoginRequest(String accessToken) {
greatjourney/src/main/java/backend/greatjourney/global/exception/CustomException.java (2)

6-9: Consider adding constructor overload for custom messages.

While the current constructor uses the ErrorCode's message, you might want to allow custom messages while preserving the ErrorCode.

 	public CustomException(ErrorCode errorCode) {
 		super(errorCode.getMessage());
 		this.errorCode = errorCode;
 	}
+
+	public CustomException(ErrorCode errorCode, String customMessage) {
+		super(customMessage);
+		this.errorCode = errorCode;
+	}
+
+	public CustomException(ErrorCode errorCode, String customMessage, Throwable cause) {
+		super(customMessage, cause);
+		this.errorCode = errorCode;
+	}

1-14: Add JavaDoc documentation for the exception class.

Consider adding comprehensive JavaDoc to document the purpose and usage of this custom exception.

+/**
+ * Custom runtime exception that wraps an ErrorCode for centralized error handling.
+ * This exception is used throughout the application to provide consistent error
+ * reporting with standardized error codes and messages.
+ * 
+ * @see ErrorCode
+ */
 public class CustomException extends RuntimeException{
greatjourney/src/main/java/backend/greatjourney/global/security/config/SecurityConfig.java (1)

34-36: Consider code consistency for comments.

The inline comments are in Korean while the rest of the codebase appears to use English. Consider maintaining consistent language throughout the codebase.

greatjourney/src/main/java/backend/greatjourney/domain/user/repository/impl/UserRepositoryImplement.java (1)

15-15: Fix variable naming typo.

The variable name quesr appears to be a typo.

-	QUser quesr = QUser.user;
+	QUser qUser = QUser.user;

And update all references to use the corrected name.

greatjourney/src/main/java/backend/greatjourney/domain/user/service/SignService.java (1)

3-3: Remove unused import.

The DomImpl import is not used in this class and should be removed.

-import org.apache.xmlbeans.impl.store.DomImpl;
greatjourney/src/main/java/backend/greatjourney/global/security/entitiy/CustomOAuth2User.java (1)

1-1: Fix package name typo.

The package name contains a typo: entitiy should be entity.

-package backend.greatjourney.global.security.entitiy;
+package backend.greatjourney.global.security.entity;

This will also require updating all import statements that reference this class.

greatjourney/src/main/java/backend/greatjourney/domain/user/service/KakaoService.java (1)

39-39: Use Spring-managed ObjectMapper

ObjectMapper should be injected as a Spring bean to ensure consistent JSON serialization/deserialization settings across the application.

-private final ObjectMapper objectMapper = new ObjectMapper();
+private final ObjectMapper objectMapper;
greatjourney/src/main/java/backend/greatjourney/domain/token/service/JwtTokenProvider.java (2)

118-129: Consider returning Long for user ID consistency

The method returns a String but the subject contains a Long userId. Consider returning Long directly to maintain type consistency.

-public String getUserIdFromToken(String token) {
+public Long getUserIdFromToken(String token) {
     try {
-        return Jwts.parser()
+        String subject = Jwts.parser()
             .verifyWith(key)
             .build()
             .parseSignedClaims(token)
             .getPayload()
             .getSubject();
+        return Long.parseLong(subject);
     } catch (JwtException e) {
         throw new CustomException(ErrorCode.JWT_PARSE_FAILED);
+    } catch (NumberFormatException e) {
+        throw new CustomException(ErrorCode.INVALID_TOKEN_FORMAT);
     }
 }

110-110: Fix formatting: add space after comma

-return new TokenResponse(accessToken,refreshToken);
+return new TokenResponse(accessToken, refreshToken);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5cd2739 and 14839bb.

📒 Files selected for processing (37)
  • .idea/compiler.xml (1 hunks)
  • .idea/gradle.xml (1 hunks)
  • .idea/modules.xml (1 hunks)
  • .idea/modules/1984157471/greatjourney.main.iml (1 hunks)
  • .idea/modules/backend.greatjourney.main.iml (1 hunks)
  • greatjourney/.gitignore (1 hunks)
  • greatjourney/build.gradle (4 hunks)
  • greatjourney/src/main/java/backend/greatjourney/GreatjourneyApplication.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/config/QueryDslConfig.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/config/SwaggerConfig.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/token/dto/TokenResponse.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/token/entity/RefreshToken.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/token/repository/RefreshTokenRepository.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/token/service/JwtAuthenticationFilter.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/token/service/JwtAuthenticationManager.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/token/service/JwtTokenProvider.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/controller/UserController.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/dto/properties/KakaoOAuthProperties.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/ChangeUserRequest.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/KakaoLoginRequest.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/SignUpRequest.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/dto/response/KakaoUserResponse.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/entity/Domain.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/entity/Status.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/entity/User.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/entity/UserRole.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/repository/UserRepository.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/repository/UserRepositoryCustom.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/repository/impl/UserRepositoryImplement.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/service/KakaoService.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/service/SignService.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/domain/user/service/UserService.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/global/exception/BaseResponse.java (0 hunks)
  • greatjourney/src/main/java/backend/greatjourney/global/exception/CustomException.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/global/exception/ErrorCode.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/global/security/config/SecurityConfig.java (1 hunks)
  • greatjourney/src/main/java/backend/greatjourney/global/security/entitiy/CustomOAuth2User.java (1 hunks)
💤 Files with no reviewable changes (1)
  • greatjourney/src/main/java/backend/greatjourney/global/exception/BaseResponse.java
🧰 Additional context used
🧬 Code Graph Analysis (3)
greatjourney/src/main/java/backend/greatjourney/domain/token/entity/RefreshToken.java (1)
greatjourney/src/main/java/backend/greatjourney/domain/token/dto/TokenResponse.java (1)
  • Getter (6-11)
greatjourney/src/main/java/backend/greatjourney/domain/user/controller/UserController.java (2)
greatjourney/src/main/java/backend/greatjourney/global/security/entitiy/CustomOAuth2User.java (1)
  • CustomOAuth2User (13-47)
greatjourney/src/main/java/backend/greatjourney/domain/user/repository/impl/UserRepositoryImplement.java (1)
  • RequiredArgsConstructor (12-40)
greatjourney/src/main/java/backend/greatjourney/domain/user/service/SignService.java (3)
greatjourney/src/main/java/backend/greatjourney/domain/user/service/UserService.java (1)
  • Service (21-93)
greatjourney/src/main/java/backend/greatjourney/domain/user/repository/impl/UserRepositoryImplement.java (1)
  • RequiredArgsConstructor (12-40)
greatjourney/src/main/java/backend/greatjourney/domain/user/service/KakaoService.java (1)
  • Slf4j (30-68)
🔇 Additional comments (19)
greatjourney/src/main/java/backend/greatjourney/config/SwaggerConfig.java (3)

3-11: LGTM! Clean import organization.

The imports are well-organized and include all necessary OpenAPI and Spring components for JWT authentication configuration.


25-30: LGTM! Well-structured API metadata.

The API information is properly configured with appropriate title, description, and version for the "어디로" (Where to go) application.


32-39: LGTM! Correct JWT security scheme configuration.

The security scheme is properly configured for JWT bearer token authentication with correct type, scheme, format, and header location.

.idea/modules.xml (1)

5-7: IDE-specific files should stay out of VCS

Committing .idea/** risks merge noise and developer-specific state. Add the directory to .gitignore unless the team explicitly version-controls IDEA settings.

greatjourney/src/main/java/backend/greatjourney/domain/user/entity/Domain.java (1)

3-7: LGTM – simple, self-explanatory enum

No issues found; naming is clear and future-proof for additional providers.

.idea/compiler.xml (2)

9-18: QueryDSL annotation processor configuration looks correct.

The added annotation processor entries for QueryDSL, Jakarta persistence, and related libraries are properly configured to support code generation.


20-20: Module name update aligns with project structure.

The module name change to backend.greatjourney.main is consistent with the project's package structure.

.idea/modules/backend.greatjourney.main.iml (1)

4-5: Module configuration for generated sources is correct.

The content root and source folder configuration for annotation processor generated sources is properly set up to support QueryDSL code generation.

greatjourney/src/main/java/backend/greatjourney/domain/user/repository/UserRepositoryCustom.java (1)

7-11: LGTM! Well-designed repository interface.

The interface follows good practices with clear method names, proper use of Optional for null safety, and follows Spring Data conventions.

greatjourney/src/main/java/backend/greatjourney/domain/user/repository/UserRepository.java (1)

7-8: LGTM! Clean repository interface design.

The interface correctly extends both JpaRepository and UserRepositoryCustom, following Spring Data best practices for combining standard and custom repository operations.

greatjourney/src/main/java/backend/greatjourney/GreatjourneyApplication.java (1)

9-9: LGTM! Correctly enables Spring Security.

Removing the SecurityAutoConfiguration exclusion is the right approach to enable the comprehensive security features being added in this PR, including JWT authentication and Kakao OAuth integration.

greatjourney/src/main/java/backend/greatjourney/config/QueryDslConfig.java (1)

11-21: LGTM! Standard QueryDSL configuration implementation.

The QueryDSL configuration follows Spring best practices with proper dependency injection and bean creation. The implementation is clean and minimal.

greatjourney/build.gradle (2)

35-37: JWT dependency upgrade looks correct.

The JWT dependency upgrade from 0.11.5 to 0.12.3 and changing jjwt-impl from runtimeOnly to implementation is correct for the newer version requirements.


73-77: QueryDSL configuration is properly set up.

The QueryDSL dependencies and annotation processors are correctly configured for Jakarta EE compatibility.

greatjourney/src/main/java/backend/greatjourney/domain/token/entity/RefreshToken.java (1)

24-30: Builder implementation is well-structured.

The builder pattern implementation properly initializes all fields and uses appropriate parameter names. The use of Instant for expiry dates is a good choice for precise timestamp handling.

greatjourney/src/main/java/backend/greatjourney/domain/token/service/JwtAuthenticationFilter.java (1)

14-20: Well-structured JWT authentication filter.

The class properly extends OncePerRequestFilter and uses constructor injection for dependencies. The filter will be executed once per request, which is appropriate for JWT authentication.

greatjourney/src/main/java/backend/greatjourney/domain/user/repository/impl/UserRepositoryImplement.java (2)

17-26: Correct implementation for existence check.

The method properly uses selectOne() with fetchFirst() to efficiently check for email existence without fetching the entire entity.


28-36: Proper optional result handling.

The method correctly uses Optional.ofNullable() with fetchOne() to handle the case where no user is found.

greatjourney/src/main/java/backend/greatjourney/global/security/entitiy/CustomOAuth2User.java (1)

42-45: Simple but effective authorities implementation.

The method correctly creates a GrantedAuthority from the role string using a lambda expression.

Consider adding role validation if needed:

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return List.of(() -> role != null ? role : "ROLE_USER");
}

Comment on lines +14 to +23
@Bean
public OpenAPI openAPI() {
SecurityRequirement securityRequirement = new SecurityRequirement().addList("BearerAuth");

return new OpenAPI()
.components(new Components())
.info(apiInfo())
.addSecurityItem(securityRequirement)
.schemaRequirement("BearerAuth", securityScheme());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix the security scheme registration method.

The method schemaRequirement on line 22 should be components() to properly register the security scheme. The current implementation won't correctly associate the security scheme with the OpenAPI specification.

Apply this diff to fix the security scheme registration:

 return new OpenAPI()
-	.components(new Components())
+	.components(new Components().addSecuritySchemes("BearerAuth", securityScheme()))
 	.info(apiInfo())
 	.addSecurityItem(securityRequirement)
-	.schemaRequirement("BearerAuth", securityScheme());

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In greatjourney/src/main/java/backend/greatjourney/config/SwaggerConfig.java
lines 14 to 23, replace the call to schemaRequirement("BearerAuth",
securityScheme()) with a proper registration of the security scheme inside the
components() method. Specifically, add the security scheme to the Components
object and set it via components() on the OpenAPI instance to correctly
associate the security scheme with the OpenAPI specification.

Comment on lines +11 to +29
public class KakaoUserResponse {
private Long id;
private KakaoAccount kakao_account;
private Properties properties;

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class KakaoAccount {
private String email;
}

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Properties {
private String nickname;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix naming & type-shadowing – use Java-style field names with @JsonProperty

kakao_account and properties violate Java naming conventions and the nested class Properties shadows java.util.Properties, which is confusing.

 public class KakaoUserResponse {
-    private Long id;
-    private KakaoAccount kakao_account;
-    private Properties properties;
+    private Long id;
+
+    @JsonProperty("kakao_account")
+    private KakaoAccount kakaoAccount;
+
+    @JsonProperty("properties")
+    private KakaoProfile profile;
@@
-    public static class Properties {
-        private String nickname;
-    }
+    public static class KakaoProfile {
+        private String nickname;
+    }
 }

This keeps JSON binding intact while restoring idiomatic Java naming and avoiding class-name clashes.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public class KakaoUserResponse {
private Long id;
private KakaoAccount kakao_account;
private Properties properties;
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class KakaoAccount {
private String email;
}
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Properties {
private String nickname;
}
}
public class KakaoUserResponse {
private Long id;
@JsonProperty("kakao_account")
private KakaoAccount kakaoAccount;
@JsonProperty("properties")
private KakaoProfile profile;
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class KakaoAccount {
private String email;
}
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class KakaoProfile {
private String nickname;
}
}
🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/domain/user/dto/response/KakaoUserResponse.java
between lines 11 and 29, rename the fields kakao_account and properties to
follow Java camelCase conventions (e.g., kakaoAccount and userProperties), and
annotate them with @JsonProperty to map to the original JSON names. Also, rename
the nested Properties class to avoid shadowing java.util.Properties, such as
renaming it to UserProperties, and update all references accordingly to maintain
clarity and proper JSON binding.

Comment on lines +28 to +37
private final HttpStatus status;
private final int code;
private final String message;


ErrorCode( HttpStatus status,int code, String message){
this.code = code;
this.status = status;
this.message = message;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Derive code from status to remove duplication

You can eliminate the integer parameter entirely:

@Getter
public enum ErrorCode {
    LOGIN_FAIL(BAD_REQUEST,"로그인에 오류가 발생하였습니다."),
    …
    USER_NOT_FOUND(BAD_REQUEST,"존재하지 않는 유저입니다.");

    private final HttpStatus status;
    private final int code;
    private final String message;

    ErrorCode(HttpStatus status,String message){
        this.status = status;
        this.code   = status.value();
        this.message= message;
    }
}

This prevents future inconsistencies at zero runtime cost.

🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/global/exception/ErrorCode.java
around lines 28 to 37, the integer code field is redundantly passed as a
constructor parameter despite being derivable from the HttpStatus. Remove the
int code parameter from the constructor and instead assign this.code =
status.value() inside the constructor. Update the constructor signature to
accept only HttpStatus and message, and adjust all enum constants accordingly to
pass only HttpStatus and message. This eliminates duplication and ensures code
consistency.

Comment on lines +11 to +24
LOGIN_FAIL(HttpStatus.BAD_REQUEST,400,"로그인에 오류가 발생하였습니다."),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,500,"서버에 오류가 발생하였습니다."),
JWT_KEY_GENERATION_FAILED(HttpStatus.BAD_REQUEST,400,"JWT 키 생성에 실패하였습니다."),
NO_REFRESH_TOKEN(UNAUTHORIZED,400, "리프레시 토큰이 없습니다."),
LOGOUT_ERROR(BAD_REQUEST,400,"로그아웃에 실패하였습니다."),
SIGNUP_ERROR(BAD_REQUEST,400,"회원가입에러입니다."),
EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, 400,"만료된 토큰입니다."),
JWT_PARSE_FAILED(BAD_REQUEST,404,"토큰 파싱이 잘못되었습니다."),

KAKAO_USER_ERROR(BAD_REQUEST,404,"카카오 유저 정보를 가져오지 못하였습니다."),
TOKEN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,500, "토큰을 제대로 생성하지 못하였습니다."),


USER_NOT_FOUND(BAD_REQUEST,404,"존재하지 않는 유저입니다.");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

HTTP status ↔ numeric code mismatch – will cause inconsistent API responses

Several constants pair HttpStatus.UNAUTHORIZED (401) with numeric code 400, and BAD_REQUEST (400) with numeric 404, e.g.:

NO_REFRESH_TOKEN(UNAUTHORIZED,400,…)
EXPIRED_REFRESH_TOKEN(UNAUTHORIZED,400,…)
JWT_PARSE_FAILED(BAD_REQUEST,404,…)

Clients relying on the numeric field will receive conflicting information.

-NO_REFRESH_TOKEN(UNAUTHORIZED,400, "리프레시 토큰이 없습니다."),
-EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, 400,"만료된 토큰입니다."),
-JWT_PARSE_FAILED(BAD_REQUEST,404,"토큰 파싱이 잘못되었습니다."),
+NO_REFRESH_TOKEN(UNAUTHORIZED,401, "리프레시 토큰이 없습니다."),
+EXPIRED_REFRESH_TOKEN(UNAUTHORIZED,401,"만료된 토큰입니다."),
+JWT_PARSE_FAILED(BAD_REQUEST,400,"토큰 파싱이 잘못되었습니다."),

Audit the entire enum to keep code == status.value(), or derive code automatically from status to prevent drift.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LOGIN_FAIL(HttpStatus.BAD_REQUEST,400,"로그인에 오류가 발생하였습니다."),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,500,"서버에 오류가 발생하였습니다."),
JWT_KEY_GENERATION_FAILED(HttpStatus.BAD_REQUEST,400,"JWT 키 생성에 실패하였습니다."),
NO_REFRESH_TOKEN(UNAUTHORIZED,400, "리프레시 토큰이 없습니다."),
LOGOUT_ERROR(BAD_REQUEST,400,"로그아웃에 실패하였습니다."),
SIGNUP_ERROR(BAD_REQUEST,400,"회원가입에러입니다."),
EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, 400,"만료된 토큰입니다."),
JWT_PARSE_FAILED(BAD_REQUEST,404,"토큰 파싱이 잘못되었습니다."),
KAKAO_USER_ERROR(BAD_REQUEST,404,"카카오 유저 정보를 가져오지 못하였습니다."),
TOKEN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,500, "토큰을 제대로 생성하지 못하였습니다."),
USER_NOT_FOUND(BAD_REQUEST,404,"존재하지 않는 유저입니다.");
JWT_KEY_GENERATION_FAILED(HttpStatus.BAD_REQUEST, 400, "JWT 키 생성에 실패하였습니다."),
NO_REFRESH_TOKEN(UNAUTHORIZED, 401, "리프레시 토큰이 없습니다."),
LOGOUT_ERROR(BAD_REQUEST, 400, "로그아웃에 실패하였습니다."),
SIGNUP_ERROR(BAD_REQUEST, 400, "회원가입에러입니다."),
EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, 401, "만료된 토큰입니다."),
JWT_PARSE_FAILED(BAD_REQUEST, 400, "토큰 파싱이 잘못되었습니다."),
KAKAO_USER_ERROR(BAD_REQUEST, 404, "카카오 유저 정보를 가져오지 못하였습니다."),
🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/global/exception/ErrorCode.java
between lines 11 and 24, the numeric codes assigned to error constants do not
match the HTTP status codes, causing inconsistent API responses. Fix this by
ensuring the numeric code matches the value of the HttpStatus enum used for each
constant, either by setting code to status.value() directly or by removing the
manual code parameter and deriving it automatically from the HttpStatus to keep
them consistent.

Comment on lines +1 to +4
package backend.greatjourney.domain.user.dto.request;

public record ChangeUserRequest(String name) {
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation annotations to prevent empty names

Incoming payloads can currently pass an empty string or null, leading to silent DB corruption or constraint violations down-stream. Add Bean-Validation:

 package backend.greatjourney.domain.user.dto.request;

+import jakarta.validation.constraints.NotBlank;
+
-public record ChangeUserRequest(String name) {
-}
+public record ChangeUserRequest(
+        @NotBlank(message = "name must not be blank")
+        String name
+) {}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
package backend.greatjourney.domain.user.dto.request;
public record ChangeUserRequest(String name) {
}
package backend.greatjourney.domain.user.dto.request;
import jakarta.validation.constraints.NotBlank;
public record ChangeUserRequest(
@NotBlank(message = "name must not be blank")
String name
) {}
🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/domain/user/dto/request/ChangeUserRequest.java
at lines 1 to 4, the ChangeUserRequest record lacks validation annotations on
the name field, allowing empty or null values that can cause database issues.
Add appropriate Bean Validation annotations such as @NotBlank or @NotEmpty and
@NotNull to the name parameter to ensure it cannot be null or empty when
received.

//회원정보수정
@Operation(summary = "회원정보 수정 API")
@PatchMapping("/change")
public BaseResponse<?> chageUserInfo(@AuthenticationPrincipal CustomOAuth2User customOAuth2User,@RequestBody
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in method name

-public BaseResponse<?> chageUserInfo(@AuthenticationPrincipal CustomOAuth2User customOAuth2User,@RequestBody
+public BaseResponse<?> changeUserInfo(@AuthenticationPrincipal CustomOAuth2User customOAuth2User, @RequestBody
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public BaseResponse<?> chageUserInfo(@AuthenticationPrincipal CustomOAuth2User customOAuth2User,@RequestBody
public BaseResponse<?> changeUserInfo(@AuthenticationPrincipal CustomOAuth2User customOAuth2User, @RequestBody
🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/domain/user/controller/UserController.java
at line 69, there is a typo in the method name "chageUserInfo". Rename the
method to "changeUserInfo" to correct the spelling.

Comment on lines +49 to +56
public BaseResponse<?> loginKakao(@RequestBody KakaoLoginRequest request){
return BaseResponse.builder()
.isSuccess(true)
.code(200)
.message("로그인이 완료되었습니다.")
.data(kakaoService.loginWithKakao(request.accessToken()))
.build();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Maintain consistent response handling pattern

The Kakao login endpoint manually builds the response while other endpoints delegate this to the service layer. Consider maintaining consistency by moving response building to the service.

 public BaseResponse<?> loginKakao(@RequestBody KakaoLoginRequest request){
-    return BaseResponse.builder()
-        .isSuccess(true)
-        .code(200)
-        .message("로그인이 완료되었습니다.")
-        .data(kakaoService.loginWithKakao(request.accessToken()))
-        .build();
+    return kakaoService.loginWithKakao(request.accessToken());
 }

Then modify KakaoService to return BaseResponse instead of TokenResponse.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public BaseResponse<?> loginKakao(@RequestBody KakaoLoginRequest request){
return BaseResponse.builder()
.isSuccess(true)
.code(200)
.message("로그인이 완료되었습니다.")
.data(kakaoService.loginWithKakao(request.accessToken()))
.build();
}
public BaseResponse<?> loginKakao(@RequestBody KakaoLoginRequest request){
return kakaoService.loginWithKakao(request.accessToken());
}
🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/domain/user/controller/UserController.java
around lines 49 to 56, the loginKakao method manually builds the BaseResponse
while other endpoints delegate this to the service layer. To fix this, refactor
the loginKakao method to directly return the response from
kakaoService.loginWithKakao without wrapping it again. Then update
KakaoService.loginWithKakao to return BaseResponse<TokenResponse> instead of
just TokenResponse, so the service handles the full response construction
consistently.

Comment on lines +44 to +52
Long userId = Long.parseLong(customOAuth2User.getUserId());
RefreshToken refreshToken = refreshTokenRepository.findByUserId(userId);

return BaseResponse.<Void>builder()
.code(200)
.message("로그아웃이 완료되었습니다.")
.data(refreshTokenRepository.deleteByToken(refreshToken.getToken()))
.isSuccess(true)
.build();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null check for refresh token

The code doesn't handle the case where no refresh token exists for the user, which could cause a NullPointerException.

 Long userId = Long.parseLong(customOAuth2User.getUserId());
 RefreshToken refreshToken = refreshTokenRepository.findByUserId(userId);
+if (refreshToken == null) {
+    return BaseResponse.<Void>builder()
+        .code(200)
+        .message("이미 로그아웃된 상태입니다.")
+        .isSuccess(true)
+        .build();
+}

 return BaseResponse.<Void>builder()
     .code(200)
     .message("로그아웃이 완료되었습니다.")
-    .data(refreshTokenRepository.deleteByToken(refreshToken.getToken()))
+    .data(null)
     .isSuccess(true)
     .build();
+refreshTokenRepository.deleteByToken(refreshToken.getToken());

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/domain/user/service/UserService.java
around lines 44 to 52, add a null check for the refreshToken object after
retrieving it from the repository. If refreshToken is null, handle this case
appropriately (e.g., return a response indicating no token found or proceed
without attempting to delete). This prevents a NullPointerException when calling
refreshToken.getToken().

Comment on lines +58 to +66
Long userId = Long.parseLong(customOAuth2User.getUserId());
RefreshToken refreshToken = refreshTokenRepository.findByUserId(userId);

refreshTokenRepository.deleteByToken(refreshToken.getToken());
User user = userRepository.findByUserId(userId)
.orElseThrow(()->new CustomException(ErrorCode.USER_NOT_FOUND));

user.setStatus(Status.DELETED);
userRepository.save(user);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null check and consider using builder pattern

Similar to the logout method, this needs null checking for the refresh token. Also, using setters on entities is not ideal for immutability.

 Long userId = Long.parseLong(customOAuth2User.getUserId());
 RefreshToken refreshToken = refreshTokenRepository.findByUserId(userId);
+if (refreshToken != null) {
+    refreshTokenRepository.deleteByToken(refreshToken.getToken());
+}
-refreshTokenRepository.deleteByToken(refreshToken.getToken());

Consider using a builder pattern or dedicated update method instead of setters for better encapsulation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Long userId = Long.parseLong(customOAuth2User.getUserId());
RefreshToken refreshToken = refreshTokenRepository.findByUserId(userId);
refreshTokenRepository.deleteByToken(refreshToken.getToken());
User user = userRepository.findByUserId(userId)
.orElseThrow(()->new CustomException(ErrorCode.USER_NOT_FOUND));
user.setStatus(Status.DELETED);
userRepository.save(user);
Long userId = Long.parseLong(customOAuth2User.getUserId());
RefreshToken refreshToken = refreshTokenRepository.findByUserId(userId);
if (refreshToken != null) {
refreshTokenRepository.deleteByToken(refreshToken.getToken());
}
User user = userRepository.findByUserId(userId)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
user.setStatus(Status.DELETED);
userRepository.save(user);
🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/domain/user/service/UserService.java
around lines 58 to 66, add a null check for the refreshToken before calling
deleteByToken to avoid NullPointerException. Replace the direct setter call on
the user entity with a builder pattern or a dedicated update method to change
the user's status to DELETED, ensuring better encapsulation and immutability.

throw new IllegalArgumentException("이미 존재하는 회원입니다.");
}
return new BaseResponse<>(true,"회원가입이 완료되었습니다",201,userRepository.save(User.builder()
.domain(Domain.valueOf(request.domain()))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for domain string

Domain.valueOf() will throw IllegalArgumentException if the domain string is invalid. Consider adding validation or using a safer parsing method.

+Domain domain;
+try {
+    domain = Domain.valueOf(request.domain());
+} catch (IllegalArgumentException e) {
+    throw new CustomException(ErrorCode.INVALID_DOMAIN);
+}
 return new BaseResponse<>(true,"회원가입이 완료되었습니다",201,userRepository.save(User.builder()
-    .domain(Domain.valueOf(request.domain()))
+    .domain(domain)
     .email(request.email())
     .status(Status.SUCCESS)
     .build()));
🤖 Prompt for AI Agents
In
greatjourney/src/main/java/backend/greatjourney/domain/user/service/UserService.java
at line 35, the use of Domain.valueOf(request.domain()) can throw an
IllegalArgumentException if the domain string is invalid. To fix this, add
validation before calling valueOf by checking if the domain string matches any
valid enum constants or use a safer parsing method that returns an Optional or
null if invalid. Handle the invalid case gracefully, such as throwing a custom
exception or returning an error response.

@minhyuk2 minhyuk2 merged commit 1346e86 into develop Jul 17, 2025
1 check passed
@minhyuk2 minhyuk2 deleted the feat/#1-login branch July 17, 2025 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 카카오 로그인 및 로그인 구조 구현

2 participants