Post

🐣 chap10. λžŒλ‹€λ₯Ό μ΄μš©ν•œ 도메인 μ „μš© μ–Έμ–΄

chap10. λžŒλ‹€λ₯Ό μ΄μš©ν•œ 도메인 μ „μš© μ–Έμ–΄

10.1 도메인 μ „μš© μ–Έμ–΄

DSL(domain specific languages)은 λ²”μš© ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄κ°€ μ•„λ‹ˆλΌ νŠΉμ • λΉ„μ¦ˆλ‹ˆμŠ€ λ„λ©”μΈμ˜ 문제λ₯Ό ν•΄κ²°ν•˜λ €κ³  λ§Œλ“  μ–Έμ–΄λ‹€.

νŠΉμ • λ„λ©”μΈμ—λ§Œ κ΅­ν•œλ˜λ―€λ‘œ 였직 μžμ‹ μ˜ 문제λ₯Ό μ–΄λ–»κ²Œ ν•΄κ²°ν• μ§€μ—λ§Œ 집쀑할 수 있고 λ³΅μž‘μ„±μ„ 잘 λ‹€λ£° 수 μžˆλ‹€.

  • DSL을 κ°œλ°œν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ 점
    • μ˜μ‚¬ μ†Œν†΅μ˜ μ™•: ν”„λ‘œκ·Έλž˜λ¨Έκ°€ μ•„λ‹Œ μ‚¬λžŒλ„ 이해할 수 μžˆλ„λ‘ μ½”λ“œμ˜ μ˜λ„κ°€ λͺ…ν™•νžˆ μ „λ‹¬λ˜μ–΄μ•Ό 함(μ½”λ“œκ°€ λΉ„μ¦ˆλ‹ˆμŠ€ μš”κ΅¬μ‚¬ν•­μ— λΆ€ν•©ν•˜λŠ”μ§€ 확인 κ°€λŠ₯)
    • ν•œ 번 μ½”λ“œλ₯Ό κ΅¬ν˜„ν•˜μ§€λ§Œ μ—¬λŸ¬λ²ˆ μ½λŠ”λ‹€: 가독성은 μœ μ§€λ³΄μˆ˜μ˜ 핡심. λ™λ£Œκ°€ μ‰½κ²Œ 이해할 수 μžˆλ„λ‘ μ½”λ“œ κ΅¬ν˜„ ν•„μˆ˜

μž₯점

  • 간결함
  • 가독성
  • μœ μ§€λ³΄μˆ˜
  • 높은 μˆ˜μ€€μ˜ 좔상화
  • 집쀑
  • 관심사 뢄리(Separation of concerns)

단점

  • DLS μ„€κ³„μ˜ 어렀움
  • 개발 λΉ„μš©
  • μΆ”κ°€ 우회 계측
  • μƒˆλ‘œ λ°°μ›Œμ•Ό ν•˜λŠ” μ–Έμ–΄
  • ν˜ΈμŠ€νŒ… μ–Έμ–΄ ν•œκ³„

λ‚΄λΆ€ DSL

  • 순수 μžλ°”μ½”λ“œ 같은 κΈ°μ‘΄ ν˜ΈμŠ€νŒ… μ–Έμ–΄λ₯Ό 기반으둜 κ΅¬ν˜„ν•œ DSL
  • μœ μ—°μ„±μ΄ 떨어지기 λ•Œλ¬Έμ— κ°„λ‹¨ν•˜κ³  ν‘œν˜„λ ₯ μžˆλŠ” DSL을 λ§Œλ“œλŠ”λ° ν•œκ³„κ°€ μžˆμ—ˆμ§€λ§Œ λžŒλ‹€ ν‘œν˜„μ‹μ΄ λ“±μž₯ν•˜λ©΄μ„œ μ–΄λŠμ •λ„ 해결될 수 있음
  • 순수 μžλ°”λ‘œ DSL 을 κ΅¬ν˜„ μ‹œ μž₯점
    • μ™ΈλΆ€ DSL에 λΉ„ν•΄ μƒˆλ‘œμš΄ νŒ¨ν„΄κ³Ό κΈ°μˆ μ„ λ°°μ›Œ DSL을 κ΅¬ν˜„ν•˜λŠ” λ…Έλ ₯이 κ°μ†Œ
    • 순수 μžλ°”λ‘œ DSL 을 κ΅¬ν˜„ν•˜λ©΄ λ‚˜λ¨Έμ§€ μ½”λ“œμ™€ ν•¨κ»˜ 컴파일 κ°€λŠ₯. (λ‹€λ₯Έ μ–Έμ–΄μ˜ 컴파일러 λ˜λŠ” μ™ΈλΆ€ DSL을 λ§Œλ“œλŠ” 도ꡬλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•„λ„ 됨)
    • μƒˆλ‘œμš΄ μ–Έμ–΄λ₯Ό λ°°μš°κ±°λ‚˜ λ³΅μž‘ν•œ μ™ΈλΆ€ 도ꡬλ₯Ό 배울 ν•„μš”κ°€ μ—†μŒ
    • DSL μ‚¬μš©μžλŠ” 기쑴의 μžλ°” IDE둜 μžλ™ μ™„μ„±, λ¦¬νŒ©ν„°λ§ κΈ°λŠ₯ κ·ΈλŒ€λ‘œ μ‚¬μš© κ°€λŠ₯
    • μΆ”κ°€ DSL을 μ‰½κ²Œ ν•©μΉ  수 있음

닀쀑 DSL

μžλ°”λŠ” μ•„λ‹ˆμ§€λ§Œ JVMμ—μ„œ μ‹€ν–‰λ˜λ©° 더 μœ μ—°ν•˜κ³  ν‘œν˜„λ ₯이 κ°€λŠ₯ν•œ μ–Έμ–΄(ex. 슀칼라, 그루비…)둜 κ΅¬ν˜„ν•œ DSL

μ½”ν‹€λ¦°, μ‹€λ‘  같이 μŠ€μΉΌλΌμ™€ ν˜Έν™˜μ„±μ΄ μœ μ§€λ˜λ©° λ‹¨μˆœν•˜κ³  μ‰½κ²Œ 배울 수 μžˆλŠ” μƒˆ 언어도 μ‘΄μž¬ν•œλ‹€.

이 언어듀은 λͺ¨λ‘ μžλ°”λ³΄λ‹€ μ ‹μœΌλ©° μ œμ•½μ„ 쀄이고, κ°„νŽΈν•œ 문법을 지ν–₯ν•˜λ„λ‘ μ„€κ³„λ˜μ—ˆλ‹€.

DSL μΉœν™”μ μ΄μ§€λ§Œ λ‹€μŒκ³Ό 같은 단점듀이 μ‘΄μž¬ν•œλ‹€.

  • μƒˆλ‘œμš΄ ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄λ₯Ό λ°°μš°κ±°λ‚˜ λˆ„κ΅°κ°€ ν•΄λ‹Ή κΈ°μˆ μ„ μ§€λ‹ˆκ³  μžˆμ–΄μ•Ό 함
  • 두 개 μ΄μƒμ˜ μ–Έμ–΄κ°€ ν˜Όμž¬ν•˜λ―€λ‘œ μ—¬λŸ¬ 컴파일러둜 μ†ŒμŠ€λ₯Ό λΉŒλ“œν•˜λ„λ‘ λΉŒλ“œ 과정을 κ°œμ„ ν•΄μ•Ό 함
  • JVM μ—μ„œ μ‹€ν–‰λ˜μ§€λ§Œ μžλ°”μ™€ ν˜Έν™˜μ„±μ΄ μ™„λ²½ν•˜μ§€ μ•Šμ€ κ²½μš°κ°€ 많음 (μ„±λŠ₯ 손싀 κ°€λŠ₯)

μ™ΈλΆ€ DSL

  • μŠ€νƒ λ“œμ–΄λ‘ (standalone)
  • ν˜ΈμŠ€νŒ… μ–Έμ–΄μ™€λŠ” λ…λ¦½μ μœΌλ‘œ 자체의 문법을 κ°€μ§€λŠ” DSL

μžμ‹ λ§Œμ˜ 문법과 ꡬ문으둜 μƒˆ μ–Έμ–΄λ₯Ό 섀계해야 ν•˜κ³  μ–Έμ–΄ νŒŒμ‹±, νŒŒμ„œμ˜ κ²°κ³Ό 뢄석, μ™ΈλΆ€ DSL μ‹€ν–‰ν•  μ½”λ“œλ₯Ό λ§Œλ“€μ–΄μ•Ό 함

이 방법을 선택해야 ν•œλ‹€λ©΄ ANTLR 같은 μžλ°” 기반 νŒŒμ„œ 생성기λ₯Ό μ΄μš©ν•˜λ©΄ 도움을 받을 수 있음

  • μž₯점
    • λ¬΄ν•œν•œ μœ μ—°μ„±μ„ 가짐
    • ν•„μš”ν•œ νŠΉμ„±μ„ μ™„λ²½ν•˜κ²Œ μ œκ³΅ν•˜λŠ” μ–Έμ–΄λ‘œ 섀계 κ°€λŠ₯
    • λΉ„μ¦ˆλ‹ˆμŠ€ 문제λ₯Ό λ¬˜μ‚¬ν•˜κ³  ν•΄κ²°ν•˜λŠ” 가독성 쒋은 μ–Έμ–΄ 선택 κ°€λŠ₯
    • μžλ°”λ‘œ 개발된 인프라ꡬ쑰 μ½”λ“œμ™€ λΉ„μ¦ˆλ‹ˆμŠ€ μ½”λ“œλ₯Ό λͺ…ν™•ν•˜κ²Œ 뢄리 κ°€λŠ₯
  • 단점
    • 일반적인 μž‘μ—…μ΄ μ•„λ‹ˆλ©° μ‰½κ²Œ κΈ°μˆ μ„ 얻을 수 μ—†μŒ
    • μž‘μ—…μ΄ λ³΅μž‘ν•˜κ³  μ œμ–΄ λ²”μœ„λ₯Ό μ‰½κ²Œ λ²—μ–΄λ‚  수 있음
    • 처음 μ„€κ³„ν•œ λͺ©μ μ„ λ²—μ–΄λ‚˜λŠ” κ²½μš°κ°€ 많음
    • DSLκ³Ό 호슀트 μ–Έμ–΄ 사이에 인곡 계측이 생김

10.2 μ΅œμ‹  μžλ°” API의 μž‘μ€ DSL

λ„€μ΄ν‹°λΈŒ μžλ°” API μ—λŠ” μžλ°” μƒˆλ‘œμš΄ κΈ°λŠ₯의 μž₯점듀이 μ μš©λ˜μ—ˆλ‹€.

슀트림 API λ₯Ό 톡해 DSL 이 μ‚¬μš©λœ 예λ₯Ό ν™•μΈν•œλ‹€.

슀트림 APIλŠ” μ»¬λ ‰μ…˜μ„ μ‘°μž‘ν•˜λŠ” DSL

StreamΒ μΈν„°νŽ˜μ΄μŠ€λŠ” λ„€μ΄ν‹°λΈŒ μžλ°” API 에 μž‘μ€ λ‚΄λΆ€ DSL을 μ μš©ν•œ 쒋은 μ˜ˆλ‹€.

μ»¬λ ‰μ…˜μ˜ ν•­λͺ©μ„ ν•„ν„°, μ •λ ¬, λ³€ν™˜, κ·Έλ£Ήν™”, μ‘°μž‘ν•˜λŠ” μž‘μ§€λ§Œ κ°•λ ₯ν•œ DSL 이닀.

StreamΒ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ΄μš©ν•˜μ—¬ ν•¨μˆ˜ν˜•μœΌλ‘œ κ΅¬ν˜„ν•˜λ©΄ 쉽고 κ°„κ²°ν•˜λ‹€.

슀트림 API의 ν”Œλ£¨μ–ΈνŠΈ ν˜•μ‹ λ˜ν•œ μ„€κ³„λœ DSL의 νŠΉμ§• 쀑 ν•˜λ‚˜μ΄λ‹€.(쀑간 μ—°μ‚° κ²ŒμœΌλ¦„, λ‹€λ₯Έ μ—°μ‚°μœΌλ‘œ νŒŒμ΄ν”„λΌμΈ κ°€λŠ₯)

1
**Files.**lines**(Paths.**get**(**fileName**)).**filter**(**line **->** line**.**startWith**(**"ERROR"**)).**limit**(**40**).**collect**(**toList**());**

데이터λ₯Ό μˆ˜μ§‘ν•˜λŠ” DSL인 Collectors

CollectorΒ μΈν„°νŽ˜μ΄μŠ€λŠ” 데이터 μˆ˜μ§‘(μˆ˜μ§‘, κ·Έλ£Ήν™”, νŒŒμ΄μ…˜)을 μˆ˜ν–‰ν•˜λŠ” DSL둜 κ°„μ£Όν•  수 μžˆλ‹€.

특히 닀쀑 ν•„λ“œ 정렬을 μ§€μ›ν•˜λ„λ‘ ν•©μ³μ§ˆ 수 있으며,Β CollectorsΒ λŠ” 닀쀑 μˆ˜μ€€ κ·Έλ£Ήν™”λ₯Ό 달성할 수 μžˆλ„λ‘ ν•©μ³μ§ˆ 수 μžˆλ‹€.

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

*// 두 Comparators μ—°κ²°*
**Comparator<Person>** comparator **=** comparing**(**Person:**:**getAge**).**thenComparing**(**Person:**:**getName**);**

*// 쀑첩*
**Collector<?** **super** **Car,** **?,** **Map<Brand,** **Map<Color,** **List<Car>>>>** carGroupingCollector **=**groupingBy**(**Car:**:**getBrand**,** groupingBy**(**Car:**:**getColor**));**

10.3 μžλ°”λ‘œ 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
public class Stock {
    private String symbol;
    private String market;
    public String getSymbol(){
        return symbol;
    }
    public void setSymbol(String symbol) {
        this.symbol = symbol;
    }
    ...
}
public class Trade {
    public enum Type { BUY, SELL }

    private Type type;
    private Stock stock;
    private int quantity;
    private double price;
    ...
}

public class Order {
    private String customer;
    private List<Trade> trades = new ArrayList<>();
    ...
}

μ£Όλ¬Έ 생성 μ½”λ“œ

1
2
3
4
5
6
7
8
9
10
11
12
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");

trade.setStock(stock1);
...

ν•˜μ§€λ§Œ μ΄λŸ¬ν•œ μ½”λ“œλŠ” μž₯ν™©ν•˜κ³  λΉ„κ°œλ°œμžμΈ 도메인 μ „λ¬Έκ°€κ°€ μ΄ν•΄ν•˜κ³  κ²€μ¦ν•˜κΈ° μ–΄λ ΅λ‹€.

μ§κ΄€μ μœΌλ‘œ 도메인 λͺ¨λΈμ„ λ°˜μ˜ν•  수 μžˆλŠ” DSL 이 ν•„μš”ν•˜λ‹€. 이 μ±…μ—μ„œλŠ” λ‹€μŒκ³Ό 같은 DSL νŒ¨ν„΄λ“€μ„ μ†Œκ°œν•˜κ³  μžˆλ‹€.

  • λ©”μ„œλ“œ 체인
  • μ€‘μ²©λœ ν•¨μˆ˜
  • λžŒλ‹€ ν‘œν˜„μ‹μ„ μ΄μš©ν•œ ν•¨μˆ˜ μ‹œν€€μ‹±

λ©”μ„œλ“œ 체인

λ©”μ„œλ“œ 체인은 DSL μ—μ„œ κ°€μž₯ ν”ν•œ 방식 쀑 ν•˜λ‚˜μ΄λ‹€.

  • μž₯점

    • μ‚¬μš©μžκ°€ μ§€μ •λœ μ ˆμ°¨μ— 따라 ν”Œλ£¨μ–ΈνŠΈ API의 λ©”μ„œλ“œ ν˜ΈμΆœμ„ κ°•μ œ
    • νŒŒλΌλ―Έν„°κ°€ λΉŒλ” λ‚΄λΆ€λ‘œ κ΅­ν•œ 됨
    • λ©”μ„œλ“œ 이름이 인수의 이름을 λŒ€μ‹ ν•˜μ—¬ 가독성 κ°œμ„ 
    • 문법적 작음이 μ΅œμ†Œν™”
    • μ„ νƒν˜• νŒŒλΌλ―Έν„°μ™€ 잘 λ™μž‘
    • 정적 λ©”μ„œλ“œ μ‚¬μš©μ„ μ΅œμ†Œν™”ν•˜κ±°λ‚˜ 없앨 수 있음
  • 단점

    • λΉŒλ”λ₯Ό κ΅¬ν˜„ν•΄μ•Ό 함
    • μƒμœ„ μˆ˜μ€€μ˜ λΉŒλ”λ₯Ό ν•˜μœ„ μˆ˜μ€€μ˜ λΉŒλ”μ™€ μ—°κ²°ν•  λ§Žμ€ μ ‘μ°© μ½”λ“œκ°€ ν•„μš”
    • λ„λ©˜μΈ 객체의 쀑첩 ꡬ쑰와 μΌμΉ˜ν•˜κ²Œ λ“€μ—¬μ“°κΈ°λ₯Ό κ°•μ œν•  수 μ—†μŒ
1
2
3
4
5
6
Order order = forCustomer("BigBank")
    .buy(80)
    .stock("IBM")
    .on("NYSE")
    ...
    .end();

β‡’ μ΅œμƒ μˆ˜μ€€ λΉŒλ”λ₯Ό λ§Œλ“€κ³  주문을 κ°μ‹Έμ„œ 거래λ₯Ό μΆ”κ°€ν•  수 μžˆλ„λ‘ ν•΄μ•Ό 함

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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) {
        return new TradeBuilder(this, Trade.Type.SELL, quantity);
    }
    public MethodChainingOrderBuilder addTrade(Trade trade) {
        order.addTrade(trade);
        return this;
    }
    public Order end() {
        return order;
    }
}

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);
    }
}

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);
    }
}

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);
    }
}

μ€‘μ²©λœ ν•¨μˆ˜ 이용

μ€‘μ²©λœ ν•¨μˆ˜ DSL νŒ¨ν„΄μ€ λ‹€λ₯Έ ν•¨μˆ˜ μ•ˆμ— ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄ 도메인 λͺ¨λΈμ„ λ§Œλ“ λ‹€.

  • μž₯점
    • 쀑첩 방식이 도메인 객체 계측 ꡬ쑰에 κ·ΈλŒ€λ‘œ 반영
    • κ΅¬ν˜„μ˜ μž₯황함을 쀄일 수 있음
  • 단점
    • κ²°κ³Ό DSL 에 더 λ§Žμ€ κ΄„ν˜Έλ₯Ό μ‚¬μš©
    • 정적 λ©”μ„œλ“œ μ‚¬μš©μ΄ 빈번
    • 인수 λͺ©λ‘μ„ 정적 λ©”μ„œλ“œμ— λ„˜κ²¨μ€˜μ•Ό 함
    • 인수의 μ˜λ―Έκ°€ 이름이 μ•„λ‹ˆλΌ μœ„μΉ˜μ— μ˜ν•΄ μ •μ˜ 됨
    • 도메인에 선택 사항 ν•„λ“œκ°€ 있으면 인수λ₯Ό μƒλž΅ν•  수 μžˆμœΌλ―€λ‘œ λ©”μ„œλ“œ μ˜€λ²„λ‘œλ”© ν•„μš”
1
2
3
4
Order order = order("BigBank",
                    buy(80, stock("IBM", on("NYSE")), at(125.00)),
                    sell(50, stock("GOOGLE", on("NASDAQ")), at(375.00))
                    );

κ΅¬ν˜„ μ½”λ“œ

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 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);
    }
    private static Trade buildTrade(int quantity, Stock stock, double price, Trade.Type type) {
        Trade trade = new Trade();
        trade.setQuantity(quantity);
        trade.setType(type);
        trade.setStock(stock);
        trade.setPrice(price);
        return trade;
    }
    public static double at(double price) {
        return price;
    }
    publid static Stock stock(String Symbol, String market) {
        Stock stock = new Stock();
        stock.setSymbol(symbol);
        stock.setMarket(market);
        return stock;
    }
}

λžŒλ‹€ ν‘œν˜„μ‹μ„ μ΄μš©ν•œ ν•¨μˆ˜ μ‹œν€€μ‹±

λžŒλ‹€ ν‘œν˜„μ‹μœΌλ‘œ μ •μ˜ν•œ ν•¨μˆ˜ μ‹œν€€μŠ€λ₯Ό μ‚¬μš©ν•œλ‹€.

  • μž₯점
    • ν”Œλ£¨μ–ΈνŠΈ λ°©μ‹μœΌλ‘œ 도메인 객체 μ •μ˜ κ°€λŠ₯
    • 쀑첩 방식이 도메인 객체 계측 ꡬ쑰에 κ·ΈλŒ€λ‘œ 반영
    • μ„ νƒν˜• νŒŒλΌλ―Έν„°μ™€ 잘 λ™μž‘
    • 정적 λ©”μ„œλ“œλ₯Ό μ΅œμ†Œν™”ν•˜κ±°λ‚˜ 없앨 수 있음
    • λΉŒλ”μ˜ μ ‘μ°© μ½”λ“œκ°€ μ—†μŒ
  • 단점
    • μ„€μ • μ½”λ“œκ°€ ν•„μš”
    • λžŒλ‹€ ν‘œν˜„μ‹ 문법에 μ˜ν•œ 작음의 영ν–₯을 λ°›μŒ

μ‘°ν•©ν•˜κΈ°

μ€‘μ²©λœ ν•¨μˆ˜ νŒ¨ν„΄κ³Ό λžŒλ‹€ 기법을 ν˜Όμš©ν•˜λ©΄ λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•  수 μžˆλ‹€.

  • μž₯점
    • 가독성 ν–₯상
  • 단점
    • μ—¬λŸ¬ 기법을 ν˜Όμš©ν–ˆκΈ° λ•Œλ¬Έμ— μ‚¬μš©μžκ°€ DSL을 λ°°μš°λŠ” μ‹œκ°„μ΄ 였래 κ±Έλ¦Ό
This post is licensed under CC BY 4.0 by the author.