클래스를 설계하다 보면 추상클래스와 인터페이스 중 무엇을 써야 할지 막히는 순간이 옵니다. 이 글에서는 두 개념의 차이를 짚어보고, 어떤 상황에서 각각을 선택해야 하는지 알아보겠습니다.
1. 추상클래스, 인터페이스란?
추상클래스는 일반 클래스와 비슷하지만, 구현이 없는 abstract 메서드를 하나 이상 포함할 수 있습니다. 자식 클래스는 이 메서드를 반드시 구현해야 하며, 직접 인스턴스화할 수 없습니다.
인터페이스는 메서드 시그니처만 정의하는 순수한 틀입니다. 구현은 전혀 없고, 이를 채택한 클래스가 모든 메서드를 구현할 책임을 집니다. 마찬가지로 직접 인스턴스화할 수 없습니다.
2. 판단 기준 - 구현을 공유할 것인가?
추상클래스와 인터페이스를 선택하는 가장 핵심적인 기준은 메서드 구현을 자식 클래스와 공유할 것인지 여부입니다.
자식 클래스들이 공통 로직을 그대로 물려받아야 한다면 추상클래스가 적합합니다. 반면 구현 방식은 각자 다르지만 동일한 규격을 강제해야 한다면 인터페이스를 선택하는 것이 좋습니다.
또한 추상클래스는 다중 상속이 불가능하지만, 인터페이스는 여러 개를 동시에 구현할 수 있습니다. 그래서 실전에서는 추상클래스로 공통 뼈대를 잡고, 인터페이스로 추가적인 역할을 부여하는 방식을 함께 사용하는 경우가 많습니다.
3. 인터페이스에서 메서드 구현이 가능한 경우
언어에 따라 인터페이스에서도 메서드 구현이 가능한 경우가 있습니다. C#은 8.0부터, Java는 8부터 지원하며, Unity의 경우 2021 버전 이상에서 사용할 수 있습니다.
이를 default 구현이라고 부르며, 기존 인터페이스에 메서드를 추가할 때 기존 구현체를 모두 수정하지 않아도 되도록 하위 호환성을 유지하기 위해 도입된 기능입니다.
Unity C# 예시는 아래와 같습니다.
public interface IInterfaceA
{
public void MethodA();
public void MethodB()
{
Debug.Log("IInterfaceA MethodB");
}
public void MethodC()
{
Debug.Log("IInterfaceA MethodC");
}
}
public class TestClassA : IInterfaceA
{
public void MethodA()
{
Debug.Log("TestClassA MetoadA");
}
public void MethodC()
{
Debug.Log("TestClassA MethodC");
}
}
각 메서드를 실행했을 때 결과는 아래와 같습니다.
TestClassA testClassA = new TestClassA();
testClassA.MethodA(); // "TestClassA MetoadA"
testClassA.MethodB(); // Error
testClassA.MethodC(); // "TestClassA MethodC"
IInterfaceA interfaceA = testClassA;
interfaceA.MethodA(); // "TestClassA MetoadA"
interfaceA.MethodB(); // "IInterfaceA MethodB"
interfaceA.MethodC(); // "TestClassA MethodC"
결과에서 두 가지 중요한 점을 확인할 수 있습니다.
첫째, MethodB를 보면 클래스 참조(testClassA)로는 인터페이스의 default 구현에 접근할 수 없습니다. 인터페이스 참조(interfaceA)로만 접근이 가능합니다. 즉, 추상클래스처럼 구현을 자식과 공유하는 개념이 아닙니다.
둘째, MethodC를 보면 자식 클래스가 동일한 메서드를 구현한 경우, 참조 타입에 관계없이 자식 클래스의 구현이 우선합니다. override는 가능하지만, 클래스 참조와 인터페이스 참조 간에 동작 차이가 생길 수 있어 혼동을 줄 수 있으므로 주의해서 사용해야 합니다.
4. 결론
추상클래스와 인터페이스 중 무엇을 선택할지는 결국 구현을 공유할 것인가에 달려 있습니다.
자식 클래스들이 공통 로직을 물려받아야 한다면 추상클래스, 구현 방식은 각자 다르지만 동일한 규격을 강제해야 한다면 인터페이스를 선택해야 합니다. 실전에서는 두 가지를 함께 사용하는 경우가 많으므로, 각각의 역할을 명확히 이해하고 상황에 맞게 활용하는 것이 중요합니다.
또한 인터페이스의 default 구현은 편리한 기능이지만, 추상클래스의 구현 공유와는 목적과 동작 방식이 다릅니다. 혼동을 줄이기 위해 꼭 필요한 경우에만 신중하게 사용하길 권장합니다.
'프로그래밍 일반' 카테고리의 다른 글
| Service Locator vs Dependency Injection (0) | 2025.07.27 |
|---|---|
| SOLID 원칙 정리 (0) | 2024.11.03 |