요청 1회시 연결을 유지한 채 실시간으로 서버로부터 계속 데이터를 받을 수 있는 방법.
기존 웹 백엔드에 가볍게 추가하면 되므로,
양방향 통신이 필요한 경우가 아니라면 부담스럽게 서버 구성하고 연결 유지해야 하는 websocket 대신 쓰기 좋을 듯.
다음은 차트 페이지에서 차트를 그리기 전 데이터를 가져오는게 좀 오래걸리는 상황에서 실시간으로 진행률을 progress bar로 표시해주는 예제.
front-end (html)
<!doctype html>
<html>
<head>
<script src="/PATH/TO/draw_chart.js"></script>
</head>
<body>
<progress id="progress_bar" value="0" max="100"></progress>
<span id="progress_percentage">0 %</span>
<br>
<button id="btn">Get</button>
</body>
</html>
front-end (js)
window.addEventListener('DOMContentLoaded', ()=>{
const progress_bar = document.getElementById('progress_bar');
const progress_percentage = document.getElementById('progress_percentage');
document.getElementById('btn').addEventListener('click', ()=>{
const evtSrc = new EventSource('/PATH/TO/get_chart_data.php');
evtSrc.addEventListener('error', (err)=>{
// 기본적으로 네트워크가 끊어지면 자동 재접속하므로 방지하려면 여기서 명시적으로 close 해줘야 함
evtSrc.close();
});
// 전송되는 데이터에 "event: " 가 없고 "data: " 만 있으면 여기로 옴
evtSrc.addEventListener('message', (evt)=>{
console.log(`message = ${evt.data}`);
});
// event: progress_value
evtSrc.addEventListener('progress_value', (evt)=>{
const progress_value = evt.data;
progress_bar.value = progress_value;
progress_percentage.textContent = `${progress_value} %`;
});
// event: complete
evtSrc.addEventListener('complete', (evt)=>{
// TODO 차트 그리기
// drawChart(JSON.parse(evt.data));
});
});
});
back-end (php)
<?php
header("Cache-Control: no-store");
header("Content-Type: text/event-stream");
// 기간은 한달
$endDate = date('Ymd', strtotime('yesterday'));
$startDate = date('Ymd', strtotime('-1 month'));
$dateDiff = date_diff(new DateTime($startDate), new DateTime($endDate))->days + 1;
$chartData = [];
$dt = $startDate;
$i = 0;
// 데이터를 하루치씩 가져오면서 클라이언트에 진행률 전송
while ($dt <= $endDate) {
// 특정 날짜에 해당하는 차트 데이터 가져오기
$chartData[$dt] = getChartData($dt);
// data만 있는 경우 테스트
if (rand(0,10) === 5) {
send('', 'no event, data only');
}
// 진행률 계산, 전송
++$i;
$progressValue = sprintf("%.1f", ($i / $dateDiff) * 100);
send('progress_value', $progressValue);
$dt = date('Ymd', strtotime($dt)+86400);
}
// 완료시 차트 데이터 전송하고 종료
send('complete', json_encode($chartData));
// 하루치 차트 데이터 가져오기. 찔끔 시간끄는 pseudo code
function getChartData($dt)
{
usleep(100000);
return [];
}
// 클라이언트에 데이터 전송
function send($msg, $data)
{
if (trim($msg)) {
echo "event: ".$msg."\n";
}
echo "data: ".$data."\n\n";
ob_end_flush();
flush();
}
[참고]
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events