티스토리 뷰

IT/OOP

상속(Inheritance) - 코드 재사용의 유혹, 그러나 신중해야 할 이유

Stv 2025. 4. 15. 15:10

목차


    1. 상속이란?

    상속(Inheritance)은 기존 클래스의 속성과 동작(메서드)을 자식 클래스가 물려받아 사용하는 객체지향의 핵심 기능이다.
    • 즉, 부모 클래스의 기능을 확장하거나 재사용하기 위해 자식 클래스가 상속을 받는 것이다.
    public class Animal {
    	public void sound() {
        	Systme.out.println("동물이 소리를 냅니다.");
        }
    }
    
    public class Dog extends Animal {
        @Override
        public void sound() {
            System.out.println("멍멍!");
        }
    }

     

    2. 상속의 목적

    목적 설명
    코드 재사용 중복 제거, 공통 로직 분리
    계층적 관계 표현 일반 -> 구체 구조 표현(is-a 관계)
    확장성 확보 부모 기능을 재정의하여 기능 확장(Overriding)

     

    3. 상속의 기본 구조 (Java 기준)

     

    📌 extends 키워드

    class 자식 extends 부모 {
    	// 부모의 필드/메서드 사용 가능
    }

     

    📌 메서드 오버라이딩 (재정의)

    @Override
    public void methodName() {
    	// 부모 메서드 재정의
    }

     

    📌 super 키워드

    • 부모의 생성자나 메서드/필드에 접근할 때 사용
    super.method();      // 부모 메서드 호출
    super.field = value; // 부모 필드 접근

     

    4. 상속이 가진 단점

    • 상속은 강력하지만 다음과 같은 설계상 문제를 유발할 수 있다.

    ❌ 강한 결합 (Tight Coupling)

    • 자식 클래스가 부모 클래스에 너무 의존하게 됨
    • 부모 변경 -> 자식에 ripple effect

    ❌ is-a 관계 오용

    • 상속 = 코드 재사용으로만 생각해 불필요하게 계층만 복잡해짐
    • 진짜 "is-a" 관계가 아닐 수도 있음 (예 : Car extends Engine ❌)

    ❌ 테스트 어려움

    • 상속 깊어질수록 테스트 단위가 복잡해짐

    ❌ 다중 상속 불가 (Java 기준)

    • Java는 클래스 간 다중 상속을 금지 -> 인터페이스로 해결 필요
    Java가 다중 상속을 지원하지 않는 이유는 대표적으로 "다이아몬드 문제(Diamond Problem)" 때문이다. 
    두 부모 클래스에서 동일한 메서드를 상속받을 경우 어떤 메서드를 호출해야 할지 모호성이 발생하고, 이는 객체의 일관성을 깨뜨리고 캡슐화의 원칙을 위반할 수 있다.
    그래서 Java는 클래스 간 다중 상속은 금지하고, 인터페이스를 통한 다중 구현으로 유연성을 확보하면서도 안정성을 유지할 수 있다.

     

    5. 실무에서 자주 보는 잘못된 상속 설계 예시

    public class Employee {
    	private String name;
        private int salary;
        
        public void printPaySlip() {
        	// 급여 출력
        }
    }
    
    public class Manager extends Employee {
    	private List<Employee> team;
        	public void evaluate(Employee e) { ... }
    }

     

    ▶ Manager is-a Employee는 맞지만, 나중에 Manage 외에 HR, TechLead, PM 등 공통점보다 차이점이 많아지고 설계가 깨지기 시작함

     

    6. 상속의 대안 -> 합성 (Composition)

     

    📌 

    상속 vs 합성 비교

    구분  상속 (Inheritance) 합성 (Composition)
    관계 is-a (이다) has-a (가지고 있다)
    결합도 높음 낮음 (유연함)
    재사용 방식 부모의 모든 기능 물려받음 필요한 기능만 포함
    설계 유연성 제한적 확장성 높음

     

    ✅ 합성 예시

    public class Logger{
    	public void log(String msg) {
        	System.out.println("[LOG] " + msg);
        }
    }
    
    public class Service {
    	private Logger logger;
        
        public Service(Logger logger) {
        	this.logger = logger;
        }
        
        public void doSomething() {
        	logger.log("서비스 수행 중...");
        }
    }
    • "Service는 Logger가 아니다" -> 상속 부적합
    • "Service는 Logger를 가지고 있다" -> 합성이 맞음

    ✅ 마무리 요약

    • 상속은 객체지향의 핵심 개념이지만 과용하면 설계가 무너지기 쉽다.
    • 자식 클래스는 부모 클래스의 강하게 결합되며, 변경에 취약해 질 수 있다.
    • 단순한 코드 재사용 목적이라면 합성(Composition)이 더 적합할 수 있다.

    🔜 다음 포스팅에서는 "다형성 - 한 가지 인터페이스, 여러 가지 구현" 라는 주제를 다뤄보려고 한다.