KillKagi 개발기: 치지직 도네이션 API 연동과 OBS 오버레이 구현

KillKagi를 만들며 겪은 치지직 도네이션 API 연동 이슈, 파티 도네이션 처리, OBS 오버레이 실시간 반영 구조를 코드와 함께 정리한 개발 일지입니다.

목차

시작하게 된 계기

스트리머 친구가 방송 중에 이런 말을 했습니다.

“도네이션 받을 때마다 킬 목표 계산하는 게 너무 귀찮아. 자동으로 해주는 툴 없나?”

그 순간 ‘아, 이거 만들면 되겠다’ 싶었습니다. 치지직 API가 공개되어 있다는 것도 알고 있었고, OBS 브라우저 소스 기능을 활용하면 오버레이도 쉽게 만들 수 있을 것 같았습니다.

관련 글: OBS 오버레이를 React로 만드는 법, KillKagi 툴 상세

이 글에서 핵심으로 다루는 내용은 아래 3가지입니다.

  1. 치지직 도네이션 이벤트(WebSocket) 파싱 구조
  2. 일반/파티 도네이션 이벤트 타입 분기 처리
  3. 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);
};

배운 점

  1. API 문서 없이도 개발할 수 있다: 네트워크 탭과 약간의 인내심이면 됩니다.
  2. 엣지 케이스를 미리 생각하자: 파티 도네이션처럼 예외적인 케이스를 놓치면 사용자 경험이 나빠집니다.
  3. SSE가 WebSocket보다 간단한 경우가 많다: 단방향 실시간 통신에는 SSE가 더 적합합니다.

운영하면서 정한 유지보수 기준

KillKagi는 방송 중에 켜두는 도구라서 기능 수보다 안정성이 더 중요했습니다. 그래서 새 기능을 넣기 전에 아래 기준을 먼저 확인하도록 정했습니다.

  • 로컬 대시보드가 열리지 않아도 콘솔 로그로 원인을 확인할 수 있어야 합니다.
  • OBS 오버레이는 새로고침해도 마지막 진행 상태를 잃지 않아야 합니다.
  • 파티 도네이션처럼 이벤트 구조가 다른 경우에는 일반 도네이션과 분리해서 테스트합니다.
  • 방송 직전에 설정을 바꾸더라도 템플릿으로 되돌릴 수 있어야 합니다.

SSE는 단방향 상태 전달에 적합하지만, 브라우저가 연결을 다시 열 수 있다는 점을 고려해야 합니다. 구현할 때는 MDN Server-sent events 문서를 참고해 재연결과 캐시 헤더를 같이 확인했습니다.

앞으로 기능을 늘릴 때도 먼저 방송 중 끊김, 새로고침, 종료 후 재실행을 기준으로 검증하고, 그다음 테마나 플랫폼 연동을 확장하는 순서가 안전합니다.

시리즈 — killkagi-launch

이전 · 다음 글

이전 글 OBS 오버레이 React 제작 가이드: Browser Source 실시간 연동 다음 글 KillKagi 설치·사용 가이드: Node.js 설치부터 OBS 오버레이 연동까지