-
Notifications
You must be signed in to change notification settings - Fork 1
[feat] 닉네임 중복 검사, 칭호 선택 view api 개발 #35
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 all commits
a0ccc91
290e7c6
fe71388
8e02a9b
f665e7a
cfd2d7f
1fa761c
67819df
d37adb3
c869199
a74ee80
5d3839e
5135aa1
047ea40
5cab3ff
d375564
32fb424
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 |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package konkuk.thip.config; | ||
|
|
||
| import com.querydsl.jpa.impl.JPAQueryFactory; | ||
| import jakarta.persistence.EntityManager; | ||
| import jakarta.persistence.PersistenceContext; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
|
|
||
| @Configuration | ||
| public class QuerydslConfig { | ||
|
|
||
| @PersistenceContext | ||
| private EntityManager entityManager; | ||
|
|
||
| @Bean | ||
| public JPAQueryFactory jpaQueryFactory() { | ||
| return new JPAQueryFactory(entityManager); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,22 @@ | ||
| package konkuk.thip.user.adapter.in.web; | ||
|
|
||
| import konkuk.thip.common.dto.BaseResponse; | ||
| import konkuk.thip.user.adapter.in.web.response.GetUserShowAliasChoiceResponse; | ||
| import konkuk.thip.user.application.port.in.ShowAliasChoiceViewUseCase; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| public class UserQueryController { | ||
|
|
||
| private final ShowAliasChoiceViewUseCase showAliasChoiceViewUseCase; | ||
|
|
||
| @GetMapping("/users/alias") | ||
| public BaseResponse<GetUserShowAliasChoiceResponse> showAliasChoiceView() { | ||
| return BaseResponse.ok(GetUserShowAliasChoiceResponse.of( | ||
| showAliasChoiceViewUseCase.getAllAliasesAndCategories() | ||
| )); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,17 +1,14 @@ | ||||||||||||||||
| package konkuk.thip.user.adapter.in.web.request; | ||||||||||||||||
|
|
||||||||||||||||
| import jakarta.validation.constraints.Email; | ||||||||||||||||
| import jakarta.validation.constraints.NotBlank; | ||||||||||||||||
| import jakarta.validation.constraints.NotNull; | ||||||||||||||||
| import jakarta.validation.constraints.*; | ||||||||||||||||
| import konkuk.thip.user.application.port.in.dto.UserSignupCommand; | ||||||||||||||||
| import org.hibernate.validator.constraints.Length; | ||||||||||||||||
|
|
||||||||||||||||
| public record UserSignupRequest( | ||||||||||||||||
| public record PostUserSignupRequest( | ||||||||||||||||
| @NotNull(message = "aliasId는 필수입니다.") | ||||||||||||||||
| Long aliasId, | ||||||||||||||||
|
|
||||||||||||||||
| @NotBlank(message = "닉네임은 공백일 수 없습니다.") | ||||||||||||||||
| @Length(max = 10, message = "닉네임은 최대 10자 입니다.") | ||||||||||||||||
| @Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)") | ||||||||||||||||
| @Size(max = 10, message = "닉네임은 최대 10자 입니다.") | ||||||||||||||||
| String nickname, | ||||||||||||||||
|
Comment on lines
+10
to
12
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. 닉네임 검증에 null 값 처리를 위한 @NotNull 어노테이션 추가가 필요합니다. 현재 + @NotNull(message = "닉네임은 필수입니다.")
@Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)")
@Size(max = 10, message = "닉네임은 최대 10자 입니다.")📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| @NotBlank(message = "이메일은 공백일 수 없습니다.") | ||||||||||||||||
|
|
||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package konkuk.thip.user.adapter.in.web.request; | ||
|
|
||
| import jakarta.validation.constraints.Pattern; | ||
| import jakarta.validation.constraints.Size; | ||
|
|
||
| public record PostUserVerifyNicknameRequest( | ||
| @Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)") | ||
| @Size(max = 10, message = "닉네임은 최대 10자 입니다.") | ||
| String nickname | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package konkuk.thip.user.adapter.in.web.response; | ||
|
|
||
| import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record GetUserShowAliasChoiceResponse(List<AliasChoice> aliasChoices) { | ||
|
|
||
| public static GetUserShowAliasChoiceResponse of(AliasChoiceViewResult result) { | ||
| List<AliasChoice> choices = result.aliasChoices().stream() | ||
| .map(ac -> new AliasChoice( | ||
| ac.aliasId(), | ||
| ac.aliasName(), | ||
| ac.categoryName(), | ||
| ac.imageUrl(), | ||
| ac.color() | ||
| )) | ||
| .toList(); | ||
| return new GetUserShowAliasChoiceResponse(choices); | ||
| } | ||
|
|
||
| public record AliasChoice( | ||
| Long aliasId, | ||
| String aliasName, | ||
| String categoryName, | ||
| String imageUrl, | ||
| String color | ||
| ) {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package konkuk.thip.user.adapter.in.web.response; | ||
|
|
||
| public record PostUserSignupResponse(Long userId) { | ||
| public static PostUserSignupResponse of(Long userId) { | ||
| return new PostUserSignupResponse(userId); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package konkuk.thip.user.adapter.in.web.response; | ||
|
|
||
| public record PostUserVerifyNicknameResponse(boolean isVerified) { | ||
| public static PostUserVerifyNicknameResponse of(boolean isVerified) { | ||
| return new PostUserVerifyNicknameResponse(isVerified); | ||
| } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package konkuk.thip.user.adapter.out.persistence; | ||
|
|
||
| import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; | ||
| import konkuk.thip.user.application.port.out.AliasQueryPort; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| @Repository | ||
| @RequiredArgsConstructor | ||
| public class AliasQueryPersistenceAdapter implements AliasQueryPort { | ||
|
|
||
| private final AliasJpaRepository aliasJpaRepository; | ||
|
|
||
| @Override | ||
| public AliasChoiceViewResult getAllAliasesAndCategories() { | ||
| return aliasJpaRepository.getAllAliasesAndCategories(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package konkuk.thip.user.adapter.out.persistence; | ||
|
|
||
| import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; | ||
|
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. 아키텍처 의존성 방향 위반 검토 필요 퍼시스턴스 레이어에서 애플리케이션 레이어의 DTO( 🤖 Prompt for AI Agents🛠️ Refactor suggestion 아키텍처 레이어 의존성 검토 필요 persistence layer에서 application port in DTO( persistence layer 전용 DTO를 만들고 adapter에서 매핑을 수행하는 방식을 고려해보세요. 🤖 Prompt for AI Agents |
||
|
|
||
| public interface AliasQueryRepository { | ||
|
|
||
| AliasChoiceViewResult getAllAliasesAndCategories(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package konkuk.thip.user.adapter.out.persistence; | ||
|
|
||
| import com.querydsl.core.types.Projections; | ||
| import com.querydsl.jpa.impl.JPAQueryFactory; | ||
| import konkuk.thip.room.adapter.out.jpa.QCategoryJpaEntity; | ||
| import konkuk.thip.user.adapter.out.jpa.QAliasJpaEntity; | ||
| import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; | ||
|
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 아키텍처 레이어 의존성 재검토 persistence layer에서 application port in DTO를 직접 사용하는 것은 레이어 간 의존성 규칙을 위반합니다. 작성자가 언급한 대로 DTO 매핑 오버헤드를 줄이기 위한 결정이지만, 장기적으로 유지보수성에 영향을 줄 수 있습니다. 🤖 Prompt for AI Agents |
||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Repository | ||
| @RequiredArgsConstructor | ||
| public class AliasQueryRepositoryImpl implements AliasQueryRepository { | ||
|
|
||
| private final JPAQueryFactory jpaQueryFactory; | ||
|
|
||
| @Override | ||
| public AliasChoiceViewResult getAllAliasesAndCategories() { | ||
| QAliasJpaEntity alias = QAliasJpaEntity.aliasJpaEntity; | ||
| QCategoryJpaEntity category = QCategoryJpaEntity.categoryJpaEntity; | ||
|
|
||
| List<AliasChoiceViewResult.AliasChoice> aliasChoices = jpaQueryFactory | ||
| .select(Projections.constructor( | ||
| AliasChoiceViewResult.AliasChoice.class, | ||
| alias.aliasId, | ||
| alias.value, | ||
| category.value, | ||
| alias.imageUrl, | ||
| alias.color | ||
| )) | ||
| .from(alias) | ||
| .leftJoin(category) | ||
| .on(category.aliasForCategoryJpaEntity.eq(alias)) | ||
| .orderBy(alias.aliasId.asc()) | ||
| .fetch(); | ||
|
Comment on lines
+24
to
+37
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 chainQueryDSL 쿼리 로직 검증 및 성능 최적화 검토 QueryDSL 구현이 전반적으로 올바르게 작성되어 있습니다. 하지만 다음 사항들을 검토해주세요:
다음 스크립트로 엔티티 관계를 확인해주세요: 🏁 Script executed: #!/bin/bash
# Description: Verify entity relationships between Alias and Category
# Expected: Confirm the relationship mapping is correct
# Search for entity relationship mappings
ast-grep --pattern $'class $_JpaEntity {
$$$
@$_
private $_ aliasForCategoryJpaEntity;
$$$
}'
# Search for alias entity definition
ast-grep --pattern $'class AliasJpaEntity {
$$$
}'Length of output: 156 매핑 정보를 재확인하기 위해 다음 스크립트를 실행해주세요: 🏁 Script executed: #!/bin/bash
# Alias–Category 매핑 필드 검색
rg "aliasForCategoryJpaEntity" -n src/main/java/konkuk/thip/user/adapter/out/persistence
# JPA 엔티티 클래스 목록 확인
rg "class .*JpaEntity" -n src/main/java/konkuk/thip/user/adapter/out/persistenceLength of output: 325 Alias–Category 엔티티 매핑 정의를 프로젝트 전체에서 재확인해주세요: 🏁 Script executed: #!/bin/bash
# 프로젝트 전체에서 aliasForCategoryJpaEntity 필드 정의 검색
rg "aliasForCategoryJpaEntity" -n .
# @Entity 어노테이션이 붙은 Category 관련 클래스 확인
rg "@Entity" -n src/main/java | grep -i category
rg "class .*Category.*" -n src/main/java
# 전체 JPA 엔티티 클래스 목록 확인
rg "class .*JpaEntity" -n src/main/javaLength of output: 4731 페이징 처리 적용 권고
조인 조건( 🤖 Prompt for AI Agents💡 Verification agent 🧩 Analysis chain쿼리 최적화 및 에러 핸들링 개선 현재 구현에서 몇 가지 개선사항을 제안합니다:
List<AliasChoiceViewResult.AliasChoice> aliasChoices = jpaQueryFactory
.select(Projections.constructor(
AliasChoiceViewResult.AliasChoice.class,
alias.aliasId,
alias.value,
category.value,
alias.imageUrl,
alias.color
))
.from(alias)
- .leftJoin(category)
+ .innerJoin(category) // 카테고리가 필수인 경우
.on(category.aliasForCategoryJpaEntity.eq(alias))
.orderBy(alias.aliasId.asc())
.fetch();다음 스크립트로 Join 조건을 검증해보세요: 🏁 Script executed: #!/bin/bash
# 칭호-카테고리 관계 및 null 값 확인
ast-grep --pattern $'class $_JpaEntity {
$$$
@JoinColumn$$$
private $_JpaEntity $category$;
$$$
}'Length of output: 107 아래 스크립트로 🏁 Script executed: #!/bin/bash
# aliasForCategoryJpaEntity 매핑 위치 확인
rg "aliasForCategoryJpaEntity" -n .
# aliasForCategoryJpaEntity 필드의 @ManyToOne(optional) 설정 확인
rg "@ManyToOne.*aliasForCategoryJpaEntity" -n .Length of output: 1174 아래 스크립트로 🏁 Script executed: #!/bin/bash
# CategoryJpaEntity 내 @ManyToOne 어노테이션과 optional 설정 확인
rg "@ManyToOne" -n src/main/java/konkuk/thip/room/adapter/out/jpa/CategoryJpaEntity.java -C3Length of output: 92 아래 스크립트로 🏁 Script executed: #!/bin/bash
# aliasForCategoryJpaEntity 필드와 어노테이션 전체 맥락 출력
rg -C3 "aliasForCategoryJpaEntity" -n src/main/java/konkuk/thip/room/adapter/out/jpa/CategoryJpaEntity.javaLength of output: 287 쿼리 Join 타입 및 예외 처리 개선 제안 CategoryJpaEntity의 aliasForCategoryJpaEntity 필드가
수정 예시: List<AliasChoiceViewResult.AliasChoice> aliasChoices = jpaQueryFactory
.select(Projections.constructor(
AliasChoiceViewResult.AliasChoice.class,
alias.aliasId,
alias.value,
category.value,
alias.imageUrl,
alias.color
))
.from(alias)
- .leftJoin(category)
+ .innerJoin(category) // Category가 항상 매핑되어야 함
.on(category.aliasForCategoryJpaEntity.eq(alias))
.orderBy(alias.aliasId.asc())
- .fetch();
+ .fetch(); // TODO: 예외 처리 추가점검 포인트:
🤖 Prompt for AI Agents |
||
|
|
||
| return new AliasChoiceViewResult(aliasChoices); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package konkuk.thip.user.adapter.out.persistence; | ||
|
|
||
| public interface UserQueryRepository { | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package konkuk.thip.user.adapter.out.persistence; | ||
|
|
||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| @Repository | ||
| public class UserQueryRepositoryImpl implements UserQueryRepository { | ||
|
|
||
| /** | ||
| * QueryDsl 을 활용한 복잡한 조회 로직 구현 | ||
| */ | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package konkuk.thip.user.application.port.in; | ||
|
|
||
| import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; | ||
|
|
||
| public interface ShowAliasChoiceViewUseCase { | ||
|
|
||
| AliasChoiceViewResult getAllAliasesAndCategories(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package konkuk.thip.user.application.port.in; | ||
|
|
||
| public interface VerifyNicknameUseCase { | ||
|
|
||
| boolean isNicknameUnique(String nickname); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package konkuk.thip.user.application.port.in.dto; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record AliasChoiceViewResult(List<AliasChoice> aliasChoices) { | ||
|
|
||
| public record AliasChoice( | ||
| Long aliasId, | ||
| String aliasName, | ||
| String categoryName, | ||
| String imageUrl, | ||
| String color | ||
| ) {} | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package konkuk.thip.user.application.port.out; | ||
|
|
||
| import konkuk.thip.user.application.port.in.dto.AliasChoiceViewResult; | ||
|
|
||
| public interface AliasQueryPort { | ||
|
|
||
| AliasChoiceViewResult getAllAliasesAndCategories(); | ||
| } |
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
닉네임 검증 로직 강화가 우수하지만 null 체크 검토가 필요합니다.
@Pattern을 통한 문자 제한과@Size를 통한 길이 제한으로 더 엄격한 검증이 가능해졌습니다. 하지만 기존@NotBlank를 제거했기 때문에 null이나 빈 문자열에 대한 검증이 누락될 수 있습니다.@Patternannotation은 null 값에 대해서는 검증을 통과시키므로,@NotBlank또는@NotNull과 함께 사용하는 것을 권장합니다.다음과 같이 수정하는 것을 제안합니다:
+ @NotBlank(message = "닉네임은 필수입니다.") @Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)") @Size(max = 10, message = "닉네임은 최대 10자 입니다.")📝 Committable suggestion
🤖 Prompt for AI Agents