{"componentChunkName":"component---src-containers-post-index-tsx","path":"/backend/kotlin-in-action/14장-코루틴/","result":{"pageContext":{"next":{"id":"f39be5ca-ac02-581d-bfd8-ddc93df57a37","html":"<h2 id=\"-131-api에서-dsl로-표현력이-좋은-커스텀-코드-구조-만들기\" style=\"position:relative;\"><a href=\"#-131-api%EC%97%90%EC%84%9C-dsl%EB%A1%9C-%ED%91%9C%ED%98%84%EB%A0%A5%EC%9D%B4-%EC%A2%8B%EC%9D%80-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%BD%94%EB%93%9C-%EA%B5%AC%EC%A1%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0\" aria-label=\" 131 api에서 dsl로 표현력이 좋은 커스텀 코드 구조 만들기 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>📖 13.1 API에서 DSL로: 표현력이 좋은 커스텀 코드 구조 만들기</h2>\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>StringUtil.capitalizes(s)</code></td>\n<td><code>s.capitalize()</code></td>\n<td>확장 함수</td>\n</tr>\n<tr>\n<td><code>1.to(\"one\")</code></td>\n<td><code>1 to \"one\"</code></td>\n<td>중위 호출</td>\n</tr>\n<tr>\n<td><code>set.add(2)</code></td>\n<td><code>set += 2</code></td>\n<td>연산자 오버로딩</td>\n</tr>\n<tr>\n<td><code>map.get(\"key\")</code></td>\n<td><code>map[\"key\"]</code></td>\n<td><code>get</code> 메소드에 대한 관례</td>\n</tr>\n<tr>\n<td><code>file.use({ f -> f.read() })</code></td>\n<td><code>file.use { it.read() }</code></td>\n<td>람다를 괄호 밖으로 빼내는 관례</td>\n</tr>\n<tr>\n<td><code>sb.append(\"yes\")</code></td>\n<td><code>with(sb) { append(\"yes\") append(\"no\") }</code></td>\n<td>수신 객체 지정 람다</td>\n</tr>\n</tbody>\n</table>\n<ul>\n<li>깔끔한 API를 작성할 수 있게 돕는 코틀린 기능에는 위와 같은 기능들이 있다.</li>\n</ul>\n<h3 id=\"-1311-도메인-특화-언어\" style=\"position:relative;\"><a href=\"#-1311-%EB%8F%84%EB%A9%94%EC%9D%B8-%ED%8A%B9%ED%99%94-%EC%96%B8%EC%96%B4\" aria-label=\" 1311 도메인 특화 언어 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>🔖 13.1.1 도메인 특화 언어</h3>\n<ul>\n<li>가장 익숙한 DSL은 SQL과 정규식일 것이다.</li>\n<li>DSL이 범용 프로그래밍 언어와 달리 더 선언적이다.\n<ul>\n<li>범용 프로그래밍 언어는 명령적이다.</li>\n</ul>\n</li>\n<li>선언적 언어는 원하는 결과를 기술하기만 하고 그 결과를 달성하기 위해 필요한 세부 실행은 언어를 해석하는 엔진에 맡긴다.</li>\n<li>DSL의 가장 큰 단점은 범용 언어로 만든 호스트 애플리케이션과 DSL을 함께 조합하기가 어렵다는 것\n<ul>\n<li>DSL은 자체 문법이 있기 때문에 다른 언어의 프로그램 안에 직접 포함시킬 수가 없다.</li>\n</ul>\n</li>\n<li>이러한 단점을 해결하면서 DSL의 다른 이점을 살리는 방법으로 코틀린에서는 내부 DSL을 만들 수 있게 해준다.</li>\n</ul>\n<h3 id=\"-1312-내부-dsl은-프로그램의-나머지-부분과-매끄럽게-통합된다\" style=\"position:relative;\"><a href=\"#-1312-%EB%82%B4%EB%B6%80-dsl%EC%9D%80-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%98-%EB%82%98%EB%A8%B8%EC%A7%80-%EB%B6%80%EB%B6%84%EA%B3%BC-%EB%A7%A4%EB%81%84%EB%9F%BD%EA%B2%8C-%ED%86%B5%ED%95%A9%EB%90%9C%EB%8B%A4\" aria-label=\" 1312 내부 dsl은 프로그램의 나머지 부분과 매끄럽게 통합된다 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>🔖 13.1.2 내부 DSL은 프로그램의 나머지 부분과 매끄럽게 통합된다</h3>\n<ul>\n<li>독립적인 문법 구조를 갖는 외부 DSL과는 반대로 내부 DSL은 범용 언어로 작성된 프로그램의 일부며, 범용 언어와 동일한 문법을 사용한다.</li>\n</ul>\n<deckgo-highlight-code language=\"sql\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">SELECT country.name, COUNT(customer.id)\nFROM country\n         JOIN customer ON customer.country_id = country.id\nGROUP BY country.name\nORDER BY COUNT(customer.id) DESC LIMIT 1;</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\">(Country innerJoin Customer)\n    .slice(Country.name, Count(Customer.id))\n    .selectAll()\n    .groupBy(Country.name)\n    .orderBy(Count(Customer.id), order = SortOrder.DESC)\n    .limit(1)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이를 내부 DSL이라고 부른다.</li>\n</ul>\n<h3 id=\"-1313-dsl의-구조\" style=\"position:relative;\"><a href=\"#-1313-dsl%EC%9D%98-%EA%B5%AC%EC%A1%B0\" aria-label=\" 1313 dsl의 구조 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>🔖 13.1.3 DSL의 구조</h3>\n<ul>\n<li>DSL에만 존재하는 특징은 구조(문법)이다.</li>\n<li>일반적인 API는 명령-질의 API이다.</li>\n<li>DSL은 문법에 의해 정해진다.\n<ul>\n<li>이러한 문법 때문에 내부 DSL을 언어라고 부를 수 있다.</li>\n<li>여러 함수 호출을 조합해서 연산을 만들며 타입 검사기는 여러 함수 호출이 바르게 조합됐는지를 검사한다.</li>\n</ul>\n</li>\n<li>메서드 호출 연쇄는 DSL 구조를 만드는 또 다른 방법이다.</li>\n</ul>\n<h3 id=\"-1314-내부-dsl로-html-만들기\" style=\"position:relative;\"><a href=\"#-1314-%EB%82%B4%EB%B6%80-dsl%EB%A1%9C-html-%EB%A7%8C%EB%93%A4%EA%B8%B0\" aria-label=\" 1314 내부 dsl로 html 만들기 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>🔖 13.1.4 내부 DSL로 HTML 만들기</h3>\n<ul>\n<li><code>kotlinx.html</code>로 HTML을 만들 수 있다.</li>\n<li>직접 HTML 텍스트를 작성하지 않고 코틀린 코드로 HTML을 만들면 타입 안전성을 보장한다.</li>\n<li>내부에 코틀린 코드를 원하는 대로 사용할 수 있다.</li>\n<li>HTML은 고전적인 마크업 언어 예제이며 DSL의 개념을 제대로 보여준다.</li>\n<li>XML과 같이 HTML과 구조가 비슷한 다른 모든 언어에 이와 비슷한 접근 방식을 택할 수 있다.</li>\n</ul>\n<h2 id=\"-132-구조화된-api-구축-dsl에서-수신-객체-지정-람다-사용\" style=\"position:relative;\"><a href=\"#-132-%EA%B5%AC%EC%A1%B0%ED%99%94%EB%90%9C-api-%EA%B5%AC%EC%B6%95-dsl%EC%97%90%EC%84%9C-%EC%88%98%EC%8B%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%EC%A0%95-%EB%9E%8C%EB%8B%A4-%EC%82%AC%EC%9A%A9\" aria-label=\" 132 구조화된 api 구축 dsl에서 수신 객체 지정 람다 사용 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>📖 13.2 구조화된 API 구축: DSL에서 수신 객체 지정 람다 사용</h2>\n<h3 id=\"-1321-수신-객체-지정-람다와-확장-함수-타입\" style=\"position:relative;\"><a href=\"#-1321-%EC%88%98%EC%8B%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%EC%A0%95-%EB%9E%8C%EB%8B%A4%EC%99%80-%ED%99%95%EC%9E%A5-%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85\" aria-label=\" 1321 수신 객체 지정 람다와 확장 함수 타입 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>🔖 13.2.1 수신 객체 지정 람다와 확장 함수 타입</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun buildString(\n    builderAction: (StringBuilder) -&gt; Unit\n): String {\n    val sb = StringBuilder()\n    builderAction(sb)\n    return sb.toString()\n}\n\nfun main() {\n    val s = buildString {\n        it.append(&quot;Hello, &quot;)\n        it.append(&quot;World!&quot;)\n    }\n    println(s)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>람다를 인자로 받는 <code>buildString</code> 정의</li>\n<li>람다 본문에서 매번 it을 사용해 <code>StringBuilder</code> 인스턴스를 참조해야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun buildString(\n    builderAction: StringBuilder.() -&gt; Unit\n): String {\n    val sb = StringBuilder()\n    sb.builderAction()\n    return sb.toString()\n}\n\nfun main() {\n    val s = buildString {\n        this.append(&quot;Hello, &quot;)\n        this.append(&quot;World!&quot;)\n    }\n    println(s)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>수신 객체 지정 람다를 인자로 넘기기 때문에 람다 안에서 it을 사용하지 않아도 된다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val appendExcl: StringBuilder.() -&gt; Unit =\n    { this.append(&quot;!&quot;) }\n\n\nfun main() {\n    val stringBuilder = StringBuilder(&quot;Hi&quot;)\n    stringBuilder.appendExcl()\n    println(stringBuilder)\n    println(buildString(appendExcl))\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 builderString(builderAction: StringBuilder.() -&gt; Unit): String =\n    StringBuilder().apply(builderAction).toString()</code>\n        </deckgo-highlight-code>\n<ul>\n<li>표준 라이브러리의 <code>builderString</code> 구현은 더 짧다.</li>\n<li>기본적으로 <code>apply</code>와 <code>with</code>는 모두 자신이 제공받은 수신 객체를 갖고 확장 함수 타입의 람다를 호출</li>\n</ul>\n<h3 id=\"-1322-수신-객체-지정-람다를-html-빌더-안에서-사용\" style=\"position:relative;\"><a href=\"#-1322-%EC%88%98%EC%8B%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%EC%A0%95-%EB%9E%8C%EB%8B%A4%EB%A5%BC-html-%EB%B9%8C%EB%8D%94-%EC%95%88%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9\" aria-label=\" 1322 수신 객체 지정 람다를 html 빌더 안에서 사용 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>🔖 13.2.2 수신 객체 지정 람다를 HTML 빌더 안에서 사용</h3>\n<ul>\n<li>HTML을 만들기 위한 코틀린 DSL을 보통은 HTML 빌더라고 부른다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun createSimpleTable() = createHTML().table {\n    tr {\n        td { +&quot;cell&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\">open class Tag\n\nclass TABLE : Tag {\n    fun tr(init: TR.() -&gt; Unit)\n}\n\nclass TR : Tag {\n    fun td(init: TD.() -&gt; Unit)\n}\n\nclass TD : Tag</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 createSimpleTable() = createHTML().table {\n    this@table.tr {\n        (this@tr).td {\n            +&quot;cell&quot;\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>this 참조를 쓰지 않아도 되면 빌더 문법이 간단해지고 전체적인 구문이 원래의 HTML 구문과 비슷해진다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@DslMarker\nannotation class HtmlTagMarker</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@DslMarker</code> 어노테이션을 사용해 내포된 람다에서 외부 람다의 수신 객체에 접근하지 못하게 제한할 수 있다.</li>\n<li>메타 어노테이션이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@DslMarker\nannotation class HtmlTagMarker\n\n@HtmlTagMarker\nopen class Tag(val name: String) {\n    private val children = mutableListOf&lt;Tag&gt;()\n\n    protected fun &lt;T : Tag&gt; doInit(child: T, init: T.() -&gt; Unit) {\n        child.init()\n        children.add(child)\n    }\n\n    override fun toString() = &quot;&lt;$name&gt;${children.joinToString(&quot;&quot;)}&lt;/$name&gt;&quot;\n}\n\nfun table(init: TABLE.() -&gt; Unit) = TABLE().apply(init)\n\nclass TABLE : Tag(&quot;table&quot;) {\n    fun tr(init: TR.() -&gt; Unit) = doInit(TR(), init)\n}\n\nclass TR : Tag(&quot;tr&quot;) {\n    fun td(init: TD.() -&gt; Unit) = doInit(TD(), init)\n}\n\nclass TD : Tag(&quot;td&quot;)\n\nfun createTable() = table {\n    tr {\n        td {\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>간단한 HTML 빌더의 전체 구현</li>\n</ul>\n<h3 id=\"-1323-코틀린-빌더-추상화와-재사용을-가능하게-해준다\" style=\"position:relative;\"><a href=\"#-1323-%EC%BD%94%ED%8B%80%EB%A6%B0-%EB%B9%8C%EB%8D%94-%EC%B6%94%EC%83%81%ED%99%94%EC%99%80-%EC%9E%AC%EC%82%AC%EC%9A%A9%EC%9D%84-%EA%B0%80%EB%8A%A5%ED%95%98%EA%B2%8C-%ED%95%B4%EC%A4%80%EB%8B%A4\" aria-label=\" 1323 코틀린 빌더 추상화와 재사용을 가능하게 해준다 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>🔖 13.2.3 코틀린 빌더: 추상화와 재사용을 가능하게 해준다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun buildBookList() = createHTML().body {\n    ul {\n        li { a(&quot;#1&quot;) { +&quot;The Three-Body Problem&quot; } }\n        li { a(&quot;#2&quot;) { +&quot;The Cartesian Product Problem&quot; } }\n        li { a(&quot;#3&quot;) { +&quot;The Conjugation Problem&quot; } }\n    }\n\n    h2 { id = &quot;1&quot;; +&quot;The Three-Body Problem&quot; }\n    p { +&quot;The Three-Body Problem is a classic physics problem from the early 20th century.&quot; }\n\n    h2 { id = &quot;2&quot;; +&quot;The Cartesian Product Problem&quot; }\n    p { +&quot;The Cartesian product is a useful mathematical construct for a wide variety of problems.&quot; }\n\n    h2 { id = &quot;3&quot;; +&quot;The Conjugation Problem&quot; }\n    p { +&quot;The Conjugation Problem is a classic computational problem from the 1970s.&quot; }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>목차로 시작하는 페이지를 코틀린 HTML 빌더로 만들기</li>\n<li>이걸 좀 더 이쁘게 다듬을 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun buildBookList() = createHTML().body {\n    listWithToc {\n        item(\n            &quot;The Three-Body Problem&quot;,\n            &quot;The Three-Body Problem is a classic physics problem from the early 20th century.&quot;\n        )\n        item(\n            &quot;The Cartesian Product Problem&quot;,\n            &quot;The Cartesian product is a useful mathematical construct for a wide variety of problems.&quot;\n        )\n        item(\n            &quot;The Conjugation Problem&quot;,\n            &quot;The Conjugation Problem is a classic computational problem from the 1970s.&quot;\n        )\n\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@HtmlTagMarker\nclass LISTWITHTOC {\n    val entries = mutableListOf&lt;Pair&lt;String, String&gt;&gt;()\n    fun item(headline: String, body: String) {\n        entries += headline to body\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@HtmlTagMarker</code> 어노테이션을 붙여 DSL 영역 규칙을 따르게 할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun BODY.listWithToc(block: LISTWITHTOC.() -&gt; Unit) {\n    val listWithToc = LISTWITHTOC()\n    listWithToc.block()\n    ul {\n        for ((index, entry) in listWithToc.entries.withIndex()) {\n            li {\n                a(&quot;#${index}&quot;) { +entry.first }\n            }\n        }\n    }\n    for ((index, entry) in listWithToc.entries.withIndex()) {\n        h2 { id = &quot;$index&quot;; +entry.first }\n        p { +entry.second }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>추상화와 재사용을 통해 코드를 개선하고 이해하기 쉽게 만드는 방법이다.</li>\n</ul>\n<h2 id=\"-133-invoke-관례를-사용해-더-유연하게-블록-내포시키기\" style=\"position:relative;\"><a href=\"#-133-invoke-%EA%B4%80%EB%A1%80%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%8D%94-%EC%9C%A0%EC%97%B0%ED%95%98%EA%B2%8C-%EB%B8%94%EB%A1%9D-%EB%82%B4%ED%8F%AC%EC%8B%9C%ED%82%A4%EA%B8%B0\" aria-label=\" 133 invoke 관례를 사용해 더 유연하게 블록 내포시키기 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>📖 13.3 invoke 관례를 사용해 더 유연하게 블록 내포시키기</h2>\n<h3 id=\"-1331-invoke-관례를-사용해-더-유연하게-블록-내포시키기\" style=\"position:relative;\"><a href=\"#-1331-invoke-%EA%B4%80%EB%A1%80%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%8D%94-%EC%9C%A0%EC%97%B0%ED%95%98%EA%B2%8C-%EB%B8%94%EB%A1%9D-%EB%82%B4%ED%8F%AC%EC%8B%9C%ED%82%A4%EA%B8%B0\" aria-label=\" 1331 invoke 관례를 사용해 더 유연하게 블록 내포시키기 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>🔖 13.3.1 invoke 관례를 사용해 더 유연하게 블록 내포시키기</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Greeter(val greeting: String) {\n    operator fun invoke(name: String) {\n        println(&quot;$greeting, $name!&quot;)\n    }\n}\n\nfun main() {\n  val bavarianGreeter = Greeter(&quot;Servus&quot;)\n  bavarianGreeter(&quot;Dmitry&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>클래스 안에서 <code>invoke</code> 메소드 정의</li>\n<li><code>invoke</code> 메서드의 시그니처에 대한 요구사항은 없다.</li>\n<li>원하는대로 파라미터 개수나 타입을 지정할 수 있다.</li>\n</ul>\n<h3 id=\"-1332-dsl의-invoke-관례-그레이들-의존관계-선언\" style=\"position:relative;\"><a href=\"#-1332-dsl%EC%9D%98-invoke-%EA%B4%80%EB%A1%80-%EA%B7%B8%EB%A0%88%EC%9D%B4%EB%93%A4-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%84%A0%EC%96%B8\" aria-label=\" 1332 dsl의 invoke 관례 그레이들 의존관계 선언 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>🔖 13.3.2 DSL의 invoke 관례: 그레이들 의존관계 선언</h3>\n<deckgo-highlight-code language=\"groovy\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">dependencies {\n  testImplementation(kotlin(&quot;test&quot;))\n  implementation(&quot;org.jetbrains.exposed:exposed-core:0.40.1&quot;)\n  implementation(&quot;org.jetbrains.exposed:exposed-dao:0.40.1&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 코드처럼 내포된 블록 구조를 허용하는 한편, 평평한 함수 호출 구조도 함께 제공하는 API를 만들고 싶다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">dependencies.implementation(&quot;org.jetbrains.exposed:exposed-core:0.40.1&quot;)\n\ndependencies {\n  implementation(&quot;org.jetbrains.exposed:exposed-core:0.40.1&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>첫 번째 구문은 <code>invoke</code>를 사용한 것이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class DependencyHandler {\n    fun implementation(coordinate: String) {\n        println(&quot;Added dependency on $coordinate&quot;)\n    }\n\n    operator fun invoke(body: DependencyHandler.() -&gt; Unit) {\n        body()\n    }\n}\n\nfun main() {\n    val dependencies = DependencyHandler()\n    dependencies.implementation(&quot;org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0&quot;)\n    dependencies {\n        implementation(&quot;org.jetbrains.kotlinx:kotlinx-datetime:0.5.0&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>유연한 DSL 문법을 제공하기 위해 <code>invoke</code> 사용</li>\n<li>꽤 적은 양의 코드지만 이렇게 재정의한 <code>invoke</code> 메서드로 인해 DSL API의 유연성이 훨씬 커진다.</li>\n</ul>\n<h2 id=\"-134-실전-코틀린-dsl\" style=\"position:relative;\"><a href=\"#-134-%EC%8B%A4%EC%A0%84-%EC%BD%94%ED%8B%80%EB%A6%B0-dsl\" aria-label=\" 134 실전 코틀린 dsl 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>📖 13.4 실전 코틀린 DSL</h2>\n<h3 id=\"-1341-중위-호출-연쇄시키기-테스트-프레임워크의-should-함수\" style=\"position:relative;\"><a href=\"#-1341-%EC%A4%91%EC%9C%84-%ED%98%B8%EC%B6%9C-%EC%97%B0%EC%87%84%EC%8B%9C%ED%82%A4%EA%B8%B0-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EC%9D%98-should-%ED%95%A8%EC%88%98\" aria-label=\" 1341 중위 호출 연쇄시키기 테스트 프레임워크의 should 함수 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>🔖 13.4.1 중위 호출 연쇄시키기: 테스트 프레임워크의 should 함수</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class PrefixTest {\n    @Test\n    fun testKPrefix() {\n        val s = &quot;kotlin&quot;.uppercase()\n        s should startWith(&quot;K&quot;)\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\">infix fun &lt;T&gt; T.should(matcher: Matcher&lt;T&gt;) = matcher.test(this)\n\ninterface Matcher&lt;T&gt; {\n  fun test(value: T)\n}\n\nfun startWith(prefix: String): Matcher&lt;String&gt; {\n  return object : Matcher&lt;String&gt; {\n    override fun test(value: String) {\n      if (!value.startsWith(prefix)) {\n        throw AssertionError(&quot;$value does not start with $prefix&quot;)\n      }\n    }\n  }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>DSL 에서 사용하려면 infix 변경자를 붙여야 한다.</li>\n<li>중위 호출과 object로 정의한 싱글턴 객체 인스턴스를 조합하면 DSL에 상당히 복잡한 문법을 도입할 수 있고, 그 문법을 사용하면 DSL 구문을 깔끔하게 만들 수 있다.</li>\n</ul>\n<h3 id=\"-1342-원시-타입에-대해-확장-함수-정의하기-날짜-처리\" style=\"position:relative;\"><a href=\"#-1342-%EC%9B%90%EC%8B%9C-%ED%83%80%EC%9E%85%EC%97%90-%EB%8C%80%ED%95%B4-%ED%99%95%EC%9E%A5-%ED%95%A8%EC%88%98-%EC%A0%95%EC%9D%98%ED%95%98%EA%B8%B0-%EB%82%A0%EC%A7%9C-%EC%B2%98%EB%A6%AC\" aria-label=\" 1342 원시 타입에 대해 확장 함수 정의하기 날짜 처리 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>🔖 13.4.2 원시 타입에 대해 확장 함수 정의하기: 날짜 처리</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val Int.days: Duration\n    get() = this.toDuration(DurationUnit.DAYS)\n\nval Int.hours: Duration\n    get() = this.toDuration(DurationUnit.HOURS)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코틀린에서는 아무 타입이나 확장 함수의 수신 객체 타입이 될 수 있다.</li>\n</ul>\n<h3 id=\"-1343-멤버-확장-함수-sql을-위한-내부-dsl\" style=\"position:relative;\"><a href=\"#-1343-%EB%A9%A4%EB%B2%84-%ED%99%95%EC%9E%A5-%ED%95%A8%EC%88%98-sql%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%82%B4%EB%B6%80-dsl\" aria-label=\" 1343 멤버 확장 함수 sql을 위한 내부 dsl 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>🔖 13.4.3 멤버 확장 함수: SQL을 위한 내부 DSL</h3>\n<ul>\n<li>클래스 안에서 확장 함수와 확장 프로퍼티를 선언하면 그들이 선언된 클래스의 멤버인 동시에 그들이 확장하는 다른 타입의 멤버이기도 하다.\n<ul>\n<li>이런 함수나 프로퍼티를 멤버 확장이라 부른다.</li>\n</ul>\n</li>\n<li>멤버 확장도 여전히 멤버다.</li>\n<li>예를 들어, <code>Table</code> 클래스의 <code>integer</code>, <code>varchar</code> 등의 메서드는 외부에서 호출할 수 없지만, 멤버 확장 함수를 통해 제한된 범위 내에서만 사용하도록 할 수 있다.</li>\n</ul>","excerpt":"📖 13.1 API에서 DSL로: 표현력이 좋은 커스텀 코드 구조 만들기 일반 구문 간결한 구문 사용한 언어 특성 StringUtil.capitalizes(s) s.capitalize() 확장 함수 1.to(\"one\") 1 to \"one\" 중위 호출 set.add(2) set += 2 연산자 오버로딩 map.get(\"key\") map[\"key\"] get 메소드에 대한 관례 file.use({ f -> f.read() }) file.use { it.read…","fields":{"slug":"/backend/kotlin-in-action/13장-DSL_만들기/"},"frontmatter":{"title":"Kotlin in Action - 13장 DSL 만들기","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"June 01, 2025"}},"previous":{"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"}},"node":{"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"}}}},"staticQueryHashes":["2374173507","2996537568","3691437124"]}