메세지란?
시스템 간에 주고받는 요청의 단위이다.
- 시스템 A가 시스템 B에 “이런 일을 해 달라” 또는 “이런 데이터를 처리했다”라는 의미 있는 정보 단위를 보낼 때, 이 정보를 하나의 ‘메시지’라고 부른다.
메세지 큐의 개념
메시지 큐(Message Queue)는 비동기적으로 서로 다른 시스템(또는 모듈) 간에 데이터를 주고받기 위한 버퍼(대기열) 역할을 한다.
메시지를 보내는 쪽과 받는 쪽이 직접 통신하는 대신, 중간에 '메시지 큐'라는 임시 저장소를 두어 메시지를 간접적으로 전달하는 방식을 의미한다.
메세지 큐 구성요소 3가지
- 생산자: 메세지를 만들어서 목적지에 보내는 주체. (klick lab에선 sdk)
- 메세지 큐: 생산자가 보낸 메시지를 임시로 안전하게 저장하는 장소. (klick lab에선 Amazon SQS)
- 소비자: 큐에 쌓여있는 메세지를 가져와서 처리하는 주체 (klick lab에선 lambda)
메세지 큐의 장점, 쓰는 이유
- 비동기 처리로 → 응답 속도 개선
- 생산자는 소비자에게 요청을 보낸 뒤, 소비자가 일을 마칠 때까지 기다릴 필요 없이 바로 자신의 다른 일을 할 수 있음.
- 의존성 제거 → 장애 격리
- 생산자가 소비자에게 직접 요청을 보내는 게 아니고 큐에 보내는 것이므로 소비자 모듈에 문제가 생겨도 생산자는 일단 정상적으로 동작할 수 있음.
- 부하 분산 및 버퍼링 → 소비자의 연산 작업 안정화
- 대량의 요청이 몰려들 때, 메세지 큐는 이 요청을 차곡차곡 쌓아두는 완충재 역할을 함. 소비자는 자신의 컴퓨팅 속도에 맞춰 작업을 처리할 수 있음
- 임시 보관소 → 데이터 유실 우려 완화
- 소비자가 메세지를 성공적으로 처리했다는 응답을 받기 전까진 큐에 메세지가 남아있음. 처리 도중 오류로 작업이 중단되도 메세지가 남아있기 때문에 재 처리 가능.
klicklab에 메세지 큐가 없다면?
시나리오 1: SQS가 없는 경우 (동기 방식)
[아키텍처]
SDK (클라이언트)
→ API Gateway
→ 하나의 거대한 처리 서버 (Lambda)
[동작 흐름]
- sdk가 api gateway를 통해 서버에 이벤트 처리를 요청(로그 데이터 적재)
- 서버는 요청을 받은 즉시 작업 시작
- 요청 데이터 검증
- 데이터 변환
- db(ClickHouse)에 데이터 쓰기
- (등등...)
- db에 쓰기가 완료되고 모든 작업이 끝나는 데 2초가 소요됐다고 가정
- 이 2초동안 sdk는 아무 것도 못하고 서버의 응답만 기다림.
- 2초가 지난 후, 서버가 "모든 작업 완료!"라는 응답을 보내주면 그제야 SDK는 다음 동작을 이어감
sqs없는 klicklab sdk가 초래할 수 있는 최악의 시나리오
javascript는 싱글 스레드 환경에서 동작함. 렌더링, 애니메이션 등 모든 것이 하나의 스레드에서 동작함.
- [상황]: 고객사의 쇼핑몰 사이트에 우리 SDK가 설치되어 있음. 한 사용자가 이 사이트에서 '구매하기' 버튼을 클릭.
- [SDK 작동]: 바로 그 순간, 우리 SDK가 "사용자가 구매 버튼을 클릭했다"는 로그를 klicklab 서버에 전달.
- [UI 정지 (UI Freeze)]: lambda가 ClickHouse에 데이터를 쓰고 응답을 줄 때까지 2초 동안, 고객사 웹사이트의 메인 스레드는 '정지’.
- [사용자 경험 붕괴]: 이 2초 동안 아래와 같은 현상이 발생.
- 사용자가 화면의 다른 버튼을 아무리 눌러도 전혀 반응이 없음.
- 페이지 스크롤이 멈추거나 심하게 버벅댐.
- 입력창에 글자를 쳐도 화면에 보이지 않음.
- 부드럽게 움직이던 모든 애니메이션이나 화면 전환 효과가 모두 멈춤.
- 웹사이트 전체가 마치 '얼어붙은' 것처럼 보임.
- [결과]:
- 이 현상은 '구매하기' 버튼 뿐만 아니라, SDK가 로그를 보내는 모든 사용자 행동(페이지 조회, 다른 버튼 클릭 등)에서 동일하게 발생.
- 결론적으로, SDK가 언블록(unblock)될 때까지는 사용자가 웹페이지에서 할 수 있는 거의 모든 시각적, 상호작용적 행위가 불가능해짐.
- 이것이 바로 서드파티 스크립트에서 동기/블로킹 방식이 금기시되는 이유.
시나리오 2: SQS가 있는 경우 (비동기 방식) - 현재 우리 아키텍처
[아키텍처]
SDK (클라이언트)
→ API Gateway
→ SQS
→ 처리 서버 (Lambda)
[동작 흐름]
- sdk가 api gateway를 통해 서버에 이벤트 처리를 요청(로그 데이터 적재)
- API Gateway는 그 요청을 SQS에 enqueue만 함. 이 작업은 0.05초(50ms)면 충분.
- SQS에 메시지가 안전하게 들어간 것이 확인되면, API Gateway는 즉시 SDK에 요청 접수 성공했다는 응답을 보냄.
- SDK는 0.05초 만에 응답을 받고 blocking 상태에서 빠져나옴. 자유롭게 다른 일을 할 수 있음.
- 한편, 백그라운드에서는 처리 서버(Lambda)가 SQS에서 메시지를 가져가 자신의 속도에 맞춰 실제 작업을 처리. 이 과정은 생산자인 SDK와 완전히 분리되어 일어남.
SQS가 없다면 SDK(클라이언트)는 서버의 모든 작업이 끝날 때까지 '붙잡혀 있는' 동기(Blocking) 상태.
SQS는 이 클라이언트와 서버 사이의 강한 연결을 끊어주는 역할을 하여, SDK가 즉시 응답을 받고 자유로워지는 비동기(Non-blocking) 환경을 만들어줌. 결론적으로 ux를 보호하고 시스템 확장성을 확보