파이썬 추상 클래스 & 인터페이스 완벽 이해: 객체 지향 프로그래밍 심화
파이썬 객체 지향 프로그래밍(OOP)을 심화하는 과정에서 중요한 개념인 추상 클래스와 인터페이스는 코드의 유연성과 확장성을 높이는 데 핵심적인 역할을 합니다. 이번 강의에서는 이 두 개념이 왜 필요하며, 언제 사용해야 하는지 쉽고 명확하게 설명해 드립니다. 추상 클래스와 인터페이스를 통해 더 견고하고 효율적인 파이썬 프로그램을 설계하는 방법을 배우게 될 것입니다.
추상 클래스와 인터페이스는 무엇일까요?
프로그램을 만들다 보면 여러 객체(사물)들이 공통적으로 가지고 있는 기능이 있지만, 그 기능이 어떻게 작동할지는 각 객체마다 다르게 정의해야 할 때가 많습니다. 예를 들어, '동물'이라는 큰 범주 안에는 '개', '고양이', '새' 등 다양한 종류의 동물이 있습니다. 이 동물들은 모두 '소리 내기'라는 기능을 가지고 있지만, 개는 '멍멍', 고양이는 '야옹', 새는 '짹짹' 하고 각기 다른 소리를 냅니다.
이때 '소리 내기'라는 기능을 모든 동물이 반드시 구현해야 하는 '규칙'으로 만들고 싶을 수 있습니다. 하지만 '동물'이라는 것 자체는 실제로 존재하지 않고, '개'나 '고양이'처럼 구체적인 동물만 존재합니다. 이런 경우에 사용하는 것이 바로 추상 클래스와 인터페이스입니다.
간단히 말해, 추상 클래스는 '미완성된 설계도'와 같습니다. 어떤 건물을 지을 때, 이 건물에는 반드시 '출입구'와 '창문'이 있어야 한다고 정해놓지만, 출입구나 창문의 모양이나 재료는 구체적인 건물마다 다르게 만들 수 있도록 비워두는 것이죠. 추상 클래스는 일부 기능만 정의하고, 반드시 구현해야 하는 기능(추상 메서드)은 '미완성' 상태로 남겨둡니다. 이 미완성된 부분을 상속받는 다른 클래스들이 채워 넣어야 합니다.
인터페이스는 '지켜야 할 규칙 목록'과 비슷합니다. 예를 들어, '운전 면허'를 따려면 '필기시험 합격', '기능 시험 합격', '도로 주행 시험 합격'이라는 세 가지 규칙을 모두 지켜야 합니다. 어떤 사람이 운전 면허를 가지고 있다면, 그 사람은 이 세 가지 규칙을 모두 통과했다는 것을 알 수 있습니다. 파이썬에서는 별도의 interface 키워드가 없지만, 모든 메서드가 추상 메서드로 이루어진 추상 클래스를 인터페이스처럼 사용할 수 있습니다.
실습 코드 예시 1: 추상 클래스 정의하고 사용하기
파이썬에서 추상 클래스를 만들려면 abc 모듈의 ABC와 abstractmethod를 사용해야 합니다. ABC는 추상 클래스의 기반이 되고, @abstractmethod 데코레이터는 해당 메서드가 추상 메서드임을 알려줍니다.
Python
from abc import ABC, abstractmethod
# 추상 클래스 '도형'을 정의합니다.
class Shape(ABC):
# 모든 도형은 '넓이 계산' 기능을 가지고 있어야 합니다.
@abstractmethod
def calculate_area(self):
pass # 이 메서드는 자식 클래스에서 반드시 구현해야 합니다.
# 추상 메서드가 아닌 일반 메서드도 가질 수 있습니다.
def describe(self):
print("저는 도형입니다.")
# '원' 클래스는 '도형' 클래스를 상속받습니다.
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
# 추상 메서드인 calculate_area를 반드시 구현해야 합니다.
def calculate_area(self):
return 3.14 * self.radius * self.radius
# '사각형' 클래스도 '도형' 클래스를 상속받습니다.
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
# calculate_area를 구현합니다.
def calculate_area(self):
return self.width * self.height
# 객체를 생성하고 기능을 사용해봅니다.
my_circle = Circle(5)
print(f"원의 넓이: {my_circle.calculate_area()}")
my_circle.describe()
my_rectangle = Rectangle(4, 6)
print(f"사각형의 넓이: {my_rectangle.calculate_area()}")
my_rectangle.describe()
# 추상 클래스는 직접 객체를 만들 수 없습니다.
# error_shape = Shape() # TypeError 발생
위 코드를 실행하면 다음과 같은 결과가 화면에 나타납니다.
출력 결과
원의 넓이: 78.5
저는 도형입니다.
사각형의 넓이: 24
저는 도형입니다.
실습 코드 예시 2: 추상 클래스를 통한 다형성 구현
추상 클래스를 사용하면 여러 구체적인 클래스들이 동일한 기능을 다른 방식으로 구현할 수 있게 됩니다. 이를 '다형성(Polymorphism)'이라고 부르는데, 마치 다양한 모양의 열쇠가 모두 같은 '자물쇠'를 열 수 있는 것과 같습니다. 이는 코드를 더 유연하고 확장 가능하게 만듭니다.
Python
from abc import ABC, abstractmethod
# 추상 클래스 '동물'을 정의합니다.
class Animal(ABC):
def __init__(self, name):
self.name = name
# 모든 동물은 소리를 내야 하지만, 그 소리는 다릅니다.
@abstractmethod
def make_sound(self):
pass
def describe(self):
print(f"저는 {self.name}입니다.")
# 구체적인 동물 클래스들
class Dog(Animal):
def make_sound(self):
print("멍멍!")
class Cat(Animal):
def make_sound(self):
print("야옹!")
class Bird(Animal):
def make_sound(self):
print("짹짹!")
# 동물들을 리스트에 담아 각자의 소리를 내게 합니다.
animals = [
Dog("바둑이"),
Cat("나비"),
Bird("짹짹이")
]
for animal in animals:
animal.describe()
animal.make_sound()
위 코드를 실행하면 다음과 같은 결과가 화면에 나타납니다.
출력 결과
저는 바둑이입니다.
멍멍!
저는 나비입니다.
야옹!
저는 짹짹이입니다.
짹짹!
실습 코드 예시 3: 파이썬에서 인터페이스처럼 사용하기
파이썬에는 다른 프로그래밍 언어처럼 '인터페이스'라는 별도의 키워드가 없습니다. 하지만 모든 메서드가 추상 메서드인 추상 클래스를 만들어서 인터페이스와 같은 역할을 수행하도록 할 수 있습니다. 이는 특정 기능을 '강제'함으로써 코드의 일관성을 유지하는 데 도움이 됩니다.
Python
from abc import ABC, abstractmethod
# '데이터 처리' 인터페이스를 정의합니다.
class IDataProcessor(ABC): # 'I'는 Interface의 약자로 자주 사용됩니다.
# 데이터를 읽는 기능
@abstractmethod
def read_data(self):
pass
# 데이터를 처리하는 기능
@abstractmethod
def process_data(self):
pass
# 데이터를 쓰는 기능
@abstractmethod
def write_data(self, data):
pass
# 파일에서 데이터를 처리하는 클래스
class FileProcessor(IDataProcessor):
def read_data(self):
print("파일에서 데이터를 읽습니다.")
return "파일 데이터"
def process_data(self):
print("파일 데이터를 처리합니다.")
return "처리된 파일 데이터"
def write_data(self, data):
print(f"파일에 {data}를 씁니다.")
# 데이터베이스에서 데이터를 처리하는 클래스
class DatabaseProcessor(IDataProcessor):
def read_data(self):
print("데이터베이스에서 데이터를 읽습니다.")
return "DB 데이터"
def process_data(self):
print("데이터베이스 데이터를 처리합니다.")
return "처리된 DB 데이터"
def write_data(self, data):
print(f"데이터베이스에 {data}를 씁니다.")
# 데이터 처리 함수
def perform_data_operations(processor: IDataProcessor):
read_result = processor.read_data()
processed_result = processor.process_data()
processor.write_data(processed_result)
print("-" * 20)
# 각 프로세서로 작업 수행
file_proc = FileProcessor()
perform_data_operations(file_proc)
db_proc = DatabaseProcessor()
perform_data_operations(db_proc)
위 코드를 실행하면 다음과 같은 결과가 화면에 나타납니다.
출력 결과
파일에서 데이터를 읽습니다.
파일 데이터를 처리합니다.
파일에 처리된 파일 데이터를 씁니다.
--------------------
데이터베이스에서 데이터를 읽습니다.
데이터베이스 데이터를 처리합니다.
데이터베이스에 처리된 DB 데이터를 씁니다.
--------------------
주의:
추상 클래스를 상속받는 클래스가 모든 추상 메서드를 구현하지 않으면 TypeError가 발생합니다. 이는 추상 메서드를 반드시 구현해야 한다는 규칙을 강력하게 강제하는 역할을 합니다.
핵심 : 추상 클래스는 '미완성된 설계도'처럼 일부 기능만 정의하고 추상 메서드를 통해 자식 클래스가 반드시 구현해야 할 기능을 지정합니다. 파이썬에서 인터페이스는 모든 메서드가 추상 메서드인 추상 클래스를 통해 유사하게 구현할 수 있으며, 이는 코드의 일관성과 확장을 위한 '규칙'을 제공합니다.
마무리 요약
이번 시간에는 파이썬에서 추상 클래스와 인터페이스 개념을 어떻게 활용하는지 자세히 알아보았습니다. 추상 클래스는 특정 기능을 반드시 구현하도록 강제하여 코드의 안정성을 높이고, 인터페이스(추상 클래스를 통해 구현)는 일관된 구조를 유지하며 다양한 구현체를 수용할 수 있게 합니다. 이 두 가지 개념은 복잡한 프로그램을 설계할 때 매우 중요한 도구가 됩니다. 다음 강의에서는 파이썬의 예외 처리와 에러 핸들링에 대해 깊이 있게 다루어 보겠습니다.
질문이 있다면 댓글에 남겨주세요.
다음 강의에서는 파이썬의 예외 처리와 에러 핸들링을 배워 봅니다.