Skip to content

Feature별 기술 상세

각 feature 모듈의 API 연동, 컴포넌트 구성, 데이터 스키마를 설명합니다.


Dashboard

주요 서비스 지표를 요약 카드와 시계열 차트로 표시합니다.

API

MethodPath설명
GET/admin/v1/dashboard/summary주요 지표 요약
GET/admin/v1/dashboard/time-series?days={n}시계열 데이터 (DAU, 일별 룹)

Summary 응답 스키마

typescript
type DashboardSummary = {
  total_users: MetricWithChange    // change_period: month_over_month
  dau: MetricWithChange            // change_period: day_over_day
  daily_loops: MetricWithChange    // change_period: week_over_week
  member_change: {
    new_signups: number
    deletions: number
    net_change: number
  }
}

type MetricWithChange = {
  value: number
  change_value: number
  change_percentage: number | null
  change_period: 'month_over_month' | 'day_over_day' | 'week_over_week' | 'today'
}

Time Series 응답 스키마

typescript
type DashboardTimeSeries = {
  dau: Array<{ date: string; value: number }>
  daily_loops: Array<{ date: string; value: number }>
}

days 파라미터는 서버에서 7~90 범위로 coerce됩니다.

컴포넌트 구성

컴포넌트역할
MetricCard지표 카드 (값 + 변동률). NumberTicker 애니메이션 적용
MemberChangeCard회원 변동 카드 (신규 가입, 탈퇴, 순증감)
DAUChart일일 활성 사용자 Area 차트 (Recharts AreaChart)
DailyPostsChart일일 룹 Area 차트 (Recharts AreaChart)
  • 기간 선택: Select 드롭다운으로 7/14/30/90일 선택. useState로 로컬 관리하며, React Query queryKeydays를 포함하여 기간별 캐시를 분리합니다.
  • 변동률 표시: 양수는 초록, 음수는 빨강 색상. DoD/WoW/MoM 라벨 자동 표시.
  • 차트: X축은 ko-KR 날짜 포맷, gradient fill 적용.

Users

유저 목록 조회, 검색, 상세 수정 기능을 제공합니다.

API

MethodPath설명
GET/admin/v1/users유저 목록 (search, is_active, page, limit)
GET/admin/v1/users/{eid}유저 상세 조회
PATCH/admin/v1/users/{eid}유저 정보 수정 (Partial update)

목록 조회

  • 검색: search 쿼리 파라미터로 username, name, phoneNumber, email ILIKE 검색
  • 필터: is_active boolean 필터
  • 페이징: offset 기반. 기본 limit=20, 최대 100

수정 검증

Zod 스키마로 클라이언트 측 검증:

  • username: 3~30자, 소문자/숫자/밑줄/마침표만 허용
  • email: 이메일 형식 검증

서버 측에서 추가로 username 중복 체크, locale 유효성, birthDate 미래 날짜 불가 등을 검증합니다.

컴포넌트 구성

컴포넌트역할
UsersTableTanStack Table 기반 유저 목록. 검색 + is_active 필터
UserEditDrawerSheet(슬라이드 패널) 기반 유저 상세/수정 폼

Cover Templates

커버 템플릿의 생성, 조회, 수정, 상태 변경, 삭제를 관리합니다.

API

MethodPath설명
POST/admin/v1/cover-templates/upload-urlS3 Presigned Upload URL 발급
POST/admin/v1/cover-templates커버 템플릿 생성
GET/admin/v1/cover-templates목록 조회 (status, category, page, limit)
GET/admin/v1/cover-templates/{eid}상세 조회
PATCH/admin/v1/cover-templates/{eid}수정
PATCH/admin/v1/cover-templates/{eid}/status상태 변경
DELETE/admin/v1/cover-templates/{eid}삭제 (DRAFT만 가능)

이미지 업로드 흐름

정보

개발 환경에서는 S3 CORS 제한을 우회하기 위해 Vite 커스텀 플러그인(s3UploadProxy)을 통해 업로드합니다. X-Upload-Url 헤더에 presigned URL을 전달하면 서버가 S3로 중계합니다. 프로덕션에서는 브라우저가 S3에 직접 PUT합니다.

상태 머신

getNextStatus() 유틸리티 함수가 현재 상태에 따라 다음 상태를 결정합니다. 삭제는 DRAFT 상태에서만 가능합니다.

카테고리

7종 enum으로 정의:

Enum라벨
DINING다이닝
DRINK음료
SOCIAL_PARTY소셜/파티
ACTIVITY액티비티
LEARNING배움
BUSINESS비즈니스
FAMILY가족

생성 스키마

typescript
const createCoverTemplateSchema = z.object({
  title: z.string().min(1).max(255),
  category: coverTemplateCategoryEnum,          // 7종 enum
  master_image_url: z.string(),
  dominant_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).or(z.literal('')).optional(),
  display_order: z.number().int().min(0).default(0),
  is_featured: z.boolean().default(false),
});

Variant

생성 시 서버에서 자동 생성됩니다. 클라이언트에서 직접 관리하지 않습니다.

typescript
type CoverTemplateVariant = {
  width: number
  height: number
  format: 'webp' | 'jpeg'
  image_url: string
  file_size_bytes: number | null
}

컴포넌트 구성

컴포넌트역할
CoverTemplatesTableTanStack Table 기반 목록. status/category 필터
CoverTemplateCreateDialogDialog 기반 생성 폼 (이미지 업로드 포함)
CoverTemplateEditDrawerSheet 기반 수정 폼
CoverTemplateDetailSheetSheet 기반 상세 보기 (variant 목록 포함)
CoverTemplateStatusDialog상태 변경 확인 Dialog
CoverTemplateDeleteDialog삭제 확인 AlertDialog (DRAFT만)

Sample Loops

신규 사용자에게 표시할 샘플 룹을 등록/관리합니다.

API

MethodPath설명
POST/admin/v1/sample-loops/resolve-invite초대 토큰으로 룹 미리보기 조회
POST/admin/v1/sample-loops샘플 룹 등록
GET/admin/v1/sample-loops샘플 룹 목록 조회
PATCH/admin/v1/sample-loops/{eid}샘플 룹 수정
DELETE/admin/v1/sample-loops/{eid}샘플 룹 삭제

등록 흐름

대상 조건 (Target Condition)

Enum설명
NO_LOOPS룹이 없는 신규 사용자에게만 표시
ALL전체 사용자에게 표시

등록 스키마

typescript
const registerSampleLoopSchema = z.object({
  invite_token: z.string().min(1),
  display_order: z.number().int().min(0).default(0),
  target_condition: z.enum(['NO_LOOPS', 'ALL']).default('NO_LOOPS'),
  is_active: z.boolean().default(true),
});

LoopPreview 응답

typescript
type LoopPreview = {
  eid: string
  title: string | null
  status: string
  datetime: string | null
  participant_count: number
}

컴포넌트 구성

컴포넌트역할
SampleLoopsTableTanStack Table 기반 목록
SampleLoopRegisterDialogDialog 기반 등록 (토큰 입력 → 미리보기 → 옵션 설정)
SampleLoopEditDrawerSheet 기반 수정 (display_order, target_condition, is_active)
SampleLoopDeleteDialog삭제 확인 AlertDialog

정보

샘플 룹 백엔드 아키텍처(도메인 모델, SampleLoopGuard, 클라이언트 API 통합)는 모듈 구조를 참조하세요.


관련 문서


변경 이력

날짜내용
2026-03-16Sample Loops 섹션 — 백엔드 아키텍처 크로스 레퍼런스 추가
2026-03-16초기 문서 작성