Ver3.0 ๐๏ธ C++ ์ํคํ ์ฒ์ ๋๊ท๋ชจ ์์คํ ์ค๊ณ ๋ชจ๋ํ ์ ๋ต

๐๏ธ C++ ์ํคํ ์ฒ์ ๋๊ท๋ชจ ์์คํ ์ค๊ณ ๋ชจ๋ํ ์ ๋ต
๋ณต์กํ ์์คํ ์ ์ฐ์ํ๊ฒ ์ค๊ณํ๋ ์ค์ ๊ฐ์ด๋
์๋ ! ๐ ์ค๋์ C++ ๊ฐ๋ฐ์๋ผ๋ฉด ๋๊ตฌ๋ ํ ๋ฒ์ฏค ๊ณ ๋ฏผํ๊ฒ ๋๋ ์ฃผ์ , ๋ฐ๋ก ๋๊ท๋ชจ ์์คํ ์ค๊ณ์ ๋ชจ๋ํ ์ ๋ต์ ๋ํด ์ด์ผ๊ธฐํด๋ณด๋ ค๊ณ ํด. ์์ ํ๋ก์ ํธ๋ ๊ทธ๋ฅ ๋ง ์ง๋ ๋๋๋ฐ, ์ฝ๋๊ฐ ์๋ง ์ค์ ๋์ด๊ฐ๊ณ ํ์์ด ๋์ด๋๋ฉด ๊ฐ์๊ธฐ ๋ชจ๋ ๊ฒ ๋ณต์กํด์ง์์? ๐
์ค์ ๋ก ๊ฒ์ ์์ง, ๊ธ์ต ์์คํ , ์๋ฒ ๋๋ ํ๋ซํผ ๊ฐ์ ๋๊ท๋ชจ C++ ํ๋ก์ ํธ๋ฅผ ๋ณด๋ฉด ์ ๋ง ์ ๊ตํ ์ํคํ ์ฒ ์ค๊ณ๊ฐ ์จ์ด์์ด. ์ด๋ฐ ์์คํ ๋ค์ ์์ญ ๋ช ์ ๊ฐ๋ฐ์๊ฐ ๋์์ ์์ ํ๊ณ , ์๋ฐฑ๋ง ์ค์ ์ฝ๋๊ฐ ์ ๊ธฐ์ ์ผ๋ก ์ฐ๊ฒฐ๋์ด ์์ง. ์ด๋ป๊ฒ ์ด๋ฐ ๊ฒ ๊ฐ๋ฅํ ๊น? ๋ฐ๋ก ์ฒด๊ณ์ ์ธ ๋ชจ๋ํ ์ ๋ต ๋๋ถ์ด์ผ! ๐ฏ
๋ณธ๊ฒฉ์ ์ผ๋ก ๋ค์ด๊ฐ๊ธฐ ์ ์, ์ ๋ชจ๋ํ๊ฐ ์ด๋ ๊ฒ ์ค์ํ์ง๋ถํฐ ์ด์ผ๊ธฐํด๋ณผ๊น? ๐ค
์์ํด๋ด. ๋๊ฐ 10๋ง ์ค์ง๋ฆฌ C++ ํ๋ก์ ํธ๋ฅผ ํ๋์ ๊ฑฐ๋ํ ํ์ผ์ ๋ค ๋๋ ค๋ฐ์๋ค๊ณ ์๊ฐํด๋ด. ๋์ฐํ์ง? ๐ฑ ์ปดํ์ผ ์๊ฐ์ ์์ฒญ๋๊ฒ ๊ธธ์ด์ง๊ณ , ํ ์ค๋ง ์์ ํด๋ ์ ์ฒด๋ฅผ ๋ค์ ๋น๋ํด์ผ ํ๊ณ , ์ด๋์ ๋ฒ๊ทธ๊ฐ ๋ฌ๋์ง ์ฐพ๊ธฐ๋ ํ๋ค์ด. ๊ฒ๋ค๊ฐ ์ฌ๋ฌ ๋ช ์ด ๋์์ ์์ ํ๋ฉด? ์ถฉ๋์ ์ฐ์์ด์ผ.
๐ฏ ๋ชจ๋ํ์ ํต์ฌ ์ด์
1. ์ ์ง๋ณด์์ฑ ํฅ์ - ๊ฐ ๋ชจ๋์ด ๋
๋ฆฝ์ ์ด๋ผ ๋ฌธ์ ๋ฐ์ ์ ํด๋น ๋ชจ๋๋ง ์์ ํ๋ฉด ๋ผ
2. ์ฌ์ฌ์ฉ์ฑ ์ฆ๊ฐ - ์ ์ค๊ณ๋ ๋ชจ๋์ ๋ค๋ฅธ ํ๋ก์ ํธ์์๋ ๊ทธ๋๋ก ์ฌ์ฉ ๊ฐ๋ฅํด
3. ๊ฐ๋ฐ ์์ฐ์ฑ - ํ์๋ค์ด ๊ฐ์ ๋ค๋ฅธ ๋ชจ๋์ ๋์์ ๊ฐ๋ฐํ ์ ์์ด
4. ์ปดํ์ผ ์๊ฐ ๋จ์ถ - ๋ณ๊ฒฝ๋ ๋ชจ๋๋ง ์ฌ์ปดํ์ผํ๋ฉด ๋๋๊น ๋น๋๊ฐ ๋นจ๋ผ์ ธ
5. ํ
์คํธ ์ฉ์ด์ฑ - ๋ชจ๋ ๋จ์๋ก ๋
๋ฆฝ์ ์ธ ํ
์คํธ๊ฐ ๊ฐ๋ฅํด
6. ์์กด์ฑ ๊ด๋ฆฌ - ๋ชจ๋ ๊ฐ ์์กด ๊ด๊ณ๊ฐ ๋ช
ํํด์ ธ์ ์์คํ
๊ตฌ์กฐ ํ์
์ด ์ฌ์
์ค์ ๋ก ๊ตฌ๊ธ์ Chromium ํ๋ก์ ํธ๋ ์ธ๋ฆฌ์ผ ์์ง ๊ฐ์ ๋๊ท๋ชจ C++ ํ๋ก์ ํธ๋ค์ ๋ณด๋ฉด, ์์ฒ ๊ฐ์ ๋ชจ๋๋ก ์ธ๋ฐํ๊ฒ ๋๋์ด์ ธ ์์ด. ์ด๊ฒ ๋ฐ๋ก ์๋ฐฑ ๋ช ์ ๊ฐ๋ฐ์๊ฐ ๋์์ ์์ ํ ์ ์๋ ๋น๊ฒฐ์ด์ง! ๐ช
์ข์ ์ํคํ ์ฒ๋ฅผ ์ค๊ณํ๋ ค๋ฉด ๋ช ๊ฐ์ง ํต์ฌ ์์น์ ์์์ผ ํด. ์ด๊ฑด ๋ง์น ๊ฑด๋ฌผ์ ์ง์ ๋ ๊ธฐ์ด ๊ณต์ฌ๊ฐ ์ค์ํ ๊ฒ๊ณผ ๊ฐ์. ๐๏ธ
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 ์์น์ ์ฒ์๋ถํฐ ์๋ฒฝํ๊ฒ ์งํค๋ ค๊ณ ํ๋ฉด ์คํ๋ ค ๊ณผ๋ํ ์ค๊ณ๊ฐ ๋ ์ ์์ด. ํ๋ก์ ํธ ์ด๊ธฐ์๋ ๊ฐ๋จํ๊ฒ ์์ํ๊ณ , ์ฝ๋๊ฐ ๋ณต์กํด์ง๋ฉด์ ๋ฆฌํฉํ ๋ง์ ํตํด ์ ์ง์ ์ผ๋ก ์์น์ ์ ์ฉํ๋ ๊ฒ ํ์ค์ ์ด์ผ! ๐ฏ
๋๊ท๋ชจ ์์คํ ์ ๋ณดํต ์ฌ๋ฌ ๊ณ์ธต์ผ๋ก ๋๋ ์ ์ค๊ณํด. ๊ฐ ๊ณ์ธต์ ๋ช ํํ ์ญํ ์ ๊ฐ์ง๊ณ , ์์ ๊ณ์ธต์ ํ์ ๊ณ์ธต์๋ง ์์กดํ๋ ๊ตฌ์กฐ์ผ:
| ๊ณ์ธต | ์ญํ | ์์ |
|---|---|---|
| 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++์์ ๋ชจ๋์ ์ด๋ป๊ฒ ๋๋๊ณ ๊ด๋ฆฌํ๋์ง ์์๋ณด์! ๐
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์ ๋ณ๊ฒฝ์ ์ํฅ์ ๋ฐ์ง ์์. ์ปดํ์ผ ์๊ฐ์ด ์์ฒญ๋๊ฒ ์ค์ด๋ค์ง! โก
๋ค์์คํ์ด์ค๋ 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)์์๋ง ์ฌ์ฉํ๊ฑฐ๋, ํจ์ ๋ด๋ถ ์ค์ฝํ์์๋ง ์ฌ์ฉํ์.
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 ๋ฑ)์ ๋ชจ๋ ์ง์ ์ฌ๋ถ๋ฅผ ํ์ธํด์ผ ํด!
๋๊ท๋ชจ ํ๋ก์ ํธ์์๋ ์ฝ๋๋ฅผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ถ๋ฆฌํ๋ ๊ฒ ์ผ๋ฐ์ ์ด์ผ. ๋ ๊ฐ์ง ๋ฐฉ์์ด ์์ด:
์ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (.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๋ง ๊ต์ฒด ๊ฐ๋ฅ |
| ์ฑ๋ฅ | ์ฝ๊ฐ ๋น ๋ฆ | ์ฝ๊ฐ ๋๋ฆผ (๊ฐ์ ํธ์ถ) |
| ํ๋ฌ๊ทธ์ธ ์์คํ | ๋ถ๊ฐ๋ฅ | ๊ฐ๋ฅ |
๋๊ท๋ชจ ์์คํ ์์ ๊ฐ์ฅ ๊ณจ์น ์ํ ๊ฒ ๋ฐ๋ก ์์กด์ฑ ๊ด๋ฆฌ์ผ. ๋ชจ๋ A๊ฐ B์ ์์กดํ๊ณ , B๊ฐ C์ ์์กดํ๊ณ ... ์ด๋ฌ๋ค ๋ณด๋ฉด ์ํ ์์กด์ฑ์ด ์๊ธฐ๊ฑฐ๋ ์ปดํ์ผ ์๊ฐ์ด ํญ๋ฐํ์ง. ๐ฑ
์์ 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๋ง ์์ ํ๋ฉด ๋์ง! ๐
์ํ ์์กด์ฑ์ ์ ๋ง ๊ณจ์น ์ํ ๋ฌธ์ ์ผ. 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
์ธํฐํ์ด์ค๋ฅผ ์ค๊ฐ์ ๋๋ฉด ์ํ ์์กด์ฑ์ ๊น๋ํ๊ฒ ํด๊ฒฐํ ์ ์์ด! ๐
์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์กด์ฑ์ ํจํค์ง ๊ด๋ฆฌ์๋ฅผ ์ฌ์ฉํ๋ฉด ํจ์ฌ ํธํด. 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 ์คํฌ๋ฆฝํธ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ๋ณ๊ณ ๊ฐ๋จํจ. ์์ ํ๋ก์ ํธ์ ์ ํฉํด.
์ด๋ก ์ ์ถฉ๋ถํ ๋ดค์ผ๋, ์ด์ ์ค์ ํ๋ก์ ํธ์์ ๋ง์ด ์ฐ๋ ์ํคํ ์ฒ ํจํด๋ค์ ์์๋ณด์! ๐
๊ฐ์ฅ ์ ํต์ ์ด๊ณ ๋๋ฆฌ ์ฐ์ด๋ ํจํด์ด์ผ. ์์คํ ์ ์ํ์ ์ธ ๊ณ์ธต์ผ๋ก ๋๋๋ ๊ฑฐ์ง:
// ์ค์ ์์ : ๊ฒ์ ์์ง ์ํคํ
์ฒ
// ============ 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;
}
๊ฐ ๊ณ์ธต์ ๋ฐ๋ก ์๋ ๊ณ์ธต์๋ง ์์กดํ๊ณ , ์ ๊ณ์ธต์ ๋ชฐ๋ผ. ์ด๋ ๊ฒ ํ๋ฉด ํ๋ซํผ์ ๋ฐ๊ฟ๋ ์์ ๊ณ์ธต์ ์์ ํ ํ์๊ฐ ์์ด! ๐
๋๋ฉ์ธ ๋ก์ง์ ์ค์ฌ์ ๋๊ณ , ์ธ๋ถ ์ธ๊ณ์์ ์ํธ์์ฉ์ ํฌํธ์ ์ด๋ํฐ๋ก ๋ถ๋ฆฌํ๋ ํจํด์ด์ผ:
// ============ 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๋ก ๋ฐ๊พธ๋ , ๋๋ฉ์ธ ์ฝ๋๋ ์ ํ ์์ ํ ํ์๊ฐ ์์ด! ๐ฏ
๋ชจ๋ ๊ฐ ๊ฒฐํฉ๋๋ฅผ ์ต์ํํ๋ ๊ฐ๋ ฅํ ํจํด์ด์ผ. ์ด๋ฒคํธ๋ฅผ ํตํด ๊ฐ์ ์ ์ผ๋ก ํต์ ํ์ง:
// ============ 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 ํ๋ ์์ํฌ - ๋ฒํผ ํด๋ฆญ, ๋ง์ฐ์ค ์ด๋ ๋ฑ ์ฌ์ฉ์ ์
๋ ฅ ์ฒ๋ฆฌ
โข ๋ง์ดํฌ๋ก์๋น์ค - ์๋น์ค ๊ฐ ๋์จํ ๊ฒฐํฉ ์ ์ง
โข ํ๋ฌ๊ทธ์ธ ์์คํ
- ํ๋ฌ๊ทธ์ธ๋ค์ด ์ฝ์ด ์์คํ
์ ์ด๋ฒคํธ์ ๋ฐ์
๊ฒ์ ๊ฐ๋ฐ์์ ํนํ ์ธ๊ธฐ ์๋ ํจํด์ด์ผ. ์์ ๋์ ์ปดํฌ์ง์ ์ ์ฌ์ฉํด์ ์ ์ฐ์ฑ์ ๊ทน๋ํํ์ง:
// ============ 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, ์๋๋ฐ์ค ๋ฑ)
โข ๋ฐํ์์ ๋์ ์ผ๋ก ๊ธฐ๋ฅ์ ์ถ๊ฐ/์ ๊ฑฐํด์ผ ํ๋ ๊ฒฝ์ฐ
โข ๋ฐ์ดํฐ ์งํฅ ์ค๊ณ๋ก ์บ์ ํจ์จ์ฑ์ ๊ทน๋ํํ๊ณ ์ถ์ ๋
โข ๋ฉํฐ์ค๋ ๋ฉ์ ํ์ฉํ ๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๋
์ํคํ ์ฒ๊ฐ ์๋ฌด๋ฆฌ ์ข์๋ ์ฑ๋ฅ์ด ๋์๋ฉด ์์ฉ์์ง! ๋๊ท๋ชจ ์์คํ ์์ ์ฑ๋ฅ์ ์ ์งํ๋ ์ ๋ต๋ค์ ์์๋ณด์. ๐
๋๊ท๋ชจ ํ๋ก์ ํธ์์ ์ปดํ์ผ ์๊ฐ์ ์์ฐ์ฑ์ ์ง๊ฒฐ๋ผ. ๋ช ๊ฐ์ง ์ต์ ํ ๊ธฐ๋ฒ์ ์๊ฐํ ๊ฒ:
์ ๋ฐฉ ์ ์ธ ์ ๊ทน ํ์ฉ
// โ ๋์ ์: ํค๋์์ ๋ชจ๋ ๊ฒ์ 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 ํ์ผ์ ํ๋๋ก ํฉ์ณ์ ์ปดํ์ผ
# ์ปดํ์ผ ์๊ฐ์ด ํฌ๊ฒ ๋จ์ถ๋์ง๋ง, ์ฆ๋ถ ๋น๋๋ ๋๋ ค์ง ์ ์์
๊ฐ์ ํจ์ ์ค๋ฒํค๋ ์ค์ด๊ธฐ
// ๊ฐ์ ํจ์๋ ํธ๋ฆฌํ์ง๋ง ์ค๋ฒํค๋๊ฐ ์์ด
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 ๋ช
๋ น์ด๋ก ๋์ฑ ์ต์ ํ ๊ฐ๋ฅ
}
ํ๋ 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 ๋ฑ) ์ฌ์ฉ ๊ถ์ฅ
โข ๊ณผ๋ํ ๋ณ๋ ฌํ๋ ์คํ๋ ค ์ฑ๋ฅ์ ๋จ์ด๋จ๋ฆด ์ ์์ด (์ปจํ
์คํธ ์ค์์นญ ์ค๋ฒํค๋)
๋๊ท๋ชจ ํ๋ก์ ํธ๋ ๋น๋ ์์คํ ์ค์ ๋ ์ค์ํด. ์ ๋๋ก ๊ตฌ์ฑํ์ง ์์ผ๋ฉด ๊ฐ๋ฐ ์์ฐ์ฑ์ด ํฌ๊ฒ ๋จ์ด์ง๊ฑฐ๋ ! ๐๏ธ
# ํ๋ก์ ํธ ๊ตฌ์กฐ
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
)
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 ๋ฌธ์์ ๊ฐ์ด๋ ํฌํจ
# .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++ ํ๋ก์ ํธ๋ค์ด ์ด๋ป๊ฒ ์ํคํ ์ฒ๋ฅผ ์ค๊ณํ๋์ง ์ดํด๋ณด์! ๐
๊ตฌ๊ธ์ 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
์ธ๋ฆฌ์ผ ์์ง์ ๊ฒ์ ๊ฐ๋ฐ์๋ค์ด ๊ฐ์ฅ ๋ง์ด ์ฐ๋ ์์ง ์ค ํ๋์ผ. ๋ชจ๋ ์์คํ ์ด ์ ๋ง ์ ๋์ด ์์ด!
๐ฎ 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;
};
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๋ง ์ถ๊ฐํ๋ฉด ๋ผ! ๐ฏ
์ด๋ก ๊ณผ ์์ ๋ฅผ ๋ง์ด ๋ดค์ผ๋, ์ด์ ์ค๋ฌด์์ ๋ฐ๋ก ์ ์ฉํ ์ ์๋ ์ค์ฉ์ ์ธ ํ๋ค์ ์ ๋ฆฌํด๋ณผ๊ฒ! ๐ผ
โ ์ํคํ ์ฒ ๊ด์
โก ์๋ก์ด ํด๋์ค/๋ชจ๋์ด SOLID ์์น์ ๋ฐ๋ฅด๋๊ฐ?
โก ์์กด์ฑ ๋ฐฉํฅ์ด ์ฌ๋ฐ๋ฅธ๊ฐ? (์์ โ ํ์)
โก ์ํ ์์กด์ฑ์ด ์๋๊ฐ?
โก ์ ์ ํ ๊ณ์ธต์ ์ฝ๋๊ฐ ์์นํ๋๊ฐ?
โก ์ธํฐํ์ด์ค์ ๊ตฌํ์ด ๋ถ๋ฆฌ๋์ด ์๋๊ฐ?
โ ๋ชจ๋ํ ๊ด์
โก ํค๋ ํ์ผ์ ๋ถํ์ํ include๊ฐ ์๋๊ฐ?
โก ์ ๋ฐฉ ์ ์ธ์ ์ ์ ํ ํ์ฉํ๋๊ฐ?
โก ๊ณต๊ฐ API์ ๋ด๋ถ ๊ตฌํ์ด ๋ช
ํํ ๊ตฌ๋ถ๋๋๊ฐ?
โก ๋ชจ๋ ๊ฐ ๊ฒฐํฉ๋๊ฐ ๋ฎ์๊ฐ?
โก ๊ฐ ๋ชจ๋์ ์ฑ
์์ด ๋ช
ํํ๊ฐ?
โ ์ฑ๋ฅ ๊ด์
โก ๋ถํ์ํ ๋ณต์ฌ๊ฐ ์๋๊ฐ? (move semantics ํ์ฉ)
โก ๊ฐ์ ํจ์๊ฐ ๊ผญ ํ์ํ ๊ณณ์๋ง ์ฌ์ฉ๋์๋๊ฐ?
โก ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ด ํจ์จ์ ์ธ๊ฐ?
โก ์บ์ ์นํ์ ์ธ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋๊ฐ?
โก ๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ ๋ถ๋ถ์ ์๋๊ฐ?
๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ๊ฐ์ ํ ๋๋ ์ ์ง์ ์ผ๋ก ์ ๊ทผํด์ผ ํด. ํ ๋ฒ์ ๋ค ๋ฐ๊พธ๋ ค๋ค๊ฐ๋ ํฐ์ผ ๋! ๐
// ๋ฆฌํฉํ ๋ง ๋จ๊ณ๋ณ ์ ๊ทผ
// 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๋จ๊ณ: ๋ชจ๋ํฐ๋ง ํ ์์ ๊ต์ฒด
// ์ ๊ตฌํ์ด ์์ ์ ์ด๋ฉด ๋ ๊ฑฐ์ ์ฝ๋ ์ ๊ฑฐ
์ข์ ์ํคํ ์ฒ๋ ๋ฌธ์ํ๊ฐ ์ ๋์ด ์์ผ๋ฉด ์์ฉ์์ด. ํ์๋ค์ด ์ดํดํ ์ ์์ด์ผ ํ๋๊น!
/**
* @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 ๋ฌธ์๋ฅผ ์์ฑํ ์ ์์ด. ์ ๋ง ํธํด! ๐
๋๊ท๋ชจ ์์คํ ์์ ์ฑ๋ฅ ๋ณ๋ชฉ์ ์ฐพ๋ ๊ฑด ์ ๋ง ์ค์ํด. ์ถ์ธกํ์ง ๋ง๊ณ ์ธก์ ํด์ผ ํด! ๐
// ๊ฐ๋จํ ํ๋กํ์ผ๋ง ์ ํธ๋ฆฌํฐ
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++ ์ํ๊ณ๋ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. ์์ผ๋ก ์ฃผ๋ชฉํด์ผ ํ ํธ๋ ๋๋ค์ ์ดํด๋ณด์! ๐ฎ
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;
}
}
C++26์์๋ ํจํค์ง ๊ด๋ฆฌ๊ฐ ํ์คํ๋ ๊ฐ๋ฅ์ฑ์ด ์์ด. ์ด๋ฏธ ์ฌ๋ฌ ์ ์์ด ์งํ ์ค์ด์ผ!
// ๋ฏธ๋์ C++ ํจํค์ง ๊ด๋ฆฌ (๊ฐ์์ ์์)
// package.cpp
module;
import std;
import boost.asio;
import fmt;
export module myproject.networking;
export namespace myproject {
class NetworkClient {
// ๊ตฌํ
};
}
๋ง์ดํฌ๋ก์๋น์ค, ์ปจํ ์ด๋, ์๋ฒ๋ฆฌ์ค ํ๊ฒฝ์์ 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
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.