일일 운세 스트리밍
curl --request POST \
--url https://bithumb-saju.vercel.app/api/daily-fortune-stream \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"saju": {},
"userName": "<string>",
"date": "<string>",
"model": "<string>"
}
'import requests
url = "https://bithumb-saju.vercel.app/api/daily-fortune-stream"
payload = {
"saju": {},
"userName": "<string>",
"date": "<string>",
"model": "<string>"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({saju: {}, userName: '<string>', date: '<string>', model: '<string>'})
};
fetch('https://bithumb-saju.vercel.app/api/daily-fortune-stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://bithumb-saju.vercel.app/api/daily-fortune-stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'saju' => [
],
'userName' => '<string>',
'date' => '<string>',
'model' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://bithumb-saju.vercel.app/api/daily-fortune-stream"
payload := strings.NewReader("{\n \"saju\": {},\n \"userName\": \"<string>\",\n \"date\": \"<string>\",\n \"model\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://bithumb-saju.vercel.app/api/daily-fortune-stream")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"saju\": {},\n \"userName\": \"<string>\",\n \"date\": \"<string>\",\n \"model\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://bithumb-saju.vercel.app/api/daily-fortune-stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"saju\": {},\n \"userName\": \"<string>\",\n \"date\": \"<string>\",\n \"model\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body운세
일일 운세 스트리밍
POST
/
api
/
daily-fortune-stream
일일 운세 스트리밍
curl --request POST \
--url https://bithumb-saju.vercel.app/api/daily-fortune-stream \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"saju": {},
"userName": "<string>",
"date": "<string>",
"model": "<string>"
}
'import requests
url = "https://bithumb-saju.vercel.app/api/daily-fortune-stream"
payload = {
"saju": {},
"userName": "<string>",
"date": "<string>",
"model": "<string>"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({saju: {}, userName: '<string>', date: '<string>', model: '<string>'})
};
fetch('https://bithumb-saju.vercel.app/api/daily-fortune-stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://bithumb-saju.vercel.app/api/daily-fortune-stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'saju' => [
],
'userName' => '<string>',
'date' => '<string>',
'model' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://bithumb-saju.vercel.app/api/daily-fortune-stream"
payload := strings.NewReader("{\n \"saju\": {},\n \"userName\": \"<string>\",\n \"date\": \"<string>\",\n \"model\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://bithumb-saju.vercel.app/api/daily-fortune-stream")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"saju\": {},\n \"userName\": \"<string>\",\n \"date\": \"<string>\",\n \"model\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://bithumb-saju.vercel.app/api/daily-fortune-stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"saju\": {},\n \"userName\": \"<string>\",\n \"date\": \"<string>\",\n \"model\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyServer-Sent Events(SSE)를 통해 운세를 실시간으로 스트리밍합니다.
AI 응답이 생성되는 대로 클라이언트에 전달되어, 사용자에게 더 빠른 피드백을 제공할 수 있습니다.
Request Body
SajuResult
required
클라이언트에서 계산한 사주 데이터 객체입니다.
string
required
사용자 이름입니다.
string
required
운세를 볼 날짜입니다. YYYY-MM-DD 형식입니다.
string
default:"haiku"
사용할 AI 모델입니다.
Response
성공
SSE 스트림이 시작됩니다. Content-Type은text/event-stream입니다.
이벤트 형식
data: {"chunk": "오늘은 "}
data: {"chunk": "투자에 "}
data: {"chunk": "좋은 날입니다."}
data: {"done": true, "fortune": {...}}
요청 예시
const response = await fetch('/api/daily-fortune-stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
saju,
userName: '김철수',
date: '2025-01-16',
model: 'haiku'
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
if (data.chunk) {
// 텍스트 청크 - UI에 추가
console.log(data.chunk);
}
if (data.done) {
// 스트리밍 완료 - 전체 데이터 사용 가능
console.log('완료:', data.fortune);
}
}
}
}
import { useState, useCallback } from 'react';
function useFortuneStream() {
const [text, setText] = useState('');
const [isStreaming, setIsStreaming] = useState(false);
const [fortune, setFortune] = useState(null);
const streamFortune = useCallback(async (saju, userName, date, model) => {
setIsStreaming(true);
setText('');
setFortune(null);
const response = await fetch('/api/daily-fortune-stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ saju, userName, date, model })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
if (data.chunk) {
setText(prev => prev + data.chunk);
}
if (data.done) {
setFortune(data.fortune);
}
}
}
}
setIsStreaming(false);
}, []);
return { text, isStreaming, fortune, streamFortune };
}
SSE 이벤트 구조
청크 이벤트
AI가 텍스트를 생성할 때마다 전송됩니다.{
"chunk": "오늘은 투자에 "
}
완료 이벤트
스트리밍이 완료되면 전체 운세 데이터와 함께 전송됩니다.{
"done": true,
"fortune": {
"todayFortune": {
"score": 78,
"overallAnalysis": "...",
"actionItems": [...]
},
"investmentFortune": {...}
}
}
에러 처리
스트리밍 중 에러가 발생하면 에러 이벤트가 전송됩니다.{
"error": true,
"message": "AI model timeout"
}
// 에러 처리 예시
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
if (data.error) {
console.error('스트리밍 에러:', data.message);
// 폴백: 일반 API 호출
const fallback = await fetch('/api/daily-fortune', {...});
}
}
}
스트리밍은 긴 응답에 특히 유용합니다. 짧은 응답(character, tendency 등)은 일반 API를 사용하는 것이 더 효율적입니다.
⌘I