Skip to content

refactor: 동아리 관리(지원서/회비/모집공고) 관련#266

Merged
JanooGwan merged 3 commits intodevelopfrom
refactor/club-info-management
Feb 18, 2026
Merged

refactor: 동아리 관리(지원서/회비/모집공고) 관련#266
JanooGwan merged 3 commits intodevelopfrom
refactor/club-info-management

Conversation

@JanooGwan
Copy link
Contributor

@JanooGwan JanooGwan commented Feb 18, 2026

🔍 개요

  • 동아리 관리 관련된 기능을 리팩토링 했습니다.
  • close #이슈번호

🚀 주요 변경 내용

  • 동아리 관리 전체 정보를 조회할 수 있는 페이지가 새로 구현됨에 따라, 이에 대한 Response를 만들었습니다.
  • 해당 Response 는 지원서/회비/모집공고 on/off, 모집공고의 모집기간, 지원서의 문항 개수, 회비 정보를 확인할 수 있습니다.
  • 그리고 기존에 있었던 회비 정보 관리 페이지에서의 회비 납부 설정 기능은 삭제했습니다(이를 상술한 페이지로 옮긴 것입니다).

💬 참고 사항


✅ Checklist (완료 조건)

  • 코드 스타일 가이드 준수
  • 테스트 코드 포함됨
  • Reviewers / Assignees / Labels 지정 완료
  • 보안 및 민감 정보 검증 (API 키, 환경 변수, 개인정보 등)

Summary by CodeRabbit

  • New Features

    • Added club settings management endpoints to view and update recruitment, application, and fee enablement.
  • Changes

    • Introduced unified club settings payloads including recruitment, application, and fee summaries.
    • Simplified fee info by removing deadline tracking and streamlining account-related fields.
    • Database schema updated to add recruitment and application feature flags.

@JanooGwan JanooGwan requested a review from Copilot February 18, 2026 17:55
@JanooGwan JanooGwan self-assigned this Feb 18, 2026
@JanooGwan JanooGwan added the 리팩토링 리팩터링을 위한 이슈입니다. label Feb 18, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a Club Settings API (interface, controller, service) to manage recruitment, application, and fee toggles; new DTOs for settings; replaces feeDeadline with boolean flags on Club; updates fee DTOs and usages; and adds a DB migration to add toggle columns.

Changes

Cohort / File(s) Summary
API & Controller
src/main/java/gg/agit/konect/domain/club/controller/ClubSettingsApi.java, src/main/java/gg/agit/konect/domain/club/controller/ClubSettingsController.java
New REST API interface and controller exposing GET and PATCH /clubs/{clubId}/settings, delegating to service and using @UserId for caller context.
Service
src/main/java/gg/agit/konect/domain/club/service/ClubSettingsService.java
New service providing getSettings and updateSettings, permission checks, and builders for recruitment/application/fee summaries.
Domain model
src/main/java/gg/agit/konect/domain/club/model/Club.java
Replaces feeDeadline with boolean fields isFeeRequired, isRecruitmentEnabled, isApplicationEnabled; updates constructors, fee methods, and adds updateSettings(...).
DTOs (settings & fee)
src/main/java/gg/agit/konect/domain/club/dto/ClubSettingsResponse.java, src/main/java/gg/agit/konect/domain/club/dto/ClubSettingsUpdateRequest.java, src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoResponse.java, src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoReplaceRequest.java
Adds settings request/response records (nested summaries). Removes isFeeRequired from fee DTOs and drops validation/annotations on accountHolder in replace request.
Call site update
src/main/java/gg/agit/konect/domain/club/service/ClubApplicationService.java
Updates call to Club.replaceFeeInfo(...) to match new signature (removed isFeeRequired parameter).
DB migration
src/main/resources/db/migration/V29__add_club_settings_toggle_columns.sql
Adds is_recruitment_enabled and is_application_enabled TINYINT columns and initializes NULLs to 1.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Controller as "ClubSettingsController"
  participant Service as "ClubSettingsService"
  participant Repo as "ClubRepository/DB"

  Client->>Controller: GET /clubs/{clubId}/settings (userId)
  Controller->>Service: getSettings(clubId, userId)
  Service->>Repo: fetch club by clubId
  Repo-->>Service: Club entity
  Service-->>Controller: ClubSettingsResponse
  Controller-->>Client: 200 OK (response)

  Client->>Controller: PATCH /clubs/{clubId}/settings (body, userId)
  Controller->>Service: updateSettings(clubId, userId, request)
  Service->>Repo: fetch club by clubId
  Repo-->>Service: Club entity
  Service->>Service: validate & update Club.updateSettings(...)
  Service->>Repo: save updated Club
  Repo-->>Service: persisted Club
  Service-->>Controller: updated ClubSettingsResponse
  Controller-->>Client: 200 OK (response)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibbled code and toggled lights,

Recruitment, apps, and fee delights.
Deadlines hopped away, flags now sing—
Clubs dance on with a brighter spring.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main refactoring effort around club management features (applications, fees, recruitment notices).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/club-info-management

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

동아리 관리 화면(지원서/회비/모집공고)에서 필요한 “설정 토글 + 요약 정보”를 한 번에 조회/수정할 수 있도록 백엔드 API/도메인/마이그레이션을 추가·정리한 PR입니다.

Changes:

  • 동아리 설정 조회/수정 API(/clubs/{clubId}/settings) 및 응답/요청 DTO 추가
  • Club 도메인에 설정 토글 필드(모집공고/지원서/회비) 반영 및 회비 설정 API 스펙 일부 변경
  • club 테이블에 설정 토글 컬럼 추가 마이그레이션(V29) 추가

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/main/resources/db/migration/V29__add_club_settings_toggle_columns.sql club 설정 토글 컬럼(모집공고/지원서) 추가
src/main/java/gg/agit/konect/domain/club/service/ClubSettingsService.java 설정 조회/수정 서비스 및 요약 정보 조립 로직 추가
src/main/java/gg/agit/konect/domain/club/service/ClubApplicationService.java 회비 정보 교체 호출 시 불필요 파라미터 제거
src/main/java/gg/agit/konect/domain/club/model/Club.java 설정 토글 필드 추가 및 회비 정보 처리 로직 조정
src/main/java/gg/agit/konect/domain/club/dto/ClubSettingsUpdateRequest.java 설정 PATCH 요청 DTO 추가
src/main/java/gg/agit/konect/domain/club/dto/ClubSettingsResponse.java 설정 화면용 종합 응답 DTO 추가
src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoResponse.java 회비 정보 응답 스펙에서 필드 제거
src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoReplaceRequest.java 회비 정보 교체 요청 스펙에서 필드 제거
src/main/java/gg/agit/konect/domain/club/controller/ClubSettingsController.java 설정 조회/수정 컨트롤러 구현 추가
src/main/java/gg/agit/konect/domain/club/controller/ClubSettingsApi.java 설정 조회/수정 API 인터페이스/스웨거 정의 추가

Comment on lines +94 to +95
int questionCount = clubApplyQuestionRepository.findAllByClubIdOrderByIdAsc(clubId).size();
return new ApplicationSummary(questionCount);
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문항 개수 계산을 위해 findAllByClubIdOrderByIdAsc(...).size()로 전체 레코드를 조회하고 있어, 문항이 많아질수록 불필요한 I/O가 발생합니다. countByClubId 같은 count 쿼리 메서드를 추가해서 개수만 조회하도록 변경하는 게 좋습니다.

Suggested change
int questionCount = clubApplyQuestionRepository.findAllByClubIdOrderByIdAsc(clubId).size();
return new ApplicationSummary(questionCount);
long questionCount = clubApplyQuestionRepository.countByClubId(clubId);
return new ApplicationSummary((int) questionCount);

Copilot uses AI. Check for mistakes.
Comment on lines 19 to +22
String accountNumber,

@Schema(description = "예금주", example = "BCSD", requiredMode = REQUIRED)
String accountHolder,

@Schema(description = "회비 납부 필요 여부", example = "true", requiredMode = REQUIRED)
Boolean isFeeRequired
String accountHolder
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClubFeeInfoResponse에서 isFeeRequired 필드가 제거되어 기존 API 응답 스펙을 사용하는 클라이언트가 깨질 수 있습니다. 의도된 변경이라면 API 문서/클라이언트 동시 배포가 필요하고, 호환성이 필요하면 필드를 유지한 채 deprecated 처리 또는 버전 분리(새 응답 DTO)로 전환하는 방안을 검토해주세요.

Copilot uses AI. Check for mistakes.
Comment on lines 21 to 25

@Size(max = 100, message = "예금주는 최대 100자 입니다.")
@Schema(description = "예금주", example = "BCSD", requiredMode = NOT_REQUIRED)
String accountHolder,

@NotNull(message = "회비 납부 필요 여부는 필수로 입력해야 합니다.")
@Schema(description = "회비 납부 필요 여부", example = "true", requiredMode = REQUIRED)
Boolean isFeeRequired
String accountHolder
) {
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClubFeeInfoReplaceRequest에서 isFeeRequired 입력이 제거되면서, 기존 클라이언트가 해당 필드를 포함해 요청할 경우 Jackson 설정에 따라 400(unknown property)로 실패할 수 있습니다. 하위 호환이 필요하면 요청 DTO에 unknown property 무시 설정(@JsonIgnoreProperties(ignoreUnknown = true))을 추가하거나, 일정 기간 구 DTO를 병행 지원하는 방안을 고려해주세요.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 마이그레이션(V29)은 club 테이블에 is_recruitment_enabled/is_application_enabled만 추가하고 있는데, 이번 변경에서 Club 엔티티에 @column(name = "is_fee_required")가 추가되어 런타임에 컬럼 미존재로 오류가 날 수 있습니다. club.is_fee_required 컬럼 추가 및 (기존에 club_recruitment.is_fee_required를 쓰고 있었다면) club으로의 데이터 마이그레이션/정합성 처리까지 함께 반영하거나, 반대로 엔티티/서비스에서 기존 스키마를 사용하도록 맞춰주세요.

Suggested change
ALTER TABLE club ADD COLUMN is_application_enabled TINYINT(1) DEFAULT NULL;
ALTER TABLE club ADD COLUMN is_application_enabled TINYINT(1) DEFAULT NULL;
ALTER TABLE club ADD COLUMN is_fee_required TINYINT(1) DEFAULT NULL;

Copilot uses AI. Check for mistakes.
Comment on lines 2 to 3
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

새 토글 컬럼들이 DEFAULT NULL로 추가되고, 서비스에서는 Boolean.TRUE.equals(...)로 해석되어 null이면 모두 false로 내려갑니다. 의도한 기본값이 ‘기존 동아리는 활성화’라면 DEFAULT 1/NOT NULL 설정 및 기존 데이터 백필(UPDATE)까지 마이그레이션에 포함하는 게 안전합니다.

Suggested change
ALTER TABLE club ADD COLUMN is_recruitment_enabled TINYINT(1) DEFAULT NULL;
ALTER TABLE club ADD COLUMN is_application_enabled TINYINT(1) DEFAULT NULL;
ALTER TABLE club ADD COLUMN is_recruitment_enabled TINYINT(1) NOT NULL DEFAULT 1;
ALTER TABLE club ADD COLUMN is_application_enabled TINYINT(1) NOT NULL DEFAULT 1;
-- 기존 동아리의 토글 컬럼 기본값을 활성(true=1)로 백필
UPDATE club SET is_recruitment_enabled = 1 WHERE is_recruitment_enabled IS NULL;
UPDATE club SET is_application_enabled = 1 WHERE is_application_enabled IS NULL;

Copilot uses AI. Check for mistakes.
@JanooGwan JanooGwan merged commit 3e1a11c into develop Feb 18, 2026
1 check passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/gg/agit/konect/domain/club/dto/ClubSettingsResponse.java (1)

14-21: Consider using primitive boolean for required fields.

The Schema annotations mark isRecruitmentEnabled, isApplicationEnabled, and isFeeEnabled as REQUIRED, but using Boolean wrapper type allows null values. If these fields must always be present in the response, consider using primitive boolean instead, or ensure the service layer guarantees non-null values.

♻️ Optional: Use primitive boolean for guaranteed non-null semantics
 `@Schema`(description = "동아리 설정 관리 응답")
 public record ClubSettingsResponse(
     `@Schema`(description = "모집공고 활성화 여부", example = "true", requiredMode = REQUIRED)
-    Boolean isRecruitmentEnabled,
+    boolean isRecruitmentEnabled,

     `@Schema`(description = "지원서 활성화 여부", example = "true", requiredMode = REQUIRED)
-    Boolean isApplicationEnabled,
+    boolean isApplicationEnabled,

     `@Schema`(description = "회비 활성화 여부", example = "false", requiredMode = REQUIRED)
-    Boolean isFeeEnabled,
+    boolean isFeeEnabled,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/gg/agit/konect/domain/club/dto/ClubSettingsResponse.java`
around lines 14 - 21, The three required response fields in
ClubSettingsResponse—isRecruitmentEnabled, isApplicationEnabled, and
isFeeEnabled—are declared as nullable Boolean wrappers; change their types to
primitive boolean to enforce non-null semantics (or, if you prefer wrappers,
ensure non-null defaults are set in the constructor/factory), and update any
constructors, builders, getters/setters, and JSON/serialization mappings that
reference those fields so they work with primitive booleans and the service
layer always provides values for them.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/gg/agit/konect/domain/club/model/Club.java`:
- Around line 78-85: The new Boolean flags in Club (isFeeRequired,
isRecruitmentEnabled, isApplicationEnabled) are left null by the Club.of(...)
factory; update the Club.of(...) method to explicitly initialize these fields to
sensible defaults (e.g., Boolean.FALSE or whatever domain default) when
constructing the Club instance so newly created clubs never have null for
isFeeRequired/isRecruitmentEnabled/isApplicationEnabled and the entity state is
explicit.

In `@src/main/resources/db/migration/V29__add_club_settings_toggle_columns.sql`:
- Around line 1-3: The migration adds is_recruitment_enabled and
is_application_enabled as NULLable, which causes existing clubs to appear
disabled because ClubSettingsService uses Boolean.TRUE.equals(...); update the
migration V29__add_club_settings_toggle_columns.sql to (1) add each column with
NOT NULL DEFAULT 1 (or DEFAULT TRUE) and (2) include an explicit backfill UPDATE
to set those columns = 1 for existing rows before adding NOT NULL, or alter them
to NOT NULL after the UPDATE; target the column names is_recruitment_enabled and
is_application_enabled so existing clubs remain enabled instead of becoming
NULL/false.

---

Nitpick comments:
In `@src/main/java/gg/agit/konect/domain/club/dto/ClubSettingsResponse.java`:
- Around line 14-21: The three required response fields in
ClubSettingsResponse—isRecruitmentEnabled, isApplicationEnabled, and
isFeeEnabled—are declared as nullable Boolean wrappers; change their types to
primitive boolean to enforce non-null semantics (or, if you prefer wrappers,
ensure non-null defaults are set in the constructor/factory), and update any
constructors, builders, getters/setters, and JSON/serialization mappings that
reference those fields so they work with primitive booleans and the service
layer always provides values for them.

Comment on lines +78 to +85
@Column(name = "is_fee_required")
private Boolean isFeeRequired;

@Column(name = "is_recruitment_enabled")
private Boolean isRecruitmentEnabled;

@Column(name = "is_application_enabled")
private Boolean isApplicationEnabled;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -i club | head -20

Repository: BCSDLab/KONECT_BACK_END

Length of output: 1575


🏁 Script executed:

find . -type f -name "Club.java" | head -5

Repository: BCSDLab/KONECT_BACK_END

Length of output: 125


🏁 Script executed:

find . -type f -name "*ClubSettingsResponse*"

Repository: BCSDLab/KONECT_BACK_END

Length of output: 139


🏁 Script executed:

cat -n src/main/java/gg/agit/konect/domain/club/model/Club.java | head -200

Repository: BCSDLab/KONECT_BACK_END

Length of output: 7881


🏁 Script executed:

cat -n src/main/java/gg/agit/konect/domain/club/dto/ClubSettingsResponse.java

Repository: BCSDLab/KONECT_BACK_END

Length of output: 3021


🏁 Script executed:

rg -n "ClubSettingsResponse" --type java -B 3 -A 10 src/main/java/gg/agit/konect/domain/club/service/

Repository: BCSDLab/KONECT_BACK_END

Length of output: 7813


🏁 Script executed:

rg -n "getIsRecruitmentEnabled|getIsApplicationEnabled|getIsFeeRequired" --type java src/main/java/gg/agit/konect/domain/club/

Repository: BCSDLab/KONECT_BACK_END

Length of output: 1042


🏁 Script executed:

cat -n src/main/java/gg/agit/konect/domain/club/service/ClubSettingsService.java | sed -n '32,90p'

Repository: BCSDLab/KONECT_BACK_END

Length of output: 2712


🏁 Script executed:

rg -n "club\.get(IsRecruitmentEnabled|IsApplicationEnabled|IsFeeRequired)" --type java

Repository: BCSDLab/KONECT_BACK_END

Length of output: 1042


🏁 Script executed:

rg -n "\.isRecruitmentEnabled()|\.isApplicationEnabled()|\.isFeeRequired()" --type java -A 2 -B 2

Repository: BCSDLab/KONECT_BACK_END

Length of output: 2451


🏁 Script executed:

cat -n src/main/java/gg/agit/konect/domain/club/service/ClubApplicationService.java | sed -n '185,200p'

Repository: BCSDLab/KONECT_BACK_END

Length of output: 824


Initialize boolean flags in the factory method or set column defaults.

The new boolean fields (isFeeRequired, isRecruitmentEnabled, isApplicationEnabled) are uninitialized in the of() factory method (lines 127-137) and will be null for newly created clubs. While the service layer safely handles null values using Boolean.TRUE.equals(), initializing these fields with sensible defaults in the factory method improves data consistency and makes the entity's state explicit:

🛠️ Proposed fix: Initialize in factory method
     public static Club of(ClubCreateRequest request, University university) {
         return Club.builder()
             .name(request.name())
             .description(request.description())
             .introduce(request.introduce())
             .imageUrl(request.imageUrl())
             .location(request.location())
             .clubCategory(request.clubCategory())
             .university(university)
+            .isFeeRequired(false)
+            .isRecruitmentEnabled(false)
+            .isApplicationEnabled(false)
             .build();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/gg/agit/konect/domain/club/model/Club.java` around lines 78 -
85, The new Boolean flags in Club (isFeeRequired, isRecruitmentEnabled,
isApplicationEnabled) are left null by the Club.of(...) factory; update the
Club.of(...) method to explicitly initialize these fields to sensible defaults
(e.g., Boolean.FALSE or whatever domain default) when constructing the Club
instance so newly created clubs never have null for
isFeeRequired/isRecruitmentEnabled/isApplicationEnabled and the entity state is
explicit.

@dh2906 dh2906 deleted the refactor/club-info-management branch February 19, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

리팩토링 리팩터링을 위한 이슈입니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments