Загружаем каталог…
Загружаем каталог…
서버가 서울에 있는 줄 알았습니다. 응답 헤더를 보니 함수는 워싱턴에 있었습니다. 한 줄 요약 응답 헤더 x-vercel-id: icn1::iad1::... 가 보여 준 길은 이랬습니다 — 손님(한국) → 엣지(서울) → 함수(미국 동부) → DB(서울) → 함수(미국) → 손님. 매 요청이 태평양을 두 번 건넌습니다. 함수 리전을 서울( icn1 )로 옮기고, 엣지 캐시를 통째로 막던 force-dynamic 을 걷어 내 전 페이지를 프리렌더 로 바꿨습니다. 이후 캐시 적중 응답은 0.16~0.20초입니다(단, 전 값은 재지 않았습니다). 배포 설정에는 하나를 더 넣었습니다. 개인정보처리방침에 적은 보유 기간 1년이 지난 문의를 매일 지우는 Cron 입니다. 설정이 빠지면 아무것도 지우지 않고 실패를 알리게 만들었습니다. 헤더가 그대로 보여 준 것 Vercel은 응답에 x-vercel-id 헤더를 붙입니다. 요청이 어느 지역을 거쳤는지가 적혀 있습니다. x-vercel-id: icn1::iad1::... 앞의 icn1 은 요청이 들어온 엣지(서울), 뒤의 iad1 은 함수가 실행된 곳(미국 워싱턴) 입니다. Vercel 함수의 기본 실행 지역이 미국 동부였는데, 그걸 바꾸지 않고 있었습니다. 그런데 DB(Supabase)는 서울에 있습니다. 그래서 요청 하나가 이렇게 움직였습니다. 손님(KR) → 엣지(KR) → 함수(US) → DB(KR) → 함수(US) → 손님(KR) DB를 한 번 읽을 때마다 태평양을 두 번 건넍니다. 고친 것 셋 커밋 하나( c9a0c8a )로 세 가지를 같이 고쳤습니다. 1 함수를 서울에서 실행합니다. { "regions": ["icn1"] } 2 콘텐츠 페이지의 force-dynamic 을 revalidate = 60 으로 바꿨습니다. 이게 리전보다 더 컸습니다. force-dynamic 은 응답에 cache-control: no-store 를 붙여 엣지 캐시를 통째로 막습니다. 방문할 때마다 함수를 새로 돌리고 DB를 다시 읽고 있었습니다. 회사 소개 성격의 사이트라 1분 늦게 반영되는 건 문제가 안 됩니다. 관리자가 글을 고치면 최대 1분 뒤에 반영됩니다. 이 1분과 매 요청 태평양 왕복을 바꾼 겁니다. 3 상세 페이지를 빌드 때 미리 만듭니다. A/S 사례와 공지 상세에 generateStaticParams 를 넣었습니다. 사례 29건은 검색으로 들어오는 첫 페이지가 되는 경우가 많아서, 첫 방문이 빨라야 했습니다. 결과적으로 빌드 결과표에서 전 페이지가 프리렌더(●/○)로 바뀌었고, 매번 실행되는 함수(ƒ)는 API 두 개만 남았습니다. 얼마나 빨라졌나 — 정직하게 전 값은 재지 않았습니다. 헤더로 원인을 확인하고 바로 고쳤기 때문입니다. 그래서 "몇 배 빨라졌다"는 말은 할 수 없습니다. 말할 수 있는 건 이후에 잰 값입니다. 2주 뒤 성능 작업을 하면서 네트워크부터 쟀을 때(이 시리즈의 번들 글), 캐시는 x-vercel-cache: HIT 이었고 TTFB는 0.16~0.20초 였습니다. 그때 네트워크를 후보에서 바로 지울 수 있었던 건 이 작업 덕분입니다. 개인정보를 지우는 일도 배포의 일부다 문의 폼으로 받은 개인정보는 방침에 보유 기간 1년 이라고 적어 두었습니다. 적어 두기만 하고 아무도 안 지우면, 방침이 거짓말이 됩니다. 그래서 지우는 일을 사람 손이 아니라 배포 설정에 넣었습니다. { "crons": [{ "path": "/api/retention", "schedule": "0 18 * * *" }] } UTC 18시, 한국 시각으로 새벽 3시에 매일 돕니다. 보유 기간은 코드에 RETENTION_DAYS = 365 로 한 곳에 둡니다. 이 기능에서 신경 쓴 건 실수로 지우는 사고 였습니다. 결정 이유 서버 상태 확인용 /api/ping 에 얹지 않고 주소를 따로 뒀다 /api/ping 은 인증 없는 공개 주소다. 지우는 일과 살아 있는지 보는 일을 한 주소에 두지 않는다 CRON_SECRET 이 맞아야만 실행한다 남의 개인정보를 실제로 지우는 길이라 아무나 부르면 안 된다 CRON_SECRET 이 설정 안 됐으면 아무것도 안 지우고 503 "설정 안 했는데 조용히 지워졌다"가 제일 나쁜 사고다 지운 건수와 기준 날짜를 로그로 남긴다 파기했다는 사실을 나중에 증명할 수 있어야 한다 if (!secret) { return NextResponse.json( { ok: false, error: "not-configured", hint: "CRON_SECRET 미설정 — 아무것도 지우지 않았습니다" }, { status: 503 }, ); } // ... console.log(`[retention] ${r.deleted}건 파기 (기준 ${r.cutoff}, 보유 ${RETENTION_DAYS}일)`); 지우는 것만큼 되살리는 것도 출시 전 점검을 하다가 반대쪽 구멍도 찾았습니다. 무료 플랜에는 시점 복구(PITR)가 없어서 , 관리자 화면에서 실수로 지우면 되돌릴 방법이 아무것도 없었습니다. 그래서 DB를 통째로 내려받는 스크립트를 만들었습니다(커밋 41cdcf8 ). npm run backup → backups/2026-10-06T07-19-11.json (products 17 · cases 33 · notices 5 · inquiries 18) 여기서도 개인정보가 걸렸습니다. 받은 파일에는 문의의 이름·연락처·이메일이 들어 있습니다. backups/ 를 .gitignore 에 넣어 저장소에 안 올라가게 했고, 방침의 보유 기간 1년은 백업 파일에도 그대로 적용된다 고 적어 두었습니다. DB에서는 지웠는데 백업에 남아 있으면 지운 게 아닙니다. 마무리 — 무엇을 배웠나 첫째, "서버가 어디서 도는지"는 헤더로 확인합니다. 설정 화면이 아니라 실제 응답의 x-vercel-id 가 사실을 말해 줬습니다. 둘째, 리전보다 캐시가 먼저입니다. 함수를 서울로 옮겨도 매 요청 함수를 돌리면 빠르지 않습니다. force-dynamic 하나가 엣지 캐시를 통째로 막고 있었습니다. 셋째, 방침에 적은 약속은 코드로 지킵니다. 보유 기간은 적는 순간이 아니라 지우는 Cron이 돌 때 사실이 됩니다. 넷째, 위험한 작업은 설정이 빠졌을 때 멈추게 만듭니다. 실패를 숨기는 것보다, 아무것도 안 하고 503을 내는 쪽이 안전합니다. 이 글에서 남는 것 x-vercel-id: icn1::iad1 은 "엣지는 서울, 함수는 미국"이라는 뜻이다. DB가 서울이면 함수도 regions: ["icn1"] . force-dynamic 은 no-store 를 붙여 엣지 캐시를 막는다. 1분 지연이 괜찮은 콘텐츠는 revalidate 로 바꾼다. 검색 유입 상세 페이지는 generateStaticParams 로 미리 만든다. 전 값을 못 재으면 "몇 배 빨라졌다"고 쓰지 않는다. 개인정보 파기는 Cron으로 자동화하고, 비밀값이 없으면 아무것도 안 지우고 실패를 알린다. 지운 건수는 로그로 남긴다. 백업 파일에도 보유 기간이 적용된다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
요청이 태평양을 두 번 건너고 있었다 — 함수 리전을 서울로, 문의 개인정보 파기는 Cron으로. 서버가 서울에 있는 줄 알았습니다. 응답 헤더를 보니 함수는 워싱턴에 있었습니다. 한 줄 요약 응답 헤더 x-vercel-id: icn1::iad1::... 가 보여 준 길은 이랬습니다 — 손님(한국) → 엣지(서울) → 함수(미국 동부) → DB(서울) → 함수(미국) → 손님. 매 요청이 태평양을 두 번 건넌습니다. 함수 리전을 서울( icn1 )로 옮기고, 엣지 캐시를 통째로 막던 force-dynamic 을 걷어 내 전 페이지를 프리렌더 로 바꿨습니다. 이후 캐시 적중 응답은 0.16~0.20초입니다(단, 전 값은 재지 않았습니다). 배포 설정에는 하나를 더 넣었습니다. 개인정보처리방침에 적은 보유…
Открыть источник