Skip to content

[FEAT] #212 어드민 패널 구현 (SSR) - #213

Merged
PBEM22 merged 8 commits into
developfrom
feat/#212/admin-panel
Jul 28, 2026
Merged

[FEAT] #212 어드민 패널 구현 (SSR)#213
PBEM22 merged 8 commits into
developfrom
feat/#212/admin-panel

Conversation

@PBEM22

@PBEM22 PBEM22 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Thymeleaf + Bootstrap 5 기반 SSR 어드민 패널 구현
  • Spring Security 별도 FilterChain(@Order(1))으로 API JWT 인증과 어드민 세션 인증 분리
  • 어드민 계정은 ADMIN_USERNAME / ADMIN_PASSWORD 환경변수로 관리 (DB 테이블 불필요)

구현 내용

페이지 경로 기능
로그인 /admin/login 폼 기반 세션 인증
대시보드 /admin/dashboard 총 유저/분철글/주문 수 통계
유저 관리 /admin/users 목록(페이징), 이용정지/해제, 강제탈퇴
분철글 관리 /admin/posts 목록(상태필터+페이징), 삭제

변경 사항

  • UserStatus SUSPENDED 추가
  • User suspend() / unsuspend() / withdraw(String reason) / withdrawalReason 필드 추가
  • ErrorStatus PROMPT_ERROR(50005) 추가
  • SecurityConfig @Order(2) 추가
  • .envADMIN_USERNAME, ADMIN_PASSWORD 추가 필요

Test plan

  • /admin/login 에서 환경변수 계정으로 로그인 확인
  • 대시보드 통계 수치 확인
  • 유저 정지/해제/탈퇴 동작 확인
  • 분철글 상태 필터 및 삭제 확인
  • 기존 API JWT 인증 정상 동작 확인 (어드민 체인과 분리 확인)

Closes #212

Summary by CodeRabbit

  • 새 기능
    • 관리자 로그인/대시보드 및 유저·분철글 관리 화면(페이지네이션, 상태 필터)을 추가했습니다.
    • 유저 정지/해제/강제 탈퇴, 분철글 삭제 기능을 제공합니다.
  • 보안/설정
    • /admin/** 접근을 관리자 권한으로 제한하고 전용 로그인/로그아웃 흐름을 적용했습니다.
    • 관리자 계정은 환경 변수로 설정하며, 테스트용 관리자 설정도 추가했습니다.
  • 버그 수정
    • 소셜 로그인 시 정지 계정도 차단되도록 개선했습니다.
    • 주문이 있는 분철글 삭제를 차단하고, 관련 오류 처리/탈퇴 사유 저장을 강화했습니다.

- Thymeleaf + Bootstrap 5 기반 SSR 어드민 패널 추가
- InMemoryUserDetailsManager로 환경변수 기반 어드민 계정 관리
- Spring Security 별도 FilterChain(@order(1))으로 API와 세션 분리
- 대시보드(통계), 유저 관리(정지/해제/강제탈퇴), 분철글 관리(상태필터/삭제) 구현
- UserStatus에 SUSPENDED 추가, User에 suspend/unsuspend/withdraw(reason) 메서드 추가
- ErrorStatus에 PROMPT_ERROR(50005) 추가
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Spring Boot와 Thymeleaf 기반 어드민 패널이 추가되었습니다. 관리자 전용 세션 인증, 대시보드 통계, 사용자 상태 관리, 게시글 필터·페이지네이션·삭제 기능과 관련 SSR 화면이 구현되었습니다.

Changes

관리자 패널

Layer / File(s) Summary
관리자 인증 및 보안 설정
build.gradle, src/main/java/org/sopt/poti/global/config/*, src/main/java/org/sopt/poti/global/security/*, src/main/resources/application.yml, src/test/resources/application-test.yml
환경변수 기반 인메모리 관리자 계정, BCrypt 인증, /admin/** 전용 필터 체인과 보안 설정 우선순위가 추가되었습니다.
관리자 도메인 작업
src/main/java/org/sopt/poti/domain/user/entity/User.java, src/main/java/org/sopt/poti/domain/admin/service/AdminService.java, src/main/java/org/sopt/poti/domain/groupbuy/repository/GroupBuyRepository.java, src/main/java/org/sopt/poti/domain/order/repository/OrderRepository.java, src/main/java/org/sopt/poti/domain/auth/service/AuthService.java, src/main/java/org/sopt/poti/global/error/ErrorStatus.java
사용자 정지·해제·강제 탈퇴, 탈퇴 사유 저장, 정지 계정 소셜 로그인 차단, refresh token 삭제와 게시글 상태별 페이징·삭제 검증이 추가되었습니다.
관리자 라우팅
src/main/java/org/sopt/poti/domain/admin/controller/AdminController.java
로그인, 대시보드 통계, 사용자·게시글 목록 조회와 사용자 상태 변경·게시글 삭제 요청을 관리자 서비스에 연결했습니다.
Thymeleaf 관리자 화면
src/main/resources/templates/admin/*
공용 레이아웃과 로그인, 대시보드, 사용자 관리, 게시글 관리 화면에 데이터 바인딩, 상태 필터, 관리 액션과 페이지네이션을 구성했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant 관리자
  participant AdminSecurityConfig
  participant AdminController
  participant AdminService
  participant Repository
  관리자->>AdminSecurityConfig: 관리자 로그인 요청
  AdminSecurityConfig-->>관리자: 세션 인증 처리
  관리자->>AdminController: 목록 또는 관리 액션 요청
  AdminController->>AdminService: 조회·상태 변경·삭제 호출
  AdminService->>Repository: 페이징 조회 또는 엔티티 변경
  Repository-->>AdminService: 조회 결과
  AdminService-->>AdminController: 결과 또는 처리 완료
  AdminController-->>관리자: Thymeleaf 화면 또는 목록 리다이렉트
Loading

Possibly related PRs

  • team-poti/POTI-SERVER#53: User 상태 변경과 AuthService의 탈퇴·정지 계정 처리 경로가 동일하게 수정됩니다.

Suggested labels: 🔥 P0, 👤 User/Auth, 📦 Deal, 🛒 Order

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Firebase Admin SDK와 테스트용 Gemini API 키 추가는 어드민 패널 구현과 직접 관련이 없어 범위를 벗어납니다. 불필요한 Firebase/Gemini 설정은 제거하거나, 어드민 패널과의 필요성을 별도 이슈로 분리해 주세요.
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 (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 어드민 패널(SSR) 구현이라는 핵심 변경을 잘 요약한 제목입니다.
Linked Issues check ✅ Passed 로그인, 대시보드, 유저/분철글 관리와 세션 인증이 모두 구현돼 #212 요구사항을 충족합니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#212/admin-panel

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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/org/sopt/poti/domain/auth/service/AuthService.java (1)

62-65: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

이용 정지(SUSPENDED) 유저의 재로그인 차단 로직을 추가해 주세요.

탈퇴한 유저뿐만 아니라 이번에 새롭게 추가된 SUSPENDED 상태의 유저도 소셜 로그인을 통해 새로운 토큰을 발급받지 못하도록 막아야 합니다. 현재 로직에서는 정지된 유저가 로그인을 시도할 경우 정상적으로 엑세스가 허용되는 보안 빈틈이 존재하네요.
Based on learnings, 코드 리뷰 시 존댓말을 사용하도록 권장하는 규칙에 따라 존댓말로 제안해 드립니다.

🛡️ 수정 제안
       // 탈퇴한 유저인지 확인
       if (user.getStatus() == UserStatus.WITHDRAWN) {
         throw new BusinessException(ErrorStatus.USER_NOT_FOUND);
       }
+      // 정지된 유저인지 확인
+      if (user.getStatus() == UserStatus.SUSPENDED) {
+        throw new BusinessException(ErrorStatus.USER_NOT_FOUND); // 또는 정지 관련 전용 에러 코드 사용
+      }
🤖 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/org/sopt/poti/domain/auth/service/AuthService.java` around
lines 62 - 65, AuthService의 기존 탈퇴 상태 검증 로직에서 UserStatus.SUSPENDED도 로그인 차단 대상에
포함해 주세요. WITHDRAWN과 SUSPENDED 사용자가 모두 USER_NOT_FOUND 예외를 받도록 처리하고, 그 외 상태의 소셜
로그인 및 토큰 발급 흐름은 유지해 주세요.

Source: Learnings

🤖 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/org/sopt/poti/domain/admin/service/AdminService.java`:
- Around line 51-70: AdminService의 suspendUser 및 forceWithdrawUser 처리에
RefreshTokenRepository를 주입하고, 대상 User를 정지하거나 강제 탈퇴시키기 전에 해당 사용자의 유효한 Refresh
Token을 저장소에서 완전히 삭제하도록 수정해 주세요. unsuspendUser의 기존 동작은 변경하지 마세요.

---

Outside diff comments:
In `@src/main/java/org/sopt/poti/domain/auth/service/AuthService.java`:
- Around line 62-65: AuthService의 기존 탈퇴 상태 검증 로직에서 UserStatus.SUSPENDED도 로그인 차단
대상에 포함해 주세요. WITHDRAWN과 SUSPENDED 사용자가 모두 USER_NOT_FOUND 예외를 받도록 처리하고, 그 외 상태의
소셜 로그인 및 토큰 발급 흐름은 유지해 주세요.
🪄 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: CHILL

Plan: Pro

Run ID: d93d5b09-b10e-4d3a-abbd-dede7f61101c

📥 Commits

Reviewing files that changed from the base of the PR and between d633702 and 0476bc0.

📒 Files selected for processing (16)
  • build.gradle
  • src/main/java/org/sopt/poti/domain/admin/controller/AdminController.java
  • src/main/java/org/sopt/poti/domain/admin/service/AdminService.java
  • src/main/java/org/sopt/poti/domain/auth/service/AuthService.java
  • src/main/java/org/sopt/poti/domain/groupbuy/repository/GroupBuyRepository.java
  • src/main/java/org/sopt/poti/domain/user/entity/User.java
  • src/main/java/org/sopt/poti/domain/user/entity/UserStatus.java
  • src/main/java/org/sopt/poti/global/config/AdminSecurityConfig.java
  • src/main/java/org/sopt/poti/global/error/ErrorStatus.java
  • src/main/java/org/sopt/poti/global/security/SecurityConfig.java
  • src/main/resources/application.yml
  • src/main/resources/templates/admin/dashboard.html
  • src/main/resources/templates/admin/layout.html
  • src/main/resources/templates/admin/login.html
  • src/main/resources/templates/admin/posts.html
  • src/main/resources/templates/admin/users.html

Comment thread src/main/java/org/sopt/poti/domain/admin/service/AdminService.java
@PBEM22 PBEM22 self-assigned this Jul 21, 2026
@PBEM22 PBEM22 added the ✨ Feat 새로운 기능 추가 label Jul 21, 2026
PBEM22 added 3 commits July 21, 2026 21:02
- Keep Thymeleaf dependencies (admin SSR)
- Keep admin credentials config (ADMIN_USERNAME, ADMIN_PASSWORD)
- Merge withdraw() with reason param and validateNoActiveTransaction from develop
- Keep suspend()/unsuspend() methods in User added for admin panel

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/org/sopt/poti/domain/admin/service/AdminService.java (1)

68-74: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

관리자 강제 탈퇴 시 FCM 토큰도 삭제해 주세요.

forceWithdrawUser는 Refresh Token만 삭제하지만, 일반 탈퇴 흐름인 AuthService.withdraw에서는 fcmTokenService.deleteAllByUserId(userId)도 수행합니다(src/main/java/org/sopt/poti/domain/auth/service/AuthService.java:173-177). 현재 구현은 강제 탈퇴된 계정의 디바이스 토큰을 남겨 이후 푸시 알림이 전달되거나 토큰이 불필요하게 보관될 수 있습니다.

수정 제안
+  private final FcmTokenService fcmTokenService;
+
   `@Transactional`
   public void forceWithdrawUser(Long userId) {
     User user = userRepository.findById(userId)
         .orElseThrow(() -> new BusinessException(ErrorStatus.USER_NOT_FOUND));
     user.withdraw(null);
+    fcmTokenService.deleteAllByUserId(userId);
     refreshTokenRepository.deleteById(userId);
   }
🤖 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/org/sopt/poti/domain/admin/service/AdminService.java` around
lines 68 - 74, Update AdminService.forceWithdrawUser to also call
fcmTokenService.deleteAllByUserId(userId) after withdrawing the user, matching
the cleanup performed by AuthService.withdraw while preserving the existing
refresh-token deletion.
src/main/resources/application.yml (1)

112-115: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

빈 관리자 비밀번호 기동을 막는 fail-fast 검증이 필요해요.

ADMIN_PASSWORD가 빈 문자열이면 존재하는 환경변수로 바인딩되어 AdminSecurityConfigBCryptPasswordEncoder.encode("") 값을 생성하고, 이후 같은 빈 비밀번호로 /admin/login 로그인이 가능해져요. 기존 spring-boot-starter-validation을 활용해 admin.username, admin.password 설정을 비공백으로 검증하는 편이 안전합니다.

🤖 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/resources/application.yml` around lines 112 - 115, application.yml의
admin.username과 admin.password가 빈 값 또는 공백으로 바인딩되지 않도록 fail-fast 검증을 추가하세요. 기존
spring-boot-starter-validation을 활용해 해당 설정 프로퍼티를 비공백으로 제한하고, AdminSecurityConfig가
빈 비밀번호를 인코딩하거나 로그인에 사용하기 전에 애플리케이션 기동이 실패하도록 연결하세요.
🤖 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.

Outside diff comments:
In `@src/main/java/org/sopt/poti/domain/admin/service/AdminService.java`:
- Around line 68-74: Update AdminService.forceWithdrawUser to also call
fcmTokenService.deleteAllByUserId(userId) after withdrawing the user, matching
the cleanup performed by AuthService.withdraw while preserving the existing
refresh-token deletion.

In `@src/main/resources/application.yml`:
- Around line 112-115: application.yml의 admin.username과 admin.password가 빈 값 또는
공백으로 바인딩되지 않도록 fail-fast 검증을 추가하세요. 기존 spring-boot-starter-validation을 활용해 해당 설정
프로퍼티를 비공백으로 제한하고, AdminSecurityConfig가 빈 비밀번호를 인코딩하거나 로그인에 사용하기 전에 애플리케이션 기동이
실패하도록 연결하세요.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dafcb130-c38a-4cfe-bb53-25b6c118948b

📥 Commits

Reviewing files that changed from the base of the PR and between 04b74bf and 6c780c8.

📒 Files selected for processing (4)
  • src/main/java/org/sopt/poti/domain/admin/service/AdminService.java
  • src/main/java/org/sopt/poti/domain/auth/service/AuthService.java
  • src/main/java/org/sopt/poti/global/error/ErrorStatus.java
  • src/main/resources/application.yml

@PBEM22
PBEM22 requested a review from 88guri July 27, 2026 07:13
@PBEM22 PBEM22 added the 🐴 채륜 막시무스 label Jul 27, 2026

@88guri 88guri left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

어드민 기능이 구조 깔끔하게 잘 나눠져있네요 ! !
SecurityFilterChain @order 분리 방식 잘 봤습니다 👍 👍 수고하셨어요 ~

Comment thread src/main/java/org/sopt/poti/domain/admin/service/AdminService.java
- deletePost() 실행 전 주문 존재 여부 체크 추가
- 주문이 있으면 POST_HAS_ORDERS(40020) 예외 반환
- OrderRepository에 existsByGroupBuyPost_Id() 추가
@PBEM22

PBEM22 commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

@88guri 지적 감사합니다! Order에 cascade 설정이 없어서 주문이 있는 글을 삭제하면 DB FK 제약 위반이 발생하는 문제였습니다.

수정 내용 (8593239):

  • OrderRepositoryexistsByGroupBuyPost_Id() 추가
  • deletePost() 실행 전 주문 존재 여부 체크 추가
  • 주문이 있으면 POST_HAS_ORDERS(40020, 400) 예외로 명시적 차단

PBEM22 added 2 commits July 27, 2026 23:20
- CLOSED/PAYMENT_DONE/SHIPPING 상태 글은 주문 유무 무관하게 삭제 차단 (POST_IN_PROGRESS 40021)
- 상태 기반 체크를 주문 존재 체크보다 먼저 수행
- 프론트 confirm: RECRUITING은 일반 경고, 그 외 상태는 '진행 중' 강조 메시지로 구분
- User.suspend/unsuspend: WITHDRAWN 계정 상태 변경 시도 시 예외 발생
- AdminService.forceWithdrawUser: 이미 탈퇴한 유저 중복 탈퇴 차단
- AdminController: BusinessException 발생 시 JSON 대신 flash 메시지로 redirect
- layout.html: 에러 메시지 alert 영역 추가
@PBEM22
PBEM22 merged commit 4b0a773 into develop Jul 28, 2026
2 checks passed
@PBEM22
PBEM22 deleted the feat/#212/admin-panel branch July 28, 2026 02:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feat 새로운 기능 추가 🐴 채륜 막시무스

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 어드민 패널 구현 (SSR)

2 participants