공식 문서: Session | CHZZK
이 문서는 공식 문서에 없거나, 공식 문서와 실제 동작이 다른 부분을 실제 연결 테스트로 확인해 정리한 것입니다.
목차
- 시작하기 전에
- 세션 연결 작업
- 개발자분들께서 많이 하시는 실수
- 클라이언트 구현 시 주의사항
- Client 라이브러리 정보
- 기타 정보
- SYSTEM
- CHAT
- DONATION
- SUBSCRIPTION
- 공식 문서와 실제 동작의 차이
- 변경 이력
- FAQ
-
채팅 메시지 조회와 (후원 알림 구독이 필요할 시)후원 조회Scope, (구독 알림 구독이 필요할 시)구독 조회Scope가 있음을 가정합니다. 해당 Scope들이 없을 경우500 Internal Server Error와 함께 세션 연결 URL 발급이 불가능합니다. -
이 문서의 JSON은 모두 JSONC로 작성되었습니다. 부가 설명을 위해 주석을 달았으며, 실제 JSON에서는 주석이 불가능한 점 유의하시길 바랍니다. 또한 가독성을 위해 들여쓰기를 했으나 실제로는 한 줄로 전달됩니다.
-
2026년 07월 26일 업데이트) Claude와 함께 실제 런타임 테스트를 진행하며 별도의 문서를 하나 더 작성했습니다. 여기에서 확인하실 수 있습니다.
많은 인원의 Event를 구독해야 하는 경우(예: 스트리머 후원을 1천 명 연동하는 경우 등), 스트리머가 개발자의 애플리케이션에 OAuth 로그인을 하게끔 하여 Access Token을 발급받아 세션을 생성하는 방식을 추천드립니다.
예를 들어보겠습니다.
| 구분 | 제한 |
|---|---|
| 한 채널당 구독 가능한 이벤트 종류 | 3개 (채팅, 후원 알림, 구독 알림) |
| 한 세션당 구독 가능한 이벤트 개수 | 30개 |
| Client 인증으로 동시 연결 유지 가능한 세션 개수 | 10개 |
| Access Token으로 동시 연결 유지 세션 개수 | 3개 |
"채널당 구독할 수 있는 이벤트 종류 및 개수"와 "동시 연결 유지 세션 개수"를 혼동하시면 안 됩니다.
Client 인증 방식의 세션 하나를 생성하여 10개 채널에 대해 구독 가능한 모든 이벤트 종류를 구독하면, 이벤트 개수 제한(30개)에 걸리게 됩니다. Client 인증으로 동시 연결을 유지할 수 있는 세션의 최대치인 10개를 모두 생성하면 100개 채널까지 구독할 수 있습니다.
일반적으로는 이 정도 제한으로도 충분하겠으나, 향후 업데이트에서 이벤트 종류가 늘어나거나(그럴 일은 없겠지만...) 세션을 이보다 더 많이 생성해야 할 경우 어려움을 겪으실 수 있습니다.
API로 발급된 세션 URL의 정확한 유효시간은 알 수 없으나, URL이 올바름에도 연결 시도 시 바로 끊기는 경우 해당 URL이 만료된 것입니다. 새로 발급하여 연결해주셔야 합니다.
개인적인 테스트에서는 약 30초간 연결 시도가 없으면 URL이 만료되는 것으로 확인했습니다.
이 특성 때문에, 클라이언트 라이브러리 차원의 자동 재연결 기능은 CHZZK 세션에 적합하지 않습니다. 끊긴 뒤 같은 URL로 재접속을 시도해봐야 이미 만료되었을 가능성이 높기 때문입니다. 재연결이 필요하다면 세션 URL 발급부터 다시 하셔야 합니다.
Client 인증으로 세션 시스템을 사용하실 때, 애플리케이션을 소유한 개발자 채널 이외에 타 스트리머의 채팅이나 후원을 구독하고 싶은 경우에는 이벤트 구독 요청의 Authorization 헤더 토큰 값을 타 스트리머가 사전에 인증 작업을 완료하여 발급한 Access Token 값으로 지정해야 합니다.
다만 앞서 서술했듯 한 세션당 구독 가능한 이벤트 개수를 고려하면, 개인적으로는 세션 자체를 Access Token 방식으로 생성하는 것을 추천드립니다.
- 내용을 읽으셨다면 짐작하셨겠지만, 네. 타 스트리머의 로그인 없이 타 스트리머의 채팅이나 후원 이벤트를 구독할 수 있는 방법은 현재로서는 없습니다.
생성된 세션 및 세션 키에 대해 아래 내용은 확인이 어렵거나 불가능한 것으로 파악됩니다.
세션 URL 발급 후 연결을 진행했으나 도중에 연결이 끊긴 경우, 그리고 최초 연결 제한 시간이 만료된 경우, 연결되지 않은 세션 정보가 세션 목록에서 사라지는 시간 (최대 39개의 세션 생성 이후 추가 세션을 생성하려 할 때 사라지는 조건으로 추측)- 2026년 07. 26일 업데이트) 공식 문서에 "연결이 끊어진 세션은 90일 동안만 조회 가능합니다."라는 내용이 추가된 것으로 확인됩니다.
- 세션을 세션 목록에서 강제로 제거하는 방법 (현재로서는 없습니다.)
-
가장 많은 착오가 발생할 수 있는 부분입니다. 공식 문서의 "최대 n개의 API 연결을 유지할 수 있음" 은 동시에 연결을 유지시킬 수 있는 개수를 뜻하며, 개발자가 최대로 생성할 수 있는 세션의 개수가 아닙니다. 세션은 언제든지 여러 개 생성할 수 있는 것으로 보입니다.
다만 최대 세션 조회 가능 개수인 39개 이상을 생성하면 어떻게 되는지는 확인해보지 못했습니다.해당 내용은 추가 조사가 필요해 보입니다. -
이벤트를 구독/구독 취소할 때 사용하는
sessionKey값은 POST 요청이어도 Query Parameter(?sessionKey=XXXXXX)로 설정해야 하며, 요청 Body에 JSON 형태로 작성하는 방식은 지원되지 않습니다. (여담..) -
WebSocket 연결이 되어 있지 않은 상태에서 REST API로 세션 키만 이용해 Event를 구독할 수 없습니다. CHZZK 측에서
400 Bad Request와 연결 확인 오류 메시지를 안내합니다.
직접 클라이언트를 구현하시거나, 기존 라이브러리가 정상 동작하지 않을 때 참고하시면 좋을 내용입니다. 아래는 모두 실제 연결로 확인한 내용입니다.
이 문서의 각 이벤트 항목에 적힌 JSON은 한 겹 벗긴 뒤의 모습입니다. 실제 Socket.IO 이벤트의 인자는 객체가 아니라 그 JSON을 문자열로 감싼 값입니다. 와이어에서는 이렇게 보입니다.
42["SYSTEM","{\"type\":\"connected\",\"data\":{\"sessionKey\":\"...\"}}"]
따라서 인자를 먼저 문자열로 파싱한 뒤, 그 문자열을 다시 JSON으로 파싱해야 합니다. 라이브러리가 인자를 특정 타입으로 자동 변환해주는 경우 여기서 실패할 수 있습니다.
다만, 공식 클라이언트인 socket.io-client 2.0.3, socket.io-client-java 1.0.2 에서는 내부 파서가 정상적으로 동작하고 있어 즉시 JSON 데이터에 접근하실 수 있습니다.
세션 URL에는 경로가 없으므로 네임스페이스는 루트(/)입니다. socket.io-client 2.x는 루트 네임스페이스에 대해서는 CONNECT 패킷(40)을 보내지 않습니다.
일부 구현체는 루트에도 CONNECT를 보내는데, auth는 핸드셰이크 쿼리에서 이미 소비된 뒤이므로 CHZZK 서버는 이를 인증 정보 없는 새 연결 시도로 취급합니다. 실제 응답은 다음과 같습니다.
-> 40 (클라이언트가 보낸 불필요한 CONNECT)
<- 42["error","auth fail"]
<- 41 (DISCONNECT)
<- 40 (CONNECT, 원래 연결 복구)
전송 계층은 끊기지 않기 때문에, 연결은 멀쩡히 살아있는데 disconnect 이벤트만 발생하는 이해하기 어려운 상황이 됩니다. 이 증상을 겪고 계시다면 사용 중인 라이브러리가 루트에 CONNECT를 보내는지 확인해보시길 바랍니다.
CHZZK가 사용하는 Engine.IO revision 3에서는 클라이언트가 2(ping)를 보내고 서버가 3(pong)으로 답합니다. Engine.IO revision 4(Socket.IO 3.x 이상)에서는 방향이 반대이므로, 최신 문서를 보고 구현하면 안 됩니다.
CHZZK의 핸드셰이크 값은 pingInterval=25000, pingTimeout=60000입니다. 즉 25초마다 ping을 보내야 하며, 생존 판정 시한은 85초입니다.
transport=websocket으로 바로 연결하는 경우, 공식 Node 구현은 upgrades: []를 반환하고 CHZZK는 ["websocket"]을 반환합니다. 서버 구현에 따라 다를 뿐 둘 다 정상입니다. 이 목록에 websocket이 있는지 검사한 뒤 없으면 연결을 포기하는 클라이언트는 정상 연결을 스스로 끊게 됩니다.
가장 조용히 틀리기 쉬운 부분입니다. CHAT 페이로드에는 시각이 두 가지 형식으로 들어있는데, 기준이 서로 다릅니다.
"messageTime": 1785003258567
"eventSentAt": "2026-07-26T03:14:18.629843820"
같은 메시지의 두 값을 맞춰보면 이렇습니다.
messageTime을 UTC로 읽으면 2026-07-25T18:14:18.567
messageTime을 KST로 읽으면 2026-07-26T03:14:18.567
eventSentAt 2026-07-26T03:14:18.629843820
즉 messageTime은 epoch 밀리초(UTC)이고, eventSentAt은 KST인데 오프셋 표기가 없습니다. 62밀리초 차이는 서버 처리 지연입니다. eventSentAt을 UTC로 해석하면 9시간이 어긋나며, 오프셋이 없으니 파싱 단계에서 오류도 발생하지 않습니다.
덧붙여 소수점 이하가 9자리라 RFC 3339 형식이 아닙니다. 언어에 따라 기본 날짜 파서로는 읽히지 않을 수 있습니다.
CHZZK 세션 서버는 Socket.IO 2.x 전용입니다. Socket.IO 3.x 이상은 프로토콜(Engine.IO revision 4)이 달라 서버가 연결을 거부합니다.
| 언어 | 라이브러리 | 비고 |
|---|---|---|
| TypeScript / JavaScript | socket.io-client | 공식 문서에는 2.0.3까지로 안내되어 있으나, 실제로는 2.x 계열이면 정상 연결됩니다. 타입이 필요하시면 @types/socket.io-client 1.4.36을 사용하시면 됩니다. |
| Java / Kotlin | socket.io-client-java | 1.0.2 (Socket.IO 2.x 프로토콜 대응 버전) |
| Go | googollee/go-socket.io | 권장하지 않습니다. 아카이브되었고, 클라이언트 쪽에 두 가지 문제가 있습니다. ① dialer의 트랜스포트 목록에 polling만 있어 websocket으로 연결할 수 없습니다. ② 루트 네임스페이스에도 CONNECT를 보내 위에서 설명한 auth fail 문제가 발생합니다. |
| Go | sdkim96/chzzk-go | Go를 사용하실 수 있다면 이 라이브러리가 권장됩니다. 작성 시점인 7월 26일 기준 활발히 관리되고 있으며 현재 치지직 공식 API에서 제공하는 기능들에 대해 구현이 완료되어 있습니다. |
| Go | fi-xz/chzzkgo | 작성 시점인 7월 26일 기준 비공개고 차후 공개로 돌릴 생각이긴 한데 어차피 제가 업무하는 곳에서 개인적으로 쌀먹하기 위한 구조로 클로드한테 짬처린 시킨 결과물이라 비추드립니다... |
-
발급받은 세션 연결 URL은
https://ssio<번호>.nchat.naver.com:443?auth=<AUTH_TOKEN>형태로 반환됩니다. "번호"에 해당하는 값은 '01' ~ '29' 까지 존재합니다. 아무런 번호나 입력해도auth쿼리 값만 유효하다면 연결하실 수 있으나, 세션 생성 시 전체 URL을 제공받기에 딱히 의의를 두진 않아도 될 것 같습니다. -
URL에 경로가 없으므로 Socket.IO 네임스페이스는 루트(
/) 입니다. 위의 클라이언트 구현 시 주의사항을 참고하시길 바랍니다. -
테스트에 사용한 코드 역시 이 Gist에 첨부합니다.
세션 연결 이후 자동으로 수신되는 시스템 메시지입니다. 별도의 구독 요청 없이 연결 직후부터 전달됩니다.
type은 connected, subscribed, unsubscribed, revoked 네 가지입니다. 이 중 connected와 subscribed는 실제 수신을 확인했습니다.
{
"type": "connected",
"data": {
"sessionKey": "<SOCKET_SESSION_KEY>"
// 현재 연결된 세션의 Session Key 값입니다.
// 이 값으로 Event를 구독하거나 세션을 구분할 수 있습니다.
}
}POST 요청으로 각 Event를 구독할 때 수신됩니다.
- 채팅 Event 구독 —
/open/v1/sessions/events/subscribe/chat?sessionKey=<SOCKET_SESSION_KEY> - 후원 Event 구독 —
/open/v1/sessions/events/subscribe/donation?sessionKey=<SOCKET_SESSION_KEY> - 구독 Event 구독 —
/open/v1/sessions/events/subscribe/subscription?sessionKey=<SOCKET_SESSION_KEY>
{
"type": "subscribed",
"data": {
"eventType": "CHAT", // 구독한 Event 타입. CHAT, DONATION, SUBSCRIPTION 중 하나입니다.
"channelId": "<SUBSCRIBED_CHANNEL_ID>" // 구독한 채널 ID
}
}POST 요청으로 각 Event 구독을 취소할 때 수신됩니다.
- 채팅 Event 구독 취소 —
/open/v1/sessions/events/unsubscribe/chat?sessionKey=<SOCKET_SESSION_KEY> - 후원 Event 구독 취소 —
/open/v1/sessions/events/unsubscribe/donation?sessionKey=<SOCKET_SESSION_KEY> - 구독 Event 구독 취소 —
/open/v1/sessions/events/unsubscribe/subscription?sessionKey=<SOCKET_SESSION_KEY>
{
"type": "unsubscribed",
"data": {
"eventType": "CHAT", // 구독 해지한 Event 타입. CHAT, DONATION, SUBSCRIPTION 중 하나입니다.
"channelId": "<UNSUBSCRIBED_CHANNEL_ID>" // 구독 해지한 채널 ID
}
}공식 문서에 기재되어 있으나 아직 실제 수신은 확인하지 못했습니다. unsubscribed와 동일한 형태로 안내되어 있습니다.
{
"type": "revoked",
"data": {
"eventType": "CHAT", // CHAT, DONATION, SUBSCRIPTION 중 하나
"channelId": "<REVOKED_CHANNEL_ID>"
}
}클라이언트를 구현하실 때는 문서에 없는
type값이 오더라도 동작이 중단되지 않도록 처리해두시길 권장드립니다.
세션 연결 이후 CHAT 이벤트를 구독하였을 때 수신되는 메시지입니다.
{
"channelId": "<STREAMING_CHANNEL_ID>", // 방송인의 채널 ID
"chatChannelId": "N2dd_1", // 채팅 채널 ID (메시지 삭제 시 사용)
"senderChannelId": "<CHAT_SENDER_CHANNEL_ID>", // 메시지를 전송한 사람의 채널 ID
"profile": { // 치지직 프로필 정보
"nickname": "<CHAT_SENDER_NICKNAME>", // 메시지를 전송한 사람의 닉네임
"verifiedMark": false, // 치지직 인증 마크 (파트너 스트리머 인증 마크로 추정)
"badges": [ // 뱃지 이미지 URL 목록
{
"imageUrl": "https://ssl.pstatic.net/static/nng/glive/icon/streamer.png" // 스트리머 본인의 뱃지 이미지
}
// 뱃지 종류가 다양한 만큼 모든 뱃지의 URL을 기입하기는 어려운 점 양해 부탁드립니다.
],
"userRoleCode": "streamer"
// 공식 문서에는 최상위 필드로 기재되어 있으나, 실제로는 profile 안에 있습니다.
// 가능한 값: streamer, common_user, streaming_channel_manager, streaming_chat_manager
},
"content": "do test {:d_47:}",
// 채팅 내용입니다. 치지직 채팅 이모지는 중괄호 안에 콜론을 두고 그 사이에 이모지 이름을 담는 형태입니다.
"emojis": { // 이모지 이름 및 URL
"d_47": "https://ssl.pstatic.net/static/nng/glive/icon/b_07.gif?type=f60_60"
},
"messageTime": 1785003258567, // 메시지 전송 시각. epoch 밀리초(UTC)입니다.
"eventSentAt": "2026-07-26T03:14:18.629843820"
// 공식 문서에 없는 필드입니다. KST이며 오프셋 표기가 없습니다.
// 위의 "클라이언트 구현 시 주의사항"을 반드시 참고해주세요.
}세션 연결 이후 DONATION 이벤트를 구독하였을 때 수신되는 메시지입니다.
{
"donationType": "CHAT", // 후원 타입. 현재 CHAT, VIDEO 두 종류입니다.
"channelId": "<DONATION_RECEIVED_CHANNEL_ID>", // 후원을 수신한 채널 ID (스트리머)
"donatorChannelId": "<DONATOR_CHANNEL_ID>", // 후원을 보낸 채널 ID (시청자)
"donatorNickname": "<DONATOR_NICKNAME>", // 후원을 보낸 채널의 닉네임 (시청자 닉네임)
"payAmount": 1000, // 후원 금액. 공식 문서에는 String으로 기재되어 있으나 실제로는 숫자입니다.
"donationText": "TEST", // 후원 내용
"emojis": {}, // 이모지 이름 및 URL
"eventSentAt": "2026-07-26T03:14:28.690447802" // 공식 문서에 없는 필드입니다. KST이며 오프셋 표기가 없습니다.
}영상 URL에 대한 별도 정보는 전달되지 않습니다.
{
"donationType": "VIDEO", // 후원 타입. 현재 CHAT, VIDEO 두 종류입니다.
"channelId": "<DONATION_RECEIVED_CHANNEL_ID>", // 후원을 수신한 채널 ID (스트리머)
"donatorChannelId": "<DONATOR_CHANNEL_ID>", // 후원을 보낸 채널 ID (시청자)
"donatorNickname": "<DONATOR_NICKNAME>", // 후원을 보낸 채널의 닉네임 (시청자 닉네임)
"payAmount": 1000, // 후원 금액
"donationText": "치지직, 스트리밍이 시작됩니다." // 영상 후원의 경우 후원 내용이 영상의 제목입니다.
}-
후원 이벤트는 대부분 실제 후원에 대해서만 동작합니다. 후원을 테스트하고 싶으시다면 수익 창출이 활성화된 채널 기준으로
치지직 스튜디오 → 방송 관리 → 알림 → 후원 알림으로 이동하여 후원 알림 구역 최하단의 "테스트 알림 보내기" 기능을 이용해주시길 바랍니다. (https://studio.chzzk.naver.com/<채널 ID>/notification) -
익명 후원 시
donatorChannelId는"anonymous",donatorNickname은""입니다. -
스트리머의 후원 금액 표시 여부와 관계없이, 공식 API로 받은 Event에서는
payAmount가 표시됩니다.
아래 내용은 실제 수신을 확인하지 못한, 공식 문서 기반의 정보입니다.
구독 기능 자체가 치지직 프로 회원에게만 제공되어 테스트 경로가 막혀 있습니다. 더불어 스튜디오의 구독 알림 테스트 호출은 후원 알림과 달리 세션 소켓으로 전달되지 않습니다. 비공식 API를 직접 호출하면
code: 200,message: "SUCCESS"가 반환되고 스튜디오 화면에도 알림이 표시되지만, 세션 소켓으로는 아무것도 오지 않습니다. (구독 요청 자체는 정상 처리되어SYSTEMsubscribed메시지는 수신됩니다.)위의 공식 문서와 실제 동작의 차이에서 보시듯 공식 문서의 타입 표기가 실제와 다른 전적이 있으므로, 아래 타입 정보 역시 그대로 신뢰하지 마시고 방어적으로 구현하시길 권장드립니다.
| 필드 | 문서상 타입 | 설명 |
|---|---|---|
channelId |
String | 이벤트 채널 ID |
subscriberChannelId |
String | 구독자 채널 ID |
subscriberNickname |
String | 구독자 닉네임 |
tierNo |
Int | 구독 티어 (1 또는 2) |
tierName |
String | 구독 티어 이름 |
month |
Int | 구독 개월 수 |
다른 이벤트에 eventSentAt이 존재하는 것으로 보아 SUBSCRIPTION에도 있을 가능성이 높으나 확인되지 않았습니다.
추후 실제 구독 알림을 수신하게 되면 이 항목을 갱신하겠습니다.
실제 연결로 확인한 불일치 목록입니다.
| 항목 | 공식 문서 | 실제 |
|---|---|---|
CHAT.userRoleCode |
최상위 필드 | profile 안에 있음 |
DONATION.payAmount |
String |
숫자 (1000) |
eventSentAt |
기재 없음 | CHAT, DONATION 양쪽에 존재 |
eventSentAt의 시간대 |
기재 없음 | KST, 오프셋 표기 없음 |
socket.io-client 지원 버전 |
2.0.3 | 2.x 계열 전반에서 정상 연결 |
| 이벤트 인자 형태 | JSON 객체 | JSON 문자열로 이중 인코딩 |
- 2026. 07. 26.
- 클라이언트 구현 시 주의사항 항목 신설 (이중 인코딩, 루트 네임스페이스 CONNECT 문제, 하트비트 방향,
upgrades필드,eventSentAt시간대) CHAT에chatChannelId,profile.userRoleCode,eventSentAt추가DONATION에emojis,eventSentAt추가SUBSCRIPTION항목 신설 (미검증)SYSTEM에revoked타입 추가,eventType에SUBSCRIPTION추가socket.io-client지원 버전 정정, Go 라이브러리 관련 정보 추가- 세션 조회 기간(90일) 관련 공식 문서 변경 반영
- 확인된
ssio번호에08,10추가 - 공식 문서와 실제 동작의 차이 정리 표 추가
- 클라이언트 구현 시 주의사항 항목 신설 (이중 인코딩, 루트 네임스페이스 CONNECT 문제, 하트비트 방향,
Q. 이거 공유해도 되나요?
A. 네 됩니다.
Q. 문서에 잘못된 정보나 오류, 오래된 정보가 포함되어 있어서 수정하고 싶어요.
A. 댓글 달아주세요. 개인 이메일로 GitHub를 통해 알림이 오기 때문에, 여유 시간에 수정해놓겠습니다.

감사합니다. 참말루 어지럽게 해놨더라고요... 클라이언트만으로 타 스트리머의 채팅을 구독하지 못 하는군요. 그럼 계속 세션에 접근하기 위해서는 스트리머가 개발자의 앱에 로그인하고, 매번 cron job 같은 걸로 refresh token 기간인 30일 안에 access token을 재발급 받아야 하는 건가요? access token과 함께 refresh token도 재발급 받을 때 30일 기간이 초기화되는지 아시나요?
흠 이런 방식이라면 만약 어떤 오류로 인해 token 발급에 문제가 생겨 스트리머가 다시 개발자 앱에 로그인해야 하는 (access token을 새로 받아야 하는) 상황이 생겨도 채팅 메시지와 같이 스트리머에게 알려줄 수 있는 방법이 제한되겠군요.