[kotlin] 코틀린 웹 서버에서 웹 소켓과 스트리밍 구현 방법

이번 포스트에서는 코틀린 웹 서버에서 웹 소켓과 스트리밍을 구현하는 방법에 대해 알아보겠습니다.

1. 웹 소켓 구현하기

1.1. Gradle 설정

먼저, 프로젝트의 build.gradle 파일에 아래의 의존성을 추가해야 합니다.

dependencies {
    implementation 'io.ktor:ktor-websockets:<version>'
}

<version>을 사용하고자 하는 Ktor 버전으로 바꿔주시면 됩니다.

1.2. 웹 소켓 핸들러 작성

다음으로, 웹 소켓 핸들러를 작성해야 합니다. 새로운 WebSocketHandler 클래스를 생성하고, 아래의 코드를 추가해주세요.

import io.ktor.routing.*
import io.ktor.websocket.*
import io.ktor.http.cio.websocket.*
import kotlinx.coroutines.channels.*

fun Application.module() {
    install(WebSockets)

    routing {
        webSocket("/websocket") {
            val incoming = produce<Message> {
                try {
                    for (frame in incoming) {
                        when (frame) {
                            is Frame.Text -> {
                                val message = frame.readText()
                                send(Message("$connection incoming: $message"))
                            }
                        }
                    }
                } finally {
                    send(Message("$connection closed"))
                }
            }
            outgoing.send(Frame.Text("Connected: $connection"))

            for (message in incoming) {
                outgoing.send(Frame.Text("Server: $message"))
            }
        }
    }
}

위의 코드는 /websocket 경로로 접근한 클라이언트와의 웹 소켓 통신을 처리합니다. 클라이언트로부터 메시지를 받아서 서버의 콘솔에 출력하고, 서버에서 전달한 메시지를 클라이언트에게 다시 보내는 동작을 합니다.

1.3. 서버 실행

이제, 웹 서버를 실행하고 /websocket에 접속해서 메시지를 주고받아볼 수 있습니다.

2. 스트리밍 구현하기

2.1. Gradle 설정

스트리밍을 구현하기 위해서는 추가적인 의존성이 필요합니다. build.gradle 파일에 아래의 의존성을 추가해주세요.

dependencies {
    implementation 'io.ktor:ktor-server-netty:<version>'
    implementation 'io.ktor:ktor-serialization:<version>'
    implementation 'io.ktor:ktor-client-cio:<version>'
}

2.2. 스트리밍 핸들러 작성

다음으로, 스트리밍 핸들러를 작성해야 합니다. 새로운 StreamingHandler 클래스를 생성하고, 아래의 코드를 추가해주세요.

import io.ktor.http.cio.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.*
import java.util.*
import kotlin.collections.HashMap
import kotlin.coroutines.CoroutineContext

class StreamingHandler(
    private val context: CoroutineContext
) : WebSocketHandler {

    private val connections = HashMap<WebSocketSession, List<OutputStream>>()

    override suspend fun onConnect(session: WebSocketSession) {
        super.onConnect(session)
        connections[session] = ArrayList()
    }

    override suspend fun onFrame(session: WebSocketSession, frame: Frame) {
        when (frame) {
            is Frame.Text -> {
                val message = frame.readText()
                sessions().send(Frame.Text(message))
            }
            is Frame.Binary -> {
                val outputStreams = connections[session]
                outputStreams?.forEach { outputStream ->
                    withContext(Dispatchers.IO) {
                        outputStream.write(frame.data.array())
                        outputStream.flush()
                    }
                }
            }
        }
    }

    override suspend fun onClose(session: WebSocketSession, code: CloseReason.Codes, reason: String?) {
        super.onClose(session, code, reason)
        connections.remove(session)
    }
}

위의 코드는 클라이언트로부터 받은 바이너리 데이터를 클라이언트에게 전달하는 스트리밍 핸들러입니다. onFrame 함수에서 클라이언트로부터 받은 데이터를 연결된 모든 OutputStream에 전달하고 있습니다.

2.3. 서버 실행

스트리밍을 구현한 웹 서버를 실행하고 클라이언트와 연결하여 데이터를 주고받습니다.

마치며

이번에는 코틀린 웹 서버에서 웹 소켓과 스트리밍을 구현하는 방법에 대해 알아보았습니다. 웹 소켓을 사용하면 실시간으로 데이터를 주고받을 수 있고, 스트리밍을 구현하면 바이너리 데이터를 실시간으로 전송할 수 있습니다. 이러한 기능들을 이용하여 다양한 웹 애플리케이션을 구현해보세요.

참고 자료: