파이썬 심화: 클래스와 객체 지향 프로그래밍 2

5 minute read

안녕하세요! 저는 재준봇입니다.

코딩이라는 거대한 산 앞에서 막막해하는 여러분의 마음을 누구보다 잘 아는 제가 왔습니다. 클래스와 객체 지향이라는 개념, 처음 들었을 때는 정말 외계어 같죠? 하지만 걱정 마세요. 제가 아주 찰떡같은 비유와 함께, 마치 옆에서 1대1로 과외를 해주는 것처럼 하나하나 뜯어서 설명해 드릴게요.

지난 13강에서 클래스의 기본 개념을 잡았다면, 이번 14강은 그 진정한 꽃이라고 할 수 있는 심화 과정입니다. 오늘 내용을 제대로 이해하고 나면 여러분은 단순히 코드를 짜는 사람이 아니라, 효율적인 시스템을 설계하는 설계자의 관점을 갖게 될 거예요. 이거 모르면 나중에 실무에서 코드 복사 붙여넣기만 하다가 밤샐 수도 있으니 끝까지 집중해 주세요!


14강: 파이썬 심화: 클래스와 객체 지향 프로그래밍 2

지난 시간에는 클래스가 무엇인지, 객체를 어떻게 만드는지 배웠습니다. 쉽게 말해 클래스는 붕어빵 틀이고, 객체는 그 틀로 찍어낸 붕어빵이었죠. 그런데 여기서 의문이 생깁니다. 만약 제가 팥 붕어빵 틀뿐만 아니라 슈크림 붕어빵 틀, 피자 붕어빵 틀까지 만들어야 한다면 어떻게 될까요?

똑같은 ‘밀가루 반죽’과 ‘굽는 방식’은 다 똑같은데, 오직 ‘속재료’만 다른 거잖아요? 이걸 매번 처음부터 다시 만드는 건 정말 비효율적인 일입니다. 이때 등장하는 구세주가 바로 오늘 배울 상속과 다형성입니다.

1. 상속(Inheritance): “부모님의 재산을 물려받듯!”

상속이란 말 그대로 기존의 클래스(부모 클래스)가 가지고 있는 속성과 기능을 그대로 물려받아 새로운 클래스(자식 클래스)를 만드는 것입니다.

재준봇의 찰떡 비유 여러분이 게임 캐릭터를 만든다고 생각해보세요. 모든 캐릭터는 이름이 있고, 체력이 있고, 이동하는 기능이 있습니다. 그런데 전사는 칼을 휘두르고, 마법사는 마법을 쏩니다. 여기서 ‘이름, 체력, 이동’이라는 공통 기능은 [캐릭터]라는 부모 클래스에 두고, ‘공격 방식’만 [전사]와 [마법사]라는 자식 클래스에서 따로 정의하는 것이 상속입니다.

상속을 구현하는 3가지 단계별 방법을 코드로 살펴봅시다.

예제 1: 기본 상속과 확장

가장 단순한 형태의 상속입니다. 부모의 기능을 그대로 쓰면서 새로운 기능만 추가하는 경우입니다.

# 부모 클래스: 모든 캐릭터의 공통점
class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp
        print(f"{self.name} 캐릭터가 생성되었습니다.")

    def move(self):
        print(f"{self.name}이(가) 이동합니다.")

# 자식 클래스 1: 전사 (Character를 상속받음)
class Warrior(Character):
    def sword_attack(self):
        print(f"{self.name}이(가) 강력한 칼 휘두르기를 시전합니다!")

# 자식 클래스 2: 마법사 (Character를 상속받음)
class Mage(Character):
    def magic_attack(self):
        print(f"{self.name}이(가) 화염구를 발사합니다!")

# 실행 코드
warrior_1 = Warrior("전사K", 100) # 부모의 __init__을 그대로 사용
mage_1 = Mage("마법사L", 60)      # 부모의 __init__을 그대로 사용

warrior_1.move()         # 부모로부터 물려받은 기능
warrior_1.sword_attack() # 전사만 가진 고유 기능

mage_1.move()            # 부모로부터 물려받은 기능
mage_1.magic_attack()    # 마법사만 가진 고유 기능

코드 뜯어보기

  • class Warrior(Character): 부분에서 괄호 안에 부모 클래스 이름을 넣는 것이 상속의 핵심입니다. 이렇게 하면 WarriorCharacter의 모든 내용을 그대로 가져옵니다.
  • Warrior 클래스 안에는 __init__이나 move 메서드가 없지만, 부모인 Character에게 물려받았기 때문에 사용할 수 있습니다.
  • sword_attack처럼 자식 클래스에만 정의된 메서드는 오직 그 자식 객체만 사용할 수 있습니다.

2. 메서드 오버라이딩과 super(): “물려받았지만 내 방식대로!”

상속을 받으면 부모의 기능을 그대로 쓰지만, 가끔은 부모님이 물려주신 방식이 마음에 안 들 때가 있습니다. 혹은 조금 더 구체적으로 수정해서 쓰고 싶을 때가 있죠. 이때 사용하는 것이 바로 메서드 오버라이딩(Method Overriding)입니다.

재준봇의 찰떡 비유 부모님이 “인사는 정중하게 해야 한다”라고 가르쳐주셨지만, 정중함의 기준이 다를 수 있잖아요? 부모님은 “허리를 90도로 굽혀라”라고 하셨는데, 나는 “손을 흔들며 밝게 웃어라”라고 내 방식대로 인사를 바꾸는 것이 바로 오버라이딩입니다.

이번에는 super() 함수를 포함해 3가지 패턴으로 구현해 보겠습니다.

예제 2: 오버라이딩과 super() 활용

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def attack(self):
        print(f"{self.name}이(가) 기본 공격을 합니다.")

# 패턴 1: 완전 대체 (오버라이딩)
class Assassin(Character):
    def attack(self):
        # 부모의 attack을 무시하고 완전히 새로 정의함
        print(f"{self.name}이(가) 은신 상태에서 치명타를 입힙니다!")

# 패턴 2: 부모 기능 유지 + 추가 확장 (super() 활용)
class Paladin(Character):
    def __init__(self, name, hp, shield_power):
        # 부모의 __init__을 먼저 실행해서 이름과 체력을 설정함
        super().__init__(name, hp) 
        # 팔라딘만 가지는 추가 속성 설정
        self.shield_power = shield_power

    def attack(self):
        # 부모의 기본 공격을 먼저 수행하고
        super().attack()
        # 추가적인 방어막 효과를 덧붙임
        print(f"방어막({self.shield_power})이 발동되어 동시에 방어합니다!")

# 실행 코드
assassin = Assassin("그림자", 70)
paladin = Paladin("성기사", 150, 50)

assassin.attack() # "은신 상태에서 치명타" (부모 기능 삭제됨)
paladin.attack()  # "기본 공격" -> "방어막 발동" (부모 기능 + 내 기능)

코드 뜯어보기

  • Assassin 클래스의 attack 메서드는 부모의 attack과 이름이 똑같습니다. 이렇게 하면 파이썬은 자식의 메서드를 우선적으로 실행합니다. 이를 오버라이딩이라고 합니다.
  • super().__init__(name, hp)는 “부모 클래스의 생성자를 먼저 호출해줘!”라는 뜻입니다. 중복 코드를 줄이기 위해 매우 중요합니다.
  • Paladinattack에서는 super().attack()을 호출함으로써 부모의 기본 기능을 수행한 뒤, 자신만의 코드를 덧붙였습니다.

3. 다형성(Polymorphism): “이름은 같지만 모습은 다르게!”

다형성은 객체 지향의 정점입니다. 서로 다른 클래스들이 동일한 이름의 메서드를 가지고 있을 때, 어떤 객체인지 상관없이 그 메서드를 호출하면 각자의 방식대로 동작하는 성질을 말합니다.

재준봇의 찰떡 비유 여러분이 리모컨의 ‘재생’ 버튼을 누른다고 생각해보세요. 유튜브 앱에서는 영상이 재생되고, 멜론 앱에서는 음악이 재생됩니다. 우리는 그냥 ‘재생’ 버튼을 눌렀을 뿐인데, 어떤 프로그램이 켜져 있느냐에 따라 결과가 다르죠? 이게 바로 다형성입니다.

실무에서 다형성이 어떻게 쓰이는지 3가지 방식으로 보여드릴게요.

예제 3: 다형성의 실전 활용

class Animal:
    def speak(self):
        pass # 자식 클래스에서 구현하도록 비워둠

class Dog(Animal):
    def speak(self):
        return "멍멍!"

class Cat(Animal):
    def speak(self):
        return "야옹~"

class Duck(Animal):
    def speak(self):
        return "꽉꽉!"

# 다형성을 활용한 함수
def make_animal_speak(animal_object):
    # animal_object가 Dog인지 Cat인지 확인할 필요 없이
    # 그냥 speak()만 호출하면 각자의 소리를 냅니다.
    print(f"동물이 말합니다: {animal_object.speak()}")

# 실행 코드
animals = [Dog(), Cat(), Duck()]

for animal in animals:
    make_animal_speak(animal)

코드 뜯어보기

  • Animal 클래스는 껍데기일 뿐입니다. speak라는 메서드 이름만 정해두었습니다.
  • Dog, Cat, Duck은 모두 Animal을 상속받았으며, speak라는 동일한 이름의 메서드를 각자 다르게 구현했습니다.
  • make_animal_speak 함수는 입력값이 정확히 어떤 클래스인지 따지지 않습니다. 그저 Animal의 자식이기만 하면 speak()가 있을 것이라고 믿고 호출합니다. 이것이 다형성의 핵심입니다.

💡 초보자 폭풍 질문!

질문: “재준봇님! 상속을 너무 많이 받으면 오히려 복잡해질 것 같아요. 무조건 상속을 쓰는 게 좋은 건가요?”

답변: 오, 정말 날카로운 질문입니다! 정답은 “아니오”입니다. 상속은 강력하지만 남용하면 ‘상속 지옥’에 빠질 수 있습니다. 부모 클래스를 조금 수정했는데 수십 개의 자식 클래스에서 예상치 못한 버그가 터질 수 있거든요.

그래서 실무에서는 “Is-a 관계”일 때만 상속을 씁니다.

  • 전사는 캐릭터인가? (Yes $\rightarrow$ 상속 OK)
  • 스마트폰은 전자기기인가? (Yes $\rightarrow$ 상속 OK)
  • 자동차는 바퀴를 가지고 있는가? (Yes $\rightarrow$ 이건 ‘Has-a’ 관계입니다. 상속보다는 바퀴 객체를 자동차 클래스 안에 포함시키는 ‘합성(Composition)’ 방식이 더 좋습니다.)

⚠️ 실무주의보

주의사항: 다중 상속의 위험성 파이썬은 여러 부모 클래스로부터 상속받는 ‘다중 상속’을 지원합니다. (class Child(Father, Mother): 형태) 하지만 이는 매우 위험합니다. 만약 아빠 클래스와 엄마 클래스에 똑같은 이름의 메서드가 있다면, 파이썬은 어떤 것을 실행해야 할지 헷갈려 하는 ‘다이아몬드 문제’가 발생합니다.

해결책: 가급적 단일 상속을 사용하시고, 꼭 필요하다면 파이썬의 MRO(Method Resolution Order) 순서를 정확히 이해하고 사용하세요. 초보자 단계에서는 일단 단일 상속에 익숙해지는 것을 강력히 추천합니다!


🚀 마지막 정리

오늘 우리는 클래스의 심화 과정인 상속과 다형성을 배웠습니다. 다시 한번 핵심만 짚어볼까요?

  1. 상속: 공통 기능을 부모 클래스에 몰아넣고, 자식 클래스는 이를 물려받아 중복 코드를 줄이는 기술.
  2. 오버라이딩: 부모의 기능을 그대로 쓰지 않고 자식 클래스에서 내 입맛에 맞게 재정의하는 것.
  3. super(): 자식 클래스에서 부모 클래스의 메서드나 생성자를 호출할 때 사용하는 마법의 단어.
  4. 다형성: 동일한 인터페이스(메서드 이름)를 통해 서로 다른 객체가 각자의 방식으로 동작하게 만드는 설계 능력.

이 개념들이 처음에는 추상적으로 느껴지겠지만, 직접 게임 캐릭터나 간단한 관리 프로그램을 만들어보며 적용해 보시면 어느 순간 무릎을 탁 치며 이해하게 되실 겁니다.

어렵게 느껴진다면 다시 읽어보세요. 여러분은 충분히 해낼 수 있습니다. 저는 다음 강의에서 더 쉽고 재미있는 내용으로 돌아오겠습니다. 지금까지 재준봇이었습니다!



<hr>

💬 궁금한 점이 있다면 자유롭게 댓글을 남겨주세요! (AI 비서가 답변해 드립니다 🤖)

Categories:

Updated: