TIL: SOLID 원칙

박춘팔·2026년 5월 4일

CPP TIL

목록 보기
14/15

📅 2026-05-04

SOLID 원칙

TextRPG를 제작하면서 특정 물체(캐릭터, 적 등)를 Actor라는 최상위 클래스에서 기본적인 요소를 관리했다.
Unreal처럼 만들어보고 싶다는 이유에서 였는데 여러 문제가 발생했는데
해당 Actor가

  • 공격이 가능한가?
  • 피해를 입을 수 있는가?

위 두가지 때문에 캐릭터(폰)라는 한정된 역할만 할 수 있었다.
그때 같은 기수의 Dr. Dragon 'D' Water 님께서 SOLID 원칙을 알려주셨다.

이전에도 FE엔지니어를 하면서 Repository 패턴을 사용할 일이 있었는데 그때 들어본 원칙이기도 하지만 제대로 알아볼 생각은 하지 않았었다.
실제로 3년넘게 실무하면서 class 사용해본 일이 손에 꼽을 정도로 FE에서는 class를 잘 사용하지 않는다.

S(SRP) - Single Responsibility Principle

단일 책임 원칙
클래스는 1개의 이유로만 변경되어야 한다.

class Player {
public:
    void Update();
    void Render();
    void SaveToFile();   // 저장 책임
    void SendNetwork();  // 네트워크 책임
};

// 분리
struct PlayerState {
    int hp;
    Vector3 position;
};

class PlayerSystem {
public:
    void Update(PlayerState&);
};

class SaveSystem {
public:
    void Save(const PlayerState&);
};

O(OCP) - Open/Closed Principle

개방/폐쇄 원칙
확장에는 열려있고 수정에는 닫혀있어야한다.

// 무기 추가할 때마다 값 추가해야함 
int CalcDamage(std::string type) {
    if (type == "sword") return 10;
    if (type == "gun") return 20;
}


// 개선
struct WeaponData
{
	int damage;
}

class Weapon
{
	WeaponData weaponData;
    
public:
	int ApplyDamage()
    {
		return weaponData.damage;
    }
	
}

L(LSP) - Liskov Substitution Principle

리스코프 치환 원칙
자식 클래스는 부모 클래스를 완전히 대체 가능해야한다.

class Character
{
public:
	virtual void Attack() = 0;
};

class Pacifist : public Character
{
public:
	// 부모 Character는 Attack 호출 시 함수가 실행되는 것을 기대함
	// Pacifist에서 예외처리로 프로그램을 비정상 종료시키기 때문에 LSP 원칙에 어긋남
	void Attack() override { throw; } 
}



// 개선
class IAttackable
{
public:
	virtual void Attack() = 0;
}

class NPC { }; // 공격 없음
class Box { }; // 공격 없음

class Warrior : public IAttackable
{
	void Attack() override { ... }
}

I(ISP) - Interface Segregation Principle

인터페이스 분리 원칙
클라이언트는 자신이 사용하지 않는 인터페이스에 의존해서는 안된다.

class IUnit
{
public:
	virtual void Move() = 0;
    virtual void Attack() = 0;
    virtual void Fly() = 0; // 거북이 Actor에 Fly???
}


// 개선
class MoveComponent { void Move(); }
class AttackComponent { void Attack(); }
class FlyComponenty { void Fly(); }

class Unit
{
	MoveComponent* move;
    AttackComponent* attack;
    // 해당 Unit은 Fly 기능이 없으므로 객체 생성X
}

D(DIP) - Dependency Inversion Principle

의존성 역전 원칙
고수준 모듈은 저수준 모듈에 의존해서는 안되고 둘 다 추상화에 의존해야한다.

// Game이 더 고수준 모듈
class Game
{
	// 저수준 모듈 Sword에 의존하면 원칙 위배
	Sword sword;
}

// 개선

class Weapon
{
public:
	virtual void Attack() = 0;
}

class Game
{
	std::unique_ptr<Weapon> weapon;
    
    Game(satd::unique_ptr<Weapon> w) : weapon(std::move(w)) { };
    
}

총 정리

SRP → “변경 이유 1개” 시스템 단위 분리
OCP → “코드 말고 데이터로 확장” 데이터 기반 확장
LSP → “상속 대신 능력 분리” 상속 최소화
ISP → “인터페이스 → 컴포넌트로” 컴포넌트
DIP → “new 하지 말고 주입해라” DI + smart pointer

profile
이것 저것 다해보는 삶

0개의 댓글