WebSocket 클라이언트

서버와의 실시간 양방향 통신을 위한 WebSocket 클라이언트.

로딩

local websocket = require("websocket")

연결

기본 연결

local client, err = websocket.connect("wss://api.example.com/ws")
if err then
    return nil, err
end

옵션과 함께

local client, err = websocket.connect("wss://api.example.com/ws", {
    headers = {
        ["Authorization"] = "Bearer " .. token
    },
    protocols = {"graphql-ws"},
    dial_timeout = "10s",
    read_timeout = "30s",
    compression = websocket.COMPRESSION.CONTEXT_TAKEOVER
})
파라미터 타입 설명
url string WebSocket URL (ws:// 또는 wss://)
options table 연결 옵션 (선택적)

반환: Client, error

연결 옵션

옵션 타입 설명
headers table 핸드셰이크용 HTTP 헤더
protocols table WebSocket 서브프로토콜
dial_timeout number/string 연결 타임아웃 (ms 또는 "5s")
read_timeout number/string 읽기 타임아웃
write_timeout number/string 쓰기 타임아웃
compression number 압축 모드 (상수 참조)
compression_threshold number 압축 최소 크기 (0-100MB)
read_limit number 최대 메시지 크기 (0-128MB)
channel_capacity number 수신 채널 버퍼 (1-10000)

타임아웃 형식: 숫자는 밀리초, 문자열은 Go duration 형식 ("5s", "1m")을 사용합니다.

메시지 보내기

텍스트 메시지

local ok, err = client:send("Hello, Server!")
if err then
    return nil, err
end

-- JSON 전송
client:send(json.encode({
    type = "subscribe",
    channel = "orders"
}))

바이너리 메시지

client:send(binary_data, websocket.BINARY)
파라미터 타입 설명
data string 메시지 내용
type number websocket.TEXT (1) 또는 websocket.BINARY (2)

반환: boolean, error

Ping

client:ping()

반환: boolean, error

메시지 받기

channel() 메서드는 메시지 수신을 위한 채널을 반환합니다. 멀티플렉싱을 위해 channel.select와 함께 작동합니다.

기본 수신

local ch = client:channel()

local msg, ok = ch:receive()
if ok then
    print("Type:", msg.type)  -- "text" 또는 "binary"
    print("Data:", msg.data)
end

메시지 루프

local ch = client:channel()

while true do
    local msg, ok = ch:receive()
    if not ok then
        break  -- 연결 닫힘
    end

    if msg.type == "text" then
        local data = json.decode(msg.data)
        handle_message(data)
    end
end

Select와 함께

local ch = client:channel()
local timeout = time.after("30s")

while true do
    local r = channel.select {
        ch:case_receive(),
        timeout:case_receive()
    }

    if r.channel == timeout then
        client:ping()  -- Keep-alive
        timeout = time.after("30s")
    else
        local data = json.decode(r.value.data)
        process(data)
    end
end

메시지 객체

필드 타입 설명
type string "text" 또는 "binary"
data string 메시지 내용

연결 닫기

-- 정상 닫기 (코드 1000)
client:close()

-- 코드와 이유와 함께
client:close(websocket.CLOSE_CODES.NORMAL, "Session ended")

-- 에러 닫기
client:close(websocket.CLOSE_CODES.INTERNAL_ERROR, "Processing failed")
파라미터 타입 설명
code number 닫기 코드 (1000-4999), 기본값 1000
reason string 닫기 이유 (선택적)

반환: boolean, error

상수

메시지 타입

-- 숫자 (send용)
websocket.TEXT    -- 1
websocket.BINARY  -- 2

-- 문자열 (수신된 메시지 type 필드)
websocket.TYPE_TEXT    -- "text"
websocket.TYPE_BINARY  -- "binary"
websocket.TYPE_PING    -- "ping"
websocket.TYPE_PONG    -- "pong"
websocket.TYPE_CLOSE   -- "close"

압축 모드

websocket.COMPRESSION.DISABLED         -- 0 (압축 없음)
websocket.COMPRESSION.CONTEXT_TAKEOVER -- 1 (슬라이딩 윈도우)
websocket.COMPRESSION.NO_CONTEXT       -- 2 (메시지별)

닫기 코드

상수 코드 설명
NORMAL 1000 정상 종료
GOING_AWAY 1001 서버 종료 중
PROTOCOL_ERROR 1002 프로토콜 에러
UNSUPPORTED_DATA 1003 지원되지 않는 데이터 타입
NO_STATUS 1005 상태 수신되지 않음
ABNORMAL_CLOSURE 1006 연결 끊김
INVALID_PAYLOAD 1007 잘못된 프레임 페이로드
POLICY_VIOLATION 1008 정책 위반
MESSAGE_TOO_BIG 1009 메시지 너무 큼
INTERNAL_ERROR 1011 서버 에러
SERVICE_RESTART 1012 서버 재시작 중
TRY_AGAIN_LATER 1013 서버 과부하
client:close(websocket.CLOSE_CODES.NORMAL, "Done")

예제

실시간 채팅

local function connect_chat(room_id, on_message)
    local client, err = websocket.connect("wss://chat.example.com/ws", {
        headers = {["Authorization"] = "Bearer " .. token}
    })
    if err then
        return nil, err
    end

    -- 방 참여
    client:send(json.encode({
        type = "join",
        room = room_id
    }))

    -- 메시지 루프
    local ch = client:channel()
    while true do
        local msg, ok = ch:receive()
        if not ok then break end

        local data = json.decode(msg.data)
        on_message(data)
    end

    client:close()
end

Keep-Alive와 함께 가격 스트림

local client = websocket.connect("wss://stream.example.com/prices")

client:send(json.encode({
    action = "subscribe",
    symbols = {"BTC-USD", "ETH-USD"}
}))

local ch = client:channel()
local heartbeat = time.after("30s")

while true do
    local r = channel.select {
        ch:case_receive(),
        heartbeat:case_receive()
    }

    if r.channel == heartbeat then
        client:ping()
        heartbeat = time.after("30s")
    elseif not r.ok then
        break  -- 연결 닫힘
    else
        local price = json.decode(r.value.data)
        update_price(price.symbol, price.value)
    end
end

client:close()

권한

WebSocket 연결은 보안 정책 평가 대상입니다.

보안 액션

액션 리소스 설명
websocket.connect - WebSocket 연결 허용/거부
websocket.connect.url URL 특정 URL 연결 허용/거부

정책 설정은 보안 모델을 참조하세요.

에러

조건 종류 재시도 가능
연결 비활성화됨 errors.PERMISSION_DENIED 아니오
URL 허용되지 않음 errors.PERMISSION_DENIED 아니오
컨텍스트 없음 errors.INTERNAL 아니오
연결 실패 errors.INTERNAL
잘못된 연결 ID errors.INTERNAL 아니오
local client, err = websocket.connect(url)
if err then
    if errors.is(err, errors.PERMISSION_DENIED) then
        print("Access denied:", err:message())
    elseif err:retryable() then
        print("Temporary error:", err:message())
    end
    return nil, err
end

에러 처리는 에러 처리를 참조하세요.