카테고리:

업데이트:

1. 서론

이번 스프린트 4.9에서는 라운지 키워드 알림 기능 개발을 맡았고, 키워드 알림 기능을 개발하면서 겪었던 시행착오들과 함께 결과를 기록하고 다른 개발자들이 도움이 되었으면 하는 바램에 이 글을 남깁니다.

2. 고민과 설계

킥오프를 진행하면서 라운지 키워드 알림 기능의 기획을 보며 설렘 반 걱정 반이였다.

설레었던 이유는 처음 스프린트 업무에 투입되기도 했고, 성공한다면 골드스푼 주요 서비스 중 하나인 라운지가 조금 더 활성화될 것이라는 기대감이 있었던 반면, 걱정했던 이유는 키워드를 어떻게 추출할지 그리고 많은 양의 알림이 발송되는 경우 성능 이슈를 고려해야했기 때문이다.

그래서 우리 백엔드 팀은 키워드 추출, 알림 발송, 전체적인 구성 이 세 가지를 주요 쟁점으로 두고 머리를 맞대었다.

먼저 키워드 추출하는 방법은 Elastic Search나 NLP의 KR-WordRank 등 생각보다 많았다. 하지만 이들은 러닝커브가 존재하여 한정된 스프린트 기간에 익혀서 적용하기에는 다소 무리가 있었다.

그래서 우리는 아호코라식(Aho-corasick) 알고리즘을 사용하기로 했다. 아호코라식 알고리즘에 대해 간략하게 설명하면 KMP 알고리즘과 비슷하다고 할 수 있는데 KMP 알고리즘은 1:1 문자열 패턴 매칭 방법인 반면 아호코라식 알고리즘은 1:N 문자열 패턴 매칭 방법이다. 따라서 하나의 라운지 게시글에 유저들이 등록한 키워드 중 어떤 키워드가 매칭되는지 알 수 있기에 우리가 개발하려던 키워드 알림 기능에 아주 적합한 알고리즘이였고, 이미 오픈소스로 구현되어 있어 러닝커브가 거의 없었다.

아호코라식 알고리즘에 대한 자세한 내용은 여기 링크를 남기겠다.

키워드 추출 방법을 확정한 우리는 이제 push 알림을 어떻게 발송할지 고민했다.

골드스푼 서비스의 알림 기능은 Onesignal 서비스를 이용하고 있는데 Onesignal 서비스는 push 발송 후에 callback 함수를 통해 성공 여부를 확인할 수 있었다. 진호님께서 각 발송에 대한 결과를 하나씩 update 하는 경우 장애로 이어질 수 있다는 조언을 해주셨고, push 발송을 비동기로 보내고 CountDownLatch를 통해 모든 발송이 완료될 때까지 기다린 후 callback을 취합하고 성공 여부 컬럼을 bulk update 하는 방법으로 진행하기로 했다.

그리고 전체적으로 키워드 알림 기능을 어떻게 구성할지도 고민했는데 처음에는 API 서버에서 라운지 등록과 동시에 SQS 메시지를 publish하고 배치 서버에서 해당 메시지를 consume하여 처리하려 했었다.

하지만, 골드스푼의 API 서비스는 Stored Procedure로 되어 있는데 SQS 기능을 사용하려면 인터셉터를 수정해야 하는 등 예외로 처리해야 했다. 따라서 해당 방법은 사용하지 않았고 오로지 배치 서버로만 처리하기로 했다.

3. 성능 개선

개발이 완료되고 성능 테스트를 해보았는데 기대한만큼 성능이 나오지 않았고 그대로 라이브 서버에 릴리즈하면 장애로 이어질 가능성이 있어보였다. 그래서 어떻게 성능을 개선할 수 있을까 고민했고 백엔드 팀원 분들께 의견을 구했다.

그래서 백엔드 팀 대상으로 코드 리뷰를 하며 성능 이슈에 관한 많은 상의를 했었는데 이 과정에서 CountDownLatch를 통해 모든 발송을 기다려서 처리하는 부분이 문제가 될 것이라 예상했고, 성능 테스트를 한 결과, 실제로 발송하고 취합하여 처리하는 부분이 매우 오래걸렸다.

그래서 이 부분을 SQS를 통해 처리하기로 했다.

기존에는 push 발송 후 callback 함수를 취합할 때까지 기다려서 처리했었던 반면, SQS를 사용하는 방법은 callback 함수에서 SQS 메시지를 publish하고 메시지 consume 스케줄러를 통해 메시지를 일괄로 받아 처리하는 것으로 더 이상 모든 push 발송을 메인 스레드에서 기다릴 필요가 없었다.

이 방법을 통해 push 발송 시간을 비약적으로 절약할 수 있었다.

그 뿐 아니라 진호님께서는 Blue-Green 배포를 하고 있는 서버로 인해 일시적으로 배치 인스턴스가 두 개가 될 수 있는 가능성을 언급해주셨다. 따라서 배포 시 일시적으로 인스턴스가 두 개가 되는 찰나의 순간에 push 발송이 동일한 유저에게 중복으로 발송될 수 있는 문제가 있었다.

그러면서 Shedlock이라는 Locking 기법을 알려주셨다. Shedlock은 시간 기법 Locking으로 위와 같은 문제를 해결하는데 유용하게 사용할 수 있다.

Shedlock에 대한 자세한 내용은 해당 포스트에 정리해놓았으니 궁금하면 참조하면 좋겠다.

4. 후기

현재 우리 서비스는 1개의 배치 서버를 사용하고 있는데 그렇다보니 많은 양의 알림을 발송할 경우 장애로 이어질 수 있을 것이라 생각한다.

이미 우리는 AWS 기준 large 사양의 서버를 사용하고 있기 때문에 Scale up을 하는 것은 더 이상 의미 없고 서버 개수를 늘리는 Scale out을 해야하는데 그렇게 되면 Scale out에 클러스터링과 같은 기술들을 적용해야 하고 각 push 알림이 중복으로 발송되지 않도록 주의해야 할 것이다.

또한 현재 구조 상 API 서버가 Stored Procedure로 되어 있어 다양한 인프라 서비스를 사용하기 어려운 부분이 존재하는데 API 서버 또한 배치 서버와 동일하게 JPA를 이용한 Spring으로 마이그레이션 작업을 진행할 예정이다.

그래서 불가피하게 진행했던 마지막 스캔 번호를 DB 테이블로 관리하는 방법에서 라운지 등록 시 SQS 메시지를 publish하고 N개의 배치 인스턴스에서 중복으로 발송하지 않도록 처리하여 지금보다 성능이 더 향상된 라운지 키워드 알림 기능을 유저들에게 제공하겠다.

            
              📕 개인 기록용 블로그입니다.
              😊 오타나 잘못된 정보가 있을 경우 댓글이나 메일로 말씀해주시면 바로 수정하겠습니다! 😊
          

댓글남기기