๐น 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
์คํธ๋ฆผ 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
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
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
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 ํ์์ ๋ ๊ฐ์ง ์ฅ์ ์ ๋ํ๋ค.
[ ์ฅ์ ]
- ๋ฉ์๋ ์ฒด์ธ ํจํด์ฒ๋ผ ํ๋ฃจ์ธํธ ๋ฐฉ์์ผ๋ก ๊ฑฐ๋ ์ฃผ๋ฌธ์ ์ ์ํ ์ ์๋ค.
- ์ค์ฒฉ ํจ์ ํ์์ฒ๋ผ ๋ค์ํ ๋๋ค ํํ์์ ์ค์ฒฉ ์์ค๊ณผ ๋น์ทํ๊ฒ ๋๋ฉ์ธ ๊ฐ์ฒด์ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ์ ์งํ๋ค.
[ ๋จ์ ]
- ๋ง์ ์ค์ ์ฝ๋๊ฐ ํ์ํ๋ฉฐ 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 ํด๋์ค๋ฅผ ํตํด ๋ชจ๋ ์ธ๊ธ ์ค์ ์ด ์ ์ฉ๋๋ค.
- ์ฒ์ ์์ ์์๋ ์ธ๊ธ์ด ์ ์ฉ๋์ง ์์์ผ๋ฏ๋ก ์ต์ข ๊ฐ์ ์ดํฉ๊ณผ ๊ฐ๋ค.
- with() ๋ฉ์๋๋ก ์ ์ธ๊ธ์ด ์ถ๊ฐ๋๋ฉด ํ์ฌ ์ธ๊ธ ๊ณ์ฐ ํจ์์ ์ด ์ธ๊ธ์ด ์กฐํฉ๋๋ ๋ฐฉ์์ผ๋ก ํ ํจ์์ ๋ชจ๋ ์ถ๊ฐ๋ ์ธ๊ธ์ด ์ ์ฉ๋๋ค.
- ์ฃผ๋ฌธ์ 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 ๋น๋์ ์ฃผ์ ๋ชฉํ์ธ ์ ๋ฌ๋๋ ๋ฉ์์ง ํ๋ฆ์ ๋ง๋ค๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๋ ๊ธฐ๋ฅ์ ์ ํฉํ๋ค.