์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ C# ๋งˆ์Šคํ„ฐ๋กœ ๊ฐ€๋Š” ์—ฌ์ •

๐Ÿš€ C# ๋งˆ์Šคํ„ฐ๋กœ ๊ฐ€๋Š” ์—ฌ์ •

์ฃผ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์™„๋ฒฝํ•œ ํ•™์Šต ๋กœ๋“œ๋งต

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

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

๊ธฐ์ดˆ ์ค‘๊ธ‰ ์‹ค์ „ ์ „๋ฌธ๊ฐ€ C# ๊ฐœ๋ฐœ์ž ์„ฑ์žฅ ๋กœ๋“œ๋งต

๐ŸŽฏ ์™œ C#์„ ๋ฐฐ์›Œ์•ผ ํ• ๊นŒ?

๋ณธ๊ฒฉ์ ์ธ ๋กœ๋“œ๋งต์— ๋“ค์–ด๊ฐ€๊ธฐ ์ „์—, ์ž ๊น C#์ด ์™œ ์ข‹์€ ์„ ํƒ์ธ์ง€ ์ด์•ผ๊ธฐํ•ด๋ณผ๊ฒŒ. ์ด๊ฑด ๋‹จ์ˆœํžˆ ๋™๊ธฐ๋ถ€์—ฌ๊ฐ€ ์•„๋‹ˆ๋ผ, ์•ž์œผ๋กœ์˜ ํ•™์Šต ๋ฐฉํ–ฅ์„ ์žก๋Š” ๋ฐ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด๊ฑฐ๋“ .

๐Ÿ’ผ ์ทจ์—… ์‹œ์žฅ

C#์€ ๊ตญ๋‚ด์™ธ ๊ธฐ์—…์—์„œ ๊พธ์ค€ํžˆ ์ˆ˜์š”๊ฐ€ ์žˆ์–ด. ํŠนํžˆ ๊ฒŒ์ž„ ๊ฐœ๋ฐœ(Unity), ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์†”๋ฃจ์…˜, ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค ๋ถ„์•ผ์—์„œ ๊ฐ•์„ธ๋ฅผ ๋ณด์ด์ง€. ์‹ค์ œ๋กœ ์‚ฌ๋žŒ์ธ, ์žก์ฝ”๋ฆฌ์•„ ๊ฐ™์€ ๊ตฌ์ธ๊ตฌ์ง ์‚ฌ์ดํŠธ์—์„œ 'C# ๊ฐœ๋ฐœ์ž'๋กœ ๊ฒ€์ƒ‰ํ•˜๋ฉด ์ˆ˜์ฒœ ๊ฐœ์˜ ๊ณต๊ณ ๊ฐ€ ๋‚˜์™€.

๐ŸŽฎ ๋‹ค์–‘ํ•œ ํ™œ์šฉ

์›น ๊ฐœ๋ฐœ(ASP.NET), ๋ฐ์Šคํฌํ†ฑ ์•ฑ(WPF, WinForms), ๋ชจ๋ฐ”์ผ(Xamarin), ๊ฒŒ์ž„(Unity), ํด๋ผ์šฐ๋“œ(Azure) ๋“ฑ ์ •๋ง ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์—์„œ ์‚ฌ์šฉ๋ผ. ํ•œ ์–ธ์–ด๋กœ ์—ฌ๋Ÿฌ ํ”Œ๋žซํผ์„ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑด ์—„์ฒญ๋‚œ ์žฅ์ ์ด์•ผ.

๐Ÿš€ ํ˜„๋Œ€์ ์ธ ์–ธ์–ด

C#์€ ๊ณ„์† ์ง„ํ™”ํ•˜๊ณ  ์žˆ์–ด. ์ตœ์‹  ๋ฒ„์ „์ธ C# 12๋Š” ํŒจํ„ด ๋งค์นญ, ๋ ˆ์ฝ”๋“œ ํƒ€์ž…, nullable ์ฐธ์กฐ ํƒ€์ž… ๋“ฑ ํ˜„๋Œ€์ ์ธ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•ด. Microsoft์˜ ๊ฐ•๋ ฅํ•œ ์ง€์›๋„ ํฐ ๋ฉ”๋ฆฌํŠธ์ง€.

๐Ÿ‘ฅ ์ปค๋ฎค๋‹ˆํ‹ฐ

Stack Overflow, GitHub, Reddit ๋“ฑ์—์„œ ํ™œ๋ฐœํ•œ ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ํ˜•์„ฑ๋˜์–ด ์žˆ์–ด. ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ๋„์›€๋ฐ›๊ธฐ ์‰ฝ๊ณ , ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋„ ๋งŽ์•„์„œ ์‹ค์ „ ์ฝ”๋“œ๋ฅผ ๋ฐฐ์šฐ๊ธฐ ์ข‹์•„.

๐Ÿ“š 1๋‹จ๊ณ„: ๊ธฐ์ดˆ ๋‹ค์ง€๊ธฐ (0-3๊ฐœ์›”)

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์‹œ์ž‘ํ•ด๋ณด์ž! ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋Š” **ํƒ„ํƒ„ํ•œ ๊ธฐ์ดˆ๋ฅผ ์Œ“๋Š” ๊ฒƒ**์ด์•ผ. ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ด ๋‹จ๊ณ„๋ฅผ ๋Œ€์ถฉ ๋„˜์–ด๊ฐ€๋Š”๋ฐ, ์ด๊ฑด ์ •๋ง ํฐ ์‹ค์ˆ˜์•ผ. ๊ธฐ์ดˆ๊ฐ€ ํŠผํŠผํ•ด์•ผ ๋‚˜์ค‘์— ๋ณต์žกํ•œ ๊ฐœ๋…๋„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ .

๐Ÿ”ค ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ณธ ๊ฐœ๋…

๋ณ€์ˆ˜์™€ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๐ŸŽฒ

C#์€ ์ •์  ํƒ€์ž… ์–ธ์–ด์•ผ. ์ฆ‰, ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•  ๋•Œ ํƒ€์ž…์„ ๋ช…์‹œํ•ด์•ผ ํ•ด. ์ฒ˜์Œ์—” ๊ท€์ฐฎ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ด๊ฒŒ ๋‚˜์ค‘์— ๋ฒ„๊ทธ๋ฅผ ์ค„์—ฌ์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๊ฐ€ ๋ผ.

// ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ํƒ€์ž…๋“ค
int age = 25;                    // ์ •์ˆ˜
double price = 19.99;            // ์‹ค์ˆ˜
string name = "๊น€๊ฐœ๋ฐœ";           // ๋ฌธ์ž์—ด
bool isStudent = true;           // ๋ถˆ๋ฆฌ์–ธ
char grade = 'A';                // ๋ฌธ์ž

// var ํ‚ค์›Œ๋“œ (ํƒ€์ž… ์ถ”๋ก )
var city = "์„œ์šธ";  // ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž๋™์œผ๋กœ string์œผ๋กœ ์ธ์‹
๐Ÿ’ก ๊ฟ€ํŒ: var๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•ด์ง€์ง€๋งŒ, ํƒ€์ž…์ด ๋ช…ํ™•ํ•˜์ง€ ์•Š์„ ๋•Œ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ํƒ€์ž…์„ ์จ์ฃผ๋Š” ๊ฒŒ ๊ฐ€๋…์„ฑ์— ์ข‹์•„. ํŠนํžˆ ํŒ€ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋”์šฑ ๊ทธ๋ž˜!

์ œ์–ด๋ฌธ๊ณผ ๋ฐ˜๋ณต๋ฌธ ๐Ÿ”„

ํ”„๋กœ๊ทธ๋žจ์˜ ํ๋ฆ„์„ ์ œ์–ดํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›Œ์•ผ ํ•ด. if-else, switch, for, while, foreach ๋“ฑ์ด ์—ฌ๊ธฐ์— ์†ํ•ด.

// if-else ๋ฌธ
if (age >= 20)
{
    Console.WriteLine("์„ฑ์ธ์ž…๋‹ˆ๋‹ค");
}
else
{
    Console.WriteLine("๋ฏธ์„ฑ๋…„์ž์ž…๋‹ˆ๋‹ค");
}

// switch ๋ฌธ (C# 8.0 ์ด์ƒ์˜ ํŒจํ„ด ๋งค์นญ)
string result = age switch
{
    < 13 => "์–ด๋ฆฐ์ด",
    < 20 => "์ฒญ์†Œ๋…„",
    _ => "์„ฑ์ธ"
};

// foreach ๋ฐ˜๋ณต๋ฌธ
string[] fruits = { "์‚ฌ๊ณผ", "๋ฐ”๋‚˜๋‚˜", "์˜ค๋ Œ์ง€" };
foreach (var fruit in fruits)
{
    Console.WriteLine(fruit);
}

๐ŸŽจ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ (OOP)

C#์˜ ํ•ต์‹ฌ์ด์ž ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด์•ผ. OOP๋ฅผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋ฉด C#์„ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์—†์–ด. ์ฒœ์ฒœํžˆ, ํ™•์‹คํ•˜๊ฒŒ ์ตํžˆ์ž!

1๏ธโƒฃ ํด๋ž˜์Šค์™€ ๊ฐ์ฒด

ํด๋ž˜์Šค๋Š” ์„ค๊ณ„๋„, ๊ฐ์ฒด๋Š” ๊ทธ ์„ค๊ณ„๋„๋กœ ๋งŒ๋“  ์‹ค์ œ ์ œํ’ˆ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ.

// ํด๋ž˜์Šค ์ •์˜
public class Student
{
    // ํ•„๋“œ (๋ฉค๋ฒ„ ๋ณ€์ˆ˜)
    private string name;
    private int age;
    
    // ํ”„๋กœํผํ‹ฐ (C#์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ!)
    public string Name 
    { 
        get { return name; }
        set { name = value; }
    }
    
    // ์ž๋™ ํ”„๋กœํผํ‹ฐ (๋” ๊ฐ„๋‹จํ•˜๊ฒŒ)
    public int Age { get; set; }
    
    // ์ƒ์„ฑ์ž
    public Student(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
    
    // ๋ฉ”์„œ๋“œ
    public void Introduce()
    {
        Console.WriteLine($"์•ˆ๋…•ํ•˜์„ธ์š”, {name}์ž…๋‹ˆ๋‹ค. {age}์‚ด์ด์—์š”.");
    }
}

// ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์‚ฌ์šฉ
Student student = new Student("๊น€์ฒ ์ˆ˜", 20);
student.Introduce();

2๏ธโƒฃ ์บก์Аํ™” (Encapsulation)

๋ฐ์ดํ„ฐ๋ฅผ ์ˆจ๊ธฐ๊ณ  ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ณต๊ฐœํ•˜๋Š” ๊ฑฐ์•ผ. private, public, protected ๊ฐ™์€ ์ ‘๊ทผ ์ œํ•œ์ž๋ฅผ ์‚ฌ์šฉํ•ด.

๐Ÿ’ก ์‹ค์ „ ํŒ: ํ•„๋“œ๋Š” ํ•ญ์ƒ private์œผ๋กœ ์„ ์–ธํ•˜๊ณ , ์™ธ๋ถ€ ์ ‘๊ทผ์ด ํ•„์š”ํ•˜๋ฉด ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‚˜์ค‘์— ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋‚˜ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์‰ฌ์›Œ์ ธ.

3๏ธโƒฃ ์ƒ์† (Inheritance)

๊ธฐ์กด ํด๋ž˜์Šค์˜ ๊ธฐ๋Šฅ์„ ๋ฌผ๋ ค๋ฐ›์•„ ์ƒˆ๋กœ์šด ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ. ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ์ด ์—„์ฒญ ์ข‹์•„์ ธ.

// ๋ถ€๋ชจ ํด๋ž˜์Šค
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public virtual void Introduce()
    {
        Console.WriteLine($"์ €๋Š” {Name}์ž…๋‹ˆ๋‹ค.");
    }
}

// ์ž์‹ ํด๋ž˜์Šค
public class Developer : Person
{
    public string Language { get; set; }
    
    // ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋ผ์ด๋”ฉ
    public override void Introduce()
    {
        base.Introduce();  // ๋ถ€๋ชจ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
        Console.WriteLine($"{Language} ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค.");
    }
}

4๏ธโƒฃ ๋‹คํ˜•์„ฑ (Polymorphism)

๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋‹ค๋ฅธ ๋™์ž‘์„ ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ. ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ถ”์ƒ ํด๋ž˜์Šค๊ฐ€ ํ•ต์‹ฌ์ด์ง€.

// ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜
public interface IDrawable
{
    void Draw();
}

// ์—ฌ๋Ÿฌ ํด๋ž˜์Šค๊ฐ€ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„
public class Circle : IDrawable
{
    public void Draw()
    {
        Console.WriteLine("์›์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค โญ•");
    }
}

public class Square : IDrawable
{
    public void Draw()
    {
        Console.WriteLine("์‚ฌ๊ฐํ˜•์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค โฌœ");
    }
}

// ๋‹คํ˜•์„ฑ ํ™œ์šฉ
List<IDrawable> shapes = new List<IDrawable>
{
    new Circle(),
    new Square()
};

foreach (var shape in shapes)
{
    shape.Draw();  // ๊ฐ ๊ฐ์ฒด์˜ Draw ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋จ
}

๐Ÿ“ฆ ์ปฌ๋ ‰์…˜๊ณผ ์ œ๋„ค๋ฆญ

๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›Œ์•ผ ํ•ด. List, Dictionary, Queue, Stack ๋“ฑ์˜ ์ปฌ๋ ‰์…˜๊ณผ ์ œ๋„ค๋ฆญ์€ ์‹ค๋ฌด์—์„œ ์ •๋ง ๋งŽ์ด ์‚ฌ์šฉ๋ผ.

List<T>

๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ปฌ๋ ‰์…˜. ๋™์  ๋ฐฐ์—ด์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ.

List<string> names = new List<string>();
names.Add("๊น€์ฒ ์ˆ˜");
names.Add("์ด์˜ํฌ");

Dictionary<K,V>

ํ‚ค-๊ฐ’ ์Œ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ. ๋น ๋ฅธ ๊ฒ€์ƒ‰์ด ํ•„์š”ํ•  ๋•Œ ์ตœ๊ณ !

Dictionary<string, int> scores = new();
scores["๊น€์ฒ ์ˆ˜"] = 95;
scores["์ด์˜ํฌ"] = 88;

์ œ๋„ค๋ฆญ์˜ ํž˜ โšก

์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•˜๋ฉด ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด.

// ์ œ๋„ค๋ฆญ ํด๋ž˜์Šค
public class Box<T>
{
    private T item;
    
    public void Pack(T item)
    {
        this.item = item;
    }
    
    public T Unpack()
    {
        return item;
    }
}

// ์‚ฌ์šฉ
Box<string> stringBox = new Box<string>();
stringBox.Pack("์„ ๋ฌผ");

Box<int> intBox = new Box<int>();
intBox.Pack(100);

๐ŸŽ“ 1๋‹จ๊ณ„ ํ•™์Šต ์ „๋žต

๐Ÿ“– ์ถ”์ฒœ ํ•™์Šต ์ž๋ฃŒ

โ€ข Microsoft Learn์˜ C# ๊ณต์‹ ๋ฌธ์„œ (๋ฌด๋ฃŒ, ํ•œ๊ธ€ ์ง€์›)
โ€ข "C# ํ”„๋กœ๊ทธ๋ž˜๋ฐ" (์ด๊ฒƒ์ด C#์ด๋‹ค) - ๋ฐ•์ƒํ˜„ ์ €
โ€ข YouTube: ์ฝ”๋”ฉ์• ํ”Œ, ๋“œ๋ฆผ์ฝ”๋”ฉ ๋“ฑ์˜ C# ๊ธฐ์ดˆ ๊ฐ•์˜
โ€ข ์žฌ๋Šฅ๋„ท์—์„œ C# ๋ฉ˜ํ† ๋ง ์ฐพ์•„๋ณด๊ธฐ (์‹ค์‹œ๊ฐ„ ์งˆ๋ฌธ ๊ฐ€๋Šฅ!)

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ: ์ด ๋‹จ๊ณ„์—์„œ ๊ฐ€์žฅ ํ”ํ•œ ์‹ค์ˆ˜๋Š” ๋„ˆ๋ฌด ๋นจ๋ฆฌ ๋„˜์–ด๊ฐ€๋ ค๋Š” ๊ฑฐ์•ผ. ๊ธฐ์ดˆ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋ฉด ๋‚˜์ค‘์— ๋ฐ˜๋“œ์‹œ ๋‹ค์‹œ ๋Œ์•„์™€์•ผ ํ•ด. ๊ฐ ๊ฐœ๋…์„ ์ง์ ‘ ์ฝ”๋”ฉํ•ด๋ณด๊ณ , ๋ณ€ํ˜•ํ•ด๋ณด๋ฉด์„œ ์™„์ „ํžˆ ๋‚ด ๊ฒƒ์œผ๋กœ ๋งŒ๋“ค์ž!
๊ธฐ์ดˆ ๋ฌธ๋ฒ• ๋ณ€์ˆ˜, ์ œ์–ด๋ฌธ OOP ํด๋ž˜์Šค, ์ƒ์† ์ปฌ๋ ‰์…˜ List, Dictionary ์˜ˆ์™ธ์ฒ˜๋ฆฌ try-catch ๊ธฐ์ดˆ ํ•™์Šต ํ๋ฆ„๋„ 1 2 3 4 1-2์ฃผ 3-6์ฃผ 7-9์ฃผ 10-12์ฃผ

๐Ÿš€ 2๋‹จ๊ณ„: ์ค‘๊ธ‰์œผ๋กœ ๋„์•ฝ (3-6๊ฐœ์›”)

๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ ๋‹ค์กŒ๋‹ค๋ฉด, ์ด์ œ **์‹ค๋ฌด์—์„œ ์ •๋ง ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ค‘๊ธ‰ ๊ฐœ๋…๋“ค**์„ ๋ฐฐ์šธ ์ฐจ๋ก€์•ผ. ์ด ๋‹จ๊ณ„๋ถ€ํ„ฐ๋Š” ๋‹จ์ˆœํžˆ ๋ฌธ๋ฒ•์„ ์•„๋Š” ๊ฒƒ์„ ๋„˜์–ด์„œ, "์™œ ์ด๋ ‡๊ฒŒ ์ฝ”๋”ฉํ•ด์•ผ ํ•˜๋Š”๊ฐ€"๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด.

๐Ÿ”ง LINQ (Language Integrated Query)

LINQ๋Š” C#์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์•ผ. ๋ฐ์ดํ„ฐ๋ฅผ ์ฟผ๋ฆฌํ•˜๊ณ  ์กฐ์ž‘ํ•˜๋Š” ๊ฑธ ์—„์ฒญ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ค˜. SQL์„ ์•Œ๊ณ  ์žˆ๋‹ค๋ฉด ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ.

LINQ์˜ ๋‘ ๊ฐ€์ง€ ๋ฌธ๋ฒ•

์ฟผ๋ฆฌ ๊ตฌ๋ฌธ(Query Syntax)๊ณผ ๋ฉ”์„œ๋“œ ๊ตฌ๋ฌธ(Method Syntax)์ด ์žˆ์–ด. ๋‘˜ ๋‹ค ์•Œ์•„๋‘๋ฉด ์ข‹์ง€๋งŒ, ์‹ค๋ฌด์—์„œ๋Š” ๋ฉ”์„œ๋“œ ๊ตฌ๋ฌธ์„ ๋” ๋งŽ์ด ์‚ฌ์šฉํ•ด.

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// ์ฟผ๋ฆฌ ๊ตฌ๋ฌธ
var evenQuery = from num in numbers
                where num % 2 == 0
                select num;

// ๋ฉ”์„œ๋“œ ๊ตฌ๋ฌธ (๋” ๋งŽ์ด ์‚ฌ์šฉ๋จ)
var evenMethod = numbers.Where(num => num % 2 == 0);

// ๋ณต์žกํ•œ ์˜ˆ์ œ
var result = numbers
    .Where(n => n > 3)           // 3๋ณด๋‹ค ํฐ ์ˆ˜
    .Select(n => n * 2)          // 2๋ฐฐ๋กœ ๋งŒ๋“ค๊ธฐ
    .OrderByDescending(n => n)   // ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ
    .Take(3);                    // ์ƒ์œ„ 3๊ฐœ๋งŒ

foreach (var num in result)
{
    Console.WriteLine(num);  // 20, 18, 16
}

๐ŸŽฏ LINQ ํ•ต์‹ฌ ๋ฉ”์„œ๋“œ๋“ค

Where ํ•„ํ„ฐ๋ง
Select ๋ณ€ํ™˜
OrderBy/OrderByDescending ์ •๋ ฌ
GroupBy ๊ทธ๋ฃนํ™”
Join ์กฐ์ธ
First/FirstOrDefault ์ฒซ ๋ฒˆ์งธ ์š”์†Œ
Any/All ์กฐ๊ฑด ํ™•์ธ
Count/Sum/Average ์ง‘๊ณ„

// ์‹ค์ „ ์˜ˆ์ œ: ํ•™์ƒ ์„ฑ์  ์ฒ˜๋ฆฌ
public class Student
{
    public string Name { get; set; }
    public int Score { get; set; }
    public string Department { get; set; }
}

List<Student> students = new List<Student>
{
    new Student { Name = "๊น€์ฒ ์ˆ˜", Score = 85, Department = "์ปด๊ณต" },
    new Student { Name = "์ด์˜ํฌ", Score = 92, Department = "์ „์ž" },
    new Student { Name = "๋ฐ•๋ฏผ์ˆ˜", Score = 78, Department = "์ปด๊ณต" },
    new Student { Name = "์ •์ˆ˜์ง„", Score = 95, Department = "์ปด๊ณต" }
};

// ์ปด๊ณต๊ณผ ํ•™์ƒ ์ค‘ 80์  ์ด์ƒ์ธ ํ•™์ƒ๋“ค์˜ ํ‰๊ท 
var avgScore = students
    .Where(s => s.Department == "์ปด๊ณต" && s.Score >= 80)
    .Average(s => s.Score);

Console.WriteLine($"ํ‰๊ท : {avgScore}");  // 90

// ํ•™๊ณผ๋ณ„ ํ‰๊ท  ์ ์ˆ˜
var deptAvg = students
    .GroupBy(s => s.Department)
    .Select(g => new 
    { 
        Department = g.Key, 
        Average = g.Average(s => s.Score) 
    });

foreach (var dept in deptAvg)
{
    Console.WriteLine($"{dept.Department}: {dept.Average}");
}
๐Ÿ’ก ์‹ค์ „ ํŒ: LINQ๋Š” ์ฒ˜์Œ์—” ์–ด๋ ต๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ์ต์ˆ™ํ•ด์ง€๋ฉด ์ฝ”๋“œ๊ฐ€ ์—„์ฒญ ๊ฐ„๊ฒฐํ•ด์ง€๊ณ  ๊ฐ€๋…์„ฑ๋„ ์ข‹์•„์ ธ. ๋งค์ผ ์กฐ๊ธˆ์”ฉ ์—ฐ์Šตํ•˜๋ฉด์„œ ์†์— ์ตํžˆ๋Š” ๊ฒŒ ์ตœ๊ณ ์•ผ!

โšก ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ (async/await)

ํ˜„๋Œ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ํ•„์ˆ˜์•ผ. ํŠนํžˆ ์›น API ํ˜ธ์ถœ, ํŒŒ์ผ I/O, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—… ๋“ฑ์—์„œ ๋ฐ˜๋“œ์‹œ ์‚ฌ์šฉํ•ด์•ผ ํ•ด.

์™œ ๋น„๋™๊ธฐ๊ฐ€ ํ•„์š”ํ• ๊นŒ? ๐Ÿค”

๋™๊ธฐ ๋ฐฉ์‹์€ ํ•œ ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๋‹ค์Œ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ด. ๋งŒ์•ฝ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด 5์ดˆ ๊ฑธ๋ฆฐ๋‹ค๋ฉด? ๊ทธ 5์ดˆ ๋™์•ˆ ํ”„๋กœ๊ทธ๋žจ์ด ๋ฉˆ์ถฐ์žˆ๋Š” ๊ฑฐ์ง€. ๋น„๋™๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์–ด!

// ๋™๊ธฐ ๋ฐฉ์‹ (๋‚˜์œ ์˜ˆ)
public void DownloadFile()
{
    // ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ์ค‘... (5์ดˆ ์†Œ์š”)
    // ์ด ๋™์•ˆ ํ”„๋กœ๊ทธ๋žจ์ด ๋ฉˆ์ถค!
    Thread.Sleep(5000);
    Console.WriteLine("๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ");
}

// ๋น„๋™๊ธฐ ๋ฐฉ์‹ (์ข‹์€ ์˜ˆ)
public async Task DownloadFileAsync()
{
    // ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ์ค‘... (5์ดˆ ์†Œ์š”)
    // ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ์ž‘์—… ๊ฐ€๋Šฅ!
    await Task.Delay(5000);
    Console.WriteLine("๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ");
}

// ์‚ฌ์šฉ
await DownloadFileAsync();
Console.WriteLine("๋‹ค๋ฅธ ์ž‘์—… ์ง„ํ–‰ ์ค‘...");

async/await์˜ ํ•ต์‹ฌ ๊ทœ์น™ ๐Ÿ“‹

1. async ๋ฉ”์„œ๋“œ๋Š” Task ๋˜๋Š” Task<T>๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ด
2. await๋Š” async ๋ฉ”์„œ๋“œ ์•ˆ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
3. ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ๋์— Async๋ฅผ ๋ถ™์ด๋Š” ๊ฒŒ ๊ด€๋ก€
4. async void๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ๋งŒ ์‚ฌ์šฉ

// ์‹ค์ „ ์˜ˆ์ œ: HTTP ์š”์ฒญ
public class ApiService
{
    private readonly HttpClient _httpClient = new HttpClient();
    
    // ๋น„๋™๊ธฐ๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
    public async Task<string> GetDataAsync(string url)
    {
        try
        {
            // await๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ธ”๋ก๋˜์ง€ ์•Š์Œ
            HttpResponseMessage response = await _httpClient.GetAsync(url);
            response.EnsureSuccessStatusCode();
            
            string content = await response.Content.ReadAsStringAsync();
            return content;
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"์—๋Ÿฌ ๋ฐœ์ƒ: {e.Message}");
            return null;
        }
    }
    
    // ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌ
    public async Task<List<string>> GetMultipleDataAsync(List<string> urls)
    {
        // Task.WhenAll์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ชจ๋“  ์š”์ฒญ์„ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌ
        var tasks = urls.Select(url => GetDataAsync(url));
        var results = await Task.WhenAll(tasks);
        
        return results.ToList();
    }
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
var service = new ApiService();
var data = await service.GetDataAsync("https://api.example.com/data");
Console.WriteLine(data);
โš ๏ธ ํ”ํ•œ ์‹ค์ˆ˜: async void๋ฅผ ์ผ๋ฐ˜ ๋ฉ”์„œ๋“œ์— ์‚ฌ์šฉํ•˜๋ฉด ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ์ ธ. ํ•ญ์ƒ async Task๋ฅผ ์‚ฌ์šฉํ•˜์ž! ๊ทธ๋ฆฌ๊ณ  await ์—†์ด async๋งŒ ๋ถ™์ด๋ฉด ๊ฒฝ๊ณ ๊ฐ€ ๋‚˜์˜ค๋Š”๋ฐ, ์ด๊ฑด ์ œ๋Œ€๋กœ ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋œป์ด์•ผ.

๐ŸŽญ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ์™€ ์ด๋ฒคํŠธ

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

๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๊ธฐ์ดˆ

๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋Š” ํ•จ์ˆ˜ ํฌ์ธํ„ฐ์˜ ์•ˆ์ „ํ•œ ๋ฒ„์ „์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ. C#์—์„œ๋Š” Func, Action, Predicate ๊ฐ™์€ ๋‚ด์žฅ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ด.

// ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์„ ์–ธ
public delegate int Calculate(int x, int y);

// ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์‚ฌ์šฉ
public class Calculator
{
    public int Add(int x, int y) => x + y;
    public int Subtract(int x, int y) => x - y;
    public int Multiply(int x, int y) => x * y;
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
Calculator calc = new Calculator();
Calculate operation;

operation = calc.Add;
Console.WriteLine(operation(5, 3));  // 8

operation = calc.Multiply;
Console.WriteLine(operation(5, 3));  // 15

// ๋‚ด์žฅ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์‚ฌ์šฉ (๋” ๊ฐ„๋‹จ!)
Func<int, int, int> add = (x, y) => x + y;
Action<string> print = message => Console.WriteLine(message);
Predicate<int> isEven = num => num % 2 == 0;

Console.WriteLine(add(10, 20));      // 30
print("์•ˆ๋…•ํ•˜์„ธ์š”!");                  // ์•ˆ๋…•ํ•˜์„ธ์š”!
Console.WriteLine(isEven(4));        // True

์ด๋ฒคํŠธ ํŒจํ„ด ๐ŸŽช

์ด๋ฒคํŠธ๋Š” ๋ฐœํ–‰-๊ตฌ๋…(Publish-Subscribe) ํŒจํ„ด์„ ๊ตฌํ˜„ํ•ด. ํ•œ ๊ฐ์ฒด๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด, ๊ตฌ๋…ํ•œ ๋‹ค๋ฅธ ๊ฐ์ฒด๋“ค์ด ์•Œ๋ฆผ์„ ๋ฐ›๋Š” ๊ฑฐ์ง€.

// ์ด๋ฒคํŠธ ๋ฐœํ–‰์ž
public class Button
{
    // ์ด๋ฒคํŠธ ์„ ์–ธ
    public event EventHandler Clicked;
    
    // ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋ฉ”์„œ๋“œ
    public void Click()
    {
        Console.WriteLine("๋ฒ„ํŠผ์ด ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!");
        
        // ์ด๋ฒคํŠธ ๋ฐœ์ƒ (๊ตฌ๋…์ž๋“ค์—๊ฒŒ ์•Œ๋ฆผ)
        Clicked?.Invoke(this, EventArgs.Empty);
    }
}

// ์ด๋ฒคํŠธ ๊ตฌ๋…์ž
public class Logger
{
    public void OnButtonClicked(object sender, EventArgs e)
    {
        Console.WriteLine("๋กœ๊ทธ: ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ ๊ฐ์ง€๋จ");
    }
}

// ์‚ฌ์šฉ
Button button = new Button();
Logger logger = new Logger();

// ์ด๋ฒคํŠธ ๊ตฌ๋…
button.Clicked += logger.OnButtonClicked;

// ๋žŒ๋‹ค์‹์œผ๋กœ๋„ ๊ตฌ๋… ๊ฐ€๋Šฅ
button.Clicked += (sender, e) => 
{
    Console.WriteLine("์ถ”๊ฐ€ ์ฒ˜๋ฆฌ: ํด๋ฆญ ํšŸ์ˆ˜ ์ฆ๊ฐ€");
};

// ๋ฒ„ํŠผ ํด๋ฆญ
button.Click();
// ์ถœ๋ ฅ:
// ๋ฒ„ํŠผ์ด ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
// ๋กœ๊ทธ: ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ ๊ฐ์ง€๋จ
// ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ: ํด๋ฆญ ํšŸ์ˆ˜ ์ฆ๊ฐ€

๐Ÿ—„๏ธ ํŒŒ์ผ I/O์™€ ์ง๋ ฌํ™”

๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ผ์— ์ €์žฅํ•˜๊ณ  ์ฝ์–ด์˜ค๋Š” ๊ฑด ์‹ค๋ฌด์—์„œ ์ •๋ง ์ž์ฃผ ํ•˜๋Š” ์ž‘์—…์ด์•ผ. JSON, XML ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๊ฒƒ๋„ ๋ฐฐ์›Œ์•ผ ํ•ด.

ํŒŒ์ผ ์ฝ๊ธฐ/์“ฐ๊ธฐ

// ํ…์ŠคํŠธ ํŒŒ์ผ ์“ฐ๊ธฐ
File.WriteAllText("test.txt", "์•ˆ๋…•ํ•˜์„ธ์š”");

// ํ…์ŠคํŠธ ํŒŒ์ผ ์ฝ๊ธฐ
string content = File.ReadAllText("test.txt");

// ์—ฌ๋Ÿฌ ์ค„ ์“ฐ๊ธฐ
string[] lines = { "์ฒซ ์ค„", "๋‘˜์งธ ์ค„", "์…‹์งธ ์ค„" };
File.WriteAllLines("lines.txt", lines);

JSON ์ง๋ ฌํ™”

using System.Text.Json;

// ๊ฐ์ฒด๋ฅผ JSON์œผ๋กœ
var person = new { Name = "๊น€์ฒ ์ˆ˜", Age = 25 };
string json = JsonSerializer.Serialize(person);

// JSON์„ ๊ฐ์ฒด๋กœ
var restored = JsonSerializer.Deserialize<Person>(json);
// ์‹ค์ „ ์˜ˆ์ œ: ์„ค์ • ํŒŒ์ผ ๊ด€๋ฆฌ
public class AppSettings
{
    public string Theme { get; set; }
    public int FontSize { get; set; }
    public bool AutoSave { get; set; }
}

public class SettingsManager
{
    private const string SettingsFile = "settings.json";
    
    // ์„ค์ • ์ €์žฅ
    public async Task SaveSettingsAsync(AppSettings settings)
    {
        var options = new JsonSerializerOptions 
        { 
            WriteIndented = true  // ๋ณด๊ธฐ ์ข‹๊ฒŒ ํฌ๋งทํŒ…
        };
        
        string json = JsonSerializer.Serialize(settings, options);
        await File.WriteAllTextAsync(SettingsFile, json);
    }
    
    // ์„ค์ • ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
    public async Task<AppSettings> LoadSettingsAsync()
    {
        if (!File.Exists(SettingsFile))
        {
            return new AppSettings 
            { 
                Theme = "Light", 
                FontSize = 12, 
                AutoSave = true 
            };
        }
        
        string json = await File.ReadAllTextAsync(SettingsFile);
        return JsonSerializer.Deserialize<AppSettings>(json);
    }
}

// ์‚ฌ์šฉ
var manager = new SettingsManager();
var settings = await manager.LoadSettingsAsync();
settings.Theme = "Dark";
await manager.SaveSettingsAsync(settings);

๐ŸŽ“ 2๋‹จ๊ณ„ ํ•™์Šต ์ „๋žต

๐Ÿ“š ์ถ”์ฒœ ํ•™์Šต ๋ฐฉ๋ฒ•

โ€ข ํ”„๋กœ์ ํŠธ ๊ธฐ๋ฐ˜ ํ•™์Šต: ๊ฐ„๋‹จํ•œ ์ฝ˜์†” ์•ฑ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ ์  ๋ณต์žกํ•œ ํ”„๋กœ์ ํŠธ๋กœ
โ€ข ์ฝ”๋“œ ๋ฆฌ๋ทฐ: GitHub์—์„œ ์˜คํ”ˆ์†Œ์Šค C# ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ ์ฝ์–ด๋ณด๊ธฐ
โ€ข ๋ฌธ์ œ ํ•ด๊ฒฐ: LeetCode, ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค์—์„œ C#๋กœ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฌธ์ œ ํ’€๊ธฐ
โ€ข ๋ฉ˜ํ† ๋ง: ์žฌ๋Šฅ๋„ท์—์„œ ๊ฒฝํ—˜ ๋งŽ์€ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋ฐ›๊ธฐ
โ€ข ์ปค๋ฎค๋‹ˆํ‹ฐ: C# ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์ฐธ์—ฌํ•ด์„œ ์งˆ๋ฌธํ•˜๊ณ  ๋‹ต๋ณ€ํ•˜๊ธฐ

๐Ÿ’ก ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด: 1. ํ•  ์ผ ๊ด€๋ฆฌ ์•ฑ (CRUD ์—ฐ์Šต)
2. ๊ฐ„๋‹จํ•œ ์ฑ„ํŒ… ํ”„๋กœ๊ทธ๋žจ (๋„คํŠธ์›Œํ‚น, ๋น„๋™๊ธฐ)
3. ํŒŒ์ผ ๊ด€๋ฆฌ ๋„๊ตฌ (ํŒŒ์ผ I/O, LINQ)
4. ๋‚ ์”จ ์ •๋ณด ์•ฑ (API ํ˜ธ์ถœ, JSON ํŒŒ์‹ฑ)
5. ๊ฐ„๋‹จํ•œ ๊ฒŒ์ž„ (์ด๋ฒคํŠธ, ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ)

๐Ÿ’ผ 3๋‹จ๊ณ„: ์‹ค์ „ ํ”„๋กœ์ ํŠธ (6-12๊ฐœ์›”)

์ด์ œ ์ง„์งœ ๊ฐœ๋ฐœ์ž์ฒ˜๋Ÿผ ์ผํ•  ์ค€๋น„๋ฅผ ํ•ด์•ผ ํ•ด! ์ด ๋‹จ๊ณ„์—์„œ๋Š” **์‹ค๋ฌด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋„๊ตฌ๋“ค**์„ ๋ฐฐ์šฐ๊ณ , ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ ๊ฑฐ์•ผ.

๐ŸŒ ASP.NET Core ์›น ๊ฐœ๋ฐœ

C# ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์˜ ํ•ต์‹ฌ์ด์•ผ. ASP.NET Core๋Š” ํฌ๋กœ์Šค ํ”Œ๋žซํผ์„ ์ง€์›ํ•˜๊ณ , ์„ฑ๋Šฅ๋„ ๋›ฐ์–ด๋‚˜์„œ ๋งŽ์€ ๊ธฐ์—…์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด.

ASP.NET Core์˜ ์ฃผ์š” ๊ฐœ๋…

โ€ข MVC ํŒจํ„ด: Model-View-Controller ์•„ํ‚คํ…์ฒ˜
โ€ข Web API: RESTful API ๊ฐœ๋ฐœ
โ€ข Middleware: ์š”์ฒญ/์‘๋‹ต ํŒŒ์ดํ”„๋ผ์ธ
โ€ข Dependency Injection: ์˜์กด์„ฑ ์ฃผ์ž…
โ€ข Entity Framework Core: ORM (๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…)

// ๊ฐ„๋‹จํ•œ Web API ์ปจํŠธ๋กค๋Ÿฌ
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;
    
    // ์˜์กด์„ฑ ์ฃผ์ž…
    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }
    
    // GET: api/products
    [HttpGet]
    public async Task<ActionResult<List<Product>>> GetProducts()
    {
        var products = await _productService.GetAllProductsAsync();
        return Ok(products);
    }
    
    // GET: api/products/5
    [HttpGet("{id}")]
    public async Task<ActionResult<Product>> GetProduct(int id)
    {
        var product = await _productService.GetProductByIdAsync(id);
        
        if (product == null)
        {
            return NotFound();
        }
        
        return Ok(product);
    }
    
    // POST: api/products
    [HttpPost]
    public async Task<ActionResult<Product>> CreateProduct(Product product)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        
        var created = await _productService.CreateProductAsync(product);
        return CreatedAtAction(nameof(GetProduct), new { id = created.Id }, created);
    }
    
    // PUT: api/products/5
    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateProduct(int id, Product product)
    {
        if (id != product.Id)
        {
            return BadRequest();
        }
        
        await _productService.UpdateProductAsync(product);
        return NoContent();
    }
    
    // DELETE: api/products/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProduct(int id)
    {
        await _productService.DeleteProductAsync(id);
        return NoContent();
    }
}

Entity Framework Core๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‹ค๋ฃจ๊ธฐ ๐Ÿ—„๏ธ

EF Core๋Š” C#์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ORM์ด์•ผ. SQL์„ ์ง์ ‘ ์“ฐ์ง€ ์•Š๊ณ ๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์–ด.

// DbContext ์ •์˜
public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) 
        : base(options)
    {
    }
    
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // ๊ด€๊ณ„ ์„ค์ •
        modelBuilder.Entity<Product>()
            .HasOne(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => p.CategoryId);
        
        // ์ธ๋ฑ์Šค ์„ค์ •
        modelBuilder.Entity<Product>()
            .HasIndex(p => p.Name);
    }
}

// ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
    
    // ๋„ค๋น„๊ฒŒ์ด์…˜ ํ”„๋กœํผํ‹ฐ
    public Category Category { get; set; }
}

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    // ์ปฌ๋ ‰์…˜ ๋„ค๋น„๊ฒŒ์ด์…˜ ํ”„๋กœํผํ‹ฐ
    public List<Product> Products { get; set; }
}

// ์„œ๋น„์Šค ๋ ˆ์ด์–ด
public class ProductService : IProductService
{
    private readonly AppDbContext _context;
    
    public ProductService(AppDbContext context)
    {
        _context = context;
    }
    
    public async Task<List<Product>> GetAllProductsAsync()
    {
        return await _context.Products
            .Include(p => p.Category)  // ๊ด€๋ จ ๋ฐ์ดํ„ฐ ํฌํ•จ
            .ToListAsync();
    }
    
    public async Task<Product> GetProductByIdAsync(int id)
    {
        return await _context.Products
            .Include(p => p.Category)
            .FirstOrDefaultAsync(p => p.Id == id);
    }
    
    public async Task<Product> CreateProductAsync(Product product)
    {
        _context.Products.Add(product);
        await _context.SaveChangesAsync();
        return product;
    }
    
    public async Task UpdateProductAsync(Product product)
    {
        _context.Entry(product).State = EntityState.Modified;
        await _context.SaveChangesAsync();
    }
    
    public async Task DeleteProductAsync(int id)
    {
        var product = await _context.Products.FindAsync(id);
        if (product != null)
        {
            _context.Products.Remove(product);
            await _context.SaveChangesAsync();
        }
    }
}
๐Ÿ’ก ์‹ค์ „ ํŒ: EF Core๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ญ์ƒ ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ž. ToListAsync(), FirstOrDefaultAsync() ๊ฐ™์€ ๊ฑฐ ๋ง์ด์•ผ. ๊ทธ๋ฆฌ๊ณ  Include()๋ฅผ ๋‚จ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ, ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ค๋„๋ก ์ฃผ์˜ํ•ด์•ผ ํ•ด!

๐ŸŽฎ Unity๋กœ ๊ฒŒ์ž„ ๊ฐœ๋ฐœ

C#์„ ๋ฐฐ์šฐ๋Š” ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ๊ฒŒ์ž„ ๊ฐœ๋ฐœ์— ๊ด€์‹ฌ์ด ์žˆ์–ด. Unity๋Š” C#์„ ์Šคํฌ๋ฆฝํŒ… ์–ธ์–ด๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ๊ฒŒ์ž„ ์—”์ง„์ด์•ผ.

Unity C# ์Šคํฌ๋ฆฝํŒ… ๊ธฐ์ดˆ

Unity์—์„œ๋Š” MonoBehaviour๋ฅผ ์ƒ์†๋ฐ›์•„ ๊ฒŒ์ž„ ์˜ค๋ธŒ์ ํŠธ์˜ ๋™์ž‘์„ ์ œ์–ดํ•ด. Start(), Update() ๊ฐ™์€ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•ด์„œ ์‚ฌ์šฉํ•˜์ง€.

using UnityEngine;

// ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ ์˜ˆ์ œ
public class PlayerController : MonoBehaviour
{
    // Inspector์—์„œ ์„ค์ • ๊ฐ€๋Šฅํ•œ ๋ณ€์ˆ˜
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float jumpForce = 10f;
    
    private Rigidbody2D rb;
    private bool isGrounded;
    
    // ์ดˆ๊ธฐํ™”
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }
    
    // ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ
    void Update()
    {
        // ์ž…๋ ฅ ์ฒ˜๋ฆฌ
        float horizontal = Input.GetAxis("Horizontal");
        Move(horizontal);
        
        // ์ ํ”„
        if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
        {
            Jump();
        }
    }
    
    void Move(float direction)
    {
        // ์ด๋™
        Vector2 movement = new Vector2(direction * moveSpeed, rb.velocity.y);
        rb.velocity = movement;
        
        // ์บ๋ฆญํ„ฐ ๋ฐฉํ–ฅ ์ „ํ™˜
        if (direction != 0)
        {
            transform.localScale = new Vector3(
                Mathf.Sign(direction), 
                1, 
                1
            );
        }
    }
    
    void Jump()
    {
        rb.velocity = new Vector2(rb.velocity.x, jumpForce);
        isGrounded = false;
    }
    
    // ์ถฉ๋Œ ๊ฐ์ง€
    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            isGrounded = true;
        }
    }
}

// ์  AI ์˜ˆ์ œ
public class EnemyAI : MonoBehaviour
{
    [SerializeField] private Transform player;
    [SerializeField] private float detectionRange = 5f;
    [SerializeField] private float attackRange = 1.5f;
    [SerializeField] private float moveSpeed = 3f;
    
    private enum State { Idle, Chase, Attack }
    private State currentState = State.Idle;
    
    void Update()
    {
        float distanceToPlayer = Vector2.Distance(transform.position, player.position);
        
        // ์ƒํƒœ ์ „ํ™˜
        switch (currentState)
        {
            case State.Idle:
                if (distanceToPlayer < detectionRange)
                {
                    currentState = State.Chase;
                }
                break;
                
            case State.Chase:
                if (distanceToPlayer < attackRange)
                {
                    currentState = State.Attack;
                }
                else if (distanceToPlayer > detectionRange)
                {
                    currentState = State.Idle;
                }
                else
                {
                    ChasePlayer();
                }
                break;
                
            case State.Attack:
                if (distanceToPlayer > attackRange)
                {
                    currentState = State.Chase;
                }
                else
                {
                    AttackPlayer();
                }
                break;
        }
    }
    
    void ChasePlayer()
    {
        Vector2 direction = (player.position - transform.position).normalized;
        transform.position = Vector2.MoveTowards(
            transform.position, 
            player.position, 
            moveSpeed * Time.deltaTime
        );
    }
    
    void AttackPlayer()
    {
        // ๊ณต๊ฒฉ ๋กœ์ง
        Debug.Log("ํ”Œ๋ ˆ์ด์–ด ๊ณต๊ฒฉ!");
    }
}

๐Ÿ—๏ธ ๋””์ž์ธ ํŒจํ„ด๊ณผ ์•„ํ‚คํ…์ฒ˜

์ฝ”๋“œ๋ฅผ ์ž˜ ์งœ๋Š” ๊ฒƒ๋งŒํผ ์ค‘์š”ํ•œ ๊ฒŒ **์ฝ”๋“œ๋ฅผ ์ž˜ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ**์ด์•ผ. ๋””์ž์ธ ํŒจํ„ด์€ ๋ฐ˜๋ณต๋˜๋Š” ๋ฌธ์ œ์— ๋Œ€ํ•œ ๊ฒ€์ฆ๋œ ํ•ด๊ฒฐ์ฑ…์ด์ง€.

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด

ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•˜๋‚˜๋งŒ ์กด์žฌํ•˜๋„๋ก ๋ณด์žฅ

public class GameManager
{
    private static GameManager _instance;
    
    public static GameManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameManager();
            }
            return _instance;
        }
    }
    
    private GameManager() { }
}

ํŒฉํ† ๋ฆฌ ํŒจํ„ด

๊ฐ์ฒด ์ƒ์„ฑ ๋กœ์ง์„ ์บก์Аํ™”

public interface IEnemy
{
    void Attack();
}

public class EnemyFactory
{
    public IEnemy CreateEnemy(string type)
    {
        return type switch
        {
            "Zombie" => new Zombie(),
            "Skeleton" => new Skeleton(),
            _ => throw new ArgumentException()
        };
    }
}

์˜ต์ €๋ฒ„ ํŒจํ„ด

์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ํ†ต์‹  (C# ์ด๋ฒคํŠธ๋กœ ๊ตฌํ˜„)

public class Player
{
    public event Action<int> OnHealthChanged;
    
    private int health;
    public int Health
    {
        get => health;
        set
        {
            health = value;
            OnHealthChanged?.Invoke(health);
        }
    }
}

์ „๋žต ํŒจํ„ด

์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์บก์Аํ™”ํ•˜๊ณ  ๊ต์ฒด ๊ฐ€๋Šฅํ•˜๊ฒŒ

public interface IAttackStrategy
{
    void Attack();
}

public class Character
{
    private IAttackStrategy _attackStrategy;
    
    public void SetAttackStrategy(IAttackStrategy strategy)
    {
        _attackStrategy = strategy;
    }
    
    public void PerformAttack()
    {
        _attackStrategy?.Attack();
    }
}

๐Ÿ›๏ธ ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜ ์›์น™

SOLID ์›์น™:
โ€ข Single Responsibility: ํ•œ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ
โ€ข Open/Closed: ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ๊ณ  ์ˆ˜์ •์—๋Š” ๋‹ซํ˜€์žˆ๊ฒŒ
โ€ข Liskov Substitution: ์ž์‹ ํด๋ž˜์Šค๋Š” ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ๋Œ€์ฒด ๊ฐ€๋Šฅ
โ€ข Interface Segregation: ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ž‘๊ฒŒ ๋ถ„๋ฆฌ
โ€ข Dependency Inversion: ๊ตฌ์ฒด์ ์ธ ๊ฒƒ์ด ์•„๋‹Œ ์ถ”์ƒ์— ์˜์กด

// SOLID ์›์น™ ์ ์šฉ ์˜ˆ์ œ

// 1. Single Responsibility
// ๋‚˜์œ ์˜ˆ: ํ•œ ํด๋ž˜์Šค๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์€ ์ผ์„ ํ•จ
public class User
{
    public void SaveToDatabase() { }
    public void SendEmail() { }
    public void GenerateReport() { }
}

// ์ข‹์€ ์˜ˆ: ์ฑ…์ž„์„ ๋ถ„๋ฆฌ
public class User
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public class UserRepository
{
    public void Save(User user) { }
}

public class EmailService
{
    public void SendEmail(User user) { }
}

// 2. Dependency Inversion
// ๋‚˜์œ ์˜ˆ: ๊ตฌ์ฒด์ ์ธ ํด๋ž˜์Šค์— ์˜์กด
public class OrderService
{
    private SqlDatabase database = new SqlDatabase();
    
    public void SaveOrder(Order order)
    {
        database.Save(order);
    }
}

// ์ข‹์€ ์˜ˆ: ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กด
public interface IDatabase
{
    void Save(Order order);
}

public class OrderService
{
    private readonly IDatabase _database;
    
    public OrderService(IDatabase database)
    {
        _database = database;
    }
    
    public void SaveOrder(Order order)
    {
        _database.Save(order);
    }
}

// ์ด์ œ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌํ˜„์ฒด๋ฅผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
public class SqlDatabase : IDatabase
{
    public void Save(Order order) { /* SQL ์ €์žฅ */ }
}

public class MongoDatabase : IDatabase
{
    public void Save(Order order) { /* MongoDB ์ €์žฅ */ }
}

๐Ÿงช ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ (TDD)

์ข‹์€ ๊ฐœ๋ฐœ์ž๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด. ๋ฒ„๊ทธ๋ฅผ ๋ฏธ๋ฆฌ ์žก๊ณ , ๋ฆฌํŒฉํ† ๋ง์„ ์•ˆ์ „ํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ . C#์—์„œ๋Š” ์ฃผ๋กœ xUnit, NUnit, MSTest๋ฅผ ์‚ฌ์šฉํ•ด.

๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑํ•˜๊ธฐ

xUnit์„ ์‚ฌ์šฉํ•œ ๊ธฐ๋ณธ์ ์ธ ํ…Œ์ŠคํŠธ ์˜ˆ์ œ๋ฅผ ๋ณด์ž.

// ํ…Œ์ŠคํŠธํ•  ํด๋ž˜์Šค
public class Calculator
{
    public int Add(int a, int b) => a + b;
    public int Subtract(int a, int b) => a - b;
    public int Divide(int a, int b)
    {
        if (b == 0)
            throw new DivideByZeroException();
        return a / b;
    }
}

// ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค
public class CalculatorTests
{
    private readonly Calculator _calculator;
    
    public CalculatorTests()
    {
        _calculator = new Calculator();
    }
    
    [Fact]
    public void Add_TwoNumbers_ReturnsSum()
    {
        // Arrange (์ค€๋น„)
        int a = 5;
        int b = 3;
        
        // Act (์‹คํ–‰)
        int result = _calculator.Add(a, b);
        
        // Assert (๊ฒ€์ฆ)
        Assert.Equal(8, result);
    }
    
    [Theory]
    [InlineData(10, 5, 5)]
    [InlineData(20, 8, 12)]
    [InlineData(0, 5, -5)]
    public void Subtract_VariousNumbers_ReturnsCorrectResult(int a, int b, int expected)
    {
        // Act
        int result = _calculator.Subtract(a, b);
        
        // Assert
        Assert.Equal(expected, result);
    }
    
    [Fact]
    public void Divide_ByZero_ThrowsException()
    {
        // Arrange
        int a = 10;
        int b = 0;
        
        // Act & Assert
        Assert.Throws<DivideByZeroException>(() => _calculator.Divide(a, b));
    }
}

// ๋ชจํ‚น์„ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ (Moq ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)
public interface IUserRepository
{
    User GetById(int id);
    void Save(User user);
}

public class UserService
{
    private readonly IUserRepository _repository;
    
    public UserService(IUserRepository repository)
    {
        _repository = repository;
    }
    
    public bool UpdateUserEmail(int userId, string newEmail)
    {
        var user = _repository.GetById(userId);
        if (user == null)
            return false;
            
        user.Email = newEmail;
        _repository.Save(user);
        return true;
    }
}

// ํ…Œ์ŠคํŠธ
public class UserServiceTests
{
    [Fact]
    public void UpdateUserEmail_ValidUser_ReturnsTrue()
    {
        // Arrange
        var mockRepo = new Mock<IUserRepository>();
        var user = new User { Id = 1, Email = "old@email.com" };
        
        mockRepo.Setup(r => r.GetById(1)).Returns(user);
        mockRepo.Setup(r => r.Save(It.IsAny<User>())).Verifiable();
        
        var service = new UserService(mockRepo.Object);
        
        // Act
        bool result = service.UpdateUserEmail(1, "new@email.com");
        
        // Assert
        Assert.True(result);
        Assert.Equal("new@email.com", user.Email);
        mockRepo.Verify(r => r.Save(user), Times.Once);
    }
}
๐Ÿ’ก TDD ์‚ฌ์ดํด: Red(์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ž‘์„ฑ) โ†’ Green(ํ…Œ์ŠคํŠธ ํ†ต๊ณผํ•˜๋Š” ์ตœ์†Œํ•œ์˜ ์ฝ”๋“œ) โ†’ Refactor(์ฝ”๋“œ ๊ฐœ์„ ). ์ด ์‚ฌ์ดํด์„ ๋ฐ˜๋ณตํ•˜๋ฉด์„œ ๊ฐœ๋ฐœํ•˜๋ฉด ํ’ˆ์งˆ ์ข‹์€ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด!

๐ŸŽ“ 3๋‹จ๊ณ„ ํ•™์Šต ์ „๋žต

๐Ÿš€ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ์ถ”์ฒœ

1. ๋ธ”๋กœ๊ทธ ํ”Œ๋žซํผ
ASP.NET Core MVC + EF Core + SQL Server
๊ธฐ๋Šฅ: ํšŒ์›๊ฐ€์ž…/๋กœ๊ทธ์ธ, ๊ธ€ ์ž‘์„ฑ/์ˆ˜์ •/์‚ญ์ œ, ๋Œ“๊ธ€, ๊ฒ€์ƒ‰

2. RESTful API ์„œ๋ฒ„
ASP.NET Core Web API + JWT ์ธ์ฆ
๊ธฐ๋Šฅ: ์‚ฌ์šฉ์ž ์ธ์ฆ, CRUD ์ž‘์—…, ํŽ˜์ด์ง•, ํ•„ํ„ฐ๋ง

3. ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… ์•ฑ
SignalR + ASP.NET Core
๊ธฐ๋Šฅ: ์‹ค์‹œ๊ฐ„ ๋ฉ”์‹œ์ง•, ์ฑ„ํŒ…๋ฐฉ, ์˜จ๋ผ์ธ ์ƒํƒœ

4. 2D ํ”Œ๋žซํฌ๋จธ ๊ฒŒ์ž„
Unity + C#
๊ธฐ๋Šฅ: ํ”Œ๋ ˆ์ด์–ด ์ด๋™, ์  AI, ์•„์ดํ…œ ์ˆ˜์ง‘, ์ ์ˆ˜ ์‹œ์Šคํ…œ

5. ์ž‘์—… ๊ด€๋ฆฌ ์‹œ์Šคํ…œ
Blazor + EF Core
๊ธฐ๋Šฅ: ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ, ํƒœ์Šคํฌ ํ• ๋‹น, ์ง„ํ–‰ ์ƒํ™ฉ ์ถ”์ 

์‹ค์ „ ๊ฐœ๋ฐœ์ž ์Šคํ‚ฌ ํŠธ๋ฆฌ C# ๊ธฐ์ดˆ ๋ฌธ๋ฒ•, OOP ์ค‘๊ธ‰ ๊ฐœ๋… LINQ, async ์›น ๊ฐœ๋ฐœ ASP.NET Core ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค EF Core, SQL ํ…Œ์ŠคํŠธ xUnit, TDD ๋””์ž์ธ ํŒจํ„ด SOLID ํด๋ผ์šฐ๋“œ Azure, AWS ๊ฒŒ์ž„ ๊ฐœ๋ฐœ Unity DevOps CI/CD, Docker ์‹œ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ์ „๋ฌธ๊ฐ€ ๊ธฐ์ˆ  ๋ฆฌ๋”์‹ญ

๐ŸŒŸ 4๋‹จ๊ณ„: ์ „๋ฌธ๊ฐ€๋กœ ์„ฑ์žฅ (12๊ฐœ์›” ์ด์ƒ)

์ถ•ํ•˜ํ•ด! ์—ฌ๊ธฐ๊นŒ์ง€ ์™”๋‹ค๋ฉด ์ด๋ฏธ ์‹ค๋ ฅ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž์•ผ. ์ด์ œ๋Š” **๋” ๊นŠ์ด ์žˆ๋Š” ์ง€์‹๊ณผ ๋ฆฌ๋”์‹ญ**์„ ํ‚ค์šธ ์ฐจ๋ก€์•ผ.

โ˜๏ธ ํด๋ผ์šฐ๋“œ์™€ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค

ํ˜„๋Œ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋Œ€๋ถ€๋ถ„ ํด๋ผ์šฐ๋“œ์—์„œ ์‹คํ–‰๋ผ. ํŠนํžˆ C#์€ Microsoft Azure์™€ ๊ถํ•ฉ์ด ์ •๋ง ์ข‹์•„.

Azure ์„œ๋น„์Šค

โ€ข App Service: ์›น ์•ฑ ํ˜ธ์ŠคํŒ…
โ€ข Functions: ์„œ๋ฒ„๋ฆฌ์Šค ์ปดํ“จํŒ…
โ€ข SQL Database: ๊ด€๋ฆฌํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
โ€ข Blob Storage: ํŒŒ์ผ ์ €์žฅ์†Œ
โ€ข Service Bus: ๋ฉ”์‹œ์ง•

๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค

โ€ข ์„œ๋น„์Šค ๋ถ„๋ฆฌ ๋ฐ ๋…๋ฆฝ ๋ฐฐํฌ
โ€ข API Gateway ํŒจํ„ด
โ€ข ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹  (gRPC, REST)
โ€ข ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜
โ€ข ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ

// Azure Functions ์˜ˆ์ œ
public static class HttpTriggerFunction
{
    [FunctionName("ProcessOrder")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] 
        HttpRequest req,
        [Queue("orders")] IAsyncCollector<Order> orderQueue,
        ILogger log)
    {
        log.LogInformation("์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ์‹œ์ž‘");
        
        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        var order = JsonSerializer.Deserialize<Order>(requestBody);
        
        // ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
        if (order == null || string.IsNullOrEmpty(order.ProductId))
        {
            return new BadRequestObjectResult("์ž˜๋ชป๋œ ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ");
        }
        
        // ํ์— ์ถ”๊ฐ€ (๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ)
        await orderQueue.AddAsync(order);
        
        return new OkObjectResult(new { message = "์ฃผ๋ฌธ์ด ์ ‘์ˆ˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค", orderId = order.Id });
    }
}

// ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹  (gRPC)
// Proto ํŒŒ์ผ ์ •์˜
/*
syntax = "proto3";

service ProductService {
  rpc GetProduct (ProductRequest) returns (ProductResponse);
}

message ProductRequest {
  string product_id = 1;
}

message ProductResponse {
  string product_id = 1;
  string name = 2;
  double price = 3;
}
*/

// gRPC ์„œ๋ฒ„ ๊ตฌํ˜„
public class ProductService : ProductServiceBase
{
    public override async Task<ProductResponse> GetProduct(
        ProductRequest request, 
        ServerCallContext context)
    {
        // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ œํ’ˆ ์กฐํšŒ
        var product = await _repository.GetByIdAsync(request.ProductId);
        
        return new ProductResponse
        {
            ProductId = product.Id,
            Name = product.Name,
            Price = product.Price
        };
    }
}

// gRPC ํด๋ผ์ด์–ธํŠธ ์‚ฌ์šฉ
public class OrderService
{
    private readonly ProductService.ProductServiceClient _productClient;
    
    public async Task<Order> CreateOrderAsync(string productId, int quantity)
    {
        // ๋‹ค๋ฅธ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ํ˜ธ์ถœ
        var product = await _productClient.GetProductAsync(
            new ProductRequest { ProductId = productId }
        );
        
        var order = new Order
        {
            ProductId = productId,
            ProductName = product.Name,
            Quantity = quantity,
            TotalPrice = product.Price * quantity
        };
        
        return order;
    }
}

๐Ÿ”’ ๋ณด์•ˆ๊ณผ ์ธ์ฆ

๋ณด์•ˆ์€ ์ ˆ๋Œ€ ์†Œํ™€ํžˆ ํ•ด์„œ๋Š” ์•ˆ ๋ผ. ํŠนํžˆ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๋”์šฑ ์ค‘์š”ํ•ด.

JWT ์ธ์ฆ ๊ตฌํ˜„

JWT(JSON Web Token)๋Š” ํ˜„๋Œ€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ธ์ฆ ๋ฐฉ์‹์ด์•ผ.

// JWT ํ† ํฐ ์ƒ์„ฑ
public class JwtTokenService
{
    private readonly IConfiguration _configuration;
    
    public JwtTokenService(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    
    public string GenerateToken(User user)
    {
        var securityKey = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])
        );
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
        
        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.Username),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(ClaimTypes.Role, user.Role)
        };
        
        var token = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],
            audience: _configuration["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddHours(24),
            signingCredentials: credentials
        );
        
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
    
    public ClaimsPrincipal ValidateToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]);
        
        try
        {
            var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = true,
                ValidIssuer = _configuration["Jwt:Issuer"],
                ValidateAudience = true,
                ValidAudience = _configuration["Jwt:Audience"],
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);
            
            return principal;
        }
        catch
        {
            return null;
        }
    }
}

// ์ธ์ฆ ์ปจํŠธ๋กค๋Ÿฌ
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly IUserService _userService;
    private readonly JwtTokenService _tokenService;
    
    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginRequest request)
    {
        // ์‚ฌ์šฉ์ž ์ธ์ฆ
        var user = await _userService.AuthenticateAsync(request.Username, request.Password);
        
        if (user == null)
        {
            return Unauthorized(new { message = "์ž˜๋ชป๋œ ์‚ฌ์šฉ์ž๋ช… ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ" });
        }
        
        // JWT ํ† ํฐ ์ƒ์„ฑ
        var token = _tokenService.GenerateToken(user);
        
        return Ok(new 
        { 
            token,
            user = new 
            {
                user.Id,
                user.Username,
                user.Email
            }
        });
    }
    
    [HttpPost("register")]
    public async Task<IActionResult> Register([FromBody] RegisterRequest request)
    {
        // ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ (์ ˆ๋Œ€ ํ‰๋ฌธ์œผ๋กœ ์ €์žฅํ•˜์ง€ ๋ง ๊ฒƒ!)
        var hashedPassword = BCrypt.Net.BCrypt.HashPassword(request.Password);
        
        var user = new User
        {
            Username = request.Username,
            Email = request.Email,
            PasswordHash = hashedPassword,
            Role = "User"
        };
        
        await _userService.CreateAsync(user);
        
        return Ok(new { message = "ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต" });
    }
    
    [Authorize]  // JWT ์ธ์ฆ ํ•„์š”
    [HttpGet("profile")]
    public async Task<IActionResult> GetProfile()
    {
        // ํ˜„์žฌ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
        var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)?.Value);
        var user = await _userService.GetByIdAsync(userId);
        
        return Ok(user);
    }
}
  • โš ๏ธ ๋ณด์•ˆ ์ฒดํฌ๋ฆฌ์ŠคํŠธ:
  • โœ… ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ํ•ญ์ƒ ํ•ด์‹ฑํ•ด์„œ ์ €์žฅ (BCrypt, Argon2)
  • โœ… SQL Injection ๋ฐฉ์ง€ (ํŒŒ๋ผ๋ฏธํ„ฐํ™”๋œ ์ฟผ๋ฆฌ ์‚ฌ์šฉ)
  • โœ… XSS ๊ณต๊ฒฉ ๋ฐฉ์ง€ (์ž…๋ ฅ ๊ฒ€์ฆ ๋ฐ ์ด์Šค์ผ€์ดํ•‘)
  • โœ… CSRF ํ† ํฐ ์‚ฌ์šฉ
  • โœ… HTTPS ์‚ฌ์šฉ (๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก)
  • โœ… ์ ์ ˆํ•œ ๊ถŒํ•œ ๊ด€๋ฆฌ (Role-based Access Control)
  • โœ… ๋กœ๊น… ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง

โš™๏ธ ์„ฑ๋Šฅ ์ตœ์ ํ™”

์ฝ”๋“œ๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, **๋น ๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ**๋„ ์ค‘์š”ํ•ด. ํŠนํžˆ ๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋•Œ๋Š” ๋”์šฑ ๊ทธ๋ž˜.

1๏ธโƒฃ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ตœ์ ํ™”

// ๋‚˜์œ ์˜ˆ: N+1 ๋ฌธ์ œ
public async Task<List<OrderDto>> GetOrdersAsync()
{
    var orders = await _context.Orders.ToListAsync();
    
    foreach (var order in orders)
    {
        // ๊ฐ ์ฃผ๋ฌธ๋งˆ๋‹ค ๋ณ„๋„์˜ ์ฟผ๋ฆฌ ์‹คํ–‰! (N+1 ๋ฌธ์ œ)
        order.Customer = await _context.Customers.FindAsync(order.CustomerId);
        order.Items = await _context.OrderItems
            .Where(i => i.OrderId == order.Id)
            .ToListAsync();
    }
    
    return orders;
}

// ์ข‹์€ ์˜ˆ: Eager Loading
public async Task<List<OrderDto>> GetOrdersAsync()
{
    var orders = await _context.Orders
        .Include(o => o.Customer)      // ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋กœ๋“œ
        .Include(o => o.Items)
            .ThenInclude(i => i.Product)
        .ToListAsync();
    
    return orders;
}

// ๋” ๋‚˜์€ ์˜ˆ: ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์„ ํƒ
public async Task<List<OrderDto>> GetOrdersAsync()
{
    var orders = await _context.Orders
        .Include(o => o.Customer)
        .Include(o => o.Items)
        .Select(o => new OrderDto
        {
            Id = o.Id,
            OrderDate = o.OrderDate,
            CustomerName = o.Customer.Name,
            TotalAmount = o.Items.Sum(i => i.Price * i.Quantity),
            ItemCount = o.Items.Count
        })
        .ToListAsync();
    
    return orders;
}

2๏ธโƒฃ ์บ์‹ฑ ์ „๋žต

// ๋ฉ”๋ชจ๋ฆฌ ์บ์‹ฑ
public class ProductService
{
    private readonly IMemoryCache _cache;
    private readonly IProductRepository _repository;
    
    public async Task<Product> GetProductAsync(int id)
    {
        // ์บ์‹œ์—์„œ ๋จผ์ € ํ™•์ธ
        if (_cache.TryGetValue($"product_{id}", out Product product))
        {
            return product;
        }
        
        // ์บ์‹œ์— ์—†์œผ๋ฉด DB์—์„œ ์กฐํšŒ
        product = await _repository.GetByIdAsync(id);
        
        // ์บ์‹œ์— ์ €์žฅ (10๋ถ„ ๋™์•ˆ ์œ ํšจ)
        var cacheOptions = new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromMinutes(10));
        
        _cache.Set($"product_{id}", product, cacheOptions);
        
        return product;
    }
}

// Redis ๋ถ„์‚ฐ ์บ์‹ฑ
public class DistributedCacheService
{
    private readonly IDistributedCache _cache;
    
    public async Task<T> GetOrCreateAsync<T>(
        string key, 
        Func<Task<T>> factory,
        TimeSpan? expiration = null)
    {
        // ์บ์‹œ์—์„œ ์กฐํšŒ
        var cached = await _cache.GetStringAsync(key);
        
        if (!string.IsNullOrEmpty(cached))
        {
            return JsonSerializer.Deserialize<T>(cached);
        }
        
        // ์บ์‹œ ๋ฏธ์Šค: ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
        var data = await factory();
        
        // ์บ์‹œ์— ์ €์žฅ
        var options = new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = expiration ?? TimeSpan.FromMinutes(30)
        };
        
        await _cache.SetStringAsync(
            key, 
            JsonSerializer.Serialize(data), 
            options
        );
        
        return data;
    }
}

3๏ธโƒฃ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์™€ ๋ณ‘๋ ฌํ™”

// ์ˆœ์ฐจ ์ฒ˜๋ฆฌ (๋А๋ฆผ)
public async Task<List<Report>> GenerateReportsAsync(List<int> userIds)
{
    var reports = new List<Report>();
    
    foreach (var userId in userIds)
    {
        var report = await GenerateUserReportAsync(userId);  // ํ•˜๋‚˜์”ฉ ์ฒ˜๋ฆฌ
        reports.Add(report);
    }
    
    return reports;
}

// ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ (๋น ๋ฆ„)
public async Task<List<Report>> GenerateReportsAsync(List<int> userIds)
{
    var tasks = userIds.Select(userId => GenerateUserReportAsync(userId));
    var reports = await Task.WhenAll(tasks);  // ๋™์‹œ์— ์ฒ˜๋ฆฌ
    
    return reports.ToList();
}

// ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ + ์ œํ•œ (๋ฆฌ์†Œ์Šค ๋ณดํ˜ธ)
public async Task<List<Report>> GenerateReportsAsync(List<int> userIds)
{
    var semaphore = new SemaphoreSlim(5);  // ์ตœ๋Œ€ 5๊ฐœ๊นŒ์ง€๋งŒ ๋™์‹œ ์‹คํ–‰
    var reports = new List<Report>();
    
    var tasks = userIds.Select(async userId =>
    {
        await semaphore.WaitAsync();
        try
        {
            return await GenerateUserReportAsync(userId);
        }
        finally
        {
            semaphore.Release();
        }
    });
    
    return (await Task.WhenAll(tasks)).ToList();
}

๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊น…

ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๋Š”์ง€ ์•Œ์•„์•ผ ํ•ด. ๋กœ๊น…๊ณผ ๋ชจ๋‹ˆํ„ฐ๋ง์€ ํ•„์ˆ˜์•ผ!

// Serilog๋ฅผ ์‚ฌ์šฉํ•œ ๊ตฌ์กฐํ™”๋œ ๋กœ๊น…
public class OrderService
{
    private readonly ILogger<OrderService> _logger;
    
    public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
    {
        _logger.LogInformation(
            "์ฃผ๋ฌธ ์ƒ์„ฑ ์‹œ์ž‘: {@Request}", 
            request
        );
        
        try
        {
            var order = new Order
            {
                CustomerId = request.CustomerId,
                Items = request.Items,
                CreatedAt = DateTime.UtcNow
            };
            
            await _repository.AddAsync(order);
            
            _logger.LogInformation(
                "์ฃผ๋ฌธ ์ƒ์„ฑ ์™„๋ฃŒ: OrderId={OrderId}, CustomerId={CustomerId}, Amount={Amount}",
                order.Id,
                order.CustomerId,
                order.TotalAmount
            );
            
            return order;
        }
        catch (Exception ex)
        {
            _logger.LogError(
                ex,
                "์ฃผ๋ฌธ ์ƒ์„ฑ ์‹คํŒจ: CustomerId={CustomerId}",
                request.CustomerId
            );
            throw;
        }
    }
}

// Application Insights๋ฅผ ์‚ฌ์šฉํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง
public class PerformanceMonitoringMiddleware
{
    private readonly RequestDelegate _next;
    private readonly TelemetryClient _telemetry;
    
    public async Task InvokeAsync(HttpContext context)
    {
        var sw = Stopwatch.StartNew();
        
        try
        {
            await _next(context);
        }
        finally
        {
            sw.Stop();
            
            // ์„ฑ๋Šฅ ๋ฉ”ํŠธ๋ฆญ ๊ธฐ๋ก
            _telemetry.TrackMetric(
                "RequestDuration",
                sw.ElapsedMilliseconds,
                new Dictionary<string, string>
                {
                    { "Path", context.Request.Path },
                    { "Method", context.Request.Method },
                    { "StatusCode", context.Response.StatusCode.ToString() }
                }
            );
            
            // ๋А๋ฆฐ ์š”์ฒญ ๊ฒฝ๊ณ 
            if (sw.ElapsedMilliseconds > 1000)
            {
                _telemetry.TrackTrace(
                    $"๋А๋ฆฐ ์š”์ฒญ ๊ฐ์ง€: {context.Request.Path} ({sw.ElapsedMilliseconds}ms)",
                    SeverityLevel.Warning
                );
            }
        }
    }
}

๐ŸŽฏ ํ•™์Šต ๋กœ๋“œ๋งต ์ด์ •๋ฆฌ

๐Ÿ“… ๋‹จ๊ณ„๋ณ„ ํ•™์Šต ๊ธฐ๊ฐ„ (์ฐธ๊ณ ์šฉ)

1๋‹จ๊ณ„ (๊ธฐ์ดˆ): 2-3๊ฐœ์›”
โ€ข ํ•˜๋ฃจ 2-3์‹œ๊ฐ„ ํ•™์Šต
โ€ข ๋งค์ผ ์ฝ”๋”ฉ ์—ฐ์Šต
โ€ข ์ž‘์€ ํ”„๋กœ์ ํŠธ 3-5๊ฐœ

2๋‹จ๊ณ„ (์ค‘๊ธ‰): 3-4๊ฐœ์›”
โ€ข ํ•˜๋ฃจ 3-4์‹œ๊ฐ„ ํ•™์Šต
โ€ข ์ค‘๊ฐ„ ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ 2-3๊ฐœ
โ€ข ์˜คํ”ˆ์†Œ์Šค ๊ธฐ์—ฌ ์‹œ์ž‘

3๋‹จ๊ณ„ (์‹ค์ „): 6-8๊ฐœ์›”
โ€ข ์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜
โ€ข ํŒ€ ํ˜‘์—… ๊ฒฝํ—˜
โ€ข ํฌํŠธํด๋ฆฌ์˜ค ๊ตฌ์ถ•

4๋‹จ๊ณ„ (์ „๋ฌธ๊ฐ€): ์ง€์†์  ํ•™์Šต
โ€ข ์ƒˆ๋กœ์šด ๊ธฐ์ˆ  ์Šต๋“
โ€ข ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ๊ฒฝํ—˜
โ€ข ๋ฉ˜ํ† ๋ง ๋ฐ ์ง€์‹ ๊ณต์œ 

๐Ÿ’ก ์„ฑ๊ณต์ ์ธ ํ•™์Šต์„ ์œ„ํ•œ ํŒ

๐ŸŽฏ ๋ชฉํ‘œ ์„ค์ •

๋ง‰์—ฐํ•˜๊ฒŒ "C#์„ ๋ฐฐ์šฐ๊ฒ ๋‹ค"๊ฐ€ ์•„๋‹ˆ๋ผ, "3๊ฐœ์›” ์•ˆ์— ๊ฐ„๋‹จํ•œ ์›น API๋ฅผ ๋งŒ๋“ค๊ฒ ๋‹ค" ๊ฐ™์€ ๊ตฌ์ฒด์ ์ธ ๋ชฉํ‘œ๋ฅผ ์„ธ์›Œ.

๐Ÿ“ ๋งค์ผ ์ฝ”๋”ฉ

ํ•˜๋ฃจ 30๋ถ„์ด๋ผ๋„ ์ข‹์œผ๋‹ˆ ๋งค์ผ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด. ์ผ์ฃผ์ผ์— ํ•œ ๋ฒˆ 5์‹œ๊ฐ„ ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋งค์ผ 30๋ถ„์ด ํ›จ์”ฌ ํšจ๊ณผ์ ์ด์•ผ.

๐Ÿค ์ปค๋ฎค๋‹ˆํ‹ฐ ์ฐธ์—ฌ

ํ˜ผ์ž ๊ณต๋ถ€ํ•˜๋ฉด ํž˜๋“ค์–ด. ์˜จ๋ผ์ธ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์ฐธ์—ฌํ•˜๊ณ , ์งˆ๋ฌธํ•˜๊ณ , ๋‹ค๋ฅธ ์‚ฌ๋žŒ์„ ๋„์™€์ฃผ๋ฉด์„œ ํ•จ๊ป˜ ์„ฑ์žฅํ•˜์ž.

๐Ÿ” ์ฝ”๋“œ ๋ฆฌ๋ทฐ

๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ฝ”๋“œ๋ฅผ ์ฝ๊ณ , ๋‚ด ์ฝ”๋“œ๋ฅผ ๋ฆฌ๋ทฐ๋ฐ›์•„. GitHub์—์„œ ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋ฅผ ๋ณด๋Š” ๊ฒƒ๋„ ํฐ ๋„์›€์ด ๋ผ.

๐Ÿ“š ๋ฌธ์„œ ์ฝ๊ธฐ

๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฝ๋Š” ์Šต๊ด€์„ ๋“ค์—ฌ. Microsoft Docs๋Š” ์ •๋ง ์ž˜ ๋˜์–ด ์žˆ์–ด. ์˜์–ด๊ฐ€ ๋ถ€๋‹ด์Šค๋Ÿฝ๋‹ค๋ฉด ํ•œ๊ธ€ ๋ฒˆ์—ญ๋„ ์žˆ์–ด!

๐ŸŽ“ ๋ฉ˜ํ†  ์ฐพ๊ธฐ

๊ฒฝํ—˜ ๋งŽ์€ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์กฐ์–ธ์„ ๊ตฌํ•ด. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๋ฉ˜ํ† ๋ฅผ ์ฐพ์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ.

โš ๏ธ ํ”ผํ•ด์•ผ ํ•  ์‹ค์ˆ˜๋“ค

1. ํŠœํ† ๋ฆฌ์–ผ ์ง€์˜ฅ ๊ฐ•์˜๋งŒ ๊ณ„์† ๋“ฃ๊ณ  ์‹ค์ œ๋กœ ์ฝ”๋”ฉ์„ ์•ˆ ํ•˜๋Š” ๊ฑฐ. ๋ฐฐ์šด ๊ฑด ๋ฐ”๋กœ๋ฐ”๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ด์•ผ ํ•ด!

2. ์™„๋ฒฝ์ฃผ์˜ ์ฒ˜์Œ๋ถ€ํ„ฐ ์™„๋ฒฝํ•œ ์ฝ”๋“œ๋ฅผ ์“ฐ๋ ค๊ณ  ํ•˜์ง€ ๋งˆ. ์ผ๋‹จ ๋™์ž‘ํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ , ๊ทธ ๋‹ค์Œ์— ๊ฐœ์„ ํ•ด.

3. ๋„ˆ๋ฌด ๋นจ๋ฆฌ ๋„˜์–ด๊ฐ€๊ธฐ ๊ธฐ์ดˆ๋ฅผ ๋Œ€์ถฉ ํ•˜๊ณ  ๊ณ ๊ธ‰ ๋‚ด์šฉ์œผ๋กœ ๋„˜์–ด๊ฐ€๋ฉด ๋‚˜์ค‘์— ๋ฐ˜๋“œ์‹œ ๋ง‰ํ˜€. ๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!

4. ํ˜ผ์ž๋งŒ ๊ณต๋ถ€ํ•˜๊ธฐ ๊ฐœ๋ฐœ์€ ํ˜‘์—…์ด์•ผ. ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค๊ณผ ์†Œํ†ตํ•˜๊ณ , ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ•˜๊ณ , ํ”ผ๋“œ๋ฐฑ์„ ์ฃผ๊ณ ๋ฐ›์•„.

๐ŸŒ ์ถ”์ฒœ ํ•™์Šต ๋ฆฌ์†Œ์Šค

๐Ÿ“– ๊ณต์‹ ๋ฌธ์„œ ๋ฐ ํŠœํ† ๋ฆฌ์–ผ

โ€ข Microsoft Learn - C# ๊ณต์‹ ํ•™์Šต ๊ฒฝ๋กœ
โ€ข C# ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ฐ€์ด๋“œ (docs.microsoft.com)
โ€ข .NET API ๋ธŒ๋ผ์šฐ์ €
โ€ข ASP.NET Core ๋ฌธ์„œ

๐Ÿ“š ์ถ”์ฒœ ๋„์„œ

โ€ข "C# 9.0 in a Nutshell" - Joseph Albahari
โ€ข "ํด๋ฆฐ ์ฝ”๋“œ" - ๋กœ๋ฒ„ํŠธ C. ๋งˆํ‹ด
โ€ข "์ดํŽ™ํ‹ฐ๋ธŒ C#" - ๋นŒ ์™€๊ทธ๋„ˆ
โ€ข "Head First Design Patterns" - ์—๋ฆญ ํ”„๋ฆฌ๋จผ

๐ŸŽฅ ์˜จ๋ผ์ธ ๊ฐ•์˜

โ€ข Pluralsight - C# ์ „๋ฌธ ๊ฐ•์˜
โ€ข Udemy - ์ž…๋ฌธ๋ถ€ํ„ฐ ์‹ค์ „๊นŒ์ง€
โ€ข YouTube - ์ฝ”๋”ฉ์• ํ”Œ, ๋“œ๋ฆผ์ฝ”๋”ฉ
โ€ข ์ธํ”„๋Ÿฐ - ํ•œ๊ธ€ ๊ฐ•์˜

๐Ÿ’ป ์‹ค์Šต ํ”Œ๋žซํผ

โ€ข LeetCode - ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฌธ์ œ
โ€ข ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค - ์ฝ”๋”ฉ ํ…Œ์ŠคํŠธ
โ€ข Exercism - ๋ฉ˜ํ† ๋ง ํฌํ•จ
โ€ข HackerRank - ๋‹ค์–‘ํ•œ ๋ฌธ์ œ

๐Ÿค ์ปค๋ฎค๋‹ˆํ‹ฐ

โ€ข Stack Overflow - ์งˆ๋ฌธ/๋‹ต๋ณ€
โ€ข Reddit r/csharp - ์˜์–ด ์ปค๋ฎค๋‹ˆํ‹ฐ
โ€ข OKKY - ํ•œ๊ตญ ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ
โ€ข ์žฌ๋Šฅ๋„ท - ๋ฉ˜ํ† ๋ง ๋ฐ ์ง€์‹ ๊ณต์œ 
โ€ข GitHub - ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ

๐Ÿš€ ๋งˆ์น˜๋ฉฐ

์™€, ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์—ˆ๋‹ค๋ฉด ์ •๋ง ๋Œ€๋‹จํ•ด! ๐Ÿ‘ ์ด ๊ธด ๊ธ€์„ ๋๊นŒ์ง€ ์ฝ์—ˆ๋‹ค๋Š” ๊ฑด C# ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ณ  ์‹ถ์€ ์ง„์‹ฌ์ด ์žˆ๋‹ค๋Š” ๊ฑฐ์ž–์•„?

์†”์งํžˆ ๋งํ• ๊ฒŒ. **๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๋Š” ๊ธธ์€ ์‰ฝ์ง€ ์•Š์•„.** ๋ฐฐ์›Œ์•ผ ํ•  ๊ฒƒ๋„ ๋งŽ๊ณ , ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„๋„ ๋งŽ์„ ๊ฑฐ์•ผ. ๋•Œ๋กœ๋Š” "๋‚˜๋Š” ๊ฐœ๋ฐœ์ž ์žฌ๋Šฅ์ด ์—†๋‚˜?"๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค ์ˆ˜๋„ ์žˆ์–ด. ํ•˜์ง€๋งŒ ๊ทธ๊ฑด ๋ชจ๋“  ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฒช๋Š” ๊ณผ์ •์ด์•ผ. ๋‚˜๋„ ๊ทธ๋žฌ๊ณ , ์ง€๊ธˆ ์‹œ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž๋“ค๋„ ๋‹ค ๊ทธ๋žฌ์–ด.

์ค‘์š”ํ•œ ๊ฑด ํฌ๊ธฐํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด์•ผ. ๋งค์ผ ์กฐ๊ธˆ์”ฉ์ด๋ผ๋„ ์•ž์œผ๋กœ ๋‚˜์•„๊ฐ€๋ฉด, ์–ด๋А์ƒˆ "์–ด? ๋‚˜ ์ด๊ฑฐ ํ•  ์ˆ˜ ์žˆ๋„ค?"๋ผ๋Š” ์ˆœ๊ฐ„์ด ์™€. ๊ทธ ์ˆœ๊ฐ„์˜ ํฌ์—ด์€ ์ •๋ง ๋Œ€๋‹จํ•ด. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋Ÿฐ ์ˆœ๊ฐ„๋“ค์ด ์Œ“์ด๋ฉด์„œ ์ง„์งœ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๋Š” ๊ฑฐ์ง€.

๐ŸŽฏ ๊ธฐ์–ตํ•ด์•ผ ํ•  ๊ฒƒ๋“ค

1. ๊ธฐ์ดˆ๊ฐ€ ๊ฐ€์žฅ ์ค‘์š”ํ•ด. ํ™”๋ คํ•œ ๊ธฐ์ˆ ๋ณด๋‹ค ํƒ„ํƒ„ํ•œ ๊ธฐ๋ณธ๊ธฐ๊ฐ€ ๋จผ์ €์•ผ.

2. ๋งค์ผ ์ฝ”๋”ฉํ•˜์ž. ํ•˜๋ฃจ 30๋ถ„์ด๋ผ๋„ ์ข‹์œผ๋‹ˆ ๊พธ์ค€ํžˆ ํ•˜๋Š” ๊ฒŒ ํ•ต์‹ฌ์ด์•ผ.

3. ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ด. ์ด๋ก ๋งŒ ๊ณต๋ถ€ํ•˜์ง€ ๋ง๊ณ  ์‹ค์ œ๋กœ ๋ญ”๊ฐ€๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

4. ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์ฐธ์—ฌํ•ด. ํ˜ผ์ž ํ•˜๋ฉด ํž˜๋“ค์–ด. ํ•จ๊ป˜ํ•˜๋ฉด ๋” ๋ฉ€๋ฆฌ ๊ฐˆ ์ˆ˜ ์žˆ์–ด.

5. ์‹ค์ˆ˜๋ฅผ ๋‘๋ ค์›Œํ•˜์ง€ ๋งˆ. ์—๋Ÿฌ๋Š” ๋ฐฐ์›€์˜ ๊ธฐํšŒ์•ผ. ๋งŽ์ด ์‹ค์ˆ˜ํ•˜๊ณ  ๋งŽ์ด ๋ฐฐ์šฐ์ž.

6. ๋ฉ˜ํ† ๋ฅผ ์ฐพ์•„๋ด. ๊ฒฝํ—˜ ๋งŽ์€ ๊ฐœ๋ฐœ์ž์˜ ์กฐ์–ธ์€ ์ •๋ง ํฐ ๋„์›€์ด ๋ผ. ์žฌ๋Šฅ๋„ท์—์„œ ์ข‹์€ ๋ฉ˜ํ† ๋ฅผ ์ฐพ์•„๋ณด๋Š” ๊ฒƒ๋„ ์ถ”์ฒœํ•ด!

๋งˆ์ง€๋ง‰์œผ๋กœ ํ•˜๊ณ  ์‹ถ์€ ๋ง์€, **๋‹น์‹ ์€ ํ•  ์ˆ˜ ์žˆ์–ด!** ์ด ๊ธ€์„ ์ฝ๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ์ด๋ฏธ ์ข‹์€ ์‹œ์ž‘์ด์•ผ. ์ง€๊ธˆ ๋‹น์žฅ Visual Studio๋ฅผ ์ผœ๊ณ , ์ฒซ ๋ฒˆ์งธ "Hello, World!"๋ฅผ ์ถœ๋ ฅํ•ด๋ด. ๊ทธ๊ฒŒ ๋ฐ”๋กœ C# ๋งˆ์Šคํ„ฐ๋กœ ๊ฐ€๋Š” ์ฒซ ๊ฑธ์Œ์ด์•ผ.

๊ฐœ๋ฐœ ๊ณต๋ถ€ํ•˜๋‹ค๊ฐ€ ๋ง‰ํžˆ๊ฑฐ๋‚˜ ๊ถ๊ธˆํ•œ ๊ฒŒ ์žˆ์œผ๋ฉด ์–ธ์ œ๋“  ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์งˆ๋ฌธํ•˜๊ณ , ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค๊ณผ ์†Œํ†ตํ•ด. ์šฐ๋ฆฌ ๋ชจ๋‘ ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฑฐ์•ผ. ํ™”์ดํŒ…! ๐Ÿ’ช

๐ŸŒŸ ๋‹น์‹ ์˜ ๊ฐœ๋ฐœ์ž ์—ฌ์ •์„ ์‘์›ํ•ฉ๋‹ˆ๋‹ค! ๐ŸŒŸ

์งˆ๋ฌธ์ด๋‚˜ ์กฐ์–ธ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์—์„œ ๊ฒฝํ—˜ ๋งŽ์€ ๋ฉ˜ํ† ๋“ค์„ ๋งŒ๋‚˜๋ณด์„ธ์š”.
ํ•จ๊ป˜ ๋ฐฐ์šฐ๊ณ  ์„ฑ์žฅํ•˜๋Š” ์ฆ๊ฑฐ์›€์„ ๋А๊ปด๋ณด์„ธ์š”!

Happy Coding! ๐ŸŽ‰

C#๊ณผ ํ•จ๊ป˜ํ•˜๋Š” ๋ฉ‹์ง„ ๊ฐœ๋ฐœ์ž ์ธ์ƒ์„ ์‹œ์ž‘ํ•˜์„ธ์š”!

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

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