Post

๐Ÿน chap10. ๋žŒ๋‹ค๋ฅผ ์ด์šฉํ•œ ๋„๋ฉ”์ธ ์ „์šฉ ์–ธ์–ด

10.1 ๋„๋ฉ”์ธ ์ „์šฉ ์–ธ์–ด(DSL)

: ํŠน์ • ๋น„์ฆˆ๋‹ˆ์Šค ๋„๋ฉ”์ธ์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“  ์–ธ์–ด

ex) ํšŒ๊ณ„ ์ „์šฉ ์†Œํ”„ํŠธ์›จ์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ

์ด ์ƒํ™ฉ์—์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋„๋ฉ”์ธ์—๋Š” ํ†ต์žฅ ์ž…์ถœ๊ธˆ ๋‚ด์—ญ์„œ, ๊ณ„์ขŒ ํ†ตํ•ฉ ๊ฐ™์€ ๊ฐœ๋…์ด ํฌํ•จ๋œ๋‹ค.

์ž๋ฐ”์—์„œ๋Š” ๋„๋ฉ”์ธ์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ํด๋ž˜์Šค์™€ ๋ฉ”์„œ๋“œ ์ง‘ํ•ฉ์ด ํ•„์š”ํ•˜๋‹ค. DSL์ด๋ž€ ํŠน์ • ๋น„์ฆˆ๋‹ˆ์Šค ๋„๋ฉ”์ธ์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋งŒ๋“  API๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

  • DSL์€ ๋ฒ”์šฉ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๊ฐ€ ์•„๋‹ˆ๋‹ค.

    DSL์—์„œ ๋™์ž‘๊ณผ ์šฉ์–ด๋Š” ํŠน์ • ๋„๋ฉ”์ธ์— ๊ตญํ•œ๋˜๋ฏ€๋กœ ๋‹ค๋ฅธ ๋ฌธ์ œ๋Š” ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†๊ณ  ์˜ค์ง ์ž์‹  ์•ž์— ๋†“์ธ ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ• ์ง€์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • DSL์„ ์ด์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ๋„๋ฉ”์ธ์˜ ๋ณต์žก์„ฑ์„ ๋” ์ž˜ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์นœํ™”์ ์ธ DSL์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

    ์ €์ˆ˜์ค€ ๊ตฌํ˜„ ์„ธ๋ถ€ ์‚ฌํ•ญ ๋ฉ”์„œ๋“œ๋Š” ํด๋ž˜์Šค์˜ ๋น„๊ณต๊ฐœ๋กœ ๋งŒ๋“ค์–ด์„œ ์ €์ˆ˜์ค€ ๊ตฌํ˜„ ์„ธ๋ถ€ ๋‚ด์šฉ์€ ์ˆจ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

DSL ๊ฐœ๋ฐœ ์‹œ ๋‘ ๊ฐ€์ง€ ํ•„์š”์„ฑ

  • ์˜์‚ฌ ์†Œํ†ต์˜ ์™•

    ์ฝ”๋“œ์˜ ์˜๋„๊ฐ€ ๋ช…ํ™•ํžˆ ์ „๋‹ฌ๋˜์–ด์•ผ ํ•˜๋ฉฐ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์•„๋‹Œ ์‚ฌ๋žŒ๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํ•œ ๋ฒˆ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ ๋ฒˆ ์ฝ๋Š”๋‹ค

    ๊ฐ€๋…์„ฑ์€ ์œ ์ง€๋ณด์ˆ˜์˜ ํ•ต์‹ฌ์ด๋‹ค. ์ฆ‰ ํ•ญ์ƒ ์šฐ๋ฆฌ์˜ ๋™๋ฃŒ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

10.1.1 DSL์˜ ์žฅ์ ๊ณผ ๋‹จ์ 

DSL์„ ๋„๋ฉ”์ธ์— ์ด์šฉํ•˜๋ฉด ์•ฝ์ด ๋˜๊ฑฐ๋‚˜ ๋…์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

+ : DSL์€ ์ฝ”๋“œ์˜ ๋น„์ฆˆ๋‹ˆ์Šค ์˜๋„๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜๊ณ  ๊ฐ€๋…์„ฑ์„ ๋†’์ธ๋‹ค.

- : DSL ๊ตฌํ˜„์€ ์ฝ”๋“œ์ด๋ฏ€๋กœ ์˜ฌ๋ฐ”๋กœ ๊ฒ€์ฆํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•ด์•ผํ•˜๋Š” ์ฑ…์ž„์ด ๋”ฐ๋ฅธ๋‹ค.

๋”ฐ๋ผ์„œ DSL์˜ ์žฅ์ ๊ณผ ๋น„์šฉ์„ ๋ชจ๋‘ ํ™•์ธํ•ด์•ผ๋งŒ ํ”„๋กœ์ ํŠธ์— DSL์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ํˆฌ์ž๋Œ€๋น„ ๊ธ์ •์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๊ฒƒ์ธ์ง€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

DSL์˜ ์žฅ์ 

  • ๊ฐ„๊ฒฐํ•จ

    API๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์บก์Šํ™”ํ•˜๋ฏ€๋กœ ๋ฐ˜๋ณต์„ ํ”ผํ•  ์ˆ˜ ์žˆ๊ณ  ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

  • ๊ฐ€๋…์„ฑ

    ๋„๋ฉ”์ธ ์˜์—ญ์˜ ์šฉ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋น„ ๋„๋ฉ”์ธ ์ „๋ฌธ๊ฐ€๋„ ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋‹ค์–‘ํ•œ ์กฐ์ง ๊ตฌ์„ฑ์› ๊ฐ„์— ์ฝ”๋“œ์™€ ๋„๋ฉ”์ธ ์˜์—ญ์ด ๊ณต์œ ๋  ์ˆ˜ ์žˆ๋‹ค.

  • ์œ ์ง€๋ณด์ˆ˜

    ์ž˜ ์„ค๊ณ„๋œ DSL๋กœ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ๋Š” ์‰ฝ๊ฒŒ ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ณ  ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค. ์œ ์ง€๋ณด์ˆ˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๊ด€๋ จ ์ฝ”๋“œ ์ฆ‰ ๊ฐ€์žฅ ๋นˆ๋ฒˆํžˆ ๋ฐ”๋€Œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ถ€๋ถ„์— ํŠนํžˆ ์ค‘์š”ํ•˜๋‹ค.

  • ๋†’์€ ์ˆ˜์ค€์˜ ์ถ”์ƒํ™”

    DSL์€ ๋„๋ฉ”์ธ๊ณผ ๊ฐ™์€ ์ถ”์ƒํ™” ์ˆ˜์ค€์—์„œ ๋™์ž‘ํ•˜๋ฏ€๋กœ ๋„๋ฉ”์ธ์˜ ๋ฌธ์ œ์™€ ์ง์ ‘์ ์œผ๋กœ ๊ด€๋ จ๋˜์ง€ ์•Š์€ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ์ˆจ๊ธด๋‹ค.

  • ์ง‘์ค‘

    ๋น„์ฆˆ๋‹ˆ์Šค ๋„๋ฉ”์ธ์˜ ๊ทœ์น™์„ ํ‘œํ˜„ํ•  ๋ชฉ์ ์œผ๋กœ ์„ค๊ณ„๋œ ์–ธ์–ด์ด๋ฏ€๋กœ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ํŠน์ • ์ฝ”๋“œ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ƒ์‚ฐ์„ฑ์ด ์ข‹์•„์ง„๋‹ค.

  • ๊ด€์‹ฌ์‚ฌ๋ถ„๋ฆฌ

    ์ง€์ •๋œ ์–ธ์–ด๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํ‘œํ˜„ํ•จ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ธํ”„๋ผ๊ตฌ์กฐ์™€ ๊ด€๋ จ๋œ ๋ฌธ์ œ์™€ ๋…๋ฆฝ์ ์œผ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๊ด€๋ จ๋œ ์ฝ”๋“œ์—์„œ ์ง‘์ค‘ํ•˜๊ธฐ๊ฐ€ ์šฉ์ดํ•˜๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

DSL์˜ ๋‹จ์ 

  • DSL ์„ค๊ณ„์˜ ์–ด๋ ค์›€

    ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ œํ•œ์ ์ธ ์–ธ์–ด์— ๋„๋ฉ”์ธ ์ง€์‹์„ ๋‹ด๋Š” ๊ฒƒ์ด ์‰ฌ์šด ์ž‘์—…์€ ์•„๋‹ˆ๋‹ค.

  • ๊ฐœ๋ฐœ ๋น„์šฉ

    ์ฝ”๋“œ์— DSL์„ ์ถ”๊ฐ€ํ•˜๋Š” ์ž‘์—…์€ ์ดˆ๊ธฐ ํ”„๋กœ์ ํŠธ์— ๋งŽ์€ ๋น„์šฉ๊ณผ ์‹œ๊ฐ„์ด ์†Œ๋ชจ๋˜๋Š” ์ž‘์—…์ด๋‹ค. ๋˜ํ•œ DSL ์œ ์ง€๋ณด์ˆ˜์™€ ๋ณ€๊ฒฝ์€ ํ”„๋กœ์ ํŠธ์— ๋ถ€๋‹ด์„ ์ฃผ๋Š” ์š”์†Œ๋‹ค.

  • ์ถ”๊ฐ€ ์šฐํšŒ ๊ณ„์ธต

    DSL์€ ์ถ”๊ฐ€์ ์ธ ๊ณ„์ธต์œผ๋กœ ๋„๋ฉ”์ธ ๋ชจ๋ธ์„ ๊ฐ์‹ธ๋ฉฐ ์ด ๋•Œ ๊ณ„์ธต์„ ์ตœ๋Œ€ํ•œ ์ž‘๊ฒŒ ๋งŒ๋“ค์–ด ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํšŒํ”ผํ•œ๋‹ค.

  • ์ƒˆ๋กœ ๋ฐฐ์›Œ์•ผ ํ•˜๋Š” ์–ธ์–ด

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

  • ํ˜ธ์ŠคํŒ… ์–ธ์–ด ํ•œ๊ณ„

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

10.1.2 JVM์—์„œ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ DSL ํ•ด๊ฒฐ์ฑ…

DSL์˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฐ€์žฅ ํ”ํ•œ ๋ฒ™๋ฒ•์€ ๋งˆํ‹ด ํŒŒ์šธ๋Ÿฌ(Martin Fowler)๊ฐ€ ์†Œ๊ฐœํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋‚ด๋ถ€ DSL๊ณผ ์™ธ๋ถ€ DSL์„ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์ด๋‹ค.

  • ๋‚ด๋ถ€ DSL(์ž„๋ฒ ๋””๋“œ DSL)

    : ์ˆœ์ˆ˜ ์ž๋ฐ” ์ฝ”๋“œ ๊ฐ™์€ ๊ธฐ์กด ํ˜ธ์ŠคํŒ… ์–ธ์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„

  • ์™ธ๋ถ€ DSL(์Šคํƒ ๋“œ์–ด๋ก (standalone))

    : ํ˜ธ์ŠคํŒ… ์–ธ์–ด์™€๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์ž์ฒด์˜ ๋ฌธ๋ฒ•์„ ๊ฐ€์ง

  • ๋‹ค์ค‘ DSL

    : JVM์œผ๋กœ ์ธํ•ด ๋‚ด๋ถ€ DSL๊ณผ ์™ธ๋ถ€ DSL์˜ ์ค‘๊ฐ„ ์นดํ…Œ๊ณ ๋ฆฌ์— ํ•ด๋‹นํ•˜๋Š” DSL์ด ๋งŒ๋“ค์–ด์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ์ƒ๊น€.

    โ†’ ์Šค์นผ๋ผ๋‚˜ ๊ทธ๋ฃจ๋น„์ฒ˜๋Ÿผ ์ž๋ฐ”๊ฐ€ ์•„๋‹ˆ์ง€๋งŒ JVM์—์„œ ์‹คํ–‰๋˜๋ฉฐ ๋” ์œ ์—ฐํ•˜๊ณ  ํ‘œํ˜„๋ ฅ์ด ๊ฐ•๋ ฅํ•œ ์–ธ์–ด๋„ ์žˆ๋‹ค.

๋‚ด๋ถ€ DSL

๋‚ด๋ถ€ DSL์ด๋ž€ ์ž๋ฐ”(์˜ˆ์‹œ)๋กœ ๊ตฌํ˜„ํ•œ DSL์„ ์˜๋ฏธํ•œ๋‹ค.

์—ญ์‚ฌ์ ์œผ๋กœ ์ž๋ฐ”๋Š” ๋‹ค์†Œ ๊ท€์ฐฎ๊ณ , ์œ ์—ฐ์„ฑ์ด ๋–จ์–ด์ง€๋Š” ๋ฌธ๋ฒ• ๋•Œ๋ฌธ์— ์ฝ๊ธฐ ์‰ฝ๊ณ , ๊ฐ„๋‹จํ•˜๊ณ , ํ‘œํ˜„๋ ฅ ์žˆ๋Š” DSL์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ํ•œ๊ณ„๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ, ๋žŒ๋‹ค ํ‘œํ˜„์‹์ด ๋“ฑ์žฅํ•˜๋ฉฐ ์–ด๋Š์ •๋„ ํ•ด๊ฒฐ๋  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋žŒ๋‹ค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ต๋ช… ๋‚ด๋ถ€ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด DSL์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์žฅํ™ฉํ•จ์„ ํฌ๊ฒŒ ์ค„์—ฌ ์‹ ํ˜ธ ๋Œ€๋น„ ์žก์Œ ๋น„์œจ์„ ์ ์ • ์ˆ˜์ค€์œผ๋กœ ์œ ์ง€ํ•˜๋Š” DSL์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
// ์ž๋ฐ” 7 ๋ฌธ๋ฒ•์œผ๋กœ ๋ฌธ์ž์—ด ๋ชฉ๋ก ์ถœ๋ ฅ
List<String> numbers = Arrays.asList("one", "two", "three");
**numbers.forEach**(new Customer<String>() {
    @Override
    public void **accept**(String s) {
        **System.out.println(s)**;
    }
});

์œ„ ์ฝ”๋“œ ์˜ˆ์ œ์—์„œ ๊ตต์€ ๊ธ€์”จ๋กœ ํ‘œ์‹œํ•œ ๋ถ€๋ถ„์ด ์ฝ”๋“œ์˜ ์žก์Œ์ด๋‹ค.

โ†’ numbers.forEach, accept, System.out.println(s);

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

1
2
// ์ž๋ฐ” 8 ๋ฌธ๋ฒ•์œผ๋กœ ๋ฌธ์ž์—ด ๋ชฉ๋ก ์ถœ๋ ฅ
numbers.forEach(s -> System.out.println(s));

๋‹ค์Œ์ฒ˜๋Ÿผ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๋กœ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

1
numbers.forEach(System.out::println);

์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ์ˆ ์ ์ธ ๋ถ€๋ถ„์„ ์—ผ๋‘์— ๋‘๊ณ  ์žˆ๋‹ค๋ฉด ์ž๋ฐ”๋ฅผ ์ด์šฉํ•ด DSL์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์ž๋ฐ” ๋ฌธ๋ฒ•์ด ํฐ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์ˆœ์ˆ˜ ์ž๋ฐ”๋กœ DSL์„ ๊ตฌํ˜„ํ•จ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

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

๋‹ค์ค‘ DSL

์š”์ฆ˜ JVM์—์„œ ์‹คํ–‰๋˜๋Š” ์–ธ์–ด๋Š” 100๊ฐœ๊ฐ€ ๋„˜๋Š”๋‹ค.

  • ์Šค์นผ๋ผ, ๋ฃจ๋น„์ฒ˜๋Ÿผ ์œ ๋ช…ํ•œ ์–ธ์–ด๋ผ๋ฉด ์‰ฝ๊ฒŒ ๊ฐœ๋ฐœ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.
  • JRuby๋‚˜ Jython ๊ฐ™์€ ๋‹ค๋ฅธ ์–ธ์–ด๋„ ์ž˜ ์•Œ๋ ค์ง„ JVM ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋‹ค.
  • ๋งˆ์ง€๋ง‰์œผ๋กœ ์ฝ”ํ‹€๋ฆฐ(Kotlin), ์‹ค๋ก (Ceylon) ๊ฐ™์ด ์Šค์นผ๋ผ์™€ ํ˜ธํ™˜์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋‹จ์ˆœํ•˜๊ณ  ์‰ฝ๊ฒŒ ๋ฐฐ์šธ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฐ•์ ์„ ๊ฐ€์ง„ ์ƒˆ ์–ธ์–ด๋„ ์žˆ๋‹ค.

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

ํŠนํžˆ ์Šค์นผ๋ผ๋Š” ์ปค๋ง, ์ž„์˜ ๋ณ€ํ™˜ ๋“ฑ DSL ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ์—ฌ๋Ÿฌ ํŠน์„ฑ์„ ๊ฐ–์ท„๋‹ค.

  • ์ฃผ์–ด์ง„ ํ•จ์ˆ˜ f๋ฅผ ์ฃผ์–ด์ง„ ํšŸ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต ์‹คํ–‰ํ•˜๋Š” ํ•จ์ˆ˜ ๊ตฌํ˜„

์ฒซ ๋ฒˆ์งธ ์‹œ๋„๋กœ ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ฐ˜๋ณต ์‹คํ–‰ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์Šค์นผ๋ผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
def times(i: Int, f: => Unit): Unit = {
    f // fํ•จ์ˆ˜ ์‹คํ–‰
    if (i > 1) times(i - 1, f) //ํšŸ์ˆ˜๊ฐ€ ์–‘์ˆ˜๋ฉด ํšŸ์ˆ˜๋ฅผ ๊ฐ์†Œ์‹œ์ผœ ์žฌ๊ท€์ ์œผ๋กœ ์‹คํ–‰
}

์Šค์นผ๋ผ์—์„œ๋Š” i๊ฐ€ ์•„์ฃผ ํฐ ์ˆซ์ž๋ผ ํ•˜๋”๋ผ๋„ ์ž๋ฐ”์—์„œ์ฒ˜๋Ÿผ ์Šคํƒ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ์Šค์นผ๋ผ๋Š” ๊ผฌ๋ฆฌ ํ˜ธ์ถœ ์ตœ์ ํ™”๋ฅผ ํ†ตํ•ด times ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์Šคํƒ์— ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๋‹ค์Œ์ฒ˜๋Ÿผ โ€œHello Worldโ€๋ฅผ ์„ธ ๋ฒˆ ๋ฐ˜๋ณต ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

1
times(3, println("Hello World"))

times ํ•จ์ˆ˜๋ฅผ ์ปค๋งํ•˜๊ฑฐ๋‚˜ ๋‘ ๊ทธ๋ฃน์œผ๋กœ ์ธ์ˆ˜๋ฅผ ๋†“์„ ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
def times(i: Int)(f: => Unit): Unit = { 
    f
    if (i > 1) times(i - 1)(f)
}

์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰ํ•  ๋ช…๋ น์„ ์ค‘๊ด„ํ˜ธ ์•ˆ์— ๋„ฃ์–ด ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

1
2
3
times(3) {
    println("Hello World")
}

๋งˆ์ง€๋ง‰์œผ๋กœ ์Šค์นผ๋ผ๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜๋ณตํ•  ์ธ์ˆ˜๋ฅผ ๋ฐ›๋Š” ํ•œ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๋ฉด์„œ Int๋ฅผ ์ต๋ช… ํด๋ž˜์Šค๋กœ ์•”๋ฌต์ ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋„๋ก ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
implicit def intToTimes(i: Int) = new { //Int๋ฅผ ๋ฌด๋ช… ํด๋ž˜์Šค๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์•”๋ฌต์  ๋ณ€ํ™˜ ์ •์˜
    //์ด ํด๋ž˜์Šค๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜ f๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” times ํ•จ์ˆ˜ ํ•œ ๊ฐœ๋งŒ ์ •์˜
    def times(f: => Unit): Unit = { 
        // ๋‘ ๋ฒˆ์งธ times ํ•จ์ˆ˜๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋ฒ”์ฃผ์—์„œ ์ •์˜ํ•œ ๋‘ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ์ด์šฉ
        def times(i: Int, f: => Unit): Unit = {
            f
            if (i > 1) times(i - 1, f)
        }
        times(i, f) // ๋‚ด๋ถ€ times ํ•จ์ˆ˜ ํ˜ธ์ถœ
    }
}

์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์ž‘์€ ์Šค์นผ๋ผ ๋‚ด์žฅ DSL ๊ตฌํ˜„ ์‚ฌ์šฉ์ž๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ โ€œHello Worldโ€๋ฅผ ์„ธ ๋ฒˆ ์ถœ๋ ฅํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
3 times {
    println("Hello World")
}

์˜ˆ์ œ์—์„œ ํ™•์ธํ–ˆ๋“ฏ์ด ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋ฌธ๋ฒ•์  ์žก์Œ์ด ์ „ํ˜€ ์—†์œผ๋ฉฐ ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹Œ ์‚ฌ๋žŒ๋„ ์‰ฝ๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ˆซ์ž 3์€ ์ž๋™์œผ๋กœ ์ปดํŒŒ์ผ๋Ÿฌ์— ์˜ํ•ด ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋กœ ๋ณ€ํ™˜๋˜๋ฉฐ i ํ•„๋“œ์— ์ €์žฅ๋œ๋‹ค. ์  ํ‘œ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  times ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ–ˆ๋Š”๋ฐ ์ด๋•Œ ๋ฐ˜๋ณตํ•  ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.

์ž๋ฐ”๋กœ๋Š” ๋น„์Šทํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ธด ์–ด๋ ต๋‹ค. ์ด๋Š” ๋ˆ„๊ฐ€ ๋” DSL ์นœํ™”์ ์ธ์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ณด์—ฌ์ค€๋‹ค. ํ•˜์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ถˆํŽธํ•จ๋„ ์ดˆ๋ž˜ํ•œ๋‹ค.

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

    ์˜ˆ๋ฅผ ๋“ค์–ด ์Šค์นผ๋ผ์™€ ์ž๋ฐ” ์ปฌ๋ ‰์…˜์€ ์„œ๋กœ ํ˜ธํ™˜๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ƒํ˜ธ ์ปฌ๋ ‰์…˜์„ ์ „๋‹ฌํ•˜๋ ค๋ฉด ๊ธฐ์กด ์ปฌ๋ ‰์…˜์„ ๋Œ€์ƒ ์–ธ์–ด์˜ API์— ๋งž๊ฒŒ ๋ณ€ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

์™ธ๋ถ€ DSL

์ƒˆ ์–ธ์–ด๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ , ํŒŒ์„œ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ถ„์„ํ•˜๊ณ , ์™ธ๋ถ€ DSL์„ ์‹คํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ๋งŒ๋“œ๋Š” ์•„์ฃผ ํฐ ์ž‘์—…์„ ํ•ด์•ผํ•œ๋‹ค. ์ด๋“ค ์ž‘์—…์€ ์ผ๋ฐ˜์ ์ธ ์ž‘์—…๋„ ์•„๋‹ˆ๋ฉฐ ์‰ฝ๊ฒŒ ๊ธฐ์ˆ ์„ ์–ป์„ ์ˆ˜๋„ ์—†๋‹ค. ์ • ์ด ๋ฐฉ๋ฒ•์„ ํƒํ•ด์•ผ ํ•œ๋‹ค๋ฉด ANTLR ๊ฐ™์€ ์ž๋ฐ” ๊ธฐ๋ฐ˜ ํŒŒ์„œ ์ƒ์„ฑ๊ธฐ๋ฅผ ์ด์šฉํ•˜๋ฉด ๋„์›€์ด ๋œ๋‹ค.

[ ์™ธ๋ถ€ DSL ๋‹จ์  ]

  • ๋…ผ๋ฆฌ ์ •์—ฐํ•œ ํ”„๋กœ๊ทธ๋žจ ์–ธ์–ด๋ฅผ ์ƒˆ๋กœ ๊ฐœ๋ฐœํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๊ฐ„๋‹จํ•˜์ง€ ์•Š๋‹ค.
  • ์‰ฝ๊ฒŒ ์ œ์–ด ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ฒ˜์Œ ์„ค๊ณ„ํ•œ ๋ชฉ์ ์„ ๋ฒ—์–ด๋‚˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

[ ์™ธ๋ถ€ DSL ์žฅ์  ]

  • ์™ธ๋ถ€ DSL์ด ๋ฌดํ•œํ•œ ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค.

    ์šฐ๋ฆฌ์—๊ฒŒ ํ•„์š”ํ•œ ํŠน์„ฑ์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ œ๊ณตํ•˜๋Š” ์–ธ์–ด๋ฅผ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ œ๋Œ€๋กœ ์–ธ์–ด๋ฅผ ์„ค๊ณ„ํ•˜๋ฉด ์šฐ๋ฆฌ์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฌธ์ œ๋ฅผ ๋ฌ˜์‚ฌํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋Š” ๊ฐ€๋…์„ฑ ์ข‹์€ ์–ธ์–ด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

  • ์ž๋ฐ”๋กœ ๊ฐœ๋ฐœ๋œ ์ธํ”„๋ผ๊ตฌ์กฐ ์ฝ”๋“œ์™€ ์™ธ๋ถ€ DSL๋กœ ๊ตฌํ˜„ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ์ฝ”๋“œ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•œ๋‹ค.

    (ํ•˜์ง€๋งŒ ์ด ๋ถ„๋ฆฌ๋กœ ์ธํ•ด DSL๊ณผ ํ˜ธ์ŠคํŠธ ์–ธ์–ด ์‚ฌ์ด์— ์ธ๊ณต ๊ณ„์ธต์ด ์ƒ๊ธฐ๋ฏ€๋กœ ์–‘๋‚ ์˜ ๊ฒ€๊ณผ ๊ฐ™๋‹ค.)

10.2 ์ตœ์‹  ์ž๋ฐ” API์˜ ์ž‘์€ DSL

์ž๋ฐ” 8์˜ Comparator ์ธํ„ฐํŽ˜์ด์Šค์— ์ƒˆ ๋ฉ”์„œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. ๋žŒ๋‹ค๊ฐ€ ์–ด๋–ป๊ฒŒ ๋„ค์ดํ‹ฐ๋ธŒ ์ž๋ฐ” API์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ๋ฉ”์„œ๋“œ ๊ฒฐํ•ฉ๋„๋ฅผ ๋†’์˜€๋Š”์ง€ ํ™•์ธํ•ด๋ณธ๋‹ค.

์‚ฌ๋žŒ(persons)๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ์ฒด ๋ชฉ๋ก์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋ฐ ์‚ฌ๋žŒ์˜ ๋‚˜์ด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฐ์ฒด๋ฅผ ์ •๋ ฌํ•œ๋‹ค ๊ฐ€์ •ํ•˜์ž. ๋žŒ๋‹ค๊ฐ€ ์—†์œผ๋ฉด ๋‚ด๋ถ€ ํด๋ž˜์Šค๋กœ Comparator ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

1
2
3
4
5
Collections.sort(persons, new Comparator<Person>() {
    public int compare(Person p1, Person p2) {
        return p1.getAge() - p2.getAge();
    }
});

๋‚ด๋ถ€ ํด๋ž˜์Šค๋ฅผ ๊ฐ„๋‹จํ•œ ๋žŒ๋‹ค ํ‘œํ˜„์‹์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

1
Collections.sort(persons, (p1, p2) -> p1.getAge() - p2.getAge());

์ด ๊ธฐ๋ฒ•์€ ์ฝ”๋“œ์˜ ์‹ ํ˜ธ ๋Œ€๋น„ ์žก์Œ ๋น„์œจ์„ ์ค„์ด๋Š”๋ฐ ํŠนํžˆ ์œ ์šฉํ•˜๋‹ค. ํ•˜์ง€๋งŒ ์ž๋ฐ”๋Š” Comparator ๊ฐ์ฒด๋ฅผ ์ข€ ๋” ๊ฐ€๋…์„ฑ ์žˆ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ •์  ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฉ”์„œ๋“œ ์ง‘ํ•ฉ๋„ ์ œ๊ณตํ•œ๋‹ค. ์ด๋“ค ์ •์  ๋ฉ”์„œ๋“œ๋Š” Comparator ์ธํ„ฐํŽ˜์ด์Šค์— ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. ์ •์ ์œผ๋กœ Comparator.comparing ๋ฉ”์„œ๋“œ๋ฅผ ์ž„ํฌํŠธํ•ด ์œ„ ์˜ˆ์ œ๋ฅผ ๋‹ค์Œ์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
Collections.sort(persons, comparing(p -> p.getAge()));

๋žŒ๋‹ค๋ฅผ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๋กœ ๋Œ€์‹ ํ•ด ์ฝ”๋“œ๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
Collections.sort(persons, comparing(Person::getAge));

๊ทธ ์™ธ์—๋„ ์ž๋ฐ” 8์—์„œ ์ถ”๊ฐ€๋œ reverse ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์‚ฌ๋žŒ๋“ค์„ ๋‚˜์ด ์—ญ์ˆœ์œผ๋กœ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

1
Collections.sort(persons, comparing(Person::getAge).reverse());

๋‹ค์Œ์ฒ˜๋Ÿผ ์ด๋ฆ„์œผ๋กœ ๋น„๊ต๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” Comparator๋ฅผ ๊ตฌํ˜„ํ•ด ๊ฐ™์€ ๋‚˜์ด์˜ ์‚ฌ๋žŒ๋“ค์„ ์•ŒํŒŒ๋ฒณ ์ˆœ์œผ๋กœ ์ •๋ ฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

1
2
Collections.sort(persons, comparing(Person::getAge)
            .thenComparing(Person::getName));

๋งˆ์ง€๋ง‰์œผ๋กœ List ์ธํ„ฐํŽ˜์ด์Šค์— ์ถ”๊ฐ€๋œ ์ƒˆ sort ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด ์ฝ”๋“œ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
persons.sort(comparing(Person::getAge)
       .thenComparing(Person::getName));

์ด ์ž‘์€ API๋Š” ์ปฌ๋ ‰์…˜ ์ •๋ ฌ ๋„๋ฉ”์ธ์˜ ์ตœ์†Œ DSL์ด๋‹ค. ์ž‘์€ ์˜์—ญ์— ๊ตญํ•œ๋œ ์˜ˆ์ œ์ง€๋งŒ ์ด๋ฏธ ๋žŒ๋‹ค์™€ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๋ฅผ ์ด์šฉํ•œ DSL์ด ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ, ์žฌ์‚ฌ์šฉ์„ฑ, ๊ฒฐํ•ฉ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋Š”์ง€ ๋ณด์—ฌ์ค€๋‹ค.

10.2.1 ์ŠคํŠธ๋ฆผ API๋Š” ์ปฌ๋ ‰์…˜์„ ์กฐ์ž‘ํ•˜๋Š” DSL

Stream ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ž๋ฐ” API์— ์ž‘์€ ๋‚ด๋ถ€ DSL์„ ์ ์šฉํ•œ ์ข‹์€ ์˜ˆ๋‹ค. Stream์€ ์ปฌ๋ ‰์…˜์˜ ํ•ญ๋ชฉ์„ ํ•„ํ„ฐ, ์ •๋ ฌ, ๋ณ€ํ™˜, ๊ทธ๋ฃนํ™”, ์กฐ์ž‘ํ•˜๋Š” ์ž‘์ง€๋งŒ ๊ฐ•๋ ฅํ•œ DSL๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๋กœ๊ทธ ํŒŒ์ผ์„ ์ฝ์–ด์„œ โ€œERRORโ€๋ผ๋Š” ๋‹จ์–ด๋กœ ์‹œ์ž‘ํ•˜๋Š” ํŒŒ์ผ์˜ ์ฒซ 40ํ–‰์„ ์ˆ˜์ง‘ํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

1
2
3
4
5
6
7
8
9
10
11
12
13
// ๋ฐ˜๋ณต ํ˜•์‹์œผ๋กœ ์˜ˆ์ œ ๋กœ๊ทธ ํŒŒ์ผ์—์„œ ์—๋Ÿฌ ํ–‰์„ ์ฝ๋Š” ์ฝ”๋“œ 
List<String> errors = new ArrayList<>();
int errorCount = 0;
BufferedReader bufferedReader
    = new BufferedReader(new FileReader(fileName));
String line = bufferedReader.readLine();
while (errorCount < 40 && line != null) {
    if (line.startsWith("ERROR")) {
        errors.add(line);
        errorCount++;
    }
    line = bufferedReader.readLine();
}

์ฝ”๋“œ๊ฐ€ ์žฅํ™ฉํ•ด์„œ ์˜๋„๋ฅผ ํ•œ ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค. ๋ฌธ์ œ๊ฐ€ ๋ถ„๋ฆฌ๋˜์ง€ ์•Š์•„ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋ชจ๋‘ ์ €ํ•˜๋˜์—ˆ๋‹ค. ๊ฐ™์€ ์˜๋ฌด๋ฅผ ์ง€๋‹Œ ์ฝ”๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ํ–‰์— ๋ถ„์‚ฐ๋˜์–ด ์žˆ๋‹ค.

  • FileReader๊ฐ€ ๋งŒ๋“ค์–ด์ง
  • ํŒŒ์ผ์ด ์ข…๋ฃŒ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” while ๋ฃจํ”„์˜ ๋‘ ๋ฒˆ์งธ ์กฐ๊ฑด
  • ํŒŒ์ผ์˜ ๋‹ค์Œ ํ–‰์„ ์ฝ๋Š” while ๋ฃจํ”„์˜ ๋งˆ์ง€๋ง‰ ํ–‰

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ฒซ 40ํ–‰์„ ์ˆ˜์ง‘ํ•˜๋Š” ์ฝ”๋“œ๋„ ์„ธ ๋ถ€๋ถ„์œผ๋กœ ํฉ์–ด์ ธ์žˆ๋‹ค.

  • errorCount ๋ณ€์ˆ˜๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ์ฝ”๋“œ
  • while ๋ฃจํ”„์˜ ์ฒซ ๋ฒˆ์งธ ์กฐ๊ฑด
  • โ€œErrorโ€์„ ๋กœ๊ทธ์—์„œ ๋ฐœ๊ฒฌํ•˜๋ฉด ์นด์šดํ„ฐ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ํ–‰

Stream ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•ด ํ•จ์ˆ˜ํ˜•์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋” ์‰ฝ๊ณ  ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
// ํ•จ์ˆ˜ํ˜•์œผ๋กœ ๋กœ๊ทธ ํŒŒ์ผ์˜ ์—ฌ๋Ÿฌ ํ–‰ ์ฝ์Œ
List<String> errors 
		= Files.lines(Paths.get(fileName)) // ํŒŒ์ผ์„ ์—ด์–ด์„œ ๋ฌธ์ž์—ด ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ฆ
                .filter(line -> line.startsWith("ERROR")) // "ERROR"๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ–‰์„ ํ•„ํ„ฐ๋ง
                .limit(40) // ๊ฒฐ๊ณผ๋ฅผ ์ฒซ 40ํ–‰์œผ๋กœ ์ œํ•œ
                .collect(toList()); // ๊ฒฐ๊ณผ ๋ฌธ์ž์—ด์„ ๋ฆฌ์ŠคํŠธ๋กœ ์ˆ˜์ง‘

String์€ ํŒŒ์ผ์—์„œ ํŒŒ์‹ฑํ•  ํ–‰์„ ์˜๋ฏธํ•˜๋ฉฐ Files.lines๋Š” ์ •์  ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฉ”์„œ๋“œ๋กœ Stream์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ํŒŒ์ผ์„ ํ•œ ํ–‰์”ฉ ์ฝ๋Š” ๋ถ€๋ถ„์˜ ์ฝ”๋“œ๋Š” ์ด๊ฒŒ ์ „๋ถ€๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ limit(40)์ด๋ผ๋Š” ์ฝ”๋“œ๋กœ ์—๋Ÿฌ ํ–‰์„ ์ฒซ 40๊ฐœ๋งŒ ์ˆ˜์ง‘ํ•œ๋‹ค.

์ŠคํŠธ๋ฆผ API์˜ ํ”Œ๋ฃจ์–ธํŠธ ํ˜•์‹์€ ์ž˜ ์„ค๊ณ„๋œ DSL์˜ ๋˜ ๋‹ค๋ฅธ ํŠน์ง•์ด๋‹ค. ๋ชจ๋“  ์ค‘๊ฐ„ ์—ฐ์‚ฐ์€ ๊ฒŒ์œผ๋ฅด๋ฉฐ ๋‹ค๋ฅธ ์—ฐ์‚ฐ์œผ๋กœ ํŒŒ์ดํ”„๋ผ์ธ๋  ์ˆ˜ ์žˆ๋Š” ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค. ์ตœ์ข… ์—ฐ์‚ฐ์€ ์ ๊ทน์ ์ด๋ฉฐ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์ด ๊ณ„์‚ฐ์„ ์ผ์œผํ‚จ๋‹ค.

10.2.2 ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” DSL์ธ Collectors

Collector ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์„ ์ˆ˜ํ–‰ํ•˜๋Š” DSL๋กœ ๊ฐ„์ฃผํ•  ์ˆ˜ ์žˆ๋‹ค.

Collector ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋‹ค์ค‘ ํ•„๋“œ ์ •๋ ฌ์„ ์ง€์›ํ•˜๋„๋ก ํ•ฉ์ณ์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, Collectors๋Š” ๋‹ค์ค‘ ์ˆ˜์ค€ ๊ทธ๋ฃนํ™”๋ฅผ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ์ณ์งˆ ์ˆ˜ ์žˆ๋‹ค.

ex) ์ž๋™์ฐจ๋ฅผ ๋ธŒ๋žœ๋“œ๋ณ„, ์ƒ‰์ƒ๋ณ„๋กœ ๊ทธ๋ฃนํ™”

1
2
3
Map<String, Map<Color, List<Car>>> carsByBrandAndColor =
    cars.stream().collect(groupingBy(Car::getBrand,
                            groupingBy(Car::getColor)));

๋‘ Comparator๋ฅผ ํ”Œ๋ฃจ์–ธํŠธ ๋ฐฉ์‹(๋ฉ”์„œ๋“œ ์ฒด์ด๋‹)์œผ๋กœ ์—ฐ๊ฒฐํ•ด์„œ ๋‹ค์ค‘ ํ•„๋“œ Comparator๋ฅผ ์ •์˜ํ–ˆ๋‹ค. โ†’ comparing().thenComparing()

1
2
Collector<Person> comparator =
    comparing(Person::getAge).thenComparing(Person::getName);

๋ฐ˜๋ฉด Collectors API๋ฅผ ์ด์šฉํ•ด Collectors๋ฅผ ์ค‘์ฒฉํ•จ์œผ๋กœ ๋‹ค์ค‘ ์ˆ˜์ค€ Collector๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

1
2
Collector<? super Car, ?, Map<Brand, Map<Color, List<Car>>>> carGroupingCollector =
    groupingBy(Car::getBrand, groupingBy(Car::getColor));

ํŠนํžˆ ์…‹ ์ด์ƒ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐํ•ฉํ•  ๋•Œ๋Š” ๋ณดํ†ต ํ”Œ๋ฃจ์–ธํŠธ ํ˜•์‹์ด ์ค‘์ฒฉ ํ˜•์‹์— ๋น„ํ•ด ๊ฐ€๋…์„ฑ์ด ์ข‹๋‹ค. ์‚ฌ์‹ค ๊ฐ€์žฅ ์•ˆ์ชฝ์˜ Collector๊ฐ€ ์ฒซ ๋ฒˆ์งธ๋กœ ํ‰๊ฐ€๋˜์–ด์•ผ ํ•˜์ง€๋งŒ ๋…ผ๋ฆฌ์ ์œผ๋กœ๋Š” ์ตœ์ข… ๊ทธ๋ฃนํ™”์— ํ•ด๋‹นํ•˜๋ฏ€๋กœ ์„œ๋กœ ๋‹ค๋ฅธ ํ˜•์‹์€ ์ด๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€๋ฅผ ์ƒ๋ฐ˜์ ์œผ๋กœ ๋ณด์—ฌ์ค€๋‹ค.

ํ”Œ๋ฃจ์–ธํŠธ ๋Œ€์‹  ์ค‘์ฒฉ ์‚ฌ์šฉ์‹œ

์•ˆ์ชฝ ๊ทธ๋ฃนํ™”๊ฐ€ ์ฒ˜์Œ ํ‰๊ฐ€๋˜๊ณ  ๋ฐ˜๋Œ€๋กœ ์ฝ”๋“œ์—์„œ๋Š” ๊ฐ€์žฅ ๋‚˜์ค‘์— ๋“ฑ์žฅ

groupingBy ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ์— ์ž‘์—…์„ ์œ„์ž„ํ•˜๋Š” GroupingBuilder๋ฅผ ๋งŒ๋“ค๋ฉด ๋” ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. GroupingBuilder๋Š” ์œ ์—ฐํ•œ ๋ฐฉ์‹์œผ๋กœ ์—ฌ๋Ÿฌ ๊ทธ๋ฃนํ™” ์ž‘์—…์„ ๋งŒ๋“ ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ์œ ์—ฐํ•œ ๊ทธ๋ฃนํ™” ์ปฌ๋ ‰ํ„ฐ ๋นŒ๋”
import static java.util.stream.Collectors.groupingBy;
public class GroupingBuilder<T, D, K> {
    private final Collector<? super T, ?, Map<K, D>> collector;

    private GroupingBuilder(Collector<? super T, ?, Map<K, D>> collector) {
        this.collector = collector;
    }

    public Collector<? super T, ?, Map<K, D>> get() {
        return collector;
    }

    public <J> GroupingBuilder<T, Map<K, D> J>
                after(Function<? super T, ? extends J> classifier) {
        return new GroupingBuilder<>(groupingBy(classifier, collector));
    }

    public static <T, D, K> GroupingBuilder<T, List<T>, K>
                groupOn(Function<? super T, ? extends K> classifier) {
        return new GroupingBuilder<>(groupingBy(classifier));
    }
}

ํ”Œ๋ฃจ์–ธํŠธ ํ˜•์‹ ๋นŒ๋”์— ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ์žˆ์„๊นŒ?

1
2
Collector<? super Car, ?, Map<Brand, Map<Color, List<Car>>>> carGroupingCollector =
    groupOn(Car::getColor).after(Car::getBrand).get()

์ค‘์ฒฉ๋œ ๊ทธ๋ฃนํ™” ์ˆ˜์ค€์— ๋ฐ˜๋Œ€๋กœ ๊ทธ๋ฃนํ™” ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์œ ํ‹ธ๋ฆฌํ‹ฐ ์‚ฌ์šฉ ์ฝ”๋“œ๊ฐ€ ์ง๊ด€์ ์ด์ง€ ์•Š๋‹ค. ์ž๋ฐ” ํ˜•์‹ ์‹œ์Šคํ…œ์œผ๋กœ๋Š” ์ด๋Ÿฐ ์ˆœ์„œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜๋Š” ์—†๋‹ค.

10.3 ์ž๋ฐ”๋กœ DSL์„ ๋งŒ๋“œ๋Š” ํŒจํ„ด๊ณผ ๊ธฐ๋ฒ•

DSL์€ ํŠน์ • ๋„๋ฉ”์ธ ๋ชจ๋ธ์— ์ ์šฉํ•  ์นœํ™”์ ์ด๊ณ  ๊ฐ€๋…์„ฑ ๋†’์€ API๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๊ฐ„๋‹จํ•œ ๋„๋ฉ”์ธ ๋ชจ๋ธ์„ ์ •์˜ํ•˜๊ณ  DSL์„ ๋งŒ๋“œ๋Š” ํŒจํ„ด์„ ์‚ดํŽด๋ณธ๋‹ค.

๋„๋ฉ”์ธ ๋ชจ๋ธ ์„ธ ๊ฐ€์ง€๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.

  1. ์ฃผ์–ด์ง„ ์‹œ์žฅ์— ์ฃผ์‹ ๊ฐ€๊ฒฉ์„ ๋ชจ๋ธ๋งํ•˜๋Š” ์ˆœ์ˆ˜ ์ž๋ฐ” ๋นˆ์ฆˆ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Stock {

    private String symbol;
    private String market;

    public String getSymbol() {
        return symbol;
    }

    public void setSymbol(String symbol) {
        this.symbol = symbol;
    }

    public String getMarket() {
        return market;
    }

    public void setMarket(String market) {
        this.market = market;
    }
}
  1. ์ฃผ์–ด์ง„ ๊ฐ€๊ฒฉ์—์„œ ์ฃผ์–ด์ง„ ์–‘์˜ ์ฃผ์‹์„ ์‚ฌ๊ฑฐ๋‚˜ ํŒŒ๋Š” ๊ฑฐ๋ž˜
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Trade {
    public enum Type { BUY, SELL }
    private Type type;

    private Stock stock;
    private int quantity;
    private double price;

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public Stock getStock() {
        return stock;
    }

    public void setStock(Stock stock) {
        this.stock = stock;
    }

    public double getValue() {
        return quantity * price;
    }
}
  1. ๊ณ ๊ฐ์ด ์š”์ฒญํ•œ ํ•œ ๊ฐœ ์ด์ƒ์˜ ๊ฑฐ๋ž˜์˜ ์ฃผ๋ฌธ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Order {

    private String customer;
    private List<Trade> trades = new ArrayList<>();

    public void addTrade(Trade trade) {
        trades.add(trade);
    }

    public String getCustomer() {
        return customer;
    }

    public void setCustomer(String customer) {
        this.customer = customer;
    }

    public double getValue() {
        return trades.stream().mapToDouble(Trade::getValue).sum();
    }
}

๋„๋ฉ”์ธ ๋ชจ๋ธ์€ ์ง๊ด€์ ์ด๋‹ค. ์ฃผ๋ฌธ์„ ์˜๋ฏธํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์กฐ๊ธˆ ๊ท€์ฐฎ์€ ์ž‘์—…์ด๋‹ค.

BigBank๋ผ๋Š” ๊ณ ๊ฐ์ด ์š”์ฒญํ•œ ๋‘ ๊ฑฐ๋ž˜๋ฅผ ํฌํ•จํ•˜๋Š” ์ฃผ๋ฌธ ๋งŒ๋“ค๊ธฐ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Order order = new Order();
order.setCustomer("BigBank");

Trade trade1 = new Trade();
trade1.setType(Trade.Type.BUY);

Stock stock1 = new Stock();
stock1.setSymbol("IBM");
stock1.setMarket("NYSE");

trade1.setStock(stock1);
trade1.setPrice(125.00);
trade1.setQuantity(80);
order.addTrade(trade1);

Trade trade2 = new Trade();
trade2.setType(Trade.Type.BUY);

Stock stock2 = new Stock();
stock2.setSymbol("GOOGLE");
stock2.setMarket("NASDAQ");

trade2.setStock(stock2);
trade2.setPrice(375.00);
trade2.setQuantity(50);
order.addTrade(trade2);

์ฝ”๋“œ๊ฐ€ ์ƒ๋‹นํžˆ ์žฅํ™ฉํ•œ ํŽธ์ด๋‹ค. ์กฐ๊ธˆ ๋” ์ง์ ‘์ ์ด๊ณ , ์ง๊ด€์ ์œผ๋กœ ๋„๋ฉ”์ธ ๋ชจ๋ธ์„ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ๋Š” DSL์ด ํ•„์š”ํ•˜๋‹ค.

10.3.1 ๋ฉ”์„œ๋“œ ์ฒด์ธ

DSL์—์„œ ๊ฐ€์žฅ ํ”ํ•œ ๋ฐฉ์‹์ธ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์ฒด์ธ์œผ๋กœ ๊ฑฐ๋ž˜ ์ฃผ๋ฌธ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
// ๋ฉ”์„œ๋“œ ์ฒด์ธ์œผ๋กœ ์ฃผ์‹ ๊ฑฐ๋ž˜ ์ฃผ๋ฌธ ๋งŒ๋“ค๊ธฐ
Order order = forCustomer("BigBank")
            .buy(80)
            .stock("IBM")
            .on("NYSE")
            .at(125.00)
            .sell(50)
            .stock("GOOGLE")
            .on("NASDAQ")
            .at(375.00)
            .end();

์ƒ๋‹นํžˆ ์ฝ”๋“œ๊ฐ€ ๊ฐœ์„ ๋˜์—ˆ๋‹ค.

ํ”Œ๋ฃจ์–ธํŠธ API๋กœ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ช‡ ๊ฐœ์˜ ๋นŒ๋”๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ตœ์ƒ์œ„ ์ˆ˜์ค€ ๋นŒ๋”๋ฅผ ๋งŒ๋“ค๊ณ  ์ฃผ๋ฌธ์„ ๊ฐ์‹ผ ๋‹ค์Œ ํ•œ ๊ฐœ ์ด์ƒ์˜ ๊ฑฐ๋ž˜๋ฅผ ์ฃผ๋ฌธ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// ๋ฉ”์„œ๋“œ ์ฒด์ธ DSL์„ ์ œ๊ณตํ•˜๋Š” ์ฃผ๋ฌธ ๋นŒ๋”
public class MethodChainingOrderBuilder {

    public final Order order = new Order(); //๋นŒ๋”๋กœ ๊ฐ์‹ผ ์ฃผ๋ฌธ

    private MethodChainingOrderBuilder(String customer) {
        order.setCustomer(customer);
    }

    public static MethodChainingOrderBuilder forCustomer(String customer) {
        // ๊ณ ๊ฐ์˜ ์ฃผ๋ฌธ์„ ๋งŒ๋“œ๋Š” ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ 
        return new MethodChainingOrderBuilder(customer);
    }

    public TradeBuilder buy(int quantity) {
        // ์ฃผ์‹์„ ์‚ฌ๋Š” TradeBuilder ๋งŒ๋“ค๊ธฐ 
        return new TradeBuilder(this, Trade.Type.BUY, quantity); 
    }

    public TradeBuilder sell(int quantity) {
        // ์ฃผ์‹์„ ํŒŒ๋Š” TradeBuilder ๋งŒ๋“ค๊ธฐ
        return new TradeBuilder(this, Trade.Type.SELL, quantity); 
    }

    public MethodChainingOrderBuilder addTrade(Trade trade) {
        order.addTrade(trade); // ์ฃผ๋ฌธ์— ์ฃผ์‹ ์ถ”๊ฐ€
        // ์œ ์—ฐํ•˜๊ฒŒ ์ถ”๊ฐ€ ์ฃผ๋ฌธ์„ ๋งŒ๋“ค์–ด ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฃผ๋ฌธ ๋นŒ๋” ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜
        return this; 
    }

    public Order end() {
        return order; // ์ฃผ๋ฌธ ๋งŒ๋“ค๊ธฐ๋ฅผ ์ข…๋ฃŒํ•˜๊ณ  ๋ฐ˜ํ™˜
    }
}

์ฃผ๋ฌธ ๋นŒ๋”์˜ buy(), sell() ๋ฉ”์„œ๋“œ๋Š” ๋‹ค๋ฅธ ์ฃผ๋ฌธ์„ ๋งŒ๋“ค์–ด ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž์‹ ์„ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TradeBuilder {
    private final MethodChainingOrderBuilder builder;
    public final Trade trade = new Trade();

    private TradeBuilder(MethodChainingOrderBuilder builder, 
                        Trade.Type type, int quantity) {
        this.builder = builder;
        trade.setType(type);
        trade.setQuantity(quantity);
    }

    public StockBuilder stock(String symbol) {
        return new StockBuilder(builder, trade, symbol);
    }
}

๋นŒ๋”๋ฅผ ๊ณ„์† ์ด์–ด๊ฐ€๋ ค๋ฉด Stock ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” TradeBuilder์˜ ๊ณต๊ฐœ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class StockBuilder {
    private final MethodChainingOrderBuilder builder;
    private final Trade trade;
    private final Stock stock = new Stock();

    private StockBuilder(MethodChainingOrderBuilder builder,
                        Trade trade, String symbol) {
        this.builder = builder;
        this.trade = trade;
        stock.setSymbol(symbol);
    }

    public TradeBuilderWithStock on(String market) {
        stock.setMarket(market);
        trade.setStock(stock);
        return new TradeBuilderWithStock(builder, trade);
    }
}

StockBuilder๋Š” ์ฃผ์‹์˜ ์‹œ์žฅ์„ ์ง€์ •ํ•˜๊ณ , ๊ฑฐ๋ž˜์— ์ฃผ์‹์„ ์ถ”๊ฐ€ํ•˜๊ณ , ์ตœ์ข… ๋นŒ๋”๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” on() ๋ฉ”์„œ๋“œ ํ•œ ๊ฐœ๋ฅผ ์ •์˜ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TradeBuilderWithStock {
    private final MethodChainingOrderBuilder builder;
    private final Trade trade;

    public TradeBuilderWithStock(MethodChainingOrderBuilder builder, Trade trade) {
        this.builder = builder;
        this.trade = trade;
    }

    public MethodChainingOrderBuilder at(double price) {
        trade.setPrice(price);
        return builder.addTrade(trade);
    }
}

ํ•œ ๊ฐœ์˜ ๊ณต๊ฐœ ๋ฉ”์„œ๋“œ TradeBuilderWithStock์€ ๊ฑฐ๋ž˜๋˜๋Š” ์ฃผ์‹์˜ ๋‹จ์œ„ ๊ฐ€๊ฒฉ์„ ์„ค์ •ํ•œ ๋‹ค์Œ ์›๋ž˜ ์ฃผ๋ฌธ ๋นŒ๋”๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. MethodChainingOrderBuilder๊ฐ€ ๋๋‚  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ๊ฑฐ๋ž˜๋ฅผ ํ”Œ๋ฃจ์–ธํŠธ ๋ฐฉ์‹์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

[ ์žฅ์  ]

โ†’ ์ฃผ๋ฌธ์— ์‚ฌ์šฉํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋นŒ๋” ๋‚ด๋ถ€๋กœ ๊ตญํ•œ๋œ๋‹ค.

โ†’ ์ •์  ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ์„ ์ตœ์†Œํ™”ํ•˜๊ณ  ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด ์ธ์ˆ˜์˜ ์ด๋ฆ„์„ ๋Œ€์‹ ํ•˜๋„๋ก ๋งŒ๋“ฆ์œผ๋กœ ์ด๋Ÿฐ ํ˜•์‹์˜ DSL์˜ ๊ฐ€๋…์„ฑ ๊ฐœ์„  ํšจ๊ณผ

โ†’ ๋ถ€๋ถ„์  ์žก์Œ์ด ์ตœ์†Œํ™”

[ ๋‹จ์  ]

โ†’ ๋นŒ๋”๋ฅผ ๊ตฌํ˜„ํ•ด์•ผํ•จ

โ†’ ์ƒ์œ„ ์ˆ˜์ค€์˜ ๋นŒ๋”๋ฅผ ํ•˜์œ„ ์ˆ˜์ค€์˜ ๋นŒ๋”์™€ ์—ฐ๊ฒฐํ•  ์ ‘์ฐฉ ๋งŽ์€ ์ ‘์ฐฉ ์ฝ”๋“œ๊ฐ€ ํ•„์š”

โ†’ ๋„๋ฉ”์ธ ๊ฐ์ฒด์˜ ์ค‘์ฒฉ ๊ตฌ์กฐ์™€ ์ผ์น˜ํ•˜๊ฒŒ ๋“ค์—ฌ์“ฐ๊ธฐ๋ฅผ ๊ฐ•์ œํ•˜๋Š” ๋ฐฉ๋ฒ• ์—†์Œ

10.3.2 ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜ ์ด์šฉ

์ค‘์ฒฉ๋œ ํ•จ์ˆ˜ DSL ํŒจํ„ด์€ ๋‹ค๋ฅธ ํ•จ์ˆ˜ ์•ˆ์— ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๋„๋ฉ”์ธ ๋ชจ๋ธ์„ ๋งŒ๋“ ๋‹ค.

1
2
3
4
5
// ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜๋กœ ์ฃผ์‹ ๊ฑฐ๋ž˜ ๋งŒ๋“ค๊ธฐ
Order order = order("BigBank", 
                    buy(80, stock("IBM", on("NYSE")), at(125.00)),
                    sell(50, stock("GOOGLE", on("NASDAQ")), at(375.00))
                    );

๋‹ค์Œ ์˜ˆ์ œ ์ฝ”๋“œ์˜ NestedFunctionOrderBuilder๋Š” ์ด๋Ÿฐ DSL ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ API๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Œ์„ ๋ณด์—ฌ์ค€๋‹ค(๋ชจ๋“  ์ •์  ๋ฉ”์„œ๋“œ๋ฅผ ์ž„ํฌํŠธํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜ DSL์„ ์ œ๊ณตํ•˜๋Š” ์ฃผ๋ฌธ ๋นŒ๋”
public class NestedFunctionOrderBuilder {

    public static Order order(String customer, Trade... trades) {
        Order order = new Order(); // ํ•ด๋‹น ๊ณ ๊ฐ์˜ ์ฃผ๋ฌธ ๋งŒ๋“ค๊ธฐ
        order.setCustomer(customer);
        Stream.of(trades).forEach(order::addTrade); // ์ฃผ๋ฌธ์— ๋ชจ๋“  ๊ฑฐ๋ž˜ ์ถ”๊ฐ€
        return order;
    }

    public static Trade buy(int quantity, Stock stock, double price) {
        return buildTrade(quantity, stock, price, Trade.Type.BUY); // ์ฃผ์‹ ๋งค์ˆ˜ ๊ฑฐ๋ž˜ ๋งŒ๋“ค๊ธฐ
    }

    public static Trade sell(int quantity, Stock stock, double price) {
        return buildTrade(quantity, stock, price, Trade.Type.SELL); // ์ฃผ์‹ ๋งค๋„ ๊ฑฐ๋ž˜ ๋งŒ๋“ค๊ธฐ
    }

    private static Trade buildTrade(int quantity, Stock stock, double price, Trade.Type buy) {
        Trade trade = new Trade();
        trade.setQuantity(quantity);
        trade.setType(buy);
        trade.setStock(stock);
        trade.setPrice(price);
        return trade;
    }

    // ๊ฑฐ๋ž˜๋œ ์ฃผ์‹์˜ ๋‹จ๊ฐ€๋ฅผ ์ •์˜ํ•˜๋Š” ๋”๋ฏธ ๋ฉ”์„œ๋“œ 
    public static double at(double price) {
        return price;
    }

    public static Stock stock(String symbol, String market) {
        Stock stock = new Stock(); //๊ฑฐ๋ž˜๋œ ์ฃผ์‹ ๋งŒ๋“ค๊ธฐ
        stock.setSymbol(symbol);
        stock.setMarket(market);
        return stock;
    }

    // ์ฃผ์‹์ด ๊ฑฐ๋ž˜๋œ ์‹œ์žฅ์„ ์ •์˜ํ•˜๋Š” ๋”๋ฏธ ๋ฉ”์„œ๋“œ ์ •์˜ 
    public static String on(String market) {
        return market;
    }
}

[ ์žฅ์  ]

๋ฉ”์„œ๋“œ ์ฒด์ธ์— ๋น„ํ•ด ํ•จ์ˆ˜ ์ค‘์ฒฉ ๋ฐฉ์‹์ด ๋„๋ฉ”์ธ ๊ฐ์ฒด ๊ณ„์ธต ๊ตฌ์กฐ์— ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜(์˜ˆ์ œ์—์„œ ์ฃผ๋ฌธ์€ ํ•œ ๊ฐœ ์ด์ƒ์˜ ๊ฑฐ๋ž˜๋ฅผ ํฌํ•จํ•˜๋ฉฐ ๊ฐ ๊ฑฐ๋ž˜๋Š” ํ•œ ๊ฐœ์˜ ์ฃผ์‹์„ ์ฐธ์กฐ)๋œ๋‹ค๋Š” ๊ฒƒ์ด ์žฅ์ ์ด๋‹ค.

[ ๋‹จ์  ]

  • ๊ฒฐ๊ณผ DSL์— ๋” ๋งŽ์€ ๊ด„ํ˜ธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ์ธ์ˆ˜ ๋ชฉ๋ก์„ ์ •์  ๋ฉ”์„œ๋“œ์— ๋„˜๊ฒจ์ค˜์•ผ ํ•œ๋‹ค.

    ๋„๋ฉ”์ธ ๊ฐ์ฒด์— ์„ ํƒ์‚ฌํ•ญ ํ•„๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์ธ์ˆ˜๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ด ๊ฐ€๋Šฅ์„ฑ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋ผ์ด๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

  • ์ธ์ˆ˜์˜ ์˜๋ฏธ๊ฐ€ ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ ์œ„์น˜์— ์˜ํ•ด ์ •์˜๋˜์—ˆ๋‹ค.

    NestedRuncionOrderBuilder์˜ at(), on() ๋ฉ”์„œ๋“œ์—์„œ ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ์ธ์ˆ˜์˜ ์—ญํ• ์„ ํ™•์‹คํ•˜๊ฒŒ ๋งŒ๋“ ๋Š ์—ฌ๋Ÿฌ ๋”๋ฏธ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด ์กฐ๊ธˆ์€ ์™„ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

10.3.3 ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์ด์šฉํ•œ ํ•จ์ˆ˜ ์‹œํ€€์‹ฑ

๋‹ค์Œ DSL ํŒจํ„ด์€ ๋žŒ๋‹ค ํ‘œํ˜„์‹์œผ๋กœ ์ •์˜ํ•œ ํ•จ์ˆ˜ ์‹œํ€€์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ํ•จ์ˆ˜ ์‹œํ€€์‹ฑ์œผ๋กœ ์ฃผ์‹ ๊ฑฐ๋ž˜ ์ฃผ๋ฌธ ๋งŒ๋“ค๊ธฐ
Order order = order(o -> {
    o.forCustomer("BigBank");
    o.buy(t -> {
        t.quantity(80);
        t.price(125.00);
        t.stock(s -> {
            s.symbol("IBM");
            s.market("NYSE");
        });
    });
    o.sell(t -> {
        t.quantity(50);
        t.price(375.00);
        t.stock(s -> {
            s.symbol("GOOGLE");
            s.market("NASDAQ"):;
        });
    });
});

์ด๋Ÿฐ DSL์„ ๋งŒ๋“ค๋ ค๋ฉด ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ๋ฐ›์•„ ์‹คํ–‰ํ•ด ๋„๋ฉ”์ธ ๋ชจ๋ธ์„ ๋งŒ๋“ค์–ด ๋‚ด๋Š” ์—ฌ๋Ÿฌ ๋นŒ๋”๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

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

  • ๋ฉ”์„œ๋“œ ์ฒด์ธ ํŒจํ„ด: ์ฃผ๋ฌธ์„ ๋งŒ๋“œ๋Š” ์ตœ์ƒ์œ„ ์ˆ˜์ค€์˜ ๋นŒ๋” ๊ฐ€์ง
  • ํ•จ์ˆ˜ ์‹œํ€€์‹ฑ: Consumer ๊ฐ์ฒด๋ฅผ ๋นŒ๋”๊ฐ€ ์ธ์ˆ˜๋กœ ๋ฐ›์Œ์œผ๋กœ DSL ์‚ฌ์šฉ์ž๊ฐ€ ๋žŒ๋‹ค ํ‘œํ˜„์‹์œผ๋กœ ์ธ์ˆ˜ ๊ตฌํ˜„ ๊ฐ€๋Šฅ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ํ•จ์ˆ˜ ์‹œํ€€์‹ฑ DSL์„ ์ œ๊ณตํ•˜๋Š” ์ฃผ๋ฌธ ๋นŒ๋”
public class LambdaOrderBuilder {
    private Order order = new Order(); // ๋นŒ๋”๋กœ ์ฃผ๋ฌธ์„ ๊ฐ์Œˆ

    public static Order order(Consumer<LambdaOrderBuilder> consumer) {
        LambdaOrderBuilder builder = new LambdaOrderBulider();
        consumer.accept(builder); // ์ฃผ๋ฌธ ๋นŒ๋”๋กœ ์ „๋‹ฌ๋œ ๋žŒ๋‹ค ํ‘œํ˜„์‹ ์‹คํ–‰
        return builder.order; // OrderBuilder์˜ Consumer๋ฅผ ์‹คํ–‰ํ•ด ๋งŒ๋“ค์–ด์ง„ ์ฃผ๋ฌธ์„ ๋ฐ˜ํ™˜
    }

    public void forCustomer(String customer) {
        order.setCustomer(customer); // ์ฃผ๋ฌธ์„ ์š”์ฒญํ•œ ๊ณ ๊ฐ ์„ค์ •
    }

    public void buy(Consumer<TradeBuilder> consumer) {
        trade(consumer, Trade.Type.BUY); // ์ฃผ์‹ ๋งค์ˆ˜ ์ฃผ๋ฌธ์„ ๋งŒ๋“ค๋„๋ก TradeBuilder ์†Œ๋น„
    }

    public void sell(Consumer<TradeBuilder> consumer) {
        trade(consumer, Trade.Type.SELL); // ์ฃผ์‹ ๋งค๋„ ์ฃผ๋ฌธ์„ ๋งŒ๋“ค๋„๋ก TradeBuilder ์†Œ๋น„
    }

    private void trade(Consumer<TradeBuilder> consumer, Trade.Type type) {
        TradeBuilder builder = new TradeBulder();
        builder.trade.setType(type);
        consumer.accept(builder); // TradeBuilder๋กœ ์ „๋‹ฌํ•  ๋žŒ๋‹ค ํ‘œํ˜„์‹ ์‹คํ–‰
        order.addTrade(builder.trade); // TradeBuilder์˜ Consumer๋ฅผ ์‹คํ–‰ํ•ด ๋งŒ๋“  ๊ฑฐ๋ž˜๋ฅผ ์ฃผ๋ฌธ์— ์ถ”๊ฐ€
    }
}

์ฃผ๋ฌธ ๋นŒ๋”์˜ buy(), sell() ๋ฉ”์„œ๋“œ๋Š” ๋‘ ๊ฐœ์˜ **Consumer ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ๋ฐ›๋Š”๋‹ค**. ์ด ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ์ฃผ์‹ ๋งค์ˆ˜, ์ฃผ์‹ ๋งค๋„ ๊ฑฐ๋ž˜๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TradeBuilder {
    private Trade trade = new Trade();

    public void quantity(int quantity) {
        trade.setQuantity(quantity);
    }

    public void price(double price) {
        trade.setPrice(price);
    }

    public void stock(Consumer<StockBuilder> consumer) {
        StockBuilder builder = new StockBuilder();
        consumer.accept(builder);
        trade.setStock(builder.stock);
    }
}

๋งˆ์ง€๋ง‰์œผ๋กœ TradeBuilder๋Š” ์„ธ ๋ฒˆ์งธ ๋นŒ๋”์˜ Consumer ์ฆ‰ ๊ฑฐ๋ž˜๋œ ์ฃผ์‹์„ ๋ฐ›๋Š”๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
public class StockBuilder {
    private Stock stock = new Stock();

    public void symbol(String symbol) {
        stock.setSymbol(symbol);
    }

    public void market(String market) {
        stock.setMarket(market);
    }
}

์ด ํŒจํ„ด์€ ์ด์ „ ๋‘ ๊ฐ€์ง€ DSL ํ˜•์‹์˜ ๋‘ ๊ฐ€์ง€ ์žฅ์ ์„ ๋”ํ•œ๋‹ค.

[ ์žฅ์  ]

  1. ๋ฉ”์„œ๋“œ ์ฒด์ธ ํŒจํ„ด์ฒ˜๋Ÿผ ํ”Œ๋ฃจ์–ธํŠธ ๋ฐฉ์‹์œผ๋กœ ๊ฑฐ๋ž˜ ์ฃผ๋ฌธ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. ์ค‘์ฒฉ ํ•จ์ˆ˜ ํ˜•์‹์ฒ˜๋Ÿผ ๋‹ค์–‘ํ•œ ๋žŒ๋‹ค ํ‘œํ˜„์‹์˜ ์ค‘์ฒฉ ์ˆ˜์ค€๊ณผ ๋น„์Šทํ•˜๊ฒŒ ๋„๋ฉ”์ธ ๊ฐ์ฒด์˜ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•œ๋‹ค.

[ ๋‹จ์  ]

  • ๋งŽ์€ ์„ค์ • ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ DSL ์ž์ฒด๊ฐ€ ์ž๋ฐ” 8 ๋žŒ๋‹ค ํ‘œํ˜„์‹ ๋ฌธ๋ฒ•์— ์˜ํ•œ ์žก์Œ์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š”๋‹ค.

10.3.4 ์กฐํ•ฉํ•˜๊ธฐ

์ƒˆ๋กœ์šด DSL์„ ๊ฐœ๋ฐœํ•ด ์ฃผ์‹ ๊ฑฐ๋ž˜ ์ฃผ๋ฌธ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
// ์—ฌ๋Ÿฌ DSL ํŒจํ„ด์„ ์ด์šฉํ•ด ์ฃผ์‹ ๊ฑฐ๋ž˜ ์ฃผ๋ฌธ ๋งŒ๋“ค๊ธฐ
Order order =
    forCustomer("BigBank", //์ตœ์ƒ์œ„ ์ˆ˜์ค€ ์ฃผ๋ฌธ์˜ ์†์„ฑ์„ ์ง€์ •ํ•˜๋Š” ์ค‘์ฒฉํ•จ์ˆ˜
                buy(t -> t.quantity(80) // ํ•œ ๊ฐœ์˜ ์ฃผ๋ฌธ์„ ๋งŒ๋“œ๋Š” ๋žŒ๋‹ค ํ‘œํ˜„์‹
                        .stock("IBM") // ๊ฑฐ๋ž˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋žŒ๋‹ค ํ‘œํ˜„์‹ ๋ฐ”๋””์˜ ๋ฉ”์„œ๋“œ ์ฒด์ธ
                        .on("NYSE")
                        .at(125.00)),
                sell(t -> t.quantity(50)
                        .stock("GOOGLE")
                        .on("NASDAQ")
                        .at(125.00)) );

์œ„ ์ฝ”๋“œ์—์„œ๋Š” ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜ ํŒจํ„ด์„ ๋žŒ๋‹ค ๊ธฐ๋ฒ•๊ณผ ํ˜ผ์šฉํ–ˆ๋‹ค.

TradeBuilder์˜ Consumer๊ฐ€ ๋งŒ๋“  ๊ฐ ๊ฑฐ๋ž˜๋Š” ๋‹ค์Œ ์ฒ˜๋Ÿผ ๋žŒ๋‹ค ํ‘œํ˜„์‹์œผ๋กœ ๊ตฌํ˜„๋œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ์—ฌ๋Ÿฌ ํ˜•์‹์„ ํ˜ผํ•ฉํ•œ DSL์„ ์ œ๊ณตํ•˜๋Š” ์ฃผ๋ฌธ ๋นŒ๋”
public class MixedBuilder {

    public static Order forCustomer(String customer, TradeBuilder... builders) {
        Order order = new Order();
        order.setCustomer(customer);
        Stream.of(builders).forEach(b -> order.addTrade(b.trade));
        return order;
    }

    public static TradeBuilder buy(Consumer<TradeBuilder> consumer) {
        return buildTrade(consumer, Trade.Type.BUY);
    }

    public static TradeBuilder sell(Consumer<TradeBulder> consumer) {
        return buildTrade(consumer, Trade.Type.SELL);
    }

    private static TradeBuilder buildTrade(Consumer<TradeBuilder> consumer, Trade.Type type) {
        TradeBuilder builder = new TradeBuilder();
        builder.trade.setType(buy);
        consumer.accept(builder);
        return builder;
    }
}

๋งˆ์ง€๋ง‰์œผ๋กœ ํ—ฌํผ ํด๋ž˜์Šค TradeBuilder์™€ StockBuilder๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฉ”์„œ๋“œ ์ฒด์ธ ํŒจํ„ด์„ ๊ตฌํ˜„ํ•ด ํ”Œ๋ฃจ์–ธํŠธ API๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์ด์ œ ๋žŒ๋‹ค ํ‘œํ˜„์‹ ๋ฐ”๋””๋ฅผ ๊ตฌํ˜„ํ•ด ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ฑฐ๋ž˜๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class TradeBuilder {
    private Trade trade = new Trade();

    public TradeBuilder quantity(int quantity) {
        trade.setQuantity(quantity);
        return this;
    }

    public TradeBuilder at(double price) {
        trade.setPrice(price);
        return this;
    }

    public StockBuilder stock(String symbol) {
        return StockBuilder(this, trade, symbol);
    }
}

public class StockBuilder {
    private final TradeBuilder builder;
    private final Trade trade;
    private final Stock stock = new Stock();

    private StockBuilder(TradeBuilder builder, Trade trade, String symbol){
        this.builder = builder;
        this.trade = trade;
        stock.setSymbol(symbol);
    }

    public TradeBuilder on(String market) {
        stock.setMarket(market);
        trade.setStock(stock);
        return builder;
    }
}

์„ธ ๊ฐ€์ง€ DSL ํŒจํ„ด์„ ํ˜ผ์šฉํ•ด ๊ฐ€๋…์„ฑ ์žˆ๋Š” DSL์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค€๋‹ค.

์—ฌ๋Ÿฌ ํŒจํ„ด์˜ ์žฅ์ ์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ฒฐ์ ๋„ ์žˆ๋‹ค. ๊ฒฐ๊ณผ DSL์ด ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๊ธฐ๋ฒ•์„ ํ˜ผ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ํ•œ ๊ฐ€์ง€ ๊ธฐ๋ฒ•์„ ์ ์šฉํ•œ DSL์— ๋น„ํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ DSL์„ ๋ฐฐ์šฐ๋Š”๋ฐ ์˜ค๋žœ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฐ๋‹ค.

10.3.5 DSL์— ๋ฉ”์„œ๋“œ ์ฐธ์กฐ ์‚ฌ์šฉํ•˜๊ธฐ

์ฃผ์‹ ๊ฑฐ๋ž˜ ๋„๋ฉ”์ธ ๋ชจ๋ธ์— โ€˜์ฃผ๋ฌธ์˜ ์ด ํ•ฉ์— 0๊ฐœ ์ด์ƒ์˜ ์„ธ๊ธˆ์„ ์ถ”๊ฐ€ํ•ด ์ตœ์ข…๊ฐ’์„ ๊ณ„์‚ฐโ€™ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ์ฃผ๋ฌธ์˜ ์ด ํ•ฉ์— ์ ์šฉํ•  ์„ธ๊ธˆ
public class Tax {
    public static double regional(double value) {
        return value * 1.1;
    }

    public static double general(double value) {
        return value * 1.3;
    }

    public static double surcharge(double value) {
        return value * 1.05;
    }
}

๋‹ค์Œ ์ฒ˜๋Ÿผ ์„ธ๊ธˆ์„ ์ ์šฉํ•  ๊ฒƒ์ธ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋ถˆ๋ฆฌ์–ธ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ์ •์  ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด ๊ฐ„๋‹จํžˆ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
public static double calculate(Order order, boolean useRegional, 
                                boolean useGeneral, boolean useSurcharge) {
    double value = order.getValue();
    if (useRegional) value = Tax.regional(value);
    if (useGeneral) value = Tax.general(value);
    if (useSurcharge) value = Tax.surcharge(value);
    return value;
}

์ด์ œ ๋‹ค์Œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ง€์—ญ ์„ธ๊ธˆ๊ณผ ์ถ”๊ฐ€ ์š”๊ธˆ์„ ์ ์šฉํ•˜๊ณ  ์ผ๋ฐ˜ ์„ธ๊ธˆ์€ ๋บ€ ์ฃผ๋ฌธ์˜ ์ตœ์ข…๊ฐ’์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

1
double value = calculate(order, true, false, true);

์ด ๊ตฌํ˜„์€ ๋ถˆ๋ฆฌ์–ธ ๋ณ€์ˆ˜์˜ ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ๋ฅผ ๊ธฐ์–ตํ•˜๊ธฐ๋„ ์–ด๋ ค์šฐ๋ฉฐ, ์–ด๋–ค ์„ธ๊ธˆ์ด ์ ์šฉ๋˜์—ˆ๋Š”์ง€๋„ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

์•„๋ž˜์—์„œ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์œ ์ฐฝํ•˜๊ฒŒ ๋ถˆ๋ฆฌ์–ธ ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•˜๋Š” DSL์„ ์ œ๊ณตํ•˜๋Š” TaxCalculator๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ์ ์šฉํ•  ์„ธ๊ธˆ์„ ์œ ์ฐฝํ•˜๊ฒŒ ์ •์˜ํ•˜๋Š” ์„ธ๊ธˆ ๊ณ„์‚ฐ๊ธฐ
public class TaxCalculator {
    private boolean useRegional;
    private boolean useGeneral;
    private boolean useSurcharge;

    public TaxCalculator withTaxRegional() {
        useRegoinal = true;
        return this;
    }

    public TaxCalculator withTaxGeneral() {
        useGeneral = true;
        return this;
    }

    public TaxCalculator withTaxSurcharge() {
        useSurcharge = true;
        return this;
    }

    public double calculate(Order order) {
        return calculate(order, useRegional, useGeneral, useSurcharge);
    }
}

๋‹ค์Œ ์ฝ”๋“œ์ฒ˜๋Ÿผ TaxCalculator๋Š” ์ง€์—ญ ์„ธ๊ธˆ๊ณผ ์ถ”๊ฐ€ ์š”๊ธˆ์€ ์ฃผ๋ฌธ์— ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋Š” ์ ์„ ๋ช…ํ™•ํžˆ ๋ณด์—ฌ์ค€๋‹ค.

1
2
3
double value = new TaxCalculator().withTaxRegional()
                                  .withTaxSurcharge()
                                  .calculate(order)

[ ๋‹จ์  ]

  • ์ฝ”๋“œ๊ฐ€ ์žฅํ™ฉํ•˜๋‹ค
  • ๋„๋ฉ”์ธ์˜ ๊ฐ ์„ธ๊ธˆ์— ํ•ด๋‹นํ•˜๋Š” ๋ถˆ๋ฆฌ์–ธ ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ ํ™•์žฅ์„ฑ์ด ์ œํ•œ์ ์ด๋‹ค.

์ž๋ฐ”์˜ ํ•จ์ˆ˜ํ˜• ๊ธฐ๋Šฅ์„ ์ด์šฉํ•˜๋ฉด ๋” ๊ฐ„๊ฒฐํ•˜๊ณ  ์œ ์—ฐํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ฐ€๋…์„ฑ์„ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ์œ ์ฐฝํ•˜๊ฒŒ ์„ธ๊ธˆ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•˜๋Š” ์„ธ๊ธˆ ๊ณ„์‚ฐ๊ธฐ(๋ฆฌํŒฉํ„ฐ๋ง)
public class TaxCalculator {
    // ์ฃผ๋ฌธ๊ฐ’์— ์ ์šฉ๋œ ๋ชจ๋“  ์„ธ๊ธˆ์„ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜
    public DoubleUnaryOperator taxFunction = d -> d;

    public TaxCalculator with(DoubleUnaryOperator f) {
        // ์ƒˆ๋กœ์šด ์„ธ๊ธˆ ๊ณ„์‚ฐ ํ•จ์ˆ˜๋ฅผ ์–ป์–ด์„œ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜์™€ ํ˜„์žฌ ํ•จ์ˆ˜๋ฅผ ํ•ฉ์นจ
        taxFunction = taxFunction.andThen(f);
        return this; // ์œ ์ฐฝํ•˜๊ฒŒ ์„ธ๊ธˆ ํ•จ์ˆ˜๊ฐ€ ์—ฐ๊ฒฐ๋  ์ˆ˜ ์žˆ๋„๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜
    }

    public double calculate(Order order) {
        // ์ฃผ๋ฌธ์˜ ์ด ํ•ฉ์—์„œ ์„ธ๊ธˆ ๊ณ„์‚ฐ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•ด ์ตœ์ข… ์ฃผ๋ฌธ๊ฐ’์„ ๊ณ„์‚ฐ
        return taxFunction.applyAsDouble(order.getValue());
    }
}

์ด ๊ธฐ๋ฒ•์€ ์ฃผ๋ฌธ์˜ ์ด ํ•ฉ์— ์ ์šฉํ•  ํ•จ์ˆ˜ ํ•œ ๊ฐœ์˜ ํ•„๋“œ๋งŒ ํ•„์š”๋กœํ•˜๋ฉฐ TaxCalculator ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์„ธ๊ธˆ ์„ค์ •์ด ์ ์šฉ๋œ๋‹ค.

  1. ์ฒ˜์Œ ์‹œ์ ์—์„œ๋Š” ์„ธ๊ธˆ์ด ์ ์šฉ๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์ตœ์ข… ๊ฐ’์€ ์ดํ•ฉ๊ณผ ๊ฐ™๋‹ค.
  2. with() ๋ฉ”์„œ๋“œ๋กœ ์ƒˆ ์„ธ๊ธˆ์ด ์ถ”๊ฐ€๋˜๋ฉด ํ˜„์žฌ ์„ธ๊ธˆ ๊ณ„์‚ฐ ํ•จ์ˆ˜์— ์ด ์„ธ๊ธˆ์ด ์กฐํ•ฉ๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•œ ํ•จ์ˆ˜์— ๋ชจ๋“  ์ถ”๊ฐ€๋œ ์„ธ๊ธˆ์ด ์ ์šฉ๋œ๋‹ค.
  3. ์ฃผ๋ฌธ์„ calculate() ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•˜๋ฉด ๋‹ค์–‘ํ•œ ์„ธ๊ธˆ ์„ค์ •์˜ ๊ฒฐ๊ณผ๋กœ ๋งŒ๋“ค์–ด์ง„ ์„ธ๊ธˆ ๊ณ„์‚ฐ ํ•จ์ˆ˜๊ฐ€ ์ฃผ๋ฌธ์˜ ํ•ฉ๊ณ„์— ์ ์šฉ๋œ๋‹ค.

๋ฆฌํŒฉํ„ฐ๋งํ•œ TaxCalculator๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
double value = new TaxCalculator().with(Tax::regional)
                                  .with(Tax::surcharge)
                                  .calculate(order);

10.4 ์‹ค์ƒํ™œ์˜ ์ž๋ฐ” 8 DSL

| ํŒจํ„ด ์ด๋ฆ„ | ์žฅ์  | ๋‹จ์  | | โ€” | โ€” | โ€” | | ๋ฉ”์„œ๋“œ ์ฒด์ธ | - ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด ํ‚ค์›Œ๋“œ ์ธ์ˆ˜ ์—ญํ• ์„ ํ•œ๋‹ค.

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

10.4.1 jOOQ

SQL์—์„œ DSL์€ ๊ฐ€์žฅ ํ”ํžˆ, ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๋ถ„์•ผ๋‹ค. jOOQ๋Š” SQL์„ ๊ตฌํ˜„ํ•˜๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ DSL๋กœ ์ž๋ฐ”์— ์ง์ ‘ ๋‚ด์žฅ๋œ ํ˜•์‹ ์•ˆ์ „ ์–ธ์–ด๋‹ค.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ ์—ญ๊ณตํ•™ํ•˜๋Š” ์†Œ์Šค์ฝ”๋“œ ์ƒ์„ฑ๊ธฐ ๋•๋ถ„์— ์ž๋ฐ” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ณต์žกํ•œ SQL ๊ตฌ๋ฌธ์˜ ํ˜•์‹์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์—ญ๊ณตํ•™ ํ”„๋กœ์„ธ์Šค ์ œํ’ˆ์ด ์ƒ์„ฑํ•œ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์šฐ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
SELECT * FROM BOOK
WHERE BOOK.PUBLISHED_IN = 2016
ORDER BY BOOK.TITLE

jOOQ DSL์„ ์ด์šฉํ•ด ์œ„ ์งˆ์˜๋ฅผ ๋‹ค์Œ์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
create.selectFrom(BOOK)
      .where(BOOK.PUBLISHED_IN.eq(2016))
      .orderBy(BOOK.TITLE)

์ŠคํŠธ๋ฆผ API์™€ ์กฐํ•ฉํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด jOOQ DSL์˜ ๋˜ ๋‹ค๋ฅธ ์žฅ์ ์ด๋‹ค. ์ด ๊ธฐ๋Šฅ ๋•๋ถ„์— ๋‹ค์Œ์—์„œ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ SQL ์งˆ์˜ ์‹คํ–‰์œผ๋กœ ๋‚˜์˜จ ๊ฒฐ๊ณผ๋ฅผ ํ•œ ๊ฐœ์˜ ํ”Œ๋ฃจ์–ธํŠธ ๊ตฌ๋ฌธ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// jOOQ DSL์„ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ฑ… ์„ ํƒ
Class.forName("org.h2.Driver");
try (Connection c =
        getConnection("jdbc:h2:~/sql-goodies-with-mapping", "sa", "")) {
    DSL.using(c)
        .select(BOOK.AUTHOR, BOOK.TITLE) // ๋งŒ๋“ค์–ด์ง„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ์„ ์ด์šฉํ•ด jOOQ SQL๋ฌธ ์‹œ์ž‘
        .where(BOOK.PUBLISHED_IN.eq(2016))
        .orderBy(BOOK.TITLE)
        .fetch() // jOOQ DSL๋กœ SQL๋ฌธ ์ •์˜
        .stream() // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ. jOOQ๋ฌธ์€ ์—ฌ๊ธฐ์„œ ์ข…๋ฃŒ
        .collect(groupingBy( // ์ŠคํŠธ๋ฆผ API๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์‹œ์ž‘
            r -> r.getValue(BOOK.AUTHOR),
            LinkedHashMap::new,
            mapping(r -> r.getValue(BOOK.TITLE), toList())))
            .forEach((author, titles) -> //์ €์ž์˜ ์ด๋ฆ„ ๋ชฉ๋ก๊ณผ ๊ฐ ์ €์ž๊ฐ€ ์ง‘ํ•„ํ•œ ์ฑ…๋“ค์„ ์ถœ๋ ฅ
                System.out.println(author + " is author of " + titles));
}

jOOQ DSL์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ๋ฉ”์„œ๋“œ ์ฒด์ธ ํŒจํ„ด์ด ์‚ฌ์šฉ๋˜์—ˆ๋‹ค. ์ž˜ ๋งŒ๋“ค์–ด์ง„ SQL์˜ ์งˆ์˜ ๋ฌธ๋ฒ•์„ ํ‰๋‚ด๋‚ด๋ ค๋ฉด ๋ฉ”์„œ๋“œ ์ฒด์ธ ํŒจํ„ด์˜ ์—ฌ๋Ÿฌ ํŠน์„ฑ(์„ ํƒ์  ํŒŒ๋ผ๋ฏธํ„ฐ ํ—ˆ์šฉ, ๋ฏธ๋ฆฌ ์ •ํ•ด์ง„ ์ˆœ์„œ๋กœ ํŠน์ • ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๊ฒŒ ๊ฐ•์ œ)์ด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

10.4.2 ํ์ปด๋ฒ„

๋™์ž‘ ์ฃผ๋„ ๊ฐœ๋ฐœ(BDD)์€ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์˜ ํ™•์žฅ์œผ๋กœ ๋‹ค์–‘ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ์„œ์ˆ ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋„๋ฉ”์ธ ์ „์šฉ ์Šคํฌ๋ฆฝํŒ… ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

ํ์ปด๋ฒ„๋Š” ๋‹ค๋ฅธ BDD ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ด๋“ค ๋ช…๋ น๋ฌธ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ด ๊ฐœ๋ฐœ ๊ธฐ๋ฒ•์œผ๋กœ ๋งŒ๋“  ์Šคํฌ๋ฆฝํŠธ ๊ฒฐ๊ณผ๋ฌผ์€ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ…Œ์ŠคํŠธ์ž„๊ณผ ๋™์‹œ์— ๋น„์ฆˆ๋‹ˆ์Šค ๊ธฐ๋Šฅ์˜ ์ˆ˜์šฉ ๊ธฐ์ค€์ด ๋œ๋‹ค.

BDD๋Š” ์šฐ์„  ์ˆœ์œ„์— ๋”ฐ๋ฅธ, ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๊ฐ€์น˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฐœ๋ฐœ ๋…ธ๋ ฅ์— ์ง‘์ค‘ํ•˜๋ฉฐ ๋น„์ฆˆ๋‹ˆ์Šค ์–ดํœ˜๋ฅผ ๊ณต์œ ํ•จ์œผ๋กœ ๋„๋ฉ”์ธ ์ „๋ฌธ๊ฐ€์™€ ํ”„๋กœ๊ทธ๋ž˜๋จธ ์‚ฌ์ด์˜ ๊ฐ„๊ฒฉ์„ ์ค„์ธ๋‹ค.

ํ์ปด๋ฒ„(BDD ๋„๊ตฌ)

๊ฐœ๋ฐœ์ž๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ‰๋ฌธ ์˜์–ด๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์คŒ

1
2
3
4
5
Feature: Buy stock
	Scenatio: Buy 10 IMB stocks
		Given the price of a "IBM" stock is 125$
		When I buy 10 "IBM"
		Then the order value should be 1250$

ํ์ปด๋ฒ„๋Š” ์„ธ ๊ฐ€์ง€๋กœ ๊ตฌ๋ถ„๋˜๋Š” ๊ฐœ๋…์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • ์ „์ œ ์กฐ๊ฑด ์ •์˜(Given)
  • ์‹œํ—˜ํ•˜๋ ค๋Š” ๋„๋ฉ”์ธ ๊ฐ์ฒด์˜ ์‹ค์งˆ ํ˜ธ์ถœ(When)
  • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์˜ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๋Š” assertion(Then)

ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ •์˜ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋Š” ์ œํ•œ๋œ ์ˆ˜์˜ ํ‚ค์›Œ๋“œ๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ ์ž์œ ๋กœ์šด ํ˜•์‹์œผ๋กœ ๋ฌธ์žฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์™ธ๋ถ€ DSL์„ ํ™œ์šฉํ•œ๋‹ค. ์ด๋“ค ๋ฌธ์žฅ์€ ํ…Œ์ŠคํŠธ์ผ€์ด์Šค์˜ ๋ณ€์ˆ˜๋ฅผ ์บก์ณํ•˜๋Š” ์ •๊ทœ ํ‘œํ˜„์‹์œผ๋กœ ๋งค์นญ๋˜๋ฉฐ ํ…Œ์ŠคํŠธ ์ž์ฒด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋กœ ์ด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

ํ์ปด๋ฒ„๋กœ ์ฃผ์‹ ๊ฑฐ๋ž˜ ์ฃผ๋ฌธ์˜ ๊ฐ’์ด ์ œ๋Œ€๋กœ ๊ณ„์‚ฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ํ์ปด๋ฒ„ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ด์šฉํ•ด ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค ๊ตฌํ˜„
public class BuyStocksSteps {
    private Map<String, Integer> stockUnitPrices = new HashMap<>();
    private Order order = new Order();

    @Given("^the price of a \"(.*?)\" stock is (\\d+)\\$$") // ์‹œ๋‚˜๋ฆฌ์˜ค์˜ ์ „์ œ ์กฐ๊ฑด์ธ ์ฃผ์‹ ๋‹จ๊ฐ€ ์ •์˜
    public void setUnitPrice(String stockName, int unitPrice) {
        stockUnitValues.put(stockName, unitPrice); // ์ฃผ์‹ ๋‹จ๊ฐ€ ์ €์žฅ
    }

    @When("^I buy (\\d+) \"(.*?)\"$") // ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์ธ ๋„๋ฉ”์ธ ๋ชจ๋ธ์— ํ–‰ํ•  ์•ก์…˜ ์ •์˜
    public void buyStocks(int quantity, String stockName) {
        Trade trade = new Trade(); // ์ ์ ˆํ•˜๊ฒŒ ๋„๋ฉ”์ธ ๋ชจ๋ธ ๋„์ถœ
        trade.setType(Trade.Type.BUY);

        Stock stock = new Stock();
        stock.setSymbol(stockName);

        trade.setStock(stock);
        trade.setPrice(stockUnitPrices.get(stockName));
        trade.setQuantity(quantity);
        order.addTrade(trade);
    }

    @Then("^the order value should be (\\d+)\\$$")
    public void checkOrderValue(int expectedValue) { // ์˜ˆ์ƒ๋˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒฐ๊ณผ ์ •์˜
        assertEquals(expectedValue, order.getValue()); // ํ…Œ์ŠคํŠธ ์–ด์„ค์…˜ ํ™•์ธ
    }
}

์ž๋ฐ” 8์ด ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์ง€์›ํ•˜๋ฉด์„œ ๋‘ ๊ฐœ์˜ ์ธ์ˆ˜ ๋ฉ”์„œ๋“œ(๊ธฐ์กด์— ์–ด๋…ธํ…Œ์ด์…˜ ๊ฐ’์„ ํฌํ•จํ•œ ์ •๊ทœ ํ‘œํ˜„์‹๊ณผ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋žŒ๋‹ค)๋ฅผ ์ด์šฉํ•ด ์–ด๋…ธํ…Œ์ด์…˜์„ ์ œ๊ฑฐํ•˜๋Š” ๋‹ค๋ฅธ ๋ฌธ๋ฒ•์„ ํ์ปด๋ฒ„๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‘ ๋ฒˆ์งธ ํ˜•์‹์ด ํ‘œ๊ธฐ๋ฒ•์„ ์ด์šฉํ•ด ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋‹ค์Œ์ฒ˜๋Ÿผ ๋‹ค์‹œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
public class BuyStocksSteps implements cucumber.api.java8.En {
    private Map<String, Integer> stockUnitPrices = new HashMap<>();
    private Order order = new Order();

    public BuyStocksSteps() {
        Given("^the price of a \"(.*?)\" stock is (\\d+)\\$$",
                (String stockName, int unitPrice) -> {
                        stockUnitValues.put(stockName, unitPrice);
                });
        // ... When๊ณผ Then ๋žŒ๋‹ค๋Š” ์ƒ๋žต
    }
}

์ด์ „๋ณด๋‹ค ์ฝ”๋“œ๊ฐ€ ๋” ๋‹จ์ˆœํ•ด์กŒ๋‹ค. ํŠนํžˆ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฌด๋ช… ๋žŒ๋‹ค๋กœ ๋ฐ”๋€Œ๋ฉด์„œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ์ฐพ๋Š” ๋ถ€๋‹ด์ด ์—†์–ด์กŒ๋‹ค.

ํ์ปด๋ฒ„์˜ DSL์€ ์•„์ฃผ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ์™ธ๋ถ€์  DSL๊ณผ ๋‚ด๋ถ€์  DSL์ด ์–ด๋–ป๊ฒŒ ํšจ๊ณผ์ ์œผ๋กœ ํ•ฉ์ณ์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋žŒ๋‹ค์™€ ํ•จ๊ป˜ ๊ฐ€๋…์„ฑ ์žˆ๋Š” ํ•จ์ถ•๋œ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ์ž˜ ๋ณด์—ฌ์ค€๋‹ค.

10.4.3 ์Šคํ”„๋ง ํ†ตํ•ฉ

โ†’ ์œ ๋ช…ํ•œ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํ†ตํ•ฉ ํŒจํ„ด์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋„๋ก ์˜์กด์„ฑ ์ฃผ์ž…์— ๊ธฐ๋ฐ˜ํ•œ ์Šคํ”„๋ง ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์„ ํ™•์žฅ

  • ํ•ต์‹ฌ ๋ชฉํ‘œ

    ๋ณต์žกํ•œ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํ†ตํ•ฉ ์†”๋ฃจ์…˜์„ ๊ตฌํ˜„ํ•˜๋Š” ๋‹จ์ˆœํ•œ ๋ชจ๋ธ์„ ์ œ๊ณตํ•˜๊ณ  ๋น„๋™๊ธฐ, ๋ฉ”์‹œ์ง€ ์ฃผ๋„ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‰ฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋•๋Š” ๊ฒƒ

  • ๊ธฐ๋Šฅ ์ง€์›

    ์Šคํ”„๋ง ํ†ตํ•ฉ์€ ์Šคํ”„๋ง ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์˜ ๊ฒฝ๋Ÿ‰์˜ ์›๊ฒฉ, ๋ฉ”์‹œ์ง•, ์Šค์ผ€์ฅด๋ง์„ ์ง€์›ํ•œ๋‹ค. ๊ธฐ์กด์˜ ์Šคํ”„๋ง XML ์„ค์ • ํŒŒ์ผ ๊ธฐ๋ฐ˜์—๋„ ์ด๋“ค ๊ธฐ๋Šฅ์„ ์ง€์›ํ•œ๋‹ค.

  • ๊ตฌํ˜„

    ์Šคํ”„๋ง ํ†ตํ•ฉ์€ ์ฑ„๋„, ์—”๋“œํฌ์ธํŠธ, ํด๋Ÿฌ, ์ฑ„๋„ ์ธํ„ฐ์…‰ํ„ฐ ๋“ฑ ๋ฉ”์‹œ์ง€ ๊ธฐ๋ฐ˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํ•„์š”ํ•œ ๊ฐ€์žฅ ๊ณตํ†ต ํŒจํ„ด์„ ๋ชจ๋‘ ๊ตฌํ˜„ํ•œ๋‹ค.

๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง€๋„๋ก ์—”๋“œํฌ์ธํŠธ๋Š” DSL์—์„œ ๋™์‚ฌ๋กœ ๊ตฌํ˜„ํ•˜๋ฉฐ ์—ฌ๋Ÿฌ ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ•œ ๊ฐœ ์ด์ƒ์˜ ๋ฉ”์‹œ์ง€ ํ๋ฆ„์œผ๋กœ ์กฐํ•ฉํ•ด์„œ ํ†ตํ•ฉ ๊ณผ์ •์ด ๊ตฌ์„ฑ๋œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ์Šคํ”„๋ง ํ†ตํ•ฉ DSL์„ ์ด์šฉํ•ด ์Šคํ”„๋ง ํ†ตํ•ฉ ํ๋ฆ„ ์„ค์ •ํ•˜๊ธฐ
@Configuration
@EnableIntegration
public class MyConfiguration {

		@Bean
		public MessageSource<?> integerMessageSource() {
            // ํ˜ธ์ถœ ์‹œ AtomicInteger๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์ƒˆ MessageSource๋ฅผ ์ƒ์„ฑ
            MethodInvokingMessageSource source =
                new MethodInvokingMessageSource();
            source.setObject(new AtomicInteger());
            source.setMethodName("getAndIncrement");
            return source;
		}

		@Bean
		public DirectChannel inputChannel() {
            return new DirectChannel(); //MessageSource์—์„œ ๋„์ฐฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜๋ฅด๋Š” ์ฑ„๋„
		}

		@Bean
		public IntegrationFlow myFlow() {
            return IntegrationFlows
                    .from(this.integerMessageSource(), //๊ธฐ์กด์— ์ •์˜ํ•œ MessageSource๋ฅผ IntegrationFlow์˜ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ
                            c -> c.poller(Pollers.fixedRate(10))) // MessageSource๋ฅผ ํด๋งํ•˜๋ฉด์„œ MessageSource๊ฐ€ ๋‚˜๋ฅด๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ด
                    .channel(this.inputChannel())
                    .filter((Integer p) -> p % 2 == 0) //์ง์ˆ˜๋งŒ ๊ฑฐ๋ฆ„
                    .transform(Object::toString)
                    .channel(MessageChannels.queue("queueChannel")) //queueChannel์„ IntegrationFlow์˜ ๊ฒฐ๊ณผ๋กœ ์„ค์ •
                    .get(); //IntegrationFlow ๋งŒ๋“ค๊ธฐ๋ฅผ ๋๋‚˜๊ณ  ๋ฐ˜ํ™˜
		}
}

์Šคํ”„๋ง ํ†ตํ•ฉ DSL์„ ์‚ฌ์šฉํ•ด myFlow()๋Š” IntegrationFlow๋ฅผ ๋งŒ๋“ ๋‹ค. ์˜ˆ์ œ๋Š” ๋ฉ”์„œ๋“œ ์ฒด์ธ ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๋Š” IntegrationFlows๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์œ ์—ฐํ•œ ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฒฐ๊ณผ ํ”Œ๋กœ๋Š” ๊ณ ์ •๋œ ์†๋„๋กœ MessageSource๋ฅผ ํด๋งํ•˜๋ฉด์„œ ์ผ๋ จ์˜ ์ •์ˆ˜๋ฅผ ์ œ๊ณตํ•˜๊ณ , ์ง์ˆ˜๋งŒ ๊ฑฐ๋ฅธ ๋‹ค์Œ, ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•ด ์ตœ์ข…์ ์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ž๋ฐ” 8 ์ŠคํŠธ๋ฆผ API์™€ ๋น„์Šทํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ถœ๋ ฅ ์ฑ„๋„์— ์ „๋‹ฌํ•œ๋‹ค. inputChannel ์ด๋ฆ„๋งŒ ์•Œ๊ณ  ์žˆ๋‹ค๋ฉด ์ด API๋ฅผ ์ด์šฉํ•ด ํ”Œ๋กœ ๋‚ด์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
@Bean
public IntegrationFlow myFlow() {
    return flow -> flow.filter((Integer p) -> p % 2 == 0)
                        .transform(Object::toString)
                        .handle(System.out::println);
}

ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์Šคํ”„๋ง ํ†ตํ•ฉ DSL์—์„œ ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์€ ๋ฉ”์„œ๋“œ ์ฒด์ธ์ด๋‹ค. ์ด ํŒจํ„ด์€ IntegrationFlow ๋นŒ๋”์˜ ์ฃผ์š” ๋ชฉํ‘œ์ธ ์ „๋‹ฌ๋˜๋Š” ๋ฉ”์‹œ์ง€ ํ๋ฆ„์„ ๋งŒ๋“ค๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ๊ธฐ๋Šฅ์— ์ ํ•ฉํ•˜๋‹ค.

This post is licensed under CC BY 4.0 by the author.