Skip to content

Conversation

@HyemIin
Copy link
Member

@HyemIin HyemIin commented Jun 6, 2025

작업 요약

signup API 구현

Issue Link

문제점 및 어려움

  • 패스워드 보안으로 위한 API 구현 필수(DB 내 계정 하드코딩 방지 목적)

해결 방안

Reference

Summary by CodeRabbit

  • 신규 기능

    • 매니저 회원가입을 위한 API 엔드포인트가 추가되었습니다.
    • 매니저 회원가입 요청 및 응답을 위한 DTO가 도입되었습니다.
    • 비밀번호 암호화 및 저장 기능이 추가되었습니다.
    • 닉네임으로 사용자 조회 기능이 추가되었습니다.
  • 개선 사항

    • Redis 연동을 위한 설정이 추가되었습니다.
    • 오류 코드가 보다 명확하고 일관된 형식으로 변경되었습니다.
    • 사용자 역할(ADMIN → MANAGER) 명칭이 변경되었습니다.
  • 보안

    • 비밀번호 암호화 방식(BCrypt) 적용을 위한 설정이 추가되었습니다.
    • 회원가입 관련 엔드포인트가 인증 없이 접근 가능하도록 허용되었습니다.

@HyemIin HyemIin self-assigned this Jun 6, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 6, 2025

"""

Walkthrough

이번 변경사항은 관리자 회원가입 기능을 위한 전체적인 시스템 확장을 포함합니다. Redis 연동, 비밀번호 암호화, 회원가입 요청/응답 DTO, 엔티티 및 저장소, 서비스와 컨트롤러 계층의 구현이 추가 및 수정되었습니다. 그 외, 에러 메시지 코드와 권한(Role) Enum도 일관성 있게 조정되었습니다.

Changes

파일/경로 그룹 변경 요약
build.gradle Redisson Spring Boot Starter 의존성 추가
.../global/config/RedisConfig.java RedisTemplate 설정용 신규 설정 클래스 추가
.../global/config/SecurityConfig.java 회원가입 API 엔드포인트 공개, BCryptPasswordEncoder 빈 추가
.../global/security/exception/ErrorMessage.java 에러 코드 문자열 일관성 있게 수정
.../user/controller/UserController.java 관리자 회원가입 POST /signup 엔드포인트 및 UserService 주입 추가
.../user/dto/ManagerSignupRequestDto.java 관리자 회원가입 요청 DTO 신설, 유효성 검증 및 엔티티 변환 메서드 포함
.../user/dto/ManagerSignupResponseDto.java 관리자 회원가입 응답 DTO 신설, User 엔티티 변환 팩토리 메서드 포함
.../user/entity/Role.java ADMIN → MANAGER로 Enum 상수 변경
.../user/entity/User.java password 필드 및 암호화 메서드 추가, 생성자 수정
.../user/repository/UserRepository.java 닉네임으로 User 조회 메서드 추가
.../user/service/UserService.java 관리자 회원가입 비즈니스 로직 메서드 추가, 중복 체크 및 비밀번호 암호화 포함

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant UserController
    participant UserService
    participant UserRepository
    participant PasswordEncoder

    Client->>UserController: POST /signup (ManagerSignupRequestDto)
    UserController->>UserService: signup(dto)
    UserService->>UserRepository: findByEmail(dto.email)
    UserService->>UserRepository: findByNickname(dto.nickname)
    UserService->>PasswordEncoder: encode(dto.password)
    UserService->>UserRepository: save(User)
    UserService-->>UserController: ManagerSignupResponseDto
    UserController-->>Client: 201 Created + ApiUtils.success()
Loading

Poem

🎉
새로운 길을 여는 회원가입,
비밀번호는 암호화로 꽁꽁!
Redis도 준비 완료,
에러코드도 더 또렷이,
MANAGER의 시대가 밝았네—
GTable의 내일을 향해
함께 달려가요! 🚀

"""


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9f039c8 and 1e1162c.

📒 Files selected for processing (1)
  • src/main/java/com/example/gtable/user/entity/User.java (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/example/gtable/user/entity/User.java
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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: 3

🧹 Nitpick comments (10)
build.gradle (1)

53-55:

Details

❓ Verification inconclusive

Redisson 의존성 추가 검토
Redisson Spring Boot Starter(org.redisson:redisson-spring-boot-starter:3.19.0)를 사용하기 위해서는 spring-boot-starter-data-redis가 자동 포함되는지 확인이 필요합니다. 누락 시 RedisTemplate 빈 생성 시 RedisConnectionFactory 구현체가 없을 수 있습니다.

아래 스크립트로 의존성 트리를 확인하거나, 필요 시 명시적으로 Spring Data Redis 스타터를 추가해주세요:

+// (선택) Redis 연동을 위한 Spring Data Redis 스타터
+implementation 'org.springframework.boot:spring-boot-starter-data-redis'

🏁 Script executed:

./gradlew dependencies --configuration runtimeClasspath | grep -C 2 redis

Length of output: 265


🚨 JAVA_HOME 오류로 의존성 확인 실패: Redisson 자동 포함 여부 재검증 필요
현재 sandbox 환경에서 JAVA_HOME 설정 오류로 spring-boot-starter-data-redis 의존성 자동 포함 여부를 확인할 수 없었습니다.
로컬 환경에서 아래 명령어를 실행해 Redis 관련 의존성 트리를 재검증해주세요:

export JAVA_HOME=/path/to/your/jdk
./gradlew dependencies --configuration runtimeClasspath | grep -C 2 redis

– 만약 spring-boot-starter-data-redis가 자동으로 포함되지 않는다면, build.gradle에 아래 라인을 추가해주세요:

+ implementation 'org.springframework.boot:spring-boot-starter-data-redis'
src/main/java/com/example/gtable/global/config/RedisConfig.java (1)

12-19: RedisTemplate 초기화 개선 제안
현재 키/값 직렬화만 설정되어 있으며, 해시 직렬화 설정과 afterPropertiesSet() 호출이 누락되어 있습니다. 해시 연산 사용 시 직렬화 문제를 방지하고, 설정을 완전히 반영하기 위해 아래와 같은 개선을 고려해주세요.

@@ public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
     template.setKeySerializer(new StringRedisSerializer());
     template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
+    // 해시 키/값 직렬화 설정
+    template.setHashKeySerializer(new StringRedisSerializer());
+    template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
+    // 설정 반영을 위한 초기화 호출
+    template.afterPropertiesSet();
     return template;
 }
src/main/java/com/example/gtable/user/repository/UserRepository.java (1)

11-11: 파라미터명 일관성을 확인해 주세요.

메서드명은 findByNickname이지만 파라미터명은 nickName으로 되어 있네요. 일관성을 위해 파라미터명을 nickname으로 변경하는 것이 어떨까요?

- Optional<User> findByNickname(String nickName);
+ Optional<User> findByNickname(String nickname);

아니면 기존 코드베이스의 네이밍 컨벤션을 따라야 한다면 현재 상태로도 괜찮습니다.

src/main/java/com/example/gtable/user/entity/User.java (2)

44-46: 생성자 파라미터 순서를 일관성 있게 정리해보시면 좋겠습니다

생성자에 password 파라미터가 추가되었는데, 파라미터 순서가 자연스럽지 않아 보입니다. email 다음에 password가 오는 것이 더 직관적일 것 같습니다.

-    public User(String email,String password, String nickname, String profileImage, SocialType socialType, Role role){
+    public User(String email, String password, String nickname, String profileImage, SocialType socialType, Role role){

또한 email 뒤에 공백이 누락되어 있네요.


70-72: encodePassword 메서드 설계가 깔끔합니다

패스워드 암호화 로직을 엔티티 내부에서 처리하도록 설계한 것이 좋습니다. 다만 한 가지 개선 제안이 있습니다:

메서드 호출 후 기존 평문 패스워드가 암호화된 값으로 대체되는데, 이미 암호화된 패스워드에 대해 중복 호출을 방지하는 안전장치가 있으면 더 견고할 것 같습니다.

 public void encodePassword(PasswordEncoder passwordEncoder) {
+    if (password == null || password.isEmpty()) {
+        throw new IllegalStateException("패스워드가 설정되지 않았습니다.");
+    }
     password = passwordEncoder.encode(password);
 }
src/main/java/com/example/gtable/user/controller/UserController.java (1)

32-41: 회원가입 엔드포인트 구현이 잘 되어있습니다

HTTP 상태코드 201(Created) 사용과 @Valid 어노테이션을 통한 요청 검증이 적절합니다. 다만 몇 가지 개선 사항을 제안드립니다:

  1. API 문서화: Swagger 어노테이션 추가 고려
  2. 예외 처리: 현재 GlobalExceptionHandler가 있지만, 회원가입 특화된 에러 메시지도 고려해볼 만합니다
+ @Operation(summary = "관리자 회원가입", description = "관리자 계정을 생성합니다")
+ @ApiResponses({
+     @ApiResponse(responseCode = "201", description = "회원가입 성공"),
+     @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터")
+ })
  @PostMapping("/signup")
  public ResponseEntity<?> signup(@RequestBody @Valid ManagerSignupRequestDto managerSignupRequestDto) {
src/main/java/com/example/gtable/user/service/UserService.java (1)

22-31: 회원가입 메서드의 전체적인 구조가 좋습니다

@Transactional 어노테이션 사용과 전체적인 플로우가 깔끔합니다. 다만 한 가지 개선 제안이 있습니다:

현재 검증 실패 시 로그가 남지 않아 디버깅이나 모니터링에 어려움이 있을 수 있습니다.

 @Transactional
 public ManagerSignupResponseDto signup(ManagerSignupRequestDto managerSignupRequestDto) {
+    log.info("관리자 회원가입 시도: email={}", managerSignupRequestDto.getEmail());
     validateEmailDuplicated(managerSignupRequestDto);
     validateNickNameDuplicated(managerSignupRequestDto.getNickname());
     User user = managerSignupRequestDto.toEntity();
     user.encodePassword(passwordEncoder);

+    log.info("관리자 회원가입 완료: userId={}", user.getId());
     return ManagerSignupResponseDto.fromEntity(userRepository.save(user));
 }
src/main/java/com/example/gtable/user/dto/ManagerSignupResponseDto.java (1)

26-34: fromEntity 팩토리 메서드가 잘 구현되었습니다

정적 팩토리 메서드 패턴을 사용하여 User 엔티티에서 DTO로의 변환이 깔끔하게 구현되었습니다. 빌더 패턴과 함께 사용하여 가독성도 좋네요.

다만 한 가지 고려사항이 있습니다: User 엔티티의 필드가 null일 가능성에 대한 방어 코드를 추가하면 더 안전할 것 같습니다.

 public static ManagerSignupResponseDto fromEntity(User user) {
+    if (user == null) {
+        throw new IllegalArgumentException("User 엔티티가 null입니다");
+    }
     return ManagerSignupResponseDto.builder()
         .id(user.getId())
         .email(user.getEmail())
         .nickname(user.getNickname())
         .role(user.getRole())
         .build();
 }
src/main/java/com/example/gtable/user/dto/ManagerSignupRequestDto.java (2)

18-22: 이메일 정규식을 좀 더 간단하고 읽기 쉽게 개선해보시면 어떨까요?

현재 정규식이 매우 복잡해서 유지보수와 가독성 면에서 어려움이 있을 수 있습니다. Spring Boot의 기본 @Email 어노테이션만으로도 충분할 것 같습니다.

다음과 같이 간소화하는 것을 고려해보세요:

-@Email(regexp = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@"
-	+ "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$")
+@Email

24-27: 비밀번호 정규식에 대한 명확한 설명이 있으면 좋겠습니다!

현재 정규식이 어떤 조건들을 검증하는지 주석이나 상수로 명시해주시면, 다른 개발자들이 이해하기 쉬울 것 같습니다.

다음과 같이 개선해보시는 건 어떨까요:

+// 숫자, 영문자, 특수문자 포함 8-20자
 @Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,20}")

또는 상수로 분리하여 재사용성을 높이는 방법도 좋습니다.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d890665 and 9f039c8.

📒 Files selected for processing (11)
  • build.gradle (1 hunks)
  • src/main/java/com/example/gtable/global/config/RedisConfig.java (1 hunks)
  • src/main/java/com/example/gtable/global/config/SecurityConfig.java (3 hunks)
  • src/main/java/com/example/gtable/global/security/exception/ErrorMessage.java (1 hunks)
  • src/main/java/com/example/gtable/user/controller/UserController.java (2 hunks)
  • src/main/java/com/example/gtable/user/dto/ManagerSignupRequestDto.java (1 hunks)
  • src/main/java/com/example/gtable/user/dto/ManagerSignupResponseDto.java (1 hunks)
  • src/main/java/com/example/gtable/user/entity/Role.java (1 hunks)
  • src/main/java/com/example/gtable/user/entity/User.java (4 hunks)
  • src/main/java/com/example/gtable/user/repository/UserRepository.java (1 hunks)
  • src/main/java/com/example/gtable/user/service/UserService.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/com/example/gtable/user/service/UserService.java (1)
src/main/java/com/example/gtable/global/security/exception/GlobalExceptionHandler.java (1)
  • Slf4j (23-105)
🔇 Additional comments (15)
src/main/java/com/example/gtable/global/config/RedisConfig.java (3)

1-9: import 구간 확인 완료
필요한 패키지가 적절히 임포트되어 있어 문제없습니다.


10-11: 클래스 선언 및 설정 어노테이션
@Configuration 어노테이션과 클래스 구조가 적절하게 정의되어 있습니다.


20-21: 클래스 종료부 확인
구조적으로 문제가 없으므로 이 부분은 그대로 두셔도 됩니다.

src/main/java/com/example/gtable/user/entity/Role.java (1)

10-10:

Details

❓ Verification inconclusive

ADMIN에서 MANAGER로의 변경에 대한 영향도를 확인해 주세요.

Role enum에서 ADMINMANAGER로 변경하신 것은 breaking change입니다. 다음 사항들을 꼼꼼히 확인해 주시기 바랍니다:

  1. 기존 데이터베이스에 ADMIN 역할로 저장된 사용자 데이터 마이그레이션
  2. 코드베이스 전체에서 Role.ADMIN을 참조하는 부분들의 업데이트
  3. 권한 체크 로직에서 ADMIN 역할을 검사하는 부분들

다음 스크립트로 ADMIN 참조를 찾아보겠습니다:


🏁 Script executed:

#!/bin/bash
# ADMIN 역할을 참조하는 코드를 찾습니다
rg -A 3 -B 3 "Role\.ADMIN|ADMIN" --type java

Length of output: 46


ADMIN 역할 참조 추가 검증 요청

현재 rg -A3 -B3 "Role\.ADMIN|ADMIN" --type java 실행 시 결과가 없어 보이나, 파일 형식 제한으로 놓친 부분이 있을 수 있습니다. 아래 스크립트로 전체 코드 및 리소스 파일을 대상으로 한 번 더 확인해 주세요.

• 위 스크립트 실행 후에도 참조가 전혀 없다면, 코드 레벨에서 ADMIN → MANAGER 변경 영향은 크지 않을 수 있습니다.
• 다만 기존 데이터베이스에 저장된 'ADMIN' 값이 있을 경우 JPA 매핑 오류가 발생할 수 있으므로, 마이그레이션 또는 enum-based converter 적용 여부를 수동으로 점검해 주세요.
• 권한 체크 로직(Express, Spring Security 등)에서 하드코딩된 ‘ADMIN’ 문자열이 있는지도 함께 확인이 필요합니다.

추가 검증 후 결과를 공유해 주시면, 최종 검토를 도와드리겠습니다.


🏁 Script executed:

#!/bin/bash
# 1) 모든 Java 파일에서 Role.ADMIN 참조 검색
rg -n "Role\.ADMIN" --glob '*.java'

# 2) SQL, XML, YAML 등 리소스 파일에서 'ADMIN' 문자열 검색
rg -n "ADMIN" --glob '*.{sql,xml,yml,yaml}'

# 3) 테스트 코드(Test) 폴더도 빠짐없이 검사
rg -n "Role\.ADMIN|ADMIN" --glob 'src/**/*Test.java'

Length of output: 285


Role.ADMIN 참조 없음 – 추가 검증 요청

  1. Role.ADMIN 및 문자열 "ADMIN"에 대한 전체 코드·리소스 검색 결과, Java 소스(.java), 테스트, SQL/XML/YAML 파일 어디에도 참조가 발견되지 않았습니다.
  2. 그러나 데이터베이스에 기존에 'ADMIN' 값이 저장된 레코드가 있을 경우 JPA 매핑 오류가 발생할 수 있으므로 반드시 확인이 필요합니다.
  3. 또한 애플리케이션 설정(.properties, .json, 스크립트 등)이나 보안 구성(예: Spring Security SpEL, @secured, hasRole) 에서 하드코딩된 "ADMIN" 문자열이 남아있지 않은지 점검해 주세요.

– DB 마이그레이션 또는 enum-based converter 적용 여부
– 설정 파일·스크립트 전반의 ADMIN 하드코딩 유무

위 항목을 검토하신 뒤 결과를 공유해 주시면 최종 승인 여부를 안내해 드리겠습니다.

src/main/java/com/example/gtable/global/config/SecurityConfig.java (3)

9-10: 적절한 import 추가입니다.

비밀번호 암호화를 위한 BCryptPasswordEncoder 관련 import가 정확히 추가되었네요.


56-57: 회원가입 엔드포인트 보안 설정이 올바릅니다.

/api/users/signup 엔드포인트를 permitAll에 추가하여 인증 없이 접근할 수 있도록 설정하신 것이 적절합니다. 회원가입 기능에 필수적인 설정이네요.


66-69: PasswordEncoder 빈 설정이 완벽합니다.

BCryptPasswordEncoder를 사용한 비밀번호 인코더 빈 설정이 보안 모범 사례를 잘 따르고 있습니다. 이렇게 하면 서비스 레이어에서 의존성 주입을 통해 안전하게 비밀번호를 암호화할 수 있겠네요.

src/main/java/com/example/gtable/global/security/exception/ErrorMessage.java (1)

10-17:

Details

✅ Verification successful

에러 코드 개선사항이 훌륭하지만 기존 참조 확인이 필요합니다.

에러 코드를 더 명확하고 일관성 있게 변경하신 것은 매우 좋은 개선사항이네요! "g001""global001", "a001""auth001" 등으로 변경하여 가독성이 크게 향상되었습니다.

다만 기존 코드에서 이 에러 코드들을 문자열로 직접 참조하는 부분이 있는지 확인이 필요합니다.

다음 스크립트로 기존 에러 코드 참조를 찾아보겠습니다:


🏁 Script executed:

#!/bin/bash
# 기존 에러 코드를 참조하는 부분을 찾습니다
rg -A 2 -B 2 '"g001"|"a001"|"t001"|"t002"' --type java

Length of output: 56


추가로 자바 파일뿐만 아니라 전체 코드베이스에서 기존 에러 코드(g001, a001, t001, t002) 참조 여부를 확인해보겠습니다.


🏁 Script executed:

#!/bin/bash
# 전체 레포지토리에서 기존 에러 코드 문자열 참조 검색
rg -n 'g001|a001|t001|t002'

Length of output: 29


기존 에러 코드 참조 없음 확인

전체 레포지토리에서 "g001", "a001", "t001", "t002"에 대한 직접 참조를 검색한 결과, 어떠한 매치도 발견되지 않았습니다.
따라서 에러 코드 변경("g001""global001", "a001""auth001", 등)은 코드베이스에 안전하게 적용된 것으로 보입니다.
추가 조치 불필요하며, 훌륭한 개선 감사합니다!

src/main/java/com/example/gtable/user/entity/User.java (1)

53-64:

Details

❌ Incorrect review comment

createUserWithId 메서드에서 password 처리가 누락되었습니다

기존 createUserWithId 정적 팩토리 메서드에서 새로 추가된 password 필드가 처리되지 않고 있습니다. 이 메서드를 사용하는 코드에서 password가 항상 null이 될 가능성이 있습니다.

메서드 사용처를 확인하여 password 파라미터 추가가 필요한지 검토해보시기 바랍니다.

다음 스크립트로 해당 메서드의 사용처를 확인해보겠습니다:


🏁 Script executed:

#!/bin/bash
# createUserWithId 메서드 사용처 확인
rg -A 3 -B 3 "createUserWithId"

Length of output: 1577


User 엔티티에 password 필드가 실제로 추가되었는지 확인하기 위해 다음 스크립트를 실행 부탁드립니다:


🏁 Script executed:

#!/bin/bash
# User.java에서 password 필드 검색
rg -n "password" src/main/java/com/example/gtable/user/entity/User.java

Length of output: 470


createUserWithId에서 password 누락은 의도된 설계입니다

현재 createUserWithId 메서드는 JWT 필터(JwtAuthorizationFilter) 내에서 토큰 기반 인증용으로만 사용되며, 실제 비밀번호는 사용되지 않습니다.
등록 시에는 별도의 가입용 팩토리 메서드(또는 빌더)를 통해 password를 설정하고 있으므로, 해당 정적 메서드에 password 파라미터를 추가할 필요가 없습니다.

Likely an incorrect or invalid review comment.

src/main/java/com/example/gtable/user/controller/UserController.java (1)

29-29: 의존성 주입이 올바르게 구성되었습니다

UserService를 final 필드로 선언하고 @RequiredArgsConstructor와 함께 사용하신 것이 좋습니다. 불변성을 보장하는 깔끔한 설계네요.

src/main/java/com/example/gtable/user/service/UserService.java (1)

39-42:

Details

✅ Verification successful

UserRepository에 findByNickname 메서드 확인이 필요합니다

코드에서 userRepository.findByNickname() 메서드를 사용하고 있는데, 이 메서드가 UserRepository에 정의되어 있는지 확인이 필요합니다.

다음 스크립트로 UserRepository의 메서드 정의를 확인해보겠습니다:


🏁 Script executed:

#!/bin/bash
# UserRepository에서 findByNickname 메서드 확인
ast-grep --pattern 'interface UserRepository {
  $$$
  findByNickname($$$) {
    $$$
  }
  $$$
}'

# 또는 메서드 선언 패턴으로 검색
rg -A 2 -B 2 "findByNickname"

Length of output: 1065


findByNickname 메서드 정의 확인 완료
UserRepository 인터페이스에 Optional<User> findByNickname(String nickName) 메서드가 정상적으로 정의되어 있어, 추가 조치가 필요하지 않습니다.

  • 파일: src/main/java/com/example/gtable/user/repository/UserRepository.java
    Optional<User> findByNickname(String nickName);
src/main/java/com/example/gtable/user/dto/ManagerSignupResponseDto.java (1)

16-24: DTO 필드 구성이 적절합니다

회원가입 응답에 필요한 핵심 정보들(id, email, nickname, role)만 포함되어 있고, 보안상 민감한 password 필드는 제외되어 있어서 좋습니다. Swagger 어노테이션도 적절하게 설정되어 있네요.

src/main/java/com/example/gtable/user/dto/ManagerSignupRequestDto.java (3)

1-12: 패키지 구조와 임포트가 깔끔하게 정리되어 있네요!

필요한 라이브러리들이 모두 적절히 임포트되어 있고, 패키지 구조도 전형적인 DTO 레이어 패턴을 잘 따르고 있습니다.


14-16: 클래스 구조가 표준 DTO 패턴을 잘 따르고 있습니다!

Lombok 어노테이션을 활용해서 보일러플레이트 코드를 효과적으로 줄인 점이 좋습니다.


29-32: 닉네임 검증 로직이 한국어와 영어를 모두 지원해서 좋네요!

2-12자 길이 제한과 함께 한글과 영문을 모두 허용하는 정규식이 적절해 보입니다.

@Column(nullable = false, unique = true)
private String email; // 카카오 이메일

private String password; // 관리자 패스워드
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

password 필드에 대한 데이터베이스 제약조건 고려가 필요합니다

관리자 계정의 password 필드는 보안상 중요한 데이터입니다. 현재 어떤 제약조건도 없이 선언되어 있는데, 다음 사항들을 고려해보시는 것이 좋겠습니다:

  • @Column(nullable = false) 어노테이션 추가로 NULL 값 방지
  • 소셜 로그인 사용자와 관리자 계정을 구분하기 위한 설계 검토
-    private String password; // 관리자 패스워드
+    @Column(nullable = false)
+    private String password; // 관리자 패스워드
🤖 Prompt for AI Agents
In src/main/java/com/example/gtable/user/entity/User.java at line 29, the
password field lacks database constraints. Add the @Column(nullable = false)
annotation to prevent null values for security. Additionally, review the design
to distinguish between social login users and admin accounts, possibly by adding
a field or mechanism to differentiate user types.

Comment on lines +32 to +43
private void validateEmailDuplicated(ManagerSignupRequestDto managerSignupRequestDto) {
userRepository.findByEmail(managerSignupRequestDto.getEmail()).ifPresent(member -> {
throw new IllegalArgumentException();
}
);
}
private void validateNickNameDuplicated(String nickName) {
userRepository.findByNickname(nickName).ifPresent(member -> {
throw new IllegalArgumentException();
}
);
}
Copy link

@coderabbitai coderabbitai bot Jun 6, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

중복 검증 로직에서 예외 메시지 개선이 필요합니다

현재 IllegalArgumentException을 메시지 없이 던지고 있어서, 클라이언트에서 구체적인 오류 원인을 파악하기 어렵습니다. GlobalExceptionHandler에서 처리하더라도 명확한 메시지가 있으면 더 좋겠습니다.

 private void validateEmailDuplicated(ManagerSignupRequestDto managerSignupRequestDto) {
     userRepository.findByEmail(managerSignupRequestDto.getEmail()).ifPresent(member -> {
-            throw new IllegalArgumentException();
+            throw new IllegalArgumentException("이미 사용 중인 이메일입니다: " + managerSignupRequestDto.getEmail());
         }
     );
 }
 private void validateNickNameDuplicated(String nickName) {
     userRepository.findByNickname(nickName).ifPresent(member -> {
-            throw new IllegalArgumentException();
+            throw new IllegalArgumentException("이미 사용 중인 닉네임입니다: " + nickName);
         }
     );
 }

이렇게 하면 API 응답에서도 더 명확한 오류 메시지를 제공할 수 있습니다.

📝 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
private void validateEmailDuplicated(ManagerSignupRequestDto managerSignupRequestDto) {
userRepository.findByEmail(managerSignupRequestDto.getEmail()).ifPresent(member -> {
throw new IllegalArgumentException();
}
);
}
private void validateNickNameDuplicated(String nickName) {
userRepository.findByNickname(nickName).ifPresent(member -> {
throw new IllegalArgumentException();
}
);
}
private void validateEmailDuplicated(ManagerSignupRequestDto managerSignupRequestDto) {
userRepository.findByEmail(managerSignupRequestDto.getEmail()).ifPresent(member -> {
throw new IllegalArgumentException("이미 사용 중인 이메일입니다: " + managerSignupRequestDto.getEmail());
}
);
}
private void validateNickNameDuplicated(String nickName) {
userRepository.findByNickname(nickName).ifPresent(member -> {
throw new IllegalArgumentException("이미 사용 중인 닉네임입니다: " + nickName);
}
);
}
🤖 Prompt for AI Agents
In src/main/java/com/example/gtable/user/service/UserService.java around lines
32 to 43, the IllegalArgumentException thrown in validateEmailDuplicated and
validateNickNameDuplicated methods lacks descriptive messages. Update these
exceptions to include clear, specific error messages indicating that the email
or nickname is already in use, so clients and logs can better understand the
cause of the error.

Copy link
Member Author

Choose a reason for hiding this comment

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

Exception 처리 관련 정책이 명확하게 결정되면 수정 예정

Copy link

Choose a reason for hiding this comment

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

@HyemIin 좋은 접근 방식이네요! 예외 처리 정책을 먼저 수립한 후 일관성 있게 적용하는 것이 코드베이스 전체의 품질과 유지보수성에 더 도움이 될 것 같습니다. 정책이 결정되면 그때 함께 개선해보시면 되겠습니다. 👍

Comment on lines +34 to +43
public User toEntity() {
return User.builder()
.email(email)
.password(password)
.nickname(nickname)
.socialType(SocialType.KAKAO)
.role(Role.MANAGER)
.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

⚠️ Potential issue

보안과 설계 관점에서 몇 가지 중요한 개선사항이 있습니다!

현재 구현에서 발견된 주요 이슈들:

  1. 보안 이슈: 비밀번호가 평문으로 전달되고 있습니다
  2. 설계 이슈: 관리자 회원가입에서 SocialType.KAKAO가 하드코딩되어 있는 것이 적절하지 않아 보입니다
  3. 책임 분리: DTO에서 비즈니스 로직(역할 할당)을 결정하고 있습니다

다음과 같은 개선을 제안드립니다:

 public User toEntity() {
     return User.builder()
         .email(email)
-        .password(password)
+        .password(password) // 서비스 레이어에서 인코딩 처리
         .nickname(nickname)
-        .socialType(SocialType.KAKAO)
+        .socialType(SocialType.NONE) // 또는 적절한 관리자용 타입
         .role(Role.MANAGER)
         .build();
 }

비밀번호 인코딩은 서비스 레이어에서 처리하는 것이 더 안전하고 적절합니다.

📝 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 User toEntity() {
return User.builder()
.email(email)
.password(password)
.nickname(nickname)
.socialType(SocialType.KAKAO)
.role(Role.MANAGER)
.build();
}
public User toEntity() {
return User.builder()
.email(email)
.password(password) // 서비스 레이어에서 인코딩 처리
.nickname(nickname)
.socialType(SocialType.NONE) // 또는 적절한 관리자용 타입
.role(Role.MANAGER)
.build();
}
🤖 Prompt for AI Agents
In src/main/java/com/example/gtable/user/dto/ManagerSignupRequestDto.java around
lines 34 to 43, the toEntity method currently passes the password in plain text,
hardcodes SocialType.KAKAO, and assigns the role within the DTO, mixing business
logic with data transfer. To fix this, remove password encoding and role
assignment from the DTO; instead, have the service layer handle password
encoding and role setting. Also, avoid hardcoding SocialType in the DTO by
passing it as a parameter or setting it in the service layer to maintain
separation of concerns and improve security.

비밀번호 DB 저장 시 notnull 제약조건 추가
@HyemIin HyemIin merged commit bec4b50 into develop Jun 6, 2025
1 check passed
@HyemIin HyemIin deleted the feature/#20-회원가입구현 branch June 6, 2025 13:07
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