포스트

2026-03-11 강의 정리

파이썬 클린코드

2026-03-11 강의 정리

목차

  1. 문제와 접근
  2. 문자열(String) 활용 - strip, f-string
  3. 리스트(List) 기초 - 인덱스, append, for, enumerate
  4. 조건문 (if / if in) - 키워드 필터링
  5. 딕셔너리(Dictionary) 기초 - OpenAI 메시지 형식과 연결
  6. 함수(Function) - 정의, 파라미터, return
  7. 예외 처리 (try / except)
  8. 파일 입출력
  9. OpenAI 메시지 구조와 파이썬 자료구조 연결
  10. 핵심 개념 총정리
  11. 자주 나오는 실수 / 주의사항
  12. 함수의 입력과 실패를 명확하게 만들기

1. 문제와 접근

LLM을 호출하는 코드도 결국 문자열과 자료구조를 주고받는 프로그램이다. 이번에는 파이썬 문법을 API 입력 정리, 메시지 구성, 결과 저장에 연결해 본다. 특히 빈 값과 누락된 값, 실패한 요청을 어떤 형태로 다음 단계에 넘길지에 초점을 둔다.

왜 이 파이썬 문법들을 배우나?

1
2
3
4
5
  문자열(str)    → 프롬프트(prompt) 조작, f-string으로 동적 프롬프트 생성
  리스트(list)   → messages 배열, 여러 질문 처리
  딕셔너리(dict) → {"role": "user", "content": "..."} OpenAI 메시지 형식 그 자체!
  함수(def)      → API 호출 반복 코드를 재사용 가능하게 묶기
  예외 처리      → API 오류, 없는 키 접근 등 안전하게 처리

2. 문자열(String) 활용 - strip, f-string

기본 문자열 조작

1
2
3
4
5
response = "1단계 : 설정 열기, 2단계 : 비밀번호 변경, 3단계 : 저장하기"

len(response)          # 38 (문자 수)
response.split(", ")   # ['1단계 : 설정 열기', '2단계 : 비밀번호 변경', '3단계 : 저장하기']
len(response.split(", "))  # 3

.strip() - 앞뒤 공백 제거

1
2
question = "   비밀번호를 잃어버렸어요   "
question.strip()   # "비밀번호를 잃어버렸어요"  ← 공백 제거됨
  • 사용자 입력에는 항상 앞뒤 공백이 붙을 수 있어서 .strip() 필수!

f-string - 동적 프롬프트 생성 (매우 중요!)

1
2
3
4
5
6
7
8
9
10
11
12
13
category = "계정"
question = "   비밀번호를 잃어버렸어요   "

prompt = f"""
카테고리 : {category}
질문 : {question.strip()}
답변형식 : 단계별로 설명해주세요
"""

print(prompt)
# 카테고리 : 계정
# 질문 : 비밀번호를 잃어버렸어요
# 답변형식 : 단계별로 설명해주세요
  • f””” “”” : 여러 줄 f-string (삼중 따옴표)
  • {변수명} : 변수 값을 문자열 안에 삽입
  • {question.strip()} : 변수에 메서드 적용도 가능

LLM 프롬프트 작성 실전 패턴

1
2
3
4
5
# 변수를 동적으로 바꿔가며 여러 질문을 만드는 패턴
categories = ["계정", "결제", "기술지원"]
for cat in categories:
    prompt = f"당신은 {cat} 전문 상담원입니다. 질문: {question}"
    # API 호출...

3. 리스트(List) 기초 - 인덱스, append, for, enumerate

리스트 생성 및 기본 조작

1
2
3
4
5
6
7
8
9
10
11
12
13
14
models = ['gpt-4o', 'gpt-4o-mini', 'claude-3']
scores = [0.95, 0.87, 0.72]

# 정방향 인덱스
models[0]   # 'gpt-4o'       (첫 번째)
models[2]   # 'claude-3'     (세 번째)

# 역방향 인덱스 ★ 유용!
models[-1]  # 'claude-3'     (마지막)
models[-2]  # 'gpt-4o-mini'  (뒤에서 두 번째)

# 요소 추가
models.append('gemini-2.5-flash')
len(models)  # 4
  • 음수 인덱스: -1은 항상 마지막, -2는 뒤에서 두 번째
  • 이 패턴은 LLM 대화에서 “가장 최근 응답”을 꺼낼 때 자주 사용

for 루프 기본 순회

1
2
3
4
5
6
for abc in models:
    print(abc)
# gpt-4o
# gpt-4o-mini
# claude-3
# gemini-2.5-flash

enumerate - 인덱스 포함 순회 (★ 자주 쓰임)

1
2
3
4
5
6
for i, abc in enumerate(models):
    print(i, abc)
# 0 gpt-4o
# 1 gpt-4o-mini
# 2 claude-3
# 3 gemini-2.5-flash
  • enumerate(리스트) → (인덱스, 값) 쌍으로 반환
  • 번호를 붙여서 출력하거나, 인덱스 기반으로 다른 리스트와 매핑할 때 유용

4. 조건문 (if / if in) - 키워드 필터링

기본 if / else

1
2
3
4
5
6
7
8
9
10
a = ['abcde', 'abc', 'bcd', 'efg']
for item in a:
    if 'bc' in item:
        print(item)   # 'bc'가 포함된 것만 출력
    else:
        print(3)
# abcde
# abc
# bcd
# 3          ← 'efg'는 'bc' 없음

실습: IT 질문에서 키워드 필터링

1
2
3
4
5
6
7
8
9
10
11
12
13
questions = [
    '비밀번호를 잊어버렸어요.',
    '계정이 해킹당했어요.',
    '로그인이 안 돼요.',
    '이메일을 변경하고 싶어요.',
    '2단계 인증이 안 돼요.'
]

keyword = "비밀번호"
for i, q in enumerate(questions):
    if keyword in q:
        print(f"{i}, {q}")
# 0, 비밀번호를 잊어버렸어요.
  • keyword in item : item 문자열 안에 keyword가 포함되면 True
  • 이 패턴 = 간단한 키워드 기반 문서 검색의 기본

5. 딕셔너리(Dictionary) 기초 - OpenAI 메시지 형식과 연결

딕셔너리의 기본값은 편리하지만, 필수 입력이 빠진 상황까지 정상 데이터처럼 만들 수 있다. 반드시 있어야 하는 키는 검증하고, 생략해도 되는 설정에만 기본값을 적용하면 호출부의 책임이 분명해진다.

딕셔너리 기본 구조

1
2
3
4
5
6
person = {'name': '홍길동', 'age': 30}
person['name']  # '홍길동'
person['age']   # 30

# 새 키 추가
person['email'] = 'hong@test.com'

★ OpenAI 메시지 = 딕셔너리!

1
2
3
4
5
6
7
8
9
10
11
12
13
# OpenAI API의 메시지 형식
message = {
    'role'   : 'user',
    'content': '비밀번호를 잃어버렸다'
}

message['role']    # 'user'
message['content'] # '비밀번호를 잃어버렸다'

# timestamp 같은 정보 추가도 가능
message['timestamp'] = '2026-03-11'
message
# {'role': 'user', 'content': '비밀번호를 잃어버렸다', 'timestamp': '2026-03-11'}
  • 즉, OpenAI API에 넘기는 messages 리스트는 “딕셔너리의 리스트”

대화 히스토리 = 딕셔너리 리스트

1
2
3
4
5
6
7
8
9
10
11
messages = [
    {"role": "system",    "content": "당신은 IT 상담원입니다"},
    {"role": "user",      "content": "비밀번호를 잊어버렸어요"},
    {"role": "assistant", "content": "비밀번호 재설정 방법을 안내드리겠습니다"}
]

for msg in messages:
    print(f"[{msg['role']}] {msg['content']}")
# [system] 당신은 IT 상담원입니다
# [user] 비밀번호를 잊어버렸어요
# [assistant] 비밀번호 재설정 방법을 안내드리겠습니다

.get() 메서드 - KeyError 방지 (중요!)

1
2
3
4
5
6
7
8
9
10
11
12
13
config = {'model': 'gpt-4o-mini', 'temperature': 0.7, 'max_tokens': 1000}

# dict[key] 방식 - 키 없으면 KeyError 발생!
config['없는키']   # KeyError!

# .get(key) 방식 - 키 없으면 None 반환
config.get('model')             # 'gpt-4o-mini'
config.get('max_tokens')        # 1000
config.get('없는키')             # None (에러 없음)

# .get(key, 기본값) - 키 없으면 기본값 반환
config.get('없는키', 100)        # 100 (기본값)
config.get('max_tokens', 100)   # 1000 (키 있으면 실제 값)
  • .get() 을 쓰는 것이 안전한 딕셔너리 접근 방법
  • 특히 API 응답처럼 예상치 못한 키가 없을 수 있는 경우에 필수

6. 함수(Function) - 정의, 파라미터, return

기본 함수 정의

1
2
3
4
5
6
7
8
9
10
11
12
13
def 인사(name):
    print(f"안녕하세요, {name} 님!")

인사('이상호')   # 안녕하세요, 이상호 님!
인사('이상호2')  # 안녕하세요, 이상호2 님!

# num 파라미터 추가 + return
def 인사(name, num):
    print(f"안녕하세요, {name} 님!" * num)   # num번 반복 출력
    return f"안녕하세요, {name} 님!"          # 호출한 곳에 값 반환

result = 인사('이상호', 3)
# 안녕하세요, 이상호 님!안녕하세요, 이상호 님!안녕하세요, 이상호 님!

기본값(default) 파라미터

1
2
3
4
5
6
7
8
9
def make_prompt(question, language='한국어'):
    prompt = f"다음 질문에 {language}로 답변해주세요 : {question}"
    return prompt

make_prompt("비밀번호 재설정 방법")
# '다음 질문에 한국어로 답변해주세요 : 비밀번호 재설정 방법'

make_prompt("비밀번호 재설정 방법", "영어")
# '다음 질문에 영어로 답변해주세요 : 비밀번호 재설정 방법'
  • language=’한국어’ : 기본값 설정 → 인자 생략 시 기본값 사용
  • 자주 쓰는 파라미터는 기본값을 설정해두면 편리

OpenAI 연동 유틸 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def make_message(role_value, content_value):
    """OpenAI 메시지 딕셔너리 생성"""
    message = {'role': role_value, 'content': content_value}
    return message

# 사용
msg = make_message("user", "비밀번호가 없습니다")
# {'role': 'user', 'content': '비밀번호가 없습니다'}

# 대화 구성에 활용
messages = [
    make_message("system",    "당신은 IT 상담원입니다"),
    make_message("user",      "비밀번호를 잊어버렸어요"),
    make_message("assistant", "비밀번호 재설정 안내드립니다"),
]
  • 반복되는 딕셔너리 생성을 함수로 묶으면 코드가 간결해짐
  • 실무에서 자주 쓰는 패턴

7. 예외 처리 (try / except)

아래의 포괄적인 except는 문법을 익히기 위한 예제다. 실제 처리 코드에서는 예상한 예외를 구분하고 원인을 남긴다. 오류를 빈 문자열로 바꾸면 뒤에서 정상적인 빈 응답과 실패를 구분하기 어려워진다.

기본 try / except

1
2
3
4
try:
    result = 10 / 0          # ZeroDivisionError 발생!
except:
    print("에러 발생")        # 에러 시 이 코드 실행

config에서 없는 키 안전하게 접근

1
2
3
4
5
6
7
8
9
10
config = {'model': 'gpt-4o-mini'}

# .get() 방법
temperature = config.get('temperature', 0.5)   # 없으면 0.5

# try/except 방법
try:
    temperature = config['temperature']
except KeyError:
    temperature = 0.5   # 기본값
  • .get(key, default) 와 try/except 두 가지 방법 모두 가능
  • 단순한 경우는 .get() 이 간결하고, 복잡한 로직은 try/except 사용

8. 파일 입출력

텍스트 파일 쓰기 / 읽기

1
2
3
4
5
6
7
8
# 쓰기 (w 모드)
with open('data.txt', 'w', encoding='utf-8') as f:
    f.write("안녕하세요")

# 읽기 (r 모드)
with open('data.txt', 'r', encoding='utf-8') as f:
    content = f.read()
print(content)   # "안녕하세요"
  • with open() 으로 파일 열기 → with 블록 끝나면 자동으로 파일 닫힘
  • encoding=’utf-8’ 한글 처리 시 필수

JSON 파일 저장/로드

1
2
3
4
5
6
7
8
9
10
11
import json

data = {"model": "gpt-4o-mini", "messages": [...]}

# 저장
with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 로드
with open('data.json', 'r', encoding='utf-8') as f:
    loaded = json.load(f)
  • ensure_ascii=False : 한글이 \uXXXX 로 변환되지 않고 그대로 저장
  • indent=2 : 들여쓰기 2칸으로 보기 좋게 저장

디렉토리 생성

1
2
3
import os
os.makedirs('sample_output', exist_ok=True)
# exist_ok=True : 폴더가 이미 있어도 에러 없이 넘어감

9. OpenAI 메시지 구조와 파이썬 자료구조 연결

오늘 배운 파이썬 → OpenAI API 어디에 쓰이나?

파이썬 자료구조OpenAI API에서 사용
딕셔너리 (dict)메시지 하나 {“role”: “user”, “content”: “…”}
리스트 (list)messages 배열 [{…}, {…}, {…}]
f-string프롬프트 동적 생성 f”당신은 {role} 전문가입니다”
함수 (def)API 호출 재사용 함수 def ask_gpt(question): …
for 루프여러 질문 반복 처리
.get()response에서 안전하게 값 꺼내기

실전 통합 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from openai import OpenAI
client = OpenAI()

def ask_gpt(role, question):
    """역할 기반 LLM 호출 함수"""
    messages = [
        {"role": "system", "content": f"당신은 {role}입니다."},
        {"role": "user",   "content": question}
    ]
    response = client.chat.completions.create(
        model    = "gpt-4o-mini",
        messages = messages
    )
    return response.choices[0].message.content

# 여러 역할로 같은 질문 물어보기
roles = ["수학 선생님", "수의사", "요리사"]
question = "1+1은?"
for role in roles:
    answer = ask_gpt(role, question)
    print(f"[{role}]: {answer[:50]}")

10. 핵심 개념 총정리

개념핵심 내용
f-string동적 프롬프트 생성: f”당신은 {role}입니다”
.strip()앞뒤 공백 제거 (사용자 입력 정제)
enumerate()인덱스 포함 순회 (번호 출력 등에 활용)
keyword in item문자열 포함 여부 확인 (키워드 필터링)
딕셔너리OpenAI 메시지 형식 그 자체
.get(key, default)KeyError 방지 안전한 딕셔너리 접근
함수 def반복 코드를 묶어 재사용 가능하게
try/except에러 상황 안전하게 처리
with open()파일 안전하게 열기 (자동 닫힘)
json.dump/load딕셔너리를 파일로 저장하고 불러오기

11. 자주 나오는 실수 / 주의사항

실수 1: 리스트 인덱스 범위 초과

1
2
3
models = ['gpt-4o', 'gpt-4o-mini', 'claude-3']
models[5]   # IndexError! (인덱스 0~2 만 유효)
models[-1]  # ← 마지막 요소는 이렇게

실수 2: 딕셔너리에서 없는 키 직접 접근

1
2
3
config = {'model': 'gpt-4o-mini'}
config['temperature']   # KeyError!
config.get('temperature', 0.7)   # 0.7 (기본값 반환)

실수 3: f-string 중괄호 빠뜨리기

1
2
3
name = "홍길동"
print(f"안녕하세요, name 님!")   # 변수가 들어가지 않음!
print(f"안녕하세요, {name} 님!") # 올바른 코드

실수 4: 파일 인코딩 미지정

1
2
3
4
5
6
# 한글 파일 읽을 때 인코딩 빠뜨리면 UnicodeDecodeError!
with open('data.txt', 'r') as f:             # 위험
    content = f.read()

with open('data.txt', 'r', encoding='utf-8') as f:  # 안전
    content = f.read()

12. 함수의 입력과 실패를 명확하게 만들기

함수는 정상 입력에 대한 반환값뿐 아니라 빈 입력과 잘못된 입력의 처리 방식까지 정해 두는 편이 좋다. 같은 실패를 어떤 함수는 None, 다른 함수는 문자열로 반환하면 호출부의 조건문이 늘어난다.

문법 복습의 확인 기준도 단순 출력에서 한 단계 더 잡는다. 공백만 있는 문자열, 누락된 설정 키, 비어 있는 파일을 넣었을 때 결과를 설명할 수 있는지 확인하면 이후 데이터 처리 코드에도 같은 기준을 적용할 수 있다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

인기 태그