์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ—๏ธ C++ ์•„ํ‚คํ…์ฒ˜์™€ ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ ์„ค๊ณ„ ๋ชจ๋“ˆํ™” ์ „๋žต

๐Ÿ—๏ธ C++ ์•„ํ‚คํ…์ฒ˜์™€ ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ ์„ค๊ณ„ ๋ชจ๋“ˆํ™” ์ „๋žต

๋ณต์žกํ•œ ์‹œ์Šคํ…œ์„ ์šฐ์•„ํ•˜๊ฒŒ ์„ค๊ณ„ํ•˜๋Š” ์‹ค์ „ ๊ฐ€์ด๋“œ

์•ˆ๋…•! ๐Ÿ‘‹ ์˜ค๋Š˜์€ C++ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ˆ„๊ตฌ๋‚˜ ํ•œ ๋ฒˆ์ฏค ๊ณ ๋ฏผํ•˜๊ฒŒ ๋˜๋Š” ์ฃผ์ œ, ๋ฐ”๋กœ ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ ์„ค๊ณ„์™€ ๋ชจ๋“ˆํ™” ์ „๋žต์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณด๋ ค๊ณ  ํ•ด. ์ž‘์€ ํ”„๋กœ์ ํŠธ๋Š” ๊ทธ๋ƒฅ ๋ง‰ ์งœ๋„ ๋˜๋Š”๋ฐ, ์ฝ”๋“œ๊ฐ€ ์ˆ˜๋งŒ ์ค„์„ ๋„˜์–ด๊ฐ€๊ณ  ํŒ€์›์ด ๋Š˜์–ด๋‚˜๋ฉด ๊ฐ‘์ž๊ธฐ ๋ชจ๋“  ๊ฒŒ ๋ณต์žกํ•ด์ง€์ž–์•„? ๐Ÿ˜…

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

C++ ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜ ๊ณ„์ธต ๊ตฌ์กฐ Application Layer (์‘์šฉ ๊ณ„์ธต) Business Logic User Interface Service Layer (์„œ๋น„์Šค ๊ณ„์ธต) Module A Module B Module C Infrastructure Layer (์ธํ”„๋ผ ๊ณ„์ธต)
๐ŸŽจ ์™œ ๋ชจ๋“ˆํ™”๊ฐ€ ์ค‘์š”ํ•œ๊ฐ€?

๋ณธ๊ฒฉ์ ์œผ๋กœ ๋“ค์–ด๊ฐ€๊ธฐ ์ „์—, ์™œ ๋ชจ๋“ˆํ™”๊ฐ€ ์ด๋ ‡๊ฒŒ ์ค‘์š”ํ•œ์ง€๋ถ€ํ„ฐ ์ด์•ผ๊ธฐํ•ด๋ณผ๊นŒ? ๐Ÿค”

์ƒ์ƒํ•ด๋ด. ๋„ˆ๊ฐ€ 10๋งŒ ์ค„์งœ๋ฆฌ C++ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ํ•œ ํŒŒ์ผ์— ๋‹ค ๋•Œ๋ ค๋ฐ•์•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ด. ๋”์ฐํ•˜์ง€? ๐Ÿ˜ฑ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์€ ์—„์ฒญ๋‚˜๊ฒŒ ๊ธธ์–ด์ง€๊ณ , ํ•œ ์ค„๋งŒ ์ˆ˜์ •ํ•ด๋„ ์ „์ฒด๋ฅผ ๋‹ค์‹œ ๋นŒ๋“œํ•ด์•ผ ํ•˜๊ณ , ์–ด๋””์„œ ๋ฒ„๊ทธ๊ฐ€ ๋‚ฌ๋Š”์ง€ ์ฐพ๊ธฐ๋„ ํž˜๋“ค์–ด. ๊ฒŒ๋‹ค๊ฐ€ ์—ฌ๋Ÿฌ ๋ช…์ด ๋™์‹œ์— ์ž‘์—…ํ•˜๋ฉด? ์ถฉ๋Œ์˜ ์—ฐ์†์ด์•ผ.

๐ŸŽฏ ๋ชจ๋“ˆํ™”์˜ ํ•ต์‹ฌ ์ด์ 

1. ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ - ๊ฐ ๋ชจ๋“ˆ์ด ๋…๋ฆฝ์ ์ด๋ผ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ํ•ด๋‹น ๋ชจ๋“ˆ๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋ผ
2. ์žฌ์‚ฌ์šฉ์„ฑ ์ฆ๊ฐ€ - ์ž˜ ์„ค๊ณ„๋œ ๋ชจ๋“ˆ์€ ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ์—์„œ๋„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด
3. ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ - ํŒ€์›๋“ค์ด ๊ฐ์ž ๋‹ค๋ฅธ ๋ชจ๋“ˆ์„ ๋™์‹œ์— ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์–ด
4. ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ๋‹จ์ถ• - ๋ณ€๊ฒฝ๋œ ๋ชจ๋“ˆ๋งŒ ์žฌ์ปดํŒŒ์ผํ•˜๋ฉด ๋˜๋‹ˆ๊นŒ ๋นŒ๋“œ๊ฐ€ ๋นจ๋ผ์ ธ
5. ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ - ๋ชจ๋“ˆ ๋‹จ์œ„๋กœ ๋…๋ฆฝ์ ์ธ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•ด
6. ์˜์กด์„ฑ ๊ด€๋ฆฌ - ๋ชจ๋“ˆ ๊ฐ„ ์˜์กด ๊ด€๊ณ„๊ฐ€ ๋ช…ํ™•ํ•ด์ ธ์„œ ์‹œ์Šคํ…œ ๊ตฌ์กฐ ํŒŒ์•…์ด ์‰ฌ์›Œ

์‹ค์ œ๋กœ ๊ตฌ๊ธ€์˜ Chromium ํ”„๋กœ์ ํŠธ๋‚˜ ์–ธ๋ฆฌ์–ผ ์—”์ง„ ๊ฐ™์€ ๋Œ€๊ทœ๋ชจ C++ ํ”„๋กœ์ ํŠธ๋“ค์„ ๋ณด๋ฉด, ์ˆ˜์ฒœ ๊ฐœ์˜ ๋ชจ๋“ˆ๋กœ ์„ธ๋ฐ€ํ•˜๊ฒŒ ๋‚˜๋‰˜์–ด์ ธ ์žˆ์–ด. ์ด๊ฒŒ ๋ฐ”๋กœ ์ˆ˜๋ฐฑ ๋ช…์˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋™์‹œ์— ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๋Š” ๋น„๊ฒฐ์ด์ง€! ๐Ÿ’ช

๐Ÿ›๏ธ C++ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„์˜ ๊ธฐ๋ณธ ์›์น™

์ข‹์€ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•˜๋ ค๋ฉด ๋ช‡ ๊ฐ€์ง€ ํ•ต์‹ฌ ์›์น™์„ ์•Œ์•„์•ผ ํ•ด. ์ด๊ฑด ๋งˆ์น˜ ๊ฑด๋ฌผ์„ ์ง€์„ ๋•Œ ๊ธฐ์ดˆ ๊ณต์‚ฌ๊ฐ€ ์ค‘์š”ํ•œ ๊ฒƒ๊ณผ ๊ฐ™์•„. ๐Ÿ—๏ธ

1๏ธโƒฃ SOLID ์›์น™

SOLID๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์„ค๊ณ„์˜ 5๋Œ€ ์›์น™์ธ๋ฐ, C++ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„์—๋„ ๊ทธ๋Œ€๋กœ ์ ์šฉ๋ผ. ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž!

S - Single Responsibility Principle (๋‹จ์ผ ์ฑ…์ž„ ์›์น™)

ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋‚˜ ๋ชจ๋“ˆ์€ ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด์•ผ. ์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ:

// โŒ ๋‚˜์œ ์˜ˆ: ์—ฌ๋Ÿฌ ์ฑ…์ž„์„ ๊ฐ€์ง„ ํด๋ž˜์Šค
class UserManager {
public:
    void createUser(const std::string& name);
    void deleteUser(int userId);
    void sendEmail(int userId, const std::string& message);  // ์ด๋ฉ”์ผ ๋ฐœ์†ก์€ ๋‹ค๋ฅธ ์ฑ…์ž„!
    void generateReport(int userId);  // ๋ฆฌํฌํŠธ ์ƒ์„ฑ๋„ ๋‹ค๋ฅธ ์ฑ…์ž„!
    void validateUserData(const UserData& data);  // ๊ฒ€์ฆ๋„ ๋ณ„๋„ ์ฑ…์ž„!
};

// โœ… ์ข‹์€ ์˜ˆ: ์ฑ…์ž„์„ ๋ถ„๋ฆฌ
class UserRepository {
public:
    void createUser(const std::string& name);
    void deleteUser(int userId);
    User getUser(int userId);
};

class EmailService {
public:
    void sendEmail(int userId, const std::string& message);
};

class ReportGenerator {
public:
    Report generateUserReport(int userId);
};

class UserValidator {
public:
    bool validate(const UserData& data);
};

๋ณด์ด์ง€? ๊ฐ ํด๋ž˜์Šค๊ฐ€ ๋ช…ํ™•ํ•œ ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ง€๋‹ˆ๊นŒ ํ›จ์”ฌ ๊น”๋”ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฌ์›Œ์ ธ! ๐Ÿ˜Š

O - Open/Closed Principle (๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™)

ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ๊ณ , ์ˆ˜์ •์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด์•ผ. ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ด:

// โŒ ๋‚˜์œ ์˜ˆ: ์ƒˆ๋กœ์šด ๋„ํ˜• ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด ์ฝ”๋“œ ์ˆ˜์ • ํ•„์š”
class Shape {
public:
    enum Type { CIRCLE, RECTANGLE, TRIANGLE };
    Type type;
};

double calculateArea(const Shape& shape) {
    if (shape.type == Shape::CIRCLE) {
        // ์› ๋ฉด์  ๊ณ„์‚ฐ
    } else if (shape.type == Shape::RECTANGLE) {
        // ์‚ฌ๊ฐํ˜• ๋ฉด์  ๊ณ„์‚ฐ
    }
    // ์ƒˆ๋กœ์šด ๋„ํ˜• ์ถ”๊ฐ€ ์‹œ ์—ฌ๊ธฐ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•จ!
}

// โœ… ์ข‹์€ ์˜ˆ: ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•œ ํ™•์žฅ
class Shape {
public:
    virtual ~Shape() = default;
    virtual double calculateArea() const = 0;
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    double calculateArea() const override {
        return 3.14159 * radius * radius;
    }
};

class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    double calculateArea() const override {
        return width * height;
    }
};

// ์ƒˆ๋กœ์šด ๋„ํ˜• ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด ์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ํ™•์žฅ ๊ฐ€๋Šฅ!
class Triangle : public Shape {
private:
    double base, height;
public:
    Triangle(double b, double h) : base(b), height(h) {}
    double calculateArea() const override {
        return 0.5 * base * height;
    }
};

L - Liskov Substitution Principle (๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™)

์ž์‹ ํด๋ž˜์Šค๋Š” ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ์™„์ „ํžˆ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด์•ผ. ์ด๊ฒŒ ๊นจ์ง€๋ฉด ๋‹คํ˜•์„ฑ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์•„:

// โŒ ๋‚˜์œ ์˜ˆ: LSP ์œ„๋ฐ˜
class Bird {
public:
    virtual void fly() { /* ๋‚ ๊ธฐ */ }
};

class Penguin : public Bird {
public:
    void fly() override {
        throw std::logic_error("ํŽญ๊ท„์€ ๋‚  ์ˆ˜ ์—†์–ด์š”!");  // ๋ถ€๋ชจ์˜ ๊ณ„์•ฝ ์œ„๋ฐ˜!
    }
};

// โœ… ์ข‹์€ ์˜ˆ: ์˜ฌ๋ฐ”๋ฅธ ์ถ”์ƒํ™”
class Bird {
public:
    virtual ~Bird() = default;
    virtual void move() = 0;
};

class FlyingBird : public Bird {
public:
    void move() override { fly(); }
    virtual void fly() = 0;
};

class FlightlessBird : public Bird {
public:
    void move() override { walk(); }
    virtual void walk() = 0;
};

class Sparrow : public FlyingBird {
public:
    void fly() override { /* ์ฐธ์ƒˆ๊ฐ€ ๋‚˜๋Š” ๊ตฌํ˜„ */ }
};

class Penguin : public FlightlessBird {
public:
    void walk() override { /* ํŽญ๊ท„์ด ๊ฑท๋Š” ๊ตฌํ˜„ */ }
};

I - Interface Segregation Principle (์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™)

ํด๋ผ์ด์–ธํŠธ๋Š” ์ž์‹ ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•˜๋ฉด ์•ˆ ๋œ๋‹ค๋Š” ์›์น™์ด์•ผ:

// โŒ ๋‚˜์œ ์˜ˆ: ๋น„๋Œ€ํ•œ ์ธํ„ฐํŽ˜์ด์Šค
class IWorker {
public:
    virtual void work() = 0;
    virtual void eat() = 0;
    virtual void sleep() = 0;
};

class Robot : public IWorker {
public:
    void work() override { /* ์ž‘์—… */ }
    void eat() override { /* ๋กœ๋ด‡์€ ๋จน์ง€ ์•Š๋Š”๋ฐ... */ }
    void sleep() override { /* ๋กœ๋ด‡์€ ์ž์ง€ ์•Š๋Š”๋ฐ... */ }
};

// โœ… ์ข‹์€ ์˜ˆ: ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ
class IWorkable {
public:
    virtual void work() = 0;
};

class IEatable {
public:
    virtual void eat() = 0;
};

class ISleepable {
public:
    virtual void sleep() = 0;
};

class Human : public IWorkable, public IEatable, public ISleepable {
public:
    void work() override { /* ์ž‘์—… */ }
    void eat() override { /* ์‹์‚ฌ */ }
    void sleep() override { /* ์ˆ˜๋ฉด */ }
};

class Robot : public IWorkable {
public:
    void work() override { /* ์ž‘์—… */ }
    // ํ•„์š”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ๊ตฌํ˜„!
};

D - Dependency Inversion Principle (์˜์กด์„ฑ ์—ญ์ „ ์›์น™)

๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ์€ ์ €์ˆ˜์ค€ ๋ชจ๋“ˆ์— ์˜์กดํ•˜๋ฉด ์•ˆ ๋˜๊ณ , ๋‘˜ ๋‹ค ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด์•ผ:

// โŒ ๋‚˜์œ ์˜ˆ: ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์ง์ ‘ ์˜์กด
class MySQLDatabase {
public:
    void connect() { /* MySQL ์—ฐ๊ฒฐ */ }
    void query(const std::string& sql) { /* ์ฟผ๋ฆฌ ์‹คํ–‰ */ }
};

class UserService {
private:
    MySQLDatabase database;  // ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์ง์ ‘ ์˜์กด!
public:
    void getUser(int id) {
        database.connect();
        database.query("SELECT * FROM users WHERE id = " + std::to_string(id));
    }
};

// โœ… ์ข‹์€ ์˜ˆ: ์ถ”์ƒํ™”์— ์˜์กด
class IDatabase {
public:
    virtual ~IDatabase() = default;
    virtual void connect() = 0;
    virtual void query(const std::string& sql) = 0;
};

class MySQLDatabase : public IDatabase {
public:
    void connect() override { /* MySQL ์—ฐ๊ฒฐ */ }
    void query(const std::string& sql) override { /* ์ฟผ๋ฆฌ ์‹คํ–‰ */ }
};

class PostgreSQLDatabase : public IDatabase {
public:
    void connect() override { /* PostgreSQL ์—ฐ๊ฒฐ */ }
    void query(const std::string& sql) override { /* ์ฟผ๋ฆฌ ์‹คํ–‰ */ }
};

class UserService {
private:
    std::unique_ptr<IDatabase> database;  // ์ถ”์ƒํ™”์— ์˜์กด!
public:
    UserService(std::unique_ptr<IDatabase> db) : database(std::move(db)) {}
    
    void getUser(int id) {
        database->connect();
        database->query("SELECT * FROM users WHERE id = " + std::to_string(id));
    }
};

๐Ÿ’ก ์‹ค์ „ ํŒ

SOLID ์›์น™์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€ํ‚ค๋ ค๊ณ  ํ•˜๋ฉด ์˜คํžˆ๋ ค ๊ณผ๋„ํ•œ ์„ค๊ณ„๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด. ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ์—๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ์‹œ์ž‘ํ•˜๊ณ , ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด์„œ ๋ฆฌํŒฉํ† ๋ง์„ ํ†ตํ•ด ์ ์ง„์ ์œผ๋กœ ์›์น™์„ ์ ์šฉํ•˜๋Š” ๊ฒŒ ํ˜„์‹ค์ ์ด์•ผ! ๐ŸŽฏ

2๏ธโƒฃ ๊ณ„์ธตํ™” ์•„ํ‚คํ…์ฒ˜ (Layered Architecture)

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์€ ๋ณดํ†ต ์—ฌ๋Ÿฌ ๊ณ„์ธต์œผ๋กœ ๋‚˜๋ˆ ์„œ ์„ค๊ณ„ํ•ด. ๊ฐ ๊ณ„์ธต์€ ๋ช…ํ™•ํ•œ ์—ญํ• ์„ ๊ฐ€์ง€๊ณ , ์ƒ์œ„ ๊ณ„์ธต์€ ํ•˜์œ„ ๊ณ„์ธต์—๋งŒ ์˜์กดํ•˜๋Š” ๊ตฌ์กฐ์•ผ:

๊ณ„์ธต ์—ญํ•  ์˜ˆ์‹œ
Presentation Layer ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค, ์ž…์ถœ๋ ฅ ์ฒ˜๋ฆฌ GUI, CLI, Web API
Application Layer ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์กฐ์œจ, ์œ ์Šค์ผ€์ด์Šค ๊ตฌํ˜„ ์„œ๋น„์Šค ํด๋ž˜์Šค, ์ปจํŠธ๋กค๋Ÿฌ
Domain Layer ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™๊ณผ ์—”ํ‹ฐํ‹ฐ ๋„๋ฉ”์ธ ๋ชจ๋ธ, ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™
Infrastructure Layer ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ํŒŒ์ผ ์‹œ์Šคํ…œ, ๋„คํŠธ์›Œํฌ Repository, ์™ธ๋ถ€ API ํด๋ผ์ด์–ธํŠธ
// ๊ณ„์ธตํ™” ์•„ํ‚คํ…์ฒ˜ ์˜ˆ์‹œ

// Infrastructure Layer - ๋ฐ์ดํ„ฐ ์ ‘๊ทผ
class IUserRepository {
public:
    virtual ~IUserRepository() = default;
    virtual User findById(int id) = 0;
    virtual void save(const User& user) = 0;
};

class MySQLUserRepository : public IUserRepository {
public:
    User findById(int id) override {
        // MySQL์—์„œ ์‚ฌ์šฉ์ž ์กฐํšŒ
    }
    void save(const User& user) override {
        // MySQL์— ์‚ฌ์šฉ์ž ์ €์žฅ
    }
};

// Domain Layer - ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
class User {
private:
    int id;
    std::string name;
    std::string email;
    
public:
    User(int id, const std::string& name, const std::string& email)
        : id(id), name(name), email(email) {}
    
    bool isValidEmail() const {
        // ์ด๋ฉ”์ผ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๋กœ์ง
        return email.find('@') != std::string::npos;
    }
    
    void updateEmail(const std::string& newEmail) {
        if (!isValidEmail()) {
            throw std::invalid_argument("Invalid email format");
        }
        email = newEmail;
    }
};

// Application Layer - ์œ ์Šค์ผ€์ด์Šค ์กฐ์œจ
class UserService {
private:
    std::shared_ptr<IUserRepository> userRepository;
    
public:
    UserService(std::shared_ptr<IUserRepository> repo) 
        : userRepository(repo) {}
    
    void updateUserEmail(int userId, const std::string& newEmail) {
        User user = userRepository->findById(userId);
        user.updateEmail(newEmail);  // ๋„๋ฉ”์ธ ๋กœ์ง ํ™œ์šฉ
        userRepository->save(user);
    }
};

// Presentation Layer - ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค
class UserController {
private:
    std::shared_ptr<UserService> userService;
    
public:
    UserController(std::shared_ptr<UserService> service)
        : userService(service) {}
    
    void handleUpdateEmailRequest(int userId, const std::string& newEmail) {
        try {
            userService->updateUserEmail(userId, newEmail);
            std::cout << "Email updated successfully!" << std::endl;
        } catch (const std::exception& e) {
            std::cerr << "Error: " << e.what() << std::endl;
        }
    }
};

์ด๋ ‡๊ฒŒ ๊ณ„์ธต์„ ๋‚˜๋ˆ„๋ฉด ๊ฐ ๊ณ„์ธต์˜ ์ฑ…์ž„์ด ๋ช…ํ™•ํ•ด์ง€๊ณ , ํ•œ ๊ณ„์ธต์˜ ๋ณ€๊ฒฝ์ด ๋‹ค๋ฅธ ๊ณ„์ธต์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์•„. ์˜ˆ๋ฅผ ๋“ค์–ด MySQL์—์„œ PostgreSQL๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ฐ”๊ฟ”๋„ ์ƒ์œ„ ๊ณ„์ธต์€ ์ „ํ˜€ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์ง€! ๐ŸŽ‰

๐Ÿ“ฆ ๋ชจ๋“ˆํ™” ์ „๋žต๊ณผ ์‹ค์ „ ๊ธฐ๋ฒ•

์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ C++์—์„œ ๋ชจ๋“ˆ์„ ์–ด๋–ป๊ฒŒ ๋‚˜๋ˆ„๊ณ  ๊ด€๋ฆฌํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž! ๐Ÿš€

1๏ธโƒฃ ํ—ค๋” ํŒŒ์ผ๊ณผ ๊ตฌํ˜„ ํŒŒ์ผ ๋ถ„๋ฆฌ

C++์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ชจ๋“ˆํ™” ๋ฐฉ๋ฒ•์ด์•ผ. ์ธํ„ฐํŽ˜์ด์Šค(ํ—ค๋”)์™€ ๊ตฌํ˜„์„ ๋ถ„๋ฆฌํ•˜๋ฉด ์ปดํŒŒ์ผ ์˜์กด์„ฑ์„ ์ค„์ผ ์ˆ˜ ์žˆ์–ด:

// UserManager.h - ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜
#ifndef USER_MANAGER_H
#define USER_MANAGER_H

#include <string>
#include <memory>

// ์ „๋ฐฉ ์„ ์–ธ์œผ๋กœ ์˜์กด์„ฑ ์ตœ์†Œํ™”
class User;
class IUserRepository;

class UserManager {
private:
    std::unique_ptr<IUserRepository> repository;
    
public:
    explicit UserManager(std::unique_ptr<IUserRepository> repo);
    ~UserManager();
    
    // ๋ณต์‚ฌ/์ด๋™ ์ƒ์„ฑ์ž ์‚ญ์ œ (์‹ฑ๊ธ€ํ†ค ํŒจํ„ด ๋“ฑ์— ํ™œ์šฉ)
    UserManager(const UserManager&) = delete;
    UserManager& operator=(const UserManager&) = delete;
    
    void createUser(const std::string& name, const std::string& email);
    User getUser(int userId);
    void deleteUser(int userId);
};

#endif // USER_MANAGER_H
// UserManager.cpp - ๊ตฌํ˜„
#include "UserManager.h"
#include "User.h"
#include "IUserRepository.h"

UserManager::UserManager(std::unique_ptr<IUserRepository> repo)
    : repository(std::move(repo)) {
}

UserManager::~UserManager() = default;

void UserManager::createUser(const std::string& name, const std::string& email) {
    User newUser(0, name, email);
    repository->save(newUser);
}

User UserManager::getUser(int userId) {
    return repository->findById(userId);
}

void UserManager::deleteUser(int userId) {
    repository->remove(userId);
}

๐Ÿ’ก Pimpl (Pointer to Implementation) ์ด๋””์—„

ํ—ค๋” ํŒŒ์ผ์˜ ์˜์กด์„ฑ์„ ๋”์šฑ ์ค„์ด๊ณ  ์‹ถ๋‹ค๋ฉด Pimpl ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด๋ด. ๊ตฌํ˜„ ์„ธ๋ถ€์‚ฌํ•ญ์„ ์™„์ „ํžˆ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์–ด์„œ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์ด ํฌ๊ฒŒ ๋‹จ์ถ•๋ผ!

// Widget.h - Pimpl ํŒจํ„ด ์ ์šฉ
#ifndef WIDGET_H
#define WIDGET_H

#include <memory>

class Widget {
private:
    class Impl;  // ์ „๋ฐฉ ์„ ์–ธ
    std::unique_ptr<Impl> pImpl;  // ๊ตฌํ˜„ ํฌ์ธํ„ฐ
    
public:
    Widget();
    ~Widget();
    
    // ๋ณต์‚ฌ/์ด๋™ ์ƒ์„ฑ์ž๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธ
    Widget(const Widget& other);
    Widget& operator=(const Widget& other);
    Widget(Widget&& other) noexcept;
    Widget& operator=(Widget&& other) noexcept;
    
    void doSomething();
    int getValue() const;
};

#endif // WIDGET_H
// Widget.cpp
#include "Widget.h"
#include <iostream>
#include <vector>
#include "SomeHeavyDependency.h"  // ํ—ค๋”์—์„œ๋Š” ๋ณด์ด์ง€ ์•Š์Œ!

// ์‹ค์ œ ๊ตฌํ˜„ ํด๋ž˜์Šค
class Widget::Impl {
public:
    std::vector<int> data;
    SomeHeavyDependency dependency;
    
    void doSomething() {
        std::cout << "Doing something..." << std::endl;
        dependency.process(data);
    }
    
    int getValue() const {
        return data.empty() ? 0 : data[0];
    }
};

// Widget ์ƒ์„ฑ์ž/์†Œ๋ฉธ์ž ๊ตฌํ˜„
Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;

Widget::Widget(const Widget& other) 
    : pImpl(std::make_unique<Impl>(*other.pImpl)) {}

Widget& Widget::operator=(const Widget& other) {
    *pImpl = *other.pImpl;
    return *this;
}

Widget::Widget(Widget&& other) noexcept = default;
Widget& Widget::operator=(Widget&& other) noexcept = default;

void Widget::doSomething() {
    pImpl->doSomething();
}

int Widget::getValue() const {
    return pImpl->getValue();
}

Pimpl ํŒจํ„ด์„ ์“ฐ๋ฉด Widget.h๋ฅผ includeํ•˜๋Š” ํŒŒ์ผ๋“ค์€ SomeHeavyDependency.h์˜ ๋ณ€๊ฒฝ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์•„. ์ปดํŒŒ์ผ ์‹œ๊ฐ„์ด ์—„์ฒญ๋‚˜๊ฒŒ ์ค„์–ด๋“ค์ง€! โšก

2๏ธโƒฃ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•œ ๋…ผ๋ฆฌ์  ๋ชจ๋“ˆํ™”

๋„ค์ž„์ŠคํŽ˜์ด์Šค๋Š” C++์—์„œ ์ฝ”๋“œ๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์•ผ:

// ๊ณ„์ธต์  ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๊ตฌ์กฐ
namespace MyCompany {
    namespace Graphics {
        namespace Rendering {
            class Renderer {
            public:
                void render();
            };
            
            class Shader {
            public:
                void compile();
            };
        }
        
        namespace UI {
            class Button {
            public:
                void draw();
            };
            
            class Window {
            public:
                void show();
            };
        }
    }
    
    namespace Audio {
        class AudioEngine {
        public:
            void playSound();
        };
        
        class Mixer {
        public:
            void mix();
        };
    }
}

// C++17 ์ดํ›„ ์ค‘์ฒฉ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๊ฐ„ํŽธ ํ‘œ๊ธฐ
namespace MyCompany::Graphics::Rendering {
    void Renderer::render() {
        // ๋ Œ๋”๋ง ๊ตฌํ˜„
    }
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
int main() {
    MyCompany::Graphics::Rendering::Renderer renderer;
    renderer.render();
    
    // using ์„ ์–ธ์œผ๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ
    using namespace MyCompany::Graphics::UI;
    Button button;
    button.draw();
    
    return 0;
}

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ

ํ—ค๋” ํŒŒ์ผ์—์„œ using namespace๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋ผ! ๊ทธ ํ—ค๋”๋ฅผ includeํ•˜๋Š” ๋ชจ๋“  ํŒŒ์ผ์— ์˜ํ–ฅ์„ ์ฃผ๊ฑฐ๋“ . ๊ตฌํ˜„ ํŒŒ์ผ(.cpp)์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ํ•จ์ˆ˜ ๋‚ด๋ถ€ ์Šค์ฝ”ํ”„์—์„œ๋งŒ ์‚ฌ์šฉํ•˜์ž.

3๏ธโƒฃ C++20 ๋ชจ๋“ˆ ์‹œ์Šคํ…œ

C++20๋ถ€ํ„ฐ๋Š” ์ง„์งœ ๋ชจ๋“ˆ ์‹œ์Šคํ…œ์ด ๋„์ž…๋์–ด! ๊ธฐ์กด์˜ ํ—ค๋” ํŒŒ์ผ ๋ฐฉ์‹๋ณด๋‹ค ํ›จ์”ฌ ๊ฐ•๋ ฅํ•˜๊ณ  ํšจ์œจ์ ์ด์•ผ. ๐ŸŽŠ

// math_module.cppm - ๋ชจ๋“ˆ ์ธํ„ฐํŽ˜์ด์Šค ํŒŒ์ผ
export module math_utils;

import <cmath>;
import <numbers>;

// ์™ธ๋ถ€๋กœ ๊ณต๊ฐœํ•  ํ•จ์ˆ˜๋“ค
export namespace MathUtils {
    double calculateCircleArea(double radius) {
        return std::numbers::pi * radius * radius;
    }
    
    double calculateSquareRoot(double value) {
        return std::sqrt(value);
    }
    
    // ๋‚ด๋ถ€ ๊ตฌํ˜„ (export ์—†์Œ - ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ ๋ถˆ๊ฐ€)
    namespace Internal {
        double helperFunction(double x) {
            return x * 2.0;
        }
    }
}

// ํด๋ž˜์Šค๋„ export ๊ฐ€๋Šฅ
export class Calculator {
private:
    double result = 0.0;
    
public:
    void add(double value) {
        result += value;
    }
    
    void multiply(double value) {
        result *= value;
    }
    
    double getResult() const {
        return result;
    }
};
// main.cpp - ๋ชจ๋“ˆ ์‚ฌ์šฉ
import math_utils;
import <iostream>;

int main() {
    // ๋ชจ๋“ˆ์—์„œ exportํ•œ ํ•จ์ˆ˜ ์‚ฌ์šฉ
    double area = MathUtils::calculateCircleArea(5.0);
    std::cout << "Circle area: " << area << std::endl;
    
    // exportํ•œ ํด๋ž˜์Šค ์‚ฌ์šฉ
    Calculator calc;
    calc.add(10);
    calc.multiply(2);
    std::cout << "Result: " << calc.getResult() << std::endl;
    
    // MathUtils::Internal::helperFunction์€ ์ ‘๊ทผ ๋ถˆ๊ฐ€!
    // (export๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ)
    
    return 0;
}

C++20 ๋ชจ๋“ˆ์˜ ์žฅ์ :

โœ… ์ปดํŒŒ์ผ ์†๋„ ํ–ฅ์ƒ - ๋ชจ๋“ˆ์€ ํ•œ ๋ฒˆ๋งŒ ์ปดํŒŒ์ผ๋˜๊ณ  ์žฌ์‚ฌ์šฉ๋ผ
โœ… ๋งคํฌ๋กœ ๊ฒฉ๋ฆฌ - ๋ชจ๋“ˆ ๋‚ด๋ถ€์˜ ๋งคํฌ๋กœ๊ฐ€ ์™ธ๋ถ€๋กœ ์ƒˆ์ง€ ์•Š์•„
โœ… ๋ช…ํ™•ํ•œ ์ธํ„ฐํŽ˜์ด์Šค - export ํ‚ค์›Œ๋“œ๋กœ ๊ณต๊ฐœ API๊ฐ€ ๋ช…ํ™•ํ•ด์ ธ
โœ… ์ˆœํ™˜ ์˜์กด์„ฑ ํ•ด๊ฒฐ - ํ—ค๋” ๊ฐ€๋“œ๋‚˜ ์ „๋ฐฉ ์„ ์–ธ ์—†์ด๋„ ๊น”๋”ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
โœ… ODR ์œ„๋ฐ˜ ๋ฐฉ์ง€ - One Definition Rule ๋ฌธ์ œ๊ฐ€ ์ž๋™์œผ๋กœ ํ•ด๊ฒฐ๋ผ

๐Ÿ’ก ์‹ค์ „ ํŒ

C++20 ๋ชจ๋“ˆ์€ ์•„์ง ์ปดํŒŒ์ผ๋Ÿฌ ์ง€์›์ด ์™„๋ฒฝํ•˜์ง€ ์•Š์•„์„œ, ์‹ค๋ฌด์—์„œ๋Š” ๊ธฐ์กด ํ—ค๋” ๋ฐฉ์‹๊ณผ ๋ณ‘ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„. GCC 11+, Clang 15+, MSVC 2019 16.10+ ๋ฒ„์ „์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด. ํ”„๋กœ์ ํŠธ์— ๋„์ž…ํ•˜๊ธฐ ์ „์— ๋นŒ๋“œ ์‹œ์Šคํ…œ(CMake, Meson ๋“ฑ)์˜ ๋ชจ๋“ˆ ์ง€์› ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด์•ผ ํ•ด!

4๏ธโƒฃ ์ •์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๋™์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ฝ”๋“œ๋ฅผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒŒ ์ผ๋ฐ˜์ ์ด์•ผ. ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์ด ์žˆ์–ด:

์ •์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (.a, .lib)

// MathLibrary.h
#ifndef MATH_LIBRARY_H
#define MATH_LIBRARY_H

namespace MathLib {
    double add(double a, double b);
    double multiply(double a, double b);
    double power(double base, int exponent);
}

#endif
// MathLibrary.cpp
#include "MathLibrary.h"
#include <cmath>

namespace MathLib {
    double add(double a, double b) {
        return a + b;
    }
    
    double multiply(double a, double b) {
        return a * b;
    }
    
    double power(double base, int exponent) {
        return std::pow(base, exponent);
    }
}
# CMakeLists.txt - ์ •์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋นŒ๋“œ
cmake_minimum_required(VERSION 3.15)
project(MathLibrary)

# ์ •์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ƒ์„ฑ
add_library(MathLib STATIC
    MathLibrary.cpp
)

target_include_directories(MathLib PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}
)

# ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์ ํŠธ
add_executable(MyApp main.cpp)
target_link_libraries(MyApp PRIVATE MathLib)

๋™์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (.so, .dll)

// PluginInterface.h
#ifndef PLUGIN_INTERFACE_H
#define PLUGIN_INTERFACE_H

#ifdef _WIN32
    #ifdef BUILDING_DLL
        #define DLL_EXPORT __declspec(dllexport)
    #else
        #define DLL_EXPORT __declspec(dllimport)
    #endif
#else
    #define DLL_EXPORT __attribute__((visibility("default")))
#endif

class DLL_EXPORT IPlugin {
public:
    virtual ~IPlugin() = default;
    virtual void initialize() = 0;
    virtual void execute() = 0;
    virtual void shutdown() = 0;
};

// ํ”Œ๋Ÿฌ๊ทธ์ธ ์ƒ์„ฑ ํ•จ์ˆ˜
extern "C" DLL_EXPORT IPlugin* createPlugin();
extern "C" DLL_EXPORT void destroyPlugin(IPlugin* plugin);

#endif
// AudioPlugin.cpp - ๋™์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ตฌํ˜„
#include "PluginInterface.h"
#include <iostream>

class AudioPlugin : public IPlugin {
public:
    void initialize() override {
        std::cout << "Audio plugin initialized" << std::endl;
    }
    
    void execute() override {
        std::cout << "Playing audio..." << std::endl;
    }
    
    void shutdown() override {
        std::cout << "Audio plugin shutdown" << std::endl;
    }
};

// ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœํ•  ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜
extern "C" DLL_EXPORT IPlugin* createPlugin() {
    return new AudioPlugin();
}

extern "C" DLL_EXPORT void destroyPlugin(IPlugin* plugin) {
    delete plugin;
}
// PluginLoader.cpp - ๋™์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋กœ๋”ฉ
#include <iostream>
#include <memory>

#ifdef _WIN32
    #include <windows.h>
    typedef HMODULE LibHandle;
#else
    #include <dlfcn.h>
    typedef void* LibHandle;
#endif

#include "PluginInterface.h"

class PluginLoader {
private:
    LibHandle handle = nullptr;
    IPlugin* plugin = nullptr;
    
public:
    bool loadPlugin(const std::string& path) {
#ifdef _WIN32
        handle = LoadLibraryA(path.c_str());
#else
        handle = dlopen(path.c_str(), RTLD_LAZY);
#endif
        
        if (!handle) {
            std::cerr << "Failed to load plugin" << std::endl;
            return false;
        }
        
        // ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
        typedef IPlugin* (*CreateFunc)();
#ifdef _WIN32
        CreateFunc createFunc = (CreateFunc)GetProcAddress(handle, "createPlugin");
#else
        CreateFunc createFunc = (CreateFunc)dlsym(handle, "createPlugin");
#endif
        
        if (!createFunc) {
            std::cerr << "Failed to find createPlugin function" << std::endl;
            return false;
        }
        
        plugin = createFunc();
        return plugin != nullptr;
    }
    
    IPlugin* getPlugin() {
        return plugin;
    }
    
    ~PluginLoader() {
        if (plugin) {
            typedef void (*DestroyFunc)(IPlugin*);
#ifdef _WIN32
            DestroyFunc destroyFunc = (DestroyFunc)GetProcAddress(handle, "destroyPlugin");
#else
            DestroyFunc destroyFunc = (DestroyFunc)dlsym(handle, "destroyPlugin");
#endif
            if (destroyFunc) {
                destroyFunc(plugin);
            }
        }
        
        if (handle) {
#ifdef _WIN32
            FreeLibrary(handle);
#else
            dlclose(handle);
#endif
        }
    }
};

int main() {
    PluginLoader loader;
    
    if (loader.loadPlugin("./libAudioPlugin.so")) {
        IPlugin* plugin = loader.getPlugin();
        plugin->initialize();
        plugin->execute();
        plugin->shutdown();
    }
    
    return 0;
}
ํŠน์ง• ์ •์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋™์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
๋งํฌ ์‹œ์  ์ปดํŒŒ์ผ ํƒ€์ž„ ๋Ÿฐํƒ€์ž„
์‹คํ–‰ ํŒŒ์ผ ํฌ๊ธฐ ํฌ๋‹ค (๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํฌํ•จ) ์ž‘๋‹ค (๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ณ„๋„)
๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ๊ฐ ํ”„๋กœ์„ธ์Šค๋งˆ๋‹ค ๋ณต์‚ฌ๋ณธ ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ณต์œ  ๊ฐ€๋Šฅ
์—…๋ฐ์ดํŠธ ์žฌ์ปดํŒŒ์ผ ํ•„์š” DLL๋งŒ ๊ต์ฒด ๊ฐ€๋Šฅ
์„ฑ๋Šฅ ์•ฝ๊ฐ„ ๋น ๋ฆ„ ์•ฝ๊ฐ„ ๋А๋ฆผ (๊ฐ„์ ‘ ํ˜ธ์ถœ)
ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ ๋ถˆ๊ฐ€๋Šฅ ๊ฐ€๋Šฅ
๋ชจ๋“ˆํ™” ์ „๋žต ๋น„๊ต ์ •์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋™์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ Main Program (๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฝ”๋“œ ํฌํ•จ) Main Program Shared Library (.so/.dll) โœ“ ๋น ๋ฅธ ์‹คํ–‰ โœ“ ์˜์กด์„ฑ ์—†์Œ โœ— ํฐ ํŒŒ์ผ ํฌ๊ธฐ โœ— ์—…๋ฐ์ดํŠธ ์–ด๋ ค์›€ โœ“ ์ž‘์€ ํŒŒ์ผ ํฌ๊ธฐ โœ“ ์‰ฌ์šด ์—…๋ฐ์ดํŠธ โœ“ ๋ฉ”๋ชจ๋ฆฌ ๊ณต์œ  โœ— ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ ์‚ฌ์šฉ ์‚ฌ๋ก€ โ€ข ๋…๋ฆฝ ์‹คํ–‰ ํ”„๋กœ๊ทธ๋žจ โ€ข ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ โ€ข ์„ฑ๋Šฅ ์ค‘์š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‚ฌ์šฉ ์‚ฌ๋ก€ โ€ข ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ โ€ข ์—ฌ๋Ÿฌ ์•ฑ์ด ๊ณต์œ ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ โ€ข ์ž์ฃผ ์—…๋ฐ์ดํŠธ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ
๐ŸŽฏ ์˜์กด์„ฑ ๊ด€๋ฆฌ ์ „๋žต

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ ๊ฐ€์žฅ ๊ณจ์น˜ ์•„ํ”ˆ ๊ฒŒ ๋ฐ”๋กœ ์˜์กด์„ฑ ๊ด€๋ฆฌ์•ผ. ๋ชจ๋“ˆ A๊ฐ€ B์— ์˜์กดํ•˜๊ณ , B๊ฐ€ C์— ์˜์กดํ•˜๊ณ ... ์ด๋Ÿฌ๋‹ค ๋ณด๋ฉด ์ˆœํ™˜ ์˜์กด์„ฑ์ด ์ƒ๊ธฐ๊ฑฐ๋‚˜ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์ด ํญ๋ฐœํ•˜์ง€. ๐Ÿ˜ฑ

1๏ธโƒฃ ์˜์กด์„ฑ ์—ญ์ „ (Dependency Inversion)

์•ž์„œ SOLID ์›์น™์—์„œ ๋ดค๋˜ DIP๋ฅผ ์‹ค์ „์—์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž:

// ๋ฌธ์ œ ์ƒํ™ฉ: ๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ์ด ์ €์ˆ˜์ค€ ๋ชจ๋“ˆ์— ์ง์ ‘ ์˜์กด
// HighLevelModule.h
#include "LowLevelModule.h"  // ์ง์ ‘ ์˜์กด!

class HighLevelModule {
private:
    LowLevelModule lowLevel;  // ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กด
public:
    void doSomething() {
        lowLevel.operation();
    }
};

// ์ด ๊ตฌ์กฐ์˜ ๋ฌธ์ œ์ :
// 1. LowLevelModule ๋ณ€๊ฒฝ ์‹œ HighLevelModule๋„ ์žฌ์ปดํŒŒ์ผ
// 2. LowLevelModule์„ ๋‹ค๋ฅธ ๊ตฌํ˜„์œผ๋กœ ๊ต์ฒดํ•˜๊ธฐ ์–ด๋ ค์›€
// 3. ํ…Œ์ŠคํŠธ ์‹œ Mock ๊ฐ์ฒด ์‚ฌ์šฉ ๋ถˆ๊ฐ€
// ํ•ด๊ฒฐ์ฑ…: ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•œ ์˜์กด์„ฑ ์—ญ์ „
// IService.h - ์ถ”์ƒ ์ธํ„ฐํŽ˜์ด์Šค
#ifndef ISERVICE_H
#define ISERVICE_H

class IService {
public:
    virtual ~IService() = default;
    virtual void operation() = 0;
};

#endif
// HighLevelModule.h - ์ถ”์ƒํ™”์— ์˜์กด
#ifndef HIGH_LEVEL_MODULE_H
#define HIGH_LEVEL_MODULE_H

#include "IService.h"
#include <memory>

class HighLevelModule {
private:
    std::unique_ptr<IService> service;  // ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กด
    
public:
    explicit HighLevelModule(std::unique_ptr<IService> svc)
        : service(std::move(svc)) {}
    
    void doSomething() {
        service->operation();
    }
};

#endif
// LowLevelModule.h - ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„
#ifndef LOW_LEVEL_MODULE_H
#define LOW_LEVEL_MODULE_H

#include "IService.h"

class LowLevelModule : public IService {
public:
    void operation() override;
};

#endif
// main.cpp - ์˜์กด์„ฑ ์ฃผ์ž…
#include "HighLevelModule.h"
#include "LowLevelModule.h"
#include <memory>

int main() {
    // ๋Ÿฐํƒ€์ž„์— ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„ ์ฃผ์ž…
    auto service = std::make_unique<LowLevelModule>();
    HighLevelModule module(std::move(service));
    
    module.doSomething();
    
    return 0;
}

์ด์ œ HighLevelModule์€ LowLevelModule์˜ ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„์„ ์ „ํ˜€ ๋ชจ๋ฅด๊ณ , IService ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์•Œ๋ฉด ๋ผ. ๋‚˜์ค‘์— ๋‹ค๋ฅธ ๊ตฌํ˜„์œผ๋กœ ๋ฐ”๊พธ๊ณ  ์‹ถ์œผ๋ฉด main.cpp๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋˜์ง€! ๐ŸŽ‰

2๏ธโƒฃ ์ˆœํ™˜ ์˜์กด์„ฑ ํ•ด๊ฒฐ

์ˆœํ™˜ ์˜์กด์„ฑ์€ ์ •๋ง ๊ณจ์น˜ ์•„ํ”ˆ ๋ฌธ์ œ์•ผ. A๊ฐ€ B๋ฅผ includeํ•˜๊ณ , B๊ฐ€ A๋ฅผ includeํ•˜๋ฉด ์ปดํŒŒ์ผ์ด ์•ˆ ๋˜๊ฑฐ๋“ :

// โŒ ์ˆœํ™˜ ์˜์กด์„ฑ ๋ฌธ์ œ
// ClassA.h
#ifndef CLASS_A_H
#define CLASS_A_H

#include "ClassB.h"  // B๋ฅผ include

class ClassA {
private:
    ClassB b;  // B ๊ฐ์ฒด ๋ฉค๋ฒ„
public:
    void useB();
};

#endif

// ClassB.h
#ifndef CLASS_B_H
#define CLASS_B_H

#include "ClassA.h"  // A๋ฅผ include (์ˆœํ™˜!)

class ClassB {
private:
    ClassA a;  // A ๊ฐ์ฒด ๋ฉค๋ฒ„
public:
    void useA();
};

#endif

// ์ปดํŒŒ์ผ ์—๋Ÿฌ ๋ฐœ์ƒ!

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 1: ์ „๋ฐฉ ์„ ์–ธ (Forward Declaration)

// ClassA.h
#ifndef CLASS_A_H
#define CLASS_A_H

class ClassB;  // ์ „๋ฐฉ ์„ ์–ธ (include ๋Œ€์‹ )

class ClassA {
private:
    ClassB* b;  // ํฌ์ธํ„ฐ๋‚˜ ์ฐธ์กฐ๋งŒ ๊ฐ€๋Šฅ
public:
    ClassA();
    ~ClassA();
    void useB();
};

#endif
// ClassA.cpp
#include "ClassA.h"
#include "ClassB.h"  // ๊ตฌํ˜„ ํŒŒ์ผ์—์„œ include

ClassA::ClassA() : b(new ClassB()) {}
ClassA::~ClassA() { delete b; }

void ClassA::useB() {
    b->someMethod();  // ์ด์ œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
}

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 2: ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ

// IObserver.h - ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜
#ifndef IOBSERVER_H
#define IOBSERVER_H

class IObserver {
public:
    virtual ~IObserver() = default;
    virtual void update() = 0;
};

#endif

// Subject.h
#ifndef SUBJECT_H
#define SUBJECT_H

#include "IObserver.h"
#include <vector>
#include <memory>

class Subject {
private:
    std::vector<IObserver*> observers;
    
public:
    void attach(IObserver* observer) {
        observers.push_back(observer);
    }
    
    void notify() {
        for (auto* obs : observers) {
            obs->update();
        }
    }
};

#endif

// ConcreteObserver.h
#ifndef CONCRETE_OBSERVER_H
#define CONCRETE_OBSERVER_H

#include "IObserver.h"
#include "Subject.h"

class ConcreteObserver : public IObserver {
private:
    Subject* subject;
    
public:
    ConcreteObserver(Subject* subj) : subject(subj) {
        subject->attach(this);
    }
    
    void update() override {
        // ์—…๋ฐ์ดํŠธ ์ฒ˜๋ฆฌ
    }
};

#endif

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ค‘๊ฐ„์— ๋‘๋ฉด ์ˆœํ™˜ ์˜์กด์„ฑ์„ ๊น”๋”ํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ‘

3๏ธโƒฃ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž ํ™œ์šฉ

์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด์„ฑ์€ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ํŽธํ•ด. C++์—์„œ ๋งŽ์ด ์“ฐ๋Š” ๋„๊ตฌ๋“ค์„ ์†Œ๊ฐœํ• ๊ฒŒ:

Conan

# conanfile.txt
[requires]
boost/1.80.0
fmt/9.1.0
spdlog/1.11.0

[generators]
cmake

[options]
boost:shared=False
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyProject)

# Conan ์„ค์ •
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

add_executable(myapp main.cpp)

# Conan์ด ๊ด€๋ฆฌํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋งํฌ
target_link_libraries(myapp ${CONAN_LIBS})
# ๋นŒ๋“œ ๋ช…๋ น
mkdir build && cd build
conan install .. --build=missing
cmake ..
cmake --build .

vcpkg

# vcpkg๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜
vcpkg install boost:x64-windows
vcpkg install fmt:x64-windows
vcpkg install spdlog:x64-windows

# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyProject)

# vcpkg ํˆด์ฒด์ธ ํŒŒ์ผ ์‚ฌ์šฉ
# cmake -DCMAKE_TOOLCHAIN_FILE=[vcpkg root]/scripts/buildsystems/vcpkg.cmake

find_package(Boost REQUIRED)
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)

add_executable(myapp main.cpp)

target_link_libraries(myapp
    PRIVATE
    Boost::boost
    fmt::fmt
    spdlog::spdlog
)

๐Ÿ’ก ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž ์„ ํƒ ๊ฐ€์ด๋“œ

Conan - ํฌ๋กœ์Šค ํ”Œ๋žซํผ ์ง€์›์ด ๋›ฐ์–ด๋‚˜๊ณ , ๋ฐ”์ด๋„ˆ๋ฆฌ ์บ์‹ฑ์œผ๋กœ ๋นŒ๋“œ ์†๋„๊ฐ€ ๋น ๋ฆ„. Python ๊ธฐ๋ฐ˜์ด๋ผ ํ™•์žฅ์„ฑ์ด ์ข‹์•„.
vcpkg - Microsoft๊ฐ€ ๊ด€๋ฆฌํ•˜๊ณ , Windows์—์„œ ํŠนํžˆ ํŽธํ•จ. CMake ํ†ตํ•ฉ์ด ๋งค์šฐ ์ž์—ฐ์Šค๋Ÿฌ์›Œ.
CPM.cmake - CMake ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ€๋ณ๊ณ  ๊ฐ„๋‹จํ•จ. ์ž‘์€ ํ”„๋กœ์ ํŠธ์— ์ ํ•ฉํ•ด.

๐Ÿ—๏ธ ์‹ค์ „ ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด

์ด๋ก ์€ ์ถฉ๋ถ„ํžˆ ๋ดค์œผ๋‹ˆ, ์ด์ œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ๋งŽ์ด ์“ฐ๋Š” ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด๋“ค์„ ์•Œ์•„๋ณด์ž! ๐Ÿš€

1๏ธโƒฃ ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜ (Layered Architecture)

๊ฐ€์žฅ ์ „ํ†ต์ ์ด๊ณ  ๋„๋ฆฌ ์“ฐ์ด๋Š” ํŒจํ„ด์ด์•ผ. ์‹œ์Šคํ…œ์„ ์ˆ˜ํ‰์ ์ธ ๊ณ„์ธต์œผ๋กœ ๋‚˜๋ˆ„๋Š” ๊ฑฐ์ง€:

// ์‹ค์ „ ์˜ˆ์ œ: ๊ฒŒ์ž„ ์—”์ง„ ์•„ํ‚คํ…์ฒ˜

// ============ Layer 1: Platform (์ตœํ•˜์œ„) ============
// Platform/Window.h
namespace Platform {
    class IWindow {
    public:
        virtual ~IWindow() = default;
        virtual void create(int width, int height) = 0;
        virtual void update() = 0;
        virtual bool shouldClose() = 0;
    };
    
    class WindowsWindow : public IWindow {
        // Windows ๊ตฌํ˜„
    };
    
    class LinuxWindow : public IWindow {
        // Linux ๊ตฌํ˜„
    };
}

// ============ Layer 2: Core (ํ•ต์‹ฌ ์‹œ์Šคํ…œ) ============
// Core/Engine.h
namespace Core {
    class Engine {
    private:
        std::unique_ptr<Platform::IWindow> window;
        bool isRunning = false;
        
    public:
        void initialize() {
#ifdef _WIN32
            window = std::make_unique<Platform::WindowsWindow>();
#else
            window = std::make_unique<Platform::LinuxWindow>();
#endif
            window->create(1920, 1080);
            isRunning = true;
        }
        
        void run() {
            while (isRunning && !window->shouldClose()) {
                update();
                render();
            }
        }
        
        void update() {
            // ๊ฒŒ์ž„ ๋กœ์ง ์—…๋ฐ์ดํŠธ
        }
        
        void render() {
            // ๋ Œ๋”๋ง
        }
    };
}

// ============ Layer 3: Game (๊ฒŒ์ž„ ๋กœ์ง) ============
// Game/Player.h
namespace Game {
    class Player {
    private:
        float x, y;
        float health;
        
    public:
        Player(float startX, float startY) 
            : x(startX), y(startY), health(100.0f) {}
        
        void move(float dx, float dy) {
            x += dx;
            y += dy;
        }
        
        void takeDamage(float damage) {
            health -= damage;
            if (health < 0) health = 0;
        }
        
        bool isAlive() const {
            return health > 0;
        }
    };
}

// ============ Layer 4: Application (์ตœ์ƒ์œ„) ============
// main.cpp
int main() {
    Core::Engine engine;
    engine.initialize();
    
    Game::Player player(100, 100);
    
    engine.run();
    
    return 0;
}

๊ฐ ๊ณ„์ธต์€ ๋ฐ”๋กœ ์•„๋ž˜ ๊ณ„์ธต์—๋งŒ ์˜์กดํ•˜๊ณ , ์œ„ ๊ณ„์ธต์€ ๋ชฐ๋ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ”Œ๋žซํผ์„ ๋ฐ”๊ฟ”๋„ ์ƒ์œ„ ๊ณ„์ธต์€ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์–ด! ๐Ÿ˜Š

2๏ธโƒฃ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ (Ports and Adapters)

๋„๋ฉ”์ธ ๋กœ์ง์„ ์ค‘์‹ฌ์— ๋‘๊ณ , ์™ธ๋ถ€ ์„ธ๊ณ„์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ํฌํŠธ์™€ ์–ด๋Œ‘ํ„ฐ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ํŒจํ„ด์ด์•ผ:

// ============ Domain (ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง) ============
// Domain/Order.h
namespace Domain {
    class Order {
    private:
        int id;
        std::string customerId;
        std::vector<std::string> items;
        double totalAmount;
        
    public:
        Order(int id, const std::string& customerId)
            : id(id), customerId(customerId), totalAmount(0.0) {}
        
        void addItem(const std::string& item, double price) {
            items.push_back(item);
            totalAmount += price;
        }
        
        double getTotalAmount() const {
            return totalAmount;
        }
        
        bool isValid() const {
            return !items.empty() && totalAmount > 0;
        }
    };
}

// ============ Ports (์ธํ„ฐํŽ˜์ด์Šค) ============
// Ports/IOrderRepository.h
namespace Ports {
    class IOrderRepository {
    public:
        virtual ~IOrderRepository() = default;
        virtual void save(const Domain::Order& order) = 0;
        virtual Domain::Order findById(int id) = 0;
    };
    
    class IPaymentGateway {
    public:
        virtual ~IPaymentGateway() = default;
        virtual bool processPayment(double amount) = 0;
    };
    
    class INotificationService {
    public:
        virtual ~INotificationService() = default;
        virtual void sendOrderConfirmation(const std::string& customerId) = 0;
    };
}

// ============ Application (์œ ์Šค์ผ€์ด์Šค) ============
// Application/OrderService.h
namespace Application {
    class OrderService {
    private:
        std::shared_ptr<Ports::IOrderRepository> orderRepo;
        std::shared_ptr<Ports::IPaymentGateway> paymentGateway;
        std::shared_ptr<Ports::INotificationService> notificationService;
        
    public:
        OrderService(
            std::shared_ptr<Ports::IOrderRepository> repo,
            std::shared_ptr<Ports::IPaymentGateway> payment,
            std::shared_ptr<Ports::INotificationService> notification
        ) : orderRepo(repo), 
            paymentGateway(payment), 
            notificationService(notification) {}
        
        bool placeOrder(Domain::Order& order) {
            // ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™ ๊ฒ€์ฆ
            if (!order.isValid()) {
                return false;
            }
            
            // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
            if (!paymentGateway->processPayment(order.getTotalAmount())) {
                return false;
            }
            
            // ์ฃผ๋ฌธ ์ €์žฅ
            orderRepo->save(order);
            
            // ์•Œ๋ฆผ ๋ฐœ์†ก
            notificationService->sendOrderConfirmation(order.getCustomerId());
            
            return true;
        }
    };
}

// ============ Adapters (๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„) ============
// Adapters/MySQLOrderRepository.h
namespace Adapters {
    class MySQLOrderRepository : public Ports::IOrderRepository {
    public:
        void save(const Domain::Order& order) override {
            // MySQL์— ์ €์žฅํ•˜๋Š” ์‹ค์ œ ๊ตฌํ˜„
            std::cout << "Saving order to MySQL..." << std::endl;
        }
        
        Domain::Order findById(int id) override {
            // MySQL์—์„œ ์กฐํšŒํ•˜๋Š” ์‹ค์ œ ๊ตฌํ˜„
            return Domain::Order(id, "customer123");
        }
    };
    
    class StripePaymentGateway : public Ports::IPaymentGateway {
    public:
        bool processPayment(double amount) override {
            // Stripe API ํ˜ธ์ถœ
            std::cout << "Processing payment of $" << amount 
                      << " via Stripe..." << std::endl;
            return true;
        }
    };
    
    class EmailNotificationService : public Ports::INotificationService {
    public:
        void sendOrderConfirmation(const std::string& customerId) override {
            // ์ด๋ฉ”์ผ ๋ฐœ์†ก
            std::cout << "Sending confirmation email to " 
                      << customerId << std::endl;
        }
    };
}

// ============ Main (์˜์กด์„ฑ ์กฐ๋ฆฝ) ============
int main() {
    // ์–ด๋Œ‘ํ„ฐ ์ƒ์„ฑ
    auto orderRepo = std::make_shared<Adapters::MySQLOrderRepository>();
    auto paymentGateway = std::make_shared<Adapters::StripePaymentGateway>();
    auto notificationService = std::make_shared<Adapters::EmailNotificationService>();
    
    // ์„œ๋น„์Šค ์ƒ์„ฑ (์˜์กด์„ฑ ์ฃผ์ž…)
    Application::OrderService orderService(
        orderRepo, 
        paymentGateway, 
        notificationService
    );
    
    // ์ฃผ๋ฌธ ์ƒ์„ฑ ๋ฐ ์ฒ˜๋ฆฌ
    Domain::Order order(1, "customer123");
    order.addItem("Laptop", 1200.00);
    order.addItem("Mouse", 25.00);
    
    if (orderService.placeOrder(order)) {
        std::cout << "Order placed successfully!" << std::endl;
    }
    
    return 0;
}

ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜์˜ ํ•ต์‹ฌ์€ ๋„๋ฉ”์ธ ๋กœ์ง์ด ์™ธ๋ถ€ ์„ธ๊ณ„์— ๋Œ€ํ•ด ์ „ํ˜€ ๋ชจ๋ฅธ๋‹ค๋Š” ๊ฑฐ์•ผ. MySQL์„ MongoDB๋กœ ๋ฐ”๊พธ๋“ , Stripe๋ฅผ PayPal๋กœ ๋ฐ”๊พธ๋“ , ๋„๋ฉ”์ธ ์ฝ”๋“œ๋Š” ์ „ํ˜€ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์–ด! ๐ŸŽฏ

3๏ธโƒฃ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜ (Event-Driven Architecture)

๋ชจ๋“ˆ ๊ฐ„ ๊ฒฐํ•ฉ๋„๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ํŒจํ„ด์ด์•ผ. ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ๊ฐ„์ ‘์ ์œผ๋กœ ํ†ต์‹ ํ•˜์ง€:

// ============ Event System ============
// Events/Event.h
namespace Events {
    // ๊ธฐ๋ณธ ์ด๋ฒคํŠธ ์ธํ„ฐํŽ˜์ด์Šค
    class IEvent {
    public:
        virtual ~IEvent() = default;
        virtual std::string getType() const = 0;
    };
    
    // ๊ตฌ์ฒด์ ์ธ ์ด๋ฒคํŠธ๋“ค
    class PlayerDiedEvent : public IEvent {
    private:
        int playerId;
        std::string cause;
        
    public:
        PlayerDiedEvent(int id, const std::string& cause)
            : playerId(id), cause(cause) {}
        
        std::string getType() const override { return "PlayerDied"; }
        int getPlayerId() const { return playerId; }
        std::string getCause() const { return cause; }
    };
    
    class ScoreChangedEvent : public IEvent {
    private:
        int playerId;
        int newScore;
        
    public:
        ScoreChangedEvent(int id, int score)
            : playerId(id), newScore(score) {}
        
        std::string getType() const override { return "ScoreChanged"; }
        int getPlayerId() const { return playerId; }
        int getNewScore() const { return newScore; }
    };
}

// Events/EventBus.h
namespace Events {
    class IEventListener {
    public:
        virtual ~IEventListener() = default;
        virtual void onEvent(const IEvent& event) = 0;
    };
    
    class EventBus {
    private:
        std::unordered_map<std::string, std::vector<IEventListener*>> listeners;
        std::queue<std::unique_ptr<IEvent>> eventQueue;
        
    public:
        void subscribe(const std::string& eventType, IEventListener* listener) {
            listeners[eventType].push_back(listener);
        }
        
        void unsubscribe(const std::string& eventType, IEventListener* listener) {
            auto& listenerList = listeners[eventType];
            listenerList.erase(
                std::remove(listenerList.begin(), listenerList.end(), listener),
                listenerList.end()
            );
        }
        
        void publish(std::unique_ptr<IEvent> event) {
            eventQueue.push(std::move(event));
        }
        
        void processEvents() {
            while (!eventQueue.empty()) {
                auto& event = eventQueue.front();
                
                auto it = listeners.find(event->getType());
                if (it != listeners.end()) {
                    for (auto* listener : it->second) {
                        listener->onEvent(*event);
                    }
                }
                
                eventQueue.pop();
            }
        }
    };
}

// ============ Game Systems (์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ) ============
// Systems/ScoreSystem.h
namespace Systems {
    class ScoreSystem : public Events::IEventListener {
    private:
        std::unordered_map<int, int> playerScores;
        Events::EventBus* eventBus;
        
    public:
        ScoreSystem(Events::EventBus* bus) : eventBus(bus) {
            eventBus->subscribe("PlayerDied", this);
        }
        
        void onEvent(const Events::IEvent& event) override {
            if (event.getType() == "PlayerDied") {
                auto& deathEvent = static_cast<const Events::PlayerDiedEvent&>(event);
                int playerId = deathEvent.getPlayerId();
                
                // ์ ์ˆ˜ ๊ฐ์†Œ
                playerScores[playerId] -= 100;
                
                // ์ ์ˆ˜ ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ ๋ฐœํ–‰
                eventBus->publish(
                    std::make_unique<Events::ScoreChangedEvent>(
                        playerId, 
                        playerScores[playerId]
                    )
                );
            }
        }
        
        void addScore(int playerId, int points) {
            playerScores[playerId] += points;
            eventBus->publish(
                std::make_unique<Events::ScoreChangedEvent>(
                    playerId, 
                    playerScores[playerId]
                )
            );
        }
    };
    
    class UISystem : public Events::IEventListener {
    private:
        Events::EventBus* eventBus;
        
    public:
        UISystem(Events::EventBus* bus) : eventBus(bus) {
            eventBus->subscribe("ScoreChanged", this);
            eventBus->subscribe("PlayerDied", this);
        }
        
        void onEvent(const Events::IEvent& event) override {
            if (event.getType() == "ScoreChanged") {
                auto& scoreEvent = static_cast<const Events::ScoreChangedEvent&>(event);
                std::cout << "UI: Player " << scoreEvent.getPlayerId() 
                          << " score updated to " << scoreEvent.getNewScore() 
                          << std::endl;
            }
            else if (event.getType() == "PlayerDied") {
                auto& deathEvent = static_cast<const Events::PlayerDiedEvent&>(event);
                std::cout << "UI: Player " << deathEvent.getPlayerId() 
                          << " died (" << deathEvent.getCause() << ")" 
                          << std::endl;
            }
        }
    };
    
    class AchievementSystem : public Events::IEventListener {
    private:
        Events::EventBus* eventBus;
        
    public:
        AchievementSystem(Events::EventBus* bus) : eventBus(bus) {
            eventBus->subscribe("ScoreChanged", this);
        }
        
        void onEvent(const Events::IEvent& event) override {
            if (event.getType() == "ScoreChanged") {
                auto& scoreEvent = static_cast<const Events::ScoreChangedEvent&>(event);
                
                // ํŠน์ • ์ ์ˆ˜ ๋‹ฌ์„ฑ ์‹œ ์—…์  ํ•ด์ œ
                if (scoreEvent.getNewScore() >= 1000) {
                    std::cout << "Achievement Unlocked: Score Master!" 
                              << std::endl;
                }
            }
        }
    };
}

// ============ Main ============
int main() {
    Events::EventBus eventBus;
    
    // ์‹œ์Šคํ…œ๋“ค ์ƒ์„ฑ (๊ฐ์ž ์ด๋ฒคํŠธ ๊ตฌ๋…)
    Systems::ScoreSystem scoreSystem(&eventBus);
    Systems::UISystem uiSystem(&eventBus);
    Systems::AchievementSystem achievementSystem(&eventBus);
    
    // ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    std::cout << "=== Game Start ===" << std::endl;
    
    // ์ ์ˆ˜ ํš๋“
    scoreSystem.addScore(1, 500);
    eventBus.processEvents();
    
    std::cout << std::endl;
    
    // ๋” ๋งŽ์€ ์ ์ˆ˜ ํš๋“
    scoreSystem.addScore(1, 600);
    eventBus.processEvents();
    
    std::cout << std::endl;
    
    // ํ”Œ๋ ˆ์ด์–ด ์‚ฌ๋ง
    eventBus.publish(
        std::make_unique<Events::PlayerDiedEvent>(1, "fell off cliff")
    );
    eventBus.processEvents();
    
    return 0;
}

์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์˜ ์žฅ์ ์€ ์‹œ์Šคํ…œ๋“ค์ด ์„œ๋กœ๋ฅผ ์ „ํ˜€ ๋ชฐ๋ผ๋„ ๋œ๋‹ค๋Š” ๊ฑฐ์•ผ. ScoreSystem์€ UISystem์ด๋‚˜ AchievementSystem์˜ ์กด์žฌ๋ฅผ ๋ชฐ๋ผ. ๊ทธ๋ƒฅ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๋ฉด, ๊ด€์‹ฌ ์žˆ๋Š” ์‹œ์Šคํ…œ๋“ค์ด ์•Œ์•„์„œ ๋ฐ˜์‘ํ•˜์ง€! ๐ŸŽช

๐ŸŽฏ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜ ํ™œ์šฉ ์‚ฌ๋ก€

โ€ข ๊ฒŒ์ž„ ์—”์ง„ - ์ถฉ๋Œ, ์ž…๋ ฅ, ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋“ฑ ๋‹ค์–‘ํ•œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
โ€ข GUI ํ”„๋ ˆ์ž„์›Œํฌ - ๋ฒ„ํŠผ ํด๋ฆญ, ๋งˆ์šฐ์Šค ์ด๋™ ๋“ฑ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ฒ˜๋ฆฌ
โ€ข ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค - ์„œ๋น„์Šค ๊ฐ„ ๋А์Šจํ•œ ๊ฒฐํ•ฉ ์œ ์ง€
โ€ข ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ - ํ”Œ๋Ÿฌ๊ทธ์ธ๋“ค์ด ์ฝ”์–ด ์‹œ์Šคํ…œ์˜ ์ด๋ฒคํŠธ์— ๋ฐ˜์‘

4๏ธโƒฃ ์—”ํ‹ฐํ‹ฐ ์ปดํฌ๋„ŒํŠธ ์‹œ์Šคํ…œ (ECS)

๊ฒŒ์ž„ ๊ฐœ๋ฐœ์—์„œ ํŠนํžˆ ์ธ๊ธฐ ์žˆ๋Š” ํŒจํ„ด์ด์•ผ. ์ƒ์† ๋Œ€์‹  ์ปดํฌ์ง€์…˜์„ ์‚ฌ์šฉํ•ด์„œ ์œ ์—ฐ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜์ง€:

// ============ Component (๋ฐ์ดํ„ฐ๋งŒ ๋‹ด๋‹น) ============
namespace ECS {
    // ๊ธฐ๋ณธ ์ปดํฌ๋„ŒํŠธ ์ธํ„ฐํŽ˜์ด์Šค
    struct IComponent {
        virtual ~IComponent() = default;
    };
    
    // ์œ„์น˜ ์ปดํฌ๋„ŒํŠธ
    struct TransformComponent : IComponent {
        float x = 0.0f;
        float y = 0.0f;
        float rotation = 0.0f;
    };
    
    // ๋ Œ๋”๋ง ์ปดํฌ๋„ŒํŠธ
    struct SpriteComponent : IComponent {
        std::string texturePath;
        int width = 0;
        int height = 0;
    };
    
    // ๋ฌผ๋ฆฌ ์ปดํฌ๋„ŒํŠธ
    struct PhysicsComponent : IComponent {
        float velocityX = 0.0f;
        float velocityY = 0.0f;
        float mass = 1.0f;
    };
    
    // ์ฒด๋ ฅ ์ปดํฌ๋„ŒํŠธ
    struct HealthComponent : IComponent {
        float current = 100.0f;
        float maximum = 100.0f;
    };
}

// ============ Entity (์ปดํฌ๋„ŒํŠธ ์ปจํ…Œ์ด๋„ˆ) ============
namespace ECS {
    class Entity {
    private:
        int id;
        std::unordered_map<std::type_index, std::unique_ptr<IComponent>> components;
        
    public:
        explicit Entity(int id) : id(id) {}
        
        int getId() const { return id; }
        
        template<typename T>
        T* addComponent() {
            auto component = std::make_unique<T>();
            T* ptr = component.get();
            components[std::type_index(typeid(T))] = std::move(component);
            return ptr;
        }
        
        template<typename T>
        T* getComponent() {
            auto it = components.find(std::type_index(typeid(T)));
            if (it != components.end()) {
                return static_cast<T*>(it->second.get());
            }
            return nullptr;
        }
        
        template<typename T>
        bool hasComponent() const {
            return components.find(std::type_index(typeid(T))) != components.end();
        }
        
        template<typename T>
        void removeComponent() {
            components.erase(std::type_index(typeid(T)));
        }
    };
}

// ============ System (๋กœ์ง ๋‹ด๋‹น) ============
namespace ECS {
    class ISystem {
    public:
        virtual ~ISystem() = default;
        virtual void update(float deltaTime, std::vector<Entity*>& entities) = 0;
    };
    
    // ๋ฌผ๋ฆฌ ์‹œ์Šคํ…œ
    class PhysicsSystem : public ISystem {
    public:
        void update(float deltaTime, std::vector<Entity*>& entities) override {
            for (auto* entity : entities) {
                // Transform๊ณผ Physics ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‘˜ ๋‹ค ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋งŒ ์ฒ˜๋ฆฌ
                if (entity->hasComponent<TransformComponent>() &&
                    entity->hasComponent<PhysicsComponent>()) {
                    
                    auto* transform = entity->getComponent<TransformComponent>();
                    auto* physics = entity->getComponent<PhysicsComponent>();
                    
                    // ์†๋„์— ๋”ฐ๋ผ ์œ„์น˜ ์—…๋ฐ์ดํŠธ
                    transform->x += physics->velocityX * deltaTime;
                    transform->y += physics->velocityY * deltaTime;
                    
                    // ์ค‘๋ ฅ ์ ์šฉ
                    physics->velocityY += 9.8f * deltaTime;
                }
            }
        }
    };
    
    // ๋ Œ๋”๋ง ์‹œ์Šคํ…œ
    class RenderSystem : public ISystem {
    public:
        void update(float deltaTime, std::vector<Entity*>& entities) override {
            for (auto* entity : entities) {
                // Transform๊ณผ Sprite ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋งŒ ๋ Œ๋”๋ง
                if (entity->hasComponent<TransformComponent>() &&
                    entity->hasComponent<SpriteComponent>()) {
                    
                    auto* transform = entity->getComponent<TransformComponent>();
                    auto* sprite = entity->getComponent<SpriteComponent>();
                    
                    std::cout << "Rendering entity " << entity->getId() 
                              << " at (" << transform->x << ", " << transform->y << ")"
                              << std::endl;
                }
            }
        }
    };
    
    // ์ฒด๋ ฅ ์‹œ์Šคํ…œ
    class HealthSystem : public ISystem {
    public:
        void update(float deltaTime, std::vector<Entity*>& entities) override {
            for (auto* entity : entities) {
                if (entity->hasComponent<HealthComponent>()) {
                    auto* health = entity->getComponent<HealthComponent>();
                    
                    // ์ฒด๋ ฅ์ด 0 ์ดํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ์ œ๊ฑฐ (์‹ค์ œ๋กœ๋Š” ์‚ญ์ œ ํ”Œ๋ž˜๊ทธ ์„ค์ •)
                    if (health->current <= 0) {
                        std::cout << "Entity " << entity->getId() 
                                  << " has died!" << std::endl;
                    }
                }
            }
        }
    };
}

// ============ World (์—”ํ‹ฐํ‹ฐ ๊ด€๋ฆฌ์ž) ============
namespace ECS {
    class World {
    private:
        std::vector<std::unique_ptr<Entity>> entities;
        std::vector<std::unique_ptr<ISystem>> systems;
        int nextEntityId = 0;
        
    public:
        Entity* createEntity() {
            auto entity = std::make_unique<Entity>(nextEntityId++);
            Entity* ptr = entity.get();
            entities.push_back(std::move(entity));
            return ptr;
        }
        
        void addSystem(std::unique_ptr<ISystem> system) {
            systems.push_back(std::move(system));
        }
        
        void update(float deltaTime) {
            // ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ ํฌ์ธํ„ฐ ์ˆ˜์ง‘
            std::vector<Entity*> entityPtrs;
            for (auto& entity : entities) {
                entityPtrs.push_back(entity.get());
            }
            
            // ๊ฐ ์‹œ์Šคํ…œ ์—…๋ฐ์ดํŠธ
            for (auto& system : systems) {
                system->update(deltaTime, entityPtrs);
            }
        }
    };
}

// ============ Main ============
int main() {
    ECS::World world;
    
    // ์‹œ์Šคํ…œ ์ถ”๊ฐ€
    world.addSystem(std::make_unique<ECS::PhysicsSystem>());
    world.addSystem(std::make_unique<ECS::RenderSystem>());
    world.addSystem(std::make_unique<ECS::HealthSystem>());
    
    // ํ”Œ๋ ˆ์ด์–ด ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ
    auto* player = world.createEntity();
    auto* playerTransform = player->addComponent<ECS::TransformComponent>();
    playerTransform->x = 100.0f;
    playerTransform->y = 200.0f;
    
    auto* playerSprite = player->addComponent<ECS::SpriteComponent>();
    playerSprite->texturePath = "player.png";
    
    auto* playerPhysics = player->addComponent<ECS::PhysicsComponent>();
    playerPhysics->velocityX = 5.0f;
    
    auto* playerHealth = player->addComponent<ECS::HealthComponent>();
    playerHealth->current = 100.0f;
    
    // ์  ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ (๋ฌผ๋ฆฌ ์—†์Œ)
    auto* enemy = world.createEntity();
    auto* enemyTransform = enemy->addComponent<ECS::TransformComponent>();
    enemyTransform->x = 300.0f;
    enemyTransform->y = 200.0f;
    
    auto* enemySprite = enemy->addComponent<ECS::SpriteComponent>();
    enemySprite->texturePath = "enemy.png";
    
    auto* enemyHealth = enemy->addComponent<ECS::HealthComponent>();
    enemyHealth->current = 50.0f;
    
    // ๊ฒŒ์ž„ ๋ฃจํ”„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    std::cout << "=== Frame 1 ===" << std::endl;
    world.update(0.016f);  // 60 FPS
    
    std::cout << "\n=== Frame 2 ===" << std::endl;
    world.update(0.016f);
    
    return 0;
}

ECS์˜ ํ•ต์‹ฌ์€ ๋ฐ์ดํ„ฐ(Component)์™€ ๋กœ์ง(System)์˜ ์™„์ „ํ•œ ๋ถ„๋ฆฌ์•ผ. ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ๊ทธ๋ƒฅ ์ƒˆ ์ปดํฌ๋„ŒํŠธ์™€ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค๋ฉด ๋ผ. ๊ธฐ์กด ์ฝ”๋“œ๋Š” ์ „ํ˜€ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์–ด! ๐ŸŽฎ

๐Ÿ’ก ECS๊ฐ€ ํŠนํžˆ ์œ ์šฉํ•œ ๊ฒฝ์šฐ

โ€ข ์—”ํ‹ฐํ‹ฐ ์ข…๋ฅ˜๊ฐ€ ๋งค์šฐ ๋‹ค์–‘ํ•œ ๊ฒŒ์ž„ (RPG, ์ƒŒ๋“œ๋ฐ•์Šค ๋“ฑ)
โ€ข ๋Ÿฐํƒ€์ž„์— ๋™์ ์œผ๋กœ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€/์ œ๊ฑฐํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
โ€ข ๋ฐ์ดํ„ฐ ์ง€ํ–ฅ ์„ค๊ณ„๋กœ ์บ์‹œ ํšจ์œจ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๊ณ  ์‹ถ์„ ๋•Œ
โ€ข ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ํ™œ์šฉํ•œ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•  ๋•Œ

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ํ™•์žฅ์„ฑ

์•„ํ‚คํ…์ฒ˜๊ฐ€ ์•„๋ฌด๋ฆฌ ์ข‹์•„๋„ ์„ฑ๋Šฅ์ด ๋‚˜์˜๋ฉด ์†Œ์šฉ์—†์ง€! ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ ์„ฑ๋Šฅ์„ ์œ ์ง€ํ•˜๋Š” ์ „๋žต๋“ค์„ ์•Œ์•„๋ณด์ž. ๐Ÿš€

1๏ธโƒฃ ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ์ตœ์ ํ™”

๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์—์„œ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์€ ์ƒ์‚ฐ์„ฑ์— ์ง๊ฒฐ๋ผ. ๋ช‡ ๊ฐ€์ง€ ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ์†Œ๊ฐœํ• ๊ฒŒ:

์ „๋ฐฉ ์„ ์–ธ ์ ๊ทน ํ™œ์šฉ

// โŒ ๋‚˜์œ ์˜ˆ: ํ—ค๋”์—์„œ ๋ชจ๋“  ๊ฒƒ์„ include
// Widget.h
#include "HeavyClass1.h"
#include "HeavyClass2.h"
#include "HeavyClass3.h"

class Widget {
private:
    HeavyClass1 obj1;
    HeavyClass2 obj2;
    HeavyClass3 obj3;
};

// โœ… ์ข‹์€ ์˜ˆ: ์ „๋ฐฉ ์„ ์–ธ + Pimpl
// Widget.h
class Widget {
private:
    class Impl;
    std::unique_ptr<Impl> pImpl;
public:
    Widget();
    ~Widget();
};

// Widget.cpp์—์„œ๋งŒ include
#include "Widget.h"
#include "HeavyClass1.h"
#include "HeavyClass2.h"
#include "HeavyClass3.h"

class Widget::Impl {
    HeavyClass1 obj1;
    HeavyClass2 obj2;
    HeavyClass3 obj3;
};

Precompiled Headers (PCH) ํ™œ์šฉ

// pch.h - ์ž์ฃผ ์“ฐ์ด์ง€๋งŒ ๊ฑฐ์˜ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํ—ค๋”๋“ค
#ifndef PCH_H
#define PCH_H

// ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <algorithm>
#include <unordered_map>

// ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
#include <boost/filesystem.hpp>
#include <fmt/format.h>

// ํ”„๋กœ์ ํŠธ ๊ณตํ†ต ํ—ค๋”
#include "Common/Types.h"
#include "Common/Macros.h"

#endif // PCH_H
# CMakeLists.txt - PCH ์„ค์ •
target_precompile_headers(MyProject PRIVATE pch.h)

Unity Builds (Jumbo Builds)

# CMakeLists.txt - Unity Build ํ™œ์„ฑํ™”
set_target_properties(MyProject PROPERTIES
    UNITY_BUILD ON
    UNITY_BUILD_BATCH_SIZE 16
)

# ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์—ฌ๋Ÿฌ .cpp ํŒŒ์ผ์„ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์„œ ์ปดํŒŒ์ผ
# ์ปดํŒŒ์ผ ์‹œ๊ฐ„์ด ํฌ๊ฒŒ ๋‹จ์ถ•๋˜์ง€๋งŒ, ์ฆ๋ถ„ ๋นŒ๋“œ๋Š” ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์Œ
2๏ธโƒฃ ๋Ÿฐํƒ€์ž„ ์„ฑ๋Šฅ ์ตœ์ ํ™”

๊ฐ€์ƒ ํ•จ์ˆ˜ ์˜ค๋ฒ„ํ—ค๋“œ ์ค„์ด๊ธฐ

// ๊ฐ€์ƒ ํ•จ์ˆ˜๋Š” ํŽธ๋ฆฌํ•˜์ง€๋งŒ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์žˆ์–ด
class Base {
public:
    virtual void process() = 0;  // ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ…Œ์ด๋ธ” ์กฐํšŒ ํ•„์š”
};

// ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ CRTP (Curiously Recurring Template Pattern) ์‚ฌ์šฉ
template<typename Derived>
class Base {
public:
    void process() {
        static_cast<Derived*>(this)->processImpl();
    }
};

class Derived : public Base<Derived> {
public:
    void processImpl() {
        // ์‹ค์ œ ๊ตฌํ˜„
        // ์ปดํŒŒ์ผ ํƒ€์ž„์— ๊ฒฐ์ •๋˜๋ฏ€๋กœ ๊ฐ€์ƒ ํ•จ์ˆ˜ ์˜ค๋ฒ„ํ—ค๋“œ ์—†์Œ!
    }
};

// ์‚ฌ์šฉ
template<typename T>
void doWork(Base<T>& obj) {
    obj.process();  // ์ธ๋ผ์ธ ๊ฐ€๋Šฅ!
}

๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์ตœ์ ํ™”

// โŒ ๋‚˜์œ ์˜ˆ: ๋นˆ๋ฒˆํ•œ ๋™์  ํ• ๋‹น
void processData() {
    for (int i = 0; i < 1000000; ++i) {
        auto data = std::make_unique<Data>();  // ๋งค๋ฒˆ new!
        data->process();
    }
}

// โœ… ์ข‹์€ ์˜ˆ: ๊ฐ์ฒด ํ’€ ์‚ฌ์šฉ
template<typename T>
class ObjectPool {
private:
    std::vector<std::unique_ptr<T>> pool;
    std::vector<T*> available;
    
public:
    ObjectPool(size_t initialSize) {
        pool.reserve(initialSize);
        available.reserve(initialSize);
        
        for (size_t i = 0; i < initialSize; ++i) {
            pool.push_back(std::make_unique<T>());
            available.push_back(pool.back().get());
        }
    }
    
    T* acquire() {
        if (available.empty()) {
            pool.push_back(std::make_unique<T>());
            return pool.back().get();
        }
        
        T* obj = available.back();
        available.pop_back();
        return obj;
    }
    
    void release(T* obj) {
        available.push_back(obj);
    }
};

// ์‚ฌ์šฉ
void processData() {
    ObjectPool<Data> pool(1000);
    
    for (int i = 0; i < 1000000; ++i) {
        Data* data = pool.acquire();  // ์žฌ์‚ฌ์šฉ!
        data->process();
        pool.release(data);
    }
}

์บ์‹œ ์นœํ™”์ ์ธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ

// โŒ ๋‚˜์œ ์˜ˆ: ์บ์‹œ ๋ฏธ์Šค๊ฐ€ ๋งŽ์€ ๊ตฌ์กฐ
struct Entity {
    int id;
    std::string name;
    Vector3 position;
    Vector3 velocity;
    Mesh* mesh;
    Material* material;
    // ... ๋งŽ์€ ๋ฐ์ดํ„ฐ
};

std::vector<Entity> entities;  // ๊ฐ Entity๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ํฉ์–ด์ ธ ์žˆ์Œ

void updatePhysics() {
    for (auto& entity : entities) {
        entity.position += entity.velocity;  // ์บ์‹œ ๋ฏธ์Šค ๋ฐœ์ƒ!
    }
}

// โœ… ์ข‹์€ ์˜ˆ: ๋ฐ์ดํ„ฐ ์ง€ํ–ฅ ์„ค๊ณ„ (Data-Oriented Design)
struct PhysicsData {
    std::vector<Vector3> positions;
    std::vector<Vector3> velocities;
};

PhysicsData physicsData;

void updatePhysics() {
    // ์—ฐ์†๋œ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ์œผ๋กœ ์บ์‹œ ํšจ์œจ ๊ทน๋Œ€ํ™”!
    for (size_t i = 0; i < physicsData.positions.size(); ++i) {
        physicsData.positions[i] += physicsData.velocities[i];
    }
    
    // SIMD ๋ช…๋ น์–ด๋กœ ๋”์šฑ ์ตœ์ ํ™” ๊ฐ€๋Šฅ
}
3๏ธโƒฃ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ์™€ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ

ํ˜„๋Œ€ CPU๋Š” ์—ฌ๋Ÿฌ ์ฝ”์–ด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋‹ˆ, ์ด๋ฅผ ํ™œ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์†ํ•ด์•ผ! ๐Ÿ”ฅ

// ์ž‘์—… ํ ๊ธฐ๋ฐ˜ ์Šค๋ ˆ๋“œ ํ’€
class ThreadPool {
private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex queueMutex;
    std::condition_variable condition;
    bool stop = false;
    
public:
    ThreadPool(size_t numThreads) {
        for (size_t i = 0; i < numThreads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    
                    {
                        std::unique_lock<std::mutex> lock(queueMutex);
                        condition.wait(lock, [this] {
                            return stop || !tasks.empty();
                        });
                        
                        if (stop && tasks.empty()) {
                            return;
                        }
                        
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    
                    task();
                }
            });
        }
    }
    
    template<typename F>
    void enqueue(F&& f) {
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            tasks.emplace(std::forward<F>(f));
        }
        condition.notify_one();
    }
    
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            stop = true;
        }
        condition.notify_all();
        
        for (auto& worker : workers) {
            worker.join();
        }
    }
};

// ์‚ฌ์šฉ ์˜ˆ์‹œ
void parallelProcessing() {
    ThreadPool pool(std::thread::hardware_concurrency());
    
    std::vector<int> data(1000000);
    std::iota(data.begin(), data.end(), 0);
    
    const size_t chunkSize = data.size() / pool.numThreads();
    std::vector<std::future<long long>> futures;
    
    for (size_t i = 0; i < pool.numThreads(); ++i) {
        size_t start = i * chunkSize;
        size_t end = (i == pool.numThreads() - 1) ? data.size() : (i + 1) * chunkSize;
        
        futures.push_back(std::async(std::launch::async, [&data, start, end]() {
            long long sum = 0;
            for (size_t j = start; j < end; ++j) {
                sum += data[j] * data[j];
            }
            return sum;
        }));
    }
    
    long long totalSum = 0;
    for (auto& future : futures) {
        totalSum += future.get();
    }
    
    std::cout << "Total sum: " << totalSum << std::endl;
}

Lock-Free ์ž๋ฃŒ๊ตฌ์กฐ

// Lock-Free ํ (๋‹จ์ˆœํ™”๋œ ๋ฒ„์ „)
template<typename T>
class LockFreeQueue {
private:
    struct Node {
        T data;
        std::atomic<Node*> next;
        Node(const T& data) : data(data), next(nullptr) {}
    };
    
    std::atomic<Node*> head;
    std::atomic<Node*> tail;
    
public:
    LockFreeQueue() {
        Node* dummy = new Node(T());
        head.store(dummy);
        tail.store(dummy);
    }
    
    void enqueue(const T& data) {
        Node* newNode = new Node(data);
        Node* oldTail;
        
        while (true) {
            oldTail = tail.load();
            Node* next = oldTail->next.load();
            
            if (oldTail == tail.load()) {
                if (next == nullptr) {
                    if (oldTail->next.compare_exchange_weak(next, newNode)) {
                        break;
                    }
                } else {
                    tail.compare_exchange_weak(oldTail, next);
                }
            }
        }
        
        tail.compare_exchange_weak(oldTail, newNode);
    }
    
    bool dequeue(T& result) {
        Node* oldHead;
        
        while (true) {
            oldHead = head.load();
            Node* oldTail = tail.load();
            Node* next = oldHead->next.load();
            
            if (oldHead == head.load()) {
                if (oldHead == oldTail) {
                    if (next == nullptr) {
                        return false;  // ํ๊ฐ€ ๋น„์–ด์žˆ์Œ
                    }
                    tail.compare_exchange_weak(oldTail, next);
                } else {
                    result = next->data;
                    if (head.compare_exchange_weak(oldHead, next)) {
                        break;
                    }
                }
            }
        }
        
        delete oldHead;
        return true;
    }
};

// ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋ฎคํ…์Šค ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ!
LockFreeQueue<Task> taskQueue;

// Producer ์Šค๋ ˆ๋“œ
void producer() {
    for (int i = 0; i < 1000; ++i) {
        taskQueue.enqueue(Task{i});
    }
}

// Consumer ์Šค๋ ˆ๋“œ
void consumer() {
    Task task;
    while (taskQueue.dequeue(task)) {
        task.execute();
    }
}

โš ๏ธ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ฃผ์˜์‚ฌํ•ญ

โ€ข ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค ์กฐ์‹ฌ! ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์•ˆ ๋ผ
โ€ข ๋ฐ๋“œ๋ฝ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ๋ฎคํ…์Šค ํš๋“ ์ˆœ์„œ๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ์œ ์ง€ํ•ด
โ€ข Lock-Free ์ž๋ฃŒ๊ตฌ์กฐ๋Š” ๊ตฌํ˜„์ด ๋ณต์žกํ•˜๋‹ˆ, ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(TBB, Folly ๋“ฑ) ์‚ฌ์šฉ ๊ถŒ์žฅ
โ€ข ๊ณผ๋„ํ•œ ๋ณ‘๋ ฌํ™”๋Š” ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์„ ๋–จ์–ด๋œจ๋ฆด ์ˆ˜ ์žˆ์–ด (์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ ์˜ค๋ฒ„ํ—ค๋“œ)

๐Ÿ› ๏ธ ๋นŒ๋“œ ์‹œ์Šคํ…œ๊ณผ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ๋Š” ๋นŒ๋“œ ์‹œ์Šคํ…œ ์„ค์ •๋„ ์ค‘์š”ํ•ด. ์ œ๋Œ€๋กœ ๊ตฌ์„ฑํ•˜์ง€ ์•Š์œผ๋ฉด ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ํฌ๊ฒŒ ๋–จ์–ด์ง€๊ฑฐ๋“ ! ๐Ÿ—๏ธ

1๏ธโƒฃ CMake๋ฅผ ํ™œ์šฉํ•œ ๋ชจ๋“ˆํ™”
# ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ
MyProject/
โ”œโ”€โ”€ CMakeLists.txt              # ๋ฃจํŠธ CMake
โ”œโ”€โ”€ cmake/
โ”‚   โ””โ”€โ”€ modules/
โ”‚       โ””โ”€โ”€ FindCustomLib.cmake
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ”œโ”€โ”€ core/
โ”‚   โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ”‚   โ”œโ”€โ”€ Engine.h
โ”‚   โ”‚   โ””โ”€โ”€ Engine.cpp
โ”‚   โ”œโ”€โ”€ graphics/
โ”‚   โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ”‚   โ”œโ”€โ”€ Renderer.h
โ”‚   โ”‚   โ””โ”€โ”€ Renderer.cpp
โ”‚   โ””โ”€โ”€ audio/
โ”‚       โ”œโ”€โ”€ CMakeLists.txt
โ”‚       โ”œโ”€โ”€ AudioEngine.h
โ”‚       โ””โ”€โ”€ AudioEngine.cpp
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ””โ”€โ”€ CoreTests.cpp
โ””โ”€โ”€ external/
    โ””โ”€โ”€ CMakeLists.txt
# ๋ฃจํŠธ CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)

# C++ ํ‘œ์ค€ ์„ค์ •
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# ๋นŒ๋“œ ํƒ€์ž…๋ณ„ ์ปดํŒŒ์ผ ์˜ต์…˜
if(MSVC)
    add_compile_options(/W4 /WX)
    if(CMAKE_BUILD_TYPE STREQUAL "Release")
        add_compile_options(/O2)
    endif()
else()
    add_compile_options(-Wall -Wextra -Wpedantic -Werror)
    if(CMAKE_BUILD_TYPE STREQUAL "Release")
        add_compile_options(-O3)
    endif()
endif()

# ๋ชจ๋“ˆ ๊ฒฝ๋กœ ์ถ”๊ฐ€
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")

# ์„œ๋ธŒ๋””๋ ‰ํ† ๋ฆฌ ์ถ”๊ฐ€
add_subdirectory(external)
add_subdirectory(src)

# ํ…Œ์ŠคํŠธ ํ™œ์„ฑํ™”
enable_testing()
add_subdirectory(tests)
# src/core/CMakeLists.txt
add_library(Core STATIC
    Engine.cpp
    Engine.h
    Logger.cpp
    Logger.h
)

# ์ธํ„ฐํŽ˜์ด์Šค ํ—ค๋” ๊ฒฝ๋กœ ์„ค์ •
target_include_directories(Core
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
        $<INSTALL_INTERFACE:include>
)

# ๋‹ค๋ฅธ ๋ชจ๋“ˆ์— ๋Œ€ํ•œ ์˜์กด์„ฑ
target_link_libraries(Core
    PUBLIC
        Graphics
        Audio
)

# ์ปดํŒŒ์ผ ์˜ต์…˜
target_compile_features(Core PUBLIC cxx_std_20)
# src/graphics/CMakeLists.txt
add_library(Graphics STATIC
    Renderer.cpp
    Renderer.h
    Shader.cpp
    Shader.h
)

target_include_directories(Graphics
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
        $<INSTALL_INTERFACE:include>
)

# ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋งํฌ
find_package(OpenGL REQUIRED)
target_link_libraries(Graphics
    PUBLIC
        OpenGL::GL
)
2๏ธโƒฃ ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค
LargeProject/
โ”œโ”€โ”€ .github/                    # GitHub Actions CI/CD
โ”‚   โ””โ”€โ”€ workflows/
โ”‚       โ””โ”€โ”€ ci.yml
โ”œโ”€โ”€ cmake/                      # CMake ๋ชจ๋“ˆ ๋ฐ ์Šคํฌ๋ฆฝํŠธ
โ”‚   โ”œโ”€โ”€ modules/
โ”‚   โ””โ”€โ”€ toolchains/
โ”œโ”€โ”€ docs/                       # ๋ฌธ์„œ
โ”‚   โ”œโ”€โ”€ api/
โ”‚   โ””โ”€โ”€ guides/
โ”œโ”€โ”€ include/                    # ๊ณต๊ฐœ ํ—ค๋” ํŒŒ์ผ
โ”‚   โ””โ”€โ”€ myproject/
โ”‚       โ”œโ”€โ”€ core/
โ”‚       โ”œโ”€โ”€ graphics/
โ”‚       โ””โ”€โ”€ audio/
โ”œโ”€โ”€ src/                        # ๊ตฌํ˜„ ํŒŒ์ผ
โ”‚   โ”œโ”€โ”€ core/
โ”‚   โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ”‚   โ”œโ”€โ”€ private/          # ๋‚ด๋ถ€ ํ—ค๋”
โ”‚   โ”‚   โ””โ”€โ”€ *.cpp
โ”‚   โ”œโ”€โ”€ graphics/
โ”‚   โ””โ”€โ”€ audio/
โ”œโ”€โ”€ tests/                      # ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
โ”‚   โ”œโ”€โ”€ unit/
โ”‚   โ”œโ”€โ”€ integration/
โ”‚   โ””โ”€โ”€ performance/
โ”œโ”€โ”€ examples/                   # ์˜ˆ์ œ ์ฝ”๋“œ
โ”‚   โ”œโ”€โ”€ basic/
โ”‚   โ””โ”€โ”€ advanced/
โ”œโ”€โ”€ tools/                      # ๊ฐœ๋ฐœ ๋„๊ตฌ
โ”‚   โ”œโ”€โ”€ code_generator/
โ”‚   โ””โ”€โ”€ asset_processor/
โ”œโ”€โ”€ external/                   # ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ””โ”€โ”€ vendor/
โ”œโ”€โ”€ scripts/                    # ๋นŒ๋“œ/๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ
โ”‚   โ”œโ”€โ”€ build.sh
โ”‚   โ””โ”€โ”€ deploy.sh
โ”œโ”€โ”€ .clang-format              # ์ฝ”๋“œ ํฌ๋งทํŒ… ๊ทœ์น™
โ”œโ”€โ”€ .clang-tidy                # ์ •์  ๋ถ„์„ ๊ทœ์น™
โ”œโ”€โ”€ CMakeLists.txt             # ๋ฃจํŠธ CMake
โ”œโ”€โ”€ CMakePresets.json          # CMake ํ”„๋ฆฌ์…‹
โ”œโ”€โ”€ conanfile.txt              # Conan ์˜์กด์„ฑ
โ””โ”€โ”€ README.md

๐Ÿ“ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ์„ค๊ณ„ ์›์น™

1. ๊ณต๊ฐœ/๋น„๊ณต๊ฐœ ๋ถ„๋ฆฌ - include/๋Š” ๊ณต๊ฐœ API๋งŒ, src/private/๋Š” ๋‚ด๋ถ€ ๊ตฌํ˜„
2. ๋ชจ๋“ˆ๋ณ„ ๋…๋ฆฝ์„ฑ - ๊ฐ ๋ชจ๋“ˆ์ด ์ž์ฒด CMakeLists.txt๋ฅผ ๊ฐ€์ง
3. ํ…Œ์ŠคํŠธ ๊ทผ์ ‘์„ฑ - ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๋ณ„๋„ ๋””๋ ‰ํ† ๋ฆฌ์— ๋ชจ๋“ˆ๋ณ„๋กœ ๊ตฌ์„ฑ
4. ๋„๊ตฌ ๋ถ„๋ฆฌ - ๋นŒ๋“œ ๋„๊ตฌ, ๊ฐœ๋ฐœ ๋„๊ตฌ๋Š” ๋ณ„๋„ ๋””๋ ‰ํ† ๋ฆฌ
5. ๋ฌธ์„œํ™” - docs/์— API ๋ฌธ์„œ์™€ ๊ฐ€์ด๋“œ ํฌํ•จ

3๏ธโƒฃ ์ง€์†์  ํ†ตํ•ฉ (CI/CD) ์„ค์ •
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  build-linux:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y cmake ninja-build
    
    - name: Configure CMake
      run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
    
    - name: Build
      run: cmake --build build --config Release
    
    - name: Run tests
      run: ctest --test-dir build --output-on-failure
  
  build-windows:
    runs-on: windows-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Configure CMake
      run: cmake -B build -DCMAKE_BUILD_TYPE=Release
    
    - name: Build
      run: cmake --build build --config Release
    
    - name: Run tests
      run: ctest --test-dir build --output-on-failure -C Release
  
  code-quality:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Run clang-tidy
      run: |
        cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
        clang-tidy src/**/*.cpp -p build
    
    - name: Run clang-format
      run: |
        find src include -name '*.cpp' -o -name '*.h' | xargs clang-format --dry-run --Werror

CI/CD๋ฅผ ์„ค์ •ํ•ด๋‘๋ฉด ์ฝ”๋“œ๋ฅผ ํ‘ธ์‹œํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•ด์ค˜. ๋ฒ„๊ทธ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ •๋ง ์œ ์šฉํ•ด! ๐ŸŽฏ

๐Ÿ“š ์‹ค์ „ ์‚ฌ๋ก€ ์—ฐ๊ตฌ

์ด๋ก ๋งŒ ๋ณด๋ฉด ์žฌ๋ฏธ์—†์œผ๋‹ˆ, ์‹ค์ œ ๋Œ€๊ทœ๋ชจ C++ ํ”„๋กœ์ ํŠธ๋“ค์ด ์–ด๋–ป๊ฒŒ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ–ˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž! ๐Ÿ”

1๏ธโƒฃ Chromium (์›น ๋ธŒ๋ผ์šฐ์ €)

๊ตฌ๊ธ€์˜ Chromium ํ”„๋กœ์ ํŠธ๋Š” ์ˆ˜์ฒœ๋งŒ ์ค„์˜ C++ ์ฝ”๋“œ๋กœ ์ด๋ฃจ์–ด์ง„ ๊ฑฐ๋Œ€ํ•œ ์‹œ์Šคํ…œ์ด์•ผ. ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ• ๊นŒ?

๐ŸŒ Chromium์˜ ์•„ํ‚คํ…์ฒ˜ ํŠน์ง•

๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค ์•„ํ‚คํ…์ฒ˜ - ๊ฐ ํƒญ์ด ๋ณ„๋„ ํ”„๋กœ์„ธ์Šค๋กœ ์‹คํ–‰๋ผ์„œ ์•ˆ์ •์„ฑ ํ–ฅ์ƒ
์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์„ค๊ณ„ - base, net, content, blink ๋“ฑ ๋ช…ํ™•ํ•œ ๋ ˆ์ด์–ด ๋ถ„๋ฆฌ
Mojo IPC - ํ”„๋กœ์„ธ์Šค ๊ฐ„ ํ†ต์‹ ์„ ์œ„ํ•œ ์ž์ฒด IPC ์‹œ์Šคํ…œ
GN ๋นŒ๋“œ ์‹œ์Šคํ…œ - ๋น ๋ฅธ ๋นŒ๋“œ๋ฅผ ์œ„ํ•œ ์ปค์Šคํ…€ ๋นŒ๋“œ ๋„๊ตฌ
Jumbo Builds - ์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ํ•ฉ์ณ์„œ ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ๋‹จ์ถ•

// Chromium ์Šคํƒ€์ผ์˜ ์ปดํฌ๋„ŒํŠธ ๊ตฌ์กฐ ์˜ˆ์‹œ
namespace content {

// ๊ณต๊ฐœ API (content/public/browser/)
class CONTENT_EXPORT WebContents {
public:
    virtual ~WebContents() = default;
    virtual void LoadURL(const GURL& url) = 0;
    virtual RenderFrameHost* GetMainFrame() = 0;
    
    // ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ
    static std::unique_ptr<WebContents> Create(
        const CreateParams& params);
};

// ๋‚ด๋ถ€ ๊ตฌํ˜„ (content/browser/)
class WebContentsImpl : public WebContents {
private:
    std::unique_ptr<NavigationController> controller_;
    std::unique_ptr<RenderFrameHostManager> frame_manager_;
    
public:
    void LoadURL(const GURL& url) override {
        controller_->LoadURL(url);
    }
    
    RenderFrameHost* GetMainFrame() override {
        return frame_manager_->GetMainFrame();
    }
};

}  // namespace content
2๏ธโƒฃ Unreal Engine (๊ฒŒ์ž„ ์—”์ง„)

์–ธ๋ฆฌ์–ผ ์—”์ง„์€ ๊ฒŒ์ž„ ๊ฐœ๋ฐœ์ž๋“ค์ด ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ๋Š” ์—”์ง„ ์ค‘ ํ•˜๋‚˜์•ผ. ๋ชจ๋“ˆ ์‹œ์Šคํ…œ์ด ์ •๋ง ์ž˜ ๋˜์–ด ์žˆ์–ด!

๐ŸŽฎ Unreal Engine์˜ ์•„ํ‚คํ…์ฒ˜ ํŠน์ง•

๋ชจ๋“ˆ ์‹œ์Šคํ…œ - ๊ฐ ๊ธฐ๋Šฅ์ด ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ๋กœ ๊ตฌ์„ฑ (Core, Engine, Renderer ๋“ฑ)
๋ฆฌํ”Œ๋ ‰์…˜ ์‹œ์Šคํ…œ - UCLASS, UPROPERTY ๋งคํฌ๋กœ๋กœ ๋Ÿฐํƒ€์ž„ ํƒ€์ž… ์ •๋ณด ์ œ๊ณต
๋ธ”๋ฃจํ”„๋ฆฐํŠธ - ๋น„์ฃผ์–ผ ์Šคํฌ๋ฆฝํŒ…๊ณผ C++ ์ฝ”๋“œ์˜ ์™„๋ฒฝํ•œ ํ†ตํ•ฉ
๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ - UObject ๊ธฐ๋ฐ˜ ์ž๋™ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ
์—๋””ํ„ฐ ํ†ตํ•ฉ - ์—”์ง„๊ณผ ์—๋””ํ„ฐ๊ฐ€ ๊ฐ™์€ ์ฝ”๋“œ๋ฒ ์ด์Šค ๊ณต์œ 

// Unreal Engine ์Šคํƒ€์ผ์˜ ๋ชจ๋“ˆ ์ •์˜
// MyGameModule.h
#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"

class FMyGameModule : public IModuleInterface
{
public:
    // ๋ชจ๋“ˆ ์‹œ์ž‘ ์‹œ ํ˜ธ์ถœ
    virtual void StartupModule() override;
    
    // ๋ชจ๋“ˆ ์ข…๋ฃŒ ์‹œ ํ˜ธ์ถœ
    virtual void ShutdownModule() override;
};

// MyGameModule.cpp
#include "MyGameModule.h"

void FMyGameModule::StartupModule()
{
    UE_LOG(LogTemp, Log, TEXT("MyGame Module Started"));
    
    // ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™” ์ฝ”๋“œ
}

void FMyGameModule::ShutdownModule()
{
    UE_LOG(LogTemp, Log, TEXT("MyGame Module Shutdown"));
    
    // ์ •๋ฆฌ ์ฝ”๋“œ
}

// ๋ชจ๋“ˆ ๋“ฑ๋ก
IMPLEMENT_MODULE(FMyGameModule, MyGame)

// ๊ฒŒ์ž„ ์˜ค๋ธŒ์ ํŠธ ์ •์˜ (๋ฆฌํ”Œ๋ ‰์…˜ ํ™œ์šฉ)
UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
    GENERATED_BODY()
    
public:
    // ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅ
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
    float Health;
    
    // ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์—์„œ ํ˜ธ์ถœ ๊ฐ€๋Šฅ
    UFUNCTION(BlueprintCallable, Category = "Actions")
    void TakeDamage(float Damage);
    
protected:
    virtual void BeginPlay() override;
    virtual void Tick(float DeltaTime) override;
};
3๏ธโƒฃ LLVM (์ปดํŒŒ์ผ๋Ÿฌ ์ธํ”„๋ผ)

LLVM์€ ๋ชจ๋“ˆํ™”์˜ ๊ต๊ณผ์„œ ๊ฐ™์€ ํ”„๋กœ์ ํŠธ์•ผ. ์ปดํŒŒ์ผ๋Ÿฌ์˜ ๊ฐ ๋‹จ๊ณ„๊ฐ€ ๋…๋ฆฝ์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด!

๐Ÿ”ง LLVM์˜ ์•„ํ‚คํ…์ฒ˜ ํŠน์ง•

3๋‹จ๊ณ„ ์„ค๊ณ„ - Frontend (Clang) โ†’ Optimizer (LLVM Core) โ†’ Backend (์ฝ”๋“œ ์ƒ์„ฑ)
IR (Intermediate Representation) - ์–ธ์–ด ๋…๋ฆฝ์ ์ธ ์ค‘๊ฐ„ ํ‘œํ˜„
Pass ์‹œ์Šคํ…œ - ์ตœ์ ํ™”๋ฅผ ๋…๋ฆฝ์ ์ธ Pass๋กœ ๊ตฌํ˜„
ํ”Œ๋Ÿฌ๊ทธ์ธ ์•„ํ‚คํ…์ฒ˜ - ์ƒˆ๋กœ์šด ์ตœ์ ํ™”๋‚˜ ๋ฐฑ์—”๋“œ๋ฅผ ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธฐ๋ฐ˜ - ๊ฐ ๊ธฐ๋Šฅ์ด ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

// LLVM ์Šคํƒ€์ผ์˜ Pass ๊ตฌํ˜„
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"

namespace {
    // ํ•จ์ˆ˜ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ฐ„๋‹จํ•œ Pass
    struct FunctionInfoPass : public llvm::FunctionPass {
        static char ID;
        FunctionInfoPass() : FunctionPass(ID) {}
        
        bool runOnFunction(llvm::Function &F) override {
            llvm::errs() << "Function: " << F.getName() << "\n";
            llvm::errs() << "  Basic Blocks: " << F.size() << "\n";
            
            unsigned InstCount = 0;
            for (auto &BB : F) {
                InstCount += BB.size();
            }
            llvm::errs() << "  Instructions: " << InstCount << "\n";
            
            return false;  // ํ•จ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ
        }
    };
}

char FunctionInfoPass::ID = 0;

// Pass ๋“ฑ๋ก
static llvm::RegisterPass<FunctionInfoPass> X(
    "function-info",
    "Function Information Pass",
    false,  // CFG๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ
    true    // ๋ถ„์„ Pass
);

LLVM์˜ ํ•ต์‹ฌ์€ ๊ฐ ๋‹จ๊ณ„๊ฐ€ ์™„์ „ํžˆ ๋…๋ฆฝ์ ์ด๋ผ๋Š” ๊ฑฐ์•ผ. ์ƒˆ๋กœ์šด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด Frontend๋งŒ ์ž‘์„ฑํ•˜๋ฉด ๋˜๊ณ , ์ƒˆ๋กœ์šด CPU ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ง€์›ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Backend๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ผ! ๐ŸŽฏ

๐ŸŽ“ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์“ฐ๋Š” ํŒ

์ด๋ก ๊ณผ ์˜ˆ์ œ๋ฅผ ๋งŽ์ด ๋ดค์œผ๋‹ˆ, ์ด์ œ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์šฉ์ ์ธ ํŒ๋“ค์„ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ! ๐Ÿ’ผ

1๏ธโƒฃ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

โœ… ์•„ํ‚คํ…์ฒ˜ ๊ด€์ 

โ–ก ์ƒˆ๋กœ์šด ํด๋ž˜์Šค/๋ชจ๋“ˆ์ด SOLID ์›์น™์„ ๋”ฐ๋ฅด๋Š”๊ฐ€?
โ–ก ์˜์กด์„ฑ ๋ฐฉํ–ฅ์ด ์˜ฌ๋ฐ”๋ฅธ๊ฐ€? (์ƒ์œ„ โ†’ ํ•˜์œ„)
โ–ก ์ˆœํ™˜ ์˜์กด์„ฑ์ด ์—†๋Š”๊ฐ€?
โ–ก ์ ์ ˆํ•œ ๊ณ„์ธต์— ์ฝ”๋“œ๊ฐ€ ์œ„์น˜ํ•˜๋Š”๊ฐ€?
โ–ก ์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ตฌํ˜„์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋Š”๊ฐ€?

โœ… ๋ชจ๋“ˆํ™” ๊ด€์ 

โ–ก ํ—ค๋” ํŒŒ์ผ์— ๋ถˆํ•„์š”ํ•œ include๊ฐ€ ์—†๋Š”๊ฐ€?
โ–ก ์ „๋ฐฉ ์„ ์–ธ์„ ์ ์ ˆํžˆ ํ™œ์šฉํ–ˆ๋Š”๊ฐ€?
โ–ก ๊ณต๊ฐœ API์™€ ๋‚ด๋ถ€ ๊ตฌํ˜„์ด ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„๋˜๋Š”๊ฐ€?
โ–ก ๋ชจ๋“ˆ ๊ฐ„ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์€๊ฐ€?
โ–ก ๊ฐ ๋ชจ๋“ˆ์˜ ์ฑ…์ž„์ด ๋ช…ํ™•ํ•œ๊ฐ€?

โœ… ์„ฑ๋Šฅ ๊ด€์ 

โ–ก ๋ถˆํ•„์š”ํ•œ ๋ณต์‚ฌ๊ฐ€ ์—†๋Š”๊ฐ€? (move semantics ํ™œ์šฉ)
โ–ก ๊ฐ€์ƒ ํ•จ์ˆ˜๊ฐ€ ๊ผญ ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์‚ฌ์šฉ๋˜์—ˆ๋Š”๊ฐ€?
โ–ก ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์ด ํšจ์œจ์ ์ธ๊ฐ€?
โ–ก ์บ์‹œ ์นœํ™”์ ์ธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๊ฐ€?
โ–ก ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋ถ€๋ถ„์€ ์—†๋Š”๊ฐ€?

2๏ธโƒฃ ๋ฆฌํŒฉํ† ๋ง ์ „๋žต

๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ๋ฅผ ๊ฐœ์„ ํ•  ๋•Œ๋Š” ์ ์ง„์ ์œผ๋กœ ์ ‘๊ทผํ•ด์•ผ ํ•ด. ํ•œ ๋ฒˆ์— ๋‹ค ๋ฐ”๊พธ๋ ค๋‹ค๊ฐ€๋Š” ํฐ์ผ ๋‚˜! ๐Ÿ˜…

// ๋ฆฌํŒฉํ† ๋ง ๋‹จ๊ณ„๋ณ„ ์ ‘๊ทผ

// 1๋‹จ๊ณ„: ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ (๊ธฐ์กด ๋™์ž‘ ๋ณด์กด ํ™•์ธ)
TEST(LegacyCodeTest, ExistingBehavior) {
    LegacyClass obj;
    EXPECT_EQ(obj.calculate(10), 100);
}

// 2๋‹จ๊ณ„: ์ธํ„ฐํŽ˜์ด์Šค ์ถ”์ถœ
class ICalculator {
public:
    virtual ~ICalculator() = default;
    virtual int calculate(int input) = 0;
};

class LegacyClass : public ICalculator {
    // ๊ธฐ์กด ์ฝ”๋“œ ์œ ์ง€
    int calculate(int input) override {
        return input * 10;  // ๋ ˆ๊ฑฐ์‹œ ๋กœ์ง
    }
};

// 3๋‹จ๊ณ„: ์ƒˆ๋กœ์šด ๊ตฌํ˜„ ์ž‘์„ฑ
class ModernCalculator : public ICalculator {
    int calculate(int input) override {
        // ๊ฐœ์„ ๋œ ๋กœ์ง
        return input * input;
    }
};

// 4๋‹จ๊ณ„: ์ ์ง„์  ๊ต์ฒด (Feature Flag ํ™œ์šฉ)
class CalculatorFactory {
public:
    static std::unique_ptr<ICalculator> create() {
        if (FeatureFlags::useModernCalculator()) {
            return std::make_unique<ModernCalculator>();
        }
        return std::make_unique<LegacyClass>();
    }
};

// 5๋‹จ๊ณ„: ๋ชจ๋‹ˆํ„ฐ๋ง ํ›„ ์™„์ „ ๊ต์ฒด
// ์ƒˆ ๊ตฌํ˜„์ด ์•ˆ์ •์ ์ด๋ฉด ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ ์ œ๊ฑฐ
3๏ธโƒฃ ๋ฌธ์„œํ™” ์ „๋žต

์ข‹์€ ์•„ํ‚คํ…์ฒ˜๋„ ๋ฌธ์„œํ™”๊ฐ€ ์•ˆ ๋˜์–ด ์žˆ์œผ๋ฉด ์†Œ์šฉ์—†์–ด. ํŒ€์›๋“ค์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋‹ˆ๊นŒ!

/**
 * @file AudioEngine.h
 * @brief ์˜ค๋””์˜ค ์žฌ์ƒ ๋ฐ ๋ฏน์‹ฑ์„ ๋‹ด๋‹นํ•˜๋Š” ํ•ต์‹ฌ ์—”์ง„
 * 
 * AudioEngine์€ ๊ฒŒ์ž„์˜ ๋ชจ๋“  ์˜ค๋””์˜ค ์žฌ์ƒ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
 * 
 * ์ฃผ์š” ๊ธฐ๋Šฅ:
 * - 3D ๊ณต๊ฐ„ ์˜ค๋””์˜ค ์žฌ์ƒ
 * - ์‹ค์‹œ๊ฐ„ ๋ฏน์‹ฑ ๋ฐ ์ดํŽ™ํŠธ ์ฒ˜๋ฆฌ
 * - ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ
 * 
 * ์‚ฌ์šฉ ์˜ˆ์‹œ:
 * @code
 * AudioEngine engine;
 * engine.initialize();
 * 
 * auto sound = engine.loadSound("explosion.wav");
 * engine.playSound(sound, position, volume);
 * @endcode
 * 
 * @note ์ด ํด๋ž˜์Šค๋Š” ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
 * @warning ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
 * 
 * @see AudioSource
 * @see AudioMixer
 */
class AudioEngine {
public:
    /**
     * @brief ์˜ค๋””์˜ค ์—”์ง„์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
     * 
     * @param sampleRate ์ƒ˜ํ”Œ๋ง ๋ ˆ์ดํŠธ (๊ธฐ๋ณธ๊ฐ’: 44100Hz)
     * @param channels ์ฑ„๋„ ์ˆ˜ (๊ธฐ๋ณธ๊ฐ’: 2 - ์Šคํ…Œ๋ ˆ์˜ค)
     * @return ์ดˆ๊ธฐํ™” ์„ฑ๊ณต ์—ฌ๋ถ€
     * 
     * @throws AudioException ์˜ค๋””์˜ค ๋””๋ฐ”์ด์Šค๋ฅผ ์—ด ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ
     */
    bool initialize(int sampleRate = 44100, int channels = 2);
    
    /**
     * @brief ์‚ฌ์šด๋“œ ํŒŒ์ผ์„ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
     * 
     * @param filepath ์‚ฌ์šด๋“œ ํŒŒ์ผ ๊ฒฝ๋กœ
     * @return ๋กœ๋“œ๋œ ์‚ฌ์šด๋“œ ํ•ธ๋“ค
     * 
     * @pre filepath๋Š” ์œ ํšจํ•œ ๊ฒฝ๋กœ์—ฌ์•ผ ํ•จ
     * @post ๋ฐ˜ํ™˜๋œ ํ•ธ๋“ค์€ playSound()์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
     */
    SoundHandle loadSound(const std::string& filepath);
};

Doxygen ์Šคํƒ€์ผ๋กœ ๋ฌธ์„œํ™”ํ•˜๋ฉด ์ž๋™์œผ๋กœ API ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด. ์ •๋ง ํŽธํ•ด! ๐Ÿ“š

4๏ธโƒฃ ๋””๋ฒ„๊น…๊ณผ ํ”„๋กœํŒŒ์ผ๋ง

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ ์„ฑ๋Šฅ ๋ณ‘๋ชฉ์„ ์ฐพ๋Š” ๊ฑด ์ •๋ง ์ค‘์š”ํ•ด. ์ถ”์ธกํ•˜์ง€ ๋ง๊ณ  ์ธก์ •ํ•ด์•ผ ํ•ด! ๐Ÿ“Š

// ๊ฐ„๋‹จํ•œ ํ”„๋กœํŒŒ์ผ๋ง ์œ ํ‹ธ๋ฆฌํ‹ฐ
class ScopedTimer {
private:
    std::string name;
    std::chrono::high_resolution_clock::time_point start;
    
public:
    ScopedTimer(const std::string& name) 
        : name(name), start(std::chrono::high_resolution_clock::now()) {}
    
    ~ScopedTimer() {
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        std::cout << name << " took " << duration.count() << " ฮผs" << std::endl;
    }
};

// ์‚ฌ์šฉ
void expensiveFunction() {
    ScopedTimer timer("expensiveFunction");
    
    // ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…
    for (int i = 0; i < 1000000; ++i) {
        // ...
    }
}  // ์†Œ๋ฉธ์ž์—์„œ ์ž๋™์œผ๋กœ ์‹œ๊ฐ„ ์ถœ๋ ฅ

// ๋” ์ •๊ตํ•œ ํ”„๋กœํŒŒ์ผ๋ง
class Profiler {
private:
    struct ProfileData {
        std::string name;
        long long totalTime = 0;
        int callCount = 0;
    };
    
    std::unordered_map<std::string, ProfileData> data;
    std::mutex mutex;
    
public:
    static Profiler& instance() {
        static Profiler inst;
        return inst;
    }
    
    void recordTime(const std::string& name, long long microseconds) {
        std::lock_guard<std::mutex> lock(mutex);
        auto& profile = data[name];
        profile.name = name;
        profile.totalTime += microseconds;
        profile.callCount++;
    }
    
    void printReport() {
        std::cout << "\n=== Profiling Report ===\n";
        for (const auto& [name, profile] : data) {
            double avgTime = static_cast<double>(profile.totalTime) / profile.callCount;
            std::cout << name << ":\n"
                      << "  Total: " << profile.totalTime << " ฮผs\n"
                      << "  Calls: " << profile.callCount << "\n"
                      << "  Average: " << avgTime << " ฮผs\n";
        }
    }
};

#define PROFILE_SCOPE(name) \
    ScopedTimer _timer_##__LINE__(name); \
    auto _cleanup_##__LINE__ = [&]() { \
        Profiler::instance().recordTime(name, \
            std::chrono::duration_cast<std::chrono::microseconds>( \
                std::chrono::high_resolution_clock::now() - _timer_##__LINE__.start \
            ).count()); \
    };

๋ฌผ๋ก  ์‹ค๋ฌด์—์„œ๋Š” Valgrind, perf, Intel VTune ๊ฐ™์€ ์ „๋ฌธ ๋„๊ตฌ๋ฅผ ์“ฐ๋Š” ๊ฒŒ ์ข‹์•„. ํ•˜์ง€๋งŒ ๊ฐ„๋‹จํ•œ ํ”„๋กœํŒŒ์ผ๋ง์€ ์ด๋ ‡๊ฒŒ ์ง์ ‘ ๋งŒ๋“ค์–ด๋„ ์ถฉ๋ถ„ํ•ด! ๐ŸŽฏ

๐Ÿš€ ๋ฏธ๋ž˜๋ฅผ ์œ„ํ•œ ์ค€๋น„

C++ ์ƒํƒœ๊ณ„๋Š” ๊ณ„์† ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์–ด. ์•ž์œผ๋กœ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ํŠธ๋ Œ๋“œ๋“ค์„ ์‚ดํŽด๋ณด์ž! ๐Ÿ”ฎ

1๏ธโƒฃ C++23๊ณผ ๊ทธ ์ดํ›„

C++23์—์„œ ์ถ”๊ฐ€๋  ๊ธฐ๋Šฅ๋“ค์€ ๋ชจ๋“ˆํ™”์™€ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„๋ฅผ ๋”์šฑ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ค„ ๊ฑฐ์•ผ:

๐Ÿ†• C++23 ์ฃผ์š” ๊ธฐ๋Šฅ

std::expected - ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ํƒ€์ž… (Result ํƒ€์ž…)
std::mdspan - ๋‹ค์ฐจ์› ๋ฐฐ์—ด ๋ทฐ
Deducing this - CRTP ํŒจํ„ด์„ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ
Static operator() - ๋žŒ๋‹ค ์ตœ์ ํ™”
Stacktrace ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ - ํ‘œ์ค€ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค

// C++23 std::expected ์˜ˆ์‹œ
#include <expected>
#include <string>

enum class ErrorCode {
    FileNotFound,
    PermissionDenied,
    InvalidFormat
};

std::expected<std::string, ErrorCode> readFile(const std::string& path) {
    if (!fileExists(path)) {
        return std::unexpected(ErrorCode::FileNotFound);
    }
    
    if (!hasPermission(path)) {
        return std::unexpected(ErrorCode::PermissionDenied);
    }
    
    return readFileContent(path);
}

// ์‚ฌ์šฉ
auto result = readFile("config.txt");
if (result) {
    std::cout << "Content: " << *result << std::endl;
} else {
    switch (result.error()) {
        case ErrorCode::FileNotFound:
            std::cerr << "File not found" << std::endl;
            break;
        case ErrorCode::PermissionDenied:
            std::cerr << "Permission denied" << std::endl;
            break;
        default:
            std::cerr << "Unknown error" << std::endl;
    }
}
2๏ธโƒฃ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์˜ ํ‘œ์ค€ํ™”

C++26์—์„œ๋Š” ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ๊ฐ€ ํ‘œ์ค€ํ™”๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์–ด. ์ด๋ฏธ ์—ฌ๋Ÿฌ ์ œ์•ˆ์ด ์ง„ํ–‰ ์ค‘์ด์•ผ!

// ๋ฏธ๋ž˜์˜ C++ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ (๊ฐ€์ƒ์˜ ์˜ˆ์‹œ)
// package.cpp
module;

import std;
import boost.asio;
import fmt;

export module myproject.networking;

export namespace myproject {
    class NetworkClient {
        // ๊ตฌํ˜„
    };
}
3๏ธโƒฃ ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ C++

๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค, ์ปจํ…Œ์ด๋„ˆ, ์„œ๋ฒ„๋ฆฌ์Šค ํ™˜๊ฒฝ์—์„œ C++๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋ก€๊ฐ€ ๋Š˜๊ณ  ์žˆ์–ด:

โ˜๏ธ ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ C++ ํŠธ๋ Œ๋“œ

gRPC - ๊ณ ์„ฑ๋Šฅ RPC ํ”„๋ ˆ์ž„์›Œํฌ
Docker ์ตœ์ ํ™” - ๋ฉ€ํ‹ฐ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ๋กœ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ตœ์†Œํ™”
Kubernetes ํ†ตํ•ฉ - C++ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜
์„œ๋ฒ„๋ฆฌ์Šค - AWS Lambda, Google Cloud Functions์—์„œ C++ ์ง€์›
WebAssembly - ๋ธŒ๋ผ์šฐ์ €์—์„œ C++ ์‹คํ–‰

# Dockerfile - ๋ฉ€ํ‹ฐ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ
# ๋นŒ๋“œ ์Šคํ…Œ์ด์ง€
FROM gcc:12 AS builder

WORKDIR /app
COPY . .

RUN cmake -B build -DCMAKE_BUILD_TYPE=Release && \
    cmake --build build --config Release

# ์‹คํ–‰ ์Šคํ…Œ์ด์ง€
FROM ubuntu:22.04

# ํ•„์š”ํ•œ ๋Ÿฐํƒ€์ž„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ๋ณต์‚ฌ
COPY --from=builder /app/build/myapp /usr/local/bin/

# ์‹คํ–‰
CMD ["myapp"]
๐Ÿ’ก ๋งˆ์น˜๋ฉฐ

์™€, ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? ๐Ÿ˜… C++ ์•„ํ‚คํ…์ฒ˜์™€ ๋ชจ๋“ˆํ™” ์ „๋žต์— ๋Œ€ํ•ด ๋งŽ์€ ๊ฑธ ๋‹ค๋ค˜์–ด!

ํ•ต์‹ฌ์„ ์ •๋ฆฌํ•˜์ž๋ฉด:

๐ŸŽฏ ํ•ต์‹ฌ ์š”์•ฝ

1. SOLID ์›์น™ - ์ข‹์€ ์•„ํ‚คํ…์ฒ˜์˜ ๊ธฐ์ดˆ
2. ๊ณ„์ธตํ™” - ๋ช…ํ™•ํ•œ ์ฑ…์ž„ ๋ถ„๋ฆฌ
3. ๋ชจ๋“ˆํ™” - ํ—ค๋”/๊ตฌํ˜„ ๋ถ„๋ฆฌ, C++20 ๋ชจ๋“ˆ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
4. ์˜์กด์„ฑ ๊ด€๋ฆฌ - ์˜์กด์„ฑ ์—ญ์ „, ์ˆœํ™˜ ์˜์กด์„ฑ ํ•ด๊ฒฐ
5. ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด - ๋ ˆ์ด์–ด๋“œ, ํ—ฅ์‚ฌ๊ณ ๋‚ , ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜, ECS
6. ์„ฑ๋Šฅ ์ตœ์ ํ™” - ์ปดํŒŒ์ผ ์‹œ๊ฐ„, ๋Ÿฐํƒ€์ž„, ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ
7. ๋นŒ๋“œ ์‹œ์Šคํ…œ - CMake, CI/CD
8. ์‹ค์ „ ์‚ฌ๋ก€ - Chromium, Unreal, LLVM

๋Œ€๊ทœ๋ชจ C++ ํ”„๋กœ์ ํŠธ๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ์™„๋ฒฝํ•˜๊ฒŒ ๋งŒ๋“ค๋ ค๊ณ  ํ•˜์ง€ ๋งˆ. ์ž‘๊ฒŒ ์‹œ์ž‘ํ•ด์„œ ์ ์ง„์ ์œผ๋กœ ๊ฐœ์„ ํ•˜๋Š” ๊ฒŒ ํ˜„์‹ค์ ์ด์•ผ. ๊ทธ๋ฆฌ๊ณ  ํ•ญ์ƒ ํŒ€์›๋“ค๊ณผ ์†Œํ†ตํ•˜๋ฉด์„œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋ฐœ์ „์‹œ์ผœ ๋‚˜๊ฐ€๋Š” ๊ฒŒ ์ค‘์š”ํ•ด! ๐Ÿค

ํ˜น์‹œ C++ ํ”„๋กœ์ ํŠธ ์„ค๊ณ„๋‚˜ ์•„ํ‚คํ…์ฒ˜ ๊ด€๋ จํ•ด์„œ ๋” ๊นŠ์ด ์žˆ๋Š” ์กฐ์–ธ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ ์ „๋ฌธ๊ฐ€๋“ค์˜ ๋„์›€์„ ๋ฐ›์•„๋ณผ ์ˆ˜๋„ ์žˆ์–ด. ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŽ์ด ํ™œ๋™ํ•˜๊ณ  ์žˆ๊ฑฐ๋“ ! ๐Ÿ˜Š

์ž, ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„๋„ ๋Œ€๊ทœ๋ชจ C++ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•  ์ค€๋น„๊ฐ€ ๋์–ด! ํ™”์ดํŒ…! ๐Ÿš€๐Ÿ’ช

๐ŸŽ‰

Happy Coding!

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

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

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

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