다크 모드
Feature별 기술 상세
각 feature 모듈의 API 연동, 컴포넌트 구성, 데이터 스키마를 설명합니다.
Dashboard
주요 서비스 지표를 요약 카드와 시계열 차트로 표시합니다.
API
| Method | Path | 설명 |
|---|---|---|
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 QueryqueryKey에days를 포함하여 기간별 캐시를 분리합니다. - 변동률 표시: 양수는 초록, 음수는 빨강 색상. DoD/WoW/MoM 라벨 자동 표시.
- 차트: X축은
ko-KR날짜 포맷, gradient fill 적용.
Users
유저 목록 조회, 검색, 상세 수정 기능을 제공합니다.
API
| Method | Path | 설명 |
|---|---|---|
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_activeboolean 필터 - 페이징: offset 기반. 기본
limit=20, 최대 100
수정 검증
Zod 스키마로 클라이언트 측 검증:
username: 3~30자, 소문자/숫자/밑줄/마침표만 허용email: 이메일 형식 검증
서버 측에서 추가로 username 중복 체크, locale 유효성, birthDate 미래 날짜 불가 등을 검증합니다.
컴포넌트 구성
| 컴포넌트 | 역할 |
|---|---|
UsersTable | TanStack Table 기반 유저 목록. 검색 + is_active 필터 |
UserEditDrawer | Sheet(슬라이드 패널) 기반 유저 상세/수정 폼 |
Cover Templates
커버 템플릿의 생성, 조회, 수정, 상태 변경, 삭제를 관리합니다.
API
| Method | Path | 설명 |
|---|---|---|
POST | /admin/v1/cover-templates/upload-url | S3 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
}컴포넌트 구성
| 컴포넌트 | 역할 |
|---|---|
CoverTemplatesTable | TanStack Table 기반 목록. status/category 필터 |
CoverTemplateCreateDialog | Dialog 기반 생성 폼 (이미지 업로드 포함) |
CoverTemplateEditDrawer | Sheet 기반 수정 폼 |
CoverTemplateDetailSheet | Sheet 기반 상세 보기 (variant 목록 포함) |
CoverTemplateStatusDialog | 상태 변경 확인 Dialog |
CoverTemplateDeleteDialog | 삭제 확인 AlertDialog (DRAFT만) |
Sample Loops
신규 사용자에게 표시할 샘플 룹을 등록/관리합니다.
API
| Method | Path | 설명 |
|---|---|---|
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
}컴포넌트 구성
| 컴포넌트 | 역할 |
|---|---|
SampleLoopsTable | TanStack Table 기반 목록 |
SampleLoopRegisterDialog | Dialog 기반 등록 (토큰 입력 → 미리보기 → 옵션 설정) |
SampleLoopEditDrawer | Sheet 기반 수정 (display_order, target_condition, is_active) |
SampleLoopDeleteDialog | 삭제 확인 AlertDialog |
정보
샘플 룹 백엔드 아키텍처(도메인 모델, SampleLoopGuard, 클라이언트 API 통합)는 모듈 구조를 참조하세요.
관련 문서
변경 이력
| 날짜 | 내용 |
|---|---|
| 2026-03-16 | Sample Loops 섹션 — 백엔드 아키텍처 크로스 레퍼런스 추가 |
| 2026-03-16 | 초기 문서 작성 |