안녕하세요! 오랜만에 블로그 포스팅으로 써보고싶은 주제가 생각나서 컴퓨터 앞에 앉았습니다. 이번 주제는 실제로 있을법한 일을 재구성해서 엔지니어링적으로 문제를 해결하는 과정을 써볼까합니다.
요즘 엉덩이 무거운 금융권도 AI를 도입하려고 여기저기서 난리다보니 국내기준 모든 개발자 수요가 줄었지만 AI엔지니어는 수요가 폭발하고 있는 상황입니다.
하지만 저는 AI엔지니어링.. 그 끝은 백엔드와 닿아있다는걸 깨닫게 되는건 그리 오래걸리지 않았습니다.
상황은 이렇습니다. 고객이 AI를 도입하는데 그래픽카드 서버를 한대만 주겠다고 하는 상황이죠. 이때 우리가 할 수 있는 다양한 해결책을 생각해봅시다.
sLLM을 올리는건 문제가 안됩니다만 문제는 사용자마다 들어나는 KV Cache이죠. 보통 나쁘지않은 질답이 오가려면 토큰이 16k정도는 있어야하고 멀티턴을 고려한다면 더 필요할 수도 있습니다. 요즘 그래픽카드 서버 한대에 올리기 가장 좋은 모델은 Gemma 4로 소문이 자자하죠.
Gemma 4의 32k가 점유하는 KV Cache는? 15GB정도 됩니다. 물론 요즘 양자화 기술이 좋아져서 웬만하면 다 8비트로 양자화 해버리긴 합니다만 사용자가 전직원이면 많이 부족하죠.
case 1. 메세지 큐잉을 도입하는 경우
가장 먼저 떠올릴 수 있는 방법입니다. 그리고 가장 쉽게 문제가 해결되는 경우이기도 하죠. 대규모 서비스에서 EDA와 AI엔지니어링은 궁합이 좋습니다. EDA가 지금껏 성행하는 이유도 중간 버퍼로 서버 부하를 막고 메세지를 안정적으로 도달하게끔 만드는데 특화된 EDA는 RDB랑 궁합이 좋았기 때문이죠. RDB는 커넥션 풀도 정해져있고 "이만큼만 요청을 받고싶습니다"의 대표주자이기 때문에 확실히 궁합이 좋죠.
LLM도 마찬가지입니다. 이만큼의 요청만 받고싶은 LLM도 EDA와 궁합이 매우 좋습니다. 실제로 대규모 서비스를 하는 곳에서는 EDA와 LLM을 많이 붙여서 사용합니다.
side effect 1. 유지보수
관리 포인트가 늘어나는건 어쩔 수 없는 문제이긴 합니다. 다만 Kafka나 Redis는 유지보수하기 꽤 까다로워서 그냥 추가합시다 하고 추가할 수 있는건 아니긴 하죠.
side effect 2. 단일 장애 지점
카프카가 죽으면? 레디스가 죽으면? 이 질문에 대한 답이 정해져있어야합니다. 이 질문에 대한 답이 없으면 MQ가 죽는순간 그래픽카드 서버도 OOM으로 죽어버리거든요.
case 2. 우린 서버 많은데요?
우리 솔루션은 Active Standby로 들어가서 서버 많아요~
그럼 HA 솔루션을 기대해볼만합니다. Kafka Cluster와 Redis Sentinel 같은거말이죠. 이것도 물론 방법이 될 수 있습니다.
side effect 1. 관리 포인트 어떡하지..
하나도 관리포인트가 쉽지않은데 여러개를 하겠다면 그것도 말리진 않겠습니다만 각오는 해야할 것입니다. 큰 힘에는 큰 책임이 따르는 것이니까요.
side effect 2. 디버깅 어떡하지..
요청을 비동기로 미뤄버리면 디버깅이 지옥인건 잘 아는 사실이실겁니다. 근데 그마저도 세개로 나눠지고있으니 어디서 요청을 보냈는지 어디가 문제가 생겼는지 알기 쉽지않죠.
case 3. 코드로 해결
이 얘길 하고싶었습니다. 이 포스팅을 쓰게된 계기이기도 하죠. 들어가기 전에 제 얘기를 조금 하고 넘어가겠습니다.
저는 문제를 항상 구조적으로 해결하는걸 좋아했습니다. 그리고 잘했죠. 위에서처럼 아키텍처로 풀어나가는 문제도 좋았지만 코드 내부에서도 전략패턴 어댑터패턴 프록시패턴 등 디자인패턴으로 문제를 해결하는것도 좋아했습니다. 요즘 AI가 이렇게 만들어줘 저렇게 만들어줘 하면 뚝딱 나오니 좋았습니다.
그래서 점점 제 약점이 드러나고 있었습니다. 바로 알고리즘이요.
그래픽카드 서버가 한대밖에 없어서 부하를 막아야한다 -> 그럼 메세지 큐 도입 -> 단일 장애 지점은? -> HA로 해결
꼴에 메세지 큐 도입하는건 관리포인트를 줄이기 위해서 레디스를 쓴다는 얘기를 했으니 말 다했죠.
더 쉽고 좋은 해결책이 알고리즘에 있었습니다. 바로 Bounded Queue와 Token Bucket 알고리즘이요.
Bounded Queue는 큐의 개수를 무한히 만들지 않습니다. 1000개면 1000개 이렇게 정해놓고 다 차면 429 (Too Many Request) 를 반환해서 자바스크립트에서 재시도를 요청하는거죠. 물론 이 재시도도 지수 백오프로 랜덤하게 안하면 큐 생기자마자 대기하고 있던 클라이언트가 다같이 미친듯이 요청보내서 서버를 죽여버릴 수도 있습니다. 이게 그 유명한 Thundering Herd죠.
Token Bucket은 1초에 생성할 수 있는 토큰을 제한하고 요청은 이 토큰을 물고 들어올 수 밖에 없게 만듭니다. 제한된 토큰 이외의 토큰은 전부 삭제되죠. 그럼 요청이 예측가능해집니다. 그리고 우리 그래픽카드 서버의 자원도 예측할 수 있죠. 그럼 수용량을 제한할 수 있습니다.
Bounded Queue와 Token Bucket 이 두개를 쓰면 카프카니 레디스니 안써도 된다는 장점이 생깁니다.
마치며
당분간 제 공부 방향은 이쪽으로 갈 것 같네요. 아키텍처 관점에서 문제를 해결하는건 몇 년동안 해왔던 것이니 이제 방향을 조금 틀어볼까 합니다. 이제 제가 가지고 있는 모든 아키텍처 관점의 문제 해결을 알고리즘으로 해결해보려고 노력해보겠습니다.
물론 이 둘이 같이 쓰면 좋지만 일단 한쪽 테크트리를 탔으니 반대쪽 테크트리도 한번 타보려고합니다.
포스팅은 여기서 마무리짓도록 하겠습니다. 긴 글은 아니었지만 그래도 봐주셔서 감사합니다. 오늘도 즐거운 하루 되세요!
'AI Engineering > 실습' 카테고리의 다른 글
| 특명: 추론 속도를 올리기 위한 온몸 비틀기 (Speculative Decoding) (0) | 2026.01.19 |
|---|---|
| AI 엔지니어링과 EDA의 만남 (0) | 2026.01.15 |