pile·
백엔드·8퍼센트8퍼센트·

for 루프에서 transaction.on_commit을 사용할 때 콜백 함수에 인자를 올바르게 넘기는 방법

장고에서 for 루프 안에 transaction.on_commit으로 콜백을 등록할 때 인자가 언제 평가되는지를 놓쳐 생기는 버그와 그 해결법이다. 당첨자 10명에게 알림을 보내려던 코드가 마지막 사용자에게만 10번 보내는 상황을 예로 든다. 익명 함수의 매개변수 기본값으로 값을 붙잡아 해결한다.

핵심 포인트
  • on_commit에 등록한 콜백은 트랜잭션 커밋 후 등록된 순서대로 실행된다.
  • 루프 변수를 그대로 참조하는 람다를 등록하면 실행 시점에는 루프가 끝나 마지막 값만 남아 있다.
  • 결과적으로 열 명에게 한 번씩이 아니라 마지막 한 명에게 열 번 알림이 간다.
  • on_commit은 매개변수 없는 함수를 받으며 호출할 때 인자를 넘겨주지 않는다.
  • 람다의 매개변수 기본값으로 루프 변수를 받으면 그 시점의 값이 고정돼 의도대로 동작한다.
상세 정리
  • 쓰임새: 트랜잭션이 성공적으로 커밋된 경우에만 어떤 동작을 실행해야 할 때 on_commit에 콜백을 등록한다.
  • 문제 코드: atomic 블록 안에서 추첨을 돌리고 당첨자 목록을 순회하며 알림 발송 람다를 등록하는 형태다.
  • 증상: 당첨자마다 한 번씩 발송될 것으로 기대했지만 마지막 사용자 한 명에게만 열 번 발송된다.
  • 원인: 등록 시점에는 콜백 안의 이름만 잡히고 값은 평가되지 않는다. 실제 실행은 순회가 모두 끝난 뒤이므로 그 이름이 가리키는 값은 마지막 사용자다.
  • 트랜잭션 경계와의 관계: 등록은 atomic 블록 안에서 일어나지만 실행은 블록을 빠져나와 커밋이 확정된 뒤이므로 등록과 실행 사이에 시점 차이가 생긴다.
  • 시그니처 제약: on_commit은 매개변수가 없는 함수를 받으므로 필수 매개변수를 가진 람다를 등록하는 방식은 쓸 수 없다.
  • 풀어쓴 형태 1: 문제 코드의 콜백들을 함수로 펼치면 열 개의 함수가 모두 같은 외부 변수를 참조하는 모양이 된다. 실행 시점에 그 변수를 평가하므로 결과가 같아진다.
  • 해결책: 람다의 매개변수에 기본값을 주는 방식으로 등록 시점의 값을 붙잡는다.
  • 풀어쓴 형태 2: 이렇게 하면 각 함수가 첫 번째 사용자, 두 번째 사용자처럼 서로 다른 기본값을 갖게 된다.
  • 동작 근거: 기본값이 정의된 함수는 인자를 넘기지 않고 호출해도 되므로 on_commit이 인자 없이 불러도 각자의 기본값을 쓴다.
  • 발견 난도: 등록 코드만 보면 사용자 수만큼 콜백이 생기므로 정상으로 보이고, 실제 발송 결과를 확인해야 드러난다.
  • 적용 범위: on_commit에 한정된 함정이 아니라 루프 안에서 콜백을 만들어 나중에 실행하는 모든 상황에 해당한다.
  • 예시 시나리오: 추첨 이벤트에서 열 명을 뽑고 당첨자에게 안내를 보내야 하는 상황을 가정한다. 커밋이 확정된 뒤에만 알림이 나가야 하므로 on_commit이 적합한 자리다.
  • 대안 형태의 한계: 콜백 자리에 사용자를 필수 매개변수로 둔 람다를 넣는 방법이 먼저 떠오르지만, on_commit이 인자를 넘기지 않으므로 이 방식은 성립하지 않는다.
  • 등록 순서: 콜백은 등록한 순서대로 실행되므로 순서에 의존하는 후처리도 같은 방식으로 이어 붙일 수 있다.
왜 읽나커밋 후 알림이나 후처리를 루프로 등록했는데 마지막 건만 반복 실행된 적이 있다면 원인과 한 줄 수정이 여기 있다.
8퍼센트
8퍼센트 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 백엔드·twilio-engTwilio Engineering·

    Programmable Messaging에서 Verify API로 마이그레이션하기

    Twilio의 Programmable Messaging API로 자체 OTP 솔루션을 운영하던 서비스가 Verify API로 전환하는 방법을 코드 예시와 함께 설명한다. Verify는 OTP 전송·검증을 위한 전용 API로, 전화번호 구매, 토큰 생성, DB 저장·만료 관리를 내부에서 처리해 개발자가 직접 구현할 코드를 크게 줄인다.

    요약 이어보기
    #authentication#twilio#sms+2
  2. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 2부

    포스타입 백엔드 엔지니어가 벡터 기반 개인화 추천 시스템을 실제 운영하며 맞닥뜨린 성능 장애와 용량 문제를 해결한 과정을 담은 2부다. 수백만 개의 벡터 KNN 검색이 피크 시간대에 전체 Elasticsearch 검색 성능을 흔드는 문제부터 클러스터 OOM 사태까지, 쿼리 최적화와 인프라 분리 두 가지 경로로 근본 해결에 이른다.

    요약 이어보기
    #elasticsearch#vector-search#recommendation-system+2
  3. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 1부

    포스타입이 태그 기반 추천의 한계를 극복하고 벡터 임베딩 기반 개인화 추천 시스템을 구축한 과정을 담은 1부다. 유사한 콘텐츠가 다른 용어를 쓰거나 동일한 태그가 전혀 다른 톤의 콘텐츠를 가리키는 문제를 임베딩 벡터로 해결하고, OpenSearch의 HNSW ANN 검색으로 수백만 벡터를 실시간 검색하는 시스템을 구축해 구매율 15% 향상을 달성했다.

    요약 이어보기
    #opensearch#vector-search#recommendation-system+2