우리가 프론트엔드 개발을 할 때 작성하는 코드(JSX, TypeScript 등)는 대부분 브라우저가 바로 이해할 수 없다. 브라우저가 직접 이해할 수 있는 언어는 HTML, CSS, JavaScript로 제한되어 있기에, 빌드 과정을 통해 HTML, CSS, JavaScript로 변환해야한다. 또한, 변환된 결과물이 사용자가 언제든 접근할 수 있는 공간에 올라가야한다.
여기서 어디에, 어떻게 올리고, 어떻게 제공하는지 가 배포이다.
1) 24시간 켜져 있는 공간
사용자가 내 PC가 꺼져 있어도, 전 세계 어디서든, 내가 만든 웹사이트에 접근할 수 있어야 한다. 이를 위해선 항상 켜져 있는 서버나 스토리지 공간이 필요하다
2) 보안과 접근 제어
내 컴퓨터를 그대로 노출하면 IP 주소가 외부에 공개되기에, 클라우드 서비스는 이런 인프라 보안을 대신 관리해주고, HTTPS 인증서 적용도 쉽게 할 수 있다.
3) 전 세계 사용자에게 빠르게 전달
한국에만 파일이 있다면 다른 해외 사용자에게는 느릴 수 밖에 없다. 정적 파일을 CDN(Content Delivery Network) 으로 전 세계에 복제해두면, 가장 가까운 지점에서 파일을 내려주기 때문에 웹사이트 접속 속도가 확연히 빨라진다.
즉, 정적 파일 = 완성된 웹사이트,배포 = 그 파일을 안전하고 빠르며 안정적으로 사용자에게 전달하는 과정이라고 볼 수 있다.
결국 웹페이지는 HTML 을 요청하고 다운로드하는 과정으로 시작하는 것이다.
| 환경 | 접근 방식 | 프로토콜 |
| 로컬(Local) | 파일 경로로 접근 | file:// |
| 리모트(Remote 서버) | URL로 접근 | HTTP/HTTPS |
따라서 로컬 브라우저는 HTML의 위치만 알면되지만, 서버는 URL과 프로토콜을 알아야 접근할 수 있다.
배포 플랫폼은 결국 아래 세가지를 보장해줘야한다.
HTML 위치를 찾아갈 수 있어야 한다 (URL) HTML을 웹에서 요청할 수 있어야 한다 (HTTP) HTML이 의도대로 동작해야 한다
정의
배포 플랫폼의 역할
리모트 URL을 파악 → 웹 서버가 HTTP GET 요청을 이해 → HTML 파일을 내려줌
→ 웹서버가 브라우저가 보낸 HTTP GET 요청을 처리한다
→ 정적 웹사이트는 단순한 웹 서버만으로도 배포 플랫폼을 구축할 수 있다 (변하는 데이터 X)
정적 웹사이트는 누구에게나 동일한 HTML만 제공하지만,
동적 웹사이트는 사용자마다 개인화된 다른 콘텐츠를 제공하고자할 때 필요하다.
웹서버의 한계
웹 서버는 파일만 내려주는 역할이라, 동적인 처리까지 하면 부하가 커지고 복잡해진다
→ HTTP 요청을 받아서 동적인 작업을 처리하는 웹 애플리케이션 서버 추가
웹 애플리케이션 서버 (WAS)
→ 동적 웹사이트는 웹 서버 + 웹 애플리케이션 서버가 함께 있어야 배포 플랫폼이 완성된다.

모든 화면 전환에 HTML 을 새로 로드해야했다.
따라서, UI/UX 측면에서 계속 새로고침 발생 → 느린 UI/UX → 화면 표시와 데이터 처리의 강한 결합 발생
이러한 불편함을 해소하기 위해 AJAX가 나왔다. 이후 브라우저 재로딩없이도 서버와 데이터를 주고받을 수 있게 되었다.
웹 서버에서 HTML을 받아온 후, 브라우저는 데이터 전달을 위해 또 다른 웹 서버에 요청을 보내 데이터를 받을 수 있는 구조로 발전하게 된다.

AJAX의 등장으로 SPA이 떠오르게 되었다.
SPA에서는
→ SPA에서 HTML은 통로가 되고, JS가 화면/상태/라우팅을 담당한다
SPA는 초기 로딩 시 정적 자원을 한 번 내려받고, 이후 화면 전환/라우팅 등을 모두 클라이언트쪽 JS가 담당하게 된다.
이때 서버는 라우팅을 모르기에 사용자가 서브 경로로(/mypage , /profile) 접근하면 서버가 대응되는 파일을 찾아 응답해줄 수 없다.
전통적인 웹사이트는 서브 경로에 맞는 HTML 을 서버가 동적으로 만들어 내려주지만, SPA는 필요한 화면 구조가 모두 JS에 포함되어 있기에 서버가 따로 만들어줄 페이지가 없다.
따라서 SPA 배포 플랫폼은 아래 조건을 만족해야한다.
→ 서브 경로 요청 시 서버는 파일을 찾지 못해 원래는 404 반환, SPA 에서는 무조건 404 대신 index.html을 반환해야 한다
SPA는 초기 로딩 시 모든 파일을 요청하기에 초기 로딩이 무겁다.
HTML GET → JS GET → CSS GET → … 이러한 과정이 연속적으로 발생하여, 워터폴 현상이 발생할 수 있다.
HTTP의 헤더를 사용하여 성능 최적화를 한다. 그중 Cache-Control를 활용해서 기존에 받은 HTML,JS,CSS 를 재활용할 수 있다.
예를 들어 아래와 같이 사용한다. (HTML,JS 모두 사용 가능)
Cache-Control: max-age=60 → HTML을 60초 동안 캐시하고, 만료되면 서버로 검증하세요
빌드를 할 때 빌드 도구를 사용해 HTML에 JS와 CSS 를 통합한다. 이 과정에서 번들링은 자바스크립트 해시값을 추가한다.
정적 배포에서는 파일명에 해시(hash)를 붙여서 변경 여부를 식별하는 방식을 사용하는 것이다.
빌드 시 무엇이 일어날까?
빌드 도구(예: Vite, Webpack, CRA)는 JS/CSS 파일을 다음과 같이 변환한다.

main.a7c391.jsstyle.42fb20.css
왜 해시를 붙일까?
브라우저는 파일명을 캐시 키로 사용한다. 즉, 파일명 = 이 파일의 버전
내용이 같으면 캐시를 쓰고, 바뀌면 자연스럽게 최신 파일을 받는다.
SPA HTML이 foo.js → bar.js 로 변경되었을 때,
이전 HTML이 foo.js 를 요청하면 404 오류가 발생할 수 있다.
→ SPA는 파일 버전 관리를 하지 않으면 HTML이 최신 JS를 가리키지 못해 동작 보장을 못하게 된다.
따라서 SPA 배포 플랫폼은
리모트 서버에서만 파일을 받으면 위치가 멀수록 응답시간이 길어진다.
CDN은 전 세계 여러 서버에 캐싱하여, 가까운 곳에서 빠르게 응답이 가능하다.
또한, 요청이 필요할 때 캐시된 응답을 제공하여 HTTP 응답 속도를 개선할 수 있다.
동작 : 브라우저 → CDN 서버 → 필요한 경우 오리진 서버
이러한 CDN 캐시는 배포 이후에도 남아있을 수 있기에, 새로운 배포시 CDN 퍼지(purge)를 통해 CDN 캐시를 무효화할 수 있다.
CDN은 SPA뿐만이 아닌 정적 웹사이트 성능 최적화에도 적용 가능.
아래에서는 실제 정적 배포 방식을 AWS에서 어떻게 진행할 수 있을지를 알아보고자 한다.
아래 과정은 React 프로젝트를 기반으로 한 내용이다.
본인 프로젝트에 맞는 명령어를 통해 build를 하여 정적인 파일들을 생성한다.
npm run build
앞선 프로젝트 빌드 후 생성된 정적 파일을 저장할 공간이 필요하다. 이러한 공간이 바로 S3이다.
S3는 원래 객체 스토리이지 서비스이지만, 정적 웹사이트 배포에서는 HTML,CSS,JS 등의 정적 파일을 저장해두고 사용자가 요청할 때 제공하는 웹 서버의 역할도 할 수 있다.
즉, 정적 파일을 저장해두고 사용자가 접근하고자할 때 사용자에게 전달해주는 과정을 S3가 해줄 수 있다!
하지만, 라우팅/캐싱/속도 등에 한계가 있어 CloudFront를 S3와 함께 사용한다.

S3 만으로 배포를 하고자한다면 퍼블릭 액세스 차단을 해지하여, S3 버킷만으로 다른 사용자가 접근가능하도록 해야한다.
하지만 우리는 이후 CloudFront를 통해 접속할 것이기 때문에, S3를 외부에 열어두지 않고 private 하게 관리하는 것이 좋다. 따라서 액세스 차단을 허용해두어 CloudFront 를 통해 사용자가 접근하면, CloudFront가 S3 객체를 가져가 호스팅할 수 있도록 해야한다.


빌드 후 생성된 정적 파일들(build 폴더 내부에 있는 파일/폴더들)을 빨간 박스에 업로드한다.

CloudFront는 AWS의 CDN(Content Delivery Network) 이다.
전 세계 여러 지역(엣지 로케이션, PoP)에 파일을 미리 캐싱해두고, 사용자에게 가장 가까운 위치에서 빠르게 제공하는 글로벌 분산 캐시 서버 라고 할 수 있다.
즉, 사용자의 입장에서 가장 가까운 곳에서 제공하여 속도와 안정성을 높일 수 있게 한다.
Distribution name - 배포를 식별하기 위한 이름으로 본인이 구분하기 위한 용도로 적으면 된다.
Description 은 말그대로 해당 cloudFront 에 대한 설명을 추가하는 것이므로 생략해도 된다.

이후 AWS의 도메인 관리 서비스인 Route53에 등록된 도메인이 있다면 배포할 사이트의 도메인을 바로 설정해주면 된다. 현재 등록이 되어있지 않다면 패스!

현재 빌드 파일을 S3에 저장했기에 origin type 을 S3로 선택한다.
Browse S3 버튼을 통해 만들어둔 S3 버킷을 선택하면 해당 버킷의 주소가 자동으로 입력된다.
origin path 는 S3 버킷 내부에서 버킷의 최상위가 아닌 곳에 빌드 파일을 넣었다면 해당 경로를 추가해주면된다. 이전 과정처럼 루트에 바로 넣었다면 패스!

Allow private S3 bucket access to CloudFront - 활성화
웹 방화벽(WAF)을 통해 공격을 막을지 등의 옵션이다.
WAF 는 추가 비용이 발생할 수 있기에 상황에 따라 적절하게 선택하면 된다!

이후 지금까지 적용한 옵션들을 확인한 후 create 를 완료하면 된다.
생성 후 S3 버킷 정책을 업데이트 해야한다는 경고가 뜰 것이다. 그때 정책을 복사하여 이전에 생성한 S3로 이동한다.
그럼 이제 cloudfront 에 배포된 URI로 접근을 할 수 있다
배포 후 다른 페이지가 안 들어가진다면 오류 페이지 설정해볼 것.

cloudfront → 배포 → 오류 페이지에 들어간 다음, 사용자 정의 오류 응답 생성

지금까지는 웹 호스팅을 위한(정적인 파일을 사용자들이 URL을 통해 접근할 수 있도록) 작업이었다. 아래는 우리가 원하는 도메인으로 연결할 수 있는 과정이다
도메인 연결 과정에 대한 과정은 아래 포스팅을 참고하면 좋을 것 같고, 이후 내용에서는 내부에서 사용되는 용어나 과정이 왜 필요한지를 간단하게 알아가고자 한다.
TODO : 도메인 연결 .. 토요일안에 채우겟습니다 ..
도메인 연결 과정에 대한 과정은 아래 포스팅을 참고하면 좋을 것 같고, 이후 내용에서는 내부에서 사용되는 용어나 과정이 왜 필요한지를 간단하게 알아가고자 한다.
본인이 원하는 도메인을 구매한 후 (구매처는 자유) Route 53을 생성해야한다.
Route 53은 특정 도메인을 CloudFront 주소로 연결해주는 DNS 역할을 위한 것이다.
Route53 에서 호스팅 영역을 만들면 DNS 레코드 관리가 가능하다.
여기서 DNS 레코드는 도메인 이름과 실제 리소스(IP, 다른 도메인 등)를 연결하는 규칙이라고 볼 수 있다.
| 레코드 | 의미 | 연결 대상 | 예시 |
| A | IPv4 주소로 연결 | 123.45.67.89 | example.com → 123.45.67.89 |
| AAAA | IPv6 주소로 연결 | 2001:db8::1 | example.com → 2001:db8::1 |
| CNAME | 다른 도메인으로 연결 | target.example.com | www.example.com → example.com |
| NS |
HTTPS를 적용하기 위해서는 SSL 인증서가 필요하다.
ACM은 AWS에서 인증서를 만들고 CloudFront에 적용할 수 있도록 해준다
[사용자의 브라우저]│example.com 요청│┌──── DNS 조회 ────┐│ 로컬 캐시 확인 │ (최근 방문했으면 DNS 캐시 사용)│ 없으면 ISP DNS │ → 루트 DNS → TLD DNS → NS 조회└──────────────────┘│Route 53 (Hosted Zone
(이 도메인에 대한 설정은 Route 53이 가지고 있음을 알려줌)
→ CloudFront 배포 주소 반환
웹서버의 역할을 해주기 위해 정적 웹사이트 호스팅을 추가해준다.


오류 문서 - index.html 동일하게 작성 (안 하면 cloudFront 에서 따로 해야함)

아래 옵션들은 기본 세팅값을 유지하면 된다.

아래 버킷 정책에 편집을 눌러 복사된 정책을 추가해준다.

완료되었는데 혹~시 확인해보고 CORS 정책 관련 에러 뜬다
| Name Server 정보 (DNS 관리 위치) |
| DNS 서버 주소 |
| ns-123.awsdns-45.com |
| MX | 이메일 서버 정보 | 메일 서버 | gmail.com mail server |
| TXT | 텍스트 정보 (검증, 보안 설정 등) | 문자열 | google-site-verification=... |