8-3 속성과 메서드: 데이터와 동작을 하나로

8-3 속성과 메서드: 데이터와 동작을 하나로

← 목차로 돌아가기

썸네일

객체 지향 프로그래밍의 핵심은 데이터(속성)와 동작(메서드)을 하나로 묶어 관리하는 것입니다. 이번 장에서는 클래스를 통해 만들어진 객체가 어떤 데이터를 가질 수 있고, 또 어떤 동작을 수행할 수 있는지 속성과 메서드의 개념을 상세히 알아보겠습니다. 이 두 가지 요소를 이해하면 훨씬 더 구조적이고 효율적인 코드를 작성할 수 있습니다.

핵심 요약

  • 속성(Attribute): 객체가 가지는 고유한 데이터로, 객체의 상태를 나타냅니다.
  • 메서드(Method): 객체가 수행할 수 있는 동작으로, 객체의 기능을 정의합니다.
  • `self` 매개변수: 메서드 내에서 해당 객체의 속성과 다른 메서드에 접근할 때 사용합니다.
  • 활용 가치: 데이터와 동작을 캡슐화하여 코드의 재사용성과 유지보수성을 높입니다.

객체의 데이터, 속성은 무엇일까요?

속성은 객체의 상태를 나타내는 고유한 데이터 조각입니다.

우리는 앞서 클래스와 객체의 개념을 붕어빵 틀과 붕어빵에 비유했습니다. 그렇다면 붕어빵 하나하나가 가진 ‘팥 맛’, ‘슈크림 맛’ 같은 정보가 바로 속성에 해당합니다. 파이썬에서는 객체 안에 저장되는 변수를 ‘속성(attribute)’이라고 부릅니다. 이 속성들은 객체가 생성될 때 초기화되거나, 객체의 생명 주기 동안 변경될 수 있습니다.

예를 들어, 사람을 나타내는 Person 클래스가 있다면, name(이름)이나 age(나이) 같은 정보들이 속성이 될 수 있습니다. 저의 경우, 데이터 분석 프로젝트에서 자주 사용하는 DataFrame 객체도 결국 ‘컬럼 이름’, ‘로우(row) 개수’와 같은 수많은 속성들을 가지고 있습니다. 2024년 기준으로, 이러한 속성들을 이해하는 것은 복잡한 데이터 구조를 다루는 데 첫걸음이 됩니다.

class Person:
    def __init__(self, name, age):
        self.name = name  # name 속성 초기화
        self.age = age    # age 속성 초기화

# Person 클래스의 인스턴스(객체) 생성
person1 = Person("홍길동", 30)
person2 = Person("김영희", 25)

# 객체의 속성에 접근하기
print(f"이름: {person1.name}, 나이: {person1.age}") # 이 코드는 person1 객체의 name과 age 속성에 접근합니다.
print(f"이름: {person2.name}, 나이: {person2.age}") # 이 코드는 person2 객체의 name과 age 속성에 접근합니다.

위 코드에서 person1.name과 같이 점(.) 연산자를 사용해 객체의 속성에 접근할 수 있습니다. __init__ 메서드 내에서 self.name = name과 같이 self를 사용하는 것은 이 name이 현재 객체(self)의 속성임을 명시하는 것입니다. 이 부분은 다음 장인 생성자 __init__: 객체 초기화하기에서 더 자세히 다루겠습니다.

객체의 동작, 메서드는 어떻게 정의하고 사용할까요?

메서드는 객체가 수행할 수 있는 동작을 정의하며, 특정 작업을 수행하는 함수와 같습니다.

붕어빵 비유로 돌아가면, 붕어빵을 ‘먹는’ 행위는 메서드에 해당합니다. 파이썬에서 메서드는 클래스 내부에 정의된 함수이며, 객체와 관련된 특정 작업을 수행합니다. 메서드는 항상 첫 번째 매개변수로 self를 받는데, 이 self는 메서드가 호출되는 객체 자신을 가리킵니다.

제가 tkinter로 간단한 GUI 프로그램을 만들 때, 버튼을 누르면 특정 동작을 수행하는 코드를 작성합니다. 이때, ‘버튼이 눌렸을 때 실행될 동작’이 바로 메서드가 되는 것이죠. 예를 들어, 텍스트 상자에 메시지를 표시하는 메서드나, 파일 내용을 저장하는 메서드 등이 있습니다. 이는 코드를 모듈화하고 관리하기 쉽게 만드는 데 결정적인 역할을 합니다.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self): # introduce 메서드 정의
        print(f"안녕하세요, 제 이름은 {self.name}이고, {self.age}살입니다.") # 이 코드는 객체의 name과 age 속성을 사용하여 소개 메시지를 출력합니다.

    def celebrate_birthday(self):
        self.age += 1 # 이 코드는 객체의 age 속성을 1 증가시킵니다.
        print(f"생일을 축하합니다! 이제 {self.age}살이 되었네요.")

# 객체 생성
person1 = Person("홍길동", 30)

# 메서드 호출
person1.introduce() # 이 코드는 person1 객체의 introduce 메서드를 호출합니다.
person1.celebrate_birthday() # 이 코드는 person1 객체의 celebrate_birthday 메서드를 호출합니다.
person1.introduce() # 이 코드는 나이가 변경된 후의 introduce 메서드를 다시 호출하여 변경된 나이를 확인합니다.

person1.introduce()처럼 점(.) 연산자를 사용하여 객체의 메서드를 호출합니다. 메서드 내부에서 self.name과 같이 속성에 접근하는 것을 볼 수 있는데, 이는 현재 객체의 속성을 사용하겠다는 의미입니다. self 매개변수는 파이썬이 메서드를 호출할 때 자동으로 해당 객체를 전달해 주기 때문에, 우리가 직접 값을 넘겨줄 필요는 없습니다.

속성과 메서드를 함께 활용하는 실제 예제

속성과 메서드는 객체를 통해 상호작용하며, 실제 세상의 복잡한 개체를 효과적으로 모델링합니다.

이번에는 자동차를 나타내는 Car 클래스를 만들어 속성과 메서드가 어떻게 유기적으로 동작하는지 살펴보겠습니다. 자동차는 ‘브랜드’, ‘모델’, ‘색상’과 같은 속성을 가지고, ‘가속’, ‘정지’, ‘경적 울리기’와 같은 동작(메서드)을 수행할 수 있습니다. 저는 파이썬으로 자동화 스크립트를 작성할 때, 특정 장치나 프로세스를 클래스로 모델링하고 그 장치의 상태를 속성으로, 제어 명령을 메서드로 구현하는 경우가 많습니다. 이렇게 하면 코드가 훨씬 직관적이고 관리가 편리해집니다.

class Car:
    def __init__(self, brand, model, color):
        self.brand = brand
        self.model = model
        self.color = color
        self.speed = 0 # 초기 속도는 0으로 설정합니다.

    def accelerate(self, amount):
        self.speed += amount # 이 코드는 현재 속도를 amount만큼 증가시킵니다.
        print(f"{self.brand} {self.model}이(가) {amount}만큼 가속하여 현재 속도 {self.speed}km/h.")

    def brake(self, amount):
        if self.speed - amount < 0:
            self.speed = 0
        else:
            self.speed -= amount # 이 코드는 현재 속도를 amount만큼 감소시킵니다.
        print(f"{self.brand} {self.model}이(가) {amount}만큼 감속하여 현재 속도 {self.speed}km/h.")

    def honk(self):
        print(f"{self.brand} {self.model}: 빵! 빵!") # 이 코드는 경적 소리를 출력합니다.

# Car 객체 생성
my_car = Car("Hyundai", "Sonata", "Black")

# 속성에 접근
print(f"내 차는 {my_car.color} {my_car.brand} {my_car.model}입니다.") # 이 코드는 my_car 객체의 속성들을 출력합니다.

# 메서드 호출을 통해 속성 변경 및 동작 수행
my_car.accelerate(50) # 이 코드는 my_car를 50km/h 가속합니다.
my_car.honk() # 이 코드는 my_car의 경적을 울립니다.
my_car.brake(20) # 이 코드는 my_car를 20km/h 감속합니다.
my_car.brake(40) # 이 코드는 my_car를 40km/h 추가 감속합니다. (속도가 0 이하로 내려가지 않음)

위 예제에서 acceleratebrake 메서드는 단순히 메시지를 출력하는 것을 넘어, 객체의 speed 속성 값을 직접 변경합니다. 이렇게 속성과 메서드가 긴밀하게 연결되어 객체의 상태를 관리하고 변화시키는 것이 객체 지향 프로그래밍의 강력한 특징입니다. 저 역시 GUI 프로그램에서 사용자가 입력 필드에 값을 넣으면 (속성), '저장' 버튼(메서드)을 눌러 그 값을 처리하는 방식으로 구현합니다. 이때 약 300줄 정도의 코드에서 5개 정도의 클래스를 활용하여 각각의 컴포넌트(버튼, 입력창 등)를 객체로 만들어 관리하면 훨씬 효율적이었습니다.

속성과 메서드를 다룰 때 흔히 하는 실수는 무엇인가요?

self 매개변수의 오용, 또는 객체 인스턴스와 클래스의 구분을 혼동하는 것이 흔한 실수입니다.

초보자분들이 가장 많이 실수하는 부분은 메서드 내에서 self를 빠뜨리거나, 클래스 변수와 인스턴스 변수를 혼동하는 것입니다. 예를 들어, 메서드 안에서 self.name 대신 그냥 name이라고만 쓰면, 파이썬은 해당 변수를 객체의 속성이 아닌 지역 변수로 인식하려고 합니다. 또한, 클래스 자체에서 속성에 직접 접근하려 시도하는 경우도 있습니다. 예를 들어 Person.name은 일반적으로 유효하지 않습니다. Person 클래스의 인스턴스(객체)를 먼저 생성한 후 person1.name과 같이 접근해야 합니다.

저도 파이썬 독학 초기에 self를 자주 빼먹어서 에러를 겪었던 기억이 생생합니다. 특히 클래스 변수와 인스턴스 변수 간의 미묘한 차이를 이해하는 데 시간이 걸렸습니다. 클래스 변수는 모든 인스턴스가 공유하는 데이터이지만, 인스턴스 변수(속성)는 각 인스턴스마다 독립적인 값을 가집니다. 이를 명확히 구분하는 것이 중요합니다.

class MyClass:
    class_attribute = "클래스 변수" # 클래스 변수

    def __init__(self, instance_attribute):
        self.instance_attribute = instance_attribute # 인스턴스 속성

    def print_attributes(self):
        print(f"인스턴스 속성: {self.instance_attribute}")
        print(f"클래스 변수: {self.class_attribute}") # 메서드 내에서 self를 통해 클래스 변수에도 접근 가능합니다.

# 잘못된 접근 시도 (예시)
# print(MyClass.instance_attribute) # 오류 발생: 인스턴스 속성은 객체 없이는 접근할 수 없습니다.

# 올바른 접근
obj1 = MyClass("인스턴스1의 데이터")
obj2 = MyClass("인스턴스2의 데이터")

print(obj1.instance_attribute) # 이 코드는 obj1의 인스턴스 속성에 접근합니다.
print(MyClass.class_attribute) # 이 코드는 클래스 변수에 직접 접근합니다.

obj1.print_attributes() # 이 코드는 obj1의 속성들을 출력합니다.
obj2.print_attributes() # 이 코드는 obj2의 속성들을 출력합니다.

위 코드를 보면 MyClass.class_attribute는 클래스 자체를 통해 접근 가능하지만, obj1.instance_attribute는 특정 객체를 통해서만 접근 가능합니다. 이러한 차이를 명확히 인지하고 코드를 작성하면 불필요한 오류를 줄일 수 있습니다.

FAQ

Q1: 속성과 변수는 같은 개념인가요?
A1: 엄밀히 말하면 다릅니다. 속성은 '객체에 소속된 변수'를 의미하며, 객체의 상태를 나타내는 데이터를 저장합니다. 일반 변수는 객체에 소속되지 않고 독립적으로 사용될 수 있습니다.

Q2: 메서드를 호출할 때 self는 왜 직접 전달하지 않나요?
A2: 파이썬 인터프리터가 메서드를 호출할 때, 자동으로 해당 객체 인스턴스를 첫 번째 매개변수(self)로 전달해 주기 때문입니다. 개발자가 명시적으로 전달할 필요가 없습니다.

Q3: __init__ 메서드는 일반 메서드와 무엇이 다른가요?
A3: __init__은 '생성자'라고 불리며, 객체가 처음 생성될 때 자동으로 호출되어 객체의 초기 상태(속성)를 설정하는 특별한 메서드입니다. 일반 메서드는 객체가 생성된 후에 필요에 따라 명시적으로 호출됩니다.

마무리하며

이번 장에서는 객체 지향 프로그래밍의 근간을 이루는 속성과 메서드에 대해 깊이 있게 알아보았습니다. 속성은 객체의 데이터를, 메서드는 객체의 동작을 정의하며, 이 둘이 유기적으로 결합하여 하나의 완결된 객체를 형성합니다. 이러한 이해는 앞으로 여러분이 더 크고 복잡한 프로그램을 설계하고 구현하는 데 필수적인 기초가 될 것입니다. 다음 장에서는 객체가 생성될 때 자동으로 호출되어 초기 설정을 담당하는 생성자 __init__에 대해 더 자세히 살펴보겠습니다. 이 개념들을 잘 익혀두시면 파이썬으로 더욱 강력한 프로그램을 만들 수 있을 것입니다.

참고 자료

  1. [1] Python 공식 튜토리얼 — 클래스 — https://docs.python.org/ko/3/tutorial/classes.html

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다