목차


청사진 그리기

< 페이지 흐름 및 카드 상태 파악 : 나의 판매 페이지에서도 씀 >

<aside>

[상태]

ACTIVE(그냥 내 거), ABAILABLE(판매 등록됨), PENDING(교환 제시됨), SOLDOUT(품절)

[흐름]

  1. 마이 갤러리 페이지와 마켓 플레이스 페이지, 나의 판매 포토카드 페이지가 있다.
  2. 마이 갤러리에서는 새 카드를 만들 수 있다. (기본 상태: ACTIVE)
  3. 마켓 플레이스에서는 소유한 카드를 판매할 수 있다. (정확히는 판매 등록)
  4. 판매 등록된 카드는 상태가 ACTIVE에서 AVAILABLE로 바뀐다.
  5. 다른 사용자가 교환 또는 구매 신청을 한다.
  6. 교환 신청을 하면, 교환하겠다고 제시한 카드의 상태가 PENDING으로 바뀐다.
  7. 구매 시엔 포인트가 차감되면서, 새로 산 카드마이 갤러리로 들어온다. (상태: ACTIVE)
  8. 판매/교환이 승인되면 내 카드(였던 것)는 품절(SOLDOUT?) 상태가 되고, (7)처럼 새 카드를 받는다. — UserCard는 생성, 교환/판매, 품절(더는 내 카드가 아니게 됨) 상태를 전부 기록

[스키마와 대응]

< 요구 분석 >

<aside>

만들 페이지 목록

  1. (사전 조건: 로그인한 사용자가 있어야 함)
  2. [상태 추출] Active
  3. [필요 쿼리] 검색, 필터링(등급, 장르) + offset pagination </aside>

Repository

async function findMyGallery(userId, { genre, grade, keyword, offset = 0, limit = 10 }) {
  // 1. query 문자열 조건절
  const whereClause = {
    userCards: {
      some: { ownerId: userId, status: 'ACTIVE' }
    }
  };

  if (grade) whereClause.grade = { id: Number(grade) };
  if (genre) whereClause.genre = { id: Number(genre) };
  if (keyword) whereClause.name = { contains: keyword, mode: 'insensitive' };

  // 2. 전체 카드 개수 (count 쿼리)
  const totalItems = await prisma.photoCard.count({
    where: whereClause
  });

  // 3. 페이지네이션 포함 쿼리 문자열 반환
  const items = await prisma.photoCard.findMany({
    select: {
      id: true,
      name: true,
      imageUrl: true,
      description: true,
      totalQuantity: true,
      grade: { select: { id: true, name: true } },
      genre: { select: { id: true, name: true } },
      userCards: {
        where: { ownerId: userId, status: 'ACTIVE' },
        select: { id: true, price: true, owner: { select: { id: true, nickname: true } } }
      },
      creator: { select: { id: true, nickname: true } }
    },

    where: whereClause,
    skip: Number(offset),
    take: Number(limit),
    orderBy: { createdAt: 'desc' }
  });

  return { totalItems, items };
}