codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›tools

정규표현식 — 패턴으로 문자열을 찾기

2026-04-28 게시· 2026-05-18 갱신·0회 조회

정규표현식 — 패턴으로 문자열을 찾기

"이 텍스트에서 이메일만 뽑아 줘" 같은 요청이 들어오면 가장 먼저 떠오르는 도구가 정규표현식 (regex). 짧은 문자열로 강한 표현력을 발휘하지만, 같은 이유로 처음 익힐 때는 외계 문자처럼 보입니다. 이 글은 정규표현식의 출자 · 핵심 메타문자 · 자주 쓰는 패턴 · 언어별 차이 · "완벽한 정규식" 의 함정.

1. 정규표현식에 대한 이야기

뿌리는 1956 년 수학자 Stephen Kleene 의 정규 집합 (regular set) 표기. 1968 년 Bell Labs 의 Ken Thompson 이 텍스트 에디터 QED 에 처음 정규식 검색을 넣었고, 같은 해 발표한 논문이 정규식 → NFA 변환 알고리즘의 표준이 됐습니다. 이후 1973 년 grep, 1980 년대 sed · awk, 1987 년 Perl 이 정규식을 보편화.

오늘날 정규식 문법은 크게 두 갈래:

갈래 출자 어디서
POSIX BRE / ERE 1986 POSIX 표준 전통 grep · sed
PCRE (Perl Compatible) 1987 Perl, 1997 PCRE 라이브러리 거의 모든 모던 도구

JS · Python · Java · .NET · Go · Rust · Ruby 모두 PCRE 와 비슷하지만 미묘한 차이. 같은 패턴이 한 언어에서 동작하고 다른 언어에서 안 동작하는 일이 흔합니다.

2. 메타문자

문자 의미
. 줄바꿈을 제외한 임의의 한 글자
^ · $ 줄 (또는 문자열) 의 시작 · 끝
* 직전 토큰 0회 이상
+ 직전 토큰 1회 이상
? 직전 토큰 0회 또는 1회
{n} · {n,m} 정확히 n회 · n~m회
[abc] 문자 클래스 (셋 중 하나)
[^abc] 부정 문자 클래스 (셋이 아닌 것)
[a-z] 범위
\d · \D 숫자 · 비숫자
\w · \W 단어 문자 ([A-Za-z0-9_]) · 그 외
\s · \S 공백 · 비공백
\b · \B 단어 경계 · 비경계
() 그룹 (캡처)
(?:) 비캡처 그룹
(?<name>) 이름 있는 그룹
| 또는 (alternation)
\ 이스케이프

3. 욕심 · 게으름

* · + · ? 는 기본적으로 욕심 (greedy). 가능한 한 길게 매칭. *? · +? · ?? 를 쓰면 게으름 으로 바뀌어 가능한 한 짧게:

"<b>hello</b>"

<.*>     → <b>hello</b>   (전체)
<.*?>    → <b>            (가장 짧은 한 묶음)

HTML 태그처럼 가까운 짝을 잡고 싶을 때 *? · +? 가 자주 등장.

4. 그룹과 역참조

패턴: (\w+)\s+\1
대상: "ho ho"
매칭: "ho ho"      (\1 이 첫 그룹과 같은 텍스트)

치환 시에도 그룹을 참조:

"2025-04-25".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1");
// "25/04/2025"

5. Lookahead · Lookbehind

매칭은 하지 않고 조건만 검사하는 도구.

표기 의미
(?=X) 뒤에 X 가 옴 (긍정 lookahead)
(?!X) 뒤에 X 가 안 옴 (부정 lookahead)
(?<=X) 앞에 X 가 옴 (긍정 lookbehind)
(?<!X) 앞에 X 가 안 옴 (부정 lookbehind)
패턴: \d+(?=원)
대상: "1500원"
매칭: "1500"   (뒤에 "원" 이 있는 숫자만, "원" 자체는 매칭에서 제외)

lookbehind 는 일부 언어가 가변 길이를 허용하지 않음. JS 는 2018 년부터 지원, Python re 는 고정 길이만, regex 라이브러리는 가변 허용.

6. 플래그

플래그 의미
i 대소문자 무시
g 전역 매칭 (모든 출현)
m 다중 줄. ^ · $ 가 줄 단위로
s dotall. . 이 줄바꿈도 매칭
u 유니코드. 코드포인트 단위
x 확장 (공백 · 주석 허용, 파이썬 · PCRE)

JS g 는 String.prototype.replaceAll · matchAll 같은 모든-매칭 동작에 필요.

7. 다른 길

같은 일을 정규식 외 도구로:

  • CSV / JSON 파싱 — 구조화된 형식은 정규식보다 전용 파서가 항상 안전.
  • HTML / XML 파싱 — 정규식으로 잘라 쓰면 무너짐. cheerio · BeautifulSoup · DOMParser 같은 파서.
  • 자연어 검색 — 길어지는 정규식보다 검색엔진 (Elasticsearch · Postgres 풀텍스트) 이 더 적절한 자리.
  • PEG · 파서 컴비네이터 — 더 복잡한 문법은 정규식의 영역이 아님.

8. 자주 쓰는 모양

기본 이메일 · URL · 전화번호 패턴은 "대충" 만 잡습니다. 완벽한 정규식은 거의 불가능에 가까움.

간단한 이메일 (대략):
^[\w.+-]+@[\w-]+\.[\w.-]+$

URL (대략):
\bhttps?://[^\s)]+

한국 휴대폰 (대략):
01[016789]-?\d{3,4}-?\d{4}

YYYY-MM-DD:
\b\d{4}-\d{2}-\d{2}\b

빈 줄:
^\s*$

ANSI 색상 코드 제거:
\x1b\[[0-9;]*m

언어별:

// JavaScript
const re = /^\d{4}-\d{2}-\d{2}$/;
re.test("2025-04-25");                  // true
"a-1, b-2".match(/[a-z]-\d/g);          // ["a-1", "b-2"]
"hello".replace(/l/g, "L");             // "heLLo"
# Python
import re
re.match(r"^\d{4}-\d{2}-\d{2}$", "2025-04-25")
re.findall(r"[a-z]-\d", "a-1, b-2")
re.sub(r"l", "L", "hello")
pat = re.compile(r"\d+", re.IGNORECASE)
// Java
Pattern p = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
Matcher m = p.matcher("2025-04-25");
m.find();
"hello".replaceAll("l", "L");
# grep · sed (mac · Linux)
grep -E "^[a-z]+@[a-z]+\.[a-z]+$" emails.txt
sed -E 's/[0-9]+/N/g' file.txt

# Windows PowerShell
Get-Content file.txt | Select-String -Pattern "ERROR"
"hello" -replace "l", "L"

9. 자주 걸리는 자리

이메일 / URL 의 완벽한 정규식은 사실상 불가능 — RFC 5321 / 5322 에 따른 진짜 이메일 정규식은 수백 자. "대략 잡고 실제 발송 또는 라이브러리 검증" 이 실용적.

HTML 정규식 파싱 — Stack Overflow 의 한 답변이 이 함정을 잘 풍자. 파서 사용.

catastrophic backtracking — (a+)+$ 같은 중첩 quantifier 가 입력에 따라 지수 시간 폭발. ReDoS 보안 취약점이 자주 여기서. 가능한 한 lookbehind / atomic group / 소유 quantifier 또는 RE2 같은 선형 시간 엔진.

. 의 줄바꿈 — 기본은 매칭 안 함. 다중 줄 텍스트를 .* 로 잡으려면 s 플래그 또는 [\s\S]*.

유니코드 — \w 가 ASCII 만이라 한글이 빠짐. JS 는 u 플래그, Python 은 기본이 유니코드, Java 는 (?U) 또는 Pattern.UNICODE_CHARACTER_CLASS.

이스케이프 — 언어 문자열 리터럴에서 한 번, 정규식에서 한 번. 두 번 이스케이프해야 하는 경우. Python 은 raw string r"\d", JS 는 정규식 리터럴 /\d/.

POSIX vs PCRE 차이 — \d 가 POSIX BRE 에서 안 통함. 전통 grep 에서는 [0-9] 또는 grep -E 의 ERE 에서 [[:digit:]].

하고픈 말

정규식은 짧은 문자열로 큰 표현력을 주는 도구. 단 "완벽한" 패턴을 추구하면 catastrophic backtracking · RFC 충돌 · 유니코드 · 언어별 차이의 늪. 대충 잡고 실제 검증은 라이브러리 가 운영 안전한 기본 자세. HTML · 자연어 · 깊은 구조에는 정규식 대신 파서 또는 검색엔진.

Next

  • git-submodule-lfs
  • (tools 끝)

regex101.com · regexr.com · RegexLearn · MDN Regular Expressions · Python re 문서 · Java Pattern · Russ Cox — Regular Expression Matching Can Be Simple And Fast · RE2 · OWASP ReDoS 를 참고합니다.

tools 카테고리의 다른 글

카테고리 전체 보기 →
  • Git Submodule · Subtree · LFS — 저장소 안에 다른 저장소
  • Python 의존성 도구의 역사
  • 린팅과 포매팅
  • 에디터 설정
  • Gradle
  • Git 워크플로