자주 묻는 질문지원 받기 앱으로 이동
목차 보기

사용 설명서 > 개발자 문서 > SDK(FC) 레퍼런스

예제 번들

마지막 확인일: 2026년 11월 6일

이 글의 목차

아래 예제는 모두 실제 검증기를 통과하는 번들입니다. LLM에 "이 형식을 따라 줘" 하고 함께 주면 결과가 안정적입니다.

가장 작은 앱(이벤트 하나 보내기)

{
  "format_version": 1,
  "manifest": {
    "name": "클릭 카운터",
    "sdk_version": "1",
    "permissions": [
      "events.emit"
    ],
    "screens": [
      {
        "key": "main",
        "label": "메인"
      }
    ],
    "events": [
      {
        "name": "example.clicked",
        "fields": {
          "count": {
            "type": "number",
            "required": true
          }
        }
      }
    ],
    "variables": [
      {
        "name": "title",
        "type": "text",
        "default": "클릭 카운터"
      }
    ]
  },
  "settings": {
    "title": "클릭 카운터"
  },
  "client": {
    "html": "<main class=\"app\">\n  <h2 data-title>클릭 카운터</h2>\n  <p data-count>0</p>\n  <button type=\"button\" data-action=\"add\">누르기</button>\n</main>\n",
    "css": ".app{font-family:Arial,sans-serif;max-width:480px;margin:auto;padding:24px}\nbutton{padding:10px 16px;border:0;border-radius:8px;background:#4A3094;color:#fff;cursor:pointer}\n",
    "js": "(() => {\n  let count = 0;\n  document.querySelector(\"[data-title]\").textContent = FC.settings.title;\n  document.querySelector(\"[data-action=add]\").addEventListener(\"click\", () => {\n    count += 1;\n    document.querySelector(\"[data-count]\").textContent = String(count);\n    FC.event.emit(\"example.clicked\", { count });\n  });\n})();\n"
  }
}

이메일을 받아 연락처로 저장하기

{
  "format_version": 1,
  "manifest": {
    "name": "소식 받기",
    "sdk_version": "1",
    "permissions": [
      "contacts.write"
    ],
    "screens": [
      {
        "key": "form",
        "label": "입력"
      },
      {
        "key": "done",
        "label": "완료"
      }
    ],
    "events": [],
    "variables": [
      {
        "name": "title",
        "type": "text",
        "default": "소식 받기"
      }
    ]
  },
  "settings": {
    "title": "소식 받기"
  },
  "client": {
    "html": "<main class=\"app\">\n  <section data-screen=\"form\">\n    <h2 data-title>소식 받기</h2>\n    <label>이메일 <input name=\"email\" type=\"email\" required maxlength=\"320\"></label>\n    <p data-error role=\"alert\"></p>\n    <button type=\"button\" data-action=\"submit\">신청하기</button>\n  </section>\n  <section data-screen=\"done\" hidden><p>신청해 주셔서 감사합니다.</p></section>\n</main>\n",
    "css": ".app{font-family:Arial,sans-serif;max-width:480px;margin:auto;padding:24px}\ninput{display:block;width:100%;padding:10px;margin:8px 0}\n",
    "js": "(() => {\n  const screen = (key) => document.querySelector(\"[data-screen=\" + key + \"]\");\n  document.querySelector(\"[data-action=submit]\").addEventListener(\"click\", async () => {\n    const email = document.querySelector(\"input[name=email]\").value.trim();\n    const error = document.querySelector(\"[data-error]\");\n    if (!email) { error.textContent = \"이메일을 입력해 주세요.\"; return; }\n    try {\n      await FC.contact.create({ email });\n      screen(\"form\").hidden = true;\n      screen(\"done\").hidden = false;\n    } catch (e) {\n      error.textContent = \"잠시 후 다시 시도해 주세요.\";\n    }\n  });\n})();\n"
  }
}

방문자 글을 AI가 요약하기(AI Action)

{
  "format_version": 1,
  "manifest": {
    "name": "글 요약기",
    "sdk_version": "1",
    "permissions": [
      "ai.generate"
    ],
    "screens": [
      {
        "key": "main",
        "label": "메인"
      }
    ],
    "events": [],
    "variables": [
      {
        "name": "title",
        "type": "text",
        "default": "글 요약기"
      }
    ],
    "actions": [
      {
        "key": "summarize",
        "kind": "ai.generate",
        "connection": {
          "mode": "managed"
        },
        "instruction": "입력된 글을 한국어로 세 문장 이내로 요약한다. 없는 내용은 만들지 않는다.",
        "output": "text"
      }
    ]
  },
  "settings": {
    "title": "글 요약기"
  },
  "client": {
    "html": "<main class=\"app\">\n  <h2 data-title>글 요약기</h2>\n  <textarea name=\"body\" rows=\"6\" maxlength=\"4000\"></textarea>\n  <button type=\"button\" data-action=\"run\">요약하기</button>\n  <p data-status role=\"status\"></p>\n  <div data-result></div>\n</main>\n",
    "css": ".app{font-family:Arial,sans-serif;max-width:560px;margin:auto;padding:24px}\ntextarea{width:100%;padding:10px;box-sizing:border-box}\n",
    "js": "(() => {\n  const button = document.querySelector(\"[data-action=run]\");\n  const status = document.querySelector(\"[data-status]\");\n  const result = document.querySelector(\"[data-result]\");\n  button.addEventListener(\"click\", async () => {\n    const input = document.querySelector(\"textarea[name=body]\").value.trim().slice(0, 4000);\n    if (!input) { status.textContent = \"글을 입력해 주세요.\"; return; }\n    button.disabled = true;\n    status.textContent = \"요약 중입니다…\";\n    result.textContent = \"\";\n    try {\n      const answer = await FC.ai.generate(input, { action: \"summarize\" });\n      result.textContent = answer.text;\n      status.textContent = \"\";\n    } catch (e) {\n      status.textContent = (e && e.message) || \"요약하지 못했습니다.\";\n    } finally {\n      button.disabled = false;\n    }\n  });\n})();\n"
  }
}

이 페이지가 도움이 되었나요?