프로그래밍의 세계에는 아주 떳떳한 도둑질이 하나 있습니다.
남이 만든 코드를 몰래 훔쳐 써도, 아무도 잡아가지 않습니다. 경찰도 안 부릅니다. 오히려 다들 그렇게 하라고 등 떠밉니다.
이 도둑질의 이름이 import입니다.
사실 여러분은 이미 도둑질을 하고 있었습니다. print(), range(), str()… 이것들 전부 파이썬이 미리 훔쳐다가 여러분 손에 공짜로 쥐여준 것들입니다. 이번 강의에서는 그 도둑질을 여러분 손으로 직접 해봅니다.
본격적으로 훔치기 전에, 파이썬이 원래부터 공짜로 쥐여주던 함수들을 몇 개 더 알아봅시다. 이런 걸 내장 함수(built-in function)라고 부릅니다. 08강에서 print()가 사실 내장 함수였다고 배웠죠. 그 친구들이 몇 명 더 있습니다.
리스트나 문자열의 길이(개수)를 알려줍니다.
items = ["foo", "bar", "baz"]
print(len(items))
word = "안녕하세요"
print(len(word))
결과:
3
5
리스트는 요소 개수, 문자열은 글자 수를 셉니다. 여러분이 직접 하나씩 셀 필요 없습니다. 파이썬이 대신 세줍니다. 07강에서 range()가 대신 숫자를 세줬던 것과 같은 정신입니다.
값의 종류(타입)를 알려줍니다. 08강에서 “숫자랑 문자열은 종류가 다르다”고 했던 그 종류를 직접 확인할 수 있습니다.
print(type(10))
print(type("안녕"))
print(type(3.14))
print(type(True))
결과:
<class 'int'>
<class 'str'>
<class 'float'>
<class 'bool'>
int(정수), str(문자열), float(소수), bool(참/거짓). 뭔가 헷갈리는 값을 만났을 때, type()으로 찍어보면 정체를 알 수 있습니다. 몸값 확인하듯이요.
사용자한테서 값을 직접 입력받습니다.
name = input("이름을 입력하세요: ")
print("안녕하세요, " + name + "님!")
결과(사용자가 “철수”라고 입력했다고 가정):
이름을 입력하세요: 철수
안녕하세요, 철수님!
지금까지는 코드 안에 값을 미리 다 박아놨습니다(name = "철수"처럼요). input()을 쓰면 프로그램이 실행되는 그 순간, 진짜 사람한테 물어보고 답을 받습니다. 참고로 input()이 돌려주는 값은 항상 문자열입니다. 숫자를 입력받고 싶으면 08강에서 배운 반대 방향의 변환, int()를 씌워야 합니다.
리스트를 다룰 때 자주 쓰는 4형제입니다.
numbers = [5, 2, 8, 1, 9]
print(sorted(numbers))
print(max(numbers))
print(min(numbers))
print(sum(numbers))
결과:
[1, 2, 5, 8, 9]
9
1
25
이런 걸 여러분이 직접 만들려면 07강에서 배운 for문 돌려가면서 하나하나 비교해야 합니다. 근데 이미 파이썬이 다 만들어놨습니다. 바퀴를 재발명할 필요 없습니다. 남이 이미 굴려놓은 바퀴에 그냥 올라타면 됩니다.
방금 배운 len(), type() 같은 애들은 부르는 데 아무 준비가 필요 없습니다. 파이썬을 켜는 순간부터 이미 옆에 대기하고 있습니다.
근데 세상에는 파이썬이 매번 다 켜놓기엔 너무 많고 다양한 기능들이 있습니다. 그런 건 미리 켜두지 않고, 서랍 속에 넣어뒀다가 필요할 때 여러분이 직접 꺼내 쓰게 합니다. 이 서랍 하나하나를 모듈(module)이라고 부르고, 파이썬이 기본으로 갖고 있는 서랍들을 통틀어 표준 라이브러리(standard library)라고 부릅니다.
서랍을 꺼내는 주문이 바로 import입니다.
수학 관련 기능이 들어있는 서랍입니다.
import math
print(math.sqrt(16))
print(math.pi)
print(math.ceil(4.2))
print(math.floor(4.8))
결과:
4.0
3.141592653589793
5
4
import math라고 한 줄 쓰면, math라는 서랍이 통째로 열립니다. 그 안에 있는 걸 쓸 때는 math.sqrt(16)처럼 서랍이름.물건이름 형태로 씁니다. sqrt는 제곱근, pi는 원주율, ceil/floor는 올림/버림입니다. 05강에서 아벨-루피니 정리 얘기하면서 5차방정식은 근호로 못 푼다고 했었죠. 그 “근호”가 여기 sqrt입니다. 그때 그 각주가 그냥 있던 게 아니었습니다.
무작위 값을 만들어주는 서랍입니다.
import random
print(random.randint(1, 10))
print(random.choice(["가위", "바위", "보"]))
결과(실행할 때마다 달라집니다):
7
바위
randint(1, 10)은 1부터 10 사이(양쪽 다 포함) 숫자 중 아무거나 하나. choice(...)는 리스트 중 아무거나 하나 골라줍니다. 이걸로 가위바위보 게임을 만들 수 있겠죠. 숙제로 남겨두겠습니다. (제가 낸 숙제, 제가 확인 안 합니다.)
날짜와 시간을 다루는 서랍입니다.
import datetime
now = datetime.datetime.now()
print(now)
print(now.year)
결과(실행하는 그 순간의 실제 날짜가 나옵니다):
2026-09-03 14:22:07.123456
2026
datetime.datetime.now()처럼 이름이 두 번 겹쳐 나오는 게 좀 이상하게 생겼습니다. datetime이라는 서랍 안에, 또 datetime이라는 이름의 물건(클래스입니다, 09강에서 배운 그 클래스요)이 들어있어서 그렇습니다. 서랍 이름이랑 그 안 물건 이름이 우연히 겹친 겁니다. 만든 사람도 이게 좀 헷갈리는 거 압니다. 근데 이미 다들 이렇게 쓰고 있어서 이제 와서 못 바꿉니다.
매번 math.sqrt, math.pi처럼 서랍 이름을 붙이기 귀찮으면, 필요한 것만 미리 콕 집어서 꺼내둘 수 있습니다.
from math import sqrt, pi
print(sqrt(16))
print(pi)
결과:
4.0
3.141592653589793
from math import sqrt, pi라고 하면, 서랍 전체를 여는 게 아니라 그 안의 sqrt랑 pi만 쏙 꺼내서 마치 원래 내 것이었던 것처럼 바로 씁니다. 편하긴 한데, 이렇게 너무 많이 꺼내다 보면 나중에 “이 함수가 어느 서랍에서 온 거였지” 헷갈릴 수 있습니다. 편함과 명확함은 항상 이렇게 조금씩 싸웁니다.
서랍 이름이 길거나 마음에 안 들면 별명을 붙일 수 있습니다.
import datetime as dt
now = dt.datetime.now()
print(now.year)
as dt라고 하면 그 뒤로는 datetime 대신 dt라고만 써도 됩니다. 타자를 아끼는 겁니다. 05강에서 대입 연산자(+=) 배울 때도 결국 게으름의 미학이라고 했었죠. 이 강의는 게으름을 참 좋아합니다.
표준 라이브러리는 파이썬을 설치하면 그냥 딸려 옵니다. 근데 세상 모든 기능을 파이썬 혼자 다 만들어둘 순 없습니다. 그래서 전 세계 사람들이 만들어서 공개해둔 외부 라이브러리라는 게 따로 있습니다.
02강에서 개발 환경을 준비할 때, pip라는 도구 얘기가 잠깐 나왔던 거 기억나시나요? 그때는 “외워주십시오”라고만 하고 넘어갔습니다. 이게 바로 외부 라이브러리를 설치하는 도구입니다. 오늘 그 떡밥을 회수합니다.
터미널(02강에서 열었던 그 검은 화면)에 이렇게 쓰면 됩니다.
{python} -m pip install sh
sh는 원래 파이썬 표준 라이브러리에 있는 subprocess(터미널 명령어를 파이썬 코드 안에서 실행하는 서랍)를 대신해서 터미널 명령어를 훨씬 편하게 실행하게 해주는, 아주 유명한 외부 라이브러리입니다. 설치가 끝나면, 표준 라이브러리 쓸 때랑 완전히 똑같이 import하면 됩니다.
import sh
print(sh.echo("안녕하세요"))
print(sh.ls())
결과:
안녕하세요
01-intro.html
02-setup.html
03-hello.html
...
원래 subprocess로 이런 걸 하려면 subprocess.run(["echo", "안녕하세요"], capture_output=True, text=True).stdout처럼 한 줄이 길고 복잡해집니다. sh를 쓰면 터미널 명령어(echo, ls)가 마치 파이썬 함수였던 것처럼, sh.echo(...), sh.ls()로 그냥 부르면 됩니다. 터미널 명령어를 파이썬 함수로 둔갑시켜주는 라이브러리인 셈입니다.
존재하지 않는 명령어를 부르면 어떻게 될까요?
import sh
sh.this_command_does_not_exist()
결과:
sh.CommandNotFound: this_command_does_not_exist
(참고로 sh는 리눅스/macOS 터미널 명령어를 기준으로 만들어졌습니다. Windows에서는 좀 다르게 동작하거나 아예 안 될 수 있습니다. 02강에서 WSL을 굳이 깔게 시킨 이유가 이런 데서 슬슬 드러납니다.)
다른 것도 하나 더 설치해봅시다.
{python} -m pip install colorama
from colorama import Fore
print(Fore.RED + "빨간 글씨입니다")
print(Fore.GREEN + "초록 글씨입니다")
결과(터미널에서 실제로는 색깔이 입혀져서 보입니다):
빨간 글씨입니다
초록 글씨입니다
colorama는 터미널 글씨에 색을 입혀주는 라이브러리입니다. 표준 라이브러리에 없는 기능인데, 누군가 만들어서 전 세계 사람들이 갖다 쓰고 있습니다. 여러분도 지금 막 그 대열에 합류했습니다.
정리하면, 서랍은 세 종류입니다.
내장 함수 → import 자체가 필요 없음 (print, len, type...)
표준 라이브러리 → 파이썬 설치하면 같이 옴 (math, random, datetime...)
외부 라이브러리 → pip install로 따로 설치해야 함 (sh, colorama...)
방금 sh로 없는 명령어를 부르면 CommandNotFound가 난다고 슬쩍 흘렸었죠. 사실 지금까지 이 강의에서 에러는 전부 나쁜 놈 취급을 받았습니다. IndentationError, TypeError, NameError… 나올 때마다 “Python이 화냅니다”로 넘어갔습니다.
근데 진짜 프로그램을 짜다 보면, 에러가 날 수도 있다는 걸 미리 알면서도 프로그램을 계속 돌리고 싶을 때가 있습니다. 사용자가 숫자 대신 이상한 걸 입력할 수도 있고, 실행하려던 명령어가 없을 수도 있고, 파일이 없을 수도 있습니다. 그때마다 프로그램이 통째로 죽어버리면 곤란합니다.
그래서 오늘은 도둑질에 이어 두 번째 주제, 예외 처리도 같이 배웁니다.
일단 에러가 실제로 프로그램을 어떻게 죽이는지부터 봅시다.
number = int("안녕")
print("여기까지 왔으면 성공입니다")
결과:
ValueError: invalid literal for int() with base 10: '안녕'
"안녕"을 숫자로 바꾸려니 파이썬이 화를 냅니다. 그리고 그 순간 프로그램이 그냥 멈춰버립니다. 바로 아래 있던 print("여기까지 왔으면 성공입니다")는 실행조차 안 됩니다. 에러 한 줄이 프로그램 전체를 끌고 죽어버린 겁니다. 사원 한 명 잘못이 회사 전체 문 닫는 걸로 이어진 셈입니다.
이걸 막으려면 try와 except를 씁니다. “일단 시도해보고(try), 문제가 생기면(except) 이렇게 해라”라는 뜻입니다.
try:
number = int("안녕")
except ValueError:
print("숫자로 못 바꿉니다")
print("여기까지 왔으면 성공입니다")
결과:
숫자로 못 바꿉니다
여기까지 왔으면 성공입니다
try 블록 안에서 에러가 나면, 프로그램이 죽는 대신 except 블록으로 넘어갑니다. except 뒤에 ValueError라고 써서, “이 종류의 에러가 나면 이렇게 처리해라”라고 콕 집어 알려줬습니다. 이 종류를 안 맞추면 어떻게 될까요?
try:
number = int("안녕")
except TypeError:
print("타입 에러입니다")
결과:
ValueError: invalid literal for int() with base 10: '안녕'
except TypeError라고 써놨는데, 실제로 난 건 ValueError라서 못 잡습니다. 그냥 프로그램이 그대로 죽습니다. 방화복을 입었는데 정작 물난리가 난 격입니다. 재해 종류를 맞게 준비해야 합니다.
여러 종류의 에러를 각각 다르게 처리하고 싶으면 except를 여러 개 쓸 수 있습니다. 06강에서 배운 elif랑 비슷한 정신입니다.
try:
number = int(input("숫자를 입력하세요: "))
result = 10 / number
print(result)
except ValueError:
print("숫자가 아닙니다")
except ZeroDivisionError:
print("0으로는 못 나눕니다")
"안녕"을 입력하면 ValueError가 잡히고, 0을 입력하면 ZeroDivisionError가 잡힙니다. 위에서부터 순서대로 확인하는 것도, 06강에서 본 elif 흐름이랑 똑같습니다.
무슨 에러인지 정확히 모르거나, 아무 에러든 다 잡고 싶으면 그냥 except만 써도 됩니다.
try:
number = int("안녕")
except:
print("뭔가 잘못됐습니다")
결과:
뭔가 잘못됐습니다
편하긴 한데, 이렇게 하면 무슨 에러가 나든 다 조용히 삼켜버립니다. 진짜 심각한 문제(오타, 로직 실수)까지 다 숨겨버릴 수 있어서, 웬만하면 종류를 콕 집어 쓰는 걸 추천합니다. 모든 화재에 그냥 물부터 뿌리고 보면, 전기 화재엔 오히려 더 위험한 것과 같습니다.
try, except 말고 두 가지가 더 있습니다.
try:
number = int("42")
except ValueError:
print("숫자가 아닙니다")
else:
print("성공적으로 변환됐습니다:", number)
finally:
print("이건 무조건 실행됩니다")
결과:
성공적으로 변환됐습니다: 42
이건 무조건 실행됩니다
지금까지는 파이썬이 알아서 에러를 냈습니다. 근데 여러분이 직접 “이건 에러다”라고 선언할 수도 있습니다.
def set_age(age):
if age < 0:
raise ValueError("나이는 음수일 수 없습니다")
print("나이:", age)
set_age(-5)
결과:
ValueError: 나이는 음수일 수 없습니다
raise로 원하는 순간에 원하는 에러를 직접 터뜨릴 수 있습니다. 06강에서 if로 조건을 확인했던 것처럼, “이 조건이면 이건 정상적인 상황이 아니다”라고 판단되면 여기서 미리 알려주는 겁니다. 나중에 이 함수를 부르는 다른 사람이 try/except로 이 에러를 잡아서 처리하면 됩니다.
오늘은 두 가지를 배웠습니다. 도둑질(import)과 사고 수습(예외 처리)입니다.
내장 함수도 몇 개 더 배웠고(len, type, input, sorted, max, min, sum), 표준 라이브러리도 배웠고(math, random, datetime), from ... import도 배웠고, as로 별명 붙이는 법도 배웠고, pip install로 외부 라이브러리 설치하는 법도 배웠고, try, except, else, finally, raise까지 배웠습니다.
이제 여러분은 남의 코드를 훔쳐 쓸 줄도 알고, 뭔가 잘못됐을 때 프로그램이 그대로 죽지 않게 수습하는 법도 압니다. 도둑질과 뒷수습을 한 강의에서 같이 배운 셈입니다. 뭔가 떳떳하지 못한 조합인데, 둘 다 실전에서 정말 많이 씁니다.
마법입니다.
지금까지 강의로 나온 건 여기까지입니다. 다음은 11, 실습입니다. 배운 걸로 직접 뭔가 만들어볼 차례입니다.