올해 1분기 쯔음 회사에서 처음으로 배포한 Nuxt 프로젝트가 있었다.
서비스가 운영 단계로 들어서면서 소스 수정이 매우 잦아졌고,
배포 과정마다 사용자는 정상적으로 서비스를 사용하기 어려운 상황을 마주했다.
원인을 분석한 결과, build 과정 초기에 .nuxt
폴더에 있는 번들파일을 제거하기 때문에
사용자들이 번들파일을 내려받지 못하여 벌어진 현상이었다.
아래는 nuxt로 만든 toy project로 재연해보았다.
SSR 서버는 살아있지만, script가 작동하지 않고 번들파일 요청이 404로 응답된 것을 볼 수 있다.
처음엔 "pm2를 사용하면 무중단에 대해 고민할 게 더 없는 게 아닌가 ?"라는 생각이 들었다.
하지만 단순히 번들파일을 내려받지 못해서 벌어진 현상이란 것을 인지하고 난 후엔,
build 과정 개선에 대해 조금 더 집중하게 되었다.
하지만, nuxt와 무중단 배포에 대해 리소스가 풍부하지 않아서 순탄치는 않았다.
솔직히 너무 구리다.
3년전 SPA 개발하던 시절에 사용하던 방식이다.
당장 해결할 수는 있지만, 장기적으로는 오히려 비용이 더 든다고 판단하여 다른 방법을 찾기로 하였다.
레퍼런스를 찾지는 못했다. 당시 deploy와 연관된 것들을 떠올리던 중
결국, deploy 시점에는 release 브랜치로의 commit이 최소 1회 발생한다는 점을 떠올렸다.
이어 "그렇다면, build directory path를 dynamic하게 부여하고 pm2에서 교체하면 되지 않을까?"라는 생각이 들었었다.
아니나 다를까 nuxt config는 build directory path를 직접 지정할 수 있었다.
정말 다행이라는 생각이 들었던 것 같다.
nuxt build
와 nuxt start
는 매 시점 nuxt.config.js
파일의 script를 실행한다.
따라서 두 시점에 현재 git HEAD revision만 가져오기만 하면 됐었다.
단순히 fs
를 사용하여 revision을 가져오고,
buildDir에만 넣어주면 됐었다.
POC를 진행한 결과, 생각보다 잘 동작했다.
하지만, 여기서 또 다른 이슈가 발생했었는데,
서비스를 이용하던 사용자는 새로고침을 할 때까지 이전 버전의 서비스를 사용한다는 점이었다.
특히 점검 공지 등을 FrondEnd에서 처리하고 있었기 때문에 버전 통합은 중요했었다.
이번엔 nuxt server-middleware의 힘을 빌리기로 하였다.
사실 이번에도 별게 없었다.
단순히 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를 적용하고 제대로 동작하는 것을 지켜봤을 때,
꽤 성취감이 느껴졌었다.
출근 시간이 다가와 도식화와 함께 빠르게 글을 맺어보겠다.