정규표현식 패턴
정규표현식이란
정규표현식(Regular Expression)은 문자열에서 특정 패턴을 찾거나, 검사하거나, 치환할 때 사용하는 문법이다. 이메일 형식 검증, 전화번호 추출, 특정 단어 치환처럼 “문자열이 원하는 규칙을 따르는지” 확인해야 하는 상황에서 자주 쓰인다.
정규표현식 만드는 방법 두 가지
// 1. 리터럴 방식 (가장 많이 씀)
const regex1 = /gori/;
// 2. 생성자 방식 (패턴을 변수로 동적으로 만들어야 할 때 사용)
const pattern = "gori";
const regex2 = new RegExp(pattern);리터럴 방식은 정규식이 고정된 경우에, 생성자 방식은 사용자 입력값 등으로 패턴이 동적으로 바뀌어야 할 때 주로 쓰인다.
기본 문법 (패턴, 플래그)
자주 쓰는 패턴 기호
| 기호 | 의미 | 예시 |
|---|---|---|
. | 임의의 문자 1개 | /g.ri/ → “gori”, “gxri” 매칭 |
* | 앞 문자가 0개 이상 | /go*/ → “g”, “go”, “goo” 매칭 |
+ | 앞 문자가 1개 이상 | /go+/ → “go”, “goo” 매칭 (“g”는 매칭 안 됨) |
? | 앞 문자가 0개 또는 1개 | /colou?r/ → “color”, “colour” 매칭 |
\d | 숫자 (0-9) | /\d{3}/ → 숫자 3자리 |
\w | 문자, 숫자, 언더스코어 | /\w+/ → “gori_01” 매칭 |
\s | 공백 문자 | /gori\sdev/ → “gori dev” 매칭 |
^ | 문자열의 시작 | /^gori/ → “gori”로 시작하는 문자열 |
$ | 문자열의 끝 | /dev$/ → “dev”로 끝나는 문자열 |
[] | 문자 집합 (하나 선택) | /[aeiou]/ → 모음 하나 |
[^] | 부정 문자 집합 | /[^0-9]/ → 숫자가 아닌 문자 |
{n,m} | 앞 문자가 n~m번 반복 | /\d{3,4}/ → 숫자 3~4자리 |
| | OR 조건 | /gori|chulsu/ → “gori” 또는 “chulsu” |
플래그 (정규식 뒤에 붙이는 옵션)
| 플래그 | 의미 |
|---|---|
g | 전체 검색 (global) — 첫 번째 매칭에서 멈추지 않고 모든 매칭을 찾음 |
i | 대소문자 구분 없음 (ignore case) |
m | 여러 줄 모드 (multiline) — ^, $가 각 줄의 시작/끝에도 적용 |
const text = "Gori and gori are good developers";
console.log(text.match(/gori/)); // ["gori"] (하나만 찾고 멈춤, 대소문자 구분)
console.log(text.match(/gori/gi)); // ["Gori", "gori"] (전체 + 대소문자 무시)자주 쓰는 메서드
test() — 매칭 여부를 boolean으로 반환
주로 유효성 검사에 사용한다.
const idRegex = /^[a-z0-9]{4,12}$/;
console.log(idRegex.test("gori01")); // true
console.log(idRegex.test("G!")); // falsematch() — 매칭된 결과를 배열로 반환
const str = "제 아이디는 gori01이고, 친구 아이디는 gori02입니다";
console.log(str.match(/gori\d{2}/)); // ["gori01"] (g 플래그 없으면 첫 매칭만)
console.log(str.match(/gori\d{2}/g)); // ["gori01", "gori02"] (g 플래그로 전체 매칭)matchAll() — 매칭 결과를 상세 정보와 함께 순회 가능한 형태로 반환
캡처 그룹 정보까지 필요할 때 match()보다 유용하다. (반드시 g 플래그 필요)
const str = "gori01, gori02";
const matches = [...str.matchAll(/gori(\d{2})/g)];
matches.forEach(m => {
console.log(m[0], m[1]); // 전체 매칭, 캡처 그룹 값
});
// gori01 01
// gori02 02replace() / replaceAll() — 매칭된 부분을 치환
const text = "안녕하세요 gori입니다";
console.log(text.replace(/gori/, "고리")); // 안녕하세요 고리입니다
const nums = "010-1234-5678";
console.log(nums.replace(/\d{4}/g, "****")); // 010-****-****exec() — 매칭 결과를 상세 정보와 함께 반환 (반복 호출 시 다음 매칭으로 이동)
const regex = /gori/g;
const str = "gori and gori";
let result;
while ((result = regex.exec(str)) !== null) {
console.log(`"${result[0]}" found at index ${result.index}`);
}
// "gori" found at index 0
// "gori" found at index 9메서드 정리
| 메서드 | 소속 | 반환값 | 주 용도 |
|---|---|---|---|
| test | RegExp | boolean | 유효성 검사 |
| match | String | 매칭 배열 또는 null | 매칭된 값 추출 |
| matchAll | String | Iterator | 캡처 그룹 포함 전체 매칭 순회 |
| replace | String | 치환된 문자열 | 문자열 치환 |
| exec | RegExp | 매칭 정보 배열 또는 null | 반복 매칭, 상세 정보 필요 시 |
캡처 그룹 / 전방·후방 탐색
캡처 그룹 ()
패턴의 일부를 괄호로 묶으면, 매칭된 값 중 그 부분만 따로 추출할 수 있다.
const str = "이름: gori, 나이: 20";
const regex = /이름: (\w+), 나이: (\d+)/;
const result = str.match(regex);
console.log(result[0]); // "이름: gori, 나이: 20" (전체 매칭)
console.log(result[1]); // "gori" (첫 번째 캡처 그룹)
console.log(result[2]); // "20" (두 번째 캡처 그룹)이름이 있는 캡처 그룹 (Named Capture Group)
(?<이름>...) 형식으로 캡처 그룹에 이름을 붙이면, 인덱스 대신 이름으로 접근할 수 있어 가독성이 좋아진다.
const str = "이름: gori, 나이: 20";
const regex = /이름: (?<name>\w+), 나이: (?<age>\d+)/;
const result = str.match(regex);
console.log(result.groups.name); // "gori"
console.log(result.groups.age); // "20"비캡처 그룹 (?:...)
그룹으로 묶긴 하되, 결과에서 캡처 값으로 추출하고 싶지 않을 때 사용한다.
const regex = /(?:gori|chulsu)\d{2}/;
console.log("gori01".match(regex)[0]); // "gori01"전방 탐색 (Lookahead) — (?=...), (?!...)
특정 패턴 뒤에 오는 조건을 확인하지만, 그 부분은 매칭 결과에 포함시키지 않는다.
// (?=...): 뒤에 특정 패턴이 있는 경우에만 매칭
const str = "gori100원, gori200개";
console.log(str.match(/gori\d+(?=원)/)); // ["gori100"] ("원" 앞의 것만 매칭)
// (?!...): 뒤에 특정 패턴이 없는 경우에만 매칭
console.log(str.match(/gori\d+(?!원)/)); // ["gori200"]후방 탐색 (Lookbehind) — (?<=...), (?<!...)
특정 패턴 앞에 오는 조건을 확인하지만, 그 부분은 매칭 결과에 포함시키지 않는다.
const str = "가격: 1000원, 수량: 5개";
// (?<=...): 앞에 특정 패턴이 있는 경우에만 매칭
console.log(str.match(/(?<=가격: )\d+/)); // ["1000"]
// (?<!...): 앞에 특정 패턴이 없는 경우에만 매칭
console.log(str.match(/(?<!가격: )\d+(?=개)/)); // ["5"]실무 예시 — 회원가입 폼 검증
function validateSignup({ id, password, email }) {
const idRegex = /^[a-z0-9]{4,12}$/;
const passwordRegex = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/;
const emailRegex = /^[\w.-]+@[\w.-]+\.\w+$/;
return {
isIdValid: idRegex.test(id),
isPasswordValid: passwordRegex.test(password),
isEmailValid: emailRegex.test(email)
};
}
console.log(validateSignup({
id: "gori01",
password: "gori1234!",
email: "gori@example.com"
}));
// { isIdValid: true, isPasswordValid: true, isEmailValid: true }비밀번호 정규식은 전방 탐색((?=...))을 여러 개 겹쳐서 “영문 포함”, “숫자 포함”, “특수문자 포함” 조건을 각각 확인하고, 마지막에 .{8,}로 전체 길이를 검사하는 방식이다.