feat: 신규 동아리 등록 요청 API 추가#636
Conversation
- 웹사이트 전용 엔티티가 사용하는 서비스와 DTO의 도메인 경계에 놓이도록 패키지를 이동 - QueryDSL static import와 테스트 fixture import를 함께 정리해 생성 클래스 경로 불일치를 방지 - 테이블명은 유지해 DB 스키마 변경 없이 Java 패키지 경계만 정리
|
Warning Review limit reached
Your plan currently allows 1 review/hour. Refill in 14 minutes and 35 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (12)
📝 WalkthroughWalkthrough이 PR은 비로그인 사용자가 동아리 등록 요청을 제출할 수 있는 새로운 API 엔드포인트를 추가하고, 웹 도메인 모델을 패키지 구조 개선을 위해 마이그레이션합니다. 동아리 등록 요청은 데이터베이스에 ChangesClub Registration Request Feature
Web Domain Package Refactoring
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as ClubRegistrationRequestController
participant Service as ClubRegistrationRequestService
participant Repo as Repository
participant Slack as SlackNotificationService
participant DB as Database
Client->>Controller: POST /clubs/registration-requests<br/>ClubRegistrationRequestDto
Controller->>Controller: `@Valid` validation
Controller->>Service: register(dto)
Service->>Service: build ClubRegistrationRequest<br/>with PENDING status
Service->>Repo: save(entity)
Repo->>DB: INSERT club_registration_request
DB-->>Repo: entity with id
Repo-->>Service: saved entity
Service->>Slack: notifyClubRegistrationRequest<br/>(id, details)
Slack->>Slack: format message template
Slack-->>Service: message sent
Service-->>Controller: void
Controller-->>Client: 200 OK
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
🧪 JaCoCo Coverage Report (Changed Files)Summary
Coverage by File
|
There was a problem hiding this comment.
Actionable comments posted: 4
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/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.java (1)
17-18:⚠️ Potential issue | 🟠 Major | ⚡ Quick win[LEVEL: medium]
university필드 required 문서화가 실제 응답과 불일치합니다.
문제: Line 17에서requiredMode = REQUIRED로 선언했지만 Line 143의recent()는university에null을 반환합니다.
영향:/konect/clubs/recent를 OpenAPI 스키마 기준으로 사용하는 클라이언트에서 non-null 가정이 깨져 검증/역직렬화 오류가 발생할 수 있으니NOT_REQUIRED로 문서를 수정하거나 recent 전용 DTO로 분리해 계약을 일치시켜 주세요; Based on learnings:Schema(requiredMode = REQUIRED)필드는 코드/DB에서 non-null 보장이 필요합니다.Also applies to: 141-144
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.java` around lines 17 - 18, The Schema for the university field in WebsiteClubsResponse is marked required (requiredMode = REQUIRED) but recent() returns null for university; change the contract to match runtime behavior by either setting the university field's `@Schema`(requiredMode = NOT_REQUIRED) in WebsiteClubsResponse or create a dedicated DTO (e.g., WebsiteClubsRecentResponse) used by recent() where university is nullable; update usages of WebsiteClubsResponse/recent() to use the chosen approach so OpenAPI matches actual responses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestController.java`:
- Line 31: The controller currently returns 200 for create requests; update the
POST handler in ClubRegistrationRequestController to return 201 by replacing
ResponseEntity.ok().build() with
ResponseEntity.status(HttpStatus.CREATED).build(), and import or reference
HttpStatus as needed so the create endpoint follows REST semantics.
In
`@src/main/java/gg/agit/konect/domain/club/dto/ClubRegistrationRequestDto.java`:
- Around line 49-52: The DTO field imageUrls in ClubRegistrationRequestDto is
annotated `@NotNull` but the entity column image_urls and service null-handling
allow nulls, causing a contract mismatch; either remove the `@NotNull` (and update
the `@Schema` requiredMode from REQUIRED to optional/not required) so the DTO
accepts absent images while keeping `@Size`(max=5), or make the requirement
consistent by marking the DB column non-null, removing null-handling in the
service, and keeping `@NotNull` on imageUrls—pick one approach and apply it
consistently across ClubRegistrationRequestDto (imageUrls), the entity
(image_urls nullability), and the service null checks.
In
`@src/main/java/gg/agit/konect/domain/club/service/ClubRegistrationRequestService.java`:
- Around line 35-44: The Slack notification is being called inside the DB
transaction (slackNotificationService.notifyClubRegistrationRequest with
saved.getId() and request.* fields), so Slack failures can roll back the save;
move notification out of the transactional boundary by publishing an event after
saving (e.g., create and publish a ClubRegistrationRequestedEvent containing
saved.getId() and the request data) and handle it in a listener annotated with
`@TransactionalEventListener`(phase = AFTER_COMMIT) or via an async executor;
ensure the listener calls slackNotificationService.notifyClubRegistrationRequest
and isolates failures (catch/log and implement retry/backoff) so notification
errors don’t propagate back to the save flow.
In `@src/main/java/gg/agit/konect/global/config/SecurityPaths.java`:
- Line 16: 현재 PUBLIC_PATHS에 있는 "/clubs/registration-requests"를 단순 경로 허용으로 두면 모든
HTTP 메서드가 인증 없이 허용될 수 있으니 SecurityConfig에서 메서드 수준으로 제한하세요: PUBLIC_PATHS에서 해당
문자열을 제거하고 SecurityConfig의 해당 보안 체인에 requestMatchers(HttpMethod.POST,
"/clubs/registration-requests").permitAll()을 추가해 POST만 익명 접근을 허용하도록 변경하며, 다른
메서드는 적절한 권한 검사(예: authenticated() 또는 hasRole(...))로 처리되도록 구성하세요.
---
Outside diff comments:
In `@src/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.java`:
- Around line 17-18: The Schema for the university field in WebsiteClubsResponse
is marked required (requiredMode = REQUIRED) but recent() returns null for
university; change the contract to match runtime behavior by either setting the
university field's `@Schema`(requiredMode = NOT_REQUIRED) in WebsiteClubsResponse
or create a dedicated DTO (e.g., WebsiteClubsRecentResponse) used by recent()
where university is nullable; update usages of WebsiteClubsResponse/recent() to
use the chosen approach so OpenAPI matches actual responses.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: fa4db8ba-8e2e-4970-b9fb-a4e32021cf7f
📒 Files selected for processing (20)
src/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestApi.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestController.javasrc/main/java/gg/agit/konect/domain/club/dto/ClubRegistrationRequestDto.javasrc/main/java/gg/agit/konect/domain/club/model/ClubRegistrationRequest.javasrc/main/java/gg/agit/konect/domain/club/repository/ClubRegistrationRequestRepository.javasrc/main/java/gg/agit/konect/domain/club/service/ClubRegistrationRequestService.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubDetailResponse.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.javasrc/main/java/gg/agit/konect/domain/website/model/WebClub.javasrc/main/java/gg/agit/konect/domain/website/model/WebUniversity.javasrc/main/java/gg/agit/konect/domain/website/repository/WebsiteQueryRepository.javasrc/main/java/gg/agit/konect/domain/website/service/WebsiteService.javasrc/main/java/gg/agit/konect/global/config/SecurityPaths.javasrc/main/java/gg/agit/konect/infrastructure/slack/enums/SlackMessageTemplate.javasrc/main/java/gg/agit/konect/infrastructure/slack/service/SlackNotificationService.javasrc/main/resources/db/migration/V77__create_club_registration_request.sqlsrc/test/java/gg/agit/konect/integration/domain/club/ClubRegistrationRequestApiTest.javasrc/test/java/gg/agit/konect/integration/domain/website/WebsiteApiTest.javasrc/test/java/gg/agit/konect/support/fixture/WebClubFixture.javasrc/test/java/gg/agit/konect/support/fixture/WebUniversityFixture.java
📜 Review details
⏰ 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). (2)
- GitHub Check: coverage
- GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.java
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.java: Java 코드에서 import로 해결할 수 있는 경우 FQCN(Full Qualified Class Name)을 사용하지 않도록 지적한다
JPA/QueryDSL 조회 변경 시 N+1, 잘못된 fetch join, count 쿼리 왜곡, pagination 깨짐, distinct 누락을 확인한다
권한 로직은 관리자 우회, 요청자와 대상자 관계, 클럽/채팅방/공지/일정의 소속 검증이 빠지지 않았는지 확인한다
soft delete, 탈퇴 사용자, 차단/제외 조건, 중복 제거가 필요한 조회에서는 응답에 노출되면 안 되는 데이터가 포함되는지 확인한다
DTO 응답 변경은 기존 클라이언트가 기대하는 필드명, nullability, enum/string 값, 정렬 순서를 깨지 않는지 확인한다
조건이 2개 이상 결합된 비즈니스 규칙, 권한 조건, soft delete 제외, 중복 제거, fallback 우선순위, 대표값 선택, DTO 변환, count 쿼리 분리, fetch join 선택 이유처럼 코드만으로 의도가 숨겨지는 지점에는 주석을 권장한다
단순 생성자 호출, 필드 매핑, 컬렉션 반환, 이름만으로 명확한 분기에는 주석을 요구하지 않는다
Files:
src/main/java/gg/agit/konect/domain/website/model/WebClub.javasrc/main/java/gg/agit/konect/domain/club/repository/ClubRegistrationRequestRepository.javasrc/test/java/gg/agit/konect/integration/domain/website/WebsiteApiTest.javasrc/test/java/gg/agit/konect/support/fixture/WebUniversityFixture.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestApi.javasrc/main/java/gg/agit/konect/domain/website/service/WebsiteService.javasrc/main/java/gg/agit/konect/infrastructure/slack/service/SlackNotificationService.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubDetailResponse.javasrc/main/java/gg/agit/konect/domain/club/service/ClubRegistrationRequestService.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestController.javasrc/main/java/gg/agit/konect/domain/website/repository/WebsiteQueryRepository.javasrc/main/java/gg/agit/konect/domain/club/dto/ClubRegistrationRequestDto.javasrc/test/java/gg/agit/konect/support/fixture/WebClubFixture.javasrc/test/java/gg/agit/konect/integration/domain/club/ClubRegistrationRequestApiTest.javasrc/main/java/gg/agit/konect/domain/club/model/ClubRegistrationRequest.javasrc/main/java/gg/agit/konect/domain/website/model/WebUniversity.javasrc/main/java/gg/agit/konect/global/config/SecurityPaths.javasrc/main/java/gg/agit/konect/infrastructure/slack/enums/SlackMessageTemplate.java
**/*.{sql,java}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
데이터베이스 변경에서는 마이그레이션 순서, 기존 데이터 호환성, nullable/default 처리, 롤백 난이도, 인덱스 필요성을 확인한다
Files:
src/main/java/gg/agit/konect/domain/website/model/WebClub.javasrc/main/java/gg/agit/konect/domain/club/repository/ClubRegistrationRequestRepository.javasrc/test/java/gg/agit/konect/integration/domain/website/WebsiteApiTest.javasrc/main/resources/db/migration/V77__create_club_registration_request.sqlsrc/test/java/gg/agit/konect/support/fixture/WebUniversityFixture.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestApi.javasrc/main/java/gg/agit/konect/domain/website/service/WebsiteService.javasrc/main/java/gg/agit/konect/infrastructure/slack/service/SlackNotificationService.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubDetailResponse.javasrc/main/java/gg/agit/konect/domain/club/service/ClubRegistrationRequestService.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestController.javasrc/main/java/gg/agit/konect/domain/website/repository/WebsiteQueryRepository.javasrc/main/java/gg/agit/konect/domain/club/dto/ClubRegistrationRequestDto.javasrc/test/java/gg/agit/konect/support/fixture/WebClubFixture.javasrc/test/java/gg/agit/konect/integration/domain/club/ClubRegistrationRequestApiTest.javasrc/main/java/gg/agit/konect/domain/club/model/ClubRegistrationRequest.javasrc/main/java/gg/agit/konect/domain/website/model/WebUniversity.javasrc/main/java/gg/agit/konect/global/config/SecurityPaths.javasrc/main/java/gg/agit/konect/infrastructure/slack/enums/SlackMessageTemplate.java
src/main/java/**/*.java
⚙️ CodeRabbit configuration file
src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.
- 코멘트는 반드시 한국어로 작성한다.
- 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
- 각 코멘트 첫 줄에 심각도를
[LEVEL: high|medium|low]형식으로 반드시 표기한다.- 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
- 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
- 가능하면 재현 조건 및 실패 시나리오도 포함한다.
- 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
- 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
- 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
- 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
- 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.
Files:
src/main/java/gg/agit/konect/domain/website/model/WebClub.javasrc/main/java/gg/agit/konect/domain/club/repository/ClubRegistrationRequestRepository.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestApi.javasrc/main/java/gg/agit/konect/domain/website/service/WebsiteService.javasrc/main/java/gg/agit/konect/infrastructure/slack/service/SlackNotificationService.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubDetailResponse.javasrc/main/java/gg/agit/konect/domain/club/service/ClubRegistrationRequestService.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestController.javasrc/main/java/gg/agit/konect/domain/website/repository/WebsiteQueryRepository.javasrc/main/java/gg/agit/konect/domain/club/dto/ClubRegistrationRequestDto.javasrc/main/java/gg/agit/konect/domain/club/model/ClubRegistrationRequest.javasrc/main/java/gg/agit/konect/domain/website/model/WebUniversity.javasrc/main/java/gg/agit/konect/global/config/SecurityPaths.javasrc/main/java/gg/agit/konect/infrastructure/slack/enums/SlackMessageTemplate.java
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
src/main/java/gg/agit/konect/domain/website/model/WebClub.javasrc/main/java/gg/agit/konect/domain/club/repository/ClubRegistrationRequestRepository.javasrc/test/java/gg/agit/konect/integration/domain/website/WebsiteApiTest.javasrc/main/resources/db/migration/V77__create_club_registration_request.sqlsrc/test/java/gg/agit/konect/support/fixture/WebUniversityFixture.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestApi.javasrc/main/java/gg/agit/konect/domain/website/service/WebsiteService.javasrc/main/java/gg/agit/konect/infrastructure/slack/service/SlackNotificationService.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubDetailResponse.javasrc/main/java/gg/agit/konect/domain/club/service/ClubRegistrationRequestService.javasrc/main/java/gg/agit/konect/domain/club/controller/ClubRegistrationRequestController.javasrc/main/java/gg/agit/konect/domain/website/repository/WebsiteQueryRepository.javasrc/main/java/gg/agit/konect/domain/club/dto/ClubRegistrationRequestDto.javasrc/test/java/gg/agit/konect/support/fixture/WebClubFixture.javasrc/test/java/gg/agit/konect/integration/domain/club/ClubRegistrationRequestApiTest.javasrc/main/java/gg/agit/konect/domain/club/model/ClubRegistrationRequest.javasrc/main/java/gg/agit/konect/domain/website/model/WebUniversity.javasrc/main/java/gg/agit/konect/global/config/SecurityPaths.javasrc/main/java/gg/agit/konect/infrastructure/slack/enums/SlackMessageTemplate.java
**/db/migration/*.sql
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Flyway 마이그레이션 파일은 파일명 버전 순서, 기존 운영 데이터 backfill, NOT NULL 추가 순서, 기본값 처리를 확인한다
Files:
src/main/resources/db/migration/V77__create_club_registration_request.sql
src/main/resources/db/migration/**/*.sql
⚙️ CodeRabbit configuration file
src/main/resources/db/migration/**/*.sql: Flyway 마이그레이션 리뷰 규칙:
- 버전 파일명 규칙(V{number}__{description}.sql) 위반 여부를 우선 확인한다.
- 이미 배포된 마이그레이션 수정/재번호 부여 위험이 있으면 반드시 차단 코멘트를 남긴다.
- 파괴적 변경(drop, rename 등)은 롤백 가능성과 운영 영향 관점에서 검토한다.
Files:
src/main/resources/db/migration/V77__create_club_registration_request.sql
🧠 Learnings (1)
📚 Learning: 2026-05-18T05:03:20.120Z
Learnt from: dh2906
Repo: BCSDLab/KONECT_BACK_END PR: 625
File: src/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.java:0-0
Timestamp: 2026-05-18T05:03:20.120Z
Learning: 응답 DTO(또는 그 하위 클래스)의 필드에 `Schema(requiredMode = REQUIRED)`(또는 OpenAPI에서 필드를 required로 문서화)한다고 가정한 경우, 실제 런타임 계약이 비어있지 않음을 코드/DB로 보장해야 합니다. 구체적으로는 해당 값이 매핑되는 DB 컬럼이 `NOT NULL`이고(필요 시 Flyway 마이그레이션에서 기존 행 백필 후 `NOT NULL` 전환), 엔티티/필드에 `NotNull` 및 `Column(nullable = false)`(또는 동등한 검증/매핑 제약)가 선언되어 있으며, DTO/응답에서도 null이 허용되지 않도록 보장(검증/타입)하세요. 이 제약이 없는 상태에서 required로 문서화하면 리뷰 시 불일치로 플래그합니다.
Applied to files:
src/main/java/gg/agit/konect/domain/website/dto/WebsiteClubsResponse.javasrc/main/java/gg/agit/konect/domain/website/dto/WebsiteClubDetailResponse.javasrc/main/java/gg/agit/konect/domain/club/dto/ClubRegistrationRequestDto.java
🔇 Additional comments (1)
src/test/java/gg/agit/konect/support/fixture/WebUniversityFixture.java (1)
7-7: LGTM!
- 생성 요청은 201 응답으로 맞춰 클라이언트가 리소스 생성 성공을 일관되게 판단할 수 있게 정리 - 사진 및 영상은 최대 개수 제한만 유지해 무첨부 요청을 허용하는 API 계약과 저장 로직을 일치 - Slack 알림은 커밋 이후 이벤트 리스너에서 처리해 외부 연동 실패가 등록 저장을 롤백하지 않도록 분리 - 공개 접근은 경로 전체 예외 대신 @publicapi 메서드 단위로 제한해 같은 경로의 향후 메서드가 자동 공개되지 않도록 정리 - 실제 null 반환 가능성이 있는 웹사이트 동아리 응답의 대학 정보 문서화를 선택값으로 맞춤
Addressed: 실제 런타임 계약에 맞춰 |
- Builder 문자열 출력에서 요청 엔티티 연관관계를 제외해 불필요한 객체 그래프 노출을 피함 - 기능 동작은 유지하면서 정적 분석 경고만 좁게 해소
🔍 개요
🚀 주요 변경 내용
POST /clubs/registration-requests공개 API와 입력 검증을 추가했습니다.WebClub,WebUniversity모델 패키지를domain.website.model로 이동했습니다.💬 참고 사항
club_registration_request.introduce는 2000자 제한 입력을 저장하기 위해TEXT로 정의했습니다.✅ Checklist (완료 조건)