정규식 테스터
문자열에 대해 정규식을 테스트하고 모든 일치 항목 강조 표시
패턴
테스트 문자열
정규식이란 무엇인가?
정규식(regex 또는 regexp)은 검색 패턴을 정의하는 문자열입니다. 정규식 테스터를 사용하면 패턴을 작성하고, 샘플 텍스트에 실행하여, 모든 일치 항목이 실시간으로 강조 표시되는 것을 확인할 수 있습니다. 이 개념은 1950년대 수학자 스티븐 클리니(Stephen Kleene)의 정규 언어 연구로 거슬러 올라가며, 켄 톰슨(Ken Thompson)은 1968년 QED 텍스트 편집기에 최초의 정규식 엔진을 구현했습니다.
정규식 엔진은 패턴을 왼쪽에서 오른쪽으로 읽으며 일치를 시도하는 과정에서 입력 문자를 소비합니다. 부분 일치가 실패하면 백트래킹을 사용하여 이전 단계로 돌아가 패턴의 다른 경로를 시도합니다. Go에서 사용하는 RE2와 같은 일부 엔진은 패턴을 결정론적 유한 오토마타(DFA)로 변환하여 백트래킹을 완전히 피하는데, 이는 역참조(back-reference)와 같은 기능을 지원하지 않는 대신 선형 시간 일치를 보장합니다.
정규식 문법은 느슨하게 표준화되어 있습니다. PCRE(Perl Compatible Regular Expressions)가 가장 널리 사용되는 방식으로, PHP, Python의 re 모듈, 그리고 약간의 차이가 있는 JavaScript에서 지원됩니다. POSIX는 grep과 sed에서 사용하는 보다 제한적인 문법을 정의합니다. 언어 간에 패턴을 이식할 때 이 차이는 중요합니다. JavaScript에서 동작하는 전방 탐색(lookahead)이 Go의 RE2 엔진에서는 컴파일조차 되지 않을 수 있습니다.
온라인 정규식 테스터를 사용하는 이유
코드 파일에서 정규식을 작성하면 패턴을 수정할 때마다 저장, 실행, 결과 확인을 반복해야 합니다. 브라우저 기반 정규식 테스터는 이 피드백 주기를 제로로 줄여줍니다. 입력하는 즉시 일치 결과를 확인할 수 있습니다.
정규식 테스터 활용 사례
정규식 문법 빠른 참조
아래 표는 가장 많이 사용되는 정규식 토큰을 다룹니다. JavaScript, Python, Go, PHP 및 대부분의 PCRE 호환 엔진에서 동작합니다. 언어별 확장 기능(Python의 조건부 패턴이나 \k 문법을 사용하는 JavaScript의 명명된 그룹 등)은 코드 예제 섹션에서 설명합니다.
| 패턴 | 이름 | 설명 |
|---|---|---|
| . | Any character | Matches any single character except newline (unless s flag is set) |
| \d | Digit | Matches [0-9] |
| \w | Word character | Matches [a-zA-Z0-9_] |
| \s | Whitespace | Matches space, tab, newline, carriage return, form feed |
| \b | Word boundary | Matches the position between a word character and a non-word character |
| ^ | Start of string/line | Matches the start of the input; with m flag, matches start of each line |
| $ | End of string/line | Matches the end of the input; with m flag, matches end of each line |
| * | Zero or more | Matches the preceding token 0 or more times (greedy) |
| + | One or more | Matches the preceding token 1 or more times (greedy) |
| ? | Optional | Matches the preceding token 0 or 1 time |
| {n,m} | Quantifier range | Matches the preceding token between n and m times |
| () | Capturing group | Groups tokens and captures the matched text for back-references |
| (?:) | Non-capturing group | Groups tokens without capturing the matched text |
| (?=) | Positive lookahead | Matches a position followed by the given pattern, without consuming it |
| (?<=) | Positive lookbehind | Matches a position preceded by the given pattern, without consuming it |
| [abc] | Character class | Matches any one of the characters inside the brackets |
| [^abc] | Negated class | Matches any character not inside the brackets |
| | | Alternation | Matches the expression before or after the pipe |
정규식 플래그 설명
플래그(수정자라고도 함)는 엔진이 패턴을 처리하는 방식을 변경합니다. JavaScript에서는 닫는 슬래시 뒤에 추가합니다: /pattern/gi. Python에서는 두 번째 인수로 전달합니다: re.findall(pattern, text, re.IGNORECASE | re.MULTILINE). 모든 플래그가 모든 언어에서 사용 가능한 것은 아닙니다.
| 플래그 | 이름 | 동작 |
|---|---|---|
| g | Global | Find all matches, not just the first one |
| i | Case-insensitive | Letters match both uppercase and lowercase |
| m | Multiline | ^ and $ match start/end of each line, not just the whole string |
| s | Dot-all | . matches newline characters as well |
| u | Unicode | Treat the pattern and subject as a Unicode string; enables \u{FFFF} syntax |
| y | Sticky | Matches only from the lastIndex position in the target string |
코드 예제
JavaScript, Python, Go, 그리고 커맨드라인에서의 정규식 예제입니다. 각 예제는 패턴 구성, 일치 항목 추출, 출력을 보여줍니다.
// Match all email addresses in a string
const text = 'Contact us at support@example.com or sales@example.com'
const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
const matches = text.matchAll(emailRegex)
for (const match of matches) {
console.log(match[0], 'at index', match.index)
}
// → "support@example.com" at index 14
// → "sales@example.com" at index 37
// Named capture groups (ES2018+)
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const result = '2026-03-30'.match(dateRegex)
console.log(result.groups)
// → { year: "2026", month: "03", day: "30" }
// Replace with a callback
'hello world'.replace(/\b\w/g, c => c.toUpperCase())
// → "Hello World"import re
# Find all IPv4 addresses
text = 'Server 192.168.1.1 responded, fallback to 10.0.0.255'
pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
matches = re.findall(pattern, text)
print(matches) # → ['192.168.1.1', '10.0.0.255']
# Named groups and match objects
date_pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
m = re.search(date_pattern, 'Released on 2026-03-30')
if m:
print(m.group('year')) # → '2026'
print(m.group('month')) # → '03'
# Compile for repeated use (faster in loops)
compiled = re.compile(r'\b[A-Z][a-z]+\b')
words = compiled.findall('Hello World Foo bar')
print(words) # → ['Hello', 'World', 'Foo']package main
import (
"fmt"
"regexp"
)
func main() {
// Find all matches
re := regexp.MustCompile(`\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b`)
text := "Contact support@example.com or sales@example.com"
matches := re.FindAllString(text, -1)
fmt.Println(matches)
// → [support@example.com sales@example.com]
// Named capture groups
dateRe := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
match := dateRe.FindStringSubmatch("2026-03-30")
for i, name := range dateRe.SubexpNames() {
if name != "" {
fmt.Printf("%s: %s\n", name, match[i])
}
}
// → year: 2026
// → month: 03
// → day: 30
// Replace with a function
result := re.ReplaceAllStringFunc(text, func(s string) string {
return "[REDACTED]"
})
fmt.Println(result)
// → Contact [REDACTED] or [REDACTED]
}# Find lines matching an IP address pattern
grep -E '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' access.log
# Extract email addresses from a file
grep -oE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' contacts.txt
# Replace dates from YYYY-MM-DD to DD/MM/YYYY using sed
echo "2026-03-30" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# → 30/03/2026
# Count matches per file in a directory
grep -rcE 'TODO|FIXME|HACK' src/
# → src/main.js:3
# → src/utils.js:1