Conversation
…lyone-Back into test/search/chan
WalkthroughClub 엔티티에 Long형 memberCount 필드를 추가하고 증감 메서드를 도입했으며, 리포지토리 쿼리들을 userClub 조인/COUNT 기반에서 club.memberCount 컬럼 기반으로 변경하고 정렬/인덱스를 갱신했습니다. ClubService에 생성/가입/탈퇴 시 memberCount 동기화가 추가되었습니다. 보안 화이트리스트와 CORS 패턴도 확장되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant S as ClubService
participant R as UserClubRepository
participant C as Club(Entity)
rect rgba(220,240,255,0.5)
note over U,S: 클럽 생성
U->>S: createClub(...)
S->>R: save(leader UserClub)
R-->>S: persisted
S->>C: incrementMemberCount()
end
rect rgba(220,255,220,0.5)
note over U,S: 클럽 가입
U->>S: joinClub(clubId)
S->>R: save(member UserClub)
R-->>S: persisted
S->>C: incrementMemberCount()
end
rect rgba(255,235,220,0.5)
note over U,S: 클럽 탈퇴
U->>S: leaveClub(clubId)
S->>R: deleteBy(user, club)
R-->>S: deleted
S->>C: decrementMemberCount() %% 하한 0
end
sequenceDiagram
autonumber
participant API as SearchService
participant Q as ClubRepositoryImpl
participant DB as DB
API->>Q: searchByKeywordWithFilter(filter)
note over Q: INNER JOIN interest\nmemberCount 컬럼 사용\n키워드 스코어 > 4.0
Q->>DB: SELECT club, club.member_count ... ORDER BY score?, member_count, created_at
DB-->>Q: rows [club, memberCount]
Q-->>API: results (cast memberCount to Long)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/example/onlyone/global/config/SecurityConfig.java (1)
75-82: ngrok 와일드카드 Origin 제거 및 CORS 설정 외부화https://*.ngrok-free.app 과 setAllowCredentials(true) 조합은 운영 환경에서 임의 서브도메인에 쿠키 포함 요청을 허용해 보안 취약을 초래합니다. 개발 프로파일로 분리하거나 환경 변수(
app.allowed-origin-patterns)로 제어하고, 운영에서는 명확한 Origin만 화이트리스트에 등록하세요.- configuration.setAllowedOriginPatterns(List.of( + configuration.setAllowedOriginPatterns(List.of( "http://localhost:8080", "http://localhost:5173", "https://only-one-front-delta.vercel.app", - "https://*.ngrok-free.app", - baseUrl + // 운영 환경 제거: "https://*.ngrok-free.app", + baseUrl ));
🧹 Nitpick comments (7)
src/main/java/com/example/onlyone/global/config/SecurityConfig.java (1)
131-131: 중복 허용 규칙 정리 제안:/ws-native가 두 곳에서 permitAll 됩니다.
AUTH_WHITELIST와authorizeHttpRequests모두에/ws-native가 있어 유지보수성이 떨어집니다. 한 곳으로 통일해 주세요.- .requestMatchers("/ws-native", "/ws-native/**").permitAll() + // AUTH_WHITELIST에 이미 포함되어 있으면 여기서는 제거src/main/java/com/example/onlyone/domain/club/service/ClubService.java (1)
101-113: 상세 조회 인원수는 memberCount 컬럼을 사용해 일관성/성능을 확보하세요.
countByClub_ClubId대신club.getMemberCount()사용이 가볍고, 검색/정렬에서도 동일 소스(memberCount)를 사용하므로 일관됩니다.- int userCount = userClubRepository.countByClub_ClubId(club.getClubId()); + long userCount = club.getMemberCount();src/main/java/com/example/onlyone/domain/search/service/SearchService.java (2)
167-167: 캐스팅을 Long으로 고정하면 DB/드라이버에 따라 ClassCastException 위험이 있습니다. Number 기반 변환으로 복원력 확보.- .memberCount((Long) result[6]) + .memberCount(((Number) result[6]).longValue())
156-158: Enum → String → Enum 왕복 변환은 불필요합니다. Repo에서 Category를 그대로 반환하도록 바꾸면 안전하고 간결합니다.Repo 매핑 변경 제안(아래 RepositoryImpl 코멘트 참고) 후:
- String categoryName = (String) result[5]; - String koreanCategoryName = Category.valueOf(categoryName).getKoreanName(); + Category category = (Category) result[5]; + String koreanCategoryName = category.getKoreanName();src/main/java/com/example/onlyone/domain/club/entity/Club.java (1)
17-21: 정렬이 memberCount 중심으로 바뀐 만큼 인덱스에 member_count 포함을 고려하세요.현재 인덱스는 created_at 중심입니다. 다음 조합(예: Flyway SQL로 생성)을 권장합니다.
- (interest_id, city, district, member_count DESC, created_at DESC)
- (interest_id, member_count DESC, created_at DESC)
- (city, district, member_count DESC, created_at DESC)
- (member_count DESC, created_at DESC)
JPA @Index로는 DESC 표기가 이식성에 제한이 있으니, SQL 마이그레이션으로 명시 생성하는 것이 안전합니다.
src/main/java/com/example/onlyone/domain/club/repository/ClubRepositoryImpl.java (2)
82-90: 카테고리를 문자열로 변환했다가 서비스에서 다시 Enum으로 복원하는 왕복 변환을 제거하세요.- tuple.get(interest.category).name(), // Category enum을 String으로 변환 + tuple.get(interest.category), // Category Enum 그대로 전달위 변경 시 SearchService의 캐스팅/한글명 조회도 단순화됩니다(해당 파일 코멘트 참고).
21-21: 불필요한 import 정리 제안(Projections 등).가독성을 위해 미사용 import 제거를 권장합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/main/java/com/example/onlyone/domain/club/entity/Club.java(3 hunks)src/main/java/com/example/onlyone/domain/club/repository/ClubRepositoryImpl.java(6 hunks)src/main/java/com/example/onlyone/domain/club/service/ClubService.java(3 hunks)src/main/java/com/example/onlyone/domain/search/service/SearchService.java(1 hunks)src/main/java/com/example/onlyone/global/config/SecurityConfig.java(4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-06T07:54:25.923Z
Learnt from: ghkddlscks19
PR: GoormOnlyOne/OnlyOne-Back#86
File: src/main/java/com/example/onlyone/domain/club/repository/ClubRepository.java:14-18
Timestamp: 2025-08-06T07:54:25.923Z
Learning: In the OnlyOne-Back project, the ClubRepository search methods use List<Object[]> return type instead of Page<Object[]> even with Pageable parameters. This is intentionally designed for infinite scrolling implementation on the frontend, where only the actual data is needed without pagination metadata.
Applied to files:
src/main/java/com/example/onlyone/domain/club/repository/ClubRepositoryImpl.java
📚 Learning: 2025-08-06T08:12:05.969Z
Learnt from: ghkddlscks19
PR: GoormOnlyOne/OnlyOne-Back#86
File: src/main/java/com/example/onlyone/domain/club/repository/ClubRepository.java:66-70
Timestamp: 2025-08-06T08:12:05.969Z
Learning: OnlyOne-Back 프로젝트의 검색 시스템은 관심사, 지역, 최신순, 멤버순 필터가 병렬적으로 선택 가능하도록 설계되어 있어서, ORDER BY 절에서 여러 CASE 문을 사용하여 다중 정렬 조건을 동시에 적용할 수 있습니다.
Applied to files:
src/main/java/com/example/onlyone/domain/club/repository/ClubRepositoryImpl.java
🔇 Additional comments (3)
src/main/java/com/example/onlyone/global/config/SecurityConfig.java (1)
120-121: SSE 필터 → JWT 필터 순서 배치는 적절합니다.SSE 경로 선처리 후 JWT 인증으로 이어지는 체인이 의도에 부합합니다.
src/main/java/com/example/onlyone/domain/club/repository/ClubRepositoryImpl.java (2)
60-62: 정렬 스펙 분리 좋습니다. 키워드/비키워드 케이스가 명확해졌습니다.
243-247: FULLTEXT 함수 템플릿 사용 확인 필요: 모드/검색 필드 구성과 임계치(>4.0)의 정합성 검증 권장.현재
function('match', {0}, {1}, {2})로 등록된 사용자 함수가 MySQLMATCH(name, description) AGAINST (? IN NATURAL LANGUAGE MODE)에 매핑되는지 확인이 필요합니다. BOOLEAN/NATURAL 모드에 따라 점수 스케일이 달라 4.0 임계값 영향이 큽니다.원한다면 문서화/테스트 케이스(키워드 vs 비키워드, 점수 분포) 작성 도와드릴게요.
src/main/java/com/example/onlyone/domain/club/repository/ClubRepositoryImpl.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/main/java/com/example/onlyone/domain/club/entity/Club.java (1)
59-61: 마이그레이션/백필 누락 시 운영 장애 가능 — 기존 데이터에 대해 NULL 제거·백필을 보장하세요.
- 기존 club 레코드의 member_count를 user_club 기준으로 역산/0 세팅 후 NOT NULL 제약 적용 순서를 지켜 주세요.
- 배포 전/후 검증 쿼리와 롤백 플랜 포함 권장.
원하시면 Flyway/Liquibase 스크립트 초안을 드리겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/main/java/com/example/onlyone/domain/club/entity/Club.java(3 hunks)
🔇 Additional comments (2)
src/main/java/com/example/onlyone/domain/club/entity/Club.java (2)
59-61: LGTM: memberCount 필드에 nullable=false와 기본값 0L 적용이 적절합니다.이전 위험(NPE/DB 제약 위반) 해소에 도움이 됩니다.
17-23: 인덱스 DDL 휴대성·중복성 검토 필요
- 휴대성:
@Index(columnList)에"DESC"지정 시, H2 등 일부 Dialect에서 DDL 오류가 발생할 수 있습니다. 마이그레이션 스크립트로 명시적 DDL 관리하거나"DESC"제거를 검토하세요.- 중복성:
•(interest_id, member_count DESC, created_at DESC)와(interest_id, city, district, member_count DESC, created_at DESC)가 각각(interest_id)및(interest_id, city, district)를 좌측 접두어로 포함합니다.
• 실제 쿼리 플랜에서 사용 여부를 확인한 뒤, 불필요한 인덱스를 제거해 쓰기 오버헤드 및 스토리지 절감하세요.- 매핑:
BaseTimeEntity에@Column(name="created_at")가 정의되어 있어 인덱스 대상 컬럼 매핑은 올바릅니다.
|
수고하셨습니다! |
[fix] 통합검색 성능 최적화 및 FULLTEXT 검색 개선
#️⃣ Issue Number
📝 요약(Summary)
🛠️ PR 유형
어떤 변경 사항이 있나요?
📸스크린샷 (선택)
💬 공유사항 to 리뷰어
✅ PR Checklist
PR이 다음 요구 사항을 충족하는지 확인하세요.
Summary by CodeRabbit