2026년 엣지 컴퓨팅: 분산 배포 전략이 실제로 의미하는 것

작년 11월, 우리 팀이 운영하는 SaaS 제품에서 이상한 일이 생겼다. 서울에 있는 사용자가 “왜 이렇게 느려요?”라는 문의를 보내왔는데, 직접 확인해보니 API 응답 시간이 평균 480ms였다. 우리는 AWS us-east-1에 단일 배포로 운영 중이었고, 한국 사용자 입장에서는 태평양을 두 번 건너는 셈이었다.

그때부터 엣지 컴퓨팅을 본격적으로 파고들기 시작했다. 2주 동안 Cloudflare Workers, Fly.io, Deno Deploy를 직접 써보고 서비스 일부를 실제로 마이그레이션했다. 결론부터 말하면 — 생각보다 효과적이었지만, 생각보다 덜 간단했다.

단일 리전 배포가 한계를 드러내는 순간

솔직히 나는 꽤 오랫동안 “us-east-1이면 충분하다”는 생각으로 살았다. 스타트업 특성상 인프라보다 기능 개발에 집중해야 한다는 논리가 있었고, 사실 틀린 말도 아니었다. 3명짜리 팀에서 멀티리전 인프라를 운영하는 건 오버엔지니어링이 맞다.

그런데 사용자 기반이 바뀌면 이야기가 달라진다. 우리 서비스는 원래 미국 사용자 위주였는데, 어느 순간 아시아 사용자가 전체의 38%를 차지하게 됐다. CDN으로 정적 에셋은 해결했지만 API 레이턴시는 다른 문제다. 클라우드 서버” rel=”nofollow sponsored” target=”_blank”>서버에서 DB를 조회하고 응답을 만들어야 하는 로직은 결국 물리적인 거리 문제고, 서울에서 버지니아까지 빛이 달려가는 시간 자체를 압축할 수는 없다.

멀티리전 배포하기” rel=”nofollow sponsored” target=”_blank”>배포하기” rel=”nofollow sponsored” target=”_blank”>배포하기” rel=”nofollow sponsored” target=”_blank”>배포가 “비용이 몇 배씩 뛰는 것”에서 “워크로드 특성에 맞게 분산하면 비슷하거나 낮아지는 것”으로 바뀐 건 비교적 최근 일이다. 이 변화 덕분에 작은 팀도 현실적인 선택지로 고려할 수 있게 됐다.

Cloudflare Workers vs Fly.io — 실제로 어느 게 다른가

세 가지를 직접 써본 기준으로 정리한다.

Cloudflare Workers는 전 세계 330개 이상 PoP에 배포된다. V8 기반 런타임이라 Node.js와 API가 비슷하지만 완전히 같지는 않다 — 이게 나중에 나를 꽤 고생시켰는데, 뒤에서 따로 얘기하겠다.

Fly.io는 성격이 다르다. Workers처럼 V8 isolate가 아니라 실제 microVM을 전 세계에 분산 배포한다. Docker 컨테이너를 그대로 올릴 수 있어서 마이그레이션 비용이 낮다. 42개 리전을 지원하고 사용자와 가장 가까운 인스턴스로 자동 라우팅해준다. 기존 백엔드 앱을 옮기기엔 셋 중에 진입 장벽이 가장 낮았다.

Deno Deploy는 솔직히 기대보다 성숙해졌다. Deno 2.0이 나오면서 npm 패키지 호환성이 많이 좋아졌고, 배포 경험 자체는 셋 중에 가장 간단했다. 다만 복잡한 워크로드보다는 간단한 API나 edge middleware 용도에 더 맞는다는 느낌이 아직 남아 있다.

세 가지를 비교하다 보니 깨달은 게 하나 있는데 — “엣지 컴퓨팅”이라는 단어가 사실 꽤 다른 두 패턴을 하나로 묶어 부른다. Workers 같은 클라우드 서버” rel=”nofollow sponsored” target=”_blank”>서버리스 엣지(stateless, 격리된 실행 환경)와 Fly.io 같은 분산 VM(더 전통적인 서버이지만 지리적으로 분산). 이걸 먼저 파악하고 시작했다면 초반에 훨씬 덜 헷갈렸을 것 같다.

인증 레이어를 엣지로 옮긴 실제 코드

우리 팀에서 먼저 엣지로 옮긴 건 인증 레이어였다. JWT 검증, 세션 확인 같은 작업은 DB 조회가 거의 없고 CPU 연산 위주라서 Workers에 딱 맞는 케이스다.

실제로 작성한 코드는 대략 이런 형태였다:

// auth-edge.ts — Cloudflare Workers
import { verify } from 'hono/jwt';

interface Env {
  JWT_SECRET: string;
  ORIGIN_URL: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // 정적 에셋은 그냥 통과
    if (url.pathname.startsWith('/static/')) {
      return fetch(request);
    }

    const authHeader = request.headers.get('Authorization');
    if (!authHeader?.startsWith('Bearer ')) {
      return new Response('Unauthorized', { status: 401 });
    }

    const token = authHeader.slice(7);

    try {
      // JWT 검증은 엣지에서 — DB 조회가 없으니 빠름
      const payload = await verify(token, env.JWT_SECRET);

      // 검증된 요청만 오리진으로 포워딩
      const originRequest = new Request(
        env.ORIGIN_URL + url.pathname + url.search,
        {
          method: request.method,
          headers: {
            ...Object.fromEntries(request.headers),
            'X-User-Id': String(payload.sub),
            'X-Verified-At-Edge': 'true',
          },
          body: ['GET', 'HEAD'].includes(request.method)
            ? undefined
            : request.body,
        }
      );

      return fetch(originRequest);
    } catch {
      return new Response('Invalid token', { status: 401 });
    }
  },
};

핵심은 “가볍고 반복적인 작업은 엣지에서, 무거운 비즈니스 로직은 오리진에서” 분리하는 것이다. JWT 검증을 엣지에서 처리하면 인증 실패 요청이 오리진까지 아예 도달하지 않는다 — 오리진 서버 부하도 줄고, 사용자 입장에서는 레이턴시가 눈에 띄게 줄어든다.

적용하고 나서 서울 기준 API 응답 시간이 480ms에서 140ms로 떨어졌다. 전체 로직을 옮긴 게 아니라 인증 레이어만 바꿨는데 이 정도면 꽤 극적이다.

금요일 오후에 저지른 실수

Workers 마이그레이션 초기에 Node.js 코드를 그냥 복붙해서 올리려 했다. 구체적으로는 crypto 모듈을 썼는데 — Workers 런타임에서는 Web Crypto API를 써야 한다. Node.js의 crypto.createHmac('sha256', secret)과 Web Crypto의 crypto.subtle.sign(...) 사이의 API 차이가 생각보다 컸다.

금요일 오후에 “그냥 빠르게 올려보자”는 생각으로 배포하기” rel=”nofollow sponsored” target=”_blank”>배포하기” rel=”nofollow sponsored” target=”_blank”>배포하기” rel=”nofollow sponsored” target=”_blank”>배포했다가, HMAC 검증 로직이 조용히 깨지면서 일부 요청이 401을 뱉기 시작했다. 모니터링에서 에러율이 올라가는 걸 보고 롤백했는데, 원인 파악하는 데 1시간 넘게 걸렸다. 로그도 불충분했고, Workers 환경과 로컬 환경 사이의 동작 차이를 처음에 의심하지 않았던 것도 문제였다.

Workers 환경에서는 Node.js 호환 API가 많이 추가됐지만 100% 동일하지는 않다. node:crypto를 폴리필로 지원하는 경우도 있는데 동작 방식이 미묘하게 다를 수 있다. Cloudflare 공식 문서의 런타임 API 호환성 페이지를 먼저 꼼꼼히 읽는 게 정답이다 — 나처럼 나중에 고생하지 말고.

그 이후로는 Workers에 새 로직을 올릴 때 반드시 wrangler dev로 충분히 로컬 테스트를 마친 다음 배포한다. 당연한 말인데, 당연한 걸 지키지 않아서 생긴 문제였다.

DB가 단일 리전이면 엣지 효과가 반감된다

인증 레이어는 Workers로 해결했지만 DB 조회가 많은 API 엔드포인트는 다른 접근이 필요했다. 이 경우엔 Fly.io가 더 나은 선택이었다.

그런데 여기서 처음에 내가 놓쳤던 부분이 있다. 엣지에 앱을 분산해도 DB가 단일 리전이면 효과가 반감된다. 서울 사용자 요청이 서울 근처 Fly.io 인스턴스에 도달해도, 그 인스턴스가 us-east-1에 있는 Postgres에 쿼리를 날려야 한다면 레이턴시는 거의 줄어들지 않는다. 앱만 분산했다가 기대했던 효과가 안 나와서 한동안 멍했던 기억이 있다.

Fly.io에서 이 문제를 해결하는 방법 중 하나는 Fly Postgres의 멀티리전 읽기 복제본이다. 쓰기는 여전히 프라이머리(우리는 Virginia)로 가지만, 읽기는 사용자와 가장 가까운 복제본에서 처리한다. 우리 서비스 특성상 읽기:쓰기 비율이 약 8:2 정도라서 이게 꽤 효과적이었다. 설정 자체는 fly.toml에서 몇 줄 추가하는 수준이라 복잡하지 않았다.

다만 복제 지연(replication lag)이 있기 때문에 방금 쓴 데이터를 바로 읽어야 하는 워크로드에서는 주의가 필요하다. 우리는 쓰기 후 즉시 읽기가 필요한 케이스를 명시적으로 프라이머리로 라우팅하도록 처리했다. 조금 번거롭지만 데이터 일관성 문제로 고생하는 것보다는 낫다.

솔직히 이 구성이 초당 수천 건 이상의 쓰기가 발생하는 워크로드에서도 잘 버티느냐는 직접 검증해보지 못했다. 우리 서비스 규모에서는 문제없이 동작했지만, 그 규모가 되면 아마 다른 고민이 더 많이 생길 것이다.

언제 엣지를 쓰지 말아야 하는가

두 달 정도 이것저것 써보고 내가 내린 결론 — 모든 걸 엣지로 옮기려는 시도는 하지 마라.

엣지가 잘 맞는 건 명확하다. 인증/인가, A/B 테스트 라우팅, 지오 기반 리다이렉션, 간단한 캐싱 레이어. 반면 복잡한 비즈니스 로직, 트랜잭션이 많은 DB 작업, 무거운 연산은 여전히 오리진 서버에 두는 게 맞다. 엣지 환경의 제약(실행 시간 제한, 메모리 한계, 런타임 차이)을 감안하면 무리하게 옮기는 게 오히려 복잡도만 높인다.

분산 배포는 복잡도를 올린다. 디버깅이 어려워지고, 배포 파이프라인이 복잡해지고, 팀이 이해해야 할 시스템이 늘어난다. 그 복잡도를 감당할 만한 실제 사용자 문제가 있을 때 도입하는 것이지, 미래를 위해 미리 구축하는 건 대부분 낭비로 끝난다.

새 프로젝트를 시작한다면 나는 이렇게 접근할 것이다. 단일 리전으로 시작하되 인증 레이어와 정적 에셋만 처음부터 엣지에 올린다. 실제 사용자 분포와 레이턴시 데이터를 6개월 정도 보고 나서, 병목이 어디인지 확인한 뒤 추가 분산 여부를 결정한다.

지금 당장 엣지를 써봐야겠다면 — Cloudflare Workers부터 시작하라. 무료 티어가 생각보다 넉넉하고, wrangler CLI 경험이 괜찮으며, 커뮤니티도 활발하다. Fly.io는 그다음 단계로, 기존 컨테이너 기반 앱을 지리적으로 분산하고 싶을 때 진입 장벽이 가장 낮다. 순서가 중요하다.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top