Conversation
|
""" Walkthrough이번 변경에서는 사용자 검색 API를 신규로 개발하고, 최근 검색어 저장 및 관련 도메인/포트/어댑터 계층을 확장하였습니다. 사용자 검색 결과에 aliasColor 등 추가 정보를 포함하도록 DTO 및 매퍼 구조가 변경되었고, 최근 검색어 중복 시 갱신 로직과 예외 코드가 추가되었습니다. 통합 테스트가 도입되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant UserQueryController
participant UserSearchService
participant UserQueryPort
participant UserQueryMapper
participant RecentSearchCreateManager
Client->>UserQueryController: GET /users?keyword=...
UserQueryController->>UserSearchService: searchUsers(UserSearchQuery)
UserSearchService->>UserQueryPort: findUsersByNicknameOrderByAccuracy(keyword, userId, size)
UserQueryPort-->>UserSearchService: List<UserQueryDto>
UserSearchService->>UserQueryMapper: toUserDtoList(List<UserQueryDto>)
UserQueryMapper-->>UserSearchService: List<UserSearchResponse.UserDto>
UserSearchService->>RecentSearchCreateManager: saveRecentSearchByUser(userId, keyword)
RecentSearchCreateManager->>RecentSearchQueryPort: findRecentSearchByKeywordAndUserId(keyword, userId)
alt 이미 존재
RecentSearchCreateManager->>RecentSearchCommandPort: update(RecentSearch)
else 신규
RecentSearchCreateManager->>RecentSearchCommandPort: save(RecentSearch)
end
UserSearchService-->>UserQueryController: UserSearchResponse
UserQueryController-->>Client: BaseResponse<UserSearchResponse>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes(해당 변경에서 명확히 이슈 범위를 벗어난 기능적 변경은 발견되지 않았습니다.) Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (7)
src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java (1)
15-15: 사용자 검색 메서드가 적절히 추가되었습니다.CQRS 패턴의 QueryPort에 맞게 조회 전용 메서드가 잘 추가되었습니다. 메서드명도 의도를 명확히 표현하고 있습니다.
매개변수 validation 강화를 권장합니다:
keyword의 null/blank 체크size의 양수 및 최대값 제한userId의 null 체크src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java (1)
11-11: 메서드 설계가 적절함메서드명이 명확하고 파라미터 구성이 논리적입니다. 정확도 기반 정렬이라는 비즈니스 요구사항이 메서드명에 잘 반영되어 있습니다.
Integer size파라미터의 null 안전성을 고려하여 다음 중 하나를 적용하는 것을 권장합니다:- List<UserQueryDto> findUsersByNicknameOrderByAccuracy(String keyword, Long userId, Integer size); + List<UserQueryDto> findUsersByNicknameOrderByAccuracy(String keyword, Long userId, int size);또는 호출부에서 적절한 기본값 검증을 추가해주세요.
src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java (1)
80-84: 검색 결과 순서에 대한 명시적 검증 추가현재 테스트는 닉네임 순서를 검증하지만, 왜 이 순서인지에 대한 설명이 부족합니다. 가중치 기반 정렬 로직을 명확히 하기 위해 주석을 추가하는 것을 권장합니다.
// then: 검색 결과 검증 + // 가중치 기반 정렬: thipalpha, thipbeta, thipgamma (시작=2), 123thip (포함=1) result.andExpect(status().isOk()) .andExpect(jsonPath("$.data.userList", hasSize(4))) .andExpect(jsonPath("$.data.userList[0].nickname").value("thipalpha"))src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchQueryRepositoryImpl.java (1)
26-26: 쿼리 성능 최적화 고려사항현재 쿼리는
userJpaEntity.userId를 통해 조인을 수행합니다. 자주 사용되는 쿼리라면(searchTerm, type, userId)복합 인덱스 추가를 고려해보세요.데이터베이스 스키마에 다음과 같은 인덱스 추가를 권장합니다:
CREATE INDEX idx_recent_search_term_type_user ON recent_search (search_term, type, user_id);src/main/java/konkuk/thip/user/application/service/UserSearchService.java (1)
23-23: 트랜잭션 범위 검토 필요최근 검색어 저장을 위한 트랜잭션 적용은 적절하나, 사용자 검색 조회 자체는 읽기 전용 작업입니다.
@Transactional(readOnly = true)를 기본으로 하고, 최근 검색어 저장 부분만 별도 트랜잭션으로 처리하는 것을 고려해보세요.@Override -@Transactional // <- 최근 검색 저장으로 인한 트랜잭션 +@Transactional(readOnly = true) public UserSearchResponse searchUsers(UserSearchQuery userSearchQuery) { var userDtoList = userQueryMapper.toUserDtoList(userQueryPort.findUsersByNicknameOrderByAccuracy( userSearchQuery.keyword().toLowerCase(), userSearchQuery.userId(), userSearchQuery.size() )); // 최근 검색어 저장 - recentSearchCreateManager.saveRecentSearchByUser(userSearchQuery.userId(), userSearchQuery.keyword()); + recentSearchCreateManager.saveRecentSearchByUser(userSearchQuery.userId(), userSearchQuery.keyword()); return UserSearchResponse.of(userDtoList); }그리고
RecentSearchCreateManager.saveRecentSearchByUser메서드에@Transactional어노테이션을 적용하세요.src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java (1)
42-71: 사용자 검색 로직이 잘 구현되었습니다가중치 기반 검색 로직이 요구사항에 맞게 정확히 구현되었습니다:
- 정확한 매칭: 가중치 3
- 접두사 매칭: 가중치 2
- 부분 매칭: 가중치 1
- 우선순위 내림차순, 닉네임 오름차순으로 정렬
다만 몇 가지 개선사항을 제안합니다.
개선 제안:
성능 최적화: LIKE 연산자 사용 시 인덱스 활용을 위해 nickname 컬럼에 인덱스가 있는지 확인해주세요.
코드 가독성: CaseBuilder 로직을 별도 메서드로 분리하면 가독성이 향상됩니다:
+ private NumberExpression<Integer> createSearchPriority(QUserJpaEntity user, String keyword) { + String exactPattern = keyword; + String prefixPattern = keyword + "%"; + String containsPattern = "%" + keyword + "%"; + + return new CaseBuilder() + .when(user.nickname.eq(exactPattern)).then(3) + .when(user.nickname.like(prefixPattern)).then(2) + .when(user.nickname.like(containsPattern)).then(1) + .otherwise(0); + } - NumberExpression<Integer> priority = new CaseBuilder() - .when(user.nickname.eq(keyword)).then(3) - .when(user.nickname.like(keyword + "%")).then(2) - .when(user.nickname.like(pattern)).then(1) - .otherwise(0); + NumberExpression<Integer> priority = createSearchPriority(user, keyword);src/main/java/konkuk/thip/user/application/port/in/dto/UserSearchQuery.java (1)
8-10: 경미:of팩토리 메서드가 현재는 불필요하게 보입니다.입력 검증을 Canonical Constructor로 이전하면
of는 단순 위임만 수행하게 되어 중복될 가능성이 큽니다.
통일성을 위해 유지할 수도 있으나, 불필요한 계층을 줄이고 싶다면 삭제를 고려해 주세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (34)
src/main/java/konkuk/thip/book/application/service/BookSearchService.java(1 hunks)src/main/java/konkuk/thip/common/entity/BaseDomainEntity.java(2 hunks)src/main/java/konkuk/thip/common/exception/code/ErrorCode.java(1 hunks)src/main/java/konkuk/thip/recentSearch/adapter/out/jpa/RecentSearchJpaEntity.java(2 hunks)src/main/java/konkuk/thip/recentSearch/adapter/out/jpa/SearchType.java(1 hunks)src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java(2 hunks)src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchQueryPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchJpaRepository.java(1 hunks)src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchQueryRepository.java(1 hunks)src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchQueryRepositoryImpl.java(1 hunks)src/main/java/konkuk/thip/recentSearch/application/port/out/RecentSearchCommandPort.java(1 hunks)src/main/java/konkuk/thip/recentSearch/application/port/out/RecentSearchQueryPort.java(1 hunks)src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java(1 hunks)src/main/java/konkuk/thip/recentSearch/domain/RecentSearch.java(2 hunks)src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java(2 hunks)src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowersResponse.java(1 hunks)src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowingResponse.java(1 hunks)src/main/java/konkuk/thip/user/adapter/in/web/response/UserSearchResponse.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java(3 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java(2 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java(2 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java(2 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java(5 hunks)src/main/java/konkuk/thip/user/application/mapper/FollowQueryMapper.java(1 hunks)src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java(1 hunks)src/main/java/konkuk/thip/user/application/port/in/UserSearchUsecase.java(1 hunks)src/main/java/konkuk/thip/user/application/port/in/dto/UserSearchQuery.java(1 hunks)src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java(1 hunks)src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java(1 hunks)src/main/java/konkuk/thip/user/application/port/out/dto/UserQueryDto.java(1 hunks)src/main/java/konkuk/thip/user/application/service/UserSearchService.java(1 hunks)src/main/java/konkuk/thip/user/application/service/following/UserGetFollowService.java(3 hunks)src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java(1 hunks)
🧰 Additional context used
🧠 Learnings (22)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/book/application/service/BookSearchService.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/recentSearch/application/port/out/RecentSearchQueryPort.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/user/application/port/in/dto/UserSearchQuery.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchQueryPersistenceAdapter.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/recentSearch/adapter/out/jpa/RecentSearchJpaEntity.java (1)
Learnt from: seongjunnoh
PR: #36
File: src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java:7-7
Timestamp: 2025-06-29T09:47:31.299Z
Learning: Spring Data JPA에서 findBy{FieldName} 패턴의 메서드는 명시적 선언 없이 자동으로 생성되며, Optional 반환 타입을 사용하는 것이 null 안전성을 위해 권장됩니다.
src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java (2)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
Learnt from: buzz0331
PR: #78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.
src/main/java/konkuk/thip/user/application/port/in/UserSearchUsecase.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/user/application/service/following/UserGetFollowService.java (2)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
Learnt from: buzz0331
PR: #78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.
src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/user/adapter/in/web/response/UserSearchResponse.java (1)
Learnt from: buzz0331
PR: #78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java (2)
Learnt from: buzz0331
PR: #78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/recentSearch/application/port/out/RecentSearchCommandPort.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchQueryRepositoryImpl.java (1)
Learnt from: seongjunnoh
PR: #36
File: src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java:7-7
Timestamp: 2025-06-29T09:47:31.299Z
Learning: Spring Data JPA에서 findBy{FieldName} 패턴의 메서드는 명시적 선언 없이 자동으로 생성되며, Optional 반환 타입을 사용하는 것이 null 안전성을 위해 권장됩니다.
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java (1)
Learnt from: seongjunnoh
PR: #36
File: src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java:7-7
Timestamp: 2025-06-29T09:47:31.299Z
Learning: Spring Data JPA에서 findBy{FieldName} 패턴의 메서드는 명시적 선언 없이 자동으로 생성되며, Optional 반환 타입을 사용하는 것이 null 안전성을 위해 권장됩니다.
src/main/java/konkuk/thip/user/application/mapper/FollowQueryMapper.java (1)
Learnt from: buzz0331
PR: #78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.
src/main/java/konkuk/thip/user/application/service/UserSearchService.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (1)
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchQueryRepository.java (1)
Learnt from: seongjunnoh
PR: #36
File: src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java:7-7
Timestamp: 2025-06-29T09:47:31.299Z
Learning: Spring Data JPA에서 findBy{FieldName} 패턴의 메서드는 명시적 선언 없이 자동으로 생성되며, Optional 반환 타입을 사용하는 것이 null 안전성을 위해 권장됩니다.
src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (2)
Learnt from: buzz0331
PR: #78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.
Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
🧬 Code Graph Analysis (4)
src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(29-293)
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchQueryRepositoryImpl.java (2)
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchQueryPersistenceAdapter.java (1)
Repository(14-26)src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java (1)
Repository(17-54)
src/main/java/konkuk/thip/user/application/mapper/FollowQueryMapper.java (1)
src/main/java/konkuk/thip/user/application/mapper/FollowDtoMapper.java (1)
FollowDtoMapper(8-14)
src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (1)
src/main/java/konkuk/thip/common/util/DateUtil.java (1)
DateUtil(12-62)
⏰ 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 (46)
src/main/java/konkuk/thip/recentSearch/adapter/out/jpa/SearchType.java (1)
13-13: DB 호환성 확인 필요
SearchType.BOOK_SEARCH의 문자열 값이"책_검색"에서"책 검색"으로 변경되었습니다.- 기존 DB의
recent_search테이블type컬럼에 여전히"책_검색"값이 남아 있는지 확인해주세요.- 필요 시 아래 중 하나를 적용해 주세요:
- 데이터 마이그레이션(SQL 스크립트)으로 기존 값을
"책 검색"으로 업데이트- JPA
AttributeConverter또는 로직 수정으로"책_검색"도 정상 매핑되도록 하위 호환성 확보src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1)
75-75: 적절한 에러 코드 추가입니다.최근 검색 기능의 삭제/업데이트 작업에서 사용할 에러 코드가 적절히 추가되었습니다. 90000번대 recentSearch 에러 범위에 맞게 분류되어 있고 메시지도 명확합니다.
src/main/java/konkuk/thip/book/application/service/BookSearchService.java (1)
75-75: 메서드 호출 방식 개선이 적절합니다.
recentSearchCommandPort.save()호출에서userId매개변수가 제거되어 더 간결해졌습니다.RecentSearch객체에userId가 포함되어 있어 별도 전달이 불필요하므로 응집도가 향상되었습니다.src/main/java/konkuk/thip/user/application/port/in/UserSearchUsecase.java (2)
6-8: UseCase 인터페이스 구조는 적절함단일 책임 원칙을 잘 따르고 있으며, 메서드 시그니처가 명확합니다.
3-3: 아키텍처 위반: 애플리케이션 레이어가 어댑터 레이어에 의존UseCase 인터페이스가 웹 어댑터 레이어의
UserSearchResponse를 import하고 있습니다. 클린 아키텍처에서 애플리케이션 레이어는 어댑터 레이어에 의존해서는 안 됩니다.다음과 같이 수정을 제안합니다:
-import konkuk.thip.user.adapter.in.web.response.UserSearchResponse; +import konkuk.thip.user.application.port.in.dto.UserSearchResponse;
UserSearchResponse를src/main/java/konkuk/thip/user/application/port/in/dto/패키지로 이동하거나, 별도의 application layer DTO를 생성하여 의존성 방향을 올바르게 유지해주세요.⛔ Skipped due to learnings
Learnt from: buzz0331 PR: THIP-TextHip/THIP-Server#78 File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3 Timestamp: 2025-07-14T18:22:56.538Z Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.Learnt from: seongjunnoh PR: THIP-TextHip/THIP-Server#43 File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0 Timestamp: 2025-07-03T03:05:05.031Z Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.src/main/java/konkuk/thip/recentSearch/application/port/out/RecentSearchQueryPort.java (1)
9-9: CQRS 패턴을 잘 준수한 QueryPort 메서드메서드 시그니처가 적절하며,
Optional<RecentSearch>반환 타입으로 null 안전성을 보장하고 있습니다. 검색된 학습 내용에 따르면 QueryPort에는 도메인 엔티티를 조회하는 메서드를 추가하는 것이 이 프로젝트의 컨벤션과 일치합니다.src/main/java/konkuk/thip/recentSearch/adapter/out/jpa/RecentSearchJpaEntity.java (1)
34-38: createdAt 필드 업데이트 로직 검토 필요
updateFrom메서드에서createdAt필드를 업데이트하고 있습니다. 일반적으로createdAt은 생성 시점을 나타내므로 업데이트 시에는 변경하지 않고,updatedAt필드를 수정하는 것이 일반적입니다.이것이 의도된 비즈니스 로직인지 확인해주세요. 만약 최근 검색어의 타임스탬프를 갱신하는 것이 요구사항이라면, 필드명을
lastSearchedAt같은 더 명확한 이름으로 변경하는 것을 고려해보세요.- this.setCreatedAt(recentSearch.getCreatedAt()); + // 비즈니스 요구사항에 따라 적절한 필드 업데이트 + this.setUpdatedAt(LocalDateTime.now()); // 또는 + this.setLastSearchedAt(recentSearch.getLastSearchedAt());src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchJpaRepository.java (1)
5-8: LGTM! 표준 Spring Data JPA 패턴을 잘 따르고 있습니다.
@Repository어노테이션 추가와RecentSearchQueryRepository확장이 적절하게 구현되었습니다. 커스텀 쿼리 기능을 위한 다중 상속 패턴이 올바르게 적용되었습니다.src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java (1)
9-13: LGTM! MapStruct 매퍼가 올바르게 구현되었습니다.Spring 컴포넌트 모델 설정과 리스트 매핑 메서드 구현이 적절합니다. 학습된 내용에 따르면 THIP 프로젝트에서는 Query API에 한해 application 계층에서 response DTO를 직접 참조하는 것이 허용되므로 현재 구현이 팀 컨벤션에 부합합니다.
src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java (1)
36-39: LGTM! CQRS QueryPort 패턴을 올바르게 구현했습니다.새로운
findUsersByNicknameOrderByAccuracy메서드가 적절하게 구현되었습니다. 학습된 내용에 따르면 QueryPort는 조회 API의 response에 해당하는 데이터를 DB로부터 조회하는 메서드를 포함해야 하며, 현재 구현이 이 컨벤션을 잘 따르고 있습니다.src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchQueryRepository.java (1)
8-10: LGTM! 쿼리 리포지토리 인터페이스가 잘 설계되었습니다.메서드명이 Spring Data JPA 컨벤션을 따르고 있으며,
Optional반환 타입을 사용하여 null 안전성을 확보했습니다. 학습된 내용에 따르면 이는 권장되는 패턴입니다.src/main/java/konkuk/thip/recentSearch/domain/RecentSearch.java (1)
21-27: LGTM! 정적 팩토리 메서드가 잘 구현되었습니다.
withoutId정적 팩토리 메서드가 적절하게 구현되어 새로운 엔티티 생성을 위한 깔끔한 API를 제공합니다. 빌더 패턴 사용도 적절합니다.src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowingResponse.java (1)
9-9: DTO 통합 및 필드 추가가 적절합니다.
Following에서FollowingDto로의 네이밍 변경과aliasColor필드 추가가 PR 목표에 부합하며, DTO 통합 작업의 일환으로 적절하게 구현되었습니다.Also applies to: 14-20
src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowersResponse.java (1)
9-9: 일관된 DTO 리팩토링이 잘 적용되었습니다.
UserFollowingResponse와 동일한 패턴으로Follower를FollowerDto로 변경하고aliasColor필드를 추가한 것이 일관성 있고 적절합니다.Also applies to: 14-20
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchQueryPersistenceAdapter.java (2)
6-6: 필요한 imports가 적절하게 추가되었습니다.도메인 엔티티
RecentSearch,Optional, 그리고USER_SEARCH상수의 static import가 새로운 메서드 구현에 필요한 의존성들을 명확하게 추가했습니다.Also applies to: 10-10, 12-12
21-25: 최근 검색어 조회 메서드가 올바르게 구현되었습니다.CQRS 패턴에 따라 QueryPort 인터페이스를 구현하며, 리포지토리 호출 후 매퍼를 통한 도메인 엔티티 변환이 적절하게 처리되었습니다.
Optional반환 타입도 데이터 존재 여부를 안전하게 처리할 수 있어 좋습니다.src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java (3)
5-5: 검증 어노테이션과 imports가 적절하게 추가되었습니다.새로운 검색 API를 위한
@NotBlank검증 어노테이션,@Validated클래스 레벨 어노테이션, 그리고 관련 imports가 올바르게 추가되었습니다.Also applies to: 15-15, 21-21
8-8: 새로운 검색 기능을 위한 의존성이 적절하게 추가되었습니다.wildcard import 사용과
UserSearchUsecase,UserSearchQuery등 검색 기능에 필요한 의존성들이 명확하게 정의되었습니다.Also applies to: 11-11, 13-13, 29-29
71-76: 사용자 검색 API가 잘 구현되었습니다.검색어 필수 검증(
@NotBlank), 사이즈 제한(1-30), 그리고 기본값 설정이 적절하며, PR 목표에 부합하는 가중치 기반 정확도 순 검색 기능이 올바르게 구현되었습니다.src/main/java/konkuk/thip/user/application/service/following/UserGetFollowService.java (3)
7-7: DTO 통합을 위한 import 변경이 적절합니다.
FollowQueryDto에서UserQueryDto로의 변경이 PR 목표인 DTO 통합 작업에 부합합니다.
32-34: 팔로워 조회 로직의 DTO 타입 변경이 일관되게 적용되었습니다.
CursorBasedList<UserQueryDto>타입 변경과toFollowerDto매퍼 메서드 호출이 DTO 통합 리팩토링에 맞게 올바르게 수정되었습니다.Also applies to: 37-37
52-54: 팔로잉 조회 로직의 DTO 타입 변경이 일관되게 적용되었습니다.팔로워 조회와 동일한 패턴으로
UserQueryDto타입과toFollowingDto매퍼 메서드가 일관성 있게 적용되었습니다.Also applies to: 57-57
src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java (1)
87-93: 최근 검색어 저장 검증 로직 승인최근 검색어가 올바르게 저장되는지 확인하는 검증 로직이 잘 구현되어 있습니다. 키워드, 검색 타입, 사용자 ID 모두 올바르게 검증하고 있습니다.
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchQueryRepositoryImpl.java (1)
20-31: QueryDSL 구현이 잘 작성되었습니다QueryDSL을 사용한 커스텀 쿼리 구현이 깔끔하고 타입 안전성을 보장합니다. Optional 반환 타입 사용으로 null 안전성도 확보되었습니다.
src/main/java/konkuk/thip/user/application/port/out/dto/UserQueryDto.java (2)
8-14: DTO 필드 추가 및 이름 변경이 적절합니다
FollowQueryDto에서UserQueryDto로의 이름 변경과aliasColor필드 추가가 사용자 검색 기능 확장에 적합합니다. QueryProjection 어노테이션도 올바르게 적용되었습니다.
22-22: Validation 일관성 확인
aliasColor필드에 대한 null 검증이 추가되었습니다. 다른 필드들과 일관된 검증 방식을 사용하고 있어 좋습니다.src/main/java/konkuk/thip/user/adapter/in/web/response/UserSearchResponse.java (2)
5-21: Response DTO 구조가 잘 설계되었습니다Java Record를 활용한 깔끔한 응답 DTO 구조입니다. 중첩된
UserDto레코드와 정적 팩토리 메서드of를 통해 가독성과 사용성이 좋습니다.
8-16: UserDto 필드 구성 확인사용자 검색 결과에 필요한 모든 필드가 포함되어 있고, 새로 추가된
aliasColor필드도 적절히 반영되었습니다. 필드명도 명확하고 일관성 있습니다.src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java (2)
23-35: Optional.ifPresentOrElse 사용이 적절합니다조건부 로직을
Optional.ifPresentOrElse로 깔끔하게 처리한 것이 좋습니다. 코드 가독성과 함수형 프로그래밍 스타일이 잘 적용되었습니다.
27-27: RecentSearchCreateManager —createdAt업데이트 의도 검토 필요
- 위치: src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java:27
- BaseDomainEntity에
createdAt·modifiedAt필드가 선언되어 있고, RecentSearch 엔티티에는updateCreatedAt(LocalDateTime)만 제공되어 있습니다.- 현 로직은 기존 검색 기록이 있을 때
createdAt을 덮어써서 정렬 순서를 제어하는 방식으로 보입니다.도메인 요구사항을 다시 한번 확인해주세요:
- 검색 기록 재사용 시
createdAt을 재설정하는 것이 실제 의도된 동작인지- 최근 검색어 정렬 기준으로
createdAt을 사용하고 있는지필드 명의 명확성을 위해
lastSearchedAt또는updatedAt같은 별도 필드 도입을 고려해 보시기 바랍니다.src/main/java/konkuk/thip/user/application/service/UserSearchService.java (2)
25-29: LGTM! 검색 로직이 잘 구현되었습니다키워드를 소문자로 변환하여 대소문자 구분 없는 검색을 지원하고, 정확도 기준 정렬을 적절히 활용하고 있습니다.
31-32: 최근 검색어 저장 시점 적절검색 결과 조회 후 최근 검색어를 저장하는 순서가 적절합니다. 검색이 실패하면 최근 검색어도 저장되지 않아 일관성이 보장됩니다.
src/main/java/konkuk/thip/recentSearch/application/port/out/RecentSearchCommandPort.java (1)
7-10: LGTM! CQRS 패턴에 맞는 인터페이스 설계CommandPort 인터페이스가 적절히 설계되었습니다:
save메서드가 도메인 객체만 받도록 단순화됨- CRUD 작업을 위한
delete,update메서드 추가- 메서드 시그니처가 도메인 중심적으로 설계됨
학습된 THIP 프로젝트의 CQRS 컨벤션에도 잘 부합합니다.
src/main/java/konkuk/thip/user/application/mapper/FollowQueryMapper.java (2)
5-5: DTO 통합이 적절히 반영됨
FollowQueryDto를UserQueryDto로 통합하는 PR 목표가 잘 반영되었습니다.
11-13: FollowDtoMapper 없음으로 중복 검토 불필요
FollowDtoMapper.java 파일이 레포지토리에 존재하지 않습니다. 현재는 FollowQueryMapper만 정의·사용 중이므로 중복 우려가 없습니다. 이전 리뷰 코멘트는 무시해도 좋습니다.Likely an incorrect or invalid review comment.
src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (1)
4-8: DTO 통합이 일관성 있게 적용됨
FollowQueryDto에서UserQueryDto로의 변경이 QueryPort 인터페이스에 일관성 있게 적용되었습니다. CQRS 패턴의 QueryPort 컨벤션에도 부합합니다.src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java (2)
4-4: DTO 통합이 repository 계층까지 일관성 있게 전파됨
UserQueryDtoimport 변경이 적절하며, 전체 계층에서 일관성 있는 DTO 통합이 이루어졌습니다.
13-14: 메서드 시그니처 업데이트 적절반환 타입이
UserQueryDto로 올바르게 변경되었으며, 메서드명도 여전히 의미가 명확합니다.src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java (1)
23-23: 필드명 변경이 일관성 있게 적용되었는지 확인 필요
jpaQueryFactory에서queryFactory로 필드명이 변경되었습니다. 변경된 필드명이 메서드 내에서 일관성 있게 사용되고 있는지 확인해주세요.src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java (2)
27-36: 메서드 시그니처 개선이 잘 적용되었습니다
RecentSearch객체에서userId를 직접 추출하여 사용하는 방식으로 개선되어 캡슐화가 향상되었습니다.
46-53: update 메서드 로직이 적절합니다엔티티 조회 후
updateFrom메서드를 사용하여 업데이트하는 방식이 깔끔하게 구현되었습니다. 예외 처리도 일관성 있게 적용되었습니다.src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (2)
5-5: DTO 통합이 일관성 있게 적용되었습니다
FollowQueryDto에서UserQueryDto로의 변경이 PR 목표에 맞게 잘 적용되었습니다.
21-42: 메서드 반환 타입 변경이 적절합니다두 메서드 모두
UserQueryDto를 사용하도록 일관성 있게 변경되었으며, 기존 로직은 그대로 유지되어 안전한 리팩토링입니다.src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java (3)
10-11: DTO 클래스 변경이 일관성 있게 적용되었습니다
QFollowQueryDto에서QUserQueryDto로의 변경이 코드베이스 전반에 걸쳐 일관성 있게 적용되었습니다.
40-56: 메서드 시그니처 업데이트가 적절합니다팔로워와 팔로잉 조회 메서드의 반환 타입이
UserQueryDto로 일관성 있게 변경되었습니다.
75-83: 쿼리 프로젝션에 aliasColor 필드 추가가 적절합니다
alias.color필드가 프로젝션에 추가되어 사용자 별칭 색상 정보를 포함하도록 개선되었습니다. 이는 통합된UserQueryDto의 새로운 필드와 일치합니다.
| @Setter | ||
| private LocalDateTime createdAt; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
createdAt 필드 수정 가능성에 대한 신중한 검토가 필요합니다.
Base 엔티티의 createdAt 필드에 @Setter를 추가한 것은 일반적인 도메인 규칙에 어긋날 수 있습니다. 생성 시간은 보통 불변이어야 하는데, 최근 검색 기능에서 "최근성" 업데이트를 위해 수정하는 것이 적절한지 검토가 필요합니다.
대안으로 다음을 고려해보세요:
- 최근 검색에서는 기존 레코드를 삭제하고 새로 생성
- 별도의
lastSearchedAt필드 추가 - RecentSearch만을 위한 별도 메서드 제공
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/common/entity/BaseDomainEntity.java around lines 13
to 14, the createdAt field currently has a @Setter which allows modification,
but createdAt should be immutable as it represents the creation timestamp.
Remove the @Setter annotation from createdAt to prevent external modification.
Instead, implement one of the suggested alternatives such as adding a separate
lastSearchedAt field or handling recent search updates by deleting and
recreating records, ensuring createdAt remains unchanged.
| @Override | ||
| public void delete(Long id) { | ||
| recentSearchJpaRepository.delete( | ||
| recentSearchJpaRepository.findById(id) | ||
| .orElseThrow(() -> new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND)) | ||
| ); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
delete 메서드 구현을 개선해주세요
현재 구현에서 findById와 delete를 연쇄 호출하고 있는데, deleteById를 사용하면 더 효율적입니다.
다음과 같이 개선해주세요:
@Override
public void delete(Long id) {
- recentSearchJpaRepository.delete(
- recentSearchJpaRepository.findById(id)
- .orElseThrow(() -> new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND))
- );
+ if (!recentSearchJpaRepository.existsById(id)) {
+ throw new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND);
+ }
+ recentSearchJpaRepository.deleteById(id);
}🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java
around lines 38 to 44, the delete method currently calls findById followed by
delete, which is inefficient. Replace this by directly using
recentSearchJpaRepository.deleteById(id) to improve performance and simplify the
code.
There was a problem hiding this comment.
오호 이런 구현 방식도 괜찮은거 같네요
There was a problem hiding this comment.
@coderabbitai 너가 제안한 delete 방식이 현재 구현된 방식보다 어떤 점에서 더 효율적인지 설명해줘
There was a problem hiding this comment.
@seongjunnoh 좋은 질문이네요! 제가 제안한 방식이 더 효율적인 이유를 설명드리겠습니다.
현재 구현 방식 vs 제안된 방식
현재 구현 (비효율적):
// 1단계: SELECT 쿼리로 전체 엔티티 조회
RecentSearchJpaEntity entity = recentSearchJpaRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND));
// 2단계: DELETE 쿼리 실행
recentSearchJpaRepository.delete(entity);제안된 구현 (효율적):
// 1단계: SELECT COUNT 쿼리로 존재 여부만 확인
if (!recentSearchJpaRepository.existsById(id)) {
throw new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND);
}
// 2단계: DELETE 쿼리 실행
recentSearchJpaRepository.deleteById(id);효율성 차이점
-
메모리 사용량:
- 현재: 전체 엔티티 객체를 메모리에 로드
- 제안: 존재 여부(boolean)만 확인
-
네트워크 트래픽:
- 현재: 모든 컬럼 데이터를 DB에서 가져옴
- 제안: 존재 여부만 확인 (훨씬 적은 데이터)
-
데이터베이스 연산:
- 현재:
SELECT * FROM recent_search WHERE id = ? - 제안:
SELECT 1 FROM recent_search WHERE id = ? LIMIT 1
- 현재:
-
JPA 최적화:
deleteById(id)는 JPA가 내부적으로 최적화된 DELETE 쿼리를 직접 실행delete(entity)는 영속성 컨텍스트에서 엔티티 관리 오버헤드 발생
특히 대용량 데이터나 빈번한 삭제 작업이 있는 환경에서 이런 최적화가 누적되면 상당한 성능 개선 효과를 가져올 수 있습니다!
✏️ Learnings added
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.918Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
There was a problem hiding this comment.
@seongjunnoh 제가 제안한 delete 방식이 더 효율적인 이유를 데이터베이스 쿼리와 메모리 관점에서 설명드리겠습니다.
현재 구현 방식의 문제점
// 현재 방식
recentSearchJpaRepository.delete(
recentSearchJpaRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND))
);실행되는 쿼리:
SELECT * FROM recent_search WHERE id = ?- 전체 엔티티 조회DELETE FROM recent_search WHERE id = ?- 삭제 실행
제안한 방식의 장점
// 제안한 방식
if (!recentSearchJpaRepository.existsById(id)) {
throw new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND);
}
recentSearchJpaRepository.deleteById(id);실행되는 쿼리:
SELECT 1 FROM recent_search WHERE id = ? LIMIT 1- 존재 여부만 확인DELETE FROM recent_search WHERE id = ?- 삭제 실행
구체적인 효율성 차이
1. 네트워크 트래픽 감소
- 현재: 전체 엔티티 데이터를 DB → 애플리케이션으로 전송
- 제안: 존재 여부(boolean)만 전송 → 데이터 전송량 대폭 감소
2. 메모리 사용량 감소
- 현재:
RecentSearchJpaEntity객체를 메모리에 생성 - 제안: 불필요한 객체 생성 없음 → 힙 메모리 절약
3. DB 성능 최적화
existsById(): DB 옵티마이저가 더 효율적인 실행 계획 수립 가능- 인덱스만으로 처리 가능, 실제 데이터 페이지 접근 불필요
실제 운영 환경에서는 이런 작은 최적화들이 누적되어 상당한 성능 차이를 만들어냅니다!
🧠 Learnings used
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
| @RequiredArgsConstructor | ||
| public class RecentSearchCreateManager { | ||
|
|
||
| private static final String USER_SEARCH_TERM = "사용자 검색"; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
하드코딩된 상수를 enum으로 개선 권장
"사용자 검색"이 하드코딩되어 있습니다. SearchType enum을 활용하거나 별도 상수 클래스에서 관리하는 것이 좋겠습니다.
- private static final String USER_SEARCH_TERM = "사용자 검색";
+ private static final SearchType USER_SEARCH_TYPE = SearchType.USER_SEARCH;그리고 line 32에서도 수정:
- RecentSearch userRecentSearch = RecentSearch.withoutId(keyword, USER_SEARCH_TERM, userId);
+ RecentSearch userRecentSearch = RecentSearch.withoutId(keyword, USER_SEARCH_TYPE.getDescription(), userId);🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java
at line 15, the string "사용자 검색" is hardcoded as a constant. Refactor this by
replacing the hardcoded string with a value from the SearchType enum or a
dedicated constants class to improve maintainability and consistency. Also,
apply the same change at line 32 where this string is used.
| public void saveRecentSearchByUser(Long userId, String keyword) { | ||
|
|
||
| // 동일 조건 (userId + keyword + type) 검색 기록이 이미 있는지 확인 | ||
| recentSearchQueryPort.findRecentSearchByKeywordAndUserId(keyword, userId) | ||
| .ifPresentOrElse( | ||
| existingRecentSearch -> { | ||
| // 이미 존재하면 createdAt만 갱신 | ||
| existingRecentSearch.updateCreatedAt(LocalDateTime.now()); | ||
| recentSearchCommandPort.update(existingRecentSearch); | ||
| }, | ||
| () -> { | ||
| // 없으면 새로 저장 | ||
| RecentSearch userRecentSearch = RecentSearch.withoutId(keyword, USER_SEARCH_TERM, userId); | ||
| recentSearchCommandPort.save(userRecentSearch); | ||
| } | ||
| ); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
트랜잭션 관리 추가 고려
메서드에서 조회 후 저장/업데이트 작업을 수행하고 있습니다. 데이터 일관성을 위해 @Transactional 어노테이션 추가를 고려해보세요.
+ @Transactional
public void saveRecentSearchByUser(Long userId, String keyword) {📝 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.
| public void saveRecentSearchByUser(Long userId, String keyword) { | |
| // 동일 조건 (userId + keyword + type) 검색 기록이 이미 있는지 확인 | |
| recentSearchQueryPort.findRecentSearchByKeywordAndUserId(keyword, userId) | |
| .ifPresentOrElse( | |
| existingRecentSearch -> { | |
| // 이미 존재하면 createdAt만 갱신 | |
| existingRecentSearch.updateCreatedAt(LocalDateTime.now()); | |
| recentSearchCommandPort.update(existingRecentSearch); | |
| }, | |
| () -> { | |
| // 없으면 새로 저장 | |
| RecentSearch userRecentSearch = RecentSearch.withoutId(keyword, USER_SEARCH_TERM, userId); | |
| recentSearchCommandPort.save(userRecentSearch); | |
| } | |
| ); | |
| } | |
| @Transactional | |
| public void saveRecentSearchByUser(Long userId, String keyword) { | |
| // 동일 조건 (userId + keyword + type) 검색 기록이 이미 있는지 확인 | |
| recentSearchQueryPort.findRecentSearchByKeywordAndUserId(keyword, userId) | |
| .ifPresentOrElse( | |
| existingRecentSearch -> { | |
| // 이미 존재하면 createdAt만 갱신 | |
| existingRecentSearch.updateCreatedAt(LocalDateTime.now()); | |
| recentSearchCommandPort.update(existingRecentSearch); | |
| }, | |
| () -> { | |
| // 없으면 새로 저장 | |
| RecentSearch userRecentSearch = RecentSearch.withoutId(keyword, USER_SEARCH_TERM, userId); | |
| recentSearchCommandPort.save(userRecentSearch); | |
| } | |
| ); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java
around lines 20 to 36, the method performs read and write operations that should
be atomic to maintain data consistency. Add the @Transactional annotation to the
saveRecentSearchByUser method or the enclosing class to ensure the entire
operation runs within a transaction boundary, preventing partial updates or
inconsistent state.
| public void updateCreatedAt(LocalDateTime localDateTime) { | ||
| this.setCreatedAt(localDateTime); | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
가변성 설계에 대한 검토가 필요합니다.
updateCreatedAt 메서드가 setCreatedAt을 호출하여 도메인 객체의 상태를 변경하고 있습니다. 이는 BaseDomainEntity가 가변 상태를 허용하도록 수정되었음을 의미합니다. 도메인 객체의 불변성 원칙과 현재 설계 선택에 대한 검토가 필요합니다.
다음 스크립트로 BaseDomainEntity의 변경사항을 확인해 주세요:
🏁 Script executed:
#!/bin/bash
# Description: BaseDomainEntity의 setCreatedAt 메서드 존재 여부와 가변성 설계 확인
# BaseDomainEntity 클래스에서 setCreatedAt 메서드 찾기
ast-grep --pattern $'class BaseDomainEntity {
$$$
setCreatedAt($_) {
$$$
}
$$$
}'
# 또는 createdAt 필드와 관련된 setter 메서드들 확인
rg -A 5 -B 5 "setCreatedAt|createdAt.*=" src/main/java/konkuk/thip/common/entity/Length of output: 950
도메인 객체의 가변성 (createdAt) 재검토 필요
RecentSearch.updateCreatedAt 메서드가 BaseJpaEntity의 프로텍티드 setter를 호출하여 createdAt을 변경하고 있습니다.
- 확인된 setter 위치
- 파일:
src/main/java/konkuk/thip/common/entity/BaseJpaEntity.java - 메서드:
protected void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
- 파일:
도메인 레벨에서 createdAt 변경을 허용할지, 불변성(immutability) 전략을 다시 검토해 주세요.
- 불변성을 유지하려면 setter 및
updateCreatedAt제거 또는 별도 빌더/팩토리 패턴, 도메인 이벤트 등 대안 적용을 제안합니다.
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/recentSearch/domain/RecentSearch.java around lines
29 to 31, the updateCreatedAt method modifies the createdAt field via a
protected setter in the base entity, which breaks domain immutability. To fix
this, remove the updateCreatedAt method and avoid exposing any setter for
createdAt. Instead, ensure createdAt is set only once during entity creation,
possibly by using a constructor, builder, or factory method, and consider domain
events if updates are needed without breaking immutability.
| public record UserSearchQuery( | ||
| String keyword, | ||
| Long userId, | ||
| Integer size | ||
| ) { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
필수: 입력 값 검증 로직을 추가해 주세요.
keyword‧size 필드에 대한 null/범위 검증이 전혀 없어 서비스 레이어까지 잘못된 값이 전파될 수 있습니다.
레코드의 Canonical Constructor를 오버라이드하여 불변 조건을 선언적으로 보장하거나, 최소한 Objects.requireNonNull 및 require(size > 0) 수준의 체크를 권장드립니다.
public record UserSearchQuery(
String keyword,
Long userId,
Integer size
) {
+ public UserSearchQuery {
+ Objects.requireNonNull(keyword, "keyword must not be null");
+ if (keyword.isBlank()) {
+ throw new IllegalArgumentException("keyword must not be blank");
+ }
+ Objects.requireNonNull(size, "size must not be null");
+ if (size <= 0) {
+ throw new IllegalArgumentException("size must be positive");
+ }
+ }📝 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.
| public record UserSearchQuery( | |
| String keyword, | |
| Long userId, | |
| Integer size | |
| ) { | |
| public record UserSearchQuery( | |
| String keyword, | |
| Long userId, | |
| Integer size | |
| ) { | |
| public UserSearchQuery { | |
| Objects.requireNonNull(keyword, "keyword must not be null"); | |
| if (keyword.isBlank()) { | |
| throw new IllegalArgumentException("keyword must not be blank"); | |
| } | |
| Objects.requireNonNull(size, "size must not be null"); | |
| if (size <= 0) { | |
| throw new IllegalArgumentException("size must be positive"); | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/user/application/port/in/dto/UserSearchQuery.java
around lines 3 to 7, the UserSearchQuery record lacks validation for the keyword
and size fields, allowing null or invalid values to propagate. Override the
canonical constructor of the record to add checks that keyword is not null and
size is not null and greater than zero, using Objects.requireNonNull for null
checks and explicit condition checks for size. This will ensure immutability and
enforce input validation at the time of object creation.
src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java
Outdated
Show resolved
Hide resolved
| List.of("thipalpha", "thipbeta", "123thip", "thipgamma", "otheruser") | ||
| .forEach(nickname -> userJpaRepository.save(TestEntityFactory.createUser(alias, nickname))); |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
테스트 데이터의 검색 가중치 검증 필요
PR 목표에 따르면 검색 결과는 가중치에 따라 정렬되어야 합니다 (정확히 일치=3, 시작=2, 포함=1). 현재 테스트 데이터와 예상 결과가 이 가중치 로직을 올바르게 반영하는지 확인이 필요합니다.
다음 스크립트를 실행하여 사용자 검색 서비스의 정렬 로직을 확인해보세요:
🏁 Script executed:
#!/bin/bash
# UserSearchService의 검색 정렬 로직 확인
ast-grep --pattern 'class UserSearchService {
$$$
}'Length of output: 56
아래 스크립트를 실행하여 UserSearchService 클래스 위치와 검색 정렬 로직(가중치 적용)을 재확인해 주세요.
🏁 Script executed:
#!/bin/bash
# UserSearchService 클래스 파일 위치 확인
fd UserSearchService.java || true
# 클래스 정의 및 상위 20줄 확인
rg -n "class UserSearchService" -A 20 || true
# 검색 메서드 및 정렬 로직 파악 (메서드명에 search 포함)
rg -n "search" -A 10 -B 5 || true
# 가중치 관련 키워드(search weight) 탐색
rg -n "weight" -A 5 -B 5 || trueLength of output: 167854
아래 스크립트를 실행해 findUsersByNicknameOrderByAccuracy 메서드가 선언된 인터페이스와 그 구현체 위치를 확인하고, 실제 정렬(가중치) 로직이 어디에 정의되어 있는지 파악해 주세요.
🏁 Script executed:
#!/bin/bash
# 1. UserQueryPort 인터페이스 위치 확인
rg -n "interface UserQueryPort" -A 5 -B 5 || true
# 2. findUsersByNicknameOrderByAccuracy 선언/구현 검색
rg -n "findUsersByNicknameOrderByAccuracy" -A 10 -B 5 || true
# 3. UserJpaRepository 에서 유사 메서드 검색 (JPA 메서드 이름 패턴)
rg -n "findByNickname.*OrderByAccuracy" -A 5 -B 5 || trueLength of output: 8347
아직 테스트에서 가중치 기반 정렬 순서(정확히 일치→접두사 일치→포함 일치)가 검증되지 않은 것 같습니다. UserSearchApiTest에 아래와 같이 순서 검증을 추가해 주세요.
- 파일: src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java
- 검색 결과 리스트의 각 인덱스별 nickname이 기대값과 일치하는지 검증
예시 추가 코드:
// then
result.andExpect(jsonPath("$.data.userList[0].nickname").value("thip")) // 정확히 일치
.andExpect(jsonPath("$.data.userList[1].nickname").value("thipalpha")) // 접두사 일치
.andExpect(jsonPath("$.data.userList[2].nickname").value("thipbeta")) // 접두사 일치
.andExpect(jsonPath("$.data.userList[3].nickname").value("thipgamma")) // 접두사 일치
.andExpect(jsonPath("$.data.userList[4].nickname").value("123thip")); // 포함 일치이렇게 하면 findUsersByNicknameOrderByAccuracy 의 CaseBuilder 가중치(3/2/1)가 실제 API 테스트에 반영되었는지 검증할 수 있습니다.
🤖 Prompt for AI Agents
In src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java around
lines 60 to 61, the test does not verify the order of search results based on
weighted sorting (exact match → prefix match → contains match). Add assertions
after the search execution to check that the nicknames in the result list match
the expected order by index, using jsonPath to verify each nickname corresponds
to the correct weighted match category as shown in the example. This ensures the
accuracy-based sorting logic is properly tested in the API.
seongjunnoh
left a comment
There was a problem hiding this comment.
고생하셨습니다! 리뷰 확인 부탁드립니다!
| public record UserQueryDto(Long userId, | ||
| String nickname, | ||
| String profileImageUrl, | ||
| String aliasName, | ||
| String aliasColor, | ||
| Integer followerCount, | ||
| LocalDateTime createdAt) { |
There was a problem hiding this comment.
LGTM
UserQueryDto 라는 네이밍이 훨씬 직관적인 것 같습니다
| return queryFactory | ||
| .select(new QUserQueryDto( | ||
| user.userId, | ||
| user.nickname, | ||
| alias.imageUrl, | ||
| alias.value, | ||
| alias.color, | ||
| user.followerCount, | ||
| user.createdAt | ||
| )) | ||
| .from(user) | ||
| .leftJoin(user.aliasForUserJpaEntity, alias) | ||
| .where(user.nickname.like(pattern) | ||
| .and(user.userId.ne(userId)) | ||
| .and(user.status.eq(StatusType.ACTIVE))) | ||
| .orderBy(priority.desc(), user.nickname.asc()) | ||
| .limit(size) | ||
| .fetch(); | ||
| } |
There was a problem hiding this comment.
like 명령어를 사용하는 방식으로 다시 수정하셨군요! 이것도 좋은 것 같습니다! 추후에 개선 방법을 같이 논의해봅시다!
There was a problem hiding this comment.
필터링은 크게 like 명령어로 걸지만, 그 안에서 정렬은 가중치를 매기는 방식을 사용했습니다!!
다른 방법은 추후에 인프라를 조정하면서 한번 생각해봐야될 것 같아요,,
| NumberExpression<Integer> priority = new CaseBuilder() | ||
| .when(user.nickname.eq(keyword)).then(3) | ||
| .when(user.nickname.like(keyword + "%")).then(2) | ||
| .when(user.nickname.like(pattern)).then(1) | ||
| .otherwise(0); |
| .where(user.nickname.like(pattern) | ||
| .and(user.userId.ne(userId)) | ||
| .and(user.status.eq(StatusType.ACTIVE))) | ||
| .orderBy(priority.desc(), user.nickname.asc()) |
| // 동일 조건 (userId + keyword + type) 검색 기록이 이미 있는지 확인 | ||
| recentSearchQueryPort.findRecentSearchByKeywordAndUserId(keyword, userId) | ||
| .ifPresentOrElse( |
There was a problem hiding this comment.
p3 : 왜 메서드의 인자값으로 type은 넘기지 않고, 메서드 내부에서 jpa repository 를 호출할때 USER_SEARCH 를 주입해주는 건가요?
There was a problem hiding this comment.
이 부분은 제가 SearchType을 서비스 로직에서 import하여 쓰지 않은 이유와 일맥상통합니다! 다만, 뒷 pr에서 정의한 RecentSearchType을 여기서 활용할 수 있겠네요! 한번 수정해보겠습니다.
| // 이미 존재하면 createdAt만 갱신 | ||
| existingRecentSearch.updateCreatedAt(LocalDateTime.now()); | ||
| recentSearchCommandPort.update(existingRecentSearch); | ||
| }, |
There was a problem hiding this comment.
p3 : 엇 createdAt이 udpate 안되지 않나요??
There was a problem hiding this comment.
네넵 그걸 뒷 pr에서 발견해서 뒤쪽 pr에서 수정하였습니다! 혼란을 드렸네요,, 지송
| }, | ||
| () -> { | ||
| // 없으면 새로 저장 | ||
| RecentSearch userRecentSearch = RecentSearch.withoutId(keyword, USER_SEARCH_TERM, userId); |
There was a problem hiding this comment.
p3 : 지금처럼 RecentSearchCreateManager에서 상수로 선언한 String 값을 주입해주는 구조보다는 유지보수를 위해
RecentSearch를 생성할 때 SearchType.USER_SEARCH.getSearchType() 을 주입
하는 방식으로 수정하는건 어떤가요?
현재 User 등 여러 도메인 엔티티에서 위와 같은 방식으로 코드가 작성되어 있긴 합니다!
public class User extends BaseDomainEntity {
private Long id;
private String nickname;
private String userRole;
private String oauth2Id;
private Integer followerCount; // 팔로워 수
private Alias alias;
public static User withoutId(String nickname, String userRole, String oauth2Id, Alias alias) {
return User.builder()
.id(null)
.nickname(nickname)
.userRole(userRole)
.oauth2Id(oauth2Id)
.followerCount(0)
.alias(alias)
.build();
}
}
public Long signup(UserSignupCommand command) {
Alias alias = Alias.from(command.aliasName());
User user = User.withoutId(
command.nickname(), USER.getType(), command.oauth2Id(), alias
);
return userCommandPort.save(user);
}There was a problem hiding this comment.
사실 처음에 이렇게 고려하려고 했습니다! 다만, SearchType은 저희가 adapter.out 계층에서 사용중인 Enum 클래스이기 때문에 해당 클래스를 서비스가 import하고 있는 것은 적절하지 않다고 판단했습니다!
뒷 pr 리뷰하시면 보실 수 있겠지만, Domain 차원에서 param과 SearchType.searchType들을 매핑지어서 활용하도록 했습니다!!
There was a problem hiding this comment.
뒷 pr에 관련해서 코멘트 달았습니다!
| @Override | ||
| public void delete(Long id) { | ||
| recentSearchJpaRepository.delete( | ||
| recentSearchJpaRepository.findById(id) | ||
| .orElseThrow(() -> new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND)) | ||
| ); | ||
| } |
There was a problem hiding this comment.
오호 이런 구현 방식도 괜찮은거 같네요
| @Override | ||
| public void delete(Long id) { | ||
| recentSearchJpaRepository.delete( | ||
| recentSearchJpaRepository.findById(id) | ||
| .orElseThrow(() -> new EntityNotFoundException(RECENT_SEARCH_NOT_FOUND)) | ||
| ); | ||
| } |
There was a problem hiding this comment.
@coderabbitai 너가 제안한 delete 방식이 현재 구현된 방식보다 어떤 점에서 더 효율적인지 설명해줘
#️⃣ 연관된 이슈
📝 작업 내용
정확도순 구현을 위해 다음과 같은 방식을 사용했습니다.
RecentSearchManager 구현
📸 스크린샷
💬 리뷰 요구사항
기존에 구현했던 팔로우 관련 조회 api에서 사용됐던 FollowQueryDto과 이번 api에서도 반환값이 비슷한 것 같아서 UserQueryDto로 통합하여 재사용하였습니다. 리뷰할때 참고바랍니다!
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit
신규 기능
기능 개선
버그 수정
테스트