์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽฏ C# ๋””์ž์ธ ํŒจํ„ด๊ณผ ์˜์กด์„ฑ ์ฃผ์ž…์œผ๋กœ ๋งŒ๋“œ๋Š” ์œ ์ง€๋ณด์ˆ˜ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ ์„ค๊ณ„ ์ „๋žต

๐ŸŽฏ C# ๋””์ž์ธ ํŒจํ„ด๊ณผ ์˜์กด์„ฑ ์ฃผ์ž…์œผ๋กœ ๋งŒ๋“œ๋Š” ์œ ์ง€๋ณด์ˆ˜ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ ์„ค๊ณ„ ์ „๋žต

์นœ๊ตฌ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ๋ฐฐ์šฐ๋Š” ์‹ค์ „ ์ฝ”๋“œ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€์ด๋“œ ๐Ÿš€

์•ˆ๋…•! ๐Ÿ‘‹ ์˜ค๋Š˜์€ C# ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๊ผญ ์•Œ์•„์•ผ ํ•  ํ•ต์‹ฌ ์ฃผ์ œ๋ฅผ ๋‹ค๋ค„๋ณผ ๊ฑฐ์•ผ.
๋ฐ”๋กœ ๋””์ž์ธ ํŒจํ„ด๊ณผ ์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection)์„ ํ™œ์šฉํ•ด์„œ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์ง€! ๐Ÿ˜Š

์ฝ”๋“œ๋ฅผ ์งœ๋‹ค ๋ณด๋ฉด ์ฒ˜์Œ์—” ์ž˜ ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ, ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด์„œ ์ˆ˜์ •ํ•˜๊ธฐ ์–ด๋ ต๊ณ  ๋ฒ„๊ทธ๋Š” ๊ณ„์† ์ƒ๊ธฐ๊ณ ... ์ด๋Ÿฐ ๊ฒฝํ—˜ ์žˆ์ง€?
์ด๊ฑด ๋Œ€๋ถ€๋ถ„ ์ฝ”๋“œ ๊ตฌ์กฐ ์„ค๊ณ„๋ฅผ ์ œ๋Œ€๋กœ ํ•˜์ง€ ์•Š์•„์„œ ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ์•ผ. ์˜ค๋Š˜ ์ด์•ผ๊ธฐํ•  ๋‚ด์šฉ๋“ค์„ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๋ฉด, ๋„ˆ์˜ ์ฝ”๋“œ๋Š” ํ›จ์”ฌ ๋” ๊น”๋”ํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๋ฉฐ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฌ์šด ๊ตฌ์กฐ๋กœ ๋ณ€์‹ ํ•  ๊ฑฐ์•ผ! โœจ

๐Ÿค” ์™œ ๋””์ž์ธ ํŒจํ„ด๊ณผ ์˜์กด์„ฑ ์ฃผ์ž…์ด ์ค‘์š”ํ• ๊นŒ?

๋ณธ๊ฒฉ์ ์œผ๋กœ ๋“ค์–ด๊ฐ€๊ธฐ ์ „์—, ์™œ ์ด๋Ÿฐ ๊ฐœ๋…๋“ค์ด ์ค‘์š”ํ•œ์ง€๋ถ€ํ„ฐ ์ด์•ผ๊ธฐํ•ด๋ณด์ž.

์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์—์„œ ๊ฐ€์žฅ ํฐ ๋น„์šฉ์€ ์‚ฌ์‹ค '์œ ์ง€๋ณด์ˆ˜'์•ผ. ์ฒ˜์Œ ๊ฐœ๋ฐœํ•˜๋Š” ์‹œ๊ฐ„๋ณด๋‹ค ๋‚˜์ค‘์— ์ˆ˜์ •ํ•˜๊ณ  ๊ฐœ์„ ํ•˜๋Š” ์‹œ๊ฐ„์ด ํ›จ์”ฌ ๋” ๋งŽ๊ฑฐ๋“ .
๊ทธ๋ž˜์„œ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ณ€๊ฒฝ์— ์œ ์—ฐํ•œ ๊ตฌ์กฐ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒŒ ์ •๋ง ์ค‘์š”ํ•ด! ๐Ÿ’ก

๐ŸŽฏ ์ข‹์€ ์ฝ”๋“œ ์„ค๊ณ„์˜ ํ•ต์‹ฌ ์›์น™:

1. ๋‚ฎ์€ ๊ฒฐํ•ฉ๋„(Low Coupling) - ํด๋ž˜์Šค๋“ค์ด ์„œ๋กœ ๋…๋ฆฝ์ ์ด์–ด์•ผ ํ•ด
2. ๋†’์€ ์‘์ง‘๋„(High Cohesion) - ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ๋“ค์€ ํ•œ ๊ณณ์— ๋ชจ์—ฌ์žˆ์–ด์•ผ ํ•ด
3. ๋‹จ์ผ ์ฑ…์ž„ ์›์น™(SRP) - ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ ธ์•ผ ํ•ด
4. ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™(OCP) - ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ๊ณ  ์ˆ˜์ •์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•ด
5. ์˜์กด์„ฑ ์—ญ์ „ ์›์น™(DIP) - ๊ตฌ์ฒด์ ์ธ ๊ฒƒ๋ณด๋‹ค ์ถ”์ƒ์ ์ธ ๊ฒƒ์— ์˜์กดํ•ด์•ผ ํ•ด

์ด๋Ÿฐ ์›์น™๋“ค์„ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋„๊ตฌ๊ฐ€ ๋ฐ”๋กœ ๋””์ž์ธ ํŒจํ„ด์ด๊ณ ,
๊ทธ์ค‘์—์„œ๋„ ์˜์กด์„ฑ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ํ•ต์‹ฌ ๊ธฐ๋ฒ•์ด ์˜์กด์„ฑ ์ฃผ์ž…์ด์•ผ! ๐Ÿ”ง

์ข‹์€ ์ฝ”๋“œ ์„ค๊ณ„์˜ ์—ฌ์ • ๋‚˜์œ ์ฝ”๋“œ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ ์ˆ˜์ • ์–ด๋ ค์›€ ๋””์ž์ธ ํŒจํ„ด ์ ์šฉ ๊ฐœ์„ ๋œ ์ฝ”๋“œ ๋А์Šจํ•œ ๊ฒฐํ•ฉ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์˜์กด์„ฑ ์ฃผ์ž… ์™„๋ฒฝํ•œ ์ฝ”๋“œ ํ…Œ์ŠคํŠธ ์šฉ์ด ์œ ์ง€๋ณด์ˆ˜ ์‰ฌ์›€ ์ ์ง„์  ๊ฐœ์„ ์„ ํ†ตํ•ด ์ฝ”๋“œ ํ’ˆ์งˆ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค

๐ŸŽจ ๋””์ž์ธ ํŒจํ„ด์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

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

1994๋…„์— 'Gang of Four(GoF)'๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ๋„ค ๋ช…์˜ ๊ฐœ๋ฐœ์ž๊ฐ€ 23๊ฐ€์ง€ ๋””์ž์ธ ํŒจํ„ด์„ ์ •๋ฆฌํ–ˆ๋Š”๋ฐ,
์ด๊ฒŒ ์ง€๊ธˆ๊นŒ์ง€๋„ ์†Œํ”„ํŠธ์›จ์–ด ์„ค๊ณ„์˜ ๋ฐ”์ด๋ธ”๋กœ ์—ฌ๊ฒจ์ง€๊ณ  ์žˆ์–ด.

๐Ÿ“š ๋””์ž์ธ ํŒจํ„ด์˜ 3๊ฐ€์ง€ ๋ถ„๋ฅ˜

๐Ÿ—๏ธ ์ƒ์„ฑ ํŒจํ„ด (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 ํŒจํ„ด์€ ๊ฐ์ฒด ์ƒ์„ฑ ๋กœ์ง์„ ๋ณ„๋„์˜ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ํŒจํ„ด์ด์•ผ.
๋ณต์žกํ•œ ๊ฐ์ฒด ์ƒ์„ฑ ๊ณผ์ •์„ ์ˆจ๊ธฐ๊ณ , ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜! ๐ŸŽ

๐Ÿ”จ Factory Method ํŒจํ„ด

์‹ค์ „ ์˜ˆ์ œ - ๋‹ค์–‘ํ•œ ๊ฒฐ์ œ ๋ฐฉ์‹ ์ฒ˜๋ฆฌ

// ๊ฒฐ์ œ ์ธํ„ฐํŽ˜์ด์Šค
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์— ์ง‘์ค‘๋ผ ๐ŸŽฏ

Factory ํŒจํ„ด ๋™์ž‘ ์›๋ฆฌ Payment Factory ๊ฐ์ฒด ์ƒ์„ฑ ๋‹ด๋‹น CreatePaymentProcessor() CreditCard Payment ์‹ ์šฉ์นด๋“œ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ์นด๋“œ ๋ฒˆํ˜ธ ๊ฒ€์ฆ ์Šน์ธ ์š”์ฒญ BankTransfer Payment ๊ณ„์ขŒ์ด์ฒด ์ฒ˜๋ฆฌ ๊ณ„์ขŒ ํ™•์ธ ์ด์ฒด ์‹คํ–‰ KakaoPay Payment ์นด์นด์˜คํŽ˜์ด ์ฒ˜๋ฆฌ ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ฒฐ์ œ ์Šน์ธ 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. ๋‹จ์ผ ์ฑ…์ž„ ์›์น™ - ๊ฐ์ฒด ์ƒ์„ฑ ์ฑ…์ž„ ๋ถ„๋ฆฌ ๐Ÿ“ฆ

์˜์กด์„ฑ ์ฃผ์ž… ์ „ํ›„ ๋น„๊ต ์˜์กด์„ฑ ์ฃผ์ž… ์—†์Œ โŒ ์˜์กด์„ฑ ์ฃผ์ž… ์žˆ์Œ โœ… UserService new SqlServerDatabase() SqlServerDatabase ๊ฐ•ํ•œ ๊ฒฐํ•ฉ ๋ฌธ์ œ์ : ๋ณ€๊ฒฝ ์–ด๋ ค์›€ ํ…Œ์ŠคํŠธ ๋ถˆ๊ฐ€๋Šฅ ์žฌ์‚ฌ์šฉ์„ฑ ๋‚ฎ์Œ ํ™•์žฅ์„ฑ ์—†์Œ UserService IDatabase ์ธํ„ฐํŽ˜์ด์Šค ์˜์กด SQL Server MySQL Database Mongo Database ์žฅ์ : ๋А์Šจํ•œ ๊ฒฐํ•ฉ ์‰ฌ์šด ํ…Œ์ŠคํŠธ ๋†’์€ ์žฌ์‚ฌ์šฉ์„ฑ ๋›ฐ์–ด๋‚œ ํ™•์žฅ์„ฑ ์œ ์—ฐํ•œ ๊ต์ฒด

๐ŸŽฏ ์˜์กด์„ฑ ์ฃผ์ž…์˜ 3๊ฐ€์ง€ ๋ฐฉ๋ฒ•

์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๊ฐ€ ์žˆ์–ด. ๊ฐ๊ฐ์˜ ํŠน์ง•๊ณผ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์•Œ์•„๋ณด์ž! ๐Ÿ“š

1๏ธโƒฃ ์ƒ์„ฑ์ž ์ฃผ์ž… (Constructor Injection)

๊ฐ€์žฅ ๊ถŒ์žฅ๋˜๋Š” ๋ฐฉ๋ฒ•! ๐ŸŒŸ

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. ํ•„์ˆ˜ ์˜์กด์„ฑ ๋ณด์žฅ - ๊ฐ์ฒด ์ƒ์„ฑ ์‹œ ๋ฐ˜๋“œ์‹œ ์˜์กด์„ฑ ์ œ๊ณต ํ•„์š”

2๏ธโƒฃ ์†์„ฑ ์ฃผ์ž… (Property Injection)

์„ ํƒ์  ์˜์กด์„ฑ์— ์‚ฌ์šฉ

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์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ’ฅ

3๏ธโƒฃ ๋ฉ”์„œ๋“œ ์ฃผ์ž… (Method Injection)

ํŠน์ • ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ์—๋งŒ ํ•„์š”ํ•œ ์˜์กด์„ฑ

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) ์ปจํ…Œ์ด๋„ˆ์•ผ!
์˜์กด์„ฑ ๊ด€๋ฆฌ๋ฅผ ์ž๋™ํ™”ํ•ด์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ง€. ๐Ÿ› ๏ธ

๐ŸŽฏ ASP.NET Core์˜ ๋‚ด์žฅ DI ์ปจํ…Œ์ด๋„ˆ

์„œ๋น„์Šค ๋“ฑ๋ก๊ณผ ์‚ฌ์šฉ

// 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 (๋‹จ์ผ)
- ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค
- ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ์ดˆ๊ธฐํ™” ๋น„์šฉ์ด ํฐ ์„œ๋น„์Šค
- ์˜ˆ: ์„ค์ • ๊ด€๋ฆฌ์ž, ์บ์‹œ, ๋กœ๊ฑฐ ํŒฉํ† ๋ฆฌ
- โš ๏ธ ์ฃผ์˜: ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ ๊ณ ๋ ค ํ•„์š”!

์„œ๋น„์Šค ์ƒ๋ช…์ฃผ๊ธฐ ๋น„๊ต Transient A1 A2 A3 A4 A5 A6 ๋งค๋ฒˆ ์ƒˆ ์ธ์Šคํ„ด์Šค ์š”์ฒญ๋งˆ๋‹ค ์ƒ์„ฑ ์ƒํƒœ ๊ณต์œ  ์—†์Œ ๊ฐ€๋ฒผ์šด ์„œ๋น„์Šค ์˜ˆ: EmailService Scoped HTTP Request 1 B1 B1 HTTP Request 2 B2 B2 ์š”์ฒญ๋‹น ํ•˜๋‚˜ ์˜ˆ: DbContext Singleton C ์ „์ฒด์—์„œ ํ•˜๋‚˜ ์•ฑ ์‹œ์ž‘์‹œ ์ƒ์„ฑ ์˜ˆ: Configuration

๐Ÿงช ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ

์˜์กด์„ฑ ์ฃผ์ž…์˜ ๊ฐ€์žฅ ํฐ ์žฅ์  ์ค‘ ํ•˜๋‚˜๋Š” ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์ด์•ผ!
์‹ค์ œ ์˜์กด์„ฑ ๋Œ€์‹  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 ์›์น™์„ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ•จ๊ป˜ ์ดํ•ดํ•ด๋ณด์ž!
์ด ์›์น™๋“ค์„ ๋”ฐ๋ฅด๋ฉด ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฝ๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด. ๐ŸŽฏ

1๏ธโƒฃ SRP - ๋‹จ์ผ ์ฑ…์ž„ ์›์น™ (Single Responsibility Principle)

ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ ธ์•ผ ํ•ด!

// โŒ ๋‚˜์œ ์˜ˆ - ์—ฌ๋Ÿฌ ์ฑ…์ž„์„ ๊ฐ€์ง„ ํด๋ž˜์Šค
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)
    {
        // ๋กœ๊น… ๋กœ์ง
    }
}
2๏ธโƒฃ OCP - ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™ (Open-Closed Principle)

ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ๊ณ , ์ˆ˜์ •์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•ด!

// โŒ ๋‚˜์œ ์˜ˆ - ์ƒˆ๋กœ์šด ๋„ํ˜• ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด ์ฝ”๋“œ ์ˆ˜์ • ํ•„์š”
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());
    }
}
3๏ธโƒฃ LSP - ๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™ (Liskov Substitution Principle)

์ž์‹ ํด๋ž˜์Šค๋Š” ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ์™„์ „ํžˆ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ด!

// โŒ ๋‚˜์œ ์˜ˆ - 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 ๋ฉ”์„œ๋“œ ์—†์Œ - ํŽญ๊ท„์€ ๋‚  ์ˆ˜ ์—†์œผ๋‹ˆ๊นŒ!
}
4๏ธโƒฃ ISP - ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™ (Interface Segregation Principle)

ํด๋ผ์ด์–ธํŠธ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•˜๋ฉด ์•ˆ ๋ผ!

// โŒ ๋‚˜์œ ์˜ˆ - ๊ฑฐ๋Œ€ํ•œ ์ธํ„ฐํŽ˜์ด์Šค
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("์ถฉ์ „ ์ค‘...");
    // ํ•„์š”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ๊ตฌํ˜„!
}
5๏ธโƒฃ DIP - ์˜์กด์„ฑ ์—ญ์ „ ์›์น™ (Dependency Inversion Principle)

๊ตฌ์ฒด์ ์ธ ๊ฒƒ์ด ์•„๋‹Œ ์ถ”์ƒ์ ์ธ ๊ฒƒ์— ์˜์กดํ•ด์•ผ ํ•ด!

// โŒ ๋‚˜์œ ์˜ˆ - ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กด
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());
SOLID ์›์น™ ์ข‹์€ ๊ฐ์ฒด์ง€ํ–ฅ ์„ค๊ณ„์˜ 5๊ฐ€์ง€ ์›์น™ S Single Responsibility ๋‹จ์ผ ์ฑ…์ž„ O Open-Closed Principle ๊ฐœ๋ฐฉ-ํ์‡„ L Liskov Substitution ๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ I Interface Segregation ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ D Dependency Inversion Principle ์˜์กด์„ฑ ์—ญ์ „ ์›์น™ ์ถ”์ƒํ™”์— ์˜์กดํ•˜๊ณ  ๊ตฌ์ฒดํ™”์— ์˜์กดํ•˜์ง€ ๋ง ๊ฒƒ

๐ŸŽจ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ์˜ˆ์ œ

์ด์ œ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋–ป๊ฒŒ ์ ์šฉํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด์ž!
์ „์ž์ƒ๊ฑฐ๋ž˜ ์‹œ์Šคํ…œ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ. ๐Ÿ›’

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ๋ฐ ๋ ˆ์ด์–ด ๋ถ„๋ฆฌ

// ========== 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
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.