๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

์•„ํ‚คํ…์ฒ˜

๐Ÿ’ซ ์†Œํ”„ํŠธ์›จ์–ด ์•„ํ‚คํ…์ฒ˜ with ๋„๋ฉ”์ธ

์•„ํ‚คํ…์ฒ˜์˜ ์ค‘์š”์„ฑ?

์•„ํ‚คํ…์ฒ˜๋Š” ์—ฌ๋Ÿฌ ์ด์œ ๋“ค๋กœ ์ •๋‹ต์ด ์—†๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

  • ์›์น™๊ณผ ํŽธ์˜์„ฑ์—์„œ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๊ฐ€ ์กด์žฌ
  • ์•„ํ‚คํ…์ฒ˜๋Š” ์ œ์•ฝ์ด๋ฉฐ ์–ธ์ œ๋“  ๋ฐ”๋€Œ๋Š” ๊ฒƒ
  • ์•„ํ‚คํ…์ฒ˜๋Š” ์‚ฌ๋žŒ๋งˆ๋‹ค, ์กฐ์ง๋งˆ๋‹ค, ํšŒ์‚ฌ๋งˆ๋‹ค ์ƒ๊ฐ์ด ๋‹ค๋ฆ„
  • ์‹œ์Šคํ…œ ์•ˆ์ •์„ฑ๊ณผ ์ถ”์ƒํ™”๊ฐ€ ๋ชจ๋‘ 1์— ๊ฐ€๊นŒ์šด ์˜์—ญ์€ Zone of Exclusion (๐Ÿ“–Clean Architecture์—์„œ)

์ด ๊ธ€๋„

"ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜๊ฐ€ ์š”์ฆ˜ ์‹ฌ์‹ฌ์ฐฎ๊ฒŒ ๋“ค๋ฆฌ๋‹ˆ ๋‹น์žฅ ๋„์ž…ํ•ด ๋ด…์‹œ๋‹ค!"

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

๐Ÿ—๏ธ ์†Œํ”„ํŠธ ์•„ํ‚คํ…์ฒ˜

๋„๋ฉ”์ธ์˜ ์ค‘์š”์„ฑ

์†Œํ”„ํŠธ์›จ์–ด์—์„œ ๋„๋ฉ”์ธ์ด๋ž€? ์ผ๋ฐ˜์ ์œผ๋กœ ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ•˜๋Š” ๋ฌธ์ œ ์˜์—ญ ๋˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ ์šฉ๋˜๋Š” ํŠน์ • ๋ถ„์•ผ๋ฅผ ์˜๋ฏธ

 

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

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

์ด ์™ธ์—๋„ ์—ฌ๋Ÿฌ ๋‚ด์šฉ์ด ๋‚˜์˜ค์ง€๋งŒ ์ง์ ‘ ์ฝ”๋“œ๋‹จ์—์„œ ๋Š๊ผˆ๋˜ ๋„๋ฉ”์ธ์˜ ์žฅ์ ์„ ์„ค๋ช…ํ•ด ๋ณด์ž๋ฉด,

@Transactional
public void updateProductForSale(Long productId, int quantitySold, int discountRate) {
    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ƒํ’ˆ ์กฐํšŒ ํ›„ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋กœ ๋ฆฌํ„ด
    Product product = productRepository.findById(productId);

    // ํ• ์ธ์œจ ๊ฒ€์ฆ
    if (discountRate < 0) {
        throw new IllegalArgumentException("ํ• ์ธ์œจ์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค.");
    }

    // ํ• ์ธ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ
    Long originalPrice = product.getPrice();
    double discount = originalPrice * (discountRate / 100.0);
    Long discountedPrice = originalPrice - (long) discount;

    // ์žฌ๊ณ  ๊ฒ€์ฆ
    if (product.getStockQuantity() < quantitySold) {
        throw new IllegalArgumentException("ํŒ๋งค๋Ÿ‰์ด ์žฌ๊ณ ๋ณด๋‹ค ๋งŽ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
    }

    // ์žฌ๊ณ  ๊ฐ์†Œ
    int newStockQuantity = product.getStockQuantity() - quantitySold;
    product.setStockQuantity(newStockQuantity);

    // ํ• ์ธ๋œ ๊ฐ€๊ฒฉ์œผ๋กœ ์—…๋ฐ์ดํŠธ
    product.setPrice(discountedPrice);

    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ €์žฅ
    productRepository.save(product);

    System.out.println("ํ• ์ธ๋œ ๊ฐ€๊ฒฉ์œผ๋กœ ํŒ๋งค ์ฒ˜๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค: " + discountedPrice + "์›");
}

์ด ์ฝ”๋“œ ์˜ˆ์ œ์—์„œ๋Š” ํ• ์ธ์œจ ๊ฒ€์ฆ, ํ• ์ธ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ, ์žฌ๊ณ  ๊ฒ€์ฆ ๋ฐ ๊ฐ์†Œ ๋“ฑ์˜ ๋ชจ๋“  ๋กœ์ง์ด ์„œ๋น„์Šค ๊ณ„์ธต ๋‚ด์— ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
์ด์ฒ˜๋Ÿผ ์„œ๋น„์Šค์— ๋งŽ์€ ๋„๋ฉ”์ธ ๊ด€๋ จ ๋กœ์ง์ด ์žˆ๋Š” ๊ฒฝ์šฐ๋ฅผ fat service๋ผ๊ณ ๋„ ๋ถ€๋ฅด๋Š”๋ฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

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

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Product์˜ ๋„๋ฉ”์ธ ๋กœ์ง์„ Product ์ฝ”๋“œ์— ์œ„์ž„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

public class Product {

    // ๊ด€๋ จ ํ•„๋“œ, ์ƒ์„ฑ์ž ๋“ฑ ์ƒ๋žต, ๋„๋ฉ”์ธ์€ POJO ๊ฐ์ฒด

    // ์ƒํ’ˆ ๊ฐ€๊ฒฉ์— ๋”ฐ๋ฅธ ํ• ์ธ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ
    public Long calculateDiscountedPrice() {
        if (this.price <= 0 || this.discountRate < 0) {
            throw new IllegalArgumentException("๊ฐ€๊ฒฉ๊ณผ ํ• ์ธ์œจ์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค.");
        }
        double discount = this.price * (this.discountRate / 100.0);
        return this.price - (long) discount;
    }

    // ์ƒํ’ˆ ํŒ๋งค๋กœ ์ธํ•œ ์žฌ๊ณ  ๊ฐ์†Œ ์ฒ˜๋ฆฌ
    public void sellProduct(int quantitySold) {
        if (this.stockQuantity < quantitySold) {
            throw new IllegalArgumentException("ํŒ๋งค๋Ÿ‰์ด ์žฌ๊ณ ๋ณด๋‹ค ๋งŽ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
        }
        this.stockQuantity -= quantitySold;
    }
}
@Transactional
public void updateProductForSale(Long productId, int quantitySold, int discountRate) {
    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ƒํ’ˆ ์กฐํšŒ ํ›„ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋กœ ๋ฆฌํ„ด
    Product product = productRepository.findById(productId);

    // ํ• ์ธ์œจ ์—…๋ฐ์ดํŠธ
    product.setDiscountRate(discountRate);

    // ํ• ์ธ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ
    Long discountedPrice = product.calculateDiscountedPrice();

    // ํŒ๋งค ์ฒ˜๋ฆฌ
    product.sellProduct(quantitySold);

    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ €์žฅ
    productRepository.save(product);

    // ํ• ์ธ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ ์ถœ๋ ฅ
    System.out.println("ํ• ์ธ๋œ ๊ฐ€๊ฒฉ: " + discountedPrice + "์›");
}

์ด๋ ‡๊ฒŒ Service๊ฐ€ ํ•˜๋˜ ์—ญํ• ์„ ๋„๋ฉ”์ธ์— ์œ„์ž„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์‹ค์ œ ์—…๋ฌด๋ฅผ ๋„๋ฉ”์ธ ์˜์—ญ์—์„œ ํ•˜๊ฒŒ ๋จ
    • ๋„๋ฉ”์ธ ๋ ˆ์ด์–ด๋Š” ๊ฐ์ฒด์ง€ํ–ฅ์Šค๋Ÿฌ์šด ๊ฐ์ฒด๋“ค์˜ ํ˜‘๋ ฅ ๊ณต๊ฐ„์ด ๋จ(TDA(Tell, Don't Ask) ๋ฒ•์น™ ์ค€์ˆ˜)
  • ๋‹จ์ˆœํ™”๋œ ์„œ๋น„์Šค ๊ณ„์ธต
    • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋„๋ฉ”์ธ ๋ชจ๋ธ ๋‚ด๋ถ€์— ์บก์Šํ™”ํ•จ์œผ๋กœ์จ, ์„œ๋น„์Šค ๊ณ„์ธต์€ ๋„๋ฉ”์ธ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์กฐํ•ฉํ•˜๋Š” ์—ญํ• ์— ์ง‘์ค‘
๐Ÿ’ก TDA (Tell, Don't Ask) ๋ฒ•์น™์ด๋ž€?
- TDA ๋ฒ•์น™์€ "๋ฌป์ง€ ๋ง๊ณ  ๋ช…๋ นํ•˜๋ผ"๋Š” ์›์น™์œผ๋กœ, ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ์ง์ ‘ ์กฐํšŒํ•˜์—ฌ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ๋ณด๋‹ค๋Š”, ํ•ด๋‹น ๊ฐ์ฒด์—๊ฒŒ ์›ํ•˜๋Š” ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์š”์ฒญ
- ์ด ๋ฒ•์น™์˜ ํ•ต์‹ฌ์€ ๊ฐ์ฒด์˜ ๋‚ด๋ถ€ ์ƒํƒœ์— ๋Œ€ํ•ด ๋ฌป์ง€ ๋ง๊ณ , ํ•„์š”ํ•œ ๋™์ž‘์„ ๊ทธ ๊ฐ์ฒด์—๊ฒŒ ๋งก๊ธฐ๋Š” ๊ฒƒ
- ๊ทธ๋ ‡๊ฒŒ ํ•  ์‹œ ๊ฐ์ฒด์˜ ์บก์Šํ™”๊ฐ€ ์œ ์ง€๋˜๊ณ , ๊ฐ์ฒด ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์•„์ ธ์„œ ์‹œ์Šคํ…œ์˜ ์œ ์—ฐ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ
public class ProductTest {

    @Test
    public void ํ• ์ธ๋œ_๊ฐ€๊ฒฉ_๊ณ„์‚ฐ_์œ ํšจํ•œ_์ž…๋ ฅ_์ •ํ™•ํ•œ_๊ฒฐ๊ณผ_๋ฐ˜ํ™˜() {
        // given
        Product product = new Product(10000L, 10);

        // when
        Long discountedPrice = product.calculateDiscountedPrice();

        // then
        assertEquals(9000L, discountedPrice);
    }

    @Test
    public void ํ• ์ธ๋œ_๊ฐ€๊ฒฉ_๊ณ„์‚ฐ_ํ• ์ธ์œจ_์Œ์ˆ˜_์˜ˆ์™ธ_๋ฐœ์ƒ() {
        // given
        Product product = new Product(10000L, -10);

        // when, then
        assertThrows(IllegalArgumentException.class, product::calculateDiscountedPrice);
    }
}

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

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

์•„ํ‚คํ…์ฒ˜ ๊ตฌ์กฐ ๋ฐ”๊ฟ”๋ณด๊ธฐ

๋„๋ฉ”์ธ์˜ ์ค‘์š”์„ฑ์„ ์•Œ์•˜์œผ๋‹ˆ ์ด๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋ฐ”๊ฟ”๋ด…์‹œ๋‹ค.

๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜

์ผ๋ฐ˜์ ์ธ ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜์ž…๋‹ˆ๋‹ค.
๊ตฌํ˜„ํ•˜๊ธฐ ์‰ฝ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

1๏ธโƒฃ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•  ๋•Œ ๋„๋ฉ”์ธ ์œ„์ฃผ๋กœ ์ƒ๊ฐํ•˜์—ฌ ์„ค๊ณ„ํ•  ์ˆ˜ ์—†๋‹ค.
   ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ ๋„๋ฉ”์ธ๊ณผ ๋„๋ฉ”์ธ ์‚ฌ์ด์˜ ๊ด€๊ณ„ ๋“ฑ์˜ ์œ ์ฆˆ ์ผ€์ด์Šค๋ฅผ ๋จผ์ € ์„ค๊ณ„ํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
   (์ƒํ’ˆ์„ ํŒŒ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋ผ๋ฉด ์ƒํ’ˆ ๊ด€๋ จ ๊ฐ€๊ฒฉ ์ •์ฑ…, ์ฃผ๋ฌธ ๋ฐฉ์‹, ์ƒํ’ˆ ๋ฆฌ๋ทฐ ๋Œ“๊ธ€ ๊ด€๋ฆฌ ๋“ฑ...)
   ์œ„์˜ ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜๋กœ ์„ค๊ณ„ํ•˜๋‹ค ๋ณด๋ฉด ๋„๋ฉ”์ธ์ด ์•„๋‹Œ ๋‹ค๋ฅธ ๋ถ€๋ถ„์„ ์œ„์ฃผ๋กœ ์„ค๊ณ„ํ•˜๊ฒŒ ๋  ํ™•๋ฅ ์ด ํฝ๋‹ˆ๋‹ค.
    - ํ•˜ํ–ฅ์‹(repository)์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉด DB ์œ„์ฃผ์˜ ์„ค๊ณ„(๋””๋น„๋Š” ์–ด๋–ค ๊ฒƒ์„ ์จ์•ผ ํ• ์ง€, ์Šคํ‚ค๋งˆ๋Š” ์–ด๋–ป๊ฒŒ ์งœ์•ผ ํ• ์ง€)
    - ์ƒํ–ฅ์‹(controller)์œผ๋กœ ์ƒ๊ฐ ์‹œ์—” REST๋ƒ gRPC๋ƒ.. ๋“ฑ์œผ๋กœ ์„ค๊ณ„๋ฅผ ์‹œ์ž‘
2๏ธโƒฃ Controller๊ฐ€ Service๋ฅผ, Service๊ฐ€ Repostory๋ฅผ ์˜์กด
    ์ด๋Š” Repository์˜ ๋ณ€ํ™”๊ฐ€ Service์™€ Controller์— ์˜ํ–ฅ์„ ๋ผ์ณ OCP๋ฅผ ์œ„๋ฐ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ํŠนํžˆ DB ๋“ฑ์ด ๋ณ€๊ฒฝ๋  ๋•Œ (์˜ˆ๋ฅผ ๋“ค์–ด MySQl์—์„œ MongoDB๋กœ DB๊ฐ€ ๋ฐ”๋€œ) ์„œ๋น„์Šค ์ฝ”๋“œ์—๋„ ํฐ ๋ณ€ํ™”๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.

๋„๋ฉ”์ธ ๋ ˆ์ด์–ด ์ถ”๊ฐ€

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

โ—๏ธ์˜์กด์„ฑ ์—ญ์ „

 

์œ„์˜ 2๋ฒˆ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Controller๋Š” Service ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์˜์กด, Service๋Š” Repository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์˜์กดํ•˜๋„๋ก ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.
๊ธฐ์กด์˜ Service์™€ Repository๋Š” ๊ฐ๊ฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝ ์‹œ ์˜์กด์„ฑ์˜ ๋ฐฉํ–ฅ์ด ๋ฐ”๋€Œ๋Š”๋ฐ์š”! ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ์˜์กด์„ฑ์˜ ์—ญ์ „์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
์ด ์˜์กด์„ฑ ์—ญ์ „์œผ๋กœ 2๋ฒˆ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌ, ๊ณ ๋ฆฝ์„ ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋˜ํ•œ Service๋‚˜ Repository๋ฅผ ์˜์กดํ•˜๊ณ  ์žˆ๋Š” ๊ฐ์ฒด์˜ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•  ๋•Œ Fake ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
(โœ‹ ์ฐธ๊ณ ๋กœ ๋‹ค๋ฅธ ๊ฐœ๋…์ด์ง€๋งŒ ์•ฝ์ž๊ฐ€ ๊ฐ™์€ ์˜์กด์„ฑ ์ฃผ์ž…์€ ๋ณดํ†ต DI(Dependency Injection), ์˜์กด์„ฑ ์—ญ์ „์€ DIP(Dependency Inversion)๋กœ ๋ถ€๋ฅด๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.)

๋„๋ฉ”์ธ์„ ๊ฐ€์šด๋ฐ๋กœ ๊ทธ๋ฆผ์„ ๋ฐ”๊พธ๊ธฐ

์ด๋Ÿฐ ์‹์œผ๋กœ ๊ฐ™์€ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋„๋ฉ”์ธ์ด ๊ฐ€์šด๋ฐ์— ํ–ฅํ•˜๋„๋ก ๊ทธ๋ฆผ์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐ŸŒŸ ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜, ํ•ต์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜

(์ด๋ฏธ์ง€ ์ถœ์ฒ˜ : https://reflectoring.io/spring-hexagonal)

ํ•ต์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜๋ผ๊ณ  ๊ฒ€์ƒ‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ทธ๋ฆผ์ด ๋‚˜์˜ค๋Š”๋ฐ์š”!

์œ„์˜ ๊ทธ๋ฆผ์ด ๊ฒฐ๊ตญ ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜, ํ•ต์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜์™€ ๊ฐ™์€ ๋‚ด์šฉ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
๋ถ€๋ฅด๋Š” ์šฉ์–ด๋“ค์€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.


ํ•ต์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜์—์„  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํฌํŠธ, ํ˜ธ์ถœํ•˜๋Š” ๊ฐ์ฒด, ๊ตฌํ˜„ ๊ฐ์ฒด๋Š” ์•„๋‹ตํ„ฐ๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

  • ์˜์กด์„ฑ์— ๋ฐฉํ–ฅ์— ๋”ฐ๋ผ ์ธํ’‹ ์•„๋‹ตํ„ฐ, ์ธํ’‹ ํฌํŠธ, ์•„์›ƒํ’‹ ํฌํŠธ, ์•„์›ƒํ’‹ ์•„๋‹ตํ„ฐ๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.


ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜์—์„  ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋„๋ฉ”์ธ๊ณผ ์œ ์ฆˆ์ผ€์ด์Šค๋ฅผ ์ œ์™ธํ•˜๊ณค ํ—˜๋ธ”(humble)์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

  • ํ•ต์‚ฌ๊ณ ๋‚ ๊ณผ ์•„ํ‚คํ…์ฒ˜์™€ ๋น„๊ตํ•˜์—ฌ ์ธํ’‹ ํฌํŠธ๋ฅผ ์œ ์ฆˆ์ผ€์ด์Šค, ์•„์›ƒํ’‹ ํฌํŠธ๋Š” ๊ฒŒ์ดํŠธ์›จ์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

๋‚ด์šฉ์ด ๊ธธ์—ˆ์ง€๋งŒ ๊ฒฐ๋ก ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋„๋ฉ”์ธ์˜ ์ค‘์š”์„ฑ์„ ์ธ์‹
    • ๋ณธ์งˆ์€ ๋„๋ฉ”์ธ, ๋„๋ฉ”์ธ ์™ธ์˜ ๊ฒƒ๋“ค (DB, ํ”„๋ ˆ์ž„์›Œํฌ, ์–ธ์–ด ๋“ฑ)์€ ์–ธ์ œ๋“  ๋ณ€ํ•  ์ˆ˜ ์žˆ๊ณ  ์ด์— ๋Œ€๋น„ํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์—ฐํ•œ ์•„ํ‚คํ…์ฒ˜๊ฐ€ ํ•„์š”
  • ๋„๋ฉ”์ธ ์ค‘์‹ฌ ์„ค๊ณ„๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šค ๊ณ„์ธต์˜ ๋ณต์žก๋„๋ฅผ ์ค„์ด๊ณ  ์œ ์ง€ ๋ณด์ˆ˜์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์„ ํ–ฅ์ƒ
  • ์˜์กด์„ฑ ์—ญ์ „์„ ์ ์šฉํ•˜์—ฌ OCP ์œ„๋ฐ˜์„ ๋ฐฉ์ง€ํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜ ์ „๋žต์„ ๊ตฌํ˜„