[FEAT] #212 어드민 패널 구현 (SSR) - #213
Conversation
- Thymeleaf + Bootstrap 5 기반 SSR 어드민 패널 추가 - InMemoryUserDetailsManager로 환경변수 기반 어드민 계정 관리 - Spring Security 별도 FilterChain(@order(1))으로 API와 세션 분리 - 대시보드(통계), 유저 관리(정지/해제/강제탈퇴), 분철글 관리(상태필터/삭제) 구현 - UserStatus에 SUSPENDED 추가, User에 suspend/unsuspend/withdraw(reason) 메서드 추가 - ErrorStatus에 PROMPT_ERROR(50005) 추가
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughSpring Boot와 Thymeleaf 기반 어드민 패널이 추가되었습니다. 관리자 전용 세션 인증, 대시보드 통계, 사용자 상태 관리, 게시글 필터·페이지네이션·삭제 기능과 관련 SSR 화면이 구현되었습니다. Changes관리자 패널
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 화면 또는 목록 리다이렉트
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ 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 |
There was a problem hiding this comment.
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
📒 Files selected for processing (16)
build.gradlesrc/main/java/org/sopt/poti/domain/admin/controller/AdminController.javasrc/main/java/org/sopt/poti/domain/admin/service/AdminService.javasrc/main/java/org/sopt/poti/domain/auth/service/AuthService.javasrc/main/java/org/sopt/poti/domain/groupbuy/repository/GroupBuyRepository.javasrc/main/java/org/sopt/poti/domain/user/entity/User.javasrc/main/java/org/sopt/poti/domain/user/entity/UserStatus.javasrc/main/java/org/sopt/poti/global/config/AdminSecurityConfig.javasrc/main/java/org/sopt/poti/global/error/ErrorStatus.javasrc/main/java/org/sopt/poti/global/security/SecurityConfig.javasrc/main/resources/application.ymlsrc/main/resources/templates/admin/dashboard.htmlsrc/main/resources/templates/admin/layout.htmlsrc/main/resources/templates/admin/login.htmlsrc/main/resources/templates/admin/posts.htmlsrc/main/resources/templates/admin/users.html
- 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
There was a problem hiding this comment.
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가 빈 문자열이면 존재하는 환경변수로 바인딩되어AdminSecurityConfig가BCryptPasswordEncoder.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
📒 Files selected for processing (4)
src/main/java/org/sopt/poti/domain/admin/service/AdminService.javasrc/main/java/org/sopt/poti/domain/auth/service/AuthService.javasrc/main/java/org/sopt/poti/global/error/ErrorStatus.javasrc/main/resources/application.yml
- deletePost() 실행 전 주문 존재 여부 체크 추가 - 주문이 있으면 POST_HAS_ORDERS(40020) 예외 반환 - OrderRepository에 existsByGroupBuyPost_Id() 추가
- CLOSED/PAYMENT_DONE/SHIPPING 상태 글은 주문 유무 무관하게 삭제 차단 (POST_IN_PROGRESS 40021) - 상태 기반 체크를 주문 존재 체크보다 먼저 수행 - 프론트 confirm: RECRUITING은 일반 경고, 그 외 상태는 '진행 중' 강조 메시지로 구분
- User.suspend/unsuspend: WITHDRAWN 계정 상태 변경 시도 시 예외 발생 - AdminService.forceWithdrawUser: 이미 탈퇴한 유저 중복 탈퇴 차단 - AdminController: BusinessException 발생 시 JSON 대신 flash 메시지로 redirect - layout.html: 에러 메시지 alert 영역 추가
Summary
@Order(1))으로 API JWT 인증과 어드민 세션 인증 분리ADMIN_USERNAME/ADMIN_PASSWORD환경변수로 관리 (DB 테이블 불필요)구현 내용
/admin/login/admin/dashboard/admin/users/admin/posts변경 사항
UserStatusSUSPENDED 추가Usersuspend()/unsuspend()/withdraw(String reason)/withdrawalReason필드 추가ErrorStatusPROMPT_ERROR(50005)추가SecurityConfig@Order(2)추가.env에ADMIN_USERNAME,ADMIN_PASSWORD추가 필요Test plan
/admin/login에서 환경변수 계정으로 로그인 확인Closes #212
Summary by CodeRabbit
/admin/**접근을 관리자 권한으로 제한하고 전용 로그인/로그아웃 흐름을 적용했습니다.