본문 바로가기

정규표현식(Regular Expression) 자동화, 로그 분석·보안 필터링·유효성 검사

728x90

정규표현식(Regular Expression, 줄여서 Regex 또는 RegExp)은 문자열의 패턴을 표현하는 특수한 문자열입니다. 텍스트에서 특정 패턴을 검색하고, 추출하고, 치환하는 데 사용되는 강력한 도구입니다.

  • 복잡한 문자열 패턴을 간결하게 표현
  • 거의 모든 프로그래밍 언어에서 지원
  • 검색, 유효성 검사, 추출, 치환 등에 활용
300x250

기본 문법과 구성 요소

메타 문자(Meta Characters)

문자 설명 예시
. 임의의 한 문자 a.c → abc, a1c, a@c
* 앞 문자가 0개 이상 ab*c → ac, abc, abbc
+ 앞 문자가 1개 이상 ab+c → abc, abbc (ac는 불가)
? 앞 문자가 0개 또는 1개 ab?c → ac, abc
^ 문자열의 시작 ^Hello → Hello로 시작하는 문자열
$ 문자열의 끝 world$ → world로 끝나는 문자열
[] 문자 클래스 [abc] → a, b, c 중 하나
[^] 부정 문자 클래스 [^abc] → a, b, c가 아닌 문자
| OR 연산자 cat|dog → cat 또는 dog
() 그룹화 (ab)+ → ab, abab, ababab

특수 이스케이프 시퀀스

시퀀스 설명 예시
\d 숫자 [0-9] \d{3} → 123, 456
\D 숫자가 아닌 문자 \D+ → abc, xyz
\w 단어 문자 [a-zA-Z0-9_] \w+ → hello, test_123
\W 단어 문자가 아닌 문자 \W+ → @#$, !!!
\s 공백 문자 \s+ → 스페이스, 탭, 개행
\S 공백이 아닌 문자 \S+ → hello, 123
\b 단어 경계 \bword\b → 독립된 word
\n 개행 문자 -
\t 탭 문자 -

수량자(Quantifiers)

수량자 설명 예시
{n} 정확히 n개 \d{4} → 1234
{n,} n개 이상 \d{2,} → 12, 123, 1234
{n,m} n개 이상 m개 이하 \d{2,4} → 12, 123, 1234
*? 0개 이상 (최소 매칭) <.*?> → HTML 태그
+? 1개 이상 (최소 매칭) .+? → 최소 매칭

분야별 활용 사례

웹 개발 / 프론트엔드

이메일 유효성 검사

const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const isValid = emailRegex.test("user@example.com");

전화번호 형식 검사

// 한국 휴대폰 번호
const phoneRegex = /^01[0-9]-?\d{3,4}-?\d{4}$/;
// 010-1234-5678, 01012345678 모두 매칭

// 국제 전화번호
const intlPhoneRegex = /^\+\d{1,3}[-.\s]?\(?\d{1,4}\)?[-.\s]?\d{1,4}[-.\s]?\d{1,9}$/;

URL 추출

const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi;
const urls = text.match(urlRegex);

백엔드 / 서버 개발

로그 파일 분석 (Apache/Nginx)

import re

# Apache 액세스 로그 파싱
log_pattern = r'(\d+\.\d+\.\d+\.\d+) - - \[(.*?)\] "(.*?)" (\d+) (\d+)'
log_line = '192.168.1.1 - - [10/Oct/2023:13:55:36 +0700] "GET /index.html HTTP/1.1" 200 2326'

match = re.match(log_pattern, log_line)
if match:
    ip, timestamp, request, status_code, size = match.groups()

SQL 인젝션 방지

# 위험한 SQL 패턴 감지
dangerous_patterns = [
    r"(\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION)\b)",
    r"(--|\#|\/\*|\*\/)",
    r"(\bOR\b\s*\d+\s*=\s*\d+)",
    r"(\bAND\b\s*\d+\s*=\s*\d+)"
]

def check_sql_injection(user_input):
    for pattern in dangerous_patterns:
        if re.search(pattern, user_input, re.IGNORECASE):
            return True
    return False

API 엔드포인트 라우팅

# Flask/Django 스타일 라우팅
routes = [
    (r'^/users/(\d+)$', 'get_user'),
    (r'^/posts/(\d+)/comments$', 'get_comments'),
    (r'^/api/v(\d+)/(.+)$', 'api_handler')
]

데이터 처리 / 분석

CSV 데이터 정제

# 쉼표가 포함된 CSV 필드 처리
csv_pattern = r'(?:^|,)("(?:[^"]+|"")*"|[^,]*)'

# 날짜 형식 표준화
date_patterns = {
    r'(\d{4})/(\d{2})/(\d{2})': r'\1-\2-\3',  # YYYY/MM/DD → YYYY-MM-DD
    r'(\d{2})/(\d{2})/(\d{4})': r'\3-\1-\2',  # MM/DD/YYYY → YYYY-MM-DD
    r'(\d{2})-(\d{2})-(\d{4})': r'\3-\1-\2'   # MM-DD-YYYY → YYYY-MM-DD
}

텍스트 마이닝

# 해시태그 추출
hashtag_pattern = r'#[가-힣a-zA-Z0-9_]+'

# 멘션 추출
mention_pattern = r'@[a-zA-Z0-9_]+'

# 한글 단어 추출
korean_word_pattern = r'[가-힣]+'

설정 파일 / 구성 관리

.env 파일 파싱

# KEY=VALUE 형식 파싱
env_pattern = r'^([A-Z_]+)=(.*)$'

def parse_env_file(content):
    env_vars = {}
    for line in content.split('\n'):
        match = re.match(env_pattern, line)
        if match:
            key, value = match.groups()
            env_vars[key] = value.strip('"\'')
    return env_vars

nginx.conf 파싱

# location 블록 추출
location_pattern = r'location\s+(.*?)\s*\{([^}]*)\}'

# upstream 서버 추출
upstream_pattern = r'server\s+([^;]+);'

보안 / 유효성 검사

비밀번호 강도 검사

const passwordRules = {
    minLength: /.{8,}/,
    hasUpperCase: /[A-Z]/,
    hasLowerCase: /[a-z]/,
    hasNumber: /\d/,
    hasSpecialChar: /[!@#$%^&*(),.?":{}|<>]/
};

function validatePassword(password) {
    return Object.entries(passwordRules).every(([rule, regex]) => 
        regex.test(password)
    );
}

XSS 방지

// 위험한 HTML 태그 제거
const dangerousTagsRegex = /<script[^>]*>.*?<\/script>|<iframe[^>]*>.*?<\/iframe>/gi;
const sanitizedHtml = userInput.replace(dangerousTagsRegex, '');

파일 처리 / 시스템 관리

파일명 패턴 매칭

# Bash에서 find와 함께 사용
find . -regex '.*\.\(jpg\|png\|gif\)$'

# Python에서 파일 필터링
import re
import os

def find_files_by_pattern(directory, pattern):
    regex = re.compile(pattern)
    matched_files = []

    for root, dirs, files in os.walk(directory):
        for file in files:
            if regex.match(file):
                matched_files.append(os.path.join(root, file))

    return matched_files

# 예: 날짜가 포함된 백업 파일 찾기
backup_files = find_files_by_pattern('.', r'backup_\d{8}_\d{6}\.tar\.gz$')

정규표현식 손쉽게 생성하는 방법

온라인 도구 활용

1. Regex101 (https://regex101.com)

  • 실시간 매칭 결과 확인
  • 상세한 설명 제공
  • 여러 언어의 정규식 엔진 지원
  • 저장 및 공유 기능

2. RegExr (https://regexr.com)

  • 시각적 표현
  • 치트시트 제공
  • 커뮤니티 패턴 라이브러리

3. Regex Visualizer (https://regexper.com)

  • 정규식을 시각적 다이어그램으로 변환
  • 복잡한 패턴 이해에 유용

AI 도구 활용

# ChatGPT나 Claude에게 요청하는 예시
"""
다음 조건을 만족하는 정규표현식을 만들어주세요:
- 한국 주민등록번호 형식 (######-#######)
- 앞 6자리는 생년월일 (YYMMDD)
- 뒤 7자리 중 첫 번째는 1-4 중 하나
"""

# 결과: ^\d{6}-[1-4]\d{6}$

패턴 빌더 라이브러리

JavaScript - VerbalExpressions

const VerEx = require('verbal-expressions');

const urlRegex = VerEx()
    .startOfLine()
    .then('http')
    .maybe('s')
    .then('://')
    .maybe('www.')
    .anythingBut(' ')
    .endOfLine();

Python - re-builder

from rebuilder import ReBuilder

rb = ReBuilder()
email_pattern = (rb
    .start()
    .one_or_more.word()
    .literal('@')
    .one_or_more.word()
    .literal('.')
    .between(2, 4).letters()
    .end()
    .build()
)

단계별 구축 전략

1단계: 기본 패턴 작성

// 목표: 이메일 주소 매칭
// 시작: 기본 구조
const step1 = /.+@.+\..+/;

2단계: 세부 규칙 추가

// 허용 문자 지정
const step2 = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/;

3단계: 경계 조건 추가

// 시작과 끝 명시
const step3 = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

4단계: 예외 처리

// 특수 케이스 고려
const final = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/i;

실전 팁과 최적화

성능 최적화

1. 불필요한 캡처 그룹 피하기

// 나쁜 예
/(https?):\/\/(www\.)?([a-z]+\.[a-z]+)/

// 좋은 예 (비캡처 그룹 사용)
/(?:https?):\/\/(?:www\.)?([a-z]+\.[a-z]+)/

2. 구체적인 문자 클래스 사용

// 나쁜 예
/.*/  // 모든 문자

// 좋은 예
/[^,]*/  // 쉼표를 제외한 문자

3. 앵커 사용으로 검색 범위 제한

// 나쁜 예
/admin/  // 문자열 전체 검색

// 좋은 예
/^admin/  // 시작 부분만 검색

가독성 향상

1. 명명된 캡처 그룹 사용

// ES2018+
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = dateRegex.exec('2023-10-15');
console.log(match.groups.year);  // 2023

2. 주석과 공백 활용 (x 플래그)

# Python
pattern = re.compile(r"""
    ^                   # 시작
    (?P<protocol>https?)  # 프로토콜
    ://                 # 구분자
    (?P<domain>[^/]+)   # 도메인
    (?P<path>/.*)?      # 경로 (선택적)
    $                   # 끝
""", re.VERBOSE)

디버깅 전략

1. 단계별 테스트

function debugRegex(pattern, testCases) {
    testCases.forEach(testCase => {
        const match = pattern.test(testCase);
        console.log(`"${testCase}": ${match ? '✓' : '✗'}`);
    });
}

2. 부분 패턴 분리

// 복잡한 패턴을 작은 단위로 분리
const protocolPattern = /https?/;
const domainPattern = /[a-zA-Z0-9.-]+/;
const pathPattern = /\/[^ ]*/;

// 조합
const urlPattern = new RegExp(
    `^${protocolPattern.source}://${domainPattern.source}${pathPattern.source}?$`
);

언어별 정규표현식 차이점

JavaScript vs Python

// JavaScript
const pattern = /\d+/g;  // 전역 플래그
const matches = text.match(pattern);

// Python
import re
pattern = r'\d+'
matches = re.findall(pattern, text)

Java 특수성

// 이스케이프 문자 이중 처리
Pattern pattern = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");

// 여러 줄 모드
Pattern multiline = Pattern.compile("^test$", Pattern.MULTILINE);

PHP 구분자

// 다양한 구분자 사용 가능
preg_match('/pattern/', $text);
preg_match('#pattern#', $text);
preg_match('~pattern~', $text);

자주 사용되는 패턴 모음

한국어 관련

// 한글만
/^[가-힣]+$/

// 한글과 공백
/^[가-힣\s]+$/

// 한국 우편번호 (새 형식)
/^\d{5}$/

// 한국 사업자등록번호
/^\d{3}-\d{2}-\d{5}$/

웹 관련

// IPv4 주소
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/

// 헥스 컬러 코드
/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/

// 유튜브 비디오 ID
/(?:youtube\.com\/watch\?v=|youtu\.be\/)([^&\n?#]+)/

데이터 유효성

// 신용카드 번호 (간단)
/^\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}$/

// ISBN-13
/^(?:ISBN(?:-13)?:?\ ?)?(?=[0-9]{13}$|(?=(?:[0-9]+[-\ ]){4})[-\ 0-9]{17}$)97[89][-\ ]?[0-9]{1,5}[-\ ]?[0-9]+[-\ ]?[0-9]+[-\ ]?[0-9]$/

// MAC 주소
/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/

정규표현식 사용 시 주의사항

1. 과도한 복잡성 피하기

  • 너무 복잡한 정규식은 유지보수가 어려움
  • 필요시 여러 개의 간단한 패턴으로 분리

2. 성능 고려

  • 백트래킹이 많이 발생하는 패턴 주의
  • 큰 텍스트에서는 정규식 대신 파싱 라이브러리 고려

3. 보안 이슈

  • 사용자 입력을 정규식 패턴으로 사용 금지 (ReDoS 공격)
  • 유효성 검사는 정규식만으로 충분하지 않을 수 있음

4. 국제화 고려

  • 유니코드 문자 처리
  • 다양한 언어와 문자 체계 지원

정규표현식은 강력한 도구이지만, 적절히 사용해야 합니다. 간단한 문자열 처리는 내장 함수로, 복잡한 파싱은 전용 파서로 처리하는 것이 좋을 수 있습니다. 정규표현식은 그 중간 영역에서 빛을 발합니다. 항상 가독성과 유지보수성을 고려하여 작성하고, 충분한 테스트를 통해 예상치 못한 경우를 대비하세요.

728x90
그리드형(광고전용)

댓글