Skip to content

[Feat] 관리자 및 마이페이지 API 계약 및 Swagger 명세 작성 - #32

Merged
kallin1 merged 10 commits into
developfrom
feat/#31-mypage-admin-api-contract
Jul 25, 2026
Merged

[Feat] 관리자 및 마이페이지 API 계약 및 Swagger 명세 작성#32
kallin1 merged 10 commits into
developfrom
feat/#31-mypage-admin-api-contract

Conversation

@kallin1

@kallin1 kallin1 commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

변경 사항

  • 마이페이지 프로필 조회(GET /users/me), 게시완료 목록(GET /prompts/me), 게시글 인사이트(GET /prompts/me/insights), 관리자 신고함 조회·처리(GET/PATCH /admin/reports), Origin 등급업 신청 조회·승인/반려(GET/PATCH /admin/grade-requests) 등 7개 API 골격을 추가했습니다.
  • 프론트엔드 선행 개발을 위한 Request/Response DTO를 정의했습니다.
  • 목록 조회 API에 쓸 공통 페이지네이션 응답(PageResponse)을 Spring Page 스타일로 새로 도입했습니다.
  • Swagger에 요청·응답 스키마와 주요 오류 응답, 미구현 상태를 표시했습니다.
  • 미구현 비즈니스 로직은 가짜 성공 대신 501 Not Implemented를 반환합니다.
  • 신고 대상을 targetType: POST | COMMENT로 통합 관리하기 위해 ReportTargetType을 추가하고, 대상 타입별 허용 사유 집합을 ReportReason.isAllowedFor로 검증하는 도메인 규칙을 추가했습니다.
  • RequestParam으로 받는 enum(targetType, status 등)에 잘못된 값이 들어오면 500 대신 400을 반환하도록 GlobalExceptionHandler를 보완했습니다.

반영 정책

  • GET /prompts/mestatus 값은 이슈 PUBLISHED였으나, 기존 PromptStatus enum의 ACTIVE가 이미 게시 완료 상태를 의미하므로 별도 값을 추가하지 않고 ACTIVE를 그대로 사용합니다.
  • 게시완료 목록은 논리 삭제된 게시물을 제외하고, 목록 카드에 필요한 필드만 포함하며 프롬프트 본문은 포함하지 않습니다.
  • 인사이트는 논리 삭제 제외 기준 실시간 합산(SUM) 방식으로 계약을 정의했습니다.
  • 신고 처리 상태는 PENDING으로 되돌릴 수 없고 RESOLVED/REJECTED만 허용합니다.
  • 등급업 신청 승인 시 신청 상태 변경과 유저 등급 변경을 하나의 트랜잭션으로 처리하고, 반려 시 유저 등급은 변경하지 않는 정책을 Swagger에 명시했습니다.
  • 임시저장 탭은 신규 API 구현 없이 기존 GET /prompts/draft를 재사용합니다(이번 PR에서 코드 변경 없음).

범위 제외 / 추후 확정

  • Application/UseCase 및 영속성 구현, 관리자 권한(@PreAuthorize) 검증은 후속 이슈로 분리했습니다.
  • 신고 생성 API는 미구현 상태이며, 이번 범위에서는 조회·처리만 제공합니다. PostReport가 현재 postId만 지원하므로 COMMENT 대상 신고는 영속성 계층 확장이 필요합니다.
  • 등급업 신청 생성 방식(유저 직접 신청 vs 시스템 자동 생성), 반려 사유 입력 여부, 중복 신청 정책 등이 미정이라 GradeRequestStatus와 관련 응답 필드는 Swagger에 "정책 미정"으로 표시했습니다.
  • 게시(ACTIVE 전환) 시각을 저장할 전용 컬럼이 아직 없어 publishedAt 필드의 실제 매핑은 후속 구현에서 확정이 필요합니다.
  • PromptController/PromptControllerDocs는 먼저 병합된 PR [Feat] 프롬프트 생성·임시저장·삭제 API 계약 및 Swagger 명세 작성 #23(프롬프트 생성·임시저장·삭제 계약)과 같은 파일을 다뤄, origin/develop 기준으로 rebase하여 두 기능을 한 컨트롤러/문서로 병합했습니다. Operation 넘버링은 PR #23의 PROMPT-005에 이어 PROMPT-006/007로 지정했습니다.

테스트

  • ./gradlew test
  • Controller 7개 라우팅 및 501 응답 검증 (UserControllerTest, PromptControllerTest, AdminReportControllerTest, AdminGradeRequestControllerTest)
  • 요청 값·페이지 크기·enum 검증 실패 시 400 응답 검증
  • Swagger 스키마 노출 및 태그 설명(미구현/정책 미정 표시) 검증 (UserOpenApiContractTest, PromptOpenApiContractTest, AdminOpenApiContractTest)
  • 신고 대상 타입별 허용 사유 도메인 규칙 단위 테스트 (ReportReasonTest)

Closes #31

@kallin1
kallin1 changed the base branch from main to develop July 24, 2026 14:47
kallin1 added 7 commits July 24, 2026 23:50
목록 조회 API에서 공통으로 쓸 페이지네이션 응답 구조가 없어 Spring Page
스타일(content, page, size, totalElements, totalPages, hasNext)로 새로 정의함
RequestParam으로 받는 enum(targetType, status 등)에 잘못된 값이 들어오면
MethodArgumentTypeMismatchException이 처리되지 않아 500으로 응답되던 문제를 수정함
신고 대상을 POST | COMMENT로 통합 관리하기 위해 ReportTargetType을 추가하고,
COMMENT 대상은 COPYRIGHT·LOW_QUALITY 사유를 허용하지 않는 도메인 규칙을
ReportReason.isAllowedFor로 추가함. 신고 생성 API가 아직 없어 실제 호출부는
후속 이슈에서 연결함
GET /api/v1/users/me 엔드포인트 골격과 UserProfileResponse(username,
profileImageUrl, email, point, gradeName)를 추가함. 실제 조회 로직은
아직 없어 NotImplementedException으로 501을 반환함
prompt/interfaces 패키지를 새로 만들고 GET /api/v1/prompts/me(status=ACTIVE
필터, 페이지네이션), GET /api/v1/prompts/me/insights 엔드포인트 골격을 추가함.
목록 응답은 카드에 필요한 필드만 포함하고 프롬프트 본문은 제외함. 인사이트는
totalViews, totalRecommends, totalCopies를 반환하도록 계약만 정의함
admin/interfaces 패키지에 AdminReportController를 추가해 GET
/api/v1/admin/reports(targetType·status 필터, 페이지네이션), PATCH
/api/v1/admin/reports/{reportId} 엔드포인트 골격을 작성함. 처리 상태는
PENDING으로 되돌릴 수 없도록 검증하며, 신고 생성 API는 이번 범위에서 제외됨을
Swagger 설명에 명시함
admin/interfaces 패키지에 AdminGradeRequestController를 추가해 GET
/api/v1/admin/grade-requests(status 필터, 페이지네이션), PATCH
/api/v1/admin/grade-requests/{requestId} 엔드포인트 골격을 작성함. 처리 결과는
APPROVED 또는 REJECTED만 허용하며, 신청 생성 방식 등 정책이 아직 정해지지
않아 GradeRequestStatus와 응답 필드는 정책 미정으로 Swagger에 표시함
@kallin1
kallin1 force-pushed the feat/#31-mypage-admin-api-contract branch from ae2fddd to c1345b2 Compare July 24, 2026 14:57

@Hanharam Hanharam left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

지금은 /api/v1/admin/**는 관리자 전용 경로지만, Security 설정에서는 관리자 권한이 아니라 로그인 여부만 확인하고 있습니다. 그래서 추후에 기능 구현하실 때는

.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()

이렇게 관리자 api endpoint 의 권한을 확인하면 좋을 것 같습니다!

String message = "%s 값이 유효하지 않습니다.".formatted(e.getName());

log.warn("[TYPE MISMATCH] {}", message);
return buildResponse(e, CommonErrorCode.BAD_REQUEST, HttpHeaders.EMPTY, request, message);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

이 예외 핸들러는 RequestParam이나 PathVariable을 타입으로 변환하지 못한 예외를 잡고 있습니다. 그래서 @Valid 예외와 같은 맥락이라고 생각해서 @Valid 핸들러와 같은 예외로 던지는 것이 좋을 것 같습니다!

예를 들어 status=WRONG 처럼 존재하지 않는 enum을 보낸 경우도 page=-1이나 DTO 검증 실패와 동일한 입력값 오류입니다. 지금은 이 경우에만 COMMON-400을 반환해 같은 종류의 오류가 서로 다른 코드로 내려가고 있습니다. 프론트엔드가 입력 오류를 하나의 기준으로 처리할 수 있도록 기존에 @Valid 예외에서 사용하고 있던 INVALID_INPUT_VALUE(COMMON-001)를 사용하는 것이 좋을 것 같습니다!

return buildResponse(
        e,
        CommonErrorCode.INVALID_INPUT_VALUE,
        HttpHeaders.EMPTY,
        request,
        message
);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

말씀하신대로 enum 값이 잘못 들어온 경우도 @Valid 검증 실패와 같은 입력값 오류로 보는 게 맞겠네요MethodArgumentTypeMismatchException 처리 부분을 INVALID_INPUT_VALUE(COMMON-001)로 바꿔서 반영했습니다. 감사합니다!

if (targetType == ReportTargetType.COMMENT) {
return !COMMENT_DISALLOWED.contains(this);
}
return true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

null 또는 추후에 추가될 수 있는 ReportTargetType이 모두 허용되고 있습니다. 신고 대상 타입이 늘어날 때 별도 정책 검토 없이 모든 사유가 허용될 수 있어서 switch로 각 타입을 명시하고 null은 거부하면 확장성 측면에서 좋을 것 같습니다!

public boolean isAllowedFor(ReportTargetType targetType) {
    if (targetType == null) {
        return false;
    }

    return switch (targetType) {
        case COMMENT -> !COMMENT_DISALLOWED.contains(this);
        case PROMPT -> true;
    };
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

제안해주신 대로 null은 거부하고 switch로 각 타입을 명시하도록 수정했습니다!

kallin1 added 3 commits July 26, 2026 00:26
…min-api-contract

# Conflicts:
#	src/main/java/com/promsearch/prompt/interfaces/PromptController.java
#	src/main/java/com/promsearch/prompt/interfaces/docs/PromptControllerDocs.java
#	src/main/java/com/promsearch/user/interfaces/UserController.java
#	src/main/java/com/promsearch/user/interfaces/docs/UserControllerDocs.java
- MethodArgumentTypeMismatchException 처리 시 COMMON-400 대신 @Valid 검증과 동일한 INVALID_INPUT_VALUE(COMMON-001) 반환
- ReportReason.isAllowedFor에서 null 대상 타입을 거부하고 switch로 대상 타입을 명시해 신규 타입 추가 시 누락을 방지
admin 패키지가 develop의 인터페이스 DTO 분리 컨벤션(#39)이 도입되기 전에 작성되어
PackageStructureTest.interfaceDtosAreSeparatedIntoRequestAndResponsePackages를
위반하고 있었습니다. GradeRequestSummaryResponse, ReportSummaryResponse는 dto/response로,
ProcessGradeRequestRequest, UpdateReportStatusRequest는 dto/request로 옮겨 다른 도메인과
동일한 구조를 따르도록 정리합니다.
@kallin1
kallin1 merged commit 7a03227 into develop Jul 25, 2026
2 checks passed
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.

[Feat] 관리자 및 마이페이지 API 계약 및 Swagger 명세 작성

2 participants