본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 1. · 15 Views
Python 클래스와 객체지향 프로그래밍 완벽 가이드
Python의 클래스와 객체지향 프로그래밍 개념을 초급 개발자 눈높이에 맞춰 설명합니다. 클래스 정의부터 상속, 특수 메서드까지 실무 예제와 함께 살펴봅니다.
목차
1. 클래스 정의와 인스턴스
김개발 씨는 회사에서 회원 관리 시스템을 만들고 있었습니다. 처음에는 딕셔너리로 회원 정보를 관리했는데, 회원이 수백 명으로 늘어나자 코드가 점점 복잡해졌습니다.
선배 박시니어 씨가 다가와 한마디 했습니다. "클래스를 써보는 건 어때요?"
클래스는 한마디로 설계도입니다. 마치 붕어빵 틀처럼, 같은 모양의 객체를 찍어낼 수 있는 틀이라고 생각하면 됩니다.
이 틀로 찍어낸 붕어빵 하나하나가 바로 인스턴스입니다. 클래스를 이해하면 데이터와 기능을 하나로 묶어 깔끔하게 관리할 수 있습니다.
다음 코드를 살펴봅시다.
# 클래스 정의: 설계도 만들기
class Member:
# 클래스 변수: 모든 인스턴스가 공유
total_count = 0
def __init__(self, name, email):
# 인스턴스 변수: 각 인스턴스마다 고유
self.name = name
self.email = email
Member.total_count += 1
# 인스턴스 생성: 설계도로 실제 객체 만들기
member1 = Member("홍길동", "hong@email.com")
member2 = Member("김철수", "kim@email.com")
print(member1.name) # 홍길동
print(Member.total_count) # 2
김개발 씨는 입사 3개월 차 주니어 개발자입니다. 오늘도 열심히 회원 관리 코드를 작성하던 중, 문득 고민에 빠졌습니다.
회원마다 이름, 이메일, 가입일, 포인트 등 여러 정보가 있는데, 이걸 딕셔너리로 관리하자니 실수하기가 너무 쉬웠습니다. 선배 개발자 박시니어 씨가 다가와 화면을 살펴봅니다.
"딕셔너리로 하면 키 이름을 잘못 써도 에러가 안 나서 버그 찾기 힘들어요. 클래스를 써보는 건 어때요?" 그렇다면 클래스란 정확히 무엇일까요?
쉽게 비유하자면, 클래스는 마치 붕어빵 틀과 같습니다. 붕어빵 틀 하나만 있으면 똑같은 모양의 붕어빵을 수십 개, 수백 개 만들어낼 수 있습니다.
팥 붕어빵도, 슈크림 붕어빵도 같은 틀에서 나오지만 속 재료는 다르듯이, 같은 클래스에서 만들어진 인스턴스들도 저마다 다른 데이터를 가질 수 있습니다. 클래스가 없던 시절에는 어땠을까요?
개발자들은 연관된 데이터를 따로따로 변수에 저장해야 했습니다. 회원 이름은 name 변수에, 이메일은 email 변수에, 포인트는 point 변수에 저장하다 보니 코드가 뒤죽박죽 되기 일쑤였습니다.
더 큰 문제는 회원이 여러 명일 때였습니다. name1, name2, name3 같은 변수를 만들거나, 리스트를 써도 어떤 인덱스가 어떤 데이터인지 헷갈렸습니다.
바로 이런 문제를 해결하기 위해 클래스가 등장했습니다. 클래스를 사용하면 관련 있는 데이터와 기능을 하나로 묶을 수 있습니다.
또한 코드의 재사용성이 크게 높아집니다. 무엇보다 실수를 줄이고 유지보수가 쉬워진다는 큰 장점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 **class Member:**를 보면 Member라는 이름의 클래스를 정의하는 것을 알 수 있습니다.
이 한 줄이 바로 설계도의 시작입니다. 그 아래 total_count = 0은 클래스 변수로, 모든 인스턴스가 공유하는 값입니다.
회원이 총 몇 명인지 세는 용도로 쓰입니다. init 메서드는 특별한 역할을 합니다.
인스턴스가 생성될 때 자동으로 호출되어 초기 설정을 담당합니다. self.name과 self.email은 인스턴스 변수로, 각 인스턴스마다 다른 값을 가집니다.
마지막으로 **Member("홍길동", "hong@email.com")**처럼 클래스 이름에 괄호를 붙여 호출하면 새 인스턴스가 생성됩니다. 마치 붕어빵 틀에 반죽을 넣어 붕어빵을 만드는 것과 같습니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 쇼핑몰 서비스를 개발한다고 가정해봅시다.
상품, 주문, 고객, 장바구니 등 다양한 개념을 각각 클래스로 정의하면 코드가 훨씬 체계적으로 정리됩니다. 각 클래스는 자신만의 데이터와 기능을 갖고 있어서 마치 레고 블록처럼 조립할 수 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 모든 것을 클래스로 만들려는 것입니다.
단순한 기능은 함수로 충분합니다. 클래스는 데이터와 그 데이터를 다루는 기능이 밀접하게 연관될 때 사용하는 것이 좋습니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 조언을 듣고 Member 클래스를 만든 김개발 씨는 고개를 끄덕였습니다.
"아, 이렇게 하면 회원 정보 관리가 훨씬 편하겠네요!" 클래스를 제대로 이해하면 더 깔끔하고 유지보수하기 쉬운 코드를 작성할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 클래스 이름은 대문자로 시작하는 CamelCase를 사용합니다
- 하나의 클래스는 하나의 책임만 갖도록 설계하세요
2. init 생성자
김개발 씨가 Member 클래스를 만들었는데, 인스턴스를 생성할 때마다 이름과 이메일을 따로 설정해야 했습니다. 실수로 설정을 깜빡하면 버그가 발생했습니다.
"인스턴스를 만들 때 자동으로 초기화할 수 없을까요?" 이런 고민에 박시니어 씨가 답했습니다. "init 생성자를 써보세요."
init은 인스턴스가 생성될 때 자동으로 호출되는 특별한 메서드입니다. 마치 새 집에 이사하면 가장 먼저 기본 가구를 배치하는 것처럼, 인스턴스에 필요한 초기 값들을 설정하는 역할을 합니다.
이를 생성자라고 부릅니다.
다음 코드를 살펴봅시다.
class Product:
def __init__(self, name, price, stock=0):
# 필수 속성 초기화
self.name = name
self.price = price
# 기본값이 있는 선택적 속성
self.stock = stock
# 자동 계산 속성
self.is_available = stock > 0
print(f"상품 '{name}'이 등록되었습니다.")
# 인스턴스 생성 시 __init__이 자동 호출됨
laptop = Product("노트북", 1500000, 10)
mouse = Product("마우스", 30000) # stock은 기본값 0
print(laptop.is_available) # True
print(mouse.stock) # 0
김개발 씨는 상품 관리 시스템을 만들고 있었습니다. 상품 클래스를 만들었는데, 인스턴스를 생성한 후에 일일이 속성을 설정하는 게 번거로웠습니다.
게다가 가끔 속성 설정을 깜빡해서 나중에 에러가 나곤 했습니다. 박시니어 씨가 코드를 보더니 말했습니다.
"init 메서드를 쓰면 인스턴스를 만들 때 자동으로 초기화할 수 있어요. 깜빡할 일이 없죠." 그렇다면 init이란 정확히 무엇일까요?
쉽게 비유하자면, init은 마치 신생아가 태어날 때 받는 출생증명서 작성과 같습니다. 아기가 태어나면 이름, 생년월일, 부모 정보 등 기본 정보를 자동으로 기록합니다.
마찬가지로 인스턴스가 생성되면 init이 자동으로 호출되어 필수 정보를 설정합니다. init이 없다면 어떻게 될까요?
인스턴스를 생성한 후 매번 속성을 수동으로 설정해야 합니다. 코드가 길어지고, 실수하기도 쉽습니다.
더 큰 문제는 필수 속성을 설정하지 않아도 에러가 나지 않는다는 것입니다. 나중에 그 속성을 사용할 때가 되어서야 에러가 발생합니다.
바로 이런 문제를 해결하기 위해 init 생성자를 사용합니다. init을 사용하면 인스턴스 생성 시점에 필수 값들을 강제할 수 있습니다.
또한 기본값을 설정해 선택적인 속성도 처리할 수 있습니다. 무엇보다 인스턴스가 항상 유효한 상태로 시작한다는 것을 보장할 수 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 **def init(self, name, price, stock=0):**를 보면 세 개의 매개변수가 있습니다.
self는 생성되는 인스턴스 자신을 가리킵니다. name과 price는 필수 매개변수이고, stock=0은 기본값이 있는 선택적 매개변수입니다.
메서드 내부에서 self.name = name처럼 self에 속성을 할당하면 인스턴스 변수가 됩니다. 이 값들은 해당 인스턴스에만 속합니다.
특히 주목할 점은 self.is_available = stock > 0 부분입니다. 이렇게 다른 속성을 기반으로 계산된 값도 초기화 시점에 설정할 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 데이터베이스에서 조회한 데이터로 객체를 만들 때 init이 유용합니다.
또한 설정 값의 유효성을 검사하거나, 로그를 남기는 등의 작업도 이 안에서 할 수 있습니다. 예제 코드의 print문처럼 상품 등록 시 로그를 남기는 것도 좋은 활용 예입니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 init 안에서 너무 많은 작업을 하는 것입니다.
무거운 연산이나 외부 API 호출 같은 작업은 별도의 메서드로 분리하는 것이 좋습니다. 생성자는 빠르게 실행되어야 합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. init을 적용한 후 코드가 훨씬 깔끔해졌습니다.
"이제 상품을 만들 때 실수로 가격을 빠뜨리면 바로 에러가 나네요. 오히려 좋아요!" init을 제대로 이해하면 안전하고 예측 가능한 객체를 만들 수 있습니다.
여러분도 클래스를 정의할 때 생성자를 적극 활용해 보세요.
실전 팁
💡 - 필수 속성은 기본값 없이, 선택 속성은 기본값과 함께 정의하세요
- 생성자에서는 복잡한 로직보다 단순한 초기화에 집중하세요
3. 인스턴스 메서드와 속성
김개발 씨는 상품 클래스에 할인 기능을 추가하고 싶었습니다. 상품마다 할인율이 다르고, 할인된 가격을 계산해야 했습니다.
"클래스 안에 함수를 넣을 수 있나요?" 박시니어 씨가 웃으며 말했습니다. "물론이죠.
그걸 메서드라고 해요."
인스턴스 메서드는 클래스 안에 정의된 함수로, 인스턴스의 데이터를 조작하거나 활용하는 기능을 담당합니다. 마치 리모컨의 버튼처럼, 객체에게 특정 동작을 수행하라고 명령하는 것입니다.
속성은 인스턴스가 가진 데이터이고, 메서드는 그 데이터를 다루는 행동입니다.
다음 코드를 살펴봅시다.
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance # 속성: 잔액
# 인스턴스 메서드: 입금
def deposit(self, amount):
if amount > 0:
self.balance += amount
return f"{amount}원 입금. 잔액: {self.balance}원"
return "유효하지 않은 금액입니다."
# 인스턴스 메서드: 출금
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount
return f"{amount}원 출금. 잔액: {self.balance}원"
return "잔액이 부족합니다."
account = BankAccount("김개발", 10000)
print(account.deposit(5000)) # 5000원 입금. 잔액: 15000원
print(account.withdraw(3000)) # 3000원 출금. 잔액: 12000원
김개발 씨는 은행 계좌 관리 프로그램을 만들고 있었습니다. 계좌에 입금하고 출금하는 기능이 필요했는데, 이걸 어디에 작성해야 할지 고민이었습니다.
클래스 바깥에 함수로 만들자니 계좌 정보를 매번 전달해야 해서 불편했습니다. 박시니어 씨가 힌트를 주었습니다.
"계좌와 관련된 기능은 계좌 클래스 안에 넣으면 돼요. 그게 바로 메서드예요." 그렇다면 인스턴스 메서드란 정확히 무엇일까요?
쉽게 비유하자면, 클래스가 설계도라면 속성은 그 물건의 특성이고, 메서드는 그 물건이 할 수 있는 행동입니다. 자동차 클래스를 생각해보세요.
색상, 속도, 연료량은 속성이고, 달리기, 멈추기, 경적 울리기는 메서드입니다. 자동차는 스스로 달릴 수 있어야 합니다.
메서드가 없다면 어떻게 될까요? 데이터와 그 데이터를 다루는 함수가 분리되어 있어 관리가 어려워집니다.
함수를 호출할 때마다 어떤 객체의 데이터를 조작할지 명시해야 합니다. 코드의 응집도가 떨어지고 실수할 가능성이 높아집니다.
바로 이런 문제를 해결하기 위해 인스턴스 메서드를 사용합니다. 메서드를 사용하면 데이터와 기능이 하나의 클래스 안에 묶입니다.
객체에게 직접 명령을 내리는 듯한 직관적인 코드를 작성할 수 있습니다. 무엇보다 self를 통해 인스턴스의 속성에 자연스럽게 접근할 수 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. deposit 메서드를 보면 첫 번째 매개변수로 self가 있습니다.
이것은 메서드를 호출한 인스턴스 자신을 가리킵니다. self.balance로 해당 계좌의 잔액에 접근할 수 있습니다.
**account.deposit(5000)**처럼 호출하면 Python이 자동으로 self 자리에 account를 전달합니다. 그래서 실제로 전달하는 인자는 amount 하나뿐입니다.
메서드 안에서 self.balance += amount처럼 속성을 변경할 수 있습니다. 이렇게 하면 해당 인스턴스의 상태가 업데이트됩니다.
실제 현업에서는 어떻게 활용할까요? 거의 모든 곳에서 활용됩니다.
사용자 객체의 비밀번호 변경 메서드, 주문 객체의 상태 변경 메서드, 게시글 객체의 조회수 증가 메서드 등 객체가 할 수 있는 모든 행동이 메서드로 표현됩니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 self를 빠뜨리는 것입니다. 메서드의 첫 번째 매개변수는 반드시 self여야 합니다.
또한 속성에 접근할 때 **self.**를 붙이지 않으면 지역 변수로 취급되어 원하는 대로 동작하지 않습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
메서드를 배운 김개발 씨는 계좌 클래스에 입금, 출금, 이체 기능을 모두 메서드로 구현했습니다. "이제 account.deposit(5000) 이렇게 쓰니까 정말 직관적이네요!" 인스턴스 메서드를 제대로 이해하면 객체가 스스로 행동하는 듯한 깔끔한 코드를 작성할 수 있습니다.
여러분도 관련 기능은 클래스 안에 메서드로 정의해 보세요.
실전 팁
💡 - 메서드의 첫 번째 매개변수는 반드시 self여야 합니다
- 속성에 접근하거나 변경할 때는 self.속성명 형태를 사용하세요
4. 상속과 오버라이딩
김개발 씨는 일반 회원과 VIP 회원을 관리해야 했습니다. 둘 다 회원이지만 VIP는 추가 혜택이 있었습니다.
회원 클래스를 복사해서 VIP 클래스를 만들자니 코드 중복이 심했습니다. "더 좋은 방법이 있을까요?" 박시니어 씨가 대답했습니다.
"상속을 쓰면 돼요."
상속은 기존 클래스의 속성과 메서드를 물려받아 새로운 클래스를 만드는 것입니다. 마치 부모에게서 재산을 물려받듯이, 자식 클래스는 부모 클래스의 모든 것을 가지면서 자신만의 것을 추가할 수 있습니다.
오버라이딩은 부모의 메서드를 자식이 재정의하는 것입니다.
다음 코드를 살펴봅시다.
class Member:
def __init__(self, name, email):
self.name = name
self.email = email
def get_discount_rate(self):
return 0.05 # 기본 5% 할인
# Member를 상속받는 VIPMember
class VIPMember(Member):
def __init__(self, name, email, vip_level):
super().__init__(name, email) # 부모 생성자 호출
self.vip_level = vip_level
# 메서드 오버라이딩: 부모 메서드 재정의
def get_discount_rate(self):
return 0.05 + (self.vip_level * 0.05) # VIP 레벨당 추가 5%
member = Member("일반회원", "normal@email.com")
vip = VIPMember("VIP회원", "vip@email.com", 3)
print(member.get_discount_rate()) # 0.05
print(vip.get_discount_rate()) # 0.2 (5% + 15%)
김개발 씨는 회원 등급 시스템을 만들어야 했습니다. 일반 회원은 5% 할인, VIP 회원은 등급에 따라 최대 20% 할인을 제공해야 했습니다.
처음에는 Member 클래스를 복사해서 VIPMember 클래스를 만들었습니다. 그런데 나중에 회원의 기본 기능을 수정할 때 두 클래스를 모두 고쳐야 해서 번거로웠습니다.
박시니어 씨가 말했습니다. "상속을 쓰면 공통 기능은 한 곳에서 관리하고, 다른 부분만 따로 정의할 수 있어요." 그렇다면 상속이란 정확히 무엇일까요?
쉽게 비유하자면, 상속은 마치 가업을 물려받는 것과 같습니다. 부모님이 운영하던 식당을 자식이 물려받으면, 기존의 레시피와 단골손님은 그대로 유지하면서 새로운 메뉴를 추가할 수 있습니다.
프로그래밍에서도 마찬가지로 부모 클래스의 속성과 메서드를 그대로 받으면서 필요한 것만 추가하거나 변경할 수 있습니다. 상속이 없다면 어떻게 될까요?
비슷한 클래스를 만들 때마다 코드를 복사해야 합니다. 공통 기능을 수정하려면 모든 클래스를 일일이 찾아서 고쳐야 합니다.
프로젝트가 커질수록 코드 중복이 눈덩이처럼 불어납니다. 바로 이런 문제를 해결하기 위해 상속을 사용합니다.
상속을 사용하면 코드 중복을 획기적으로 줄일 수 있습니다. 공통 기능은 부모 클래스에서 한 번만 정의하면 됩니다.
또한 오버라이딩을 통해 필요한 부분만 다르게 동작하도록 만들 수 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
**class VIPMember(Member):**에서 괄호 안의 Member가 부모 클래스입니다. 이렇게 하면 VIPMember는 Member의 모든 속성과 메서드를 상속받습니다.
**super().init(name, email)**은 부모 클래스의 생성자를 호출합니다. VIPMember도 name과 email이 필요하니까, 부모의 초기화 로직을 그대로 활용하는 것입니다.
그 다음 self.vip_level로 자식만의 속성을 추가합니다. get_discount_rate 메서드는 부모에도 있지만 자식에서 다시 정의했습니다.
이것이 바로 오버라이딩입니다. 자식 인스턴스에서 이 메서드를 호출하면 자식의 버전이 실행됩니다.
실제 현업에서는 어떻게 활용할까요? 웹 프레임워크를 사용할 때 상속을 자주 만납니다.
예를 들어 Django의 모든 뷰는 기본 View 클래스를 상속받습니다. 게임 개발에서도 모든 캐릭터가 Character 클래스를 상속받고, 전사, 마법사, 궁수 등이 각자의 특성을 오버라이딩합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 상속을 남용하는 것입니다.
"A는 B의 일종이다"라는 관계가 성립할 때만 상속을 사용해야 합니다. 단순히 코드를 재사용하고 싶다고 아무 클래스나 상속받으면 나중에 구조가 꼬여버립니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 상속을 적용한 후 코드가 훨씬 간결해졌습니다.
"이제 회원 기본 기능을 수정하면 VIP에도 자동으로 반영되네요. 유지보수가 정말 편해졌어요!" 상속과 오버라이딩을 제대로 이해하면 확장 가능한 구조를 설계할 수 있습니다.
여러분도 공통 기능은 부모 클래스로, 특화 기능은 자식 클래스로 분리해 보세요.
실전 팁
💡 - 상속은 "is-a" 관계일 때만 사용하세요 (VIPMember is a Member)
- super()를 사용해 부모의 기능을 활용하면서 확장하세요
5. 특수 메서드
김개발 씨가 상품 객체를 만들고 **print(product)**를 했더니 이상한 문자열이 출력되었습니다. <main.Product object at 0x7f...> 뭔가 알아보기 힘든 주소 같은 것이었습니다.
"객체를 print하면 상품명이랑 가격이 나왔으면 좋겠는데..." 박시니어 씨가 말했습니다. "str 메서드를 정의해보세요."
특수 메서드는 언더스코어 두 개로 감싸진 메서드로, Python이 특정 상황에서 자동으로 호출합니다. str은 print()나 str()에서 호출되고, repr은 개발자용 표현을 반환합니다.
이들을 정의하면 객체의 출력 형태를 원하는 대로 커스터마이징할 수 있습니다.
다음 코드를 살펴봅시다.
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
# 사용자 친화적 문자열 (print, str용)
def __str__(self):
return f"{self.name} - {self.price:,}원"
# 개발자용 상세 표현 (디버깅용)
def __repr__(self):
return f"Product(name='{self.name}', price={self.price})"
# 객체 비교 가능하게 만들기
def __eq__(self, other):
return self.name == other.name and self.price == other.price
laptop = Product("노트북", 1500000)
print(laptop) # 노트북 - 1,500,000원
print(repr(laptop)) # Product(name='노트북', price=1500000)
print(laptop == Product("노트북", 1500000)) # True
김개발 씨는 디버깅을 하다가 객체의 내용을 확인하고 싶었습니다. **print(product)**를 했더니 메모리 주소 같은 이상한 문자열만 나왔습니다.
매번 **print(product.name, product.price)**처럼 속성을 일일이 출력하는 건 너무 번거로웠습니다. 박시니어 씨가 다가와 말했습니다.
"str을 정의하면 print할 때 원하는 형태로 출력할 수 있어요." 그렇다면 특수 메서드란 정확히 무엇일까요? 쉽게 비유하자면, 특수 메서드는 마치 객체에게 부여하는 특별한 능력과 같습니다.
사람으로 치면 자기소개 능력(str), 신분증 보여주기(repr), 다른 사람과 비교하기(eq) 같은 것입니다. Python이 특정 상황에서 이 능력들을 자동으로 호출합니다.
특수 메서드가 없다면 어떻게 될까요? **print()**를 할 때마다 알아보기 힘든 메모리 주소가 출력됩니다.
두 객체가 같은지 비교하려면 속성을 하나하나 비교해야 합니다. 객체를 리스트에 넣고 정렬하는 것도 불가능합니다.
바로 이런 문제를 해결하기 위해 특수 메서드를 정의합니다. 특수 메서드를 정의하면 객체가 Python의 기본 기능과 자연스럽게 어우러집니다.
print, 비교 연산자, 산술 연산자 등이 우리가 원하는 대로 동작합니다. 이것을 연산자 오버로딩이라고도 합니다.
위의 코드를 한 줄씩 살펴보겠습니다. str 메서드는 **print()**나 str() 함수가 호출될 때 자동으로 실행됩니다.
여기서 반환하는 문자열이 출력됩니다. 사용자가 보기 좋은 형태로 작성합니다.
repr 메서드는 개발자용 표현입니다. 주로 디버깅이나 로깅에 사용됩니다.
가능하면 이 문자열로 객체를 다시 만들 수 있는 형태가 좋습니다. **Product(name='노트북', price=1500000)**처럼요.
eq 메서드는 == 연산자가 호출될 때 실행됩니다. 기본적으로 객체의 동등성은 메모리 주소로 판단하지만, 이 메서드를 정의하면 내용으로 비교할 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 데이터 모델 클래스에서 str과 repr은 거의 필수입니다.
로그를 남기거나 디버깅할 때 객체의 내용을 쉽게 파악할 수 있기 때문입니다. eq는 테스트 코드에서 두 객체가 같은지 비교할 때 유용합니다.
하지만 주의할 점도 있습니다. str과 repr의 용도를 헷갈리지 마세요.
str은 최종 사용자용, repr은 개발자용입니다. 만약 str이 없으면 Python은 repr을 대신 사용합니다.
따라서 repr은 가급적 항상 정의하는 것이 좋습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
특수 메서드를 정의한 후 디버깅이 훨씬 수월해졌습니다. "이제 print만 해도 상품 정보가 바로 보이니까 정말 편하네요!" 특수 메서드를 제대로 이해하면 Python의 다양한 기능과 자연스럽게 통합되는 객체를 만들 수 있습니다.
여러분도 자주 쓰는 클래스에는 최소한 repr은 정의해 보세요.
실전 팁
💡 - repr은 항상 정의하고, 필요하면 str도 추가하세요
- repr은 가능하면 객체를 재생성할 수 있는 형태로 작성하세요
6. PyTorch 모델 클래스 미리보기
김개발 씨는 머신러닝에 관심이 생겼습니다. PyTorch 튜토리얼을 보는데 **class Net(nn.Module):**이라는 코드가 나왔습니다.
지금까지 배운 클래스 문법이 여기서도 쓰이는 것 같은데, 정확히 어떻게 연결되는지 궁금했습니다. "이제 클래스를 배웠으니 이해할 수 있을 거예요." 박시니어 씨가 말했습니다.
PyTorch에서 신경망 모델은 nn.Module을 상속받아 만듭니다. 지금까지 배운 클래스, init, 상속, 오버라이딩이 모두 활용됩니다.
이 구조를 이해하면 딥러닝 모델의 기본 골격을 파악할 수 있습니다.
다음 코드를 살펴봅시다.
import torch
import torch.nn as nn
# nn.Module을 상속받아 신경망 정의
class SimpleNet(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__() # 부모 클래스 초기화
# 속성으로 레이어 정의
self.layer1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.layer2 = nn.Linear(hidden_size, output_size)
# forward 메서드 오버라이딩: 데이터 흐름 정의
def forward(self, x):
x = self.layer1(x)
x = self.relu(x)
x = self.layer2(x)
return x
model = SimpleNet(784, 128, 10) # 인스턴스 생성
print(model) # 모델 구조 출력 (__repr__ 활용)
김개발 씨는 딥러닝 공부를 시작했습니다. PyTorch 공식 튜토리얼을 따라하는데, 신경망을 정의하는 코드가 눈에 들어왔습니다.
클래스 문법이 사용되고 있었습니다. "아, 이게 그거구나!" 지금까지 배운 내용이 실제로 어떻게 쓰이는지 깨달은 순간이었습니다.
박시니어 씨가 옆에서 설명을 덧붙였습니다. "PyTorch에서 모든 신경망 모델은 nn.Module을 상속받아요.
지금까지 배운 거 다 쓰이죠?" 그렇다면 PyTorch 모델 클래스의 구조를 살펴볼까요? 먼저 **class SimpleNet(nn.Module):**을 봅시다.
이것은 우리가 배운 상속입니다. nn.Module이라는 부모 클래스를 상속받아 SimpleNet이라는 자식 클래스를 만드는 것입니다.
nn.Module에는 신경망에 필요한 다양한 기능이 이미 구현되어 있습니다. init 메서드 안에서 **super().init()**을 호출합니다.
이것은 부모 클래스의 생성자를 실행하는 것입니다. nn.Module의 초기화 로직이 먼저 실행되어야 신경망이 제대로 동작합니다.
우리가 배운 상속에서 super()의 역할 그대로입니다. 그 다음 self.layer1, self.relu, self.layer2 같은 속성을 정의합니다.
이것들은 신경망의 구성 요소인 레이어입니다. 인스턴스 속성으로 저장해두면 forward 메서드에서 사용할 수 있습니다.
forward 메서드는 오버라이딩의 예입니다. nn.Module에도 forward 메서드가 있지만, 자식 클래스에서 재정의하여 우리 모델만의 데이터 흐름을 정의합니다.
입력 x가 layer1을 거치고, ReLU를 통과하고, layer2를 거쳐 출력됩니다. 마지막으로 **print(model)**을 하면 모델 구조가 예쁘게 출력됩니다.
nn.Module이 repr을 잘 정의해두었기 때문입니다. 우리가 배운 특수 메서드가 여기서도 활용됩니다.
실제 현업에서는 어떻게 활용할까요? 딥러닝 모델 개발의 기본 패턴입니다.
이미지 분류, 자연어 처리, 추천 시스템 등 거의 모든 딥러닝 프로젝트가 이 구조를 따릅니다. 클래스를 이해했다면 어떤 복잡한 모델 코드도 읽을 수 있습니다.
하지만 주의할 점도 있습니다. PyTorch를 처음 배울 때 왜 이렇게 해야 하는지 이해하지 못하고 그냥 따라 치는 경우가 많습니다.
하지만 지금 여러분은 상속, init, 메서드, 오버라이딩을 이해하고 있습니다. 이 기초가 있으면 PyTorch 코드가 훨씬 명확하게 보일 것입니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 클래스를 배운 덕분에 PyTorch 코드가 수월하게 읽혔습니다.
"클래스 기초가 이렇게 중요한 거였네요. 기본기를 잘 다져두길 정말 잘했어요!" 객체지향 프로그래밍은 Python의 거의 모든 라이브러리에서 사용됩니다.
여러분도 오늘 배운 클래스 개념을 바탕으로 다양한 라이브러리를 탐험해 보세요.
실전 팁
💡 - PyTorch 모델은 항상 nn.Module을 상속받고 forward를 오버라이딩합니다
- __init__에서 레이어 정의, forward에서 연산 흐름 정의가 기본 패턴입니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.