본문으로 바로가기 본문 바로가기
WIKI 기술 지식 베이스

Workers KV로 다양한 웹 서버에 요청 라우팅하기

원문 보기 위키 갱신

Workers KV에 라우팅 데이터를 저장해서 Workers 애플리케이션으로 들어오는 요청을 다양한 웹 서버로 라우팅하는 방법을 알아봐요. Cloudflare의 전역 저지연 키-값 저장소인 KV를 활용하면 서버 구성을 유연하게 관리하면서 높은 읽기 성능을 유지할 수 있어요.

출처: 문서

본문

Workers KV에 라우팅 데이터를 저장해서 Workers로 들어오는 요청을 다양한 웹 서버로 라우팅하는 것은 Workers KV의 이상적인 사용 사례예요. 라우팅 작업은 읽기 볼륨이 높을 수 있는데, Workers KV의 저지연 읽기가 라우팅 결정을 빠르고 효율적으로 내리는 데 도움을 줘요.

라우팅은 단일 Cloudflare Worker 애플리케이션으로 들어오는 요청을 요청의 경로, 호스트네임, 또는 기타 요청 속성에 따라 다양한 웹 서버로 보내는 데 유용해요.

단일 테넌트 애플리케이션에서는 비즈니스 도메인에 따라 요청을 다양한 오리진 서버로 라우팅할 수 있어요 (예: /admin 요청은 관리 서버로, /store 요청은 스토어프런트 서버로, /api 요청은 API 서버로).

멀티 테넌트 애플리케이션에서는 요청을 각 테넌트의 오리진 리소스로 라우팅할 수 있어요 (예: tenantA.your-worker-hostname.com 요청은 테넌트 A 서버로, tenantB.your-worker-hostname.com 요청은 테넌트 B 서버로).

라우팅은 또한 자체 외부 애플리케이션을 위한 A/B 테스트, 카나리 배포, 또는 블루-그린 배포 ↗︎를 구현하는 데도 사용할 수 있어요. Cloudflare Workers에 완전히 구축된 애플리케이션의 카나리 또는 블루-그린 배포를 구현하려면 Workers 점진적 배포를 참조하세요.

Workers KV로 요청 라우팅하기

이 예시에서는 클라우드플레어 Workers에 멀티 테넌트 전자상거래 애플리케이션이 구축돼 있어요. 각 스토어프런트는 서로 다른 테넌트이며 각자 자체 외부 웹 서버를 가지고 있어요. 우리의 Cloudflare Worker는 모든 스토어프런트의 요청을 받아서 스토어프런트 ID에 따라 올바른 오리진 웹 서버로 라우팅하는 역할을 해요.

데모의 단순화를 위해 스토어프런트는 URL 경로에 스토어프런트 ID가 포함된 요소로 식별할게요. 여기서 https://<WORKER_HOSTNAME>/<STOREFRONT_ID>/... 가 스토어프런트의 URL 패턴이에요. 실제 시나리오에서는 스토어프런트를 식별하기 위해 서브도메인을 사용하는 것을 선호할 수도 있어요.

// Example routing data stored in Workers KV:
// Key: "storefrontA" | Value: {"origin": "https://storefrontA-server.example.com"}
// Key: "storefrontB" | Value: {"origin": "https://storefrontB-server.example.com"}

interface Env {
ROUTING_CONFIG: KVNamespace;
}

export default {
  async fetch(request, env, ctx) {

    // Parse the URL to extract the storefront ID from the path
    const url = new URL(request.url);
    const pathParts = url.pathname.split('/').filter(part => part !== '');

    // Check if a storefront ID is provided in the path, otherwise return 400
    if (pathParts.length === 0) {
      return new Response('Welcome to our multi-tenant platform. Please specify a storefront ID in the URL path.', {
        status: 400,
        headers: { 'Content-Type': 'text/plain' }
      });
    }

    // Extract the storefront ID from the first path segment
    const storefrontId = pathParts[0];

    try {
      // Look up the storefront configuration in KV using env.ROUTING_CONFIG
      const storefrontConfig = await env.ROUTING_CONFIG.get<{
    		origin: string;
    	}>(storefrontId, {type: "json"});

      // If no configuration is found, return a 404
      if (!storefrontConfig) {
        return new Response(`Storefront "${storefrontId}" not found.`, {
          status: 404,
          headers: { 'Content-Type': 'text/plain' }
        });
      }

      // Construct the new URL for the origin server
      // Remove the storefront ID from the path when forwarding
      const newPathname = '/' + pathParts.slice(1).join('/');
      const originUrl = new URL(newPathname, storefrontConfig.origin);
      originUrl.search = url.search;

      // Create a new request to the origin server
      const originRequest = new Request(originUrl, {
        method: request.method,
        headers: request.headers,
        body: request.body,
        redirect: 'follow'
      });

      // Send the request to the origin server
      const response = await fetch(originRequest);

    		console.log(response.status)

      // Clone the response and add a custom header
      const modifiedResponse = new Response(response.body, response);
      modifiedResponse.headers.set('X-Served-By', 'Cloudflare Worker');
      modifiedResponse.headers.set('X-Storefront-ID', storefrontId);

      return modifiedResponse;

    } catch (error) {
      // Handle any errors
      console.error(`Error processing request for storefront ${storefrontId}:`, error);
      return new Response('An error occurred while processing your request.', {
        status: 500,
        headers: { 'Content-Type': 'text/plain' }
      });
    }

}
} satisfies ExportedHandler<Env>;
{
	"$schema": "node_modules/wrangler/config-schema.json",
	"name": "<ENTER_WORKER_NAME>",
	"main": "src/index.ts",
	"compatibility_date": "2025-03-03",
	"observability": {
		"enabled": true
	},
	"kv_namespaces": [
		{
			"binding": "ROUTING_CONFIG",
			"id": "<YOUR_BINDING_ID>"
		}
	]
}

이 예시에서 Cloudflare Worker는 요청을 받아 URL 경로에서 스토어프런트 ID를 추출해요. 스토어프런트 ID는 get() 메서드를 사용해 Workers KV에서 오리진 서버 URL을 조회하는 데 사용돼요. 그런 다음 요청이 오리진 서버로 전달되고, 응답이 커스텀 헤더를 포함하도록 수정된 뒤 클라이언트로 반환돼요.

관련 리소스

더 알아보기 (Learn more)

Workers KV의 전역 저지연 특성과 라우팅 설계에 대해 더 알고 싶다면 Cloudflare 공식 문서의 KV 개념 섹션과 관련 리소스 문서를 함께 참고하면 좋아요.