정규표현식, 고수로 가는 길 (고급 기능과 성능 최적화 전략)
안녕하세요, 아이홀입니다! 정규표현식 마스터를 향한 여정, 그 마지막 시간입니다!
지난 1편에서는 정규표현식의 기본 개념과 필수 문법을 배웠고, 2편에서는 실전에서 유용한 패턴들과 응용 사례를 살펴봤죠.
오늘은 그 배움을 토대로 정규표현식의 고급 기능들을 알아보고, 더 나아가 성능 최적화 팁까지 다루면서 정규표현식 '고수'로 발돋움하는 시간을 가져보겠습니다!
1. 정규표현식의 고급 기능 🎩
정규표현식을 더욱 강력하고 정교하게 만들어주는 몇 가지 고급 기능들을 예시와 함께 자세히 살펴봅시다.
(1) 역참조 (Backreferences): \1, \2...
역참조는 정규표현식 내에서 이전에 캡처(소괄호 ()로 묶었던 부분)했던 그룹의 내용을 다시 참조하여 사용하는 기능입니다.
즉, "이전에 찾았던 그 패턴을 다시 찾아줘!"라고 명령하는 것과 같아요. 동일한 패턴이 반복되는 경우에 특히 유용합니다.
사용 전: 반복 패턴 찾기 어려움
문자열: "Hello Hello World와 Bye World"
검색: 같은 단어가 연속으로 반복되는 패턴
결과: 수동으로 확인하거나 복잡한 로직이 필요
사용 후: 역참조 적용
문자열: "Hello Hello World와 Bye World"
정규식: \b(\w+)\s+\1\b
결과: "Hello Hello" 매칭 (반복 단어 자동 감지 및 추출)
정규식(\b(\w+)\s+\1\b) 해설:
\b: 단어 경계를 의미합니다. (알파벳, 숫자, _ 가 아닌 문자와의 경계)(: 첫 번째 캡처 그룹의 시작입니다.\w+: 알파벳, 숫자, 밑줄()이 1번 이상 반복되는 단어를 의미합니다.): 첫 번째 캡처 그룹의 끝입니다. (찾은 단어, 예를 들어 'Hello'가 이 그룹에 저장됩니다.)\s+:\s: 공백(띄어쓰기, 탭, 줄바꿈 등) 중 하나를 의미합니다.+: 앞의 문자(\s)가 1번 이상 반복되는 것을 의미합니다. (즉, 하나 이상의 공백)
\1: 첫 번째 캡처 그룹((\w+))에서 찾았던 것과 정확히 동일한 내용을 다시 찾으라는 역참조입니다. (즉, 'Hello' 뒤에 다시 'Hello'를 찾으라는 의미)\b: 다시 단어 경계를 의미합니다.
이 패턴은 두 단어가 공백으로 구분되어 연속으로 반복되는 경우를 찾습니다.
(2) 전방/후방 탐색 (Lookarounds): 조건부 매칭
전방/후방 탐색은 특정 패턴이 '있는지 없는지'만 확인하고, 실제로 그 패턴을 매칭 결과에 포함시키지는 않는 고급 기능입니다. "이것 앞에/뒤에 저것이 있으면 매칭하지만, 저것은 결과에 포함하지 마"라고 생각하시면 돼요.
사용 전: 특정 조건 문자열 포함하여 추출
문자열: "가격: 5000원, 할인가: 3000원"
정규식: \d+원
결과: "5000원", "3000원" (모든 가격 추출)
사용 후: 전방탐색 적용
문자열: "가격: 5000원, 할인가: 3000원"
정규식: (?<=할인가: )\d+원
결과: "3000원" (할인가만 정확히 추출, '할인가: '는 매칭에 포함되지 않음)
정규식((?<=할인가: )\d+원) 해설:
(?<=pattern): 긍정형 후방 탐색입니다. 괄호 안의pattern(할인가:)이 바로 앞에 있는지 확인하되, 그 패턴 자체는 최종 매칭 결과에는 포함하지 않습니다.할인가:: '할인가: '라는 문자열을 그대로 찾습니다. (이 부분이 뒤에 오는 패턴 앞에 있는지 확인하는 조건이 됩니다.)\d+: 숫자(\d)가 1번 이상(+) 반복되는 것을 의미합니다.원: '원'이라는 문자열을 그대로 찾습니다.
이 패턴은 '할인가: '라는 문자열이 바로 앞에 있는 경우에만 '숫자원' 형식의 값을 찾아 매칭합니다. 이때 '할인가: '는 매칭 결과에 포함되지 않고 오직 '숫자원' 부분만 추출됩니다.
(3) 비캡처 그룹 (Non-capturing groups): (?:pattern)
소괄호 ()는 기본적으로 그룹핑과 캡처링 두 가지 역할을 합니다. 하지만 단순히 여러 문자를 묶어 하나의 단위로 처리(그룹핑)하고 싶을 뿐, 그 내용을 별도로 캡처할 필요가 없을 때가 있어요. 이럴 때 ?:를 붙여 비캡처 그룹을 사용합니다.
사용 전: 불필요하게 그룹을 캡처하는 경우
문자열: "사과 주스 또는 포도 주스"
정규식: (사과|포도) 주스
결과: "사과 주스", "포도 주스" (그리고 내부적으로 '사과', '포도'도 캡처됨)
사용 후: 비캡처 그룹 적용
문자열: "사과 주스 또는 포도 주스"
정규식: (?:사과|포도) 주스
결과: "사과 주스", "포도 주스" (그룹 캡처 없이 매칭만 수행)
정규식((?:사과|포도) 주스) 해설:
(?:pattern): 비캡처 그룹을 나타냅니다.pattern부분을 하나의 그룹으로 묶어주지만, 그 내용을 별도의 캡처 그룹으로 저장하지는 않습니다.사과|포도: '사과' 또는 '포도'를 매칭합니다.주스: ' 주스'라는 문자열을 그대로 찾습니다.
이 패턴은 '사과 주스' 또는 '포도 주스'를 찾지만, '사과'나 '포도' 자체를 별도로 추출할 필요가 없을 때 사용합니다. 불필요한 캡처를 줄여 성능 향상에 기여할 수 있습니다.
(4) 탐욕적 vs 게으른 매칭 (Greedy vs. Lazy) - 심화
지난 2편에서 맛보았던 탐욕적/게으른 매칭을 좀 더 명확한 예시와 함께 다시 한번 살펴봅시다. 기본적으로 *, +, {}와 같은 수량자는 가능한 한 가장 길게 매칭하려는 '탐욕적' 특성을 가집니다. 이를 '게으른' 특성으로 바꾸려면 뒤에 ?를 붙여줍니다.
사용 전: HTML 태그에서 의도치 않게 전체가 매칭됨 (탐욕적)
문자열: "<div>첫 번째 내용</div><div>두 번째 내용</div>"
정규식: <div>.*</div>
결과: "<div>첫 번째 내용</div><div>두 번째 내용</div>" (전체 문자열이 하나의 덩어리로 매칭됨)
사용 후: 게으른 매칭 적용
문자열: "<div>첫 번째 내용</div><div>두 번째 내용</div></div>"
정규식: <div>.*?</div>
결과: "<div>첫 번째 내용</div>" 그리고 "<div>두 번째 내용</div>" (각각 따로 매칭)
정규식(<div>.*?</div>) 해설:
<div>: ''라는 문자열을 그대로 찾습니다..*?:.: 줄바꿈 문자를 제외한 모든 문자 하나를 의미합니다.*: 앞의 문자(여기서는.)가 0번 이상 반복되는 것을 의미합니다.?: 앞의 수량자(*)를 '탐욕적'이 아닌 '게으르게' 만들라는 지시입니다. 즉, 가능한 한 가장 짧은 매칭을 시도합니다.
</div>: ''라는 문자열을 그대로 찾습니다.
이 패턴은 ' '로 시작해서 ' '로 끝나는 문자열을 찾는데, 중간 내용을 최소한으로 포함(게으른 매칭)하여 각각의 HTML 태그 블록을 분리해서 찾아줍니다.
(5) 그룹과 캡처링 ( ) - 심화 활용
소괄호 ()의 강력함은 단순히 묶는 것을 넘어, 특정 부분을 추출하고 재사용하는 '캡처링' 기능에서 빛을 발합니다. 특히 데이터를 구조적으로 분리해야 할 때 필수적입니다.
사용 전: 이름과 전화번호가 하나로 인식됨
문자열: "아이홀 - 01012345678"
정규식: \w+ - \d+
결과: "아이홀 - 01012345678" (전체 문자열만 얻을 수 있어 개별 정보 추출이 어려움)
사용 후: 그룹 캡처링 적용
문자열: "아이홀 - 01012345678"
정규식: (\w+) - (\d+)
결과: 그룹1: "아이홀", 그룹2: "01012345678" (각각 이름과 전화번호를 따로 추출 가능)
정규식((\w+) - (\d+)) 해설:
(: 캡처 그룹의 시작을 알립니다.\w+:\w: 알파벳, 숫자, 밑줄() 중 하나를 의미합니다.+: 앞의 문자(\w)가 1번 이상 반복되는 것을 의미합니다. (예: 아이홀)
): 첫 번째 캡처 그룹의 끝을 알립니다. (아이홀이 첫 번째 캡처 그룹으로 저장됩니다.)-: ' - '라는 문자열(공백-하이픈-공백)을 그대로 찾습니다.(: 두 번째 캡처 그룹의 시작을 알립니다.\d+:\d: 숫자(0-9) 하나를 의미합니다.+: 앞의 문자(\d)가 1번 이상 반복되는 것을 의미합니다. (예: 01012345678)
): 두 번째 캡처 그룹의 끝을 알립니다. (01012345678이 두 번째 캡처 그룹으로 저장됩니다.)
이 패턴은 '단어'와 '숫자' 사이에 ' - '가 있는 문자열을 찾고, 이때 '단어'와 '숫자'를 각각 별도의 그룹으로 분리하여 추출합니다.
(6) 이것도 되고 저것도 되는! 선택 | (OR) - 실용 예제
여러 개의 대안 패턴 중 하나를 매칭하고 싶을 때 파이프 기호 |를 사용합니다. 이는 코드에서 OR 조건과 유사한 역할을 하여 검색의 유연성을 높여줍니다.
사용 전: 여러 패턴 검색 시 각각 따로 검색해야 함
문자열: "사과 주스와 포도 주스 모두 있습니다"
검색1: "사과 주스"
검색2: "포도 주스"
결과: 각각 따로 검색해야 함
사용 후: OR 패턴 적용
문자열: "사과 주스와 포도 주스 모두 있습니다"
정규식: (사과|포도) 주스
결과: "사과 주스"와 "포도 주스" 모두 한 번에 찾음
정규식((사과|포도) 주스) 해설:
(: 그룹의 시작을 알립니다. (여기서는 선택지를 묶어주는 역할)사과: '사과'라는 문자열을 그대로 찾습니다.|: '또는(OR)'을 의미합니다. 앞(사과) 또는 뒤(포도) 중 하나를 선택합니다.포도: '포도'라는 문자열을 그대로 찾습니다.): 그룹의 끝을 알립니다.주스: ' 주스'라는 문자열(공백-주스)을 그대로 찾습니다.이 패턴은 '사과 주스' 또는 '포도 주스'라는 두 가지 표현을 동시에 찾습니다.
(7) 실전 예제: 전화번호 찾기
다양한 형태의 전화번호를 한 번에 찾아야 할 때 정규표현식이 유용합니다. 특히 하이픈(-)의 유무처럼 선택적으로 나타나는 패턴을 처리할 때 그 진가가 드러납니다.
사용 전: 하이픈 유무에 따라 다른 패턴 필요
문자열1: "연락처는 010-1234-5678입니다"
문자열2: "연락처는 01012345678입니다"
정규식1: 010-\d{4}-\d{4}
정규식2: 010\d{8}
결과: 두 가지 패턴을 각각 검색해야 함
사용 후: 선택적 하이픈 적용
문자열1: "연락처는 010-1234-5678입니다"
문자열2: "연락처는 01012345678입니다"
정규식: 010-?\d{4}-?\d{4}
결과: 두 형식 모두 한 번에 찾을 수 있음
정규식(010-?\d{4}-?\d{4}) 해설:
010: '010'이라는 문자열을 그대로 찾습니다.-?:-: 하이픈 문자입니다.?: 앞의 문자(여기서는-)가 0번 또는 1번 나타나는 것을 의미합니다. 즉, 하이픈이 있어도 되고 없어도 된다는 뜻입니다.
\d{4}:\d: 숫자(0-9) 하나를 의미합니다.{4}: 앞의 문자(\d)가 정확히 4번 반복되는 것을 의미합니다.
- 두 번째
-?도 위와 동일하게 하이픈이 선택적으로 존재함을 의미합니다.
이 패턴은 '010'으로 시작하고, 중간에 하이픈이 있거나 없거나 상관없이 4자리 숫자가 두 번 반복되는 11자리 전화번호 형식을 찾습니다.
(8) 실전 예제: 간단한 이메일 주소 형식 검증
사용자로부터 입력받은 이메일 주소가 올바른 형식인지 아닌지 검증하는 것은 웹 개발에서 매우 흔한 작업입니다. 정규표현식을 사용하면 복잡한 여러 조건을 한 번에 검사할 수 있습니다.
사용 전: 복잡한 조건 확인 필요
문자열: "문의는 user@example.com으로 보내주세요"
검증:
1. @ 포함 여부 확인
2. 도메인 확인
3. 최상위 도메인 확인
결과: 여러 단계의 조건문과 함수 호출로 검증해야 함
사용 후: 정규식 패턴 적용
문자열: "문의는 user@example.com으로 보내주세요"
정규식: \w+@\w+\.\w+
결과: "user@example.com" 매칭 (한 번에 검증 가능)
정규식(\w+@\w+\.\w+) 해설:
\w+:\w: 알파벳, 숫자, 밑줄(_) 중 하나를 의미합니다.+: 앞의 문자(\w)가 1번 이상 반복되는 것을 의미합니다. (이메일의 사용자 이름 부분)
@: '@' 문자 그대로를 찾습니다.\w+: 도메인 이름 부분(예: 'example')입니다.\.:.: 점(.)은 정규식에서 '모든 문자'를 의미하는 메타문자이므로, 실제 점 문자를 찾기 위해서는 앞에 역슬래시(\)를 붙여 이스케이프(\.)해야 합니다.
\w+: 최상위 도메인(예: 'com') 부분입니다.
이 패턴은 '단어+@+단어.단어'와 같은 간단한 이메일 주소 형식을 찾습니다.
2. 정규표현식 성능 최적화 전략 🚀
강력한 정규표현식은 때때로 성능 문제를 일으킬 수 있습니다. 특히 복잡한 패턴을 사용하거나 방대한 데이터에 적용할 때 더욱 그렇죠. 효율적인 정규표현식 작성을 위한 몇 가지 팁입니다.
(1) 명확하고 구체적으로 작성하기 (Be Specific)
가능한 한 패턴을 명확하고 구체적으로 작성하는 것이 중요합니다. .*와 같이 광범위하게 매칭하는 패턴은 내부적으로 많은 조합을 시도해야 하므로 성능 저하의 원인이 될 수 있습니다.
- 나쁜 예:
<h1>.*</h1>(지나치게 포괄적) - 좋은 예:
<h1>[^<]*</h1>(<문자가 아닌 것을 찾아서 매칭 범위 제한)
(2) 게으른 매칭 활용하기 (Lazy Matching)
앞서 보았듯이, *?, +?, ??, {n,m}?처럼 게으른 매칭을 사용하면 불필요한 백트래킹(다시 돌아가서 다른 경우의 수를 탐색하는 과정)을 줄여 성능을 개선할 수 있습니다.
(3) 탐욕적 백트래킹 (Catastrophic Backtracking) 피하기
가장 흔하고 치명적인 성능 저하 원인 중 하나입니다. 중첩된 수량자(예: (a+)+ 또는 (a|aa)+)가 있거나, 매칭될 수 있는 조합이 너무 많아 컴퓨터가 모든 경우의 수를 시도하다가 멈춰버리는 현상입니다.
- 특징: 정규표현식이 매우 느려지거나 멈춥니다.
- 해결: 패턴을 단순화하거나, 원자적 그룹 (Atomic Grouping)
(?>pattern)을 사용합니다. 원자적 그룹은 그룹 내부에서 한 번 매칭되면 다시 백트래킹하지 않도록 강제하여 불필요한 재탐색을 막아줍니다.
(4) 앵커(Anchors) 활용하기
^(문자열 시작)와 $(문자열 끝) 같은 앵커를 적절히 사용하면 매칭 범위를 크게 줄여 불필요한 탐색을 막을 수 있습니다.
- 예시: 전체 문자열이 숫자인지 검증할 때
^\d+$
(5) 불필요한 그룹 줄이기
캡처링이 필요 없는 그룹에는 (?:...)와 같은 비캡처 그룹을 사용하여 메모리와 처리 시간을 절약할 수 있습니다.
마치며... 🚶
정규표현식은 간단한 패턴에서부터 복잡한 데이터 처리까지, 그 활용 범위가 무궁무진한 강력한 도구입니다.
오늘 배운 고급 기능들을 활용하고 성능 최적화 팁을 숙지하신다면, 진정한 정규표현식 '고수'로서 데이터 처리의 효율성을 한층 더 높일 수 있을 겁니다.
정규표현식은 꾸준한 연습과 시행착오를 통해 능숙해지는 도구입니다.
다양한 예제를 직접 시도해보면서 패턴을 읽고 쓰는 감각을 키워보세요.
언젠가 여러분의 눈에는 텍스트 속 모든 패턴이 한눈에 들어오는 날이 올 겁니다! (저는 아직...)
지금까지 정규표현식 시리즈를 함께 해주셔서 정말 감사합니다! 다음에도 더 유익한 정보로 찾아오겠습니다.
감사합니다! 👋
♥읽어주셔서 감사합니다♥
티스토리 댓글과 공감♥은 로그인이 필요 없습니다.
로그인하시면 구독 가능합니다.
'IT Lab > 코드 크래프트' 카테고리의 다른 글
| 정규표현식, 실전 활용 꿀팁 (자주 쓰는 패턴과 응용 사례) (0) | 2025.09.17 |
|---|---|
| 정규표현식, 첫걸음 (개념과 알아야 할 기본 문법) (1) | 2025.09.15 |
| 티스토리 관리자에서 글 URL 한 번에 추출하고, 파이썬으로 제목 자동 매핑·파싱하기 (3) | 2025.08.29 |
| [Wi-Fi] QR Code Generator-무선접속 QR코드 생성 (10) | 2020.12.07 |
| [F5] L4스위치 Log 확인 방법 (13) | 2020.11.26 |
| [Cisco] ACS 인증서버 admin password 변경 방법(CLI) (2) | 2020.10.07 |
| [Cisco] Archive Backup Falure_ %ARCHIVE_CONFIG-4-ARCHIVE_SKIPPED (0) | 2020.06.24 |
| [Cisco] NX-OS Auto Config Backup (EEM) (0) | 2020.04.28 |