OOP 객체지향 5대 원칙 – LSP(리스코프 치환 원칙)

DevBison·2025년 11월 28일

객체지향에서 자주 듣는 SOLID 원칙 중 하나가 LSP(Liskov Substitution Principle, 리스코프 치환 원칙)이다. 말만 들으면 어렵지만, 실제로는 “상속을 제대로 쓰기 위한 최소한의 규칙”에 가깝다.


1. LSP가 말하고 싶은 핵심 한 줄

부모 타입에 자식 타입을 넣어도 아무 문제 없이 동작해야 한다.
즉, 부모가 기대하는 행동을 자식도 반드시 지켜야 한다.


2. 왜 이런 원칙이 필요할까?

상속은 코드 재사용에 좋지만, 잘못 쓰면 구조가 엉망이 된다.
특히 자식 클래스가 부모 클래스의 약속을 깨버리면, 다음과 같은 문제가 생긴다.

  • 부모를 기준으로 작성된 코드가 예측대로 작동하지 않음
  • 자식 클래스가 부모의 일부 기능을 무너뜨림
  • 유지보수 시 “왜 이 코드가 여기서 깨지지?” 하는 상황 발생

LSP는 이런 상황을 방지하기 위한 기준이다.


3. 대표적인 잘못된 예 – 직사각형(Rectangle) vs 정사각형(Square)

고전적인 예제로 많이 나오는 케이스다.

class Rectangle {
public:
    virtual void SetWidth(int w) { width = w; }
    virtual void SetHeight(int h) { height = h; }

protected:
    int width, height;
};

class Square : public Rectangle {
public:
    void SetWidth(int w) override {
        width = height = w;
    }
    void SetHeight(int h) override {
        width = height = h;
    }
};

문제는 부모를 기준으로 코드가 작성되어 있을 때 발생한다.

void ResizeTo(Rectangle& rect) {
    rect.SetWidth(10);
    rect.SetHeight(20);

    // Rectangle이라면 width=10, height=20을 기대하지만,
    // Square가 들어오면 width=20, height=20이 되어버린다.
}

정사각형은 직사각형의 규칙(가로와 세로가 독립적)을 지킬 수 없으므로 상속 관계 자체가 잘못된 것이다.
LSP를 지키지 않은 대표적 사례.


4. LSP를 지키기 위한 실제 기준

1) 부모의 기능을 약화시키거나 제한하면 안 된다

예: 부모 함수는 아무 때나 SetWidth 가능하지만,
자식에서 “특정 조건에서만 가능하다”고 바꾼다면 LSP 위반이다.

2) 부모의 기대 행동을 깨면 안 된다

예: 부모의 Move()는 1m 이동인데
자식이 Move()를 2m 이동하게 만들면 안 된다.

3) 예외를 더 많이 던져도 안 된다

부모는 정상 실행인데
자식은 자꾸 예외를 던지면 부모의 계약을 위반한 것이다.

4) 부모의 입력/출력 타입을 바꾸면 안 된다

부모는 int를 받는데
자식은 float만 받는 식으로 바꿔버리면 완전히 어긋난다.


5. 게임 개발·언리얼 기준으로 본 LSP 예시

잘못된 예

class AWeapon {
public:
    virtual void Attack() { /* 기본 공격 */ }
};

class ABow : public AWeapon {
public:
    void Attack() override {
        // 조건: 무조건 화살이 있어야만 Attack 가능
        // 부모는 조건 없이 Attack 가능한데 자식이 제한 조건을 추가함
    }
};

부모 기준으로 Attack()은 어떤 상황에서도 실행되리라 기대하지만,
자식이 자기 마음대로 조건을 추가하면 부모를 치환했을 때 코드가 깨진다.

올바른 수정 방향

  • SetArrowCount(), CanShoot() 같은 검증은 Attack() 바깥에서 처리한다.
  • 혹은 Weapon → RangedWeapon → Bow 같은 구조로 레벨을 나눈다.

6. 결국 LSP가 지키려는 것

LSP는 “상속을 설계할 때 최소한의 약속을 지키도록 강제하는 원칙”이다.

정리하면 다음과 같다.

  • 상속은 단순히 코드 재사용 목적이 아니다
  • 부모의 규칙을 자식이 지키지 못한다면 상속 관계를 다시 생각해야 한다
  • 자식 객체를 부모처럼 다뤄도 프로그램이 정상 동작해야 한다

이 기준만 지켜도 클래스 구조가 훨씬 견고해진다.


7. LSP 정리 한 문장

상속 관계라면 “부모처럼 행동할 수 있는가?”를 반드시 확인해야 한다.


SOLID 중 LSP는 실제로 상속 설계할 때 가장 자주 깨지는 원칙이기도 하다.

profile
응애 개발자

0개의 댓글