리스코프 치환 원칙

우노구나·2025년 8월 11일

리스코프 치환 원칙(Liskov Substitution Principle, LSP)은 객체 지향 설계 원칙(SOLID) 중 하나로,
Barbara Liskov가 1987년에 제안한 개념이다.


📌 정의

"서브타입은 언제나 기반 타입(Base type)으로 교체할 수 있어야 한다."

즉, 부모 클래스(또는 인터페이스)를 사용하는 코드에서 자식 클래스로 교체해도 문제없이 동작해야 한다는 뜻입니다.


🔍 쉽게 설명

부모 클래스의 객체를 사용하는 자리에 자식 클래스 객체를 넣어도
프로그램의 기능, 의미, 결과가 깨지면 안 된다.

자식 클래스는 부모 클래스의 규약(contract) 을 그대로 지켜야 하며,
부모가 기대하는 동작을 변경하거나 약화시키면 LSP를 위반합니다.


📖 예시

✅ LSP를 지킨 예

class Bird {
    void fly() {
        System.out.println("날고 있어요!");
    }
}

class Sparrow extends Bird {
    @Override
    void fly() {
        System.out.println("참새가 날고 있어요!");
    }
}

public class Main {
    public static void main(String[] args) {
        Bird bird = new Sparrow(); // 부모 타입으로 참새 대입
        bird.fly(); // 정상 동작
    }
}
  • Sparrow는 Bird의 계약(날 수 있음)을 지키고 있음 → 문제 없음

❌ LSP를 위반한 예

class Bird {
    void fly() {
        System.out.println("날고 있어요!");
    }
}

class Penguin extends Bird {
    @Override
    void fly() {
        throw new UnsupportedOperationException("펭귄은 날 수 없어요!");
    }
}
  • Penguin은 Bird의 "날 수 있다" 라는 전제 조건을 깨뜨림

  • Bird를 기대하는 코드에서 Penguin을 넣으면 예외 발생 → LSP 위반


LSP 위반시 발생 문제

1. 예상치 못한 동작 및 버그 발생

부모 타입을 기대하는 코드에서 자식 타입을 넣었더니 의미가 달라져서 잘못된 결과가 나옴.

런타임에서만 오류가 드러나 테스트 통과 → 배포 후 장애 같은 상황이 생김.

예:

Bird bird = new Penguin();
bird.fly(); // 예외 발생 → LSP 위반

Penguin이 부모 Bird의 계약("날 수 있다")을 깨버림 → 호출 측에서 예외 처리 필요.


2. 다형성(Polymorphism) 무력화

상속의 핵심인 "부모 타입으로 자식 객체를 자유롭게 교체" 가 불가능해짐.

결국 if-elseinstanceof 같은 타입 체크가 늘어나 절차적 코드로 퇴화.

예:

if (bird instanceof Penguin) {
    // 펭귄 전용 처리
} else {
    bird.fly();
}

→ 이렇게 되면 "상속"을 쓴 의미가 사라짐.


3. OCP(개방-폐쇄 원칙) 위반

원래는 새로운 자식 클래스를 만들어서 기능 확장 → 기존 코드 변경 없이 사용해야 함.

그런데 LSP 위반 시, 자식 클래스 맞춤 처리를 위해 기존 코드도 함께 수정해야 함.


4. 유지보수 비용 증가

새로운 서브클래스가 추가될 때마다, 기존 로직을 전부 점검해야 함.

특히 팀 프로젝트에서는 새로운 사람이 들어와서 자식 클래스를 추가하면
원래 정상 동작하던 부분이 깨질 위험이 높음.


5. 테스트 코드 복잡화

LSP가 지켜지면 부모 타입에 대한 테스트를 한 번만 작성하면 됨.

하지만 위반 시, 자식 클래스마다 따로 테스트해야 하고, 경우에 따라서는
부모-자식 간 호환성까지 검증해야 함.

profile
기술 블로그

0개의 댓글