들어가며
묵묵히 자신의 길을 걷는 사람이 있지만, 옆에서 도전하는 사람을 묵묵하게 응원하는 사람도 있습니다. 나의 도전을 응원해주시는 부모님과 사랑하는 사람을 생각하며 지난 상반기를 돌아봅니다. 누군가 도전하는 삶을 살아야 한다고 하지만, 어쩌면 나의 도전은 당신의 젊음을 갉아먹었기에 가능한 것은 아니었을까 생각하며. 나는 과연 나의 삶을 응원한 당신에게 보답할 수 있는 삶을 살았는가 돌아보기 위해 이 글을 적습니다.
기본에 관하여
이제 막 숟가락 사용법을 파악한 사람에게 땅을 파라고 요청하면, 숟가락 사용법 밖에 모르는 사람은 땅을 숟가락으로 파고 있을 것입니다. 작은 스타트업의 개발자로 일하면서, 원티드 프리온보딩 백엔드 코스에 참여하면서, 땅을 숟가락으로 파는 경험을 하고 있다고 생각했습니다. 기술에 매몰되어서, 이 기술을 왜 사용해야 하는지를 고민하지 않아서 생긴 문제라고 생각했습니다. 도구를 사용한다면 왜 도구를 사용해야 하는지를 고민할 수 있는 기본이 있는 개발자가 되고 싶었습니다.
기본에 충실해야 훗날 업무를 진행할 때도 작은 실수를 하지 않을 수 있다고 생각합니다. 기본이 없어서, 실수를 하고, 그 실수가 비즈니스에 영향을 준다면, 이보다 부끄러울 수 없다고 생각합니다. 코드를 작성할 때, 코드 한 줄 한 줄에도 의미를 담을 수 있는 개발자가 되고 싶었습니다. 기본에 관한 부족함을 해결하기 위해 '기본'에 대해 알아야겠다고 생각했습니다.
상실
기본에 관하여 생각하지 얼마 안 되어 허망하게도 상실을 겪으며 2022년을 시작했습니다. 친구에게 미안하지만 꿈이 생겨서, 지금은 공부에 집중해야 할 시기이니, 자리 잡으면 같이 술 한 잔 하자며 친구와의 약속을 미루기만 했는데, 그렇게 허망하게 친구를 보냈습니다. 친구를 보내고 오는 길, 나의 꿈을 응원해준 사람을 더 이상은 만날 수 없다는 생각에 무너져 내렸습니다. 그리고 시간이 지나, 친구와 약속을 지키기 위해서는 반드시 더 나은 사람이 되어야겠다고 생각했습니다.
친구에게 약속을 지킬 수 있는 사람이 되기 위해 더욱 치열하게 공부해야겠다고 생각했습니다. 친구에게 약속을 지켰다고, 떳떳하게 꽃 한 송이 사들고 인사 갈 수 있는 사람이 되기 위해 더 노력해야겠다고 생각했습니다.
POG
올해 초, 얼마 전부터 알게 된 동생으로부터 아이템 개발을 도와달라는 부탁을 들었습니다. 누군가에게 도움이 되기 위해 개발하는 과정에서 성장할 수 있는 기회라고 생각했습니다. 심지어 해결해야 할 문제가 쉽지 않았기에 그 문제를 해결하는 과정에서 반드시 성장할 수 있다고 믿었습니다.
POG가 해결하려는 문제는 다음과 같았습니다. 게임 '롤'을 즐겨하는 유저는 특정 게이머의 전적을 알기 위해 전적 검색 서비스를 사용합니다. 이 과정에서 매번 웹 사이트로 들어가서 전적이 알고 싶은 게이머를 검색하고, 전적 갱신 버튼을 클릭해야만 전적을 살펴볼 수 있었습니다. 이 과정이 사용자 입장에서 대단히 불편하다고 생각했습니다.
이 불편함을 해결하기 위해 POG에서는 앱 안에서 특정 게이머를 즐겨찾기 하면, 게이머가 게임을 완료했을 때 전적이 갱신된 내용을 푸시 알림을 제공하고, 앱에서도 바로 전적 정보를 살펴볼 수 있도록 개발하려 했습니다. 즉, POG에서 개발해야 할 기능은 푸시 알림 기능이었습니다. 푸시 알림을 개발할 때 다음과 같은 고려를 해야 했습니다.
1. 특정 게이머가 게임을 완료하면, 특정 게이머를 팔로우한 사용자에게 푸시 알림이 가야 한다.
2. 사용자가 팔로우한 게이머가 전적이 변동됐는지 특정 시간마다 매번 확인해야 한다.
3. 특정 게이머의 전적 변동 여부를 파악하기 위해 최신 전적을 저장하고, Riot API에서 특정 유저의 전적을 비교한다.
1. 100만 명에게 푸시 알림을 보내기
먼저 1번 문제를 해결하기 위해 다음을 고민했습니다. 특정 게이머를 팔로우한 사람이 100만 명이라면, 100만 명에게 푸시 알림을 보내기 위해서는 어떻게 해야 할지 고민해야 했습니다. 작년에 일했던 스타트업에서는 푸시 알림을 보내기 위해 특정 유저의 FCM 토큰을 DB에서 조회한 후, 한 명씩 푸시 알림을 보내는 방식으로 개발했습니다. 하지만 POG 서비스에서도 같은 방식으로 푸시 알림을 보낸다면 100만 명의 FCM 토큰을 조회하는 과정에서 큰 비효율을 가져올 수 있다고 생각했습니다. 또한 100만 명에게 푸시 알림을 보내는 과정이 동기적으로 동작하기에 이 동작 방식이 서버에 큰 무리를 가져올 수 있다고 생각했습니다. 이 문제를 해결하기 위해 먼저 FCM 공식 문서부터 세세하게 분석하면서, 문제 해결의 실마리를 얻고 싶었습니다.
FCM 공식 문서를 살펴보면서, 푸시를 보낼 때 한 명씩 보내는 것이 아닌 최대 500명에게 같은 푸시 알림을 보낼 수 있다는 것을 파악했지만, 이 마저도 100만 명에게 푸시를 보낼 때 큰 무리를 줄 수 있다고 판단했습니다.
그렇게 FCM 공식 문서를 살펴보는 과정에서 FCM Topic이라는 개념을 알 수 있었습니다. FCM Topic 적용 과정은 다음과 같았습니다. 사용자가 특정 Topic을 구독합니다. 그럼 서버에서는 FCM 서버에 특정 Topic과 함께, 보내려 하는 푸시 알림 내용을 전송합니다. 그럼 Topic을 구독하는 사용자에게 푸시 알림이 전송됩니다. FCM Topic을 프로젝트에 적용하면 100만 명의 FCM 토큰을 조회하지 않아도 된다고 생각했습니다. 이를 통해 문제 해결의 큰 실마리를 얻었습니다.
2. 100만 명의 게이머 정보 조회하기
1번에서는 1명의 소환사를 100만 명이 팔로우하고 있는 경우를 생각했습니다. 하지만 사용자는 다양한 롤 게이머를 즐겨찾기 할 수 있습니다. 예를 들어 서비스의 유저들이 100만 명의 게이머를 팔로우하고 있다면, 100만 명의 게이머가 전적이 갱신됐는지 모두 확인해야 했습니다. 심지어 1분 간격으로 100만 명의 게이머의 전적 갱신 여부를 파악해야 했습니다. 그럼 적어도 데이터베이스에서 100만 명의 데이터를 1분마다 조회해야 하는 상황이었습니다. 이 문제를 해결하려면 어떻게 해야 할까 고민했습니다.
먼저, 데이터베이스만 활용해서는 이 문제를 해결할 수 없다고 판단했습니다. 그래서 다른 방법을 찾는 과정에서 Redis의 Sets 자료구조를 활용한다면 이 문제를 해결할 수 있다고 생각했습니다. Sets은 중복되지 않은 문자열의 집합입니다. Sets 자료구조에 POG의 사용자가 즐겨찾기 한 게이머를 저장하면 매번 데이터베이스에 100만 번 조회하는 것이 아닌, Redis의 Sets 자료구조로 저장된 키 값 한 번만 조회하면, 100만 개의 데이터를 가져올 수 있습니다. 그럼 데이터베이스에 100만 번 조회하는 것이 아니라 Redis에 1번의 조회만으로 문제를 해결할 수 있으니 대단히 효율적이라고 생각했습니다.
3. 100만 명의 게이머 전적 갱신 프로세스 구축하기
이렇게 100만 명의 게이머를 1번에 Redis 조회로 쉽게 찾아올 수 있었습니다. 그럼 다음 문제는 100만 명의 게이머 전적이 모두 갱신됐다면 갱신된 정보를 데이터베이스에 업데이트하는 과정에서 부하를 가져올 수 있다고 생각했습니다. 적어도 데이터베이스에 100만 번 Update 요청을 보내야 하는데, 이는 심각한 부하를 초래할 수 있다고 생각했습니다.
이 문제를 해결하기 위해 Redis를 활용했습니다. 인 메모리 데이터베이스를 활용하면 RDBMS에 데이터를 수정하는 것보다 훨씬 효율적으로 동작할 수 있다고 생각했습니다. Redis에 특정 소환사 Id를 key로 설정하고 값으로 전적을 저장함으로써 문제를 해결했습니다. Redis 키 값으로 승리, 패배, 현재 티어 정보를 저장함으로써 승리 혹은 패배 여부와 전적 변동 여부를 확인할 수 있도록 설정했습니다. 만약 Redis에 장애가 발생하여 모든 데이터가 삭제된다면 최신 정보를 Redis에 저장하는 로직을 구축함으로써 장애 대비를 할 수 있었습니다.
4. 100만 명의 게이머 전적 갱신 여부를 확인하는 프로세스 구축하기
이렇게 100만 명의 게이머를 1번의 Redis 조회로 쉽게 찾아올 수 있었고, 데이터를 수정하는 작업도 효율적으로 할 수 있었습니다. 하지만 기능이 더욱 문제없이 돌아가기 위해서는 크게 두 가지를 고려해야 한다고 생각했습니다.
1. 푸시 서버를 별도로 만들어야 한다.
2. Queue를 활용해야 한다.
그럼 지금부터 위 두 가지 관점에 대해 알아보겠습니다.
1. 푸시 서버
먼저 푸시 서버를 별도로 만들어야 한다고 생각했습니다. 만약 100만 명의 게이머 전적이 변동됐는지 여부를 파악하고, 푸시 알림을 보내는 로직이 API 서버에 있다고 생각해 보겠습니다. 그럼 푸시 알림을 보내는 과정에서 에러가 발생하거나, 혹은 API를 개발하는 과정에서 에러가 발생해서 서버에 장애가 발생한다면 푸시 알림도 받을 수 없고, 앱도 확인할 수 없는 문제가 발생합니다. 하나의 기능 장애가 다른 기능 또한 사용할 수 없게 장애를 전파하는 프로세스를 분리하기 위해서는 푸시 알림을 보내는 서버를 분리해야겠다고 생각했습니다.
2. Queue
두 번째 Queue를 활용해야 한다고 생각했습니다. 현재 구조에서는 100만 명의 게이머 정보를 조회하고, 수정하는 로직이 동기적으로 처리됐습니다. 만약 푸시 알림을 보내는 중간에 푸시 서버에 장애가 발생한다면, 어디까지 푸시 알림이 보내졌고, 어디서부터 푸시 알림을 보내야 하는지 알 수 없는 문제가 발생했습니다. 이 문제를 해결하기 위해서는 Queue를 활용해야 한다고 생각했습니다.
Queue를 활용하는 로직은 다음과 같습니다. 만약 100만 명의 게이머 중 특정 게이머의 전적이 수정됐다면, 사용자에게 푸시 알림을 보내야 하기에 전적이 수정된 소환사 정보를 Queue에 담아둡니다. 그렇게 Queue에 푸시 알림을 보내야 하는 소환사 정보가 누적됩니다. 그럼 Queue에 정보가 들어올 때마다 순차적으로 소환사를 즐겨찾기 한 사용자에게 푸시 알림을 보냅니다. 이 과정에서 푸시 서버에 장애가 발생하더라도 어느 소환사까지 푸시 알림을 보냈는지 알 수 있었습니다.
또한 비동기적으로 동작하도록 설정함으로써, 100만 명의 게이머 전적을 조회하고, 전적이 변동됐다면 전적을 수정하는 로직을 큐에 빠르게 넣음으로써 반복문을 빠르게 마무리 지을 수 있었습니다.
마지막으로 Queue를 활용하면 만약 하나의 서버에서 Queue에 적재되어 있는 작업을 처리하기가 힘들어진다면 서버를 늘려서 Queue에 들어있는 작업을 처리한다면 더 빠르게 작업을 처리할 수 있으니 푸시 알림을 보내는 로직이 훨씬 효율적으로 변동될 수 있다고 판단했습니다.
5. 문제 해결 과정을 공유하며 성장하기
POG에서 문제를 해결하는 과정에서 어떤 기술을 어떻게 활용하고 왜 활용해야 하는지 고민하며 고민한 내용을 글로 작성해야겠다고 생각했습니다. 이렇게 글로 작성하는 과정에서 현재 내가 부족한 지식은 무엇인지 하나둘씩 알아갈 수 있었습니다. 그 과정에서 스스로 성장할 수 있었다고 생각했습니다.
문제 해결을 하면서도 코드를 더 잘 작성하기 위해서는 어떻게 해야 할지 고민하기 위해 NestJS, 테스트 코드, 데이터베이스, OOP에 대해 조금씩 꾸준히 공부했습니다. 이렇게 노력한 결과 POG는 투자를 받고 비즈니스적으로 성장할 수 있었습니다. 이렇게 지인에게 도움을 주고, POG에서 나와서 저만의 길을 찾기 위해 노력했습니다.
부족함을 깨닫고 채워가는 것.
POG에서는 저를 포함하여 두 명의 백엔드 개발자가 있었습니다. 한 명은 비즈니스 로직을 담당했고, 한 명은 서버 배포를 담당했습니다. 이 때 각자의 역할이 구분되어 있었기에, 저는 서버 배포 부분을 알지 못했습니다. POG를 마무리하면서, 서버 배포를 어떻게 해야 하는지에 대해 공부해야 한다고 생각했습니다.
그래서 서버 배포 부분을 담당한 팀원을 붙잡고 배포 프로세스에 대해 물어보면서 공부했습니다. 먼저 Docker와 Docker-compose를 활용했고, AWS EB를 활용했으며 Github Action을 활용했습니다. 그럼 먼저 POG에서 배포 프로세스가 어떻게 동작하는지 알 수 있다면 스스로도 배포 프로세스를 구축할 수 있지 않을까 생각했습니다. 이를 위해 배포와 관련된 내용을 공부했습니다.
도커를 공부하면서, 조금씩 배포 프로세스를 익힐 수 있었습니다. 해당 개념을 완벽하게 알 순 없지만 적어도 프로젝트에 작성된 dockerfile, docker-compose 에 작성된 코드가 어떻게 동작하는지 파악할 수 있었습니다.
마치며
혹독한 겨울을 이겨내며, 따뜻한 봄이 오고서야 하얀 꽃을 피운 후 노랗게 변하는 식물인 인동초를 좋아합니다. 추운 겨울을 이겨내는 과정에서 성장할 수 있는 인동초와 같이, 나의 소중한 인생을 위해 추움을 이겨낼 수 있는 사람이 되고 싶습니다. 그 과정에서 나에게 도움을 준 가족과 사랑하는 사람을 기억하면서, 당신에게 도움이 될 수 있는 사람이 되기 위해 잠시 이 글에 기착(寄着 : 목적지로 가는 도중에 어떤 곳에 잠깐 들름.) 하며 2022년의 상반기를 마무리합니다.
나의 도전을 믿어주고, 묵묵히 지켜봐준 당신에게 받은 고마움을 갚을 수 있는 사람이 되기 위해 꾸준히, 그리고 성실하게 노력하겠습니다.
'일상 정리 > 회고' 카테고리의 다른 글
[회고] 2023년 상반기 - 친절한 자세를 흩뜨리지 않도록 (3) | 2023.07.02 |
---|---|
[회고] 2022년 하반기 - 다정함을 닮을 수 있다면 (5) | 2023.01.01 |
[회고] 2021년 하반기 - 돌에 글을 새기면 백 년을 간다 (8) | 2021.12.27 |
[회고] 2021년 상반기 - 스타트업에서 내가 배운 것 (2) | 2021.07.31 |
[회고] 2020년 하반기 - SOPT와 함께한 성장기 (0) | 2021.02.21 |