- 본 파트의 모든 예제 코드는 위 파일에서 직접 실행하고 수정해볼 수 있습니다. 리스트, 딕셔너리, 셋 등의 다양한 활용법을 직접 확인해보세요.
---
## 1. 학습 목표 (Learning Objectives)
지난 시간에는 변수, 제어문, 함수 등 파이썬의 기본적인 뼈대를 세웠습니다. 이는 마치 우리가 **목공의 기본 연장(톱, 망치, 드라이버) 사용법**을 익힌 것과 같습니다.
이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다.
이번 주차에는 그 연장들을 넣어둘 **'전문가용 공구함(컬렉션)'**을 깊이 있게 탐구합니다. 단순히 물건을 담는 상자가 아니라, 용도에 따라 칸이 나뉘고, 특정 작업에 최적화된 특수 공구들을 꺼내 쓰는 법을 배우게 됩니다. 상황에 맞는 최적의 '데이터 그릇'을 선택하는 능력을 길러, 코드의 효율성과 가독성을 한 단계 끌어올려 보겠습니다.
- 리스트의 다양한 메서드를 활용하여 데이터를 자유자재로 조작할 수 있습니다.
-`sort()`와 `sorted()`의 차이점을 명확히 구분하여 사용합니다.
-`Counter`, `defaultdict`, `deque`, `namedtuple` 등 특수 목적 컬렉션의 장점을 이해하고 적재적소에 활용할 수 있습니다.
-[튜플 언패킹](glossaries/part_3_glossary.md#튜플-언패킹-tuple-unpacking)을 사용하여 간결하고 가독성 높은 코드를 작성합니다.
- 문제 상황에 가장 적합한 [자료구조](glossaries/part_3_glossary.md#자료구조-data-structure)를 선택하여 코드의 효율성을 높일 수 있습니다.
지난 시간에 파이썬의 기본 뼈대를 세웠다면, 이번 주차에는 그 연장들을 넣어둘 **'전문가용 공구함(컬렉션)'**을 깊이 있게 탐구합니다. 단순히 데이터를 담는 것을 넘어, 용도에 따라 최적화된 특수 공구들을 꺼내 쓰는 법을 배우게 됩니다. 상황에 맞는 최적의 '데이터 그릇'을 선택하는 능력을 길러, 코드의 효율성과 가독성을 한 단계 끌어올려 보겠습니다.
### 1. 리스트 핵심 메서드 정복
> [!TIP]
> 본 파트의 모든 예제 코드는 `source_code/part_2_3_python_syntax_collections.py` 파일에서 직접 실행해볼 수 있습니다.
| 메서드 | 설명 | 예제 |
|---|---|---|
| [`append(x)`](./glossary.md#리스트list) | 리스트 맨 끝에 항목 `x`를 추가 | `[1, 2].append(3)` → `[1, 2, 3]` |
> 함수가 `("John Doe", 30, "New York")`이라는 소포(튜플)를 반환할 때, 우리가 `name`, `age`, `city`라는 세 사람이 팔을 벌리고 서서 각자의 물건을 한 번에 받는 모습과 같습니다. 임시 변수라는 창고에 일단 내려놓고 하나씩 꺼낼 필요 없이, 각자의 위치에 맞게 내용물을 바로 전달받아 코드가 매우 깔끔해집니다.
### 2. 키가 없을 때의 구원투수: `collections.defaultdict`
일반 [딕셔너리](./glossary.md#딕셔너리dictionary)는 존재하지 않는 키에 접근하면 `KeyError`가 발생합니다. [`defaultdict`](./glossary.md#collectionsdefaultdict)는 키가 없을 경우, **지정된 기본값을 자동으로 생성**하여 이러한 오류를 방지하고 코드를 간결하게 만들어줍니다.
### 5.2. 키가 없을 때의 구원투수: `collections.defaultdict`
존재하지 않는 키에 접근할 때 `KeyError` 대신 지정된 기본값을 자동으로 생성하는 '자동 완성 노트'입니다. 데이터 그룹핑 시 `if`문을 줄여 코드를 매우 간결하게 만듭니다.
```python
fromcollectionsimportdefaultdict
# 일반 딕셔너리로 리스트에 항목 추가하기
regular_dict={}
words=['apple','ant','bee','banana','cat']
forwordinwords:
first_char=word[0]
iffirst_charnotinregular_dict:
regular_dict[first_char]=[]# 키가 없을 때 빈 리스트를 직접 생성
regular_dict[first_char].append(word)
print(f"일반 딕셔너리: {regular_dict}")
# defaultdict 사용하기 (훨씬 간결!)
# 키가 없을 때 기본값으로 빈 리스트(list)를 생성하도록 지정
default_dict=defaultdict(list)
forwordinwords:
default_dict[word[0]].append(word)# if문 없이 바로 append 가능
> - **리스트**: '한 줄로 서 있는 긴 대기줄'입니다. 맨 앞사람(`list[0]`)이 빠져나가면, 뒤에 있는 모든 사람이 한 칸씩 앞으로 움직여야 합니다. 매우 비효율적이죠.
> - **`deque`**: '양쪽 끝에 문이 달린 대기 공간'입니다. 사람들이 앞문으로든 뒷문으로든 들어오고 나갈 수 있어서, 다른 사람들의 위치 이동 없이 입구와 출구에서 빠르게 작업이 이루어집니다. 데이터의 앞뒤에서 추가/삭제가 빈번할 때 `deque`를 쓰는 이유입니다.
### 3. 이름을 가진 튜플: `collections.namedtuple`
튜플의 각 항목에 이름을 붙여, 인덱스뿐만 아니라 **이름으로도 접근**할 수 있게 만든 자료구조입니다. 코드의 가독성을 획기적으로 높여줍니다.
### 6.2. 이름을 가진 튜플: `collections.namedtuple`
튜플의 각 항목에 이름을 붙여, 인덱스(`p[0]`)뿐만 아니라 **이름(`p.x`)으로도 접근**할 수 있게 만든 '이름표가 붙은 칸막이 상자'입니다. 코드의 가독성을 획기적으로 높여줍니다.
```python
fromcollectionsimportnamedtuple
# 'Point'라는 이름의 namedtuple 클래스 생성
Point=namedtuple('Point',['x','y','z'])
p1=Point(10,20,30)
print(f"p1: {p1}")
# 인덱스로 접근 (튜플처럼)
print(f"p1[0]: {p1[0]}")
# 이름으로 접근 (객체처럼!)
print(f"p1.y: {p1.y}")
print(f"p1.z: {p1.z}")
Point=namedtuple('Point',['x','y'])
p1=Point(10,20)
print(f"좌표: ({p1.x}, {p1.y})")# 인덱스보다 훨씬 명확함
```
#### 💡 쉽게 이해하기: namedtuple은 '이름표가 붙은 칸막이 상자'
> - **[튜플](./glossary.md#튜플tuple)**: `(10, 20, 30)`은 '내용물만 있는 투명한 칸막이 상자'입니다. 첫 번째 칸, 두 번째 칸... 하고 순서로만 내용물을 구분할 수 있습니다.
> - **[`namedtuple`](./glossary.md#collectionsnamedtuple)**: `Point(x=10, y=20, z=30)`은 각 칸마다 'x좌표', 'y좌표', 'z좌표'라고 **이름표를 붙여준 상자**입니다. `p1.y`처럼 이름으로 내용물을 꺼낼 수 있으니, `p1[1]`보다 훨씬 의미가 명확하고 실수를 줄여줍니다.
---
## 4일차(목): 파이썬 자료구조 종합 실습
### 🎯 오늘의 목표
- 지금까지 배운 여러 자료구조를 활용하여 실제 미니 문제를 해결합니다.
---
## 7. 직접 해보기 (Hands-on Lab)
지금까지 배운 여러 자료구조를 활용하여 다음 문제들을 해결해보세요.
### 문제 1: 최빈 단어 분석기
**요구사항**: 주어진 영어 텍스트에서 가장 많이 등장하는 단어 3개를 찾아 출력하세요. 단, 대소문자는 구분하지 않고, 구두점은 무시합니다. (`collections.Counter` 활용)
...
...
@@ -259,27 +135,14 @@ print(f"p1.z: {p1.z}")
importre
fromcollectionsimportCounter
text="""
Python is an interpreted, high-level, general-purpose programming language.
Python's design philosophy emphasizes code readability with its notable use of
significant whitespace. Its language constructs and object-oriented approach
aim to help programmers write clear, logical code for small and large-scale projects.
"""
# 풀이
# 1. 정규표현식으로 단어만 추출하고 소문자로 변환
text="""Python is an interpreted, high-level, general-purpose programming language."""
words=re.findall(r'\w+',text.lower())
# 2. Counter로 단어 빈도 계산
word_counts=Counter(words)
# 3. 가장 흔한 단어 3개 출력
print(f"가장 많이 나온 단어 3개: {word_counts.most_common(3)}")
# 예상 출력: [('python', 3), ('code', 2), ('its', 2)]
```
### 문제 2: 연락처 그룹 관리
**요구사항**: 친구들의 이름을 그룹('가족', '회사' 등)별로 관리하는 프로그램을 만드세요. 그룹에 친구를 추가하는 기능을 구현하세요. (`collections.defaultdict` 활용)
**요구사항**: 친구들의 이름을 그룹('가족', '회사' 등)별로 관리하고 출력하는 `ContactManager` 클래스를 만드세요. (`collections.defaultdict` 활용)
```python
fromcollectionsimportdefaultdict
...
...
@@ -287,30 +150,21 @@ from collections import defaultdict
- 본 파트의 모든 예제 코드는 위 파일에서 직접 실행하고 수정해볼 수 있습니다. 클래스, 상속, 다형성 등 OOP의 핵심 개념을 코드로 확인해보세요.
---
지금까지 우리는 파이썬의 문법과 다양한 데이터 구조(컬렉션)를 배웠습니다. 이는 프로그래밍의 '재료'를 다듬는 법을 익힌 것과 같습니다. 이번 주차에는 이 재료들을 조합하여 '요리'를 하는 법, 즉 더 크고 체계적인 프로그램을 만드는 '[객체 지향 프로그래밍(OOP)](./glossary.md#객체-지향-프로그래밍oop)' 패러다임을 학습합니다.
## 1. 학습 목표 (Learning Objectives)
**왜 OOP가 AI 개발에서 중요할까요?**
이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다.
여러분이 앞으로 사용하게 될 `Scikit-learn`, `PyTorch` 같은 AI 라이브러리의 동작 방식이 바로 OOP에 기반하기 때문입니다.
- 클래스(설계도)를 정의하고, 그로부터 고유한 속성과 행동을 가진 객체(제품)를 생성할 수 있습니다.
- 상속을 통해 기존 클래스의 기능을 재사용하고, 메서드 오버라이딩으로 기능을 확장/변경할 수 있습니다.
- 다형성을 활용하여 유연하고 확장성 높은 코드를 작성할 수 있습니다.
- 캡슐화로 데이터를 안전하게 보호하고, 추상화로 클래스의 필수 기능을 강제할 수 있습니다.
- 현실 세계의 문제를 객체들 간의 상호작용으로 모델링하여 체계적인 프로그램을 설계할 수 있습니다.
-`model = RandomForestClassifier()`: 이 코드는 `RandomForestClassifier`라는 **클래스(설계도)**로 `model`이라는 **객체(실체)**를 만드는 과정입니다.
-`model.fit(X, y)`: `model` 객체가 가진 `fit`이라는 **메서드(행동)**를 호출하여 모델을 학습시킵니다.
## 2. 핵심 키워드 (Keywords)
이처럼 OOP를 이해하면, 복잡한 AI 라이브러리의 구조를 꿰뚫어 보고, 각 부분이 어떻게 상호작용하는지 명확하게 파악할 수 있습니다.
> OOP의 여러 개념을 이해하기 위해, 우리는 '붕어빵 장사'라는 비즈니스 모델을 사용할 것입니다. 앞으로 나오는 모든 개념을 이 붕어빵 가게에 빗대어 생각해 보세요!
>
> - **[클래스(Class)](./glossary.md#클래스class)**: 붕어빵의 모양, 크기, 굽는 방식을 정의하는 **'붕어빵 틀'** 그 자체입니다.
> - **[객체(Object)](./glossary.md#객체object--인스턴스instance)**: 붕어빵 틀에서 막 찍어낸, 세상에 단 하나뿐인 **'붕어빵'** 한 개입니다.
> - **[속성(Attribute)](./glossary.md#속성attribute)**: 각각의 붕어빵이 가진 고유한 특징, 예를 들어 '팥 앙금', '슈크림 앙금' 같은 **'속재료'** 입니다.
> - **[메서드(Method)](./glossary.md#메서드method)**: 붕어빵 틀을 이용해 할 수 있는 행동, 즉 **'굽기()', '뒤집기()', '포장하기()'** 같은 기능입니다.
## 3. OOP와 함께 '붕어빵 비즈니스' 시작하기 (Introduction)
파이썬은 모든 것이 객체(Object)로 이루어진 강력한 객체 지향 프로그래밍(OOP) 언어입니다. Scikit-learn, PyTorch 등 우리가 사용할 대부분의 AI 라이브러리는 OOP 원칙에 기반하여 설계되었습니다. OOP를 이해하면 라이브러리의 구조를 파악하고 더 깊이 있게 활용할 수 있으며, 재사용과 확장이 용이한 코드를 직접 작성할 수 있게 됩니다.
지금까지 우리는 프로그래밍의 '재료'를 다듬는 법을 익혔습니다. 이번 주차에는 이 재료들을 조합하여 '요리'를 하는 법, 즉 더 크고 체계적인 프로그램을 만드는 **[객체 지향 프로그래밍(OOP)](glossaries/part_4_glossary.md#객체-지향-프로그래밍oop)** 패러다임을 학습합니다.
---
Scikit-learn, PyTorch 등 대부분의 AI 라이브러리는 OOP에 기반하며, `model = RandomForestClassifier()` 와 같은 코드는 OOP를 이해해야 그 구조를 꿰뚫어 볼 수 있습니다.
## 1일차(월): 클래스와 객체 - OOP의 시작
> **💡 핵심 비유: OOP는 '붕어빵 비즈니스'입니다**
> OOP의 모든 개념을 '붕어빵 장사'에 빗대어 이해해 봅시다!
> - **클래스(Class)**: 붕어빵을 만드는 **'붕어빵 틀'**
> - **객체(Object)**: 붕어빵 틀에서 찍어낸 **'붕어빵'** 하나하나
> - **속성(Attribute)**: 붕어빵의 '팥', '슈크림' 같은 **'속재료'**
> - **메서드(Method)**: 붕어빵 틀의 **'굽기()', '뒤집기()'** 같은 **'행동'**
### 🎯 오늘의 목표
- 클래스(Class)와 객체(Object)의 개념을 이해하고 직접 클래스를 정의합니다.
-[`__init__`](./glossary.md#init)과 [`self`](./glossary.md#self)를 사용하여 객체의 고유한 상태를 만듭니다.
-[`@dataclass`](./glossary.md#dataclass) 데코레이터로 상용구(boilerplate) 코드를 획기적으로 줄입니다.
> [!TIP]
> 본 파트의 모든 예제 코드는 `source_code/part_4_object_oriented_programming.py` 파일에서 직접 실행해볼 수 있습니다.
---
### 1. 클래스(Class)와 객체(Object)
-**[클래스(Class)](./glossary.md#클래스class)**: 객체를 만들기 위한 **'설계도'** 또는 '틀'. 객체가 가질 속성(데이터)과 행동(메서드)을 정의합니다. (예: '자동차' 설계도, **'붕어빵 틀'**)
-**[객체(Object)/인스턴스(Instance)](./glossary.md#객체object--인스턴스instance)**: 클래스로부터 생성된 **'실체'**. '자동차' 클래스로부터 '빨간색 페라리', '검은색 아반떼' 같은 구체적인 객체를 만듭니다. (예: **'팥 붕어빵', '슈크림 붕어빵'**)
```python
# 'Dog'라는 이름의 클래스를 정의합니다.
classDog:
# 클래스 속성: 모든 Dog 객체가 공유하는 값
species="Canis familiaris"
# 초기화 메서드: 객체가 생성될 때 자동으로 호출되어 속성을 초기화합니다.
def__init__(self,name:str,age:int):
# 인스턴스 속성: 각 객체마다 고유하게 가지는 값
self.name=name
self.age=age
# 인스턴스 메서드: 객체가 수행할 수 있는 행동
defspeak(self,sound:str):
returnf"{self.name}가 '{sound}' 하고 짖습니다."
```
> **붕어빵 비유**:
> - `__init__`: 붕어빵 틀(`Dog`)에 반죽을 붓고, 각 붕어빵마다 다른 앙금(`name`, `age`)을 넣는 과정입니다.
> - `self`: 지금 막 만들어지고 있는 **'바로 그 붕어빵'** 자신을 의미합니다. 이 `self` 덕분에 "보리"라는 붕어빵과 "짱구"라는 붕어빵이 서로 다른 이름(`self.name`)을 가질 수 있습니다.
## 4. 클래스와 객체: 붕어빵 틀과 붕어빵
### 2. `self`의 정체
`self`는 **메서드를 호출한 객체 자기 자신**을 가리키는, 파이썬 OOP의 핵심 규칙입니다.
> **🎯 1일차 목표:** 클래스(Class)와 객체(Object)의 개념을 이해하고, `__init__`과 `self`를 사용하여 객체의 고유한 상태를 만듭니다.
#### 💡 쉽게 이해하기: `self`는 객체의 '나 자신'이라는 대명사
> "보리야, 짖어봐!" 라고 `bory.speak("멍멍")`을 실행하면, `Dog` 클래스는 `bory`라는 객체를 `self`라는 이름으로 받습니다. 그리고 `speak` 메서드 안에서 `self.name`이라고 쓰는 것은, `bory` 객체가 " **나(bory)의** 이름은 '보리'야 " 라고 자기 자신의 속성에 접근하는 것과 같습니다. 이 `self` 덕분에 수많은 Dog 객체들이 각자 자신의 이름과 나이를 헷갈리지 않고 사용할 수 있습니다.
- 메서드를 정의할 때 첫 번째 매개변수로 반드시 `self`를 써주어야 합니다.
-`bory.speak("멍멍")` 코드는 내부적으로 `Dog.speak(bory, "멍멍")`과 같이 동작합니다. `self` 매개변수로 `bory` 객체 자신이 전달되는 것입니다.
### 3. 보일러플레이트 줄이기: `@dataclass`
데이터를 담는 용도의 클래스를 만들 때, `__init__`, `__repr__` (객체 출력) 같은 반복적이고 지루한 코드(Boilerplate)를 자동으로 생성해주는 매우 유용한 [데코레이터](./glossary.md#데코레이터decorator)입니다.
#### 💡 쉽게 이해하기: `@dataclass`는 '클래스 자동 생성기'
> [`@dataclass`](./glossary.md#dataclass)는 마치 **'클래스의 귀찮은 부분들을 알아서 만들어주는 기계'**와 같습니다. 우리는 `name: str`, `age: int`처럼 필요한 속성들만 선언해주면, 이 기계가 객체를 초기화하는 `__init__` 메서드나, 객체를 예쁘게 출력해주는 `__repr__` 메서드 같은 것들을 보이지 않는 곳에서 자동으로 뚝딱 만들어줍니다.
-**클래스(Class)**: 객체를 만들기 위한 **'설계도'** 또는 '붕어빵 틀'.
-**객체(Object)**: 클래스로부터 생성된 **'실체'** 또는 '팥 붕어빵', '슈크림 붕어빵'.
> **`__init__`과 `self`**: `@dataclass`가 자동으로 만들어주는 `__init__`은 붕어빵 틀에 반죽과 **앙금(`flavor`, `price`)**을 넣는 과정입니다. `self`는 지금 만들어지고 있는 **'바로 그 붕어빵'** 자신을 가리키는 대명사로, 붕어빵들이 서로의 앙금을 헷갈리지 않게 해줍니다.
---
### 1. 상속(Inheritance)과 메서드 오버라이딩
**[상속](./glossary.md#상속inheritance)**은 부모 클래스(Superclass)의 속성과 메서드를 자식 클래스(Subclass)가 물려받는 것입니다. 이는 마치 **'기본 설계도'를 바탕으로 '수정 설계도'를 만드는 것**과 같습니다. 자식 클래스는 부모의 기능을 그대로 사용하거나, **[메서드 오버라이딩(Method Overriding)](./glossary.md#메서드-오버라이딩method-overriding)**을 통해 자신에게 맞게 수정할 수 있습니다.
## 5. 상속: 새로운 맛의 붕어빵 출시하기
> **🎯 2일차 목표:** 상속으로 코드 중복을 줄이고, 메서드 오버라이딩과 `super()`로 기능을 확장합니다.
> **붕어빵 비유**:
> '기본 붕어빵 틀(`Dog`)'을 물려받아, 기능이 추가된 '피자 붕어빵 틀(`GoldenRetriever`)'을 새로 만드는 것과 같습니다. 모양은 같지만, 굽는 방식(`speak`)은 피자 토핑에 맞게 살짝 달라질 수 있습니다.
**상속(Inheritance)**은 '기본 붕어빵 틀'을 물려받아 '피자 붕어빵 틀' 같은 신제품을 만드는 것입니다.
> '프리미엄 붕어빵 틀(`SpecialDog`)'을 만들 때, 기존 붕어빵 틀(`Dog`)의 모양과 앙금 넣는 방식(`__init__`)은 그대로 쓰고 싶습니다. 이때 `super().__init__()`를 호출하면, 마치 기본 틀의 기능을 그대로 가져와 사용하고, 우리는 프리미엄 재료(`ability`)를 추가하는 작업에만 집중할 수 있습니다.
# super() 사용 예시
@dataclass
classPremiumBungeoppang(Bungeoppang):
origin:str
```python
classSpecialDog(Dog):
def__init__(self,name:str,age:int,ability:str):
# super()를 이용해 부모 클래스(Dog)의 __init__을 호출하여
# name과 age 속성 초기화를 위임합니다.
super().__init__(name,age)
# 자식 클래스만의 속성을 추가로 초기화합니다.
self.ability=ability
defdescribe(self):
# 부모의 describe가 있다면 호출하고, 없다면 직접 구현
# (Dog 클래스에 describe가 없으므로 직접 구현)
returnf"{self.name}는 {self.age}살이며, '{self.ability}' 능력이 있습니다."
special_dog=SpecialDog("슈퍼독",5,"비행")
print(special_dog.describe())
# 출력: 슈퍼독는 5살이며, '비행' 능력이 있습니다.
def__post_init__(self):
# super()는 부모 클래스를 의미. 부모의 기능을 확장할 때 사용.
# 여기서는 Bungeoppang의 속성을 그대로 쓰되, 가격만 500원 인상
self.price+=500
```
> **메서드 오버라이딩과 `super()`**: 기본 붕어빵의 판매 방식(`sell`)을 피자 붕어빵에 맞게 바꾸는 것이 **오버라이딩**입니다. 프리미엄 붕어빵을 만들 때, 기존 가격 정책에 추가 금액만 더하는 것처럼 부모의 기능을 그대로 활용하며 확장할 때 **`super()`**를 사용합니다.
---
## 3일차(수): 다형성과 캡슐화 - 유연하고 안전한 코드
## 6. 다형성과 캡슐화: 유연하고 안전한 비즈니스
### 🎯 오늘의 목표
- 다형성(Polymorphism)을 통해 코드의 유연성과 확장성을 높입니다.
- 캡슐화(Encapsulation)를 통해 객체의 데이터를 안전하게 보호합니다.
> **🎯 3일차 목표:** 다형성(Polymorphism)으로 코드의 유연성을 높이고, 캡슐화(Encapsulation)로 데이터를 안전하게 보호합니다.
---
### 1. 다형성(Polymorphism): 같은 이름, 다른 행동
"여러(Poly) 개의 형태(Morph)"라는 뜻으로, 동일한 이름의 메서드가 객체의 종류에 따라 다르게 동작하는 것입니다. 이것이 바로 [다형성](./glossary.md#다형성polymorphism)입니다.
> **붕어빵 비유**:
> 손님이 "붕어빵 하나 주세요!"라고 말합니다. 가게 주인은 손님이 보는 앞에서 `판매하기()`라는 동일한 동작을 취합니다. 하지만 손님에게 전달되는 것은 '팥 붕어빵'일 수도, '피자 붕어빵'일 수도 있습니다. 이처럼 **같은 `판매하기()` 요청에 대해, 대상(객체)에 따라 다른 결과가 나타나는 것**이 다형성입니다.
-**다형성(Polymorphism)**: "주문하신 것 나왔습니다!" 라는 **동일한 요청(`sell()`)**에 대해, 대상 객체가 '팥 붕어빵'이냐 '피자 붕어빵'이냐에 따라 **다른 결과가 나오는 것**입니다. 코드의 유연성을 크게 높여줍니다.
-**캡슐화(Encapsulation)**: 붕어빵 맛의 핵심인 **'반죽의 비밀 레시피(`__secret_recipe`)'**를 외부에서 함부로 바꾸지 못하게 숨기고, 정해진 방법으로만 접근하게 하여 데이터를 보호하는 것입니다.
```python
classCat:
def__init__(self,name):
self.name=name
defspeak(self):# Dog 클래스와 동일한 이름의 메서드
return"야옹"
# Horse 클래스를 새로 추가해도 animal_sound 함수는 바꿀 필요가 없습니다!
classHorse:
def__init__(self,name):
self.name=name
defspeak(self):
return"히히힝"
bory=Dog("보리",3)
nabi=Cat("나비")
pony=Horse("포니")
# animal_sound 함수는 객체의 클래스가 무엇인지(개가, 고양이인지, 말인지) 신경쓰지 않고
# .speak() 메서드만 호출합니다. 이것이 다형성의 힘입니다.
defanimal_sound(animal):
print(f"{animal.name}: {animal.speak()}")
animal_sound(bory)# Dog 객체의 speak()가 호출됨
animal_sound(nabi)# Cat 객체의 speak()가 호출됨
animal_sound(pony)# Horse 객체의 speak()가 호출됨
```
> **AI 라이브러리 속 다형성**: Scikit-learn의 모든 모델(`LogisticRegression`, `DecisionTree` 등)은 `.fit()`과 `.predict()`라는 동일한 이름의 메서드를 가집니다. 덕분에 우리는 모델의 종류와 상관없이 일관된 방식으로 코드를 작성할 수 있습니다.
### 2. 캡슐화(Encapsulation): 소중한 데이터 보호하기
객체의 속성을 외부에서 직접 수정하지 못하도록 숨기고, 정해진 메서드(getter/setter)를 통해서만 접근하도록 제어하는 것, 즉 [캡슐화](./glossary.md#캡슐화encapsulation)입니다.
classBungeoppangFactory:
def__init__(self,initial_flour_kg):
self.__flour_kg=initial_flour_kg# 밀가루 재고(비밀 데이터)
> **붕어빵 비유**:
> 붕어빵 맛의 핵심인 **'반죽의 비밀 레시피(`__balance`)'**는 아무나 볼 수도, 수정할 수도 없게 주방 깊숙한 곳에 숨겨둡니다. 레시피를 변경하려면, 반드시 점장님의 허락(메서드 호출)을 거쳐 정해진 절차(`deposit`)에 따라서만 가능합니다. 이렇게 소중한 데이터를 외부로부터 보호하는 것이 캡슐화입니다.
defget_stock(self):# 데이터를 확인하는 정해진 통로 (getter)
returnself.__flour_kg
-`_` (Protected): "내부용이니 가급적 건드리지 마세요" 라는 개발자 간의 약속.
-`__` (Private): 이름 앞에 `_클래스명`이 붙어([Name Mangling](./glossary.md#네임-맹글링name-mangling)) 외부에서 직접 접근이 거의 불가능해짐.
defadd_flour(self,amount):# 데이터를 변경하는 정해진 통로 (setter)
ifamount>0:self.__flour_kg+=amount
```python
classBankAccount:
def__init__(self,owner,balance):
self.owner=owner
self.__balance=balance# 잔액은 외부에서 직접 수정 불가 (private)
# getter: 잔액을 외부로 알려주는 통로
defget_balance(self):
returnself.__balance
# setter: 잔액을 정해진 규칙에 따라 수정하는 통로
defdeposit(self,amount):
ifamount>0:
self.__balance+=amount
print(f"{amount}원 입금 완료. 현재 잔액: {self.__balance}원")
else:
print("입금액은 0보다 커야 합니다.")
my_account=BankAccount("홍길동",10000)
# my_account.__balance = 999999 # 직접 수정 시도 -> AttributeError 발생
my_account.deposit(5000)
print(f"최종 잔액 조회: {my_account.get_balance()}")
factory=BungeoppangFactory(100)
# factory.__flour_kg = 999 #! 직접 수정 불가 (AttributeError)
factory.add_flour(20)# 정해진 방법으로만 수정 가능
print(f"현재 밀가루 재고: {factory.get_stock()}kg")
```
---
## 4일차(목): 추상화와 매직 메서드
## 7. 추상화와 매직 메서드: 비즈니스 규칙과 마법
### 🎯 오늘의 목표
- 추상화를 통해 클래스가 반드시 구현해야 할 메서드를 '규칙'으로 강제합니다.
- 매직 메서드로 파이썬 내장 기능(출력, 비교 등)을 커스터마이징합니다.
> **🎯 4일차 목표:** 추상화로 클래스의 필수 기능을 강제하고, 매직 메서드로 파이썬 내장 기능을 커스터마이징합니다.
---
### 1. 추상화(Abstraction): 필수 기능 강제하기
[추상 클래스](./glossary.md#추상-클래스abstract-class)는 '미완성 설계도'와 같습니다. `abc` 모듈과 `@abstractmethod`를 사용하여 자식 클래스가 반드시 구현해야 할 메서드를 명시할 수 있습니다. 이것이 [추상화](./glossary.md#추상화abstraction)입니다.
> **붕어빵 비유**:
> 붕어빵 프랜차이즈 본사에서 '가맹점 운영 매뉴얼(`AbstractAnimal`)'을 내놓았습니다. 이 매뉴얼에는 "모든 가맹점은 반드시 '굽기(`speak`)' 기능을 스스로 구현해야 합니다"라는 **규칙**이 명시되어 있습니다. 본사는 어떻게 구울지(how)는 알려주지 않고, 무엇을 해야 하는지(what)만 강제합니다. 이 규칙을 따르지 않은 가게는 오픈할 수 없습니다(에러 발생).
-**추상화(Abstraction)**: 프랜차이즈 본사에서 "모든 가맹점은 반드시 **'굽기()' 기능을 스스로 구현해야 한다**"는 '운영 매뉴얼'을 만들어 규칙을 강제하는 것입니다.
-**매직 메서드(Magic Methods)**: `print(붕어빵)`을 했을 때 예쁜 설명이 나오게 하거나(`__str__`), `붕어빵1 + 붕어빵2` 처럼 객체 간의 연산을 정의하는 등, 파이썬의 내장 기능을 우리 객체에 맞게 만드는 마법입니다.
```python
fromabcimportABC,abstractmethod
# ABC를 상속받아 추상 클래스로 지정
classAbstractAnimal(ABC):
def__init__(self,name):
self.name=name
# @abstractmethod: 자식 클래스는 이 메서드를 반드시 구현해야 함
@abstractmethod
defspeak(self):
pass
classFranchiseManual(ABC):# 추상 클래스
@abstractmethod# 추상 메서드
defbake(self):pass
# 추상 클래스를 상속받는 자식 클래스
classLion(AbstractAnimal):
# 부모의 추상 메서드인 speak()를 반드시 구현해야 합니다.
# 만약 구현하지 않으면 TypeError가 발생합니다.
defspeak(self):
return"어흥"
classMyStore(FranchiseManual):# 가맹점
defbake(self):# '굽기' 규칙을 반드시 구현해야 함
print("우리 가게만의 방식으로 붕어빵을 굽습니다.")
# 추상 클래스는 직접 객체로 만들 수 없습니다.
# my_animal = AbstractAnimal("동물") # -> TypeError 발생
[매직 메서드](./glossary.md#매직-메서드magic-methods)(또는 던더 메서드, Dunder Methods)는 `__이름__` 형태를 가지며, 파이썬의 내장 연산자나 함수(`+`, `len()`, `str()` 등)가 우리 클래스의 객체와 함께 사용될 때 호출되는 특별한 메서드입니다.
> **붕어빵 비유**:
> 우리가 만든 '붕어빵' 객체를 `print(my_bungeoppang)` 했을 때 "팥이 든 맛있는 붕어빵"처럼 예쁜 설명이 나오게 하거나, `bungeoppang1 + bungeoppang2` 처럼 붕어빵을 합치는 새로운 규칙을 만들 수 있게 해주는 마법 같은 기능입니다.
| 매직 메서드 | 호출되는 상황 | 설명 |
|---|---|---|
| `__str__(self)` | `str(obj)`, `print(obj)` | 객체를 사람이 읽기 좋은 **문자열**로 표현할 때 |
| `__repr__(self)`| `repr(obj)` | 객체를 **개발자가 명확히** 이해할 수 있는 문자열로 표현할 때 |
지난 4주간 우리는 파이썬이라는 언어와 객체 지향 설계 능력을 길렀습니다. 이제 이 강력한 언어를 날개 삼아, 데이터의 세계를 본격적으로 탐험할 시간입니다. 이번 주부터는 데이터 분석과 머신러닝 프로젝트의 실질적인 '주연 배우'들인 NumPy, Pandas 사용법을 깊이 있게 학습합니다.
## ⭐️ 2부의 시작: 핵심 라이브러리 관계도 (The Big Picture)
> [!TIP]
> 본 파트의 모든 예제 코드는 `source_code/part_5_ai_core_libraries.py` 파일에서 직접 실행해볼 수 있습니다.
본격적으로 각 라이브러리를 배우기 전에, 이들이 데이터 분석 및 머신러닝 프로젝트에서 어떻게 함께 사용되는지에 대한 전체적인 그림을 이해하는 것이 중요합니다.
### AI 프로젝트의 큰 그림: 라이브러리 관계도
각 라이브러리를 배우기 전에, 이들이 프로젝트에서 어떻게 함께 사용되는지 전체적인 그림을 이해하는 것이 중요합니다.
```mermaid
graph TD
...
...
@@ -43,32 +55,28 @@ graph TD
B -->|"plt.plot(array)"| D
C -->|"결과 분석"| D
```
-**시작은 [Pandas](./glossary.md#pandas)**: 대부분의 프로젝트는 Pandas를 사용해 다양한 소스(CSV 등)로부터 데이터를 불러와 `DataFrame`으로 만드는 것에서 시작합니다.
-**계산을 위해 [NumPy](./glossary.md#numpy)로**: 정제된 데이터는 고성능 수치 계산을 위해 NumPy 배열(`ndarray`)로 변환되어 머신러닝 모델의 입력으로 준비됩니다.
-**[Scikit-learn](./glossary.md#scikit-learn)으로 모델링**: NumPy 배열로 변환된 데이터를 사용하여 Scikit-learn으로 모델을 학습하고 예측합니다.
-**모든 과정에서 시각화**: Pandas와 NumPy 데이터는 [Matplotlib/Seaborn](./glossary.md#matplotlib--seaborn)을 통해 시각화되어 데이터의 특징을 파악(EDA)하거나 모델의 성능을 분석하는 데 사용됩니다.
이 흐름을 염두에 두고 각 라이브러리를 학습하면, 지식이 파편적으로 남지 않고 하나의 큰 그림으로 연결될 것입니다.
-**시작은 Pandas**: 대부분의 프로젝트는 Pandas `DataFrame`으로 데이터를 불러와 정제하는 것에서 시작합니다.
-**계산은 NumPy로**: 정제된 데이터는 고성능 수치 계산을 위해 NumPy 배열(`ndarray`)로 변환되어 머신러닝 모델의 입력으로 준비됩니다.
-**모델링은 Scikit-learn으로**: NumPy 배열을 사용하여 Scikit-learn으로 모델을 학습하고 예측합니다.
-**모든 과정은 Matplotlib으로 시각화**: 데이터의 특징을 파악(EDA)하거나 모델의 성능을 분석하는 데 사용됩니다.
이 흐름을 염두에 두면, 지식이 파편적으로 남지 않고 하나의 큰 그림으로 연결될 것입니다.
---
---
# 1부: 데이터 과학자의 연장통 - NumPy & Pandas (5주차)
## 4. NumPy: 고성능 수치 계산의 초석
### 🎯 5주차 학습 목표
-**[NumPy](./glossary.md#numpy)**를 사용하여 고성능 과학 계산 및 다차원 배열을 다룹니다.
-**[Pandas](./glossary.md#pandas)**를 사용하여 표 형식의 데이터를 자유자재로 읽고, 선택하고, 필터링하고, 그룹화하고, 병합합니다.
> **🎯 1일차 목표:** NumPy를 사용하여 고성능 과학 계산 및 다차원 배열을 다룹니다.
---
**NumPy(Numerical Python)**는 파이썬에서 과학 계산을 위한 가장 기본적인 패키지입니다. `ndarray`라는 효율적인 다차원 배열 객체를 중심으로, 벡터, 행렬 등 고성능 수치 연산을 위한 방대한 함수들을 제공합니다.
## 1일차(월): NumPy - 고성능 수치 계산의 초석
-**NumPy(Numerical Python)**: 파이썬에서 과학 계산을 위한 가장 기본적인 패키지. [`ndarray`](./glossary.md#ndarray)라는 효율적인 다차원 배열 객체를 중심으로, 벡터, 행렬 등 고성능 수치 연산을 위한 방대한 함수들을 제공합니다.
> **💡 비유: NumPy 배열은 '계란판', 파이썬 리스트는 '장바구니'**
> - **파이썬 리스트 ('장바구니')**: 뭐든 담을 수 있어 유연하지만, 내용물이 제각각이라 전체를 한 번에 계산하기는 어렵고 느립니다.
> - **NumPy 배열 ('계란판')**: 오직 한 종류(숫자)만 담을 수 있는 '계란판'과 같습니다. 모든 칸이 균일하고 규칙적이어서, '모든 계란에 도장 찍기' 같은 작업을 기계로 한 번에 처리할 수 있습니다. 이 **균일성** 덕분에 NumPy는 엄청나게 빠른 속도로 대규모 숫자 계산을 할 수 있습니다.
#### 💡 쉽게 이해하기: NumPy 배열은 '계란판', 파이썬 리스트는 '장바구니'
> - **파이썬 리스트**: 뭐든 담을 수 있는 만능 '장바구니'입니다. 사과, 책, 신발 등 종류에 상관없이 담을 수 있어 유연하지만, 내용물이 제각각이라 전체를 한 번에 계산하기는 어렵고 느립니다.
> - **NumPy 배열**: 오직 한 종류(숫자)만 담을 수 있는 '계란판'과 같습니다. 모든 칸의 모양과 크기가 같아, '모든 계란에 도장 찍기' 같은 작업을 기계로 한 번에 처리할 수 있습니다. 이런 **균일성**과 **규칙성** 덕분에 NumPy는 엄청나게 빠른 속도로 숫자 계산을 할 수 있습니다. AI와 데이터 과학에서 대규모 데이터를 다룰 때 NumPy가 필수인 이유입니다.
-**Pandas**: [`DataFrame`](./glossary.md#dataframe)(2차원 테이블)과 [`Series`](./glossary.md#series)(1차원 배열) 자료구조를 사용하여, 엑셀이나 DB 테이블처럼 데이터를 직관적으로 다루게 해주는 라이브러리. 데이터 전처리에 없어서는 안 될 필수 도구입니다.
### 1. `Series`와 `DataFrame`
## 5. Pandas: '슈퍼 엑셀' DataFrame 다루기
> **🎯 2-4일차 목표:** Pandas를 사용하여 표 형식의 데이터를 자유자재로 읽고, 선택하고, 필터링하고, 그룹화하고, 병합합니다.
**Pandas**는 `DataFrame`(2차원 테이블)과 `Series`(1차원 배열) 자료구조를 사용하여, 엑셀이나 DB 테이블처럼 데이터를 직관적으로 다루게 해주는 라이브러리입니다. 데이터 전처리에 없어서는 안 될 필수 도구입니다.
print("\n가격 1000원 미만 AND 재고 10개 미만:\n",df[(df['가격']<1000)&(df['재고']<10)])
```
> 🚫 **`SettingWithCopyWarning` 주의!**: 값을 변경할 때는 `.loc`를 사용해 `df.loc[행조건, 열이름] = 값` 형태로 한 번에 접근해야 의도치 않은 오류를 막을 수 있습니다. 경고는 "원본을 바꿀지, 복사본을 바꿀지 애매하니 명확히 해달라"는 신호입니다.
> 🚫 **`SettingWithCopyWarning` 주의!**: 값을 변경할 때는 `df.loc[행조건, 열이름] = 값` 형태로 한 번에 접근해야 의도치 않은 오류를 막을 수 있습니다.
---
## 4일차(목): Pandas - 데이터 분해와 조립 (`groupby`)
`groupby()`는 데이터를 특정 기준으로 그룹화하여 각 그룹에 대한 집계(합계, 평균 등)를 수행하는 강력한 기능입니다.
### 5-3. 데이터 분해와 조립 (`groupby`)
#### 💡 쉽게 이해하기: `groupby`는 '반별 성적 평균 계산' (Split-Apply-Combine)
> `groupby`는 '반별 성적을 계산하는 선생님'과 같습니다.
> 1. **Split (나누기)**: 전체 학생 명단을 '1반', '2반', '3반'으로 나눕니다 (`df.groupby('반')`).
> 2. **Apply (적용하기)**: 각 반별로 '평균 점수 계산'이라는 동일한 작업을 수행합니다 (`.agg('mean')`).
> **💡 비유: `groupby`는 '반별 성적 평균 계산' (Split-Apply-Combine)**
> 1. **Split (나누기)**: 전체 학생 명단을 '1반', '2반'으로 나눕니다 (`df.groupby('반')`).
이전 파트에서 우리는 NumPy와 Pandas로 데이터를 자유자재로 다루는 법을 배웠습니다. 하지만 숫자로만 가득한 표는 우리에게 많은 것을 알려주지 않습니다. 이번 파트에서는 데이터에 '생명'을 불어넣는 두 가지 핵심 기술, **시각화**와 **머신러닝**을 배웁니다.
### 🎯 6주차 학습 목표
-**[Matplotlib](./glossary.md#matplotlib--seaborn)**과 **[Seaborn](./glossary.md#matplotlib--seaborn)**으로 데이터를 시각화하여 인사이트를 발견합니다.
-**[Scikit-learn](./glossary.md#scikit-learn)을 사용하여 머신러닝의 기본 프로세스(학습, 예측, 평가)를 이해합니다.
- 모델 성능을 높이기 위한 핵심 데이터 전처리 기법을 학습합니다.
-**시각화**는 데이터 속에 숨겨진 패턴과 이야기를 발견하는 '눈'이 되어줄 것입니다.
-**머신러닝**은 그 패턴을 학습하여 미래를 예측하는 '두뇌' 역할을 합니다.
Scikit-learn이라는 강력한 도구를 통해, 우리는 이 모든 과정을 직접 체험해볼 것입니다.
> [!TIP]
> 본 파트의 모든 예제 코드는 `source_code/part_6_machine_learning.py` 파일에서 직접 실행하고 수정해볼 수 있습니다.
---
## 1-2일차(월-화): 데이터 시각화 - 그림으로 데이터 보기
## 4. 데이터 시각화: 그림으로 데이터와 대화하기
데이터를 숫자로만 보는 것은 마치 숲을 보지 못하고 나무 하나하나만 보는 것과 같습니다. 시각화는 데이터 속에 숨겨진 패턴, 관계, 이상치를 한눈에 파악하게 해주는 강력한 도구입니다.
> **🎯 1-2일차 목표:** Matplotlib와 Seaborn으로 데이터를 시각화하여 인사이트를 발견합니다.
-**[Matplotlib](./glossary.md#matplotlib--seaborn)**: 파이썬 시각화의 근간. 거의 모든 종류의 그래프를 그릴 수 있는 강력한 라이브러리. (비유: 하얀 도화지)
-**[Seaborn](./glossary.md#matplotlib--seaborn)**: Matplotlib을 더 쉽고 예쁘게 사용할 수 있도록 만든 고수준 라이브러리. 통계적인 그래프 기능이 특화되어 있습니다. (비유: 밑그림과 색칠 도구가 갖춰진 스케치북)
데이터를 숫자로만 보는 것은 숲을 보지 못하고 나무만 보는 것과 같습니다. 시각화는 데이터 속에 숨겨진 패턴, 관계, 이상치를 한눈에 파악하게 해주는 강력한 도구입니다.
#### 💡 쉽게 이해하기: 어떤 그래프를 언제 쓸까?
> - **선 그래프 (Line Plot)**: '시간'에 따른 주가 변화처럼, 연속적인 데이터의 '추세'를 볼 때
> - **막대 그래프 (Bar Plot)**: 반별 평균 키처럼, 여러 '그룹' 간의 값을 '비교'할 때
> - **산점도 (Scatter Plot)**: 공부 시간과 성적처럼, 두 변수 간의 '관계'나 '분포'를 볼 때
> - **히스토그램 (Histogram)**: 우리 반 학생들의 키 분포처럼, 특정 데이터 구간에 얼마나 많은 값이 있는지 '분포'를 볼 때
> - **히트맵 (Heatmap)**: 여러 변수들 간의 상관관계를 한눈에 '색상'으로 파악할 때
-**Matplotlib**: 파이썬 시각화의 근간. 거의 모든 종류의 그래프를 그릴 수 있습니다. (비유: 하얀 도화지)
-**Seaborn**: Matplotlib을 더 쉽고 예쁘게 사용하도록 만든 고수준 라이브러리. 통계 기능이 특화되어 있습니다. (비유: 밑그림과 색칠 도구가 갖춰진 스케치북)
#### 4-1. 어떤 그래프를 언제 쓸까?
-**선 그래프 (Line Plot)**: 시간에 따른 주가 변화처럼, 연속적인 데이터의 **추세**를 볼 때
-**막대 그래프 (Bar Plot)**: 반별 평균 키처럼, 여러 그룹 간의 값을 **비교**할 때
-**산점도 (Scatter Plot)**: 공부 시간과 성적처럼, 두 변수 간의 **관계**나 분포를 볼 때
-**히스토그램 (Histogram)**: 우리 반 학생들의 키 분포처럼, 데이터의 **분포**를 볼 때
-**히트맵 (Heatmap)**: 여러 변수들 간의 상관관계를 한눈에 **색상**으로 파악할 때
**⬅️ 이전 시간: [Part 7: 딥러닝 기초와 PyTorch](./part_7_deep_learning.md)** | **➡️ 다음 시간: [Part 8: FastAPI를 이용한 모델 서빙](./part_8_model_serving_with_fastapi.md)**
**⬅️ 이전 시간: [Part 7: 딥러닝 기초와 PyTorch](./part_7_deep_learning.md)**
**➡️ 다음 시간: [Part 8: FastAPI를 이용한 모델 서빙](./part_8_model_serving_with_fastapi.md)**
- 본 파트에서 다루는 LangChain RAG 파이프라인 예제 코드를 직접 실행하고 수정해볼 수 있습니다.
---
## 1. 거대 언어 모델(LLM)의 발전과 새로운 패러다임
최근 몇 년간 딥러닝 분야, 특히 자연어 처리(NLP) 분야는 [거대 언어 모델(LLM)](./glossary.md#llm-large-language-model)의 등장으로 인해 혁명적인 변화를 겪고 있습니다. GPT-3, LLaMA, PaLM과 같은 모델들은 수십억 개에서 수천억 개의 파라미터를 가지며, 방대한 양의 텍스트 데이터로 사전 훈련(pre-training)되었습니다.
이러한 LLM들은 단순히 텍스트를 생성하는 것을 넘어, 번역, 요약, 질의응답, 코드 생성 등 다양한 작업에서 인간에 가까운 성능을 보여주며, 이는 '파운데이션 모델(Foundation Model)'이라는 새로운 패러다임의 등장을 이끌었습니다. 파운데이션 모델은 특정 작업에 국한되지 않고, 다양한 다운스트림 작업(downstream task)에 약간의 [미세조정(fine-tuning)](./glossary.md#미세조정fine-tuning)이나 [프롬프트 엔지니어링(prompt engineering)](./glossary.md#프롬프트-엔지니어링prompt-engineering)만으로도 적용될 수 있는 범용적인 모델을 의미합니다.
LLM의 등장은 개발자들이 AI 애플리케이션을 구축하는 방식에도 큰 변화를 가져왔습니다. 과거에는 특정 작업을 위해 모델을 처음부터 훈련하거나 복잡한 파이프라인을 구축해야 했다면, 이제는 강력한 LLM을 '두뇌'로 활용하고, 다양한 외부 도구 및 데이터와 연결하여 훨씬 더 복잡하고 지능적인 애플리케이션을 손쉽게 개발할 수 있게 되었습니다.
## 2. LLM 애플리케이션의 핵심, LangChain
LLM을 기반으로 애플리케이션을 개발할 때, 단순히 모델에 질문하고 답변을 받는 것을 넘어, 외부 데이터베이스를 참조하거나, 특정 API를 호출하거나, 여러 단계를 거쳐 복잡한 추론을 수행해야 하는 경우가 많습니다. 이러한 과정은 생각보다 복잡하며, 많은 반복적인 코드를 요구합니다.
**[LangChain](./glossary.md#langchain)**은 바로 이러한 문제점을 해결하기 위해 등장한 프레임워크입니다. LangChain은 LLM을 활용한 애플리케이션 개발을 위한 강력하고 유연한 도구 모음을 제공하며, 개발자가 LLM을 외부 데이터 소스나 계산 로직과 쉽게 '연결(chain)'할 수 있도록 돕습니다.
### LangChain의 핵심 컴포넌트
LangChain은 여러 핵심 컴포넌트들의 조합으로 이루어져 있으며, 이를 통해 모듈식으로 애플리케이션을 구성할 수 있습니다.
-**Models**: 다양한 종류의 언어 모델(LLM, Chat Models, Text Embedding Models 등)을 표준화된 인터페이스로 제공합니다.
-**Prompts**: 사용자 입력과 추가적인 정보를 결합하여 모델에 전달할 프롬프트를 동적으로 생성하고 관리합니다.
-**Chains**: 가장 핵심적인 개념으로, LLM 호출과 다른 컴포넌트(예: 다른 Chain, 외부 API, 데이터 처리 로직 등)들을 순차적으로 연결하여 복잡한 작업을 수행하는 파이프라인을 구성합니다.
-**Indexes & Retrievers**: 외부 데이터 소스를 LLM이 활용할 수 있도록 구조화하고(Indexing), 사용자의 질문과 관련된 정보를 효율적으로 찾아오는(Retrieval) 기능을 담당합니다.
-**Agents**: LLM을 '추론 엔진'으로 사용하여, 어떤 도구(Tool)를 어떤 순서로 사용해야 할지 스스로 결정하고 작업을 수행하도록 만듭니다.
LangChain을 사용하면 개발자는 LLM 자체의 한계를 넘어, 외부 세계의 정보와 상호작용하는 훨씬 더 강력하고 실용적인 애플리케이션을 만들 수 있습니다.
## 2.5. RAG의 핵심 부품: 벡터 데이터베이스 (Vector Database)
RAG 파이프라인을 구축하려면 대량의 텍스트 조각(Chunk)들을 벡터로 변환하고, 사용자 질문 벡터와 가장 유사한 벡터들을 빠르게 찾아내야 합니다. 이 과정을 효율적으로 처리해주는 것이 바로 **[벡터 데이터베이스(Vector Database)](./glossary.md#벡터-데이터베이스vector-database)**입니다.
> **💡 비유: '의미로 책을 찾는 도서관 사서'**
>
> 일반적인 데이터베이스가 '정확한 제목'이나 '저자'로 책을 찾는다면, 벡터 데이터베이스는 "사랑과 희생에 관한 슬픈 이야기"처럼 **추상적인 의미**로 책을 찾아주는 똑똑한 사서와 같습니다.
> - **인덱싱(책 정리)**: 사서는 도서관의 모든 책(Chunk)을 읽고, 각 책의 핵심 내용을 요약한 '의미 태그(Vector)'를 붙여 특수한 서가(DB)에 정리합니다.
> - **검색(책 찾기)**: 사용자가 질문하면, 사서는 질문의 '의미'를 파악하고, 서가에서 가장 비슷한 '의미 태그'를 가진 책들을 순식간에 찾아 건네줍니다.
## 1. 학습 목표 (Learning Objectives)
수백만, 수억 개의 벡터 속에서 가장 가까운 벡터를 찾는 것은 엄청난 계산량을 요구하는 어려운 문제입니다('차원의 저주'). 벡터 데이터베이스는 **ANN(Approximate Nearest Neighbor, 근사 최근접 이웃)** 같은 알고리즘을 사용하여, 100% 정확하지는 않지만 매우 빠른 속도로 충분히 정확한 결과를 찾아줍니다.
이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다.
RAG에서 주로 사용되는 벡터 데이터베이스는 다음과 같습니다.
- LLM의 한계점(최신성 부족, 환각 등)을 설명하고, RAG가 이를 어떻게 해결하는지 이해할 수 있습니다.
- LangChain의 역할을 이해하고, RAG 파이프라인의 핵심 5단계(Load, Split, Embed, Store, Retrieve)를 설명할 수 있습니다.
- 문서 분할(Chunking)의 중요성을 이해하고, 기본적인 분할 전략을 적용할 수 있습니다.
- LangChain을 사용하여 외부 문서의 내용을 기반으로 질문에 답변하는 RAG 시스템을 직접 코드로 구현할 수 있습니다.
-**FAISS (Facebook AI Similarity Search)**
-**특징**: Facebook(Meta)에서 개발한, 매우 빠른 유사도 검색에 특화된 **라이브러리**입니다.
-**장점**: 인메모리(in-memory) 기반으로 작동하여 속도가 매우 빠르고, 별도의 서버 설치 없이 쉽게 사용할 수 있어 프로토타이핑이나 연구에 적합합니다.
-**단점**: 기본적으로 데이터를 메모리에 저장하므로, 프로세스가 종료되면 데이터가 사라집니다. (파일로 저장하고 불러올 수는 있습니다.)
## 2. 핵심 키워드 (Keywords)
-**Chroma (또는 ChromaDB)**
-**특징**: AI 네이티브(AI-native)를 표방하는 오픈소스 **벡터 데이터베이스**입니다.
-**장점**: 데이터를 디스크에 영속적으로(persistently) 저장하며, 클라이언트-서버 모드로 실행할 수 있어 여러 애플리케이션이 동시에 접근하는 실제 서비스 환경에 더 적합합니다. SDK가 사용하기 쉽게 설계되었습니다.
이 외에도 Pinecone, Weaviate, Milvus 등 다양한 상용/오픈소스 벡터 데이터베이스가 있으며, 각기 다른 특징과 장단점을 가지고 있습니다.
## 3. 도입: 똑똑한 LLM을 더 똑똑하게 만들기 (Introduction)
## 3. 외부 지식의 통합: 검색 증강 생성 (Retrieval-Augmented Generation, RAG)
우리는 앞에서 LLM이라는 강력한 '두뇌'를 배웠습니다. 하지만 이 두뇌는 몇 가지 결정적인 한계를 가집니다. 훈련 데이터에 없는 최신 정보나, 우리 회사 내부 문서 같은 사적인 내용은 전혀 알지 못합니다. 가끔은 그럴듯한 거짓말, 즉 '환각' 현상을 보이기도 하죠.
LLM은 방대한 지식을 학습했지만, 몇 가지 본질적인 한계를 가집니다.
이번 시간에는 이 한계를 극복하는 가장 효과적인 기술인 **검색 증강 생성(RAG, Retrieval-Augmented Generation)**을 배웁니다. RAG의 핵심 아이디어는 간단합니다.
1.**지식의 최신성 문제(Knowledge Cut-off)**: 모델이 훈련된 특정 시점 이후의 정보는 알지 못합니다.
2.**[환각(Hallucination)](./glossary.md#환각hallucination)**: 사실이 아닌 내용을 그럴듯하게 지어내는 경향이 있습니다.
3.**정보 출처의 부재**: 답변이 어떤 근거로 생성되었는지 알기 어렵습니다.
4.**사적인 데이터 접근 불가**: 기업 내부 문서나 개인적인 파일과 같은 비공개 데이터에 대해서는 알지 못합니다.
> **"LLM에게 정답을 바로 묻지 말고, 먼저 관련 정보를 찾아서 '오픈북 시험'을 보게 하자!"**
**[검색 증강 생성(RAG)](./glossary.md#rag-retrieval-augmented-generation)**는 이러한 한계를 극복하기 위한 매우 효과적인 방법론입니다. RAG의 핵심 아이디어는 LLM이 답변을 생성하기 전에, 먼저 외부 지식 소스(Knowledge Base)에서 사용자의 질문과 관련된 정보를 검색하고, 검색된 정보를 LLM에게 '참고 자료'로 함께 제공하는 것입니다.
그리고 이 복잡한 '오픈북 시험' 과정을 손쉽게 만들어주는 도구가 바로 **LangChain**입니다. LangChain을 통해 LLM이 외부 지식과 소통하게 하여, 훨씬 더 정확하고 신뢰성 있는 AI 애플리케이션을 만드는 방법을 탐험해 봅시다.
### RAG의 작동 방식
> [!TIP]
> 본 파트의 모든 예제 코드는 `source_code/part_7_5_llm_application_development.py` 파일에서 직접 실행하고 수정해볼 수 있습니다.
RAG 파이프라인은 크게 **Indexing**과 **Retrieval & Generation** 두 단계로 나뉩니다.
1.**Indexing (인덱싱)**
-**Load**: PDF, TXT, HTML 등 다양한 형식의 외부 문서를 불러옵니다.
-**Split (Chunking)**: 불러온 문서를 LLM이 처리하기 좋은 크기의 작은 조각(Chunk)으로 나눕니다. 이 과정이 바로 **'Chunking'**입니다.
-**Embed**: 각 Chunk를 텍스트 임베딩 모델을 사용하여 고차원의 벡터(Vector)로 변환합니다. 이 벡터는 해당 Chunk의 의미적인 내용을 담고 있습니다.
-**Retrieve**: 사용자의 질문(Query) 또한 [임베딩 모델](./glossary.md#임베딩-모델embedding-model)을 통해 벡터로 변환한 뒤, 벡터 저장소에서 이와 유사한(가까운) 벡터를 가진 Chunk들을 찾아냅니다.
-**Generate**: 검색된 Chunk들을 사용자의 원본 질문과 함께 프롬프트에 담아 LLM에 전달합니다. LLM은 이 정보를 바탕으로 신뢰성 높고 정확한 답변을 생성합니다.
## 4. RAG 파이프라인: 5단계로 완전 정복
## 4. RAG의 핵심, 문서 분할 (Document Splitting / Chunking)
RAG는 크게 **'인덱싱(Indexing)'**과 **'검색 및 생성(Retrieval & Generation)'** 두 단계로 나뉩니다. 이 과정을 5개의 세부 단계로 나누어 살펴보겠습니다.
RAG 파이프라인의 성능은 '얼마나 관련성 높은 정보를 잘 찾아오는가'에 크게 좌우되며, 이는 Chunking 전략에 직접적인 영향을 받습니다. 문서를 어떻게 의미 있는 단위로 잘 나누는가(Chunking)가 RAG 시스템 전체의 성공을 결정하는 핵심 요소 중 하나입니다.
query="AI 비전공자가 입문하기 좋은 책은 무엇인가요? 그리고 그 이유는 무엇인가요?"
response=qa_chain.invoke(query)
print(response["result"])
```
이 예제는 RAG의 가장 기본적인 흐름을 보여줍니다. 실제 애플리케이션에서는 문서의 종류에 맞는 `Loader`와 `TextSplitter`를 선택하고, `retriever`의 검색 옵션을 조정하며, `chain_type`을 변경하는 등 다양한 방식으로 파이프라인을 고도화할 수 있습니다.
---
## 6. 또 다른 선택지: LlamaIndex
LangChain이 LLM 애플리케이션을 위한 '범용 스위스 칼'이라면, **LlamaIndex**는 'RAG 전문 수술 도구'에 비유할 수 있습니다. LlamaIndex는 LLM이 외부 데이터를 더 쉽게 활용할 수 있도록, 데이터의 수집(ingestion), 인덱싱(indexing), 검색(retrieval) 과정에 특화된 기능들을 매우 상세하고 강력하게 제공하는 데이터 프레임워크입니다.
### LangChain vs. LlamaIndex
| 구분 | LangChain | LlamaIndex |
|:--- |:--- |:--- |
| **핵심 철학** | LLM을 중심으로 다양한 컴포넌트(Tool, Chain)를 연결하는 범용 프레임워크 | 데이터를 중심으로 LLM과 연결하는 데이터 프레임워크 |
| **주요 강점** | 에이전트(Agent)와 다양한 종류의 체인(Chain)을 이용한 복잡한 워크플로우 생성 | 고성능 RAG를 위한 고급 인덱싱, 데이터 수집, 검색 전략 제공 |
| **주요 사용 사례** | 챗봇, 자율 에이전트, API 연동 등 | 고도화된 RAG, 지식 관리 시스템, 문서 기반 질의응답 |
두 프레임워크는 경쟁 관계라기보다는 상호 보완적이며, 실제로 LlamaIndex의 데이터 인덱싱/검색 기능을 LangChain의 에이전트와 함께 사용하는 경우도 많습니다.
### 실습: LlamaIndex와 오픈소스 모델로 RAG 파이프라인 만들기
이번에는 LlamaIndex를 사용하여 RAG 파이프라인을 구축해 보겠습니다. 임베딩 모델뿐만 아니라 LLM까지 Hugging Face의 무료 추론 API를 사용하여, 완전한 오픈소스 스택으로 RAG를 구현하는 방법을 알아봅니다.
-[LangChain 공식 문서: RAG](https://python.langchain.com/v0.2/docs/concepts/#retrieval-augmented-generation-rag): LangChain이 설명하는 RAG의 개념과 다양한 활용법
-[Pinecone: What is RAG?](https://www.pinecone.io/learn/retrieval-augmented-generation/): 대표적인 벡터 DB 회사인 Pinecone이 설명하는 RAG 가이드
-[Hugging Face 블로그: RAG](https://huggingface.co/docs/transformers/main/en/rag): 허깅페이스에서 제공하는 RAG에 대한 기술적인 설명
# 스트리밍 형태로 답변 받기
streaming_response = query_engine.query(query)
streaming_response.print_response_stream()
print()
```
이 코드는 LlamaIndex가 어떻게 데이터 소스, 임베딩 모델, 벡터 저장소, LLM을 유연하게 조합하여 RAG 파이프라인을 구성하는지 보여줍니다. `Settings`를 통해 기본 컴포넌트를 설정하고, `SimpleDirectoryReader`로 데이터를 쉽게 로드하며, `VectorStoreIndex`로 인덱싱과 검색 과정을 아름답게 추상화하는 LlamaIndex의 디자인 철학을 엿볼 수 있습니다.
---
LangChain, LlamaIndex와 같은 프레임워크와 RAG, Chunking, 벡터 데이터베이스에 대한 이해는 최신 AI 트렌드를 따라가고, LLM의 잠재력을 최대한 활용하는 지능형 애플리케이션을 개발하는 데 있어 필수적인 역량이 될 것입니다.
\ No newline at end of file
**➡️ 다음 시간: [Part 8: FastAPI를 이용한 모델 서빙](./part_8_model_serving_with_fastapi.md)**
> - **Scikit-learn**: '트럭', '비행기'처럼 이미 완성된 모델을 가져와 사용하는 **'완성품 장난감'**과 같습니다. 사용하기 편리하고 대부분의 정형 데이터 문제에 효과적입니다.
> - **PyTorch**: 다양한 모양의 **'레고 블록'**을 제공하여, 사용자가 원하는 어떤 복잡한 구조(신경망)라도 직접 조립할 수 있게 해줍니다. 이미지, 텍스트 등 비정형 데이터를 다루는 딥러닝에 필수적이며, 연구와 산업 현장에서 가장 널리 쓰이는 도구 중 하나입니다.
> [!TIP]
> 본 파트의 모든 예제 코드는 `source_code/part_7_deep_learning.py` 파일에서 직접 실행하고 수정해볼 수 있습니다.
---
-**[PyTorch](./glossary.md#pytorch)**: 페이스북(메타)에서 개발한 딥러닝 프레임워크. 유연성과 직관적인 코드로 연구자와 개발자들에게 널리 사랑받고 있습니다.
## 4. PyTorch 딥러닝의 기본 구성 요소
#### 💡 쉽게 이해하기: PyTorch는 '레고 블록', Scikit-learn은 '완성된 장난감'
> - **Scikit-learn**: '트럭', '비행기'처럼 이미 완성된 모델을 가져와 사용하는 '완성품 장난감'과 같습니다. 사용하기 편리하고 대부분의 정형 데이터 문제에 효과적입니다.
> - **PyTorch**: 다양한 모양의 '레고 블록'을 제공하여, 사용자가 원하는 어떤 복잡한 구조(신경망)라도 직접 조립할 수 있게 해줍니다. 이미지, 텍스트 등 비정형 데이터를 다루는 딥러닝에 필수적입니다.
> **🎯 1-2일차 목표:** PyTorch의 텐서를 이해하고, 인공 신경망의 기본 구조를 코드로 구현합니다.
### 1. 딥러닝의 핵심, 텐서(Tensor)
-**텐서**: PyTorch에서 데이터를 다루는 기본 단위. NumPy의 `ndarray`와 매우 유사하지만, GPU를 사용한 가속 연산이 가능하고 자동 미분(Autograd) 기능을 지원한다는 결정적인 차이가 있습니다.
### 4-1. 딥러닝의 데이터, 텐서(Tensor)
-**텐서**: PyTorch에서 데이터를 다루는 기본 단위입니다. NumPy의 `ndarray`와 매우 유사하지만, 두 가지 결정적인 차이가 있습니다.
1.**GPU 가속**: `.to('cuda')` 코드 한 줄로 GPU를 사용한 초고속 연산이 가능합니다.
2.**자동 미분(Autograd)**: 딥러닝 학습의 핵심인 '역전파'를 위해 자동으로 미분값을 계산해줍니다.
> 선형 계층(`nn.Linear`)만 계속 쌓으면 결국 하나의 큰 선형 계층과 다를 바 없습니다. 활성화 함수는 여기에 '비선형성'이라는 마법을 더해, 복잡하고 구불구불한 데이터 패턴도 학습할 수 있게 해주는 필수 요소입니다.
---
## 4-5일차(목-금): 딥러닝 모델 학습시키기
### 1. 손실 함수(Loss Function)와 옵티마이저(Optimizer)
-**[손실 함수](./glossary.md#손실-함수loss-function)**: 모델의 예측이 실제 정답과 얼마나 다른지를 측정하는 '오차 측정기'. (예: `CrossEntropyLoss` - 분류 문제)
-**[옵티마이저](./glossary.md#옵티마이저optimizer)**: 손실 함수가 계산한 오차를 기반으로, 모델의 파라미터(가중치)를 어느 방향으로 얼마나 업데이트할지 결정하는 '조율사'. (예: `Adam`, `SGD`)
## 5. 딥러닝 모델 학습 과정
> **🎯 3-4일차 목표:** 손실 함수, 옵티마이저의 개념을 이해하고 전체 학습 루프를 구현합니다.
### 5-1. 모델의 나침반과 조율사
-**손실 함수(Loss Function)**: 모델의 예측이 실제 정답과 얼마나 다른지를 측정하는 **'오차 측정기'**. (예: `nn.CrossEntropyLoss` - 분류 문제)
-**옵티마이저(Optimizer)**: 손실 함수가 계산한 오차를 기반으로, 모델의 파라미터(가중치)를 어느 방향으로 얼마나 업데이트할지 결정하는 **'조율사'**. (예: `torch.optim.Adam`)
### 2. 학습 루프(Training Loop)
딥러닝 모델 학습은 '에포크(Epoch)'라는 단위로 반복됩니다. 1 에포크는 전체 훈련 데이터를 한 번 모두 사용하는 것을 의미합니다.
### 5-2. 핵심 프로세스: 학습 루프(Training Loop)
딥러닝 모델 학습은 '에포크(Epoch)'라는 단위로 반복됩니다. 1 에포크는 전체 훈련 데이터를 한 번 모두 사용하는 것을 의미하며, 학습은 다음 4단계로 이루어집니다.
1.**`model(inputs)`**: 모델에 입력을 넣어 예측값(`outputs`)을 계산합니다 (**순전파**).
2.**`loss = criterion(outputs, labels)`**: 예측값과 실제 정답을 비교하여 오차(`loss`)를 계산합니다.
3.**`loss.backward()`**: 계산된 오차를 기반으로, 각 파라미터가 오차에 얼마나 기여했는지 미분값을 계산합니다 (**역전파**).
4.**`optimizer.step()`**: 계산된 미분값을 바탕으로 모델의 파라미터를 업데이트하여 오차를 줄이는 방향으로 나아갑니다.
> **💡 순전파와 역전파**
> - **순전파 (Forward)**: 내가 만든 레시피로 요리를 해서(모델 예측) 손님에게 내놓는 과정.
> - **역전파 (Backward)**: 손님의 피드백("너무 짜요!")을 듣고, 소금을 얼마나 많이 넣었는지, 간장을 얼마나 넣었는지 원인을 거슬러 올라가 분석하는 과정. `optimizer.step()`은 이 분석을 바탕으로 다음 요리에서는 소금을 덜 넣는 행동입니다.
---
## 6. 직접 해보기 (Hands-on Lab): MNIST 손글씨 분류
> **🎯 5일차 목표:** PyTorch를 사용하여 딥러닝의 "Hello, World!"인 MNIST 손글씨 분류기를 직접 만듭니다.
### 문제:
`torchvision`을 사용하여 MNIST 데이터셋을 불러오고, 간단한 CNN(합성곱 신경망) 모델을 구축하여 손글씨 숫자를 분류하는 전체 코드를 완성하세요.
# for i, (images, labels) in enumerate(train_loader):
# - images, labels를 device로 이동
# - 순전파, 손실 계산, 역전파, 파라미터 업데이트 코드 작성
# 5. 평가 루프 구현
# with torch.no_grad():
# - test_loader에서 데이터를 가져와 예측
# - 전체 테스트 정확도 계산 및 출력 코드 작성
```
위 코드는 개념 설명을 위한 것으로, 실제 실행 가능한 전체 코드는 `source_code/part_7_deep_learning.py`에서 확인해주세요.
### 📚 7주차 마무리 및 다음 단계
이번 주 우리는 PyTorch를 사용하여 딥러닝의 기본 원리를 이해하고, 직접 신경망을 만들어 학습시키는 과정을 경험했습니다.
---
## 7. 되짚어보기 (Summary)
이번 주 우리는 PyTorch라는 '레고 블록'으로 딥러닝 모델을 직접 조립하는 방법을 배웠습니다.
다음 시간에는 지금까지 배운 머신러닝/딥러닝 지식을 바탕으로, 최근 가장 뜨거운 분야인 **거대 언어 모델(LLM)**과 **LangChain**을 활용하여 간단한 챗봇 애플리케이션을 만들어보는 시간을 갖습니다.
-**PyTorch의 구성요소**: GPU 연산이 가능한 `Tensor`와, `nn.Module`을 상속받아 만드는 우리만의 모델 구조를 이해했습니다.
-**딥러닝 학습 과정**: '오차 측정기'인 **손실 함수**와 '조율사'인 **옵티마이저**를 사용하여, **순전파 → 손실 계산 → 역전파 → 파라미터 업데이트**로 이어지는 학습 루프의 원리를 파악했습니다.
-**실전 경험**: MNIST 손글씨 분류 실습을 통해 이미지 데이터를 다루는 CNN 모델을 직접 구현하고 학습시키는 전 과정을 체험했습니다.
## 8. 더 깊이 알아보기 (Further Reading)
-[PyTorch 공식 60분 튜토리얼](https://tutorials.pytorch.kr/beginner/deep_learning_60min_blitz.html): PyTorch의 핵심 기능을 빠르게 훑어볼 수 있는 최고의 가이드
-[cs231n: Convolutional Neural Networks for Visual Recognition](https://cs231n.github.io/): 스탠포드 대학의 전설적인 딥러닝 강의. CNN의 원리를 깊이 있게 이해하고 싶다면 필독.
-[A Comprehensive Introduction to Torch.nn for Deep Learning](https://www.assemblyai.com/blog/a-comprehensive-introduction-to-torch-nn-for-deep-learning/): `torch.nn` 모듈에 대한 상세한 설명
---
**➡️ 다음 시간: [Part 7.5: LangChain으로 LLM 애플리케이션 개발 맛보기](./part_7.5_llm_application_development_with_langchain.md)**
**⬅️ 이전 시간: [Part 7.5: LLM 애플리케이션 개발과 LangChain](./part_7.5_llm_application_development_with_langchain.md)** | **➡️ 다음 시간: [Part 9: 프로덕션 레벨 API와 Docker](./part_9_production_ready_api.md)**
**⬅️ 이전 시간: [Part 7.5: LLM 애플리케이션 개발 맛보기](./part_7.5_llm_application_development_with_langchain.md)**
**➡️ 다음 시간: [Part 9: 프로덕션 레벨 API와 Docker](./part_9_production_ready_api.md)**
- 본 파트에서 다루는 FastAPI 서버 예제 코드(`main.py`, `requirements.txt` 등)를 직접 실행하고 수정해볼 수 있습니다.
---
## 1. 학습 목표 (Learning Objectives)
지금까지 우리는 Scikit-learn과 PyTorch를 이용해 강력한 AI 모델을 만드는 방법을 배웠습니다. 하지만 이 모델들은 아직 우리 컴퓨터 안에만 머물러 있습니다. 어떻게 하면 이 모델의 예측 능력을 다른 사람이나 서비스에게 제공할 수 있을까요?
이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다.
> **💡 비유: '나만 아는 맛집 레시피를 세상에 공개하기'**
>
> 우리가 AI 모델을 만든 것은, 세상에 없는 '비법 레시피'를 개발한 것과 같습니다. 하지만 이 레시피를 내 서랍 속에만 둔다면 아무도 그 맛을 볼 수 없습니다.
>
> **[API](./glossary.md#api-application-programming-interface) 서버**는 이 비법 레시피로 요리를 만들어 손님에게 판매하는 **'레스토랑'**을 차리는 과정입니다. 손님(클라이언트)은 주방(모델 서버)이 어떻게 돌아가는지 몰라도, 메뉴판(API 문서)을 보고 주문하면 맛있는 요리(예측 결과)를 받을 수 있습니다.
>
> 이번 주차에는 파이썬의 최신 웹 프레임워크인 **[FastAPI](./glossary.md#fastapi)**를 이용해 빠르고 세련된 AI 레스토랑을 여는 방법을 배워보겠습니다.
- API의 개념을 '레스토랑 주문'에 비유하여 설명할 수 있습니다.
- FastAPI를 사용하여 기본적인 웹 서버를 구축하고 `uvicorn`으로 실행할 수 있습니다.
-`lifespan` 이벤트를 사용하여 서버 시작 시 머신러닝 모델을 효율적으로 로드할 수 있습니다.
- Pydantic을 사용하여 API의 요청/응답 데이터 형식을 정의하고 자동으로 검증할 수 있습니다.
- FastAPI의 자동 생성 문서(Swagger UI)를 통해 브라우저에서 직접 API를 테스트하고 디버깅할 수 있습니다.
지금까지 우리는 Scikit-learn과 PyTorch로 강력한 AI 모델, 즉 세상에 없는 '비법 레시피'를 개발했습니다. 하지만 이 레시피를 내 서랍 속에만 둔다면 아무도 그 맛을 볼 수 없습니다.
-**API의 개념 이해**: '레스토랑 주문 카운터' 비유를 통해 API가 왜 필요하며, 어떤 역할을 하는지 이해합니다.
-**FastAPI로 서버 구축**: FastAPI를 설치하고, uvicorn으로 내 컴퓨터에서 웹 서버를 실행하는 방법을 배웁니다.
-**예측 API 구현**: 우리가 만든 머신러닝 모델을 FastAPI 서버에 탑재하여, 외부 요청에 따라 예측 결과를 반환하는 API를 만듭니다.
-**데이터 유효성 검사**: **[Pydantic](./glossary.md#pydantic)**을 이용해 '깐깐한 주문서 양식'처럼 API로 들어오는 데이터의 형식을 정의하고 자동으로 검증하는 방법을 익힙니다.
-**자동 API 문서 활용**: FastAPI의 강력한 기능인 **[Swagger UI](./glossary.md#swagger-ui--openapi)**를 통해, 코드를 건드리지 않고 브라우저에서 직접 API를 테스트하는 방법을 배웁니다.
> **API 서버는 이 비법 레시피로 요리를 만들어 손님에게 판매하는 '레스토랑'을 차리는 과정입니다.**
> 손님(클라이언트)은 주방(모델 서버)이 어떻게 돌아가는지 몰라도, 메뉴판(API 문서)을 보고 주문하면 맛있는 요리(예측 결과)를 받을 수 있습니다.
이번 주차에는 파이썬의 최신 웹 프레임워크인 **FastAPI**를 이용해, 빠르고 세련된 'AI 레스토랑'을 여는 방법을 배워보겠습니다.
> [!TIP]
> 본 파트의 모든 예제 코드는 `source_code/part_8_model_serving_with_fastapi` 폴더에서 직접 실행하고 수정해볼 수 있습니다.
---
### 1일차: FastAPI 입문 - 우리의 AI 레스토랑 개점
## 4. FastAPI로 우리의 AI 레스토랑 개점하기
#### **API란 무엇일까?**
[API(Application Programming Interface)](./glossary.md#api-application-programming-interface)는 소프트웨어들이 서로 정보를 주고받는 '약속된 방식'입니다. 우리 'AI 레스토랑'에서는 "이런 형식으로 주문(요청)하면, 저런 형식으로 요리(응답)를 내어드립니다"라고 정해놓은 **'주문 창구'** 와 같습니다.
> **🎯 1-2일차 목표:** FastAPI와 uvicorn으로 서버를 실행하고, Pydantic으로 데이터 형식을 정의합니다.
#### **왜 FastAPI일까?**
**[FastAPI](./glossary.md#fastapi)**는 우리 레스토랑을 위한 최고의 선택지입니다.
-**엄청난 속도**: 주문이 밀려들어도 막힘없이 처리하는 '베테랑 직원'처럼 매우 빠릅니다. (Node.js, Go와 비슷한 성능)
### 4-1. 왜 FastAPI일까?
-**엄청난 속도**: 주문이 밀려들어도 막힘없이 처리하는 '베테랑 직원'처럼 매우 빠릅니다.
-**쉬운 메뉴 관리**: '레시피(코드)'가 간결하고 명확해서, 실수를 줄이고 새로운 메뉴를 빠르게 추가하기 쉽습니다.
-**스마트 메뉴판 자동 생성**: 코드를 짜면 손님들이 직접 보고 테스트할 수 있는 **자동 대화형 문서([Swagger UI](./glossary.md#swagger-ui--openapi))**를 공짜로 만들어줍니다.
#### **개발 환경 준비**
먼저 FastAPI와 우리 레스토랑의 '서버 매니저' 역할을 할 [uvicorn](./glossary.md#uvicorn--gunicorn)을 설치합니다.
-**스마트 메뉴판 자동 생성**: 코드를 짜면 손님들이 직접 보고 테스트할 수 있는 자동 대화형 문서(Swagger UI)를 공짜로 만들어줍니다.
### 4-2. 서버 실행과 데이터 형식 정의
먼저 FastAPI와 서버 매니저 `uvicorn`을 설치합니다.
```bash
pip install fastapi "uvicorn[standard]"
```
#### **우리의 첫 FastAPI 앱**
`main.py` 파일을 만들어 레스토랑의 기본 틀을 잡아봅니다.
이제 `main.py` 파일에 레스토랑의 기본 틀을 잡고, 손님의 '주문서 양식'을 **Pydantic**으로 정의합니다.
-`data: IrisFeatures`: 손님의 주문(JSON)을 우리가 정의한 `IrisFeatures` 양식에 맞춰 자동으로 확인하고, 문제가 없으면 `data` 변수에 담아줍니다. 형식이 틀리면 FastAPI가 알아서 "주문 잘못하셨어요" 하고 오류를 보냅니다.
> **[필수]** 6주차 실습에서 저장한 `iris_model.pkl` 파일을 `main.py`와 같은 폴더에 준비하고, `scikit-learn`, `joblib`을 설치해주세요.
> `pip install scikit-learn joblib numpy`
---
### 4-5일차: API 테스트와 종합 - 레스토랑 시식회
## 6. 직접 해보기 (Hands-on Lab): 스마트 메뉴판으로 시식하기
이제 완성된 우리 레스토랑의 시그니처 메뉴를 맛볼 시간입니다. FastAPI의 가장 강력한 기능, **스마트 메뉴판(자동 대화형 API 문서)**을 이용해봅시다.
> **🎯 5일차 목표:** FastAPI의 자동 API 문서를 사용하여, 코드를 건드리지 않고 브라우저에서 직접 API를 테스트합니다.
#### **Swagger UI로 API 테스트하기**
FastAPI의 가장 강력한 기능, **스마트 메뉴판(Swagger UI)**을 이용해봅시다.
1.`uvicorn main:app --reload` 명령으로 서버가 실행 중인지 확인합니다.
2. 웹 브라우저에서 http://127.0.0.1:8000/docs 로 접속합니다.
1.`uvicorn main:app --reload` 명령으로 서버가 실행 중인지 확인합니다.
2.웹 브라우저에서 **`http://127.0.0.1:8000/docs`** 로 접속합니다.
> **💡 비유: '스마트 메뉴판 Swagger UI'**
>
> `.../docs`는 단순한 메뉴판이 아닙니다.
> **💡 비유: '스마트 메뉴판' Swagger UI**
> `/docs`는 단순한 메뉴판이 아닙니다.
> - **모든 메뉴(엔드포인트)가 목록으로** 보입니다.
> - 각 메뉴를 클릭하면 **필요한 재료(요청 형식)와 나오는 요리(응답 형식)**에 대한 상세한 설명이 나옵니다.
> - 가장 멋진 기능! **`Try it out`** 버튼을 누르면 메뉴판에서 바로 **'맛보기 주문'**을 넣어볼 수 있습니다. 주방에 직접 가지 않아도 그 자리에서 바로 요리 결과를 확인할 수 있습니다.
3.`POST /predict` 메뉴를 클릭하여 펼친 뒤 `Try it out` 버튼을 누릅니다.
4.**Request body**에 예시로 채워진 주문서를 그대로 두거나 다른 값으로 바꿔봅니다.
```json
{
"features":[6.7,3.0,5.2,2.3]
}
```
5.`Execute` 버튼을 눌러 주문을 주방으로 전송하고, 그 아래에서 방금 나온 따끈따끈한 요리(예측 결과)를 실시간으로 확인할 수 있습니다.
### 문제:
1.`/docs` 페이지에서 `POST /predict` 메뉴를 클릭하여 펼치고 `Try it out` 버튼을 누르세요.
2.**Request body**에 다른 붓꽃 데이터를 넣어보세요. (예: `versicolor` 품종인 `[6.0, 2.2, 4.0, 1.0]` 또는 `virginica` 품종인 `[6.7, 3.0, 5.2, 2.3]`)
3.`Execute` 버튼을 눌러 예측 결과를 확인하고, 모델이 올바르게 예측하는지 검증해보세요.
---
## 📝 8주차 요약
## 7. 되짚어보기 (Summary)
이번 주에는 FastAPI를 이용하여 머신러닝 모델을 API로 만드는 첫걸음을 떼었습니다.
-**FastAPI**: 'AI 레스토랑'을 만드는 웹 서버 프레임워크 FastAPI를 배우고 **Uvicorn**으로 실행했습니다.
-**lifespan**: 서버 시작 시 모델('주방기구')을 효율적으로 로드했습니다.
-**Pydantic**: 입력 데이터('주문서')의 형식을 강제하고 유효성을 검사했습니다.
-**Swagger UI**: '/docs'의 '스마트 메뉴판'을 통해 API를 쉽고 빠르게 테스트했습니다.
다음 시간에는 이 '1인 식당'을 여러 셰프와 직원이 협업하는 '프랜차이즈 레스토랑'으로 확장하는 방법을 배웁니다. 프로젝트의 구조를 체계적으로 개선하고, **[Docker](./glossary.md#docker)**를 이용해 우리 레스토랑을 어떤 쇼핑몰에든 쉽게 입점시키는 '밀키트' 기술을 배우겠습니다.
## 🔍 8주차 연습 문제
**문제 1: 7주차 Fashion-MNIST 모델 서빙하기**
- 7주차에서 만들었던 PyTorch CNN 모델(`SimpleCNN`)을 저장하고, 이 모델을 로드하여 예측하는 FastAPI 서버를 만들어보세요.
-**요구사항**:
1.`torch.save(model.state_dict(), 'fashion_mnist_cnn.pth')`로 모델의 가중치를 저장합니다.
2. 서버 시작 시(lifespan)에 모델 구조를 만들고 저장된 가중치를 `model.load_state_dict(...)`로 불러옵니다. `model.eval()` 모드로 설정하는 것을 잊지 마세요.
3. 입력 데이터는 28x28 픽셀 값을 펼친 784개의 float 리스트로 받도록 Pydantic 모델을 정의하세요.
4. 예측 엔드포인트(`POST /predict/fashion`)는 입력 리스트를 받아 `torch.Tensor`로 변환하고, 모델에 맞는 모양(`1, 1, 28, 28`)으로 reshape하여 예측을 수행해야 합니다.
5. 예측 결과로 가장 확률이 높은 클래스의 이름(e.g., 'T-shirt/top', 'Sneaker')을 반환하세요.
**문제 2: 책 정보 조회 API 만들기**
- 간단한 인-메모리(in-memory) 딕셔너리를 '데이터베이스'로 사용하여, 책 정보를 조회하는 API를 만들어보세요.
-**요구사항**:
1. 서버에 `books_db = {1: {"title": "클린 코드", "author": "로버트 C. 마틴"}, 2: ...}` 와 같이 책 데이터를 미리 만들어두세요.
2.`GET /books/{book_id}` 엔드포인트를 만드세요.
3.**경로 매개변수**로 받은 `book_id`를 이용하여 `books_db`에서 책을 찾아 정보를 반환하세요.
4. 만약 해당 `book_id`가 DB에 없다면, FastAPI의 `HTTPException`을 이용하여 `404 Not Found` 오류와 함께 "해당 ID의 책을 찾을 수 없습니다." 메시지를 반환하세요.
-**AI 레스토랑**: 모델을 '레시피', API 서버를 '레스토랑'에 비유하여 모델 서빙의 개념을 이해했습니다.
-**FastAPI와 Uvicorn**: 파이썬 웹 서버를 구축하고 실행하는 방법을 배웠습니다.
-**Pydantic과 lifespan**: API의 데이터 형식을 강제하고, 서버 시작 시 모델을 효율적으로 로드하는 방법을 익혔습니다.
-**Swagger UI**: `/docs`의 '스마트 메뉴판'을 통해 API를 쉽고 빠르게 테스트하는 강력한 기능을 체험했습니다.
## 8. 더 깊이 알아보기 (Further Reading)
-[FastAPI 공식 튜토리얼](https://fastapi.tiangolo.com/tutorial/): FastAPI의 모든 것을 배울 수 있는 최고의 가이드
-[Pydantic 공식 문서](https://docs.pydantic.dev/latest/): 데이터 유효성 검사를 더 깊이 있게 활용하는 방법
-[Serving Machine Learning Models](https://www.manning.com/books/serving-machine-learning-models): 모델 서빙에 대한 전반적인 내용을 다루는 전문 서적
---
**➡️ 다음 시간: [Part 9: 프로덕션 레벨 API와 Docker](./part_9_production_ready_api.md)**
- 본 파트에서 다루는 프로덕션 레벨의 FastAPI 프로젝트 구조(`app` 폴더), `Dockerfile`, `docker-compose.yml` 등의 전체 코드를 확인하고 직접 실행해볼 수 있습니다.
---
## 1. 학습 목표 (Learning Objectives)
지난 시간에는 FastAPI를 이용해 '1인 식당'처럼 빠르게 AI 모델 API를 만들어보았습니다. 프로토타입으로는 훌륭하지만, 메뉴가 다양해지고(기능 추가) 여러 셰프가 협업해야 하는(팀 개발) 실제 '프랜차이즈 레스토랑' 환경에서는 한계가 명확합니다.
이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다.
이번 주차에는 `main.py` 하나에 모든 것을 담던 방식에서 벗어나, 유지보수와 확장이 용이한 **프로덕션 레벨의 프로젝트 구조**로 우리 레스토랑을 리팩터링합니다. 또한, 어떤 백화점에 입점하든 동일한 맛과 서비스를 보장하는 **'밀키트([Docker](./glossary.md#docker) 컨테이너)'** 기술을 배워, 우리 AI 서비스를 세상 어디에든 쉽고 안정적으로 배포할 수 있는 능력을 갖추게 될 것입니다.
- 유지보수와 협업에 용이하도록 FastAPI 프로젝트를 기능별로 구조화할 수 있습니다.
-`APIRouter`를 사용하여 API 엔드포인트를 모듈화하고 관리할 수 있습니다.
- "제 컴퓨터에선 되는데요..." 문제를 해결하는 Docker의 원리를 이해하고, 이미지/컨테이너 개념을 설명할 수 있습니다.
-`Dockerfile`을 작성하여 파이썬 애플리케이션을 컨테이너 이미지로 만들 수 있습니다.
-`docker-compose.yml`을 사용하여 여러 서비스(API 서버, DB 등)를 한 번에 실행하고 관리할 수 있습니다.
-**프로젝트 구조화**: '1인 식당'에서 '프랜차이즈 본사'로 확장하며, 유지보수와 협업에 용이한 전문적인 프로젝트 구조를 이해합니다.
-**관심사 분리**: API의 각 기능(설정, DB, 모델, 스키마, 라우터)을 독립적인 파일로 분리하고, APIRouter로 엔드포인트를 기능별('한식/중식 코너')로 모듈화합니다.
-**[Docker](./glossary.md#docker) 개념 이해**: "제 컴퓨터에선 되는데요..." 문제를 해결하는 컨테이너 기술의 원리를 이해하고, 이미지/컨테이너/도커파일의 개념을 익힙니다.
-**[Dockerfile](./glossary.md#dockerfile) 작성**: 'AI 레스토랑 밀키트 레시피'인 Dockerfile을 직접 작성하여 우리 앱을 이미지로 만듭니다.
-**[Docker Compose](./glossary.md#docker-compose) 활용**: API 서버, 데이터베이스 등 여러 서비스를 명령어 하나로 관리하는 `docker-compose.yml`의 원리를 익힙니다.
지난 시간에는 FastAPI로 '1인 식당'처럼 빠르게 AI 모델 API를 만들었습니다. 프로토타입으로는 훌륭하지만, 메뉴가 다양해지고(기능 추가) 여러 셰프가 협업해야 하는(팀 개발) 실제 '프랜차이즈 레스토랑' 환경에서는 한계가 명확합니다.
이번 주차에는 `main.py` 하나에 모든 것을 담던 방식에서 벗어나, **유지보수와 확장이 용이한 프로젝트 구조**로 우리 레스토랑을 리팩터링합니다. 또한, 어떤 백화점에 입점하든 동일한 맛과 서비스를 보장하는 **'밀키트(Docker 컨테이너)'** 기술을 배워, 우리 AI 서비스를 세상 어디에든 쉽고 안정적으로 배포하는 능력을 갖추게 될 것입니다.
> [!TIP]
> 본 파트의 모든 예제 코드는 `source_code/part_9_production_ready_api` 폴더에서 직접 실행하고 수정해볼 수 있습니다.
---
### 1-2일차: 프로덕션 레벨 프로젝트 구조 설계
#### **왜 구조를 바꿔야 할까?**
`main.py` 파일 하나에 모든 코드가 있는 것은, 주방장이 혼자 주문받고, 요리하고, 서빙하고, 계산까지 하는 동네 분식집과 같습니다. 처음엔 빠르지만, 손님이 많아지면 금방 한계에 부딪힙니다.
## 4. 1단계: 프로덕션 레벨 프로젝트 구조 설계
> **💡 비유: '1인 식당'에서 '프랜차이즈 레스토랑'으로**
>
> 우리는 이제 레스토랑을 프랜차이즈화할 것입니다. 이를 위해서는 역할 분담이 필수적입니다.
> - **`settings.py`**: 본사의 운영 방침 (환경 설정)
> - **`database.py`**: 식자재 창고와의 연결 통로 (DB 접속)
> - **`models.py`**: 창고에 보관된 식자재의 형태 정의 (DB 테이블 구조)
> - **`schemas.py`**: 모든 지점에서 통일된 '주문서 양식' (데이터 형식 정의)
> - **`crud.py`**: 식자재를 창고에서 꺼내고 넣는 '표준 작업 절차' (DB 처리 로직)
> - **`routers/`**: '한식 코너', '중식 코너' 등 메뉴별로 분리된 주방 (기능별 API)
> - **`main.py`**: 모든 코너를 총괄하고 손님을 맞는 '총지배인'
> **🎯 1-2일차 목표:** '관심사 분리' 원칙에 따라 FastAPI 프로젝트를 기능별 파일과 폴더로 재구성합니다.
`main.py` 하나에 모든 코드가 있는 것은, 주방장이 혼자 주문받고, 요리하고, 서빙까지 하는 것과 같습니다. 우리는 이제 역할을 분담하여 '프랜차이즈 본사'처럼 체계적인 시스템을 만들 것입니다.
#### **1단계: 프로젝트 폴더 및 기본 설정 파일 생성**
-**`settings.py`**: 본사의 운영 방침 (환경 설정)
-**`schemas.py`**: 모든 지점에서 통일된 '주문서 양식' (데이터 형식 정의)
-**`routers/`**: '한식 코너', '중식 코너' 등 메뉴별로 분리된 주방 (기능별 API)
-**`main.py`**: 모든 코너를 총괄하고 손님을 맞는 '총지배인'
먼저 아래 구조에 따라 `fastapi_project` 폴더 및 하위 폴더들을 만듭니다.
### 4-1. 프로젝트 폴더 및 라우터 분리
먼저 아래와 같이 프로젝트 구조를 만듭니다. `main.py`에 있던 예측 관련 로직을 `routers/predictions.py`로 옮깁니다.
-**[SQLAlchemy](./glossary.md#sqlalchemy)**: 파이썬 코드로 데이터베이스와 상호작용할 수 있게 해주는 ORM(Object-Relational Mapping) 라이브러리입니다. SQL 쿼리를 직접 작성하지 않고, 파이썬 객체(모델)를 통해 DB 테이블을 조작할 수 있습니다.
#### **2단계: 데이터 모델과 로직 분리**
**`app/models.py` (식자재의 형태 정의)**: DB 테이블 구조를 정의합니다.
-**[`Depends(get_db)`](./glossary.md#의존성-주입dependency-injection-di) (의존성 주입)**:`create_prediction`이라는 '셰프'가 요리를 시작하기 전에, FastAPI라는 '총지배인'이 `get_db`라는 '주방 보조'를 시켜 DB 연결이라는 '깨끗한 프라이팬'을 가져다주는 것과 같습니다. 셰프는 프라이팬을 어떻게 가져왔는지 신경 쓸 필요 없이 요리에만 집중할 수 있습니다.
이제 `uvicorn app.main:app --reload`로 서버를 실행하고 `http://127.0.0.1:8000/docs`에 접속하면, `predictions`라는 태그로 그룹화된 API를 확인할 수 있습니다.
---
### 4일차: Docker로 우리 앱 포장하기
#### **왜 Docker를 사용해야 할까?**
## 5. 2단계: Docker로 우리 앱 포장하기
> **🎯 3-4일차 목표:** Dockerfile을 작성하여 우리 앱을 이미지로 만들고, docker-compose로 실행합니다.
### 5-1. 왜 Docker를 써야 할까?
> **💡 비유: '어디서든 똑같은 맛을 내는 밀키트'**
>
> 우리 레스토랑의 레시피와 재료를 완벽하게 준비해도, 지점마다 주방 환경(OS, 라이브러리 버전 등)이 다르면 음식 맛이 달라질 수 있습니다. "제 주방(컴퓨터)에서는 되는데요?" 라는 문제는 개발자들의 흔한 악몽입니다.
>
> **[Docker](./glossary.md#docker)**는 우리 레스토랑의 모든 것(코드, 라이브러리, 설정)을 하나의 **'밀키트(이미지)'**로 완벽하게 포장하는 기술입니다. 이 밀키트는 어떤 주방(서버)에서 데우든 항상 똑같은 환경에서, 똑같은 맛의 요리를 만들어냅니다.
> **Docker**는 우리 레스토랑의 모든 것(코드, 라이브러리, 설정)을 하나의 **'밀키트(이미지)'**로 완벽하게 포장하는 기술입니다. 이 밀키트는 어떤 주방(서버)에서 데우든 항상 똑같은 환경에서, 똑같은 맛의 요리를 만들어냅니다.
#### **Dockerfile 작성하기**
### 5-2. Dockerfile: 밀키트 레시피 작성
`Dockerfile`은 이 밀키트를 만드는 '레시피'입니다. 프로젝트 최상위 폴더에 `Dockerfile`이라는 이름으로 파일을 만듭니다.
```dockerfile
...
...
@@ -241,113 +129,70 @@ def read_root():
FROM python:3.9-slim
# 2. 작업 디렉토리 설정 (조리대 위치 지정)
WORKDIR /app
WORKDIR /code
# 3. 의존성 파일 먼저 복사 (소스가 바뀌어도 의존성이 같으면 캐시 재사용)
COPY ./requirements.txt .
COPY ./requirements.txt /code/requirements.txt
# 4. 의존성 설치 (필요한 조미료 채우기)
RUN pip install--no-cache-dir-rrequirements.txt
RUN pip install--no-cache-dir--upgrade-r /code/requirements.txt
docker run -d-p 8000:80 --name my-first-restaurant franchise-restaurant:1.0
```
---
### 5일차: Docker Compose로 여러 서비스 관리하기
#### **왜 Docker Compose가 필요할까?**
> **💡 비유: '푸드코트 전체를 한 번에 오픈하기'**
>
> 지금까지는 '레스토랑' 하나만 Docker로 열었습니다. 하지만 실제 서비스는 API 서버 외에도 데이터베이스, 캐시 서버 등 여러 컴포넌트가 함께 동작하는 '푸드코트'와 같습니다.
>
> **[Docker Compose](./glossary.md#docker-compose)**는 이 푸드코트의 전체 설계도(`docker-compose.yml`)와 같습니다. 각 코너(컨테이너)의 종류, 위치, 연결 관계를 모두 정의해두면, **`docker-compose up`** 이라는 명령어 한 번으로 전체 푸드코트를 열고 닫을 수 있습니다.
#### **docker-compose.yml 작성하기**
프로젝트 최상위 폴더에 `docker-compose.yml` 파일을 만듭니다.
### 5-3. Docker Compose: 프랜차이즈 매장 동시 오픈
`docker-compose.yml`은 여러 '밀키트'(서비스)들을 정의하고 한 번에 관리하는 '프랜차이즈 오픈 계획서'입니다. 지금은 API 서버 하나지만, 나중에 DB 등이 추가되면 그 위력을 실감하게 됩니다.
-**프로젝트 구조화**: `APIRouter`를 사용해 기능별로 코드를 분리하여, 유지보수와 협업이 용이한 프로젝트 구조를 설계했습니다.
-**Docker 컨테이너화**: '밀키트' 비유를 통해 Docker의 필요성을 이해하고, `Dockerfile`을 작성하여 우리 앱을 재현 가능한 이미지로 만들었습니다.
-**Docker Compose**: `docker-compose.yml`을 통해 여러 서비스를 명령어 하나로 관리하는 방법을 익혀, 복잡한 애플리케이션 배포의 기초를 다졌습니다.
**문제 2: Dockerfile 개선**
- 현재 `Dockerfile`은 소스 코드가 바뀔 때마다 `pip install`을 다시 수행할 수 있습니다. 어떻게 하면 의존성이 바뀌지 않았을 때 `pip install` 과정을 건너뛰고 캐시를 사용할 수 있을까요? (`COPY` 명령어의 순서를 조정해보세요.)
## 8. 더 깊이 알아보기 (Further Reading)
-[TestDriven.io: FastAPI and Docker](https://testdriven.io/blog/dockerizing-fastapi/): Docker를 사용한 FastAPI 배포에 대한 상세한 튜토리얼
-[Docker 공식 문서: Get Started](https://docs.docker.com/get-started/): Docker의 기본 개념부터 차근차근 배울 수 있는 최고의 자료
-[『코어 자바스크립트』 저자의 Docker/k8s 강의](https://www.inflearn.com/course/%EB%8F%84%EC%BB%A4-k8s-%ED%95%B5%EC%8B%AC%EA%B0%9C%EB%념-%EC%9D%B5%ED%9E%88%EA%B8%B0): Docker와 쿠버네티스의 핵심 원리를 비유를 통해 쉽게 설명하는 강의