정규 표현식
======

* « 이전
* 다음 »

정규 표현식, 또는 정규식은 문자열에서 특정 문자 조합을 찾기 위한 패턴입니다. JavaScript에서는 정규 표현식도 객체로서, `RegExp`의 `exec()` (en-US)와 `test()` 메서드를 사용할 수 있습니다. `String`의 `match()`, `matchAll()` (en-US), `replace()` (en-US), `replaceAll()`, `search()` (en-US), `split()` 메서드와도 함께 사용할 수 있습니다. 이 장에서는 JavaScript의 정규 표현식을 설명합니다.

정규 표현식 만들기
----------

정규 표현식은 두 가지 방법으로 만들 수 있습니다.

* 정규 표현식 리터럴. 다음과 같이 슬래시로 패턴을 감싸서 작성합니다.

```js
const re = /ab+c/;
```

정규 표현식 리터럴은 스크립트를 불러올 때 컴파일되므로, 바뀔 일이 없는 패턴의 경우 리터럴을 사용하면 성능이 향상될 수 있습니다.
* `RegExp` 객체의 생성자 호출.

```js
const re = new RegExp("ab+c");
```

생성자 함수를 사용하면 정규 표현식이 런타임에 컴파일됩니다. 바뀔 수 있는 패턴이나, 사용자 입력 등 외부 출처에서 가져오는 패턴의 경우 이렇게 사용하세요.
정규 표현식 패턴 작성하기
--------------

정규 표현식 패턴은 `/abc/`처럼 단순한 문자로 구성하거나, `/ab+c/`와 `/Chapter (\d+)\.\d*/`처럼 단순한 문자와 특수 문자의 조합으로 구성할 수도 있습니다. 특히 `(\d+)`에 나타난 괄호는 정규 표현식에서 기억 장치처럼 쓰여서, 괄호의 안쪽 패턴과 일치한 부분을 나중에 사용할 수 있도록 기억합니다. 그룹 사용하기 (en-US)에서 더 알아보세요.

### 단순 패턴 사용하기

단순 패턴은 문자열을 있는 그대로 탐색할 때 사용합니다. 예를 들어, `/abc/` 패턴은 문자열에서 정확한 순서로 `"abc"`라는 문자의 조합이 나타나는 부분과 일치합니다. 그러므로 이 패턴은 `"Hi, do you know your abc's?"`와 `"The latest airplane designs evolved from slabcraft."` 두 문자열에서 일치에 성공하고, 일치하는 부분은 `"abc"`일 것입니다. 반면 `"Grab crab"`에서는 일치하지 않는데, 이 문자열은 부분 문자열로 `"ab c"`를 포함하긴 하지만, 정확하게 `"abc"`를 포함하지는 않기 때문입니다.

### 특수 문자 사용하기

하나 이상의 "b"를 찾는다거나 공백 문자를 찾는 등 직접적인 일치 이상의 탐색이 필요할 땐 특수 문자를 사용합니다. 예컨대 "하나의 `"a"` 이후에 0개 이상의 `"b"`, 그 뒤의 `"c"`"와 일치해야 하면 `/ab*c/` 패턴을 사용할 수 있습니다. `"b"` 뒤의 `*`는 "이전 항목의 0번 이상 반복"을 의미합니다. 이 패턴을 문자열 `"cbbabbbbcdebc"`에 대해 사용하면, 일치하는 부분 문자열은 `"abbbbc"`일 것입니다.

아래의 문서들에선 각각의 범주에 속하는 다양한 특수 문자의 목록과 설명, 예제를 찾아볼 수 있습니다.

어서션 (en-US)

어서션에는 줄이나 단어의 시작과 끝을 나타내는 경계와, 일치가 가능한 방법을 나타내는 패턴(전방탐색, 후방탐색, 조건 표현식 등)이 포함됩니다.

문자 클래스 (en-US)

글자와 숫자처럼 다른 유형의 문자를 구분합니다.

그룹과 범위 (en-US)

표현 문자의 그룹과 범위를 나타냅니다.

수량자 (en-US)

일치할 문자나 표현이 반복되어야 할 횟수를 나타냅니다.

유니코드 속성 이스케이프 (en-US)

대/소문자, 수학 기호, 문장 부호처럼, 유니코드 문자 속성에 따라 문자를 구분합니다.

아래 표는 정규 표현식에서 사용하는 모든 특수문자의 목록입니다.

정규 표현식 특수문자| 문자 / 조합 | 문서 |
| --- | --- |
| `\`, `.`, `\cX`, `\d`,
 `\D`, `\f`, `\n`, `\r`,
 `\s`, `\S`, `\t`, `\v`,
 `\w`, `\W`, `\0`, `\xhh`,
 `\uhhhh`, `\uhhhhh`, `[\b]` | 문자 클래스 (en-US) |
| `^`, `$`, `x(?=y)`,
 `x(?!y)`, `(?<=y)x`,
 `(?<!y)x`, `\b`, `\B` | 어서션 (en-US) |
| `(x)`, `(?:x)`, `(?<Name>x)`,
 `x|y`, `[xyz]`, `[^xyz]`,
 `\*Number*` | 그룹과 범위 (en-US) |
| `*`, `+`, `?`,
 `x{*n*}`, `x{*n*,}`,
 `x{*n*,*m*}` | 수량자 (en-US) |
| `\p{*UnicodeProperty*}`,
 `\P{*UnicodeProperty*}` | 유니코드 속성 이스케이프 (en-US) |

**참고:** 위 문서의 일부만 발췌해 정리해놓은, 더 큰 치트 시트도 있습니다 (en-US).

### 이스케이핑

특수 문자를 있는 그대로 탐색(`"*"`을 직접 찾는 등)해야 하는 경우, 특수 문자 앞에 역슬래시(\)를 배치해서 이스케이프 해야 합니다. 예를 들어 `"a"` 뒤의 별표(`"*"`) 뒤의 `"b"`와 일치해야 하면 `/a\*b/`를 사용하면 됩니다. 역슬래시가 `"*"`를 "이스케이프"해서, 특수 문자가 아닌 문자 리터럴로 취급합니다.

마찬가지로, 슬래시(/)와 일치해야 하는 경우에도 이스케이프를 해야 합니다. 그냥 빗금을 사용하면 패턴이 끝나버립니다. 예를 들어 문자열 "/example/"과 그 뒤 하나 이상의 알파벳을 찾으려면 `/\/example\/[a-z]/`를 사용할 수 있습니다. 각각의 슬래시 앞에 놓인 역슬래시가 슬래시를 이스케이프합니다.

리터럴 역슬래시에 일치하려면 역슬래시를 이스케이프합니다. "A:\", "B:\", "C:\", ..., "Z:\"와 일치하는 패턴은 `/[A-Z]:\\/`입니다. 앞의 역슬래시가 뒤의 역슬래시를 이스케이프해서, 결과적으로 하나의 리터럴 역슬래시와 일치하게 됩니다.

`RegExp` 생성자와 문자열 리터럴을 사용하는 경우, 역슬래시가 문자열 리터럴의 이스케이프로도 작동한다는 것을 기억해야 합니다. 그러므로 정규 표현식의 역슬래시를 나타내려면 문자열 리터럴 수준의 이스케이프도 해줘야 합니다. 즉, 앞서 살펴본 `/a\*b/` 패턴을 생성하려면 `new RegExp("a\\*b")`가 되어야 합니다.

이스케이프 되지 않은 문자열을 이미 가지고 있을 땐 `String.replace` (en-US)를 활용해 이스케이프를 해줄 수 있습니다.


```js
function escapeRegExp(string) {
  return string.replace(/[.\*+?^${}()|[\]\\]/g, "\\$&"); // $&은 일치한 문자열 전체를 의미
}
```


정규 표현식 뒤의 "g"는 전체 문자열을 탐색해서 모든 일치를 반환하도록 지정하는 전역 탐색 플래그입니다. 플래그에 대해서는 아래의 플래그를 활용한 고급 탐색에서 확인할 수 있습니다.

"왜 `escapeRegExp()`가 JavaScript의 일부가 아닌가요?" 관련 제안은 있었으나 TC39가 거부했습니다.

### 괄호 사용하기

정규 표현식의 아무 부분이나 괄호로 감싸게 되면, 그 부분과 일치하는 부분 문자열을 기억하게 됩니다. 기억한 부분 문자열은 불러와서 다시 사용할 수 있습니다. 그룹과 범위 (en-US) 문서에서 자세히 알아보세요.

JavaScript에서 정규 표현식 사용하기
------------------------

정규 표현식은 `RegExp`의 메서드 `test()`와 `exec()` (en-US), `String`의 메서드 `match()`, `replace()` (en-US), `search()` (en-US), `split()`에서 사용할 수 있습니다.

| 메서드 | 설명 |
| --- | --- |
| `exec()` (en-US) | 문자열에서 일치하는 부분을 탐색합니다. 일치 정보를 나타내는 배열, 또는 일치가 없는 경우 `null`을 반환합니다. |
| `test()` | 문자열에 일치하는 부분이 있는지 확인합니다. `true` 또는 `false`를 반환합니다. |
| `match()` | 캡처 그룹을 포함해서 모든 일치를 담은 배열을 반환합니다. 일치가 없으면 `null`을 반환합니다. |
| `matchAll()` (en-US) | 캡처 그룹을 포함해서 모든 일치를 담은 반복기를 반환합니다. |
| `search()` (en-US) | 문자열에서 일치하는 부분을 탐색합니다. 일치하는 부분의 인덱스, 또는 일치가 없는 경우 `-1`을 반환합니다. |
| `replace()` (en-US) | 문자열에서 일치하는 부분을 탐색하고, 그 부분을 대체 문자열로 바꿉니다. |
| `replaceAll()` | 문자열에서 일치하는 부분을 모두 탐색하고, 모두 대체 문자열로 바꿉니다. |
| `split()` | 정규 표현식 또는 문자열 리터럴을 사용해서 문자열을 부분 문자열의 배열로 나눕니다. |

문자열 내부에 패턴과 일치하는 부분이 존재하는지만 알아내려면 `test()`나 `search()` 메서드를 사용하세요. 더 느리더라도 일치에 관한 추가 정보가 필요하면 `exec()`과 `match()` 메서드를 사용하세요. 일치하는 부분이 존재하면, `exec()`과 `match()`는 일치에 관한 데이터를 포함한 배열을 반환하고, 일치에 사용한 정규 표현식 객체의 속성을 업데이트합니다. 일치하지 못한 경우 `null`을 반환합니다. (`null`은 조건 평가 시 `false`와 같습니다)

아래의 예제에서는, 문자열에서 일치하는 부분을 찾기 위해 `exec()` 메서드를 사용합니다.


```js
const myRe = /d(b+)d/g;
const myArray = myRe.exec("cdbbdbsbz");
```


만약 정규 표현식 객체의 속성에 접근할 필요가 없으면 아래와 같이 짧게 쓸 수도 있습니다.


```js
const myArray = /d(b+)d/g.exec("cdbbdbsbz");
// 'cdbbdbsbz'.match(/d(b+)d/g); 와 비슷하지만,
// 'cdbbdbsbz'.match(/d(b+)d/g)의 반환 값은 [ 'dbbd' ]인 반면
// /d(b+)d/g.exec('cdbbdbsbz')의 반환 값은 [ 'dbbd', 'bb', index: 1, input: 'cdbbdbsbz' ]
```


(아래의 `exec()`과 전역 탐색 플래그 사용하기에서 동작 방식의 차이에 대해 더 알아보세요)

정규 표현식을 문자열에서 만들고 싶으면 아래처럼 사용할 수도 있습니다.


```js
const myRe = new RegExp("d(b+)d", "g");
const myArray = myRe.exec("cdbbdbsbz");
```


아래의 표는 위 스크립트에서 일치를 성공한 후, 반환하는 배열과 업데이트되는 정규 표현식 객체의 속성입니다.

정규 표현식 실행 결과| 객체 | 속성 또는 인덱스 | 설명 | 위 예제에서의 값 |
| --- | --- | --- | --- |
| `myArray` |  | 일치한 문자열 및 기억한 모든 부분 문자열. | `['dbbd', 'bb', index: 1, input: 'cdbbdbsbz']` |
| `index` | 일치한 부분이 주어진 문자열에서 위치한 인덱스. (0부터 시작) | `1` |
| `input` | 주어진 원본 문자열. | `'cdbbdbsbz'` |
| `[0]` | 마지막으로 일치한 부분 문자열. | `'dbbd'` |
| `myRe` | `lastIndex` | 
 다음 일치를 시작할 인덱스. (g 옵션을 지정한 정규 표현식의 경우에만 설정됩니다.
 플래그를 활용한 고급 탐색을 참고하세요)
  | `5` |
| `source` | 패턴의 텍스트. 정규 표현식이 생성될 때 갱신됩니다. 실행 시점에는 갱신되지 않습니다. | `'d(b+)d'` |

위 예제의 두 번째 형태처럼, 정규 표현식 객체를 변수에 대입하지 않고도 사용할 수 있습니다. 하지만, 이러면 매 사용마다 정규 표현식 객체가 새로 생성되며, 업데이트되는 속성에 접근할 수 없습니다. 다음과 같은 코드를 생각해보겠습니다.


```js
const myRe = /d(b+)d/g;
const myArray = myRe.exec("cdbbdbsbz");
console.log(`lastIndex의 값은 ${myRe.lastIndex}`);

// "lastIndex의 값은 5"
```


그러나 위의 코드 대신 아래 코드를 사용하게 되면...


```js
const myArray = /d(b+)d/g.exec("cdbbdbsbz");
console.log(`lastIndex의 값은 ${/d(b+)d/g.lastIndex}`);

// "lastIndex의 값은 0"
```


두 개의 `/d(b+)d/g` 는 서로 다른 정규 표현식 객체이므로 별개의 `lastIndex` 속성을 갖습니다. 정규 표현식 객체의 속성에 접근해야 하면, 우선 변수에 할당하세요.

### 플래그를 활용한 고급 탐색

정규 표현식은 전역 탐색이나 대소문자 무시와 같은 특성을 지정하는 플래그를 가질 수 있습니다. 플래그는 단독으로 사용할 수도 있고, 순서에 상관 없이 한꺼번에 여럿을 지정할 수도 있습니다.

| 플래그 | 설명 | 대응하는 속성 |
| --- | --- | --- |
| `d` | 부분 문자열 일치에 대해 인덱스 생성. | `RegExp.prototype.hasIndices` (en-US) |
| `g` | 전역 탐색. | `RegExp.prototype.global` |
| `i` | 대소문자를 구분하지 않음. | `RegExp.prototype.ignoreCase` |
| `m` | 여러 줄에 걸쳐 탐색. | `RegExp.prototype.multiline` |
| `s` | 개행 문자가 `.`과 일치함. | `RegExp.prototype.dotAll` |
| `u` | "unicode", 패턴을 유니코드 코드 포인트의 시퀀스로 간주함. | `RegExp.prototype.unicode` (en-US) |
| `y` | "접착" 탐색, 대상 문자열의 현재 위치에서 탐색을 시작함. `sticky` (en-US)를 참고하세요. | `RegExp.prototype.sticky` (en-US) |

플래그는 다음과 같은 구문으로 정규 표현식에 지정할 수 있습니다.


```js
const re = /pattern/flags;
```


생성자를 사용할 경우 이렇게 지정합니다.


```js
const re = new RegExp("pattern", "flags");
```


플래그는 정규식과 완전히 합쳐지므로 나중에 추가하거나 제거할 수 없습니다.

예를 들어, `re = /\w+\s/g`는 한 개 이상의 글자와 그 뒤의 공백 하나를, 문자열 전체에 대해 탐색합니다.


```js
const re = /\w+\s/g;
const str = "fee fi fo fum";
const myArray = str.match(re);
console.log(myArray);

// ["fee ", "fi ", "fo "]
```


아래 코드는...


```js
const re = /\w+\s/g;
```


이렇게 생성자를 사용하도록 바꿀 수도 있습니다.


```js
const re = new RegExp("\\w+\\s", "g");
```


두 구문 모두 동일한 결과를 낳습니다.

`m` 플래그는 여러 줄에 걸친 입력 문자열을 여러 줄로 취급하게 합니다. 달리 말해, `m` 플래그를 지정할 경우, `^`와 `$`는 각각 전체 입력 문자열의 시작과 끝이 아니라, 각 줄의 시작과 끝에 대응하게 됩니다.

#### exec()과 전역 탐색 플래그 사용하기

`RegExp.prototype.exec()` (en-US) 메서드와 `g` 플래그를 사용하면, 일치한 부분 문자열들과 각각의 인덱스를 하나씩 순차적으로 반환합니다.


```js
const str = "fee fi fo fum";
const re = /\w+\s/g;

console.log(re.exec(str)); // ["fee ", index: 0, input: "fee fi fo fum"]
console.log(re.exec(str)); // ["fi ", index: 4, input: "fee fi fo fum"]
console.log(re.exec(str)); // ["fo ", index: 7, input: "fee fi fo fum"]
console.log(re.exec(str)); // null
```


반면, `String.prototype.match()` 메서드는 모든 일치를 한 번에 반환하지만, 각각의 인덱스는 포함하지 않습니다.


```js
console.log(str.match(re)); // ["fee ", "fi ", "fo "]
```

예제
--

**참고:** 다음 문서에서도 정규 표현식의 사용 예제를 볼 수 있습니다.

* `exec()` (en-US), `test()`, `match()`, `matchAll()` (en-US), `search()` (en-US), `replace()` (en-US), `split()` 메서드 참조
* 이 안내서의 하위 문서: 문자 클래스 (en-US), 어서션 (en-US), 그룹과 범위 (en-US), 수량자 (en-US), 유니코드 속성 이스케이프 (en-US)

### 정규 표현식 특수 문자를 사용한 입력 값 검증

아래 예제에서는 사용자가 전화번호를 입력해야 합니다. 사용자가 "확인" 버튼을 누르면 입력한 값을 스크립트로 검증합니다. 값이 유효하면, 즉 정규 표현식과 일치하는 문자의 시퀀스를 받았으면, 스크립트는 감사 메시지를 출력합니다. 값이 유효하지 않으면, 사용자에게 올바르지 않은 값임을 알려줍니다.

정규 표현식의 구조는 다음과 같습니다.

1. 데이터의 시작점(`^`)
2. 비캡처 그룹(`(?:)`)으로, 1의 뒤를 잇는 세 개의 숫자(`\d{3}`), 또는(`|`), 여는 괄호(`\(`)의 뒤를 잇는 세 개의 숫자(`\d{3}`)의 뒤를 잇는 닫는 괄호(`\)`)
3. 캡처 그룹(`()`)으로, 2의 뒤를 잇는 하나의 대시, 슬래시, 또는 마침표
4. 3의 뒤를 잇는 네 개의 숫자(`\d{4}`)
5. 4의 뒤를 잇는, 첫 번째 캡처 그룹에서 기억한 부분 문자열(`\1`)
6. 5의 뒤를 잇는 네 개의 숫자(`\d{4}`)
7. 데이터의 끝점(`$`)

#### HTML


```html
<p>
  전화번호를 입력 후 "확인" 버튼을 누르세요.
  <br />
  ###-####-####의 형식으로 입력하세요.
</p>
<form id="form">
  <input id="phone" />
  <button type="submit">확인</button>
</form>
<p id="output"></p>
```


#### JavaScript


```js
const form = document.querySelector("#form");
const input = document.querySelector("#phone");
const output = document.querySelector("#output");

const re = /^(?:\d{3}|\(\d{3}\))([-\/\.])\d{4}\1\d{4}$/;

function testInfo(phoneInput) {
  const ok = re.exec(phoneInput.value);

  if (!ok) {
    output.textContent = `형식에 맞지 않는 전화번호입니다. (${phoneInput.value})`;
  } else {
    output.textContent = `감사합니다. 전화번호는 ${ok[0]} 입니다.`;
  }
}

form.addEventListener("submit", (event) => {
  event.preventDefault();
  testInfo(input);
});
```


#### 결과

도구
--

RegExr

정규 표현식을 배우고, 만들고, 시험할 수 있는 온라인 도구입니다.

Regex tester

정규 표현식 생성기/디버거입니다.

Regex visualizer

시각적 정규 표현식 테스터입니다.

* « 이전
* 다음 »