๐ŸŽญ Groovy์˜ ๋นŒ๋” ํŒจํ„ด: DSL ์ƒ์„ฑ ๊ธฐ๋ฒ• ๐Ÿ› ๏ธ

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽญ Groovy์˜ ๋นŒ๋” ํŒจํ„ด: DSL ์ƒ์„ฑ ๊ธฐ๋ฒ• ๐Ÿ› ๏ธ

 

 

์•ˆ๋…•ํ•˜์„ธ์š”, ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ ์ •๋ง ํฅ๋ฏธ์ง„์ง„ํ•œ ์ฃผ์ œ๋กœ ์—ฌ๋Ÿฌ๋ถ„๊ณผ ํ•จ๊ป˜ํ•  ๊ฑฐ์˜ˆ์š”. ๋ฐ”๋กœ "Groovy์˜ ๋นŒ๋” ํŒจํ„ด: DSL ์ƒ์„ฑ ๊ธฐ๋ฒ•"์— ๋Œ€ํ•ด ๊นŠ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ๋ž๋‹ˆ๋‹ค. ์ด ์ฃผ์ œ, ์ฒ˜์Œ ๋“ค์œผ๋ฉด ์ข€ ์–ด๋ ต๊ฒŒ ๋Š๊ปด์งˆ ์ˆ˜ ์žˆ์ฃ ? ํ•˜์ง€๋งŒ ๊ฑฑ์ • ๋งˆ์„ธ์š”! ์ œ๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„์˜ ๋ˆˆ๋†’์ด์— ๋งž์ถฐ ์‰ฝ๊ณ  ์žฌ๋ฏธ์žˆ๊ฒŒ ์„ค๋ช…ํ•ด๋“œ๋ฆด๊ฒŒ์š”. ๋งˆ์น˜ ์นดํ†ก์œผ๋กœ ์นœ๊ตฌ์™€ ๋Œ€ํ™”ํ•˜๋“ฏ์ด ๋ง์ด์ฃ . ใ…‹ใ…‹ใ…‹

๊ทธ๋Ÿผ ์ด์ œ๋ถ€ํ„ฐ Groovy ์–ธ์–ด์˜ ๋งค๋ ฅ์ ์ธ ์„ธ๊ณ„๋กœ ๋น ์ ธ๋ณผ๊นŒ์š”? ๐Ÿš€

์ž ๊น! ํ˜น์‹œ ์—ฌ๋Ÿฌ๋ถ„ ์ค‘์— ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๊ด€์‹ฌ ์žˆ์œผ์‹  ๋ถ„๋“ค ๊ณ„์‹ ๊ฐ€์š”? ๊ทธ๋ ‡๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ ๋‹ค์–‘ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€๋ จ ์žฌ๋Šฅ์„ ์ฐพ์•„๋ณด์„ธ์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์‹ค๋ ฅ ํ–ฅ์ƒ์— ํฐ ๋„์›€์ด ๋  ๊ฑฐ์˜ˆ์š”!

๐Ÿค” Groovy๊ฐ€ ๋ญ๊ธธ๋ž˜?

์ž, ๋จผ์ € Groovy์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณผ๊นŒ์š”? Groovy๋Š” Java ํ”Œ๋žซํผ์„ ์œ„ํ•œ ๋™์  ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์˜ˆ์š”. Java์™€ ๋น„์Šทํ•˜๋ฉด์„œ๋„ ๋” ๊ฐ„๊ฒฐํ•˜๊ณ  ์œ ์—ฐํ•œ ๋ฌธ๋ฒ•์„ ๊ฐ€์ง€๊ณ  ์žˆ์ฃ . ๋งˆ์น˜ Java์˜ ์ฟจํ•œ ์‚ฌ์ดŒ ๊ฐ™์€ ๋Š๋‚Œ์ด๋ž„๊นŒ์š”? ๐Ÿ˜Ž

Groovy์˜ ํŠน์ง•์„ ๋ช‡ ๊ฐ€์ง€ ์‚ดํŽด๋ณผ๊นŒ์š”?

  • Java์™€ 100% ํ˜ธํ™˜๋ผ์š”. Java ์ฝ”๋“œ๋ฅผ ๊ทธ๋Œ€๋กœ Groovy์—์„œ ์“ธ ์ˆ˜ ์žˆ์–ด์š”!
  • ๋™์  ํƒ€์ดํ•‘์„ ์ง€์›ํ•ด์š”. ๋ณ€์ˆ˜ ํƒ€์ž…์„ ์ผ์ผ์ด ์„ ์–ธํ•˜์ง€ ์•Š์•„๋„ ๋ผ์š”.
  • ํด๋กœ์ €(Closure)๋ฅผ ์ง€์›ํ•ด์š”. ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ฝƒ์ด์ฃ !
  • ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ฐ€๋Šฅํ•ด์š”. ํ”„๋กœ๊ทธ๋žจ์ด ์ž๊ธฐ ์ž์‹ ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋‹ˆ, ์ฉ๋‹ค ์ฉ๋‹ค~ ๐Ÿ‘

์ด๋Ÿฐ ํŠน์ง•๋“ค ๋•Œ๋ฌธ์— Groovy๋Š” ํŠนํžˆ DSL(Domain-Specific Language) ๋งŒ๋“ค๊ธฐ์— ์•„์ฃผ ์ ํ•ฉํ•ด์š”. ๊ทธ๋Ÿผ DSL์ด ๋ญ”์ง€ ๊ถ๊ธˆํ•˜์‹œ์ฃ ?

๐Ÿ—ฃ๏ธ DSL์ด ๋ญ์˜ˆ์š”? ๋จน๋Š” ๊ฑด๊ฐ€์š”?

ใ…‹ใ…‹ใ…‹ ์•„๋‹ˆ์š”, DSL์€ ๋จน๋Š” ๊ฒŒ ์•„๋‹ˆ์—์š”! DSL์€ "Domain-Specific Language"์˜ ์•ฝ์ž๋กœ, ํŠน์ • ๋„๋ฉ”์ธ(๋ถ„์•ผ)์— ํŠนํ™”๋œ ์–ธ์–ด๋ฅผ ๋งํ•ด์š”. ์‰ฝ๊ฒŒ ๋งํ•ด, ์–ด๋–ค ํŠน์ •ํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ '๋งž์ถค ์–ธ์–ด'๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ์š”.

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ์š”? HTML์€ ์›น ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ DSL์ด์—์š”. SQL์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ DSL์ด๊ณ ์š”. ์ด๋ ‡๊ฒŒ ํŠน์ • ๋ชฉ์ ์— ๋งž์ถฐ ๋งŒ๋“ค์–ด์ง„ ์–ธ์–ด๋“ค์ด ๋ฐ”๋กœ DSL์ด์—์š”.

์ฃผ์˜! DSL์„ ๋งŒ๋“ค ๋•Œ๋Š” ๊ทธ ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•  ์‚ฌ๋žŒ๋“ค์„ ํ•ญ์ƒ ์ƒ๊ฐํ•ด์•ผ ํ•ด์š”. ๋„ˆ๋ฌด ๋ณต์žกํ•˜๋ฉด ์•„๋ฌด๋„ ์•ˆ ์“ธ ๊ฑฐ์˜ˆ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ์„ ์„ค๋ช…ํ•  ๋•Œ์ฒ˜๋Ÿผ, ๋ˆ„๊ตฌ๋‚˜ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์•ผ ํ•ด์š”!

๊ทธ๋Ÿผ ์ด์ œ Groovy์—์„œ ์–ด๋–ป๊ฒŒ DSL์„ ๋งŒ๋“œ๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ์š”? ์—ฌ๊ธฐ์„œ ๋นŒ๋” ํŒจํ„ด์ด ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค!

๐Ÿ—๏ธ ๋นŒ๋” ํŒจํ„ด? ๊ฑด์ถ•๊ฐ€ ๋˜๋Š” ๊ฑฐ์˜ˆ์š”?

ใ…‹ใ…‹ใ…‹ ์•„๋‹ˆ์—์š”, ์—ฌ๋Ÿฌ๋ถ„์ด ์ง์ ‘ ๊ฑด๋ฌผ์„ ์ง“๋Š” ๊ฑด ์•„๋‹ˆ์—์š”! ํ•˜์ง€๋งŒ ๋น„์œ ํ•˜์ž๋ฉด ๊ฝค ๋น„์Šทํ•ด์š”. ๋นŒ๋” ํŒจํ„ด์€ ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ๋งŒ๋“ค์–ด๊ฐ€๋Š” ๋””์ž์ธ ํŒจํ„ด์ด์—์š”. ๋งˆ์น˜ ๊ฑด๋ฌผ์„ ์ง€์„ ๋•Œ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์ฐจ๊ทผ์ฐจ๊ทผ ์˜ฌ๋ผ๊ฐ€๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”.

Groovy์—์„œ๋Š” ์ด ๋นŒ๋” ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์„œ ์•„์ฃผ ์šฐ์•„ํ•˜๊ณ  ์ฝ๊ธฐ ์‰ฌ์šด DSL์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”. ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€ ํ•œ๋ฒˆ ๋ณผ๊นŒ์š”?


def html = new MarkupBuilder()
html.html {
    head {
        title 'My Awesome Page'
    }
    body {
        h1 'Welcome to my page!'
        p 'This is a paragraph.'
    }
}

์ด ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด HTML์„ ๋งŒ๋“œ๋Š” DSL์ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์–ด์š”. ๋งˆ์น˜ HTML ๊ตฌ์กฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์ฝ”๋“œ๋กœ ์˜ฎ๊ฒจ๋†“์€ ๊ฒƒ ๊ฐ™์ฃ ? ์ด๊ฒŒ ๋ฐ”๋กœ Groovy์˜ ๋นŒ๋” ํŒจํ„ด์„ ์ด์šฉํ•œ DSL์˜ ํž˜์ด์—์š”!

๊ฟ€ํŒ! DSL์„ ๋งŒ๋“ค ๋•Œ๋Š” ํ•ญ์ƒ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ƒ๊ฐํ•ด์•ผ ํ•ด์š”. ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ์„ ์„ค๋ช…ํ•˜๋“ฏ์ด, ๋ˆ„๊ตฌ๋‚˜ ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์•ผ ํ•ด์š”. ๊ทธ๋ž˜์•ผ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์‚ฌ์šฉํ•˜๊ฒ ์ฃ ?

๐ŸŽจ Groovy๋กœ DSL ๋งŒ๋“ค๊ธฐ: step by step

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ Groovy๋กœ DSL์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊นŒ์š”? ์ฒœ์ฒœํžˆ, ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…ํ•ด๋“œ๋ฆด๊ฒŒ์š”. ์ค€๋น„๋˜์…จ๋‚˜์š”? Let's go! ๐Ÿš€

Step 1: ํด๋กœ์ €(Closure) ์ดํ•ดํ•˜๊ธฐ

Groovy์˜ DSL์„ ์ดํ•ดํ•˜๋ ค๋ฉด ๋จผ์ € ํด๋กœ์ €์— ๋Œ€ํ•ด ์•Œ์•„์•ผ ํ•ด์š”. ํด๋กœ์ €๋Š” ๊ฐ„๋‹จํžˆ ๋งํ•ด '์ฝ”๋“œ ๋ธ”๋ก'์ด์—์š”. ํ•จ์ˆ˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ๋ญ‰์น˜๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ์š”.


def greet = { name -> 
    println "์•ˆ๋…•, $name!"
}

greet("์˜ํฌ")  // ์ถœ๋ ฅ: ์•ˆ๋…•, ์˜ํฌ!

์ด๋ ‡๊ฒŒ ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๋ฅผ ๋” ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”. DSL์„ ๋งŒ๋“ค ๋•Œ ์ด ํด๋กœ์ €๊ฐ€ ์•„์ฃผ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ด์š”!

Step 2: ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ๋ฌธ๋ฒ• ์ดํ•ดํ•˜๊ธฐ

Groovy์—์„œ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๊ด„ํ˜ธ๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๊ณ ์š”? DSL์„ ๋” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”!


// ์ผ๋ฐ˜์ ์ธ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ
println("Hello, World!")

// Groovy ์Šคํƒ€์ผ
println "Hello, World!"

์–ด๋•Œ์š”? ๋‘ ๋ฒˆ์งธ ๋ฐฉ์‹์ด ๋” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฝํžˆ์ง€ ์•Š๋‚˜์š”?

Step 3: ๋นŒ๋” ํŒจํ„ด ๊ตฌํ˜„ํ•˜๊ธฐ

์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ๋นŒ๋” ํŒจํ„ด์„ ๊ตฌํ˜„ํ•ด๋ณผ๊นŒ์š”? ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ ์‹œ์ž‘ํ•ด๋ณผ๊ฒŒ์š”. ํ”ผ์ž๋ฅผ ์ฃผ๋ฌธํ•˜๋Š” DSL์„ ๋งŒ๋“ค์–ด๋ณผ๊นŒ์š”?


class Pizza {
    String size
    List toppings = []
    
    def size(String s) {
        size = s
    }
    
    def topping(String t) {
        toppings << t
    }
}

def pizza(closure) {
    Pizza pizzaBuilder = new Pizza()
    closure.delegate = pizzaBuilder
    closure()
    return pizzaBuilder
}

def myPizza = pizza {
    size "large"
    topping "cheese"
    topping "pepperoni"
}

println "๋‚ด๊ฐ€ ์ฃผ๋ฌธํ•œ ํ”ผ์ž: ${myPizza.size} ์‚ฌ์ด์ฆˆ, ํ† ํ•‘: ${myPizza.toppings.join(', ')}"

์šฐ์™€~ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ”ผ์ž ์ฃผ๋ฌธ์„ ๋งˆ์น˜ ์˜์–ด๋กœ ๋งํ•˜๋“ฏ์ด ์ฝ”๋“œ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”! ๐Ÿ˜‹๐Ÿ•

์žฌ๋ฏธ์žˆ๋Š” ์‚ฌ์‹ค! ์ด๋Ÿฐ ์‹์œผ๋กœ DSL์„ ๋งŒ๋“ค๋ฉด, ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ž˜ ๋ชจ๋ฅด๋Š” ์‚ฌ๋žŒ๋“ค๋„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ์‰ฝ๊ฒŒ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”!

Step 4: ์ค‘์ฒฉ ๊ตฌ์กฐ ๋งŒ๋“ค๊ธฐ

์‹ค์ œ DSL์€ ๋ณดํ†ต ๋” ๋ณต์žกํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”. ์ค‘์ฒฉ๋œ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊นŒ์š”? ์ด๋ฒˆ์—๋Š” ๊ฐ„๋‹จํ•œ HTML DSL์„ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”.


class HtmlBuilder {
    def html = new StringBuilder()
    
    def methodMissing(String name, args) {
        html << "<$name>"
        if (args) {
            if (args[0] instanceof Closure) {
                args[0].delegate = this
                args[0].call()
            } else {
                html << args[0]
            }
        }
        html << "</$name>"
    }
}

def html(closure) {
    def builder = new HtmlBuilder()
    closure.delegate = builder
    closure()
    return builder.html.toString()
}

def result = html {
    html {
        head {
            title "My Awesome Page"
        }
        body {
            h1 "Welcome!"
            p "This is a paragraph."
        }
    }
}

println result

์™€์šฐ! ์ด์ œ HTML์„ ๋งˆ์น˜ Groovy ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์–ด์š”. ์ด๊ฒŒ ๋ฐ”๋กœ DSL์˜ ํž˜์ด์—์š”! ๐Ÿ˜Ž

๐ŸŽญ DSL์˜ ์žฅ๋‹จ์ 

์ž, ์ด์ œ DSL์— ๋Œ€ํ•ด ๊ฝค ๋งŽ์ด ์•Œ๊ฒŒ ๋˜์—ˆ์ฃ ? ๊ทธ๋Ÿผ DSL์˜ ์žฅ๋‹จ์ ์— ๋Œ€ํ•ด ํ•œ๋ฒˆ ์ƒ๊ฐํ•ด๋ณผ๊นŒ์š”?

์žฅ์  ๐Ÿ‘

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

๋‹จ์  ๐Ÿ‘Ž

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

์ƒ๊ฐํ•ด๋ณด๊ธฐ: DSL์„ ๋งŒ๋“ค ๋•Œ๋Š” ํ•ญ์ƒ ๊ทธ DSL์ด ์ •๋ง ํ•„์š”ํ•œ์ง€ ๊ณ ๋ฏผํ•ด๋ด์•ผ ํ•ด์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ƒˆ๋กœ์šด ์žฌ๋Šฅ์„ ๋“ฑ๋กํ•  ๋•Œ ๊ทธ ์žฌ๋Šฅ์ด ์ •๋ง ๊ฐ€์น˜ ์žˆ๋Š”์ง€ ๊ณ ๋ฏผํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”!

๐Ÿš€ ์‹ค์ „: ๋ณต์žกํ•œ DSL ๋งŒ๋“ค๊ธฐ

์ž, ์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ๊ฑธ ํ† ๋Œ€๋กœ ์กฐ๊ธˆ ๋” ๋ณต์žกํ•œ DSL์„ ๋งŒ๋“ค์–ด๋ณผ๊นŒ์š”? ์ด๋ฒˆ์—๋Š” ๊ฐ„๋‹จํ•œ ๊ฒŒ์ž„ ์บ๋ฆญํ„ฐ๋ฅผ ๋งŒ๋“œ๋Š” DSL์„ ๊ตฌํ˜„ํ•ด๋ณผ๊ฒŒ์š”!


class Character {
    String name
    int health = 100
    int strength = 10
    List<String> skills = []
    Map<String, String> equipment = [:]

    def name(String n) { name = n }
    def health(int h) { health = h }
    def strength(int s) { strength = s }
    def skill(String s) { skills << s }
    def equip(String slot, String item) { equipment[slot] = item }
}

def character(Closure closure) {
    def characterBuilder = new Character()
    closure.delegate = characterBuilder
    closure()
    return characterBuilder
}

def myHero = character {
    name "์Šˆํผ ๊ฐœ๋ฐœ์ž"
    health 120
    strength 15
    skill "์ฝ”๋”ฉ ํญํ’"
    skill "๋””๋ฒ„๊น… ๋งˆ์Šคํ„ฐ"
    equip "๋จธ๋ฆฌ", "์ง€์‹์˜ ์™•๊ด€"
    equip "์†", "๊ธฐ๊ณ„์‹ ํ‚ค๋ณด๋“œ"
}

println "์บ๋ฆญํ„ฐ ์ •๋ณด:"
println "์ด๋ฆ„: ${myHero.name}"
println "์ฒด๋ ฅ: ${myHero.health}"
println "ํž˜: ${myHero.strength}"
println "์Šคํ‚ฌ: ${myHero.skills.join(', ')}"
println "์žฅ๋น„: ${myHero.equipment}"

์™€~ ์ •๋ง ๋ฉ‹์ง„ ์บ๋ฆญํ„ฐ๊ฐ€ ํƒ„์ƒํ–ˆ๋„ค์š”! ๐Ÿ˜Ž ์ด๋ ‡๊ฒŒ DSL์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๊ฐ์ฒด๋„ ์•„์ฃผ ์ง๊ด€์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”.

๐Ÿ’ก DSL ์„ค๊ณ„ ํŒ

DSL์„ ๋งŒ๋“ค ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ๋“ค์ด ์žˆ์–ด์š”. ํ•จ๊ป˜ ์‚ดํŽด๋ณผ๊นŒ์š”?

  1. ๊ฐ„๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜์„ธ์š”: DSL์€ ๊ฐ„๊ฒฐํ• ์ˆ˜๋ก ์ข‹์•„์š”. ๋ถˆํ•„์š”ํ•œ ๋ฌธ๋ฒ•์€ ๊ณผ๊ฐํžˆ ์ œ๊ฑฐํ•˜์„ธ์š”.
  2. ์ผ๊ด€์„ฑ์„ ์ง€ํ‚ค์„ธ์š”: ๋น„์Šทํ•œ ๋™์ž‘์€ ๋น„์Šทํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ํ‘œํ˜„๋˜์–ด์•ผ ํ•ด์š”.
  3. ์ง๊ด€์ ์œผ๋กœ ๋งŒ๋“œ์„ธ์š”: DSL์„ ์ฒ˜์Œ ๋ณด๋Š” ์‚ฌ๋žŒ๋„ ๋Œ€์ถฉ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ด์š”.
  4. ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•˜์„ธ์š”: ๋‚˜์ค‘์— ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์‰ฝ๋„๋ก ์„ค๊ณ„ํ•˜์„ธ์š”.
  5. ๋ฌธ์„œํ™”๋ฅผ ์ž˜ํ•˜์„ธ์š”: ์•„๋ฌด๋ฆฌ ์ง๊ด€์ ์ธ DSL์ด๋ผ๋„ ๋ฌธ์„œํ™”๋Š” ํ•„์ˆ˜์˜ˆ์š”!

๊ฟ€ํŒ! DSL์„ ์„ค๊ณ„ํ•  ๋•Œ๋Š” ํ•ญ์ƒ ์‚ฌ์šฉ์ž์˜ ์ž…์žฅ์—์„œ ์ƒ๊ฐํ•ด๋ณด์„ธ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ์„ ์„ค๋ช…ํ•  ๋•Œ ๊ตฌ๋งค์ž์˜ ์ž…์žฅ์„ ๊ณ ๋ คํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”!

๐Ÿ” Groovy DSL์˜ ์‹ค์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€

Groovy DSL์€ ์‹ค์ œ๋กœ ๋งŽ์€ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์–ด์š”. ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ์‚ดํŽด๋ณผ๊นŒ์š”?

1. Gradle

์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์ž๋“ค์€ ์•„์‹ค ๊ฑฐ์˜ˆ์š”. Gradle์€ ๋นŒ๋“œ ์ž๋™ํ™” ๋„๊ตฌ์ธ๋ฐ, Groovy DSL์„ ์‚ฌ์šฉํ•ด์„œ ๋นŒ๋“œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•ด์š”.


apply plugin: 'java'

dependencies {
    compile 'org.springframework:spring-core:5.2.6.RELEASE'
    testCompile 'junit:junit:4.12'
}

์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ”„๋กœ์ ํŠธ ์„ค์ •์„ ํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋ฉ‹์ง€์ฃ ? ๐Ÿ˜Ž

2. Jenkins Pipeline

CI/CD ๋„๊ตฌ์ธ Jenkins์—์„œ๋„ Groovy DSL์„ ์‚ฌ์šฉํ•ด ํŒŒ์ดํ”„๋ผ์ธ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์–ด์š”.


pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋นŒ๋“œ ๊ณผ์ •์„ ์•„์ฃผ ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”!

3. Spock Framework

Spock์€ Groovy๋กœ ์ž‘์„ฑ๋œ ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ์˜ˆ์š”. Groovy DSL์„ ์‚ฌ์šฉํ•ด์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์•„์ฃผ ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”.


def "๋‘ ์ˆ˜์˜ ํ•ฉ์„ ๊ณ„์‚ฐํ•œ๋‹ค"() {
    given:
    def calculator = new Calculator()

    when:
    def result = calculator.add(a, b)

    then:
    result == expected

    where:
    a | b || expected
    1 | 2 || 3
    3 | 4 || 7
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ๋งˆ์น˜ ๋ฌธ์„œ์ฒ˜๋Ÿผ ์ฝํžˆ์ฃ ? ๐Ÿ‘€

์žฌ๋ฏธ์žˆ๋Š” ์‚ฌ์‹ค: ์ด๋ ‡๊ฒŒ DSL์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด, ์ฝ”๋“œ ์ž์ฒด๊ฐ€ ๋ฌธ์„œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ ์„ค๋ช…์„ ์ฝ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ๊ทธ ์žฌ๋Šฅ์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”!

๐Ÿค” Groovy DSL vs ๋‹ค๋ฅธ ์–ธ์–ด์˜ DSL

Groovy DSL์ด ์ข‹์€ ๊ฑด ์•Œ๊ฒ ๋Š”๋ฐ, ๋‹ค๋ฅธ ์–ธ์–ด์—์„œ๋Š” ์–ด๋–ป๊ฒŒ DSL์„ ๋งŒ๋“ค๊นŒ์š”? ๋ช‡ ๊ฐ€์ง€ ๋น„๊ตํ•ด๋ณผ๊นŒ์š”?

Ruby DSL

Ruby๋„ DSL์„ ๋งŒ๋“ค๊ธฐ ์ข‹์€ ์–ธ์–ด๋กœ ์œ ๋ช…ํ•ด์š”. Ruby on Rails ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์ฃ .


class Product < ActiveRecord::Base
  belongs_to :category
  has_many :orders
  
  validates :name, presence: true
  validates :price, numericality: { greater_than: 0 }
end

Ruby์˜ DSL๋„ ๊ฝค ์ฝ๊ธฐ ์‰ฝ์ฃ ? ํ•˜์ง€๋งŒ Groovy๋งŒํผ ์ž์œ ๋กญ์ง„ ์•Š์•„์š”.

Kotlin DSL

Kotlin๋„ DSL์„ ์ง€์›ํ•ด์š”. ํŠนํžˆ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜์ฃ .


fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()
    html.init()
    return html
}

val result = html {
    head {
        title { +"HTML encoding with Kotlin" }
    }
    body {
        h1 { +"HTML encoding with Kotlin" }
        p { +"this format can be used as an alternative markup to HTML" }
    }
}

Kotlin์˜ DSL์€ ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ์ข‹์•„์š”. ํ•˜์ง€๋งŒ Groovy๋งŒํผ ๋™์ ์ด์ง„ ์•Š์ฃ .

Python DSL

Python์—์„œ๋„ DSL์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”. ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ์–ธ์–ด๋“ค๋งŒํผ ์šฐ์•„ํ•˜์ง„ ์•Š์•„์š”.


from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run()

Python์˜ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ DSL์ด์—์š”. ๊ฐ„๋‹จํ•˜์ง€๋งŒ, Groovy๋งŒํผ ์œ ์—ฐํ•˜์ง„ ์•Š์ฃ .

์ƒ๊ฐํ•ด๋ณด๊ธฐ: ๊ฐ ์–ธ์–ด๋งˆ๋‹ค DSL์„ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์ด ๋‹ค๋ฅด๋„ค์š”. ์–ด๋–ค ์–ธ์–ด๊ฐ€ ๊ฐ€์žฅ ๋งˆ์Œ์— ๋“œ๋‚˜์š”? ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ ์ค‘ ์ž์‹ ์—๊ฒŒ ๋งž๋Š” ๊ฑธ ๊ณ ๋ฅด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ํ”„๋กœ์ ํŠธ์— ๋งž๋Š” ์–ธ์–ด์™€ DSL ์Šคํƒ€์ผ์„ ์„ ํƒํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด์š”!

๐ŸŽจ DSL ๋””์ž์ธ ํŒจํ„ด

DSL์„ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ๋””์ž์ธ ํŒจํ„ด๋“ค์ด ์žˆ์–ด์š”. ๋ช‡ ๊ฐ€์ง€ ์‚ดํŽด๋ณผ๊นŒ์š”?

1. ์ค‘์ฒฉ ํ•จ์ˆ˜ ํŒจํ„ด

์ด ํŒจํ„ด์€ ํ•จ์ˆ˜ ์•ˆ์— ํ•จ์ˆ˜๋ฅผ ์ค‘์ฒฉํ•ด์„œ ๊ตฌ์กฐ๋ฅผ ํ‘œํ˜„ํ•ด์š”.


menu {
    category("์Œ๋ฃŒ") {
        item("์ปคํ”ผ", 3000)
        item("์ฃผ์Šค", 3500)
    }
    category("๋””์ €ํŠธ") {
        item("์ผ€์ดํฌ", 5000)
        item("์ฟ ํ‚ค", 2000)
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์•„์ฃผ ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”!

2. ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹ ํŒจํ„ด

์ด ํŒจํ„ด์€ ๋ฉ”์†Œ๋“œ๋ฅผ ์—ฐ์†ํ•ด์„œ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์ด์—์š”.


new Robot()
    .setName("R2D2")
    .setColor("Silver")
    .setHeight(1.2)
    .build()