Notice
Recent Posts
Recent Comments
반응형
관리 메뉴

HM소프트

대량 메일 템플릿 변수 치환 + 값 미리보기 엔진 (1) 본문

기술 개발일지

대량 메일 템플릿 변수 치환 + 값 미리보기 엔진 (1)

HM소프트 2026. 6. 11. 17:11
반응형

대량 메일 자동화 도구의 템플릿 변수 치환 엔진 구현. 리치 텍스트 에디터가 변수 글자 사이에 HTML 태그를 끼워 넣어도 잡아내는 유연 정규식을 동적 생성하고, 흔한 경우는 replace로 거르는 2단 치환으로 비용을 줄인다. 엑셀 파싱 단계의 빈 셀·타입 정규화, 발송 전 컬럼 값 분포를 보여주는 값 미리보기로 틀린 메일 사고를 막는다.

 

시리즈를 시작하며

수신자마다 내용이 다른 메일을 한 번에 보내는 도구를 만들었다. 표(스프레드시트)로 받은 수신자 데이터를 올리면, 템플릿의 변수가 각 행의 값으로 치환되어 개인화된 메일이 나가고, 행마다 다른 PDF를 첨부할 수도 있다.

세 편으로 나눠 기록한다.

  1. (이 글) 템플릿 변수 치환과 값 미리보기 엔진
  2. 수신자별 PDF 첨부 — 업로드부터 매칭·발송까지
  3. 프론트/백 경계와 Docker 배포

백엔드는 Python, 프론트엔드는 TypeScript다. 고객사 정보는 빼고 구현만 다룬다.

구조: 작성은 프론트, 발송은 백엔드

역할을 명확히 갈랐다. 프론트엔드(MailComposer)는 메일을 작성하고 미리보기를 보여주고, 백엔드는 실제 발송과 첨부 처리를 한다. 둘은 REST API로만 통신한다.

핵심: 템플릿 변수 치환

가장 기본이 되는 기능이다. 본문에 변수를 심어두면, 업로드한 표의 컬럼 값으로 수신자마다 다르게 채워진다.

안녕하세요 {{name}}님,
{{company}}의 {{product}} 견적서를 보내드립니다.

치환 자체는 간단해 보이지만, 실무에서 흔들리는 건 엣지 케이스다.

  • 값이 비어 있는 행 ({{name}}이 빈 칸)
  • 특수문자가 든 값 (&, <, 따옴표 → HTML 메일에서 깨짐)
  • 템플릿엔 있는데 표엔 없는 컬럼 ({{phone}} 인데 phone 컬럼 부재)

이 부분을 안정화하는 데 공을 들였다. 누락 컬럼은 빈 문자열로 안전하게 처리하고, HTML 메일이므로 값은 이스케이프해서 레이아웃이 깨지지 않게 했다.

import html, re

def render(template: str, row: dict[str, str]) -> str:
    def sub(m):
        key = m.group(1).strip()
        return html.escape(str(row.get(key, "")))   # 누락은 빈 값, 값은 이스케이프
    return re.sub(r"\{\{\s*([\w]+)\s*\}\}", sub, template)

차별점: 컬럼 값 미리보기(스캔)

대량 발송에서 제일 무서운 건 "변수만 박아놓고 발송 버튼을 눌렀는데 엉뚱한 값이 들어가는" 사고다. 그래서 작성기에서 컬럼을 선택하면 그 컬럼의 실제 값들을 스캔해서 보여주는 기능을 넣었다.

발송 전에 "이 변수엔 실제로 이런 값들이 들어간다"를 눈으로 확인할 수 있다. 작은 기능이지만, 대량 발송의 사고를 막는 가장 효과적인 안전장치였다.

명함(서명) 이미지와 이메일 레이아웃

메일 하단에 들어가는 명함/서명 이미지 기능을 붙이고, 이메일 레이아웃을 정리했다. HTML 메일은 메일 클라이언트마다 렌더링이 제각각이라(테이블 기반 레이아웃, 인라인 스타일 요구 등), 어디서 열어도 깨지지 않게 다듬는 게 생각보다 일이 많았다.

정리하며

1편의 핵심은 개인화의 정확성과 안전성이다.

  • 템플릿 치환은 엣지 케이스(빈 값·특수문자·누락 컬럼)에서 안정적이어야 한다.
  • HTML 메일이므로 값 이스케이프는 필수.
  • 컬럼 값 미리보기가 발송 전 사고를 막는 핵심 안전장치였다.

다음 2편에서는 이 도구에서 가장 공들인 기능, 수신자별 PDF 첨부를 단계별로 다룬다.

반응형