-
Notifications
You must be signed in to change notification settings - Fork 3
Feature/user language select #180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,6 +17,9 @@ public record ModifyUserInfoRequest( | |||||||||||||||||||||||||
| String introduction, | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @Schema(description = "나이", example = "28") | ||||||||||||||||||||||||||
| Integer age | ||||||||||||||||||||||||||
| Integer age, | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @Schema(description = "언어 id", example = "1") | ||||||||||||||||||||||||||
| Long languageId | ||||||||||||||||||||||||||
|
Comment on lines
+20
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainlanguageId/age 유효성 명시 및 널 처리 정책 확립 필요
아래와 같이 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 등)에서 수정 제안:
해당 DTO 파일:
적용 예시(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— 이후 컨트롤러/서비스 레이어에서 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,10 @@ | ||||||||||||||||||||||
| package org.ezcode.codetest.application.usermanagement.user.dto.response; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import org.ezcode.codetest.domain.language.model.entity.Language; | ||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.entity.User; | ||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.enums.AuthType; | ||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.enums.Tier; | ||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.enums.UserRole; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -47,10 +51,18 @@ public class UserInfoResponse { | |||||||||||||||||||||
| @Schema(description = "사용자가 푼 문제 총 개수", example = "1235") | ||||||||||||||||||||||
| private final int totalSolvedCount; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Schema(description = "사용자가 선택한 언어. 기본적으로 1번 언어로 세팅됩니다", example = "1") | ||||||||||||||||||||||
| private final Language language; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Schema(description = "사용자가 가입한 경로(자체/소셜)의 리스트를 보여줍니다", example = "[GOOGLE, GITHUB, EMAIL]") | ||||||||||||||||||||||
| private final List<AuthType> userAuthTypes; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
Comment on lines
+54
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion DTO에서 JPA 엔티티(Language) 직접 노출 금지: ID 또는 전용 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
Suggested change
|
||||||||||||||||||||||
| @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; | ||||||||||||||||||||||
|
Comment on lines
60
to
66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Builder 시그니처/할당 업데이트 필요(Language → languageId) 위 제안대로 - 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 |
||||||||||||||||||||||
| this.email = email; | ||||||||||||||||||||||
| this.nickname = nickname; | ||||||||||||||||||||||
|
|
@@ -63,6 +75,8 @@ public UserInfoResponse(String username, String email, String nickname, UserRole | |||||||||||||||||||||
| this.userRole = userRole; | ||||||||||||||||||||||
| this.verified = verified; | ||||||||||||||||||||||
| this.totalSolvedCount = totalSolvedCount; | ||||||||||||||||||||||
| this.language = language; | ||||||||||||||||||||||
| this.userAuthTypes = userAuthTypes; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| public static UserInfoResponse fromEntity(User user) { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,12 +3,15 @@ | |||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDateTime; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.temporal.ChronoUnit; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.stream.Collectors; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.application.usermanagement.user.dto.response.GrantAdminRoleResponse; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.application.usermanagement.user.dto.response.UserDailySolvedHistoryResponse; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.application.usermanagement.user.dto.response.UserProfileImageResponse; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.application.usermanagement.user.dto.response.UserReviewTokenResponse; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.application.usermanagement.user.model.UsersByWeek; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.language.model.entity.Language; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.language.service.LanguageDomainService; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.submission.dto.DailyCorrectCount; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.submission.dto.WeeklySolveCount; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.application.usermanagement.user.dto.request.ChangeUserPasswordRequest; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -23,6 +26,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.exception.code.UserExceptionCode; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.entity.AuthUser; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.entity.User; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.entity.UserAuthType; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.enums.AuthType; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.model.enums.UserRole; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.ezcode.codetest.domain.user.service.MailService; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -46,6 +50,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||
| public class UserService { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private final UserDomainService userDomainService; | ||||||||||||||||||||||||||||||||||||||||||||||
| private final LanguageDomainService languageDomainService; | ||||||||||||||||||||||||||||||||||||||||||||||
| private final SubmissionDomainService submissionDomainService; | ||||||||||||||||||||||||||||||||||||||||||||||
| private final RedisTemplate<String, String> redisTemplate; | ||||||||||||||||||||||||||||||||||||||||||||||
| private final S3Uploader s3Uploader; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -55,6 +60,9 @@ public UserInfoResponse getUserInfo(AuthUser authUser) { | |||||||||||||||||||||||||||||||||||||||||||||
| log.info("authUserEmail: {}, authUserID : {}", authUser.getEmail(), authUser.getId()); | ||||||||||||||||||||||||||||||||||||||||||||||
| User user = userDomainService.getUserById(authUser.getId()); | ||||||||||||||||||||||||||||||||||||||||||||||
| int userSubmissionCount = submissionDomainService.findSubmissionCountByUserId(user.getId()); | ||||||||||||||||||||||||||||||||||||||||||||||
| List<UserAuthType> userAuthTypes = userDomainService.getUserAuthTypesByUser(user); | ||||||||||||||||||||||||||||||||||||||||||||||
| List<AuthType> authTypes = userAuthTypes.stream() | ||||||||||||||||||||||||||||||||||||||||||||||
| .map(UserAuthType::getAuthType).toList(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| return UserInfoResponse.builder() | ||||||||||||||||||||||||||||||||||||||||||||||
| .username(user.getUsername()) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -69,19 +77,23 @@ public UserInfoResponse getUserInfo(AuthUser authUser) { | |||||||||||||||||||||||||||||||||||||||||||||
| .tier(user.getTier()) | ||||||||||||||||||||||||||||||||||||||||||||||
| .verified(user.isVerified()) | ||||||||||||||||||||||||||||||||||||||||||||||
| .totalSolvedCount(userSubmissionCount) | ||||||||||||||||||||||||||||||||||||||||||||||
| .language(user.getLanguage()) | ||||||||||||||||||||||||||||||||||||||||||||||
| .userAuthTypes(authTypes) | ||||||||||||||||||||||||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @Transactional | ||||||||||||||||||||||||||||||||||||||||||||||
| public UserInfoResponse modifyUserInfo(AuthUser authUser, ModifyUserInfoRequest request, MultipartFile image) { | ||||||||||||||||||||||||||||||||||||||||||||||
| User user = userDomainService.getUserById(authUser.getId()); | ||||||||||||||||||||||||||||||||||||||||||||||
| Language findLangauge = languageDomainService.getLanguage(request.languageId()); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| user.modifyUserInfo( | ||||||||||||||||||||||||||||||||||||||||||||||
| request.nickname(), | ||||||||||||||||||||||||||||||||||||||||||||||
| request.githubUrl(), | ||||||||||||||||||||||||||||||||||||||||||||||
| request.blogUrl(), | ||||||||||||||||||||||||||||||||||||||||||||||
| request.introduction(), | ||||||||||||||||||||||||||||||||||||||||||||||
| request.age()); | ||||||||||||||||||||||||||||||||||||||||||||||
| request.age(), | ||||||||||||||||||||||||||||||||||||||||||||||
| findLangauge); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+88
to
97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 오타 및 널 처리 보강: findLangauge → findLanguage, languageId가 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| if (image != null && !image.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||
| String profileImageUrl = uploadProfileImage(image); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -105,6 +117,7 @@ public UserInfoResponse modifyUserInfo(AuthUser authUser, ModifyUserInfoRequest | |||||||||||||||||||||||||||||||||||||||||||||
| .githubUrl(user.getGithubUrl()) | ||||||||||||||||||||||||||||||||||||||||||||||
| .userRole(user.getRole()) | ||||||||||||||||||||||||||||||||||||||||||||||
| .tier(user.getTier()) | ||||||||||||||||||||||||||||||||||||||||||||||
| .language(user.getLanguage()) | ||||||||||||||||||||||||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,4 +27,11 @@ public List<AuthType> getUserAuthType(User user) { | |
| .map(UserAuthType::getAuthType).toList(); | ||
| } | ||
|
|
||
| @Override | ||
| public List<UserAuthType> getUserAuthTypesByUser(User user) { | ||
| userAuthTypeJpaRepository.findUserAuthTypeByUser(user).stream() | ||
| .map(UserAuthType::getAuthType).toList(); | ||
| return List.of(); | ||
| } | ||
|
Comment on lines
+30
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 반환 타입 불일치 및 잘못된 반환값(항상 빈 리스트) — 기능 불능 버그
이는 상위 서비스( @Override
public List<UserAuthType> getUserAuthTypesByUser(User user) {
- userAuthTypeJpaRepository.findUserAuthTypeByUser(user).stream()
- .map(UserAuthType::getAuthType).toList();
- return List.of();
+ return userAuthTypeJpaRepository.findUserAuthTypeByUser(user);
}검증 스크립트(간단 동작 확인: #!/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 |
||
|
|
||
| } | ||
There was a problem hiding this comment.
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()제공Also applies to: 86-94