프론트엔드 작업을 할 때 마다 사용자의 입력을 확인하고 변경해야 할 일이 생긴다.
이럴 때 자주 사용하는 것이 정규 표현식, RegExp 이다.
당연하게 이거를 안쓰고 직접 체크할 수 있고, 변경 또한 replace 를 활용하면 된다.
하지만 이 정규표현식을 알고 사용해본 사람들이면 만드는 것이 어려울 뿐 효과는 확실하다는 것을 알 것이다.
본인도 해당 상황에 많이 사용을 했었는데 실제로 내가 원하는 정규표현식을 만들라고 하면 어려워한다.
그렇기 때문에 이번 기회에 한 번 알아보려고 한다.
정규 표현식의 정의는 무엇일까?
정규 표현식이란 일정한 패턴을 가진 문자열의 집합을 표현하기 위해 사용하는 형식 언어이다.
이러한 정규 표현식은 자바스크립트만 있는 것은 아니며 대다수의 프로그래밍 언어에 내장되어 있다.
그러면 이 정규표현식은 어디에 쓰일까? 바로 문자열을 대상으로 한 패턴 매칭 기능에 사용한다.
패턴 매칭 기능이란 특정 패턴과 일치하는 문자열을 검색하거나 추출 또는 치환하는 기능을 말한다.
우리가 회원가입을 할 때 주로 입력하는 것이 무엇일까?
이메일, 전화번호, 이름, 생년월일 등등 이 있을 것이다.
이것들의 구조를 생각해보면 정형화 된 친구들이 많다.
예를 들어 이메일은 [문자들]@[문자들].[문자들],
전화번호는 [숫자 2~3개] - [숫자 4개] - [숫자 4개],
생년월일은 [숫자 4개] - [숫자 1~2개] - [숫자 1~2개],
이렇게 유저들이 정확하게 입력해주면 좋겠지만 그렇지 않은 경우가 많다.
부정확하게 입력된 것을 확인하고 이를 인지시켜줘야 할 때, 우리가 생각하는 구조와 맞는지를 확인해야 한다. 이럴 때 정규 표현식을 사용할 수 있다.
const phone = '010-2222-33사4';
const regExp = /^\d{3}-\d{4}-\d{4}$/;
regExp.test(phone); // false
위 경우는 핸드폰 번호를 채크하는 정규식을 만들어서 테스트를 실행시키는 경우이다.
우리가 회원가입을 했을 때 입력한 것이 맞는지 체크해주는 것이라 보면 된다.
우리는 지금 정규 표현식이 뭔지, 왜 필요한 지를 알아보았다.
본격 적으로 어떻게 만들고 사용하는지 알아보자.
정규 표현식을 만드는 방법에는 리터럴을 사용한 방법(위에서 사용), RegExp 생성자 함수를 통한 생성, 이렇게 두 가지 방법이 있다.
const regExp1 = /^\d{3}-\d{4}-\d{4}$/;
const regExp2 = new RegExp(/^\d{3}-\d{4}-\d{4}$/);
const regExp3 = new RegExp(hi, 'g');
이 두 가지 방법 중 본인이 원하는 방법을 사용해도 무방하다.
다른 점이라면 생성자 함수를 사용하는 방식에서는 플래그를 뒤로 뺄 수 있고, 입력하는 패턴의 앞 뒤의 / 를 생략할 수 있다.
본인이 생각하기에는 리터럴 방식이 더 간단해보여서 해당 방식을 기준으로 글을 이어갈 생각이다.
그러면 내부에 들어가는 것은 무엇일까?
정규식은 크게 패턴과 플래그로 구성된다.
/ 의 경우는 시작기호, 종료기호를 나타내며, 내부에 들어가있는 것이 패턴, 패턴 뒤에 붙는 것이 플래그이다.
const regExp = /pattern/gi // 시작기호(/), 패턴, 종료기호(/), 플래그 순이다.
우리는 지금 어떻게 만드는지 구조에 대해서 배웠다.
그러면 차후 알아볼 것은 아래와 같을 것이다.
하나씩 살펴보도록 하자
주로 우리는 정규 표현식을 특정 문자 구조가 맞는지를 검사하는데 사용한다.
그렇다면 그런 기능을 제공하는 메서드가 있을 것이다.
크게 exec, test, match 등을 사용할 수 있다.
하나씩 살펴보자.
exec 는 인수로 전달받은 문자열에 대해 정규 표현식의 패턴을 검색하여 매칭 결과를 배열로 반환한다. 이 때 매칭 결과가 없는 경우 null 을 반환한다.
const target = 'Is this all is?';
const regExp = /is/;
regExp.exec(target);
// ["is", index: 5, input: "Is this all is?", groups: undefined]
이 때 주의해야 할 것은 문자열 내의 모든 패턴을 검색하는 g 플래그를 설정을 해도 첫 번째 매칭 결과만 반환한다는 점을 주의해야 한다.
test 는 인수로 전달받은 문자열에 대해 정규 표현식의 패턴을 검색하여 매칭 결과를 불리언 값으로 반환한다.
const target = "Is this all is?";
const regExp = /is/;
regExp.test(target); // true
match 는 위 두 메서드와는 다르게 String 의 프로토타입 객체에 있는 메서드이다.
위 두 개는 RegExp 의 프로토타입 객체에 있는 메서드이다.
match 는 대상 문자열과 인수로 전달받은 정규 표현식과의 매칭 결과를 배열로 반환한다.
const target = "Is this all is?";
const regExp1 = /is/;
const regExp2 = /is/g;
target.match(regExp1); // ["is", index: 5, input: "Is this all is?", groups: undefined]
target.match(regExp2); // ["is", "is"]
exec 와 유사해보이나 다른 점이 있다면 모든 패턴을 검색하는 g 플래그를 설정하면 모든 매칭 결과를 배열로 반환한다는 차이가 있다.
플래그는 정규 표현식의 검색 방식을 설정하기 위해서 사용한다.
총 6개의 플래그가 있는데 이 중에서 자주 사용할 3개를 살펴보도록 하자.
그 전에 플래그 설명을 조금 이어가자면
플래그는 옵션이다. 따라서 옵션을 아에 선택하지 않아도 무방한데 이럴 경우 디폴트는 대소문자를 구별하고 패턴 매칭이 여러번 일어나도 맨 처음의 값만 찾고 종료된다.
또한 플래그는 여러개를 사용해도 좋은데 이는 예제코드에서 보여주도록 하겠다.
const target = "Is this all is?";
target.match(/is/);
// ["is", index: 5, input: "Is this all is?", groups: undefined]
target.match(/is/i);
// ["Is", index: 0, input: "Is this all is?", groups: undefined]
target.match(/is/g);
// ["is", "is"]
target.match(/is/ig);
// ["Is", "is", "is"]
정규 표현식에서 패턴은 문자열의 일정한 규칙을 표현하기 위해 사용한다.
이는 우리가 앞에서 살펴본 예제 코드에서도 확인할 수 있다.
해당 방식처럼 궁금한 문자열을 넣을 수도 있지만 특별한 의미를 가지는 메타문자를 활용해서 처리를 할 수 있다.
이러한 경우에 집중해서 한번 살펴보려한다.
아무런 문자가 와도 상관 없어요~ 를 표현하기 위해서 우리는 . 을 사용할 수 있다.
알파벳, 숫자, 특수문자, 빈칸 등등이 다 매칭이 된다.
아래 예제코드를 보면 이해할 수 있다.
const target = "Is this all there is?";
const regExp = /.../g;
target.match(regExp);
// ["Is ", "thi", "s a", "ll ", "the", "re ", "is?"]
우리는 특정 문자가 반복되는 것을 찾고 싶을 수 있다.
또한 그 문자가 몇 번까지 반복되는 지 모를 때도 있다.
이럴 때 {} , +, ? 를 사용할 수 있다.
{m, n} 을 통해서 우리는 앞선 패턴을 최소 m번, 최대 n번 반복되는 문자열을 찾아낼 수 있다. 예제를 살펴보면서 아 이렇게 쓸 수 있구나 확인해보길 바란다.
+ 는 앞선 패턴이 최소 한번 이상 반복되는 문자열을 의미한다.
즉 + 는 {1,} 과 같다. 이 또한 예제를 통해 빠르게 이해할 수 있다.
? 의 경우에는 앞선 패턴이 최대 한 번이상 반복되는 문자열을 의미한다.
{0,1} 이라고 생각하면 쉽다. 있어도 되고 없어도 되는 문자를 처리할 때 사용하면 된다.
const target1 = "A AA B BB Aa Bb AAA";
// 시작 n, 끝 m 값을 모두 넣어준 경우
const regExp1 = /A{1,2}/g;
// 끝 값을 안넣어준 경우 -> 이 경우는 {n,n} 으로 동작해 정확하게 n 번 반복만 찾는다.
const regExp2 = /A{2}/g;
// 끝 값을 공란으로 비운 경우 -> 이 경우는 n 번 이상 반복되는 문자열을 찾는다.
const regExp3 = /A{2,}/g;
// + 의 경우 -> {1,} 이라 생각하면 된다.
const regExp4 = /A+/g;
// ? 의 경우 -> {0,1} 이라 생각하면 된다.
const target2 = "color colour";
const regExp5 = /colou?r/g;
target1.match(regExp1); // ["A", "AA", "A", "AA", "A"]
target1.match(regExp2); // ["AA", "AA"]
target1.match(regExp3); // ["AA", "AAA"]
target1.match(regExp4); // ["A", "AA", "A", "AAA"]
target2.match(regExp5); // ["color", "colour"]
이놈 또는 저놈만 있으면 된다! 를 표현하기 위해서 우리는 | 기호를 사용한다.
논리 연산에 사용하던 그 기호 맞다. 그렇기에 예제를 보면서 한번 이해해보자.
const target = "A AA B BB Aa Bb AAA";
const regExp = /A|B/g;
target.match(regExp); // ["A", "A", "A", "B", "B", "B", "A", "B"]
하지만 생각을 해보면 이거는 너무 귀찮은 경우도 있을 것이다.
왜냐하면 우리는 알파벳으로만 몇 자 이상 같은 경우를 사용할 때도 있을 것이다.
이럴 때는 모든 알파벳을 | 로 처리해야 할까?
이런 경우를 위해서 [] 라는 좋은 도구가 있다.
[] 안에 or 처리할 문자들을 넣어두면 된다.
또한 더 좋은 처리가 가능한데 예를 들어서 알파벳, 숫자 같은 경우는 [A-Z], [a-z], [0-9] 라는 방식으로 처리가 가능하다. 이는 예제를 통해서 더 알아보자.
const target1 = "A AA B BB Aa Bb AAA";
// 대문자만 이어진 경우
const regExp1 = /[A-Z]+/g;
// 대소문자 구분없이 이어진 경우
const regExp2 = /[A-Za-z]+/g;
target1.match(regExp1); // ["A", "AA", "B", "BB", "A", "B", "AAA"]
target1.match(regExp2); // ["A", "AA", "B", "BB", "Aa", "Bb", "AAA"]
const target2 = "12,345";
// 숫자로만 이어진 경우
const regExp3 = /[0-9]+/g;
// 숫자와 쉼표로 이어진 경우
const regExp4 = /[0-9,]+/g;
target2.match(regExp3); // ["12", "345"]
target2.match(regExp4); // ["12,345"]
위 에제 코드를 더 간단하게 하기 위해 제공되는 것들도 있다.
이는 다음과 같다.
앞서 알아본 [] 안에 ^ 기호를 사용하면 not 의 의미를 가진다.
예를 들어서 [^0-9] 라고 우리가 패턴을 만든다면 이는 숫자를 아에 제외한다는 뜻이다.
const target = "AA BB 12 Aa Bb";
const regExp = /[^0-9]+/g;
target.match(regExp); // ["AA BB ", " Aa Bb"]
[] 밖에서 ^ 를 사용하면 문자열의 시작을 의미하고,
$ 표시는 문자열의 마지막을 의미한다.
한마디로 접두사, 접미사 관련 처리를 할 수 있다는 것이다.
const target = "https://test.com"
const regExp1 = /^https/;
const regExp2 = /com$/;
regExp1.test(target); // true
regExp2.test(target); // true
나름은 자주 사용하던 정규 표현식인데 매번 검색하면서 오.. 이거.. 하면서 사용을 했었는데 이제는 조금이나마 이해를 한 것 같다.
물론 이를 익숙하게 사용하고 하려먼 자주 사용해보고 고민해보는 시간이 필요할 것이라고 보지만 문법이나 사용방식을 익혔다는 점에서 만족스러운 학습 정리였다.
또 사용하면서 꿀팁이라던지 자주 사용하는 친구들이 있다면 소개를 해보도록 할 예정이다.