Skip to content

Conversation

@minjee2758
Copy link
Collaborator

@minjee2758 minjee2758 commented Aug 24, 2025

PR 생성 시 아래 항목을 채워주세요.

제목 예시: feat : Pull request template 작성

(작성 후 이 안내 문구는 삭제해주세요)


작업 내용

  • 어떤 기능(또는 수정 사항)을 구현했는지 간략하게 설명해주세요.
  • 예) "회원가입 API에 이메일 중복 검사 기능 추가"

변경 사항

  • 구현한 주요 로직, 클래스, 메서드 등을 bullet 형식으로 기술해주세요.
  • 예)
    • UserService.createUser() 메서드 추가
    • @Email 유효성 검증 적용

트러블 슈팅

  • 구현 중 마주한 문제와 해결 방법을 기술해주세요.
  • 예)
    • 문제: @Transactional이 적용되지 않음
    • 해결: 메서드 호출 방식 변경 (this.AopProxyUtils. 사용)

해결해야 할 문제

  • 기능은 동작하지만 리팩토링이나 논의가 필요한 부분을 적어주세요.
  • 예)D
    • UserController에서 비즈니스 로직 일부 처리 → 서비스로 이전 고려 필요

참고 사항

  • 기타 공유하고 싶은 정보나 참고한 문서(링크 등)가 있다면 작성해주세요.

코드 리뷰 전 확인 체크리스트

  • 불필요한 콘솔 로그, 주석 제거
  • 커밋 메시지 컨벤션 준수 (type : )
  • 기능 정상 동작 확인

Summary by CodeRabbit

  • 신기능
    • 회원가입 시 기본 사용 언어가 자동 설정되어 초기 경험이 향상되었습니다.
    • 내 정보 조회 응답에 사용 언어와 계정 연동(인증) 유형 목록이 추가되어 설정과 연동 상태를 한눈에 확인할 수 있습니다.
    • 프로필 수정 시 languageId로 사용 언어를 변경할 수 있어 개인화와 지역화 설정이 쉬워졌습니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 24, 2025

Warning

Rate limit exceeded

@minjee2758 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9a33670 and 859ec2d.

📒 Files selected for processing (1)
  • src/test/java/org/ezcode/codetest/domain/user/UserDomainServiceTest.java (4 hunks)

Walkthrough

User 엔티티에 Language 연관을 추가하고, 회원 생성/수정/조회 흐름에 Language 및 UserAuthType 정보를 전달·노출하도록 서비스, DTO, 리포지토리, 팩토리, 테스트를 확장했습니다. 일부 팩토리/빌더/메서드 시그니처가 Language를 수용하도록 변경되었습니다.

Changes

Cohort / File(s) Summary
Domain: User 엔티티 확장
src/main/java/.../domain/user/model/entity/User.java
@ManyToOne Language language 필드 추가. 팩토리 메서드(emailUser, socialUser, githubUser), @Builder 생성자, modifyUserInfoLanguage 파라미터 추가 및 필드 설정 반영.
Domain: UserFactory 변경
src/main/java/.../domain/user/model/entity/UserFactory.java
소셜 유저 생성 메서드에 Language language 파라미터 추가하고 전달하도록 수정.
Application: Auth 서비스 연동
src/main/java/.../application/usermanagement/auth/service/AuthService.java
LanguageDomainService 주입. 회원가입 시 getLanguage(1L)로 기본 Language 조회 후 User.emailUser(..., language) 호출로 변경.
Application: User 서비스 확장
src/main/java/.../application/usermanagement/user/service/UserService.java
LanguageDomainService 의존성 추가. getUserInfo에서 getUserAuthTypesByUser 호출해 AuthType으로 매핑하여 UserInfoResponselanguage·userAuthTypes 포함. modifyUserInfo에서 languageIdLanguage 조회 후 도메인에 전달.
DTO: Modify 요청/Response 확장
src/main/java/.../application/usermanagement/user/dto/request/ModifyUserInfoRequest.java, src/main/java/.../application/usermanagement/user/dto/response/UserInfoResponse.java
ModifyUserInfoRequest에 Long languageId 추가(@Schema). UserInfoResponse에 Language language, List<AuthType> userAuthTypes 필드 및 빌더 파라미터 추가(기존 fromEntity 빌더 호출에는 해당 값들이 누락될 수 있음).
Domain Service: User 도메인 조회 확장
src/main/java/.../domain/user/service/UserDomainService.java
public List<UserAuthType> getUserAuthTypesByUser(User user) 메서드 추가(리포지토리 위임).
Domain: Language 도메인 사용
src/main/java/.../domain/language/service/LanguageDomainService.java
서비스에서 getLanguage(languageId) 호출 사용(새 의존성 주입 대상).
Repository: UserAuthType 인터페이스
src/main/java/.../domain/user/repository/UserAuthTypeRepository.java
List<UserAuthType> getUserAuthTypesByUser(User user) 메서드 시그니처 추가.
Repository Impl: UserAuthTypeRepositoryImpl
src/main/java/.../infrastructure/persistence/repository/user/UserAuthTypeRepositoryImpl.java
getUserAuthTypesByUser(User user) 오버라이드 추가 — 내부에서 매핑을 시도하나 현재 List.of()로 빈 리스트를 반환하는 불일치(논리 오류 가능).
Presentation: 컨트롤러 정리
src/main/java/.../presentation/usermanagement/UserController.java
LanguageResponse 임포트 추가, 미사용 임포트 제거 및 포매팅 정리(퍼블릭 API 변경 없음).
Tests: 시그니처 반영
src/test/java/.../ProblemServiceTest.java, src/test/java/.../UserDomainServiceTest.java
테스트에서 User 생성자 호출에 Language 인자 추가 및 LanguageDomainService 주입/사용 반영.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant AuthService
  participant LanguageService as LanguageDomainService
  participant UserEntity as User

  Client->>AuthService: signup(request)
  AuthService->>LanguageService: getLanguage(1L)
  LanguageService-->>AuthService: Language
  AuthService->>UserEntity: User.emailUser(..., Language)
  Note right of UserEntity: language 필드 설정
  AuthService-->>Client: signup 응답
Loading
sequenceDiagram
  autonumber
  participant Client
  participant UserService
  participant UserDomain
  participant LanguageService as LanguageDomainService
  participant UserEntity as User

  Client->>UserService: getUserInfo(userId)
  UserService->>UserDomain: getUserAuthTypesByUser(user)
  UserDomain-->>UserService: List<UserAuthType>
  UserService->>UserService: map to List<AuthType>
  UserService-->>Client: UserInfoResponse(language, userAuthTypes 포함)
Loading
sequenceDiagram
  autonumber
  participant Client
  participant UserService
  participant LanguageService as LanguageDomainService
  participant UserEntity as User

  Client->>UserService: modifyUserInfo(request{languageId,...})
  UserService->>LanguageService: getLanguage(request.languageId)
  LanguageService-->>UserService: Language
  UserService->>UserEntity: modifyUserInfo(..., Language)
  UserService-->>Client: 수정 완료
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • thezz9
  • Kimminu7
  • pokerbearkr
  • NCookies

Poem

나는 토끼, 코드 밭을 훑네 🥕
언어 한 알을 등에 달아주고
가입하고 수정할 때 다정히 전해주네
타입들도 반짝, 연결되었구나
풀밭에서 뛰노는 배포의 날!

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/user-language-select

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java (1)

117-128: 이메일 인증 코드 검증 시 사용자 null/삭제 상태 처리 누락

userDomainService.getUserByEmail(email)이 null을 반환하거나 삭제 사용자일 경우 NPE 또는 보안상 부적절한 응답이 발생할 수 있습니다. findPassword와 동일한 수준으로 방어 코드를 추가해 주세요.

- User user = userDomainService.getUserByEmail(email);
+ User user = userDomainService.getUserByEmail(email);
+ if (user == null || user.isDeleted()) {
+   throw new UserException(UserExceptionCode.USER_NOT_FOUND);
+ }
src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (1)

98-106: 신규 프로필 이미지 업로드 후 삭제 대상 URL 잘못 사용 — 새 이미지가 삭제됩니다

user.modifyProfileImage(profileImageUrl) 호출 후 s3Uploader.delete(user.getProfileImageUrl(), "profile")를 호출하는데, 이 시점의 user.getProfileImageUrl()은 “새 URL”입니다. 결과적으로 방금 업로드한 이미지를 지우게 됩니다. 반드시 oldImageUrl을 삭제 대상으로 사용하세요. 또한 "profile" 하드코딩 대신 S3Directory.PROFILE.getDir()를 사용해 일관성을 유지하세요.

- user.modifyProfileImage(profileImageUrl);
- if (oldImageUrl!=null) {
-   s3Uploader.delete(user.getProfileImageUrl(), "profile");
- }
+ user.modifyProfileImage(profileImageUrl);
+ if (oldImageUrl != null) {
+   s3Uploader.delete(oldImageUrl, S3Directory.PROFILE.getDir());
+ }
src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (1)

91-105: 치명적: 이메일 회원가입 사용자의 기본 역할이 ADMIN으로 설정됨

테스트용 주석이 있으나, 그대로 배포되면 신규 이메일 가입자가 관리자 권한이 됩니다. 즉시 USER로 되돌려야 합니다.

- .role(UserRole.ADMIN) // 테스트용
+ .role(UserRole.USER)
🧹 Nitpick comments (11)
src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java (1)

3-3: 사용되지 않는 import 제거

LanguageResponse를 이 클래스 내에서 사용하지 않습니다. 정리하여 불필요한 의존을 줄여 주세요.

-import org.ezcode.codetest.application.submission.dto.response.language.LanguageResponse;
src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java (3)

139-147: 액세스 토큰 생성 로직 중복

createAccessToken(user) 유틸이 있음에도 refreshToken에서 동일 파라미터로 재구성하고 있습니다. 단일 메서드로 통일해 중복을 제거하면 유지보수성이 올라갑니다.

- String newAccessToken = jwtUtil.createAccessToken(
-   user.getId(),
-   user.getEmail(),
-   user.getRole(),
-   user.getUsername(),
-   user.getNickname(),
-   user.getTier()
- );
+ String newAccessToken = createAccessToken(user);

Also applies to: 216-223


185-197: 로그아웃 Redis 오류 처리 개선 제안

블랙리스트 등록과 리프레시 토큰 삭제가 동일 try 블록에 묶여 있어 한쪽 실패 시 다른 쪽도 수행되지 않습니다. 실패 허용 정책이라면, 각 연산을 분리하고 개별 예외를 잡아 로그를 남기는 편이 운영 안정성에 유리합니다.

원하시면 try-catch 분리/로깅 개선 패치도 제안하겠습니다.


65-71: EMAIL 인증타입만 검사하도록 구현이 확인되었습니다

  • UserDomainService.checkEmailUnique(String email) 메서드는 내부적으로 userRepository.findByEmail(email)AuthType.EMAIL 보유 여부만 검사해 예외를 던지도록 구현되어 있습니다.
    → 즉, 기존 소셜 유저(EMAIL 타입 미포함)는 중복 예외 없이 AuthService.updateExistingUser 분기로 진입 가능합니다.
    (확인 위치: src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java:36–45)
  • 정책이 의도대로 반영되었으나, 가독성과 유지보수를 위해 아래 선택적 리팩토링을 권장드립니다.
    • Javadoc 또는 메서드명에 검사 범위를 명시(checkEmailUniqueForEmailAuth 등)
    • “EMAIL 타입 미포함 유저” 시나리오(예: AuthType.GOOGLE만 보유) 단위 테스트 추가
    (테스트 파일 위치: src/test/java/org/ezcode/codetest/domain/user/UserDomainServiceTest.java)
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/UserInfoResponse.java (1)

82-95: fromEntity가 새 필드(languageId/userAuthTypes) 미설정 — 호출부 영향 점검

현재 fromEntity(User)는 새로 추가된 필드를 세팅하지 않아 응답에서 null이 반환됩니다. 이 정적 팩토리를 사용하는 컨트롤러/서비스가 있다면 일관성 문제가 생깁니다. 최소한 languageId는 안전하게 채워주세요(없으면 null).

 return UserInfoResponse.builder()
   .username(user.getUsername())
   ...
   .tier(user.getTier())
+  .languageId(user.getLanguage() != null ? user.getLanguage().getId() : null)
   .build();

호출부(예: UserService#getUserInfo)도 아래처럼 맞춰야 합니다:

// 지원 코드 예시(파일 외)
.languageId(user.getLanguage() != null ? user.getLanguage().getId() : null)
.userAuthTypes(authTypes)
src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (3)

63-66: 인증 타입 매핑 로직은 명확하고 간결합니다

List<UserAuthType> → List<AuthType> 변환은 가독성/의도 모두 좋습니다. 다만 import java.util.stream.Collectors;는 더 이상 사용하지 않으므로 제거 가능합니다.


109-121: 수정 응답의 일관성: userAuthTypes 누락

getUserInfo 응답에는 userAuthTypes가 포함되지만, modifyUserInfo 응답에는 누락되어 있습니다. 클라이언트가 동일 DTO를 기대한다면 통일하는 것이 좋습니다.

원하시면 modifyUserInfo에서도 userAuthTypes를 조회/셋팅하는 패치 제안드리겠습니다.


63-66: N+1 가능성 검토: 인증 타입 리스트 조회

userDomainService.getUserAuthTypesByUser(user)가 매 요청마다 별도 쿼리를 발생시킨다면 N+1에 취약할 수 있습니다(특히 사용자 리스트 API에서). Fetch Join 또는 일괄 조회용 Repo 메서드 제공을 고려해 주세요.

src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (3)

78-81: 언어 연관 컬럼 제약 명시 고려

업무상 “사용자 언어가 항상 존재”한다면 @JoinColumn(nullable = false)로 제약을 명시하고 기본값 보장을 서비스/마이그레이션으로 확보하세요. 반대로 선택값이라면 현 상태 유지도 가능하나, API/도메인 정책과 일치하는지 점검 바랍니다.

- @JoinColumn(name = "language_id")
+ @JoinColumn(name = "language_id", nullable = false)

146-159: Builder 시그니처 확장 합리적 — 단, 필드 순서/누락 회귀 테스트 권장

Builder에 language 추가는 자연스럽습니다. 생성 경로별 회귀 테스트(EMAIL/SOCIAL/GITHUB)로 누락 필드가 없는지 확인해 주세요.

원하시면 세 경로별 사용자 생성 단위 테스트 템플릿을 제공하겠습니다.


23-24: 불필요한 import 정리

jakarta.persistence.OneToOne는 본 파일에서 사용되지 않습니다. 정리해 주세요.

- import jakarta.persistence.OneToOne;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d16d016 and ec27eed.

📒 Files selected for processing (9)
  • src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java (3 hunks)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/request/ModifyUserInfoRequest.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/UserInfoResponse.java (3 hunks)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (6 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (6 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/repository/UserAuthTypeRepository.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserAuthTypeRepositoryImpl.java (1 hunks)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (2)
src/main/java/org/ezcode/codetest/domain/user/model/entity/UserGithubInfo.java (1)
  • Entity (16-53)
src/main/java/org/ezcode/codetest/domain/problem/model/entity/ErrorNote.java (1)
  • Entity (21-66)
🔇 Additional comments (5)
src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java (1)

131-133: 리포지토리 위임 방식 적절

엔티티 단위의 인증 타입 컬렉션을 제공하는 공개 메서드 추가는 타 계층(응답 매핑 등)에서 확장 정보를 사용할 수 있게 해 유용합니다. 구현 자체는 단순 위임으로 적절합니다.

src/main/java/org/ezcode/codetest/domain/user/repository/UserAuthTypeRepository.java (1)

14-14: 엔티티 컬렉션 조회 메서드 추가 적절

List<UserAuthType> 반환 메서드 추가로 용도 분리가 명확해졌습니다(List<AuthType> 반환 메서드와 병행). 인터페이스 측 변경은 타당합니다.

src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java (1)

46-46: LanguageDomainService 주입 추가는 방향성 OK

도메인에서 기본/선호 언어를 조회해 유스케이스에 연결하는 방향은 적절합니다.

src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (2)

127-141: 소셜/GitHub 생성자에 Language 연동은 적절합니다

신규 연동 파라미터 추가와 빌더 연결이 일관되게 반영되어 있습니다.

Also applies to: 111-124


165-172: 프로필/나이/언어 수정 로직 명확하고 방어적

null/blank 유지 정책이 잘 반영되어 있습니다. 언어 변경 권한/감사로그가 필요하다면 서비스 계층에서 보강만 하면 됩니다.

public class AuthService {

private final UserDomainService userDomainService;
private final LanguageDomainService languageDomainService;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

기본 언어 ID 하드코딩(1L) 제거 및 널/예외 처리 보강 필요

회원가입 시 기본 언어를 1L로 고정하는 매직 넘버 사용은 환경별 설정 불가·운영 중 변경 불가·데이터 마이그레이션 시 장애 위험을 키웁니다. 또한 languageDomainService.getLanguage(1L)이 null/예외를 반환할 경우 현재 흐름에서는 조용히 NPE가 터질 수 있습니다. 기본값은 설정값으로 주입하거나, getDefaultLanguage() 같은 도메인 메서드로 캡슐화하고, 조회 실패 시 명시적으로 예외를 던지도록 해주세요.

적용 예(개념안):

  • 설정: app.user.default-language-id 프로퍼티로 주입
  • 또는 도메인: languageDomainService.getDefaultLanguage() 제공
  • 가입 시: 조회 실패 시 의미 있는 예외(예: Language 관련 도메인 예외) 발생
- Language language = languageDomainService.getLanguage(1L); //기본적으로 1번 언어로 가입 시 세팅
+ // TODO: 설정 기반 혹은 도메인 메서드로 기본 언어 조회 + 예외 처리
+ Language language = languageDomainService.getDefaultLanguage(); // 존재하지 않으면 도메인 예외

Also applies to: 86-94

Comment on lines +20 to +23
Integer age,

@Schema(description = "언어 id", example = "1")
Long languageId
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

languageId/age 유효성 명시 및 널 처리 정책 확립 필요

  • 요청 스키마에 languageId가 추가되었지만 유효성 제약이 없어 잘못된 값(음수, 0 등) 입력을 허용합니다. 또한 필수 여부가 불명확합니다.
  • age 역시 음수 등을 허용할 여지가 있습니다.
  • 서비스 계층에서 languageId == null일 때 업데이트를 스킵하는지(현 행위 유지) 또는 기본 언어로 강제 설정하는지 정책 확인이 필요합니다.

아래와 같이 Bean Validation을 추가하고(널 허용 시 Positive만, 필수라면 NotNull+Positive), 컨트롤러/서비스에서 null 처리 분기 유지를 확인해 주세요.

적용 diff(선택 1: languageId 선택 입력, null 시 미변경):

 	@Schema(description = "나이", example = "28")
-	Integer age,
+	@jakarta.validation.constraints.PositiveOrZero
+	Integer age,
 
 	@Schema(description = "언어 id", example = "1")
-	Long languageId
+	@jakarta.validation.constraints.Positive
+	Long languageId

적용 diff(선택 2: languageId 필수 입력):

 	@Schema(description = "나이", example = "28")
-	Integer age,
+	@jakarta.validation.constraints.PositiveOrZero
+	Integer age,
 
 	@Schema(description = "언어 id", example = "1")
-	Long languageId
+	@jakarta.validation.constraints.NotNull
+	@jakarta.validation.constraints.Positive
+	Long languageId

서비스 레벨 null 처리 검증 스크립트:


🏁 Script executed:

#!/bin/bash
# ModifyUserInfoRequest.languageId 사용부에서 null 분기 여부 확인
rg -nC2 -P --type=java '\blanguageId\(\)' src/main/java | sed -n '1,200p'

Length of output: 5229


ModifyUserInfoRequest DTO에 유효성 제약 추가 필요

서비스 레이어의 모든 사용처(예: UserService, SubmissionService, DiscussionService 등)에서 request.languageId()를 null 체크 없이 바로 languageDomainService.getLanguage(...)에 전달하고 있어, null 또는 0 이하 값 입력 시 NPE 또는 잘못된 조회가 발생할 수 있습니다. 또한 age 필드도 음수 입력을 방지할 유효성 검증이 빠져 있습니다.

수정 제안:

  • languageId는 필수 입력이므로 @NotNull@Positive 애노테이션 추가
  • age는 선택 입력(널 허용)이지만 음수 방지를 위해 @PositiveOrZero 애노테이션 추가

해당 DTO 파일:

  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/request/ModifyUserInfoRequest.java

적용 예시(diff):

  @Schema(description = "나이", example = "28")
- Integer age,
+ @jakarta.validation.constraints.PositiveOrZero
+ Integer age,

  @Schema(description = "언어 id", example = "1")
- Long languageId
+ @jakarta.validation.constraints.NotNull
+ @jakarta.validation.constraints.Positive
+ Long languageId

— 이후 컨트롤러/서비스 레이어에서 @Valid 검증이 정상 동작하는지, 검증 실패 시 적절한 에러 응답이 내려가는지 확인 부탁드립니다.

📝 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
Integer age,
@Schema(description = "언어 id", example = "1")
Long languageId
@Schema(description = "나이", example = "28")
@jakarta.validation.constraints.PositiveOrZero
Integer age,
@Schema(description = "언어 id", example = "1")
@jakarta.validation.constraints.NotNull
@jakarta.validation.constraints.Positive
Long languageId
🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/request/ModifyUserInfoRequest.java
around lines 20-23, the DTO lacks validation: languageId is treated as required
by services but can be null/<=0 causing NPE or bad lookups, and age can be
negative. Add javax.validation annotations: mark languageId with @NotNull and
@Positive, and mark age (which remains nullable) with @PositiveOrZero; also add
the necessary imports for these annotations. After updating the DTO, ensure
controller methods accept @Valid for this request so validation triggers and
returns appropriate error responses.

Comment on lines +54 to +59
@Schema(description = "사용자가 선택한 언어. 기본적으로 1번 언어로 세팅됩니다", example = "1")
private final Language language;

@Schema(description = "사용자가 가입한 경로(자체/소셜)의 리스트를 보여줍니다", example = "[GOOGLE, GITHUB, EMAIL]")
private final List<AuthType> userAuthTypes;

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

DTO에서 JPA 엔티티(Language) 직접 노출 금지: ID 또는 전용 DTO로 교체 필요

Language 엔티티를 응답 DTO에 직접 담으면 LAZY 프록시 직렬화 문제, 과다한 필드 노출, 순환 참조 위험이 있습니다. 또한 @Schema example = "1"은 타입이 Language인 현재 정의와 충돌합니다. API 안정성과 성능을 위해 languageId(Long)로 대체하거나 LanguageResponse 같은 전용 응답 DTO를 사용하세요.

ID만 노출하는 간단한 변경 예:

- @Schema(description = "사용자가 선택한 언어. 기본적으로 1번 언어로 세팅됩니다", example = "1")
- private final Language language;
+ @Schema(description = "사용자가 선택한 언어 ID. 기본값은 시스템 설정에 따릅니다", example = "1")
+ private final Long languageId;

auth 타입 리스트는 그대로 유지 가능:

 @Schema(description = "사용자가 가입한 경로(자체/소셜)의 리스트를 보여줍니다", example = "[GOOGLE, GITHUB, EMAIL]")
 private final List<AuthType> userAuthTypes;
📝 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
@Schema(description = "사용자가 선택한 언어. 기본적으로 1번 언어로 세팅됩니다", example = "1")
private final Language language;
@Schema(description = "사용자가 가입한 경로(자체/소셜)의 리스트를 보여줍니다", example = "[GOOGLE, GITHUB, EMAIL]")
private final List<AuthType> userAuthTypes;
@Schema(description = "사용자가 선택한 언어 ID. 기본값은 시스템 설정에 따릅니다", example = "1")
private final Long languageId;
@Schema(description = "사용자가 가입한 경로(자체/소셜)의 리스트를 보여줍니다", example = "[GOOGLE, GITHUB, EMAIL]")
private final List<AuthType> userAuthTypes;

Comment on lines 60 to 66
@Builder
public UserInfoResponse(String username, String email, String nickname, UserRole userRole, Tier tier,
Integer age, String githubUrl, String blogUrl, String profileImageUrl, String introduction, boolean verified,
int totalSolvedCount) {
int totalSolvedCount,
Language language,
List<AuthType> userAuthTypes) {
this.username = username;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Builder 시그니처/할당 업데이트 필요(Language → languageId)

위 제안대로 languageId로 바꾸는 경우 Builder 파라미터와 필드 할당도 함께 수정되어야 합니다.

- int totalSolvedCount,
- Language language,
- List<AuthType> userAuthTypes) {
+ int totalSolvedCount,
+ Long languageId,
+ List<AuthType> userAuthTypes) {
    ...
-   this.language = language;
+   this.languageId = languageId;
    this.userAuthTypes = userAuthTypes;

Also applies to: 78-79

🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/UserInfoResponse.java
around lines 60-66 (and also update the corresponding constructor/assignments at
lines 78-79), the builder parameter and assignments still use Language language
while the field was renamed to languageId; update the builder signature to
accept Long languageId (or the appropriate type) and change the constructor body
to assign this.languageId = languageId (and any other occurrences in those
lines) so the constructor parameter and field assignment match the new
name/type.

Comment on lines +88 to 97
Language findLangauge = languageDomainService.getLanguage(request.languageId());

user.modifyUserInfo(
request.nickname(),
request.githubUrl(),
request.blogUrl(),
request.introduction(),
request.age());
request.age(),
findLangauge);

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

오타 및 널 처리 보강: findLangauge → findLanguage, languageId가 null일 때 안전 처리

변수명이 findLangauge로 오타입니다. 또한 request.languageId()가 null이면 현재는 NPE가 발생합니다(도메인 서비스 호출 시). null이면 기존 언어 유지가 의도라면 안전하게 처리하세요.

- Language findLangauge = languageDomainService.getLanguage(request.languageId());
+ Long languageId = request.languageId();
+ Language findLanguage = (languageId == null) ? null : languageDomainService.getLanguage(languageId);
   ...
-   request.age(),
-   findLangauge);
+   request.age(),
+   findLanguage);
📝 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
Language findLangauge = languageDomainService.getLanguage(request.languageId());
user.modifyUserInfo(
request.nickname(),
request.githubUrl(),
request.blogUrl(),
request.introduction(),
request.age());
request.age(),
findLangauge);
Long languageId = request.languageId();
Language findLanguage = (languageId == null)
? null
: languageDomainService.getLanguage(languageId);
user.modifyUserInfo(
request.nickname(),
request.githubUrl(),
request.blogUrl(),
request.introduction(),
request.age(),
findLanguage);
🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java
around lines 88 to 97, rename the misspelled variable findLangauge to
findLanguage and add a null-safe handling for request.languageId(): only call
languageDomainService.getLanguage(...) when request.languageId() is non-null,
otherwise obtain and pass the user's existing language (or null if the domain
allows keeping current value) into user.modifyUserInfo so no NPE occurs; ensure
the variable name is updated where used and that modifyUserInfo receives the
correct language reference.

Comment on lines +30 to +35
@Override
public List<UserAuthType> getUserAuthTypesByUser(User user) {
userAuthTypeJpaRepository.findUserAuthTypeByUser(user).stream()
.map(UserAuthType::getAuthType).toList();
return List.of();
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

반환 타입 불일치 및 잘못된 반환값(항상 빈 리스트) — 기능 불능 버그

  • 시그니처는 List<UserAuthType> 반환인데, 구현은 map(UserAuthType::getAuthType)List<AuthType>를 생성해 버립니다.
  • 더 나아가 해당 결과도 무시하고 List.of()를 반환하여 항상 빈 결과를 돌려줍니다.

이는 상위 서비스(UserDomainService#getUserAuthTypesByUser) 기능을 무력화합니다. 아래처럼 엔티티 리스트를 그대로 반환하도록 수정해 주세요.

 	@Override
 	public List<UserAuthType> getUserAuthTypesByUser(User user) {
-		userAuthTypeJpaRepository.findUserAuthTypeByUser(user).stream()
-			.map(UserAuthType::getAuthType).toList();
-		return List.of();
+		return userAuthTypeJpaRepository.findUserAuthTypeByUser(user);
 	}

검증 스크립트(간단 동작 확인: List.of()가 더 이상 존재하지 않는지 확인):

#!/bin/bash
rg -n 'getUserAuthTypesByUser\(User user\)' -n -C2 src/main/java
rg -nP 'return\s+List\.of\(\)\s*;' src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserAuthTypeRepositoryImpl.java
🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserAuthTypeRepositoryImpl.java
around lines 30-35, the method currently maps repository results to AuthType and
then ignores it, returning List.of() (always empty) which breaks functionality;
change the implementation to return the List<UserAuthType> obtained from
userAuthTypeJpaRepository.findUserAuthTypeByUser(user) (e.g., collect the stream
into a List and return it) and remove the incorrect map and the hardcoded
List.of() so the actual entity list is returned to callers.

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: 0

🧹 Nitpick comments (5)
src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java (3)

12-17: Locale-안전한 provider 정규화 및 enum(AuthType) 사용 고려

provider.toLowerCase()는 기본 JVM Locale에 따라 오동작할 수 있습니다(예: 터키어 i). 최소 변경으로 Locale.ROOT를 사용해 주세요. 더 나아가 문자열 대신 이미 사용 중인 AuthType enum으로 시그니처를 전환하면 오타·대소문자 이슈를 근본적으로 제거할 수 있습니다.

프로젝트 러닝(팩토리 내부 검증 생략)과 정합성을 고려하여 validation 추가는 권하지 않고, 입력 보장은 상위(Service/DTO)에서 계속하는 방향을 유지하는 게 좋아 보입니다.

적용 예시(최소 변경):

@@
-import java.util.UUID;
+import java.util.UUID;
+import java.util.Locale;
@@
-        return switch (provider.toLowerCase()) {
+        return switch (provider.toLowerCase(Locale.ROOT)) {

중간 규모 리팩터링(선택): createSocialUser(..., AuthType authType, Language language)로 변경 후 switch (authType) 사용.


23-26: GitHub URL 설정 중복 가능성

이 분기에서 githubUrl을 세팅하고, 서비스 레이어 updateGithubUrl(...)에서도 다시 user.setGithubUrl(...)를 호출합니다. 기능상 문제는 없지만 중복입니다. 한 곳으로 책임을 모으는 것이 깔끔합니다. 예: 팩토리에서는 URL을 세팅하지 않고 서비스의 updateGithubUrl에서만 일괄 처리(또는 서비스에서 “없을 때만 세팅”).

원하시면 중복 제거 방향(팩토리/서비스 중 택1)으로 구체 diff 드리겠습니다.


27-34: UserFactory default 분기: 미지원 provider 예외 처리로 통일 권장

CustomOAuth2UserService#createResponse가 지원되지 않는(provider) 경우에 예외를 던지는 반면, UserFactory.createSocialUser에서는 default 분기에서 일반 social 유저를 생성하고 있어 두 정책이 일치하지 않습니다. 잠재적 안전 사고를 방지하려면 팩토리에서도 미지원 provider를 예외로 처리하는 편이 좋습니다.

  • 대상 파일:
    src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java
    (switch 표현식의 default 분기, 약 27–34번째 줄)

제안하는 수정 예시:

--- a/src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java
+++ b/src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java
@@ -27,8 +27,8 @@ public static User createSocialUser(
             case "GOOGLE" -> User.googleUser(
                 response.getEmail(), response.getName(), nickname, UUID.randomUUID().toString(), language
             );
-            default -> User.socialUser(
-                response.getEmail(),
+            default -> throw new IllegalArgumentException(
+                "Unsupported provider: " + provider
+            );
         };

이렇게 변경하면 서비스 레이어와 팩토리 양쪽에서 미지원 provider에 대해 일관되게 예외를 던지게 되어, 예기치 않은 잘못된 사용자 생성 로직 실행을 예방할 수 있습니다.

src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (2)

8-10: 언어 도메인 의존 추가: OK. 캐싱 고려

Language/LanguageDomainService 의존 추가는 기능 목적과 일치합니다. 기본 언어 조회가 고정된 키(아래 1L) 기반이라면 LanguageDomainService#getLanguage에 캐시를 얹어도 좋습니다(예: @Cacheable("language")), 로그인 트래픽에서 불필요한 조회를 줄일 수 있습니다.


88-89: 하드코딩된 기본 언어 ID 제거 및 도메인 서비스 책임 위임

하드코딩된 1L 기본 언어 ID는 데이터 변경(마이그레이션, 시드 수정 등) 시 깨질 위험이 있습니다. 기본 언어 결정 로직을 도메인 서비스에 위임해 호출부를 단순화하고 유연성을 확보하세요.

주요 수정 지점:

  • src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java (라인 86)
  • src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (라인 88–89)

제안 diff:

// AuthService.java:86
-        Language language = languageDomainService.getLanguage(1L); // 기본 언어 하드코딩
+        Language language = languageDomainService.getDefaultLanguage(); // 도메인 서비스에 위임

// CustomOAuth2UserService.java:88
-        Language language = languageDomainService.getLanguage(1L); // 기본 언어 하드코딩
+        Language language = languageDomainService.getDefaultLanguage(); // 도메인 서비스에 위임

// CustomOAuth2UserService.java:89
         User newUser = UserFactory.createSocialUser(response, nickname, provider, language);
// → createSocialUser 시그니처 변경 불필요 (language 파라미터 유지)

도메인 서비스 예시:

// LanguageDomainService.java
public Language getDefaultLanguage() {
    // 1) 설정(Properties) 기반
    // return getLanguage(languageProperties.getDefaultId());
    // 2) 코드 기준(예: "en")
    // return getLanguageByCode("en");
    // 3) 기본값 미설정 시 예외 처리
    // throw new DefaultLanguageNotConfiguredException();
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ec27eed and af5b25e.

📒 Files selected for processing (2)
  • src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java (2 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: minjee2758
PR: ezcode-my/backend#43
File: src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java:77-89
Timestamp: 2025-06-10T02:13:33.890Z
Learning: 이 프로젝트에서는 입력 검증을 DTO 레벨에서 수행하고 있으며, User 엔티티의 팩토리 메서드에서는 별도의 입력 검증을 하지 않는다.
🧬 Code graph analysis (1)
src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (1)
src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java (1)
  • UserFactory (8-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (1)

38-38: DI 배선 문제 없음

languageDomainService 주입 추가는 정합적으로 보입니다.

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: 1

🧹 Nitpick comments (3)
src/test/java/org/ezcode/codetest/domain/user/UserDomainServiceTest.java (2)

4-5: Language/LanguageDomainService import 추가는 합리적입니다만, 테스트에서 도메인 서비스 의존은 최소화 권장

테스트 데이터(언어) 주입 목적이라면 도메인 서비스 호출 대신 고정값(Language 인스턴스)을 직접 생성하는 편이 단위 테스트 안정성과 속도에 유리합니다. 아래 다른 코멘트(라인 43-47, 169-172)와 함께 반영을 고려해 주세요.


169-172: 테스트 메서드 내 도메인 서비스 호출 제거 제안

해당 테스트는 “삭제된 유저” 분기만 검증하면 충분합니다. Language는 도메인 서비스에서 조회하지 말고 클래스 필드에 마련한 고정값을 재사용하세요. 단위 테스트가 외부 의존성에 흔들리지 않게 됩니다.

적용 예:

-        Language language = languageDomainService.getLanguage(1L);
         User deletedUser = new User("[email protected]","Aa12345**", "username",
-            "[email protected]", 100, Tier.CODER, UserRole.USER, true, true, "gitUrl.com", true, language);
+            "[email protected]", 100, Tier.CODER, UserRole.USER, true, true, "gitUrl.com", true, language);

상단 코멘트(라인 43-47)와 함께 반영 시, 해당 테스트 클래스는 도메인 서비스에 전혀 의존하지 않게 됩니다.

src/test/java/org/ezcode/codetest/application/problem/service/ProblemServiceTest.java (1)

84-89: 테스트 픽스처(언어/유저) 생성 중복 최소화 제안

Language language = new Language("java", "17", 30L);와 확장된 User 생성 패턴이 다른 테스트들과 중복됩니다. 테스트 가독성과 유지보수를 위해 간단한 팩토리/헬퍼를 도입하면 좋습니다(본 파일 또는 공용 테스트 유틸).

예시(참고용):

private static Language defaultLanguage() {
    return new Language("java", "17", 30L);
}

private static User defaultUserWith(Language lang) {
    return new User("test@test", "test", "test1", "test1", 13,
        Tier.NEWBIE, UserRole.ADMIN, false, false, "testurl", false, lang);
}

이후 테스트에서는 Language lang = defaultLanguage(); User user = defaultUserWith(lang);처럼 간결하게 작성할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between af5b25e and 9a33670.

📒 Files selected for processing (2)
  • src/test/java/org/ezcode/codetest/application/problem/service/ProblemServiceTest.java (3 hunks)
  • src/test/java/org/ezcode/codetest/domain/user/UserDomainServiceTest.java (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: minjee2758
PR: ezcode-my/backend#43
File: src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java:77-89
Timestamp: 2025-06-10T02:13:33.890Z
Learning: 이 프로젝트에서는 입력 검증을 DTO 레벨에서 수행하고 있으며, User 엔티티의 팩토리 메서드에서는 별도의 입력 검증을 하지 않는다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/test/java/org/ezcode/codetest/domain/user/UserDomainServiceTest.java (1)

60-61: User 생성자 시그니처 반영 OK

gitPushStatus와 마지막 인자로 language를 전달하도록 업데이트된 부분이 현재 시그니처에 맞게 잘 반영되어 있습니다.

src/test/java/org/ezcode/codetest/application/problem/service/ProblemServiceTest.java (2)

18-18: Language import 추가 적절

User 생성자 변경에 맞춰 Language를 사용하는 방향으로 테스트가 정리된 점 좋습니다.


62-62: 사소한 공백 라인 추가

동작에는 영향이 없으므로 그대로 두셔도 됩니다.

@minjee2758 minjee2758 merged commit 0f78457 into dev Aug 24, 2025
2 checks passed
@minjee2758 minjee2758 deleted the feature/user-language-select branch August 24, 2025 04:49
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.

2 participants