1. SRP - 단일 책임 원칙(Single Responsibility Principle)

정의: 클래스는 단 하나의 변경 이유만 가져야 한다.

나쁜 예

class Player
{
public:
    void Move();
    void SaveToFile();
    void RenderUI();
};

문제:

- 이동 로직 변경 -> 수정

- 저장 방식 변경 -> 수정

- UI 변경 -> 수정

좋은 예

class PlayerMovement {};
class PlayerSaveSystem {};
class PlayerUI {};

각각 책임 분리

언리얼에서는 액터 컴포넌트 구조 자체가 SRP 철학을 가지고 있으며, 저 또한 그 철학 유지하며

 

class FF_API UEquipmentSystem : public UActorComponent    : 장착 역할을 가진 컴포넌트
class FF_API UInventorySystem : public UActorComponent    : 인벤토리 역할을 가진 컴포넌트
class FF_API ULootingSystem : public UActorComponent    : 루팅 역할을 가진 컴포넌트

이 같은 구성을 유지하고 있습니다.

 

2. OCP - 개방 폐쇄 원칙(Open-Closed Principle)

정의: 확장에는 열려 있고, 수정에는 닫혀 있어야 한다.

나쁜 예

int CalcDamage(int type)
{
    if(type == 0) ...
    else if(type == 1) ...
}

몹 추가할 때 마다 수정

좋은 예

class DamagePolicy
{
public:
    virtual int Calc() = 0;
};

class FireDamage : public DamagePolicy {};
class IceDamage : public DamagePolicy {};

새 타입 추가 = 클래스 추가만

언리얼에서는 DataAsset, DataTable 클래스를 활용하여 Data-Driven 기반으로

새로운 무기/스킬/AI(Behavior Tree) 등을 관리할 수 있습니다.

여기서 switch / if 문이 계속 늘어나면 OCP 원칙에 위반할 가능성이 높아진다고 하네요.

 

3. LSP - 리스코프 치환 원칙(Liskov Substitutaion Principle)

자식 클래스는 부모를 완전히 대체할 수 있어야 한다.

나쁜 예

class Bird {
    virtual void Fly();
};

class Penguin : public Bird {
    void Fly() override { throw; }
};

좋은 예

class Bird {};
class FlyingBird : public Bird {};

주로 Weapon 계열을 구현할 때, 총기에만 사용하는 Fire를

냉병기나 방패 같은 클래스도 상속을 받으면 구조가 잘못됐다고 합니다.

최근에 저도 TPS 기준으로 개발하면서 Fire로 지은 함수를 일반적인 Attack으로 리팩토링 한 기억이 남습니다.

중요한 건 이 객체를 부모 타입으로 써도 문제 없는지 살펴보는 감각이라고 하네요.

 

4. ISP - 인터페이스 분리 원칙(Interface Segregation Principle)

정의: 클라이언트는 사용하지 않는 인터페이스에 의존하면 안 된다.

나쁜 예

class ICharacter
{
    virtual void Fly();
    virtual void Swim();
    virtual void Shoot();
};

이렇게 되면 가만히 서있는 NPC가 있을 경우 빈 함수로 내버려둬야 할 것입니다.

좋은 예

class IFlyable {};
class ISwimmable {};
class IShootable {};

필요한 것만 구현하기 위해서 인터페이스는 작고 구체적일수록 좋으며,

저도 인터페이스가 편하긴 한데 추가할 때 신중한 편인 것 같습니다.

 

5. DIP - 의존성 역전 원칙(Dependency Inversion Principle)

정의: 상위 모듈은 하위 모듈에 의존하면 안 된다.

둘 다 추상화에 의존해야 한다.

나쁜 예

class Game
{
    SteamNetwork net;
};

여기서 Steam이 아닌 PSN으로 바꾸면 게임 전체를 수정하게 됩니다.

좋은 예

class INetwork {};

class Game
{
    INetwork* net;
};


class SteamNetwork : public INetwork {};
class PSNNetwork : public INetwork {};

플랫폼/서버/입력 시스템을 변경할 때 이 구조를 가지는게 중요합니다

요약

*SRP: 한 클래스 = 한 역할

*OCP: 수정하지 말고 확장하라

*LSP: 자식은 부모처럼 동작해야

*ISP: 인터페이스는 작게 쪼개라

*DIP: 구현이 아니라 추상에 의존

 

+ 유니티에서 좋은 영상이 올라와 추가

https://youtu.be/e9JfXuRcwkM?si=YBjQ3BOHk25RNVQ4

 

'CS' 카테고리의 다른 글

C++17 표준  (0) 2026.03.06
C++14 표준  (1) 2026.03.04
C++11 표준  (0) 2026.03.03
C++23: The Next C++ Standard  (0) 2026.03.03
정렬 알고리즘  (0) 2026.03.01

+ Recent posts