사이트픽 블로그
← 목록으로

개발일지 2026-08-17

개발일지 2026-08-17

오늘은 피아노 학원 플랫폼(엠포레)에서 두 가지 기능을 완성해 운영에 배포했다. 하나는 관리자 알림톡 발송의 템플릿 변수 동적 바인딩, 다른 하나는 학원 사이트의 본사 공지사항 열람 화면이다. 둘 다 백엔드부터 프론트, DB, 배포까지 풀스택으로 이어진 작업이라 흐름 중심으로 정리한다.

1. 알림톡 템플릿 변수 동적 바인딩 (admin + backend)

문제

관리자 알림톡 발송 화면에서 템플릿을 고르면, 본문에 [*이름*], [*1*], [*2*]… 같은 치환 변수 토큰이 들어있다. 그런데 백엔드가 변수 바인딩 없는 방식으로 발송을 호출하고 있어서, 변수가 있는 템플릿은 빈 값으로 나가거나 뿌리오(발송사)가 거부하는 상태였다.

바인딩 규칙

뿌리오 발송 API는 name(수신자 이름)과 changeWord(번호형 변수 목록) 두 축으로 치환한다.

  • [*이름*] → 수신자별 이름. 수신자마다 다르니 자동 치환(관리자 입력 불필요).
  • [*1*], [*2*]… → 관리자가 공통값을 입력해서 전체 수신자에 동일하게 발송.

원래는 [*1*]=예약일시, [*3*]=강사명처럼 수신자 데이터에서 자동 매핑하는 걸 검토했지만, 이런 값들은 예약 트리거 전용 데이터라 수기 단체발송에는 매핑 소스가 없다. 그래서 번호형은 전부 관리자 입력으로 정리했다.

구현

  • 백엔드: 발송 요청에 changeWord 필드 추가. 발송 서비스에서 템플릿 본문의 번호형 토큰 개수를 정규식(\[\*(\d+)\*\])으로 세어, 입력값 개수와 안 맞으면 거부하는 방어 검증을 넣었다. 발송 호출을 이름+변수 바인딩 방식으로 교체하고, 발송 로그에도 치환이 끝난 실제 본문을 저장하도록 했다.
  • 프론트: 템플릿을 선택하면 본문을 파싱해서 번호형 토큰별 입력 행을 자동 생성한다. [*이름*]은 "수신자 이름 자동 치환" 안내 행(읽기전용)으로 표시하고, 번호형은 각각 입력칸을 만든다. 템플릿의 변수 설명([1]=학원명, [2]=예약일시…)을 파싱해 각 입력칸에 친절한 라벨을 붙였다.
  • 입력 강제: 번호형 변수 중 하나라도 비면 발송 버튼을 비활성화하고 빨간 경고를 띄운다.
  • 미리보기: 우측 미리보기에서 [*이름*]→○○○, [*N*]→입력값으로 실시간 치환해 보여준다. 같은 토큰이 여러 번 나와도 모두 대체된다.

토큰이 없는 템플릿은 기존 동작 그대로(입력행 없음, 변수 없이 발송) 유지된다.

2. 본사 공지사항 열람 화면 (academy + backend)

문제

학원 사이트에는 원래 '앱 공지사항' 화면이 있었지만, 이건 학원이 자기 학생 앱에 노출하는 공지(academy scope)였다. 정작 본사(엠포레)가 전체 가맹 학원에 올린 공지(system scope)를 학원 원장이 볼 수 있는 화면은 없었다. (학생 앱에는 본사+학원 공지 합산 열람이 있었지만, 원장용은 빠져 있었다.)

구현

  • 백엔드: 학원 열람용 엔드포인트 GET /academy/announcements/system 신설. 본사(SYSTEM) scope이면서 발행(PUBLISHED) 상태이고 게시기간이 유효한 공지만 조회하는 쿼리를 추가했다. 게시기간은 start_at ≤ now ≤ end_at으로 판정하되, 시작·종료가 비어있으면 상시로 처리한다.
  • 프론트: 읽기 전용 페이지 신설. 목록(구분/제목/게시일)에서 제목을 누르면 상세 다이얼로그가 열리고, 본문 HTML을 렌더한다. 등록·수정·삭제는 없다.
  • 본문 안전 렌더: 이 앱에는 별도 sanitize 라이브러리가 없어서, 브라우저 파서로 script·이벤트 핸들러·javascript: 링크를 제거한 뒤 표시하는 경량 sanitize를 직접 넣었다. 외부 링크는 새 탭 + noopener로 강제했다.
  • 메뉴/라우팅: 새 화면 라우트를 등록하고, 사이드바 메뉴가 기존 '앱 공지사항'과 경로가 겹치지 않도록 '본사 공지사항' 전용 경로로 정리했다.

마무리

두 작업 모두 "데이터는 이미 있는데 그걸 꺼내 쓰는 통로가 없던" 케이스였다. 알림톡은 뿌리오 바인딩 규칙(이름 자동 / 번호형 수기)을 화면 UX로 풀어냈고, 공지사항은 본사(SYSTEM)와 학원(ACADEMY) scope를 분리한 상태에서 원장이 본사 공지를 보는 창구를 명확히 열었다. 백엔드·프론트 각각 커밋/푸시 후, 백엔드는 도커 이미지로 EC2 컨테이너를 교체하고 프론트는 S3/CloudFront로 배포까지 반영했다.

댓글 0

  • 첫 번째 댓글을 남겨보세요.