Life/컨퍼런스

Kotlin Backend Meetup 후기

샤아이인 2025. 2. 16. 21:51

 

오래간만에 새로운 내용들을 접하려 컨퍼런스에 참여하게 되었다.

JVM을 주로 사용해온 유저로써 Kotlin + Backend 두 단어로 이미 마음을 따뜻하게 해주는 주제인 것 같다~

 

추가로, 코틀린 유저 그룹의 오거나이저를 담당하고 있는 현우가 밤마다 설레발을 카톡으로 전해주었기에 ㅋㅋㅋ

나 또한 기대를 많이하고 있었다!

 

추가로, 아라운 께서 연사자로 나오시는 것을 보고 티켓 오픈 당일날 바로 신청하였다!~~

 

1. 당일날

 

살짝 당황스러웠던 점이 있다면.... 당일날 발표 순서가 변경된 점이 조금 아쉬웠다 ㅠ,ㅠ...

Ktor 헨즈온 세션에 참여해보고 싶었는데... 아라운의 세션과 겹쳐 아라운의 RPC 쪽 세션을 선택하였다.

(Ktor, 뭐 혼자 써보면 되지 뭐~~)

 

아참 참여자들에게 귀여운(?) Kotlin 캐릭터 배지를 하나씩 나누어 주셨다.

 

여담으로, 당근의 지인인 하이디를 컨퍼런스에서 만났다 ㅋㅋㅋ, 나랑 하이디 둘 다 혼자 왔기 때문에 2개의 세션을 같이 듣고 네트워킹을 도 같이하게 되었다!!

오래간만에 하이디의 긍정긍정 에너지를 Dependency Injection 받고 나니 힐링되는 느낌이었다 ㅋㅋㅋㅋ

 

2. 세션 시작~~

2-1) Kotlinx.rpc - 박용권

 

일단 이번 세션을 통해 kotlinx-rpc 에 대하여 나는 처음 알게 되었다.

 

GitHub - Kotlin/kotlinx-rpc: Add asynchronous RPC services to your multiplatform applications.

Add asynchronous RPC services to your multiplatform applications. - Kotlin/kotlinx-rpc

github.com

 

우선 다음 kotlin.rpc와 gRPC을 비교하는 장표부터 처음에 보여주셔서 좋았다.

출처 - https://speakerdeck.com/arawn

 

사실 들으면서 든 생각들을 바로 적어보자면...? 음 크게 사용성에 다른 것 같지는 않은데???
똑같이 stub 만들고 요청 보내고 받는 건 동일한 것 같은데??

 

추가로 든 생각으로는~

1. kotlinx.rpc를 사용한다면 어떤 장점이 있지?

2. 기존의 gRPC가 사용하기 많이 불편했을까?

3. gRPC의 사용이유로 나는 빠른 전송 속도부터 생각했는데?

 

우선 kotlin.rpc는 새롭게 정의한 통신 규약(protocol)인 kRPC을 사용한다고 한다.

https://github.com/Kotlin/kotlinx-rpc

 

독자적인 프로토콜을 만들면서까지 뭐를 이루고 싶었던 것일까?

출처 - https://speakerdeck.com/arawn

 

그렇다! 요약하면 그냥 Kotlin하나로 모든 것을 다 하겠다는(?) Kotlin Multiplatform(KMP)과 같은 환경에서 큰 이점이 생기는 것이다!

 

Kotlin Multiplatform환경에서 동일한 코드로 모든 것을 하겠다는? 일종의 추상화의 끝판으로 가겠다는 것 아닐까?

"코드로 선언만 해~ 네트워크 밑단의 구현은 우리가 다 알아서 한다고~"에 가까운 방법이라 생각된다

 

또한 어떤 기종의 App이든 동일한 방식의 코드로 사용가능하다는 장점이 생길 것이다.

모두 동일한 kRPC 방법을 사용했으니 말이다~

 

아라운의 PPT와 다음 문서를 확인해 보면서 사용법을 참고하였다.

https://github.com/Kotlin/kotlinx-rpc?tab=readme-ov-file#quick-start

 

1. 일단 다음과 같이 Rpc 선언

import kotlinx.rpc.RemoteService
import kotlinx.rpc.annotations.Rpc

@Rpc
interface AwesomeService : RemoteService {
    suspend fun getNews(city: String): Flow<String>
    
    suspend fun daysUntilStableRelease(): Int
}

 

2. 이후 다음과 같이 구현체를 만들어주고~

class AwesomeServiceImpl(
    val parameters: AwesomeParameters,
    override val coroutineContext: CoroutineContext,
) : AwesomeService {
    override suspend fun getNews(city: String): Flow<String> {
        return flow { 
            emit("Today is 23 degrees!")
            emit("Harry Potter is in $city!")
            emit("New dogs cafe has opened doors to all fluffy customers!")
        }
    }
    
    override suspend fun daysUntilStableRelease(): Int {
        return if (parameters.stable) 0 else {
            parameters.daysUntilStable ?: error("Who says it will be stable?")
        }
    }
}

 

3. 서버는 다음과 같이 registerService라는 방식으로 등록하는 것 같다.

data class AwesomeParameters(val stable: Boolean, val daysUntilStable: Int?)

fun main() {
    embeddedServer(Netty, 8080) {
        install(Krpc)
        routing {
            rpc("/awesome") {
                rpcConfig { serialization { json() }}

                registerService<AwesomeService> { ctx -> 
                    AwesomeServiceImpl(AwesomeParameters(false, null), ctx) 
                }
            }
        }
    }.start(wait = true)
}

 

4. 클라이언트에서 사용할 때는 withService 메서드를 호출하면서 해당 서비스 타입을 넘겨 사용하는 것 같다.

val rpcClient = HttpClient { installKrpc() }.rpc {
    url("ws://localhost:8080/awesome")

    rpcConfig { serialization { json() } }
}

val service = rpcClient.withService<AwesomeService>()

service.daysUntilStableRelease()

streamScoped {
    service.getNews("KotlinBurg").collect { article -> println(article)}
}

 

또한 단순 원시타입들을 주고받는 것 이 아닌, 객체타입을 주고받기 위해서 별도의 kotlinx의 Serializable 에너테이션을 선언하여 사용한다 하셨다.

출처 - https://speakerdeck.com/arawn

 

또한 gRPC 도 사용할 수 있다고 깃허브에는 명시되어 있다. 위에서 말했듯 하부의 구현사항과 상위 level의 추상화를 분리하였기에 이렇게 다른 프로토콜 방식의 gRPC도 같이 사용할 수 있는 것이다.

 

아라운 께서도 이 부분을 강조하여 알려주셨다.

구현체야 뭔들~~

네트워크 구간에서의 방식을 감추고, 상위 추상 Level에서 바라보는것이 핵심!

 

추가로 아라운에게 다음과 같은 질문을 했는데,

 

: "gRPC는 속도 때문에 사용한다 생각해요, 네트워크 구간은 동일한 HTTP2라 하면 속도 차이가 없을 것 같구, 직렬, 역직렬 과정에서 기존의 gRPC가 proto기반이라 더 빠르다 생각한건데, 이 방식은 그런 장점이 없어지지 않나요?"

 

아라운 : 발표에서 잠시 언급했듯, 해당 기술의 에니지어 들도 어떤 점에서는 성능이 더 안좋을 수도, 더 좋은 부분도 있음을 언급하셨다 하였다. 다만 그런한 성능적 이점보다, 위에서 말한 KMP환경 에서의 추상화의 장점이 커서 특별한 상황에 사용하면 더 좋을 수 있다.

 

라고 이야기 해주셨다!

 

끝나구~ 랑, 하이디랑, 아라온 한컷~

너무 정면으로 나와 부담스러울 수 있으니? 초상권 보호 해드릴게요!

 

2-2) 코루틴 컴파일 과정 파헤치기 - 김찬우

 

발표전에 연사자분이 첫 발표라고, 다 같이 사진 찍자 하신 점이 재미있었다 ㅋㅋㅋㅋ "오픈소스~"라고 외치며 찍자고 하셨다 ㅋㅋㅋ

 

각각의 상태 머신

출처 - https://github.com/Kotlin-User-Groups-Seoul/kotlin-backend-meetup-2025/tree/main/%EA%B9%80%EC%B0%AC%EC%9A%B0

 

내부코드를 생각해 보면서 설명해 주셨는데, return을 보면 CORUTINE_SUSPENDED와 Unit 중 하나를 반환하게 된다.

이는 최상위 타입인 Any?를 반환하여 처리한다는 점을 알게 되었다.

 

최상위 타입을 활용한 반환의 개념은 아마 List관련하여 in, out 키워드를 공부해 본 사람이라면 한 번쯤 스쳐 들어본 적 있는 내용일 것 같다.

 

또한 Continuation Passing Style에 대하여 처음 알게 되었다. 이를 통해 일반 함수에서 suspend를 호출하지 못하는 이유에 대하여 알게 되었다.

 

그냥 간단하게 Continuation이라는 단어를 Callback으로 바꿔서 생각하면 편하다.

즉 Callback Passing Style으로 생각하면, Callback 함수를 전달하는 방식의 스케줄링 기법을 의미한다는 것이다.

 

일단 내가 이해한 것을 정리해 보면 다음과 같다.

 

▶ 일반 함수 호출 방식과 suspend 함수 호출 방식의 차이

일반적인 함수 호출 방식은 함수가 호출되면 스택 프레임이 생성되고, 함수가 끝나면 스택에서 제거되는 방식

반면, Suspend 함수는 Continuation Passing Style (CPS)를 따른다는 것이다.

 

이는 호출된 함수가 바로 반환되지 않고, 중단된 지점 이후의 실행을 나중에 다시 이어갈 수 있도록 Continuation (콜백)을 전달하는 방식입니다.

 

▶ Continuation Passing Style (CPS) 설명

일반 함수 (Direct Style)는 다음과 같이 호출하면, 스택 기반으로 실행됩니다.

fun normalFunction() {
    println("Start")
    println("End")
}

fun main() {
    normalFunction()
}

이 함수는 호출되면, 현재 실행 컨텍스트 (스택 프레임)에서 바로 실행됩니다. 중단할 수 없고, 호출이 끝나면 즉시 반환되는 것 이죠~

 

suspend 함수 (CPS 기반)

반면, suspend 함수는 함수 실행을 중단하고 나중에 다시 이어갈 수 있어야 합니다.

즉, 일반적인 호출 방식이 아니라, Continuation을 통해 다음 실행을 제어해야 합니다.

suspend fun suspendFunction() {
    println("Before suspension")
    delay(1000) // 실행을 중단하고, 나중에 continuation을 호출해야 함
    println("After suspension")
}

 

 

코루틴의 suspend 함수는 다음과 같이 함수 호출 invoke() 시 인자로 Continuation 객체를 인자로 받는다고 설명해 주셨다.

출처 - https://github.com/Kotlin-User-Groups-Seoul/kotlin-backend-meetup-2025/tree/main/%EA%B9%80%EC%B0%AC%EC%9A%B0

 

이제 핵심을 살펴보죠.
일반 함수는 Continuation을 인자로 받지 않습니다. 그러므로 일반 함수 내에서는 suspend 함수를 호출할 방법이 없습니다.

왜냐하면 suspend Function() 은 내부적으로 Continuation을 필요로 하는데, 일반 함수는 이를 제공할 수 없기 때문입니다.

 

2-3) 잠시 네트워킹

컨퍼런스를 주최해 주신 오거나이저 3분과 다른 회사를 다니시는(하이디 지인)분들과 잠시 네트워킹을 할 수 있었다.

어떤 한분은 벌써 8년 차라는 말을 들었을 때 대단하단 생각을 하게 되었다 ㅋㅋㅋㅋ 나는 그 나이 때 뭐 했더라?.....

모두의 초상권은 소중하니까...

2-4) Spring Webflux Overview

 

요청의 시작부터 하여 response 반환이 나가기까지 아르메니아 내부의 과정을 설명해 주셨다.

Mono기준으로 설명하셨으며, Flux로 할 경우 사실 Mono 방식의 반복임을 알게 되었다.

 

그 외에는.... 죄 죄송합니다... 이 이건 제가 초반에 정신줄이 못 잡은 것 같아요... 윽윽.........

나중에 따로 유튜브에 영상을 올려주신다 하셨다!!

 

3. 끝으로~

오래간만에 사람들 많은 콘퍼런스 와서 사람들도 만나고, 열정 흡수할 수 있었다~

근심 가득한 요즘 조금은 근심들을 내려두고 참여할 수 있어서 좋았다. 발표자 분들이 발표할 때의 그 열정이 느껴져서 좋았던 거 같다.

 

더 나아가 나의 걱정에 관한 이야기를 들어주시는 분이 있다는 사실 자체도 너무 좋았다.

 

하이디가 중간에 가셔서 모르시겠지만.... 마지막 선물 추첨할 때 렌덤으로 돌렸는데 나란하게 61등과 62등 한 레전드~~ ㅋㅋㅋㅋㅋㅋㅋㅋ

전체인원 150명 중 나란하게 61, 62등 할 확률은? 약 0.00009%

 

?? 그 그냥.... 그렇다고요 ㅋㅋㅋ

 

이후 아라운 덕분에 발표 연사자들과 저녁을 함께 먹으면서 이야기를 나누어 볼 수 있어 좋았다!!
하루 잘 마무리하고 좋은 영향도 받으면서 저녁까지 알차게 보낸 것 같다~ 내일부터 한주도 또 알차게 살아가야겠다!