BottleH Blog

Kotlin in Action - 13์žฅ DSL ๋งŒ๋“ค๊ธฐ

    Tags

  • Kotlin
Kotlin in Action - 13์žฅ DSL ๋งŒ๋“ค๊ธฐ thumbnail

๐Ÿ“– 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() } ๋žŒ๋‹ค๋ฅผ ๊ด„ํ˜ธ ๋ฐ–์œผ๋กœ ๋นผ๋‚ด๋Š” ๊ด€๋ก€
sb.append("yes") with(sb) { append("yes") append("no") } ์ˆ˜์‹  ๊ฐ์ฒด ์ง€์ • ๋žŒ๋‹ค
  • ๊น”๋”ํ•œ API๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋•๋Š” ์ฝ”ํ‹€๋ฆฐ ๊ธฐ๋Šฅ์—๋Š” ์œ„์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ๋“ค์ด ์žˆ๋‹ค.

๐Ÿ”– 13.1.1 ๋„๋ฉ”์ธ ํŠนํ™” ์–ธ์–ด

  • ๊ฐ€์žฅ ์ต์ˆ™ํ•œ DSL์€ SQL๊ณผ ์ •๊ทœ์‹์ผ ๊ฒƒ์ด๋‹ค.
  • DSL์ด ๋ฒ”์šฉ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์™€ ๋‹ฌ๋ฆฌ ๋” ์„ ์–ธ์ ์ด๋‹ค.
    • ๋ฒ”์šฉ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋Š” ๋ช…๋ น์ ์ด๋‹ค.
  • ์„ ์–ธ์  ์–ธ์–ด๋Š” ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์ˆ ํ•˜๊ธฐ๋งŒ ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์„ธ๋ถ€ ์‹คํ–‰์€ ์–ธ์–ด๋ฅผ ํ•ด์„ํ•˜๋Š” ์—”์ง„์— ๋งก๊ธด๋‹ค.
  • DSL์˜ ๊ฐ€์žฅ ํฐ ๋‹จ์ ์€ ๋ฒ”์šฉ ์–ธ์–ด๋กœ ๋งŒ๋“  ํ˜ธ์ŠคํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ DSL์„ ํ•จ๊ป˜ ์กฐํ•ฉํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค๋Š” ๊ฒƒ
    • DSL์€ ์ž์ฒด ๋ฌธ๋ฒ•์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์–ธ์–ด์˜ ํ”„๋กœ๊ทธ๋žจ ์•ˆ์— ์ง์ ‘ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜๊ฐ€ ์—†๋‹ค.
  • ์ด๋Ÿฌํ•œ ๋‹จ์ ์„ ํ•ด๊ฒฐํ•˜๋ฉด์„œ DSL์˜ ๋‹ค๋ฅธ ์ด์ ์„ ์‚ด๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ๋‚ด๋ถ€ DSL์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

๐Ÿ”– 13.1.2 ๋‚ด๋ถ€ DSL์€ ํ”„๋กœ๊ทธ๋žจ์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„๊ณผ ๋งค๋„๋Ÿฝ๊ฒŒ ํ†ตํ•ฉ๋œ๋‹ค

  • ๋…๋ฆฝ์ ์ธ ๋ฌธ๋ฒ• ๊ตฌ์กฐ๋ฅผ ๊ฐ–๋Š” ์™ธ๋ถ€ DSL๊ณผ๋Š” ๋ฐ˜๋Œ€๋กœ ๋‚ด๋ถ€ DSL์€ ๋ฒ”์šฉ ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ํ”„๋กœ๊ทธ๋žจ์˜ ์ผ๋ถ€๋ฉฐ, ๋ฒ”์šฉ ์–ธ์–ด์™€ ๋™์ผํ•œ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.
SELECT country.name, COUNT(customer.id) FROM country JOIN customer ON customer.country_id = country.id GROUP BY country.name ORDER BY COUNT(customer.id) DESC LIMIT 1;
  • ์ด๋ฅผ ์ฝ”ํ‹€๋ฆฐ๊ณผ ์ต์Šคํฌ์ฆˆ๋“œ(์ฝ”ํ‹€๋ฆฐ์œผ๋กœ ์ž‘์„ฑ๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ”„๋ ˆ์ž„์›Œํฌ)๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌํ˜„ํ•œ ์˜ˆ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
(Country innerJoin Customer) .slice(Country.name, Count(Customer.id)) .selectAll() .groupBy(Country.name) .orderBy(Count(Customer.id), order = SortOrder.DESC) .limit(1)
  • ์ด๋ฅผ ๋‚ด๋ถ€ DSL์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๐Ÿ”– 13.1.3 DSL์˜ ๊ตฌ์กฐ

  • DSL์—๋งŒ ์กด์žฌํ•˜๋Š” ํŠน์ง•์€ ๊ตฌ์กฐ(๋ฌธ๋ฒ•)์ด๋‹ค.
  • ์ผ๋ฐ˜์ ์ธ API๋Š” ๋ช…๋ น-์งˆ์˜ API์ด๋‹ค.
  • DSL์€ ๋ฌธ๋ฒ•์— ์˜ํ•ด ์ •ํ•ด์ง„๋‹ค.
    • ์ด๋Ÿฌํ•œ ๋ฌธ๋ฒ• ๋•Œ๋ฌธ์— ๋‚ด๋ถ€ DSL์„ ์–ธ์–ด๋ผ๊ณ  ๋ถ€๋ฅผ ์ˆ˜ ์žˆ๋‹ค.
    • ์—ฌ๋Ÿฌ ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์กฐํ•ฉํ•ด์„œ ์—ฐ์‚ฐ์„ ๋งŒ๋“ค๋ฉฐ ํƒ€์ž… ๊ฒ€์‚ฌ๊ธฐ๋Š” ์—ฌ๋Ÿฌ ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด ๋ฐ”๋ฅด๊ฒŒ ์กฐํ•ฉ๋๋Š”์ง€๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค.
  • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์—ฐ์‡„๋Š” DSL ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“œ๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด๋‹ค.

๐Ÿ”– 13.1.4 ๋‚ด๋ถ€ DSL๋กœ HTML ๋งŒ๋“ค๊ธฐ

  • kotlinx.html๋กœ HTML์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • ์ง์ ‘ HTML ํ…์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ  ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋“œ๋กœ HTML์„ ๋งŒ๋“ค๋ฉด ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค.
  • ๋‚ด๋ถ€์— ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋“œ๋ฅผ ์›ํ•˜๋Š” ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • HTML์€ ๊ณ ์ „์ ์ธ ๋งˆํฌ์—… ์–ธ์–ด ์˜ˆ์ œ์ด๋ฉฐ DSL์˜ ๊ฐœ๋…์„ ์ œ๋Œ€๋กœ ๋ณด์—ฌ์ค€๋‹ค.
  • XML๊ณผ ๊ฐ™์ด HTML๊ณผ ๊ตฌ์กฐ๊ฐ€ ๋น„์Šทํ•œ ๋‹ค๋ฅธ ๋ชจ๋“  ์–ธ์–ด์— ์ด์™€ ๋น„์Šทํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“– 13.2 ๊ตฌ์กฐํ™”๋œ API ๊ตฌ์ถ•: DSL์—์„œ ์ˆ˜์‹  ๊ฐ์ฒด ์ง€์ • ๋žŒ๋‹ค ์‚ฌ์šฉ

๐Ÿ”– 13.2.1 ์ˆ˜์‹  ๊ฐ์ฒด ์ง€์ • ๋žŒ๋‹ค์™€ ํ™•์žฅ ํ•จ์ˆ˜ ํƒ€์ž…

fun buildString( builderAction: (StringBuilder) -> Unit ): String { val sb = StringBuilder() builderAction(sb) return sb.toString() } fun main() { val s = buildString { it.append("Hello, ") it.append("World!") } println(s) }
  • ๋žŒ๋‹ค๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” buildString ์ •์˜
  • ๋žŒ๋‹ค ๋ณธ๋ฌธ์—์„œ ๋งค๋ฒˆ it์„ ์‚ฌ์šฉํ•ด StringBuilder ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•ด์•ผ ํ•œ๋‹ค.
fun buildString( builderAction: StringBuilder.() -> Unit ): String { val sb = StringBuilder() sb.builderAction() return sb.toString() } fun main() { val s = buildString { this.append("Hello, ") this.append("World!") } println(s) }
  • ์ˆ˜์‹  ๊ฐ์ฒด ์ง€์ • ๋žŒ๋‹ค๋ฅผ ์ธ์ž๋กœ ๋„˜๊ธฐ๊ธฐ ๋•Œ๋ฌธ์— ๋žŒ๋‹ค ์•ˆ์—์„œ it์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
val appendExcl: StringBuilder.() -> Unit = { this.append("!") } fun main() { val stringBuilder = StringBuilder("Hi") stringBuilder.appendExcl() println(stringBuilder) println(buildString(appendExcl)) }
  • ์ฝ”๋“œ์—์„œ ์ˆ˜์‹  ๊ฐ์ฒด ์ง€์ • ๋žŒ๋‹ค๋Š” ์ผ๋ฐ˜ ๋žŒ๋‹ค์™€ ๋˜‘๊ฐ™๋‹ค ๋ณด์ธ๋‹ค.
fun builderString(builderAction: StringBuilder.() -> Unit): String = StringBuilder().apply(builderAction).toString()
  • ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ builderString ๊ตฌํ˜„์€ ๋” ์งง๋‹ค.
  • ๊ธฐ๋ณธ์ ์œผ๋กœ apply์™€ with๋Š” ๋ชจ๋‘ ์ž์‹ ์ด ์ œ๊ณต๋ฐ›์€ ์ˆ˜์‹  ๊ฐ์ฒด๋ฅผ ๊ฐ–๊ณ  ํ™•์žฅ ํ•จ์ˆ˜ ํƒ€์ž…์˜ ๋žŒ๋‹ค๋ฅผ ํ˜ธ์ถœ

๐Ÿ”– 13.2.2 ์ˆ˜์‹  ๊ฐ์ฒด ์ง€์ • ๋žŒ๋‹ค๋ฅผ HTML ๋นŒ๋” ์•ˆ์—์„œ ์‚ฌ์šฉ

  • HTML์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์ฝ”ํ‹€๋ฆฐ DSL์„ ๋ณดํ†ต์€ HTML ๋นŒ๋”๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.
fun createSimpleTable() = createHTML().table { tr { td { +"cell" } } }
  • ๋ชจ๋‘ ํ‰๋ฒ”ํ•œ ํ•จ์ˆ˜๋‹ค.
  • ๊ฐ ์ˆ˜์‹  ๊ฐ์ฒด ์ง€์ • ๋žŒ๋‹ค๊ฐ€ ์ด๋ฆ„ ๊ฒฐ์ • ๊ทœ์น™์„ ๋ฐ”๊พผ๋‹ค.
open class Tag class TABLE : Tag { fun tr(init: TR.() -> Unit) } class TR : Tag { fun td(init: TD.() -> Unit) } class TD : Tag
  • ๋ชจ๋‘ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋‹ค.
fun createSimpleTable() = createHTML().table { this@table.tr { (this@tr).td { +"cell" } } }
  • this ์ฐธ์กฐ๋ฅผ ์“ฐ์ง€ ์•Š์•„๋„ ๋˜๋ฉด ๋นŒ๋” ๋ฌธ๋ฒ•์ด ๊ฐ„๋‹จํ•ด์ง€๊ณ  ์ „์ฒด์ ์ธ ๊ตฌ๋ฌธ์ด ์›๋ž˜์˜ HTML ๊ตฌ๋ฌธ๊ณผ ๋น„์Šทํ•ด์ง„๋‹ค.
@DslMarker annotation class HtmlTagMarker
  • @DslMarker ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด ๋‚ดํฌ๋œ ๋žŒ๋‹ค์—์„œ ์™ธ๋ถ€ ๋žŒ๋‹ค์˜ ์ˆ˜์‹  ๊ฐ์ฒด์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฉ”ํƒ€ ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค.
@DslMarker annotation class HtmlTagMarker @HtmlTagMarker open class Tag(val name: String) { private val children = mutableListOf<Tag>() protected fun <T : Tag> doInit(child: T, init: T.() -> Unit) { child.init() children.add(child) } override fun toString() = "<$name>${children.joinToString("")}</$name>" } fun table(init: TABLE.() -> Unit) = TABLE().apply(init) class TABLE : Tag("table") { fun tr(init: TR.() -> Unit) = doInit(TR(), init) } class TR : Tag("tr") { fun td(init: TD.() -> Unit) = doInit(TD(), init) } class TD : Tag("td") fun createTable() = table { tr { td { } } }
  • ๊ฐ„๋‹จํ•œ HTML ๋นŒ๋”์˜ ์ „์ฒด ๊ตฌํ˜„

๐Ÿ”– 13.2.3 ์ฝ”ํ‹€๋ฆฐ ๋นŒ๋”: ์ถ”์ƒํ™”์™€ ์žฌ์‚ฌ์šฉ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ค€๋‹ค

fun buildBookList() = createHTML().body { ul { li { a("#1") { +"The Three-Body Problem" } } li { a("#2") { +"The Cartesian Product Problem" } } li { a("#3") { +"The Conjugation Problem" } } } h2 { id = "1"; +"The Three-Body Problem" } p { +"The Three-Body Problem is a classic physics problem from the early 20th century." } h2 { id = "2"; +"The Cartesian Product Problem" } p { +"The Cartesian product is a useful mathematical construct for a wide variety of problems." } h2 { id = "3"; +"The Conjugation Problem" } p { +"The Conjugation Problem is a classic computational problem from the 1970s." } }
  • ๋ชฉ์ฐจ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํŽ˜์ด์ง€๋ฅผ ์ฝ”ํ‹€๋ฆฐ HTML ๋นŒ๋”๋กœ ๋งŒ๋“ค๊ธฐ
  • ์ด๊ฑธ ์ข€ ๋” ์ด์˜๊ฒŒ ๋‹ค๋“ฌ์„ ์ˆ˜ ์žˆ๋‹ค.
fun buildBookList() = createHTML().body { listWithToc { item( "The Three-Body Problem", "The Three-Body Problem is a classic physics problem from the early 20th century." ) item( "The Cartesian Product Problem", "The Cartesian product is a useful mathematical construct for a wide variety of problems." ) item( "The Conjugation Problem", "The Conjugation Problem is a classic computational problem from the 1970s." ) } } @HtmlTagMarker class LISTWITHTOC { val entries = mutableListOf<Pair<String, String>>() fun item(headline: String, body: String) { entries += headline to body } }
  • @HtmlTagMarker ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ DSL ์˜์—ญ ๊ทœ์น™์„ ๋”ฐ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.
fun BODY.listWithToc(block: LISTWITHTOC.() -> Unit) { val listWithToc = LISTWITHTOC() listWithToc.block() ul { for ((index, entry) in listWithToc.entries.withIndex()) { li { a("#${index}") { +entry.first } } } } for ((index, entry) in listWithToc.entries.withIndex()) { h2 { id = "$index"; +entry.first } p { +entry.second } } }
  • ์ถ”์ƒํ™”์™€ ์žฌ์‚ฌ์šฉ์„ ํ†ตํ•ด ์ฝ”๋“œ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

๐Ÿ“– 13.3 invoke ๊ด€๋ก€๋ฅผ ์‚ฌ์šฉํ•ด ๋” ์œ ์—ฐํ•˜๊ฒŒ ๋ธ”๋ก ๋‚ดํฌ์‹œํ‚ค๊ธฐ

๐Ÿ”– 13.3.1 invoke ๊ด€๋ก€๋ฅผ ์‚ฌ์šฉํ•ด ๋” ์œ ์—ฐํ•˜๊ฒŒ ๋ธ”๋ก ๋‚ดํฌ์‹œํ‚ค๊ธฐ

class Greeter(val greeting: String) { operator fun invoke(name: String) { println("$greeting, $name!") } } fun main() { val bavarianGreeter = Greeter("Servus") bavarianGreeter("Dmitry") }
  • ํด๋ž˜์Šค ์•ˆ์—์„œ invoke ๋ฉ”์†Œ๋“œ ์ •์˜
  • invoke ๋ฉ”์„œ๋“œ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— ๋Œ€ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์€ ์—†๋‹ค.
  • ์›ํ•˜๋Š”๋Œ€๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐœ์ˆ˜๋‚˜ ํƒ€์ž…์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ”– 13.3.2 DSL์˜ invoke ๊ด€๋ก€: ๊ทธ๋ ˆ์ด๋“ค ์˜์กด๊ด€๊ณ„ ์„ ์–ธ

dependencies { testImplementation(kotlin("test")) implementation("org.jetbrains.exposed:exposed-core:0.40.1") implementation("org.jetbrains.exposed:exposed-dao:0.40.1") }
  • ์ด ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋‚ดํฌ๋œ ๋ธ”๋ก ๊ตฌ์กฐ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ํ•œํŽธ, ํ‰ํ‰ํ•œ ํ•จ์ˆ˜ ํ˜ธ์ถœ ๊ตฌ์กฐ๋„ ํ•จ๊ป˜ ์ œ๊ณตํ•˜๋Š” API๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค.
dependencies.implementation("org.jetbrains.exposed:exposed-core:0.40.1") dependencies { implementation("org.jetbrains.exposed:exposed-core:0.40.1") }
  • ์ฒซ ๋ฒˆ์งธ ๊ตฌ๋ฌธ์€ invoke๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ์ด๋‹ค.
class DependencyHandler { fun implementation(coordinate: String) { println("Added dependency on $coordinate") } operator fun invoke(body: DependencyHandler.() -> Unit) { body() } } fun main() { val dependencies = DependencyHandler() dependencies.implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") dependencies { implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") } }
  • ์œ ์—ฐํ•œ DSL ๋ฌธ๋ฒ•์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด invoke ์‚ฌ์šฉ
  • ๊ฝค ์ ์€ ์–‘์˜ ์ฝ”๋“œ์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์žฌ์ •์˜ํ•œ invoke ๋ฉ”์„œ๋“œ๋กœ ์ธํ•ด DSL API์˜ ์œ ์—ฐ์„ฑ์ด ํ›จ์”ฌ ์ปค์ง„๋‹ค.

๐Ÿ“– 13.4 ์‹ค์ „ ์ฝ”ํ‹€๋ฆฐ DSL

๐Ÿ”– 13.4.1 ์ค‘์œ„ ํ˜ธ์ถœ ์—ฐ์‡„์‹œํ‚ค๊ธฐ: ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์˜ should ํ•จ์ˆ˜

class PrefixTest { @Test fun testKPrefix() { val s = "kotlin".uppercase() s should startWith("K") } }
  • ์ผ๋ฐ˜ ์˜์–ด์ฒ˜๋Ÿผ ์ฝ”๋“œ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.
infix fun <T> T.should(matcher: Matcher<T>) = matcher.test(this) interface Matcher<T> { fun test(value: T) } fun startWith(prefix: String): Matcher<String> { return object : Matcher<String> { override fun test(value: String) { if (!value.startsWith(prefix)) { throw AssertionError("$value does not start with $prefix") } } } }
  • DSL ์—์„œ ์‚ฌ์šฉํ•˜๋ ค๋ฉด infix ๋ณ€๊ฒฝ์ž๋ฅผ ๋ถ™์—ฌ์•ผ ํ•œ๋‹ค.
  • ์ค‘์œ„ ํ˜ธ์ถœ๊ณผ object๋กœ ์ •์˜ํ•œ ์‹ฑ๊ธ€ํ„ด ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์กฐํ•ฉํ•˜๋ฉด DSL์— ์ƒ๋‹นํžˆ ๋ณต์žกํ•œ ๋ฌธ๋ฒ•์„ ๋„์ž…ํ•  ์ˆ˜ ์žˆ๊ณ , ๊ทธ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด DSL ๊ตฌ๋ฌธ์„ ๊น”๋”ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ”– 13.4.2 ์›์‹œ ํƒ€์ž…์— ๋Œ€ํ•ด ํ™•์žฅ ํ•จ์ˆ˜ ์ •์˜ํ•˜๊ธฐ: ๋‚ ์งœ ์ฒ˜๋ฆฌ

val Int.days: Duration get() = this.toDuration(DurationUnit.DAYS) val Int.hours: Duration get() = this.toDuration(DurationUnit.HOURS)
  • ์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ์•„๋ฌด ํƒ€์ž…์ด๋‚˜ ํ™•์žฅ ํ•จ์ˆ˜์˜ ์ˆ˜์‹  ๊ฐ์ฒด ํƒ€์ž…์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ”– 13.4.3 ๋ฉค๋ฒ„ ํ™•์žฅ ํ•จ์ˆ˜: SQL์„ ์œ„ํ•œ ๋‚ด๋ถ€ DSL

  • ํด๋ž˜์Šค ์•ˆ์—์„œ ํ™•์žฅ ํ•จ์ˆ˜์™€ ํ™•์žฅ ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ ์–ธํ•˜๋ฉด ๊ทธ๋“ค์ด ์„ ์–ธ๋œ ํด๋ž˜์Šค์˜ ๋ฉค๋ฒ„์ธ ๋™์‹œ์— ๊ทธ๋“ค์ด ํ™•์žฅํ•˜๋Š” ๋‹ค๋ฅธ ํƒ€์ž…์˜ ๋ฉค๋ฒ„์ด๊ธฐ๋„ ํ•˜๋‹ค.
    • ์ด๋Ÿฐ ํ•จ์ˆ˜๋‚˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ฉค๋ฒ„ ํ™•์žฅ์ด๋ผ ๋ถ€๋ฅธ๋‹ค.
  • ๋ฉค๋ฒ„ ํ™•์žฅ๋„ ์—ฌ์ „ํžˆ ๋ฉค๋ฒ„๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, Table ํด๋ž˜์Šค์˜ integer, varchar ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋Š” ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†์ง€๋งŒ, ๋ฉค๋ฒ„ ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ œํ•œ๋œ ๋ฒ”์œ„ ๋‚ด์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.
Written by@BottleH
Back-End Developer

GitHub