CASE-001
그렙 앤 코 데이터 탐정 사무소
의뢰서
의뢰인
PC방 "레벨업" 사장
증거물
결제 서버 로그. 손님 한 명이 4.99달러를 냈는데 기록이 없다고 항의하고 있다.
해결 조건
로그에서 오류가 난 줄을 찾고, 정확히 $4.99인 결제만 골라낸다.
첫 출근 날 아침, 소장이 커피잔을 내려놓으며 종이 한 장을 밀어 줍니다. "어제 PC방 사장이 왔다 갔어. 손님은 4.99달러를 냈다는데 기록이 없대. 로그는 거짓말을 안 해. 찾아봐."
로그는 이렇게 생겼습니다. 오늘은 이 다섯 줄에서 증거를 찾습니다.
2026-10-20 09:12:01 INFO PAY user=kim amount=$4.99 status=OK
2026-10-20 09:12:07 INFO PAY user=lee amount=$14.99 status=OK
2026-10-20 09:13:44 ERROR PAY user=park amount=$4.99 status=TIMEOUT
2026-10-20 09:15:02 INFO PAY user=choi amount=$4999 status=OK
2026-10-20 09:16:30 ERROR DB connection lost정규식(regular expression)은 찾고 싶은 글자의 모양을 적는 작은 언어입니다. 파이썬에서는 re 모듈로 씁니다. 가장 간단한 정규식은 그냥 글자입니다. ERROR라는 패턴은 ERROR라는 글자를 찾습니다.
import re
log = "2026-10-20 09:13:44 ERROR PAY user=park"
m = re.search("ERROR", log)
print(m) # <re.Match object; span=(20, 25), match='ERROR'>re.search(패턴, 문자열)은 문자열의 앞에서부터 패턴이 처음 나오는 곳을 찾습니다.None을 돌려줍니다.증거 분석실
re.search(r"", text)
돌려준 값
Match 객체는 "찾았다"는 증거이자, 무엇을 어디서 찾았는지 적힌 보고서입니다.
m = re.search("ERROR", log)
if m: # 찾았으면 참, None이면 거짓
print(m.group()) # ERROR (찾은 글자)
print(m.span()) # (20, 25) (시작 위치, 끝 위치)if m:처럼 바로 참·거짓으로 검사합니다. 못 찾으면 None이라 m.group()을 부르면 AttributeError가 납니다.span()의 끝 위치는 포함하지 않습니다. log[20:25]가 "ERROR"인 것과 같은 규칙입니다.대부분의 글자는 자기 자신을 찾습니다. 그런데 아래 14개는 정규식에서 특별한 뜻을 가진 메타문자입니다.
. ^ $ * + ? { } [ ] \ | ( )오늘은 그중 첫 번째, .만 배웁니다. .은 줄바꿈을 뺀 아무 글자 하나를 뜻합니다. 나머지는 앞으로 한 교시에 하나씩 꺼내 씁니다.
re.search("a.c", "abc") # 매치: b가 . 자리에
re.search("a.c", "a-c") # 매치: -도 아무 글자
re.search("a.c", "ac") # None: . 은 반드시 글자 하나를 차지한다
re.search("a.c", "a\nc") # None: 줄바꿈은 . 이 아니다도트의 한마디
저는 사무소의 책상, 의자, 소장님 키보드 위까지 아무 데나 올라가요. 딱 하나, 창문(줄바꿈)만은 못 넘어요. 그래서 이름이 도트(.)예요. 언젠가 누가 창문을 열어 주면 그때는 넘어갈 수 있대요.
증거 분석실
re.search(r"", text)
돌려준 값
이제 사건으로 돌아갑니다. 맨 위의 로그 다섯 줄이 pay_log라는 문자열에 들어 있다고 합시다. 4.99달러 결제를 찾으려고 4.99라고 쓰면 어떻게 될까요?
re.findall("4.99", pay_log) # ['4.99', '4.99', '4.99', '4999'] ← 네 개?두 개여야 할 결과가 네 개입니다. $14.99의 뒷부분 4.99가 잡혔고, .이 "아무 글자"라서 $4999의 4999까지 들어왔습니다. 메타문자를 글자 그대로 찾으려면 앞에 백슬래시 \를 붙입니다. \.은 진짜 마침표 하나입니다.
$도 메타문자입니다. 뜻은 4교시에 배우지만, 지금은 "그대로 쓰면 달러 기호를 찾지 않는다"는 것만 기억하세요. 그래서 정답은 \$4\.99입니다.
re.findall(r"\$4\.99", pay_log) # ['$4.99', '$4.99']증거 분석실
re.search(r"", text)
돌려준 값
위에서 r"\$4\.99"처럼 따옴표 앞에 r을 붙였습니다. 이것을 raw 문자열이라고 합니다.
백슬래시는 파이썬 문자열에서도 특별합니다. "\n"은 줄바꿈, "\t"는 탭이죠. 그래서 정규식을 일반 문자열로 쓰면 파이썬이 먼저 백슬래시를 해석해 버리고, re는 엉뚱한 패턴을 받습니다.
print(len("\b")) # 1 ← 파이썬이 \b 를 '백스페이스' 글자 하나로 바꿨다
print(len(r"\b")) # 2 ← 백슬래시와 b, 두 글자 그대로r"..."는 백슬래시를 손대지 않고 그대로 re에 넘깁니다. 정규식은 항상 raw 문자열로 씁니다."\d"처럼 파이썬이 모르는 이스케이프를 일반 문자열에 쓰면 SyntaxWarning이 납니다. raw 문자열이면 경고가 없습니다.PC방 사장이 "손님 아이디가 kim.99+인데 이것도 찾아 줘"라고 합니다. 이런 글자를 직접 패턴으로 쓰면 .과 +가 메타문자로 해석됩니다. 사람이 입력한 글자를 그대로 찾을 때는 re.escape가 메타문자 앞에 백슬래시를 붙여 줍니다.
print(re.escape("kim.99+")) # kim\.99\+
re.search(re.escape("kim.99+"), "user=kim.99+") # 매치정리하면, 오류가 난 줄은 ERROR로 찾고, 정확히 4.99달러인 결제는 \$4\.99로 찾습니다. 두 조건을 겹쳐 보면 손님 park의 결제가 TIMEOUT 오류로 기록되지 않은 것이 드러납니다.
for line in pay_log.splitlines():
if re.search("ERROR", line) and re.search(r"\$4\.99", line):
print(line)
# 2026-10-20 09:13:44 ERROR PAY user=park amount=$4.99 status=TIMEOUT사건 종결 보고서 — 결제 서버가 09:13:44에 시간 초과로 결제를 실패 처리했으나 손님 화면에는 완료로 표시됨. PC방 사장에게 환불을 권고함.
INFO인 줄만 모두 찾아보세요. 몇 개인가요?\$1.\.99는 무엇을 찾을까요? 분석실에서 확인하기 전에 먼저 예상해 보세요.re.escape("1+1=2? [yes]")의 결과를 예상해 보고 직접 실행해 보세요.소장의 수첩
대부분의 글자는 자기 자신을 찾는다. 메타문자 14개 . ^ $ * + ? { } [ ] \ | ( ) 만 특별하다.
. 은 줄바꿈을 뺀 아무 글자 하나. 진짜 마침표는 \.
re.search 는 Match 또는 None. if m: 으로 검사하고 m.group(), m.span() 으로 본다.
정규식은 항상 r"..." raw 문자열로.
사람이 입력한 글자를 그대로 찾을 때는 re.escape.
새 교시·새 강의 소식 받기
이 강의에 새 교시가 더해지거나 이어지는 강의가 열리면 이메일로 알려 드려요.
R
ReflexKR
눌러 보며 배우는 파이썬과 Reflex. 모든 예제는 Reflex 0.10.0에서 직접 실행해 확인했습니다.
ReflexKR은 배너 광고 없이 무료로 운영돼요. 도움이 되셨다면 커피 한 잔으로 응원해 주세요.
© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
질문과 답변
이 교시에서 궁금한 점을 남겨 주세요. 모든 언어 페이지의 질문이 함께 모입니다.