정규식 URL 검증 완벽 가이드 - 실무 패턴부터 한계와 대안까지 총정리
URL 검증 정규식을 복사해 붙여넣기 전에 알아야 할 것들이 있습니다. 실무 검증 패턴과 JavaScript, Python 코드, 정규식으로 해결되지 않는 상황의 대안까지 한 번에 정리했습니다.
![]()
회원가입 폼이나 게시판에 URL 입력란을 만들어 본 개발자라면 한 번쯤 겪는 고민이 있습니다. 검색해서 찾은 정규식을 복사해 붙여넣었더니 멀쩡한 주소가 거부되거나, 반대로 말도 안 되는 문자열이 통과되는 상황입니다. 정규식 URL 검증은 간단해 보이지만 실제로는 함정이 많은 주제입니다. 실무에서 널리 쓰이는 패턴과 그 한계, 그리고 정규식보다 나은 대안까지 순서대로 정리했습니다.
정규식 URL 검증이 생각보다 어려운 이유
URL의 공식 문법은 국제 표준 문서인 RFC 3986에 정의되어 있습니다. 문제는 이 표준이 허용하는 범위가 매우 넓다는 점입니다. 스킴, 호스트, 포트, 경로, 쿼리 스트링, 프래그먼트까지 구성 요소마다 허용 문자와 규칙이 다르기 때문에, 모든 경우를 정규식 하나로 완벽하게 표현하는 것은 사실상 불가능합니다.
실제로 검증 로직을 만들다 보면 아래와 같은 애매한 입력들을 만나게 됩니다.
localhost:3000처럼 점이 없는 호스트. 개발 환경에서는 유효하지만 대부분의 정규식이 거부합니다https://192.168.0.1같은 IP 주소 형태의 호스트https://한글도메인.한국같은 국제화 도메인(IDN)- 쿼리 스트링에 포함된
&,=,%인코딩 문자 ftp://,mailto:처럼 웹 주소가 아닌 스킴
실무에서 바로 쓰는 URL 검증 정규식 패턴
완벽한 패턴은 없지만, 목적에 맞는 패턴은 있습니다. 검증 강도별로 4가지를 정리했습니다. 모두 대소문자 무시 옵션(i 플래그)과 함께 쓰는 것을 전제로 합니다.
| 패턴 종류 | 정규식 | 특징 |
|---|---|---|
| 최소 검증 | ^https?://\S+$ | http 또는 https로 시작하고 공백이 없는지만 확인합니다. 통과 범위가 넓지만 가볍습니다 |
| 도메인 형태 확인 | ^https?://([\w-]+\.)+[\w-]{2,}(/\S*)?$ | 점으로 구분된 도메인 구조까지 확인합니다. 일반적인 웹 서비스에 적합합니다 |
| 포트 허용 | ^https?://([\w-]+\.)+[\w-]{2,}(:\d{1,5})?(/\S*)?$ | example.com:8080 같은 포트 표기를 지원합니다 |
| 프로토콜 생략 허용 | ^(https?://)?([\w-]+\.)+[\w-]{2,}(:\d{1,5})?(/\S*)?$ | 사용자가 www.example.com처럼 입력해도 통과시킵니다 |
어느 패턴을 쓸지는 서비스 성격에 따라 달라집니다.
- 일반 웹 서비스: 도메인 형태 확인 패턴이면 충분한 경우가 많습니다
- 개발자 도구: localhost와 IP 주소를 허용해야 하므로 정규식보다 URL 파서가 낫습니다
- 사용자 편의 우선: 프로토콜 생략 허용 패턴을 쓰고, 저장 시점에 https://를 붙여주는 방식이 좋습니다
JavaScript와 Python 구현 예시
JavaScript
정규식으로 1차 필터링을 하고, URL 생성자로 최종 확인하는 2단계 구성이 실무에서 가장 흔한 방식입니다.
// 1차 필터: 정규식으로 형태 확인
const urlPattern = /^https?:\/\/([\w-]+\.)+[\w-]{2,}(:\d{1,5})?(\/\S*)?$/i;
urlPattern.test('https://example.com/path?q=1'); // true
urlPattern.test('example.com'); // false (스킴 없음)
// 2차 검증: URL 생성자 활용
function isValidUrl(value) {
try {
const url = new URL(value);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch {
return false;
}
}Python
파이썬은 표준 라이브러리의 urlparse를 활용하는 방식이 일반적입니다. urlparse는 잘못된 입력에도 예외를 던지지 않는 경우가 많아서, 스킴과 호스트가 실제로 채워졌는지 직접 확인해야 합니다.
from urllib.parse import urlparse
def is_valid_url(value):
try:
result = urlparse(value)
return result.scheme in ('http', 'https') and bool(result.netloc)
except ValueError:
return False
print(is_valid_url('https://example.com')) # True
print(is_valid_url('example.com')) # False (스킴 없음)정규식의 한계와 URL 파서 대안
정규식 검증에는 구조적인 한계가 두 가지 있습니다. 첫째, 앞서 본 것처럼 표준의 모든 경우를 반영할 수 없습니다. 둘째, 패턴이 복잡해질수록 ReDoS(정규식 서비스 거부) 위험이 커집니다. 중첩된 수량자가 들어간 패턴은 특정 입력에서 검사 시간이 기하급수적으로 늘어나 서버 응답을 멈추게 할 수도 있습니다.
그래서 최신 JavaScript 환경에서는 URL 생성자를 검증기로 쓰는 방식이 사실상 표준으로 자리 잡았습니다. 2023년 말부터 주요 브라우저에 도입된 URL.canParse() 메서드를 쓰면 try catch 없이 한 줄로 확인할 수 있고, Node.js는 18.17 버전부터 지원합니다.
완벽한 URL 정규식은 존재하지 않습니다. 정규식은 입력 형태를 빠르게 거르는 1차 필터로 쓰고, 최종 판정은 언어에 내장된 URL 파서에 맡기는 조합이 실무에서 가장 안정적입니다.
다만 URL 파서에도 주의점이 있습니다. 문법만 맞으면 존재하지 않는 도메인도 그대로 통과합니다. 검증의 목적이 '형식 확인'인지 '실제 접속 가능 여부'인지 먼저 정하고, 후자라면 HEAD 요청 등으로 실제 응답을 확인하는 단계를 별도로 두어야 합니다.
실전 활용 팁과 주의사항
패턴을 적용하기 전에 입력값 전처리부터 챙기는 것이 좋습니다. 사용자 입력에는 앞뒤 공백이 섞여 들어오는 경우가 흔하므로 trim()을 먼저 적용하고, 프로토콜 대소문자를 구분하지 않도록 i 플래그를 붙입니다.
^와 $ 앵커를 빼먹으면 'abc https://example.com xyz' 같은 문자열도 통과합니다. 검증용 정규식에는 시작과 끝 앵커를 반드시 포함하세요.이런 형태 검증은 URL에만 쓰이는 것이 아닙니다. HEX 색상 코드를 받는 입력란이라면 ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$ 패턴으로 잘못된 값을 걸러낼 수 있습니다. 색상 변환기 같은 온라인 도구도 입력된 색상 값이 올바른 형식인지 이런 검증을 거친 뒤 변환을 수행합니다. 사이트를 배포하기 전이라면 파비콘 생성기로 만든 아이콘 파일의 경로나 사이트맵에 등록한 URL 목록을 정규식으로 일괄 점검하는 것도 링크 실수를 줄이는 방법입니다.
정리하면 지금 프로젝트에서 확인할 것은 두 가지입니다. 첫째, URL 입력란의 정규식에 앵커가 제대로 들어 있는지 점검하세요. 둘째, 정규식은 1차 필터로만 쓰고 최종 검증은 URL.canParse()나 urlparse 같은 내장 파서로 옮기세요. 이 두 가지만 적용해도 URL 검증 관련 버그의 대부분을 예방할 수 있습니다.