항해플러스 프론트엔드 5기 후기(9주차) - 인프라 관점의 성능 최적화

유한별·2025년 5월 31일
1
post-thumbnail

기존에는 웹 최적화라고 하면 그동안은 프론트엔드 코드에서 할 수 있는 것들만 생각했었다.
JS 번들 크기를 줄인다든지, 이미지 압축을 한다든지, 코드 스플리팅으로 초기 로딩 속도를 개선한다든지.

이번 과제를 하면서 처음으로 인프라 레벨에서 성능 최적화가 얼마나 큰 차이를 만들어낼 수 있는지 직접 체감할 수 있었다.
S3에 올린 정적 파일과 CloudFront를 통한 CDN 구조의 차이를 눈으로 확인해보니, 같은 코드를 어떻게 전달하느냐에 따라 사용자 경험이 크게 달라진다는 걸 배울 수 있었다.

이번 글에서는 S3CloudFront 기반의 정적 사이트 배포 과정과, 직접 측정한 성능 차이 분석을 중심으로 그 과정에서 새롭게 알게 된 것들을 정리해보려고 한다.

🚀 배포 파이프라인 구성

Github Acitions

이번 과제에서는 GitHub Actions를 이용해 정적 사이트 배포 파이프라인을 구성하는 것이 첫 단계였다.
평소에는 Vercel 같은 플랫폼에 자동 배포를 맡기는 경우가 많아서, 직접 CI/CD 워크플로우를 작성해보는 경험은 처음이었다.

워크플로우는 기본적으로 코드 푸시빌드S3 업로드CloudFront 캐시 무효화 순서로 구성했다.

name: Deploy Next.js to S3 and invalidate CloudFront

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Deploy to S3
        run: |
          aws s3 sync out/ s3://${{ secrets.S3_BUCKET_NAME }} --delete

      - name: Invalidate CloudFront cache
        run: |
          aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"

단계별로는 단순해 보여도 처음에는 어떤 명령어를 언제 실행해야 하는지, S3CloudFront가 어떤 식으로 연결되는지 개념을 잡는 데 시간이 좀 걸렸다.

특히 aws cloudfront create-invalidation 명령어를 사용해 캐시 무효화를 따로 해줘야 최신 빌드가 사용자에게 바로 반영된다는 점은 이번에 처음 알게 된 부분이었다.
그동안은 플랫폼에서 알아서 해주던 과정을 직접 구성하면서 배포 자동화에서 어떤 부분이 중요한지 한 단계 더 깊이 이해할 수 있었다.

구조 구성 이유

이번 파이프라인을 구성하면서 가장 먼저 고민했던 건 정적 사이트 배포라는 특성상 어떤 흐름이 가장 안정적일까였다.
Next.jsnext export로 빌드하면 서버가 필요 없는 순수 정적 파일들이 나오기 때문에 S3에 정적 파일을 저장하고, CloudFront를 앞단에 두어 CDN으로 빠르게 전달하는 구조가 가장 적합하다고 판단했다.

또, GitHub Actions로 자동화를 구성한 이유는 단순한 CLI 배포보다 반복성과 안정성이 높기 때문이었다.
수동으로 빌드하고 CLI로 배포하는 경우엔 사람이 실수할 여지가 많은데, CI/CD 파이프라인을 구성해두면 코드 변경과 배포가 항상 같은 흐름으로 진행되기 때문에 유지보수도 더 용이해진다.

마지막으로 CloudFront 캐시 무효화 단계까지 넣은 이유는, 정적 사이트는 기본적으로 캐시가 강하게 적용되기 때문에 업데이트 시점에 사용자가 이전 버전 파일을 보는 문제가 발생할 수 있어서였다.
처음에는 왜 invalidate가 필요한지 몰랐는데, 실제로 캐시가 적용된 상태에서 변경한 파일이 바로 반영되지 않는 걸 확인하고 나서야 캐시 무효화의 필요성을 체감할 수 있었다.

📊 성능 차이 실측 및 분석

이번 테스트는 Chrome DevTools의 네트워크 탭을 활용해 직접 측정했다.
같은 Next.js 프로젝트를 next exportS3 정적 웹사이트 호스팅과 CloudFront 배포를 통해 서비스하고, 두 환경에서 동일한 페이지에 대해 최초 요청 시점과 반복 요청 시점을 비교했다.

네트워크 상태는 기본값(Fast 3G, Slow 4G 등)을 따르지 않고 실제 내 네트워크 환경에서 측정했으며, 요청 간 브라우저 캐시를 비우고 진행했다.

성능 비교 결과

항목S3 배포CloudFront 배포비교 (감소율)
Finish256ms203ms20.7% 감소
DOMContentLoaded101ms76ms24.8% 감소
Load222ms183ms17.6% 감소
Transferred487KB205KB57.9% 감소
Resources481KB481KB동일
문서 파일 (HTML)13.4KB (54ms)3.4KB (38ms)크기 74.6%, 시간 29.6% 감소

CloudFront 배포는 전반적으로 전송 속도와 데이터 크기 측면에서 더 우수한 성능을 보였다. 특히 HTML 문서의 크기와 전송 속도 개선이 뚜렷하게 드러났다.

차이 발생 원인

가장 큰 차이는 다음과 같다.

  • 캐싱 위치
    CloudFront는 사용자와 가까운 엣지 로케이션에서 응답한다.
    반면 S3는 특정 리전의 S3 버킷에서 직접 응답하므로 물리적 거리에서 오는 네트워크 지연이 발생한다.

  • 압축 적용 여부
    CloudFront는 기본적으로 Gzip 압축이 적용된다.
    반면 S3 정적 웹 호스팅은 Content-Encoding 헤더가 없어 원본 그대로 전송된다.

  • 반복 요청 최적화
    CloudFront는 동일 요청에 대해 엣지 서버 캐시에서 빠르게 응답할 수 있고, 재전송 시 더 빠른 속도를 보여준다.
    S3는 매번 버킷에서 원본을 내려준다.

얻을 수 있는 이점

  • 첫 페이지 로드 개선: 첫 로드에서 약 20% 이상의 속도 차이를 체감할 수 있다.

  • 모바일 환경에서의 데이터 절감: Transferred 크기가 절반 이하로 줄면서 모바일 데이터 사용량이 줄어든다.

  • 반복 요청에서 UX 개선: 엣지 캐싱으로 인해 페이지 이동이나 새로고침 시 반응 속도가 더 빠르다.

  • 트래픽 비용 절감 가능성: 전송량이 줄면 AWS 트래픽 비용에도 긍정적 영향을 준다.

사실 빌드 결과물은 S3CloudFront 모두 동일한 정적 파일이기 때문에, 처음에는 네트워크 속도에 큰 차이가 나지 않을 거라고 생각했다.
'파일 크기만 같으면 전송 시간도 비슷하겠지' 정도로 가볍게 생각했는데, 네트워크 탭에서 Transferred 크기와 응답 시간이 예상보다 많이 달라져서 놀랐다.

특히 S3에서는 압축이 적용되지 않고 원본 크기로 내려갔던 반면, CloudFront에서는 자동으로 Gzip 압축이 적용돼 전송량 자체가 크게 줄어드는 걸 확인할 수 있었다.
그 결과 DOMContentLoaded 시점과 전체 Load 시점 모두 눈에 띄게 개선됐고, 같은 파일이라도 '어디에서 어떻게 전송하느냐'가 사용자 경험에 얼마나 영향을 주는지를 실제로 체감할 수 있었다.

✍️ 회고

이번 과제를 진행하면서 웹 최적화라는 게 꼭 코드 수준에서만 이루어지는 건 아니라는 점을 처음으로 경험할 수 있었다.
지금까지는 코드 압축, 이미지 최적화, 코드 스플리팅 같은 프론트엔드 레이어의 최적화에만 익숙했는데, 이번에는 같은 파일이라도 인프라 레벨에서 어떻게 전송하느냐에 따라 사용자 경험이 크게 달라질 수 있다는 사실을 눈으로 확인할 수 있었다.

특히 S3CloudFront의 성능 차이를 측정하면서, 전송 경로와 네트워크 최적화가 사용자에게 체감되는 속도에 직접적인 영향을 준다는 것이 가장 인상 깊었다.
압축 적용 여부, 캐시 위치, 반복 요청 처리 방식 같은 요소들이 생각보다 큰 차이를 만들어냈고, '같은 파일이라도 어디에서 어떻게 전달하느냐'가 중요한 최적화 포인트라는 걸 새롭게 배웠다.

이번 경험을 통해 한편으로는 새로운 궁금증도 생겼다.

Vercel이나 Netlify 같은 플랫폼들은 이런 최적화를 내부적으로 어떻게 처리하고 있을까? 별도의 CDN 설정 없이도 빠른 응답 속도를 제공하는데, 내부적으로 어떤 구조와 전략이 적용되고 있는지 궁금해졌다.

또한 CDN을 사용하지 않고도 비슷한 최적화를 할 수 있는 방법은 무엇이 있을까 하는 생각도 들었다.

그리고 무엇보다 중요한 질문은, 'CDN이 항상 최선의 선택일까?' 라는 점이었다.
실시간성이 중요한 서비스에서는 캐싱이 오히려 문제가 될 수도 있다.
이런 경우에는 어떤 인프라 아키텍처가 더 적합할지, 어떤 상황에서 CDN을 쓰고 어떤 상황에서는 다른 전략을 선택해야 할지에 대한 고민이 앞으로 더 필요할 것 같다.

이번 경험을 통해 프론트엔드 개발이라고 해서 꼭 브라우저 렌더링이나 코드 최적화에만 갇혀 있을 필요는 없겠다는 생각이 들었다.

결국 사용자가 느끼는 속도는 네트워크, 서버, 전송 구조까지 포함한 전반적인 시스템 설계에 영향을 받기 때문에, 앞으로는 이런 인프라적인 부분도 더 이해하고 활용해보고 싶다.

profile
세상에 못할 일은 없어!

0개의 댓글