Skip to content

feat: 어드민 기능 구현#55

Merged
Bumnote merged 3 commits intodevfrom
feat/admin
Mar 22, 2026
Merged

feat: 어드민 기능 구현#55
Bumnote merged 3 commits intodevfrom
feat/admin

Conversation

@Bumnote
Copy link
Copy Markdown
Member

@Bumnote Bumnote commented Mar 22, 2026

#️⃣ 연관된 이슈

#51

📝 작업 내용

Feat

  • ADMIN role인 관리자만 문장을 새롭게 추가할 수 있도록 Controller 및 Service를 구현했습니다.
  • @PreAuthorize 어노테이션을 통해서 ADMIN 계정이 아닌 경우, 403 FORBIDDEN 예외를 반환합니다.

Refactor

  • Member Role Enum에도 ADMIN 타입을 추가합니다.
  • 이전에 누락된 ADMIN role을 모든 Controller에 추가합니다.
  • 하드코딩 되어있는 Role(GUEST, USER) 로직을 제거하고, ADMIN 역할도 포함할 수 있도록 파라미터로 분리했습니다.
  • 예외 코드에 HttpStatus를 추가하여 보다 자세한 예외 응답을 반환할 수 있도록 추가했습니다.
  • 접두사 "ROLE_" 이 없어 생기는 오류를 해결하기 위해서 접두사를 붙였습니다.

💬 리뷰 요구사항

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


Bumnote added 3 commits March 22, 2026 23:20
- ADMIN role인 관리자만 문장을 새롭게 추가할 수 있도록 Controller 및 Service를 구현했습니다.
- @PreAuthorize 어노테이션을 통해서 ADMIN 계정이 아닌 경우, 403 FORBIDDEN 예외를 반환합니다.

issue #51
- Member Role Enum에도 ADMIN 타입을 추가합니다.
- 이전에 누락된 ADMIN role을 모든 Controller에 추가합니다.

issue #51
- 하드코딩 되어있는 Role(GUEST, USER) 로직을 제거하고, ADMIN 역할도 포함할 수 있도록 파라미터로 분리했습니다.
- 예외 코드에 HttpStatus를 추가하여 보다 자세한 예외 응답을 반환할 수 있도록 추가했습니다.
- 접두사 "ROLE_" 이 없어 생기는 오류를 해결하기 위해서 접두사를 붙였습니다.

issue #51
@Bumnote Bumnote requested a review from Copilot March 22, 2026 15:08
@Bumnote Bumnote self-assigned this Mar 22, 2026
@Bumnote Bumnote merged commit 2c13322 into dev Mar 22, 2026
Copy link
Copy Markdown
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

Implements admin capabilities by introducing an ADMIN role, issuing JWTs with role claims, and adding an admin-only bulk phrase creation API.

Changes:

  • Add ADMIN role and update Spring Security role handling to consistently use the ROLE_ prefix.
  • Include role in JWT claims and build authentication authorities from the token’s role claim.
  • Introduce admin-only phrase bulk creation controller/service and improve exception codes with HttpStatus.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/main/java/dasi/typing/jwt/JwtTokenProvider.java Adds role claim support to JWTs and exposes role extraction from token.
src/main/java/dasi/typing/handler/OAuth2AuthenticationSuccessHandler.java Generates JWTs using member role and updates redirect path.
src/main/java/dasi/typing/filter/JwtAuthenticationFilter.java Builds authorities from JWT role claim with ROLE_ prefix.
src/main/java/dasi/typing/filter/GuestAuthenticationFilter.java Aligns guest authority to ROLE_GUEST.
src/main/java/dasi/typing/exception/Code.java Extends error codes to include HttpStatus for richer responses.
src/main/java/dasi/typing/domain/member/Role.java Adds ADMIN role.
src/main/java/dasi/typing/api/service/member/MemberService.java Uses role-aware JWT issuance, adds logout, refactors temp token key handling and sign-up validation.
src/main/java/dasi/typing/api/service/admin/phrase/request/PhraseCreateServiceRequest.java Adds service-layer request record for admin phrase creation.
src/main/java/dasi/typing/api/service/admin/phrase/AdminPhraseService.java Implements bulk phrase creation and assigns random randId.
src/main/java/dasi/typing/api/controller/typing/TypingController.java Allows ADMIN to access typing endpoints.
src/main/java/dasi/typing/api/controller/ranking/RankingController.java Allows ADMIN to access ranking endpoints.
src/main/java/dasi/typing/api/controller/phrase/PhraseController.java Allows ADMIN to access phrase endpoints.
src/main/java/dasi/typing/api/controller/mypage/MyPageController.java Allows ADMIN access to mypage endpoints.
src/main/java/dasi/typing/api/controller/member/MemberController.java Allows ADMIN reissue + adds logout endpoint clearing cookies and refresh token.
src/main/java/dasi/typing/api/controller/consent/ConsentController.java Allows ADMIN to access consent endpoints.
src/main/java/dasi/typing/api/controller/admin/phrase/request/PhraseCreateRequest.java Adds controller DTO and mapping to service request.
src/main/java/dasi/typing/api/controller/admin/phrase/request/PhraseBulkCreateRequest.java Adds bulk request wrapper and conversion to service requests.
src/main/java/dasi/typing/api/controller/admin/phrase/AdminPhraseController.java Adds admin-only endpoint for bulk phrase creation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

List<Consent> consents = consentRepository.findByTypeInAndActiveTrue(request.getAgreements());

if (consents.size() != REQUIRED_CONSENT_COUNT) {
if (consents.size() < REQUIRED_CONSENT_COUNT) {
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

consents.size() < REQUIRED_CONSENT_COUNT can incorrectly pass when the request includes extra consent types while still missing one of the required ones (size meets the threshold but required set is incomplete). Prefer validating that all required consent types are present (e.g., set inclusion check against required types) rather than relying on list size.

Suggested change
if (consents.size() < REQUIRED_CONSENT_COUNT) {
if (request.getAgreements() == null
|| request.getAgreements().size() != REQUIRED_CONSENT_COUNT
|| consents.size() != REQUIRED_CONSENT_COUNT) {

Copilot uses AI. Check for mistakes.
Comment on lines 41 to +45
if (isNotEmpty(token) && (isReissueRequest(requestURI) || jwtTokenProvider.validateAccessToken(token))) {

String kakaoId = jwtTokenProvider.getKakaoId(token);
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("USER");
String role = jwtTokenProvider.getRole(token);
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_" + role);
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

This parses JWT claims at least twice per request (getKakaoId and getRole each call into token parsing). Consider extracting claims once (e.g., a single provider method returning the parsed claims or a DTO containing both kakaoId and role) to avoid duplicated parsing work on every authenticated request.

Copilot uses AI. Check for mistakes.
.author(req.author())
.lang(req.lang())
.type(req.type())
.randId(ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE))
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

Assigning randId via nextInt(Integer.MAX_VALUE) risks collisions, especially over time or under bulk inserts. If randId is used for lookup or has a uniqueness constraint, prefer a collision-resistant approach (DB-generated value/sequence, UUID, or a repository-level uniqueness strategy) and/or ensure uniqueness within the batch before saveAll.

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +20
public List<PhraseCreateServiceRequest> toServiceRequests() {
return phrases.stream()
.map(PhraseCreateRequest::toServiceRequest)
.toList();
}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

phrases.stream() will throw a NullPointerException if the request body omits phrases (or sends it as null). Consider adding bean validation (@NotNull / @NotEmpty) to phrases and using @Valid on the controller parameter to fail fast with a controlled 400 response instead of an NPE.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants