2026년 4월 1일
오늘은 OOP 주요 특성인 상속과 다형성을 구현하기 위해 꼭 필요한 개념인 인터페이스에 대하여 정리해보았습니다.
인터페이스란 넓은 의미에서 서로 다른 시스템에서 상호작용하는 시스템을 의미합니다. 이를 프로그래밍에 접목해보면 클래스가 구현해야하는 기능(메서드)의 규칙을 정의한 것입니다. 우리는 이를 통해 특정 메서드의 입력과 출력이 어떻게 되는지 확인할 수 있습니다.
다음과 같은 상황을 생각해봅시다.
난 연세대학교 학번 담당자다. 입학생이 들어오면 학과에 맞는 학번을 부여해준다.
이를 코드로 구현해봅시다(학번은 전공 + 숫자라고 가정합니다)
class YonseiIDManager {
private int count = 1;
public void assignId(CS student) { student.id = "CS" + count++; }
public void assignId(BIZ student) { student.id = "BIZ" + count++; }
}
class Student {
String name;
String id;
Student(String name) {
this.name = name;
}
}
// 컴퓨터과학과
class CS extends Student {
CS(String name) { super(name); }
}
// 경영학과
class BIZ extends Student {
BIZ(String name) { super(name); }
}위 코드에서 YonseiIDManager는 학과에 따라 학번을 부여하는 assignId 메서드를 수행합니다. 만약 학과가 두 학과 밖에 없다면 클래스를 수정하지 않아도 되지만 학과는 계속해서 생길 수 있습니다. 그러면 학과가 추가 될 때마다 매번 assignId 메서드를 추가해야합니다. 이런 문제를 해결하기 위해 학과를 새로 만들때 assignId 메서드를 그 학과에 대해서만 새롭게 정의해주면 어떨까요. 그러면 우리는 YonseiIDManager 클래스를 작성하는 시점에서는 Student의 하위 클래스가 몇 개나 있을 지 고민할 필요가 없습니다. 이를 인터페이스로 해결할 수 있다고 생각하면 편합니다. 코드로 구현하여 살펴보겠습니다.
interface Enrollable {
String getDeptCode();
}
abstract class Student implements Enrollable {
String name;
String id;
Student(String name) { this.name = name; }
}
class YonseiIDManager {
private int count = 1;
public void assignId(Student student) {
student.id = student.getDeptCode() + count++;
}
}
class CS extends Student {
CS(String name) { super(name); }
@Override
public String getDeptCode() { return "CS"; }
}
class BIZ extends Student {
BIZ(String name) { super(name); }
@Override
public String getDeptCode() { return "BIZ"; }
}
class CEE extends Student {
CEE(String name) { super(name); }
@Override
public String getDeptCode() { return "CEE"; }
}위 코드를 살펴보면 이전에는 학과의 갯수만큼 YonseiiDManager에 메소드를 작성해야해 했지만 이제는 Enrollable이라는 인터페이스를 통해 단 한 개의 assignId 메서드만 사용하여 구현이 가능해졌습니다. 여기서 중요한 것은 메서드의 갯수가 줄어들 것뿐만 아니라 학번부여자 클래스가 특정 학생 클래스에 의존하지 않는다는 것입니다. 다시 개념적으로 설명해보겠습니다.
상속과 관련된 부분은 위의 예제로 충분히 다뤘으니 넘어가고, 처음 이야기한 다형성에 대해서 이야기가 필요합니다. 다형성이란 하나의 객체 또는 메서드가 여러 가지 형태를 가질 수 있는 OOP의 특징 중 하나입니다. 우리가 컴퓨터 USB 포트를 통해서 메모리, 스마트폰, 마우스 등 다양한 도구를 연결 할 수 있는 것을 생각해봅시다. 이 USB 포트가 물리적인 인터페이스라고 비유할 수 있습니다. USB 포트의 규격을 알면 어떤 기기도 연결할 수 있습니다. 또, 컴퓨터는 USB 포트만 제공하고 어떤 기기가 연결되는지 신경 쓸 필요가 없습니다. 이를 위의 예제에 대입해보면 YonseiiDManager는 어떤 학생 클래스인지에 의존하지 않고 학번 부여 메서드를 만든 것과 동일합니다. 추가로 예제의 방식은 오버라이딩으로 다형성을 구현한 것입니다. 하위 클래스에서 assignID를 재정의하면(매개변수를 CS, BIZ 객체로 하여) 오버로딩 방식이 될 것입니다. 오버로딩 방식은 상속과는 관계가 없고, 결정 시점이 다릅니다(어떤 함수를 호출할 지 결정하는 단계). 상황에 맞게 사용하면 될 것 같습니다.
결국 인터페이스는 클래스가 무엇을 해야 하는지에 대한 규칙을 정해 주고, 어떻게 구현할지는 각 클래스에 맡기는 장치라고 볼 수 있습니다. 이를 통해 객체마다 서로 다른 구현을 가지더라도 동일한 방식으로 다룰 수 있고, 새로운 클래스가 추가되더라도 기존 코드를 크게 수정하지 않아도 됩니다. 즉, 인터페이스는 코드의 유연성과 확장성을 높이며, 다형성을 실현하는 중요한 기반이 됩니다. 이를 잘 활용하여 서비스를 구현해볼 수 있도록 노력하겠습니다.
참고자료(Wikidocs) https://wikidocs.net/217
댓글 0