Skip to content

Instantly share code, notes, and snippets.

@fi-xz
Last active April 27, 2025 08:41
Show Gist options
  • Save fi-xz/69ce1f35ca1b2318a2b410c0d5757e0f to your computer and use it in GitHub Desktop.
Save fi-xz/69ce1f35ca1b2318a2b410c0d5757e0f to your computer and use it in GitHub Desktop.
CHZZK Official API - Session Test Results
import io from "socket.io-client";
const socketURL =
"https://ssio07.nchat.naver.com:443?auth=<AUTH_TOKEN>";
const socketOptions = {
reconnection: true,
forceNew: true,
timeout: 3000,
transports: ["websocket"],
} as SocketIOClient.ConnectOpts;
const socket = io.connect(socketURL, socketOptions);
socket.on("connect", () => {
console.log("connected");
});
socket.on("disconnect", () => {
console.log("disconnected");
});
socket.on("SYSTEM", function (data) {
console.log("SYSTEM: ", data);
});
socket.on("CHAT", function (data) {
console.log("CHAT: ", data);
});
socket.on("DONATION", function (data) {
console.log("DONATION: ", data);
});
socket.connect();
package foo.bar.myapp
import io.socket.client.IO
import kotlinx.coroutines.*
private const val SESSIONURL =
"https://ssio10.nchat.naver.com:443?auth=<AUTH_TOKEN>"
val socketOptions: IO.Options = IO.Options().apply {
reconnection = false
forceNew = true
timeout = 3000L
transports = arrayOf("websocket")
}
fun main() {
val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
connect()
}
runBlocking { delay(Long.MAX_VALUE) }
}
fun connect() {
val socket = IO.socket(SESSIONURL, socketOptions)
socket.on("connect") {
println("connected")
}
socket.on("disconnect") {
println("disconnected")
}
socket.on("SYSTEM") {
println(it[0])
}
socket.on("CHAT") {
println(it[0])
}
socket.on("DONATION") {
println(it[0])
}
socket.connect()
}

시작하기 전에

  • Ref - Session | CHZZK
  • 채팅 메시지 조회와 (후원 구독이 필요할 시) 후원 조회 Scope에 대한 권한 심사가 모두 마쳤음을 가정합니다. 그렇지 않을 경우 500 Internal Server Error와 함께 세션 연결 URL부터 발급이 불가능합니다.

세션 연결 작업

  • 많은 인원의 Event를 구독해야하는 경우(예: 마인크래프트 스트리머 후원을 1천명 연동하는 경우 등), 각 스트리머가 개발자의 애플리케이션에 OAuth 로그인을 하게끔 하여 Access Token을 발급 받아 세션을 생성하는 방식을 추천드립니다. 이는 Client 인증으로 세션을 발급 받아 15개의 채널의 채팅, 후원 이벤트를 모두 구독하는 경우 30개 제한에 도달하며, Access Token 방식은 한 세션당 최대 30개의 이벤트를 구독할 수 있는 제한을 벗어나기 위한 방법이기 때문입니다.
  • API로 발급된 Session Socket URL은 유효한 시간이 얼마나 되는지는 정확하게 알 순 없으나(예상으로는 약 30초 ~ 1분?), 만약 연결을 시도했을 때 URL이 올바름에도 불구 연결 시도시 연결이 바로 끊기는 경우, 해당 URL이 만료된것입니다. 새로 발급 받으셔야합니다.
  • 현재 Client 인증만을 이용해서 개발자의 채널 이외 타 스트리머의 채팅이나 후원을 구독하고 싶은 경우, REST API로 구독 요청을 전송 할 때 Authorization 헤더의 Bearer 토큰 값을 스트리머가 사전에 인증 Scheme을 완료하여 발급한 Access Token 값으로 지정하면 가능합니다. 다만, 한 세션당 최대로 구독할 수 있는 이벤트 개수를 고려하여, 개인적으로 세션 자체를 Access Token 방식으로 생성하는 것을 추천드립니다.
    • 내용을 대충 읽으셨다면 알겠지만, 네. 스트리머의 로그인을 포함하지 않고 해당 스트리머 채널의 채팅이나 후원 이벤트를 구독할 수 있는 방법은 아예 없습니다. 내 애플리케이션 Client 정보 단일로 내 채널 혹은 채널 구독은 아예 불가능하다는 의미입니다. 내 채널을 구독할 때에도 REST API에 인증 토큰을 넣어야하니까요.
  • 현재 Client 인증을 통해 생성된 Session(및 Session Key)에 대해 다음 내용은 확인이 어렵거나 불가능한것으로 파악됩니다:
    • 세션 URL을 발급 한 뒤 연결을 진행 했으나 연결이 끊긴 경우나 최초 연결 제한 시간이 지난 이후, 세션 목록을 조회했을때 연결이 끊겼거나 연결 시간이 지나버린 세션 정보가 세션 목록에서 사라지는 시간 (현재 최대 39개의 세션 생성 이후 추가 세션을 생성하려고 할 때 사라지는 조건으로 추측됨)
    • 강제로 세션을 세션 목록에서 제거할 수 있는 방법 (아마 그냥 미제공하는것으로 추측합니다.)

많이 하는 실수

  • 가장 큰 착오가 발생 할 수 있는 부분으로, 공식 문서에서 "최대 n개의 API 연결을 유지 할 수 있음" 내용은 동시에 연결을 유지시킬 수 있는 개수를 뜻하며 개발자가 최대로 생성 할 수 있는 세션의 개수가 아닙니다. 세션은 언제든지 여러개 생성할 수 있는것으로 보입니다. 다만 최대 세션 조회 가능 개수인 39개 이상을 생성하면 어떻게 되는지에 대해서는 확인해보질 못했습니다.
  • 두번째로 가장 많은 착오로, 이벤트를 구독/구독 취소 할 때 사용하는 sessionKey값은 POST 요청이어도 Query Parameter(?sessionKey=XXXXXX)로 설정해야하며 요청 Body에 JSON 형태로 작성하는 방식은 지원되지 않습니다. (여담..)
  • WebSocket 연결이 되어 있지 않는 상태에서 REST API를 통해 Session Key 정보만을 이용하여 Event를 구독할 수는 없습니다. 400 Bad Request와 연결 확인 오류 메시지를 안내합니다.

Client 라이브러리 정보

  • TypeScript 기준 socket.io-client 버전 2.0.3, @types/socket.io-client 버전 1.4.36이 가능한 최대 버전으로 보입니다. 이외의 버전을 사용하면 서버단에서 강제로 연결해주지 않습니다.
  • Java/Kotlin 기준 socket.io-client-java 버전 1.0.2가 가능한 최대 버전으로 보입니다. 이외의 버전을 사용하면 서버단에서 강제로 연결해주지 않습니다.

기타 정보

  • Session Socket URL(발급 받은 세션 연결 URL을 의미)은 https://ssio<번호>.nchat.naver.com:443?auth=<AUTH_TOKEN>로 반환되며 주로 확인된 번호들로 07, 09, 27이 존재합니다. 발급된 URL 내의 숫자 이외 다른 숫자로 연결이 정상적으로 진행되는지는 시도해보지 않았습니다.

  • 테스트를 진행하기 위해 사용한 코드 역시 이 Gist에 첨부합니다.

SYSTEM

아래의 내용은 세션 연결 이후 SYSTEM 이벤트를 구독하였을 때 수신받을 수 있는 메시지 종류입니다.

모든 메시지는 JSON 메시지로 이루어져있으며, 가독성을 위해 Indent를 주었습니다. 실제로는 한 줄로 표시됩니다.

부가적인 설명을 위하여 문서의 JSON은 모두 JSONC로 작성되었습니다! 실제 JSON에서는 주석이 불가능한 점 유의하시길 바랍니다.

  • 채팅 연결 진행 시
{
    "type": "connected",
    "data": {
        "sessionKey": "<SOCKET_SESSION_KEY>"
        // 현재 연결된 세션의 Session Key 값을 획득하실 수 있습니다.
        // 위 정보를 이용하여 Event를 구독하거나 세션을 구분하는 용도로 사용할 수 있습니다.
    }
}
  • POST 요청으로 채팅 Event를 구독할 시 (/open/v1/sessions/events/subscribe/chat?sessionKey=<SOCKET_SESSION_KEY>)
{
    "type": "subscribed",
    "data": {
        "eventType": "CHAT", // 구독 Event 타입, 수신 가능한 값은 CHAT, DONATION입니다.
        "channelId": "<SUBSCRIBED_CHANNEL_ID>" // 구독한 채널 ID
    }
}
  • POST 요청으로 후원 Event를 구독할 시 (/open/v1/sessions/events/subscribe/donation?sessionKey=<SOCKET_SESSION_KEY>)
{
    "type": "subscribed",
    "data": {
        "eventType": "DONATION", // 구독 Event 타입, 수신 가능한 값은 CHAT, DONATION입니다.
        "channelId": "<SUBSCRIBED_CHANNEL_ID>" // 구독한 채널 ID
    }
}
{
    "type": "unsubscribed",
    "data": {
        "eventType": "CHAT", // 구독 해지 Event 타입, 수신 가능한 값은 CHAT, DONATION입니다.
        "channelId": "<UNSUBSCRIBED_CHANNEL_ID>" // 구독 해지한 채널 ID
    }
}
{
    "type": "unsubscribed",
    "data": {
        "eventType": "DONATION", // 구독 해지 Event 타입, 수신 가능한 값은 CHAT, DONATION입니다.
        "channelId": "<UNSUBSCRIBED_CHANNEL_ID>" // 구독 해지한 채널 ID
    }
}

CHAT

아래의 내용은 세션 연결 이후 CHAT 이벤트를 구독하였을 때 수신받을 수 있는 메시지 종류입니다.

모든 메시지는 JSON 메시지로 이루어져있으며, 가독성을 위해 Indent를 주었습니다. 실제로는 한 줄로 표시됩니다.

부가적인 설명을 위하여 문서의 JSON은 모두 JSONC로 작성되었습니다! 실제 JSON에서는 주석이 불가능한 점 유의하시길 바랍니다.

  • 일반 채팅
{
    "channelId": "<STREAMING_CHANNEL_ID>", // 방송인의 채널 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을 기입하기는 어려운 점 양해 부탁드립니다.
        ]
    },
    "content": "do test {:d_47:}", // 채팅 내용, 치지직 채팅 이모지는 중괄호 내의 콜론 표시와 그 안에 이모지 이름을 담는 형태로 구성되어있습니다.
    "emojis": { // 이모지 이름 및 URL
        "d_47": "https://ssl.pstatic.net/static/nng/glive/icon/b_07.gif?type=f60_60"
    },
    "messageTime": 1739110481425 // 메시지 전송 시각
}

DONATION

아래의 내용은 세션 연결 이후 DONATION 이벤트를 구독하였을 때 수신받을 수 있는 메시지 종류입니다.

모든 메시지는 JSON 메시지로 이루어져있으며, 가독성을 위해 Indent를 주었습니다. 실제로는 한 줄로 표시됩니다.

부가적인 설명을 위하여 문서의 JSON은 모두 JSONC로 작성되었습니다! 실제 JSON에서는 주석이 불가능한 점 유의하시길 바랍니다.

  • 일반 후원
{
    "donationType": "CHAT", // 후원 타입, 가능한 타입은 현재 CHAT, VIDEO 두 종류 입니다.
    "channelId": "<DONATION_RECEIVED_CHANNEL_ID>", // 후원을 수신한 채널 ID (스트리머)
    "donatorChannelId": "<DONATOR_CHANNEL_ID>", // 후원을 보낸 채널 ID (시청자)
    "donatorNickname": "<DONATOR_NICKNAME>", // 후원을 보낸 채널의 닉네임 (시청자 닉네임)
    "payAmount": 1000, // 후원 금액
    "donationText": "TEST" // 후원 내용
}
  • 영상 후원
    • 영상 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가 표시됩니다.

FAQ

Q. 이거 공유 해도 되나요?

A. 네 됩니다.

@fi-xz
Copy link
Author

fi-xz commented Apr 27, 2025

Gist에서는 Issue/PR과는 다르게 실시간으로 댓글 추가/수정에 대해서 반영이 되지 않거나 느린 모양입니다. 빠뜨리는 부분이 존재해도 양해 부탁드리겠습니다 🙏

@HyunWinter
Copy link

@fi-xz 친절하게 알려주셔서 다시 감사드립니다. 일단 냅다 만들고 테스트하면서 바꿔야 하나 싶었는데, 덕분에 자세한 방향성이 잡혔어요. 좋은 하루되세요! (_ _

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment