기술블로그 구독서비스 개발 후기 - 2부
1부에서는 기술블로그 구독서비스(이하 서비스)를 왜 만들게 되었고 어떤구조로 만들가에 대해 이야기를 해보았다면, 이번 포스팅에서는 만들면서 만나게 된 각종 트러블슈팅 종합세트(?)
를 하나씩 풀어보고자 한다. 물론 개발을 하면서 아무 문제 없이 잘 되면 당연히 좋겠으나 잘되도 이상한게 개발이라는 세계가 아니던가.
잘 안되면 문제, 잘 되도 문제 ㅠㅠ, 출처 : https://www.clien.net/service/board/park/9111495
- 1부 : 왜 만들게 되었는가 그리고 어떤 구조로 만들었는가
- 2부 : 문제발생 및 Trouble Shooting
- 3부 : 앞으로의 계획과 방향성
지난 1부에서 이야기 했던것처럼 문제 - 해결, 문제 - 해결 식으로 나열해보고자 한다. 다소 글의 전개가 뒤죽박죽일수도 있겠지만 말 그대로 트러블슈팅 종합세트
이니 독자들의 양해를 미리 구한다.
- 트러블 슈팅 리스트
- 10시에 로직이 실행되었지만 메일을 11시 넘어서 받게 된다.
- 제목없는 글? 블로그 RSS파싱 오류? 간헐적으로 오류가 생긴다.
- 메일 내에 class를 적용하여 CSS 처리가 불가능하다.
- 메일을 보냈으나 스팸으로 처리된다.
- Elastic Stack을 사용할수 없다.
- 메일 보내는 발송속도가 너무 느리다.
- 구독해제가 아닌 자체 수신거부는 어찌 처리할까?
# 10시에 로직이 실행되었지만 메일을 11시 넘어서 받게 된다.
👉 해결방안 : Divide and Conquer
본 서비스의 요구사항중 하나는 매일 오전 10시, 구독자들에게 어제 등록된 글을 수집하여 메일로 보내주는게 있다. 우선 로직은 다음과 같은 순서로 진행되게 개발하였고, jenkins 등 별도의 스케쥴러 관리 어플리케이션에 의해 할수도 있었으나 이 또한 심플하게 crontab 에 등록하여 매일 오전 10시에 실행되도록 하였다.
- awesome-devblog 에서 블로거들의 RSS 피드를 조회한다.
- 어제 등록된 글이 있다면 리스트에 담는다.
- 조회가 끝나면 메일형식에 맞추어 html 문자열을 만든다.
- 만들어진 문자열을 가지고 등록된 구독자들에게 메일을 보낸다.
로직은 아주 간단했다. 데이터를 파싱하는 방법이나 메일형식에 맞추어 html문자열을 만드는 등 별도의 라이브러리를 사용하는 다소 복잡한 부분만 빼면 단순히 for문과 if문을 조합해서 로직을 구성할수 있었다. 헌데, 10시에 해당로직이 실행되었지만 최대 1시간이 지나고서야 메일을 받는 경우도 있었다. 이게 무슨일일까!?
눈치를 챘을수도 있지만 RSS 피드를 조회하는 곳에서 오래걸린 것이다. 티스토리나 네이버등 다른 블로그들은 RSS를 읽고 파싱하는 속도가 그렇게 오래 걸리지 않았는데 (1초 이내) 유독 이글루스 블로그의 RSS파싱이 오래걸리는건 1분까지도 걸리던 것이였다. ( 참고로 RSS 파싱모듈, yaml 파싱모듈 을 사용했다. )
아마 RSS의 형식이 약간 달라서 그런것 같긴 한데 그렇다고 이글루스 일 경우에 파싱을 다르게 하는건 좀 그렇고… 추후 이글루스가 아닌 또다른 파싱속도가 느린 블로그의 RSS를 만날수도 있기에 RSS 타입별로 예외처리를 하는건 좀 아닌것 같았다.
이런저런 고민끝에 아주 간단하게도 임무(?)를 나누는식으로 해결 하였다. 즉, RSS를 읽고 메일에 보낼 데이터를 만드는 job 하나와 만들어진 데이터를 가지고 이메일을 보내는 job 으로 나눈뒤 RSS를 분석하는 job은 9시에, 메일보내는 job은 10시에 보내도록 해서 생각보다 아주 심플하게 문제를 해결할수 있었다.
복잡하고 어려운 문제를 꼭 복잡하고 어렵게만 해결해야하는 법은 없는것 같다. 모로 가도 서울만 가면 된다
라는 속담이 있지 않는가.
# 제목없는 글? 블로그 RSS파싱 오류? 간헐적으로 오류가 생긴다.
👉 해결방안 : 언제나 신경써야 하는 예외처리(try-catch)
내가 만든 코드는 언제나 내 생각대로만
돌아갔으면 하는건 모든 개발자의 마음과 같다.
흔한 IT 종사자들.deploy, 출처 : https://9gag.com/gag/a0Yxw4B/operations-team-before-leaving-for-holidays
하지만 그생각도 잠시 언제나 예외는 발생하기 마련. ( 물론 전혀 예외가 발생 안할수도 있으나 만약 발생하지 않았다 할지라도 발생할수 있는 가능성은 염두해둬야 한다. ) 파싱하는 과정에서 제목이 없는글로 온다거나, 가끔 RSS url 응답이 404 또는 503 인 경우가 있었다.
참고로 필자는 출근이 늦은편이라 아침마다 늦잠을 자곤 했는데 이 서비스를 만들면서 덕분에(?) 9시 에는 메일에 보낼 데이터가 잘 만들어 졌는지, 10시에는 메일이 잘 갔는지 확인을 하다보니 일찍 일어나는 습관이 길러졌다 -ㅁ-
이러한 경우는 예외처리를 해서 제목이 없는 경우엔 임의로 ‘제목없음’ 이라는 제목으로 만들어주고, RSS url 응답이 정상적이지 않는 경우엔 해당 블로그 RSS를 무시할수 있도록 try-catch (python에서는 try-except) 구문을 사용하여 로직 도중에 어떠한 이유라도 중단되는 일이 없도록 하였다.
언제나 예외처리는 항상 신경쓰자. 물론 내가 생각한 예외가 전혀 발생하지 않더라도 예외처리를 하는 습관을 기르다 보면 호미로 막을것을 가래로 막는 불상사
는 만나지 않을것 같다.
# 메일 내에 class를 적용하여 CSS 처리가 불가능하다.
👉 해결방안 : Inline Style 처리
개인적으로 디자인을 신경쓰는 개발자(?)인지라 메일보낼때도 그냥 텍스트보다는 그럴싸한 형식으로 메일을 보내고 싶었다. 그래서 Bootstrap을 활용하여 이쁘장하게 만들었는데 막상 보내보니 설정한 class는 다 날라가고 날것의(?) html으로만 보내지는 것이였다. 좀 찾아보니 이메일 내에는 여러 부분에서 html요소들이 제한된다고 한다. ( 관련링크: 이메일 클라이언트 CSS지원 )
그래서 찾다보니 다행히도 class로 설정된 html을 이쁘게 inline style 로 바꿔주는 서비스(Emogrifier)가 있어서 이를 활용하여 전부 inline style로 바꾸게 되었다. 정말 세상엔 능력자들이 너무 많다…
# 메일을 보냈으나 스팸으로 처리된다.
👉 해결방안 : 도메인 구입과 DKIM 설정
메일 발송은 본 서비스에서 가장 중요한 부분이다. 이런저런 과정을 통해 파싱해서 메일 보낼 데이터를 만들었는데 메일을 못보내면 말짱 꽝이기 때문. 그래서 메일보내는 방법을 다양하게 생각해봤는데 얕은 지식과 더 얕은 경험으로 두가지 방법을 생각할수 있었다.
- 자체 SMTP 구축
- Google SMTP 서버 활용
우선 처음엔 자체 SMTP 구축을 해서 메일을 보내게 되었다. 메일보낼때 시스템 부하가 발생하면 어쩌지 하는 걱정을 하였지만 그 걱정에 앞서 수신자의 이메일이 naver.com
인 경우는 메일이 잘 보내지는데 google.com
인 경우에는 스팸으로 메일이 가게 된것이다. 스팸이라… 전혀 생각하지 못한 예외가 발생하였다!
이 서비스를 만들면서 가장 큰 위기였던것 같다. 우선 자체 SMTP를 구축해서 메일을 보내고 있었기에 어떻게든 설정을 통해 ‘난 스팸메일을 보내지 않는다’를 알리고 싶었다. 그에 찾아본 키워드로는 DKIM(DomainKeys Identified Mail)과 SPF(Sender Policy Framework)이 있었으나 … 귀차니즘인건지 열정의 부족인건지 서버에 셋팅하다 포기, 다시 셋팅하다 포기를 몇번을 한지 모른다.
가장 큰 위기, 내가 이 서비스를 만들수 있을까? (정치적 메세지는 아닙니다.), 출처 : https://news.sbs.co.kr/news/endPage.do?news_id=N1003101192
서버설정으로 스팸을 회피하기는 내 실력으론 부족한걸 인지하고, 다른방법으로 Google SMTP서버를 사용해보았더니 놀랍게도 너무 간단하게 스팸으로 발송이 안되고 정상적으로 메일을 발송할수 있었다. 그러나 그 행복(?)도 잠시 무료
라는 키워드엔 언제나 제한
이 있기 마련. 계정당 하루 최대 500개밖에 보낼수 없었다. 그렇다면 계정을 여러개 만들면 되는게 아닐까하고 서비스에 적용하기 앞서 계정을 여러개 만들어 테스트를 해보니 실 user당 하루 500개를 넘지 못하는걸 확인할수 있었다. (구글 SMTP를 사용하기 위해서는 항상 본인인증을 하는데 이게 제한 포인트인것같다.) 그럼 결국 Google SMTP도 정답은 아니고…
처음 서비스를 만들때 어떠케든 비용이 들지 않는 선에서 만들고 싶었으나 투자하는 시간에 비해 퍼포먼스가 나오지 않아 결국 돈을 지불하고서라도 해야겠다는 마음으로 SMTP 호스팅 업체를 찾다 AWS에 SES라는 서비스를 발견하게 된다. 비용은 1,000건당 0.1달라이니 구독자가 1,000명이고 30일을 발송한다고 가정하면 한달에 약 3달라가 드는셈. 원화로 따지면 한달에 3천원인데 나중에는 이 비용조차 줄여볼수 있는 방안을 고려해봐야 겠다. 이는 3부에서 정리해본다.
AWS의 SES를 활용하면 앞서 그토록 어려웠던 스팸메일 우회를 아주 간단하게 할수 있었고, 그러다 보니 도메인도 구매하게 되고 깔끔하게 스팸메일 우회를 처리하면서 도메인도 이쁘게(?) 만들수 있었다. ( 물론 도메인을 구입하면서 부가세 포함 13.2달라가 발생하였지만… 열정페이로 이또한 수익을 얻는 구조로 생각을 해봐야 겠다. 1년만 서비스 할게 아니였으니… )
AWS의 도메인을 구입하고 AWS-SES를 통해 스팸을 우회하여 메일을 보내는 과정은 아래 포스팅을 참조하였다. (워낙 정리가 잘되어있어 별도로 정리하지는 않고, 링크를 공유하고자 한다.)
- http://jojoldu.tistory.com/246
- https://insidestory.kr/11477
- http://blog.naver.com/my0biho/220937119874
# Elastic Stack을 사용할수 없다.
👉 해결방안 : 직접 만들어버리자!
로깅은 서비스를 운영하는데 있어 하나의 무기가 될수 있다. 기본 서버에서는 아파치와 Elasticsearch, kibana를 동시에 돌릴만한 메모리가 안되었기에 (1G….) 서버 한대를 더 받고 Elasticsearch와 Kibana를 설치하였고 가입자수, 포스팅수 등 다양한 로깅을 보려했는데 몇일이 지나고 AWS에서 메일이 온다.
너님, 이대로 가다가는 돈낼수 있으니 참고해!
한달에 사용할수 있는 서버운영 제한시간이 곧 다가온다는 이야기. 이것저것 검색해보니 Free Tier 일 경우 한달간 서버 한대를 24시간 사용하는데만 무료이고 그 이상은 과금이 나간다고 한다. 덕분에 한 3달라 정도 과금이 발생해버렸다. (정리하면, AWS Free Tier를 사용할경우 24시간 서버 운영시 한달에 한대밖에 사용하지 못한다. 서버를 중지했다가 다시 살리는 수고를 하면 여러대를 사용할수도 있긴 하겠다만 구지…)
해서 몇일간 Elasticsearch에 로깅해둔 데이터는 눈물을 머금고 기존 회원들의 이메일 리스트를 담고있는 sqlite3으로 백업 한 뒤 추가로 받았던 Elasticsearch용 서버는 다시 반납을 하게 되고, 이 데이터를 어찌 볼수 있을까 고민끝에 tui.chart를 사용하여 뷰를 만들게 된다. (아 물론, billboard.js도 훌륭하다. 개인 취향차이 같다. … )
오픈소스인 kibana
직접 만든 뷰, 직접 만들어서인지 더 이뻐보이는건 기분탓이겠지
로깅 뷰는 http://daily-devblog.com/log/view 에서 확인이 가능하다. (최근 일주일의 데이터) 이번에 로깅 뷰를 만들면서 느낀거지만 kibana는 정말 좋은 오픈소스 툴인것 같다. 데이터 양이 많으면 자동으로 군집화(?)해서 보여주고… 이런게 스크립트단에서 처리를 하려면 엄청 복잡… 다시한번 ElasticStack을 찬양하며…
# 메일 보내는 발송속도가 너무 느리다.
👉 해결방안 : Thread 활용
앞서 메일 보낼 데이터를 만드는 job과 메일을 보내는 job을 나눠서 메일을 보다 빠르게 발송할수 있었는데, 보내야 하는 사람이 많아지다 보니 그만큼 속도가 늦게 보내지는것이였다. 즉, 메일 보낼 사람의 메일이 구독 신청을 빨리해서 메일링 리스트 앞에 있다면 10시 부근에 받는데 최근에 구독 신청을 해서(늦게 구독 신청을 해서) 메일링 리스트의 뒷부분에 있다면 앞사람 다 보내고 받아야 하는 이슈가 있었다. 다시말하면 순차적으로 메일을 보내다보니 사람이 많아질수록 그만큼 늦게 받는 사람이 발생할수 있다는 것.
이 문제는 간단히 스레드를 활용해서 해결할수 있었다. 테스트하면서 캡쳐해둔게 있는데 첫번째 스크린샷은 한사람에게 메일을 그냥 10번 반복해서 보냈고 두번째 스크린샷은 스레드를 활용해서 보내보았다. 확실히 스레드를 활용하니 속도가 빠른걸 확인할수 있었다.
순차적으로 발송했을 경우 : 약 30초 소요
스레드를 활용했을 경우 : 거의 1초 내외
물론 AWS-SES에서 처리하는 메일발송 시간에 따라 달라지겠지만 실제로 스레드를 활용한 메일을 전부 열어보면 거의 동시에 온것을 확인할수 있었다. (구독자가 500명이 되었을때 확인해본 결과 1초 이내에 AWS-SES로 메일을 보낼수 있도록 처리한것을 확인할수 있었다. )
# 구독해제가 아닌 자체 수신거부는 어찌 처리할까?
👉 해결방안 : AWS 반송/거부 확인 메일
미약하지만 메일 발송도 AWS-SES를 사용하다보니 이제는 비용이 발생하게 되었다. 따라서 비용을 최소화 하기 위해서는 쓸떼없는 메일(?)을 발송해선 안된다. 그래서 매일 보내는 메일 하단에 구독 취소룰 할수 있도록 링크를 만들어 뒀는데 이렇게 정상적인 구독취소가 아닌 메일 자체를 사용하고 있는 메일 시스템에서 거부를 할 경우. 이를 어떻게 알고 처리할수 있을까?
(역시 AWS~ AWS~)이러한 부분도 AWS에서 가이드를 해주고 있다. (무려 한글 ㅠㅠ 감사…)
https://aws.amazon.com/ko/blogs/korea/automatic-managing-bounced-emails-by-amazon-ses/
물론 또다른 문제들도 있었으나 메이저급(?) 문제들만 다뤄봤다. 가장 컸던건 앞서 말한 메일 스팸처리 ㅠㅠ 도메인도 구입하고 메일보내는데 비용도 들지만 그간 고생하고 삽질했던걸 생각하면 하나도 아깝지 않다. (어쩔수 없는 자본주의일까?)
마지막으로 3부에서는 이러한 서비스를 이제 어떤식으로 운영할꺼고, 어떻게 발전시켜 나갈것이며, 수익 모델은 어떻게 생각하고 있는지에 대해 이야기 해보고자 한다. (수익모델이라 하지만… 그저 이 서비스를 운영하는 정도?;;)
※ 원글 : https://taetaetae.github.io/2018/08/09/daily-dev-blog-2/