logo

Nuxt 무중단 배포 구현기

nuxt · 2022년 9월 28일 · 11 min read

들어가며

올해 1분기 쯔음 회사에서 처음으로 배포한 Nuxt 프로젝트가 있었다.
서비스가 운영 단계로 들어서면서 소스 수정이 매우 잦아졌고,
배포 과정마다 사용자는 정상적으로 서비스를 사용하기 어려운 상황을 마주했다.

원인을 분석한 결과, build 과정 초기에 .nuxt 폴더에 있는 번들파일을 제거하기 때문에
사용자들이 번들파일을 내려받지 못하여 벌어진 현상이었다.

아래는 nuxt로 만든 toy project로 재연해보았다.
SSR 서버는 살아있지만, script가 작동하지 않고 번들파일 요청이 404로 응답된 것을 볼 수 있다.
ex_screenshot"

고민과정

처음엔 "pm2를 사용하면 무중단에 대해 고민할 게 더 없는 게 아닌가 ?"라는 생각이 들었다.
하지만 단순히 번들파일을 내려받지 못해서 벌어진 현상이란 것을 인지하고 난 후엔,
build 과정 개선에 대해 조금 더 집중하게 되었다.

하지만, nuxt와 무중단 배포에 대해 리소스가 풍부하지 않아서 순탄치는 않았다.

build 결과물을 서버에 업로드

솔직히 너무 구리다.
3년전 SPA 개발하던 시절에 사용하던 방식이다.
당장 해결할 수는 있지만, 장기적으로는 오히려 비용이 더 든다고 판단하여 다른 방법을 찾기로 하였다.

git HEAD revision에 따라 dynamic build directory path 부여

레퍼런스를 찾지는 못했다. 당시 deploy와 연관된 것들을 떠올리던 중
결국, deploy 시점에는 release 브랜치로의 commit이 최소 1회 발생한다는 점을 떠올렸다.
이어 "그렇다면, build directory path를 dynamic하게 부여하고 pm2에서 교체하면 되지 않을까?"라는 생각이 들었었다.
아니나 다를까 nuxt config는 build directory path를 직접 지정할 수 있었다.
정말 다행이라는 생각이 들었던 것 같다.

생각보다 간단한 구현

nuxt buildnuxt start는 매 시점 nuxt.config.js 파일의 script를 실행한다.
따라서 두 시점에 현재 git HEAD revision만 가져오기만 하면 됐었다.

ex_screenshot"
단순히 fs를 사용하여 revision을 가져오고,
ex_screenshot"
buildDir에만 넣어주면 됐었다.

POC를 진행한 결과, 생각보다 잘 동작했다.
하지만, 여기서 또 다른 이슈가 발생했었는데,
서비스를 이용하던 사용자는 새로고침을 할 때까지 이전 버전의 서비스를 사용한다는 점이었다.
특히 점검 공지 등을 FrondEnd에서 처리하고 있었기 때문에 버전 통합은 중요했었다.

이번엔 nuxt server-middleware의 힘을 빌리기로 하였다.
ex_screenshot"
사실 이번에도 별게 없었다.

단순히 runtime process.env.GIT_HEAD에 revision을 넣어두고,
nuxt server-middleware에서 http 요청 시 서버의 현재 revision 값을 내려주는 API만 작성하여 배포하였다.
그리고 Client에서는 router.path를 watch하고 변경될 때마다 해당 API를 호출하여
Client에서 보유하고있는 process.env.GIT_HEAD와 API로부터 전달받은 GIT_HEAD를 비교하고,
만약 일치하지 않다면, 새로고침만 시켜주었다.

polling 방식을 사용할까 싶었는데, 리소스 낭비 같기도 했었고, 사용자의 특정 이벤트가 발생할때만 체크하면 되겠다는 생각에 route 변경 이벤트을 선택했던 것 같다.

결과 정리

구현을 마친 후, 운영 중인 서버에 해당 feature를 적용하고 제대로 동작하는 것을 지켜봤을 때,
꽤 성취감이 느껴졌었다.
출근 시간이 다가와 도식화와 함께 빠르게 글을 맺어보겠다.
ex_screenshot"

관련 포스트
post image
nested router의 기본 route 지정해주기
안녕하세요. 오늘은 제가 올해 초 진행했었던 유튜브 뮤직 클론코딩 프로젝트를 리팩토링하면서.  개선이 필요한 코드를 발견하여 수정한 경험을 공유해보고자 합니다. 해당 페이지에서는 nested router를 통해 재생목록, 앨범, 노래, 아티스트, 구독 탭을 보여주고 있습니다. url에 child route를 입력해주지 않은 경우 탭에서 아무것도 보여주지 않아 리다이렉트를 시켜주고자 했었던 것 같습니다. 여기서 문제는 ... mounted 훅에서 검사를 시켜주고 있었네요 ...
nuxt·2021년 6월 13일·5 min read
post image
Fetch API와 Created Hook에서 API 호출 시 차이
안녕하세요. 오늘은 Nuxt의 Fetch API와 라이프사이클의 Created 훅에서 API를 호출 했을 때 각각의 차이점에 대해 알아보겠습니다. SSR 기준으로, Nuxt 2.12 버전 이후 Fetch API는 Vue instance가 create 된 후에 실행 됩니다. 그러면 created 훅에서 실행하는 편이 더 빠른 API Response를 가져오지 않을까 궁금해졌는데요. 이를 따로 비교한 글을 찾지 못해 제가 직접 한번 테스트를 해보았습니다.
nuxt·2021년 6월 8일·4 min read
post image
Nuxt 프로젝트에서 환경변수 관리하기 (feat. dotenv)
서비스를 배포하실 때 환경별로 포트번호 등이 달라서 소스코드를 직접 수정하셨던 경험이 있으신가요 ? 포트번호와 같이 환경별로 다른 값이나 DB 접속 정보 등 소스코드에 포함되면 안될 중요한 값을 dotenv 패키지를 통해서 환경변수로 관리한다면, 별도의 추가 작업을 할 일이 없으실 겁니다.
nuxt·2021년 4월 7일·10 min read