배경
기존 스키마가 auth.users 테이블과 auth.uid() 함수를 참조한다. 로컬 순정 Postgres 에는 auth 스키마 자체가 없어서 migration 실행 불가. 이 문제를 근본 해결하고 prod 에서도 같은 구조로 통일.
Scope
1. auth.users FK → public.users 이관
기존 REFERENCES auth.users(id) 를 모두 REFERENCES public.users(id) 로 변경.
근거: public.users 는 앱 도메인 엔티티이며, GoTrue trigger 가 auth.users → public.users 로 UUID 동기화하므로 1:1 매핑됨. 앱 레벨 FK 는 public.users 를 가리키는 것이 의미적으로 더 올바름.
대상 (pre-check 로 확인):
public.post_magazines.approved_by
- 기타
SELECT ... FROM pg_constraint WHERE contype='f' AND confrelid='auth.users'::regclass 결과
Pre-check SQL (migration 적용 전 반드시 실행):
-- approved_by 값이 모두 public.users 에 있는지 확인
SELECT id, approved_by FROM public.post_magazines
WHERE approved_by IS NOT NULL
AND approved_by NOT IN (SELECT id FROM public.users);
-- 결과 0 row 여야 안전. 아니면 데이터 정합성 복구 선행.
2. auth.uid() stub (로컬 전용, prod 무영향)
-- SeaORM migration 의 raw SQL
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_proc
WHERE proname='uid'
AND pronamespace='auth'::regnamespace) THEN
CREATE SCHEMA IF NOT EXISTS auth;
CREATE OR REPLACE FUNCTION auth.uid() RETURNS uuid
LANGUAGE sql STABLE AS $fn$ SELECT NULL::uuid $fn$;
END IF;
END $$;
- 로컬: auth 스키마 없음 → stub 생성 → RLS policies 가 compile-pass
- Prod: Supabase Auth 가 실제
auth.uid() 제공 → 조건 실패 → skip (덮어쓰기 없음)
3. JWT verify 후 public.users upsert (middleware)
로컬에서 원격 Supabase Auth 로 로그인한 유저의 UUID 가 로컬 public.users 에 없으면 자동 생성.
// api-server/src/middleware/auth.rs 수정
pub async fn auth_middleware(...) {
let claims = verify_supabase_token(token, ...).await?;
let user = User::from_claims(claims)?;
// 신규: public.users 에 없으면 upsert
upsert_user_if_missing(&state.db, &user).await?;
request.extensions_mut().insert(user);
Ok(next.run(request).await)
}
Files
packages/api-server/migration/src/m20260501_000001_decouple_auth_users_fk.rs (신규)
packages/api-server/migration/src/m20260501_000002_auth_uid_stub.rs (신규)
packages/api-server/migration/src/lib.rs (등록)
packages/api-server/src/middleware/auth.rs (수정 — user upsert)
packages/api-server/src/services/user/ 또는 유사 위치 (upsert 로직)
Idempotency
모든 DDL:
- FK:
DROP CONSTRAINT IF EXISTS → ADD CONSTRAINT
- 함수:
CREATE OR REPLACE FUNCTION (단 stub 은 조건문 안에)
- 스키마:
CREATE SCHEMA IF NOT EXISTS
Prod 영향 (안전 보장)
auth.uid() stub 은 IF NOT EXISTS 로 prod 실제 함수 덮어쓰지 않음
- FK 이관은
DROP IF EXISTS + ADD 패턴 → 재적용 안전
- pre-check SQL 통과 후에만 배포
Acceptance
- 로컬 순정 Postgres 에서 api-server 기동 시
Migrator::up() 성공
- prod 에서 migration 재적용 시 no-op (기존 스키마 변경 최소)
- JWT 로그인 후
public.users 에 자동 row 생성 확인
- RLS policies 가 로컬/prod 양쪽에서 compile pass
연관
배경
기존 스키마가
auth.users테이블과auth.uid()함수를 참조한다. 로컬 순정 Postgres 에는auth스키마 자체가 없어서 migration 실행 불가. 이 문제를 근본 해결하고 prod 에서도 같은 구조로 통일.Scope
1. auth.users FK → public.users 이관
기존
REFERENCES auth.users(id)를 모두REFERENCES public.users(id)로 변경.근거:
public.users는 앱 도메인 엔티티이며, GoTrue trigger 가auth.users→public.users로 UUID 동기화하므로 1:1 매핑됨. 앱 레벨 FK 는public.users를 가리키는 것이 의미적으로 더 올바름.대상 (pre-check 로 확인):
public.post_magazines.approved_bySELECT ... FROM pg_constraint WHERE contype='f' AND confrelid='auth.users'::regclass결과Pre-check SQL (migration 적용 전 반드시 실행):
2. auth.uid() stub (로컬 전용, prod 무영향)
auth.uid()제공 → 조건 실패 → skip (덮어쓰기 없음)3. JWT verify 후 public.users upsert (middleware)
로컬에서 원격 Supabase Auth 로 로그인한 유저의 UUID 가 로컬
public.users에 없으면 자동 생성.Files
packages/api-server/migration/src/m20260501_000001_decouple_auth_users_fk.rs(신규)packages/api-server/migration/src/m20260501_000002_auth_uid_stub.rs(신규)packages/api-server/migration/src/lib.rs(등록)packages/api-server/src/middleware/auth.rs(수정 — user upsert)packages/api-server/src/services/user/또는 유사 위치 (upsert 로직)Idempotency
모든 DDL:
DROP CONSTRAINT IF EXISTS→ADD CONSTRAINTCREATE OR REPLACE FUNCTION(단 stub 은 조건문 안에)CREATE SCHEMA IF NOT EXISTSProd 영향 (안전 보장)
auth.uid()stub 은IF NOT EXISTS로 prod 실제 함수 덮어쓰지 않음DROP IF EXISTS+ADD패턴 → 재적용 안전Acceptance
Migrator::up()성공public.users에 자동 row 생성 확인연관