docs(admin-member): 회원 목록 lazy 초기화 수정 계획을 추가한다
This commit is contained in:
77
docs/20260627_관리자_회원목록_LazyInitializationException_수정/prd.md
Normal file
77
docs/20260627_관리자_회원목록_LazyInitializationException_수정/prd.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# PRD: 관리자 회원 목록 LazyInitializationException 수정
|
||||
|
||||
## 1. Overview
|
||||
`spring.jpa.open-in-view=false` 환경에서 관리자 회원 리스트와 크리에이터 리스트 조회 시 `Member.signOutReasons` lazy collection 접근으로 발생하는 `LazyInitializationException`을 방지한다.
|
||||
|
||||
---
|
||||
|
||||
## 2. Problem
|
||||
- 관리자 회원 목록 응답 생성 중 `Member.signOutReasons`를 읽어 탈퇴일을 계산한다.
|
||||
- 현재 `AdminMemberService`의 목록 조회 메서드는 트랜잭션 경계가 없어 QueryDSL 조회 후 영속성 컨텍스트가 닫힌 상태에서 lazy collection을 접근할 수 있다.
|
||||
- `spring.jpa.open-in-view=false` 환경에서는 이 접근이 `org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: kr.co.vividnext.sodalive.member.Member.signOutReasons`로 이어진다.
|
||||
- 같은 응답 매핑 흐름을 사용하는 관리자 회원 리스트, 회원 검색, 크리에이터 리스트, 크리에이터 검색 모두 같은 위험이 있다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Goals
|
||||
- `osiv=false` 환경에서도 관리자 회원 리스트와 크리에이터 리스트가 예외 없이 응답된다.
|
||||
- 기존 API 응답 스키마와 정렬/필터 조건을 변경하지 않는다.
|
||||
- 탈퇴 이력이 있는 회원의 `signOutDate` 계산 동작을 유지한다.
|
||||
- 서비스 계층에 명확한 read-only 트랜잭션 기본 경계를 둔다.
|
||||
- 실패 재현 테스트를 먼저 작성하고, 최소 수정으로 통과시킨다.
|
||||
|
||||
---
|
||||
|
||||
## 4. Non-Goals
|
||||
- 관리자 회원 목록 API의 응답 필드를 변경하지 않는다.
|
||||
- `Member.signOutReasons` fetch 전략을 전역 eager로 바꾸지 않는다.
|
||||
- 목록 조회를 projection 전용 쿼리로 전면 개편하지 않는다.
|
||||
- pagination/count 쿼리 구조를 리팩터링하지 않는다.
|
||||
- OSIV 설정을 다시 켜지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 5. Target Users
|
||||
- 관리자: 관리자 화면에서 회원 목록과 크리에이터 목록을 조회하는 사용자
|
||||
- 운영자: 탈퇴 또는 차단 이력이 있는 회원을 포함한 목록을 안정적으로 확인해야 하는 사용자
|
||||
|
||||
---
|
||||
|
||||
## 6. User Stories
|
||||
- 관리자는 탈퇴 이력이 있는 일반 회원이 포함된 회원 리스트를 조회해도 서버 오류를 만나지 않아야 한다.
|
||||
- 관리자는 탈퇴 이력이 있는 크리에이터가 포함된 크리에이터 리스트를 조회해도 서버 오류를 만나지 않아야 한다.
|
||||
- 관리자는 검색 결과에서도 동일하게 탈퇴일과 활성 상태를 확인할 수 있어야 한다.
|
||||
|
||||
---
|
||||
|
||||
## 7. Core Features
|
||||
|
||||
### Feature A. 관리자 회원 목록 조회 트랜잭션 보강
|
||||
|
||||
#### Requirements
|
||||
- `AdminMemberService`는 클래스 레벨 `@Transactional(readOnly = true)`로 조회 기본 트랜잭션을 제공한다.
|
||||
- `AdminMemberService.getMemberList(...)`는 read-only 트랜잭션 안에서 조회와 응답 매핑을 완료한다.
|
||||
- `AdminMemberService.searchMember(...)`는 read-only 트랜잭션 안에서 조회와 응답 매핑을 완료한다.
|
||||
- `AdminMemberService.getCreatorList(...)`는 read-only 트랜잭션 안에서 조회와 응답 매핑을 완료한다.
|
||||
- `AdminMemberService.searchCreator(...)`는 read-only 트랜잭션 안에서 조회와 응답 매핑을 완료한다.
|
||||
- `AdminMemberService.updateMember(...)`, `AdminMemberService.resetPassword(...)`는 메서드 레벨 `@Transactional`로 쓰기 트랜잭션을 유지한다.
|
||||
- 기존 `processMemberListToGetAdminMemberListResponseItemList(...)`의 응답 필드 계산 방식은 유지한다.
|
||||
|
||||
#### Edge Cases
|
||||
- `signOutReasons`가 비어 있으면 기존처럼 `signOutDate`는 빈 문자열이다.
|
||||
- `signOutReasons`가 있으면 기존처럼 마지막 탈퇴 이력의 `createdAt`을 KST `yyyy-MM-dd HH:mm` 형식으로 내려준다.
|
||||
- `auth` lazy one-to-one 접근도 같은 read-only 트랜잭션 안에서 처리되어야 한다.
|
||||
|
||||
---
|
||||
|
||||
## 8. Technical Constraints
|
||||
- Kotlin + Spring Boot 2.7.14 + Spring Data JPA 기준으로 구현한다.
|
||||
- 테스트 환경의 `spring.jpa.open-in-view=false` 설정을 유지한다.
|
||||
- 서비스 클래스에는 `@Transactional(readOnly = true)`를 사용하고, 쓰기 메서드는 기존 메서드 레벨 `@Transactional`을 유지한다.
|
||||
- 변경 범위는 `AdminMemberService`와 해당 테스트로 제한한다.
|
||||
|
||||
---
|
||||
|
||||
## 9. Metrics
|
||||
- `AdminMemberServiceTest`에서 OSIV off 조건의 회원/크리에이터 목록 조회 테스트가 통과한다.
|
||||
- 관련 단일 테스트와 `ktlintCheck`가 통과한다.
|
||||
Reference in New Issue
Block a user