Javascript/토이프로젝트

웹 버전 관리 시스템

강디너 2021. 2. 17. 16:49
728x90

웹 버전 관리 시스템

 

만든 이유는 단 한 가지입니다.

 

사용자가 이전 버전의 API 규격, 데이터 타입으로 호출해서 오류가 생기기 시작

 

웹 서비스 배포 후, 사용자가 이전 버전의 웹을 보고 있어서 이슈 발생하는 것을 감지했습니다.

딱 담당하고 있던 프로젝트 두 개에서 같은 이유로 이슈가 발생해서 해결해야 한다고 생각했습니다.

 

우선 원인은 SPA 특성 때문에 발생한 이슈였습니다.

생각지 못한 곳에서 나타났는데, SPA의 장단점은 여러 가지 있지만 그중 크게 중요하지 않게 생각 한 문장이 있습니다.

 

처음 접속했을 때 웹에 필요한 대부분의 리소스를 다운로드하기 때문에 초기 로딩 속도가 느리다.
하지만 그 후 필요한 리소스만 다운로드하기 때문에 빠른 렌더링이 가능하다.

 

"초기 로딩 속도가 느리다"라는 것만 이슈하고 생각 후 Lazy loading를 사용해서 조금이라도 빠르게 할 생각만 했었지, 처음 접속했을 때 리소스를 "한 번에 대부분의 리소스를 다운로드" 한다라는 문장은 크게 중요하지 않게 봤었습니다. 

 

하지만 필요한 부분만 새로 렌더링 한다는 장점 때문에 잊힌 "한 번만 로드"하는 방식은 저에게 치명적으로 다가왔습니다.

 

 

이 문제를 해결하기 위해서 여러 가지 관련 레퍼런스를 보고, 방법을 생각해봤습니다.

  1. 백엔드 API Response에 version을 넣어서 API 통신 때마다 체크
  2. Firebase Cloude Messaging
  3. Firebase Remote Config
  4. Socket.io를 이용한 실시간 통신

1. 백엔드 API Response에 version을 넣어서 API 통신 때마다 체크하는 방법

Access Token을 백엔드에서 체크하는 것처럼, 프론트에서 버전을 체크하는 방법입니다.

백엔드 API Response 규격에 version을 추가하고, response version과 프론트에서 저장한 버전을 체크 후 다르면 새로고침 해달라고 요청하는 것이었습니다.

 

예를 들어 DB에 web_version 컬럼을 만들어 500 이란 값을 저장합니다. 그 후 API 쏠 때마다 web_version을 meta에 넣어 보내줍니다. 웹에서도 500이란 값을 저장해서 체크합니다. 서로 500 이란 값이 맞다면 정상 작동합니다.

웹에서 다음 버전으로 배포를 하면 version을 501이란 값으로 수정하고, 백엔드에 501 이란 값을 보내달라고 요청합니다. 그러면 이전 버전 500인 웹은 version이 다르기 때문에 새로고침을 해달라는 alert이 뜰 것입니다.

 

좌: 컨플루언스 새로고침 요청 이미지 / 우: Flex 버전 데이터

이 방식에 대해 파트 내에서 대화를 나눴는데, 백엔드에 요청하지 않고 우리끼리 할 수 있는 방법으로 한번 더 찾아보자 해서 이 방식이 나쁘진 않지만 더 해보기로 했습니다.

 

 

2. Firebase Cloude Messaging

Firebase Cloude Messaging 관련해서 토이로 몇 번 해본 경험이 있어서 테스트하기 편했습니다.

 

장점으로는 프론트에서 구성만 하면 백엔드 요청 없이 실시간으로 푸시를 날릴 수 있습니다. 단점으로는 사용자가 알림 설정을 필수적으로 허용해야지만 메시지를 보낼 수 있다는 것이었습니다.

만약 사용자가 거부한다면 소용없는 서비스가 돼버리기 때문에 바로 기각하였습니다.

 

 

3. Firebase Remote Config

모바일 팀에서 Firebase Remote Config를 자주 바뀌는 팝업 또는 긴급 점검 때 사용한다고 얼핏 들었는데, 버전 관리도 긴급하게(?)

바꾸는 것이니 비슷한 유형일 것이라 생각해서 사용해봤다.

 

Firebase 설정에서 변수에 대한 값을 미리 설정해놓으면 준비는 끝이다. 

const remoteConfig = firebase.remoteConfig();
remoteConfig.fetchAndActivate()
  .then(() => {
  // ...
  const val = remoteConfig.getValue('version');
  console.log('version : ', val);
});

위와 같은 방식으로 Firebase 콘솔에 있는 데이터를 불러서 체크할 수 있다.

만약 Remote Config를 사용한다면, axios를 쏠 때마다 or 다른 페이지를 이동할 때마다 or 전부 일 때 체크해야 하기 때문에 비용이 많이 들 것 같아서 킵해뒀습니다.

 

 

4. Socket.io를 이용한 실시간 통신

"이것도 고민, 저것도 고민이라면 간단한 소켓 서버를 만들어서 운영해볼까"라는 생각이 들었습니다.

 

소켓으로 여러 개의 프로젝트에 연결할 수 있고, 내가 원할 때 알림을 보낼 수 있기 때문에 지금 이슈 상황에 딱 어울린다고 생각했습니다.

상호 통신할 필요는 없고, 소켓 서버에서 프로젝트들한테 알림만 주면 되는 기능이 필요했습니다.

 

처음에 파트에 이러한 생각을 공유하고, Vue.js+ Express.js를 이용해서 간단하게 만들어 보겠다고 했습니다. 하지만 어차피 새로운 도전이니 Nuxt.js를 이용해서 서버사이드로 소켓 서버를 만들어 보라고 제안하셔서, "재미있겠군..." 하고 바로 관련 레퍼런스를 찾아보면서 개발을 시작했습니다. 

개발하면서 깨달은 것은 Nuxt.js를 이용하면 구성한 앱 내 클라이언트에서 소켓을 이용해서 통신하는 것이고, 그 외의 방법은 nuxt config를 수정해서 socket.io처럼 만들어서 사용하는 것이었습니다. nuxt를 socket.io처럼 바꿔서 사용하느니 그냥 express 서버에 소켓 설정하는 것이 맞겠다는 생각이 들었습니다. 그리고 소켓 만드는 토이 프로젝트도 정식 프로젝트 중간에 몰래(?) 하는 거라 프로젝트가 끝나기 전에는 테스트까지 완벽하게 해내야 했기 때문에 빠르게 접었습니다. 

 

그래도 길이 정해지니 빠르게 진행되었습니다.

소켓 서버에는 로그인 페이지, 각각의 프로젝트에 알림을 보낼 수 있는 페이지를 만들었습니다.

 

로그인 페이지에서는 딜리셔스 회사 도메인으로만 로그인 가능한 기능을 만들고(firebase auth)

프로젝트에 알림을 보낼 수 있는 페이지에는 프로젝트당 버튼이 두 개입니다.

한 개는 사용자에게 알림을 주는 버튼, 다른 한 개는 급할 때 강제 새로고침용 버튼 한 개를 만들었습니다.

 

그리고 API를 만들어서 강제인지, 아닌지 체크합니다.

 

강제 업데이트가 아니라면 아래 디자인과 같이 알림을 보여줍니다.

socket.on('--------', (data) => {
  if (data.contents.isForce) {
    window.location.reload();
    return;
  }
  showVersionCheck();
});

 

개발은 완료되었는데, "만약 사용자가 엄청 많아져서 소켓 서버가 감당하지 못해 터져 버리면 어떡하지?"라는 생각이 들어서 불안했습니다.

그래서 바로 전문가인 백엔드분들과 데브옵스분들께 상황은 이런데 서버 구성이나, 배포를 어떻게 해야 하는지 탁구 치면서(응?) 물어봤습니다. 

 

  1. 만든 버전 관리 프로젝트를 Dockerfile로 빌드할 수 있도록 준비한다.
  2. AWS Elastic Container Registry(ECR)에 내가 만든 Docker image를 올린다.
  3. AWS Elastic Beanstalk(EB)를 이용하여 배포
  4. AWS Auto Scaling을 이용하여 사용자가 많이 들어와도 서버가 터지지 않도록 대비

 

dockerfile에 대한 개념과, AWS에 대해서 많이 공부해 볼 수 있는 기회가 돼서 매우 좋았는데 힘들었습니다 ㅠ.(매우 바빴다)

배포하는 과정이 까다롭다고 느껴졌는데, 파일을 수정하면 AWS 보안 키(몇 시간 후 만료됨)를 받고, ECR에 올리고, EB에 배포하는 과정이 매우 복잡했습니다. 나중에는 데브옵스 파트원분이 젠킨스를 이용해서 한 번에 쭈루루루루룩 빌드될 수 있도록 만들어주셔서 허탈+감사했습니다.(역시 전문가분들은 다르다)

 

이렇게 배포까지 완료되고 현재는 서비스를 잘 이용하고 있습니다.

아직은 수동으로 버튼을 눌러줘야 하지만 이것도 아이디어가 생기면 다른 방법으로도 언제든 바뀔 수 있을 것 같습니다.

 

반응형