{"componentChunkName":"component---src-containers-post-index-tsx","path":"/backend/kotlin-in-action/15장-구조화된_동시성/","result":{"pageContext":{"next":{"id":"86fd1f6e-8d93-5de7-b6c9-272ee6f10e07","html":"<h2 id=\"-141-동시성과-병렬성\" style=\"position:relative;\"><a href=\"#-141-%EB%8F%99%EC%8B%9C%EC%84%B1%EA%B3%BC-%EB%B3%91%EB%A0%AC%EC%84%B1\" aria-label=\" 141 동시성과 병렬성 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 14.1 동시성과 병렬성</h2>\n<ul>\n<li>동시성\n<ul>\n<li>여러 작업을 동시에 실행하는 것</li>\n<li>CPU 코어 하나에서 동시성 사용 가능</li>\n</ul>\n</li>\n<li>병렬성\n<ul>\n<li>코드를 여러 부분으로 나눠서 동시에 수행할 수 있는 능력</li>\n<li>여러 CPU 코어에서 물리적으로 동시에 실행하는 것</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"-142-코틀린의-동시성-처리-방법-일시-중단-함수와-코루틴\" style=\"position:relative;\"><a href=\"#-142-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%9D%98-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95-%EC%9D%BC%EC%8B%9C-%EC%A4%91%EB%8B%A8-%ED%95%A8%EC%88%98%EC%99%80-%EC%BD%94%EB%A3%A8%ED%8B%B4\" aria-label=\" 142 코틀린의 동시성 처리 방법 일시 중단 함수와 코루틴 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 14.2 코틀린의 동시성 처리 방법: 일시 중단 함수와 코루틴</h2>\n<ul>\n<li>코루틴은 비동기적으로 실행되는 넌블로킹 동시성 코드를 우아하게 작성할 수 있게 해준다.</li>\n<li>스레드에 비해 매우 가볍다.</li>\n<li>동시성 작업과 생명주기 관리 기능 제공</li>\n</ul>\n<h2 id=\"-143-스레드와-코루틴-비교\" style=\"position:relative;\"><a href=\"#-143-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EB%B9%84%EA%B5%90\" aria-label=\" 143 스레드와 코루틴 비교 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 14.3 스레드와 코루틴 비교</h2>\n<ul>\n<li>JVM에서 병렬 프로그래밍과 동시성 프로그래밍을 위한 고전적인 추상화는 스레드를 사용하는</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    println(&quot;I&#39;m on ${Thread.currentThread().name}&quot;)\n    thread {\n        println(&quot;And I&#39;m on ${Thread.currentThread().name}&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>스레드는 애플리케이션을 더 반응성 있게 만들어주고, 멀티코어 CPU의 여러 코어에 작업을 분산시켜 현대적 시스템을 더 효율적으로 사용할 수 있게 해준다.</li>\n<li>스레드 간 전환은 운영체제 커널 수준에서 실행되는 작업</li>\n<li>스레드가 어떤 작업이 완료되길 기다리는 동안에는 블록된다.</li>\n<li>스레드는 기본적으로 독립적인 프로세스로 존재하기 때문에 작업을 관리하고 조정하는 데 어려움이 있을 수 있다.</li>\n</ul>\n<p>코틀린은 스레드에 대한 대안으로 코루틴이라는 추상화를 도입</p>\n<ul>\n<li>코루틴은 초경량 추상화다.</li>\n<li>코루틴은 시스템 자원을 블록시키지 않고 실행을 일시 중단할 수 있으며, 중단 지점에서 재개할 수 있다.</li>\n<li>코루틴은 구조화된 동시성이라는 개념을 통해 동시 작업의 구조와 계층을 확립하며, 취소 및 오류 처리를 위한 메커니즘 제공</li>\n<li>코루틴은 하나 이상의 JVM 스레드에서 실행</li>\n</ul>\n<h2 id=\"-144-잠시-멈출-수-있는-함수-일시-중단-함수\" style=\"position:relative;\"><a href=\"#-144-%EC%9E%A0%EC%8B%9C-%EB%A9%88%EC%B6%9C-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%95%A8%EC%88%98-%EC%9D%BC%EC%8B%9C-%EC%A4%91%EB%8B%A8-%ED%95%A8%EC%88%98\" aria-label=\" 144 잠시 멈출 수 있는 함수 일시 중단 함수 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 14.4 잠시 멈출 수 있는 함수: 일시 중단 함수</h2>\n<ul>\n<li>코틀린 코루틴이 스레드, 반응형 스트림, 콜백과 같은 다른 동시성 접근 방식과 다른 핵심 속성으로는 상단수의 경우 코드 <strong>형태</strong>를 크게 변경할 필요가 없다는 점이 있다.</li>\n</ul>\n<h3 id=\"-1441-일시-중단-함수를-사용한-코드는-순차적으로-보인다\" style=\"position:relative;\"><a href=\"#-1441-%EC%9D%BC%EC%8B%9C-%EC%A4%91%EB%8B%A8-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%BD%94%EB%93%9C%EB%8A%94-%EC%88%9C%EC%B0%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B3%B4%EC%9D%B8%EB%8B%A4\" aria-label=\" 1441 일시 중단 함수를 사용한 코드는 순차적으로 보인다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.4.1 일시 중단 함수를 사용한 코드는 순차적으로 보인다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun login(credentials: Credentials): UserID\nfun loadUserData(userID: UserID): UserData\nfun showData(data: UserData)\n\nfun showUserInfo(credentials: Credentials) {\n    val userID = login(credentials)\n    val userData = loadUserData(userID)\n    showData(userData)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>블록된 스레드는 자원을 낭비</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun login(credentials: Credentials): UserID\nsuspend fun loadUserData(userID: UserID): UserData\nfun showData(data: UserData)\n\nfun showUserInfo(credentials: Credentials) {\n    val userID = login(credentials)\n    val userData = loadUserData(userID)\n    showData(userData)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>suspend</code> 변경자는 함수가 실행을 잠시 멈출 수도 있다는 뜻\n<ul>\n<li>ex) 네트워크 응답을 기다리는 경우 실행을 일시 중단</li>\n</ul>\n</li>\n<li>일시 중단은 기저 스레드를 블록시키지 않는다.</li>\n<li>물론 <code>login</code>과 <code>loadUserData</code>의 구현도 코틀린 코루틴을 고려해 작성돼야 한다.</li>\n</ul>\n<h2 id=\"-145-코루틴을-다른-접근-방법과-비교\" style=\"position:relative;\"><a href=\"#-145-%EC%BD%94%EB%A3%A8%ED%8B%B4%EC%9D%84-%EB%8B%A4%EB%A5%B8-%EC%A0%91%EA%B7%BC-%EB%B0%A9%EB%B2%95%EA%B3%BC-%EB%B9%84%EA%B5%90\" aria-label=\" 145 코루틴을 다른 접근 방법과 비교 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 14.5 코루틴을 다른 접근 방법과 비교</h2>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun loginAsync(credentials: Credentials, callback: (UserID) -&gt; Unit) {}\nfun loadUserDataAsync(userID: UserID, callback: (UserData) -&gt; Unit) {}\nfun showData(data: UserData) {}\n\nfun showUserInfo(credentials: Credentials) {\n    loginAsync(credentials) { userID -&gt;\n        loadUserDataAsync(userID) { userData -&gt;\n            showData(userData)\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>로직이 커지면 콜백이 중첩된 코드가 돼 가독성이 급격히 떨어진다.\n<ul>\n<li>콜백 지옥</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun loginAsync(credentials: Credentials): CompletableFuture&lt;UserID&gt;\nfun loadUserDataAsync(userID: UserID): CompletableFuture&lt;UserData&gt;\nfun showData(data: UserData)\n\nfun showUserInfo(credentials: Credentials) {\n    loginAsync(credentials)\n        .thenCompose { loadUserDataAsync(it) }\n        .thenAccept { showData(it) }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>CompletableFuture</code>을 사용하면 콜백 지옥을 피할 수 있지만 새로운 연산자의 의미를 배워야 한다.</li>\n<li>함수의 반환 타입도 변경해야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun login(credentials: Credentials): Single&lt;UserID&gt;\nfun loadUserData(userID: UserID): Single&lt;UserData&gt;\nfun showData(data: UserData)\n\nfun showUserInfo(credentials: Credentials) {\n    login(credentials)\n        .flatMap { loadUserData(it) }\n        .doOnSuccess { showData(it) }\n        .subscribe()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>반응형 스트림을 통한 구현은 콜백 지옥을 피할 수 있지만 함수 시그니처 변경 및 연산자를 사용해야 한다.</li>\n</ul>\n<p>위와 비교해보면 코루틴을 사용하는 접근 방식에서는 함수에 <code>suspend</code> 변경자만 추가하면 된다.</p>\n<h3 id=\"-1451-일시-중단-함수-호출\" style=\"position:relative;\"><a href=\"#-1451-%EC%9D%BC%EC%8B%9C-%EC%A4%91%EB%8B%A8-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C\" aria-label=\" 1451 일시 중단 함수 호출 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.5.1 일시 중단 함수 호출</h3>\n<ul>\n<li>일시 중단 함수는 실행을 일시 중단할 수 있기 때문에 일반 코드 아무 곳에서나 호출할 수는 ㅇ벗다.</li>\n<li>일시 중단 함수 실행을 일시 중단할 수 있는 코드 블록 안에서만 호출할 수 있다.</li>\n<li>그런 블록 중 하나가 다른 일시 중단 함수일 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun mySuspendingFunction() {}\n\nfun main() {\n    mySuspendingFunction() // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>일반적인 일시 중단 코드가 아닌 코드에서 일시 중단 함수를 호출하려고 하면 오류가 발생</li>\n<li>main 함수를 <code>suspend</code>로 하면 호출 가능</li>\n</ul>\n<h2 id=\"-146-코루틴-세계로-들어가기-코루틴-빌더\" style=\"position:relative;\"><a href=\"#-146-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%84%B8%EA%B3%84%EB%A1%9C-%EB%93%A4%EC%96%B4%EA%B0%80%EA%B8%B0-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EB%B9%8C%EB%8D%94\" aria-label=\" 146 코루틴 세계로 들어가기 코루틴 빌더 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 14.6 코루틴 세계로 들어가기: 코루틴 빌더</h2>\n<ul>\n<li>코루틴은 일시 중단 가능한 계산의 인스턴스다.</li>\n<li>다른 코루틴들과 동시에 실행될 수 있는 코드 블록</li>\n<li>스레드와 비슷하지만 코루틴은 함수 실행을 일시 중단하는 데 필요한 메커니즘을 포함하고 있다.\n<ul>\n<li><code>runBlocking</code>은 블로킹 코드와 일시 중단 함수의 세계를 연결할 때 쓰인다.</li>\n<li><code>launch</code>는 값을 반환하지 않는 새로운 코루틴을 시작할 때 쓰인다.</li>\n<li><code>async</code>는 비동기적으로 값을 계산할 때 쓰인다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1461-일반-코드에서-코루틴의-세계로-runblocking\" style=\"position:relative;\"><a href=\"#-1461-%EC%9D%BC%EB%B0%98-%EC%BD%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%BD%94%EB%A3%A8%ED%8B%B4%EC%9D%98-%EC%84%B8%EA%B3%84%EB%A1%9C-runblocking\" aria-label=\" 1461 일반 코드에서 코루틴의 세계로 runblocking permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.6.1 일반 코드에서 코루틴의 세계로: runBlocking</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun doSomethingSlowly() {\n    delay(500.milliseconds)\n    println(&quot;I&#39;m done&quot;)\n}\n\nfun main() = runBlocking {\n    doSomethingSlowly()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>새 코루틴을 생성하고 실행하며, 해당 코루틴이 완료될 때까지 현재 스레드를 블록시킨다.</li>\n<li><code>runBlocking</code>을 사용할 때는 하나의 스레드를 블로킹한다.</li>\n<li>다만, 추가적인 자식 코루틴을 얼마든지 시작할 수 있고, 이들은 다른 스레드를 더 이상 블록시키지 않는다.</li>\n</ul>\n<h3 id=\"-1462-발사-후-망각-코루틴-생성-launch-함수\" style=\"position:relative;\"><a href=\"#-1462-%EB%B0%9C%EC%82%AC-%ED%9B%84-%EB%A7%9D%EA%B0%81-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%83%9D%EC%84%B1-launch-%ED%95%A8%EC%88%98\" aria-label=\" 1462 발사 후 망각 코루틴 생성 launch 함수 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.6.2 발사 후 망각 코루틴 생성: launch 함수</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">private var zeroTime = System.currentTimeMillis()\nfun log(message: Any?) =\n    println(&quot;${System.currentTimeMillis() - zeroTime} &quot; + &quot;[${Thread.currentThread().name}] $message&quot;)\n\nfun main() = runBlocking {\n    log(&quot;The first, parent, coroutine starts&quot;)\n    launch {\n        log(&quot;The second coroutine starts and is ready to be suspended&quot;)\n        delay(100.milliseconds)\n        log(&quot;The second coroutine is resumed&quot;)\n    }\n    launch {\n        log(&quot;The third coroutine can run in the meantime&quot;)\n    }\n    log(&quot;The first coroutine has launched two more coroutines&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 예제를 실행하면 아래와 같은 결과가 출력된다.\n<ul>\n<li><code>-Dkotlinx.coroutines.debug</code> JVM 옵션 포함 실행</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">38 [main @coroutine#1] The first, parent, coroutine starts\n52 [main @coroutine#1] The first coroutine has launched two more coroutines\n53 [main @coroutine#2] The second coroutine starts and is ready to be suspended\n58 [main @coroutine#3] The third coroutine can run in the meantime\n163 [main @coroutine#2] The second coroutine is resumed</code>\n        </deckgo-highlight-code>\n<ul>\n<li>병렬성 없이 교차 실행되는 경우다. 즉, 모든 코루틴이 같은 스레드에서 실행</li>\n<li><code>launch</code>를 사용해 새로운 기본 코루틴을 시작할 수 잇다.</li>\n</ul>\n<h3 id=\"-1463-대기-가능한-연산-async-빌더\" style=\"position:relative;\"><a href=\"#-1463-%EB%8C%80%EA%B8%B0-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%97%B0%EC%82%B0-async-%EB%B9%8C%EB%8D%94\" aria-label=\" 1463 대기 가능한 연산 async 빌더 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.6.3 대기 가능한 연산: async 빌더</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun slowlyAddNumbers(a: Int, b: Int): Int {\n    log(&quot;Waiting a bit before calculating $a + $b&quot;)\n    delay(100.milliseconds * a)\n    return a + b\n}\n\nfun main() = runBlocking {\n    log(&quot;Starting the async computation&quot;)\n    val myFirstDeferred = async { slowlyAddNumbers(2, 2) }\n    val mySecondDeferred = async { slowlyAddNumbers(4, 4) }\n    log(&quot;Waiting for the deferred value to be available&quot;)\n    log(&quot;The first result: ${myFirstDeferred.await()}&quot;)\n    log(&quot;The second result: ${mySecondDeferred.await()}&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 예제를 실행하면 아래와 같은 결과가 출력된다.</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">44 [main @coroutine#1] Starting the async computation\n60 [main @coroutine#1] Waiting for the deferred value to be available\n65 [main @coroutine#2] Waiting a bit before calculating 2 + 2\n71 [main @coroutine#3] Waiting a bit before calculating 4 + 4\n287 [main @coroutine#1] The first result: 4\n481 [main @coroutine#1] The second result: 8</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>async</code>를 호출할 때마다 새로운 코루틴을 시작함으로써 두 계산이 동시에 일어나게 했다.</li>\n<li><code>await</code>를 호출하면 그 <code>Deferred</code>에서 결괏값이 사용 가능해질 때까지 루트 코루틴이 일시 중단된다.</li>\n<li><code>Deferred</code> 객체는 아직 사용할 수 없는 값을 나타낸다.\n<ul>\n<li>매래에 언젠가는 값을 알게 될 것이라는 약속, 연기된 계산 결괏값을 나타낸다.</li>\n</ul>\n</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>빌더</th>\n<th>반환값</th>\n<th>쓰임새</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>runBlocking</code></td>\n<td>람다가 계산한 값</td>\n<td>블로킹 코드와 넌블로킹 코드 사이를 연결</td>\n</tr>\n<tr>\n<td><code>launch</code></td>\n<td><code>Job</code></td>\n<td>발사 후 망각 코루틴 시작(부수 효과가 있음)</td>\n</tr>\n<tr>\n<td><code>async</code></td>\n<td><code>Deferred&#x3C;T></code></td>\n<td>값을 비동기로 계산(값을 기다릴 수 있음)</td>\n</tr>\n</tbody>\n</table>\n<h2 id=\"-147-어디서-코드를-실행할지-정하기-디스패처\" style=\"position:relative;\"><a href=\"#-147-%EC%96%B4%EB%94%94%EC%84%9C-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%8B%A4%ED%96%89%ED%95%A0%EC%A7%80-%EC%A0%95%ED%95%98%EA%B8%B0-%EB%94%94%EC%8A%A4%ED%8C%A8%EC%B2%98\" aria-label=\" 147 어디서 코드를 실행할지 정하기 디스패처 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 14.7 어디서 코드를 실행할지 정하기: 디스패처</h2>\n<ul>\n<li>코루틴의 디스패처는 코루틴을 실행할 스레드를 결정</li>\n<li>디스패처를 선택함으로써 코루틴을 특정 스레드로 제한하거나 스레드 풀에 분산시킬 수 있으며, 코루틴이 한 스레드에서만 실행될지 여러 스레드에서 실행될지 결정할 수 있다.</li>\n</ul>\n<h3 id=\"-1471-디스패처-선택\" style=\"position:relative;\"><a href=\"#-1471-%EB%94%94%EC%8A%A4%ED%8C%A8%EC%B2%98-%EC%84%A0%ED%83%9D\" aria-label=\" 1471 디스패처 선택 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.7.1 디스패처 선택</h3>\n<ul>\n<li>코루틴은 기본적으로 부모 코루틴에서 디스패처를 상속받으므로 모든 코루틴에 대해 명시적으로 디스패처를 지정할 필요는 없다.</li>\n</ul>\n<h4 id=\"️-다중-스레드를-사용하는-범용-디스패처-dispatchersdefault\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-%EB%8B%A4%EC%A4%91-%EC%8A%A4%EB%A0%88%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B2%94%EC%9A%A9-%EB%94%94%EC%8A%A4%ED%8C%A8%EC%B2%98-dispatchersdefault\" aria-label=\"️ 다중 스레드를 사용하는 범용 디스패처 dispatchersdefault permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🛠️ 다중 스레드를 사용하는 범용 디스패처: Dispatchers.Default</h4>\n<ul>\n<li>가장 일반적인 디스패처</li>\n<li>CPU 코어 수만큼의 스레드로 구성된 스레드 풀을 기반으로 함</li>\n<li>여러 스레드에서 코루틴이 분산돼 실행</li>\n</ul>\n<h4 id=\"️-ui-스레드에서-실행-dispatchersmain\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-ui-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%97%90%EC%84%9C-%EC%8B%A4%ED%96%89-dispatchersmain\" aria-label=\"️ ui 스레드에서 실행 dispatchersmain permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🛠️ UI 스레드에서 실행: Dispatchers.Main</h4>\n<ul>\n<li>UI 프레임워크를 사용할 때는 특정 작업을 UI 스레드나 메인 스레드라고 불리는 특정 스레드에서 실행해야 될 때가 있다.</li>\n<li><code>Dispatchers.Main</code>의 실제 값은 사용하는 프레임워크에 따라 다르다.</li>\n</ul>\n<h4 id=\"️-블로킹되는-io-작업-처리-dispatchersio\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-%EB%B8%94%EB%A1%9C%ED%82%B9%EB%90%98%EB%8A%94-io-%EC%9E%91%EC%97%85-%EC%B2%98%EB%A6%AC-dispatchersio\" aria-label=\"️ 블로킹되는 io 작업 처리 dispatchersio permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🛠️ 블로킹되는 IO 작업 처리: Dispatchers.IO</h4>\n<ul>\n<li>서드파티 라이브러리를 사용할 때 코루틴을 염두에 두고 설계된 API를 선택할 수 없는 경우</li>\n<li><code>Dispatchers.IO</code>에서 실행된 코루틴은 자동으로 확장되는 스레드 풀에서 실행</li>\n<li>CPU 집약적이지 않은 작업(블로킹 API 응답 대기)에 적합</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>디스패처</th>\n<th>스레드 개수</th>\n<th>쓰임새</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>Dispatchers.Default</code></td>\n<td>CPU 코어 수</td>\n<td>일반적인 연산, CPU 집약적인 작업</td>\n</tr>\n<tr>\n<td><code>Dispatchers.Main</code></td>\n<td>1</td>\n<td>UI 프레임워크의 맥락에서만 작업</td>\n</tr>\n<tr>\n<td><code>Dispatchers.IO</code></td>\n<td>64 + CPU 코어 개수(단, 최대 64개만 병렬 실행)</td>\n<td>블로킹 IO 작업, 네트워크 작업, 파일 작업</td>\n</tr>\n<tr>\n<td><code>Dispatchers.Unconfined</code></td>\n<td>아무 스레드나</td>\n<td>즉시 스케줄링해야 하는 특별한 경우</td>\n</tr>\n<tr>\n<td><code>limitedParallelism(n)</code></td>\n<td>커스텀(n)</td>\n<td>커스텀 시나리오</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"-1472-코루틴-빌더에-디스패처-전달\" style=\"position:relative;\"><a href=\"#-1472-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EB%B9%8C%EB%8D%94%EC%97%90-%EB%94%94%EC%8A%A4%ED%8C%A8%EC%B2%98-%EC%A0%84%EB%8B%AC\" aria-label=\" 1472 코루틴 빌더에 디스패처 전달 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.7.2 코루틴 빌더에 디스패처 전달</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    runBlocking {\n        log(&quot;Doing some work&quot;)\n        launch(Dispatchers.Default) {\n            log(&quot;Doing some background work&quot;)\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코루틴 빌더 함수는 코루틴 디스패처를 명시적으로 지정할 수 있게 한다.</li>\n<li>위 코드의 실행 결과는 아래와 같다.</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">45 [main @coroutine#1] Doing some work\n63 [DefaultDispatcher-worker-2 @coroutine#2] Doing some background work</code>\n        </deckgo-highlight-code>\n<h3 id=\"-1473-withcontext를-사용해-코루틴-안에서-디스패처-바꾸기\" style=\"position:relative;\"><a href=\"#-1473-withcontext%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%95%88%EC%97%90%EC%84%9C-%EB%94%94%EC%8A%A4%ED%8C%A8%EC%B2%98-%EB%B0%94%EA%BE%B8%EA%B8%B0\" aria-label=\" 1473 withcontext를 사용해 코루틴 안에서 디스패처 바꾸기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.7.3 withContext를 사용해 코루틴 안에서 디스패처 바꾸기</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">launch(Dispatchers.Default) {\n    val result = performBackgroundOperation()\n    withContext(Dispatchers.Main) {\n        updateUI(result)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이미 실행 중인 코루틴에서 디스패처를 바꿀 때는 <code>withContext</code> 함수에 다른 디스패처를 전달</li>\n</ul>\n<h3 id=\"-1474-코루틴과-디스패처는-스레드-안전성-문제에-대한-마법-같은-해결책이-아니다\" style=\"position:relative;\"><a href=\"#-1474-%EC%BD%94%EB%A3%A8%ED%8B%B4%EA%B3%BC-%EB%94%94%EC%8A%A4%ED%8C%A8%EC%B2%98%EB%8A%94-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%95%88%EC%A0%84%EC%84%B1-%EB%AC%B8%EC%A0%9C%EC%97%90-%EB%8C%80%ED%95%9C-%EB%A7%88%EB%B2%95-%EA%B0%99%EC%9D%80-%ED%95%B4%EA%B2%B0%EC%B1%85%EC%9D%B4-%EC%95%84%EB%8B%88%EB%8B%A4\" aria-label=\" 1474 코루틴과 디스패처는 스레드 안전성 문제에 대한 마법 같은 해결책이 아니다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 14.7.4 코루틴과 디스패처는 스레드 안전성 문제에 대한 마법 같은 해결책이 아니다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    runBlocking {\n        launch(Dispatchers.Default) {\n            var x = 0\n            repeat(10_000) {\n                x++\n            }\n            println(x)\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코루틴이 종료된 후 x 값은 10000으로 정확하다.</li>\n<li>한 코루틴이 임의의 스레드에서 실행되더라도 그 로직이 엄격하게 순차적으로 실행되기 때문</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    runBlocking {\n        var x = 0\n        repeat(10_000) {\n            launch(Dispatchers.Default) {\n                x++\n            }\n        }\n        delay(1.seconds)\n        println(x)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>카운터 값이 예상보다 낮다.</li>\n<li>여러 코루틴이 같은 데이터를 수정하고 있기 때문</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    val mutex = Mutex()\n    var x = 0\n    repeat(10_000) {\n        launch(Dispatchers.Default) {\n            mutex.withLock {\n                x++\n            }\n        }\n    }\n    delay(1.seconds)\n    println(x)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>Mutex</code> 잠금을 통해 코드 임계 영역이 한 번에 하나의 코루틴만 실행되게 보장할 수 있다.</li>\n</ul>\n<h2 id=\"-148-코루틴은-코루틴-콘텍스트에-추가적인-정보를-담고-있다\" style=\"position:relative;\"><a href=\"#-148-%EC%BD%94%EB%A3%A8%ED%8B%B4%EC%9D%80-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%BD%98%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%97%90-%EC%B6%94%EA%B0%80%EC%A0%81%EC%9D%B8-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EB%8B%B4%EA%B3%A0-%EC%9E%88%EB%8B%A4\" aria-label=\" 148 코루틴은 코루틴 콘텍스트에 추가적인 정보를 담고 있다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 14.8 코루틴은 코루틴 콘텍스트에 추가적인 정보를 담고 있다</h2>\n<ul>\n<li>각 코루틴은 추가적인 문맥 정보를 담고 있는데, 이 문맥은 <code>CoroutineContext</code>라는 형태로 제공</li>\n<li><code>CoroutineContext</code>는 여러 요소로 이뤄진 집합</li>\n<li>이 요소 중 하나는 코루틴이 어떤 스레드에서 실행될지를 결정하는 디스패처</li>\n<li><code>CoroutineContext</code>에는 코루틴의 생명주기와 취소를 관리하는 <code>Job</code> 객체 포함</li>\n<li><code>CoroutineContext</code>에는 <code>CoroutineNmae</code>, <code>CoroutineExceptionHandler</code>와 같은 메타데이터도 포함</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun introspect() {\n    log(coroutineContext)\n}\n\nfun main() {\n    runBlocking {\n        introspect()\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 결과는 아래와 같다.</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">39 [main @coroutine#1] [CoroutineId(1), &quot;coroutine#1&quot;:BlockingCoroutine{Active}@32709393, BlockingEventLoop@3d99d22e]</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    runBlocking(Dispatchers.IO + CoroutineName(&quot;Coolroutine&quot;)) {\n        introspect()\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코루틴 빌더에 인자를 전달하면 자식 코루틴의 콘텍스트에서 해당 요소를 덮어쓴다.</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">43 [DefaultDispatcher-worker-1 @Coolroutine#1] [CoroutineName(Coolroutine), CoroutineId(1), &quot;Coolroutine#1&quot;:BlockingCoroutine{Active}@1be729a6, Dispatchers.IO]</code>\n        </deckgo-highlight-code>","excerpt":"📖 14.1 동시성과 병렬성 동시성 여러 작업을 동시에 실행하는 것 CPU 코어 하나에서 동시성 사용 가능 병렬성 코드를 여러 부분으로 나눠서 동시에 수행할 수 있는 능력 여러 CPU 코어에서 물리적으로 동시에 실행하는 것 📖 14.2 코틀린의 동시성 처리 방법: 일시 중단 함수와 코루틴 코루틴은 비동기적으로 실행되는 넌블로킹 동시성 코드를 우아하게 작성할 수 있게 해준다. 스레드에 비해 매우 가볍다. 동시성 작업과 생명주기 관리 기능 제공 📖 14.3 스레드와 코루틴 비교 JVM…","fields":{"slug":"/backend/kotlin-in-action/14장-코루틴/"},"frontmatter":{"title":"Kotlin in Action - 14장 코루틴","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"June 08, 2025"}},"previous":{"id":"4cf91309-fe0d-5841-9f3f-1b78e4544680","html":"<h2 id=\"-161-플로우는-연속적인-값의-스트림을-모델링한다\" style=\"position:relative;\"><a href=\"#-161-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%8A%94-%EC%97%B0%EC%86%8D%EC%A0%81%EC%9D%B8-%EA%B0%92%EC%9D%98-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%84-%EB%AA%A8%EB%8D%B8%EB%A7%81%ED%95%9C%EB%8B%A4\" aria-label=\" 161 플로우는 연속적인 값의 스트림을 모델링한다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 16.1 플로우는 연속적인 값의 스트림을 모델링한다</h2>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun createValues(): List&lt;Int&gt; {\n    return buildList {\n        add(1)\n        delay(1.seconds)\n        add(2)\n        delay(1.seconds)\n        add(3)\n        delay(1.seconds)\n    }\n}\n\nfun main() = runBlocking {\n    val list = createValues()\n    list.forEach {\n        log(it)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>모든 값이 계산 된 후 함수가 값을 반환함.</li>\n<li>함수가 실행을 마칠 때까지 기다리지 않고 값을 사용할 수 있도록 비동기적으로 반환하고 싶을 때 플롱우가 유용하다.</li>\n<li>플로우는 시간이 지남에 따라 나타나는 값과 작업할 수 있게 해주는 코루틴 기반의 추상화다</li>\n</ul>\n<h3 id=\"-1611-플로우를-사용하면-배출되자마자-원소를-처리할-수-있다\" style=\"position:relative;\"><a href=\"#-1611-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EB%B0%B0%EC%B6%9C%EB%90%98%EC%9E%90%EB%A7%88%EC%9E%90-%EC%9B%90%EC%86%8C%EB%A5%BC-%EC%B2%98%EB%A6%AC%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4\" aria-label=\" 1611 플로우를 사용하면 배출되자마자 원소를 처리할 수 있다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.1.1 플로우를 사용하면 배출되자마자 원소를 처리할 수 있다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun createValues(): Flow&lt;Int&gt; {\n    return flow {\n        emit(1)\n        delay(1.seconds)\n        emit(2)\n        delay(1.seconds)\n        emit(3)\n        delay(1.seconds)\n    }\n}\n\nfun main() = runBlocking {\n    val myFlowOfValues = createValues()\n    myFlowOfValues.collect { log(it) }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [main @coroutine#1] 1\n1036 [main @coroutine#1] 2\n2042 [main @coroutine#1] 3</code>\n        </deckgo-highlight-code>\n<ul>\n<li>원소가 배출되는 즉시 표시된다.</li>\n</ul>\n<h3 id=\"-1612-코틀린-플로우의-여러-유형\" style=\"position:relative;\"><a href=\"#-1612-%EC%BD%94%ED%8B%80%EB%A6%B0-%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%9D%98-%EC%97%AC%EB%9F%AC-%EC%9C%A0%ED%98%95\" aria-label=\" 1612 코틀린 플로우의 여러 유형 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.1.2 코틀린 플로우의 여러 유형</h3>\n<ul>\n<li>콜드 플로우\n<ul>\n<li>비동기 데이터 스트림</li>\n<li>값이 실제로 소비되기 시작할 때만 값을 배출</li>\n</ul>\n</li>\n<li>핫 플로우\n<ul>\n<li>브로드캐스트 방식</li>\n<li>값이 실제로 소비되고 있는지와 상관없이 독립적으로 배출</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"-162-콜드-플로우\" style=\"position:relative;\"><a href=\"#-162-%EC%BD%9C%EB%93%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0\" aria-label=\" 162 콜드 플로우 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 16.2 콜드 플로우</h2>\n<h3 id=\"-1621-flow-빌더-함수를-사용해-콜드-플로우-생성\" style=\"position:relative;\"><a href=\"#-1621-flow-%EB%B9%8C%EB%8D%94-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EC%BD%9C%EB%93%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%83%9D%EC%84%B1\" aria-label=\" 1621 flow 빌더 함수를 사용해 콜드 플로우 생성 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.2.1 flow 빌더 함수를 사용해 콜드 플로우 생성</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    val letters = flow {\n        log(&quot;Emitting A!&quot;)\n        emit(&quot;A&quot;)\n        delay(200.milliseconds)\n        log(&quot;Emitting B!&quot;)\n        emit(&quot;B&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>아무런 출력도 나타나지 않는다.</li>\n<li><code>flow</code> 빌더 함수를 호출해도 실제 작업이 시작되지 않음.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val counterFlow = flow {\n    var x = 0\n    while (true) {\n        emit(x++)\n        delay(200.milliseconds)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 루프는 실제로 플로우가 수집될 때만 실행</li>\n</ul>\n<h3 id=\"-1622-콜드-플로우는-수집되기-전까지-작업을-수행하지-않는다\" style=\"position:relative;\"><a href=\"#-1622-%EC%BD%9C%EB%93%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%8A%94-%EC%88%98%EC%A7%91%EB%90%98%EA%B8%B0-%EC%A0%84%EA%B9%8C%EC%A7%80-%EC%9E%91%EC%97%85%EC%9D%84-%EC%88%98%ED%96%89%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4\" aria-label=\" 1622 콜드 플로우는 수집되기 전까지 작업을 수행하지 않는다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.2.2 콜드 플로우는 수집되기 전까지 작업을 수행하지 않는다</h3>\n<ul>\n<li><code>Flow</code>에 대해 <code>collect</code> 함수를 호출하면 그 로직이 실행된다</li>\n<li>플로우를 수집할 때는 플로우 내부의 일시 중단 코드를 실행하므로 <code>collect</code>는 일시 중단 함수이며, 플로우가 끝날 때까지 일시 중단된다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val letters = flow {\n    log(&quot;Emitting A!&quot;)\n    emit(&quot;A&quot;)\n    delay(200.milliseconds)\n    log(&quot;Emitting B!&quot;)\n    emit(&quot;B&quot;)\n}\n\nfun main() = runBlocking {\n    letters.collect {\n        log(&quot;Collecting: $it&quot;)\n        delay(500.milliseconds)\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [main @coroutine#1] Emitting A!\n15 [main @coroutine#1] Collecting: A\n737 [main @coroutine#1] Emitting B!\n737 [main @coroutine#1] Collecting: B</code>\n        </deckgo-highlight-code>\n<ul>\n<li>수집자가 플로우의 로직을 실행하는 책임이 있다.</li>\n<li>원소 A, B 사이의 지연시간은 700밀리초이다.\n<ul>\n<li>수집자가 플로우 빌더에 정의된 로직의 실행을 촉발해서 첫 번째 배출 발생</li>\n<li>수집자가 연결된 람다가 호출되면서 메시지를 기록하고 500밀리초 동안 지연</li>\n<li>플로우 람다가 계속 실행되며 200밀리초 동안 추가 지연과 배출이 발생</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    letters.collect {\n        log(&quot;(1) Collecting: $it&quot;)\n        delay(500.milliseconds)\n    }\n    letters.collect {\n        log(&quot;(2) Collecting: $it&quot;)\n        delay(500.milliseconds)\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [main @coroutine#1] Emitting A!\n16 [main @coroutine#1] (1) Collecting: A\n743 [main @coroutine#1] Emitting B!\n743 [main @coroutine#1] (1) Collecting: B\n1247 [main @coroutine#1] Emitting A!\n1247 [main @coroutine#1] (2) Collecting: A\n1958 [main @coroutine#1] Emitting B!\n1958 [main @coroutine#1] (2) Collecting: B</code>\n        </deckgo-highlight-code>\n<ul>\n<li>같은 플로우를 여러 번 수집할 수 있다.</li>\n</ul>\n<h3 id=\"-1623-플로우-수집-취소\" style=\"position:relative;\"><a href=\"#-1623-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%88%98%EC%A7%91-%EC%B7%A8%EC%86%8C\" aria-label=\" 1623 플로우 수집 취소 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.2.3 플로우 수집 취소</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    val collector = launch {\n        counterFlow.collect {\n            println(it)\n        }\n    }\n    delay(5.seconds)\n    collector.cancel()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>수집자의 코루틴을 취소하면 다음 취소 지점에서 플로우의 수집이 중단된다.</li>\n</ul>\n<h3 id=\"-1624-콜드-플로우의-내부-구현\" style=\"position:relative;\"><a href=\"#-1624-%EC%BD%9C%EB%93%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%9D%98-%EB%82%B4%EB%B6%80-%EA%B5%AC%ED%98%84\" aria-label=\" 1624 콜드 플로우의 내부 구현 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.2.4 콜드 플로우의 내부 구현</h3>\n<ul>\n<li>코틀린의 콜드 플로우는 일시 중단 함수와 수신 객체 지정 람다를 결합한 똑똑한 조합이다.</li>\n<li>콜드 플로우의 정의는 매우 간단하다.\n<ul>\n<li><code>Flow</code>와 <code>FlowCollector</code>라는 2가지 인터페이스만 필요</li>\n</ul>\n</li>\n<li><code>collect</code>를 호출하면 플로우 빌더 함수의 본문이 실행</li>\n<li>이 코드가 <code>emit</code>을 호출하면 <code>emit</code>에 전달된 파라미터로 <code>collect</code>에 전달된 람다가 호출</li>\n<li>람다 표현식이 실행을 완료하면 함수는 빌더 함수의 본문으로 돌아가 계속 실행</li>\n<li>콜드 플로우는 값 스트림을 처리하는 가벼우면서도 아주 쓸모 있고 확장성 있는 추상화를 제공</li>\n</ul>\n<h3 id=\"-1625-채널-플로우를-사용한-동시성-플로우\" style=\"position:relative;\"><a href=\"#-1625-%EC%B1%84%EB%84%90-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%ED%94%8C%EB%A1%9C%EC%9A%B0\" aria-label=\" 1625 채널 플로우를 사용한 동시성 플로우 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.2.5 채널 플로우를 사용한 동시성 플로우</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun getRandomNumber(): Int {\n    delay(500.milliseconds)\n    return Random.nextInt()\n}\n\nval randomNumbers = flow {\n    repeat(10) {\n        emit(getRandomNumber())\n    }\n}\n\nfun main() = runBlocking {\n    randomNumbers.collect {\n        log(it)\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [main @coroutine#1] -1441864547\n513 [main @coroutine#1] 1101626017\n1020 [main @coroutine#1] 313359960\n1527 [main @coroutine#1] -2002910241\n2033 [main @coroutine#1] 890150628\n2538 [main @coroutine#1] -1433970862\n3045 [main @coroutine#1] 1503636281\n3548 [main @coroutine#1] -1528949568\n4055 [main @coroutine#1] -1562012272\n4561 [main @coroutine#1] 2076525050</code>\n        </deckgo-highlight-code>\n<ul>\n<li>플로우는 순차적으로 실행되며, 모든 계산은 동일 코루틴에서 실행</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val randomNumbers = channelFlow {\n    repeat(10) {\n        launch {\n            send(getRandomNumber())\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>동시성으로 호출할 수 있는 플로우 빌더: <code>channelFlow</code></li>\n<li>채널 플로우는 내부적으로 또 다른 동시성 기본 요소인 채널을 관리해야 하기 때문에 생성하는 데 약간 비용이 든다.\n<ul>\n<li>채널은 코루틴 간 통신을 위한 비교적 저수준의 추상화</li>\n</ul>\n</li>\n<li>플로우 안에서 새로운 코루틴을 시작해야 하는 경우에만 채널 플로우 선택</li>\n</ul>\n<h2 id=\"-163-핫-플로우\" style=\"position:relative;\"><a href=\"#-163-%ED%95%AB-%ED%94%8C%EB%A1%9C%EC%9A%B0\" aria-label=\" 163 핫 플로우 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 16.3 핫 플로우</h2>\n<ul>\n<li>핫 플로우에서는 각 수집자가 플로우 로직 실행을 독립적으로 촉발하는 대신, 여러 구독자라고 불리는 수집자들이 배출된 항목을 공유한다.\n<ul>\n<li>공유 플로우: 값을 브로드캐스트하기 위해 사용</li>\n<li>상태 플로우: 상태를 전달하는 특별한 경우에 사용</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1631-공유-플로우는-값을-구독자에게-브로드캐스트한다\" style=\"position:relative;\"><a href=\"#-1631-%EA%B3%B5%EC%9C%A0-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%8A%94-%EA%B0%92%EC%9D%84-%EA%B5%AC%EB%8F%85%EC%9E%90%EC%97%90%EA%B2%8C-%EB%B8%8C%EB%A1%9C%EB%93%9C%EC%BA%90%EC%8A%A4%ED%8A%B8%ED%95%9C%EB%8B%A4\" aria-label=\" 1631 공유 플로우는 값을 구독자에게 브로드캐스트한다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.3.1 공유 플로우는 값을 구독자에게 브로드캐스트한다</h3>\n<ul>\n<li>공유 플로우는 구독자가 존재하는지 여부에 상관없이 배출이 발생하는 브로드캐스트 방식</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class RadioStation {\n    private val _messageFlow = MutableSharedFlow&lt;Int&gt;()\n    val messageFlow = _messageFlow.asSharedFlow()\n\n    fun beginBroadcasting(scope: CoroutineScope) {\n        scope.launch {\n            while (true) {\n                delay(500.milliseconds)\n                val number = Random.nextInt(0..10)\n                log(&quot;Emitting $number!&quot;)\n                _messageFlow.emit(number)\n            }\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>플로우 빌더를 사용하는 대신 가변적인 플로우에 대한 참조를 얻는다.</li>\n<li>배출이 구독자 유무와 관계없이 발생하므로 여러분이 실제 배출을 수행하는 코루틴을 시작할 책임이 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    RadioStation().beginBroadcasting(this)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>구독자가 없어도 브로드캐스트가 즉시 시작</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    val radioStation = RadioStation()\n    radioStation.beginBroadcasting(this)\n    delay(600.milliseconds)\n    radioStation.messageFlow.collect {\n        log(&quot;A collecting: $it!&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>약간의 지연 후에 공유 플로우 구독</li>\n</ul>\n<h4 id=\"️-구독자를-위한-값-재생\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-%EA%B5%AC%EB%8F%85%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B0%92-%EC%9E%AC%EC%83%9D\" aria-label=\"️ 구독자를 위한 값 재생 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🛠️ 구독자를 위한 값 재생</h4>\n<ul>\n<li>공유 플로우 구독자는 구독을 시작한 이후에 배출된 값만 수신</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">private val _messageFlow = MutableSharedFlow&lt;Int&gt;(replay = 5)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이전에 배출된 원소도 수신하기를 원한다면 <code>replay</code> 파라미터 설정</li>\n</ul>\n<h4 id=\"️-sharein으로-콜드-플로우를-공유-플로우로-전환\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-sharein%EC%9C%BC%EB%A1%9C-%EC%BD%9C%EB%93%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%A5%BC-%EA%B3%B5%EC%9C%A0-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%A1%9C-%EC%A0%84%ED%99%98\" aria-label=\"️ sharein으로 콜드 플로우를 공유 플로우로 전환 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🛠️ shareIn으로 콜드 플로우를 공유 플로우로 전환</h4>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun querySensor(): Int = Random.nextInt(-10..30)\n\nfun getTemperatures(): Flow&lt;Int&gt; {\n    return flow {\n        while (true) {\n            emit(querySensor())\n            delay(500.milliseconds)\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>일정한 간격으로 값을 반환</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun celsiusToFahrenheit(celsius: Int) = celsius * 9.0 / 5.0 + 32.0\n\nfun main() {\n    val temps = getTemperatures()\n    runBlocking {\n        launch {\n            temps.collect {\n                log(&quot;$it Celsius&quot;)\n            }\n        }\n        launch {\n            temps.collect {\n                log(&quot;${celsiusToFahrenheit(it)} Fahrenheit&quot;)\n            }\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>여러번 호출하려면 각 수집자가 센서에 독립적으로 질의</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val temps = getTemperatures()\n    runBlocking {\n        val sharedTemps = temps.shareIn(this, SharingStarted.Lazily)\n        launch {\n            sharedTemps.collect {\n                log(&quot;$it Celsius&quot;)\n            }\n        }\n        launch {\n            sharedTemps.collect {\n                log(&quot;${celsiusToFahrenheit(it)} Fahrenheit&quot;)\n            }\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>shareIn</code> 함수를 사용하면 주어진 콜드 플로우를 한 플로우인 공유 플로우로 변환할 수 있다.</li>\n<li>두번째 파라미터 <code>started</code>는 플로우가 실제로 언제 시작돼야 하는지를 정의\n<ul>\n<li><code>Eagerly</code>는 플로우 수집을 즉시 시작</li>\n<li><code>Lazily</code>는 첫 번째 구독자가 나타나야만 수집 시작</li>\n<li><code>WhileSubscribed</code>는 첫 번째 구독자가 나타나야 수집을 시작하고, 마지막 구독자가 사라지면 플로우 수집을 취소</li>\n</ul>\n</li>\n<li>코틀린에서는 시간이 지남에 따라 여러 값을 계산하는 작업을 단순한 콜드 플로우로 노출하고, 필요할 때 콜드 플로우를 핫 플로우로 변환하는 패턴이 자주 사용된다.</li>\n</ul>\n<h3 id=\"-1632-시스템-상태-추적-상태-플로우\" style=\"position:relative;\"><a href=\"#-1632-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%83%81%ED%83%9C-%EC%B6%94%EC%A0%81-%EC%83%81%ED%83%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0\" aria-label=\" 1632 시스템 상태 추적 상태 플로우 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.3.2 시스템 상태 추적: 상태 플로우</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class ViewCounter {\n    private val _counter = MutableStateFlow(0)\n    val counter = _counter.asStateFlow()\n    fun increment() {\n        _counter.update { it + 1 }\n    }\n}\n\nfun main() {\n    val vc = ViewCounter()\n    vc.increment()\n    println(vc.counter.value)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>값을 배출하는 <code>emit</code>을 사용하는 대신, 값을 갱신하는 <code>update</code> 함수 사용</li>\n</ul>\n<h4 id=\"️-update-함수로-안전하게-상태-플로우에-쓰기\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-update-%ED%95%A8%EC%88%98%EB%A1%9C-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EC%83%81%ED%83%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%97%90-%EC%93%B0%EA%B8%B0\" aria-label=\"️ update 함수로 안전하게 상태 플로우에 쓰기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🛠️ UPDATE 함수로 안전하게 상태 플로우에 쓰기</h4>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">    fun increment() {\n    _counter.value++\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>value</code> 는 가변 속성이니까 위와 같이 구현해도 될까?</li>\n<li>위 연산은 원자적이지 않다.</li>\n<li>즉, 코루틴들이 여러 스레드에서 실행되기 때문에 문제가 있다.\n<ul>\n<li>어느 한쪽의 증가 연산이 무효화</li>\n</ul>\n</li>\n</ul>\n<h4 id=\"️-상태-플로우는-값이-실제로-달라졌을-때만-값을-배출한다-동등성-기반-통합\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-%EC%83%81%ED%83%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%8A%94-%EA%B0%92%EC%9D%B4-%EC%8B%A4%EC%A0%9C%EB%A1%9C-%EB%8B%AC%EB%9D%BC%EC%A1%8C%EC%9D%84-%EB%95%8C%EB%A7%8C-%EA%B0%92%EC%9D%84-%EB%B0%B0%EC%B6%9C%ED%95%9C%EB%8B%A4-%EB%8F%99%EB%93%B1%EC%84%B1-%EA%B8%B0%EB%B0%98-%ED%86%B5%ED%95%A9\" aria-label=\"️ 상태 플로우는 값이 실제로 달라졌을 때만 값을 배출한다 동등성 기반 통합 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🛠️ 상태 플로우는 값이 실제로 달라졌을 때만 값을 배출한다: 동등성 기반 통합</h4>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">enum class Direction { LEFT, RIGHT }\n\nclass DirectionSelector {\n    private val _direction = MutableStateFlow(Direction.LEFT)\n    val direction = _direction.asStateFlow()\n\n    fun turn(d: Direction) {\n        _direction.update { d }\n    }\n}\n\nfun main() = runBlocking {\n    val switch = DirectionSelector()\n    launch {\n        switch.direction.collect {\n            log(&quot;Direction now $it&quot;)\n        }\n    }\n    delay(200.milliseconds)\n    switch.turn(Direction.RIGHT)\n    delay(200.milliseconds)\n    switch.turn(Direction.LEFT)\n    delay(200.milliseconds)\n    switch.turn(Direction.LEFT)\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [main @coroutine#2] Direction now LEFT\n210 [main @coroutine#2] Direction now RIGHT\n405 [main @coroutine#2] Direction now LEFT</code>\n        </deckgo-highlight-code>\n<ul>\n<li>LEFT 인자가 한번만 호출이 된다.</li>\n<li>상태 플로우가 동등성 기반 통합을 수행하기 때문\n<ul>\n<li>값이 실제로 달라졌을 때만 구독자에게 값을 배출</li>\n</ul>\n</li>\n</ul>\n<h4 id=\"️-statein-으로-콜드-플로우를-상태-플로우로-변환하기\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-statein-%EC%9C%BC%EB%A1%9C-%EC%BD%9C%EB%93%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%A5%BC-%EC%83%81%ED%83%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0\" aria-label=\"️ statein 으로 콜드 플로우를 상태 플로우로 변환하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🛠️ stateIn 으로 콜드 플로우를 상태 플로우로 변환하기</h4>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    val temps = getTemperatures()\n\n    runBlocking {\n        val tempState = temps.stateIn(this)\n        println(tempState.value)\n        delay(800.milliseconds)\n        println(tempState.value)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>stateIn</code>으로 콜드 플로우를 상태 플로우로 변환할 수 있다.</li>\n<li>원래 플로우에서 배출된 최신 값을 항상 읽을 수 있다.</li>\n</ul>\n<h3 id=\"-1633-상태-플로우와-공유-플로우의-비교\" style=\"position:relative;\"><a href=\"#-1633-%EC%83%81%ED%83%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%99%80-%EA%B3%B5%EC%9C%A0-%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%9D%98-%EB%B9%84%EA%B5%90\" aria-label=\" 1633 상태 플로우와 공유 플로우의 비교 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.3.3 상태 플로우와 공유 플로우의 비교</h3>\n<ul>\n<li>일반적으로 상태 플로우는 공유 플로우보다 더 간단한 API를 제공</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Broadcaster {\n    private val _messages = MutableSharedFlow&lt;String&gt;()\n    val messages = _messages.asSharedFlow()\n    fun beginBroadcasting(scope: CoroutineScope) {\n        scope.launch {\n            _messages.emit(&quot;Hello!&quot;)\n            _messages.emit(&quot;Hi!&quot;)\n            _messages.emit(&quot;Hola!&quot;)\n        }\n    }\n}\n\nfun main(): Unit = runBlocking {\n    val broadcaster = Broadcaster()\n    broadcaster.beginBroadcasting(this)\n    delay(200.milliseconds)\n    broadcaster.messages.collect {\n        println(&quot;Message: $it&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>첫 번째 구독자가 나타나기 전에 모든 메시지를 배출하는 브로드캐스트</li>\n<li>메시지를 출력하지 않음.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Broadcaster {\n    private val _messages = MutableStateFlow&lt;List&lt;String&gt;&gt;(emptyList())\n    val messages = _messages.asStateFlow()\n    fun beginBroadcasting(scope: CoroutineScope) {\n        scope.launch {\n            _messages.update { it + &quot;Hello!&quot; }\n            _messages.update { it + &quot;Hi!&quot; }\n            _messages.update { it + &quot;Hola!&quot; }\n        }\n    }\n}\n\nfun main() = runBlocking {\n    val broadcaster = Broadcaster()\n    broadcaster.beginBroadcasting(this)\n    delay(200.milliseconds)\n    println(broadcaster.messages.value)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>상태 플로우는 전체 메시지 기록을 리스트로 저장하면서 구독자가 모든 이전 메시지에 쉽게 접근할 수 있게 할 수 있다.</li>\n</ul>\n<h3 id=\"-1634-핫-플로우-콜드-플로우-공유-플로우-상태-플로우-언제-어떤-플로우를-사용할까\" style=\"position:relative;\"><a href=\"#-1634-%ED%95%AB-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%BD%9C%EB%93%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EA%B3%B5%EC%9C%A0-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%83%81%ED%83%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%96%B8%EC%A0%9C-%EC%96%B4%EB%96%A4-%ED%94%8C%EB%A1%9C%EC%9A%B0%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C\" aria-label=\" 1634 핫 플로우 콜드 플로우 공유 플로우 상태 플로우 언제 어떤 플로우를 사용할까 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 16.3.4 핫 플로우, 콜드 플로우, 공유 플로우, 상태 플로우: 언제 어떤 플로우를 사용할까?</h3>\n<h3 id=\"표-161-콜드-플로우와-핫-플로우-비교\" style=\"position:relative;\"><a href=\"#%ED%91%9C-161-%EC%BD%9C%EB%93%9C-%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%99%80-%ED%95%AB-%ED%94%8C%EB%A1%9C%EC%9A%B0-%EB%B9%84%EA%B5%90\" aria-label=\"표 161 콜드 플로우와 핫 플로우 비교 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>표 16.1 콜드 플로우와 핫 플로우 비교</h3>\n<table>\n<thead>\n<tr>\n<th>콜드 플로우</th>\n<th>핫 플로우</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>기본적으로 비활성(수집자에 의해 활성화됨)</td>\n<td>기본적으로 활성화됨</td>\n</tr>\n<tr>\n<td>수집자가 하나 있음</td>\n<td>여러 구독자가 있음</td>\n</tr>\n<tr>\n<td>수집자는 모든 배출을 받음</td>\n<td>구독자는 구독 시작 시점부터 배출을 받음</td>\n</tr>\n<tr>\n<td>보통은 완료됨</td>\n<td>완료되지 않음</td>\n</tr>\n<tr>\n<td>하나의 코루틴에서 배출 발생(channelFlow 사용 시 예외)</td>\n<td>여러 코루틴에서 배출할 수 있음</td>\n</tr>\n</tbody>\n</table>","excerpt":"📖 16.1 플로우는 연속적인 값의 스트림을 모델링한다 모든 값이 계산 된 후 함수가 값을 반환함. 함수가 실행을 마칠 때까지 기다리지 않고 값을 사용할 수 있도록 비동기적으로 반환하고 싶을 때 플롱우가 유용하다. 플로우는 시간이 지남에 따라 나타나는 값과 작업할 수 있게 해주는 코루틴 기반의 추상화다 🔖 16.1.1 플로우를 사용하면 배출되자마자 원소를 처리할 수 있다 원소가 배출되는 즉시 표시된다. 🔖 16.1.…","fields":{"slug":"/backend/kotlin-in-action/16장-플로우/"},"frontmatter":{"title":"Kotlin in Action - 16장 플로우","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"June 22, 2025"}},"node":{"id":"368e41f3-7b7f-530b-b274-9dd74a426734","html":"<h2 id=\"-151-코루틴-스코프가-코루틴-간의-구조를-확립한다\" style=\"position:relative;\"><a href=\"#-151-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%8A%A4%EC%BD%94%ED%94%84%EA%B0%80-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EA%B0%84%EC%9D%98-%EA%B5%AC%EC%A1%B0%EB%A5%BC-%ED%99%95%EB%A6%BD%ED%95%9C%EB%8B%A4\" aria-label=\" 151 코루틴 스코프가 코루틴 간의 구조를 확립한다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 15.1 코루틴 스코프가 코루틴 간의 구조를 확립한다</h2>\n<ul>\n<li>구조화된 동시성을 통해 각 코루틴은 코루틴 스코프에 속하게 된다.</li>\n<li>다른 코루틴 빌더의 본문에서 <code>launch</code>나 <code>async</code>를 사용해 새로운 코루틴을 만들면 이 새로운 코루틴은 자동으로 해당 코루틴의 자식이 된다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    runBlocking {\n        launch {\n            delay(1.seconds)\n            launch {\n                delay(250.milliseconds)\n                log(&quot;Grandchild done&quot;)\n            }\n            log(&quot;Child 1 done!&quot;)\n        }\n        launch {\n            delay(500.milliseconds)\n            log(&quot;Child 2 done!&quot;)\n        }\n        log(&quot;Parent done!&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [main @coroutine#1] Parent done!\n524 [main @coroutine#3] Child 2 done!\n1020 [main @coroutine#2] Child 1 done!\n1275 [main @coroutine#4] Grandchild done</code>\n        </deckgo-highlight-code>\n<ul>\n<li>모든 자식 코루틴이 완료될 때까지 프로그램이 종료되지 않는다.</li>\n</ul>\n<h3 id=\"-1511-코루틴-스코프-생성-coroutinescope-함수\" style=\"position:relative;\"><a href=\"#-1511-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%8A%A4%EC%BD%94%ED%94%84-%EC%83%9D%EC%84%B1-coroutinescope-%ED%95%A8%EC%88%98\" aria-label=\" 1511 코루틴 스코프 생성 coroutinescope 함수 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.1.1 코루틴 스코프 생성: coroutineScope 함수</h3>\n<ul>\n<li>코루틴 빌더를 사용해 새로운 코루틴을 만들면 이 코루틴은 자체적인 <code>CoroutineScope</code>를 생성한다.</li>\n<li><code>CoroutineScope</code> 함수의 전형적인 사용 사례는 동시적 작업 분해(여러 코루틴을 활용해 계산 수행)</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun generateValue(): Int {\n    delay(500.milliseconds)\n    return Random.nextInt(0, 10)\n}\n\nsuspend fun computeSum() {\n    log(&quot;Computing a sum...&quot;)\n    val sum = coroutineScope {\n        val a = async { generateValue() }\n        val b = async { generateValue() }\n        a.await() + b.await()\n    }\n    log(&quot;Sum is $sum&quot;)\n}</code>\n        </deckgo-highlight-code>\n<h3 id=\"-1512-코루틴-스코프를-컴포넌트와-연관시키기-coroutinescope\" style=\"position:relative;\"><a href=\"#-1512-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%8A%A4%EC%BD%94%ED%94%84%EB%A5%BC-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%99%80-%EC%97%B0%EA%B4%80%EC%8B%9C%ED%82%A4%EA%B8%B0-coroutinescope\" aria-label=\" 1512 코루틴 스코프를 컴포넌트와 연관시키기 coroutinescope permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.1.2 코루틴 스코프를 컴포넌트와 연관시키기: CoroutineScope</h3>\n<ul>\n<li><code>coroutineScope</code> 함수가 작업을 분해하는 데 사용되는 반면 구체적 생명주기를 정의하고, 동시 처리나 코루틴의 시작과 종료를 관리하는 클래스를 만들고 싶을 대도 잇다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class ComponentWithScope(dispatcher: CoroutineDispatcher = Dispatchers.Default) {\n    private val scope = CoroutineScope(dispatcher + SupervisorJob())\n\n    fun start() {\n        log(&quot;Starting&quot;)\n        scope.launch {\n            while (true) {\n                delay(500.milliseconds)\n                log(&quot;Component working!&quot;)\n            }\n        }\n        scope.launch {\n            log(&quot;Doing a one-off task...&quot;)\n            delay(500.milliseconds)\n            log(&quot;Task done!&quot;)\n        }\n    }\n\n    fun stop() {\n        log(&quot;Stopping!&quot;)\n        scope.cancel()\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 <code>Component</code> 클래스의 인스턴스를 생성하고 <code>start</code>를 호출하면 컴포넌트 내부에서 코루틴이 시작된다.</li>\n</ul>\n<h3 id=\"-1513-globalscope의-위험성\" style=\"position:relative;\"><a href=\"#-1513-globalscope%EC%9D%98-%EC%9C%84%ED%97%98%EC%84%B1\" aria-label=\" 1513 globalscope의 위험성 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.1.3 GlobalScope의 위험성</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    GlobalScope.launch {\n        delay(1000)\n        launch {\n            delay(250)\n            log(&quot;Grandchild done&quot;)\n        }\n        log(&quot;Child 1 done!&quot;)\n    }\n\n    GlobalScope.launch {\n        delay(500)\n        log(&quot;Child 2 done!&quot;)\n    }\n    log(&quot;Parent done!&quot;)\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [main @coroutine#1] Parent done!</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>GlobalScope</code>는 전역 수준에 존재하는 스코프</li>\n<li><code>GlobalScope</code>를 사용하면 구조화된 동시성이 제공하는 모든 이점을 포기</li>\n<li>자동취소 불가, 생명주기 개념 없음</li>\n</ul>\n<h3 id=\"-1514-코루틴-콘텍스트와-구조화된-동시성\" style=\"position:relative;\"><a href=\"#-1514-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%BD%98%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%99%80-%EA%B5%AC%EC%A1%B0%ED%99%94%EB%90%9C-%EB%8F%99%EC%8B%9C%EC%84%B1\" aria-label=\" 1514 코루틴 콘텍스트와 구조화된 동시성 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.1.4 코루틴 콘텍스트와 구조화된 동시성</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    runBlocking(Dispatchers.Default) {\n        log(coroutineContext)\n        launch {\n            log(coroutineContext)\n            launch(Dispatchers.IO + CoroutineName(&quot;mine&quot;)) {\n                log(coroutineContext)\n            }\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [DefaultDispatcher-worker-1 @coroutine#1] [CoroutineId(1), &quot;coroutine#1&quot;:BlockingCoroutine{Active}@3a617b0f, Dispatchers.Default]\n13 [DefaultDispatcher-worker-2 @coroutine#2] [CoroutineId(2), &quot;coroutine#2&quot;:StandaloneCoroutine{Active}@5e2dba97, Dispatchers.Default]\n14 [DefaultDispatcher-worker-3 @mine#3] [CoroutineName(mine), CoroutineId(3), &quot;mine#3&quot;:StandaloneCoroutine{Active}@12764f6a, Dispatchers.IO]</code>\n        </deckgo-highlight-code>\n<ul>\n<li>자식 코루틴은 부모의 콘텍스트 상속</li>\n<li>새로운 코루틴은 부모-자식 관계 설정하는 역할을 하는 새 Job 객체 생성</li>\n<li>디스패처를 지정하지 않고 새로운 코루틴을 시작하면 부모 코루틴의 디스패처에서 실행</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking(CoroutineName(&quot;A&quot;)) {\n    log(&quot;A&#39;s job: ${coroutineContext.job}&quot;)\n    launch(CoroutineName(&quot;B&quot;)) {\n        log(&quot;B&#39;s job: ${coroutineContext.job}&quot;)\n        log(&quot;B&#39;s parent: ${coroutineContext.job.parent}&quot;)\n    }\n    log(&quot;A&#39;s children: ${coroutineContext.job.children.toList()}&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코루틴 간의 부모-자식 관계를 확인할 수 있음</li>\n<li>구조화된 동시성에 의해 설정된 이 부모-자식 관계는 취소와도 연관이 있다.</li>\n</ul>\n<h2 id=\"-152-취소\" style=\"position:relative;\"><a href=\"#-152-%EC%B7%A8%EC%86%8C\" aria-label=\" 152 취소 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 15.2 취소</h2>\n<ul>\n<li>취소는 코드가 완료되기 전에 실행을 중단하는 것을 의미</li>\n<li>취소는 불필요한 작업을 막아준다.</li>\n<li>취소는 메모리나 리소스 누수 방지에 도움을 준다.</li>\n<li>취소는 오류 처리에서도 중요한 역할을 한다.</li>\n</ul>\n<h3 id=\"-1521-취소-촉발\" style=\"position:relative;\"><a href=\"#-1521-%EC%B7%A8%EC%86%8C-%EC%B4%89%EB%B0%9C\" aria-label=\" 1521 취소 촉발 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.1 취소 촉발</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    val launchedJob = launch {\n        log(&quot;I&#39;m launched!&quot;)\n        delay(1000.milliseconds)\n        log(&quot;I&#39;m done!&quot;)\n    }\n    val asyncDeferred = async {\n        log(&quot;I&#39;m async&quot;)\n        delay(1000.milliseconds)\n        log(&quot;I&#39;m done!&quot;)\n    }\n    delay(200.milliseconds)\n    launchedJob.cancel()\n    asyncDeferred.cancel()\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">0 [main @coroutine#2] I&#39;m launched!\n11 [main @coroutine#3] I&#39;m async</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>cancel</code>을 호출해 해당 코루틴의 취소를 촉발할 수 있다.</li>\n</ul>\n<h3 id=\"-1522-시간제한이-초과된-후-자동으로-취소-호출\" style=\"position:relative;\"><a href=\"#-1522-%EC%8B%9C%EA%B0%84%EC%A0%9C%ED%95%9C%EC%9D%B4-%EC%B4%88%EA%B3%BC%EB%90%9C-%ED%9B%84-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%B7%A8%EC%86%8C-%ED%98%B8%EC%B6%9C\" aria-label=\" 1522 시간제한이 초과된 후 자동으로 취소 호출 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.2 시간제한이 초과된 후 자동으로 취소 호출</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun calculateSomething(): Int {\n    delay(3.seconds)\n    return 2 + 2\n}\n\nfun main() = runBlocking {\n    val quickResult = withTimeoutOrNull(500.milliseconds) {\n        calculateSomething()\n    }\n    println(quickResult) // null\n    val slowResult = withTimeoutOrNull(5.seconds) {\n        calculateSomething()\n    }\n    println(slowResult) // 4\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>withTimeout</code>, <code>withTimeoutOrNull</code> 함수는 계산에 쓸 최대 시간을 제한하면서 값을 계산할 수 있게 해준다.</li>\n</ul>\n<h3 id=\"-1523-취소는-모든-자식-코루틴에게-전파된다\" style=\"position:relative;\"><a href=\"#-1523-%EC%B7%A8%EC%86%8C%EB%8A%94-%EB%AA%A8%EB%93%A0-%EC%9E%90%EC%8B%9D-%EC%BD%94%EB%A3%A8%ED%8B%B4%EC%97%90%EA%B2%8C-%EC%A0%84%ED%8C%8C%EB%90%9C%EB%8B%A4\" aria-label=\" 1523 취소는 모든 자식 코루틴에게 전파된다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.3 취소는 모든 자식 코루틴에게 전파된다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() = runBlocking {\n    val job = launch {\n        launch {\n            launch {\n                launch {\n                    log(&quot;I&#39;m started&quot;)\n                    delay(500.milliseconds)\n                    log(&quot;I&#39;m done!&quot;)\n                }\n            }\n        }\n    }\n    delay(200.milliseconds)\n    job.cancel()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코루틴을 취소하면 해당 코루틴의 모든 자식 코루틴도 자동으로 취소된다.</li>\n<li>여러 계층에 걸쳐 코루틴이 중첩돼 있는 경우에도 가장 바깥쪽 코루틴을 취소하면 고손자 코루틴까지 모두 적절히 취소된다.</li>\n</ul>\n<h3 id=\"-1524-취소된-코루틴은-특별한-지점에서-cancellationexception을-던진다\" style=\"position:relative;\"><a href=\"#-1524-%EC%B7%A8%EC%86%8C%EB%90%9C-%EC%BD%94%EB%A3%A8%ED%8B%B4%EC%9D%80-%ED%8A%B9%EB%B3%84%ED%95%9C-%EC%A7%80%EC%A0%90%EC%97%90%EC%84%9C-cancellationexception%EC%9D%84-%EB%8D%98%EC%A7%84%EB%8B%A4\" aria-label=\" 1524 취소된 코루틴은 특별한 지점에서 cancellationexception을 던진다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.4 취소된 코루틴은 특별한 지점에서 CancellationException을 던진다</h3>\n<ul>\n<li>취소 메커니즘은 <code>CancellationException</code>이라는 특수한 예외를 특별한 지점에서 던지는 방식으로 작동</li>\n<li>취소된 코루틴은 일시 중단 지점에서 <code>CancellationException</code>을 던진다.</li>\n<li>코루틴 계층에서 취소를 전파하기 때문에 이 예외를 직접 처리하지 않아야 한다.</li>\n</ul>\n<h3 id=\"-1525-취소는-협력적이다\" style=\"position:relative;\"><a href=\"#-1525-%EC%B7%A8%EC%86%8C%EB%8A%94-%ED%98%91%EB%A0%A5%EC%A0%81%EC%9D%B4%EB%8B%A4\" aria-label=\" 1525 취소는 협력적이다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.5 취소는 협력적이다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun doCpuHeavyWork(): Int {\n    log(&quot;I&#39;m doing work!&quot;)\n    var counter = 0\n    val startTime = System.currentTimeMillis()\n    while (System.currentTimeMillis() &lt; startTime + 500) {\n        counter++\n    }\n    return counter\n}\n\nfun main() = runBlocking {\n    val myJob = launch {\n        repeat(5) {\n            doCpuHeavyWork()\n        }\n    }\n    delay(600.milliseconds)\n    myJob.cancel()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>프로그램이 종료되기 전에 <code>doCpuHeavyWork</code>가 5번 완료된다.</li>\n<li><code>doCpuHeavyWork</code> 함수는 일시 중단 지점을 포함하지 않는다.</li>\n<li>코틀린 코루틴의 취소가 협력적인 이유는 결국 스스로 취소 가능하게 로직을 제공해야 하기 때문\n<ul>\n<li><code>delay</code> 호출을 추가하면 됨.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1526-코루틴이-취소됐는지-확인\" style=\"position:relative;\"><a href=\"#-1526-%EC%BD%94%EB%A3%A8%ED%8B%B4%EC%9D%B4-%EC%B7%A8%EC%86%8C%EB%90%90%EB%8A%94%EC%A7%80-%ED%99%95%EC%9D%B8\" aria-label=\" 1526 코루틴이 취소됐는지 확인 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.6 코루틴이 취소됐는지 확인</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val myJob = launch {\n    repeat(5) {\n        doCpuHeavyWork()\n        if (!isActive) return@launch\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코루틴이 취소됐는지 확인할 때는 <code>isActive</code> 속성을 확인\n<ul>\n<li><code>false</code>이면 비활성화</li>\n</ul>\n</li>\n<li><code>ensureActive</code> 함수는 비활성화일 때, <code>CancellationException</code>을 던진다.</li>\n</ul>\n<h3 id=\"-1527-다른-코루틴에게-기회를-주기-yield-함수\" style=\"position:relative;\"><a href=\"#-1527-%EB%8B%A4%EB%A5%B8-%EC%BD%94%EB%A3%A8%ED%8B%B4%EC%97%90%EA%B2%8C-%EA%B8%B0%ED%9A%8C%EB%A5%BC-%EC%A3%BC%EA%B8%B0-yield-%ED%95%A8%EC%88%98\" aria-label=\" 1527 다른 코루틴에게 기회를 주기 yield 함수 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.7 다른 코루틴에게 기회를 주기: yield 함수</h3>\n<ul>\n<li>코루틴 라이브러리는 <code>yield</code> 함수 제공\n<ul>\n<li>취소 가능 지점 제공</li>\n<li>점유된 디스패처에서 다른 코루틴이 작업할 수 있게 해줌</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun doCpuHeavyWork(): Int {\n    var counter = 0\n    val startTime = System.currentTimeMillis()\n    while (System.currentTimeMillis() &lt; startTime + 500) {\n        counter++\n    }\n    return counter\n}\n\nfun main() {\n    runBlocking {\n        launch {\n            repeat(3) {\n                doCpuHeavyWork()\n            }\n        }\n        launch {\n            repeat(3) {\n                doCpuHeavyWork()\n            }\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>첫 번째 코루틴이 완료될 때까지 두 번째 코루틴은 실행되지 않음.</li>\n<li>일시 중단 지점이 없기 때문</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">suspend fun doCpuHeavyWork(): Int {\n    var counter = 0\n    val startTime = System.currentTimeMillis()\n    while (System.currentTimeMillis() &lt; startTime + 500) {\n        counter++\n        yield()\n    }\n    return counter\n}\n\nfun main() {\n    runBlocking {\n        launch {\n            repeat(3) {\n                doCpuHeavyWork()\n            }\n        }\n        launch {\n            repeat(3) {\n                doCpuHeavyWork()\n            }\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>yield</code> 함수를 사용하면 코루틴이 교차 실행됨</li>\n</ul>\n<h3 id=\"-1528-리소스를-얻을-때-취소를-염두에-두기\" style=\"position:relative;\"><a href=\"#-1528-%EB%A6%AC%EC%86%8C%EC%8A%A4%EB%A5%BC-%EC%96%BB%EC%9D%84-%EB%95%8C-%EC%B7%A8%EC%86%8C%EB%A5%BC-%EC%97%BC%EB%91%90%EC%97%90-%EB%91%90%EA%B8%B0\" aria-label=\" 1528 리소스를 얻을 때 취소를 염두에 두기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.8 리소스를 얻을 때 취소를 염두에 두기</h3>\n<ul>\n<li>취소 후, <code>close</code> 함수가 호출되지 않고 리소스가 누수될 수 있음.</li>\n<li><code>finally</code> 블록을 사용해 명시적으로 닫자.</li>\n<li>리소스가 <code>AutoClosable</code> 인터페이스를 구현하는 경우 <code>.use</code> 함수를 사용해 같은 동작을 더 간결하게 처리할 수 있다.</li>\n</ul>\n<h3 id=\"-1529-프레임워크가-여러분-대신-취소를-할-수-있다\" style=\"position:relative;\"><a href=\"#-1529-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EA%B0%80-%EC%97%AC%EB%9F%AC%EB%B6%84-%EB%8C%80%EC%8B%A0-%EC%B7%A8%EC%86%8C%EB%A5%BC-%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4\" aria-label=\" 1529 프레임워크가 여러분 대신 취소를 할 수 있다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 15.2.9 프레임워크가 여러분 대신 취소를 할 수 있다</h3>\n<ul>\n<li>많은 실제 어플리케이션에서는 프레임워크가 코루틴 스코프를 제공하고 자동취소한다.</li>\n<li>사용자는 적절한 코루틴 스코프를 선택</li>\n</ul>","excerpt":"📖 15.1 코루틴 스코프가 코루틴 간의 구조를 확립한다 구조화된 동시성을 통해 각 코루틴은 코루틴 스코프에 속하게 된다. 다른 코루틴 빌더의 본문에서 launch나 async를 사용해 새로운 코루틴을 만들면 이 새로운 코루틴은 자동으로 해당 코루틴의 자식이 된다. 모든 자식 코루틴이 완료될 때까지 프로그램이 종료되지 않는다. 🔖 15.1.1 코루틴 스코프 생성: coroutineScope 함수 코루틴 빌더를 사용해 새로운 코루틴을 만들면 이 코루틴은 자체적인 CoroutineScope…","fields":{"slug":"/backend/kotlin-in-action/15장-구조화된_동시성/"},"frontmatter":{"title":"Kotlin in Action - 15장 구조화된 동시성","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"June 15, 2025"}}}},"staticQueryHashes":["2374173507","2996537568","3691437124"]}