๐ซ ์ํํธ์จ์ด ์ํคํ ์ฒ with ๋๋ฉ์ธ
์ํคํ ์ฒ์ ์ค์์ฑ?
์ํคํ ์ฒ๋ ์ฌ๋ฌ ์ด์ ๋ค๋ก ์ ๋ต์ด ์๋ค๊ณ ํฉ๋๋ค.
- ์์น๊ณผ ํธ์์ฑ์์
ํธ๋ ์ด๋์คํ
๊ฐ ์กด์ฌ - ์ํคํ
์ฒ๋
์ ์ฝ
์ด๋ฉฐ ์ธ์ ๋ ๋ฐ๋๋ ๊ฒ - ์ํคํ
์ฒ๋ ์ฌ๋๋ง๋ค, ์กฐ์ง๋ง๋ค, ํ์ฌ๋ง๋ค
์๊ฐ์ด ๋ค๋ฆ
- ์์คํ
์์ ์ฑ๊ณผ ์ถ์ํ๊ฐ ๋ชจ๋ 1์ ๊ฐ๊น์ด ์์ญ์
Zone of Exclusion
(๐Clean Architecture์์)
์ด ๊ธ๋
"ํฅ์ฌ๊ณ ๋ ์ํคํ
์ฒ๊ฐ ์์ฆ ์ฌ์ฌ์ฐฎ๊ฒ ๋ค๋ฆฌ๋ ๋น์ฅ ๋์
ํด ๋ด
์๋ค!"
์ ๊ฐ์ ๊ธ์ด ์๋ ๋๋ฉ์ธ์ ์ค์ํ๊ฒ ์ฌ๊ธฐ๋ ๊ฐ๋ฐ๊ณผ ์ํคํ ์ฒ๋ฅผ ๊ณต๋ถํ๋ฉฐ ์ป์ ๋๋ ์ ๋ค์ ๊ณต์ ํ๊ณ ์ ๊ธ์ ์์ฑํฉ๋๋ค.
- ๋์๋ฐ์ ๊ฐ์, ์ฑ : ํด๋ฆฐ ์ํคํ ์ฒ, ์ธํ๋ฐ ๊ฐ์-Java/Spring ์ฃผ๋์ด ๊ฐ๋ฐ์๋ฅผ ์ํ ์ค๋ต๋ ธํธ
๐๏ธ ์ํํธ ์ํคํ ์ฒ
๋๋ฉ์ธ์ ์ค์์ฑ
์ํํธ์จ์ด์์ ๋๋ฉ์ธ์ด๋? ์ผ๋ฐ์ ์ผ๋ก ์ํํธ์จ์ด๊ฐ ํด๊ฒฐํ๋ ค๊ณ ํ๋ ๋ฌธ์ ์์ญ ๋๋ ๋น์ฆ๋์ค ๋ก์ง์ด ์ ์ฉ๋๋ ํน์ ๋ถ์ผ๋ฅผ ์๋ฏธ
์ํคํ
์ฒ์ ๋ํ ์๊ธฐ๋ฅผ ํ๊ธฐ ์ ๋๋ฉ์ธ์ ์ค์์ฑ์ ์๊ธฐํด ๋ณด๊ณ ์ ํฉ๋๋ค.
์ํํธ์จ์ด ๊ฐ๋ฐ์์ ๋๋ฉ์ธ์ด ์ค์ํ๋ค๊ณ ํฉ๋๋ค.
- ๋๋ฉ์ธ์ ์ํํธ์จ์ด์ ํต์ฌ์ด๊ณ ๋น์ฆ๋์ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ฐ์ฒด
- ๋๋ฉ์ธ ์ง์์ ๋ฌธ์ ๋ฅผ ๊น์ด ์ดํดํ๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํจ๊ณผ์ ์ธ ํด๊ฒฐ์ฑ ์ ์ค๊ณํ๋ ๋ฐ ํ์์
- ์์คํ
์ ๋ชจ๋์ฑ ์ฆ๊ฐ
- ๋๋ฉ์ธ ์ค์ฌ์ผ๋ก ์์คํ ์ ๊ตฌ์ฑํ๋ฉด, ๊ด๋ จ ๊ธฐ๋ฅ๊ณผ ๋ฐ์ดํฐ๋ฅผ ๋๋ฉ์ธ ๋ณ๋ก ๊ทธ๋ฃนํ
- ๋ถ๋ฆฌ์ ์ถ์ํ์ ์ค์์ฑ
- ๋๋ฉ์ธ ๋ก์ง๊ณผ ์ธํ๋ผ์คํธ๋ญ์ฒ, 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 ์๋ฐ์ ๋ฐฉ์งํ๋ ์ํคํ ์ฒ ์ ๋ต์ ๊ตฌํ