[Python 100일 챌린지] Day 31 - 클래스와 객체 기초
지금까지 함수로 “동작”만 묶어왔는데, 현실 세계를 표현하기엔 부족하죠? 🤔 ──예를 들어 “자동차”는 색상, 속도, 제조사 같은 데이터와 주행(), 정지() 같은 동작이 함께 있어야 합니다! 😊
게임 캐릭터도 마찬가지입니다. 이름, HP, 공격력 같은 속성과 공격(), 방어() 같은 행동이 하나로 묶여야 하죠.
바로 이것이 객체지향 프로그래밍(OOP)입니다! 데이터와 기능을 하나로 묶어 현실 세계를 코드로 표현하는 강력한 방법! 💡
🎯 오늘의 학습 목표
⭐⭐⭐ (30-40분 완독)
📚 사전 지식
🎯 학습 목표 1: 객체지향 프로그래밍의 개념 이해하기
지금까지 우리는 절차적 프로그래밍(Procedural Programming)을 배웠습니다. 순차적으로 코드를 작성하고, 필요할 때 함수를 호출했죠.
이제 객체지향 프로그래밍(Object-Oriented Programming, OOP)이라는 새로운 패러다임을 배워봅시다!
🏠 실생활 비유: 집 설계도와 실제 집
클래스(Class) = 집 설계도 📋
- 방 개수, 구조, 기능 등을 정의한 설계도
- 실제로 살 수는 없지만, 집을 만들 수 있는 틀
객체/인스턴스(Object/Instance) = 실제 집 🏠
- 설계도를 바탕으로 지은 실제 집
- 실제로 살 수 있고, 각각 다른 주소와 내부 인테리어를 가짐
1
2
3
4
5
6
7
8
# 집 설계도 (클래스)
class House:
pass
# 실제 집 (객체/인스턴스)
my_house = House() # 강남구 집
your_house = House() # 마포구 집
park_house = House() # 송파구 집
같은 설계도로 여러 집을 지을 수 있듯이, 하나의 클래스로 여러 객체를 만들 수 있습니다!
🎯 학습 목표 2: 클래스와 객체의 차이점 알기
기본 문법
1
2
3
4
5
class ClassName:
"""클래스 설명 (docstring)"""
# 클래스 내용
pass
클래스 네이밍 규칙
✅ 파스칼 케이스(PascalCase) 사용
- 각 단어의 첫 글자를 대문자로
- 단어 사이 공백 없음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ✅ 올바른 클래스 이름
class Student:
pass
class BankAccount:
pass
class StudentGradeManager:
pass
# ❌ 잘못된 클래스 이름
class student: # 소문자 시작
pass
class bank_account: # 스네이크 케이스
pass
class Student_grade: # 혼합된 스타일
pass
첫 번째 클래스 만들기
1
2
3
4
5
6
7
8
9
10
11
class Dog:
"""강아지를 표현하는 클래스"""
pass
# 객체 생성 (인스턴스화)
my_dog = Dog()
your_dog = Dog()
print(my_dog) # <__main__.Dog object at 0x...>
print(your_dog) # <__main__.Dog object at 0x...>
print(type(my_dog)) # <class '__main__.Dog'>
실행 결과:
1
2
3
<__main__.Dog object at 0x10a5e8d90>
<__main__.Dog object at 0x10a5e8dd0>
<class '__main__.Dog'>
💡 각 객체는 다른 메모리 주소(0x...)를 가집니다!
🎯 학습 목표 3: 간단한 클래스 정의하고 사용하기
속성(Attribute) 추가하기
객체에 데이터를 저장하려면 속성을 추가해야 합니다.
방법 1: 객체 생성 후 속성 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Dog:
pass
# 객체 생성
my_dog = Dog()
# 속성 추가
my_dog.name = "멍멍이"
my_dog.age = 3
my_dog.breed = "진돗개"
# 속성 접근
print(f"이름: {my_dog.name}")
print(f"나이: {my_dog.age}살")
print(f"품종: {my_dog.breed}")
실행 결과:
1
2
3
이름: 멍멍이
나이: 3살
품종: 진돗개
방법 2: 클래스 내부에 속성 정의 (권장 ✅)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Dog:
"""강아지를 표현하는 클래스"""
# 클래스 속성 (모든 인스턴스가 공유)
species = "Canis familiaris"
def set_info(self, name, age, breed):
"""강아지 정보를 설정하는 메서드"""
self.name = name
self.age = age
self.breed = breed
def bark(self):
"""짖는 소리를 출력하는 메서드"""
return f"{self.name}: 멍멍!"
def get_info(self):
"""강아지 정보를 반환하는 메서드"""
return f"{self.name}({self.age}살, {self.breed})"
# 사용 예시
my_dog = Dog()
my_dog.set_info("멍멍이", 3, "진돗개")
print(my_dog.bark()) # 멍멍이: 멍멍!
print(my_dog.get_info()) # 멍멍이(3살, 진돗개)
print(my_dog.species) # Canis familiaris
실행 결과:
1
2
3
멍멍이: 멍멍!
멍멍이(3살, 진돗개)
Canis familiaris
🎯 학습 목표 4: 인스턴스 생성과 속성 접근 방법 익히기
self 키워드 이해하기
self는 “현재 객체 자신”을 가리키는 참조입니다.
시각화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Dog:
def set_name(self, name):
self.name = name # "이 객체"의 name 속성에 저장
def bark(self):
return f"{self.name}: 멍멍!"
dog1 = Dog()
dog1.set_name("멍멍이") # self = dog1
dog2 = Dog()
dog2.set_name("바둑이") # self = dog2
print(dog1.bark()) # dog1.name 사용 → "멍멍이: 멍멍!"
print(dog2.bark()) # dog2.name 사용 → "바둑이: 멍멍!"
작동 원리
1
2
3
4
5
# 이렇게 호출하면
dog1.bark()
# 실제로는 이렇게 동작합니다
Dog.bark(dog1) # dog1이 self로 전달됨
💡 왜 self를 항상 첫 번째 매개변수로 쓸까?
1
2
3
4
5
6
7
8
9
class Calculator:
def add(self, a, b): # self가 첫 번째
return a + b
def get_result(self):
return self.result
calc = Calculator()
# calc.add(5, 3)는 Calculator.add(calc, 5, 3)로 변환됨
⚠️ self는 관례일 뿐 - 다른 이름도 가능
1
2
3
4
5
6
class Dog:
def bark(this): # self 대신 this 사용
return "멍멍!"
dog = Dog()
print(dog.bark()) # 작동은 하지만 권장하지 않음!
✅ 하지만 Python 커뮤니티의 99.9%가 self를 사용하므로, self를 쓰세요!
💡 실전 예제와 활용
예제 1: 학생 관리 시스템
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class Student:
"""학생 정보를 관리하는 클래스"""
school_name = "파이썬 고등학교" # 클래스 속성
def set_info(self, name, student_id, grade):
"""학생 정보 설정"""
self.name = name
self.student_id = student_id
self.grade = grade
self.scores = [] # 성적 리스트
def add_score(self, subject, score):
"""과목 성적 추가"""
self.scores.append({
"subject": subject,
"score": score
})
def get_average(self):
"""평균 성적 계산"""
if not self.scores:
return 0
total = sum(item["score"] for item in self.scores)
return total / len(self.scores)
def print_report(self):
"""성적표 출력"""
print(f"\n{'='*40}")
print(f"학교: {Student.school_name}")
print(f"이름: {self.name} ({self.student_id})")
print(f"학년: {self.grade}학년")
print(f"{'-'*40}")
if self.scores:
print("과목별 성적:")
for item in self.scores:
print(f" - {item['subject']}: {item['score']}점")
print(f"{'-'*40}")
print(f"평균: {self.get_average():.2f}점")
else:
print("등록된 성적이 없습니다.")
print(f"{'='*40}\n")
# 사용 예시
student1 = Student()
student1.set_info("홍길동", "2024001", 2)
student1.add_score("수학", 95)
student1.add_score("영어", 88)
student1.add_score("과학", 92)
student1.print_report()
student2 = Student()
student2.set_info("김철수", "2024002", 2)
student2.add_score("수학", 78)
student2.add_score("영어", 85)
student2.print_report()
실행 결과:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
========================================
학교: 파이썬 고등학교
이름: 홍길동 (2024001)
학년: 2학년
----------------------------------------
과목별 성적:
- 수학: 95점
- 영어: 88점
- 과학: 92점
----------------------------------------
평균: 91.67점
========================================
========================================
학교: 파이썬 고등학교
이름: 김철수 (2024002)
학년: 2학년
----------------------------------------
과목별 성적:
- 수학: 78점
- 영어: 85점
----------------------------------------
평균: 81.50점
========================================
예제 2: 은행 계좌 시스템
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class BankAccount:
"""은행 계좌를 표현하는 클래스"""
bank_name = "파이썬 은행"
def set_account(self, owner, account_number, balance=0):
"""계좌 정보 설정"""
self.owner = owner
self.account_number = account_number
self.balance = balance
self.transactions = [] # 거래 내역
def deposit(self, amount):
"""입금"""
if amount <= 0:
return "입금액은 0보다 커야 합니다."
self.balance += amount
self.transactions.append(f"입금: +{amount:,}원")
return f"{amount:,}원이 입금되었습니다. (잔액: {self.balance:,}원)"
def withdraw(self, amount):
"""출금"""
if amount <= 0:
return "출금액은 0보다 커야 합니다."
if amount > self.balance:
return f"잔액이 부족합니다. (현재 잔액: {self.balance:,}원)"
self.balance -= amount
self.transactions.append(f"출금: -{amount:,}원")
return f"{amount:,}원이 출금되었습니다. (잔액: {self.balance:,}원)"
def get_balance(self):
"""잔액 조회"""
return f"{self.owner}님의 잔액: {self.balance:,}원"
def print_statement(self):
"""거래 내역 출력"""
print(f"\n{'='*50}")
print(f"{BankAccount.bank_name} - 거래 내역서")
print(f"{'='*50}")
print(f"예금주: {self.owner}")
print(f"계좌번호: {self.account_number}")
print(f"{'-'*50}")
if self.transactions:
print("거래 내역:")
for i, transaction in enumerate(self.transactions, 1):
print(f" {i}. {transaction}")
else:
print("거래 내역이 없습니다.")
print(f"{'-'*50}")
print(f"현재 잔액: {self.balance:,}원")
print(f"{'='*50}\n")
# 사용 예시
account = BankAccount()
account.set_account("홍길동", "123-456-789", 10000)
print(account.deposit(50000))
print(account.withdraw(20000))
print(account.deposit(30000))
print(account.withdraw(100000)) # 잔액 부족
print(account.get_balance())
account.print_statement()
실행 결과:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
50,000원이 입금되었습니다. (잔액: 60,000원)
20,000원이 출금되었습니다. (잔액: 40,000원)
30,000원이 입금되었습니다. (잔액: 70,000원)
잔액이 부족합니다. (현재 잔액: 70,000원)
홍길동님의 잔액: 70,000원
==================================================
파이썬 은행 - 거래 내역서
==================================================
예금주: 홍길동
계좌번호: 123-456-789
--------------------------------------------------
거래 내역:
1. 입금: +50,000원
2. 출금: -20,000원
3. 입금: +30,000원
--------------------------------------------------
현재 잔액: 70,000원
==================================================
예제 3: 게임 캐릭터 시스템
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class Character:
"""게임 캐릭터를 표현하는 클래스"""
def create(self, name, char_class, level=1):
"""캐릭터 생성"""
self.name = name
self.char_class = char_class
self.level = level
self.hp = 100
self.max_hp = 100
self.exp = 0
self.exp_to_next_level = 100
def take_damage(self, damage):
"""피해 입기"""
self.hp = max(0, self.hp - damage)
if self.hp == 0:
return f"{self.name}이(가) 쓰러졌습니다!"
else:
return f"{self.name}이(가) {damage}의 피해를 입었습니다. (HP: {self.hp}/{self.max_hp})"
def heal(self, amount):
"""회복"""
old_hp = self.hp
self.hp = min(self.max_hp, self.hp + amount)
healed = self.hp - old_hp
return f"{self.name}이(가) {healed}만큼 회복했습니다. (HP: {self.hp}/{self.max_hp})"
def gain_exp(self, exp):
"""경험치 획득"""
self.exp += exp
message = f"{self.name}이(가) {exp} 경험치를 획득했습니다."
# 레벨업 체크
if self.exp >= self.exp_to_next_level:
self.level_up()
message += f"\n🎉 레벨 업! {self.name}의 레벨이 {self.level}이 되었습니다!"
return message
def level_up(self):
"""레벨업"""
self.level += 1
self.exp -= self.exp_to_next_level
self.exp_to_next_level = int(self.exp_to_next_level * 1.5)
# 스탯 증가
self.max_hp += 20
self.hp = self.max_hp
def get_status(self):
"""상태 확인"""
status = f"""
{'='*40}
[캐릭터 정보]
이름: {self.name}
직업: {self.char_class}
레벨: {self.level}
HP: {self.hp}/{self.max_hp}
경험치: {self.exp}/{self.exp_to_next_level}
{'='*40}
"""
return status.strip()
# 사용 예시
player = Character()
player.create("용사", "전사")
print(player.get_status())
print("\n" + "="*40)
print("전투 시작!")
print("="*40 + "\n")
print(player.take_damage(30))
print(player.take_damage(20))
print(player.heal(15))
print(player.gain_exp(80))
print(player.gain_exp(50)) # 레벨업!
print("\n" + player.get_status())
실행 결과:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
========================================
[캐릭터 정보]
이름: 용사
직업: 전사
레벨: 1
HP: 100/100
경험치: 0/100
========================================
========================================
전투 시작!
========================================
용사이(가) 30의 피해를 입었습니다. (HP: 70/100)
용사이(가) 20의 피해를 입었습니다. (HP: 50/100)
용사이(가) 15만큼 회복했습니다. (HP: 65/100)
용사이(가) 80 경험치를 획득했습니다.
용사이(가) 50 경험치를 획득했습니다.
🎉 레벨 업! 용사의 레벨이 2이 되었습니다!
========================================
[캐릭터 정보]
이름: 용사
직업: 전사
레벨: 2
HP: 120/120
경험치: 30/150
========================================
클래스 속성 vs 인스턴스 속성
클래스 속성 (Class Attribute)
모든 인스턴스가 공유하는 속성입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Dog:
species = "Canis familiaris" # 클래스 속성
def set_name(self, name):
self.name = name # 인스턴스 속성
dog1 = Dog()
dog2 = Dog()
print(dog1.species) # Canis familiaris
print(dog2.species) # Canis familiaris
# 클래스 속성 변경
Dog.species = "개과"
print(dog1.species) # 개과 (모든 인스턴스에 영향!)
print(dog2.species) # 개과
인스턴스 속성 (Instance Attribute)
각 인스턴스가 독립적으로 가지는 속성입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog:
def set_info(self, name, age):
self.name = name # 인스턴스 속성
self.age = age # 인스턴스 속성
dog1 = Dog()
dog1.set_info("멍멍이", 3)
dog2 = Dog()
dog2.set_info("바둑이", 5)
print(dog1.name, dog1.age) # 멍멍이 3
print(dog2.name, dog2.age) # 바둑이 5
혼합 사용 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Employee:
company_name = "파이썬 주식회사" # 클래스 속성
employee_count = 0 # 클래스 속성
def set_info(self, name, position):
self.name = name # 인스턴스 속성
self.position = position # 인스턴스 속성
Employee.employee_count += 1 # 클래스 속성 증가
def introduce(self):
return f"{Employee.company_name}의 {self.position} {self.name}입니다."
emp1 = Employee()
emp1.set_info("홍길동", "개발자")
emp2 = Employee()
emp2.set_info("김철수", "디자이너")
print(emp1.introduce())
print(emp2.introduce())
print(f"총 직원 수: {Employee.employee_count}명")
실행 결과:
1
2
3
파이썬 주식회사의 개발자 홍길동입니다.
파이썬 주식회사의 디자이너 김철수입니다.
총 직원 수: 2명
💡 핵심 실전 팁
OOP를 처음 배우는 분들을 위해 가장 중요한 10가지 핵심 팁만 정리했습니다. 이것만 잘 이해하고 실천하면 클래스를 자유롭게 사용할 수 있습니다!
Tip 1: self 빠뜨리지 않기 ⭐⭐⭐
클래스 메서드의 첫 번째 매개변수는 항상 self여야 합니다.
1
2
3
4
5
6
class Dog:
def bark(): # ❌ self가 없음!
return "멍멍!"
dog = Dog()
dog.bark() # TypeError: bark() takes 0 positional arguments but 1 was given
해결:
1
2
3
class Dog:
def bark(self): # ✅ self 추가
return "멍멍!"
Tip 2: self로 속성 접근하기 ⭐⭐⭐
인스턴스 속성을 사용할 때는 반드시 self.속성명 형태로 접근합니다.
1
2
3
4
5
6
class Dog:
def set_name(self, name):
name = name # ❌ 인스턴스 속성에 저장 안 됨!
def bark(self):
return f"{name}: 멍멍!" # NameError!
해결:
1
2
3
4
5
6
class Dog:
def set_name(self, name):
self.name = name # ✅ self.name으로 저장
def bark(self):
return f"{self.name}: 멍멍!" # ✅ self.name으로 접근
Tip 3: 클래스 이름은 PascalCase로 ⭐⭐
파이썬 커뮤니티의 표준 명명 규칙을 따릅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
# ❌ 잘못된 명명
class my_class:
pass
# ✅ 올바른 명명 (PEP 8 권장)
class MyClass:
pass
class Student:
pass
class BankAccount:
pass
규칙: 클래스는 PascalCase, 함수/변수는 snake_case
Tip 4: __init__으로 초기화하기 ⭐⭐⭐
객체를 생성할 때 필요한 정보는 __init__ 메서드에서 받습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ❌ 비효율적인 방법
class Student:
def set_info(self, name, age):
self.name = name
self.age = age
student = Student()
student.set_info("김철수", 20) # 생성 후 별도로 설정
# ✅ 권장 방법
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
student = Student("김철수", 20) # 생성과 동시에 초기화
Tip 5: 메서드 vs 일반 함수 이해하기 ⭐⭐
둘의 차이를 명확히 알아야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 일반 함수 - 클래스 밖에서 정의
def calculate_area(width, height):
return width * height
# 메서드 - 클래스 안에서 정의, self로 속성 접근
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def calculate_area(self):
return self.width * self.height
# 사용
area1 = calculate_area(10, 5) # 함수: 인자 전달
rect = Rectangle(10, 5)
area2 = rect.calculate_area() # 메서드: self로 자동 접근
Tip 6: 클래스 vs 함수 선택하기 ⭐⭐
모든 것을 클래스로 만들 필요는 없습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ❌ 불필요한 클래스 사용
class Calculator:
def add(self, a, b):
return a + b
# ✅ 간단한 연산은 함수로
def add(a, b):
return a + b
# ✅ 상태를 유지해야 할 때는 클래스
class ShoppingCart:
def __init__(self):
self.items = [] # 상태 유지
def add_item(self, item):
self.items.append(item)
def get_total(self):
return sum(item['price'] for item in self.items)
선택 기준:
- 함수: 상태 없는 단순 연산 (계산기, 변환 함수)
- 클래스: 상태를 유지해야 하는 경우 (장바구니, 게임 캐릭터)
Tip 7: 속성 명명 규칙 이해하기 ⭐
언더스코어(_)의 의미를 이해하면 코드가 명확해집니다.
1
2
3
4
5
6
7
8
9
10
11
12
class Student:
def __init__(self, name, student_id):
# ✅ 공개 속성: 일반 이름
self.name = name
self.student_id = student_id
# ✅ 내부 사용: 언더스코어(_) 접두사
self._grade = None
student = Student("김철수", "2024001")
print(student.name) # ✅ 공개 속성 접근
print(student._grade) # ⚠️ 가능하지만 "건드리지 마세요" 신호
규칙:
attribute: 공개 속성 (자유롭게 사용)_attribute: 내부 사용 (접근하지 않는 것이 관례)
Tip 8: 메서드는 동사로 명명하기 ⭐
메서드 이름은 “무엇을 하는지” 명확하게 표현합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
class BankAccount:
def __init__(self, balance):
self.balance = balance
# ✅ 동작을 나타내는 동사 사용
def deposit(self, amount): # 입금하다
self.balance += amount
def withdraw(self, amount): # 출금하다
self.balance -= amount
def get_balance(self): # 잔액을 가져오다
return self.balance
Tip 9: 클래스와 인스턴스 구분하기 ⭐⭐
클래스는 설계도, 인스턴스는 실제 객체입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog:
species = "개" # 클래스 속성
# ❌ 잘못된 사용
print(Dog.name) # AttributeError: 클래스에는 name 속성이 없음!
dog = Dog()
dog.name = "멍멍이" # 인스턴스 속성 생성
# ✅ 올바른 사용
print(Dog.species) # 클래스 속성은 클래스로 접근
print(dog.name) # 인스턴스 속성은 인스턴스로 접근
print(dog.species) # 클래스 속성도 인스턴스로 접근 가능
Tip 10: 단일 책임 원칙 지키기 ⭐⭐
하나의 클래스는 하나의 역할만 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# ❌ 너무 많은 책임
class Student:
def __init__(self, name):
self.name = name
def study(self): # 학생의 역할 ✅
pass
def save_to_database(self): # DB의 역할 ❌
pass
def send_email(self): # 이메일의 역할 ❌
pass
# ✅ 단일 책임 원칙
class Student:
def __init__(self, name):
self.name = name
def study(self):
pass
class StudentRepository: # DB 저장 전담
def save(self, student):
pass
class EmailService: # 이메일 전담
def send(self, student, message):
pass
원칙: 클래스는 하나의 변경 이유만 가져야 합니다 (Single Responsibility Principle)
🧪 연습 문제
문제 1: 도서 관리 클래스
다음 요구사항을 만족하는 Book 클래스를 작성하세요:
요구사항:
- 책 정보(제목, 저자, ISBN, 가격)를 설정하는 메서드
- 할인율을 적용한 가격을 반환하는 메서드
- 책 정보를 보기 좋게 출력하는 메서드
1
2
3
4
5
# 사용 예시
book = Book()
book.set_info("파이썬 정복", "홍길동", "978-1234567890", 25000)
print(book.get_discounted_price(10)) # 10% 할인
book.print_info()
예상 출력:
1
2
3
4
5
6
7
22500.0
=====================================
제목: 파이썬 정복
저자: 홍길동
ISBN: 978-1234567890
가격: 25,000원
=====================================
💡 힌트
1
2
3
4
5
6
7
8
9
10
11
12
class Book:
def set_info(self, title, author, isbn, price):
# 인스턴스 속성 설정
pass
def get_discounted_price(self, discount_rate):
# 할인된 가격 = 원가 * (1 - 할인율/100)
pass
def print_info(self):
# 책 정보 출력
pass
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Book:
"""도서 정보를 관리하는 클래스"""
def set_info(self, title, author, isbn, price):
"""책 정보 설정"""
self.title = title
self.author = author
self.isbn = isbn
self.price = price
def get_discounted_price(self, discount_rate):
"""할인된 가격 반환"""
discount = self.price * (discount_rate / 100)
return self.price - discount
def print_info(self):
"""책 정보 출력"""
print("=" * 37)
print(f"제목: {self.title}")
print(f"저자: {self.author}")
print(f"ISBN: {self.isbn}")
print(f"가격: {self.price:,}원")
print("=" * 37)
# 테스트
book = Book()
book.set_info("파이썬 정복", "홍길동", "978-1234567890", 25000)
print(book.get_discounted_price(10))
book.print_info()
문제 2: 쇼핑 카트 시스템
다음 요구사항을 만족하는 ShoppingCart 클래스를 작성하세요:
요구사항:
- 상품 추가 메서드 (상품명, 가격, 수량)
- 상품 제거 메서드 (상품명)
- 총 금액 계산 메서드
- 장바구니 내용 출력 메서드
1
2
3
4
5
6
7
8
# 사용 예시
cart = ShoppingCart()
cart.set_owner("홍길동")
cart.add_item("노트북", 1500000, 1)
cart.add_item("마우스", 30000, 2)
cart.add_item("키보드", 80000, 1)
cart.print_cart()
print(f"총 금액: {cart.get_total():,}원")
예상 출력:
1
2
3
4
5
6
7
8
========================================
홍길동님의 장바구니
========================================
1. 노트북 - 1,500,000원 x 1개
2. 마우스 - 30,000원 x 2개
3. 키보드 - 80,000원 x 1개
========================================
총 금액: 1,640,000원
💡 힌트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ShoppingCart:
def set_owner(self, owner):
self.owner = owner
self.items = [] # 상품 리스트
def add_item(self, name, price, quantity):
# 딕셔너리 형태로 items에 추가
pass
def remove_item(self, name):
# items에서 해당 상품 제거
pass
def get_total(self):
# 각 상품의 (가격 * 수량)을 모두 더하기
pass
def print_cart(self):
# 장바구니 내용 출력
pass
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class ShoppingCart:
"""쇼핑 카트를 관리하는 클래스"""
def set_owner(self, owner):
"""카트 소유자 설정"""
self.owner = owner
self.items = []
def add_item(self, name, price, quantity):
"""상품 추가"""
self.items.append({
"name": name,
"price": price,
"quantity": quantity
})
def remove_item(self, name):
"""상품 제거"""
for item in self.items:
if item["name"] == name:
self.items.remove(item)
return f"{name}이(가) 제거되었습니다."
return f"{name}을(를) 찾을 수 없습니다."
def get_total(self):
"""총 금액 계산"""
total = 0
for item in self.items:
total += item["price"] * item["quantity"]
return total
def print_cart(self):
"""장바구니 출력"""
print("=" * 40)
print(f"{self.owner}님의 장바구니")
print("=" * 40)
if not self.items:
print("장바구니가 비어있습니다.")
else:
for i, item in enumerate(self.items, 1):
print(f"{i}. {item['name']} - {item['price']:,}원 x {item['quantity']}개")
print("=" * 40)
# 테스트
cart = ShoppingCart()
cart.set_owner("홍길동")
cart.add_item("노트북", 1500000, 1)
cart.add_item("마우스", 30000, 2)
cart.add_item("키보드", 80000, 1)
cart.print_cart()
print(f"총 금액: {cart.get_total():,}원")
문제 3: 온도 변환기 클래스
섭씨, 화씨, 켈빈 온도를 관리하고 변환하는 Temperature 클래스를 작성하세요:
요구사항:
- 섭씨 온도를 설정하는 메서드
- 화씨로 변환하는 메서드 (°F = °C × 9/5 + 32)
- 켈빈으로 변환하는 메서드 (K = °C + 273.15)
- 모든 온도 정보를 출력하는 메서드
1
2
3
4
# 사용 예시
temp = Temperature()
temp.set_celsius(25)
temp.print_all()
예상 출력:
1
2
3
4
5
6
7
========================================
온도 정보
========================================
섭씨: 25.0°C
화씨: 77.0°F
켈빈: 298.15K
========================================
💡 힌트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Temperature:
def set_celsius(self, celsius):
self.celsius = celsius
def to_fahrenheit(self):
# 화씨 = 섭씨 × 9/5 + 32
pass
def to_kelvin(self):
# 켈빈 = 섭씨 + 273.15
pass
def print_all(self):
# 모든 온도 출력
pass
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Temperature:
"""온도 변환을 관리하는 클래스"""
def set_celsius(self, celsius):
"""섭씨 온도 설정"""
self.celsius = celsius
def to_fahrenheit(self):
"""화씨로 변환"""
return self.celsius * 9/5 + 32
def to_kelvin(self):
"""켈빈으로 변환"""
return self.celsius + 273.15
def print_all(self):
"""모든 온도 정보 출력"""
print("=" * 40)
print("온도 정보")
print("=" * 40)
print(f"섭씨: {self.celsius}°C")
print(f"화씨: {self.to_fahrenheit()}°F")
print(f"켈빈: {self.to_kelvin()}K")
print("=" * 40)
# 테스트
temp = Temperature()
temp.set_celsius(25)
temp.print_all()
print()
temp2 = Temperature()
temp2.set_celsius(0)
temp2.print_all()
print()
temp3 = Temperature()
temp3.set_celsius(100)
temp3.print_all()
실행 결과:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
========================================
온도 정보
========================================
섭씨: 25°C
화씨: 77.0°F
켈빈: 298.15K
========================================
========================================
온도 정보
========================================
섭씨: 0°C
화씨: 32.0°F
켈빈: 273.15K
========================================
========================================
온도 정보
========================================
섭씨: 100°C
화씨: 212.0°F
켈빈: 373.15K
========================================
📝 오늘 배운 내용 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| 클래스 | 객체를 만들기 위한 설계도 | class Dog: |
| 객체/인스턴스 | 클래스로 만든 실제 데이터 | my_dog = Dog() |
| 속성 | 객체가 가진 데이터 | self.name = "멍멍이" |
| 메서드 | 객체가 수행하는 동작 | def bark(self): |
| self | 현재 인스턴스를 가리키는 참조 | self.name |
| 클래스 속성 | 모든 인스턴스가 공유하는 속성 | species = "개" |
| 인스턴스 속성 | 각 인스턴스가 독립적으로 가지는 속성 | self.name = name |
클래스 작성 순서
- 클래스 정의 (
class ClassName:) - 독스트링 작성 (클래스 설명)
- 클래스 속성 정의 (필요한 경우)
- 메서드 작성 (항상 첫 번째 매개변수는
self) - 객체 생성 및 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 템플릿
class MyClass:
"""클래스 설명"""
class_attribute = "공유 속성" # 클래스 속성
def set_info(self, param1, param2):
"""정보 설정"""
self.attribute1 = param1 # 인스턴스 속성
self.attribute2 = param2
def do_something(self):
"""동작 수행"""
return f"{self.attribute1}이(가) {self.attribute2}을(를) 실행"
# 사용
obj = MyClass()
obj.set_info("값1", "값2")
print(obj.do_something())
❓ FAQ - 자주 묻는 질문
Q1: 클래스와 객체의 차이가 정확히 뭔가요?
A: 클래스는 설계도, 객체는 실제 제품입니다.
1
2
3
4
5
6
7
8
# 클래스 = 자동차 설계도
class Car:
def __init__(self, color):
self.color = color
# 객체(인스턴스) = 실제 자동차
my_car = Car("빨강") # 빨간 자동차 1대
your_car = Car("파랑") # 파란 자동차 1대
비유:
- 클래스: 붕어빵 틀 (하나만 있으면 됨)
- 객체: 붕어빵 (틀로 여러 개 만듦)
핵심:
- 클래스 1개로 객체 여러 개 생성 가능
- 각 객체는 독립적인 데이터를 가짐
Q2: 왜 객체지향을 사용해야 하나요? 함수만으로는 안 되나요?
A: 함수만으로도 가능하지만, 복잡한 프로그램은 객체지향이 훨씬 관리하기 쉽습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# ❌ 함수만 사용 - 데이터와 기능이 분리됨
student_name = "홍길동"
student_age = 20
student_score = 85
def print_student(name, age, score):
print(f"{name}({age}세): {score}점")
def update_score(score, delta):
return score + delta
# 여러 학생이면 변수가 폭발적으로 증가!
student1_name = "홍길동"
student1_age = 20
student1_score = 85
student2_name = "김철수"
student2_age = 21
student2_score = 90
# ...
# ✅ 객체지향 - 데이터와 기능이 묶여 있음
class Student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def print_info(self):
print(f"{self.name}({self.age}세): {self.score}점")
def update_score(self, delta):
self.score += delta
# 여러 학생도 간단!
student1 = Student("홍길동", 20, 85)
student2 = Student("김철수", 21, 90)
객체지향의 장점:
- 관련 데이터와 기능을 묶어 관리 (응집도 높음)
- 재사용성 (클래스 1개로 객체 여러 개 생성)
- 유지보수 용이 (변경사항이 클래스 안에서만 처리됨)
- 현실 세계 모델링 (게임 캐릭터, 은행 계좌 등)
Q3: 인스턴스, 객체, 오브젝트는 모두 같은 말인가요?
A: 거의 같은 의미지만, 미묘한 차이가 있습니다.
1
2
3
4
5
class Dog:
def __init__(self, name):
self.name = name
my_dog = Dog("뽀삐")
용어 정리:
- 객체(Object): 일반적인 용어, 가장 넓은 의미
- 인스턴스(Instance): 특정 클래스로 만든 객체를 강조할 때
- 오브젝트: 객체의 영어 표현
예시:
my_dog는 객체입니다 (일반적 표현)my_dog는Dog클래스의 인스턴스입니다 (클래스 강조)- Python에서 모든 것은 객체입니다 (숫자, 문자열, 함수 등)
실무에서:
- “Dog 클래스의 인스턴스를 생성했다” ✅
- “Dog 객체를 만들었다” ✅
- 혼용해도 대부분 문제없음
Q4: self는 왜 필요한가요? 생략하면 안 되나요?
A: self는 필수입니다. “이 객체 자신”을 가리키기 위해 필요합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Counter:
def __init__(self):
self.count = 0 # self.count = 이 객체의 count
def increment(self):
self.count += 1 # self.count = 이 객체의 count
print(self.count)
# 두 개의 독립적인 카운터
counter1 = Counter()
counter2 = Counter()
counter1.increment() # 1 (counter1의 count)
counter1.increment() # 2 (counter1의 count)
counter2.increment() # 1 (counter2의 count)
self가 없으면?
1
2
3
4
5
6
class Counter:
def __init__(): # ❌ 에러! self 필수
count = 0 # 지역 변수 (객체 속성 아님)
def increment(): # ❌ 에러! self 필수
count += 1 # count를 찾을 수 없음
self의 역할:
- 객체의 속성 접근:
self.name,self.age - 객체의 메서드 호출:
self.other_method() - 여러 객체 구분: 각 객체가 독립적인 데이터 유지
이름 규칙:
self는 관례일 뿐, 다른 이름도 가능 (예:this,me)- 하지만 절대 self를 사용하세요! (Python 관례)
Q5: 클래스 이름은 왜 대문자로 시작하나요?
A: PEP 8 스타일 가이드에서 권장하는 Python 명명 규칙입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ✅ 올바른 명명
class Student: # 클래스: 대문자로 시작 (파스칼 케이스)
pass
class MyBankAccount: # 여러 단어: 각 단어 대문자
pass
# ✅ 올바른 명명
def calculate_sum(): # 함수: 소문자 + 언더스코어 (스네이크 케이스)
pass
student1 = Student() # 변수: 소문자
# ❌ 나쁜 예시
class student: # 클래스인데 소문자
pass
class my_bank_account: # 클래스인데 스네이크 케이스
pass
명명 규칙 요약:
- 클래스:
PascalCase(각 단어 첫 글자 대문자) - 함수/변수:
snake_case(소문자 + 언더스코어) - 상수:
UPPER_SNAKE_CASE(모두 대문자)
이유:
- 가독성: 코드를 보는 순간 클래스인지 함수인지 구분
- 국제 표준: 전 세계 Python 개발자들이 사용하는 관례
- 도구 지원: IDE가 자동완성, 경고 등에 활용
Q6: 속성을 직접 접근하는 것과 메서드로 접근하는 것의 차이는?
A: Python에서는 직접 접근도 가능하지만, 메서드를 사용하면 더 안전하고 유연합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class BankAccount:
def __init__(self, balance):
self.balance = balance
def deposit(self, amount):
"""입금 메서드 - 검증 로직 포함"""
if amount > 0:
self.balance += amount
print(f"✅ {amount:,}원 입금 완료")
else:
print("❌ 양수만 입금 가능합니다")
def get_balance(self):
"""잔액 조회 메서드"""
return self.balance
account = BankAccount(10000)
# 방법 1: 직접 접근 (간단하지만 위험)
account.balance += 5000 # 검증 없이 변경됨
account.balance = -1000 # ❌ 음수 잔액도 가능!
# 방법 2: 메서드 사용 (안전)
account.deposit(5000) # ✅ 검증 로직 실행
account.deposit(-1000) # ❌ 자동으로 거부됨
메서드 사용의 장점:
- 검증 로직 추가 가능 (음수 방지, 범위 체크 등)
- 로깅/디버깅 (누가 언제 변경했는지 기록)
- 나중에 내부 구현 변경 가능 (외부 코드는 변경 불필요)
- 부수 효과 처리 (잔액 변경 시 알림 등)
Python의 특징:
- Java처럼 private 강제 없음 (관례로
_balance사용) - 간단한 속성은 직접 접근도 OK
- 복잡한 로직 필요하면 메서드 사용
Q7: 하나의 파일에 여러 클래스를 정의할 수 있나요?
A: 네, 가능합니다! 하지만 관련 있는 클래스끼리 묶는 것이 좋습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# game.py - 게임 관련 클래스들
class Player:
def __init__(self, name):
self.name = name
self.hp = 100
def attack(self):
return 10
class Enemy:
def __init__(self, name):
self.name = name
self.hp = 50
def attack(self):
return 5
class Item:
def __init__(self, name, effect):
self.name = name
self.effect = effect
# 사용
player = Player("용사")
enemy = Enemy("슬라임")
potion = Item("회복 포션", "HP +50")
파일 구성 가이드:
- 작은 프로젝트: 관련 클래스를 한 파일에
- 큰 프로젝트: 클래스별로 파일 분리
1 2 3 4
game/ player.py (Player 클래스) enemy.py (Enemy 클래스) item.py (Item 클래스)
권장사항:
- 응집도 높은 클래스끼리 묶기 (Player, Enemy → game.py)
- 한 파일에 3-5개 클래스까지 (그 이상이면 분리 고려)
- 메인 클래스 이름으로 파일명 (BankAccount → bank_account.py)
Q8: 클래스를 언제 만들어야 하고, 언제 함수로 충분한가요?
A: 데이터와 기능이 함께 있어야 하면 클래스, 단순 처리만 필요하면 함수!
클래스를 사용해야 하는 경우:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ✅ 클래스 사용 - 상태(데이터)를 유지해야 함
class ShoppingCart:
def __init__(self):
self.items = [] # 상태 유지
def add_item(self, item):
self.items.append(item)
def get_total(self):
return sum(item.price for item in self.items)
cart = ShoppingCart()
cart.add_item(apple)
cart.add_item(banana)
print(cart.get_total()) # 장바구니 상태가 유지됨
함수로 충분한 경우:
1
2
3
4
5
6
7
8
9
10
11
12
# ✅ 함수 사용 - 상태 유지 불필요, 단순 계산
def calculate_tax(price, tax_rate=0.1):
"""단순 계산, 상태 유지 불필요"""
return price * tax_rate
def format_currency(amount):
"""단순 변환, 상태 유지 불필요"""
return f"{amount:,}원"
# 매번 독립적으로 실행
tax = calculate_tax(10000)
formatted = format_currency(10000)
판단 기준:
| 상황 | 클래스 | 함수 |
|---|---|---|
| 데이터 유지 필요 | ✅ | ❌ |
| 관련 기능 여러 개 | ✅ | △ |
| 단순 계산/변환 | ❌ | ✅ |
| 상태 변경 추적 | ✅ | ❌ |
| 여러 개 생성 필요 | ✅ | ❌ |
예시:
- 클래스: 게임 캐릭터, 은행 계좌, 학생 정보, 파일 핸들러
- 함수: 수학 계산, 문자열 포맷, 데이터 변환, 검증
팁: 처음에는 함수로 시작하고, 상태 관리가 복잡해지면 클래스로 리팩토링하세요!
🔗 관련 자료
📖 공식 문서
-
Python 공식 문서 - 클래스 파이썬 공식 튜토리얼의 클래스 섹션입니다. 클래스 정의, 인스턴스 객체, 메서드 객체, 클래스 변수와 인스턴스 변수 등 OOP의 기초부터 고급 개념까지 체계적으로 설명되어 있습니다.
-
Python 공식 문서 - 데이터 모델 (Special Methods)
__init__,__str__,__repr__등 특수 메서드(매직 메서드)에 대한 완벽한 레퍼런스입니다. 각 메서드의 역할과 사용법을 상세히 설명합니다.
🎓 학습 자료
- Real Python - Object-Oriented Programming (OOP) in Python 3 영문이지만 매우 친절하고 상세한 OOP 튜토리얼입니다. 클래스와 객체의 개념부터 상속, 다형성까지 실용적인 예제와 함께 설명합니다. 초보자도 따라하기 쉽게 구성되어 있습니다.
💡 베스트 프랙티스
-
PEP 8 - Style Guide for Python Code (Class Naming) 파이썬 코딩 스타일 가이드 중 클래스 명명 규칙 섹션입니다. PascalCase 사용, 예외 클래스 명명법 등 파이썬 커뮤니티의 표준 관례를 확인할 수 있습니다.
-
Google Python Style Guide - Classes 구글의 파이썬 스타일 가이드 중 클래스 관련 섹션입니다. 클래스 설계 원칙, docstring 작성법, 메서드 순서 등 실무에서 사용하는 모범 사례를 배울 수 있습니다.
📚 이전 학습
Day 30: 미니 프로젝트 - 계산기 만들기 ⭐⭐⭐
어제는 Phase 3의 모든 함수 개념을 활용해 계산기 프로젝트를 완성했습니다!
📚 다음 학습
Day 32: 생성자와 소멸자 (init, del) ⭐⭐⭐
내일은 __init__ 메서드로 객체를 초기화하고 __del__ 메서드로 객체를 정리하는 방법을 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
Day 31/100 Phase 4: 객체지향 프로그래밍 #100DaysOfPython
