SQS를 이용한 구조 설정

이진우·2026년 5월 17일

스프링 학습

목록 보기
50/50

배경

특정 유튜버의 영상을 텍스트 기반으로 찾을 수 있게 한 StreamAnalyzer project를 수행하고 있었다.

이 프로젝트는 총 2개의 서버로 구성되어 있는데,

하나는 로컬 Whisper 모델을 활용한 STT, 유튜브 영상 자막 등을 활용하여 나온 텍스트를 벡터화하여 DB에 저장하는 파이썬 기반 서버이고

다른 하나는 사용자가 자유롭게 텍스트 기반으로 질의할 수 있는 메인 백엔드 서버를 구성되어 있다.

특정 스트리머의 모든 영상을 수집·벡터화하는 방식은 처리 비용과 안정성 측면에서 한계가 있었기에 사용자가 직접 유튜브 링크를 전달하면 해당 영상을 추가적으로 벡터화하도록 구조를 변경하였다. 또한 추후 로그인 기능 도입과 서버 역할 분리를 고려하여, 해당 기능은 메인 백엔드 서버에서 처리하도록 설계하였다.

이제는 메인 백엔드 서버와 벡터를 돌리는 서버 사이의 정보를 어떻게 전달할지에 대해 생각해볼 것이다.

결과

마무리 결과 대략적인 아키텍처는 SQS를 통해 두 서버 사이의 정보(벡터화할 유튜브 링크)를 전달하기로 했다.

상황 정리 및 목표

1) 두 서버 사이의 통신 방법을 확정하는 것이 목표이다.
2) 취약한 파이썬 서버

현재 벡터를 돌리는 파이썬 서버는 외부 API나 오픈 소스를 정말 많이 사용한다. 유튜브가 제공하는 API도 사용하고, 유튜브 영상을 크롤링하는 오픈 소스도 사용하다 보니, 유튜브에서 RATE LIMIT에 걸리거나, 유튜브 영상을 크롤링하는 yt-dlp도 유튜브와의 버전이 안맞거나 하면 바로 오류를 뱉어버린다. 뿐만 아니라 영상에서 자동 생성 자막을 끄는 경우가 종종 있는데

이러면 자체 Whisper 모델을 통해 텍스트로 추출하는 과정이 CPU 사용량을 엄청 잡아먹는다.(EC2 프리티어 기준)

즉 정리하면 외부 오픈 소스의 버전 변경에 의존적, 유튜브 제공 API에 의존적, Whisper모델을 로컬에서 돌렸을 때 CPU 사용량이 높아져 파이썬 서버가 장애가 나고 뻗을 확률이 높다.

3) 처리율 제한

로컬 Whisper 모델을 돌릴 때 CPU 사용량이 높다. 이게 하나의 요청만 받아도 이렇게 되는데, 3개 4개를 한다면?? 상상하기 싫다.
로컬 Whisper 모델은 C++ 기반이라 GIL이 막아주지도 않을 것이다.
어느 정도 파이썬 서버에서 필요할 때 꺼내 쓰는 구조가 좋다.

4) 비용

무턱대고 스케일 업과 별도의 인프라 구축을 최대한 지양한다.

5) 확장성

그럴리는 절대 없지만 서비스가 너무 잘되어서 벡터를 만드는 서버를 여러 대 둘 경우를 가정할 것이다. (3번과 충돌되는 면이 있지만 최대한 확장성을 생각한다에 초점을 맞출 것이다)

6) 간편성

이것도 3번과 충돌되는 면이 있을 수 있지만, A라는 기능을 구현하기 위해 더 쉬운 방법이 있고 비용적으로 감당이 가능했을 때 더 쉬운 방법을 택할 것이다.

고려한 대안

HTTP를 이용한 통신

기본

가장 생각하기 좋은 형태이다. RestTemplate이나 WebClient로 스프링에서 파이썬 백엔드로 그냥 HTTP 요청을 하면 끝나는 상태이다.

하지만 기본적인 HTTP 프로토콜의 형태가 요청이 오면 응답 할 때 까지 대기를 하는 "동기적" 구조를 가정했을 때는 단점이 많아 보인다.

1) time out: WebClient 등 http 요청을 했을 때 timeout 을 설정을 할 텐데, 파이썬 서버에서 영상을 벡터화하는 파이프라인의 시간이 들쭉날쭉하여 이를 맞추기 어렵다.

2) 메인 백엔드 서버의 쓰레드 낭비: 메인 백엔드 서버의 쓰레드가 파이썬 서버가 결과를 낼 때까지 대기를 하는 구조는 너무 쓰레드 낭비이다. 유튜브 자막을 통해 벡터화하는 파이프라인은 최소 30~ 40초 이상으로 이 시간 만큼 대기를 하는 것은 효율적이지 않다. (컨텍스트 스위칭 비용은 ... 모르겠다. 아마 쓰레드가 sleep 되면 CPU 스케줄링에 넣어지지 않을텐데 일단 낭비는 맞다)

장점

1) 굳이 따지자면 사용자가 바로 바로 자신의 유튜브 영상 신청 요청이 성공했는지 실패했는지 알 수 있다.

아무튼 이 기본 형태의 문제점은 HTTP가 동기적이라는 데에 있다.

Async

그럼 생각을 틀어서 클라이언트 코드(메인 백엔드 서버)에서 @Async를 통해 비동기 요청을 하면 되지 않을까?

이렇게 하면 동기 요청의 문제점은 해결이 된다.

그런데 요청을 하고, 그 요청을 처리하는 중에 파이썬 서버가 죽을 가능성이 생긴다.

그러면 사용자의 요청은 사라져버리는 것이다. 특히 2번 현재 파이썬 서버가 취약한 서버라고 생각하기에 이런 단점은 크게 부각되는 것 같다.

DB Polling

그럼 http 요청은 포기하고, 이렇게 해보는 거는 어떨까?

특정 유튜브 링크에 대해 벡터처리 해달라는 요청이오면 db에 pending 상태로 넣어놓는 것이다.

그러면 다른 파이썬 워커 서버에서 이를 지속적으로 polling 해서 벡터 처리하는 것이다.

이거는 나름 가능할 것 같다.

일단 보류하도록 하자

SQS

이 구조도 나쁘지 않다. 메인 백엔드 서버에서 sqs 외부 큐로 요청을 넣어놓고, Vector-creating worker에서 자기 스스로 꺼내어 사용할 수 있는 구조다. 일단 보류하도록 하자

kafka 및 Rabbit MQ

Kafka와 Rabbit MQ를 운영하고 학습하는데 드는 비용이 매우 크다는 게 컷 사유이다.

특히 Kafka의 경우 1) 대용량 처리가 필요한 상황 2) Kafka의 영구적인 보존이 필요한 경우 등의 장점이 필요한 상황이 아니다.

아무튼 추가적인 인프라 구축 비용이 매우 크다는 게 문제이다.

DB Polling vs SQS

아까 상황 정리 및 목표를 바탕으로 정해보자.

1) 취약한 파이썬 서버를 고려하는가? 요청이 유실되지는 않는가? -> 둘다 O

2) 처리율을 제한할 방법이 있는가? -> 둘다 O

2) 비용: SQS의 사용량에 따라 다르지만, SQS가 DB 구축에 비해 돈이 들어갈 가능성이 높은 거는 맞다. -> DB Polling 승

3) 확장성: worker 서버가 지속적으로 늘어남에 따라 운영적으로 힘들어지는 것은 DB Polling이다. 워커 서버 마다 DB에 지속적으로 Polling을 해야 함은 물론, 동시성 처리를 위한 Lock, 그에 따른 트랜잭션 관리까지 Database에 대한 부담이 커진다. 반면 SQS는 그냥 consumer를 늘려버리면 끝이다.

4) 간편성: DLQ, 재시도, 모니터링 등의 관점에서 보았을 때 SQS가 압도적이다.

즉 비용이 sqs가 더 들어갈 수 있지만, 요청의 수에 따라 가격이 결정되어 큰 부담이 안된다는 점, 확장성과 간편성이 높다는 점을 들어 SQS를 최종적으로 두 서버 사이의 연결 통로로 설정하게 되었다.

결론

결론은 sqs를 채택하게 되었고 최종적으로 아래 그림과 같은 흐름을 가지게 되었다.

profile
기록을 통해 실력을 쌓아가자

0개의 댓글