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 코드를 실행하는 기본 형태는 이렇습니다.

CODE
python app.py

이 한 줄이 시키는 일을 풀면: "python이라는 프로그램을 실행해서, app.py라는 파일을 인자로 넘겨라." 3부에서 본 "명령어 + 대상" 구조 그대로입니다.

콜아웃 · 여기서 2부가 다시 등장한다
python app.py현재 작업 디렉터리(cwd)에 있는 app.py를 찾습니다. 그래서 실행 전에 반드시 cd로 그 파일이 있는 폴더에 가 있어야 합니다. "그런 파일 없음(No such file or directory)" 오류의 단골 원인이 바로 이것 — 엉뚱한 폴더에 서서 실행한 경우입니다. 막히면 pwdls로 "지금 여기 app.py가 보이나?"부터 확인하세요.

파일 없이 한 줄씩: 대화형 모드(REPL)

python을 파일 이름 없이 그냥 치면, 코드를 한 줄씩 입력하면 바로 결과를 보여주는 대화형 모드로 들어갑니다. 이를 REPL(read-eval-print loop)이라고 합니다.

CODE
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)라고 합니다.

PYTHON
# 파이썬에 기본 내장된 모듈 불러오기
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 코드를 복붙해 돌리다 막혔을 때 적어도 무슨 일이 벌어지는지 읽을 수 있어야 합니다. 파이썬 코드에서 반복해 보게 될 다섯 가지만 짚습니다. 외우지 말고 "이런 모양은 이런 뜻"으로 눈에 익히세요.

PYTHON
# ① 변수: 값에 이름표를 붙여 담아둔다
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칸)로 "이 줄들이 한 덩어리"임을 나타냅니다. forif, 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개면 성공"으로 외우면 됩니다. 평소엔 신경 쓸 일이 적지만, 여러 명령을 자동으로 이어 돌리거나 도구가 "이 단계 실패"를 판단할 때 이 숫자를 씁니다.

방금 실행한 명령의 종료코드는 이렇게 확인합니다.

CODE
# 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 키를 코드에 쓰지 말고 환경변수로 처리하라고 하는 게 이 방식입니다. 코드를 공유해도 키가 새지 않게 하는 안전장치입니다.

환경변수를 잠깐 설정하고 확인하는 법:

CODE
# 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)을 만들어 비밀값을 적어두고, 코드가 이를 읽어 환경변수처럼 쓰는 방식을 자주 씁니다.

CODE
# .env  (프로젝트 폴더 안, 점으로 시작하는 숨김 파일)
MY_API_KEY=sk-실제키값
PYTHON
# 코드에서 .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번만 먼저 해보세요.)

  1. 종료코드를 눈으로 봅니다. 터미널에서 아무 정상 명령(예: ls)을 친 뒤, echo $LASTEXITCODE(mac/Linux는 echo $?)를 쳐보세요. 0이 나옵니다. 이번엔 일부러 없는 명령 lssss를 친 뒤 같은 걸 확인하면 0이 아닌 값이 나옵니다.
  2. PATH를 들여다봅니다. PowerShell에서 $env:PATH(mac/Linux는 echo $PATH)를 쳐보세요. 세미콜론(또는 콜론)으로 구분된 폴더 목록이 쭉 나옵니다. 이 폴더들 안에서 OS가 프로그램을 찾습니다.
  3. (Python 설치 후) 한 줄을 실행합니다. python -c "print('안녕, 세상')"을 쳐보세요. python이 따옴표 안 코드를 인터프리트해 stdout으로 결과를 내보냅니다. 만약 command not found가 나오면, 4.7에서 배운 대로 PATH 문제일 가능성이 큽니다 — 5부를 보세요.
  1. (Python 설치 후) REPL을 열어봅니다. python만 치고 들어가, >>> 뒤에 import os를 친 뒤 os.getcwd()를 쳐보세요. 현재 작업 디렉터리(cwd)가 출력됩니다 — 2부의 cwd를 코드로 확인한 셈입니다. exit()로 나옵니다. 이어서 import 없는모듈이름을 쳐 ModuleNotFoundError가 어떻게 생겼는지도 눈에 익혀두세요.

이 챕터 한 줄 정리

소스코드는 인터프리터/런타임(예: python)이 읽어야 돌고, import로 남의 모듈을 빌려 쓰며(없으면 ModuleNotFoundError), 실행하면 입력·출력·오류 통로로 글자를 주고받고, 끝나면 종료코드(0=성공)를 남긴다. PATH가 "프로그램을 어디서 찾을지"를, .env가 "비밀값을 코드 밖에서 주입"하는 통로를 맡는다.

다음 챕터에서는 실제로 코드 에디터와 Python을 설치하고, 가상환경을 만들고, 패키지를 설치합니다. RAG 안내서 첫 코드를 돌릴 준비를 끝냅니다.

← 이전: 터미널과 친해지기 · 목차 · 다음: 개발 도구 세팅 →