Coroutine Basic

박성수·2025년 7월 22일

알고쓰자

목록 보기
6/8

Coroutine 공식 가이드를 읽다가 간단히 정리해본다.

Coroutine은 크게 아래 두가지 개념을 이해해야 한다.

  • coroutine context: 코루틴 실행 환경을 정의하는 값들의 집합
    • job: 생명주기를 제어
    • dispatcher: 어떤 스레드에서 동작할지 결정
    • handler
  • coroutine scope: 코루틴을 실행 할 수 있는 범위
    • Global scope: 전역 스코프
    • Coroutine Scope: 개발자가 직접 만드는 스코프 (추천)

coroutine scope는 coroutine context를 포함하고 있다.

coroutine은 왜 필요한가?

비동기 로직을 보다 효율적으로 처리하기 위함

  • 가독성(콜백지옥X)
  • 스레드보다 가벼움
  • non-blocking I/O
  • 구조화된 동시성으로 생명주기를 보다 안전하게 관리

flux나 mono 로도 충분히 비동기 로직을 구현할 수 있지 않나?

오히려 백프레셔 처리는 flux가 명시적으로 지원하므로 고부하 시스템에서는 더 제어하기 좋다

그럼에도 coroutine을 사용하는 이유

  • 가독성이 좋다.
    • suspend, flow로 동기코드 처럼 읽힘
    • functional, chain 스타일이 아니다
  • 가볍다
    • 경량 스레드
    • Flux는 체인이 복잡해질수록 Publisher의 체인 관리 비용이 증가

개인적으로는 동기코드처럼 읽히는 것이 가장 큰 이유라고 생각한다.
또한 spring과 조합하면 coroutine context도 프레임워크에서 관리해주니까 명시적으로 선언할 필요도 없이 suspend만으로 context를 전파하고 혹시 별도의 dispatcher가 필요하다면 contextWith로 필요한 부분만 지정 가능하므로 번거로움이 없다.

그럼 kotlin 기반에서는 가볍고 가독성 좋은 coroutine을 두고 flux나 mono를 쓸 필요가 있나?

당연히 Yes.
위에서 잠깐 언급한거처럼 고부하 시스템에서는 명시적으로 백프레셔를 지원하는 flux를 쓰는게 더 효율적이라고 생각한다.
예를 들어 api와 같은 비즈니스 처리 로직에서는 coroutine으로 비동기 로직을 구현해서 가독성을 높이고, kafka consumer와 같은 대용량 데이터들을 처리해야하는 로직에서는 flux를 사용하여 백프레셔 등의 기능들을 활용하는 등 적재적소에 필요한 기술들을 쓰는 것이 우리 개발자들의 역량인 것 같다.

coroutine도 부분적으로 백프레셔를 지원하지만 정밀 제어가 안되는 것이다.

  • 부분 지원이라함은 속도를 제어하는 것으로 producer가 consumer의 속도에 따라 멈춘다.
  • 지원되지 않는 것들은 아래와 같은 백프레셔 전략이나 정밀 제어들이다.
    • onBackpressureBuffer(): consumer가 느릴때 버퍼
    • onBackpressureDrop(): consumer가 느릴때 초과분은 버리는
    • 요청량 지정
    • retry 타이밍

결론

api 입장에서 전체 flow를 비동기로 구성하는 것은 당연한 스펙이 되었다.
시스템적으로나 로직적으로 비동기 구성을 좀 더 효율적으로 구현할 수 있는 방안들을 계속해서 고민하고 적절한 아키텍처를 위한 공부를 계속 해나가야겠다.
또한 프로젝트가 커지다보면 나도 모르게 blocking 코드로 작성된게 있을수도 있다. 따라서 blockhound와 같은 blocking 코드를 감지해주는 툴을 활용하여 성능을 저해하는 요소도 제거해주면 완성도 있는 프로젝트가 될 것 이다.

profile
Java 백엔드 개발자입니다. 제가 생각하는 개발자로서 가져야하는 업무적인 기본 소양과 현업에서 가지는 고민들을 같이 공유하고 소통하려고 합니다.

0개의 댓글