Ver3.0 ๐ฏ C# ๋์์ธ ํจํด๊ณผ ์์กด์ฑ ์ฃผ์ ์ผ๋ก ๋ง๋๋ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋ ์ค๊ณ ์ ๋ต

๐ฏ C# ๋์์ธ ํจํด๊ณผ ์์กด์ฑ ์ฃผ์ ์ผ๋ก ๋ง๋๋ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋ ์ค๊ณ ์ ๋ต
์น๊ตฌ์ฒ๋ผ ์ฝ๊ฒ ๋ฐฐ์ฐ๋ ์ค์ ์ฝ๋ ์ํคํ ์ฒ ๊ฐ์ด๋ ๐
์๋
! ๐ ์ค๋์ C# ๊ฐ๋ฐ์๋ผ๋ฉด ๊ผญ ์์์ผ ํ ํต์ฌ ์ฃผ์ ๋ฅผ ๋ค๋ค๋ณผ ๊ฑฐ์ผ.
๋ฐ๋ก ๋์์ธ ํจํด๊ณผ ์์กด์ฑ ์ฃผ์
(Dependency Injection)์ ํ์ฉํด์ ์ ์ง๋ณด์๊ฐ ์ฌ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ด์ง! ๐
์ฝ๋๋ฅผ ์ง๋ค ๋ณด๋ฉด ์ฒ์์ ์ ๋์๊ฐ๋ ๊ฒ ๊ฐ์๋ฐ, ์๊ฐ์ด ์ง๋๋ฉด์ ์์ ํ๊ธฐ ์ด๋ ต๊ณ ๋ฒ๊ทธ๋ ๊ณ์ ์๊ธฐ๊ณ ... ์ด๋ฐ ๊ฒฝํ ์์ง?
์ด๊ฑด ๋๋ถ๋ถ ์ฝ๋ ๊ตฌ์กฐ ์ค๊ณ๋ฅผ ์ ๋๋ก ํ์ง ์์์ ์๊ธฐ๋ ๋ฌธ์ ์ผ. ์ค๋ ์ด์ผ๊ธฐํ ๋ด์ฉ๋ค์ ์ ๋๋ก ์ดํดํ๋ฉด, ๋์ ์ฝ๋๋ ํจ์ฌ ๋ ๊น๋ํ๊ณ ํ์ฅ ๊ฐ๋ฅํ๋ฉฐ ํ
์คํธํ๊ธฐ ์ฌ์ด ๊ตฌ์กฐ๋ก ๋ณ์ ํ ๊ฑฐ์ผ! โจ
๐ค ์ ๋์์ธ ํจํด๊ณผ ์์กด์ฑ ์ฃผ์ ์ด ์ค์ํ ๊น?
๋ณธ๊ฒฉ์ ์ผ๋ก ๋ค์ด๊ฐ๊ธฐ ์ ์, ์ ์ด๋ฐ ๊ฐ๋
๋ค์ด ์ค์ํ์ง๋ถํฐ ์ด์ผ๊ธฐํด๋ณด์.
์ํํธ์จ์ด ๊ฐ๋ฐ์์ ๊ฐ์ฅ ํฐ ๋น์ฉ์ ์ฌ์ค '์ ์ง๋ณด์'์ผ. ์ฒ์ ๊ฐ๋ฐํ๋ ์๊ฐ๋ณด๋ค ๋์ค์ ์์ ํ๊ณ ๊ฐ์ ํ๋ ์๊ฐ์ด ํจ์ฌ ๋ ๋ง๊ฑฐ๋ .
๊ทธ๋์ ์ฒ์๋ถํฐ ๋ณ๊ฒฝ์ ์ ์ฐํ ๊ตฌ์กฐ๋ก ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ ์ ๋ง ์ค์ํด! ๐ก
๐ฏ ์ข์ ์ฝ๋ ์ค๊ณ์ ํต์ฌ ์์น:
1. ๋ฎ์ ๊ฒฐํฉ๋(Low Coupling) - ํด๋์ค๋ค์ด ์๋ก ๋
๋ฆฝ์ ์ด์ด์ผ ํด
2. ๋์ ์์ง๋(High Cohesion) - ๊ด๋ จ๋ ๊ธฐ๋ฅ๋ค์ ํ ๊ณณ์ ๋ชจ์ฌ์์ด์ผ ํด
3. ๋จ์ผ ์ฑ
์ ์์น(SRP) - ํ๋์ ํด๋์ค๋ ํ๋์ ์ฑ
์๋ง ๊ฐ์ ธ์ผ ํด
4. ๊ฐ๋ฐฉ-ํ์ ์์น(OCP) - ํ์ฅ์๋ ์ด๋ ค์๊ณ ์์ ์๋ ๋ซํ์์ด์ผ ํด
5. ์์กด์ฑ ์ญ์ ์์น(DIP) - ๊ตฌ์ฒด์ ์ธ ๊ฒ๋ณด๋ค ์ถ์์ ์ธ ๊ฒ์ ์์กดํด์ผ ํด
์ด๋ฐ ์์น๋ค์ ์ค์ ๋ก ๊ตฌํํ๋ ๋๊ตฌ๊ฐ ๋ฐ๋ก ๋์์ธ ํจํด์ด๊ณ ,
๊ทธ์ค์์๋ ์์กด์ฑ ๊ด๋ฆฌ๋ฅผ ์ํ ํต์ฌ ๊ธฐ๋ฒ์ด ์์กด์ฑ ์ฃผ์
์ด์ผ! ๐ง
๐จ ๋์์ธ ํจํด์ด๋ ๋ฌด์์ธ๊ฐ?
๋์์ธ ํจํด์ ์ํํธ์จ์ด ์ค๊ณ์์ ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค์ ๋ํ ๊ฒ์ฆ๋ ํด๊ฒฐ์ฑ
์ด์ผ.
๋ง์น ์๋ฆฌํ ๋ ๋ ์ํผ๋ฅผ ๋ฐ๋ผํ๋ฏ์ด, ํน์ ์ํฉ์์ ์ด๋ป๊ฒ ์ฝ๋๋ฅผ ๊ตฌ์กฐํํ๋ฉด ์ข์์ง์ ๋ํ ๊ฐ์ด๋๋ผ์ธ์ด์ง! ๐จโ๐ณ
1994๋
์ 'Gang of Four(GoF)'๋ผ๊ณ ๋ถ๋ฆฌ๋ ๋ค ๋ช
์ ๊ฐ๋ฐ์๊ฐ 23๊ฐ์ง ๋์์ธ ํจํด์ ์ ๋ฆฌํ๋๋ฐ,
์ด๊ฒ ์ง๊ธ๊น์ง๋ ์ํํธ์จ์ด ์ค๊ณ์ ๋ฐ์ด๋ธ๋ก ์ฌ๊ฒจ์ง๊ณ ์์ด.
๐๏ธ ์์ฑ ํจํด (Creational)
๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๊ดํ ํจํด
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
๐ง ๊ตฌ์กฐ ํจํด (Structural)
ํด๋์ค๋ ๊ฐ์ฒด๋ฅผ ์กฐํฉํ๋ ํจํด
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Proxy
โก ํ์ ํจํด (Behavioral)
๊ฐ์ฒด ๊ฐ ์ํธ์์ฉ์ ๊ดํ ํจํด
- Strategy
- Observer
- Command
- Template Method
- Iterator
C#์์ ํนํ ์์ฃผ ์ฌ์ฉ๋๋ ํจํด๋ค์ ์ค์ฌ์ผ๋ก ์ค์ ์ฝ๋ ์์ ์ ํจ๊ป ์ดํด๋ณผ๊ฒ! ๐ป
๐ฏ Singleton ํจํด - ๋จ ํ๋์ ์ธ์คํด์ค
Singleton ํจํด์ ํด๋์ค์ ์ธ์คํด์ค๊ฐ ์ค์ง ํ๋๋ง ์กด์ฌํ๋๋ก ๋ณด์ฅํ๋ ํจํด์ด์ผ.
์๋ฅผ ๋ค์ด, ์ ํ๋ฆฌ์ผ์ด์
์ค์ ๊ด๋ฆฌ์๋ ๋ก๊ทธ ๊ธฐ๋ก๊ธฐ ๊ฐ์ ๊ฒฝ์ฐ ์ฌ๋ฌ ๊ฐ๊ฐ ์์ผ๋ฉด ์ ๋์์? ์ด๋ด ๋ ์ฌ์ฉํด! ๐ช
โ ๋์ ์์ - Singleton ์์ด ๊ตฌํ
public class ConfigurationManager
{
private Dictionary<string, string> settings;
public ConfigurationManager()
{
settings = new Dictionary<string, string>();
LoadSettings(); // ๋งค๋ฒ ํ์ผ์์ ์ค์ ์ ์ฝ์ด์ด
}
private void LoadSettings()
{
// ํ์ผ์์ ์ค์ ๋ก๋ (๋น์ฉ์ด ํฐ ์์
)
}
}
// ์ฌ์ฉํ๋ ๊ณณ๋ง๋ค ์๋ก์ด ์ธ์คํด์ค ์์ฑ
var config1 = new ConfigurationManager(); // ํ์ผ ์ฝ๊ธฐ
var config2 = new ConfigurationManager(); // ๋ ํ์ผ ์ฝ๊ธฐ
var config3 = new ConfigurationManager(); // ๋๋ ํ์ผ ์ฝ๊ธฐ
// ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น, ์ฑ๋ฅ ์ ํ!
โ ์ข์ ์์ - Singleton ํจํด ์ ์ฉ
public sealed class ConfigurationManager
{
private static readonly Lazy<ConfigurationManager> instance =
new Lazy<ConfigurationManager>(() => new ConfigurationManager());
private Dictionary<string, string> settings;
// private ์์ฑ์๋ก ์ธ๋ถ์์ ์ธ์คํด์ค ์์ฑ ๋ฐฉ์ง
private ConfigurationManager()
{
settings = new Dictionary<string, string>();
LoadSettings(); // ๋ฑ ํ ๋ฒ๋ง ์คํ๋จ
}
public static ConfigurationManager Instance => instance.Value;
private void LoadSettings()
{
// ํ์ผ์์ ์ค์ ๋ก๋
settings["DatabaseConnection"] = "Server=localhost;Database=MyDB";
settings["ApiKey"] = "abc123xyz";
}
public string GetSetting(string key)
{
return settings.ContainsKey(key) ? settings[key] : null;
}
}
// ์ฌ์ฉ ์์
var config1 = ConfigurationManager.Instance;
var config2 = ConfigurationManager.Instance;
var config3 = ConfigurationManager.Instance;
// ๋ชจ๋ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ์ฐธ์กฐ! (config1 == config2 == config3)
string dbConnection = ConfigurationManager.Instance.GetSetting("DatabaseConnection");
๐ก Lazy<T>๋ฅผ ์ฌ์ฉํ๋ ์ด์
C#์ Lazy<T>๋ ์ค๋ ๋ ์์ ์ฑ์ ์๋์ผ๋ก ๋ณด์ฅํด์ค!
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ Instance์ ์ ๊ทผํด๋ ์ธ์คํด์ค๋ ๋ฑ ํ ๋ฒ๋ง ์์ฑ๋ผ.
์ด์ ๋ฐฉ์(double-check locking)๋ณด๋ค ํจ์ฌ ๊ฐ๊ฒฐํ๊ณ ์์ ํด! ๐
โ ๏ธ Singleton ํจํด ์ฌ์ฉ ์ ์ฃผ์์ฌํญ
Singleton์ ํธ๋ฆฌํ์ง๋ง ๋จ์ฉํ๋ฉด ์ ๋ผ!
- ์ ์ญ ์ํ๋ฅผ ๋ง๋ค์ด์ ํ
์คํธ๊ฐ ์ด๋ ค์์ง ์ ์์ด
- ์์กด์ฑ์ด ์จ๊ฒจ์ ธ์ ์ฝ๋ ์ดํด๊ฐ ์ด๋ ค์์ง ์ ์์ด
- ์ ๋ง ํ๋์ ์ธ์คํด์ค๋ง ํ์ํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ์! ๐ฏ
๐ญ Factory ํจํด - ๊ฐ์ฒด ์์ฑ์ ์บก์ํ
Factory ํจํด์ ๊ฐ์ฒด ์์ฑ ๋ก์ง์ ๋ณ๋์ ํด๋์ค๋ก ๋ถ๋ฆฌํ๋ ํจํด์ด์ผ.
๋ณต์กํ ๊ฐ์ฒด ์์ฑ ๊ณผ์ ์ ์จ๊ธฐ๊ณ , ํด๋ผ์ด์ธํธ ์ฝ๋๋ ๊ฐ๋จํ๊ฒ ๊ฐ์ฒด๋ฅผ ๋ฐ์ ์ธ ์ ์๊ฒ ํด์ค! ๐
์ค์ ์์ - ๋ค์ํ ๊ฒฐ์ ๋ฐฉ์ ์ฒ๋ฆฌ
// ๊ฒฐ์ ์ธํฐํ์ด์ค
public interface IPaymentProcessor
{
bool ProcessPayment(decimal amount);
string GetPaymentMethod();
}
// ์ ์ฉ์นด๋ ๊ฒฐ์
public class CreditCardPayment : IPaymentProcessor
{
private string cardNumber;
public CreditCardPayment(string cardNumber)
{
this.cardNumber = cardNumber;
}
public bool ProcessPayment(decimal amount)
{
Console.WriteLine($"์ ์ฉ์นด๋๋ก {amount}์ ๊ฒฐ์ ์ฒ๋ฆฌ ์ค...");
// ์ค์ ๊ฒฐ์ ๋ก์ง
return true;
}
public string GetPaymentMethod() => "์ ์ฉ์นด๋";
}
// ๊ณ์ข์ด์ฒด ๊ฒฐ์
public class BankTransferPayment : IPaymentProcessor
{
private string accountNumber;
public BankTransferPayment(string accountNumber)
{
this.accountNumber = accountNumber;
}
public bool ProcessPayment(decimal amount)
{
Console.WriteLine($"๊ณ์ข์ด์ฒด๋ก {amount}์ ๊ฒฐ์ ์ฒ๋ฆฌ ์ค...");
// ์ค์ ๊ฒฐ์ ๋ก์ง
return true;
}
public string GetPaymentMethod() => "๊ณ์ข์ด์ฒด";
}
// ์นด์นด์คํ์ด ๊ฒฐ์
public class KakaoPayPayment : IPaymentProcessor
{
private string userId;
public KakaoPayPayment(string userId)
{
this.userId = userId;
}
public bool ProcessPayment(decimal amount)
{
Console.WriteLine($"์นด์นด์คํ์ด๋ก {amount}์ ๊ฒฐ์ ์ฒ๋ฆฌ ์ค...");
// ์ค์ ๊ฒฐ์ ๋ก์ง
return true;
}
public string GetPaymentMethod() => "์นด์นด์คํ์ด";
}
// Factory ํด๋์ค
public class PaymentProcessorFactory
{
public static IPaymentProcessor CreatePaymentProcessor(
string paymentType, string credential)
{
switch (paymentType.ToLower())
{
case "creditcard":
return new CreditCardPayment(credential);
case "banktransfer":
return new BankTransferPayment(credential);
case "kakaopay":
return new KakaoPayPayment(credential);
default:
throw new ArgumentException($"์ง์ํ์ง ์๋ ๊ฒฐ์ ๋ฐฉ์: {paymentType}");
}
}
}
// ์ฌ์ฉ ์์
public class OrderService
{
public void ProcessOrder(string paymentType, string credential, decimal amount)
{
// Factory๋ฅผ ํตํด ์ ์ ํ ๊ฒฐ์ ํ๋ก์ธ์ ์์ฑ
IPaymentProcessor processor =
PaymentProcessorFactory.CreatePaymentProcessor(paymentType, credential);
Console.WriteLine($"{processor.GetPaymentMethod()} ์ ํ๋จ");
if (processor.ProcessPayment(amount))
{
Console.WriteLine("๊ฒฐ์ ์ฑ๊ณต! โ
");
}
else
{
Console.WriteLine("๊ฒฐ์ ์คํจ! โ");
}
}
}
// ์คํ
var orderService = new OrderService();
orderService.ProcessOrder("creditcard", "1234-5678-9012-3456", 50000);
orderService.ProcessOrder("kakaopay", "user123", 30000);
๐ก Factory ํจํด์ ์ฅ์
1. ๊ฐ์ฒด ์์ฑ ๋ก์ง ์บก์ํ - ๋ณต์กํ ์์ฑ ๊ณผ์ ์ ์จ๊ธธ ์ ์์ด
2. ๋์จํ ๊ฒฐํฉ - ํด๋ผ์ด์ธํธ๋ ๊ตฌ์ฒด์ ์ธ ํด๋์ค๋ฅผ ๋ชฐ๋ผ๋ ๋ผ
3. ํ์ฅ์ฑ - ์๋ก์ด ๊ฒฐ์ ๋ฐฉ์ ์ถ๊ฐ๊ฐ ์ฌ์
4. ๋จ์ผ ์ฑ
์ ์์น - ๊ฐ์ฒด ์์ฑ ์ฑ
์์ด Factory์ ์ง์ค๋ผ ๐ฏ
๐ญ Strategy ํจํด - ์๊ณ ๋ฆฌ์ฆ์ ์บก์ํ
Strategy ํจํด์ ์คํ ์ค์ ์๊ณ ๋ฆฌ์ฆ์ ์ ํํ ์ ์๊ฒ ํด์ฃผ๋ ํจํด์ด์ผ.
๊ฐ์ ๋ชฉ์ ์ ๊ฐ์ง ์ฌ๋ฌ ์๊ณ ๋ฆฌ์ฆ์ด ์์ ๋, ์ํฉ์ ๋ฐ๋ผ ์ ์ ํ ์๊ณ ๋ฆฌ์ฆ์ ์ ํํด์ ์ฌ์ฉํ ์ ์์ด! ๐ฏ
์ค์ ์์ - ๋ค์ํ ํ ์ธ ์ ์ฑ
// ํ ์ธ ์ ๋ต ์ธํฐํ์ด์ค
public interface IDiscountStrategy
{
decimal ApplyDiscount(decimal originalPrice);
string GetDiscountDescription();
}
// ์ ์ก ํ ์ธ
public class FixedAmountDiscount : IDiscountStrategy
{
private readonly decimal discountAmount;
public FixedAmountDiscount(decimal amount)
{
discountAmount = amount;
}
public decimal ApplyDiscount(decimal originalPrice)
{
return Math.Max(0, originalPrice - discountAmount);
}
public string GetDiscountDescription()
{
return $"{discountAmount}์ ํ ์ธ";
}
}
// ์ ๋ฅ ํ ์ธ
public class PercentageDiscount : IDiscountStrategy
{
private readonly decimal percentage;
public PercentageDiscount(decimal percent)
{
percentage = percent;
}
public decimal ApplyDiscount(decimal originalPrice)
{
return originalPrice * (1 - percentage / 100);
}
public string GetDiscountDescription()
{
return $"{percentage}% ํ ์ธ";
}
}
// VIP ํ์ ํ ์ธ (๋ณตํฉ ํ ์ธ)
public class VipMemberDiscount : IDiscountStrategy
{
public decimal ApplyDiscount(decimal originalPrice)
{
// ๊ธฐ๋ณธ 20% ํ ์ธ + 10๋ง์ ์ด์ ๊ตฌ๋งค์ ์ถ๊ฐ 5% ํ ์ธ
decimal discountedPrice = originalPrice * 0.8m;
if (originalPrice >= 100000)
{
discountedPrice *= 0.95m;
}
return discountedPrice;
}
public string GetDiscountDescription()
{
return "VIP ํ์ ํน๋ณ ํ ์ธ (20% + ์ถ๊ฐ ํ ์ธ)";
}
}
// ํ ์ธ ์์
public class NoDiscount : IDiscountStrategy
{
public decimal ApplyDiscount(decimal originalPrice)
{
return originalPrice;
}
public string GetDiscountDescription()
{
return "ํ ์ธ ์์";
}
}
// ์ผํ ์นดํธ ํด๋์ค
public class ShoppingCart
{
private List<decimal> items = new List<decimal>();
private IDiscountStrategy discountStrategy;
public ShoppingCart()
{
// ๊ธฐ๋ณธ๊ฐ์ ํ ์ธ ์์
discountStrategy = new NoDiscount();
}
public void AddItem(decimal price)
{
items.Add(price);
Console.WriteLine($"์ํ ์ถ๊ฐ: {price}์");
}
// ํ ์ธ ์ ๋ต์ ๋์ ์ผ๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅ!
public void SetDiscountStrategy(IDiscountStrategy strategy)
{
discountStrategy = strategy;
Console.WriteLine($"ํ ์ธ ์ ์ฑ
๋ณ๊ฒฝ: {strategy.GetDiscountDescription()}");
}
public decimal GetTotalPrice()
{
decimal originalTotal = items.Sum();
decimal discountedTotal = discountStrategy.ApplyDiscount(originalTotal);
Console.WriteLine($"\n์๋ ๊ฐ๊ฒฉ: {originalTotal}์");
Console.WriteLine($"ํ ์ธ ์ ์ฉ: {discountStrategy.GetDiscountDescription()}");
Console.WriteLine($"์ต์ข
๊ฐ๊ฒฉ: {discountedTotal}์");
Console.WriteLine($"์ ์ฝ ๊ธ์ก: {originalTotal - discountedTotal}์ ๐ฐ\n");
return discountedTotal;
}
}
// ์ฌ์ฉ ์์
var cart = new ShoppingCart();
cart.AddItem(50000);
cart.AddItem(30000);
cart.AddItem(40000);
// ํ ์ธ ์์ด ๊ณ์ฐ
cart.GetTotalPrice();
// ์ ์ก ํ ์ธ ์ ์ฉ
cart.SetDiscountStrategy(new FixedAmountDiscount(10000));
cart.GetTotalPrice();
// ์ ๋ฅ ํ ์ธ ์ ์ฉ
cart.SetDiscountStrategy(new PercentageDiscount(15));
cart.GetTotalPrice();
// VIP ํ์ ํ ์ธ ์ ์ฉ
cart.SetDiscountStrategy(new VipMemberDiscount());
cart.GetTotalPrice();
๐ก Strategy ํจํด์ ํต์ฌ ์ฅ์
1. ๋ฐํ์์ ์๊ณ ๋ฆฌ์ฆ ๋ณ๊ฒฝ ๊ฐ๋ฅ - ์คํ ์ค์ ์ ๋ต์ ๋ฐ๊ฟ ์ ์์ด
2. ์กฐ๊ฑด๋ฌธ ์ ๊ฑฐ - if-else๋ switch ๋์ ๊ฐ์ฒด๋ก ์ฒ๋ฆฌ
3. ๊ฐ๋ฐฉ-ํ์ ์์น - ์๋ก์ด ์ ๋ต ์ถ๊ฐ๊ฐ ๊ธฐ์กด ์ฝ๋ ์์ ์์ด ๊ฐ๋ฅ
4. ํ
์คํธ ์ฉ์ด - ๊ฐ ์ ๋ต์ ๋
๋ฆฝ์ ์ผ๋ก ํ
์คํธํ ์ ์์ด! ๐งช
๐ Observer ํจํด - ์ด๋ฒคํธ ๊ธฐ๋ฐ ํต์
Observer ํจํด์ ํ ๊ฐ์ฒด์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๊ทธ ๊ฐ์ฒด์ ์์กดํ๋ ๋ค๋ฅธ ๊ฐ์ฒด๋ค์๊ฒ ์๋์ผ๋ก ์๋ฆผ์ ๋ณด๋ด๋ ํจํด์ด์ผ.
C#์ ์ด๋ฒคํธ(Event) ์์คํ
์ด ๋ฐ๋ก ์ด ํจํด์ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก์ด! ๐ข
์ค์ ์์ - ์ฃผ์ ๊ฐ๊ฒฉ ๋ชจ๋ํฐ๋ง ์์คํ
// ์ฃผ์ ๊ฐ๊ฒฉ ๋ณ๋ ์ด๋ฒคํธ ์ธ์
public class StockPriceChangedEventArgs : EventArgs
{
public string StockSymbol { get; set; }
public decimal OldPrice { get; set; }
public decimal NewPrice { get; set; }
public decimal ChangePercentage { get; set; }
public DateTime Timestamp { get; set; }
}
// ์ฃผ์ (Subject/Observable)
public class Stock
{
private string symbol;
private decimal price;
// ์ด๋ฒคํธ ์ ์ธ
public event EventHandler<StockPriceChangedEventArgs> PriceChanged;
public Stock(string symbol, decimal initialPrice)
{
this.symbol = symbol;
this.price = initialPrice;
}
public string Symbol => symbol;
public decimal Price => price;
public void UpdatePrice(decimal newPrice)
{
if (price != newPrice)
{
decimal oldPrice = price;
price = newPrice;
// ๊ฐ๊ฒฉ ๋ณ๋๋ฅ ๊ณ์ฐ
decimal changePercentage = ((newPrice - oldPrice) / oldPrice) * 100;
// ์ด๋ฒคํธ ๋ฐ์
OnPriceChanged(new StockPriceChangedEventArgs
{
StockSymbol = symbol,
OldPrice = oldPrice,
NewPrice = newPrice,
ChangePercentage = changePercentage,
Timestamp = DateTime.Now
});
}
}
protected virtual void OnPriceChanged(StockPriceChangedEventArgs e)
{
PriceChanged?.Invoke(this, e);
}
}
// ๊ด์ฐฐ์ 1: ์ด๋ฉ์ผ ์๋ฆผ ์๋น์ค
public class EmailNotificationService
{
private string emailAddress;
public EmailNotificationService(string email)
{
emailAddress = email;
}
public void Subscribe(Stock stock)
{
stock.PriceChanged += OnStockPriceChanged;
Console.WriteLine($"๐ง {emailAddress}๋ก {stock.Symbol} ์๋ฆผ ๊ตฌ๋
์์");
}
public void Unsubscribe(Stock stock)
{
stock.PriceChanged -= OnStockPriceChanged;
Console.WriteLine($"๐ง {emailAddress}์ {stock.Symbol} ์๋ฆผ ๊ตฌ๋
ํด์ ");
}
private void OnStockPriceChanged(object sender, StockPriceChangedEventArgs e)
{
string direction = e.ChangePercentage > 0 ? "์์น ๐" : "ํ๋ฝ ๐";
Console.WriteLine($"\n[์ด๋ฉ์ผ ๋ฐ์ก] {emailAddress}");
Console.WriteLine($"{e.StockSymbol} ๊ฐ๊ฒฉ {direction}");
Console.WriteLine($"{e.OldPrice}์ โ {e.NewPrice}์ ({e.ChangePercentage:F2}%)");
}
}
// ๊ด์ฐฐ์ 2: SMS ์๋ฆผ ์๋น์ค
public class SmsNotificationService
{
private string phoneNumber;
private decimal alertThreshold;
public SmsNotificationService(string phone, decimal threshold)
{
phoneNumber = phone;
alertThreshold = threshold;
}
public void Subscribe(Stock stock)
{
stock.PriceChanged += OnStockPriceChanged;
Console.WriteLine($"๐ฑ {phoneNumber}๋ก {stock.Symbol} SMS ์๋ฆผ ๊ตฌ๋
(์๊ณ๊ฐ: {alertThreshold}%)");
}
private void OnStockPriceChanged(object sender, StockPriceChangedEventArgs e)
{
// ๋ณ๋๋ฅ ์ด ์๊ณ๊ฐ์ ์ด๊ณผํ ๋๋ง SMS ๋ฐ์ก
if (Math.Abs(e.ChangePercentage) >= alertThreshold)
{
Console.WriteLine($"\n[๊ธด๊ธ SMS] {phoneNumber}");
Console.WriteLine($"โ ๏ธ {e.StockSymbol} ๊ธ๊ฒฉํ ๋ณ๋ ๊ฐ์ง!");
Console.WriteLine($"๋ณ๋๋ฅ : {e.ChangePercentage:F2}%");
}
}
}
// ๊ด์ฐฐ์ 3: ๋ก๊น
์๋น์ค
public class StockPriceLogger
{
private string logFilePath;
public StockPriceLogger(string filePath)
{
logFilePath = filePath;
}
public void Subscribe(Stock stock)
{
stock.PriceChanged += OnStockPriceChanged;
Console.WriteLine($"๐ {stock.Symbol} ๊ฐ๊ฒฉ ๋ณ๋ ๋ก๊น
์์");
}
private void OnStockPriceChanged(object sender, StockPriceChangedEventArgs e)
{
string logEntry = $"[{e.Timestamp:yyyy-MM-dd HH:mm:ss}] " +
$"{e.StockSymbol}: {e.OldPrice} โ {e.NewPrice} " +
$"({e.ChangePercentage:F2}%)";
Console.WriteLine($"\n[๋ก๊ทธ ๊ธฐ๋ก] {logEntry}");
// ์ค์ ๋ก๋ ํ์ผ์ ๊ธฐ๋ก
}
}
// ์ฌ์ฉ ์์
var samsungStock = new Stock("์ผ์ฑ์ ์", 70000);
// ์ฌ๋ฌ ๊ด์ฐฐ์ ๋ฑ๋ก
var emailService = new EmailNotificationService("investor@example.com");
var smsService = new SmsNotificationService("010-1234-5678", 3.0m);
var logger = new StockPriceLogger("stock_prices.log");
emailService.Subscribe(samsungStock);
smsService.Subscribe(samsungStock);
logger.Subscribe(samsungStock);
Console.WriteLine("\n=== ์ฃผ์ ๊ฐ๊ฒฉ ๋ณ๋ ์๋ฎฌ๋ ์ด์
===\n");
// ๊ฐ๊ฒฉ ๋ณ๋ - ๋ชจ๋ ๊ด์ฐฐ์์๊ฒ ์๋์ผ๋ก ์๋ฆผ!
samsungStock.UpdatePrice(71500); // 2.14% ์์น
Thread.Sleep(1000);
samsungStock.UpdatePrice(73000); // 2.10% ์์น
Thread.Sleep(1000);
samsungStock.UpdatePrice(70500); // 3.42% ํ๋ฝ (SMS ๋ฐ์ก!)
Thread.Sleep(1000);
// ํน์ ๊ด์ฐฐ์ ๊ตฌ๋
ํด์
emailService.Unsubscribe(samsungStock);
samsungStock.UpdatePrice(72000); // ์ด๋ฉ์ผ์ ์ ๊ฐ!
๐ฏ Observer ํจํด์ ์ค์ ํ์ฉ ์ฌ๋ก
1. GUI ์ด๋ฒคํธ ์ฒ๋ฆฌ - ๋ฒํผ ํด๋ฆญ, ํ
์คํธ ๋ณ๊ฒฝ ๋ฑ
2. ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ - WPF, Xamarin์ INotifyPropertyChanged
3. ๋ฉ์์ง ํ - RabbitMQ, Azure Service Bus
4. ์ค์๊ฐ ์๋ฆผ - SignalR์ ํตํ ์ค์๊ฐ ์
๋ฐ์ดํธ
5. ๋ก๊น
์์คํ
- ์ฌ๋ฌ ๋ก๊ฑฐ์ ๋์์ ๋ก๊ทธ ์ ์ก ๐
๐ ์์กด์ฑ ์ฃผ์ (Dependency Injection)์ด๋?
์, ์ด์ ์ค๋์ ํต์ฌ ์ฃผ์ ์ธ ์์กด์ฑ ์ฃผ์
์ ๋ํด ๊น์ด ํ๊ณ ๋ค์ด๋ณด์! ๐
์์กด์ฑ ์ฃผ์
์ ๊ฐ์ฒด๊ฐ ํ์๋ก ํ๋ ์์กด์ฑ(๋ค๋ฅธ ๊ฐ์ฒด)์ ์ธ๋ถ์์ ์ฃผ์
๋ฐ๋ ๋์์ธ ํจํด์ด์ผ.
๊ฐ์ฒด๊ฐ ์ค์ค๋ก ์์กด์ฑ์ ์์ฑํ๋ ๊ฒ ์๋๋ผ, ์ธ๋ถ์์ ๋ง๋ค์ด์ ๋ฃ์ด์ฃผ๋ ๊ฑฐ์ง!
โ ๋์ ์์ - ๊ฐํ ๊ฒฐํฉ
// ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ ํด๋์ค
public class SqlServerDatabase
{
public void SaveData(string data)
{
Console.WriteLine($"SQL Server์ ๋ฐ์ดํฐ ์ ์ฅ: {data}");
// ์ค์ SQL Server ์ ์ฅ ๋ก์ง
}
}
// ์ฌ์ฉ์ ์๋น์ค - ์ง์ ์์กด์ฑ ์์ฑ
public class UserService
{
private SqlServerDatabase database;
public UserService()
{
// ๋ฌธ์ : SqlServerDatabase์ ๊ฐํ๊ฒ ๊ฒฐํฉ๋จ!
database = new SqlServerDatabase();
}
public void RegisterUser(string username)
{
Console.WriteLine($"์ฌ์ฉ์ ๋ฑ๋ก: {username}");
database.SaveData(username);
}
}
// ๋ฌธ์ ์ ๋ค:
// 1. UserService๋ SqlServerDatabase์ ๊ฐํ๊ฒ ๊ฒฐํฉ๋จ
// 2. ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฒ ์ด์ค(MySQL, MongoDB)๋ก ๋ณ๊ฒฝํ๋ ค๋ฉด UserService ์ฝ๋ ์์ ํ์
// 3. ํ
์คํธํ ๋ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ด ํ
์คํธ ๋ถ๊ฐ๋ฅ
// 4. UserService๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ ์ด๋ ค์
โ ์ข์ ์์ - ์์กด์ฑ ์ฃผ์ ์ ์ฉ
// ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธํฐํ์ด์ค (์ถ์ํ)
public interface IDatabase
{
void SaveData(string data);
}
// SQL Server ๊ตฌํ
public class SqlServerDatabase : IDatabase
{
public void SaveData(string data)
{
Console.WriteLine($"SQL Server์ ๋ฐ์ดํฐ ์ ์ฅ: {data}");
}
}
// MySQL ๊ตฌํ
public class MySqlDatabase : IDatabase
{
public void SaveData(string data)
{
Console.WriteLine($"MySQL์ ๋ฐ์ดํฐ ์ ์ฅ: {data}");
}
}
// MongoDB ๊ตฌํ
public class MongoDatabase : IDatabase
{
public void SaveData(string data)
{
Console.WriteLine($"MongoDB์ ๋ฐ์ดํฐ ์ ์ฅ: {data}");
}
}
// ํ
์คํธ์ฉ ๊ฐ์ง ๋ฐ์ดํฐ๋ฒ ์ด์ค
public class FakeDatabase : IDatabase
{
public List<string> SavedData { get; } = new List<string>();
public void SaveData(string data)
{
SavedData.Add(data);
Console.WriteLine($"ํ
์คํธ DB์ ๋ฐ์ดํฐ ์ ์ฅ: {data}");
}
}
// ์ฌ์ฉ์ ์๋น์ค - ์์กด์ฑ ์ฃผ์
๋ฐ์
public class UserService
{
private readonly IDatabase database;
// ์์ฑ์๋ฅผ ํตํ ์์กด์ฑ ์ฃผ์
(Constructor Injection)
public UserService(IDatabase database)
{
this.database = database;
}
public void RegisterUser(string username)
{
Console.WriteLine($"์ฌ์ฉ์ ๋ฑ๋ก: {username}");
database.SaveData(username);
}
}
// ์ฌ์ฉ ์์ - ๋ค์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ์ฝ๊ฒ ๊ต์ฒด ๊ฐ๋ฅ!
Console.WriteLine("=== SQL Server ์ฌ์ฉ ===");
var sqlService = new UserService(new SqlServerDatabase());
sqlService.RegisterUser("ํ๊ธธ๋");
Console.WriteLine("\n=== MySQL ์ฌ์ฉ ===");
var mysqlService = new UserService(new MySqlDatabase());
mysqlService.RegisterUser("๊น์ฒ ์");
Console.WriteLine("\n=== MongoDB ์ฌ์ฉ ===");
var mongoService = new UserService(new MongoDatabase());
mongoService.RegisterUser("์ด์ํฌ");
Console.WriteLine("\n=== ํ
์คํธ ํ๊ฒฝ ===");
var fakeDb = new FakeDatabase();
var testService = new UserService(fakeDb);
testService.RegisterUser("ํ
์คํธ์ ์ ");
Console.WriteLine($"์ ์ฅ๋ ๋ฐ์ดํฐ ๊ฐ์: {fakeDb.SavedData.Count}๊ฐ");
๐ก ์์กด์ฑ ์ฃผ์ ์ ํต์ฌ ์ฅ์
1. ๋์จํ ๊ฒฐํฉ - ๊ตฌ์ฒด์ ์ธ ๊ตฌํ์ด ์๋ ์ธํฐํ์ด์ค์ ์์กด ๐
2. ํ
์คํธ ์ฉ์ด์ฑ - Mock ๊ฐ์ฒด๋ก ์ฝ๊ฒ ํ
์คํธ ๊ฐ๋ฅ ๐งช
3. ์ ์ฐ์ฑ - ๋ฐํ์์ ๋ค๋ฅธ ๊ตฌํ์ฒด๋ก ๊ต์ฒด ๊ฐ๋ฅ ๐
4. ์ฌ์ฌ์ฉ์ฑ - ๊ฐ์ ์ฝ๋๋ฅผ ๋ค์ํ ํ๊ฒฝ์์ ์ฌ์ฌ์ฉ โป๏ธ
5. ๋จ์ผ ์ฑ
์ ์์น - ๊ฐ์ฒด ์์ฑ ์ฑ
์ ๋ถ๋ฆฌ ๐ฆ
๐ฏ ์์กด์ฑ ์ฃผ์ ์ 3๊ฐ์ง ๋ฐฉ๋ฒ
์์กด์ฑ์ ์ฃผ์ ํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ์ธ ๊ฐ์ง๊ฐ ์์ด. ๊ฐ๊ฐ์ ํน์ง๊ณผ ์ฌ์ฉ ์๋๋ฆฌ์ค๋ฅผ ์์๋ณด์! ๐
๊ฐ์ฅ ๊ถ์ฅ๋๋ ๋ฐฉ๋ฒ! ๐
public interface IEmailService
{
void SendEmail(string to, string subject, string body);
}
public class SmtpEmailService : IEmailService
{
public void SendEmail(string to, string subject, string body)
{
Console.WriteLine($"SMTP๋ก ์ด๋ฉ์ผ ๋ฐ์ก: {to}");
}
}
public class NotificationService
{
private readonly IEmailService emailService;
private readonly ILogger logger;
// ์์ฑ์๋ฅผ ํตํด ์์กด์ฑ ์ฃผ์
public NotificationService(IEmailService emailService, ILogger logger)
{
// null ์ฒดํฌ๋ก ํ์ ์์กด์ฑ ๋ณด์ฅ
this.emailService = emailService ?? throw new ArgumentNullException(nameof(emailService));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void SendWelcomeEmail(string userEmail)
{
logger.Log("ํ์ ์ด๋ฉ์ผ ๋ฐ์ก ์์");
emailService.SendEmail(userEmail, "ํ์ํฉ๋๋ค!", "ํ์๊ฐ์
์ ์ถํํฉ๋๋ค.");
logger.Log("ํ์ ์ด๋ฉ์ผ ๋ฐ์ก ์๋ฃ");
}
}
// ์ฌ์ฉ
var emailService = new SmtpEmailService();
var logger = new ConsoleLogger();
var notificationService = new NotificationService(emailService, logger);
๐ก ์์ฑ์ ์ฃผ์ ์ ์ฅ์
1. ๋ถ๋ณ์ฑ - readonly๋ก ์ ์ธํ์ฌ ์์กด์ฑ ๋ณ๊ฒฝ ๋ฐฉ์ง
2. ๋ช
ํ์ฑ - ํ์ํ ์์กด์ฑ์ด ์์ฑ์์ ๋ช
์๋จ
3. ํ
์คํธ ์ฉ์ด - ์์กด์ฑ์ ์ฝ๊ฒ Mock์ผ๋ก ๊ต์ฒด ๊ฐ๋ฅ
4. ํ์ ์์กด์ฑ ๋ณด์ฅ - ๊ฐ์ฒด ์์ฑ ์ ๋ฐ๋์ ์์กด์ฑ ์ ๊ณต ํ์
์ ํ์ ์์กด์ฑ์ ์ฌ์ฉ
public class ReportGenerator
{
// ํ์ ์์กด์ฑ์ ์์ฑ์๋ก
private readonly IDataSource dataSource;
public ReportGenerator(IDataSource dataSource)
{
this.dataSource = dataSource;
}
// ์ ํ์ ์์กด์ฑ์ ์์ฑ์ผ๋ก
public ILogger Logger { get; set; }
public IEmailService EmailService { get; set; }
public void GenerateReport()
{
Logger?.Log("๋ฆฌํฌํธ ์์ฑ ์์");
var data = dataSource.GetData();
var report = CreateReport(data);
Logger?.Log("๋ฆฌํฌํธ ์์ฑ ์๋ฃ");
// ์ด๋ฉ์ผ ์๋น์ค๊ฐ ์ค์ ๋์ด ์์ผ๋ฉด ๋ฐ์ก
if (EmailService != null)
{
EmailService.SendEmail("admin@example.com", "๋ฆฌํฌํธ", report);
}
}
private string CreateReport(object data)
{
return "๋ฆฌํฌํธ ๋ด์ฉ...";
}
}
// ์ฌ์ฉ
var generator = new ReportGenerator(new SqlDataSource());
generator.Logger = new FileLogger(); // ์ ํ์ ์ผ๋ก ์ค์
generator.EmailService = new SmtpEmailService(); // ์ ํ์ ์ผ๋ก ์ค์
generator.GenerateReport();
โ ๏ธ ์์ฑ ์ฃผ์ ์ฌ์ฉ ์ ์ฃผ์์ฌํญ
์์ฑ ์ฃผ์
์ ์์กด์ฑ์ด ์ ํ์ ์ผ ๋๋ง ์ฌ์ฉํด์ผ ํด!
ํ์ ์์กด์ฑ์ ๋ฐ๋์ ์์ฑ์ ์ฃผ์
์ ์ฌ์ฉํ์.
๊ทธ๋ ์ง ์์ผ๋ฉด NullReferenceException์ด ๋ฐ์ํ ์ ์์ด! ๐ฅ
ํน์ ๋ฉ์๋ ์คํ ์์๋ง ํ์ํ ์์กด์ฑ
public class DocumentProcessor
{
public void ProcessDocument(string documentPath, IDocumentValidator validator)
{
// ๋ฉ์๋ ํ๋ผ๋ฏธํฐ๋ก ์์กด์ฑ ์ฃผ์
if (!validator.Validate(documentPath))
{
throw new InvalidOperationException("๋ฌธ์ ๊ฒ์ฆ ์คํจ");
}
Console.WriteLine($"๋ฌธ์ ์ฒ๋ฆฌ ์ค: {documentPath}");
// ๋ฌธ์ ์ฒ๋ฆฌ ๋ก์ง
}
public void ConvertDocument(string sourcePath, string targetPath,
IDocumentConverter converter)
{
// ๋ค๋ฅธ ๋ฉ์๋์์๋ ๋ค๋ฅธ ์์กด์ฑ ์ฃผ์
converter.Convert(sourcePath, targetPath);
}
}
// ์ฌ์ฉ - ๋ฉ์๋ ํธ์ถ ์๋ง๋ค ๋ค๋ฅธ ๊ตฌํ์ฒด ์ ๋ฌ ๊ฐ๋ฅ
var processor = new DocumentProcessor();
processor.ProcessDocument("doc1.pdf", new PdfValidator());
processor.ProcessDocument("doc2.docx", new WordValidator());
processor.ConvertDocument("source.pdf", "target.docx", new PdfToWordConverter());
processor.ConvertDocument("source.docx", "target.pdf", new WordToPdfConverter());
| ์ฃผ์ ๋ฐฉ๋ฒ | ์ฌ์ฉ ์๋๋ฆฌ์ค | ์ฅ์ | ๋จ์ |
|---|---|---|---|
| ์์ฑ์ ์ฃผ์ | ํ์ ์์กด์ฑ | ๋ถ๋ณ์ฑ, ๋ช ํ์ฑ, ์์ ์ฑ | ์์ฑ์ ํ๋ผ๋ฏธํฐ ์ฆ๊ฐ ๊ฐ๋ฅ |
| ์์ฑ ์ฃผ์ | ์ ํ์ ์์กด์ฑ | ์ ์ฐ์ฑ, ๊ฐ๋จํจ | null ์ฒดํฌ ํ์, ๋ถ๋ณ์ฑ ์์ |
| ๋ฉ์๋ ์ฃผ์ | ๋ฉ์๋๋ณ ๋ค๋ฅธ ์์กด์ฑ | ๋ฉ์๋ ๋จ์ ์ ์ด | ๋ฉ์๋ ์๊ทธ๋์ฒ ๋ณต์กํด์ง ์ ์์ |
๐๏ธ IoC ์ปจํ ์ด๋์ DI ํ๋ ์์ํฌ
์ค์ ํ๋ก์ ํธ์์๋ ์์ญ, ์๋ฐฑ ๊ฐ์ ํด๋์ค๊ฐ ์๋ก ์์กดํ๊ณ ์์ด.
์ด๊ฑธ ์ผ์ผ์ด ์๋์ผ๋ก ์ฃผ์
ํ๋ฉด ์ ๋ง ํ๋ค๊ฒ ์ง? ๐ฐ
๊ทธ๋์ ๋ฑ์ฅํ ๊ฒ IoC(Inversion of Control) ์ปจํ
์ด๋์ผ!
์์กด์ฑ ๊ด๋ฆฌ๋ฅผ ์๋ํํด์ฃผ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ง. ๐ ๏ธ
์๋น์ค ๋ฑ๋ก๊ณผ ์ฌ์ฉ
// Startup.cs ๋๋ Program.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 1. Transient: ์์ฒญํ ๋๋ง๋ค ์ ์ธ์คํด์ค ์์ฑ
services.AddTransient<IEmailService, SmtpEmailService>();
// 2. Scoped: HTTP ์์ฒญ๋น ํ๋์ ์ธ์คํด์ค (์น ์ ํ๋ฆฌ์ผ์ด์
)
services.AddScoped<IUserRepository, SqlUserRepository>();
// 3. Singleton: ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด์์ ํ๋์ ์ธ์คํด์ค
services.AddSingleton<IConfiguration, AppConfiguration>();
// ๊ตฌ์ฒด ํด๋์ค๋ ๋ฑ๋ก ๊ฐ๋ฅ
services.AddTransient<NotificationService>();
// ํฉํ ๋ฆฌ ํจํด์ผ๋ก ๋ฑ๋ก
services.AddTransient<IPaymentProcessor>(provider =>
{
var config = provider.GetRequiredService<IConfiguration>();
var paymentType = config.GetSetting("PaymentType");
return paymentType switch
{
"CreditCard" => new CreditCardPayment("default"),
"BankTransfer" => new BankTransferPayment("default"),
_ => throw new InvalidOperationException("์ง์ํ์ง ์๋ ๊ฒฐ์ ๋ฐฉ์")
};
});
}
}
// ์ปจํธ๋กค๋ฌ์์ ์ฌ์ฉ
public class UserController : ControllerBase
{
private readonly IUserRepository userRepository;
private readonly IEmailService emailService;
private readonly ILogger<UserController> logger;
// ์์ฑ์์ ํ์ํ ์์กด์ฑ ์ ์ธ๋ง ํ๋ฉด ์๋์ผ๋ก ์ฃผ์
๋จ!
public UserController(
IUserRepository userRepository,
IEmailService emailService,
ILogger<UserController> logger)
{
this.userRepository = userRepository;
this.emailService = emailService;
this.logger = logger;
}
[HttpPost]
public async Task<IActionResult> Register(UserRegistrationDto dto)
{
logger.LogInformation($"์ฌ์ฉ์ ๋ฑ๋ก ์๋: {dto.Email}");
var user = await userRepository.CreateUser(dto);
emailService.SendEmail(
dto.Email,
"ํ์ํฉ๋๋ค!",
"ํ์๊ฐ์
์ด ์๋ฃ๋์์ต๋๋ค."
);
return Ok(user);
}
}
๐ ์๋น์ค ์๋ช ์ฃผ๊ธฐ ๋น๊ต
Transient (์ผ์์ )
- ์์ฒญํ ๋๋ง๋ค ์๋ก์ด ์ธ์คํด์ค ์์ฑ
- ๊ฐ๋ฒผ์ด stateless ์๋น์ค์ ์ ํฉ
- ์: ์ด๋ฉ์ผ ๋ฐ์ก, ๋ฐ์ดํฐ ๋ณํ ์๋น์ค
Scoped (๋ฒ์)
- HTTP ์์ฒญ๋น ํ๋์ ์ธ์คํด์ค
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปจํ
์คํธ์ ์ฃผ๋ก ์ฌ์ฉ
- ์: Entity Framework DbContext, Repository
Singleton (๋จ์ผ)
- ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด์์ ํ๋์ ์ธ์คํด์ค
- ์ํ๋ฅผ ๊ณต์ ํ๊ฑฐ๋ ์ด๊ธฐํ ๋น์ฉ์ด ํฐ ์๋น์ค
- ์: ์ค์ ๊ด๋ฆฌ์, ์บ์, ๋ก๊ฑฐ ํฉํ ๋ฆฌ
- โ ๏ธ ์ฃผ์: ์ค๋ ๋ ์์ ์ฑ ๊ณ ๋ ค ํ์!
๐งช ํ ์คํธ ๊ฐ๋ฅํ ์ฝ๋ ์์ฑํ๊ธฐ
์์กด์ฑ ์ฃผ์
์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ค ํ๋๋ ํ
์คํธ ์ฉ์ด์ฑ์ด์ผ!
์ค์ ์์กด์ฑ ๋์ Mock ๊ฐ์ฒด๋ฅผ ์ฃผ์
ํ๋ฉด ์ธ๋ถ ์์คํ
์์ด๋ ํ
์คํธํ ์ ์์ด. ๐ฏ
ํ ์คํธํ๊ธฐ ์ด๋ ค์ด ์ฝ๋ โ
public class OrderService
{
public bool PlaceOrder(Order order)
{
// ์ง์ ์์กด์ฑ ์์ฑ - ํ
์คํธ ๋ถ๊ฐ๋ฅ!
var paymentGateway = new PayPalGateway();
var emailService = new SmtpEmailService();
var database = new SqlServerDatabase();
// ์ค์ ๊ฒฐ์ ์๋ (ํ
์คํธ ์ ๋ฌธ์ !)
if (!paymentGateway.ProcessPayment(order.TotalAmount))
{
return false;
}
// ์ค์ DB์ ์ ์ฅ (ํ
์คํธ ์ ๋ฌธ์ !)
database.SaveOrder(order);
// ์ค์ ์ด๋ฉ์ผ ๋ฐ์ก (ํ
์คํธ ์ ๋ฌธ์ !)
emailService.SendEmail(order.CustomerEmail, "์ฃผ๋ฌธ ํ์ธ", "์ฃผ๋ฌธ์ด ์๋ฃ๋์์ต๋๋ค.");
return true;
}
}
ํ ์คํธํ๊ธฐ ์ฌ์ด ์ฝ๋ โ
// ์ธํฐํ์ด์ค ์ ์
public interface IPaymentGateway
{
bool ProcessPayment(decimal amount);
}
public interface IEmailService
{
void SendEmail(string to, string subject, string body);
}
public interface IOrderRepository
{
void SaveOrder(Order order);
}
// ์์กด์ฑ ์ฃผ์
์ ์ฌ์ฉํ๋ ์๋น์ค
public class OrderService
{
private readonly IPaymentGateway paymentGateway;
private readonly IEmailService emailService;
private readonly IOrderRepository orderRepository;
public OrderService(
IPaymentGateway paymentGateway,
IEmailService emailService,
IOrderRepository orderRepository)
{
this.paymentGateway = paymentGateway;
this.emailService = emailService;
this.orderRepository = orderRepository;
}
public bool PlaceOrder(Order order)
{
if (!paymentGateway.ProcessPayment(order.TotalAmount))
{
return false;
}
orderRepository.SaveOrder(order);
emailService.SendEmail(order.CustomerEmail, "์ฃผ๋ฌธ ํ์ธ", "์ฃผ๋ฌธ์ด ์๋ฃ๋์์ต๋๋ค.");
return true;
}
}
// ๋จ์ ํ
์คํธ (xUnit + Moq ์ฌ์ฉ)
public class OrderServiceTests
{
[Fact]
public void PlaceOrder_์ฑ๊ณต์ ์ธ๊ฒฐ์ _์ฃผ๋ฌธ์ ์ฅ๋ฐ์ด๋ฉ์ผ๋ฐ์ก()
{
// Arrange - ํ
์คํธ ์ค๋น
var mockPaymentGateway = new Mock<IPaymentGateway>();
var mockEmailService = new Mock<IEmailService>();
var mockOrderRepository = new Mock<IOrderRepository>();
// Mock ๋์ ์ค์
mockPaymentGateway
.Setup(x => x.ProcessPayment(It.IsAny<decimal>()))
.Returns(true); // ๊ฒฐ์ ์ฑ๊ณต ์๋ฎฌ๋ ์ด์
var orderService = new OrderService(
mockPaymentGateway.Object,
mockEmailService.Object,
mockOrderRepository.Object
);
var order = new Order
{
OrderId = 1,
CustomerEmail = "test@example.com",
TotalAmount = 50000
};
// Act - ํ
์คํธ ์คํ
var result = orderService.PlaceOrder(order);
// Assert - ๊ฒฐ๊ณผ ๊ฒ์ฆ
Assert.True(result);
// ๊ฒฐ์ ๊ฐ ์ ํํ ๊ธ์ก์ผ๋ก ํธ์ถ๋์๋์ง ๊ฒ์ฆ
mockPaymentGateway.Verify(
x => x.ProcessPayment(50000),
Times.Once
);
// ์ฃผ๋ฌธ์ด ์ ์ฅ๋์๋์ง ๊ฒ์ฆ
mockOrderRepository.Verify(
x => x.SaveOrder(order),
Times.Once
);
// ์ด๋ฉ์ผ์ด ๋ฐ์ก๋์๋์ง ๊ฒ์ฆ
mockEmailService.Verify(
x => x.SendEmail(
"test@example.com",
"์ฃผ๋ฌธ ํ์ธ",
It.IsAny<string>()
),
Times.Once
);
}
[Fact]
public void PlaceOrder_๊ฒฐ์ ์คํจ_์ฃผ๋ฌธ์ ์ฅ์๋จ()
{
// Arrange
var mockPaymentGateway = new Mock<IPaymentGateway>();
var mockEmailService = new Mock<IEmailService>();
var mockOrderRepository = new Mock<IOrderRepository>();
// ๊ฒฐ์ ์คํจ ์๋ฎฌ๋ ์ด์
mockPaymentGateway
.Setup(x => x.ProcessPayment(It.IsAny<decimal>()))
.Returns(false);
var orderService = new OrderService(
mockPaymentGateway.Object,
mockEmailService.Object,
mockOrderRepository.Object
);
var order = new Order { OrderId = 1, TotalAmount = 50000 };
// Act
var result = orderService.PlaceOrder(order);
// Assert
Assert.False(result);
// ๊ฒฐ์ ์คํจ ์ ์ฃผ๋ฌธ ์ ์ฅ๊ณผ ์ด๋ฉ์ผ ๋ฐ์ก์ด ํธ์ถ๋์ง ์์๋์ง ๊ฒ์ฆ
mockOrderRepository.Verify(
x => x.SaveOrder(It.IsAny<Order>()),
Times.Never
);
mockEmailService.Verify(
x => x.SendEmail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()),
Times.Never
);
}
}
๐ก Mock ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ํ ์คํธ์ ์ฅ์
1. ๊ฒฉ๋ฆฌ๋ ํ
์คํธ - ์ธ๋ถ ์์คํ
์์ด ๋
๋ฆฝ์ ์ผ๋ก ํ
์คํธ ๐
2. ๋น ๋ฅธ ์คํ - ์ค์ DB, API ํธ์ถ ์์ด ์ฆ์ ์คํ โก
3. ์์ธก ๊ฐ๋ฅ - Mock ๋์์ ์ ํํ ์ ์ด ๊ฐ๋ฅ ๐ฏ
4. ์ฃ์ง ์ผ์ด์ค ํ
์คํธ - ์๋ฌ ์ํฉ๋ ์ฝ๊ฒ ์๋ฎฌ๋ ์ด์
๐งช
5. ๋น์ฉ ์ ๊ฐ - ์ค์ ๊ฒฐ์ ๋ ์ด๋ฉ์ผ ๋ฐ์ก ๋ถํ์ ๐ฐ
๐๏ธ SOLID ์์น๊ณผ ๋์์ธ ํจํด
์ข์ ์ฝ๋ ์ค๊ณ์ ๊ธฐ๋ฐ์ด ๋๋ SOLID ์์น์ ๋์์ธ ํจํด๊ณผ ํจ๊ป ์ดํดํด๋ณด์!
์ด ์์น๋ค์ ๋ฐ๋ฅด๋ฉด ์ ์ง๋ณด์ํ๊ธฐ ์ฝ๊ณ ํ์ฅ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ด. ๐ฏ
ํ๋์ ํด๋์ค๋ ํ๋์ ์ฑ ์๋ง ๊ฐ์ ธ์ผ ํด!
// โ ๋์ ์ - ์ฌ๋ฌ ์ฑ
์์ ๊ฐ์ง ํด๋์ค
public class User
{
public string Name { get; set; }
public string Email { get; set; }
// ์ฌ์ฉ์ ๋ฐ์ดํฐ ๊ฒ์ฆ
public bool Validate()
{
return !string.IsNullOrEmpty(Name) && Email.Contains("@");
}
// ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์ฅ
public void Save()
{
// DB ์ ์ฅ ๋ก์ง
}
// ์ด๋ฉ์ผ ๋ฐ์ก
public void SendWelcomeEmail()
{
// ์ด๋ฉ์ผ ๋ฐ์ก ๋ก์ง
}
// ๋ก๊ทธ ๊ธฐ๋ก
public void LogActivity()
{
// ๋ก๊น
๋ก์ง
}
}
// โ
์ข์ ์ - ์ฑ
์ ๋ถ๋ฆฌ
public class User
{
public string Name { get; set; }
public string Email { get; set; }
}
public class UserValidator
{
public bool Validate(User user)
{
return !string.IsNullOrEmpty(user.Name) && user.Email.Contains("@");
}
}
public class UserRepository
{
public void Save(User user)
{
// DB ์ ์ฅ ๋ก์ง
}
}
public class UserEmailService
{
public void SendWelcomeEmail(User user)
{
// ์ด๋ฉ์ผ ๋ฐ์ก ๋ก์ง
}
}
public class UserActivityLogger
{
public void LogActivity(User user, string activity)
{
// ๋ก๊น
๋ก์ง
}
}
ํ์ฅ์๋ ์ด๋ ค์๊ณ , ์์ ์๋ ๋ซํ์์ด์ผ ํด!
// โ ๋์ ์ - ์๋ก์ด ๋ํ ์ถ๊ฐ ์ ๊ธฐ์กด ์ฝ๋ ์์ ํ์
public class AreaCalculator
{
public double CalculateArea(object shape)
{
if (shape is Rectangle rectangle)
{
return rectangle.Width * rectangle.Height;
}
else if (shape is Circle circle)
{
return Math.PI * circle.Radius * circle.Radius;
}
// ์๋ก์ด ๋ํ ์ถ๊ฐ ์ ์ฌ๊ธฐ๋ฅผ ์์ ํด์ผ ํจ!
return 0;
}
}
// โ
์ข์ ์ - Strategy ํจํด ํ์ฉ
public interface IShape
{
double CalculateArea();
}
public class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public double CalculateArea()
{
return Width * Height;
}
}
public class Circle : IShape
{
public double Radius { get; set; }
public double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
// ์๋ก์ด ๋ํ ์ถ๊ฐ - ๊ธฐ์กด ์ฝ๋ ์์ ์์ด ํ์ฅ!
public class Triangle : IShape
{
public double Base { get; set; }
public double Height { get; set; }
public double CalculateArea()
{
return 0.5 * Base * Height;
}
}
public class AreaCalculator
{
public double CalculateTotalArea(IEnumerable<IShape> shapes)
{
return shapes.Sum(shape => shape.CalculateArea());
}
}
์์ ํด๋์ค๋ ๋ถ๋ชจ ํด๋์ค๋ฅผ ์์ ํ ๋์ฒดํ ์ ์์ด์ผ ํด!
// โ ๋์ ์ - LSP ์๋ฐ
public class Bird
{
public virtual void Fly()
{
Console.WriteLine("๋ ์๊ฐ๋๋ค!");
}
}
public class Penguin : Bird
{
public override void Fly()
{
// ํญ๊ท์ ๋ ์ ์์ - LSP ์๋ฐ!
throw new NotSupportedException("ํญ๊ท์ ๋ ์ ์์ต๋๋ค!");
}
}
// โ
์ข์ ์ - ์ธํฐํ์ด์ค ๋ถ๋ฆฌ
public interface IBird
{
void Eat();
void Move();
}
public interface IFlyable
{
void Fly();
}
public class Sparrow : IBird, IFlyable
{
public void Eat() => Console.WriteLine("๋จน์ด๋ฅผ ๋จน์ต๋๋ค.");
public void Move() => Console.WriteLine("์ด๋ํฉ๋๋ค.");
public void Fly() => Console.WriteLine("๋ ์๊ฐ๋๋ค!");
}
public class Penguin : IBird
{
public void Eat() => Console.WriteLine("๋ฌผ๊ณ ๊ธฐ๋ฅผ ๋จน์ต๋๋ค.");
public void Move() => Console.WriteLine("๋ค๋ฑ๋ค๋ฑ ๊ฑท์ต๋๋ค.");
// Fly ๋ฉ์๋ ์์ - ํญ๊ท์ ๋ ์ ์์ผ๋๊น!
}
ํด๋ผ์ด์ธํธ๋ ์ฌ์ฉํ์ง ์๋ ์ธํฐํ์ด์ค์ ์์กดํ๋ฉด ์ ๋ผ!
// โ ๋์ ์ - ๊ฑฐ๋ํ ์ธํฐํ์ด์ค
public interface IWorker
{
void Work();
void Eat();
void Sleep();
void GetPaid();
}
public class Robot : IWorker
{
public void Work() => Console.WriteLine("์์
์ค...");
// ๋ก๋ด์ ๋จน์ง๋ ์์ง๋ ์๋๋ฐ ๊ตฌํํด์ผ ํจ!
public void Eat() => throw new NotImplementedException();
public void Sleep() => throw new NotImplementedException();
public void GetPaid() => Console.WriteLine("์ถฉ์ ์ค...");
}
// โ
์ข์ ์ - ์ธํฐํ์ด์ค ๋ถ๋ฆฌ
public interface IWorkable
{
void Work();
}
public interface IFeedable
{
void Eat();
}
public interface ISleepable
{
void Sleep();
}
public interface IPayable
{
void GetPaid();
}
public class Human : IWorkable, IFeedable, ISleepable, IPayable
{
public void Work() => Console.WriteLine("์ผํฉ๋๋ค.");
public void Eat() => Console.WriteLine("์์ฌํฉ๋๋ค.");
public void Sleep() => Console.WriteLine("์ ์ ์ก๋๋ค.");
public void GetPaid() => Console.WriteLine("๊ธ์ฌ๋ฅผ ๋ฐ์ต๋๋ค.");
}
public class Robot : IWorkable, IPayable
{
public void Work() => Console.WriteLine("์์
์ค...");
public void GetPaid() => Console.WriteLine("์ถฉ์ ์ค...");
// ํ์ํ ์ธํฐํ์ด์ค๋ง ๊ตฌํ!
}
๊ตฌ์ฒด์ ์ธ ๊ฒ์ด ์๋ ์ถ์์ ์ธ ๊ฒ์ ์์กดํด์ผ ํด!
// โ ๋์ ์ - ๊ตฌ์ฒด ํด๋์ค์ ์์กด
public class EmailNotification
{
public void Send(string message)
{
Console.WriteLine($"์ด๋ฉ์ผ ๋ฐ์ก: {message}");
}
}
public class NotificationService
{
private EmailNotification emailNotification;
public NotificationService()
{
// ๊ตฌ์ฒด ํด๋์ค์ ์ง์ ์์กด!
emailNotification = new EmailNotification();
}
public void Notify(string message)
{
emailNotification.Send(message);
}
}
// โ
์ข์ ์ - ์ถ์ํ์ ์์กด (์์กด์ฑ ์ฃผ์
)
public interface INotification
{
void Send(string message);
}
public class EmailNotification : INotification
{
public void Send(string message)
{
Console.WriteLine($"๐ง ์ด๋ฉ์ผ ๋ฐ์ก: {message}");
}
}
public class SmsNotification : INotification
{
public void Send(string message)
{
Console.WriteLine($"๐ฑ SMS ๋ฐ์ก: {message}");
}
}
public class PushNotification : INotification
{
public void Send(string message)
{
Console.WriteLine($"๐ ํธ์ ์๋ฆผ: {message}");
}
}
public class NotificationService
{
private readonly INotification notification;
// ์ถ์ํ(์ธํฐํ์ด์ค)์ ์์กด!
public NotificationService(INotification notification)
{
this.notification = notification;
}
public void Notify(string message)
{
notification.Send(message);
}
}
// ์ฌ์ฉ - ์ฝ๊ฒ ๊ต์ฒด ๊ฐ๋ฅ!
var emailService = new NotificationService(new EmailNotification());
var smsService = new NotificationService(new SmsNotification());
var pushService = new NotificationService(new PushNotification());
๐จ ์ค์ ํ๋ก์ ํธ ๊ตฌ์กฐ ์์
์ด์ ๊น์ง ๋ฐฐ์ด ๋ด์ฉ์ ์ข
ํฉํด์ ์ค์ ํ๋ก์ ํธ์์ ์ด๋ป๊ฒ ์ ์ฉํ๋์ง ์ดํด๋ณด์!
์ ์์๊ฑฐ๋ ์์คํ
์ ์๋ก ๋ค์ด๋ณผ๊ฒ. ๐
ํ๋ก์ ํธ ๊ตฌ์กฐ ๋ฐ ๋ ์ด์ด ๋ถ๋ฆฌ
// ========== Domain Layer (๋๋ฉ์ธ ๊ณ์ธต) ==========
// ๋น์ฆ๋์ค ๋ก์ง๊ณผ ์ํฐํฐ
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int StockQuantity { get; set; }
public bool IsAvailable() => StockQuantity > 0;
public void DecreaseStock(int quantity)
{
if (quantity > StockQuantity)
throw new InvalidOperationException("์ฌ๊ณ ๊ฐ ๋ถ์กฑํฉ๋๋ค.");
StockQuantity -= quantity;
}
}
public class Order
{
public int Id { get; set; }
public string CustomerId { get; set; }
public List<OrderItem> Items { get; set; } = new List<OrderItem>();
public OrderStatus Status { get; set; }
public DateTime CreatedAt { get; set; }
public decimal GetTotalAmount()
{
return Items.Sum(item => item.Quantity * item.UnitPrice);
}
}
public class OrderItem
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
public enum OrderStatus
{
Pending,
Paid,
Shipped,
Delivered,
Cancelled
}
// ========== Repository Interfaces (์ ์ฅ์ ์ธํฐํ์ด์ค) ==========
public interface IProductRepository
{
Task<Product> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetAllAsync();
Task<Product> AddAsync(Product product);
Task UpdateAsync(Product product);
Task DeleteAsync(int id);
}
public interface IOrderRepository
{
Task<Order> GetByIdAsync(int id);
Task<IEnumerable<Order>> GetByCustomerIdAsync(string customerId);
Task<Order> AddAsync(Order order);
Task UpdateAsync(Order order);
}
// ========== Service Interfaces (์๋น์ค ์ธํฐํ์ด์ค) ==========
public interface IPaymentService
{
Task<PaymentResult> ProcessPaymentAsync(Order order, PaymentMethod method);
}
public interface INotificationService
{
Task SendOrderConfirmationAsync(Order order);
Task SendShippingNotificationAsync(Order order);
}
public interface IInventoryService
{
Task<bool> CheckAvailabilityAsync(int productId, int quantity);
Task ReserveStockAsync(int productId, int quantity);
Task ReleaseStockAsync(int productId, int quantity);
}
// ========== Application Layer (์ ํ๋ฆฌ์ผ์ด์
๊ณ์ธต) ==========
// ๋น์ฆ๋์ค ๋ก์ง ์กฐ์จ
public class OrderService
{
private readonly IOrderRepository orderRepository;
private readonly IProductRepository productRepository;
private readonly IPaymentService paymentService;
private readonly INotificationService notificationService;
private readonly IInventoryService inventoryService;
private readonly ILogger<OrderService> logger;
public OrderService(
IOrderRepository orderRepository,
IProductRepository productRepository,
IPaymentService paymentService,
INotificationService notificationService,
IInventoryService inventoryService,
ILogger<OrderService> logger)
{
this.orderRepository = orderRepository;
this.productRepository = productRepository;
this.paymentService = paymentService;
this.notificationService = notificationService;
this.inventoryService = inventoryService;
this.logger = logger;
}
public async Task<OrderResult> PlaceOrderAsync(CreateOrderRequest request)
{
logger.LogInformation($"์ฃผ๋ฌธ ์์ฑ ์์: ๊ณ ๊ฐ {request.CustomerId}");
try
{
// 1. ์ฌ๊ณ ํ์ธ
foreach (var item in request.Items)
{
var isAvailable = await inventoryService.CheckAvailabilityAsync(
item.ProductId,
item.Quantity
);
if (!isAvailable)
{
return OrderResult.Failure($"์ํ {item.ProductId}์ ์ฌ๊ณ ๊ฐ ๋ถ์กฑํฉ๋๋ค.");
}
}
// 2. ์ฃผ๋ฌธ ์์ฑ
var order = new Order
{
CustomerId = request.CustomerId,
Status = OrderStatus.Pending,
CreatedAt = DateTime.UtcNow
};
foreach (var item in request.Items)
{
var product = await productRepository.GetByIdAsync(item.ProductId);
order.Items.Add(new OrderItem
{
ProductId = product.Id,
ProductName = product.Name,
Quantity = item.Quantity,
UnitPrice = product.Price
});
}
// 3. ์ฌ๊ณ ์์ฝ
foreach (var item in order.Items)
{
await inventoryService.ReserveStockAsync(item.ProductId, item.Quantity);
}
// 4. ๊ฒฐ์ ์ฒ๋ฆฌ
var paymentResult = await paymentService.ProcessPaymentAsync(
order,
request.PaymentMethod
);
if (!paymentResult.IsSuccess)
{
// ๊ฒฐ์ ์คํจ ์ ์ฌ๊ณ ๋ณต๊ตฌ
foreach (var item in order.Items)
{
await inventoryService.ReleaseStockAsync(item.ProductId, item.Quantity);
}
return OrderResult.Failure("๊ฒฐ์ ์ฒ๋ฆฌ์ ์คํจํ์ต๋๋ค.");
}
// 5. ์ฃผ๋ฌธ ์ํ ์
๋ฐ์ดํธ ๋ฐ ์ ์ฅ
order.Status = OrderStatus.Paid;
await orderRepository.AddAsync(order);
// 6. ์๋ฆผ ๋ฐ์ก
await notificationService.SendOrderConfirmationAsync(order);
logger.LogInformation($"์ฃผ๋ฌธ ์์ฑ ์๋ฃ: ์ฃผ๋ฌธ๋ฒํธ {order.Id}");
return OrderResult.Success(order);
}
catch (Exception ex)
{
logger.LogError(ex, "์ฃผ๋ฌธ ์์ฑ ์ค ์ค๋ฅ ๋ฐ์");
return OrderResult.Failure("์ฃผ๋ฌธ ์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
}
}
public async Task<bool> CancelOrderAsync(int orderId)
{
var order = await orderRepository.GetByIdAsync(orderId);
if (order == null)
return false;
if (order.Status == OrderStatus.Shipped || order.Status == OrderStatus.Delivered)
{
throw new InvalidOperationException("๋ฐฐ์ก ์ค์ด๊ฑฐ๋ ์๋ฃ๋ ์ฃผ๋ฌธ์ ์ทจ์ํ ์ ์์ต๋๋ค.");
}
// ์ฌ๊ณ ๋ณต๊ตฌ
foreach (var item in order.Items)
{
await inventoryService.ReleaseStockAsync(item.ProductId, item.Quantity);
}
order.Status = OrderStatus.Cancelled;
await orderRepository.UpdateAsync(order);
return true;
}
}
// ========== Infrastructure Layer (์ธํ๋ผ ๊ณ์ธต) ==========
// ์ค์ ๊ตฌํ์ฒด
public class SqlProductRepository : IProductRepository
{
private readonly DbContext dbContext;
public SqlProductRepository(DbContext dbContext)
{
this.dbContext = dbContext;
}
public async Task<Product> GetByIdAsync(int id)
{
return await dbContext.Products.FindAsync(id);
}
// ๋ค๋ฅธ ๋ฉ์๋ ๊ตฌํ...
}
public class StripePaymentService : IPaymentService
{
private readonly IConfiguration configuration;
public StripePaymentService(IConfiguration configuration)
{
this.configuration = configuration;
}
public async Task<PaymentResult> ProcessPaymentAsync(Order order, PaymentMethod method)
{
// Stripe API๋ฅผ ํตํ ์ค์ ๊ฒฐ์ ์ฒ๋ฆฌ
// ...
return new PaymentResult { IsSuccess = true };
}
}
public class EmailNotificationService : INotificationService
{
private readonly IEmailService emailService;
public EmailNotificationService(IEmailService emailService)
{
this.emailService = emailService;
}
public async Task SendOrderConfirmationAsync(Order order)
{
var subject = $"์ฃผ๋ฌธ ํ์ธ - ์ฃผ๋ฌธ๋ฒํธ {order.Id}";
var body = $"์ฃผ๋ฌธ์ด ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ต๋๋ค. ์ด ๊ธ์ก: {order.GetTotalAmount()}์";
await emailService.SendEmailAsync(order.CustomerId, subject, body);
}
public async Task SendShippingNotificationAsync(Order order)
{
var subject = $"๋ฐฐ์ก ์์ - ์ฃผ๋ฌธ๋ฒํธ {order.Id}";
var body = "์ํ์ด ๋ฐฐ์ก ์์๋์์ต๋๋ค.";
await emailService.SendEmailAsync(order.CustomerId, subject, body);
}
}
// ========== Dependency Injection ์ค์ ==========
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Repository ๋ฑ๋ก
services.AddScoped<IProductRepository, SqlProductRepository>();
services.AddScoped<IOrderRepository, SqlOrderRepository>();
// Service ๋ฑ๋ก
services.AddScoped<OrderService>();
services.AddScoped<IPaymentService, StripePaymentService>();
services.AddScoped<INotificationService, EmailNotificationService>();
services.AddScoped<IInventoryService, InventoryService>();
// Infrastructure
services.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddControllers();
}
}
๐ก ๋ ์ด์ด ๋ถ๋ฆฌ์ ์ฅ์
1. ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ - ๊ฐ ๋ ์ด์ด๊ฐ ๋ช
ํํ ์ฑ
์์ ๊ฐ์ง ๐ฏ
2. ํ
์คํธ ์ฉ์ด์ฑ - ๊ฐ ๋ ์ด์ด๋ฅผ ๋
๋ฆฝ์ ์ผ๋ก ํ
์คํธ ๊ฐ๋ฅ ๐งช
3. ์ ์ง๋ณด์์ฑ - ๋ณ๊ฒฝ ์ํฅ ๋ฒ์๊ฐ ์ ํ๋จ ๐ง
4. ์ฌ์ฌ์ฉ์ฑ - ์ธํฐํ์ด์ค๋ฅผ ํตํด ๋ค์ํ ๊ตฌํ์ฒด ๊ต์ฒด ๊ฐ๋ฅ โป๏ธ
5. ํ์ฅ์ฑ - ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ๊ฐ ์ฌ์ ๐
๐ ์ค๋ฌด์์์ ๋ฒ ์คํธ ํ๋ํฐ์ค
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋, ์ด์ ์ค๋ฌด์์ ์ ๋ง ์ค์ํ ํ๋ค์ ๊ณต์ ํ ๊ฒ!
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ํ๋ก๊ทธ๋๋ฐ ์ฌ๋ฅ์ ๊ณต์ ํ ๋๋ ์ด๋ฐ ์ง์์ด ์ ๋ง ์ ์ฉํด. ๐ผ
โ ํด์ผ ํ ๊ฒ๋ค
1. ์ธํฐํ์ด์ค ์ฐ์ ์ค๊ณ
๊ตฌํ๋ณด๋ค ์ธํฐํ์ด์ค๋ฅผ ๋จผ์ ์ ์ํ์
2. ์์ ํด๋์ค, ์์ ๋ฉ์๋
ํ ๋ฉ์๋๋ ํ ๊ฐ์ง ์ผ๋ง ํ๋๋ก
3. ์์กด์ฑ ๋ช
์
์์ฑ์๋ฅผ ํตํด ํ์ํ ์์กด์ฑ์ ๋ช
ํํ
4. ๋ถ๋ณ์ฑ ์ ์ง
readonly ํค์๋ ์ ๊ทน ํ์ฉ
5. ๋จ์ ํ
์คํธ ์์ฑ
๋ชจ๋ ๋น์ฆ๋์ค ๋ก์ง์ ํ
์คํธ ์ฝ๋ ์์ฑ
โ ํผํด์ผ ํ ๊ฒ๋ค
1. new ํค์๋ ๋จ๋ฐ
์์กด์ฑ์ ์ง์ ์์ฑํ์ง ๋ง์
2. static ๋ฉ์๋ ๊ณผ์ฉ
ํ
์คํธ์ ํ์ฅ์ด ์ด๋ ค์์ง
3. ๊ฑฐ๋ํ ํด๋์ค
God Object ์ํฐํจํด ์ฃผ์
4. ์ํ ์์กด์ฑ
A โ B โ A ๊ฐ์ ๊ตฌ์กฐ ํผํ๊ธฐ
5. ๊ณผ๋ํ ์ถ์ํ
ํ์ ์ด์์ ์ธํฐํ์ด์ค๋ ์คํ๋ ค ๋ณต์ก๋ ์ฆ๊ฐ
๐ฏ ์ฝ๋ ๋ฆฌ๋ทฐ ์ฒดํฌ๋ฆฌ์คํธ
์์กด์ฑ ๊ด๋ฆฌ
โก ๋ชจ๋ ์์กด์ฑ์ด ์์ฑ์๋ฅผ ํตํด ์ฃผ์
๋๋๊ฐ?
โก ๊ตฌ์ฒด ํด๋์ค๊ฐ ์๋ ์ธํฐํ์ด์ค์ ์์กดํ๋๊ฐ?
โก ์ํ ์์กด์ฑ์ด ์๋๊ฐ?
SOLID ์์น
โก ๊ฐ ํด๋์ค๊ฐ ๋จ์ผ ์ฑ
์์ ๊ฐ์ง๋๊ฐ?
โก ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ ์ ๊ธฐ์กด ์ฝ๋ ์์ ์ด ์ต์ํ๋๋๊ฐ?
โก ์ธํฐํ์ด์ค๊ฐ ์ ์ ํ ๋ถ๋ฆฌ๋์ด ์๋๊ฐ?
ํ
์คํธ ๊ฐ๋ฅ์ฑ
โก ์ธ๋ถ ์์กด์ฑ์ Mock์ผ๋ก ๊ต์ฒดํ ์ ์๋๊ฐ?
โก ๋จ์ ํ
์คํธ ์์ฑ์ด ์ฉ์ดํ๊ฐ?
โก ํ
์คํธ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ์ถฉ๋ถํ๊ฐ?
๊ฐ๋
์ฑ
โก ํด๋์ค์ ๋ฉ์๋ ์ด๋ฆ์ด ๋ช
ํํ๊ฐ?
โก ์ฃผ์ ์์ด๋ ์ฝ๋์ ์๋๋ฅผ ์ดํดํ ์ ์๋๊ฐ?
โก ๋งค์ง ๋๋ฒ๋ ํ๋์ฝ๋ฉ๋ ๊ฐ์ด ์๋๊ฐ?
๐ ๋ ๊ณต๋ถํ๋ฉด ์ข์ ์ฃผ์ ๋ค
์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ด ๊ธฐ์ด๋ผ๋ฉด, ๋ค์ ๋จ๊ณ๋ก ๋์๊ฐ๊ธฐ ์ํ ์ฃผ์ ๋ค์ ์๊ฐํ ๊ฒ!
์ฌ๋ฅ๋ท์์ ์ด๋ฐ ์ฃผ์ ๋ก ์ง์์ ๊ณต์ ํ๊ฑฐ๋ ๋ฐฐ์ธ ์๋ ์์ด. ๐
๐ฅ ๊ณ ๊ธ ๋์์ธ ํจํด
CQRS (Command Query Responsibility Segregation)
๋ช
๋ น๊ณผ ์กฐํ๋ฅผ ๋ถ๋ฆฌํ๋ ํจํด. ๋๊ท๋ชจ ์์คํ
์์ ์ฑ๋ฅ ์ต์ ํ์ ์ ์ฉํด.
Event Sourcing
์ํ ๋ณ๊ฒฝ์ ์ด๋ฒคํธ๋ก ์ ์ฅํ๋ ํจํด. ๊ฐ์ฌ ์ถ์ ๊ณผ ์๊ฐ ์ฌํ์ด ๊ฐ๋ฅํด.
Mediator ํจํด
๊ฐ์ฒด ๊ฐ ํต์ ์ ์ค์ฌ์๋ฅผ ํตํด ์ฒ๋ฆฌ. MediatR ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ํ์ ์ด์ผ.
Repository ํจํด
๋ฐ์ดํฐ ์ ๊ทผ ๋ก์ง์ ์ถ์ํ. Entity Framework์ ํจ๊ป ์์ฃผ ์ฌ์ฉ๋ผ.
Unit of Work ํจํด
์ฌ๋ฌ Repository์ ์์
์ ํ๋์ ํธ๋์ญ์
์ผ๋ก ๋ฌถ์ด ์ฒ๋ฆฌ.
๐๏ธ ์ํคํ ์ฒ ํจํด
Clean Architecture
์์กด์ฑ ๋ฐฉํฅ์ ๋ด๋ถ๋ก ํฅํ๊ฒ ํ๋ ์ํคํ
์ฒ. Uncle Bob์ด ์ ์ํ์ด.
Hexagonal Architecture (Ports and Adapters)
๋น์ฆ๋์ค ๋ก์ง์ ์ธ๋ถ ์์คํ
์ผ๋ก๋ถํฐ ์์ ํ ๋ถ๋ฆฌ.
Onion Architecture
๋๋ฉ์ธ ์ค์ฌ์ ๋ ์ด์ด๋ ์ํคํ
์ฒ. Clean Architecture์ ์ ์ฌํด.
Microservices Architecture
์๋น์ค๋ฅผ ์์ ๋จ์๋ก ๋ถ๋ฆฌ. ๊ฐ ์๋น์ค๊ฐ ๋
๋ฆฝ์ ์ผ๋ก ๋ฐฐํฌ ๊ฐ๋ฅ.
๐ ๏ธ ์ ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๊ตฌ
AutoMapper - ๊ฐ์ฒด ๊ฐ ๋งคํ ์๋ํ
FluentValidation - ์ ์ฐํ ์ ํจ์ฑ ๊ฒ์ฆ
MediatR - Mediator ํจํด ๊ตฌํ
Polly - ์ฌ์๋, ์ํท ๋ธ๋ ์ด์ปค ๋ฑ ๋ณต์๋ ฅ ํจํด
Serilog - ๊ตฌ์กฐํ๋ ๋ก๊น
Moq - Mock ๊ฐ์ฒด ์์ฑ (ํ
์คํธ์ฉ)
xUnit / NUnit - ๋จ์ ํ
์คํธ ํ๋ ์์ํฌ
BenchmarkDotNet - ์ฑ๋ฅ ๋ฒค์น๋งํน
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ ๋ง ๊ธด ์ฌ์ ์ด์์ง? ๐
์ค๋ ์ฐ๋ฆฌ๋ C# ๋์์ธ ํจํด๊ณผ ์์กด์ฑ ์ฃผ์
์ ๋ํด ๊น์ด ์๊ฒ ์์๋ดค์ด.
Singleton, Factory, Strategy, Observer ๊ฐ์ ๋์์ธ ํจํด๋ถํฐ
์์กด์ฑ ์ฃผ์
์ ์ธ ๊ฐ์ง ๋ฐฉ๋ฒ, IoC ์ปจํ
์ด๋, SOLID ์์น๊น์ง!
์ด๋ฐ ๊ฐ๋
๋ค์ด ์ฒ์์ ์ด๋ ต๊ฒ ๋๊ปด์ง ์ ์์ด.
ํ์ง๋ง ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ค ๋ณด๋ฉด ์ ์ ์์ฐ์ค๋ฌ์์ง ๊ฑฐ์ผ. ๐ช
ํต์ฌ์ ์ ๋ฆฌํ๋ฉด:
1. ์ถ์ํ์ ์์กดํ๋ผ ๐ฏ
๊ตฌ์ฒด์ ์ธ ๊ตฌํ์ด ์๋ ์ธํฐํ์ด์ค์ ์์กดํ๋ฉด ์ฝ๋๊ฐ ์ ์ฐํด์ ธ.
2. ์์กด์ฑ์ ์ฃผ์
๋ฐ์๋ผ ๐
๊ฐ์ฒด๊ฐ ์ค์ค๋ก ์์กด์ฑ์ ์์ฑํ์ง ๋ง๊ณ ์ธ๋ถ์์ ๋ฐ์.
3. ๋จ์ผ ์ฑ
์์ ์ง์ผ๋ผ ๐ฆ
ํ๋์ ํด๋์ค๋ ํ๋์ ์ด์ ๋ก๋ง ๋ณ๊ฒฝ๋์ด์ผ ํด.
4. ํ
์คํธ๋ฅผ ์์ฑํ๋ผ ๐งช
์ข์ ์ค๊ณ๋ ํ
์คํธํ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ๋ง๋ค์ด.
5. ์ ์ง์ ์ผ๋ก ๊ฐ์ ํ๋ผ ๐
์๋ฒฝํ ์ฝ๋๋ฅผ ์ฒ์๋ถํฐ ๋ง๋ค ์ ์์ด. ๋ฆฌํฉํ ๋งํ๋ฉด์ ๊ฐ์ ํ์!
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ด๋ฐ ์ง์์ ๊ณต์ ํ๊ณ ๋ฐฐ์ฐ๋ฉด์
ํจ๊ป ์ฑ์ฅํ๋ ๊ฒ๋ ์ ๋ง ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ๐ฑ
์ฝ๋ฉ์ ์์ ์ด์ ๊ณผํ์ด์ผ.
์ค๋ ๋ฐฐ์ด ์์น๋ค์ ๊ธฐ์ตํ๋ฉด์, ์๋ฆ๋ต๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ์์ฑํด๋ณด์!
์ง๋ฌธ์ด๋ ๋ ์๊ณ ์ถ์ ๋ด์ฉ์ด ์๋ค๋ฉด ์ธ์ ๋ ๋ฌผ์ด๋ด.
์ฐ๋ฆฌ ๋ชจ๋ ๋ ๋์ ๊ฐ๋ฐ์๊ฐ ๋๋ ๊ทธ๋ ๊น์ง! ํ์ดํ
! ๐ฅโจ
๐ Happy Coding! ๐
๋ ๋์ ์ฝ๋, ๋ ๋์ ๊ฐ๋ฐ์๋ฅผ ํฅํด!
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.