본문 바로가기
개발/개발

[알림] 알림톡 일괄 전송 개발기

by 글쓰는 개발자 2026. 1. 27.

배경 

  • '서비스 알림 동의'를 선택한 유저리스트를 가져와서, 원하는 알림톡을 보내야 한다.
  • 프로필 리스트에 '서비스 알림 동의' 한 사람으로 재조회후 전체 전송 기능을 추가하였다.
  • 기능을 추가하고 보니, 백단에서 '서비스 알림'이 동의한 사람을 재검증하려고 하니, 그 실패할 경우는 어떻게 하지?라는 의문이 생겼다.
  • 전에 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) 팝업창의 발송 버튼 클릭 

  1. 선택된 user_id 10개를 서버로 POST
  2. 서버가 Job 생성, BulkSendJob 테이블에 1건이 생성
  3. 서버가 Target 10개를 BulkSendTarget에 저장(status=QUEUED)
  4. 서버가 Celery에 “이 Job 발송해”를 요청
    • dispatch_job.delay(job_id) 호출
      → 이 순간 Redis에 task 메시지 1개가 쌓임 (“job_id=101 발송 시작해”)
  5. 응답: "발송 작업(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를 활용한 분산 비동기 작업 처리

https://velog.io/@gihwan2/Django-RabbitMQ-Celery-Redis-%EB%B6%84%EC%82%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B3%91%EB%A0%AC-%EC%9E%91%EC%97%85-%EC%B2%98%EB%A6%AC

 

MQ 비교 (Kafka, RabbitMQ, Redis)

https://velog.io/@mdy0102/MQ-%EB%B9%84%EA%B5%90-Kafka-RabbitMQ-Redis

반응형