
0. 학습 목표
→ OOP를 왜 배우는지, 어떤 순서로 학습해야 하는지 전체 방향을 이해합니다.
이번 단계에서는 OOP객체지향 프로그래밍 학습 방향을 정리합니다.
OOP는 단순히 class 문법을 외우는 것이 아닙니다.
기존 개발 방식에서 어떤 문제가 생길 수 있었는지 확인하고, OOP가 그 문제를 어떻게 해결하는지 이해하는 것이 중요합니다.
이번 글에서는 OOP 등장배경, 정보 은닉, 캡슐화, Class, 추상화, 상속, 다형성, 파일 분할 구조를 단계별로 정리합니다.
| 학습 단계 | 내용 |
| STEP 1 | OOP가 왜 등장했는지, 정보 은닉과 캡슐화가 왜 필요한지 이해합니다. |
| STEP 2 | Class, 추상화, self, super, 생성자, 객체 참조와 복사 개념을 이해합니다. |
| STEP 3 | 상속, 일반화, 다형성, 오버라이딩, 오버로딩, 파일 분할 구조를 이해합니다. |
이번 단계의 핵심: OOP는 객체를 만들기 위한 문법이 아니라, 커진 프로그램을 역할별 객체로 나누어 더 안전하고 관리하기 쉽게 만드는 설계 방식입니다.
1. OOP 학습 방향: 기존 문제를 먼저 확인하기
→ 새로운 프로그래밍 개념을 배울 때는 기존 방식의 문제를 먼저 확인합니다.
1.1 신입 개발자의 기술 역량 목표
신입 개발자가 갖추어야 할 기술적 목표는 단순히 문법을 많이 아는 것이 아닙니다.
기본적인 Client-Server-DB클라이언트-서버-DB 구조의 서비스를 분석, 설계, 구현, 테스트할 수 있어야 합니다.
가능하다면 암호화, 권한 관리, 배포 환경까지 이해하면 좋습니다.
다만 장애 대응, 로그, 모니터링은 처음부터 완벽하게 다루기 어렵습니다.
| 기술 목표 | 설명 |
| 분석 | 서비스가 어떤 구조로 동작하는지 읽고 이해합니다. |
| 설계 | 기능을 어떤 역할과 구조로 나눌지 정합니다. |
| 구현 | 설계한 구조를 코드로 작성합니다. |
| 테스트 | 작성한 코드가 의도대로 동작하는지 확인합니다. |
즉, 하나의 Software가 어떻게 만들어지고 운영되는지 전체 흐름을 이해해야 합니다.
OOP는 이 흐름 안에서 코드를 역할별로 나누고 관리하기 위한 중요한 도구입니다.
1.2 새로운 개념을 학습하는 방법
새로운 프로그래밍 개념을 배울 때는 먼저 기존 방식의 문제를 확인하는 것이 좋습니다.
그다음 그 개념이 어떤 문제를 해결하기 위해 사용되는지 살펴보면 더 쉽게 이해할 수 있습니다.
새로운 개념 학습 방법
기존 방식 확인
↓
기존 방식의 문제 파악
↓
새 개념이 해결하는 부분 확인
↓
문법과 예제로 정리
OOP도 마찬가지입니다.
처음부터 class 문법만 외우기보다, 기존 절차 중심 코드에서 어떤 문제가 생길 수 있는지 먼저 확인해야 합니다.
중요한 점: OOP는 절차 지향 프로그래밍이 틀렸기 때문에 등장한 것이 아닙니다. 절차 중심 코드가 커졌을 때 발생하기 쉬운 데이터 관리, 변경 추적, 코드 구조 관리 문제를 줄이기 위해 널리 사용되는 방식입니다.
1.3 OOP를 배울 때 먼저 봐야 할 것
OOP를 배울 때는 다음 질문을 먼저 생각해야 합니다.
QUESTION
보다 효과적인 OOP 학습 방법은 무엇일까요?
답은 기존 방식의 문제를 먼저 파악하는 것입니다.
기존 Procedural Programming절차 지향 프로그래밍 방식에서는 코드가 커질수록 공용 데이터 문제, 데이터 변경 문제, 코드 관리 문제가 생길 수 있습니다.
| 기존 방식에서 생길 수 있는 문제 | 설명 |
| 공용 데이터 문제 | 여러 함수가 같은 데이터를 직접 수정하면 데이터 상태를 추적하기 어렵습니다. |
| 데이터 변경 문제 | 데이터 구조가 바뀌면 관련 함수도 여러 곳에서 함께 수정해야 할 수 있습니다. |
| 코드 관리 문제 | 기능이 많아질수록 어떤 코드가 어떤 역할인지 파악하기 어려워집니다. |
OOP는 이런 문제를 줄이기 위해 데이터와 기능을 객체라는 단위로 묶어 관리합니다.
이번 장의 핵심: OOP 학습은 class 문법 암기에서 시작하지 않습니다. 기존 방식의 문제를 확인하고, OOP가 그 문제를 어떻게 줄이는지 이해하는 것에서 시작합니다.
2. STEP 1. OOP 등장배경과 캡슐화
→ OOP가 왜 필요해졌는지, 정보 은닉과 캡슐화가 왜 중요한지 이해합니다.
2.1 OOP 등장배경
OOP가 널리 사용되는 이유는 프로그램이 점점 커지고 복잡해졌기 때문입니다.
처음에는 순서대로 실행되는 절차 중심 코드만으로도 충분할 수 있습니다.
하지만 기능이 많아지고 데이터가 늘어나면 코드 변경과 관리가 어려워집니다.
절차 중심 코드
↓
처음에는 단순함
↓
기능 증가
데이터 증가
수정 범위 증가
↓
역할별로 나누어 관리할 필요 발생
↓
OOP 필요성 증가
OOP는 데이터를 가진 대상과 그 대상이 수행하는 기능을 하나의 단위로 묶어 관리합니다.
이 단위를 객체라고 부릅니다.
2.2 정보 은닉이 필요한 이유
정보 은닉은 객체 내부의 데이터를 외부에서 마음대로 바꾸지 못하게 보호하는 개념입니다.
외부에서 중요한 데이터를 직접 바꾸면 객체의 상태가 잘못될 수 있습니다.
class User:
def __init__(self, name, age):
self.name = name
self.age = age
user = User("철수", 20)
user.age = -100
print(user.age)
위 코드는 실행은 됩니다.
하지만 나이가 -100이 되는 것은 정상적인 상태가 아닙니다.
이처럼 외부에서 데이터를 직접 바꾸면 객체의 상태가 잘못될 수 있습니다.
| 문제 | 설명 |
| 잘못된 값 입력 | 나이, 금액, 권한 같은 값이 잘못 바뀔 수 있습니다. |
| 상태 불안정 | 객체가 정상적으로 동작할 수 없는 상태가 될 수 있습니다. |
| 수정 범위 증가 | 데이터를 어디서 바꾸는지 추적하기 어려워집니다. |
2.3 Python에서의 정보 은닉 관례
Python에서는 변수 이름 앞에 밑줄 하나를 붙여 내부용 변수라는 의미를 표현할 수 있습니다.
예를 들어 _age는 외부에서 직접 접근하지 말고, 클래스 내부에서 관리하자는 의미로 사용됩니다.
class User:
def __init__(self, name, age):
self.name = name
self._age = age
중요한 점은 _age가 강제 private는 아니라는 점입니다.
Python에서는 _age에 외부에서 접근할 수 있습니다.
다만 관례적으로 외부에서 직접 수정하지 말자는 의미를 표현합니다.
참고: Python에서 _age는 강제 접근 제한이 아니라 관례입니다. 초보 단계에서는 “외부에서 직접 수정하지 말고 메서드나 property를 통해 다루자”는 의미로 이해하면 됩니다.
2.4 캡슐화 개념
캡슐화는 데이터와 기능을 하나의 객체 안에 묶고, 외부에서는 정해진 방법으로만 접근하게 만드는 개념입니다.
즉, 객체 내부 데이터는 직접 바꾸지 않고 메서드를 통해 다루게 합니다.
class User:
def __init__(self, name, age):
self.name = name
self._age = age
def set_age(self, age):
if age < 0:
print("나이는 0보다 작을 수 없습니다.")
return
self._age = age
def get_age(self):
return self._age
user = User("철수", 20)
user.set_age(-100)
print(user.get_age())
user.set_age(21)
print(user.get_age())
이제 age를 바꿀 때 set_age()를 거칩니다.
set_age() 안에서 잘못된 값인지 검사할 수 있습니다.
외부 코드
↓
set_age()
↓
값 검사
↓
정상 값만 객체 내부에 저장
2.5 Property로 더 자연스럽게 표현하기
Python에서는 Getter와 Setter를 직접 호출하는 방식 대신 property를 사용할 수 있습니다.
property를 사용하면 겉으로는 속성처럼 보이지만, 내부에서는 값 검사를 수행할 수 있습니다.
class User:
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
print("나이는 0보다 작을 수 없습니다.")
return
self._age = value
user = User("철수", 20)
user.age = -100
print(user.age)
user.age = 21
print(user.age)
user.age = 21처럼 속성에 값을 넣는 것처럼 보입니다.
하지만 실제로는 @age.setter가 실행되어 값 검사를 거칩니다.
STEP 1 핵심: OOP는 커진 프로그램을 역할별 객체로 나누기 위해 사용됩니다. 정보 은닉과 캡슐화는 객체 내부 데이터를 안전하게 보호하고, 정해진 방법으로만 변경하게 만드는 개념입니다.
3. STEP 2. 클래스와 추상화 이해
→ Class가 현실의 대상을 코드화하고 역할별로 관리하는 기본 단위임을 이해합니다.
3.1 Class 구현 연습이 필요한 이유
Python으로 기초 문법만 학습했다면 이제 Class를 사용해 직접 자료 구조와 역할 단위를 만들어 보는 연습을 시작해야 합니다.
Class는 새로운 객체 타입을 정의하는 코드 단위입니다.
현실의 대상을 코드로 표현하고, 관련된 데이터와 기능을 하나의 단위로 묶을 수 있습니다.
현실 대상
↓
필요한 정보와 기능 선택
↓
Class로 표현
↓
객체 생성
↓
프로그램에서 사용
3.2 현실 대상의 추상화
추상화는 현실의 대상에서 필요한 특징만 뽑아 코드로 표현하는 과정입니다.
현실의 모든 정보를 코드에 넣을 필요는 없습니다.
프로그램 목적에 필요한 정보와 기능만 선택합니다.
| 현실 대상 | 필요한 정보 | Class 후보 |
| 회원 | 이름, 이메일, 등급 | Member |
| 예약 | 예약자, 날짜, 상태 | Reservation |
| 직원 | 이름, 부서, 직급 | Employee |
회원과 관련된 코드는 Member Class에 작성할 수 있습니다.
예약과 관련된 코드는 Reservation Class에 작성할 수 있습니다.
직원과 관련된 코드는 Employee Class에 작성할 수 있습니다.
3.3 Class 설계에 필요한 관점
Class를 사용하기 위해서는 Digitalization, Coding, Abstraction, Generalization, Document Management 관점을 함께 봐야 합니다.
| 관점 | 의미 |
| Digitalization | 현실의 정보를 컴퓨터가 다룰 수 있는 데이터로 바꾸는 과정입니다. |
| Coding | 선택한 데이터를 실제 코드로 구현하는 과정입니다. |
| Abstraction | 필요한 특징만 선택해 단순화하는 과정입니다. |
| Generalization | 공통 구조를 찾아 재사용 가능한 기준으로 만드는 과정입니다. |
| Document Management | 코드를 역할별 파일과 Class 단위로 나누어 관리하는 과정입니다. |
현실의 대상
↓
필요한 정보 선택
↓
디지털 데이터로 표현
↓
소스 코드로 구현
↓
Class로 정리
↓
공통 구조로 일반화
중요한 점: 문서 관리 관점에서 보면 Class는 관련된 데이터와 기능을 한곳에 모아 설명하는 문서 단위처럼 볼 수 있습니다.
3.4 현실 대상을 Class로 변환하기
예를 들어 사용자 정보를 코드로 표현하면 다음과 같습니다.
class User:
def __init__(self, name, email, role):
self.name = name
self.email = email
self.role = role
user = User("철수", "chulsoo@example.com", "관리자")
print(user.name)
print(user.email)
print(user.role)
User 클래스는 사용자라는 현실 대상을 코드로 표현한 것입니다.
이름, 이메일, 권한은 프로그램에서 필요한 정보입니다.
3.5 self, super, 생성자 이해하기
self는 현재 만들어진 객체 자기 자신을 의미합니다.
클래스 안에서 객체의 속성이나 메서드에 접근할 때 사용합니다.
class User:
def __init__(self, name):
self.name = name
def say_hello(self):
print(f"안녕하세요. 저는 {self.name}입니다.")
user1 = User("철수")
user2 = User("영희")
user1.say_hello()
user2.say_hello()
user1과 user2는 같은 User 클래스로 만들었지만 서로 다른 객체입니다.
self는 각각의 객체 자신을 가리킵니다.
super는 부모 클래스의 기능을 사용할 때 쓰는 키워드입니다.
class Person:
def __init__(self, name):
self.name = name
class Student(Person):
def __init__(self, name, grade):
super().__init__(name)
self.grade = grade
student = Student("철수", 3)
print(student.name)
print(student.grade)
초보 단계에서는 super().__init__()을 부모 클래스의 초기화 코드도 함께 실행한다고 이해하면 됩니다.
생성자는 객체가 만들어질 때 자동으로 실행되는 메서드입니다.
Python에서는 __init__() 메서드를 생성자로 사용합니다.
3.6 객체 참조와 재할당 이해하기
아래 예제는 얕은 복사와 깊은 복사 예제가 아닙니다.
함수 매개변수가 같은 리스트 객체를 바라볼 때 생기는 변화를 보여주는 예제입니다.
def func(x):
x.append("추가")
a = [1, 2, 3]
func(a)
print(a)
이 코드는 원본 리스트 a가 변경됩니다.
함수 안의 x가 원본 리스트와 같은 객체를 바라보기 때문입니다.
a
↓
[1, 2, 3]
func(a)
↓
x도 같은 리스트를 바라봄
↓
append("추가")
↓
원본 a도 변경됨
이번에는 함수 안에서 x에 새 리스트를 대입하는 예제를 보겠습니다.
def func(x):
x = [9, 8, 7]
a = [1, 2, 3]
func(a)
print(a)
이 경우 원본 리스트 a는 바뀌지 않습니다.
x가 새로운 리스트를 바라보도록 바뀌었을 뿐, a 자체를 수정한 것은 아니기 때문입니다.
3.7 얕은 복사와 깊은 복사 구분하기
진짜 얕은 복사와 깊은 복사는 copy 모듈로 구분해서 볼 수 있습니다.
얕은 복사는 바깥 객체는 복사하지만 내부 객체는 공유할 수 있습니다.
깊은 복사는 내부 객체까지 새로 복사합니다.
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
original[0].append(99)
print("original:", original)
print("shallow:", shallow)
print("deep:", deep)
shallow는 바깥 리스트는 복사하지만 내부 리스트는 같은 객체를 공유합니다.
deep은 내부 리스트까지 새로 복사합니다.
original
↓
내부 리스트 변경
shallow
↓
내부 리스트를 공유하므로 함께 영향받을 수 있음
deep
↓
내부 리스트까지 복사했으므로 영향받지 않음
3.8 객체를 함수 매개변수로 전달하기
객체도 함수에 전달할 수 있습니다.
함수 안에서 객체의 속성을 읽거나 수정할 수 있습니다.
class Model:
def __init__(self, name, email):
self.name = name
self.email = email
user = Model("철수", "이메일")
def print_model(model):
print("1", model.name)
print("2", model.email)
print_model(user)
print_model()은 user 객체를 전달받아 name과 email을 출력합니다.
이번에는 객체의 값을 수정해 보겠습니다.
class Model:
def __init__(self, name, email):
self.name = name
self.email = email
user = Model("철수", "이메일")
def change_model_data(model):
model.name = "ABCD"
print("3", model.name)
change_model_data(user)
print(user.name)
함수 안에서 model.name을 바꾸면 원본 user 객체의 name도 바뀝니다.
model과 user가 같은 객체를 바라보고 있기 때문입니다.
STEP 2 핵심: Class는 현실 대상을 코드로 표현하는 기본 단위입니다. self는 객체 자신을 의미하고, 생성자는 객체가 만들어질 때 필요한 초기 값을 설정합니다. 객체를 함수에 전달하면 같은 객체를 바라볼 수 있다는 점도 함께 이해해야 합니다.
4. STEP 3. 상속, 다형성과 일반화 이해
→ 상속, 일반화, 다형성을 이해하고 파일 분할 구조의 필요성을 확인합니다.
4.1 문서 관리 관점에서 상속 이해하기
상속을 처음 배울 때는 문서 관리 방식으로 생각해 보면 이해하기 쉽습니다.
예를 들어 A회사, B회사, C회사에 이력서를 작성한다고 가정해 보겠습니다.
이력서에서 이름, 연락처, 학력, 기술 스택처럼 공통되는 내용은 하나의 공통 문서로 관리할 수 있습니다.
하지만 회사마다 다르게 작성해야 하는 지원동기나 강조 경험은 별도의 문서로 나누어 관리할 수 있습니다.

이 비유는 상속을 처음 이해하기 위한 보조 설명입니다.
정확한 개념은 공통 속성과 기능을 부모 클래스로 일반화하고, 자식 클래스에서 구체적인 동작을 확장하는 구조입니다.
참고: 문서 관리 비유는 상속을 이해하기 위한 예시입니다. 실제 OOP에서 상속은 공통 속성과 기능을 Parent Class에 두고, Child Class에서 구체적인 기능을 확장하는 구조입니다.
4.2 상속 개념
Inheritance상속는 기존 클래스의 속성과 기능을 물려받아 새로운 클래스를 만드는 개념입니다.
공통 기능은 Parent Class부모 클래스에 두고, 세부 기능은 Child Class자식 클래스에서 확장할 수 있습니다.
상속을 단순히 코드 재사용 문법으로만 이해하면 부족합니다.
상속의 중요한 목적은 여러 Child Class를 Parent Class라는 하나의 공통 기준으로 관리하게 만드는 것입니다.
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name}이 먹습니다.")
class Dog(Animal):
def bark(self):
print(f"{self.name}이 짖습니다.")
dog = Dog("초코")
dog.eat()
dog.bark()
Dog는 Animal을 상속했기 때문에 eat()을 사용할 수 있습니다.
그리고 Dog만의 기능인 bark()도 추가로 가질 수 있습니다.
Animal
└── 공통 속성: name
└── 공통 기능: eat()
Dog
└── Animal의 속성과 기능을 물려받음
└── Dog만의 기능 bark() 추가
상속의 핵심: Inheritance는 단순히 코드를 물려받는 문법이 아니라, 여러 Class를 공통된 기준으로 묶어 관리하기 위한 구조입니다.
4.3 일반화 개념
Generalization일반화는 여러 클래스에서 공통되는 특징을 찾아 부모 클래스로 끌어올리는 과정입니다.
예를 들어 Dog, Cat, Bird는 서로 다른 동물이지만 공통점이 있습니다.
이름을 가지고 있고, 먹을 수 있으며, 소리를 낼 수 있습니다.
Dog
Cat
Bird
↓
공통점 찾기
↓
Animal
공통 기능을 Animal에 두면 중복을 줄일 수 있습니다.
그리고 Dog, Cat, Bird는 Animal이라는 공통 기준으로 묶을 수 있습니다.
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name}이 먹습니다.")
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog("초코")
cat = Cat("나비")
dog.eat()
cat.eat()
| 개념 | 설명 |
| 상속 | 부모 클래스의 기능을 자식 클래스가 물려받아 확장하는 구조입니다. |
| 일반화 | 여러 클래스의 공통 특징을 찾아 부모 클래스로 묶는 사고 과정입니다. |
4.4 다형성 개념
Polymorphism다형성은 여러 자식 객체를 부모 클래스라는 하나의 기준으로 다루면서, 실제 실행되는 동작은 각 객체에 따라 달라지게 만드는 개념입니다.
예를 들어 Animal이라는 Parent Class가 있고, Dog, Cat, Bird라는 Child Class가 있다고 생각해 보겠습니다.
세 객체는 모두 Animal로 다룰 수 있지만, 각 객체가 소리를 내는 방식은 다를 수 있습니다.

class Animal:
def sound(self):
print("동물이 소리를 냅니다.")
class Dog(Animal):
def sound(self):
print("멍멍")
class Cat(Animal):
def sound(self):
print("야옹")
class Bird(Animal):
def sound(self):
print("짹짹")
animals = [Dog(), Cat(), Bird()]
for animal in animals:
animal.sound()
animals 리스트에는 Dog, Cat, Bird 객체가 함께 들어 있습니다.
반복문에서는 모두 animal이라는 같은 기준으로 다룹니다.
하지만 sound()를 실행하면 실제 객체에 따라 다른 결과가 나옵니다.
Animal 기준으로 다룸
↓
Dog.sound() → 멍멍
Cat.sound() → 야옹
Bird.sound() → 짹짹
다형성의 핵심: Polymorphism은 여러 객체를 하나의 기준으로 다루면서, 실제 동작은 객체마다 다르게 실행되도록 만드는 개념입니다.
4.5 오버라이딩 개념
Overriding오버라이딩은 부모 클래스의 메서드를 자식 클래스에서 다시 정의하는 것입니다.
같은 이름의 메서드라도 자식 클래스에 맞게 다르게 동작하게 만들 수 있습니다.
class Animal:
def sound(self):
print("동물이 소리를 냅니다.")
class Dog(Animal):
def sound(self):
print("멍멍")
dog = Dog()
dog.sound()
Dog는 Animal의 sound()를 그대로 사용하지 않습니다.
Dog 클래스 안에서 sound()를 다시 정의했기 때문에 Dog에 맞는 동작이 실행됩니다.
Animal.sound()
↓
기본 동작
Dog.sound()
↓
Dog에 맞게 다시 정의한 동작
같은 메서드 이름
다른 실행 결과
4.6 오버로딩 개념과 Python에서의 처리 방식
Overloading오버로딩은 같은 이름의 메서드를 입력 형태에 따라 다르게 사용하는 개념입니다.
C++, C#, Java 같은 언어에서는 같은 이름의 메서드를 매개변수 형태별로 여러 개 정의할 수 있습니다.
하지만 Python에서는 같은 이름의 메서드를 여러 번 정의하면 마지막 정의가 앞의 정의를 덮어씁니다.
class Calculator:
def add(self, a, b):
return a + b
def add(self, a, b, c):
return a + b + c
calc = Calculator()
print(calc.add(1, 2, 3))
# print(calc.add(1, 2)) # 오류 발생
위 코드에서 두 번째 add()가 첫 번째 add()를 덮어씁니다.
따라서 Python에서는 Java나 C#처럼 메서드 오버로딩을 그대로 사용하지 않습니다.
대신 기본값, 가변 인자, 타입 검사 등을 사용해 비슷한 효과를 만들 수 있습니다.
class Calculator:
def add(self, *numbers):
result = 0
for number in numbers:
result += number
return result
calc = Calculator()
print(calc.add(1, 2))
print(calc.add(1, 2, 3, 4))
참고: Python에서는 전통적인 의미의 메서드 오버로딩을 그대로 지원하지 않습니다. 대신 기본값, *args, 타입 검사, singledispatch 등을 사용해 비슷한 구조를 만들 수 있습니다.
4.7 파일 분할과 문서 관리 개념
OOP를 사용하면 클래스가 많아집니다.
처음에는 한 파일에 모두 작성할 수 있지만, 클래스가 많아지면 파일을 나누어 관리해야 합니다.
파일을 나누는 이유는 단순히 코드가 길어졌기 때문만은 아닙니다.
각 Class가 어떤 역할을 맡고 있는지, 어떤 Class가 공통 기준이고, 어떤 Class가 구체적인 구현인지 드러나도록 나누어야 합니다.
oop_project/
├── main.py
├── animal.py
├── dog.py
├── cat.py
└── bird.py
| 파일 | 역할 |
| animal.py | 공통 기준이 되는 Animal 클래스를 작성합니다. |
| dog.py | Dog 객체의 구체적인 동작을 작성합니다. |
| cat.py | Cat 객체의 구체적인 동작을 작성합니다. |
| bird.py | Bird 객체의 구체적인 동작을 작성합니다. |
| main.py | 객체를 생성하고 공통 기준으로 실행 흐름을 확인합니다. |
이 구조에서는 각 파일이 하나의 문서처럼 역할을 가집니다.
animal.py는 공통 기준을 설명하는 문서이고, dog.py와 cat.py는 각각의 구체적인 동작을 설명하는 문서가 됩니다.
문서 관리 관점에서 중요한 것은 파일을 많이 나누는 것이 아닙니다.
역할이 다른 코드를 다른 파일로 분리하고, 같은 기준으로 묶이는 코드는 관계가 드러나도록 배치하는 것입니다.
STEP 3 핵심: Inheritance와 Polymorphism을 학습한 뒤에는 파일을 단순히 분리하는 것이 아니라, 공통 기준과 구체적인 구현이 드러나도록 문서 구조를 설계해야 합니다.
5. 실습 과제 정리
→ 각 개념을 정리하고 예제를 만들어 카페에 올릴 수 있도록 과제 형태로 정리합니다.
5.1 STEP 1 과제
OOP 등장배경, 정보 은닉, 캡슐화 개념을 각각 정리합니다.
정의만 작성하지 말고, 왜 필요한지와 간단한 예제 코드를 함께 작성합니다.
| 정리할 개념 | 작성 방향 |
| OOP 등장배경 | 프로그램이 커지면서 역할 분리가 필요해진 흐름을 설명합니다. |
| 정보 은닉 | 객체 내부 데이터를 외부에서 직접 바꾸지 못하게 해야 하는 이유를 설명합니다. |
| 캡슐화 | 데이터와 기능을 하나로 묶고, 정해진 메서드나 property로 접근하게 하는 구조를 설명합니다. |
작성 예시
1. OOP 등장배경
- 기존 절차 중심 코드에서 어떤 문제가 생길 수 있는지 설명
- OOP가 그 문제를 어떻게 줄이는지 설명
2. 정보 은닉
- 잘못된 데이터가 직접 들어가는 예제 작성
- 데이터를 보호해야 하는 이유 설명
3. 캡슐화
- set_age() 또는 property 예제 작성
- 값 검사를 객체 내부에서 처리하는 이유 설명
5.2 STEP 2 과제
Class, 추상화, self, super, 생성자, 객체 참조, 얕은 복사와 깊은 복사 예제를 정리합니다.
| 정리할 개념 | 작성 방향 |
| 추상화 | 현실 대상에서 필요한 정보만 뽑아 Class로 표현하는 흐름을 설명합니다. |
| Class | 관련된 데이터와 기능을 하나의 코드 단위로 묶는 구조를 설명합니다. |
| self | 객체 자기 자신을 가리키는 개념으로 설명합니다. |
| super | 부모 클래스의 초기화 코드나 기능을 호출할 때 사용하는 개념으로 설명합니다. |
| 생성자 | 객체가 만들어질 때 초기값을 설정하는 메서드로 설명합니다. |
| 객체 참조 | 함수에 객체를 전달하면 같은 객체를 바라볼 수 있다는 점을 예제로 설명합니다. |
| 복사 | copy.copy()와 copy.deepcopy()를 사용해 얕은 복사와 깊은 복사를 구분합니다. |
작성 예시
1. 현실 대상 하나 선택
예: 회원, 예약, 상품, 직원
2. 필요한 정보 선택
예: 이름, 이메일, 등급
3. Class로 구현
예: class Member
4. 객체 생성 후 출력
5. 함수에 객체 전달 후 값 변경 확인
6. copy.copy(), copy.deepcopy() 예제로 복사 차이 확인
5.3 STEP 3 과제
상속, 일반화, 다형성, 오버라이딩, 오버로딩, 파일 분할 구조를 정리합니다.
| 정리할 개념 | 작성 방향 |
| 상속 | 공통 기능을 부모 클래스에 두고 자식 클래스가 물려받는 구조를 설명합니다. |
| 일반화 | 여러 클래스의 공통점을 부모 클래스로 끌어올리는 개념을 설명합니다. |
| 다형성 | 여러 객체를 하나의 기준으로 다루면서 실제 동작은 객체별로 달라지는 구조를 설명합니다. |
| 오버라이딩 | 부모 메서드를 자식 클래스에서 다시 정의하는 개념을 설명합니다. |
| 오버로딩 | Python에서는 Java/C#식 오버로딩이 그대로 동작하지 않는다는 점을 포함해 설명합니다. |
| 파일 분할 | 공통 기준과 구체적인 구현이 드러나도록 파일을 나누는 이유를 설명합니다. |
작성 예시
1. Parent Class 만들기
예: Animal
2. Child Class 만들기
예: Dog, Cat, Bird
3. 공통 메서드 만들기
예: sound()
4. 자식 클래스에서 오버라이딩하기
5. 리스트에 여러 객체 담기
6. 반복문으로 같은 메서드 호출하기
7. 파일 구조 설계하기
animal.py
dog.py
cat.py
bird.py
main.py
과제 핵심: 각 개념은 정의만 쓰지 말고, 왜 필요한지와 간단한 예제 코드를 함께 작성해야 합니다.
6. 정리
→ OOP 학습 방향과 단계별 핵심 개념을 마지막으로 정리합니다.
이번 글에서는 OOP 학습 방향을 세 단계로 정리했습니다.
OOP는 class 문법만 외우는 것이 아니라, 프로그램을 역할별 객체로 나누어 관리하는 사고방식입니다.
| 단계 | 핵심 정리 |
| STEP 1 | OOP는 프로그램이 커지고 복잡해지면서 역할 분리와 데이터 보호를 위해 널리 사용됩니다. |
| STEP 2 | Class는 현실 대상을 코드화하고, 필요한 특징만 추상화해 표현하는 기본 단위입니다. |
| STEP 3 | 상속과 다형성을 사용하면 공통 기준으로 여러 객체를 관리하고, 객체별로 다른 동작을 실행할 수 있습니다. |
OOP 학습 흐름
기존 방식의 문제 확인
↓
OOP 등장배경 이해
↓
정보 은닉과 캡슐화
↓
Class와 추상화
↓
self, super, 생성자
↓
객체 참조와 복사
↓
상속, 일반화, 다형성
↓
파일 분할과 문서 관리
이번 단계의 핵심: OOP는 현실의 대상을 코드로 표현하고, 역할별 객체로 나누어 프로그램을 더 안전하고 관리하기 쉽게 만드는 방법입니다.
기억할 문장: OOP는 객체를 만들기 위한 문법이 아니라, 커진 프로그램을 역할별로 나누어 관리하기 위한 설계 방식입니다.