본문 바로가기
🤖 1인 에이전트 구축기

인스타그램 DM 자동화 구축: Meta API·LLM 답변·상담원 전환

by BRIEFER 2026. 6. 18.

인스타그램 프로페셔널 계정의 반복 DM을 자동화하되, 권한·정책·개인정보·상담원 전환을 먼저 설계하려는 운영자와 개발자를 위한 구현 가이드입니다.

핵심은 “LLM이 모든 메시지에 답하게 하기”가 아닙니다. 사용자가 먼저 시작한 대화를 웹훅으로 받고, 요청 서명을 검증하고, 허용된 질문만 승인된 지식으로 답하며, 불확실하거나 민감한 사안은 상담원에게 넘기는 것이 안전한 출발점입니다.

이 글의 범위
예제는 로컬 모의 웹훅만 처리합니다. 실제 Meta API나 LLM API를 호출하지 않으며, 액세스 토큰·고객 대화·연락처를 사용하지 않습니다. Meta 제품 화면, 권한 이름, API 버전과 정책은 바뀔 수 있으므로 운영 반영 직전에 공식 문서를 다시 확인하세요.

먼저 확인할 구축 전제

Meta의 Instagram 플랫폼 개요에 따르면 API 대상은 비즈니스 또는 크리에이터 유형의 Instagram 프로페셔널 계정입니다. 로그인 구성에 따라 조건도 달라집니다.

항목 Instagram 로그인 구성 Facebook 로그인 구성
계정 연결 Instagram에만 존재하는 프로페셔널 계정 지원 Facebook 페이지와 연결된 프로페셔널 계정 필요
API 호스트 graph.instagram.com graph.facebook.com
메시지 권한 예시 instagram_business_basic, instagram_business_manage_messages instagram_basic, instagram_manage_messages
메시징 제품 Instagram API의 메시징 기능 Messenger 플랫폼의 Instagram 메시징 구성

공식 문서가 설명하는 액세스 수준도 구분해야 합니다.

  • Standard Access: 개발자가 소유·관리하거나 앱 역할에 추가한 계정으로 개발·테스트할 때 사용하는 기본 범위입니다.
  • Advanced Access: 소유하거나 관리하지 않는 다른 프로페셔널 계정에 서비스를 제공할 때 필요합니다. Meta 문서는 이 경우 앱 검수와 비즈니스 인증이 필요하다고 설명합니다.
  • 메시징에는 로그인 방식에 맞는 메시지 관리 권한이 필요합니다. 최소 권한만 요청하고, 앱 대시보드에서 실제 승인 상태를 확인합니다.
  • 웹훅 콜백은 유효한 TLS/SSL 인증서를 사용하는 HTTPS 엔드포인트여야 합니다. Meta의 웹훅 설정 문서는 자체 서명 인증서를 지원하지 않는다고 명시합니다.
  • 프로덕션 수신 전에는 앱 모드, 웹훅 필드 구독, 프로페셔널 계정의 구독 활성화, 앱 역할과 계정 권한을 각각 확인합니다.

메시지를 보낼 수 있는 조건

Instagram Messaging API 문서는 대화가 Instagram 사용자의 메시지로 시작되어야 하며, 앱은 사용자가 프로페셔널 계정에 메시지를 보낸 경우에만 답할 수 있다고 설명합니다. 일반 응답은 사용자의 메시지 이후 24시간 안에 보내야 합니다.

인간 상담원이 문제 해결에 시간이 더 필요한 경우를 위한 Human Agent 기능도 있지만, 이는 임의의 마케팅 후속 발송용이 아닙니다. 플랫폼 개요는 허용된 용도를 “표준 메시지 응답 기간 안에 해결되지 않은 문제에 대한 인간 상담원 지원”으로 설명하며, 해당 태그의 기간과 조건을 별도로 제시합니다. 운영 전 최신 조건과 승인된 사용 사례를 확인하세요.

따라서 다음과 같은 설계는 피합니다.

  • DM을 보내지 않은 사람에게 먼저 광고 메시지 발송
  • 응답 기간을 우회하기 위한 태그 사용
  • 고객 동의나 근거 없이 과거 대화로 프로필·리드 점수 생성
  • LLM 판단만으로 할인, 환불, 주문 변경 같은 계정 작업 실행

권장 아키텍처: 수신과 답변을 분리한다

Instagram 사용자
  → Meta Webhook
  → HTTPS 수신기
      1. X-Hub-Signature-256 검증
      2. 원문 JSON 스키마 검증
      3. event_id/mid 원자적 중복 예약
      4. 최소 필드만 내부 큐에 저장
  → 정책 필터
      ├─ 승인된 FAQ + 충분한 근거 → 답변 후보
      └─ 불확실·민감·금지·고객 요청 → 상담원 큐
  → LLM(선택 사항)
      1. 개인정보 제거
      2. 승인된 지식만 검색
      3. 구조화된 답변 후보 생성
      4. 출력 정책 재검사
  → 전송 어댑터 또는 상담원 받은편지함
  → 감사 로그(원문 대신 최소 메타데이터)

웹훅 수신기는 생성 모델을 기다리지 말고, 검증된 이벤트를 내구성 있는 큐에 기록한 뒤 빠르게 성공 응답을 반환하는 편이 좋습니다. 이는 Meta가 강제하는 단일 아키텍처가 아니라, 타임아웃·중복 부작용을 줄이기 위한 운영 권장안입니다.

웹훅 검증과 중복 방지

Meta 웹훅 문서는 이벤트 원문을 앱 시크릿으로 HMAC-SHA256 서명하고 X-Hub-Signature-256: sha256=... 헤더에 담는다고 설명합니다. 공식 문서는 검증을 권장하며, 프로덕션에서는 검증 실패 요청을 처리 큐에 넣지 않는 것이 안전합니다.

검증할 때는 다음 원칙을 지킵니다.

  1. JSON 파싱 전의 원시 바이트로 HMAC을 계산합니다.
  2. 일반 문자열 비교 대신 상수 시간 비교 함수를 사용합니다.
  3. 이벤트 식별자(메시지 이벤트라면 mid 등)를 DB의 unique key로 원자적으로 예약합니다.
  4. 이미 처리한 식별자는 성공으로 확인하되 답변·이관 같은 부작용은 반복하지 않습니다.
  5. 서명 오류, 필수 필드 누락, 과도한 본문 크기는 재시도하지 않고 거부합니다.

Meta의 일반 웹훅 문서는 실패한 알림을 재전송할 수 있으므로 중복 제거를 구현하라고 안내합니다. 문서에는 Messenger 이벤트의 전달 빈도는 별도라고도 적혀 있으므로, 특정 재시도 횟수나 시간을 Instagram DM의 고정 보장처럼 가정하지 마세요.

LLM은 답변자가 아니라 ‘제약된 답변 후보 생성기’로 둔다

RAG를 붙여도 잘못된 답변이 사라지지는 않습니다. 오래된 상품 문서, 잘못 검색된 구절, 프롬프트 주입, 모델의 추론 오류가 남을 수 있습니다. 다음 세 단계를 분리하세요.

1. 입력 정책 필터

다음 메시지는 자동 답변하지 않고 상담원에게 넘기는 것을 기본값으로 둡니다.

유형 예시 처리
고객의 명시적 요청 “상담원과 이야기할래요” 즉시 이관
계정·금전 작업 환불, 취소, 주문 변경, 결제 분쟁 본인 확인 절차가 있는 상담원 큐
고위험 조언 의료 진단·처방, 법률 자문, 투자 추천 금지 답변 후 전문 상담 경로 안내
개인정보 포함 연락처, 주소, 계정 비밀번호 LLM 전송 금지, 최소 권한 상담 처리
낮은 신뢰도 승인 지식에서 근거를 찾지 못함 추측하지 않고 이관
공격·위협·안전 문제 신체 위험, 자해·타해 등 사전 정의된 안전 절차와 사람 검토

2. 승인된 지식 검색

가격, 재고, 배송, 반품 규정에는 source_id, 승인자, 유효 시작일, 만료일을 둡니다. 실시간 값은 검색 문서의 자연어가 아니라 해당 업무 시스템의 읽기 전용 조회 결과를 우선합니다. 조회 실패 시 “재고 있음”처럼 추측하지 않습니다.

3. 출력 정책 필터

LLM 출력에는 다음을 허용하지 않습니다.

  • 근거에 없는 가격·재고·효능·배송일 보장
  • 고객 특성에 따른 차별적 제안이나 자격 판단
  • 비밀번호, 결제정보, 주민등록번호 같은 민감정보 요청
  • 상담원인 척하거나 자동화 사실을 숨기는 표현
  • 승인되지 않은 쿠폰 발급, 주문 변경, 환불 확정
  • 다른 고객의 대화나 개인정보 노출

모델이 내놓은 답은 바로 보내지 말고 answer, source_ids, confidence, handoff_reason 같은 구조로 제한한 뒤 정책 엔진이 최종 전송 여부를 결정하게 합니다.

자동화 고지와 상담원 전환

Messaging API 문서는 관련 법률이 요구하는 경우 대화 시작, 상당한 시간이 지난 뒤, 사람 대화에서 자동화 환경으로 전환될 때 자동화 사실을 고지해야 한다고 설명합니다. 법적 의무가 없는 경우에도 사용자 기대 관리를 위해 고지를 권장합니다.

첫 자동 응답은 다음처럼 짧고 명확하게 구성할 수 있습니다.

안녕하세요. 이 대화는 자동 응답 도우미가 먼저 확인합니다. 주문 변경·환불·개인정보가 포함된 문의는 상담원에게 전달하겠습니다. “상담원”이라고 입력하면 사람에게 연결합니다.

상담원 큐에는 원문 전체를 무기한 복제하지 말고 업무에 필요한 범위만 표시합니다.

{
  "conversation_ref": "CONVERSATION_DEMO",
  "event_ref": "MID_DEMO_011",
  "reason": "human_requested_or_account_action",
  "received_at": "DEMO_TIMESTAMP",
  "priority": "normal",
  "message_preview": "[최소화된 테스트 문구]"
}

상담원이 답한 뒤에는 담당자, 처리 시각, 적용한 정책 버전, 전송 결과를 기록하되 액세스 토큰과 앱 시크릿은 로그에서 제외합니다.

개인정보 최소 수집·보관·삭제

Meta 플랫폼 약관은 공개적으로 접근 가능한 개인정보처리방침을 제공하고, 처리 데이터·방법·목적·삭제 요청 방법을 명확히 설명하도록 요구합니다. 또한 적법한 목적에 더 이상 필요하지 않거나 사용자 또는 Meta가 삭제를 요청하는 등의 경우 플랫폼 데이터를 삭제하도록 규정합니다.

운영 정책에는 최소한 다음을 문서화하세요.

  • 수집: 메시지 처리에 필요한 이벤트 ID, 대화 참조, 수신 시각, 이관 이유만 우선 저장
  • 비수집: 비밀번호, 결제수단 원문, 불필요한 프로필 속성, 전체 대화의 무기한 복사
  • LLM 전달: 목적에 필요하지 않은 사용자 ID·연락처·주소를 제거하고, 선택한 LLM 제공업체가 서비스 제공업체로서 약관·계약·지역 규정을 충족하는지 검토
  • 보관: 데이터 종류별 목적과 삭제 시점을 정하고 자동 삭제 작업을 운영. 예를 들어 “일반 로그 30일”은 내부 정책의 예시일 뿐 모든 사업자에게 적용되는 공식 기간이 아닙니다.
  • 삭제 요청: 개인정보처리방침과 상담 채널에서 찾기 쉬운 요청 경로를 제공하고, 운영 DB·검색 인덱스·분석 저장소·백업의 처리 절차를 연결
  • 보안: 앱 시크릿·토큰은 서버 측 시크릿 저장소에 두고, 최소 권한·암호화·접근 로그·회수 절차 적용

Meta 약관은 LLM 제공업체를 쓴다는 이유로 책임이 이전된다고 보지 않습니다. 서비스 제공업체와 서면 조건을 마련하고, 처리를 중단할 때 해당 업체가 보유한 플랫폼 데이터도 삭제되도록 해야 합니다.

API 키와 고객정보의 저장·유출 대응은 API 키 유출 방지 체크리스트에서 더 자세히 다룹니다.

로컬 모의 웹훅: 서명·정책·이관·재시도 테스트

아래 코드는 네트워크를 사용하지 않습니다. Meta가 공개한 웹훅 모양을 단순화한 합성 payload를 만들고 다음 항목만 검증합니다.

  • 원시 본문의 HMAC-SHA256 서명
  • mid 기반 중복 이벤트 차단
  • 승인 FAQ와 상담원 이관 정책
  • 일시 오류만 최대 3회까지 재시도하는 내부 전송 함수
  • 20개 합성 문장의 기대 경로 일치 여부

이 예제의 APP_SECRET은 로컬 테스트 전용 문자열입니다. 실제 앱 시크릿이나 액세스 토큰으로 바꾸어 글·저장소·로그에 남기지 마세요.

#!/usr/bin/env python3
"""Local-only Instagram webhook policy demo. No network or paid API calls."""
from __future__ import annotations

import hashlib
import hmac
import json
import re
import time
from dataclasses import dataclass
from typing import Callable

APP_SECRET = b"LOCAL_DEMO_APP_SECRET_NOT_FOR_PRODUCTION"
SEEN_EVENT_IDS: set[str] = set()

FAQ = {
    "배송": "배송 일정은 주문 화면의 안내를 확인해 주세요.",
    "운영시간": "상담 운영시간은 평일 업무시간입니다.",
    "반품 절차": "반품 가능 여부는 주문 조건 확인 후 상담원이 안내합니다.",
}
HANDOFF_WORDS = ("상담원", "환불", "취소", "분쟁", "불만", "주문 변경")
HIGH_RISK_WORDS = ("진단", "처방", "법률 자문", "투자 추천", "계정 비밀번호")
PII_PATTERN = re.compile(
    r"(?:\b01[016789][- ]?\d{3,4}[- ]?\d{4}\b|"
    r"\b[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}\b)"
)


@dataclass(frozen=True)
class Decision:
    route: str
    reason: str
    reply: str | None = None


def sign(raw_body: bytes) -> str:
    digest = hmac.new(APP_SECRET, raw_body, hashlib.sha256).hexdigest()
    return f"sha256={digest}"


def verify_signature(raw_body: bytes, header: str) -> bool:
    return hmac.compare_digest(sign(raw_body), header)


def policy_filter(text: str) -> Decision:
    normalized = " ".join(text.strip().split())
    if PII_PATTERN.search(normalized) or "연락처" in normalized or "주소" in normalized:
        return Decision("handoff", "personal_data")
    if any(word in normalized for word in HIGH_RISK_WORDS):
        return Decision("handoff", "high_risk_or_prohibited")
    if any(word in normalized for word in HANDOFF_WORDS):
        return Decision("handoff", "human_requested_or_account_action")
    for keyword, approved_reply in FAQ.items():
        if keyword in normalized:
            return Decision("auto_reply", "approved_faq", approved_reply)
    return Decision("handoff", "low_confidence_or_unknown")


def event_id(payload: dict) -> str:
    return str(payload["entry"][0]["messaging"][0]["message"]["mid"])


def message_text(payload: dict) -> str:
    return str(payload["entry"][0]["messaging"][0]["message"].get("text", ""))


def process_webhook(raw_body: bytes, signature_header: str) -> Decision:
    if not verify_signature(raw_body, signature_header):
        return Decision("reject", "invalid_signature")
    payload = json.loads(raw_body)
    eid = event_id(payload)
    if eid in SEEN_EVENT_IDS:
        return Decision("duplicate", "already_processed")
    # 운영에서는 부작용 실행 전에 내구성 DB의 unique key로 원자적으로 예약한다.
    SEEN_EVENT_IDS.add(eid)
    return policy_filter(message_text(payload))


class TransientDeliveryError(RuntimeError):
    pass


class PermanentDeliveryError(RuntimeError):
    pass


def deliver_with_limited_retry(
    operation: Callable[[], None], max_attempts: int = 3, base_delay: float = 0.001
) -> int:
    """일시적 내부 전송 오류만 제한적으로 재시도한다."""
    for attempt in range(1, max_attempts + 1):
        try:
            operation()
            return attempt
        except PermanentDeliveryError:
            raise
        except TransientDeliveryError:
            if attempt == max_attempts:
                raise
            time.sleep(base_delay * (2 ** (attempt - 1)))
    raise AssertionError("unreachable")


def make_payload(index: int, text: str) -> bytes:
    payload = {
        "object": "instagram",
        "entry": [{
            "id": "IG_ACCOUNT_DEMO",
            "time": 0,
            "messaging": [{
                "sender": {"id": "IGSID_DEMO"},
                "recipient": {"id": "IG_ACCOUNT_DEMO"},
                "timestamp": 0,
                "message": {"mid": f"MID_DEMO_{index:03d}", "text": text},
            }],
        }],
    }
    return json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode()


def run_self_test() -> None:
    cases = [
        ("배송은 언제 시작하나요?", "auto_reply"),
        ("배송 일정이 궁금합니다", "auto_reply"),
        ("운영시간을 알려주세요", "auto_reply"),
        ("오늘 운영시간은요?", "auto_reply"),
        ("반품 절차가 궁금해요", "auto_reply"),
        ("반품 절차 안내 부탁해요", "auto_reply"),
        ("배송 문의입니다", "auto_reply"),
        ("운영시간 문의입니다", "auto_reply"),
        ("배송 일정 확인", "auto_reply"),
        ("반품 절차 확인", "auto_reply"),
        ("상담원과 이야기할래요", "handoff"),
        ("환불을 요청합니다", "handoff"),
        ("주문 취소를 도와주세요", "handoff"),
        ("주문 변경이 필요해요", "handoff"),
        ("제품 사용 후 진단해 주세요", "handoff"),
        ("법률 자문을 해주세요", "handoff"),
        ("투자 추천을 해주세요", "handoff"),
        ("연락처를 보냈어요", "handoff"),
        ("주소를 변경하고 싶어요", "handoff"),
        ("이 문장의 뜻을 알려주세요", "handoff"),
    ]
    SEEN_EVENT_IDS.clear()
    latencies_ms: list[float] = []
    correct = 0
    route_counts: dict[str, int] = {}
    first_raw = b""
    for index, (text, expected) in enumerate(cases, start=1):
        raw = make_payload(index, text)
        if index == 1:
            first_raw = raw
        started = time.perf_counter_ns()
        decision = process_webhook(raw, sign(raw))
        latencies_ms.append((time.perf_counter_ns() - started) / 1_000_000)
        correct += decision.route == expected
        route_counts[decision.route] = route_counts.get(decision.route, 0) + 1

    duplicate = process_webhook(first_raw, sign(first_raw))
    bad_signature = process_webhook(make_payload(999, "배송 문의"), "sha256=invalid")

    attempts = {"count": 0}
    def flaky_operation() -> None:
        attempts["count"] += 1
        if attempts["count"] < 3:
            raise TransientDeliveryError("synthetic transient failure")
    retry_attempts = deliver_with_limited_retry(flaky_operation)

    ordered = sorted(latencies_ms)
    p95 = ordered[max(0, int(len(ordered) * 0.95) - 1)]
    assert correct == len(cases)
    assert duplicate.route == "duplicate"
    assert bad_signature.route == "reject"
    assert retry_attempts == 3
    print(f"cases={len(cases)} policy_match={correct}/{len(cases)}")
    print(f"routes={json.dumps(route_counts, sort_keys=True)}")
    print(f"local_policy_p95_ms={p95:.3f}")
    print(f"duplicate={duplicate.route} bad_signature={bad_signature.route}")
    print(f"limited_retry_attempts={retry_attempts} network_calls=0")
    print("PASS")


if __name__ == "__main__":
    run_self_test()

실행 방법:

python3 -m py_compile instagram_dm_policy_demo.py
python3 instagram_dm_policy_demo.py

직접 실행한 검증 결과

2026-07-20, Python 3.14.4가 설치된 로컬 WSL 환경에서 위와 같은 로직을 문법 검사하고 실행했습니다.

cases=20 policy_match=20/20
routes={"auto_reply": 10, "handoff": 10}
local_policy_p95_ms=0.025
duplicate=duplicate bad_signature=reject
limited_retry_attempts=3 network_calls=0
PASS

이 결과는 합성 문장 20개에 대해 미리 정의한 정책 경로와 코드의 결과가 일치했다는 뜻일 뿐, 실제 상담 정확도나 Instagram/LLM API 응답 속도를 의미하지 않습니다. 실제 배포 전에는 승인된 테스트 계정에서 웹훅 필드별 payload, 첨부 파일, 답장 기간, 권한 거부, API 오류를 별도로 검증해야 합니다.

또한 예제의 메모리 set은 프로세스 재시작과 다중 인스턴스를 견디지 못합니다. 운영에서는 PostgreSQL unique constraint, Redis의 원자 명령 등으로 이벤트 예약과 상태 전이를 구현하세요.

제한된 재시도와 실패 처리

재시도는 오류 종류를 구분해야 합니다.

상황 처리 권장안
웹훅 서명 불일치 즉시 거부, 재시도하지 않음, 원문을 일반 로그에 남기지 않음
JSON/스키마 오류 격리 큐 또는 최소 오류 로그, 자동 답변 금지
이미 처리한 mid 성공 확인만 하고 부작용 생략
내부 큐·DB의 일시 장애 짧은 지수 백오프, 제한 횟수 후 장애 큐/알림
Meta 또는 LLM의 429·5xx·타임아웃 공급자 지침과 Retry-After를 우선하고 횟수·총 대기시간 제한
권한 부족·정책 거부·잘못된 요청 재시도하지 않고 설정 또는 입력 수정
LLM 근거 없음·정책 필터 실패 재생성 루프 대신 상담원 이관

같은 메시지를 재시도할 때는 전송 작업에도 idempotency key를 전달하거나 내부 outbox 상태를 확인해 중복 답장을 막습니다. “성공 여부를 알 수 없는 타임아웃”은 무조건 재전송하지 말고 공급자가 제공하는 조회·중복 방지 수단을 먼저 확인합니다.

비용·권한·할당량·정책 한계

  • 비용: Meta 플랫폼 약관은 플랫폼이 항상 무료라고 보장하지 않습니다. LLM, 벡터 검색, 큐, 데이터베이스, 모니터링도 별도 비용이 생길 수 있습니다. 가격은 공급자 공식 페이지와 실제 청구 로그로 관리하세요.
  • 할당량: 고정 호출량을 글에 박아 넣지 말고 현재 API 버전 문서, 앱 대시보드, 응답 헤더와 오류를 기준으로 모니터링하세요.
  • 권한: 개발 역할에서 성공한 테스트가 외부 계정에서도 동작한다는 뜻은 아닙니다. Standard/Advanced Access, 앱 검수, 비즈니스 인증과 계정별 동의를 각각 확인해야 합니다.
  • 메시지 창: 사용자가 시작한 대화와 일반 응답 기간을 지켜야 합니다. Human Agent 기능은 자동 마케팅의 우회 수단이 아닙니다.
  • 기능 제한: 공식 Messaging API 문서는 그룹 메시지를 지원하지 않고, 대화당 고객 한 명을 전제로 한다고 설명합니다. 받은편지함 폴더 정보와 API 동작에도 차이가 있습니다.
  • 개인정보: 플랫폼 약관뿐 아니라 사업 지역의 개인정보·전자상거래·광고·자동화 고지 관련 법규를 별도로 검토해야 합니다.
  • 모델 한계: RAG, 프롬프트, 출력 필터는 오류 위험을 줄일 수 있지만 정확성을 보장하지 않습니다. 고위험·금전·계정 작업에는 사람 승인을 둡니다.

배포 전 검증 체크리스트

Meta 설정

  • 대상 계정이 비즈니스 또는 크리에이터 프로페셔널 계정이다.
  • 로그인 구성과 API 호스트를 혼용하지 않았다.
  • 필요한 최소 메시지 권한과 액세스 수준을 확인했다.
  • 외부 계정 제공 시 앱 검수·Advanced Access·비즈니스 인증 상태를 확인했다.
  • HTTPS 콜백, verify token, 웹훅 필드, 계정 구독을 검증했다.
  • 앱 시크릿과 액세스 토큰을 서버 측 시크릿 저장소에 보관했다.

안전·운영

  • X-Hub-Signature-256을 원시 본문으로 검증한다.
  • mid 등 이벤트 키가 내구성 DB에서 unique이다.
  • 수신 확인과 LLM/전송 작업을 분리했다.
  • 자동화 고지와 “상담원” 명령을 실제 대화에서 확인했다.
  • 금지 답변·낮은 신뢰도·계정 작업·개인정보가 상담원으로 이관된다.
  • 일시 오류만 제한적으로 재시도하고 영구 오류는 중단한다.
  • 원문 대신 최소 감사 메타데이터를 남기며 토큰은 로그에서 제거한다.
  • 보관기간, 자동 삭제, 사용자 삭제 요청과 백업 처리 절차가 연결되어 있다.

품질 측정

실제 승인 테스트 계정으로 최소 테스트 세트를 만들고 다음을 기록합니다.

지표 계산 함께 남길 정보
정책 경로 일치율 기대 경로와 일치한 건수 ÷ 전체 테스트 건수 테스트 문장 버전, 정책 버전, 검수자
상담원 이관률 이관 건수 ÷ 전체 처리 건수 이관 사유별 건수
자동 답변 근거 일치율 승인된 출처가 답변을 뒷받침한 건수 ÷ 자동 답변 건수 source_id, 지식 승인일
p95 처리시간 처리시간의 95번째 백분위수 수신/큐/검색/모델/전송 구간 분리
중복 부작용 같은 이벤트로 답변·이관이 반복된 건수 idempotency key와 outbox 상태

성과 수치는 기간, 표본, 산식, 원본 로그가 있을 때만 공개합니다. 자동화 도입이 매출이나 전환율 상승을 보장한다고 표현해서는 안 됩니다. 이벤트와 전환을 측정하려면 마케팅 자동화 전 데이터 설계 가이드를 함께 참고하세요. 여러 상담 채널의 이관 큐를 통합하려면 카카오·네이버 문의를 Slack으로 모으는 CS 통합 설계의 데이터 최소화와 상관관계 ID 원칙도 적용할 수 있습니다.

자주 묻는 질문

개인 계정도 Messaging API로 자동화할 수 있나요?

공식 개요는 API 대상을 Instagram 프로페셔널 계정으로 설명합니다. 비즈니스 또는 크리에이터 계정과 선택한 로그인 구성의 연결 조건을 먼저 갖춰야 합니다.

LLM 없이도 시작할 수 있나요?

가능합니다. 승인된 FAQ, 명시적 키워드, 상담원 이관만으로 먼저 웹훅·보안·운영 절차를 검증하는 편이 안전합니다. LLM은 실제 실패 사례와 평가 세트가 준비된 뒤 제한된 답변 후보 생성에 추가할 수 있습니다.

자동화라고 반드시 밝혀야 하나요?

Messaging API 문서는 관련 법률이 요구하는 경우 고지가 필요하다고 설명하고, 법적 의무가 없는 경우에도 고지를 권장합니다. 사업 지역의 법률 검토와 별개로 투명한 첫 메시지를 기본값으로 두는 것이 좋습니다.

모든 문의를 24시간 안에 자동으로 답해야 하나요?

아닙니다. 일반 메시지 전송 기간은 자동화 성과 목표가 아니라 플랫폼 사용 조건입니다. 정확히 답할 수 없는 문의는 즉시 상담원 큐로 넘기고, Human Agent 기능은 공식 허용 목적과 최신 조건 안에서만 사용하세요.

RAG를 붙이면 잘못된 답변이 없어지나요?

아닙니다. 검색 실패, 오래된 자료, 잘못된 인용, 모델 오류가 남습니다. 승인·만료가 있는 지식, 출처 ID, 출력 필터, 회귀 테스트, 상담원 전환을 함께 운영해야 합니다.

공식 문서

아래 링크는 2026-07-20에 최종 URL과 HTTP 200 응답을 확인했습니다.

  1. Meta — Instagram Messaging API: 메시지 보내기 — 프로페셔널 계정, 사용자 시작 대화, 24시간 응답, 자동화 고지, 상담원 전환, 권한과 제한
  2. Meta — Instagram 플랫폼 개요 — 로그인 구성, Standard/Advanced Access, 앱 검수, 비즈니스 인증, 권한과 Human Agent 기능
  3. Meta — Instagram 웹훅 설정 — HTTPS 콜백, 검증 요청, X-Hub-Signature-256, 이벤트 payload, 구독과 중복 처리
  4. Meta — 플랫폼 약관 — 데이터 목적 제한, 개인정보처리방침, 보관·삭제, 서비스 제공업체, 데이터 보안

발행 메모: canonical과 CMS ID는 기존 https://joshua12.com/entry/48, CMS 48을 유지합니다. 대표 이미지는 위 아키텍처 alt를 사용하고, 상담원 화면을 추가할 경우 “자동응답 실패 후 상담원 큐로 전달된 익명 합성 대화”처럼 목적을 설명하세요. 근거 없는 매출 상승 이미지는 사용하지 않습니다.