작성일: 2026년 3월 23일 진행 단계: Phase 6 (백엔드 개발) 진행 중 이전 일지: Day 3 참고 (Phase 1~5 완료)


✅ Phase 6 전체 진행 현황

API 도메인 엔드포인트 수 상태
Auth 5개 ✅ 완료
Medications 5개 ✅ 완료
Schedules 1개 🔜 다음
Dose 4개 ⬜ 대기
Notifications 3개 ⬜ 대기
합계 18개 10개 완료 / 8개 대기

1. 사전 작업 — DB 마이그레이션 재작성

문제 발견

초기 설계에서 medications.user_idpublic.users.id 를 참조했으나, Supabase Auth는 auth.users 를 별도로 관리하여 FK 오류 발생.

Error: insert or update on table "medications" violates foreign key constraint
Key (user_id)=(uuid) is not present in table "users"

해결 — 방법 B 채택 (public.users 별도 유지)

방법 A vs 방법 B 비교

방법 A (auth.users 직접 참조) 방법 B (public.users 별도 유지)
개발 속도 빠름 느림
확장성 낮음 높음
글로벌 런칭 대비 부족 적합
보호자 연동 (v2) 어려움 쉬움

→ 글로벌 런칭과 보호자 연동을 고려해 방법 B 채택

최종 마이그레이션 (fix_users_sync)

-- users (auth.users 참조)
CREATE TABLE users (
  id           UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  email        TEXT UNIQUE,
  name         TEXT NOT NULL DEFAULT '',
  timezone     TEXT NOT NULL DEFAULT 'Asia/Seoul',
  language     TEXT NOT NULL DEFAULT 'ko',
  provider     TEXT NOT NULL DEFAULT 'email',
  created_at   TIMESTAMPTZ DEFAULT now(),
  deleted_at   TIMESTAMPTZ
);

-- medications
CREATE TABLE medications (
  id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id      UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  name         TEXT NOT NULL,
  dosage       TEXT,
  color_tag    TEXT DEFAULT '#1D9E75',
  memo         TEXT,
  is_active    BOOLEAN DEFAULT true,
  created_at   TIMESTAMPTZ DEFAULT now(),
  deleted_at   TIMESTAMPTZ
);
CREATE INDEX idx_medications_user ON medications(user_id)
  WHERE deleted_at IS NULL;

-- schedules
CREATE TABLE schedules (
  id             UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  medication_id  UUID NOT NULL REFERENCES medications(id) ON DELETE CASCADE,
  scheduled_time TIME NOT NULL,
  cycle_type     TEXT NOT NULL,
  cycle_value    JSONB,
  is_active      BOOLEAN DEFAULT true
);
CREATE INDEX idx_schedules_medication ON schedules(medication_id);

-- dose_logs
CREATE TABLE dose_logs (
  id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  schedule_id  UUID NOT NULL REFERENCES schedules(id),
  user_id      UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  log_date     DATE NOT NULL,
  status       TEXT NOT NULL DEFAULT 'pending',
  taken_at     TIMESTAMPTZ,
  created_at   TIMESTAMPTZ DEFAULT now(),
  UNIQUE (schedule_id, log_date)
);
CREATE INDEX idx_dose_logs_user_date ON dose_logs(user_id, log_date DESC);
CREATE INDEX idx_dose_logs_schedule  ON dose_logs(schedule_id);

-- notification_logs
CREATE TABLE notification_logs (
  id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  schedule_id  UUID NOT NULL REFERENCES schedules(id),
  user_id      UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  sent_at      TIMESTAMPTZ DEFAULT now(),
  status       TEXT NOT NULL,
  snooze_count INT DEFAULT 0
);
CREATE INDEX idx_notif_logs_user ON notification_logs(user_id, sent_at DESC);

-- device_tokens
CREATE TABLE device_tokens (
  id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id      UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  token        TEXT NOT NULL UNIQUE,
  platform     TEXT NOT NULL,
  updated_at   TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_device_tokens_user ON device_tokens(user_id);

2. Auth API

구현 파일

app/schemas/auth.py          ← 요청/응답 Pydantic 모델
app/services/auth_service.py ← 비즈니스 로직
app/routers/auth.py          ← API 엔드포인트

핵심 구현 내용