-
[Supabase][보안] Schedule Functions와 pg_net을 같이 쓸 때 Vault secret과 apikey 헤더를 어느 호출부터 먼저 나누나기타개발지식/풀스택개발 2026. 8. 3. 09:18
IT 리서치 노트
[Supabase][보안] Schedule Functions와 pg_net을 같이 쓸 때 Vault secret과 apikey 헤더를 어느 호출부터 먼저 나누나
Supabase에서 schedule functions와 pg_net 기반 호출이 같이 돌아가기 시작하면 어느 호출부터 Vault와 apikey header를 정리해야 할지 경계가 금방 섞이기 쉽다. 하지만 2026년 8월 3일 기준 Supabase 공식 문서를 다시 보면, scheduled Edge Function 호출 토큰은 Vault에 두는 편을 권장하고, backend code는 새 secret key로 옮기며, pg_net과 Database Webhooks는 같은 backend caller 계열로 볼 수 있다. 이 글은 Schedule Functions와 pg_net을 같이 쓸 때 Vault secret과 apikey 헤더를 어느 호출부터 먼저 나누는 편이 덜 꼬이는지 정리한 것이다.
1. 개요
결론부터 말하면 Supabase backend caller 정리는 header 이름보다 caller inventory와 secret 저장 위치를 먼저 나누는 편이 빠르다. schedule functions와 pg_net SQL 호출은 Vault ref를 먼저 만들고 apikey header로 맞추고, Database Webhooks는 같은 backend caller 축으로 묶어 같이 정리하는 편이 안전하다. 브라우저나 사용자 JWT route는 이 집합과 절대 섞지 않는 것이 기본값이다.
즉 schedule functions 문제는 cron 문법보다 호출 경계 문제다. 어떤 route가 publishable key를 써야 하는지, 어떤 route가 secret key를 써야 하는지, 어떤 secret이 Vault에 있어야 하는지를 먼저 적어 두어야 cutover가 짧아진다.
2. 어디서 실제로 막히는가
실무에서 먼저 막히는 지점은 세 가지다. 첫째, schedule functions 예제를 바로 복사해 넣고 Vault name과 실제 route inventory를 따로 적지 않는다. 둘째, pg_net SQL 호출과 Database Webhooks를 다른 문제로 보고 header 전환 순서를 두 번 반복한다. 셋째, Edge Function 안의 admin client secret과 Postgres 호출용 secret을 같은 저장 위치에 두고 노출 범위를 헷갈린다.
Supabase schedule functions 문서는 scheduled Edge Function 호출 토큰을 Vault에 저장하라고 권장하고, 예제에서도 Vault에 URL과 key를 넣은 뒤
net.http_post에서 꺼내 쓰는 순서를 보여 준다. secrets 문서는SUPABASE_SECRET_KEYS가 Edge Functions 안에서는 안전하지만 브라우저에서는 쓰면 안 되고 RLS를 우회할 수 있다고 적는다. migration 문서는 backend code의service_role을 새secretkey로 바꾸라고 하고, Database Webhooks 문서는 webhook이 결국 pg_net 기반 wrapper라고 설명한다. 이 네 문서를 같이 읽으면 schedule caller, webhook caller, Edge Function 내부 admin caller를 서로 다른 경계로 적어야 한다는 결론이 나온다.- 증상: schedule 호출은 되는데 일부 route만 401/403이 반복된다.
- 실패: Vault ref 없이 key를 SQL이나 Dashboard에 직접 넣는다.
- 막힘: pg_net SQL과 Database Webhooks를 다른 cutover로 따로 관리한다.
- 누락: 브라우저 route와 backend caller route를 같은 inventory에 분리해 적지 않는다.
질문 먼저 볼 곳 실무 판단 어느 호출부터 바꿀까 caller inventory schedule + pg_net + webhook을 같은 backend caller 집합으로 본다 secret은 어디에 둘까 Vault vs runtime env Postgres caller는 Vault, Edge Function 내부 admin은 env 기준으로 나눈다 header는 무엇을 써야 하나 apikey / Authorization backend caller는 apikey, 사용자 route는 JWT Authorization으로 나눈다 3. 실무에서 적용하는 순서
운영 순서는 다섯 단계가 가장 짧다. 먼저 schedule functions, pg_net SQL, Database Webhooks, Edge Function 내부 admin route를 한 inventory 표에 적는다. 다음으로 각 caller가 어떤 secret을 쓰는지, 어느 필드에 header를 넣는지, 어디에 저장되는지 확인한다. 세 번째로 Postgres caller는 Vault ref를 먼저 만들고 header 이름을 apikey로 통일한다. 네 번째로 Edge Function 내부 admin client는 runtime env 또는
SUPABASE_SECRET_KEYS로 관리하고 브라우저 route와 섞지 않는다. 마지막으로 recent 2xx/4xx 비율, 호출 로그, old key 흔적을 보고 남은 caller를 닫는다.- backend caller inventory를 먼저 만든다.
- 각 caller의 secret 저장 위치를 Vault와 env로 나눈다.
- pg_net와 webhook caller는 apikey header로 먼저 맞춘다.
- Edge Function 내부 admin secret은 runtime env로 분리한다.
- 응답 코드와 old key 흔적으로 남은 caller를 지운다.
실제 점검에서는 SQL editor의
net.http_post호출, Dashboard webhook 설정 화면, Edge Function admin client 초기화 코드를 한 번에 열어 두는 편이 좋다. 같은 secret key라도 Postgres caller는 Vault에서 꺼내 쓰고, Edge Function 내부 admin route는 hosted env에서 읽고, 브라우저 route는 publishable key와 사용자 JWT만 쓰게 해야 추적이 단순해진다. 이때는 SQL editor 쿼리, webhook header 필드, 함수 env 이름, 응답 코드, 로그 시간대를 같은 메모 표에 저장해 두면 호출 경로를 다시 확인하기 쉽다.caller_note: caller=schedule_pg_net route=/functions/v1/daily-sync secret_source=vault:secret_key_default header_name=apikey browser_access=false last_http_status=200이 메모가 있으면 'schedule function이 안 돈다'는 큰 문장을, Vault 누락인지, header field mismatch인지, browser route 혼입인지로 바로 자를 수 있다. 특히 Dashboard에서 보이는 cron 이름, SQL editor에 남긴 함수명, 로그에 찍힌 상태 코드, Vault secret 이름을 같은 순서로 적어 두면 원인 확인 시간이 짧아진다.
4. 공식 문서와 예시 화면으로 확인하기
첫 공식 화면은 schedule functions 문서의 Vault 권고 문장이다. Supabase는 scheduled Edge Function 호출 토큰을 Vault에 두라고 명시한다.
즉 이 구간은 단순한 cron 예제가 아니라 호출용 secret 보관 위치를 먼저 정하라는 신호다. pg_net과 schedule functions를 같이 쓰면 header 이름보다 secret 보관 위치가 먼저 꼬이기 쉽다.
두 번째 자료는 Vault 저장 예시다. schedule functions 예제는 project_url과 publishable_key를 Vault에 넣고, 그 뒤 net.http_post에서 꺼내 쓰는 순서를 보여 준다.
이 흐름은 키 값을 SQL에 직접 넣지 말고 Vault ref로 끊으라는 뜻이다. 호출이 여러 개로 늘어나면 Vault name과 실제 route를 inventory로 같이 적는 편이 낫다.
세 번째 공식 화면은 Edge Functions secrets 문서다. `SUPABASE_SECRET_KEYS`는 Edge Functions 안에서는 안전하지만 브라우저에서는 쓰면 안 되고, RLS를 우회할 수 있다고 적고 있다.
즉 Edge Function 안에서 쓰는 secret과 Postgres에서 pg_net로 호출할 때 쓰는 secret은 같은 값일 수 있어도, 저장 위치와 노출 범위는 다르게 가져가는 편이 맞다.
네 번째 자료는 새 API key migration 표다. legacy `service_role`이 새 `secret` key로 옮겨 가고, 서버·Edge Functions·worker 같은 backend code에 쓰라고 적혀 있다.
이미 pg_net와 Database Webhooks의 old Authorization cutover 글이 header 전환을 다뤘다면, 이번 글은 schedule caller까지 포함해 secret 저장 위치와 header 이름을 같이 닫는 후속편이다.
다섯 번째 공식 화면은 Database Webhooks 설명이다. Database Webhooks가 결국 pg_net 기반 wrapper라는 점을 보여 주므로, schedule functions와 webhook route를 같은 inventory에 넣는 편이 실용적이다.
따라서 header 전환과 Vault 저장 위치는 pg_net SQL 호출과 webhook 호출을 따로 보지 말고 하나의 backend caller 집합으로 보는 편이 빠르다.
마지막 자료는 caller inventory 표다. schedule functions, pg_net SQL, Database Webhooks, Edge Function 내부 호출을 한 표에 적어 두면 무엇부터 바꿔야 하는지 빨리 보인다.
같은 auth-security 가지의 앞단인 verify_jwt=false와 apikey 경계 글과 backend별 secret key rotation 글을 같이 보면 route 경계와 secret 경계를 한 줄로 묶기 좋다.
5. 주의사항과 리스크
첫 번째 리스크는 Vault ref 없이 key를 SQL이나 Dashboard에 직접 남기는 것이다. 두 번째는 schedule caller와 webhook caller를 별개 문제로 보고 같은 cutover를 두 번 반복하는 것이다. 세 번째는 Edge Function 내부 admin secret과 브라우저 route를 같은 경계로 다뤄 secret key를 과하게 퍼뜨리는 것이다.
운영 전에 확인할 때는 caller 이름, route, secret 저장 위치, header 이름, browser 접근 여부 다섯 칸을 같은 표에 두는 편이 좋다. 그래야 어느 caller가 backend-only인지와 어느 secret이 Vault로 가야 하는지가 분명해진다. 화면 기준으로는 SQL editor 결과, webhook 설정 필드, Vault secret 목록, 함수 로그 패널을 같은 순서로 열어 두고 각 항목을 확인해야 누락이 줄어든다.
- Vault ref와 header 이름은 함께 적는다.
- pg_net와 Database Webhooks는 같은 backend caller 집합으로 본다.
- secret key route와 publishable key route를 절대 섞지 않는다.
6. 결론
Supabase schedule functions와 pg_net caller를 같이 운영할 때는 어느 header를 쓰는지만 보면 항상 늦다. caller inventory를 먼저 만들고, Vault와 runtime env를 나누고, backend caller는 apikey 기준으로 맞추면 같은 secret key migration도 훨씬 짧게 닫힌다. 실제 작업에서는 호출 표, header 필드, Vault secret 이름, 함수 로그 결과를 같은 문서에 저장해 두는 편이 가장 재현 가능하다.
- caller inventory를 먼저 만든다.
- Postgres caller는 Vault ref를 기준으로 정리한다.
- backend caller와 browser route를 분리한다.
이 정리를 실제 배포 문서로 옮길 때는 새 후속 글인 Schedule Functions와 pg_net·Database Webhooks caller를 같이 정리할 때 Vault secret 이름과 publishable·secret key를 어떤 순서로 inventory 하나를 같이 보는 편이 좋다. caller 표를 만든 뒤 Vault 이름, API Keys 값, browser route 분리를 어떤 표 순서로 고정할지까지 이어서 정리해 두었다.
같은 가지의 앞단에는 pg_net와 Database Webhooks의 old Authorization cutover 글, verify_jwt=false와 apikey 경계 글, legacy key migration 글이 있다. 그 위에 이번 schedule caller inventory와 Vault 분기를 얹으면 backend caller 정리가 실제 운영 순서에 더 가까워진다.
7. 참고 링크
'기타개발지식 > 풀스택개발' 카테고리의 다른 글