Skip to content

Comments

[feature] 개발자 페이지 만들어서 관리 쉽게 ㄱㄱ #1164

Merged
lepitaaar merged 4 commits intodevelop/befrom
feature/#1161-add-admin-page-MOA-620
Feb 13, 2026
Merged

[feature] 개발자 페이지 만들어서 관리 쉽게 ㄱㄱ #1164
lepitaaar merged 4 commits intodevelop/befrom
feature/#1161-add-admin-page-MOA-620

Conversation

@Zepelown
Copy link
Member

@Zepelown Zepelown commented Feb 8, 2026

#️⃣연관된 이슈

ex) #이슈번호, #이슈번호
#1161

📝작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지/동영상 첨부 가능)

개발자 인증 체계 + 개발자 포털 만들었습니다.

제공 기능

  • 동아리 정보 수정
  • 단어 사전 수정

링크
http://localhost:8080/dev.html

중점적으로 리뷰받고 싶은 부분(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 관리자용 클럽 관리 기능 추가 - 모든 클럽 조회, 클럽 정보 및 모집 정보 수정 가능
    • 개발자 등록 시스템 구현
    • 개발자 포털 UI 추가 - 클럽 관리 및 단어 사전 관리 기능 포함
  • 보안

    • 개발자 권한에 대한 역할 기반 접근 제어 추가

Zepelown and others added 4 commits February 8, 2026 20:07
- .gitignore: /.cursor 추가
- dev 포털: 스티키 헤더·섹션 네비, 동아리 목록 페이지네이션·ID 복사, 토스트·저장 피드백, 스타일·접근성 정리

Co-authored-by: Cursor <cursoragent@cursor.com>
- edit.html: 동아리 수정 전용 페이지 추가 (약력/모집정보 폼)\n- index.html: 수정 UI 제거, 목록 클릭 시 openEditWindow로 수정 창 오픈

Co-authored-by: Cursor <cursoragent@cursor.com>
- dict-edit.html: 단어사전 수정 전용 페이지 추가\n- index.html: 단어사전 목록·페이지네이션, 단일/CSV 추가, 수정 창 연동, 삭제, 캐시 새로고침

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel
Copy link

vercel bot commented Feb 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
moadong Ready Ready Preview, Comment Feb 8, 2026 11:46am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 8, 2026

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

개발자 관리 포털 기능을 추가합니다. 개발자 등록 API, 클럽 관리 엔드포인트, 역할 기반 권한 제어, 그리고 클럽 정보 및 사전 편집을 위한 웹 UI를 포함합니다.

Changes

Cohort / File(s) Summary
User & Authentication Management
backend/src/main/java/moadong/user/entity/User.java, backend/src/main/java/moadong/user/entity/enums/UserRole.java, backend/src/main/java/moadong/user/controller/DevAuthController.java, backend/src/main/java/moadong/user/payload/request/DevRegisterRequest.java, backend/src/main/java/moadong/user/payload/request/UserRegisterRequest.java, backend/src/main/java/moadong/user/service/UserCommandService.java
사용자 역할 시스템 추가(CLUB_ADMIN, DEVELOPER), 개발자 등록 엔드포인트 추가, 역할별 사용자 생성 로직 구현
Club Admin API
backend/src/main/java/moadong/club/controller/ClubAdminController.java, backend/src/main/java/moadong/club/payload/response/ClubListResponse.java, backend/src/main/java/moadong/club/service/ClubProfileService.java
클럽 목록 조회, 클럽 정보 및 모집정보 수정 엔드포인트 추가
Security & Portal Setup
backend/src/main/java/moadong/global/config/SecurityConfig.java, backend/src/main/java/moadong/global/controller/DevPortalController.java
/api/admin 경로에 DEVELOPER 역할 요구 권한 설정, 개발자 포털 리디렉션 컨트롤러 추가
Developer Portal UI
backend/src/main/resources/static/dev/index.html, backend/src/main/resources/static/dev/edit.html, backend/src/main/resources/static/dev/dict-edit.html
토큰 기반 인증, 클럽 관리 및 사전 편집 기능을 포함한 개발자 포털 웹 UI
Configuration
backend/.gitignore
/.cursor 무시 규칙 추가

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Developer Browser
    participant Portal as Dev Portal UI
    participant Auth as Auth API
    participant Server as Admin API
    participant DB as Database

    rect rgba(200, 150, 255, 0.5)
    Note over Browser,DB: 개발자 등록 & 로그인 흐름
    Browser->>Portal: /dev 접속
    Portal->>Auth: POST /auth/dev/register (userId, password, secret)
    Auth->>DB: 개발자 사용자 생성 (role=DEVELOPER)
    Auth-->>Portal: 등록 성공
    Browser->>Portal: /auth/user/login 제출 (userId, password)
    Auth->>DB: 자격증명 검증
    Auth-->>Portal: 토큰 + userId 반환
    Portal->>Portal: sessionStorage에 토큰 저장
    end

    rect rgba(100, 200, 255, 0.5)
    Note over Browser,DB: 클럽 관리 흐름
    Portal->>Server: GET /api/admin/clubs (Authorization: Bearer token)
    Server->>DB: 모든 클럽 조회
    DB-->>Server: ClubListResponse
    Server-->>Portal: 클럽 목록 표시
    Browser->>Portal: 클럽 편집 클릭
    Portal->>Server: GET /api/club/{clubId}
    Server->>DB: 클럽 상세 조회
    DB-->>Portal: 클럽 데이터
    Portal->>Portal: 편집 폼 표시
    Browser->>Portal: 정보 수정 후 저장
    Portal->>Server: PUT /api/admin/club/{clubId}/info (ClubInfoRequest)
    Server->>DB: 클럽 정보 업데이트
    Server-->>Portal: 성공 응답
    end

    rect rgba(100, 255, 200, 0.5)
    Note over Browser,DB: 사전 관리 흐름
    Portal->>Server: GET /api/admin/word-dictionary
    Server->>DB: 사전 목록 조회
    DB-->>Portal: 사전 데이터
    Portal->>Portal: 사전 테이블 표시
    Browser->>Portal: 사전 항목 편집
    Portal->>Portal: 편집 페이지 (dict-edit.html) 오픈
    Browser->>Portal: 단어 저장
    Portal->>Server: PUT /api/admin/dictionary/{id} (inputWords)
    Server->>DB: 사전 항목 업데이트
    Server-->>Portal: 성공 응답
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

✨ Feature, 📬 API, 💾 BE

Suggested reviewers

  • lepitaaar
  • seongwon030
  • oesnuj
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed PR의 모든 코드 변경사항은 MOA-620 관련 이슈(개발자 페이지 생성)의 목표를 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항은 개발자/관리 페이지 기능 구현 범위 내에 있으며 범위를 벗어난 변경은 없습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 개발자 페이지 생성과 관리 개선이라는 주요 변경사항을 명확히 설명하고 있으며, 풀 리퀘스트의 핵심 목표와 일치합니다.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#1161-add-admin-page-MOA-620

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.

❤️ Share

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

@github-actions
Copy link

github-actions bot commented Feb 8, 2026

Test Results

77 tests   77 ✅  16s ⏱️
16 suites   0 💤
16 files     0 ❌

Results for commit 75b5ad7.

Copy link
Contributor

@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: 7

🤖 Fix all issues with AI agents
In `@backend/src/main/java/moadong/club/controller/ClubAdminController.java`:
- Around line 53-58: The updateClubDescription method is missing request-body
validation: add the `@Valid` annotation to the ClubRecruitmentInfoUpdateRequest
parameter in updateClubDescription (matching the existing use on
ClubInfoRequest) so Spring will perform bean validation before calling
clubProfileService.updateClubRecruitmentInfoByClubId; import the appropriate
`@Valid` (javax.validation or jakarta.validation) if not already present and keep
the parameter order/annotations consistent with other controller methods.

In `@backend/src/main/java/moadong/user/payload/request/DevRegisterRequest.java`:
- Around line 9-24: DevRegisterRequest is missing the compact-constructor
validation that prevents userId and password being identical (as present in
UserRegisterRequest); add a compact constructor in the DevRegisterRequest record
that checks if userId.equals(password) and throws an IllegalArgumentException
(or appropriate validation exception) with a clear message when they match,
mirroring the validation logic used in UserRegisterRequest so the same security
policy applies during developer registration.

In `@backend/src/main/java/moadong/user/service/UserCommandService.java`:
- Around line 67-88: In registerDeveloper in UserCommandService: remove the
unnecessary club creation for DEVELOPER by not generating clubId and deleting
the createClub(clubId, userId) call (generate only userId and pass null/empty
for clubId when calling createUserWithRole or use the overload for roles that
don't need a club); and replace the plain
devRegistrationSecret.equals(request.secret()) check with a constant-time
comparison using MessageDigest.isEqual on the UTF-8 byte arrays (ensure you
handle nulls by failing when either secret is null before comparing).

In `@backend/src/main/resources/static/dev/dict-edit.html`:
- Around line 87-95: The fetch response is being parsed with res.json() before
checking res.ok, which throws on non-JSON error pages; change the load fetch
(the fetch to API_BASE + '/api/admin/word-dictionary/' +
encodeURIComponent(dictId)) to first check res.ok and, if not ok, read the body
with res.text() and attempt to JSON-parse that text to extract a message
(falling back to the raw text or 'HTTP ' + res.status) for display; only call
res.json() for successful responses (and apply the same pattern to the save
request around Line 127 where res.json() is invoked before res.ok).

In `@backend/src/main/resources/static/dev/edit.html`:
- Around line 107-115: The code calls res.json() before checking res.ok which
can throw on non-JSON error responses; update the fetch handling in the blocks
that call fetch(API_BASE + '/api/club/' + clubId, ...) (and the similar blocks
at the other occurrences around lines referenced) to first check res.ok and only
call res.json() when appropriate, or use a safe JSON parse helper (e.g.,
try/catch around res.text() -> JSON.parse) so that HTML/error pages do not cause
uncaught parsing errors; ensure you still extract and show data.message when
available and show a fallback message like 'HTTP ' + res.status when parsing
fails.
- Around line 96-108: The code uses the raw clubId URL param directly in the
fetch URL (see clubId and the fetch(API_BASE + '/api/club/' + clubId, { headers:
headers() }) call); add validation right after obtaining clubId (before setting
pageTitle or entering the async IIFE) to reject malformed values — for example
check against an allowed pattern such as a MongoDB ObjectId regex
(/^[a-fA-F0-9]{24}$/) or other app-specific whitelist, show the existing
loadError message and return/abort if invalid, and if valid use the sanitized
value (or at minimum encodeURIComponent(clubId)) when building the fetch URL to
prevent path traversal or injection.

In `@backend/src/main/resources/static/dev/index.html`:
- Around line 414-418: The code inserts e.message directly into tbody.innerHTML
causing XSS risk; change the error rendering in the catch blocks that use
tbody.innerHTML and banner.textContent to instead set text via safe DOM APIs:
clear tbody, create a tr/td element, set its textContent (or use createTextNode)
with e.message || '오류', append it to tbody, and ensure banner.textContent (not
innerHTML) is used for the banner; update both occurrences that reference
e.message and tbody.innerHTML to use these safe DOM operations (refer to the
catch blocks that access banner, tbody, and e.message).
🧹 Nitpick comments (10)
backend/.gitignore (1)

47-47: Cursor IDE 디렉토리 무시 규칙 추가를 확인했습니다.

.cursor 디렉토리를 무시하는 것은 적절한 변경사항입니다. 다만 다른 IDE 패턴들(.idea, .vscode/)과의 일관성을 위해 선행 슬래시를 제거하는 것을 고려해볼 수 있습니다.

♻️ 다른 IDE 패턴과 일관성을 맞추기 위한 제안
-/.cursor
+.cursor/

이렇게 하면 .cursor 디렉토리가 backend 디렉토리의 어디에 생성되더라도 무시됩니다. 다만 현재 패턴도 실무에서는 문제없이 동작할 것입니다.

backend/src/main/resources/static/dev/edit.html (2)

116-129: API 응답 구조에 대한 다중 폴백 패턴이 취약합니다.

data.data?.club || data?.club || {}club.name || club.club?.name 등 여러 레벨의 폴백이 혼재되어 있어, API 응답 스키마와 맞지 않을 경우 조용히 빈 값으로 채워질 수 있습니다. API 응답 스키마를 하나로 확정하고, 예상 외 구조일 때 명시적으로 에러를 표시하는 것이 유지보수에 유리합니다.


140-171: 저장 전 필수 필드 유효성 검사가 없습니다.

name 등 필수값이 비어있어도 그대로 PUT 요청이 전송됩니다. 서버 측 검증에만 의존하면 사용자에게 불친절한 에러가 반환될 수 있으므로, 클라이언트 측에서 최소한의 검증을 추가하는 것을 권장합니다.

✅ 간단한 클라이언트 검증 예시
  document.getElementById('btnUpdateInfo').onclick = async function() {
    if (!clubId) return;
+   const name = document.getElementById('editName').value.trim();
+   if (!name) {
+     showResult('infoResult', false, '이름은 필수 입력 항목입니다.');
+     return;
+   }
    const btn = this;
backend/src/main/java/moadong/global/controller/DevPortalController.java (1)

6-12: /dev 경로에 대한 접근 제어가 없습니다.

이 컨트롤러는 인증 없이 /dev 포털 UI에 접근할 수 있도록 합니다. API 엔드포인트(/api/admin/**)는 별도로 보호되고, 정적 페이지 자체는 sessionStorage 토큰으로 클라이언트 측 인증을 처리하지만, 개발자 포털 페이지 자체의 존재를 노출시키는 것은 보안 표면을 넓힐 수 있습니다.

프로덕션 환경에서 /dev/** 경로를 DEVELOPER 역할로 제한하거나, 프로파일 기반으로 이 컨트롤러를 활성화/비활성화하는 것을 고려해 보세요 (예: @Profile("dev")).

backend/src/main/java/moadong/user/entity/User.java (1)

54-55: role 필드에 @NotNull 어노테이션이 누락되었습니다.

다른 필드들(emailVerified, clubId, status)은 모두 @NotNull로 선언되어 있지만 role은 빠져 있습니다. getAuthorities()에서 null 방어 처리를 하고 있지만, 새로 생성되는 문서에 대해서는 @NotNull을 추가하여 일관성을 유지하는 것이 좋습니다.

제안 diff
     `@Builder.Default`
+    `@NotNull`
     private UserRole role = UserRole.CLUB_ADMIN;
backend/src/main/java/moadong/user/service/UserCommandService.java (1)

49-50: @Value 필드 주입 vs 생성자 주입 일관성

다른 모든 의존성은 @RequiredArgsConstructor를 통한 생성자 주입을 사용하지만, devRegistrationSecret는 non-final 필드 주입입니다. final로 선언하면 생성자 주입으로 통합되어 일관성이 높아지고, 테스트 시에도 더 편리합니다.

제안 diff
-    `@Value`("${app.dev-registration-secret:}")
-    private String devRegistrationSecret; // set by Spring after construction (field injection)
+    `@Value`("${app.dev-registration-secret:}")
+    private final String devRegistrationSecret;
backend/src/main/java/moadong/user/controller/DevAuthController.java (1)

24-29: 개발자 등록 엔드포인트에 대한 Rate Limiting 부재

/auth/dev/registerSecurityConfig에서 permitAll()로 공개되어 있습니다. 시크릿 키 검증이 서비스 레이어에서 이루어지더라도, 무차별 대입 공격(brute-force)으로 시크릿 키를 추측하려는 시도를 방지하기 위해 rate limiting을 적용하는 것을 권장합니다.

backend/src/main/java/moadong/club/service/ClubProfileService.java (2)

90-115: 기존 메서드와 중복 코드가 많습니다

updateClubInfoByClubIdupdateClubInfo(Line 40-47)와, updateClubRecruitmentInfoByClubIdupdateClubRecruitmentInfo(Line 49-62)와 클럽 조회 방식만 다르고 나머지 로직이 동일합니다. 클럽 조회 부분을 헬퍼로 추출하면 중복을 줄일 수 있습니다.

♻️ 헬퍼 메서드 추출 예시
+    private Club findClubByUserId(String userId) {
+        return clubRepository.findClubByUserId(userId)
+                .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
+    }
+
+    private Club findClubById(String clubId) {
+        ObjectId objectId = ObjectIdConverter.convertString(clubId);
+        return clubRepository.findClubById(objectId)
+                .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
+    }

이후 각 업데이트 메서드에서 조회 로직을 헬퍼 호출로 대체할 수 있습니다.


64-74: getAllClubsForAdmin의 페이지네이션 부재

현재 모든 클럽을 한 번에 메모리에 로드합니다. 클럽 수가 증가하면 성능 문제가 될 수 있으므로, 향후 서버 사이드 페이지네이션 도입을 고려해 주세요.

backend/src/main/resources/static/dev/index.html (1)

302-310: navigator.clipboard.writeText 에러 미처리

navigator.clipboard.writeText는 Promise를 반환하며, HTTPS가 아닌 환경이나 권한이 없을 때 실패할 수 있습니다. Line 355, 448에서도 동일한 패턴이 사용됩니다. .catch()로 실패 시 폴백을 추가하는 것을 권장합니다.

🔧 수정 예시
-        navigator.clipboard.writeText(t);
-        const fb = document.getElementById('copyFeedback');
-        fb.classList.remove('hidden');
-        setTimeout(() => fb.classList.add('hidden'), 2000);
+        navigator.clipboard.writeText(t).then(() => {
+          const fb = document.getElementById('copyFeedback');
+          fb.classList.remove('hidden');
+          setTimeout(() => fb.classList.add('hidden'), 2000);
+        }).catch(() => {
+          showToast('복사 실패', 'error');
+        });

@Zepelown Zepelown changed the title Feature/#1161 add admin page moa 620 [feature] 개발자 페이지 만들어서 관리 쉽게 ㄱㄱ Feb 8, 2026
Copy link
Contributor

@lepitaaar lepitaaar left a comment

Choose a reason for hiding this comment

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

아주 좋습니다.
다만 개발자 전용 엔드포인트들은 스웨거 설명이 필요하지않을꺼같아요. @Hidden어노테이션으로 설명을 가리는게 어떨까 싶습니다.

/auth/dev/register 엔드포인트도 프로덕션에서 보여줄필욘없겠네요

개발자 관리페이지 접근주소도 막는게 좋아보여서 어떻게 가릴지 얘기해보면좋겠습니다

@lepitaaar lepitaaar added ✨ Feature 기능 개발 💾 BE Backend labels Feb 12, 2026
@lepitaaar lepitaaar merged commit 66acef9 into develop/be Feb 13, 2026
5 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 13, 2026
@lepitaaar lepitaaar deleted the feature/#1161-add-admin-page-MOA-620 branch February 21, 2026 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💾 BE Backend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants