제작 계기
중학생 때부터 알던 랜선 친구들이 있다. 그 때는 irc로 서로 알게 됐고, 이렇게 오래 연락하고 지내게 될지 상상도 못했는데, 여하튼 지금까지도 카톡에 단톡방을 만들어놓고 시답잖은 이야기를 나누고, 여유가 되면 비정기적으로 오프라인으로 만나서 바보들의 행진을 하곤 한다. 어느 놈 하나 정상인이 없는 방이다.
작년 11월 쯤부터 갑자기 그 단톡방 중 한 놈이 '현재 미세먼지 농도 순위'라면서 우리들이 사는 지역의 미세먼지 수치를 순위로 매겨서 주기적으로 올리기 시작했다.
처음엔 "또 쓸데없는 짓 시작이구만…" 정도의 반응이었지만 시간이 갈 수록 우리는 이 미세먼지 농도 순위에 익숙해져 갔다.
그리고 이 뻘짓이 장기화되자 급기야 가만히 있는 나에게까지 협업의 요구(?)가 빗발치게 됐다.
방의 유일한 개발자였던 나는 졸지에 대책 위원장이라는 중임을 맡게 되었고, 이렇게 현미농(현시각 미세먼지 농도) 의 개발이 시작되었다.
접근
제작 방식
배포 방식이 가장 문제였다. 카카오톡이 텔레그램이나 잔디 앱처럼 단톡방에 봇을 넣을 수 있으면 참 좋았겠지만, 가계정 남발을 우려한 카카오톡의 정책상 단톡방에 봇을 만들어 넣는 것은 무리가 있었다. 그래서 선택한 차선책은 바로 웹앱. 현미농 순위를 표시해주는 웹앱을 만들어서 내 서버에 올려놓고, 궁금할 때마다 들어가서 확인해보도록 하면 되겠다 싶었다.
미세먼지 Open API: 공공데이터포털, 에어코리아
웹앱이 되었건, 뭐가 되었건 간에 일단은 "미세먼지 정보를 어떻게 얻어오냐"가 포인트다. 대기오염정보 같은 공공적 성격의 정보는 보통 공공데이터포털에서 제공하고 있다.
(2011년도부터 2019년까지 누적으로 사용된 Open API 중에서 당당하게 대기오염정보 조회 서비스가 1위를 차지하고 있다. 흠터레스팅…)
공공데이터포털에 들어가 가입 후에 API 활용신청을 하면 대기오염정보 조회를 할 수 있다. 에어코리아의 API가 제공된다. 자세한 사항은 직접 들어가서 확인하자.
오픈 API 활용신청은 처음 해봤는데, 불편한 점이 크게 두 가지가 있었다.
- 활용신청을 하면 바로 신청이 받아들여진다. 화면상으로는 그렇다. 실제로 해보면 안 된다. 댓글란을 보니 "최소 몇 시간부터 길게는 24시간을 기다려야 제대로 승인이 된다"라고 적혀있었다. 실제로 난 활용 승인이 난 그 당일에는 API를 전혀 쓸 수 없었고, 그 다음날이 되어서야 사용할 수 있었다.
- API가 XML 기준으로 작성되어 있다. JSON도 당연히 지원하지만 실제 사용 예시나 문서 가이드를 읽어보면 API를 제공하는 측에서 XML 위주로 고려하고, JSON은 겸사겸사 넣었다는 느낌이 강했다. json 형태로 response 받기 위해서는 GET할 때 _returnType을 json으로 설정해줘야 하는데, 가이드 문서에는 그 안내사항이 매우 찾기 힘든 위치에 딱 한 줄 적혀있었다.
백엔드 구상
Crontab + Python Script + Node.js
스케줄링
실시간 미세먼지라고는 해도, 갱신 주기는 1시간이고, 어느 지역의 정보를 가져올지는 고정되어 있으므로 미세먼지 Open API를 웹 앱을 들어갈 때마다 불러올 필요가 없었다. 오히려 요청 수만 쓸데없이 늘릴 뿐. (용도에 따라 API의 최대 요청수가 정해져있다.)
그래서 내가 생각한 방법은, 백엔드 서버가 1시간 단위로 미세먼지 API를 이용해 정보를 받고, 서버 내부에 그 정보를 기록하도록 하는 것이었다.
이 방법을 실행하기 위해선 서버 내에서 사용할 스케줄러와, 스케줄러가 실행할 수 있는 프로그램이 필요했다. 내 서버의 OS인 라즈비안(raspbian)에는 crontab이라는 성능 좋은 스케줄러가 있었으므로 이를 채택했다. 스케줄러가 실행하는 프로그램은 파이썬으로 구현했으며, 실행될 때마다 다섯번의 GET을 실행해서 다섯 지역의 대기오염 정보를 가져와 로컬에 .json 형태로 저장하도록 하였다.
API 서버
이제 미세먼지 정보도 서버에 저장되도록 하였으니 이를 프론트단에서 끌어올 수 있도록 API 서버를 구상해야 했다. 이를 위해 Node.js와 express를 이용하여 GET 요청을 받으면 서버에 저장된 json을 그대로 전송하는 API 서버를 구현했다.
프론트엔드 구상
사용 라이브러리 : React
공교롭게도 난 웹 개발 경험이 별로 없었고, 특히 프론트엔드에 관한 지식은 전무하다시피 했었다. html, css, js의 기본적인 것들을 익히긴 하였으나, html과 css만 가지고선 정적인 서비스밖에 하지 못하니 사실상 무용지물이고, js는.. 순수 자바스크립트만으로 현업에서 볼 수 있는 퀄리티의 프론트엔드를 구현한다는 것은 어지간해선 불가능한 일이다. 학교에서는 순수 자바스크립트만을 가르쳤고, 문법의 기본적인 내용은 이해했으나, 나로선 이를 그대로 실무에 적용하기에는 무리가 있었고, 그렇게 자연스럽게 프론트엔드와 담을 쌓았었다.
그러나 이 탓에 개인 프로젝트를 구상할 때마다 늘 프론트엔드에서 발목이 잡혔다. 특히나 이번 현미농은 예상 사용자층이 아무리 많아봐야 5명이었기에, 이거 하나 만들자고 프론트엔드 개발자를 찾을 수도 없었다. 그래서 이 기회에 프론트엔드를 배우기로 마음을 먹었고, 고민 끝에 나는 React를 선택했다.
어떻게 하면 React에 효과적으로 입문할 수 있을까. 하고 고민하던 차에 인프런(Inflearn)에서 React JS로 웹 서비스 만들기라는 강의를 발견했다. 영화 정보를 보여주는 웹앱을 클론코딩하는 강의였는데, 이 코드를 따라치면서 리액트의 기초적인 사항들을 배울 수 있었다.
레이아웃 구상
화면 구성은 내가 클론코딩했던 Movie App 부분을 많이 참조했다. Plate 컴포넌트를 떼어다가 적당히 잘 개조해서, 이름과 미세먼지 수치가 표시되도록 하였다. 처음에는 각 지역마다 색깔만 조금 다르게 하는 정도로 디자인을 크게 신경 쓰지 않았는데, 첫 배포 당시에 단톡방의 미친놈들이 "밋밋하다. 이거 바꿔라. 저거 바꿔라. 이 기능 넣어라. 저 기능 넣어라" 라며 온갖 개선의견(?)을 내 준 덕분에, 지금은
- 플레이트마다 각자 대표 이미지도 들어가고,
- pm10이냐 pm2.5냐로 기준을 정해서 정렬을 할 수 있게 되었으며,
- 각 수치들은 계기판처럼 움직여서 표현되도록 바뀌었다.
로고는 현미농의 시발(始發)인 심슨이 포토샵으로 수고해줬다. 딱히 수고는 아닌거 같은데 본인이 넣어달래서 넣어줬다.
빌드 & 배포
궁금할 리가 없겠지만 여기에서 실제 서비스를 확인해볼 수 있다.
빌드하고 배포하는 과정이 아주 살짝 까다로웠다. React를 서버사이드 렌더링하는 것이 아니라 static 파일로 전부 빌드를 한 다음에 제공하도록 했는데, 빌드 후의 이 static 경로를 어떻게 설정해줘야 하나 조금 난감했다. 지금이야 내 서버에 돌아가는 웹앱이 하나이므로 static 경로 설정을 아무렇게나 해도 상관 없지만, 앞으로 올라가는 앱이 더 많아진다면 어떻게 처리해야할지, 규칙을 정해놓아야 할 것 같다.
또한, url 파싱 문제도 있었다. 친구들이 외우기 쉽도록 url에 한글을 집어넣었는데, nginx에서는 utf-8의 문자 그대로 적어도 제대로 Proxy pass가 이루어졌는데, node.js에서는 퍼센트 인코딩으로 endpoint를 작성하지 않으면 접근이 되지 않았다. 구체적으로 어떠한 이유에서 이런 차이가 생겼는지는 모르겠으나, 앞으로 있을지도 모를 utf-8 문자가 포함된 url을 어떻게 대처해야하는지에 대한 경험이 아주 조금 쌓인 것 같다.
남은 문제들
Static 경로
위에서도 언급한 내용이다. 앞으로 static 파일들을 정확히 어디에 저장해야할 지 원칙을 세우는 게 좋을 듯하다.
배포 자동화
현재는 Github의 레포지토리를 하나 잡고, 내가 맥북에서 localhost로 React 작업을 한 뒤에, 레포지토리에 푸쉬 후, 서버에 ssh로 접속해서 pull한 다음 build하는 방식을 쓰고 있다. 조금 귀찮은 방법이다. 젠킨스에서도 React 자동 배포하는 방법이 있는 것으로 아는데, 찾아봐야 할 듯하다.
결측치
전국에 있는 모든 측정소들이 다 똑같지는 않은지, 특정 지역은 갱신되어야 하는 시간임에도 불구하고 갱신이 안 되는 경우가 있다. 값이 0 혹은 아예 빈 값으로 들어와버려서 정렬을 할 때 문제가 생겼었다. 지금은 이러한 경우는 그냥 강제로 999로 바꿔버리고, React에서는 ∞로 표시되도록 만들었다. 즉, 자기 지역 관측소에 문제가 생기면 그 놈은 자동으로 먼지왕이 되는 것이다. 매 시간마다 제대로 된 값이 들어왔으면 좋겠다. (이건 내가 해결할 수는 없으니 마음 속으로 간절히 바라는 수밖에… 그리고 왜 내 고향은 목록에 없는거니? ㅠㅠ)
향후 계획
실시간으로 보여주는 것 외에도, 시간대별로 별도로 기록을 하여서, 각 지역마다 시간대별로 미세먼지가 어떻게 변화하였는지를 보여주는 기능을 추가할 예정이다. 이를 위해선 react-route를 도입해야하고, 로컬 스토리지 이외에 DB와의 연동도 고려를 해야할 것이다.
감상
시작은 다소 엉뚱했으나, 나에게 현미농은 최초로 모든 과정을 혼자서, 빌드와 배포까지 전부 해낸 프로젝트다. 프론트엔드는 나와 맞지 않는다며 오로지 백엔드만 고집했을 때보다, 훨씬 그럴싸한 결과물을 낼 수 있었다. 역시 뭐든 두루두루 배워놓고 볼 일이다. 이 현미농을 발판으로 앞으로 다른 프로젝트들에도 도전해볼 생각이다.