본문 바로가기
빌드 및 배포

프론트엔드 배포 일지

by iborymagic 2021. 8. 24.

언젠가는 배포에 관해 공부를 해야겠다고 생각을 했다.

하지만 자바스크립트 공부만 하기에도 너무 바빴기 때문에 미루고 미뤘었다.

만약 우테코가 아니었더라면 취업하기 전에 배포를 직접 만져볼 일이 있었을까 싶기도 하고.

맨 땅에 했더라면 굉장히 막막했을텐데, 같은 팀의 백엔드 크루인 '현구막'이 도와줘서 수월하게 마칠 수 있었다.

이전 포스팅에서도 말했듯이, 디스코드로 화면공유하면서 거의 아바타 소개팅처럼 배포를 진행했던 기억.

 

기존에는 배포할 일이 있어도 조그마한 토이프로젝트 정도였기 때문에 그냥 Netlify에다가 올려놓기만 했었다.

그래서 이번에도 처음에는 빠른 배포를 위해 Netlify를 사용했는데, 프로젝트의 사이즈가 점점 커지기도 하고

캐싱, 최적화 세팅 등등 설정할 세부사항들이 많아져서 마냥 Netlify에만 기대기에는 애매해져버렸다.

이미 백엔드가 AWS를 통해 배포를 진행해놨기 때문에 배포 환경을 AWS로 통합하는 것이 관리하기도 편하고.

이런저런 이유로 인해 S3와 Cloudfront를 이용해서 배포를 하기로 결정이 났던 것.


Netlify를 통한 배포

처음에는 빠른 배포를 위해 Netlify를 이용해서 배포를 진행했다.

기획 관련 회의로 시간을 엄청 많이 사용했고, 나머지 시간에는 코드를 짜기에도 시간이 부족했기 때문에

그냥 익숙하게 슉슉 배포할 수 있는 Netlify를 이용하는 편이 좋겠다고 결론이 났다.

 

사실 Netlify는 워낙 사용법이 쉽기도 하고 해서 따로 언급할 만한 내용이 없지만, 

혹시나 도움이 될 수도 있으니 Publish directory에 관해 문제를 겪은 경험을 잠깐만 언급하고 넘어가야겠다.

복잡한 문제는 아니고. Netlify를 통해 성공적으로 배포를 끝내고, 지정된 도메인으로 접속을 했는데 

웬걸, HTML 파일에 자바스크립트 파일이 주입되지 않아서 빈 HTML 파일만 열리는 현상이 발생했다.

 

나중에 알고보니 Publish directory를 잘못 설정해줘서 생긴 문제인 것 같았다.

Base directory를 '/front'로 설정했는데, Netlify를 이용하면 어차피 우리 로컬 디렉토리에 

빌드 파일이 직접 생성되는 게 아니기 때문에 그냥 괜찮겠지 하고 Publish directory도 '/front'로 해버린 것.

그것 때문에 발생한 현상이었다.

 

결과적으로 보자면, Publish directory는 Base directory에 포함되어있는 별도의 폴더여야 하는 것 같다.

Publish directory를 '/front'에서 '/front/dist'로 변경하니 정상적으로 배포가 되었다.

참고로, 로컬 디렉토리에 해당 폴더가 존재해야 정상적으로 배포가 될 수 있다.

Publish directory를 '/front/dist'에서 '/front/publish'로 변경했더니 정상적으로 배포가 되지 않았다.

우리 로컬 디렉토리에는 dist 폴더는 존재하지만 publish 라는 폴더는 존재하지 않았기 때문.

 

반드시 Publish directory path에 Base directory가 포함되어있어야 한다는 사실을 잊지마시라.

(Base directory가 '/front'라면 Publish directory는 '/front/abcd' 이런 식으로 prefix처럼 앞에 명시되어 있어야한다)


S3란?

S3는 '확장성, 데이터 가용성 및 보안과 성능을 제공하는 객체 스토리지 서비스' 이다. (공식문서 소개.. ㅎ)

S3는 'Amazon Simple Storage Service'의 줄임말.

정적 컨텐츠 저장소라서 static이 들어가는 줄 알았더만..

모든 종류의 데이터를 객체 형태로 무한정으로 저장할 수 있고, 저장 및 검색에 사용되는 고유한 key를 제공한다.

보안도 뛰어나다. 

 

버킷이라는 공간에 정적 컨텐츠를 저장하는데,

버킷이 자동으로 확대/축소되어 저장 공간을 미리 계산할 필요가 없다.

객체가 파일이라면, 버킷은 파일들을 모아놓은 하나의 최상위 디렉토리라고 생각하면 된다.

서버를 따로 설치하지 않으므로 서버 관리를 고려할 필요가 없고,

서버를 사용하더라도 서버가 정적 컨텐츠 요청 처리를 하지 않아도 되므로 비용 부담을 덜 수 있다.

 

쉽게 말하자면, S3는 정적 웹페이지나 이미지, 동영상 같은 컨텐츠를

무한정으로 저장할 수 있는 '거대한 하드디스크' 라고 생각하면 된다.

 

데이터 저장뿐만 아니라, S3에서는 '정적 웹 사이트 호스팅' 기능도 제공한다. 

S3 버킷을 웹 사이트 호스팅용으로 설정하고나서

HTML, CSS, JS 파일들을 버킷에 업로드하면 S3로 정적 웹 사이트를 호스팅하는 것이 가능하다.

우리가 사용한 기능은 정확히는 이 '정적 웹 사이트 호스팅' 기능.

 

왜 Netlify를 버렸나?

일단, Netlify에서 S3로 갈아타게 된 이유를 먼저 간단하게 설명하자면.

사실 Netlify는 간단한 설정만으로도 배포를 할 수 있게 만들어 놓은 SaaS 서비스이다.

간단한 설정만으로도 배포가 가능하다는 건, Netlify 측에서 사전에 설정해 놓은 부분이 많다는 것이겠지.

이러한 부분들이 처음에는 편하게 느껴질 수 있지만,

뭔가를 세부적으로 건드리려고 하는 순간 상당한 불편함으로 다가온다.

 

가장 대표적으로 느낀 부분은 최적화.

Netlify에서는 별도로 설정하지 않아도 JS, CSS, 그리고 몇몇 이미지 포맷들을 최적화하게끔 해놨는데,

이러한 최적화는 별도의 CDN에서 수행하기 때문에 우리가 따로 설정해줄 순 없는 것으로 알고있다.

(심지어 JS 코드가 let과 const를 포함하면 압축 로직이 제대로 동작하지 않는 버그도 있다고..)

 

반면 AWS는 IaaS 서비스이기 때문에 높은 자유도를 자랑하며, 그만큼 높은 학습량을 요구한다.

공부가 많이 필요하긴 했지만 전반적인 네트워크와 인프라 지식들을 학습할 수 있는 기회였고

Netlify의 제한적인 옵션들에 차츰 불편함을 느끼기 시작하던 터라 AWS로의 이전을 결정하게 되었다.

(그리고 Netlify의 세부 설정에 관해 구글링해봐도 자료가 충분하지 않다는 점도 한 몫 했다)

 

왜 S3를 선택했나?

사실 우리의 고려 대상이었던 아마존 서비스는 S3와 EC2 두 가지였는데,

EC2에 관해서도 잠깐 간단하게 알아보고 넘어가자.

EC2는 'Elastic Computing Cloud'의 줄임말. 클라우드 웹 서비스 솔루션이다.

주로 어플리케이션을 호스팅할 때 사용한다.

S3가 거대한 하드디스크라면, EC2는 하나의 '거대한 컴퓨터'를 빌린다고 생각하면 된다.

EC2에다가 특정 소프트웨어나 어플리케이션을 설치하여 실행시킬 수 있다.

 

EC2는 말 그대로 컴퓨터를 하나 통째로 빌리는 것이므로 서버를 밑바닥부터 설정할 수 있다.

그래서 보통 서버 로직을 올려놓고 구동시켜놓는 서버 컴퓨터 용도로 사용한다.

 

프론트 서버를 EC2로 구성하는 경우는 대개 프론트 서버에서 로직을 돌려야 하는 경우, 즉 SSR이다.

SSR(Server-side rendering)을 위해서는 렌더링 로직을 프론트 서버에서 구동하여

그 결과로 생성되는 HTML 파일과 JS 파일을 클라이언트(브라우저)에게 전송해야 하기 때문.

 

우리 프로젝트의 경우는 CSR(Client-side rendering)을 사용하는 SPA이기 때문에 

프론트 서버는 그저 정적인 파일(HTML, CSS, JS)을 전달해주는 용도로만 사용된다. 로직이 전혀 필요없다.

그래서 굳이 불필요한 비용을 들여가며 서버를 EC2로 구성할 필요가 없었고,

S3의 정적 웹 사이트 호스팅 서비스를 이용하는 것만으로 충분할 거라고 판단했다.

 

Cloudfront는 뭔데?

Cloudfront에 관해 알아보기 전에, 먼저 CDN이 무엇인지에 대해 알아보자.

CDN(Content Delivery Network)은 사용자에게 웹 컨텐츠를 효율적으로 제공하기 위한 서버의 분산 네트워크이다.

서버와 사용자 사이의 물리적 거리를 줄여 웹 페이지 컨텐츠 로드 지연(컨텐츠 로딩 시간)을 최소화하는,

촘촘히 분산된 서버로 이루어진 플랫폼이 바로 CDN인 것.

짧게 정리하면, 지리적 제약 없이 전 세계 사용자에게 빠르게 컨텐츠를 전송할 수 있도록 만들어주는 기술이다.

Cloudflare의 CDN 설명도

만약 원본 서버가 미국에 있다고 치자. 

이 때 한국에 있는 사용자건 프랑스에 있는 사용자건 관계 없이

전부 미국에 있는 서버를 이용하면 컨텐츠를 전송받는 속도가 겁나 느리겠지.

그래서 전 세계 각지에 캐시 서버를 둬서, 한국 사용자에게는 한국에 있는 캐시 서버가 정보를 보내고

프랑스 사용자에게는 프랑스에 있는 캐시 서버가 정보를 보내게끔 만들어주는 것. 

→ 사용자와 가장 가까운 캐시 서버에서 정보를 전송하여 로딩 시간을 최소화한다.

 

Cloudfront는 정적, 동적 컨텐츠를 빠르게 응답하기 위한 캐시 기능을 제공하는 AWS의 CDN 서비스이다.

캐싱을 지원하기 때문에 S3에 있는 컨텐츠를 직접 접근하지 않아도 된다.

이는 곧 S3의 비용이 감소함을 의미하고, 응답 속도가 더 빨라진다는 것을 의미한다.

(반면 자주 바뀌는 파일들은 캐싱을 사용하는 Cloudfront와 어울리지 않는다)

또 중요한 컨텐츠, 데이터, 코드 및 인프라에 대한 보안을 강화해준다.

그래서 대부분의 경우에 S3와 Cloudfront를 함께 사용하여 효율성을 극대화한다.

 

 요약

CSR을 이용하기 때문에 프론트서버에 로직이 필요없었다.

그래서 별도의 웹서버 없이 효율적으로 웹페이지를 호스팅할 수 있는 S3를 선택했다.

Cloudfront S3에 성능과 보안을 더해주기 위해 함께 사용하는 걸로 결론지었다.


S3를 통한 정적 파일 배포

일단, IAM에서 권한을 설정하는 건 완료된 상태라고 가정.

또, AWS CLI 설치 및 유저 추가까지 전부 되어 있는 상태에서 시작한다.

(백엔드 크루들이 미리 백엔드 배포하면서 다 해놔가지고.. ㅎ)

 

S3 버킷 생성하기

1. 우선 S3 콘솔로 들어가서 우측 상단에 있는 '버킷 만들기' 버튼을 누른다.

2. 버킷 이름과 리전(지역)을 입력한다. (한국이니까 지역은 당연히 서울)

그 다음, 모든 퍼블릭 액세스를 차단하도록 설정해준다. 

그러면 이 버킷은 S3 웹 콘솔로만 접근할 수 있고, 그 외의 모든 접근은 차단된다.

(사실 이 설정은 퍼블릭 오픈을 금지한다는 프로젝트의 제한사항 때문이기도 하다.

정적 리소스 읽기 작업은 오직 Cloudfront를 통해서만 가능하게끔 하라는 규칙.)

3. 버킷 버전 관리는 Git을 생각하면 편하다. 

버킷 버전 관리를 사용하면, 실수로 삭제하거나 덮어쓴 객체를 복구할 수 있게 된다.

하지만 우리는 이미 Git을 통해 리소스를 관리하고 있기 때문에 불필요하다고 판단되어 비활성화했다.

4. 말 그대로 버킷에 태그를 달아주겠냐 하는 것.

각 버킷을 구별하기 위해 부가적인 설명을 달아놓을 필요가 있을 때 태그를 달아준다고 생각하면 편하다.

우리 프로젝트는 S3를 몇 개 안쓰기 때문에 이들을 구별하기 위해 별도의 태그가 필요하진 않았다.

5. 설명에 나와있는대로, 버킷에 저장되는 새 객체에 암호화를 해주겠냐 하는 것.

우리는 어차피 퍼블릭 액세스를 차단해놓았기 때문에 별도의 암호화가 필요하지 않다고 생각했다. 

여기까지 설정을 마치고, '버킷 만들기' 버튼을 눌러서 버킷을 생성하면 된다.

 

OAI 키 발급하기

S3에 Cloudfront를 연결해서 Cloudfront URL을 통해 리소스에 접근하려고 해도

S3 입장에서는 S3 웹 콘솔이 아니고 URL을 통해 접근했으므로 퍼블릭 액세스이다.

근데 아까 퍼블릭 액세스를 차단해놨었기 때문에 당연히 파일을 읽을 수가 없다.

그래서 S3가 Cloudfront를 허용하게 해주는 설정이 필요하고, 이를 위해서는 OAI 키를 발급받아야 한다.

 

1. Cloudfront 콘솔의 좌측 햄버거 메뉴를 펼치면 Security 아래에 'Origin access identities'가 보일 것.

이 녀석을 클릭해주자.

2. 여기서 키를 발급해주면 된다.

이름만 입력해주면 키가 바로 발급된다.

 

Cloudfront Distribution 생성하기

1. 마찬가지로, 상단의 Create Distribution 버튼을 클릭한다.

2. Cloudfront는 Origin의 앞단에서 캐싱, 보안 등을 담당하는 친구.

Origin domain에서 방금 생성한 S3를 선택해준다.

3. 그러면 하단에 'S3 bucket access' 라는 항목이 생기는데, 여기서 Yes use OAI를 선택해주면

하단에 Origin access identity, 즉 OAI 키를 선택할 수 있는 공간이 생긴다.

방금 발급받은 OAI 키를 입력해주자.

4. Viewer protocol policy에서 'Redirect HTTP to HTTPS'를 선택한다.

그 이후의 옵션들은 전부 default로 두고 Distribution을 생성하면 된다.

여기까지 설정도 Cloudfront 입장에서의 옵션만 설정해줬을 뿐, S3에서 따로 설정을 해주지 않았다.

S3 쪽에서도 '이러한 키로 접근할테니, 나라는 걸 인식해서 OK 해줘!' 라는 설정을 해줘야 함.

그래야 S3가 Cloudfront를 인식할 수 있게 된다. (Cloudfront를 통한 S3 리소스 접근이 가능하다)

이를 위해선 S3의 버킷 정책을 설정해줘야 한다.

 

S3 버킷 정책 설정

다시 S3 콘솔로 가서 버킷 정책을 설정해주는 곳으로 가보자.

우측 상단에 있는 '편집' 버튼을 눌러서 JSON 형식의 버킷 정책을 입력해줘야 한다.

첫 번째 모자이크(Origin Access Identity 뒤에)에는 아까 발급받았던 OAI의 ID를 입력해주고,

두 번째 모자이크(arn:aws:s3::: 뒤에)에는 현재 접속해 있는 S3 버킷의 이름을 적어주면 된다.

 

여기까지 설정을 마치면, 이제 S3와 Cloudfront가 쌍방으로 연결된 상태이기 때문에

브라우저 → S3 접근은 여전히 안되지만, 브라우저 → Cloudfront → S3 접근은 가능하게 된다.

(즉, Cloudfront를 통한 리소스 읽기가 가능해졌다는 말)

 

배포

이제 정적 웹 사이트 호스팅을 활성화시켜주기만 하면 된다.

(default 값이 비활성화이므로 활성화로 바꿔줘야 함)

이제, S3에 웹 페이지 정적 리소스들(HTML, JS 빌드 파일)을 업로드하고, 

Cloudfront URL을 통해 해당 리소스에 접근하면 정상적으로 웹 페이지에 접속할 수 있다.


개인적으로 이번 배포를 진행하면서,

뭔가 네트워크와 인프라 학습의 포문을 연 듯한 느낌이라 굉장히 유익한 경험이었다.

 

이번 기회를 발판으로, 인프라에 대해 지속적으로 관심을 가지면서

감을 잃지 않도록 노력해야겠다.

댓글