[실전 서버리스 16] Queues로 비동기 처리, 무거운 작업 뒤로 미루기

앞 편에서 큐를 잠깐 소개했는데, 실제로 가장 자주 쓰는 도구라 따로 파고들 만하다. 큐의 핵심은 무거운 작업을 사용자 요청에서 떼어내 뒤로 미루는 것이다. 사용자는 빠른 응답을 받고, 무거운 일은 뒤에서 처리된다. 이번 편은 큐로 비동기 처리를 하는 실전을 다룬다.


서버리스에서 응답 속도는 곧 사용자 경험이자 비용이다. 무거운 일을 요청 안에서 처리하면 느리고 비싼데, 큐가 이걸 해결한다.

1. 왜 비동기가 필요한가

사용자가 어떤 동작을 하면, 그에 따라 여러 일이 벌어진다. 예를 들어 글에 이미지를 올리면, 이미지를 저장하고, 압축하고, 썸네일을 만들고, 알림을 보내는 등의 일이 필요하다. 이걸 다 사용자 요청 안에서 처리하면, 사용자는 그 모든 게 끝날 때까지 기다려야 한다.


문제는 이 중 일부가 무겁고 느리다는 것이다. 이미지 압축이나 썸네일 생성은 시간이 걸린다. 이런 걸 요청 안에서 하면 응답이 느려지고, 서버리스에서는 처리 시간 한도에 부딪힐 수도 있다. 앞서 다룬 CPU 시간도 그만큼 쓴다. 사용자 경험과 비용, 한도 모두에 나쁘다.


그래서 무거운 일은 뒤로 미룬다. 사용자에게 꼭 필요한 응답, 즉 이미지가 올라갔다는 것만 빠르게 돌려주고, 압축이나 썸네일 같은 후처리는 큐에 넣어 뒤에서 한다. 사용자는 즉시 응답을 받고, 무거운 일은 사용자를 기다리게 하지 않고 진행된다. 이게 비동기 처리의 핵심이다.


비동기의 필요성은 무거운 작업을 겪어보면 절실해진다. 이미지 압축이나 동영상 변환을 사용자 요청 안에서 하면, 사용자가 그게 끝날 때까지 기다려야 하고 처리 시간 한도에도 걸린다. 그래서 이런 무거운 일은 뒤로 미루는 게 필수다. 사용자에게 꼭 필요한 응답만 즉시 주고 나머지는 뒤에서 처리한다.


한 가지 더, 비동기 처리는 사용자에게 진행 상태를 알려주는 설계와 함께 가면 좋다. 무거운 작업을 뒤로 미루면 사용자는 그게 언제 끝나는지 궁금할 수 있다. 그래서 처리 중이라는 상태를 보여주거나, 끝나면 알려주는 장치를 두면 경험이 매끄럽다. 미룬 작업의 결과를 사용자가 자연스럽게 확인할 수 있게 하는 것이다.

2. 큐의 동작 방식

큐는 두 부분으로 나뉜다. 작업을 넣는 쪽과 꺼내 처리하는 쪽이다. 사용자 요청을 처리하는 워커가 무거운 작업을 큐에 넣는다. 그러면 별도의 처리기가 큐에서 작업을 꺼내 처리한다. 넣는 것과 처리하는 게 분리돼 있어서, 넣는 쪽은 빠르게 끝나고 처리는 뒤에서 독립적으로 돈다.


큐는 작업을 모아서 한꺼번에 처리하기도 한다. 하나씩 처리하는 것보다 여러 개를 묶어 처리하는 게 효율적일 때가 있기 때문이다. 예를 들어 알림 여러 개를 모아 한 번에 보내는 식이다. 이 묶음 처리가 자원을 아낀다. 큐가 작업을 어떻게 묶고 언제 처리할지를 조절할 수 있다.


그리고 큐는 처리 실패에 대비한다. 작업 처리가 실패하면 다시 시도한다. 그래도 계속 실패하는 작업은 따로 빼두어 나중에 확인할 수 있게 한다. 그래서 큐에 넣은 작업은 잃어버리지 않고 확실히 처리되거나, 실패해도 흔적이 남는다. 반드시 처리돼야 하는 작업을 안심하고 맡길 수 있다.


큐가 넣는 쪽과 처리하는 쪽으로 분리된 게 핵심 구조다. 사용자 요청 워커는 작업을 큐에 넣기만 하고 빠르게 끝나고, 별도 처리기가 뒤에서 큐를 비운다. 이 분리 덕에 넣는 건 빠르고 처리는 독립적으로 돈다. 사용자는 처리를 기다리지 않고, 처리는 사용자 응답 시간에 매이지 않는다.

3. 무엇을 큐로 넘길까

큐로 넘길 작업을 고르는 기준은 무겁거나 급하지 않은 것이다. 사용자가 즉시 결과를 볼 필요가 없고, 처리에 시간이 걸리는 작업이 후보다. 이미지 후처리, 알림 발송, 통계 집계, 외부 서비스 연동 같은 것들이다. 이런 건 뒤에서 처리해도 사용자 경험에 지장이 없다.


반대로 큐로 넘기면 안 되는 것도 있다. 사용자가 즉시 결과를 봐야 하는 작업이다. 예를 들어 글을 저장하고 그 결과를 바로 보여줘야 한다면, 그건 요청 안에서 처리해야지 큐로 미루면 안 된다. 큐는 지연을 감수하는 작업에만 쓴다. 무엇을 미룰 수 있고 무엇은 즉시 해야 하는지 구분하는 게 중요하다.


내 커뮤니티에서는 미디어 후처리를 주로 큐로 넘긴다. 사용자가 동영상을 올리면 올라갔다는 것만 즉시 응답하고, 그 동영상을 적절히 변환하고 썸네일을 만드는 무거운 작업은 큐로 넘겨 뒤에서 처리한다. 사용자는 기다리지 않고, 변환은 알아서 진행된다. 이 분리가 사용자 경험을 크게 개선했다.


무엇을 큐로 넘길지 고르는 기준이 중요하다. 무겁고 급하지 않은 것, 즉 사용자가 즉시 결과를 볼 필요 없는 작업이 후보다. 반대로 사용자가 바로 결과를 봐야 하는 건 요청 안에서 처리한다. 이 구분을 잘못하면 즉시 필요한 걸 큐로 미뤄 사용자가 결과를 못 보는 사고가 난다. 미룰 것과 즉시 할 것을 나누는 게 핵심이다.


또 하나, 큐로 넘긴 작업이 실패했을 때의 대비도 중요하다. 큐는 실패를 재시도하지만 계속 실패하는 작업은 따로 빼둔다. 이 실패한 작업 목록을 주기적으로 확인해, 왜 실패하는지 파악하고 고쳐야 한다. 큐에 넣었다고 끝이 아니라, 실패한 것들이 어떻게 되는지까지 챙기는 게 완결된 비동기 처리다.

4. 큐와 CPU 시간 과금

큐는 앞서 다룬 CPU 시간 과금과도 잘 맞는다. 무거운 작업을 사용자 요청 안에서 처리하면 그 요청의 CPU 시간이 길어져 과금이 늘고 한도에 가까워진다. 큐로 넘기면 그 무거운 처리가 별도로 돌아, 사용자 요청 자체는 가볍게 유지된다.


또 큐 처리기는 사용자 응답 시간에 매이지 않아서, 상대적으로 여유 있게 무거운 작업을 할 수 있다. 사용자가 기다리는 요청은 빨라야 하지만, 뒤에서 도는 큐 처리는 조금 오래 걸려도 된다. 그래서 무거운 작업은 큐로 넘겨 여유 있는 환경에서 처리하는 게 여러모로 낫다. 급한 것과 안 급한 것을 나누는 것이다.


이렇게 급한 것은 요청에서 빠르게, 안 급한 것은 큐에서 여유 있게 처리하면, 비용과 사용자 경험이 함께 좋아진다. 앞서 CPU 시간 과금 편에서 코드를 과금 구조에 맞게 짜라고 했는데, 큐가 바로 그 방법의 하나다. 무거운 작업을 요청에서 떼어내는 것이다.


큐가 CPU 시간 과금과 잘 맞는 것도 실용적 이점이다. 무거운 작업을 요청 안에서 하면 그 요청의 CPU 시간이 길어져 비용이 늘고 한도에 가까워진다. 큐로 넘기면 사용자 요청은 가볍게 유지되고, 무거운 처리는 여유 있는 환경에서 돈다. 급한 것과 안 급한 것을 나누니 비용과 경험이 함께 좋아진다.

5. 비동기 설계의 감각

큐를 쓰다 보면 비동기 설계의 감각이 생긴다. 어떤 일을 즉시 해야 하고 어떤 일을 뒤로 미룰 수 있는지 구분하는 감각이다. 사용자에게 꼭 필요한 최소한만 즉시 처리하고, 나머지는 뒤로 미루는 것이다. 이 구분이 사이트를 빠르고 저렴하게 만든다.


이 감각은 서버리스만이 아니라 시스템 설계 전반에 쓰인다. 무거운 일을 사용자와 분리하고, 확실한 처리를 큐로 보장하고, 실패에 대비하는 것. 이런 원리는 어떤 규모의 시스템에도 통한다. 큐를 배우는 건 단순히 한 도구를 배우는 게 아니라, 비동기라는 설계 방식을 익히는 것이다.


정리하면 큐는 무겁거나 급하지 않은 작업을 사용자 요청에서 떼어내 뒤에서 처리하게 해서, 응답을 빠르게 하고 비용과 한도 부담을 줄이며, 비동기 설계의 감각을 길러준다. 다음 편에서는 또 하나의 중요한 자동화 도구, 정기적으로 작업을 돌리는 크론 트리거를 다룬다.


큐를 쓰다 보면 비동기 설계의 감각이 생긴다. 어떤 일은 즉시, 어떤 일은 뒤로 미룰 수 있는지 구분하는 감각이다. 이건 서버리스만이 아니라 시스템 설계 전반에 통하는 원리다. 큐를 배우는 건 단순히 한 도구를 배우는 게 아니라, 무거운 일을 사용자와 분리하는 비동기라는 설계 방식을 익히는 것이다.


덧붙이면, 비동기 처리를 도입하면 사이트가 한결 견고해진다. 무거운 작업이 사용자 응답을 막지 않으니 트래픽이 몰려도 응답이 빠르게 유지되고, 큐가 실패를 재시도하니 일시적 문제로 작업이 누락되지 않는다. 빠름과 견고함을 동시에 얻는 것이다. 큐를 도입한 뒤로 사이트가 부하에 훨씬 강해졌다.


다음 편에서는 또 하나의 중요한 자동화 도구, 정기적으로 작업을 돌리는 크론 트리거를 다룬다. 서버 없이 통계 집계와 캐시 채우기, 정리 작업을 자동으로 돌리는 방법이다.