4부 · 프로그램은 어떻게 도는가
← 이전: 터미널과 친해지기 · 목차 · 다음: 개발 도구 세팅 →
이제 폴더를 돌아다닐 수 있으니, 그 안의 코드를 실제로 실행할 차례입니다. 이 챕터는 "내가 쓴 텍스트 파일이 어떻게 살아 움직이는가", 그리고 실행 중에 오가는 입력·출력·종료코드·환경변수를 다룹니다. 이걸 알면 RAG 코드의 import, os.environ, python app.py 같은 표현이 더 이상 주문처럼 보이지 않습니다.
4.1 소스코드에서 실행까지
우리가 쓰는 app.py는 사람이 읽는 글자, 즉 소스코드(source code)입니다. CPU는 이 글자를 직접 이해하지 못합니다. CPU는 0과 1로 된 기계어만 압니다. 그래서 둘 사이를 번역해 주는 프로그램이 필요합니다.
번역 방식은 크게 두 갈래입니다.
flowchart TB
SRC["소스코드<br/>app.py (사람이 쓴 글자)"]
SRC --> I["인터프리터 방식<br/>(예: Python)"]
SRC --> C["컴파일 방식<br/>(예: C, Go)"]
I --> IR["실행하는 그 순간<br/>한 줄씩 읽어 바로 실행"]
C --> CR["미리 통째로 번역해<br/>실행파일(.exe)을 만든 뒤 실행"]
IR --> OUT["실행 결과"]
CR --> OUT
classDef src fill:#CDE7FF,stroke:#3B82C4,color:#000
classDef way fill:#C8F0E8,stroke:#2BA88E,color:#000
classDef out fill:#D7F2C8,stroke:#5BA63B,color:#000
class SRC src
class I,C,IR,CR way
class OUT out
Python은 인터프리터(interpreter) 방식입니다. python app.py라고 치면, python이라는 프로그램이 app.py를 위에서부터 한 줄씩 읽어 그 자리에서 실행합니다. 그래서 Python을 쓰려면 컴퓨터에 python이라는 프로그램(=인터프리터)이 설치돼 있어야 합니다. 이게 5부에서 "Python을 설치한다"의 정확한 의미입니다 — 언어 규칙이 아니라, 코드를 실행해 줄 그 번역 프로그램을 까는 것입니다.
"인터프리터/런타임이 있어야 코드가 돈다"는 사실이 입문 단계의 핵심 통찰입니다. python: command not found 오류는 결국 "번역해 줄 프로그램이 없다"는 뜻입니다.
4.2 프로그램을 실행하는 두 가지 방법
더블클릭 vs 터미널
평소에 앱을 더블클릭으로 켜듯, 어떤 프로그램은 더블클릭으로도 실행됩니다. 하지만 개발에서는 거의 항상 터미널에서 실행합니다. 이유가 있습니다: 더블클릭은 오류가 나면 창이 휙 닫혀버려 무슨 일이 났는지 볼 수 없지만, 터미널에서 실행하면 출력과 오류 메시지가 화면에 남습니다. 6부에서 다룰 "오류 메시지 읽기"가 가능하려면 터미널 실행이 거의 필수입니다.
Python 코드를 실행하는 기본 형태는 이렇습니다.
python app.py
이 한 줄이 시키는 일을 풀면: "python이라는 프로그램을 실행해서, app.py라는 파일을 인자로 넘겨라." 3부에서 본 "명령어 + 대상" 구조 그대로입니다.
콜아웃 · 여기서 2부가 다시 등장한다python app.py는 현재 작업 디렉터리(cwd)에 있는app.py를 찾습니다. 그래서 실행 전에 반드시cd로 그 파일이 있는 폴더에 가 있어야 합니다. "그런 파일 없음(No such file or directory)" 오류의 단골 원인이 바로 이것 — 엉뚱한 폴더에 서서 실행한 경우입니다. 막히면pwd와ls로 "지금 여기 app.py가 보이나?"부터 확인하세요.
파일 없이 한 줄씩: 대화형 모드(REPL)
python을 파일 이름 없이 그냥 치면, 코드를 한 줄씩 입력하면 바로 결과를 보여주는 대화형 모드로 들어갑니다. 이를 REPL(read-eval-print loop)이라고 합니다.
PS C:\...> python >>> 1 + 1 2 >>> "안녕" * 3 '안녕안녕안녕' >>> exit()
앞에 >>>가 보이면 "지금 파이썬과 직접 대화 중"이라는 뜻입니다. 작은 걸 빠르게 시험해 볼 때(이 함수 이름이 맞나? 이 값이 뭐가 나오지?) 아주 유용합니다. 나가려면 exit()를 치거나 Ctrl + Z 후 엔터(mac/Linux는 Ctrl + D)를 누릅니다.
헷갈림 주의:>>>가 보이는 동안에는 터미널 명령(ls,cd등)이 아니라 파이썬 코드를 입력하는 상태입니다. 터미널 명령을 쓰려면 먼저exit()로 빠져나와야 합니다. "왜cd가 안 먹지?" 하면 REPL 안에 갇혀 있는 경우가 많습니다.
4.3 import와 모듈 — 남이 만든 코드 빌려 쓰기
코드를 짜다 보면 모든 걸 직접 만들지 않습니다. 이미 누군가 만들어 둔 코드 묶음을 불러와(import) 씁니다. 이 묶음을 모듈(module) 또는 패키지(package)라고 합니다.
# 파이썬에 기본 내장된 모듈 불러오기 import os # 운영체제 관련 기능 import json # JSON 데이터 다루기 # 바깥에서 설치한 패키지 불러오기 (5부에서 pip로 설치) import requests # 네트워크 요청
import 한 줄이 하는 일은 "그 모듈을 찾아서 지금 코드에서 쓸 수 있게 가져오기"입니다. 그런데 그 모듈을 어디서 찾을까요? 두 종류가 있습니다. os·json처럼 파이썬에 처음부터 들어 있는 표준 라이브러리는 그냥 import하면 됩니다. requests처럼 외부에서 만든 것은 먼저 설치(pip install)해야 import할 수 있습니다.
flowchart TB
A["import requests"] --> B{"이 패키지가<br/>설치돼 있나?"}
B -->|"표준 라이브러리<br/>(os, json 등)"| C["바로 사용 가능"]
B -->|"외부 패키지<br/>(requests 등)"| D{"pip install 했나?"}
D -->|예| C
D -->|아니오| E["ModuleNotFoundError"]
classDef start fill:#FFF3B0,stroke:#C9A227,color:#000
classDef check fill:#CDE7FF,stroke:#3B82C4,color:#000
classDef good fill:#D7F2C8,stroke:#5BA63B,color:#000
classDef bad fill:#FAD4D4,stroke:#C0392B,color:#000
class A start
class B,D check
class C good
class E bad
이 그림의 빨간 끝, ModuleNotFoundError: No module named '...'는 입문자가 가장 자주 만나는 오류입니다. 뜻은 "import하려는 그 모듈을 못 찾겠다"이고, 원인은 거의 항상 둘 중 하나입니다 — 아직 pip install을 안 했거나, 5부에서 배울 가상환경을 안 켜서 설치한 곳과 실행하는 곳이 다르거나. 6부 함정 표에서 다시 정리합니다.
4.4 코드를 "읽을" 수 있을 만큼만 — 최소 문법
이 가이드는 코딩 교본이 아니지만, RAG 코드를 복붙해 돌리다 막혔을 때 적어도 무슨 일이 벌어지는지 읽을 수 있어야 합니다. 파이썬 코드에서 반복해 보게 될 다섯 가지만 짚습니다. 외우지 말고 "이런 모양은 이런 뜻"으로 눈에 익히세요.
# ① 변수: 값에 이름표를 붙여 담아둔다 chunk_size = 500 # 숫자 question = "RAG가 뭐야?" # 글자(문자열) # ② 자료형: 값의 종류. 자주 보는 것 몇 가지 count = 3 # 정수(int) ratio = 0.8 # 소수(float) done = True # 참/거짓(bool) docs = ["a.pdf", "b.pdf"] # 목록(list) — 여러 개를 순서대로 config = {"model": "small"} # 사전(dict) — 이름:값 짝 # ③ 함수: "이런 입력을 주면 이런 일을 한다"는 동작 묶음 def load(path): # path를 받아서 return open(path).read() # 파일을 읽어 돌려준다 text = load("data/manual.pdf") # 이렇게 불러 쓴다 (호출) # ④ 반복: 목록의 항목을 하나씩 처리 for doc in docs: print(doc) # docs의 각 항목을 차례로 출력 # ⑤ 조건: 경우에 따라 다르게 if count > 0: print("문서 있음") else: print("문서 없음")
| 모양 | 이름 | 한마디 |
|---|---|---|
이름 = 값 | 변수 할당 | 값에 이름표 붙이기 |
[ ... ] | 리스트 | 여러 개를 순서대로 |
{ "키": 값 } | 딕셔너리 | 이름표-값 짝 모음 |
def 이름(...) | 함수 정의 | 동작 묶음 만들기 |
이름(...) | 함수 호출 | 그 동작 실행하기 |
for ... in ... | 반복문 | 하나씩 훑기 |
if ... else | 조건문 | 갈림길 |
콜아웃 · 들여쓰기가 문법이다
파이썬은 줄 앞의 들여쓰기(보통 공백 4칸)로 "이 줄들이 한 덩어리"임을 나타냅니다.for나if,def아래 줄들이 들여쓰기로 묶이죠. 그래서 들여쓰기가 어긋나면IndentationError가 납니다. 복붙할 때 들여쓰기가 깨지는 게 입문자 단골 사고이니, 5부에서 쓸 VS Code 같은 에디터로 붙여넣는 게 안전합니다.
이 정도면 RAG 코드를 보며 "아, 여기서 문서 목록을 만들고(리스트), 하나씩 돌면서(for) 함수를 불러 처리하는구나"를 따라갈 수 있습니다. 더 깊은 문법은 필요할 때 그때그때 익히면 됩니다.
4.5 표준 입력·출력·오류 — 프로그램의 세 통로
프로그램이 도는 동안, 바깥과 글자를 주고받는 통로가 세 개 있습니다. 입문 단계에서 이 셋의 이름만 알아둬도 오류 읽기가 쉬워집니다.
flowchart LR
IN["표준 입력<br/>(stdin)<br/>키보드 입력 등"] --> P["프로그램"]
P --> OUT["표준 출력<br/>(stdout)<br/>정상 결과"]
P --> ERR["표준 오류<br/>(stderr)<br/>오류·경고 메시지"]
classDef in fill:#FFF3B0,stroke:#C9A227,color:#000
classDef prog fill:#C8F0E8,stroke:#2BA88E,color:#000
classDef out fill:#D7F2C8,stroke:#5BA63B,color:#000
classDef err fill:#FAD4D4,stroke:#C0392B,color:#000
class IN in
class P prog
class OUT out
class ERR err
표준 출력(stdout)은 정상적인 결과가 나오는 통로입니다. Python의 print(...)가 여기로 글자를 내보냅니다. 표준 오류(stderr)는 오류·경고가 나오는 별도 통로입니다. 둘 다 보통 같은 터미널 화면에 섞여 보이지만, 내부적으로는 다른 통로입니다. 표준 입력(stdin)은 프로그램이 사용자에게 키보드 입력을 받는 통로입니다.
이 구분이 왜 중요할까요? 빨간 글씨로 길게 쏟아지는 오류 메시지(error / traceback)는 stderr로 나온 것이고, 그게 6부에서 "읽어야 할 보물"입니다. 무섭게 생겼지만 사실은 "어디서 왜 멈췄는지"를 친절히 적어준 안내문입니다.
4.6 종료코드 — 성공인가 실패인가
프로그램이 끝나면, 보이지 않게 숫자 하나를 남깁니다. 이를 종료코드(exit code) 또는 반환코드라고 합니다. 약속은 단순합니다.
| 종료코드 | 뜻 |
|---|---|
0 | 성공 (아무 문제 없이 끝남) |
0이 아닌 값 | 실패 (어딘가 문제가 있었음) |
"0이 성공"이라는 게 직관과 반대라 헷갈리지만, "오류가 0개면 성공"으로 외우면 됩니다. 평소엔 신경 쓸 일이 적지만, 여러 명령을 자동으로 이어 돌리거나 도구가 "이 단계 실패"를 판단할 때 이 숫자를 씁니다.
방금 실행한 명령의 종료코드는 이렇게 확인합니다.
# PowerShell echo $LASTEXITCODE # mac/Linux echo $?
4.7 환경변수 — 프로그램이 참고하는 "주변 설정"
환경변수(environment variable)는 OS가 들고 있다가 프로그램에게 건네주는 이름표 붙은 값들입니다. 코드 안에 직접 적지 않고 바깥에서 주입하는 설정값이라고 보면 됩니다. 프로그램은 실행될 때 이 값들을 읽어 참고합니다.
환경변수가 입문자에게 중요한 이유는 두 가지입니다.
첫째, PATH라는 특별한 환경변수가 있습니다. 이건 "프로그램을 어느 폴더들에서 찾을지" 목록입니다. 터미널에서 python이라고만 쳤을 때 OS가 python.exe가 어디 있는지 아는 건, python이 설치된 폴더가 PATH에 등록돼 있기 때문입니다. python: command not found의 흔한 원인이 바로 "설치는 됐지만 PATH에 없어서 못 찾는" 경우입니다. 5부에서 Python 설치 시 "Add to PATH"를 꼭 체크하라고 강조하는 이유가 이것입니다.
flowchart LR
A["사용자가<br/>'python' 입력"] --> B{"PATH 목록의<br/>폴더들을 차례로 검색"}
B -->|찾음| C["해당 python.exe 실행"]
B -->|못 찾음| D["command not found"]
classDef in fill:#FFF3B0,stroke:#C9A227,color:#000
classDef check fill:#CDE7FF,stroke:#3B82C4,color:#000
classDef good fill:#D7F2C8,stroke:#5BA63B,color:#000
classDef bad fill:#FAD4D4,stroke:#C0392B,color:#000
class A in
class B check
class C good
class D bad
둘째, 비밀값(API 키 등)을 코드에 직접 박지 않고 환경변수로 넘깁니다. RAG 안내서에서 임베딩 API 키를 코드에 쓰지 말고 환경변수로 처리하라고 하는 게 이 방식입니다. 코드를 공유해도 키가 새지 않게 하는 안전장치입니다.
환경변수를 잠깐 설정하고 확인하는 법:
# PowerShell — 이번 터미널 세션에만 적용 $env:MY_API_KEY = "예시값" echo $env:MY_API_KEY # mac/Linux export MY_API_KEY="예시값" echo $MY_API_KEY
위 방식은 그 터미널 창에서만 유효하고, 창을 닫으면 사라집니다. 영구 설정이나 .env 파일로 관리하는 법은 실제 RAG 프로젝트에서 다시 다룹니다. 지금은 "코드 바깥에서 값을 주입하는 통로가 있다"는 개념만 가져가면 됩니다.
.env 파일 — 비밀값을 다루는 실전 방식 (미리보기)
터미널에 매번 키를 입력하는 건 번거롭습니다. 그래서 실무에서는 프로젝트 폴더 안에 .env라는 숨김 파일(2.6)을 만들어 비밀값을 적어두고, 코드가 이를 읽어 환경변수처럼 쓰는 방식을 자주 씁니다.
# .env (프로젝트 폴더 안, 점으로 시작하는 숨김 파일) MY_API_KEY=sk-실제키값
# 코드에서 .env를 읽어 환경변수로 올리기 (python-dotenv 패키지 사용) import os from dotenv import load_dotenv load_dotenv() # .env를 읽어 환경변수로 등록 api_key = os.environ["MY_API_KEY"] # 코드엔 키를 직접 안 쓰고 이렇게 꺼냄
여기서 가장 중요한 안전 규칙: .env 파일은 절대 남과 공유하거나 인터넷(GitHub 등)에 올리지 않습니다. 그래서 5부에서 배울 .gitignore에 .env를 꼭 등록해, 실수로 업로드되지 않게 막습니다. RAG 안내서가 "API 키는 환경변수로 처리하라"고 할 때, 실제로는 대개 이 .env 방식을 가리킵니다.
지금 이걸 다 따라 할 필요는 없습니다. "비밀값은 코드 밖(.env)에 두고, 코드는 그걸 꺼내 쓰고, 그 파일은 공유하지 않는다"는 흐름만 머리에 넣어두면 충분합니다.
직접 해보기
아주 작은 Python 실행을 흉내 내봅니다. (아직 Python을 설치하지 않았다면 이 실습은 5부를 끝낸 뒤 다시 와서 해도 됩니다. 개념 확인용 1~2번만 먼저 해보세요.)
- 종료코드를 눈으로 봅니다. 터미널에서 아무 정상 명령(예:
ls)을 친 뒤,echo $LASTEXITCODE(mac/Linux는echo $?)를 쳐보세요.0이 나옵니다. 이번엔 일부러 없는 명령lssss를 친 뒤 같은 걸 확인하면 0이 아닌 값이 나옵니다. - PATH를 들여다봅니다. PowerShell에서
$env:PATH(mac/Linux는echo $PATH)를 쳐보세요. 세미콜론(또는 콜론)으로 구분된 폴더 목록이 쭉 나옵니다. 이 폴더들 안에서 OS가 프로그램을 찾습니다. - (Python 설치 후) 한 줄을 실행합니다.
python -c "print('안녕, 세상')"을 쳐보세요.python이 따옴표 안 코드를 인터프리트해 stdout으로 결과를 내보냅니다. 만약command not found가 나오면, 4.7에서 배운 대로 PATH 문제일 가능성이 큽니다 — 5부를 보세요.
- (Python 설치 후) REPL을 열어봅니다.
python만 치고 들어가,>>>뒤에import os를 친 뒤os.getcwd()를 쳐보세요. 현재 작업 디렉터리(cwd)가 출력됩니다 — 2부의 cwd를 코드로 확인한 셈입니다.exit()로 나옵니다. 이어서import 없는모듈이름을 쳐ModuleNotFoundError가 어떻게 생겼는지도 눈에 익혀두세요.
이 챕터 한 줄 정리
소스코드는 인터프리터/런타임(예: python)이 읽어야 돌고, import로 남의 모듈을 빌려 쓰며(없으면 ModuleNotFoundError), 실행하면 입력·출력·오류 통로로 글자를 주고받고, 끝나면 종료코드(0=성공)를 남긴다. PATH가 "프로그램을 어디서 찾을지"를, .env가 "비밀값을 코드 밖에서 주입"하는 통로를 맡는다.
다음 챕터에서는 실제로 코드 에디터와 Python을 설치하고, 가상환경을 만들고, 패키지를 설치합니다. RAG 안내서 첫 코드를 돌릴 준비를 끝냅니다.