Files

4.4 KiB

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가 통과한다.