메인 콘텐츠로 건너뛰기

개요

이 튜토리얼에서는 일반적인 예약 워크플로우를 설정합니다:
  • 사용자가 예약을 완료한 후 예약 확인 이메일을 전송합니다.
  • 사용자가 예약을 시작했지만 시간 내에 완료하지 않으면 복구 이메일을 전송합니다.
완료되면 다음을 갖게 됩니다:
  • 두 개의 커스텀 이벤트 (booking_started, booking_complete)
  • 완료 대 방치에 따라 분기하는 하나의 Journey
  • 확인 세부 정보를 위한 예약 Data Feed
  • 복구 인센티브를 위한 선택적 쿠폰 Data Feed
이 가이드는 OneSignal 구성에 중점을 둡니다. 예약 시스템과 백엔드는 모든 언어나 프레임워크로 구현할 수 있습니다.

설정 플로우

  1. 앱이 booking_started 커스텀 이벤트를 추적합니다.
  2. 이것이 사용자를 Journey에 진입시킵니다.
  3. Journey는 booking_complete 이벤트를 기다리고, 시간 내에 수신되지 않으면 후속 리마인더를 전송합니다.
  4. 예약이 완료되면, OneSignal은 전송 시 예약 Data Feed를 호출하고 최신 예약 세부 정보가 포함된 확인 이메일을 전송합니다.
  5. 예약이 대기 시간 내에 완료되지 않으면, Journey는 만료 경로를 따라 복구 이메일을 전송합니다.

설정

전제 조건

시작하기 전에 다음이 있는지 확인하세요:
  • 이메일 채널이 활성화된 OneSignal 앱
  • 예약 및/또는 쿠폰 데이터를 JSON으로 반환할 수 있는 백엔드 엔드포인트
  • 앱, 백엔드 및 OneSignal 외부 ID 간에 공유되는 안정적인 사용자 식별자
  • 커스텀 이벤트에 대한 액세스

1. 예약 이벤트 추적

다음 커스텀 이벤트를 추적하세요. 이들은 앱에서 (SDK 사용) 또는 백엔드에서 (REST API 사용) 올 수 있습니다. 이벤트 이름:
  • booking_started — 사용자가 예약 플로우를 시작할 때
  • booking_complete — 예약이 성공적으로 완료될 때
모바일 SDK 및/또는 웹 SDKtrackEvent() 메서드를 사용하여 앱/웹사이트에서 직접 커스텀 이벤트를 전송하세요.
예시
OneSignal.User.trackEvent("booking_started");
OneSignal.User.trackEvent("booking_complete");
이벤트 추적 시와 백엔드에서 데이터를 반환할 때 동일한 사용자 ID를 사용하세요. 일치하지 않는 ID는 개인화가 누락되는 가장 일반적인 원인입니다.

2. Data Feed 별칭 생성

OneSignal에서 설정 > Data Feeds로 이동하여 다음 별칭을 생성하세요. 예약 Data Feed: 이 피드를 사용하여 전송 시 최신 예약 세부 정보를 가져옵니다.
  • 별칭: booking_data
  • 메서드: GET
  • URL:
엔드포인트 예시
https://your-domain.com/datafeed/booking?user_id={{subscription.external_id}}
응답 예시:
JSON
{
  "first_name": "Sam",
  "last_booking": {
    "service_type": "상담",
    "booking_date": "2026년 1월 22일",
    "booking_time": "오후 2:00",
    "price": 45
  }
}
쿠폰 Data Feed (선택사항): 복구 이메일에 쿠폰 코드를 포함하려면 이 선택적 피드를 사용하세요.
  • 별칭: coupon
  • 메서드: GET
  • URL:
엔드포인트 예시
https://your-domain.com/datafeed/coupon?user_id={{subscription.external_id}}
응답 예시:
JSON
{
  "first_name": "Sam",
  "code": "PROMO8F3K2",
  "discount_text": "10%",
  "expires_in_hours": 2,
  "deep_link": "https://your-domain.com/checkout?coupon=PROMO8F3K2"
}
Data Feed 엔드포인트를 보호하세요. 프로덕션에서는 요청 헤더에 API 키(예: x-api-key)를 전송하고 URL에 비밀을 포함하는 대신 설정 > Data Feeds에서 해당 헤더를 구성하세요.

3. 이메일 템플릿 생성

예약 확인 이메일:

제목:
예약 세부 정보
본문:
{{ data_feed.booking_data.first_name | default: "고객님" }}, 안녕하세요

예약해 주셔서 감사합니다! 예약 세부 정보는 다음과 같습니다:

서비스: {{ data_feed.booking_data.last_booking.service_type }}
날짜: {{ data_feed.booking_data.last_booking.booking_date }}
시간: {{ data_feed.booking_data.last_booking.booking_time }}
가격: {{ data_feed.booking_data.last_booking.price }}

만나 뵙기를 기대하겠습니다!

예약 복구 이메일

제목:
예약을 완료하고 할인 받으세요
본문:
{{ data_feed.coupon.first_name | default: "고객님" }}, 안녕하세요

앞으로 {{ data_feed.coupon.expires_in_hours }}시간 내에 예약을 완료하시면 
이 코드로 {{ data_feed.coupon.discount_text }} 할인을 받으실 수 있습니다:

{{ data_feed.coupon.code }}

여기서 사용하세요:
{{ data_feed.coupon.deep_link }}
Data Feed 필드가 누락된 경우 빈 콘텐츠를 방지하기 위해 Liquid에 항상 default 필터를 포함하세요.

4. Journey 구축

  1. OneSignal에서 메시지 > Journeys > Journey 생성으로 이동
  2. 입장 트리거를 다음으로 설정:
    • 커스텀 이벤트: booking_started
  3. 다음까지 대기 단계 추가:
    • 조건: 커스텀 이벤트 발생
    • 이벤트 이름: booking_complete
    • 최대 대기 시간: 10분
    • 만료 경로 활성화
  4. 분기 구성:
    • 완료됨: 예약 확인 이메일 전송
      • Data Feed: booking_data
    • 만료됨: 복구 이메일 전송
      • Data Feed: coupon
만료 분기를 통해 앱에서 추가 로직 없이 방치를 처리할 수 있습니다. 참조:
  • Journey 설정 - 커스텀 이벤트 입장 및 퇴장 규칙의 세부 정보
  • Journeys 작업 - 다음까지 대기 단계 및 만료 분기의 세부 정보

5. 테스트 및 확인

이벤트 확인

앱이나 백엔드에서 커스텀 이벤트를 트리거하고 확인하세요. OneSignal에서 분석 > 커스텀 이벤트로 이동하여 다음이 표시되는지 확인:
  • 외부 ID에 대해 booking_started 이벤트가 나타남
  • 외부 ID에 대해 booking_complete 이벤트가 나타남

Data Feeds 확인

알려진 사용자 ID를 사용하여 Data Feed 엔드포인트를 수동으로 호출하고 확인:
  • 200 응답이 반환됨
  • 모든 예상 필드가 존재함

이메일 확인

Journey 편집기에서 테스트 메시지를 전송하고 확인:
  • 예약 이메일에 실제 예약 세부 정보가 포함됨
  • 복구 이메일에 유효한 쿠폰이 포함됨
  • Liquid 변수가 비어있게 렌더링되지 않음
개인화가 누락된 경우, Data Feed 요청의 사용자 ID가 Journey를 트리거한 사용자와 일치하는지 확인하세요.

예시: Data Feed 구현

이 예시는 예약 확인 및 복구 Data Feed를 위한 최소 Express 구현을 보여줍니다. JSON 응답 형태가 이메일 템플릿과 일치하는 한 백엔드 언어, 프레임워크 및 데이터 소스는 다를 수 있습니다.

예약 Data Feed 예시

import express from "express";

const app = express();

function dataFeedAuth(req, res, next) {
  if (req.headers["x-api-key"] !== process.env.DATAFEED_API_KEY) {
    return res.status(401).json({ error: "Unauthorized" });
  }
  next();
}

app.get("/datafeed/booking", dataFeedAuth, async (req, res) => {
  const { user_id } = req.query;

  if (!user_id) {
    return res.status(400).json({ error: "Missing user_id" });
  }

  const booking = await getLatestBookingForUser(user_id);

  if (!booking) {
    return res.status(404).json({ error: "No booking found" });
  }

  res.json({
    first_name: booking.first_name,
    last_booking: {
      service_type: booking.service_type,
      booking_date: booking.booking_date,
      booking_time: booking.booking_time,
      price: booking.price
    }
  });
});

쿠폰 Data Feed 예시

app.get("/datafeed/coupon", dataFeedAuth, async (req, res) => {
  const { user_id } = req.query;

  if (!user_id) {
    return res.status(400).json({ error: "Missing user_id" });
  }

  const coupon = await generateCouponForUser(user_id);

  res.json({
    first_name: coupon.first_name,
    code: coupon.code,
    discount_text: coupon.discount_text,
    expires_in_hours: coupon.expires_in_hours,
    deep_link: coupon.deep_link
  });
});

구현 가이드라인

  • 응답을 빠르게 유지 (Data Feeds는 전송 시 호출됨)
  • 항상 예측 가능한 JSON 구조 반환
  • 데이터가 존재하지 않을 때 404 사용
  • 요청 헤더를 통해 전송된 API 키로 엔드포인트 보안

일반적인 문제

이메일에 빈 값 표시

  • Data Feed가 404 반환
  • JSON 응답에서 필드 이름 변경
  • 사용자 식별 불일치

Journey가 분기하지 않음

  • booking_complete 이벤트가 추적되지 않음
  • 이벤트 이름 불일치 (대소문자 구분)
  • 대기 시간 외부에서 이벤트 발생

Data Feed가 401 또는 403 반환

  • API 키 누락 또는 무효
  • Data Feed 설정에서 헤더가 구성되지 않음

다음 단계

  • 더 고급 Journey 조건을 위한 이벤트 속성(서비스 유형, 가격) 추가
  • 푸시 또는 SMS 리마인더와 같은 추가 복구 단계 추가
  • 반복되는 복구 메시지를 방지하기 위해 Journey 종료 규칙 사용