결과화면

ticker_ws.py

from fastapi import APIRouter, WebSocket
import websockets
import json
router = APIRouter()
UPBIT_WS_URL = "wss://api.upbit.com/websocket/v1"
@router.websocket("/chart")
async def ticker(ws: WebSocket):
await ws.accept()
async with websockets.connect(UPBIT_WS_URL) as upbit_ws:
subscribe = [{"ticket": "proxy"}, {"type": "ticker", "codes": ["KRW-BTC"]}]
await upbit_ws.send(json.dumps(subscribe))
while True:
raw = await upbit_ws.recv() # bytes
text = raw.decode("utf-8") # JSON 문자열로 변환
await ws.send_text(text) # 문자열로 전송
1️⃣ WebSocket이 뭔데?
일반 HTTP는 “요청 → 응답” 1번만 하고 끝나는 연결이다.
하지만 WebSocket은 서버 ↔ 클라이언트가 계속 연결을 유지해서
서버가 ‘자유롭게’ 데이터를 실시간으로 보낼 수 있게 만드는 통신 방식이다. 즉,
- HTTP: "나 이거 줘" → 서버: "여기" → 끝
- WebSocket: 서로 초대 → 연결 유지 → 필요할 때마다 계속 "야 이거 새로 들어왔어"라고 보낼 수 있음
2️⃣ async / await는 왜 필요한가?
🌟 비유로 설명
일반 함수는 한 번에 하나의 일만 처리한다. 내부에서 느린 작업이 있으면 그 시간 동안 아무것도 못 하고 멈춘다.
→ 하지만 실시간 WebSocket은 “기다리는 시간”이 매우 많다. 업비트에서 데이터가 들어올 때까지 컴퓨터는 놀고 있음.
그래서 Python은 “비동기(async)” 기능을 제공한다.
async = 이 함수는 비동기 작업을 할 수 있는 함수다.
await = 여기서 잠깐 쉬어도 된다. 다른 작업은 계속 해라.
즉 async/await의 핵심 목적:
✔️ 서버가 멈추지 않게 하기
✔️ WebSocket처럼 “데이터 올 때까지 기다려야 하는 작업”을 효율적으로 처리
3️⃣ async def ticker(ws: WebSocket) 설명
@router.websocket("/chart")
async def ticker(ws: WebSocket):
- FastAPI에게 "이 URL은 WebSocket용"이라고 알려주는 코드
- ws는 브라우저와 연결되는 WebSocket 객체
즉,
🔹 React → ws://localhost:8000/chart 로 접속
🔹 FastAPI는 이 함수를 실행해서 브라우저와 WebSocket 연결을 맺음
4️⃣ await ws.accept()는 뭐야?
브라우저가 WebSocket 연결을 하려고 하면 서버에 인사함:
“나 WebSocket 연결해도 돼?”
ws.accept()는:
✔️ 서버가 그 요청을 받아들이고
✔️ WebSocket 통신을 시작하는 단계
await은:
✔️ "이 작업 끝날 때까지 기다리되, 다른 작업은 해도 됨"
5️⃣ Upbit WebSocket 연결(서버 → 업비트)
asyncwith websockets.connect(UPBIT_WS_URL) as upbit_ws:
이 줄은:
FastAPI 서버가 업비트의 WebSocket 서버에 접속하는 코드다.
브라우저가 FastAPI에게 “실시간 데이터줘”
→ FastAPI는 Upbit에게 “데이터 주세요” 하고 연결함
브라우저 ←→ FastAPI ←→ 업비트(실시간)
6️⃣ 구독 요청(subscription) 보내기
subscribe = [ {"ticket": "proxy"}, {"type": "ticker", "codes": ["KRW-BTC"]} ]
await upbit_ws.send(json.dumps(subscribe))
Upbit WebSocket은 연결만 된다고 해서 데이터를 막 보내지 않음.
그래서 “KRW-BTC의 ticker 데이터를 보내줘”라고 서버에 요청해야 한다.
json.dumps(subscribe)는:
✔️ 파이썬 리스트/딕셔너리를 JSON 문자열로 변환하는 작업
7️⃣ while True는 왜 필요한가?
이건 “무한 반복”이다.
왜 무한 반복?
업비트 실시간 데이터는:
🔸 1번 요청 → 1번 받는 게 아니라
🔸 연결이 유지되고 계속 들어온다
즉 패턴은:
- 업비트: "새로운 데이터!"
- 업비트: "새로운 거래가 발생!"
- 업비트: "또 발생!"
- … 이렇게 수십, 수백, 수천 번 무한히 보내줌
그래서 우리는 계속 recv() 하고 → 브라우저로 send() 해야 함.
8️⃣ raw = await upbit_ws.recv() ← 이게 핵심
🔥 recv()란?
WebSocket 서버가 보낸 메시지를 받는 함수
→ 업비트가 실시간으로 보내는 데이터를 받아오는 역할
반환 타입
- Upbit는 “바이너리(bytes)”로 데이터를 보낸다.
- 즉 파이썬이 보기엔 그냥 이진 데이터 뭉텅이.
9️⃣ .decode("utf-8")는 왜 필요한가?
이진(bytes) 데이터를 사람이 읽을 수 있는 문자열로 바꿈.
비유하면:
- bytes = 암호처럼 생긴 디지털 0/1 조합
- decode = 그것을 인간이 읽을 수 있는 글자로 변환
🔟 await ws.send_text(text)는 뭐야?
이건 FastAPI가 React 브라우저에게 실시간적으로 보내는 부분.
즉 전체 흐름은:
업비트 → bytes → raw
→ decode → 문자열 text
→ React 브라우저로 send_text
CandleChart.js


import React, { useState, useEffect } from "react";
import Chart from "react-apexcharts";
import { getCandle } from "../services/upbitApi";
function CandleChart() {
// 1) 이전 캔들 데이터 조회
const [candle, setCandle] = useState([]);
useEffect(() => {
getCandle().then(res => setCandle(res.data));
}, []);
const seriesData = candle.map(item => ({
x: new Date(item.candle_date_time_kst),
y: [
item.opening_price,
item.high_price,
item.low_price,
item.trade_price,
]
}));
// 2) 실시간 웹소켓 받은 데이터
const [liveData, setLiveData] = useState(null);
// WebSocket 실시간 ticker 수신 -> 캔들 업데이트
useEffect(() => {
const ws = new WebSocket("ws://127.0.0.1:8000/chart");
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setLiveData(data);
};
ws.onopen = () => console.log("WS 연결됨");
ws.onclose = () => console.log("WS 종료됨");
return () => ws.close();
}, []);
const chartOptions = {
chart: { type: "candlestick", height: 450 },
xaxis: { type: "datetime" },
yaxis: { tooltip: { enabled: true } },
};
return (
<div> {liveData ? (
<div>
<p>현재가: {liveData.trade_price}</p>
<p>체결 시간: {new Date(liveData.trade_timestamp).toLocaleTimeString()}</p>
<p>거래량: {liveData.trade_volume}</p>
</div>
) : (
<p>실시간 데이터 수신 중...</p>
)}
<Chart
options={chartOptions}
series={[{ data: seriesData }]}
type="candlestick"
height={450}
/>
</div>
);
}
export default CandleChart;
과거 차트는 HTTP로 가져오고, 실시간 가격은 WebSocket으로 받아서 화면에서 계속 보여주는 코드이다.
🧱 1. React 기본 구조부터 설명
React 컴포넌트는 하나의 화면 조각이라고 생각.
function CandleChart() {
return (화면);
}
CandleChart는 “캔들 차트 화면”을 표시하는 컴포넌트.
🧱 2. useState — 변수 저장하는 곳
React에서는 일반 변수를 쓰지 않고 화면이 자동으로 다시 그려지도록 하는 특별한 변수 useState를 사용.
예)
const [candle, setCandle] = useState([]);
- candle → “캔들 데이터”가 들어감
- setCandle → candle에 값을 넣는 함수
- useState([]) → 처음엔 빈 배열로 시작하겠다
🧱 3. useEffect — “페이지 켜질 때 한 번 실행”
React에서 useEffect는:
“화면이 처음 로딩될 때 실행해줘!”
라는 의미.
useEffect(() => {
getCandle().then(res => setCandle(res.data));
}, []);
✔ 이 코드가 하는 일
- Upbit API를 HTTP로 불러옴 (getCandle())
- 그 결과를 candle에 저장 (setCandle(res.data))
🧱 4. 과거 차트 데이터 → apexcharts에 넣기 위한 가공
const seriesData = candle.map(item => ({
x: newDate(item.candle_date_time_kst),
y: [
item.opening_price, item.high_price, item.low_price, item.trade_price,
]
}));
🧠 하는 일
Upbit에서 받은 데이터 형태는 차트가 읽을 수 없어서,
차트 라이브러리(apexcharts)가 원하는 모양으로 바꿔줌.
- x: 시간
- y: [시가, 고가, 저가, 종가]
캔들 차트의 기본 구조 그대로.
🧱 5. 실시간 데이터 저장소 만들기
const [liveData, setLiveData] = useState(null);
여기에 업비트 WebSocket에서 받은 실시간 가격을 넣는다.
🧱 6. WebSocket 연결 — 실시간 가격 받기
여기가 이 파일의 핵심 👇
useEffect(() => {
const ws = newWebSocket("ws://127.0.0.1:8000/chart");
📌 WebSocket은 뭐야?
HTTP는 "한 번 요청하면 한 번만 응답"
하지만,
WebSocket은 양방향 실시간 통신이 가능한 파이프
코인 가격은 계속 바뀌니 한 번만 받으면 안 되잖아?
그래서 “웹소켓”으로 계속 들어오는 데이터를 받는 것.
🧠 ws.onmessage
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setLiveData(data);
};
- 코인 가격이 변경될 때마다 server → client로 메시지를 보냄
- event.data = 서버에서 보낸 JSON 문자열
- JSON.parse() 해서 실제 객체로 변환
- setLiveData(data)로 상태에 저장 → 화면 자동 업데이트됨
🧠 ws.onopen / ws.onclose
ws.onopen = () => console.log("WS 연결됨");
ws.onclose = () => console.log("WS 종료됨");
- 연결될 때 로그 찍기
- 페이지 나갈 때 종료시키기
🧱 7. 차트 옵션 설정
constchartOptions= {
chart: { type:"candlestick", height:450 },
xaxis: { type:"datetime" },
yaxis: { tooltip: { enabled:true } },
};
그냥 apexcharts 설정.
- 캔들 차트로 표시
- X축은 시간
- Y축은 숫자

다음엔, 실시간으로 받은 데이터 소켓으로, 이전 데이터 차트와 합하여, 실시간으로 차트가 움직이는걸 구현해보려고한다.
https://bright-landscape.tistory.com/465
[FastAPI, Recat] 이전 데이터 + 실시간 데이터(WebSocket) 합하기 (2)
CandleChart.jsimport React, { useState, useEffect, useRef } from "react";import Chart from "react-apexcharts";import axios from "axios";function CandleChart() { /** ----------------------------- * 1) 캔들 데이터 저장할 state * ---------------------
bright-landscape.tistory.com
'✨ python > FastAPI' 카테고리의 다른 글
| [FastAPI, Recat] 이전 데이터 + 실시간 데이터(WebSocket) 합하기 (3) (0) | 2025.12.14 |
|---|---|
| [FastAPI, Recat] 이전 데이터 + 실시간 데이터(WebSocket) 합하기 (2) (0) | 2025.12.12 |
| [FastApi, React] 업비트 WebSocket으로 실시간 시세 불러오기 (0) | 2025.12.10 |
| FastAPI로 업비트 차트 React로 JSON 형식 데이터 가져오기 (0) | 2025.12.08 |
| 업비트 API 환경변수 세팅 및 FastAPI 로 자산 조회하기 (0) | 2025.12.06 |
댓글