[Javascript] 정규 표현식 총정리 | 자바스크립트에서 특정 문자열 패턴 찾기

Re_Go·2024년 7월 28일
0

Javascript

목록 보기
41/44
post-thumbnail

1. 아이디를 검증해보자

만약 다음과 같이 사용자에게서 ID를 받아오는 입력창과 버튼이 있다고 생각해 봅시다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>회원가입</title>
  </head>
  <body>
    <form id="sign">
      <input
        type="text"
        id="id"
        name="id"
        placeholder="아이디를 입력해 주세요."
      />
      <input type="submit" />
    </form>
  </body>
</html>
<script src="index.js" defer></script>

위 양식에서 사용자로부터 아이디를 입력받을텐데요. 이때 사용자에게 입력 받을 아이디에 대해서 제약 사항을 정해본다면 다음과 같이 지정할 수 있을 것 같습니다.

  • 아이디는 무조건 이메일 형식일 것
  • 이메일 아이디 중 아이디 부분은 숫자와 영어 혼합으로 다섯 자 이상일 것

그렇다면 이 조건을 한 번 JS 코드로 적어볼까요?

document.getElementById("sign").addEventListener("submit", function (event) {
  event.preventDefault(); // 버튼을 누를 때 폼은 기본적으로 페이지를 이동시키므로 이러한 기본 동작을 막습니다.

  // 사용자 입력 값 받아오기
  const email = document.getElementById("id").value;

  if (validateEmail(email)) {
    console.log("이메일이 유효합니다.");
  } else {
    console.log("이메일이 유효하지 않습니다.");
  }
});

funct>ion validateEmail(email) {
  const atIndex = email.indexOf("@");
  const dotIndex = email.lastIndexOf(".");

  // 기본적으로 이메일 형식을 id@domain.com 형식이므로 @의 인덱스와 .의 인덱스의 위치에 대한 검사를 실시함
  // atIndex < 1 : 적어도 문자나 숫자 이후에 오는 형식인지
  // dotIndex <= atIndex + 1 : . 기호가 @ 기호와 하나의 입력 형식보다 앞에 오지 않는지
  // dotIndex >= email.length - 1 :. 기호가 전체 이메일 중 끝에 있지는 않은지
  if (atIndex < 1 || dotIndex <= atIndex + 1 || dotIndex >= email.length - 1) {
    return false;
  }

  const userId = email.substring(0, atIndex);
  let hasEnglish = false;
  let hasDigit = false;

  // 2. 아이디중 아이디(이메일 앞에 오는)는
  // 영어 및 숫자 혼합으로 5자 이상 입력하여야 한다.
  for (let i = 0; i < userId.length; i++) {
    const char = userId.charCodeAt(i);
    if ((char >= 65 && char <= 90) || (char >= 97 && char <= 122)) {
      hasEnglish = true;
    }
    if (char >= 48 && char <= 57) {
      hasDigit = true;
    }
  }

  return userId.length >= 5 && hasEnglish && hasDigit;
}

분명 두 가지 단순한 조건임에도 불구하고 생각보다 많은 조건과 변수가 사용된 모습을 확인할 수 있습니다. 이처럼 우리가 특정 조건을 이용해 사용자에게 입력 형식에 제약을 주기 위해서는 생각보다 많고 복잡한 로직을 코드로 짜야만 하는데요.

하지만 이러한 작업을 정규 표현식으로 작성한다면 다음과 같이 깔끔하게 작성할 수 있게 됩니다.

document.getElementById("sign").addEventListener("submit", function(event) {
    event.preventDefault(); // 폼의 기본 제출 동작을 막습니다.

    const email = document.getElementById("id").value;
	
    if (validateEmail(email)) {
        console.log("이메일이 유효합니다.");
    } else {
        console.log("이메일이 유효하지 않습니다.");
    }
});

function validateEmail(email) {
    // 이메일 형식 검사
    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailPattern.test(email)) {
        return false;
    }

    // 사용자 아이디(이메일 앞에 오는 부분) 조건 검사
    const userId = email.split('@')[0];
    const userIdPattern = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{5,}$/;
    return userIdPattern.test(userId);
}

2. 정규 표현식이란?

정규 표현식은 문자열에서 특정 문자 조합을 찾기 위한 패턴으로, 짧게 줄여 정규식이라고도 이야기를 하는데요.

이러한 정규식은 크게 리터럴 표기법과 RegExp 객체로 생성하는 법으로 생성할 수 있습니다.

let re1 = /ab+c/ // 정적으로 생성할 때
let re2 = new RegExp(re1); // 동적으로 전달 받아 사용할 때
let re3 = new RegExp("ab+c"); // 정적으로 생성할 때는 문자열로 감싸줘야함

그리고 이러한 정규식의 패턴 작성법은 다음과 같은 규칙으로 작성할 수 있는데요. 특히나 이스케이프 시퀀스 조합으로 같이 작성되기에 이스케이스 시퀀스를 모르실 분들을 위해 아래에 첨부를 해두겠습니다.

  • ^ : 패턴 시작 (괄호 안에서는 부정을 의미)
  • $ : 패턴 종료
  • [0-9] : 괄호 안의 숫자로 범위 지정
    • : 앞의 패턴이 한 번 이상 반복되어야 함
  • [a-z] : 알파벳 소문자로 범위 지정
  • [A-Z] : 알파벳 대문자로 범위 지정
  • ? : 기호 앞에 정규식 단어가 있어도 되고, 없어도 되는 선택사항을 의미
    • : 기호 앞에 정규식 단어가 0번 이상나타날 수 있음을 의미.

예를 들어 앞서 보여드린 정규식의 예시를 들어 설명을 해보겠습니다.

const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  1. /.../ : 리터럴 표현식의 범위를 의미
  2. ^ : 패턴 시작을 알림
  3. [^\s@]+ : 단어가 공백 문자나(\s) @(@)를 포함하지 않는(^) 단어가 최소한 하나 이상(+) 있어야함
  4. @ : 말 그대로 @기호를 의미함.
  5. +. : 말 그대로 .기호를 의미함 이때 이스케이프 시퀀스로서의 의미인 백스페이스를 붙여줌.
  6. $ : 패턴 끝을 알림

3. 정규식 메서드

정규식은 리터럴 표현식으로 지정을 하든, RegExp 객체로 지정을 하든 지정되는 순간 여러가지 메서드를 제공하는데요. 대표적으로 test, exec, search, match 메서드가 대표적인 메서드들입니다.

test

문자열이 정규 표현식과 일치하는지 여부를 확인하며, 결과값을 true 및 false로 반환해 줍니다.

// c 문자 앞에는 ab라는 단어가 하나 이상 포함해야 함
let re = new RegExp("ab+c");
// 타겟 문자열들
let target1 = "abc";
let target2 = "adc";

if (re.test(target1)) {
  console.log("올바른 값입니다.");
} else {
  console.log("올바른 값이 아닙니다.");
}
if (re.test(target2)) {
  console.log("올바른 값입니다.");
} else {
  console.log("올바른 값이 아닙니다.");
}

exec

문자열에서 정규 표현식과 일치하는 부분을 검색한 뒤 일치하는 결과를 포함하는 배열, 또는 일치하지 않은 경우 null을 반환합니다.

// abc라는 단어와 그 안에 포함된 (b)라는 단어를 따로 캡쳐 그룹으로 포착하도록 정규식을 정의
let re = new RegExp("a(b)c");
// 타겟 문자열들
let target1 = "fabcdeb";
let target2 = "addef";

let result1 = re.exec(target1);
let result2 = re.exec(target2);

console.log(result1)
console.log(result2)

문자열에서 정규 표현식이 있는지를 검색한 후 찾은 경우 해당 단어의 첫번째 인덱스를 반환하고, 찾지 못한 경우 -1을 반환합니다. 특히 이 메서드는 정규식이 아니라 대상 문자열에서 호출하는 메서드임을 기억해야 합니다.

// c 문자 앞에는 ab라는 단어가 하나 이상 포함해야 함
let re = new RegExp("abc");
// 타겟 문자열들
let target1 = "edfabcab";
let target2 = "edfabdadc";

if (target1.search(re) > 0) {
  console.log(
    `해당 단어는 ${target1.search(re)} 번째 인덱스에 위치해 있습니다.`
  );
} else {
  console.log("못찾았습니다.");
}
if (target2.search(re) > 0) {
  console.log(
    `해당 단어는 ${target2.search(re)} 번째 인덱스에 위치해 있습니다.`
  );
} else {
  console.log("못찾았습니다.");
}

match

문자열에서 정규 표현식과 일치하는 부분을 검색합니다. 일치하는 결과를 포함하는 배열을 반환하거나 일치하지 않으면 null을 반환합니다.

특히 해당 메서드는 정규식에서 호출하는 exec과 달리 문자열에서 호출하는 메서드이기 때문에 이 두 메서드의 반환값은 비슷하더라도 엄연히 호출 대상과 검색 대상의 범위에 따라 구분해서 사용되는 메서드임을 기억해야 합니다.

let re = new RegExp("a(b)c");
// 타겟 문자열들
let target1 = "fabcdeb";
let target2 = "addef";

let result1 = target1.match(re);
let result2 = target2.match(re);

console.log(result1)
console.log(result2)

부록 : 이스케이프 시퀸스

이스케이프 시퀀스는 정규식에서 사용되는 대표적인 시퀀스로, 해당 시퀀스가 무엇인지를 알아놓으면 다음에 사용할 때 조건에 맞는 적절한 시퀀스들을 사용할 수 있기에 소개를 드려볼까 합니다.

문자 클래스 이스케이프 시퀀스

  • \d: 숫자 (digit), [0-9]와 동일
  • \D: 숫자가 아닌 문자 (non-digit)
  • \w: 단어 문자 (word character), [A-Za-z0-9_]와 동일
  • \W: 단어 문자가 아닌 문자 (non-word character)
  • \s: 공백 문자 (space character), 공백, 탭, 줄 바꿈 등을 포함
  • \S: 공백 문자가 아닌 문자 (non-space character)

경계 이스케이프 시퀀스

  • \b: 단어 경계 (word boundary)
  • \B: 단어 경계가 아닌 위치 (non-word boundary)
  • \^: 문자열의 시작 (문자 클래스 안에서는 부정의 의미)
  • \$: 문자열의 끝

일반적인 이스케이프 시퀀스

  • \\: 백슬래시 (backslash) 문자 자체
  • \.: 마침표 (dot) 문자 자체
  • \|: 파이프 (pipe) 문자 자체
  • \(: 여는 소괄호 (opening parenthesis) 문자 자체
  • \): 닫는 소괄호 (closing parenthesis) 문자 자체
  • \{: 여는 중괄호 (opening curly brace) 문자 자체
  • \}: 닫는 중괄호 (closing curly brace) 문자 자체
  • \[: 여는 대괄호 (opening square bracket) 문자 자체
  • \]: 닫는 대괄호 (closing square bracket) 문자 자체
  • \+: 더하기 (plus) 기호 자체
  • \*: 별표 (asterisk) 기호 자체
  • \?: 물음표 (question mark) 기호 자체
  • \^: 캐럿 (caret) 문자 자체 (문자 클래스 안에서는 제외)

제어 문자 이스케이프 시퀀스

  • \t: 탭 (tab) 문자
  • \n: 줄 바꿈 (newline) 문자
  • \r: 캐리지 리턴 (carriage return) 문자
  • \f: 폼 피드 (form feed) 문자
  • \v: 수직 탭 (vertical tab) 문자
profile
인생은 본인의 삶을 곱씹어보는 R과 타인의 삶을 배워 나아가는 L의 연속이다.

0개의 댓글