코로나보드는 일일 최대 200만 뷰를 어떻게 감당할 수 있었을까

[Must Have] 코로나보드로 배우는 실전 웹 서비스 개발
골든래빗 출판사
권영재, 주은진 지음


학습 목표
코로나보드의 프론트엔드와 백엔드의 전체적인 아키텍처를 파악하고, 이러한 아키텍처로 설계된 이유를 알아봅니다.
학습 순서
서비스와 아키텍처
서비스는 웹 또는 앱과 같이 온라인으로 사용자들이 접속하여 글을 올리거나, 필요한 정보를 얻거나, 쇼핑을 하는 등의 행위를 제공해주는 제공자를 말합니다. 제대로 서비스를 제공하려면 아키텍처를 잘 설계하고 적용해야 합니다. 아키텍처는 서비스 요구사항을 만족하는 시스템을 구축하는 전체 시스템 구조와 요소를 통칭합니다.
1.1 한눈에 보는 코로나보드
아키텍처를 파헤쳐보기 전에 코로나보드라는 서비스가 어떻게 생겼고 어떤 정보를 서비스하는지 빠르게 훑어보겠습니다. 코로나보드 사이트는 크게 실시간 상황판, 국가별 현황과, 대한민국 현황, 글로벌 차트, 국내 차트, 예방 수칙, 확진자 동선, 뉴스로 구성되어 있습니다.
실시간 상황판
전 세계와 우리나라의 현황을 한눈에 보여주는 요약 패널입니다. 다음 그림과 같이 ‘확진자 수’, ‘사망자 수’, ‘격리해제 수’, ‘치명률’ 같은 주요 정보를 전 세계와 대한민국으로 나눠 보여줍니다.
국가별 현황과 대한민국 현황
지도 형태의 차트와 표로 구성됩니다. 지도는 지역별(국가나 시도) 확진자 수를 색 농도로 표시해 고위험 지역을 쉽게 구분할 수 있게 했으며, 표는 각 지역의 구체적인 수치를 상세히 보여줍니다. 다음 그림은 대한민국 현황의 모습이며, 국가별 현황(세계 현황)은 대한민국 지도 대신 세계 지도를, 대한민국 시도 대신 각 국가별 수치를 보여줍니다.
글로벌 차트
전 세계 코로나 확진자 추이와, 확진자 추이를 보고 싶은 국가를 선택하여 비교하는 차트를 보여줍니다. 전 세계 확진자 숫자의 단위가 100만 명 이상으로 증가함에 따라 큰 단위의 숫자에 대한 증가 추세를 더 직관적으로 볼 수 있는 로그스케일 그래프도 보여줍니다.
국내 차트
국내 차트에서는 질병관리청에서 제공하는 데이터를 기반으로 확진자 추이를 보여주는 누적/일별/월별 차트를 제공합니다. 관심사에 따라 지역별, 성별, 연령별 등 방식을 선택할 수 있습니다.
예방 수칙
코로나19 확산 초기에는 국민들이 어떻게 하면 코로나 전파를 최소화할 수 있는지 알지 못했습니다. 그래서 이를 지속적으로 알리고자 질병관리청에서 배포하는 예방 수칙 포스터를 가져다가 모바일에서 보기 편하게 보여줍니다.
확진자 동선
코로나19 확산 초기에는 확진자 수가 적기도 했고, 정보를 투명하게 공개해야 국민들의 불안감을 최대한 덜어줄 수 있다는 취지로 확진자 한 명 한 명의 동선을 공개했습니다. 하지만 개인정보 침해 소지가 있어서 얼마 후 공개 범위가 축소됐습니다. 또한 전국적으로 확진자 숫자가 크게 늘어 모든 확진자 동선을 한 화면에 보여줄 수 없게 되었습니다. 현재는 각 지자체 페이지로 가는 링크 목록만을 제공합니다.
1.2 개발 언어 선택하기
코로나보드 개발에는 프론트엔드와 백엔드 모두 자바스크립트를 사용했습니다. 이유는 크게 다음과 같습니다.
  1. 빠른 개발 속도
  2. 웹 프론트엔드 개발 시 자바스크립트는 필수이기 때문
  3. 프론트엔드와 백엔드에서 같은 언어 사용 시의 여러 장점
코로나보드 개발을 시작할 당시 가장 중요하게 생각한 부분은 빠른 개발이었습니다. 개발을 시작한 2020년 1월 말에 이미 중국에서는 하루에 수백 명씩 확진자가 나오고, 한국에서도 확진자 4명이 나온 상황이어서 정보를 수집해서 보여주는 서비스가 시급해보였죠. 빠르게 개발하여 배포해야 했기 때문에 심사가 까다롭고, 개발하는 데 상대적으로 오래 걸리는 앱을 포기하고 웹사이트로 만들었습니다.
웹 프론트엔드 개발에 자바스크립트는 필수입니다. 자연스럽게 프론트엔드 개발 언어로 자바스크립트를 택했습니다. 국내외 여러 소스로부터 데이터를 수집하는 웹 크롤러crawler와, 수집된 데이터를 저장하고 변환해서 정보를 제공하는 백엔드에도 자바스크립트를 사용하기로 했습니다. 프론트엔드와 백엔드에서 같은 언어를 사용하면 둘 모두에서 사용되는 공통 코드를 한 번만 작성하면 된다는 장점이 있습니다. 게다가 웹에서 데이터 교환에 가장 대중적으로 사용하는 JSONJavaScript Object Notation 형식을 별다른 설정이나 라이브러리 없이도 바로 사용할 수 있어 개발 시간이 크게 단축되죠. 또한 자바스크립트는 이름 그대로 스크립트 언어이다 보니 컴파일할 필요 없이 코드를 바로 실행해볼 수 있다는 점도 매력적입니다. 마지막으로 함수/클래스 등을 작성할 때 변수에 타입type을 일일이 지정할 필요가 없는 동적 타입dynamically typed 언어라서, 자바나 코틀린 같은 정적 타입statically typed 언어에 비해 코드 작성량이 적어서 초기 생산성이 높습니다. 이러한 장점 덕에 자바스크립트는 소규모 웹사이트를 빠르게 만들어서 출시하는 데 적합합니다.
Note
물론 정적 타입 시스템(static type system)을 가진 자바나 코틀린은 코드 가독성이 더 좋고 컴파일타임에 타입 안전성(type safety)을 보장해주기 때문에 런타임(runtime) 오류의 발생 가능성을 그만큼 줄여주는 장점이 있습니다. 참여 개발자가 많거나 코드 규모가 커질수록 스크립트 언어보다는 컴파일 언어의 효용이 더 커지는 점도 알아두면 좋습니다.
1.3 프론트엔드 선택하기
코로나보드의 웹 프론트엔드는 개츠비gatsby 기반의 정적 웹사이트로 만들었습니다. 정적 웹사이트는 페이지를 빌드하는 시점에 필요한 데이터를 가져와서 페이지에 미리 채워넣습니다. 이렇게 빌드된 정적 웹페이지는 서버에서 동적으로 데이터를 가져올 필요가 없으니 속도가 빠른 데다 애플리케이션 서버 비용을 대폭 절감할 수 있습니다. 반면 동적 웹사이트는 페이지가 로드될 때마다 서버에서 동적으로 데이터를 가져옵니다(정적 웹사이트는 5장 ‘웹사이트 UI 구성하기 : 개츠비’ 참조).
코로나보드는 기본적으로 정적 웹사이트이지만 웹페이지 용량을 줄여 초기 로딩 속도를 높일 목적으로 일부 데이터에 동적인 방식을 사용합니다. 예를 들어 ‘국가별 누적 추이’ 그래프는 사용자가 특정 국가를 선택하는 시점에 선택된 국가의 통계 정보를 다운로드하여 그래프에 표시합니다.
데스크톱과 모바일에 동시 대응하고자 부트스트랩Bootstrap의 그리드 시스템을 이용해서 반응형 디자인으로 구현했습니다. 그 외에도 부트스트랩에서 기본 제공하는 버튼, 카드, 모달 다이얼로그 modal dialog, 툴팁tooltip 등의 UI 구성요소들을 CSS만 조금씩 수정해서 사용했습니다(6장 ‘반응형 웹 디자인하기 : 부트스트랩’ 참조).
1.4 백엔드 설계하기
코로나보드의 백엔드는 다음 그림처럼 구성되어 있습니다.
각 요소의 역할은 다음과 같습니다(클라이언트와 가까운 순서로).
  1. 클라우드플레어 : 클라이언트의 모든 요청은 CDN 서비스인 클라우드플레어를 통해서 웹 서버로 보내집니다. 이렇게 하면 클라우드플레어에서 제공하는 다양한 기능을 사용할 수 있습니다.
  2. 웹 서버 및 정적 페이지 저장소 : 클라이언트의 요청에 맞게 정적 페이지 저장소에 저장된 페이지를 보여줍니다.
  3. 정적 페이지 빌더 : 데이터 저장소의 최신 정보를 토대로 정적 페이지를 만들어 정적 페이지 저장소에 올립니다(20분 주기)
  4. API 서버 : 데이터저장소에 필요한 통계 정보를 업데이트하고 원하는 형태로 조회할 수 있게 해줍니다.
  5. 데이터 저장소 : 코로나보드에 보여줄 다양한 정보를 저장합니다.
  6. 크롤러 : 코로나 관련 데이터를 모니터링하다가 변경 사항이 발견되면 API 서버를 통해 데이터 저장소에 반영합니다.
웹페이지가 만들어지는 과정은 이 역순이므로 크롤러부터 자세히 살펴봅시다.
1.4.1 크롤러
크롤러는 자동화된 방법으로 웹페이지 전체 또는 일부를 추출하여 정보를 얻는 컴퓨터 프로그램을 지칭합니다. 코로나보드는 노드JSNode.js 기반으로 작성된 다수의 웹 크롤러를 이용하여 질병관리청 코로나19 웹사이트 및 여러 외국 사이트NHK(nhk.or.jp 일본), The New York Times(nytimes.com, 미국), 丁香园(dxy.cn, 중국), 월드오미터(worldometers.info)에서 지속적으로 데이터를 수집합니다. 이 크롤러들은 AWS EC2 기반의 리눅스 서버에서 실행됩니다. 크롤러는 크론탭으로 스케줄링되어 자동으로 일정 시간마다 데이터를 수집하고 변경 사항이 발견되면 API 서버를 통해 변경된 데이터를 데이터베이스에 저장합니다(자세한 설명은 4장 ‘데이터 자동 수집하기 : 크롤링’, 14.5.2절 ‘크롤러 스케줄링하기’ 참조).
크론탭
유닉스 계열(unix-like) 운영체제에서는 크론cron이라는 스케쥴러를 사용해 주기적으로 특정 명령을 실행할 수 있습니다. 크론탭crontab은 이러한 크론을 실행할 설정을 담고 있는 크론 테이블입니다.
1.4.2 데이터 저장소
데이터 저장소로는 구글 시트와 MySQL을 사용합니다.
구글 시트
구글 시트는 구글이 제공하는 API를 이용하여 스프레드시트 안의 데이터를 불러오거나 업데이트 하는 것이 가능해서 작은 데이터베이스처럼 활용할 수 있습니다. 또한 스프레드시트이다 보니 웹이나 구글 시트 모바일 앱으로 자유롭게 편집할 수 있다는 점도 큰 장점입니다.
코로나보드 서비스 초기에는 아직 코로나 관련 데이터의 소스들이 명확하지 않거나 제공되는 형식이 불규칙한 곳이 많아서 크롤러에 의지하기에는 제약이 많았습니다. 그러다 보니 직접 뉴스를 찾아보거나 사이트를 조회해서 데이터를 일일이 수동으로 입력하는 일이 매우 잦았습니다. 새로운 데이터가 계속 추가되면서 데이터 형식이 달라지는 일도 종종 생겼습니다.
만약 데이터를 MySQL로 관리한다면 MySQL 워크벤치MySQL Workbench 같은 GUI 도구를 이용하거나, 웹 기반 관리 도구를 직접 만들어서 데이터를 편집해야 합니다. 하지만 전자는 모바일에서 사용하기가 힘들고, 후자는 관리 도구를 만드는 데만 꽤 많은 개발 시간이 소요됩니다. 이러한 불편함에서 벗어나기 위해 코로나보드 개발 초기에는 주요 통계 데이터를 구글 시트로 관리했습니다.
다만 구글 시트 API에는 몇 가지 제약이 있습니다. 예를 들어 시트 하나는 최대 500만 개의 셀만 담을 수 있고 100초 동안 최대 100번만 호출할 수 있습니다. 불러오는 속도도 느린 편이죠. 따라서 데이터양이 많다거나, 자주 불러와야 한다거나 혹은 응답 속도가 빨라야 할 때는 적합하지 않습니다(자세한 설명은 3장 ‘저장소 구축하기 : 구글 시트’ 참조).
MySQL
코로나19 팬데믹이 장기화됨에 따라 관리하는 데이터양이 많아져 시트 크기 제한에 도달했습니다. 그래서 일부 데이터를 MySQL로 이전했습니다. 구글 시트에서 직접 편집하는 편리함은 사라졌지만, 오직 코드로만 데이터를 불러오고 업데이트하다 보니 개발자 입장에서는 오히려 더 편하고 안정적이었습니다(자세한 설명은 2.4절 ‘데이터베이스 준비하기 : MySQL’ 참조).
1.4.3 API 서버
노드JS로 만든 간단한 웹 애플리케이션 서버입니다. 크롤러가 수집한 각종 정보를 API 서버를 통해 데이터 저장소에 저장합니다. 정적 페이지 빌더는 빌드 시점에 API 서버를 통해 페이지에 필요한 데이터를 조회합니다(자세한 설명은 2장 ‘API 서버 만들기’ 참조).
1.4.4 정적 페이지 빌더
코로나보드는 프론트엔드가 매번 백엔드 API를 호출하여 데이터를 받아오지는 않습니다. 데이터 저장소에서 데이터를 가져와서 미리 만들어둔 HTML 템플릿 파일에 주입하여 정적인 HTML 파일을 만들어두고, 클라이언트가 요청하면 이 HTML을 보내줍니다. 이런 방식을 정적 웹페이지static web page라고 합니다.
정적 웹페이지를 만드는 데 최적화된 개츠비가 바로 이 정적 페이지 빌더 역할을 합니다. 개츠비의 웹사이트 빌드 과정에서 구글 시트와 API 서버에서 원하는 데이터를 불러온 후 프론트엔드에서 원하는 형태로 데이터를 주입해주게 됩니다. 코로나보드에서 정적 웹페이지 빌드 및 배포는 20분에 한 번씩 이루어집니다. 또한 수동으로 빌드하여 배포할 수도 있습니다(자세한 설명은 5장 ‘웹사이트 UI 구성하기 : 개츠비’ 참조).
정적 웹페이지와 동적 웹페이지
동적 웹페이지는 사용자 요청을 받은 애플리케이션 서버가 동적으로 사용자의 요청 내용에 맞게 콘텐츠를 생성하여 응답하는 방식입니다. 이 방식은 사용자 요청에 따라 콘텐츠를 다르게 보여주는 장점이 있지만 별도의 애플리케이션 서버가 필요합니다. 애플리케이션 서버에서는 보통 복잡한 비즈니스 로직이 수행되면서 서버의 CPU 자원을 많이 사용합니다. 또한 많은 데이터가 저장된 데이터베이스에서 원하는 데이터를 검색하는 등의 작업에 시간이 걸려 응답 속도가 느려집니다. 요청이 많이 들어오면 처리되지 못한 요청들이 서버에 쌓이면서 서버의 메모리를 잠식해 자칫 서버 장애로 이어질 가능성이 큽니다. 그래서 동일한 요청량을 처리하는 데 필요한 서버 수는 동적 웹페이지 방식이 정적 웹페이지 방식보다 더 많습니다.
반면 정적 웹페이지는 미리 만들어둔 콘텐츠를 파일로 저장해두고 웹 서버가 요청받은 내 용에 해당하는 파일을 찾아 바로 응답합니다. 이 방식은 파일을 읽어서 응답하는 웹 서버 만 있으면 되기 때문에 별도의 애플리케이션 서버가 필요 없습니다. 사용자에게 보여주고 싶은 콘텐츠가 담긴 HTML 파일들만 미리 서버에 업로드해두면 사용자 요청에 따라 해당 HTML 파일 내용을 응답으로 내려주면 되기 때문에 CPU 부담이 거의 없고 처리 속도가 매우 빨라서 적은 수의 웹 서버만으로도 동적 웹페이지 방식 대비 훨씬 더 많은 요청량을 처리할 수 있습니다.
웹사이트를 만들 때 모든 페이지가 정적 웹페이지인 정적 웹사이트를 만들 수도 있고, 모 든 페이지가 동적 웹페이지인 동적 웹사이트를 만들 수도 있지만, 상황에 따라 적절하게 정적/동적 웹페이지가 혼합된 웹사이트를 만들 수도 있습니다.
1.4.5 정적 페이지 저장소 및 웹 서버
빌드가 완료된 HTML 페이지 파일들을 저장해두는 곳입니다. ‘정적’이라는 말이 뜻하듯 이 파일들은 삭제해서 다시 업로드하기 전까지는 내용이 변하지 않습니다, AWS S3Simple Storage Service를 사용하면 이런 파일 데이터를 저장하고, 저장된 파일을 서빙serving하는 웹 서버 역할까지 할 수 있기 때문에 S3를 활용했습니다(자세한 내용은 15장 ‘파일 서버 운영하기 : AWS S3’ 참조).
1.4.6 클라우드플레어
사용자가 웹 서버에 직접 요청해서 웹페이지를 받아가도 되지만, 사용자와 웹 서버 사이에 CDNcontent delivery network 역할을 하는 클라우드플레어Cloudflare 서비스를 추가했습니다(CDN은 사용자와 가까운 위치의 분산된 캐시 서버로부터 사진, 비디오 등의 콘텐츠를 내려받아 대기 시간을 최소화하는 콘텐츠 전송 기술입니다). 이렇게 하면 사용자는 웹 서버에 직접 접속하지 않고 클라우드플레어 서버를 통해서 웹페이지를 받아갑니다. 따라서 클라우드플레어에 이미 캐시cache된 웹페이지에 대한 요청은 클라우드플레어에서 바로 응답하므로 웹 서버로 직접 들어오는 트래픽을 줄일 수 있습니다. 게다가 클라우드플레어의 대역폭 비용이 무료라서 운영 비용을 대폭 절감할 수 있습니다. 이외에도 HTTPS와 웹사이트 운영에 필요한 여러 리디렉션 규칙을 적용할 수 있습니다(자세한 내용은 17.2절 ‘검색 엔진에 웹사이트 등록하기’ 참조).
1.5 코로나보드 아키텍처 핵심 포인트
코로나보드 아키텍처에 적용된 핵심 포인트를 요약해보겠습니다.
정적 웹페이지 & 빌더 – 트래픽 걱정은 그만
정적 웹페이지 방식으로 웹사이트를 운영하면 트래픽이 아무리 늘어도 걱정할 필요가 없습니다. 트래픽이 늘어나면 늘어난 트래픽에 따른 대역폭 사용료만 지불하면 됩니다. 트래픽에 따라 애플리케이션 서버 혹은 데이터베이스 서버를 증설할 필요가 없기 때문에 전체 서버 운영비를 최소화할 수 있습니다.
자동 크롤링 파이프라인 – 나를 쉬게 하는 비법
크롤러는 데이터 수집을 자동화해서 사람의 개입을 최소화하는 프로그램입니다. 스케줄러를 통해 원하는 시간에 크롤러가 수행되도록 설정하면 반복되는 업무를 자동화할 수 있습니다. 또한 절약 한 시간을 더 중요한 기능을 개발하는 데 사용할 수 있습니다.
구글 시트 – 저장소로 드는 돈을 아끼는 비법
구글 시트를 저장소로 사용하면 여러 사람과 문서를 공유해서 데이터를 편집할 수 있습니다. 권한 관리와 변경 내역 추적까지 제공하는 강력한 데이터 편집 툴을 같이 얻게 되는 셈입니다. 게다가 무료입니다.
클라우드플레어 CDN - 트래픽 비용까지 무료로
클라우드플레어는 트래픽에 따른 대역폭 사용료가 무료이고 HTTPS, 리디렉션 설정 등의 다양한 기능을 제공합니다. 이를 이용하여 운영비를 줄이면서도 서비스 운영에 필수적인 설정을 손쉽게 할 수 있습니다.
광고 - 부족한 유지비를 보충하는 비법
아무리 서버 비용을 최소화하더라도 서비스를 운영하는 데 다양한 비용이 발생할 수밖에 없습니다. 서비스 유지 비용을 넘어 수익까지 창출할 수 있다면 계속해서 서비스를 개선하고 발전시켜나 가는 원동력이 될 수 있습니다. 수익화를 위해서 서비스 경험을 해치지 않는 범위 내에서 코로나 보드에 광고를 게재했습니다.
학습 마무리
코로나보드에 적용된 기술 스택과 아키텍처를 알아보았고, 왜 그렇게 선택하고 설계했는지도 살펴보았습니다. 개발하려는 서비스의 특징에 맞춰서 여러 요소를 고려하여 아키텍처를 설계하는 것이 중요합니다.

권영재
연세대학교 전기전자공학과 학사, 글로벌융합공학과 석사를 졸업하고 LINE에서 라인뮤직 iOS 앱과 서버를 4년간 개발했습니다. 현재는 여행 서비스를 개발하는 스퀘어랩에서 플레이윙즈와 카이트를 만들고 있으며 리모트 워크를 하면서 디지털 노마드로 살고 있는 풀스택 개발자입니다

주은진
카이스트 전산학과를 졸업하고 LINE에서 라인뮤직 iOS 앱을 5년간 개발했습니다. 현재는 둔딘스튜디오를 창업해 ‘나리의 언어생활’ 이모티콘을 그리며 개인 앱도 개발합니다. 디지털 노마드로 살고 있는 ‘개발하는 일러스트레이터’입니다.

Leave a Reply

©2020 GoldenRabbit. All rights reserved.
상호명 : 골든래빗 주식회사
(04051) 서울특별시 마포구 양화로 186, 5층 512호, 514호 (동교동, LC타워)
TEL : 0505-398-0505 / FAX : 0505-537-0505
대표이사 : 최현우
사업자등록번호 : 475-87-01581
통신판매업신고 : 2023-서울마포-2391호
master@goldenrabbit.co.kr
개인정보처리방침
배송/반품/환불/교환 안내