메인 콘텐츠로 건너뛰기
이러한 복사/붙여넣기 HTML 템플릿을 사용하여 OneSignal의 맞춤형 인앱 메시지를 더 빠르게 구축하세요. 저희의 인앱 메시지 HTML 에디터를 사용하면 HTML, CSS, JavaScript를 사용하여 인앱 메시지 레이아웃과 동작을 완전히 제어할 수 있습니다. 에디터에는 내장 템플릿이 포함되지 않지만, 이 페이지에서는 에디터에 붙여넣고 사용자 지정할 수 있는 바로 사용 가능한 예제를 제공합니다.
이러한 템플릿은 인앱 메시지 웹뷰 내에서 실행됩니다. 메시지를 닫고, URL을 열고, 사용자를 태그하고, 클릭을 캐표하려면 인앱 메시지 JS API를 사용하세요.

전제 조건

시작하기 전에 다음을 검토하는 것이 좋습니다:
템플릿 코드에 비밀 정보(API 키, 토큰)를 넣지 마세요. 모든 인앱 메시지 입력을 신뢰할 수 없는 것으로 취급하고 앱 또는 백엔드에서 유효성을 검사하세요.

템플릿 사용 방법

  1. OneSignal에서 메시지 > 인앱 > 새 인앱으로 이동합니다.
  2. HTML 에디터를 선택합니다.
  3. 아래에서 템플릿을 찾습니다.
  4. 코드 블록에서 전체 HTML을 복사하여 에디터에 붙여넣습니다.
  5. 자리 표시자(URL, 엔드포인트, 날짜, 콘텐츠)를 업데이트합니다.
  6. 실제 기기에서 테스트한 후 게시합니다.

사용 가능한 템플릿

email-form

이메일 수집 양식

사용자의 이메일을 요청하고 클릭 이름을 통해 앱에 전송합니다.
sms-form

전화번호 수집 양식

SMS 전송에 대한 동의를 요청하고 얻습니다. E.164 형식의 전화번호를 포함하고 클릭 이름을 통해 앱에 전송합니다.
checklist-survey

체크리스트 설문조사

백엔드에 보내거나 태그로 변환할 수 있는 다중 선택 설문조사.
count_down

카운트다운

시간 제한 프로모션용 카운트다운 타이머.
promo-wheel

프로모 휠

돌리기-투-윈 프로모 경험(프로모 처리 사용자 지정).
quiz_modal

퀴즈 모달

사용자에게 점수로 태그를 지정할 수 있는 퀴즈 경험.
ranking-survey

평점 설문조사

1–5점 평점 설문조사(엔드포인트로 전송 또는 사용자 태그).
ui-ui

오디오/비디오 플레이어

직접 MP3 파일용 간단한 오디오 미리보기 UI.
vertical-swiping

세로 스와이프

다중 슬라이드 세로 스와이프 온보딩 또는 기능 투어.

이메일 양식

인얁 메시지를 통해 이메일 구독을 수집합니다. 이 양식의 작동 방식:
  1. 사용자가 이메일 주소를 입력하고 동의 체크박스를 체크합니다.
  2. 제출 시 OneSignal의 Create User API가 호출되어 앱에서 이메일 구독이 생성됩니다.
  3. 또한 템플릿이 OneSignalIamApi.addClickName(e, email)을 호출하여 이메일 주소를 SDK의 인앱 메시지 클릭 리스너에 전달합니다.
  4. 앱 내에서 인얁 메시지 클릭 리스너를 추가하여 클릭 이름을 읽고 이메일을 SDK의 addEmail 메서드에 전달할 수 있습니다.
2단계와 4단계 모두 이메일 구독 생성을 포함한다는 것을 알 수 있습니다.
  • 2단계는 앱에 직접 코드를 추가할 필요가 없지만 login 메서드를 호출한 경우 사용자에게 이메일 구독을 추가하지도 않습니다.
  • 4단계는 추가 코드(인얁 메시지 클릭 리스너)가 필요하지만 login 메서드를 호출한 경우에도 사용자에게 이메일 구독을 추가합니다.
**설정 > 키 및 ID에서 찾은 OneSignal 앱 ID로 YOUR_APP_ID를 교체하세요.
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <!-- iOS 입력 포커스 시 확대 방지 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <style>
        /* ===== 리셋 ===== */
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        /* ===== 기본 ===== */
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: transparent;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            padding: 20px;
        }
        
        /* ===== 카드 ===== */
        .container {
            position: relative;
            background: #ffffff;
            border-radius: 16px;
            padding: 32px 24px;
            max-width: 340px;
            width: 100%;
            box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
            text-align: center;
        }
        
        /* ===== 닫기 버튼 ===== */
        .close-btn {
            position: absolute;
            top: 12px;
            right: 12px;
            background: none;
            border: none;
            font-size: 24px;
            color: #999;
            cursor: pointer;
            padding: 4px 8px;
            line-height: 1;
        }
        
        .close-btn:hover {
            color: #333;
        }
        
        /* ===== 타이포그래피 ===== */
        h1 {
            font-size: 22px;
            font-weight: 600;
            color: #333;
            margin-bottom: 8px;
        }
        
        p {
            font-size: 14px;
            color: #666;
            margin-bottom: 24px;
            line-height: 1.5;
        }
        
        /* ===== 이메일 입력 ===== */
        .email-input {
            width: 100%;
            padding: 14px 16px;
            font-size: 16px; /* iOS 자동 확대 방지를 위한 16px */
            border: 2px solid #e0e0e0;
            border-radius: 10px;
            margin-bottom: 16px;
            outline: none;
            transition: border-color 0.2s;
            touch-action: manipulation;
        }
        
        .email-input:focus {
            border-color: #007AFF;
        }
        
        .email-input::placeholder {
            color: #aaa;
        }
        
        /* ===== 동의 체크박스 ===== */
        .consent-wrapper {
            display: flex;
            align-items: flex-start;
            gap: 10px;
            text-align: left;
            margin-bottom: 16px;
        }
        
        .consent-wrapper input[type="checkbox"] {
            width: 18px;
            height: 18px;
            margin-top: 2px;
            cursor: pointer;
            flex-shrink: 0;
        }
        
        .consent-wrapper label {
            font-size: 13px;
            color: #666;
            line-height: 1.4;
            cursor: pointer;
        }
        
        /* ===== 제출 버튼 ===== */
        .submit-btn {
            width: 100%;
            padding: 14px 24px;
            font-size: 16px;
            font-weight: 600;
            color: #fff;
            background: #007AFF;
            border: none;
            border-radius: 10px;
            cursor: pointer;
            transition: background 0.2s, opacity 0.2s;
        }
        
        .submit-btn:hover {
            background: #0056b3;
        }
        
        .submit-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            opacity: 0.7;
        }
        
        /* ===== 상태 메시지 ===== */
        .error-msg,
        .success-msg {
            font-size: 12px;
            margin-top: -12px;
            margin-bottom: 12px;
            display: none;
        }
        
        .error-msg {
            color: #e53935;
        }

        .success-msg {
            color: #4CAF50;
            font-size: 14px;
        }
        
        /* ===== 로딩 상태 ===== */
        .loading {
            pointer-events: none;
            opacity: 0.7;
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- 닫기 버튼 -->
        <button id="close-btn" class="close-btn" data-onesignal-unique-label="close-button">×</button>
        
        <!-- 헤더 -->
        <h1>연결을 유지하세요!</h1>
        <p>이메일을 입력하여 업데이트와 독점 오퍼를 받아보세요. 언제든 구독을 취소할 수 있습니다!</p>
        
        <!-- 이메일 입력 -->
        <input 
            type="email" 
            id="email-input" 
            class="email-input" 
            placeholder="이메일을 입력하세요" 
            data-onesignal-unique-label="email-input" 
            autocomplete="email" 
            autocapitalize="off"
        >
        
        <!-- 상태 메시지 -->
        <p id="error-msg" class="error-msg">유효한 이메일 주소를 입력하세요</p>
        <p id="success-msg" class="success-msg">구독해 주셔서 감사합니다!</p>
        
        <!-- 동의 체크박스 -->
        <div class="consent-wrapper">
            <input type="checkbox" id="consent-checkbox" name="consent">
            <label for="consent-checkbox">마케팅 이메일 수신에 동의합니다</label>
        </div>
        
        <!-- 제출 버튼 -->
        <button id="submit-btn" class="submit-btn" data-onesignal-unique-label="submit-email" disabled>
            구독하기
        </button>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            
            // ===== DOM 참조 =====
            var emailInput = document.getElementById("email-input");
            var submitBtn = document.getElementById("submit-btn");
            var closeBtn = document.getElementById("close-btn");
            var errorMsg = document.getElementById("error-msg");
            var successMsg = document.getElementById("success-msg");
            var consentCheckbox = document.getElementById("consent-checkbox");
            
            // ===== 도움 함수 =====
            
            // 이메일 형식 유효성 검사
            function isValidEmail(email) {
                return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
            }
            
            // 이메일이 유효하고 동의가 체크된 경우에만 제출 버튼 활성화
            function updateSubmitState() {
                var email = emailInput.value.trim();
                submitBtn.disabled = !(isValidEmail(email) && consentCheckbox.checked);
            }
            
            // OneSignal API를 통해 이메일 구독 생성
            async function createOneSignalUser(email) {
                // ⚠️ OneSignal 앱 ID로 교체
                var appId = "YOUR_APP_ID";
                var url = "https://api.onesignal.com/apps/" + appId + "/users";
                
                var payload = {
                    properties: {
                        tags: { email_created_from: "iam" },
                        language: "ko"
                    },
                    subscriptions: [{
                        type: "Email",
                        token: email,
                        enabled: true
                    }]
                };
                
                var response = await fetch(url, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(payload)
                });
                
                if (!response.ok) throw new Error("API 요청이 실패했습니다");
                return response.json();
            }
            
            // ===== 이벤트 리스너 =====
            
            // 닫기 버튼 - IAM 닫기
            closeBtn.addEventListener("click", function(e) {
                OneSignalIamApi.close(e);
            });
            
            // 체크박스 - 버튼 상태 업데이트
            consentCheckbox.addEventListener("change", updateSubmitState);
            
            // 이메일 입력 - 버튼 상태 업데이트 및 오류 숨기기
            emailInput.addEventListener("input", function() {
                errorMsg.style.display = "none";
                updateSubmitState();
            });
            
            // 이메일 입력 - Enter 키로 제출
            emailInput.addEventListener("keypress", function(e) {
                if (e.key === "Enter" && !submitBtn.disabled) {
                    submitBtn.click();
                }
            });
            
            // 제출 버튼 - 구독 처리
            submitBtn.addEventListener("click", async function(e) {
                var email = emailInput.value.trim();
                
                if (!isValidEmail(email) || !consentCheckbox.checked) {
                    errorMsg.textContent = "유효한 이메일 주소를 입력하세요";
                    errorMsg.style.display = "block";
                    return;
                }
                
                // OneSignal 클릭 핸들러에 이메일 전달(동기적으로 호출해야 함)
                OneSignalIamApi.addClickName(e, email);
                
                // 로딩 상태 표시
                errorMsg.style.display = "none";
                submitBtn.textContent = "구독 중...";
                submitBtn.classList.add("loading");
                
                try {
                    await createOneSignalUser(email);
                    
                    // 성공 - 메시지 표시 및 자동 닫기
                    successMsg.style.display = "block";
                    submitBtn.textContent = "구독 완료!";
                    
                    setTimeout(function() {
                        closeBtn.click();
                    }, 1500);
                    
                } catch (error) {
                    // 오류 - 재설정 및 메시지 표시
                    submitBtn.textContent = "구독하기";
                    submitBtn.classList.remove("loading");
                    errorMsg.textContent = "문제가 발생했습니다. 다시 시도해 주세요.";
                    errorMsg.style.display = "block";
                }
            });
        });
    </script>
</body>
</html>
4단계에 필요한 사항:
  1. HTML 제출 핸들러에서 addClickName 호출을 유지하세요.
  2. SDK의 인앱 메시지 클릭 리스너로 입력을 읽어오세요
  3. 클릭 이름이 이메일처럼 보일 때 인앱 메시지 클릭 리스너 내에서 addEmail 메서드를 호출하세요.
인앱 메시지 클릭 리스너와 addEmail 메서드를 사용한 예제:
// HTML 인앱 메시지에서 이메일과 전화를 캡처하는 인앱 메시지 클릭 핸들러 예제
class InAppMessageClickHandler: NSObject, OSInAppMessageClickListener {
    func onClick(event: OSInAppMessageClickEvent) {
        // 이벤트에서 클릭 이름(actionId) 가져오기
        let clickName = event.result.actionId
        print("인앱 메시지 클릭, actionId: \(clickName ?? "nil")")
        
        guard let value = clickName else { return }
        
        // 클릭 이름이 이메일 주소처럼 보이는지 확인
        if value.contains("@") && value.contains(".") {
            OneSignal.User.addEmail(value)
            print("OneSignal에 이메일 추가: \(value)")
        }
        // 클릭 이름이 E.164 형식의 전화번호처럼 보이는지 확인(+1XXXXXXXXXX)
        else if value.hasPrefix("+") && value.count >= 11 {
            OneSignal.User.addSms(value)
            print("OneSignal에 SMS 추가: \(value)")
        }
    }
}

SMS 양식

인앱 메시지를 통해 SMS 구독을 수집합니다. 이 양식의 작동 방식:
  1. 사용자가 국가 코드를 선택하고 10자리 번호를 입력한 후 동의 체크박스를 체크합니다.
  2. 제출 시 OneSignal의 Create User API가 호출되어 앱에서 SMS 구독이 생성됩니다.
  3. 또한 템플릿이 OneSignalIamApi.addClickName(e, e164Phone)을 호출하여 전화번호를 SDK의 인앱 메시지 클릭 리스너에 전달합니다.
  4. 앱 내에서 인앱 메시지 클릭 리스너를 추가하여 클릭 이름을 읽고 전화번호를 SDK의 addSms 메서드에 전달할 수 있습니다.
2단계와 4단계 모두 SMS 구독 생성을 포함한다는 것을 알 수 있습니다.
  • 2단계는 앱에 직접 코드를 추가할 필요가 없지만 login 메서드를 호출한 경우 사용자에게 SMS 구독을 추가하지도 않습니다.
  • 4단계는 추가 코드(인앱 메시지 클릭 리스너)가 필요하지만 login 메서드를 호출한 경우에도 사용자에게 SMS 구독을 추가합니다.
**설정 > 키 및 ID에서 찾은 OneSignal 앱 ID로 YOUR_APP_ID를 교체하세요.
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <style>
        /* 스타일은 이메일 양식과 동일하나 전화 입력용으로 조정 */
        * { box-sizing: border-box; margin: 0; padding: 0; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: transparent;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            padding: 20px;
        }
        .container {
            position: relative;
            background: #ffffff;
            border-radius: 16px;
            padding: 32px 24px;
            max-width: 340px;
            width: 100%;
            box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
            text-align: center;
        }
        /* 기타 스타일... */
    </style>
</head>
<body>
    <div class="container">
        <button id="close-btn" class="close-btn">×</button>
        <h1>SMS로 최신 소식을 받아보세요!</h1>
        <p>전화번호를 입력하여 중요한 업데이트를 SMS로 받아보세요.</p>
        
        <div class="phone-input-wrapper">
            <select id="country-select" class="country-select">
                <option value="+1">🇺🇸 +1</option>
                <option value="+82">🇰🇷 +82</option>
                <!-- 기타 국가 코드... -->
            </select>
            <input type="tel" id="phone-input" class="phone-input" placeholder="1234567890" maxlength="10">
        </div>
        
        <div class="consent-wrapper">
            <input type="checkbox" id="consent-checkbox">
            <label for="consent-checkbox">SMS 마케팅 메시지 수신에 동의합니다</label>
        </div>
        
        <button id="submit-btn" class="submit-btn" disabled>구독하기</button>
    </div>
    
    <script>
        // JavaScript 로직(이메일 양식과 동일하나 전화번호 처리용으로 조정)
        // 자세한 구현은 이메일 양식과 동일한 패턴
    </script>
</body>
</html>

기타 템플릿

위의 이메일 및 SMS 양식 외에도 다음 템플릿들도 사용할 수 있습니다:
  • 체크리스트 설문조사: 다중 선택 설문조사
  • 카운트다운: 시간 제한 프로모션
  • 프로모 휠: 돌리기-투-윈 경험
  • 퀴즈 모달: 점수 기반 퀴즈
  • 평점 설문조사: 1–5점 평가
  • 오디오/비디오 플레이어: 미디어 미리보기
  • 세로 스와이프: 다중 슬라이드 투어
각 템플릿의 상세한 HTML 코드와 구현 단계는 위의 카드 링크에서 각 섹션을 참조하세요.