{"componentChunkName":"component---src-containers-post-index-tsx","path":"/backend/kotlin-in-action/13장-DSL_만들기/","result":{"pageContext":{"next":{"id":"9b2d33a9-b5b2-5ac0-8299-1e8a5e308b54","html":"<h2 id=\"-121-어노테이션-선언과-적용\" style=\"position:relative;\"><a href=\"#-121-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%84%A0%EC%96%B8%EA%B3%BC-%EC%A0%81%EC%9A%A9\" aria-label=\" 121 어노테이션 선언과 적용 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>📖 12.1 어노테이션 선언과 적용</h2>\n<h3 id=\"-1211-어노테이션을-적용해-선언해-표지-남기기\" style=\"position:relative;\"><a href=\"#-1211-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%B4-%EC%84%A0%EC%96%B8%ED%95%B4-%ED%91%9C%EC%A7%80-%EB%82%A8%EA%B8%B0%EA%B8%B0\" aria-label=\" 1211 어노테이션을 적용해 선언해 표지 남기기 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>🔖 12.1.1 어노테이션을 적용해 선언해 표지 남기기</h3>\n<ul>\n<li>코틀린에서 어노테이션을 적용하려면 @와 어노테이션 이름을 선언 앞에 넣으면 된다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Deprecated(&quot;Use removeAt(index) instead.&quot;, ReplaceWith(&quot;removeAt(index)&quot;))\nfun remove(index: Int) {\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@Deprecated</code> 어노테이션은 최대 3가지 파라미터를 받는다.\n<ul>\n<li><code>message</code>: 사용중단 예고 이유</li>\n<li><code>replaceWith</code>: 옛 버전을 대신할 수 있는 패턴 제시</li>\n<li><code>level</code>: 점진적인 사용 중단을 지원 제공\n<ul>\n<li><code>WARNING</code>: 경고만 함</li>\n<li><code>ERROR</code>: 컴파일되지 못하게 막음.</li>\n<li><code>HIDDEN</code>: 컴파일되지 못하게 막으며, 예전에 컴파일된 코드와의 이진호환성만 유지</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>코틀린에서 어노테이션 인자를 지정하는 문법은 자바와 약간 다르다.\n<ul>\n<li>클래스를 어노테이션 인자로 지정: <code>::class</code>를 클래스 이름 뒤에 넣어야 한다.</li>\n<li>다른 어노테이션을 인자로 지정: 인자로 들어가는 어노테이션의 이름 앞에 <code>@</code>를 넣지 말라.</li>\n<li>배열을 인자로 지정: 각괄호(<code>[]</code>)를 사용, <code>arrayOf</code> 함수 사용</li>\n</ul>\n</li>\n<li>어노테이션 인자를 컴파일 시점에 알 수 있어야 한다.\n<ul>\n<li>즉, 임의의 프로퍼티를 인자로 지정할 수는 없다.</li>\n<li>프로퍼티를 인자로 사용하려면 const 변경자를 붙여야 한다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1212-어노테이션이-참조할-수-있는-정확한-선언-지정-어노테이션-타깃\" style=\"position:relative;\"><a href=\"#-1212-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%B4-%EC%B0%B8%EC%A1%B0%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%A0%95%ED%99%95%ED%95%9C-%EC%84%A0%EC%96%B8-%EC%A7%80%EC%A0%95-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%ED%83%80%EA%B9%83\" aria-label=\" 1212 어노테이션이 참조할 수 있는 정확한 선언 지정 어노테이션 타깃 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>🔖 12.1.2 어노테이션이 참조할 수 있는 정확한 선언 지정: 어노테이션 타깃</h3>\n<ul>\n<li><strong>사용 지점 타깃</strong> 선언은 통해 어노테이션을 붙일 요소를 정할 수 있다.\n<ul>\n<li>@ 기호와 어노테이션 이름 사이에 붙으며 어노테이션 이름과는 콜론으로 분리된다.</li>\n<li><code>@get:JvmName(\"obtainCertificate\")</code></li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@JvmName(&quot;performCalculation&quot;)\nfun calculate(): Int {\n    return (2 + 2) - 1\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>calculate</code> 함수를 자바 쪽에서 <code>performCalculation()</code>로 호출하게 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class CertificateManager {\n    @get:JvmName(&quot;obtainCertificate&quot;)\n    @set:JvmName(&quot;putCertificate&quot;)\n    var certificate: String = &quot;-----BEGIN CERTIFICATE-----&quot;\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>certificate</code> 프로퍼티를 <code>obtainCertificate</code>, <code>putCertificate</code> 라는 이름으로 사용할 수 있다.</li>\n</ul>\n<p>사용 지점 타깃을 지정할 때 지원하는 타깃 목록</p>\n<ul>\n<li>property: 프로퍼티 전체</li>\n<li>field: 프로퍼티에 의해 생성되는 필드</li>\n<li>get: 프로퍼티 게터</li>\n<li>set: 프로퍼티 세터</li>\n<li>receiver: 확장 함수나 프로퍼티의 수신 객체 파라미터</li>\n<li>param: 생성자 파라미터</li>\n<li>setparam: 세터 파라미터</li>\n<li>delegate: 위임 프로퍼티의 위임 인스턴스를 담아둔 필드</li>\n<li>file: 파일 안에 선언된 최상위 함수와 프로퍼티를 담아두는 클래스</li>\n</ul>\n<p>코틀린에는 컴파일러 경고를 무시하기 위한 <code>@Suppress</code> 어노테이션이 있다.</p>\n<h3 id=\"-1213-어노테이션을-활용해-json-직렬화-제어\" style=\"position:relative;\"><a href=\"#-1213-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%B4-json-%EC%A7%81%EB%A0%AC%ED%99%94-%EC%A0%9C%EC%96%B4\" aria-label=\" 1213 어노테이션을 활용해 json 직렬화 제어 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>🔖 12.1.3 어노테이션을 활용해 JSON 직렬화 제어</h3>\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\">data class Person(val name: String, val age: Int)\n\nfun main() {\n    val person = Person(&quot;Alice&quot;, 29)\n    println(serialize(person))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Person 인스턴스를 serialize 함수에 전달하면 JSON 표현이 담긴 문자열을 돌려받는다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val json = &quot;&quot;&quot;{&quot;name&quot;: &quot;Alice&quot;, &quot;age&quot;: 29&quot;}&quot;&quot;&quot;\n    println(deserialize&lt;Person&gt;(json))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li></li>\n<li>JSON 표현을 다시 코틀린 객체로 만들려면 deserialize 함수를 호출한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Person(\n    @JsonName(&quot;alias&quot;) val firstName: String,\n    @JsonExclude val age: Int? = null\n)</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@JsonExclude</code> 어노테이션을 사용하면 직렬화나 역직렬화할 때 무시해야 하는 프로퍼티를 표시할 수 있다.\n<ul>\n<li>기본값 지정 필요</li>\n</ul>\n</li>\n<li><code>@JsonName</code> 어노테이션을 사용하면 프로퍼티를 표현하는 키/값 쌍의 키로 프로퍼티 이름 대신 어노테이션이 지정한 문자열을 쓰게 할 수 있다.</li>\n</ul>\n<h3 id=\"-1214-어노테이션-선언\" style=\"position:relative;\"><a href=\"#-1214-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%84%A0%EC%96%B8\" aria-label=\" 1214 어노테이션 선언 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>🔖 12.1.4 어노테이션 선언</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">annotation class JsonExclude</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\">annotation class JsonName(val name: String)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>파라미터가 있는 어노테이션을 정의하려면 어노테이션 클래스의 주 생성자에 파라미터를 선언해야 한다.</li>\n<li>주 생성자 구문을 사용하며, <code>val</code>로 선언</li>\n</ul>\n<h3 id=\"-1215-메타어노테이션-어노테이션을-처리하는-방법-제어\" style=\"position:relative;\"><a href=\"#-1215-%EB%A9%94%ED%83%80%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%EC%B2%98%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-%EC%A0%9C%EC%96%B4\" aria-label=\" 1215 메타어노테이션 어노테이션을 처리하는 방법 제어 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>🔖 12.1.5 메타어노테이션: 어노테이션을 처리하는 방법 제어</h3>\n<ul>\n<li>메타어노테이션: 어노테이션 클래스에 적용할 수 있는 어노테이션</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Target(AnnotationTarget.PROPERTY)\nannotation class JsonExclude</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@Target</code> 메타어노테이션은 어노테이션을 적용할 수 있는 요소의 유형을 지정</li>\n<li>어노테이션 클래스에 대해 구체적인 <code>@Target</code>을 지정하지 않으면 모든 선언에 적용할 수 있는 어노테이션이 된다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Target(AnnotationTarget.ANNOTATION_CLASS)\nannotation class BindingAnnotation\n\n@BindingAnnotation\nannotation class MyBinding</code>\n        </deckgo-highlight-code>\n<ul>\n<li>메타어노테이션을 직접 만들어야 한다면 <code>ANNOTATION_CLASS</code>를 타깃으로 지정하자.</li>\n</ul>\n<h3 id=\"-1216-어노테이션-파라미터로-클래스-사용\" style=\"position:relative;\"><a href=\"#-1216-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A1%9C-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%82%AC%EC%9A%A9\" aria-label=\" 1216 어노테이션 파라미터로 클래스 사용 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>🔖 12.1.6 어노테이션 파라미터로 클래스 사용</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface Company {\n    val name: String\n}\n\ndata class CompanyImpl(override val name: String) : Company\n\ndata class Person(\n    val name: String,\n    @DeserializeInterface(CompanyImpl::class) val company: Company\n)</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@DeserializeInterface</code>는 인터페이스 타입인 프로퍼티에 대한 역직렬화를 제어할 때 쓰는 어노테이션이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">annotation class DeserializeInterface(val targetClass: KClass&lt;out Any&gt;)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>클래스 참조를 인자로 받는 어노테이션은 위와 같이 정의한다.</li>\n</ul>\n<h3 id=\"-1217-어노테이션-파라미터로-제네릭-클래스-받기\" style=\"position:relative;\"><a href=\"#-1217-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A1%9C-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%B0%9B%EA%B8%B0\" aria-label=\" 1217 어노테이션 파라미터로 제네릭 클래스 받기 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>🔖 12.1.7 어노테이션 파라미터로 제네릭 클래스 받기</h3>\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\">interface ValueSerializer&lt;T&gt; {\n    fun toJsonValue(value: T): Any?\n    fun fromJsonValue(jsonValue: Any?): T\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 인터페이스는 코틀린 객체에서 JSON 표현으로의 변환을 제공하며, 반대의 경우도 제공한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Person(\n    val name: String,\n    @CustomSerializer(DateSerializer::class) val birthDate: Date\n)</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">annotation class CustomSerializer(val serializerClass: KClass&lt;out ValueSerializer&lt;*&gt;&gt;)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>클래스를 어노테이션 인자로 받아야 할 때마다 같은 패턴 사용이 가능하다.\n<ul>\n<li><code>KClass&#x3C;out 자신의 클래스 이름></code></li>\n</ul>\n</li>\n<li>자신의 클래스 이름 자체가 타입 인자를 받아야 한다면 <code>KClass&#x3C;out 자신의 클래스 이름&#x3C;*>></code> 처럼 타입인자를 *로 바꾼다.</li>\n</ul>\n<h2 id=\"-122-리플렉션-실행-시점에-코틀린-객체-내부-관찰\" style=\"position:relative;\"><a href=\"#-122-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98-%EC%8B%A4%ED%96%89-%EC%8B%9C%EC%A0%90%EC%97%90-%EC%BD%94%ED%8B%80%EB%A6%B0-%EA%B0%9D%EC%B2%B4-%EB%82%B4%EB%B6%80-%EA%B4%80%EC%B0%B0\" aria-label=\" 122 리플렉션 실행 시점에 코틀린 객체 내부 관찰 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>📖 12.2 리플렉션: 실행 시점에 코틀린 객체 내부 관찰</h2>\n<ul>\n<li>리플렉션은 실행 시점에 객체의 프로퍼티와 메서드에 접근할 수 있게 해주는 방법</li>\n</ul>\n<h3 id=\"-1221-코틀린-리플렉션-api-kclass-kcallable-kfunction-kproperty\" style=\"position:relative;\"><a href=\"#-1221-%EC%BD%94%ED%8B%80%EB%A6%B0-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98-api-kclass-kcallable-kfunction-kproperty\" aria-label=\" 1221 코틀린 리플렉션 api kclass kcallable kfunction kproperty 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>🔖 12.2.1 코틀린 리플렉션 API: KClass, KCallable, KFunction, KProperty</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface KClass&lt;T : Any&gt; {\n    val simpleName: String?\n    val qualifiedName: String?\n    val members: Collection&lt;KCallable&lt;*&gt;&gt;\n    val constructors: Collection&lt;KFunction&lt;T&gt;&gt;\n    val nestedClasses: Collection&lt;KClass&lt;*&gt;&gt;\n    // ...\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>KClass</code> 선언을 찾아보면 클래스 내부를 살펴볼 때 사용할 수 있는 다양하고 유용한 메서드를 볼 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface KCallable&lt;out R&gt; {\n    fun call(vararg args: Any?): R\n    // ...\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>KCallable은</code> 함수와 프로퍼티를 아우르는 공통 상위 인터페이스다.</li>\n<li><code>call</code>을 사용할 때는 함수 인자를 <code>vararg</code> 리스트로 전달</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun foo(x: Int) = println(x)\n\nfun main() {\n    val kFunction = ::foo\n    kFunction.call(42)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>::foo</code> 식의 값 타입이 <code>KCallable</code> 클래스의 인스턴스이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun sum(x: Int, y: Int) = x + y\n\nfun main() {\n    val kFunction = KFunction2&lt;Int, Int, Int&gt; = ::sum\n    println(kFunction.invoke(1, 2) + kFunction.invoke(3, 4))\n    kFunction(1)\n    // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>invoke 메서드를 호출할 때는 인자 개수나 타입을 실수로 틀릴 수 없다. 컴파일이 안되기 때문</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">var counter = 0\n\nfun main() {\n    val kProperty = ::counter\n    kProperty.setter.call(21)\n    println(kProperty.get())\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>최상위 읽기 전용과 가변 프로퍼티는 각각 <code>KProperty0</code>이나, <code>KMutableProperty0</code> 인터페이스의 인스턴스로 표현\n<ul>\n<li>인자 없는 get method 제공</li>\n</ul>\n</li>\n<li><code>KProperty1</code>은 제네릭 클래스이다.</li>\n</ul>\n<h3 id=\"-1222-리플렉션을-사용해-객체-직렬화-구현\" style=\"position:relative;\"><a href=\"#-1222-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4-%EA%B0%9D%EC%B2%B4-%EC%A7%81%EB%A0%AC%ED%99%94-%EA%B5%AC%ED%98%84\" aria-label=\" 1222 리플렉션을 사용해 객체 직렬화 구현 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>🔖 12.2.2 리플렉션을 사용해 객체 직렬화 구현</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun serialize(obj: Any): String</code>\n        </deckgo-highlight-code>\n<ul>\n<li>제이키드의 직렬화 함수 선언이다.</li>\n<li>이 함수는 결과 JSON을 <code>StringBuilder</code> 인스턴스 안에 구축한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">private fun StringBuilder.serializeObject(x: Any) {\n    append(/*...*/)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>별도로 수신 객체를 지정하지 않고 append 메서드를 편하게 사용할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun serialize(obj: Any): String = buildString { serializeObject(obj) }</code>\n        </deckgo-highlight-code>\n<ul>\n<li>대부분의 작업을 <code>serializeObject</code>에 위임한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">private fun StringBuilder.serializeObject(obj: Any) {\n    val kClass = obj::class as KClass&lt;Any&gt;\n    val properties = kClass.memberProperties\n\n    properties.joinToStringBuilder(\n        this, prefix = &quot;{&quot;, postfix = &quot;}&quot;\n    ) { prop -&gt;\n        serializeString(prop.name)\n        append(&quot;: &quot;)\n        serializePropertyValue(prop.get(obj))\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>클래스의 각 프로퍼티를 차례로 직렬화 한다.</li>\n</ul>\n<h3 id=\"-1223-어노테이션을-활용해-직렬화-제어\" style=\"position:relative;\"><a href=\"#-1223-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%B4-%EC%A7%81%EB%A0%AC%ED%99%94-%EC%A0%9C%EC%96%B4\" aria-label=\" 1223 어노테이션을 활용해 직렬화 제어 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>🔖 12.2.3 어노테이션을 활용해 직렬화 제어</h3>\n<p>어노테이션을 지원하기 위해 <code>serializeObject</code>를 어떻게 수정할지 알아본다.</p>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val properties = kClass.memberProperties.filter { it.findAnnotation&lt;JsonExclude&gt;() == null }</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위와 같이 <code>@JsonExclude</code> 어노테이션이 붙지 않은 프로퍼티만 남길 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val jsonNameAnn = prop.findAnnotation&lt;JsonName&gt;()\nval propName = jsonNameAnn?.name ?: prop.name</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\">private fun StringBuilder.serializeObject(obj: Any) {\n    (obj::class as KClass&lt;Any&gt;)\n        .memberProperties\n        .filter { it.findAnnotation&lt;JsonExclude&gt;() == null }\n        .joinToStringBuilder(this, prefix = &quot;{&quot;, postfix = &quot;}&quot;) {\n            serializeProperty(it, obj)\n        }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@JsonExclude</code> 어노테이션한 프로퍼티를 제외시킨다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">private fun StringBuilder.serializeObject(prop: KProperty1&lt;Any, *&gt;, obj: Any) {\n    val jsonNameAnn = prop.findAnnotation&lt;JsonName&gt;()\n    val propName = jsonNameAnn?.name ?: prop.name\n    serializeString(propName)\n    append(&quot;: &quot;)\n    serializePropertyValue(prop.get(obj))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@JsonName</code>에 따라 프로퍼티 이름을 처리한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun KProperty&lt;*&gt;.getSerializer(): ValueSerializer&lt;Any?&gt;? {\n    val customSerializerAnn = findAnnotation&lt;CustomSerializer&gt;() ?: return null\n    val serializerClass = customSerializerAnn.serializerClass\n\n    val valueSerializer = serializerClass.objectInstance\n        ?: serializerClass.createInstance()\n    @Suppress(&quot;UNCHECKED_CAST&quot;)\n    return valueSerializer as ValueSerializer&lt;Any?&gt;\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\">private fun StringBuilder.serializeProperty(\n    prop: KProperty1&lt;Any, *&gt;, obj: Any\n) {\n    val jsonNameAnn = prop.findAnnotation&lt;JsonName&gt;()\n    val propName = jsonNameAnn?.name ?: prop.name\n    serializeString(propName)\n    append(&quot;: &quot;)\n\n    val value = prop.get(obj)\n    val jsonValue = prop.getSerializer()?.toJsonValue(value) ?: value\n    serializePropertyValue(jsonValue)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>serializeProperty</code> 구현 안에서 <code>getSerializer</code>를 사용할 수 있다.</li>\n</ul>\n<h3 id=\"-1224-json-파싱과-객체-역직렬화\" style=\"position:relative;\"><a href=\"#-1224-json-%ED%8C%8C%EC%8B%B1%EA%B3%BC-%EA%B0%9D%EC%B2%B4-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94\" aria-label=\" 1224 json 파싱과 객체 역직렬화 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>🔖 12.2.4 JSON 파싱과 객체 역직렬화</h3>\n<ul>\n<li>제이키드의 JSON 역직렬화기는 3단계로 구현돼 있다.\n<ol>\n<li>어휘 분석기(렉서)\n<ul>\n<li>토큰의 리스트로 변환(문자토큰, 값 토큰)</li>\n</ul>\n</li>\n<li>문법 분석기(파서)\n<ul>\n<li>토큰의 리스트를 구조화된 표현으로 변환</li>\n</ul>\n</li>\n<li>역직렬화 컴포넌트\n<ul>\n<li>필요한 클래스의 인스턴스를 생성해 반환</li>\n</ul>\n</li>\n</ol>\n</li>\n</ul>\n<h3 id=\"-1225-최종-역직렬화-단계-callby와-리플렉션을-사용해-객체-만들기\" style=\"position:relative;\"><a href=\"#-1225-%EC%B5%9C%EC%A2%85-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94-%EB%8B%A8%EA%B3%84-callby%EC%99%80-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4-%EA%B0%9D%EC%B2%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0\" aria-label=\" 1225 최종 역직렬화 단계 callby와 리플렉션을 사용해 객체 만들기 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>🔖 12.2.5 최종 역직렬화 단계: callBy()와 리플렉션을 사용해 객체 만들기</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface KCallable&lt;out R&gt; {\n    fun callBy(args: Map&lt;KParameter, Any?&gt;): R\n    // ...\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>KCallable.call</code>은 디폴트 파라미터 값을 지원하지 않는다.</li>\n<li><code>KCallable.callBy</code>는 디폴트 파라미터 값을 지원한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">object BooleanSerializer : ValueSerializer&lt;Boolean&gt; {\n    override fun fromJsonValue(jsonValue: Any?): Boolean {\n        if (jsonValue !is Boolean) throw JkidException(&quot;Boolean Expected&quot;)\n            return jsonValue\n    }\n    override fun toJsonValue(value: Boolean) = value\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Boolean 값을 위한 직렬화기</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class ClassInfoCache {\n    private val cacheData = mutableMapOf&lt;KClass&lt;*&gt;, ClassInfo&lt;*&gt;&gt;()\n\n    @Suppress(&quot;UNCHECKED_CAST&quot;)\n    operator fun &lt;T : Any&gt; get(cls: KClass&lt;T&gt;): ClassInfo&lt;T&gt; =\n        cacheData.getOrPut(cls) { ClassInfo(cls) } as ClassInfo&lt;T&gt;\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>리플렉션 데이터 캐시 저장소</li>\n<li>성능을 위해 검색 결과를 캐시에 넣어둔다.</li>\n</ul>","excerpt":"📖 12.1 어노테이션 선언과 적용 🔖 12.1.1 어노테이션을 적용해 선언해 표지 남기기 코틀린에서 어노테이션을 적용하려면 @와 어노테이션 이름을 선언 앞에 넣으면 된다. @Deprecated 어노테이션은 최대 3가지 파라미터를 받는다. message: 사용중단 예고 이유 replaceWith: 옛 버전을 대신할 수 있는 패턴 제시 level: 점진적인 사용 중단을 지원 제공 WARNING: 경고만 함 ERROR: 컴파일되지 못하게 막음. HIDDEN…","fields":{"slug":"/backend/kotlin-in-action/12장-어노테이션과_리플렉션/"},"frontmatter":{"title":"Kotlin in Action - 12장 애노테이션과 리플렉션","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"May 25, 2025"}},"previous":{"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"}},"node":{"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"}}}},"staticQueryHashes":["2374173507","2996537568","3691437124"]}