KillKagi 개발기: 치지직 도네이션 API 연동과 OBS 오버레이 구현
KillKagi를 만들며 겪은 치지직 도네이션 API 연동 이슈, 파티 도네이션 처리, OBS 오버레이 실시간 반영 구조를 코드와 함께 정리한 개발 일지입니다.
목차
시작하게 된 계기
스트리머 친구가 방송 중에 이런 말을 했습니다.
“도네이션 받을 때마다 킬 목표 계산하는 게 너무 귀찮아. 자동으로 해주는 툴 없나?”
그 순간 ‘아, 이거 만들면 되겠다’ 싶었습니다. 치지직 API가 공개되어 있다는 것도 알고 있었고, OBS 브라우저 소스 기능을 활용하면 오버레이도 쉽게 만들 수 있을 것 같았습니다.
관련 글: OBS 오버레이를 React로 만드는 법, KillKagi 툴 상세
이 글에서 핵심으로 다루는 내용은 아래 3가지입니다.
- 치지직 도네이션 이벤트(WebSocket) 파싱 구조
- 일반/파티 도네이션 이벤트 타입 분기 처리
- SSE 기반 OBS 오버레이 실시간 업데이트 패턴
치지직 API 탐색
치지직은 네이버에서 운영하는 게임 스트리밍 플랫폼입니다. 공식 API 문서가 잘 정리되어 있지는 않아서, 처음에는 네트워크 탭을 열어서 직접 분석해야 했습니다.
WebSocket 연결
도네이션 이벤트는 WebSocket을 통해 실시간으로 전달됩니다.
const ws = new WebSocket('wss://kr-ss1.chat.naver.com/chat');
ws.onopen = () => {
// 채널 구독
ws.send(JSON.stringify({
svcid: 'game',
cid: channelId,
svcNo: 1,
cmd: 100,
tid: 1,
bdy: {
accTkn: accessToken,
auth: 'READ',
devType: 2001,
}
}));
};
도네이션 이벤트 파싱
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// 도네이션 이벤트 타입 확인
if (data.cmd === 10100) {
const donation = data.bdy;
const amount = donation.payAmount;
const nickname = donation.userNickname;
console.log(`${nickname}님이 ${amount}원 도네이션!`);
updateKillCounter(amount);
}
};
가장 힘들었던 부분: 파티 도네이션
일반 도네이션은 비교적 쉽게 처리할 수 있었습니다. 문제는 파티 도네이션이었습니다.
파티 도네이션은 여러 명이 함께 도네이션하는 기능인데, 이벤트 구조가 일반 도네이션과 달랐습니다.
// 일반 도네이션
{
"cmd": 10100,
"bdy": {
"payAmount": 1000,
"userNickname": "시청자A"
}
}
// 파티 도네이션 (구조가 다름!)
{
"cmd": 10101,
"bdy": {
"partyDonation": {
"totalAmount": 5000,
"participants": [
{ "nickname": "시청자A", "amount": 2000 },
{ "nickname": "시청자B", "amount": 3000 }
]
}
}
}
처음에는 파티 도네이션을 놓쳐서 킬 카운터가 제대로 업데이트되지 않는 버그가 있었습니다. 이벤트 타입을 10101로 별도 처리하는 로직을 추가해서 해결했습니다.
OBS 오버레이 구현
OBS 브라우저 소스는 기본적으로 Chromium 기반이라 일반 웹 기술을 그대로 사용할 수 있습니다.
실시간 업데이트
서버에서 클라이언트로 실시간 데이터를 전달하기 위해 Server-Sent Events(SSE)를 사용했습니다.
// 서버 (Express)
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const sendUpdate = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
killCounter.on('update', sendUpdate);
req.on('close', () => {
killCounter.off('update', sendUpdate);
});
});
// 클라이언트 (오버레이)
const eventSource = new EventSource('/events');
eventSource.onmessage = (event) => {
const { current, target } = JSON.parse(event.data);
document.getElementById('kill-count').textContent = `${current} / ${target}`;
updateProgressBar(current / target * 100);
};
배운 점
- API 문서 없이도 개발할 수 있다: 네트워크 탭과 약간의 인내심이면 됩니다.
- 엣지 케이스를 미리 생각하자: 파티 도네이션처럼 예외적인 케이스를 놓치면 사용자 경험이 나빠집니다.
- SSE가 WebSocket보다 간단한 경우가 많다: 단방향 실시간 통신에는 SSE가 더 적합합니다.
운영하면서 정한 유지보수 기준
KillKagi는 방송 중에 켜두는 도구라서 기능 수보다 안정성이 더 중요했습니다. 그래서 새 기능을 넣기 전에 아래 기준을 먼저 확인하도록 정했습니다.
- 로컬 대시보드가 열리지 않아도 콘솔 로그로 원인을 확인할 수 있어야 합니다.
- OBS 오버레이는 새로고침해도 마지막 진행 상태를 잃지 않아야 합니다.
- 파티 도네이션처럼 이벤트 구조가 다른 경우에는 일반 도네이션과 분리해서 테스트합니다.
- 방송 직전에 설정을 바꾸더라도 템플릿으로 되돌릴 수 있어야 합니다.
SSE는 단방향 상태 전달에 적합하지만, 브라우저가 연결을 다시 열 수 있다는 점을 고려해야 합니다. 구현할 때는 MDN Server-sent events 문서를 참고해 재연결과 캐시 헤더를 같이 확인했습니다.
앞으로 기능을 늘릴 때도 먼저 방송 중 끊김, 새로고침, 종료 후 재실행을 기준으로 검증하고, 그다음 테마나 플랫폼 연동을 확장하는 순서가 안전합니다.