[EXT-003][입문] 문자열 모양을 정규식으로 확인하기
페이지 정보

본문
[이번 수업]
정규 표현식은 문자열 모양을 설명하는 규칙입니다. 주문 코드를 로컬 Python에서 검사하며 패턴 읽기와 전체 일치를 익힙니다. 짧고 제한된 패턴을 정확하게 쓰는 데 집중합니다.
[선수지식]
PY-002의 문자열과 PY-003의 리스트, BACK-005의 입력 검증 개념을 알면 좋습니다. Python 파일을 만들고 실행할 수 있으면 충분합니다.
[학습목표]
1. 리터럴, 문자 집합, 반복 횟수를 읽습니다.
2. `fullmatch()`로 입력 전체가 규칙에 맞는지 확인합니다.
3. 정규식으로 확인할 형식과 별도 코드로 확인할 의미를 구분합니다.
[핵심개념]
정규 표현식, 줄여서 정규식은 글자의 반복과 위치를 기호로 나타내는 패턴입니다. `[A-Z]`는 영문 대문자 한 글자, `[0-9]`는 ASCII 숫자 한 글자, `{3}`은 바로 앞 규칙을 정확히 세 번 반복한다는 뜻입니다. 따라서 `[A-Z]{3}-[0-9]{4}`는 대문자 세 글자, 하이픈, 숫자 네 자리로 된 코드 모양을 나타냅니다.
`search()`는 문자열 안의 일부를 찾지만 `fullmatch()`는 처음부터 끝까지 모두 맞아야 성공합니다. 입력 한 칸 전체는 보통 `fullmatch()`, 긴 로그에서 조각을 찾을 때는 `search()`가 알맞습니다.
Python에서는 정규식 앞에 `r`을 붙인 원시 문자열을 자주 씁니다. `r"\d+"`처럼 쓰면 Python 문자열의 역슬래시 처리와 정규식의 역슬래시 처리를 덜 헷갈릴 수 있습니다. 다만 Python의 `\d`는 기본적으로 유니코드의 십진 숫자도 포함합니다. 영문 코드의 숫자를 0부터 9로 제한하려면 `[0-9]`처럼 의도를 분명히 적습니다.
정규식은 형식을 확인할 뿐 의미까지 보장하지 않습니다. `[0-9]{4}`는 `0000`도 통과시키므로 실제 연도 범위가 필요하면 숫자로 바꾼 뒤 별도 조건문으로 검사해야 합니다. 이메일, URL, 프로그래밍 언어처럼 규칙이 큰 대상을 한 줄 정규식으로 모두 해석하려 하지 말고 전용 파서나 표준 라이브러리를 우선 검토하세요.
[따라하기]
빈 폴더에 pattern_check.py를 만들고 다음 코드를 저장합니다.
```python
import re
code_pattern = re.compile(r"[A-Z]{3}-[0-9]{4}")
samples = ["ABC-2026", "AB-2026", "ABC-20A6", "ABC-0000"]
for value in samples:
shape_ok = code_pattern.fullmatch(value) is not None
year_ok = shape_ok and 2000 <= int(value[-4:]) <= 2099
result = "통과" if year_ok else "거부"
print(f"{value}: {result}")
```
macOS와 Linux에서는 `python3 pattern_check.py`, Windows에서는 `py pattern_check.py`로 실행합니다. 예상 결과는 다음과 같습니다.
```text
ABC-2026: 통과
AB-2026: 거부
ABC-20A6: 거부
ABC-0000: 거부
```
첫 번째 검사는 문자열 모양을 확인하고, 두 번째 검사는 연도가 2000~2099 범위인지 확인합니다. 형식과 의미를 나누면 패턴이 짧아지고 오류 메시지도 더 구체적으로 만들 수 있습니다. 운영체제에 따른 코드 차이는 없습니다.
[흔한 실수]
`search()` 결과만으로 입력 전체가 유효하다고 판단하지 마세요. 점 `.`은 아무 글자 한 개이므로 실제 점은 `\.`로 씁니다. 일반 문자열을 패턴에 붙일 때는 `re.escape()`를 쓰고, 거대한 패턴은 작은 검사나 전용 파서로 나누세요.
[보안 주의]
일부 정규식 엔진은 모호한 반복을 되돌아가며 확인해 입력이 길 때 CPU를 오래 쓸 수 있습니다. 이를 정규식 서비스 거부, ReDoS라고 합니다. 외부 입력은 먼저 최대 길이를 제한하고, 중첩된 무제한 반복을 피하며, 긴 실패 입력으로 성능 테스트를 합니다. 필요하면 시간 제한이나 선형 시간 엔진을 선택하세요. 실습은 본인 소유의 로컬·격리 환경과 짧은 가짜 문자열에서만 진행합니다.
[직접 해볼 과제]
코드 규칙을 `대문자 두 글자-숫자 여섯 자리`로 바꾸고 정상 2개, 형식 오류 2개, 범위 오류 1개를 테스트하세요. 입력 길이가 20자를 넘으면 정규식을 실행하기 전에 거부하고, 형식 오류와 연도 오류를 다른 메시지로 출력해 보세요.
[확인문제]
1. `search()`와 `fullmatch()`의 차이는 무엇인가요?
2. 영문 코드에서 `\d`보다 `[0-9]`가 의도를 더 분명히 하는 이유는 무엇인가요?
3. 형식 검사와 의미 검사를 나누면 어떤 장점이 있나요?
[다음 학습]
다음 확장 수업에서는 `없음`, 빈 문자열, 숫자 0을 구분합니다. 데이터베이스의 NULL과 프로그램의 빈 값을 섞을 때 생기는 오류를 작은 예제로 확인합니다.
[공식 참고 자료]
Python re 공식 문서
https://docs.python.org/3/library/re.html
Python 정규 표현식 HOWTO
https://docs.python.org/3/howto/regex.html
Unicode Technical Standard #18
https://www.unicode.org/reports/tr18/
POSIX 정규 표현식 표준
https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap09.html
OWASP ReDoS 설명
https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
CWE-1333 비효율적인 정규식 복잡도
https://cwe.mitre.org/data/definitions/1333.html
정규 표현식은 문자열 모양을 설명하는 규칙입니다. 주문 코드를 로컬 Python에서 검사하며 패턴 읽기와 전체 일치를 익힙니다. 짧고 제한된 패턴을 정확하게 쓰는 데 집중합니다.
[선수지식]
PY-002의 문자열과 PY-003의 리스트, BACK-005의 입력 검증 개념을 알면 좋습니다. Python 파일을 만들고 실행할 수 있으면 충분합니다.
[학습목표]
1. 리터럴, 문자 집합, 반복 횟수를 읽습니다.
2. `fullmatch()`로 입력 전체가 규칙에 맞는지 확인합니다.
3. 정규식으로 확인할 형식과 별도 코드로 확인할 의미를 구분합니다.
[핵심개념]
정규 표현식, 줄여서 정규식은 글자의 반복과 위치를 기호로 나타내는 패턴입니다. `[A-Z]`는 영문 대문자 한 글자, `[0-9]`는 ASCII 숫자 한 글자, `{3}`은 바로 앞 규칙을 정확히 세 번 반복한다는 뜻입니다. 따라서 `[A-Z]{3}-[0-9]{4}`는 대문자 세 글자, 하이픈, 숫자 네 자리로 된 코드 모양을 나타냅니다.
`search()`는 문자열 안의 일부를 찾지만 `fullmatch()`는 처음부터 끝까지 모두 맞아야 성공합니다. 입력 한 칸 전체는 보통 `fullmatch()`, 긴 로그에서 조각을 찾을 때는 `search()`가 알맞습니다.
Python에서는 정규식 앞에 `r`을 붙인 원시 문자열을 자주 씁니다. `r"\d+"`처럼 쓰면 Python 문자열의 역슬래시 처리와 정규식의 역슬래시 처리를 덜 헷갈릴 수 있습니다. 다만 Python의 `\d`는 기본적으로 유니코드의 십진 숫자도 포함합니다. 영문 코드의 숫자를 0부터 9로 제한하려면 `[0-9]`처럼 의도를 분명히 적습니다.
정규식은 형식을 확인할 뿐 의미까지 보장하지 않습니다. `[0-9]{4}`는 `0000`도 통과시키므로 실제 연도 범위가 필요하면 숫자로 바꾼 뒤 별도 조건문으로 검사해야 합니다. 이메일, URL, 프로그래밍 언어처럼 규칙이 큰 대상을 한 줄 정규식으로 모두 해석하려 하지 말고 전용 파서나 표준 라이브러리를 우선 검토하세요.
[따라하기]
빈 폴더에 pattern_check.py를 만들고 다음 코드를 저장합니다.
```python
import re
code_pattern = re.compile(r"[A-Z]{3}-[0-9]{4}")
samples = ["ABC-2026", "AB-2026", "ABC-20A6", "ABC-0000"]
for value in samples:
shape_ok = code_pattern.fullmatch(value) is not None
year_ok = shape_ok and 2000 <= int(value[-4:]) <= 2099
result = "통과" if year_ok else "거부"
print(f"{value}: {result}")
```
macOS와 Linux에서는 `python3 pattern_check.py`, Windows에서는 `py pattern_check.py`로 실행합니다. 예상 결과는 다음과 같습니다.
```text
ABC-2026: 통과
AB-2026: 거부
ABC-20A6: 거부
ABC-0000: 거부
```
첫 번째 검사는 문자열 모양을 확인하고, 두 번째 검사는 연도가 2000~2099 범위인지 확인합니다. 형식과 의미를 나누면 패턴이 짧아지고 오류 메시지도 더 구체적으로 만들 수 있습니다. 운영체제에 따른 코드 차이는 없습니다.
[흔한 실수]
`search()` 결과만으로 입력 전체가 유효하다고 판단하지 마세요. 점 `.`은 아무 글자 한 개이므로 실제 점은 `\.`로 씁니다. 일반 문자열을 패턴에 붙일 때는 `re.escape()`를 쓰고, 거대한 패턴은 작은 검사나 전용 파서로 나누세요.
[보안 주의]
일부 정규식 엔진은 모호한 반복을 되돌아가며 확인해 입력이 길 때 CPU를 오래 쓸 수 있습니다. 이를 정규식 서비스 거부, ReDoS라고 합니다. 외부 입력은 먼저 최대 길이를 제한하고, 중첩된 무제한 반복을 피하며, 긴 실패 입력으로 성능 테스트를 합니다. 필요하면 시간 제한이나 선형 시간 엔진을 선택하세요. 실습은 본인 소유의 로컬·격리 환경과 짧은 가짜 문자열에서만 진행합니다.
[직접 해볼 과제]
코드 규칙을 `대문자 두 글자-숫자 여섯 자리`로 바꾸고 정상 2개, 형식 오류 2개, 범위 오류 1개를 테스트하세요. 입력 길이가 20자를 넘으면 정규식을 실행하기 전에 거부하고, 형식 오류와 연도 오류를 다른 메시지로 출력해 보세요.
[확인문제]
1. `search()`와 `fullmatch()`의 차이는 무엇인가요?
2. 영문 코드에서 `\d`보다 `[0-9]`가 의도를 더 분명히 하는 이유는 무엇인가요?
3. 형식 검사와 의미 검사를 나누면 어떤 장점이 있나요?
[다음 학습]
다음 확장 수업에서는 `없음`, 빈 문자열, 숫자 0을 구분합니다. 데이터베이스의 NULL과 프로그램의 빈 값을 섞을 때 생기는 오류를 작은 예제로 확인합니다.
[공식 참고 자료]
Python re 공식 문서
https://docs.python.org/3/library/re.html
Python 정규 표현식 HOWTO
https://docs.python.org/3/howto/regex.html
Unicode Technical Standard #18
https://www.unicode.org/reports/tr18/
POSIX 정규 표현식 표준
https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap09.html
OWASP ReDoS 설명
https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
CWE-1333 비효율적인 정규식 복잡도
https://cwe.mitre.org/data/definitions/1333.html
- 이전글[EXT-004][기초] 없는 값과 빈 값을 구분하기 26.09.07
- 다음글[EXT-002][입문] 소수점 계산이 어긋나는 이유 알기 26.09.06
댓글목록
등록된 댓글이 없습니다.
