For the complete documentation index, see llms.txt. This page is also available as Markdown.

커넥티드 콘텐츠 (Connected Content) 작성하기

메시지를 보내는 그 순간, 외부 API에서 실시간 데이터를 가져와 본문에 끼워 넣는 기능입니다. 잔여 포인트, 오늘의 쿠폰, 개인화 추천 상품처럼 Hackle에 저장돼 있지 않은 정보를 메시지에 활용할 수 있습니다.

비개발자를 위한 커넥티드 콘텐츠 작성 방법

lightbulb

코드를 직접 쓸 필요는 없습니다. 메시지 편집기의 {...} 개인화 변수 추가 버튼을 누르면, 폼만 채우는 방식으로 {% connected_content %} 태그가 자동으로 만들어집니다.

시작 전 준비물 (개발팀에서 받아 두세요)

  1. 호출할 API 주소(https://로 시작)

  2. 요청 방식(GET 또는 POST)

  3. 응답 예시(JSON) - 어떤 필드를 메시지에 쓸 수 있는지 확인용

먼저 메시지 편집기에서 {...} 버튼 클릭 → "커넥티드 콘텐츠" 탭 선택하세요

모달이 열리면 상단에서 속성 / 커넥티드 콘텐츠 중 "커넥티드 콘텐츠 — 외부 API에서 실시간 데이터"를 선택합니다.

STEP 1. API URL 입력 후 API 테스트 클릭

API URL을 입력합니다. 인증 헤더나 POST 본문이 필요하면 요청 옵션을 펼쳐 입력합니다. URL에는 {{user_properties["id"]}} 같은 개인화 변수도 그대로 끼워 넣을 수 있습니다. 그다음 API 테스트 버튼을 눌러 실제 응답이 잘 오는지, 어떤 필드가 포함되어 있는지 미리 확인합니다.

STEP 2. 메시지에 넣을 값 선택 + 변수명 입력

응답 결과에서 메시지에 노출할 필드(예: code, discount)를 체크합니다. 그리고 **응답 변수명(:save)**에 사용할 이름(예: product)을 입력합니다. 이 이름이 곧 본문에서 값을 꺼내 쓸 때 사용할 변수가 됩니다.

STEP 3. 저장하기 클릭 → 본문에서 변수 클릭으로 삽입

저장하기를 누르면 모달이 만든 변수가 "사용할 수 있는 변수" 목록에 추가됩니다. 본문 원하는 위치에 커서를 두고 해당 변수를 클릭하면 {{coupon.code}}처럼 자동 삽입됩니다.

안전하게 쓰는 팁

응답이 비어 올 때를 대비해 기본 문구를 함께 넣어 두세요.

기본 문법

  • <URL> (필수): 호출할 외부 엔드포인트의 절대 URL. https://만 허용됩니다.

  • :method : HTTP 메서드. GET / POST만 지원, 기본값 GET.

  • :body : POST 요청 본문. JSON 객체 문자열 형태로 작성해야 합니다.

  • :headers : 요청 헤더. JSON 객체 문자열 형태로 작성해야 하며, 값은 모두 문자열이어야 합니다. 지정하지 않으면 Content-Type: application/json이 기본 적용됩니다.

  • :save : 응답 JSON을 담을 변수 이름. 이후 {{변수.필드}} 형태로 메시지에서 꺼내 쓸 수 있습니다. 지정하지 않으면 응답을 사용할 수 없습니다.

URL, body, headers의 값에 {{user_properties["name"] | default: "고객님"}} , {{event_properties["campaign_id"] | default: "1"}} 같은 개인화 변수를 자유롭게 사용 할 수 있습니다.

파라미터

URL

  • 반드시 절대 URL이며 https:// 스킴만 허용합니다.

  • 공인 IP로 해석되는 최종 URL만 호출할 수 있습니다.

:method

  • 지원: GET, POST , 기본값 GET

  • 그 외 메서드(PUT, DELETE, PATCH 등)는 거부되고 태그가 통째로 스킵됩니다.

:body

  • :method POST일 때 사용합니다. GET에서는 무시됩니다.

  • 인라인 JSON을 그대로 적을 수 있습니다. 예: :body {"foo":"bar"}

:headers

  • 반드시 JSON 객체 문자열이어야 하고 모든 값은 문자열이어야 합니다.

    • 좋은 예: :headers {"Authorization":"Bearer abc","X-Trace":"123"}

    • 잘못된 예: :headers {"Retry": 3} (값이 숫자) → 검증 실패로 태그 스킵

  • Content-Type: application/json 타입만을 지원하며, 명시하지 않아도 기본값으로 자동 적용됩니다.

:save

  • 응답 JSON 객체를 이 이름의 변수로 컨텍스트에 저장합니다.

  • 같은 템플릿 안의 이후 태그/Output 노드 모두에서 참조 가능합니다. 예를 들어 첫 번째 connected_content의 결과를 두 번째 connected_content의 URL이나 body에 다시 사용할 수 있습니다.

  • 응답이 JSON 객체가 아니거나 비어 있으면 빈 값으로 저장됩니다(렌더링 시 빈 문자열).


예제

1. 가장 단순한 GET 호출

응답이 {"name":"운동화","price":59000}이라면 → 운동화 59000원

2. URL에 사용자/이벤트 변수 끼워 넣기

event_properties.order_id = "ABC-123"이면 실제 호출 URL은 https://api.example.com/orders/ABC-123이 됩니다.

3. POST + JSON body

4. 인증 헤더가 필요한 호출

5. 중첩된 JSON 응답에서 값 꺼내기

응답 {"product":{"name":"티셔츠","brand":"Nike"}}티셔츠 by Nike

6. 태그 체이닝 (앞 호출 결과를 다음 호출에 사용)

7. POST + form-encoded 본문 + 커스텀 헤더


응답 처리 규칙

응답 형태
결과

200 OK + JSON 객체

JSON을 파싱하여 :save 변수에 매핑. 필드는 {{var.field}} 또는 {{var["field"]}}로 접근

200 OK + JSON 배열 또는 원시값

JSON 객체가 아니므로 빈 값으로 처리

200 OK + 빈 본문

빈 값으로 처리

200이 아닌 모든 상태 코드 (4xx, 5xx, 3xx 등)

빈 값으로 처리 (리다이렉트 따라가지 않음)

응답 본문이 1MB 초과

빈 값으로 처리

네트워크 오류, 타임아웃

빈 값으로 처리

빈 값(empty) 으로 처리되었다는 것은 :save로 지정한 변수에 빈 맵이 저장되었다는 뜻입니다. {{var.anything}}은 빈 문자열로 렌더링되며, 이로 인해 메시지 발송 자체가 실패하지는 않습니다.


제약 사항과 보안 정책

보호 한도

항목
한도

URL 스킴

https://만 허용

호스트

공인 IP로 해석되는 호스트만 허용 (사내망 / 사설 대역 차단)

리다이렉트

따라가지 않음 (최종 URL을 직접 지정해야 함)

응답 본문 크기

최대 1MB (초과 시 빈 값 처리)

단일 호출 타임아웃

2초

한 메시지(Liquid render) 내부 누적 호출 시간

총 2초 (한도 초과 후 호출은 HTTP 호출 없이 스킵)

HTTP 메서드

GET, POST

헤더 값 형식

JSON 객체 문자열, 값은 모두 문자열


실패 시 동작

Connected Content는 메시지 발송을 막지 않습니다. 모든 실패 케이스는 "default 값으로 대체" 됩니다.

대표 케이스:

  • URL이 비어 있거나 플래그-값 쌍이 맞지 않는 경우 → 태그 전체 스킵

  • :methodPUT 같은 미지원 메서드를 지정 → 태그 스킵

  • :headers가 유효한 JSON 객체 문자열이 아닌 경우 → 태그 스킵

  • URL 검증 실패 (HTTPS 아님, 사설 IP 등) → 빈 응답

  • 네트워크 오류, 200 외 응답, 본문 1MB 초과, JSON 객체 아님 → 빈 응답

  • 한 메시지 내 누적 호출 시간 2초 초과 이후의 호출 → HTTP 호출 없이 스킵, 빈 응답

빈 응답이 저장된 변수를 메시지에서 사용해도 빈 문자열로 렌더링됩니다. 따라서 응답이 비어 있을 때를 대비해 다음과 같은 방어 코드를 곁들이는 것을 권장합니다.


자주 묻는 질문

Q. 한 메시지에 connected_content를 여러 번 써도 되나요?

가능합니다. 단, 한 메시지의 모든 connected_content 호출은 누적 2초의 시간 예산을 공유합니다. 첫 번째 호출이 1.8초 걸렸다면 두 번째 호출은 0.2초 안에 끝나야 하고, 그 사이에 한도를 초과하면 이후 호출은 네트워크 호출 없이 즉시 스킵됩니다. 외부 API의 평균 응답시간이 충분히 짧을 때만 다중 호출을 사용하세요.

Q. 응답을 받기 전에 발송 시간이 오래 걸리진 않나요?

각 호출은 최대 2초 안에 종료됩니다. 또한 한 메시지의 누적 한도가 2초이므로 발송 지연이 무한정 누적되지 않습니다.

Q. 응답이 JSON 배열일 때는 어떻게 접근하나요?

최상위가 배열이면 객체로 인식되지 않아 빈 값이 됩니다. API 측에서 {"items":[...]}처럼 객체로 감싼 형태로 내려주도록 하거나, 래핑 엔드포인트를 따로 두세요.

Q. JSON이 아닌 응답(예: 단순 텍스트, HTML)도 사용할 수 있나요?

지원하지 않습니다. JSON 객체로 파싱되지 않으면 빈 값으로 처리됩니다.

Q. 응답을 메시지에 직접 노출하지 않고 조건 분기에만 쓰고 싶어요.

:save 변수를 {% if %} 등에서 그대로 쓸 수 있고, 메시지 본문에 출력할 필요는 없습니다. 위의 "실패 시 동작" 섹션의 예시를 참고하세요.

Q. 키에 한글, 공백, 점이 들어 있는 응답 필드는 어떻게 꺼내나요?

{{var["키 이름"]}} 형태의 bracket subscript를 사용하세요. 작은따옴표(')도 사용 가능합니다.

마지막 업데이트