Skip to content

[Fix] 관리자 프로젝트 페이지네이션이 정상 동작하지 않는 문제 해결 #30 - #38

Merged
jjjjjk12 merged 5 commits into
mainfrom
fix/#30/admin-project-pagination
Jun 18, 2026
Merged

[Fix] 관리자 프로젝트 페이지네이션이 정상 동작하지 않는 문제 해결 #30#38
jjjjjk12 merged 5 commits into
mainfrom
fix/#30/admin-project-pagination

Conversation

@callme-waffle

@callme-waffle callme-waffle commented Jun 13, 2026

Copy link
Copy Markdown
Member

📌 변경 요약 (Summary)

이 PR에서 무엇을 변경했는지 간단히 설명해주세요.

  • 관리자 프로젝트 목록/프로젝트 생성요청 목록에서 PageRequest가 정상 바인딩되지 않아 limit, marker가 무시되던 문제를 수정했습니다.
  • 프로젝트 생성요청 목록을 OpenStack 도메인과 동일한 ID cursor 방식으로 정리해 next/prev 페이지 이동이 실제로 동작하도록 수정했습니다.
  • Keystone project pagination 응답에서 links.next URL 전체가 아닌 marker 값만 추출하도록 수정했습니다.
  • 인증 도메인 사용자/역할/공지 목록에서 null PageRequest와 반복 marker에 대한 방어 로직을 보강했습니다.

🔗 관련 이슈 (Related Issue)

이 PR과 관련된 이슈를 작성해주세요.

Closes #30


🛠 작업 내용 / 작업 순서 (Implementation Details)

이번 작업에서 수행한 내용이나 작업 순서를 작성해주세요.

  1. 이슈 [Bug] 관리자 프로젝트 관련 엔드포인트 페이지네이션 관련 오류 #30 내용 확인 및 Swagger 기반 증상 재현
  2. 관리자 프로젝트 문서 계층의 PageRequest 바인딩 방식 점검
  3. 프로젝트 생성요청 목록의 marker 미사용 및 offset 기반 계산 흐름 확인
  4. Keystone project pagination links 파싱 로직 확인
  5. 공통 PaginationUtils 추가 및 null/blank/default marker 처리 통합
  6. 관리자/사용자 프로젝트 생성요청 목록을 projectRequestId 기반 cursor 조회로 변경
  7. 관리자 프로젝트 목록의 Keystone query map 생성 및 marker 파싱 보강
  8. 사용자/역할/공지 인증 도메인 목록의 PageRequest 정규화 및 반복 marker 방어 적용
  9. 단위 테스트, 전체 테스트, 실제 API 동작 테스트 수행
  10. 테스트 데이터 DB/Redis/백그라운드 프로세스 정리 후 잔여물 0건 확인

✨ 주요 변경 사항 (Key Changes)

  • PaginationUtils 추가

    • PageRequest null/default 처리
    • blank marker 정규화
    • Base64 offset marker encode/decode 유틸 제공
    • 기존 offset 기반 도메인의 계약 보존 가능 구조 추가
  • 관리자/사용자 프로젝트 생성요청 목록 수정

    • pageRequest.getMarker() 호출만 하고 값을 쓰지 않던 문제 수정
    • projectRequestId > marker / projectRequestId < marker 기반 cursor 조회 적용
    • direction=prev 조회 시 DESC로 가져온 결과를 응답 순서에 맞게 ASC로 재정렬
    • limit + 1 조회로 다음/이전 페이지 존재 여부를 판단
    • nextMarker, prevMarker, first, last 계산을 실제 cursor 결과 기준으로 정리
  • Keystone project pagination 수정

    • 프로젝트 목록 query map에 marker, limit가 실제 전달되도록 보정
    • 사용자 프로젝트 목록에서 생성한 query map을 버리던 문제 수정
    • links.next/links.previous에서 marker query param만 추출하도록 수정
  • 인증 도메인 보강

    • User/Role/Notice 목록에서 null PageRequest를 기본값으로 정규화
    • Role 목록에서 Keystone이 동일 marker를 반복 반환하는 경우 다음 페이지를 중단하도록 방어

🧪 테스트 결과 (Test Results)

테스트 환경

  • 로컬 Spring Boot 애플리케이션: http://127.0.0.1:8080
  • 테스트 DB: aolda-issue1-mariadb MariaDB 컨테이너
  • 테스트 Redis: aolda-test-redis Redis 컨테이너
  • OpenStack 테스트 환경 연동
  • 인증 방식: Keycloak 로그인 미사용, Redis에 테스트용 SessionData를 직접 생성하고 acc-session-id 쿠키로 API 호출

테스트 방법

  1. 대상 자동 테스트 실행

    • ./gradlew test --tests com.acc.local.service.modules.auth.ProjectModuleTest --tests com.acc.local.service.adapters.project.AdminProjectServiceAdapterTest --tests com.acc.global.common.PaginationUtilsTest
    • 결과: BUILD SUCCESSFUL
  2. 전체 자동 테스트 실행

    • ./gradlew test
    • 결과: BUILD SUCCESSFUL
  3. 실제 API 테스트 환경 확인

    • Spring Boot 애플리케이션 /actuator/health 호출로 UP 확인
    • DB/Redis/OpenStack 연동 상태에서 API 호출
  4. 프로젝트 생성요청 테스트 데이터 생성

    • 테스트 DB에 PENDING 상태의 issue30-selftest-* 프로젝트 생성요청 5건 삽입
    • Redis에 테스트용 세션 issue30-selftest-session 생성
    • 세션의 keystoneUserId는 관리자 테스트 사용자로 설정
  5. 관리자 프로젝트 생성요청 목록 실제 API 호출

    • GET /api/v1/admin/projects/request?keyword=issue30-selftest&limit=2&direction=next
    • GET /api/v1/admin/projects/request?keyword=issue30-selftest&limit=2&direction=next&marker=issue30-selftest-002
    • GET /api/v1/admin/projects/request?keyword=issue30-selftest&limit=2&direction=prev&marker=issue30-selftest-003
    • GET /api/v1/admin/projects/request?keyword=issue30-selftest&limit=2&direction=next&marker=issue30-selftest-004
  6. 사용자 프로젝트 생성요청 목록 실제 API 호출

    • GET /api/v1/projects/request?keyword=issue30-selftest&limit=2&direction=next
    • GET /api/v1/projects/request?keyword=issue30-selftest&limit=2&direction=next&marker=issue30-selftest-002
    • GET /api/v1/projects/request?keyword=issue30-selftest&limit=2&direction=prev&marker=issue30-selftest-003
  7. 관리자 프로젝트 목록 실제 API 호출

    • GET /api/v1/admin/projects?limit=1&direction=next
    • GET /api/v1/admin/projects?limit=1&direction=next&marker={projectId}
  8. 인증 도메인 실제 API 호출

    • GET /api/v1/admin/users?limit=1&direction=next
    • GET /api/v1/admin/users?limit=1&direction=next&marker={userId}
    • GET /api/v1/admin/roles?limit=1&direction=next
    • GET /api/v1/admin/roles?limit=1&direction=next&marker={roleId}
    • GET /api/v1/admin/notices?limit=1&direction=next
  9. 테스트 리소스 정리 검증

    • DB project_requests 테스트 데이터 삭제
    • Redis 테스트 세션 삭제
    • Swagger/TTL keeper 백그라운드 프로세스 종료
    • 최종 잔여물 0건 확인

테스트 결과

  • 정상 동작 확인
  • 예외 상황 테스트
  • 기존 기능 영향 없음

검증 결과 요약:

대상 테스트:
./gradlew test --tests ProjectModuleTest --tests AdminProjectServiceAdapterTest --tests PaginationUtilsTest
BUILD SUCCESSFUL

전체 테스트:
./gradlew test
BUILD SUCCESSFUL

관리자 프로젝트 생성요청 목록:
next 첫 페이지 -> ids=[issue30-selftest-001, issue30-selftest-002], first=true, last=false, nextMarker=issue30-selftest-002
next 다음 페이지 -> ids=[issue30-selftest-003, issue30-selftest-004], first=false, last=false, nextMarker=issue30-selftest-004, prevMarker=issue30-selftest-003
prev 이전 페이지 -> ids=[issue30-selftest-001, issue30-selftest-002], first=true, last=false, nextMarker=issue30-selftest-002
next 마지막 페이지 -> ids=[issue30-selftest-005], first=false, last=true, prevMarker=issue30-selftest-005

사용자 프로젝트 생성요청 목록:
next 첫 페이지 -> ids=[issue30-selftest-001, issue30-selftest-002], first=true, last=false, nextMarker=issue30-selftest-002
next 다음 페이지 -> ids=[issue30-selftest-003, issue30-selftest-004], first=false, last=false, nextMarker=issue30-selftest-004, prevMarker=issue30-selftest-003
prev 이전 페이지 -> ids=[issue30-selftest-001, issue30-selftest-002], first=true, last=false, nextMarker=issue30-selftest-002

관리자 프로젝트 목록:
limit=1 -> size=1, first=true, last=false, nextMarker=08e88040e8354113895d7a6cec1cbdc0
marker=08e88040e8354113895d7a6cec1cbdc0, limit=1 -> size=1, first=false, last=false, nextMarker=59bebee681494f4faa26fb7474349bc8, prevMarker=59bebee681494f4faa26fb7474349bc8

인증 도메인:
admin/users limit=1 -> size=1, first=true, last=false, nextMarker=c43dd81f6f484ce485acf93b4a46feef
admin/users marker=c43dd81f6f484ce485acf93b4a46feef -> size=0, first=false, last=true, nextMarker=null
admin/roles limit=1 -> size=1, first=true, last=false, nextMarker=2b38fedf1c874254b309157b8a60c3dd
admin/roles marker=2b38fedf1c874254b309157b8a60c3dd -> size=0, first=false, last=true, nextMarker=null
admin/notices limit=1 -> size=0, first=true, last=true

정리 후 확인:
db_project_requests=0
redis_session_exists=0
screen_sessions=0
localhost:8080=stopped

Copy link
Copy Markdown
Member Author

추가 반영: Project/ProjectRequest direction=prev 실제 동작 보정

이번 후속 커밋에서 Project 및 ProjectRequest 페이지네이션의 direction=prev가 실제 응답에 반영되도록 보강했습니다.

변경 요약

  • ProjectRequest 목록은 Base64 offset marker 기준으로 direction=prev 요청 시 이전 페이지 offset을 계산하도록 수정했습니다.
  • ProjectRequest 응답의 prevMarkerdirection=prev에 바로 사용할 수 있는 현재 페이지 시작 offset으로 정리했습니다.
  • Keystone Project 목록은 Keystone 응답의 previous 링크가 비어 있는 환경에서도 이전 페이지 요청이 가능하도록, 현재 페이지 첫 프로젝트 ID를 prevMarker로 제공하고 direction=prev 요청 시 앞 페이지를 탐색해 반환하도록 보정했습니다.
  • 관련 단위 테스트를 추가/수정했습니다.

추가 검증

./gradlew test: BUILD SUCCESSFUL

ProjectRequest 실제 API:
GET /api/v1/admin/projects/request?keyword=issue30-prev-live&limit=2&direction=next
- 200 OK
- contents: issue30-prev-live-001, issue30-prev-live-002
- first=true, last=false, nextMarker=Mg==

GET /api/v1/admin/projects/request?keyword=issue30-prev-live&marker=NA==&limit=2&direction=next
- 200 OK
- contents: issue30-prev-live-005
- first=false, last=true, prevMarker=NA==

GET /api/v1/admin/projects/request?keyword=issue30-prev-live&marker=NA==&limit=2&direction=prev
- 200 OK
- contents: issue30-prev-live-003, issue30-prev-live-004
- first=false, last=false, nextMarker=NA==, prevMarker=Mg==

Project 실제 API:
GET /api/v1/admin/projects?limit=1&direction=next
- 200 OK
- contents: lb-test
- first=true, last=false, nextMarker=08e88040e8354113895d7a6cec1cbdc0

GET /api/v1/admin/projects?marker=08e88040e8354113895d7a6cec1cbdc0&limit=1&direction=next
- 200 OK
- contents: service
- first=false, last=false
- nextMarker=59bebee681494f4faa26fb7474349bc8
- prevMarker=59bebee681494f4faa26fb7474349bc8

GET /api/v1/admin/projects?marker=59bebee681494f4faa26fb7474349bc8&limit=1&direction=prev
- 200 OK
- contents: lb-test
- first=true, last=false, nextMarker=08e88040e8354113895d7a6cec1cbdc0

@LEE-HYUN-JE

Copy link
Copy Markdown
Collaborator

LGTM

PaginationUtils 으로 페이지 네이션 처리에 대한 공통 처리 로직이 생긴 것이 너무 좋은 것 같습니다.

고생하셨습니다!

@jjjjjk12

Copy link
Copy Markdown
Member

Conflicht 해결해주시고 작업이 완료되었다면 Draft 풀어주세요

Copy link
Copy Markdown
Member Author

추가 반영: ProjectRequest 페이지네이션 marker 규약 정리

ProjectRequest 목록도 OpenStack 리소스형 도메인과 동일하게 ID cursor 방식으로 맞췄습니다.

  • 기존: marker를 Base64 offset으로 해석
  • 변경: markerprojectRequestId 경계 ID로 해석
  • direction=next: marker 이후 ID를 오름차순 조회
  • direction=prev: marker 이전 ID를 역방향 조회 후 정방향으로 반환
  • nextMarker: 현재 페이지 마지막 projectRequestId
  • prevMarker: 현재 페이지 첫 projectRequestId

검증 결과:

./gradlew test: BUILD SUCCESSFUL

ProjectRequest 실제 API 검증:
1) keyword=issue30-prev-live, limit=2, direction=next
   -> 001, 002 / nextMarker=issue30-prev-live-002

2) marker=issue30-prev-live-002, direction=next
   -> 003, 004 / nextMarker=issue30-prev-live-004 / prevMarker=issue30-prev-live-003

3) marker=issue30-prev-live-003, direction=prev
   -> 001, 002 / nextMarker=issue30-prev-live-002

4) marker=issue30-prev-live-004, direction=next
   -> 005 / last=true / prevMarker=issue30-prev-live-005

Project 실제 API 회귀 검증:
- 기존 Project next/prev pagination 정상 동작 확인

로컬 Swagger 테스트 환경:

  • 앱 실행 상태: http://127.0.0.1:8080, health UP
  • Redis 테스트 세션: issue30-project-page-session, TTL 1시간 갱신
  • Swagger: http://127.0.0.1:8080/swagger-ui/index.html#/Admin%20Project/getProjectRequests_1

@jjjjjk12

Copy link
Copy Markdown
Member

Conflict 해결해주시고 Draft 풀어주세요.

@callme-waffle
callme-waffle marked this pull request as ready for review June 17, 2026 10:06

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f3c7804d2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

try {
Map<String, String> keystoneListProjectRequest = Collections.emptyMap();
if (pageRequest != null) KeystoneAPIUtils.createKeystoneListProjectRequest(
PageRequest normalized = PaginationUtils.normalize(pageRequest, false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve unpaginated user project listings

When the non-paginated GET /api/v1/projects flow calls ProjectModule.getAllProjectListForUser, it passes pageRequest as null, but this normalization now turns that into limit=10 and sends the limit to Keystone. Users with more than 10 accessible projects will only see the first 10 in this list endpoint, whereas the previous null-page path sent no pagination query and the caller still expects all projects.

Useful? React with 👍 / 👎.

…-pagination

# Conflicts:
#	src/test/java/com/acc/local/service/adapters/project/AdminProjectServiceAdapterTest.java
#	src/test/java/com/acc/local/service/modules/auth/ProjectModuleTest.java

Copy link
Copy Markdown
Member Author

업데이트 반영 및 실제 동작 검증 완료

Codex Agent Comment 제안을 반영해 GET /api/v1/projects의 기존 비페이지네이션 계약을 보존하도록 수정했습니다.

  • pageRequest == null인 사용자 프로젝트 목록 조회는 Keystone 요청에 limit를 넣지 않고 전체 페이지를 순회하도록 정리했습니다.
  • 기존 응답 형태인 List<ProjectResponse> 계약은 유지했습니다.
  • 관련 단위 테스트를 추가했고, 대상 테스트는 BUILD SUCCESSFUL로 확인했습니다.
  • 실제 API 검증을 다시 수행했습니다.
    • GET /api/v1/projects: 200, Keystone /v3/users/{userId}/projects 호출에 limit=10 미포함 확인
    • GET /api/v1/admin/projects?limit=2: next/prev 이동 및 marker, limit OpenStack 전달 확인
    • GET /api/v1/admin/projects/request?limit=2: API로 생성한 테스트 요청 3건 기준 next/prev 이동 확인
  • 테스트 후 생성한 project_requests 3건과 관련 outbox_events 3건, Redis 테스트 세션을 모두 정리했고 잔여물 0건을 확인했습니다.
  • 테스트용 bootRun/screen 세션도 종료했습니다.

재검토 요청드립니다.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0bfd6a8249

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/main/java/com/acc/local/service/modules/auth/RoleModule.java
@jjjjjk12

Copy link
Copy Markdown
Member

LGTM

@jjjjjk12
jjjjjk12 merged commit b99c416 into main Jun 18, 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.

[Bug] 관리자 프로젝트 관련 엔드포인트 페이지네이션 관련 오류

3 participants