Regex Basics

re.sub 한 줄에서 시작하는 정규표현식 기초

  • Haram Lee
  • 2026-10-06
  • memo

코드 리뷰에서 re.sub 한 줄을 만나 정규표현식을 다시 정리했다. 정규표현식은 파이썬 re, BigQuery의 REGEXP_* 함수, 로그 검색, grep에서 거의 같은 문법으로 쓰인다. 한 번 익혀 두면 여러 곳에서 쓸 수 있다.

출발점: 값을 규칙에 맞게 고치는 한 줄

python
import re

safe = re.sub(r'[^A-Za-z0-9_.-]', '_', name)[:63].strip('_.-')

쿠버네티스 라벨 값에는 63자 이하여야 하고, 영문자, 숫자, _, ., -만 쓸 수 있으며, 양 끝은 영숫자여야 한다는 규칙이 있다. 이 코드는 이런 규칙이 있는 곳에 임의의 이름을 넣기 전에 쓴다.

  1. re.sub(패턴, 바꿀 문자열, 원본)은 원본에서 패턴에 맞는 부분을 모두 바꾼다. [^A-Za-z0-9_.-]는 “영문자, 숫자, _, ., -가 아닌 문자 하나"라서 허용되지 않는 문자가 전부 _로 바뀐다.
  2. [:63]은 정규표현식이 아니라 문자열 자르기다.
  3. .strip('_.-')도 정규표현식이 아니다. 양 끝의 _, ., -를 떼어 낸다.
python
'my report/2026'  → 'my_report_2026'
'-draft.'         → 'draft'
'a' * 70          → 'a' * 63

정규표현식이 하는 일은 1번뿐이다. 정규표현식을 읽을 때는 어디까지가 패턴인지부터 구분하면 덜 헷갈린다.

패턴 문법

문자 하나 고르기

패턴뜻
a글자 a 그대로
.줄바꿈을 뺀 아무 문자 하나
[abc]대괄호 안 문자 중 하나
[a-z]범위 안 문자 하나
[^abc]대괄호 안 문자가 아닌 것 하나
\d숫자 하나
\w영문자, 숫자, _ 하나
\s공백 문자 하나 (스페이스, 탭, 줄바꿈)
\D, \W, \S각각 위의 반대
  • ^는 대괄호 맨 앞에서만 “아닌 것"이다. 대괄호 밖에서는 “문자열 시작"이다.
  • 대괄호 안에서 -는 범위를 만든다. 글자 -를 넣으려면 맨 끝에 둔다.
  • ., *, ( 같은 특수 문자를 글자로 찾으려면 앞에 \를 붙인다(\.). 대괄호 안의 .은 특수 문자가 아니어서 \가 필요 없다.

반복

패턴뜻
*0번 이상
+1번 이상
?0번 또는 1번
{3}정확히 3번
{2,5}2번에서 5번

반복 기호는 바로 앞 한 덩어리에만 붙는다. ab+는 a 뒤에 b가 1번 이상이고, (ab)+는 ab가 1번 이상이다.

위치

패턴뜻
^문자열 시작
$문자열 끝
\b단어 경계

묶기와 고르기

패턴뜻
(...)묶고, 맞은 부분을 따로 꺼낼 수 있게 저장
(?P<name>...)저장한 부분에 이름 붙이기 (파이썬 문법)
(?:...)묶기만 하고 저장하지 않음
a|ba 또는 b

파이썬 re 함수

함수하는 일결과
re.search(p, s)어디서든 처음 맞는 곳 하나Match 또는 None
re.match(p, s)맨 앞에서만 확인Match 또는 None
re.fullmatch(p, s)문자열 전체가 맞는지Match 또는 None
re.findall(p, s)맞는 곳 전부문자열 목록 (묶음이 있으면 묶음 내용)
re.sub(p, r, s)맞는 곳 전부 바꾸기새 문자열
re.split(p, s)패턴을 구분자로 나누기문자열 목록
re.compile(p)패턴을 미리 만들어 재사용패턴 객체

match와 search를 자주 헷갈린다.

python
re.match(r'\d+', 'abc123')           # None  (맨 앞이 숫자가 아님)
re.search(r'\d+', 'abc123').group()  # '123'

패턴 앞의 r은 raw string 표시다. r 없이 '\b'를 쓰면 파이썬이 먼저 백스페이스 문자로 해석한다. 정규표현식은 항상 r'...'로 쓴다.

바로 쓸 수 있는 예

로그 줄에서 요청 id 꺼내기. 묶음 하나를 쓰고 group(1)으로 꺼낸다.

python
line = '2026-10-18 09:12:03 ERROR request_id=req-7f3a9c failed after 3 retries'
re.search(r'request_id=(\S+)', line).group(1)
# 'req-7f3a9c'

여러 id를 한꺼번에 꺼내기.

python
re.findall(r'app-(\d{8}-\d{4})', 'started app-20261018-0001, retry app-20261018-0002')
# ['20261018-0001', '20261018-0002']

이름 붙은 묶음으로 날짜와 시각 꺼내기.

python
m = re.search(r'(?P<date>\d{4}-\d{2}-\d{2}) (?P<hour>\d{2}):', line)
m.group('date'), m['hour']
# ('2026-10-18', '09')

값을 고치지 않고 규칙에 맞는지만 검사하기. 첫 글자는 영숫자, 가운데는 허용 문자 61자 이하, 끝 글자는 영숫자여야 한다. 한 글자짜리 값도 통과하도록 가운데와 끝을 ?로 묶었다.

python
NAME = r'[A-Za-z0-9]([A-Za-z0-9_.-]{0,61}[A-Za-z0-9])?'
bool(re.fullmatch(NAME, 'my_report_2026'))  # True
bool(re.fullmatch(NAME, '-bad'))            # False
bool(re.fullmatch(NAME, 'x'))               # True

camelCase를 snake_case로 바꾸기. 바꿀 문자열의 \1, \2는 첫째, 둘째 묶음에 맞은 내용이다.

python
re.sub(r'([a-z])([A-Z])', r'\1_\2', 'createdAt').lower()
# 'created_at'

여러 종류의 구분자로 나누기.

python
re.split(r'[,\s]+', 'a, b  c,d')
# ['a', 'b', 'c', 'd']

자주 하는 실수

욕심 많은 반복. *와 +는 가능한 한 길게 맞춘다. 뒤에 ?를 붙이면 가능한 한 짧게 맞춘다.

python
re.sub(r'<.+>',  '', '<a>x<b>y')   # 'y'   (<a>x<b> 전체가 지워짐)
re.sub(r'<.+?>', '', '<a>x<b>y')   # 'xy'  (<a>, <b>만 지워짐)

.을 글자로 착각하기. .은 아무 문자 하나다.

python
bool(re.search(r'example.com',  'exampleXcom'))  # True
bool(re.search(r'example\.com', 'exampleXcom'))  # False

부분 일치를 전체 일치로 착각하기. re.search(r'\d+', 'abc123')이 맞았다고 값이 숫자로만 이뤄진 것은 아니다. 검증은 fullmatch로 한다.

다른 도구에서 쓸 때

뼈대(문자 고르기, 반복, 위치, 묶기)는 어디서나 같다. 차이는 고급 기능에 있다.

도구엔진주의할 점
파이썬 re자체 엔진기능이 가장 넓다
BigQuery REGEXP_*RE2lookahead 같은 앞뒤 살피기와 \1 역참조가 없다
Grafana Loki LogQL |~RE2BigQuery와 같다. 라벨 매칭 =~는 값 전체와 맞아야 한다
grep기본, -E, -P기본 모드에서는 +, ?, |, ()에 \가 필요하다. -E를 쓰면 파이썬과 비슷하다

(?i)는 대소문자를 구분하지 않는다는 표시로, RE2와 파이썬 모두에서 쓸 수 있다. 파이썬의 re.IGNORECASE 플래그와 같다.

python
re.findall(r'(?i)error|timeout', 'ERROR x Timeout y')
# ['ERROR', 'Timeout']

연습할 곳

  • 파이썬 셸에서 import re로 바로 해 본다.
  • regex101.com에 패턴과 문자열을 넣으면 각 부분이 무엇과 맞았는지 색으로 보여 준다. 엔진을 Python으로 고를 수 있다.
Discussion