배경
- '서비스 알림 동의'를 선택한 유저리스트를 가져와서, 원하는 알림톡을 보내야 한다.
- 프로필 리스트에 '서비스 알림 동의' 한 사람으로 재조회후 전체 전송 기능을 추가하였다.
- 기능을 추가하고 보니, 백단에서 '서비스 알림'이 동의한 사람을 재검증하려고 하니, 그 실패할 경우는 어떻게 하지?라는 의문이 생겼다.
- 전에 AWS의 SQS로 알림톡 전송자 리스트를 보내고 알림톡을 전송하기 위해서는 다시 서버로 들어와서 파이썬워커를 통해 전송이 되도록 구성이 되어있었다.
- 가비아(Python Worker) -> AWS(SQS) -> 가비아(Python Worker) -> 전송완료
- 걱정되는 부분 :
- 네트워크 왕복 지연
- 외부 장애 지점 증가
- 트래픽/비용/운영 복잡도 증가
- 서버 안에서 알림톡을 보낸다고 하면, 굳이 외부 AWS로 보냈다가 다시 돌아오는 설계는 맞지 않다고 생각했다.
요구사항
- 7만 건의 알림톡을 일괄전송해야 한다
- 주기는 그렇게 많이 않음. 일주일에 1번 정도?
- 분석툴을 이용하여 나중에 고도화하는 작업이 필요하다
- 전송이 실패한다면 어떤 유저가 실패했는지 알고 싶다
- 가능하다면 재전송 기능까지 필요하다
- 발송은 오전 10시-6시 사이에 발송할 예정
- 단일서버에서 보낼 수 있어야 한다
변경 방안
1. AWS SQS -> 내부 큐로 변경
Redis VS RabbitMQ?
- 둘 다 메시지 브로커로써 많이들 사용하나 빠른 개발, 낮은 운영 복잡도를 생각하여 Redis를 선택 하였다.
- RabbitMQ가 우선순위/라우팅/DLQ의 기능이 있고, 대량작업 시에는 많이들 사용하지만,
나의 경우에는 혼자서 개발과 운영을 함께 해야 하고 알림톡기능을 만들었으나 얼마나 사용할지 알 수 없기에
우선 Redis로 뼈대를 구성한 다음, 나중에 RabbitMQ로 변환하는 흐름으로 만들어야겠다고 결정했다.
2. 파이썬 워커 -> Celery로 변경
- "DB 원장(Job/Target) + 비동기 워커 + 재시도/재큐잉"가 핵심인데, Celery가 제공하고 있다. 아니면 내가 개발해야 함.
- 운영이 되고 있다면 부분적으로 변경을 해야 하나, 사용하지 않기에 Celery로 변경하여 개발하기로 함.
3. 대량 발송 작업 관리할 DB추가
- 기존의 테이블은 발송 1건당 기록 1건을 관리하기 위한 테이블이다. 따라서 1000건을 일괄적으로 발송한다 하면 1000건이 등록되는 방식.
- 추가할 테이블은 “알림톡 대량 전송을 하나의 작업(Job)으로 관리하기 위한 테이블"
- 예를 들어, 한 번에 여러 개의 알림톡을 전송하게 된다면,
1. BulkSendJob에 1행 생성, 대량 발송 작업 1건
BulkSendJob
| id | tpl_code | status | total_target | sent | failed | skipped | filters_json |
| -- | ----------- | -------- | -----------: | ---: | -----: | ------: | ----------------------------------- |
| 41 | CASTING_001 | BUILDING | 0 | 0 | 0 | 0 | `{agree:true, region:"seoul", ...}` |
2. BulkSendTarget에 작업에 포함된 대상자 N명, N행 생성
BulkSendTarget
| id | job_id | profile_id | status | attempt_count |
| ----: | -----: | ---------: | ------ | ------------: |
| 10001 | 41 | 501 | QUEUED | 0 |
| 10002 | 41 | 502 | QUEUED | 0 |
| ... | 41 | ... | QUEUED | 0 |
3. 기존 테이블에 기록. 실제 발송 시도 로그, 0~N건
작업흐름예상
Step A) 팝업창의 발송 버튼 클릭
- 선택된 user_id 10개를 서버로 POST
- 서버가 Job 생성, BulkSendJob 테이블에 1건이 생성
- 서버가 Target 10개를 BulkSendTarget에 저장(status=QUEUED)
- 서버가 Celery에 “이 Job 발송해”를 요청
- dispatch_job.delay(job_id) 호출
→ 이 순간 Redis에 task 메시지 1개가 쌓임 (“job_id=101 발송 시작해”)
- dispatch_job.delay(job_id) 호출
- 응답: "발송 작업(Job)이 시작되었습니다"라는 메시지 문구를 띄워주고 팝업창 닫기
Step B) Celery 워커가 Redis에서 task(메시지)를 꺼냄
Celery의 작업함수 dispatch_job(job_id) 호출
역할: 작업 관리자
- DB에서 BulkSendTarget (status=QUEUED) 조회
- 지금은 10명이니까 한 묶음, 7만이면 1000명씩 분할
- 각 묶음마다 다음 작업을 큐에 넣음
- send_chunk.delay(job_id, target_ids) 큐잉
→ Redis에 1개 메시지 쌓임
send_chunk.delay(job_id, target_ids)
Celery의 작업함수 send_chunk(job_id, target_ids) 호출
역할: 실제 발송 담당자
- 각 타겟에 대해:
- 대상자 하나씩 처리
- 동의 재검증 (비동의면 SKIPPED)
- 알림톡 API 호출
- 결과를 DB에 기록
- 성공하면 SENT / 실패면 FAILED
- BulkSendTarget 상태를 SENT/FAILED/SKIPPED로 업데이트
- BulkSendJob의 집계(sent_count/failed_count/skipped_count) 업데이트
- job 집계 업데이트
참고
[Lablup Conf 5th] 우리 서비스의 Redis는 왜 매일 장애가 날까? (강대명)
https://www.youtube.com/watch?v=KrFffRlvHAg
rabbitmq와 redis의 차이
https://ctsictai.medium.com/rabbitmq%EC%99%80-redis%EC%9D%98-%EC%B0%A8%EC%9D%B4-8a5403c0267f
[Django + RabbitMQ + Celery + Redis] Celery를 활용한 분산 비동기 작업 처리
MQ 비교 (Kafka, RabbitMQ, Redis)
https://velog.io/@mdy0102/MQ-%EB%B9%84%EA%B5%90-Kafka-RabbitMQ-Redis
'개발 > 개발' 카테고리의 다른 글
| [스크랩 기능]폴더 하나 만들면서 UX와 설계를 같이 고민해본 이야기_1 (1) | 2026.02.11 |
|---|---|
| 풀스택개발자가 TDD를 도전하며 겪은 어려움1(feat.claude를 곁들인) (0) | 2026.02.05 |
| Python 워커 와 Django 워커의 차이 (0) | 2026.01.17 |
| [알림] 서비스 알림 설정 유도 페이지 기획 (1) | 2026.01.10 |
| [알림톡]카카오 알림톡 개발 관련 생각해봐야 할 것들 (0) | 2025.12.22 |