[feat] 회원가입 api 개발#29
Hidden character warning
Conversation
수정된 User, Alias 도메인의 구성에 맞도록 기존 테스트 코드 수정
## Walkthrough
회원가입 API가 도입되어 사용자 회원가입 요청을 처리하는 전체적인 흐름이 구현되었습니다. 유효성 검증이 적용된 요청 객체, 도메인 및 JPA 매핑, 예외 처리, 테스트 코드 등 회원가입 기능을 위한 컨트롤러부터 서비스, 어댑터, 테스트까지 일관되게 추가 및 수정되었습니다.
## Changes
| 파일/그룹 | 변경 요약 |
|---|---|
| `src/main/java/konkuk/thip/common/exception/code/ErrorCode.java` | `ALIAS_NOT_FOUND` 오류 코드 추가 및 `API_BAD_REQUEST` 코드 변경 |
| `src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java` | 회원가입 API 엔드포인트 및 의존성 추가 |
| `src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java` | 회원가입 요청 DTO, 유효성 검증 및 Lombok 적용, toCommand() 메서드 추가 |
| `src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java` | 회원가입 응답 DTO 신규 생성 |
| `src/main/java/konkuk/thip/user/adapter/in/web/response/DummyResponse.java` | DummyResponse 클래스 삭제 |
| `src/main/java/konkuk/thip/user/adapter/out/jpa/AliasJpaEntity.java`,<br>`src/main/java/konkuk/thip/user/domain/Alias.java` | `imageUrl`, `color` 필드 추가 |
| `src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java`,<br>`src/main/java/konkuk/thip/user/domain/User.java` | `email` 필드 추가, User에 `withoutId` 팩토리 메서드 추가 |
| `src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java` | Alias ↔ AliasJpaEntity 매핑에 `imageUrl`, `color` 필드 추가 |
| `src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java` | User ↔ UserJpaEntity 매핑에 `email` 필드 추가 |
| `src/main/java/konkuk/thip/user/adapter/out/persistence/AliasCommandPersistenceAdapter.java` | AliasCommandPort 구현체 신규 추가 및 예외 처리 구현 |
| `src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java` | User 저장 구현, AliasJpaRepository 의존성 추가 및 예외 처리, 필드명 변경 |
| `src/main/java/konkuk/thip/user/application/port/in/UserSignupUseCase.java`,<br>`src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java` | 회원가입 유스케이스 인터페이스 및 명령 DTO 추가 |
| `src/main/java/konkuk/thip/user/application/port/out/AliasCommandPort.java` | AliasCommandPort 인터페이스 신규 생성 |
| `src/main/java/konkuk/thip/user/application/port/out/UserCommandPort.java` | User 저장 메서드 추가 |
| `src/main/java/konkuk/thip/user/application/service/UserSignupService.java` | 회원가입 서비스 구현체 신규 생성 |
| `src/main/java/konkuk/thip/user/application/port/in/DummyUseCase.java`,<br>`src/main/java/konkuk/thip/user/application/service/UserService.java` | Dummy 인터페이스 및 서비스 삭제 |
| `src/test/java/konkuk/thip/user/adapter/in/web/UserCommandControllerTest.java` | 회원가입 API 통합 테스트 및 유효성 검증 테스트 추가 |
| `src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java`,<br>`src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java`,<br>`src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java`,<br>`src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java`,<br>`src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java` | Alias, User 엔티티의 신규 필드(`imageUrl`, `color`, `email`) 반영, 패키지/임포트 정리 등 테스트 코드 수정 |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant Client
participant Controller as UserCommandController
participant Service as UserSignupService
participant AliasPort as AliasCommandPort
participant UserPort as UserCommandPort
participant DB
Client->>Controller: POST /users/signup (UserSignupRequest)
Controller->>Service: signup(UserSignupCommand)
Service->>AliasPort: findById(aliasId)
AliasPort->>DB: find Alias by aliasId
DB-->>AliasPort: AliasJpaEntity or Exception
AliasPort-->>Service: Alias
Service->>UserPort: save(User)
UserPort->>DB: save UserJpaEntity
DB-->>UserPort: userId
UserPort-->>Service: userId
Service-->>Controller: userId
Controller-->>Client: BaseResponse<UserSignupResponse>Assessment against linked issues
Assessment against linked issues: Out-of-scope changes해당 변경사항 중 범위를 벗어난 코드는 발견되지 않았습니다. Suggested labels
Suggested reviewers
Poem
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java (1)
50-50: 테스트 데이터 일관성을 위한 상수 사용을 고려해보세요.여러 테스트 파일에서 동일한 테스트 데이터를 사용하고 있습니다. 테스트 데이터 일관성과 유지보수성을 위해 공통 상수나 테스트 유틸리티 클래스 사용을 고려해보세요.
src/main/java/konkuk/thip/user/domain/User.java (1)
23-32: 정적 팩토리 메서드 패턴을 잘 활용했습니다.도메인 객체 생성 의도를 명확히 표현하는 좋은 패턴입니다. 다만
id(null)설정이 명시적으로 필요한지 검토해보세요.public static User withoutId(String email, String nickname, String imageUrl, String userRole, Long aliasId) { return User.builder() - .id(null) .email(email) .nickname(nickname) .imageUrl(imageUrl) .userRole(userRole) .aliasId(aliasId) .build(); }src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java (1)
52-56: 테스트 데이터 상수화 고려새로운 필드들이 적절히 추가되었습니다. 다만 테스트 데이터의 일관성을 위해 하드코딩된 값들을 상수로 분리하는 것을 고려해보세요.
+ private static final String TEST_IMAGE_URL = "test-image-url"; + private static final String TEST_COLOR = "red"; + private static final String TEST_ALIAS_VALUE = "칭호";Also applies to: 96-100
src/main/java/konkuk/thip/user/adapter/out/jpa/AliasJpaEntity.java (1)
24-28: 새 필드 추가 적절이미지 URL과 색상 필드가 적절히 추가되었습니다.
imageUrl을 TEXT 타입으로,color를 10자로 제한한 것이 합리적입니다.다만 색상 필드에 대한 유효성 검증 (예: 헥스 코드 형식, 색상명 제한 등)을 도메인 레벨에서 고려해보세요.
src/main/java/konkuk/thip/user/application/service/UserSignupService.java (1)
16-16: 사용자 역할 상수화 고려하드코딩된 역할 값을 설정 파일이나 별도 상수 클래스로 관리하는 것을 고려해보세요.
- private static final String NORMAL_USER_ROLE = "일반유저";대신
UserRoleenum이나 설정 파일에서 관리하면 더 유연합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
src/main/java/konkuk/thip/common/exception/code/ErrorCode.java(1 hunks)src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java(1 hunks)src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java(1 hunks)src/main/java/konkuk/thip/user/adapter/in/web/response/DummyResponse.java(0 hunks)src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/jpa/AliasJpaEntity.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java(2 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/AliasCommandPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/user/application/port/in/DummyUseCase.java(0 hunks)src/main/java/konkuk/thip/user/application/port/in/UserSignupUseCase.java(1 hunks)src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java(1 hunks)src/main/java/konkuk/thip/user/application/port/out/AliasCommandPort.java(1 hunks)src/main/java/konkuk/thip/user/application/port/out/UserCommandPort.java(1 hunks)src/main/java/konkuk/thip/user/application/service/UserService.java(0 hunks)src/main/java/konkuk/thip/user/application/service/UserSignupService.java(1 hunks)src/main/java/konkuk/thip/user/domain/Alias.java(1 hunks)src/main/java/konkuk/thip/user/domain/User.java(2 hunks)src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java(2 hunks)src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java(3 hunks)src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java(2 hunks)src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java(3 hunks)src/test/java/konkuk/thip/user/adapter/in/web/UserCommandControllerTest.java(1 hunks)src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java(3 hunks)
💤 Files with no reviewable changes (3)
- src/main/java/konkuk/thip/user/adapter/in/web/response/DummyResponse.java
- src/main/java/konkuk/thip/user/application/port/in/DummyUseCase.java
- src/main/java/konkuk/thip/user/application/service/UserService.java
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java (1)
src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java (1)
Getter(12-28)
src/main/java/konkuk/thip/user/adapter/out/persistence/AliasCommandPersistenceAdapter.java (2)
src/main/java/konkuk/thip/common/exception/EntityNotFoundException.java (1)
EntityNotFoundException(5-10)src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java (1)
Repository(14-31)
🔇 Additional comments (34)
src/main/java/konkuk/thip/user/domain/Alias.java (1)
15-17: 새로운 도메인 필드 추가가 깔끔합니다.
imageUrl과color필드가 기존 패턴을 잘 따르고 있으며, 도메인 엔티티의 책임에 적합합니다.src/main/java/konkuk/thip/user/application/port/out/UserCommandPort.java (2)
3-3: 적절한 import 추가입니다.User 도메인 엔티티 import가 올바르게 추가되었습니다.
7-7: 깔끔한 포트 인터페이스 설계입니다.
save메서드 시그니처가 명확하고, Long 타입 반환으로 저장된 사용자 ID를 반환하는 것이 직관적입니다.src/main/java/konkuk/thip/user/application/port/out/AliasCommandPort.java (1)
1-8: 잘 설계된 포트 인터페이스입니다.단일 책임 원칙을 잘 따르고 있으며, 헥사고날 아키텍처 패턴에 맞게 구현되었습니다.
findById메서드 시그니처도 명확합니다.src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1)
20-24: 에러 코드 추가가 기존 패턴을 잘 따릅니다.
- HTTP 상태 코드, 에러 코드 번호, 한글 메시지가 모두 적절합니다
- 비즈니스 예외 범위(60000+)에 맞게 번호가 할당되었습니다
- 주석 구조도 기존 스타일과 일관됩니다
src/main/java/konkuk/thip/user/application/port/in/UserSignupUseCase.java (1)
5-8: 헥사고날 아키텍처 패턴을 잘 따른 입력 포트 인터페이스입니다.단일 책임을 가진 명확한 인터페이스 설계로, 회원가입 유스케이스의 경계를 명확히 정의하고 있습니다.
src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java (1)
6-11: 간단하고 명확한 응답 DTO입니다.회원가입 성공 시 필요한 최소한의 정보만을 포함하여 응답 구조가 깔끔합니다.
src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java (2)
1-1: 패키지 구조 개선이 잘 이루어졌습니다.도메인별 패키지 구조로 변경되어 코드 구조가 더 명확해졌습니다.
41-45: 테스트 데이터 생성이 도메인 모델 변경사항을 잘 반영했습니다.새로 추가된
imageUrl과color필드가 적절히 포함되어 있습니다.src/main/java/konkuk/thip/user/domain/User.java (1)
13-13: 도메인 모델 확장이 적절합니다.회원가입 기능을 위한 email 필드 추가가 잘 이루어졌습니다.
src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java (2)
1-1: 패키지 구조 개선이 잘 이루어졌습니다.도메인별 패키지 구조로 변경되어 코드 구조가 더 명확해졌습니다.
54-58: 테스트 데이터 생성이 도메인 모델 변경사항을 잘 반영했습니다.새로 추가된
imageUrl과color필드가 적절히 포함되어 있으며, 다른 테스트 파일들과 일관된 패턴을 따르고 있습니다.src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java (1)
6-15: 깔끔한 Command 객체 구현입니다.헥사고날 아키텍처의 port-in 패턴을 잘 따르고 있으며, 필요한 필드들이 적절히 정의되어 있습니다. Lombok을 활용한 간결한 구현이 좋습니다.
src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java (2)
13-14: 새로운 필드 매핑이 올바르게 추가되었습니다.toJpaEntity 메서드에 imageUrl과 color 필드가 적절히 추가되었습니다.
22-23: 도메인 엔티티 매핑도 일관성 있게 구현되었습니다.toDomainEntity 메서드에도 동일한 필드들이 추가되어 양방향 매핑이 일관성 있게 유지되고 있습니다.
src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java (3)
1-1: 패키지 선언이 올바르게 수정되었습니다.잘못된 패키지 경로가 올바르게 수정되었습니다.
34-35: 새로운 Alias 필드들이 테스트에 적절히 반영되었습니다.imageUrl과 color 필드가 테스트 데이터에 추가되어 도메인 모델 변경사항이 잘 반영되었습니다.
40-41: 사용자 엔티티의 새로운 필드가 테스트에 포함되었습니다.email 필드 추가와 nickname 변경이 적절히 반영되었습니다.
src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java (4)
1-1: 패키지 선언이 올바르게 수정되었습니다.패키지 경로가 정확하게 수정되었습니다.
51-55: createUser 메서드의 Alias 엔티티 생성이 업데이트되었습니다.새로운 필드들(imageUrl, color)이 일관성 있게 추가되었습니다.
60-60: 사용자 엔티티에 email 필드가 추가되었습니다.도메인 모델 변경사항이 테스트에 적절히 반영되었습니다.
96-100: createCategory 메서드도 일관성 있게 업데이트되었습니다.모든 헬퍼 메서드에서 동일한 필드 추가가 이루어져 테스트 데이터의 일관성이 유지되고 있습니다.
src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java (2)
18-18: UseCase 의존성 주입이 적절합니다.헥사고날 아키텍처의 port-in 패턴을 잘 따르고 있습니다.
20-31: ```shell
#!/bin/bashUserSignupRequest 클래스 위치 확인 및 검증 어노테이션 확인
rg "class UserSignupRequest" -n -C3 src
해당 파일에서 검증 어노테이션(@NotNull, @notblank, @Email 등) 존재 여부 확인
rg "@(NotNull|NotBlank|Email|Size|Pattern)" -n src
</details> <details> <summary>src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java (2)</summary> `1-1`: **패키지 경로 수정 적절** 패키지 선언이 올바르게 수정되었습니다. --- `60-60`: **이메일 필드 추가 확인** User 도메인 모델의 이메일 필드 추가에 맞춰 테스트가 적절히 업데이트되었습니다. </details> <details> <summary>src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java (1)</summary> `14-14`: **이메일 필드 매핑 추가 확인** 도메인 엔티티와 JPA 엔티티 간 이메일 필드 매핑이 양방향으로 올바르게 추가되었습니다. Also applies to: 25-25 </details> <details> <summary>src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java (1)</summary> `24-30`: **헥사고날 아키텍처 준수 및 예외 처리 적절** User 저장 로직이 잘 구현되었습니다: - Alias 존재성 검증과 적절한 예외 처리 - 매퍼를 통한 도메인-JPA 엔티티 변환 - 포트 인터페이스 규약 준수 전체적으로 헥사고날 아키텍처 원칙을 잘 따르고 있습니다. </details> <details> <summary>src/main/java/konkuk/thip/user/application/service/UserSignupService.java (1)</summary> `22-29`: **서비스 로직 구현 적절** Use Case 인터페이스 구현과 포트를 통한 의존성 관리가 헥사고날 아키텍처에 잘 맞습니다. 도메인 객체 생성과 저장 과정이 명확하게 분리되어 있습니다. 컨트롤러 레벨에서 Bean Validation이 적용되는지 확인이 필요합니다. 다음 스크립트로 컨트롤러의 검증 어노테이션을 확인해보겠습니다: ```shell #!/bin/bash # UserSignupRequest의 validation 어노테이션 확인 ast-grep --pattern 'class UserSignupRequest { $$$ }'src/main/java/konkuk/thip/user/adapter/out/persistence/AliasCommandPersistenceAdapter.java (1)
13-27: LGTM! 헥사고날 아키텍처를 올바르게 준수한 구현입니다어댑터가 포트 인터페이스를 올바르게 구현하고 있으며, 다음과 같은 좋은 설계 원칙을 따르고 있습니다:
- 의존성 주입을 통한 느슨한 결합
- 적절한 예외 처리 (EntityNotFoundException with ALIAS_NOT_FOUND)
- 도메인 엔티티와 JPA 엔티티 간의 명확한 매핑 분리
src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java (1)
18-27: 검증 로직이 잘 구현되었습니다각 필드에 대한 검증 어노테이션이 비즈니스 요구사항을 올바르게 반영하고 있습니다:
aliasId: @NotNull로 필수 값 검증nickname: @notblank + @Length(max=10)로 공백 방지 및 길이 제한한국어 오류 메시지도 사용자 친화적으로 작성되어 있습니다.
src/test/java/konkuk/thip/user/adapter/in/web/UserCommandControllerTest.java (3)
43-78: 포괄적인 통합 테스트가 잘 작성되었습니다성공 시나리오 테스트가 전체 플로우를 올바르게 검증하고 있습니다:
- HTTP 응답 검증 (상태 코드, JSON 구조)
- 데이터베이스 영속성 검증
- 요청 데이터와 저장된 데이터 간의 일치성 확인
테스트 데이터 설정과 검증 로직이 명확하게 구현되어 있습니다.
80-173: 검증 실패 시나리오들이 철저하게 테스트되었습니다각 검증 어노테이션에 대한 실패 케이스가 개별적으로 테스트되어 있어 좋습니다:
- aliasId null 검증
- nickname 공백/길이 초과 검증
- email 공백/형식 오류 검증
에러 코드(40002)와 메시지 검증도 포함되어 있어 완전한 테스트 커버리지를 제공합니다.
26-41: 테스트 설정이 적절하게 구성되었습니다Spring Boot 통합 테스트를 위한 어노테이션들이 올바르게 설정되어 있고, 필요한 의존성들이 적절히 주입되었습니다. 테스트 프로파일 사용으로 운영 환경과 분리된 안전한 테스트 환경을 구성했습니다.
| UserSignupCommand command = UserSignupCommand.builder() | ||
| .aliasId(request.getAliasId()) | ||
| .nickname(request.getNickname()) | ||
| .email(request.getEmail()) | ||
| .build(); |
There was a problem hiding this comment.
P2: UserSignupRequest -> UserSignupCommand의 변환에 대한 책임을 컨트롤러가 갖게 하는 것보다 Request dto가 갖게 하는 것 어떨까요?
UserSignupRequest 내에 다음과 같은 변환 메서드를 뚫어두면 Controller 핸들러가 좀더 간단해질 것 같아욥
public UserSignupCommand toCommand() {
return UserSignupCommand.builder()
.aliasId(this.aliasId)
.nickname(this.nickname)
.email(this.email)
.build();
}There was a problem hiding this comment.
좋습니다
그러면 request dto 가 use case의 입력모델인 Command 로의 변환책임을 가지도록 통일하면 좋을것 같습니다
| return BaseResponse.ok(UserSignupResponse.builder() | ||
| .userId(userSignupUseCase.signup(command)) | ||
| .build()); |
There was a problem hiding this comment.
P3: Response dto는 보통 도메인을 반환하는 경우가 많으니 가독성을 높이기 위해서 of 같은 정적 팩토리 메서드를 사용하는 것도 고려해보면 좋을 것 같습니다!
그러면 다음과 같이 핸들러가 깔끔해질 것 같아요.
@PostMapping("/users/signup")
public BaseResponse<UserSignupResponse> signup(@Validated @RequestBody UserSignupRequest request) {
Long userId = userSignupUseCase.signup(request.toCommand());
return BaseResponse.ok(UserSignupResponse.from(userId));
}There was a problem hiding this comment.
오 좋은 생각입니다!
다만 현재 signupResponse는 내부 필드값이 userId 1개 이지만, 반환해야하는 데이터들이 많을 경우의 정적 팩토리 메서드 사용은 가독성이 안좋다고 생각해서, 이 부분은 다시 얘기해보면 좋을거같습니다
| @Getter | ||
| @Setter | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class UserSignupRequest { |
There was a problem hiding this comment.
테스트 코드 작성시에 @AllArgsConstructor 를 통해서 한방에 request를 구성할 수도 있지만, @Setter를 통해 값을 변경할 수도 있지 않을까 싶어서 request dto에만 setter를 추가해봤습니다!
| @Override | ||
| public Long signup(UserSignupCommand command) { | ||
| Alias alias = aliasCommandPort.findById(command.getAliasId()); | ||
| User user = User.withoutId( | ||
| command.getEmail(), command.getNickname(), alias.getImageUrl(), NORMAL_USER_ROLE, alias.getId() | ||
| ); | ||
|
|
||
| return userCommandPort.save(user); | ||
| } |
There was a problem hiding this comment.
P1 : @Transactional 다는 것이 안전해보입니다!
There was a problem hiding this comment.
앗 깜빡했네요! 수정하겠습니다
| @RequiredArgsConstructor | ||
| public class UserSignupService implements UserSignupUseCase { | ||
|
|
||
| private static final String NORMAL_USER_ROLE = "일반유저"; |
There was a problem hiding this comment.
P1 : 저희 user 패키지 안에 UserRole로 이미 enum이 선언되어 있어서 해당 enum 사용하면 될 것 같습니다!
There was a problem hiding this comment.
음 UserRole 이라는 enum 은 JPA 엔티티와의 의존성만을 유지하고, 도메인 로직과는 의존성을 갖지않도록 하기 위해(= 도메인이 가지는 의존성을 최소로 유지하는게 저희의 목표이니) User 도메인에서는 String type의 role 필드값을 가지도록 했습니다!
그래서 Service 코드에서도 static final String type의 값을 이용해 User 도메인을 만들도록 하였는데, 어떤 방식이 더 나을까요?
There was a problem hiding this comment.
제 생각은 UserRole이라는 것이 User 라는 도메인이 결국 가지고 있어야 하는 상태이기 때문에 Jpa 엔티티와의 의존성이 있다고 하더라도 같이 사용하는 것이 중복 상수 제거 및 데이터 일관성(?) 면에서 더 괜찮다고 생각합니다!
There was a problem hiding this comment.
흠 아니면 User 도메인엔티티는 String type으로 유지하고
User 도메인 엔티티를 생성할 때, role 의 값으로 UserRole.USER.getValue() 를 주입해주는 것은 어떤가요?
| @Override | ||
| public Long save(User user) { | ||
| AliasJpaEntity aliasJpaEntity = aliasJpaRepository.findById(user.getAliasId()).orElseThrow( | ||
| () -> new EntityNotFoundException(ALIAS_NOT_FOUND)); | ||
|
|
||
| UserJpaEntity userJpaEntity = userMapper.toJpaEntity(user, aliasJpaEntity); | ||
| return userJpaRepository.save(userJpaEntity).getUserId(); | ||
| } |
There was a problem hiding this comment.
P2 : 음 저는 여기서 id를 반환하지 않고 User 엔티티 자체를 반환한 후 컨트롤러 핸들러 안의 response dto의 매개변수로 User 엔티티 자체를 넣어서 그 안에서 파싱되도록 하는 것이 더 캡슐화가 잘 지켜지는 것 아닐까 싶은데 다른 분들은 어떻게 생각하시나욥
There was a problem hiding this comment.
음 저는 User 도메인은 application 패키지를 벗어나지 않는게 좋다고 생각합니다!
아니면 persistenceAdapter 의 save 메서드가 User를 반환하고, service에서 controller 로 데이터를 반환할때는 User.getId() 로 Long type의 id값을 반환하는 방법도 있을거 같습니다
|
|
||
| @Test | ||
| @DisplayName("[칭호id]값이 null일 경우, 400 error가 발생한다.") | ||
| void signup_whenAliasIdNull_thenBadRequest() throws Exception { |
There was a problem hiding this comment.
P2 : 테스트 코드 메서드 명을 카멜 케이스와 스네이크 케이스를 혼용해서 사용중인데 카멜 케이스로 통일하는 거 어떨까요!
There was a problem hiding this comment.
엇 카멜케이스만으로는 너무 메서드 네이밍이 길어지는 거 같아서 혼종으로 한번 작성해보았습니다
아니면 메서드 네이밍을 좀 줄여볼까요?? 긴 네이밍을 모두 카멜 케이스로 하면 가독성이 낮아질꺼 같습니다!
There was a problem hiding this comment.
아 알겠습니다! 그러면 이대로도 괜찮을 것 같아요! 테스트 코드 메서드는 길어질 경우 혼용해서 사용하는 걸로 하죠
| mockMvc.perform(post("/users/signup") | ||
| .contentType(MediaType.APPLICATION_JSON) | ||
| .content(objectMapper.writeValueAsString(request))) | ||
| .andExpect(status().isBadRequest()) | ||
| .andExpect(jsonPath("$.code").value("40002")) | ||
| .andExpect(jsonPath("$.message", containsString("aliasId는 필수입니다."))); | ||
| } |
There was a problem hiding this comment.
P2 : 40002 또는 에러 메시지를 ErrorCode enum에서 가져와서 사용하면 조금더 테스트 코드 이해가 빠를 것 같아요!
There was a problem hiding this comment.
동의합니다! 수정해보겠습니다
hd0rable
left a comment
There was a problem hiding this comment.
테스트 코드 설명 자세히적어쥬서서 좋네요!! 🤩🤩
수고하셨습니당~~ 리뷰확인부탁드립니닷!
| @Setter | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class UserSignupRequest { |
There was a problem hiding this comment.
P2: 저희 dto @ Record 어노테이션 사용하기로 하지않았나욥??
There was a problem hiding this comment.
엇 깜빡했습니다! Record 관련 레퍼런스 한번 다시 찾아보겠습니다
| import org.hibernate.validator.constraints.Length; | ||
|
|
||
| @Getter | ||
| @Setter |
There was a problem hiding this comment.
이 부분은 테스트 코드 시에 사용할 수도 있지 않을까 싶어서 request dto에만 살짝 setter를 붙여보았는데, 제거하는게 좋을까요?
There was a problem hiding this comment.
dto가 불변속성이라는건 변하지않으니까 제거하는게 좋을것같아요!
| @AllArgsConstructor | ||
| public class UserSignupRequest { | ||
|
|
||
| @NotNull(message = "aliasId는 필수입니다.") |
There was a problem hiding this comment.
@notblank 어노테이션은 String 타입의 데이터에 대한 validation으로 알고 있습니다!
| @Column(name = "alias_value", length = 50, nullable = false) | ||
| private String value; | ||
|
|
||
| @Column(name = "image_url", columnDefinition = "TEXT", nullable = false) |
| @Column(name = "user_id") | ||
| private Long userId; | ||
|
|
||
| @Column(name = "email", length = 30, nullable = false) |
There was a problem hiding this comment.
P3: 이메일은 좀더 넉넉하게줘도 좋을것같아요! 만약 정말 이메일 길이가 긴 사용자가 가입하려고하면 예외처리가 안되어있어서..
There was a problem hiding this comment.
아 이메일 길이제한이 저럴줄은 몰랐네염
수정해보겠습니다!
| @Override | ||
| public Long save(User user) { | ||
| AliasJpaEntity aliasJpaEntity = aliasJpaRepository.findById(user.getAliasId()).orElseThrow( | ||
| () -> new EntityNotFoundException(ALIAS_NOT_FOUND)); |
| @RequiredArgsConstructor | ||
| public class UserSignupService implements UserSignupUseCase { | ||
|
|
||
| private static final String NORMAL_USER_ROLE = "일반유저"; |
There was a problem hiding this comment.
P3: UserRole 도메인 영역에서도 인플루언서유저도 있을수 있을테니 도메인용 userRole enum을 따로 정의하는건 어떨까욤?.. getValue()를 사용해 String으로 주입하면 될것같은데 .. 보통 이런경우는 어떻게 구현하셨나요??
There was a problem hiding this comment.
도메인 용 UserRole은 생각 못해봤습니다!
현재 회원가입 api는 인플루언서는 고려하지 않고, 일반유저의 회원가입을 위한 api 이고, 추후에 인플루언서가 도입될 경우에는 회원가입 api를 분리하거나, 아니면 api 호출 없이 바로 DB에 인플루언서 데이터를 insert 하는 방식을 생각했긴합니다!
아니면 User 도메인 엔티티를 생성할 때, role 의 값으로 UserRole.USER.getValue() 를 주입해주는 것도 괜찮다는 생각입니다!
There was a problem hiding this comment.
저는 도메인과 JPA엔티티가 분리되어야한다는것만 중점으로 생각해서 JPA엔티티의 UserRole을 재사용하면 안되는줄알앗네용 ㅎㅎ..머쓱😅
도메인은 String으로 유지하면서 UserRole.USER.getValue()를 주입하는 방식으로 사용하면 좋을것같네요 !!👍🏻
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| @Builder |
There was a problem hiding this comment.
P3: 속성 값 하나일땐 @ Builder를 사용하는게 더 버거워보이는데 어떻게생각하세용?
| @Builder | |
| @Getter | |
| @AllArgsConstructor(staticName = "of") | |
| public class UserSignupResponse { | |
| private Long userId; | |
| } |
static 패턴을 사용하는게 더 좋을것같습니당!
There was a problem hiding this comment.
이 어노테이션 너무 좋네요! 하나 알아갑니다!
엇 브랜치명이 이상한가염?? |
한국어라깨졌어여.. 영어루 고고 |
앗 한국어네요 허허,, 바로 수정하겠습니다 |
하드코딩 값 대신 ErrorCode.code 로 검증하도록 테스트 코드 수정
| API_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 50000, "서버 내부 오류입니다."), | ||
|
|
||
| API_BAD_REQUEST(HttpStatus.BAD_REQUEST, 40002, "잘못된 요청입니다."), | ||
| API_BAD_REQUEST(HttpStatus.BAD_REQUEST, 40000, "잘못된 요청입니다."), |
There was a problem hiding this comment.
오 이거 잘못되어 있었네요,, 수정 감사합니다 ~
| .andExpect(status().isBadRequest()) | ||
| .andExpect(jsonPath("$.code").value("40002")) | ||
| .andExpect(jsonPath("$.code").value(API_INVALID_PARAM.getCode())) | ||
| .andExpect(jsonPath("$.message", containsString("aliasId는 필수입니다."))); |
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java (1)
5-10: Record에서 @builder 사용을 재고해보세요.Java record는 이미 모든 필드에 대한 생성자를 제공하므로,
@Builder어노테이션이 불필요할 수 있습니다. Record의 철학은 단순함과 불변성이므로, 직접 생성자를 사용하는 것이 더 적절할 것 같습니다.다음과 같이 단순화할 수 있습니다:
-@Builder public record UserSignupCommand( Long aliasId, String nickname, String email ) {}그리고 사용하는 곳에서는:
new UserSignupCommand(aliasId, nickname, email)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java(1 hunks)src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java(1 hunks)src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java(1 hunks)src/main/java/konkuk/thip/user/application/service/UserSignupService.java(1 hunks)src/test/java/konkuk/thip/user/adapter/in/web/UserCommandControllerTest.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/konkuk/thip/user/application/service/UserSignupService.java
- src/test/java/konkuk/thip/user/adapter/in/web/UserCommandControllerTest.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java (1)
src/main/java/konkuk/thip/user/application/port/in/dto/DummyCommand.java (1)
Builder(6-10)
🔇 Additional comments (3)
src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java (1)
3-7: 깔끔한 구현입니다.단일 필드를 가진 record에 대해 static factory method를 제공하는 현재 구현이 적절합니다. 이전 리뷰 댓글에서 제안된
@AllArgsConstructor(staticName = "of")보다도 더 명확하고 간결합니다.src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java (2)
9-20: 검증 어노테이션 사용이 적절합니다.각 필드에 대한 검증 어노테이션이 올바르게 적용되었습니다:
Long aliasId에@NotNull사용 (올바름)String필드들에@NotBlank와 추가 제약조건 사용 (적절함)- 닉네임 최대 길이 10자 제한 (합리적)
- 이메일 형식 검증 포함
이전 리뷰 댓글의 우려사항들이 모두 해결되었습니다.
21-27: toCommand() 메서드에서 Builder 패턴 사용을 확인해주세요.
UserSignupCommand에서@Builder를 사용하고 있는데, 앞서 언급한 대로 record에서는 불필요할 수 있습니다. 만약UserSignupCommand에서@Builder를 제거한다면, 이 부분도 일반 생성자 호출로 변경해야 합니다.public UserSignupCommand toCommand() { return new UserSignupCommand(aliasId, nickname, email); }
buzz0331
left a comment
There was a problem hiding this comment.
확인 완료했습니다! 고생하셨어욥 👍🏻 👍🏻



#️⃣ 연관된 이슈
📝 작업 내용
회원가입 api 를 개발하였습니다
📸 스크린샷
💬 리뷰 요구사항
회원가입 api는 복잡한 비즈니스 로직 없이, 단순히 request를 DB에 저장하는 api 여서 통합 테스트만 작성했습니다.
제가 어떤 작업을 했는지 쉽게 알아볼 수 있는 문서 용도로 테스트 코드를 활용할 수 있게끔 자세히 작성했는데, 한번 확인해주시면 감사하겠습니다!
또한 헥사고날 아키텍처에서의 controller -> use case -> service -> port -> adapter -> ,, 의 흐름도 참고해주시면 좋을 것 같습니다
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit
신규 기능
버그 수정
기타 변경