소개
웹사이트는 다크 테마나 선호 언어 같은 화면 표시 설정을 기억할 수 있습니다. 이러한 설정은 방문할 때마다 읽는 경우가 많지만 변경되는 일은 드물기 때문에 Workers KV 를 설명하는 좋은 예가 됩니다. 기능 플래그에 단어 하나를 저장하는 대신, 이름이 있는 필드를 하나의 값으로 묶는 텍스트인 JSON을 저장합니다. Worker 는 이 텍스트를 다시 사용할 수 있는 설정으로 변환합니다.
이 실습에서 Alice 와 Bob 은 실제 사용자가 아닌 가상의 계정 레이블입니다. 두 계정에 서로 다른 환경 설정을 지정하고, 누락되거나 손상된 항목에는 적절한 기본값을 반환하도록 구성합니다. 또한 값과 함께 저장되는 짧은 설명인 메타데이터를 추가하여 설정의 버전을 식별합니다. 버전 번호는 어떤 데이터가 읽혔는지 설명하는 데 도움이 되지만, 모든 위치에서 항상 최신 값을 즉시 본다는 것을 보장하지는 않습니다.
먼저 Create a Feature Flag Store 를 완료해야 합니다. 이 실습은 /home/labex/project/account-preferences의 새 VM 에서 시작하며, Node.js 22.22.0 과 프로젝트 전용 Wrangler 4.131.1 이 이미 설치되어 있습니다. 학습 계정에 새 Worker 와 네임스페이스를 만들고, 동일한 계정 읽기, Worker 쓰기 및 KV 쓰기 권한을 사용합니다. 공개 데모에서는 가상의 화면 표시 설정만 제공하며, URL 의 계정 레이블은 인증 수단이 아닙니다. 이 작은 실습에는 유료 업그레이드나 구매한 도메인이 필요하지 않습니다. VM 을 떠나기 전에 리소스 정리를 완료합니다.
환경 설정 네임스페이스 연결
이 단계에서는 샘플 계정 환경 설정을 저장할 독립적인 네임스페이스를 연결합니다. 네임스페이스는 이 서비스의 값을 묶어 관리하고, PREFERENCES 바인딩은 Worker 가 해당 네임스페이스에 접근할 때 사용할 고정된 이름을 제공합니다. 이 새 VM 은 이전 실습의 네임스페이스나 권한 부여를 재사용하지 않고, 계정 정보만 재사용합니다.
준비된 프로젝트로 이동합니다.
cd /home/labex/project/account-preferences
한 번만 사용할 고유한 이름을 생성합니다. openssl rand -hex 6은 무작위 접미사를 출력하고, $(...)은 그 값을 이름에 삽입합니다. 셸 변수에 이름을 저장하면 이 터미널에서 이어서 실행할 명령에서도 사용할 수 있습니다.
WORKER_NAME="labex-prefs-$(openssl rand -hex 6)"
printf '%s\n' "$WORKER_NAME"
이 VM 을 인증합니다. 계정 ID 를 읽는 것 외에도 Workers Scripts Write 권한은 배포와 삭제를 허용하고, Workers KV Write 권한은 이 실습의 네임스페이스와 키를 관리할 수 있게 합니다.
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write
표시된 디바이스 링크를 브라우저에서 열고, 현재 코드를 입력한 다음 요청된 권한과 학습 계정을 확인하고 Wrangler 를 승인합니다. 동의 페이지에 백그라운드 액세스 권한이 표시될 수도 있습니다. 터미널로 돌아와 로그인이 완료될 때까지 기다립니다.
Developer Platform을 펼쳐 Workers Scripts Write 와 Workers KV Storage Write 를 확인합니다. 이 권한은 Create a Feature Flag Store 에서 소개한 리소스 관리 권한과 동일합니다.
npx wrangler whoami --json
계정이 하나만 표시되더라도 loggedIn: true와 학습 계정의 name을 확인합니다. 해당 계정의 id를 복사합니다. 아래 구성에서 YOUR_ACCOUNT_ID를 복사한 값으로 바꾼 후 명령을 실행합니다. 여기서 cat의 here-document 는 두 JSON 줄 사이의 모든 내용을 파일에 기록하고, >는 파일을 덮어씁니다. 따옴표로 감싸지 않은 구분자는 셸이 $WORKER_NAME을 삽입하도록 합니다.
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true
}
JSON
해당 계정에 네임스페이스를 생성합니다. 네임스페이스 제목은 나중에 두 리소스를 구분할 수 있도록 Worker 의 고유한 이름을 공유합니다. --update-config=false를 지정하면 Wrangler 가 파일을 자동으로 수정하지 않고 바인딩 편집 내용을 직접 확인할 수 있습니다.
npx wrangler kv namespace create "$WORKER_NAME-preferences" --update-config=false
출력에 새 네임스페이스 ID 가 포함됩니다. ID 를 복사한 다음, 아래의 완전한 구성에서 YOUR_ACCOUNT_ID와 YOUR_NAMESPACE_ID를 각각 실제 값으로 바꿉니다. PREFERENCES 바인딩 이름은 코드에서 사용할 이름이고, ID 는 실제 Cloudflare 리소스를 식별합니다.
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true,
"kv_namespaces": [
{ "binding": "PREFERENCES", "id": "YOUR_NAMESPACE_ID" }
]
}
JSON
npx wrangler kv namespace list
이 실습에서 만든 네임스페이스 제목을 찾아 파일에 기록한 ID 와 비교합니다. 다른 네임스페이스가 표시될 수 있지만 그대로 둡니다. 이 구성은 이후 명령이 사용할 계정과 리소스를 지정합니다. 바인딩은 네임스페이스에 대한 참조이며, 데이터의 복사본이 아닙니다.
JSON 값과 버전 메타데이터 저장
이 단계에서는 일반적인 설정과 현실적인 데이터 오류 두 가지가 포함된 작은 데이터 세트를 준비합니다. JSON 에서는 필드 이름과 문자열에 큰따옴표를 사용합니다. 명령 인자를 작은따옴표로 감싸면 셸이 JSON 의 큰따옴표를 해석하지 않습니다.
로컬 항목을 작성합니다. Alice 는 다크 모드와 영어를 선호하고, Bob 은 라이트 모드와 프랑스어를 선호합니다. --metadata는 키에 별도의 JSON 객체를 연결합니다. 여기서 revision 번호는 저장된 버전을 나타내는 레이블이며, 보안 판단이나 자동 증가 카운터가 아닙니다.
npx wrangler kv key put account:alice '{"theme":"dark","language":"en"}' --binding PREFERENCES --local --metadata '{"revision":7}'
npx wrangler kv key put account:bob '{"theme":"light","language":"fr"}' --binding PREFERENCES --local --metadata '{"revision":8}'
npx wrangler kv key put account:broken 'not-json' --binding PREFERENCES --local
npx wrangler kv key put account:invalid '{"theme":"neon","language":"en"}' --binding PREFERENCES --local
account:broken에는 JSON 으로 구문 분석할 수 없는 텍스트가 들어 있습니다. account:invalid은 올바른 JSON 이지만 애플리케이션에서 지원하지 않는 테마를 지정합니다. 두 경우를 모두 유지하면 텍스트 구조를 읽는 **구문 분석 (parsing)**과 필드가 애플리케이션에 적합한지 확인하는 **검증 (validation)**을 구분할 수 있습니다. Charlie 항목은 만들지 않습니다. 누락된 키 경로를 테스트하는 데 사용합니다.
npx wrangler kv key list --binding PREFERENCES --local
키 이름이 네 개 표시되는지 확인합니다. Alice 와 Bob 의 버전 메타데이터는 각각 7과 8이어야 합니다. 나머지 두 항목에는 메타데이터가 없습니다. 목록에는 이름과 메타데이터가 표시되지만 모든 값이 표시되지는 않습니다.
npx wrangler kv key get account:alice --binding PREFERENCES --local --text
{"theme":"dark","language":"en"}이 출력되는지 확인합니다. 이 명령은 값만 읽으므로 버전은 이 JSON 텍스트에 포함되지 않습니다.
이제 동일한 네 개의 가상 테스트 데이터를 이 실습의 클라우드 네임스페이스에 기록합니다. 아래의 명시적인 원격 명령은 별도의 작업입니다. 로컬 쓰기는 Cloudflare 로 업로드되지 않습니다.
npx wrangler kv key put account:alice '{"theme":"dark","language":"en"}' --binding PREFERENCES --remote --metadata '{"revision":7}'
npx wrangler kv key put account:bob '{"theme":"light","language":"fr"}' --binding PREFERENCES --remote --metadata '{"revision":8}'
npx wrangler kv key put account:broken 'not-json' --binding PREFERENCES --remote
npx wrangler kv key put account:invalid '{"theme":"neon","language":"en"}' --binding PREFERENCES --remote
npx wrangler kv key list --binding PREFERENCES --remote
동일한 네 개의 키 이름과 버전 메타데이터가 표시되는지 확인합니다. 이 데이터는 삭제해도 되는 데모 레코드입니다. 관련 없는 네임스페이스는 변경하지 않습니다.
안전한 기본값으로 환경 설정 읽기
이 단계에서는 값과 메타데이터를 함께 가져오는 핸들러를 작성합니다. getWithMetadata()는 value와 metadata 필드를 포함한 객체를 반환합니다. 키가 없으면 value는 null입니다. 값이 있어도 메타데이터는 null일 수 있습니다.
다음 핸들러를 작성합니다. 따옴표로 감싼 JS here-document 는 코드를 그대로 보존합니다. 이 경로는 짧은 소문자 계정 레이블을 받고 account:alice와 같은 고유한 키를 생성합니다. 이전 요청의 계정을 전역 변수에 저장하지 않습니다.
cat > src/index.js <<'JS'
function fallback(account, source) {
return Response.json({
account, theme: "light", language: "en", source, revision: null
});
}
export default {
async fetch(request, env) {
const match = new URL(request.url).pathname.match(/^\/preferences\/([a-z]{1,20})$/);
if (!match) return new Response("Not found", { status: 404 });
const account = match[1];
let entry;
try {
entry = await env.PREFERENCES.getWithMetadata(`account:${account}`, "text");
} catch {
return Response.json({ error: "Preferences temporarily unavailable" }, { status: 503 });
}
if (entry.value === null) return fallback(account, "missing");
let preferences;
try {
preferences = JSON.parse(entry.value);
} catch {
return fallback(account, "invalid");
}
if (!preferences || typeof preferences !== "object" || Array.isArray(preferences) ||
!["light", "dark"].includes(preferences.theme) ||
!["en", "fr"].includes(preferences.language)) {
return fallback(account, "invalid");
}
const revision = Number.isInteger(entry.metadata?.revision) && entry.metadata.revision > 0
? entry.metadata.revision : null;
return Response.json({
account, theme: preferences.theme, language: preferences.language,
source: "stored", revision
});
}
};
JS
첫 번째 try/catch는 KV 를 사용할 수 없을 때 HTTP 503을 반환하도록 처리합니다. HTTP 503은 서비스를 일시적으로 사용할 수 없다는 뜻입니다. 이 경우 계정이 없는 것처럼 처리하지 않습니다. 값을 "text"로 읽은 다음 별도의 try/catch에서 구문 분석하면, 저장소 오류와 손상된 JSON 을 구분할 수 있습니다. "json" 옵션으로 읽으면 자동으로 구문 분석할 수 있지만, 이 실습에서는 두 작업을 분리하여 각 오류 경로가 명확하게 드러나도록 합니다.
누락된 환경 설정과 유효하지 않은 환경 설정은 모두 라이트 모드와 영어로 대체됩니다. source 필드는 기본값을 사용한 이유를 설명합니다. 유효한 값의 경우 응답에는 지원되는 테마와 언어 필드만 사용됩니다. entry.metadata?.revision은 메타데이터가 없어도 안전하게 처리합니다. 양의 정수인 버전만 표시하고, 그렇지 않으면 null을 사용합니다. 이러한 기본값은 선택적인 화면 표시 설정을 계속 사용할 수 있게 하지만, 인증이나 권한을 대신할 수는 없습니다.
로컬 Worker 를 시작하고 프로세스 ID 를 저장한 다음 준비 메시지를 기다립니다.
npx wrangler dev --local --ip 0.0.0.0 --port 8080 > local.log 2>&1 &
DEV_PID=$!
cat local.log
백그라운드 프로세스가 실행되므로 터미널을 계속 사용할 수 있으며, local.log에는 프로세스 출력이 저장됩니다. 시작이 아직 끝나지 않았다면 로그 명령을 다시 실행합니다. 각 경우를 요청합니다.
curl -i http://127.0.0.1:8080/preferences/alice
curl -i http://127.0.0.1:8080/preferences/bob
curl -i http://127.0.0.1:8080/preferences/charlie
curl -i http://127.0.0.1:8080/preferences/broken
curl -i http://127.0.0.1:8080/preferences/invalid
다섯 요청 모두 JSON 과 함께 HTTP 200을 반환해야 합니다. 다음 표와 응답의 차이를 확인합니다.
| 계정 | 테마 | 언어 | 출처 | 버전 |
|---|---|---|---|---|
| alice | dark | en | stored | 7 |
| bob | light | fr | stored | 8 |
| charlie | light | en | missing | null |
| broken | light | en | invalid | null |
| invalid | light | en | invalid | null |
예를 들어 Alice 의 응답 본문은 {"account":"alice","theme":"dark","language":"en","source":"stored","revision":7}입니다. Bob 을 요청한 후 Alice 를 다시 요청해도 설정이 계속 Alice 의 설정인지 확인합니다. 정리 단계까지 로컬 서버를 실행한 상태로 둡니다.
배포된 환경 설정 서비스 확인
이 단계에서는 클라우드 네임스페이스를 사용하여 동일한 경우를 실행합니다. 독립적인 클라우드 확인을 통해 선택한 계정, 배포된 네임스페이스 바인딩, 저장된 레코드 및 실제 HTTP 응답을 검증합니다.
npx wrangler deploy
출력에서 생성된 Worker 이름과 PREFERENCES 바인딩을 확인합니다. 배포된 공개 주소를 복사하여 아래 변수에 넣고 예시 주소를 바꿉니다.
WORKER_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$WORKER_URL/preferences/alice"
curl -i "$WORKER_URL/preferences/bob"
curl -i "$WORKER_URL/preferences/charlie"
curl -i "$WORKER_URL/preferences/broken"
curl -i "$WORKER_URL/preferences/invalid"
다섯 응답을 로컬 테스트 표와 비교합니다. Alice 와 Bob 은 각각의 환경 설정과 버전 메타데이터를 유지해야 하며, Charlie 와 손상된 두 레코드는 설명한 기본값을 사용해야 합니다. 최근에 작성한 항목이 아직 보이지 않으면 KV 전파가 완료될 때까지 기다린 후 다시 시도합니다. 첫 배포 직후에는 공개 호스트 이름이 준비되는 데 시간이 걸릴 수도 있습니다. 연결 오류는 기본값 응답으로 간주하지 않습니다.
Dashboard 에서 학습 계정을 선택하고 Storage & databases → Workers KV를 열어 이 실습의 labex-prefs-...-preferences 네임스페이스를 찾습니다. KV Pairs를 선택하여 네 개의 레코드를 확인하고, account:alice 옆의 View를 클릭해 JSON 값을 터미널 출력과 비교합니다. 이 화면에는 키와 값이 표시됩니다. 리비전 메타데이터는 앞의 Wrangler 키 목록과 API 응답으로 확인하세요. 고유한 네임스페이스 이름과 ID 는 예시와 다릅니다.

공개 엔드포인트는 가상의 화면 표시 설정 데모일 뿐입니다. 실제 비공개 환경 설정 서비스라면 어떤 계정 키에 접근할 수 있는지 결정하기 전에 요청자를 식별해야 합니다.
임시 클라우드 리소스 삭제
이 단계에서는 Wrangler 가 아직 인증된 상태에서 두 리소스를 모두 삭제합니다. 네임스페이스는 Worker 보다 오래 남을 수 있으므로 애플리케이션만 삭제해서는 데이터가 정리되지 않습니다.
이 터미널에서 시작한 로컬 개발 프로세스를 중지합니다.
kill "$DEV_PID"
삭제하기 전에 저장된 리소스 참조를 확인합니다.
cat wrangler.jsonc
labex-prefs-... Worker 이름과 PREFERENCES 네임스페이스 ID 를 확인합니다. 이 구성으로 선택되는 Worker 를 삭제합니다.
npx wrangler delete
확인 메시지가 표시되면 이름이 이 실습의 이름과 일치하는지 확인하고 y를 입력합니다. 그런 다음 PREFERENCES가 참조하는 네임스페이스만 삭제합니다.
npx wrangler kv namespace delete --binding PREFERENCES
확인 메시지에 표시된 네임스페이스를 검토한 후 승인합니다. 독립적인 확인 절차가 삭제되어야 할 리소스를 식별할 수 있도록 wrangler.jsonc는 그대로 둡니다.
npx wrangler kv namespace list
이 실습의 네임스페이스가 목록에서 사라지고, 관련 없는 네임스페이스는 그대로 있어야 합니다. Dashboard 의 목록을 새로 고쳐 실습의 Worker 와 네임스페이스가 사라졌는지도 확인합니다. 요청 실패나 로그인 만료만으로는 삭제가 완료되었다고 볼 수 없습니다. 로그아웃하기 전에 이 단계의 확인을 실행해야 인증된 인벤토리를 검사할 수 있습니다.
VM 인증 종료
이 단계에서는 정리 확인이 통과된 후 Wrangler 의 연결을 해제합니다. 로그아웃하면 이 VM 에 저장된 Wrangler 인증이 종료되지만, 클라우드 리소스가 삭제되거나 일반 Dashboard 브라우저 세션에서 로그아웃되는 것은 아닙니다.
npx wrangler logout
npx wrangler whoami --json
구조화된 결과에 "loggedIn": false가 표시되는지 확인합니다. 인증되지 않은 이 명령은 0 이 아닌 종료 상태로 끝날 수 있으며, 여기서는 정상입니다. 명시적인 인증 상태 없이 연결 오류만 표시되면 연결이 정상화된 후 다시 시도합니다.
남아 있는 로컬 파일과 로컬 KV 상태는 이 임시 VM 에 속합니다. 이미 삭제한 클라우드 리소스와는 별개입니다. 이제 실습을 종료할 수 있습니다.
요약
Workers KV 에 구조화된 환경 설정과 버전 메타데이터를 저장한 다음, Worker 바인딩을 통해 읽었습니다. Alice 와 Bob 의 설정을 서로 분리하고, 누락되거나 잘못된 형식이거나 지원되지 않는 값에는 이유가 드러나는 기본값을 반환하도록 했습니다. 또한 저장소 오류와 레코드 누락을 구분하여 두 경우를 동일한 응답으로 숨기지 않았습니다.
로컬 응답과 클라우드 응답을 비교한 후, 임시 Worker 와 네임스페이스를 삭제하고 로그아웃했습니다. 다음 실습에서는 임시 알림에 애플리케이션 만료 시간과 KV 만료 시간을 지정합니다.



