Sidetalk사용자 가이드
사이드톡 홈페이지 ↗대시보드 열기

대화형 답변 생성 API

안내

이 문서는 이전 버전인 v1 API에 대한 문서입니다.
신규 연동 시에는 기능과 응답 구조가 개선된 v2 API 사용을 권장합니다.
기존 v1 API를 사용 중인 경우에도 안정적인 서비스 이용을 위해 v2로의 전환을 권장합니다.

목적

이 API는 사용자 메시지를 전달하고, AI의 응답을 반환합니다. 응답은 일반 JSON 또는 스트림(SSE, Server-Sent Events) 방식으로 받을 수 있습니다.
또한 응답에 포함되는 chat_log_uuid를 이용해, 해당 대화에 대해 도움이 됐어요 / 도움이 안 됐어요 피드백을 저장할 수 있습니다.

URL


HTTP 메서드

Shell
POST

헤더

헤더값설명
Content-Typeapplication/json요청 본문이 JSON 형식임을 나타냅니다.
AuthorizationBearer <api_key>Sidetalk 대시보드에서 설치하기 메뉴에 제공된 API 키를 사용하세요.

본문

파라미터타입필수설명
messageString예사용자 메시지입니다.
streamBoolean아니요응답을 스트림으로 받을지 여부를 지정합니다. true 또는 false. 지정하지 않으면 기본값 false가 사용됩니다.
sessionIdString아니요대화 세션을 구분하는 고유 키(Key)입니다. 이전 대화 내용을 기억하여 대화를 이어가려면 동일한 값을 유지해야 합니다.
memberIdString아니요고객을 구분하는 회원 고유 ID입니다. 대화 로그를 특정 고객과 연결하려면 전달합니다. 동일한 고객의 대화를 추적하려면 항상 같은 값을 사용하세요.

요청 예시

Shell
curl -X POST https://api.sidetalk.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${API_KEY}" \
  -d '{
    "message": "안녕하세요, 도움이 필요해요.",
    "stream": false,
    "sessionId": "test-session-id",
    "memberId": "user_123"
  }'

응답 형식

응답은 stream 값에 따라 두 가지 방식으로 제공됩니다.

1-1. 스트림 응답 (stream: true)

응답은 SSE(Server-Sent Events) 형식으로 전달됩니다.
각 이벤트는 \n\n 으로 구분되며, event: 와 data: 필드를 포함할 수 있습니다.
stream: true 인 경우 에이전트 실행 결과는 extra 필드로 반환되지 않고, 각 이벤트별로 개별 전달됩니다.

주요 이벤트 종류

이벤트명설명
message일반 텍스트 응답 조각입니다.
showCard카드형 응답 데이터입니다.
inputForm입력 폼 데이터입니다.
reservationBookingForm예약 입력 폼 데이터입니다.
reservationLookupForm예약 조회 폼 데이터입니다.
pageRedirect페이지 이동용 링크 데이터입니다.

message 이벤트 데이터 구조

필드타입설명
chunkStringAI 응답의 일부 텍스트입니다.
chat_log_uuidString해당 대화 로그의 UUID입니다. 이후 피드백 저장 시 사용합니다.
outsideDataString응답 외부에 표시할 추가 데이터입니다. 있을 때만 포함될 수 있습니다.

기본 응답 예시 (message)

JSON
event: message
data: {"chunk":"안녕하세요, ","chat_log_uuid":"2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"}

event: message
data: {"chunk":"무엇을 도와드릴까요?","chat_log_uuid":"2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"}

data: [DONE]

카드 응답 예시 (showCard)

JSON
event: showCard
data: {
  "schema": "sidetalk.card.v1",
  "cardType": "text",
  "displayMode": "carousel",
  "items": [
    {
      "type": "text",
      "title": "최근 주문 내역",
      "description": "주문번호 1234 / 결제완료",
      "buttons": [
        {
          "label": "상세 보기",
          "action": "user_utterance",
          "value": "주문 상세 보여줘"
        }
      ]
    }
  ]
}

입력 폼 응답 예시 (inputForm)

JSON
event: inputForm
data: {
  "type": "showInputForm",
  "task_id": "task_101",
  "order": 1,
  "question": "예약 신청할게요.",
  "form_id": 101,
  "form_name": "예약 신청",
  "guide_message": "아래 정보를 입력해 주세요.",
  "field_list": [
    {
      "field_id": "customer_name",
      "field_label": "이름",
      "field_type": "text",
      "field_placeholder": "이름을 입력하세요",
      "field_default_value": "",
      "field_required_value": "1",
      "order_index": 1,
      "linked_column_id": "",
      "field_options": ""
    },
    {
      "field_id": "customer_phone",
      "field_label": "연락처",
      "field_type": "text",
      "field_placeholder": "010-0000-0000",
      "field_default_value": "",
      "field_required_value": "1",
      "order_index": 2,
      "linked_column_id": "",
      "field_options": ""
    }
  ],
  "executionHistory": [
    {
      "task_order": 1,
      "action_type": "showInputForm",
      "description": "입력폼 표시",
      "data": null
    }
  ],
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"
}

입력폼 응답 후 처리

inputForm 응답이 반환되면 클라이언트는 입력 폼 UI를 표시해야 합니다.
사용자가 입력을 완료하면, 응답에 포함된 form_id, task_id, order, executionHistory, chat_log_uuid 값과 사용자가 입력한 form_data 를 함께 구성하여 입력폼 제출 API로 전달합니다.
자세한 요청 형식은 입력폼 제출 API 문서를 참고하세요.

예약 입력 폼 응답 예시 (reservationBookingForm)

JSON
event: reservationBookingForm
data: {
  "type": "reservationBookingForm",
  "form_id": "reservation_booking_request",
  "form_name": "예약 신청",
  "guide_message": "예약에 필요한 정보를 입력해 주세요.",
  "reservation_step": "customer_info",
  "field_list": [
    {
      "field_id": "customer_name",
      "field_label": "이름",
      "field_type": "text",
      "field_placeholder": "이름을 입력하세요",
      "field_required_value": "1"
    },
    {
      "field_id": "customer_phone",
      "field_label": "연락처",
      "field_type": "text",
      "field_placeholder": "010-0000-0000",
      "field_required_value": "1"
    }
  ],
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"
}

예약 조회 폼 응답 예시 (reservationLookupForm)

JSON
event: reservationLookupForm
data: {
  "type": "reservationLookupForm",
  "form_id": "reservation_lookup_request",
  "form_name": "예약 조회",
  "guide_message": "예약번호를 입력해 주세요.",
  "field_list": [
    {
      "field_id": "reservation_no",
      "field_label": "예약번호",
      "field_type": "text",
      "field_placeholder": "예약번호를 입력하세요",
      "field_required_value": "1"
    }
  ],
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"
}

페이지 이동 응답 예시 (pageRedirect)

JSON
event: pageRedirect
data: {
  "url": "https://example.com/reservation"
}

스트림 처리 시 주의사항

  • 각 메시지를 \n\n 기준으로 분리합니다.
  • event: 값이 있는 경우 이벤트 종류를 함께 처리해야 합니다.
  • data: [DONE] 이 오면 스트림이 종료된 것입니다.
  • message 이벤트의 chat_log_uuid 는 피드백 API 호출 시 필요하므로 저장해 두는 것을 권장합니다.
  • showCard, inputForm, reservationBookingForm, reservationLookupForm, pageRedirect 이벤트는 일반 텍스트가 아니라 구조화된 에이전트 응답 데이터이므로 이벤트 이름에 따라 별도로 처리해야 합니다.

1-2. JSON 응답 (stream: false 또는 생략)

응답은 JSON 형식으로 반환됩니다.
stream: false 인 경우 일반 텍스트 응답은 message.content 로 반환되며,
에이전트 실행 결과가 있는 경우 extra 필드에 함께 포함될 수 있습니다.

기본 응답 본문

JSON
{
  "success": true,
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
  "message": {
    "content": "안녕하세요, 무엇을 도와드릴까요?"
  }
}

응답 필드 설명

필드타입설명
successBoolean요청 성공 여부입니다. 성공 시 항상 true 입니다.
chat_log_uuidString해당 대화 로그의 UUID입니다. 피드백 저장 시 사용합니다.
messageObjectAI의 응답 메시지 객체입니다.
message.contentStringAI의 전체 응답 텍스트입니다.
extraObject에이전트 실행 결과가 있을 때만 포함될 수 있는 확장 필드입니다.

에이전트 실행 결과가 포함된 JSON 응답

에이전트 실행 결과가 있는 경우 extra 필드에 구조화된 응답 데이터가 포함될 수 있습니다.
실제 응답에서는 필요한 extra 필드만 포함됩니다.

1) 카드 응답 예시 (extra.showCard)

JSON
{
  "success": true,
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
  "message": {
    "content": ""
  },
  "extra": {
    "showCard": {
      "schema": "sidetalk.card.v1",
      "cardType": "text",
      "displayMode": "carousel",
      "items": [
        {
          "type": "text",
          "title": "최근 주문 내역",
          "description": "주문번호 1234 / 결제완료",
          "buttons": [
            {
              "label": "상세 보기",
              "action": "user_utterance",
              "value": "주문 상세 보여줘"
            }
          ]
        }
      ]
    }
  }
}

2) 입력 폼 응답 예시 (extra.inputForm)

JSON
{
  "success": true,
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
  "message": {
    "content": "예약 입력이 필요합니다."
  },
  "extra": {
    "inputForm": {
      "type": "showInputForm",
      "task_id": "task_101",
      "order": 1,
      "question": "예약 신청할게요.",
      "form_id": 101,
      "form_name": "예약 신청",
      "guide_message": "아래 정보를 입력해 주세요.",
      "field_list": [
        {
          "field_id": "customer_name",
          "field_label": "이름",
          "field_type": "text",
          "field_placeholder": "이름을 입력하세요",
          "field_default_value": "",
          "field_required_value": "1",
          "order_index": 1,
          "linked_column_id": "",
          "field_options": ""
        },
        {
          "field_id": "customer_phone",
          "field_label": "연락처",
          "field_type": "text",
          "field_placeholder": "010-0000-0000",
          "field_default_value": "",
          "field_required_value": "1",
          "order_index": 2,
          "linked_column_id": "",
          "field_options": ""
        }
      ],
      "executionHistory": [
        {
          "task_order": 1,
          "action_type": "showInputForm",
          "description": "입력폼 표시",
          "data": null
        }
      ],
      "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"
    }
  }
}

입력폼 응답 후 처리

inputForm 응답이 반환되면 클라이언트는 입력 폼 UI를 표시해야 합니다.
사용자가 입력을 완료하면, 응답에 포함된 form_id, task_id, order, executionHistory, chat_log_uuid 값과 사용자가 입력한 form_data 를 함께 구성하여 입력폼 제출 API로 전달합니다.
자세한 요청 형식은 입력폼 제출 API 문서를 참고하세요.

3) 예약 입력 폼 응답 예시 (extra.reservationBookingForm)

JSON
{
  "success": true,
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
  "message": {
    "content": "예약 입력이 필요합니다."
  },
  "extra": {
    "reservationBookingForm": {
      "type": "reservationBookingForm",
      "form_id": "reservation_booking_request",
      "form_name": "예약 신청",
      "guide_message": "예약에 필요한 정보를 입력해 주세요.",
      "reservation_step": "customer_info",
      "field_list": [
        {
          "field_id": "customer_name",
          "field_label": "이름",
          "field_type": "text",
          "field_placeholder": "이름을 입력하세요",
          "field_required_value": "1"
        },
        {
          "field_id": "customer_phone",
          "field_label": "연락처",
          "field_type": "text",
          "field_placeholder": "010-0000-0000",
          "field_required_value": "1"
        }
      ]
    }
  }
}

4) 예약 조회 폼 응답 예시 (extra.reservationLookupForm)

JSON
{
  "success": true,
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
  "message": {
    "content": "예약 조회가 필요합니다."
  },
  "extra": {
    "reservationLookupForm": {
      "type": "reservationLookupForm",
      "form_id": "reservation_lookup_request",
      "form_name": "예약 조회",
      "guide_message": "예약번호를 입력해 주세요.",
      "field_list": [
        {
          "field_id": "reservation_no",
          "field_label": "예약번호",
          "field_type": "text",
          "field_placeholder": "예약번호를 입력하세요",
          "field_required_value": "1"
        }
      ]
    }
  }
}

5) 페이지 이동 응답 예시 (extra.pageRedirect)

JSON
{
  "success": true,
  "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
  "message": {
    "content": "예약 페이지로 이동합니다."
  },
  "extra": {
    "pageRedirect": {
      "url": "https://example.com/reservation"
    }
  }
}

extra 필드 설명

필드타입설명
extra.showCardObject카드형 응답 데이터입니다. 채팅창에서는 카드 UI로 표시됩니다.
extra.inputFormObject입력 폼 데이터입니다. 채팅창에서는 입력 폼 UI로 표시됩니다.
extra.reservationBookingFormObject예약 입력 폼 데이터입니다. 채팅창에서는 예약 입력 폼 UI로 표시됩니다.
extra.reservationLookupFormObject예약 조회 폼 데이터입니다. 채팅창에서는 예약 조회 폼 UI로 표시됩니다.
extra.pageRedirectObject페이지 이동 데이터입니다. 클라이언트에서 링크 이동 처리에 사용합니다.

클라이언트 구현 권장 순서

  1. extra.showCard 가 있으면 카드 UI를 표시합니다.
  2. extra.inputForm 이 있으면 입력 폼 UI를 표시합니다.
  3. extra.reservationBookingForm 이 있으면 예약 입력 폼 UI를 표시합니다.
  4. extra.reservationLookupForm 이 있으면 예약 조회 폼 UI를 표시합니다.
  5. extra.pageRedirect 가 있으면 이동 버튼 또는 자동 이동을 처리합니다.
  6. 위 값이 없으면 message.content 를 일반 텍스트 답변으로 표시합니다.


2. 대화 피드백 API

대화 응답에서 받은 chat_log_uuid를 이용해, 해당 대화에 대한 피드백을 저장할 수 있습니다.

URL

Plain Text
https://api.sidetalk.ai/v1/chat/feedback

HTTP 메서드

Plain Text
POST

헤더

헤더값설명
Content-Typeapplication/json요청 본문이 JSON 형식임을 나타냅니다.
AuthorizationBearer <api_key>Sidetalk 대시보드에서 제공된 API 키를 사용합니다.

본문

파라미터타입필수설명
session_idString예피드백을 저장할 대화의 세션 ID입니다. 대화 요청 시 사용한 sessionId와 동일한 값이어야 합니다.
chat_log_uuidString예chat/completions 응답에서 받은 대화 UUID입니다.
chat_log_feedbackString예피드백 값입니다. 1은 도움이 됨, 2는 도움이 안 됨을 의미합니다.

요청 예시

Shell
curl -X POST https://api.sidetalk.ai/v1/chat/feedback \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${API_KEY}" \
  -d '{
    "session_id": "test-session-id",
    "chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
    "chat_log_feedback": "1"
  }'

성공 응답 예시

JSON
{
  "success": true,
  "updated": true,
  "already_saved": false,
  "feedback": "1"
}

응답 필드 설명

필드타입설명
successBoolean요청 성공 여부입니다.
updatedBoolean이번 요청으로 실제 저장이 이루어졌는지 여부입니다.
already_savedBoolean이미 피드백이 저장되어 있었는지 여부입니다.
feedbackString최종 저장된 피드백 값입니다.

3. 피드백 저장 조건 및 주의사항

  • 피드백 저장 시 session_id는 대화 요청 시 사용한 sessionId와 동일해야 합니다.
  • chat_log_uuid는 반드시 해당 응답에서 받은 값이어야 합니다.
  • 피드백은 완료된 대화에만 저장할 수 있습니다.
  • 이미 저장된 피드백은 다시 변경되지 않을 수 있습니다. 이 경우 already_saved: true가 반환될 수 있습니다.
  • chat_log_feedback는 반드시 문자열 "1" 또는 "2"여야 합니다.
  • 권장 방식은:
    1. chat/completions 호출
    2. 응답에서 chat_log_uuid 저장
    3. 사용자가 버튼 클릭
    4. chat/feedback 호출

예시 코드 (JavaScript)

스트림 응답 처리 (stream이 true인 경우)

JavaScript
function sendMessage(message) {
    const apiUrl = "https://api.sidetalk.ai/v1/chat/completions";
    const requestData = {
        "message": message,
        "stream": true // 없는 경우 기본값 false로 설정됩니다.
    };

    fetch(apiUrl, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "Authorization": "Bearer [API_KEY]"
        },
        body: JSON.stringify(requestData),
    })
    .then(async response => {
        // HTTP 상태 코드 확인
        if (!response.ok) {
            const errorData = await response.json();
            console.error("Server Error:", errorData.message || 'Unknown error');
            return;
        }

        if (!response.body) throw new Error("ReadableStream not supported in response.");

        const reader = response.body.getReader();
        const decoder = new TextDecoder();

        function readStream() {
            return reader.read().then(({ done, value }) => {
                if (done) {
                    console.log("Stream complete.");
                    return;
                }

                const chunk = decoder.decode(value, { stream: true });
                const messages = chunk.split("\n\n");

                messages.forEach(message => {
                    if (message.startsWith("data: ")) {
                        const dataStr = message.replace("data: ", "");

                        if (dataStr === "[DONE]") {
                            console.log("Stream finished with [DONE] signal.");
                            reader.cancel();
                            return;
                        }

                        try {
                            const data = JSON.parse(dataStr);

                            // 서버에서 success: false를 보내는 경우 오류 처리
                            if (data.success === false) {
                                console.error("Error from server:", data.message || 'Unknown error');
                                reader.cancel();
                                return;
                            }

                            if (data.chunk) {
                                console.log("Chunk received:", data.chunk);
                                // 응답 처리 로직 추가
                            }

                            if (data.outsideData) {
                                console.log("Outside data received:", data.outsideData);
                                // 추가 데이터 처리 로직 추가
                            }
                        } catch (error) {
                            console.error("Error parsing JSON:", error);
                        }
                    }
                });

                return readStream();
            });
        }

        return readStream();
    })
    .catch(error => {
        console.error("Error receiving stream:", error);
    });
}

4. JavaScript 예시

4-1. JSON 응답 처리 예시

JavaScript
async function sendMessage(message) {
  const response = await fetch("https://api.sidetalk.ai/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer [API_KEY]"
    },
    body: JSON.stringify({
      message,
      stream: false,
      sessionId: "test-session-id",
      memberId: "user_123"
    })
  });

  const data = await response.json();

  if (!response.ok || !data.success) {
    console.error("Server Error:", data.message || data.error || "Unknown error");
    return;
  }

  console.log("AI 답변:", data.message.content);
  console.log("chat_log_uuid:", data.chat_log_uuid);

  return {
    answer: data.message.content,
    chatLogUuid: data.chat_log_uuid
  };
}

4-2. 스트림 응답 처리 예시

JavaScript
async function sendMessageStream(message) {
  const response = await fetch("https://api.sidetalk.ai/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer [API_KEY]"
    },
    body: JSON.stringify({
      message,
      stream: true,
      sessionId: "test-session-id"
    })
  });

  if (!response.ok) {
    const errorData = await response.json();
    console.error("Server Error:", errorData.message || errorData.error || "Unknown error");
    return;
  }

  if (!response.body) {
    throw new Error("ReadableStream not supported in response.");
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let savedChatLogUuid = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunkText = decoder.decode(value, { stream: true });
    const blocks = chunkText.split("\n\n");

    for (const block of blocks) {
      if (!block.trim()) continue;

      const lines = block.split("\n");
      let eventName = "message";
      const dataLines = [];

      for (const line of lines) {
        if (line.startsWith("event:")) {
          eventName = line.replace("event:", "").trim();
        }
        if (line.startsWith("data:")) {
          dataLines.push(line.replace("data:", "").trimStart());
        }
      }

      const dataStr = dataLines.join("\n").trim();
      if (!dataStr) continue;

      if (dataStr === "[DONE]") {
        console.log("Stream finished.");
        return savedChatLogUuid;
      }

      try {
        const data = JSON.parse(dataStr);

        if (eventName === "message" && data.chunk) {
          console.log("Chunk:", data.chunk);
        }

        if (data.chat_log_uuid && !savedChatLogUuid) {
          savedChatLogUuid = data.chat_log_uuid;
          console.log("chat_log_uuid:", savedChatLogUuid);
        }

        if (eventName === "showCard") {
          console.log("Show card payload:", data);
        }

        if (eventName === "inputForm") {
          console.log("Input form payload:", data);
        }

        if (eventName === "pageRedirect") {
          console.log("Redirect payload:", data);
        }
      } catch (error) {
        console.error("Error parsing stream JSON:", error);
      }
    }
  }

  return savedChatLogUuid;
}

4-3. 피드백 저장 예시

JavaScript
async function sendFeedback({ sessionId, chatLogUuid, isHelpful }) {
  const feedbackValue = isHelpful ? "1" : "2";

  const response = await fetch("https://api.sidetalk.ai/v1/chat/feedback", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer [API_KEY]"
    },
    body: JSON.stringify({
      session_id: sessionId,
      chat_log_uuid: chatLogUuid,
      chat_log_feedback: feedbackValue
    })
  });

  const data = await response.json();

  if (!response.ok || !data.success) {
    console.error("Feedback Error:", data.error || data.message || "Unknown error");
    return null;
  }

  console.log("Feedback saved:", data);
  return data;
}


5. 오류 응답

오류가 발생한 경우 다음과 같은 형식으로 응답이 반환될 수 있습니다.

응답 예시

JavaScript
{
  "success": false,
  "message": "서버 오류가 발생했습니다.",
  "error": "오류 메시지 내용"
}
또는 피드백 API에서는 아래와 같은 형식이 반환될 수 있습니다.
JavaScript
{
  "success": false,
  "error": "chat_log_feedback must be 1 or 2"
}

주요 오류 케이스

HTTP 상태예시설명
400chat_log_feedback must be 1 or 2피드백 값이 잘못된 경우
400session_id, chat_log_uuid, chat_log_feedback are required필수 파라미터가 누락된 경우
403Authentication errorAPI 인증에 실패한 경우
404not_found해당 대화 로그를 찾을 수 없는 경우
500서버 오류서버 내부 오류가 발생한 경우

6. 참고 사항

  • stream을 지정하지 않으면 기본값은 false이며, JSON 응답으로 반환됩니다.
  • 피드백 저장을 위해서는 반드시 chat_log_uuid를 클라이언트에서 저장해야 합니다.
  • chat/completions 요청 시의 sessionId와 chat/feedback 요청 시의 session_id는 같은 값을 사용해야 합니다.
  • 스트림 응답은 message 외에도 다양한 event:를 포함할 수 있으므로, 단순히 data:만 파싱하지 말고 event:도 함께 처리하는 것을 권장합니다.
  • 피드백 API는 good, bad를 별도 엔드포인트로 나누지 않고 하나의 API에서 chat_log_feedback 값으로 구분합니다.