파이썬에서 클래스를 다루다 보면 @classmethod라는 데코레이터(Decorator)를 만나게 됩니다. 이는 @staticmethod나 일반 인스턴스 메서드(Instance Method)와는 명확히 구분되는 고유한 목적을 가집니다.

이 글에서는 @classmethod가 무엇인지, 그리고 @staticmethod 및 인스턴스 메서드와 비교하여 어떤 상황에서 어떻게 사용해야 하는지 명확하게 알아보겠습니다.


1. 메서드의 세 가지 유형: Instance vs. Class vs. Static



파이썬 클래스에는 기본적으로 세 가지 유형의 메서드가 존재합니다. 이들의 가장 큰 차이는 메서드가 첫 번째 인자로 무엇을 받느냐에 있습니다.

class MyClass:
    # 1. 인스턴스 메서드 (Instance Method)
    def instance_method(self, arg1):
        # 'self'는 클래스의 인스턴스를 받습니다.
        # 인스턴스 속성(self.x)에 접근하고 수정할 수 있습니다.
        print(f"Instance method called with {self} and {arg1}")

    # 2. 클래스 메서드 (Class Method)
    @classmethod
    def class_method(cls, arg1):
        # 'cls'는 클래스 자체(MyClass)를 받습니다.
        # 클래스 속성에 접근하거나,
        # 인스턴스를 생성하는 '팩토리' 역할을 할 때 주로 사용됩니다.
        print(f"Class method called with {cls} and {arg1}")

    # 3. 정적 메서드 (Static Method)
    @staticmethod
    def static_method(arg1):
        # 'self'나 'cls'를 받지 않습니다.
        # 클래스나 인스턴스 상태와는 무관한 유틸리티 함수입니다.
        # 이 함수는 클래스 내부에 '속해있을' 뿐입니다.
        print(f"Static method called with {arg1}")

# 메서드 호출 예시
obj = MyClass()

# 인스턴스 메서드 (인스턴스를 통해 호출)
obj.instance_method("Hello") 

# 클래스 메서드 (클래스 또는 인스턴스를 통해 호출 가능)
MyClass.class_method("World")
obj.class_method("World") # 이것도 가능하지만, 클래스로 호출하는 것이 명확합니다.

# 정적 메서드 (클래스 또는 인스턴스를 통해 호출 가능)
MyClass.static_method("Python")
obj.static_method("Python")

한눈에 비교하기

구분 첫 번째 인자 접근 대상 주요 용도
인스턴스 메서드 self (인스턴스) 인스턴스 속성 (self.name) 객체의 상태를 조작하거나 접근
클래스 메서드 cls (클래스) 클래스 속성 (cls.count) 대체 생성자 (팩토리)
정적 메서드 없음 없음 클래스와 논리적으로 관련 있는 유틸리티 함수

2. @classmethod의 핵심 활용: 팩토리 메서드

@classmethod의 가장 중요하고 일반적인 사용 사례는 "대체 생성자(Alternative Constructor)" 또는 "팩토리 메서드(Factory Method)"를 정의하는 것입니다.

기본 생성자인 __init__은 보통 명확한 인자들을 받아 객체를 초기화합니다. 하지만 때로는 다른 방식(예: 문자열 파싱, 딕셔너리, 특정 계산 값)으로 객체를 생성해야 할 필요가 있습니다.

@classmethod를 사용하면 __init__ 외에 클래스 인스턴스를 반환하는 또 다른 '진입점'을 만들 수 있습니다.

classmethod개념도

예제: 날짜 문자열로 Person 객체 생성하기

Person 클래스가 nameage를 받는다고 가정해봅시다. 만약 '이름-출생연도' 형식의 문자열로도 객체를 생성하고 싶다면 어떻게 해야 할까요?

import datetime

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

    def greet(self):
        print(f"안녕하세요, 저는 {self.name}이고 {self.age}살입니다.")

    @classmethod
    def from_birth_year(cls, name, birth_year):
        """
        출생 연도를 받아 나이를 계산하여 Person 인스턴스를 생성합니다.
        """
        current_year = datetime.datetime.now().year
        age = current_year - birth_year

        # 'cls'는 Person 클래스 자체를 가리킵니다.
        # return Person(name, age)와 동일하지만,
        # 'cls'를 사용하면 상속 시 이점을 가집니다. (아래에서 설명)
        return cls(name, age)

# 기본 생성자 사용
person1 = Person("Alice", 30)
person1.greet()

# @classmethod로 만든 대체 생성자 사용
person2 = Person.from_birth_year("MadHatter", 1995)
person2.greet()

출력 결과:

안녕하세요, 저는 Alice이고 30살입니다.
안녕하세요, 저는 MadHatter이고 30살입니다. (현재 연도 2025 기준)

위 예제에서 from_birth_year 메서드는 cls라는 인자를 통해 Person 클래스 자체에 접근합니다. 그리고 계산된 age를 사용하여 cls(name, age)를 호출, 이는 곧 Person(name, age)를 호출하는 것과 같으며 새로운 Person 인스턴스를 반환합니다.


3. @classmethod와 상속



"그냥 Person(...)을 호출하면 되지, 왜 굳이 cls(...)를 사용하나요?"

@classmethod의 진정한 힘은 상속에서 드러납니다. cls는 현재 메서드가 호출된 _바로 그 클래스_를 가리킵니다.

Person을 상속받는 Student 클래스를 만든다고 가정해봅시다.

class Student(Person):
    def __init__(self, name, age, major):
        super().__init__(name, age) # 부모 클래스의 __init__ 호출
        self.major = major

    def greet(self):
        # 메서드 오버라이딩
        print(f"안녕하세요, 저는 {self.major} 전공 {self.name}이고 {self.age}살입니다.")

# Student 클래스에서 부모의 @classmethod를 호출합니다.
student1 = Student.from_birth_year("Jhon", 2005, "컴퓨터공학") 
# 앗, TypeError가 발생합니다!

# Student.from_birth_year를 호출하면 
# Person.from_birth_year가 실행되고, 
# 이때 'cls'는 'Student'가 됩니다.
# 즉, return cls(name, age)는 return Student(name, age)가 됩니다.
# 하지만 Student의 __init__은 'major' 인자가 추가로 필요합니다.

위 코드는 Student__init__major를 필요로 하기 때문에 에러가 납니다. 하지만 핵심은 clsPerson이 아닌 Student를 가리켰다는 점입니다.

만약 from_birth_year@staticmethod였거나, 내부에서 Person(name, age)을 하드코딩했다면, Student.from_birth_year를 호출해도 Student 객체가 아닌 Person 객체가 반환되었을 것입니다.

@classmethodcls를 통해 자식 클래스가 호출하더라도 올바른 자식 클래스의 인스턴스를 생성하도록 보장합니다. (물론 위 예처럼 자식 클래스의 __init__ 시그니처가 바뀐다면 from_birth_year도 오버라이딩해야 합니다.)


4. 정리

@classmethod에 대해 요약하자면 다음과 같습니다.

  1. @classmethodcls (클래스 자체)를 첫 번째 인자로 받습니다.

  2. 주된 용도는 '대체 생성자 (팩토리 메서드)'입니다. __init__ 외에 객체를 생성하는 다양한 방법을 제공할 때 사용합니다.

  3. 상속과 함께 사용할 때 강력합니다. 자식 클래스에서 호출해도 cls는 올바른 자식 클래스를 가리켜, 해당 클래스의 인스턴스를 생성합니다.

@staticmethod는 클래스/인스턴스 상태가 전혀 필요 없는 유틸리티 함수에, @classmethod는 클래스 자체에 접근해야 하는 팩토리 메서드에, 그리고 일반 인스턴스 메서드는 객체의 상태(self)를 다룰 때 사용한다는 것을 기억하면 코드를 훨씬 더 명확하고 파이썬답게 작성할 수 있습니다.