정규 표현식: 초보자 가이드
· 12분 읽기
📑 목차
정규 표현식(regex)은 개발자의 무기고에서 가장 강력한 도구 중 하나입니다. 처음에는 어렵게 보일 수 있지만, 기본을 이해하고 나면 텍스트 처리, 유효성 검사, 데이터 추출에 없어서는 안 될 도구가 됩니다.
사용자 입력 유효성 검사, 로그 파일 파싱, 데이터 변환 등 어떤 작업을 하든, 정규식은 텍스트 패턴을 다루는 간결하고 유연한 방법을 제공합니다. 이 가이드는 완전한 초보자에서 자신감 있는 정규식 사용자로 안내합니다.
정규 표현식이란?
정규 표현식은 검색 패턴을 정의하는 문자 시퀀스입니다. 텍스트 패턴을 설명하는 미니 언어라고 생각하세요. 정확한 문자열을 검색하는 대신 "모든 이메일 주소" 또는 "모든 전화번호"와 같은 패턴을 검색할 수 있습니다.
정규 표현식은 거의 모든 프로그래밍 언어와 텍스트 편집기에서 사용됩니다. JavaScript, Python, Java, PHP, Ruby, Go 및 수많은 다른 언어에서 지원됩니다. grep, sed, awk와 같은 명령줄 도구도 정규식에 크게 의존합니다.
정규식의 장점은 구문을 배우면 다양한 도구와 언어에 적용할 수 있다는 것입니다. 정규식의 "플레이버"(PCRE, JavaScript, Python 등) 간에 약간의 차이가 있지만 핵심 개념은 동일합니다.
프로 팁: 간단한 패턴으로 시작하여 점차 복잡성을 높이세요. 첫 시도에서 완벽한 정규식을 작성하려고 하지 마세요. 테스트하면서 반복하고 개선하세요.
기본 구성 요소
모든 정규식 패턴은 기본 구성 요소로 만들어집니다. 더 복잡한 패턴으로 넘어가기 전에 이러한 구성 요소를 이해하는 것이 필수적입니다.
리터럴 문자
가장 간단한 정규식은 일반 텍스트입니다. cat 패턴은 문자열의 어디에서나 정확한 텍스트 "cat"과 일치합니다. 대부분의 영숫자 문자는 그 자체로 리터럴하게 일치합니다.
그러나 일부 문자는 정규식에서 특별한 의미를 가지며 백슬래시로 이스케이프해야 합니다: . ^ $ * + ? { } [ ] \ | ( )
리터럴 마침표와 일치시키려면 .가 아닌 \.로 작성해야 합니다.
점 메타문자
점(.)은 줄바꿈을 제외한 모든 단일 문자와 일치하는 와일드카드입니다. c.t 패턴은 "cat", "cot", "cut", "c9t", 심지어 "c@t"와도 일치합니다.
이것은 점을 매우 강력하게 만들지만 부주의하게 사용하면 잠재적으로 위험할 수도 있습니다. 나중에 더 구체적으로 만드는 방법을 다루겠습니다.
문자 클래스
대괄호는 문자 클래스를 만들어 대괄호 안의 모든 단일 문자와 일치합니다:
[aeiou]는 모든 모음과 일치[0-9]는 모든 숫자와 일치[a-zA-Z]는 모든 문자(대소문자)와 일치[a-z0-9]는 모든 소문자 또는 숫자와 일치
캐럿으로 문자 클래스를 부정할 수도 있습니다: [^0-9]는 숫자가 아닌 모든 문자와 일치합니다.
수량자 설명
수량자는 패턴이 몇 번 일치해야 하는지 지정합니다. 반복하려는 요소 뒤에 배치됩니다.
| 수량자 | 의미 | 예제 |
|---|---|---|
* |
0회 이상 | ab*c는 "ac", "abc", "abbc"와 일치 |
+ |
1회 이상 | ab+c는 "abc", "abbc"와 일치하지만 "ac"는 아님 |
? |
0회 또는 1회 (선택적) | colou?r는 "color"와 "colour"와 일치 |
{n} |
정확히 n회 | \d{3}는 정확히 3개의 숫자와 일치 |
{n,} |
n회 이상 | \d{2,}는 2개 이상의 숫자와 일치 |
{n,m} |
n회에서 m회 사이 | \d{2,4}는 2, 3 또는 4개의 숫자와 일치 |
탐욕적 vs. 게으른 매칭
기본적으로 수량자는 탐욕적입니다. 가능한 한 많은 텍스트와 일치합니다. .* 패턴은 가능한 모든 것을 소비합니다.
HTML 태그 매칭을 고려해보세요: <.+>를 <b>bold</b>에 적용하면 <b>만이 아니라 전체 문자열과 일치합니다.
수량자를 게으르게 만들려면(가능한 한 적게 일치) 물음표를 추가하세요: <.+?>는 이제 <b>와 </b>를 별도로 일치시킵니다.
빠른 팁: 확실하지 않을 때는 게으른 수량자를 사용하세요. 더 예측 가능하고 예상치 못한 일치를 일으킬 가능성이 적습니다.
문자 클래스와 단축키
[0-9]를 반복적으로 작성하는 것은 지루합니다. 정규식은 일반적인 패턴에 대한 단축 문자 클래스를 제공합니다.
| 단축키 | 동등한 표현 | 설명 |
|---|---|---|
\d |
[0-9] |
모든 숫자 |
\D |
[^0-9] |
숫자가 아닌 모든 것 |
\w |
[a-zA-Z0-9_] |
모든 단어 문자 |
\W |
[^a-zA-Z0-9_] |
단어 문자가 아닌 모든 것 |
\s |
[ \t\r\n\f] |
모든 공백 문자 |
\S |
[^ \t\r\n\f] |
공백이 아닌 모든 문자 |
패턴을 주목하세요: 대문자 버전은 소문자 버전의 부정입니다. 이것은 정규식을 더 읽기 쉽고 간결하게 만듭니다.
단축키를 사용한 실용적인 예제
\d{3}-\d{4}는 "555-1234"와 같은 전화번호와 일치\w+@\w+\.\w+는 간단한 이메일 주소와 일치\s+는 하나 이상의 공백 문자와 일치\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}는 IP 주소와 일치 (완벽하지는 않음)
앵커와 경계
앵커는 문자와 일치하지 않고 텍스트의 위치와 일치합니다. 정확한 패턴 매칭에 필수적입니다.
줄 앵커
^는 줄의 시작과 일치$는 줄의 끝과 일치
^Hello 패턴은 줄의 시작 부분에 있는 "Hello"와만 일치합니다. 마찬가지로 world$는 줄의 끝에 있는 "world"와만 일치합니다.
전체 줄을 정확히 일치시키려면 둘 다 사용하세요: ^Hello world$는 앞뒤에 아무것도 없이 정확히 "Hello world"를 포함하는 줄만 일치합니다.
단어 경계
\b 앵커는 단어 경계와 일치합니다. 단어 문자(\w)와 비단어 문자 사이의 위치입니다.
이것은 전체 단어를 일치시키는 데 매우 유용합니다. \bcat\b 패턴은 "cat"과 일치하지만 "category"나 "scat"과는 일치하지 않습니다.
단어 경계가 없으면 cat은 세 가지 모두와 일치합니다. 단어 경계는 복잡성을 추가하지 않고 패턴을 더 정확하게 만듭니다.
프로 팁: 전체 단어를 검색할 때는 항상 단어 경계를 사용하세요. 잘못된 일치를 방지하고 정규식을 더 신뢰할 수 있게 만듭니다.
그룹과 캡처
괄호는 정규식에서 두 가지 목적을 제공합니다: 그룹화와 캡처. 작동 방식을 이해하면 가장 강력한 기능 중 하나입니다.
수량자를 위한 그룹화
괄호를 사용하면 여러 문자에 수량자를 적용할 수 있습니다. (ha)+ 패턴은 "ha", "haha", "hahaha" 등과 일치합니다.
괄호가 없으면 ha+는 "ha", "haa", "haaa"와 일치합니다. 수량자는 앞의 문자에만 적용됩니다.
캡처 그룹
그룹은 나중에 사용하기 위해 일치하는 텍스트도 캡처합니다. 이 전화번호 패턴을 고려하세요: (\d{3})-(\d{3})-(\d{4})
이것은 세 개의 캡처 그룹을 만듭니다: 지역 코드, 접두사, 회선 번호. 대부분의 언어에서 이러한 캡처에 액세스할 수 있습니다:
- JavaScript:
match[1],match[2],match[3] - Python:
match.group(1),match.group(2),match.group(3) - 치환에서:
$1,$2,$3또는\1,\2,\3
비캡처 그룹
때로는 캡처 없이 그룹화만 원할 수 있습니다. 비캡처 그룹에는 (?:...)를 사용하세요: (?:https?://)?www\.example\.com
이것은 프로토콜을 그룹화하지만 캡처 그룹을 만들지 않아 성능을 향상시키고 코드를 단순화할 수 있습니다.
명명된 캡처 그룹
번호가 매겨진 그룹 대신 명확성을 위해 이름을 지정할 수 있습니다: (?<area>\d{3})-(?<prefix>\d{3})-(?<line>\d{4})
Python에서는 match.group('area')로, JavaScript에서는 match.groups.area로 명명된 그룹에 액세스합니다. 이것은 코드를 자체 문서화합니다.
실용적인 예제
배운 내용을 실제 시나리오에 적용해 봅시다. 이러한 패턴은 시작점입니다. 특정 요구 사항에 맞게 조정해야 하는 경우가 많습니다.
이메일 유효성 검사
간단한 이메일 패턴: [\w.+-]+@[\w.-]+\.[a-zA-Z]{2,}
이것은 대부분의 일반적인 이메일 형식과 일치하지만 RFC를 준수하지는 않습니다. 프로덕션 사용을 위해서는 전용 이메일 유효성 검사 라이브러리 사용을 고려하세요. 이메일 정규식은 매우 복잡해질 수 있습니다.
URL 매칭
HTTP 및 HTTPS URL 일치: https?://[\w.-]+(?:\.[\w.-]+)+(?:/[\w./?&=%-]*)?
이것은 도메인, 경로 및 쿼리 문자열을 처리합니다. s?는 'https'의 's'를 선택적으로 만듭니다.
전화번호
유연한 형식의 미국 전화번호: \(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}
다음과 같은 형식과 일치합니다:
- (555) 123-4567
- 555-123-4567
- 555.123.4567
- 5551234567
날짜 형식
ISO 날짜 형식 (YYYY-MM-DD): \d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])
이것은 월이 01-12이고 일이 01-31인지 확인합니다. 2024-99-99와 같은 잘못된 날짜를 허용하는 \d{4}-\d{2}-\d{2}보다 더 정확합니다.
IP 주소
IPv4 주소: \b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b
이것은 각 옥텟이 0-255 사이인지 확인하여 999.999.999.999와 같은 일치를 방지합니다.
신용카드 번호
선택적 공백 또는 대시가 있는 신용카드 번호 일치: \d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}
Luhn 알고리즘을 사용하여 체크섬을 별도로 검증하는 것을 잊지 마세요. 정규식만으로는 카드 번호가 유효한지 확인할 수 없습니다.
보안 참고: 신용카드 번호를 일반 텍스트로 로깅하거나 저장하지 마세요. 이러한 패턴은 초기 형식 유효성 검사에만 사용한 다음 즉시 민감한 데이터를 토큰화하세요.
로그에서 데이터 추출
Apache 로그 항목 파싱: