본문 바로가기

STUDY/WEB

정규식을 이용해서 카드 번호 Formatter 만들기

요즘 Payments 과제를 진행 중이다.

과제 도중 카드 번호와, 만료일을 포맷팅 할 필요가 생겨 정규식을 이용해서 구현해 보았다.

 

구현할 것은 다음과 같다.

input : 1234567890123456
output : 1234-5678-9012-3456

유의할 점은 16자리 모두 완성되지 않은 상태에서도 `1234-56` 과 같이 4글자마다 '-'(dash)를 삽입해 주어야 한다는 것이다.

구현에는 두 가지 방식을 모두 이용해 보았다.

 

### match를 이용한 방법

export const cardSerialNumberFormatter = (serialnumber) => {
  if (serialnumber.length > 16) return;

  return serialnumber.match(/[0-9●]{1,4}/g)?.join('-') || serialnumber;
};

이 방법에서는 숫자를 4개를 기준으로 나눈 후, match를 통해 얻어진 array를 join을 이용해 하나의 string으로 변경했다. 

 

123456 이 있다면,

['1234', '56']으로 분리한 후,

1234-56으로 변경하는 식이다.

 

사용한 정규식은 /[0-9●]{1,4}/g로 식을 해석하면

'0-9 사이의 값 또는 ●에 해당하는 값'이 '1개부터 4개 사이인 값'을 '전체'에서 찾겠다 라는 말과 같다.

최대 개수를 벗어나면, 그 이후의 값에서 또다시 해당하는 값을 찾는다.

 

https://regexr.com/ 에서 match 를 확인하면 다음과 같이 나온다.

### replace를 이용한 방법

export const cardSerialNumberFormatter = (serialnumber) => {
  if (serialnumber.length > 16) return;

  return serialnumber.replace(/(?<=^(.{4})+)(?=.+)/g, '-');
};

match를 이용하는 것보다 정규식이 까다로운데,

 

사용한 정규식은 /(? <=^(. {4})+)(?=.+)/g로 해석하면

'이전 범위에, 시작부터 아무 값이나 4개인 것을 그룹으로 하는 그룹이 최소 1개 이상이고',

'이후 범위에 아무 값이나 1개 이상'인 값을 찾아라 라는 뜻이 된다.

 

정규식으로는 boundary 도 찾을 수 있는데 위 식을 이용하여 boundary를 찾아준 후, '-' 기호로 변환한다.

 

간단한 예시로 보자면,

1234●●567이라는 문자열이 들어온다고 가정하자.

not word boundary는 B로, word boundary b로 해서 다시 작성하면,

1(B) 2(B) 3(B) 4(b)●(B)●(B) 5(B) 6(B) 7(b)와 같다.

 

이전 범위에, 시작부터 아무 값이나 4개인 것을 그룹으로 하는 것이 최소 1개 이상이 의미하는 바는

1234와 같이 4개의 문자로 묶인 것을 한 그룹으로 한다.라는 의미이며,

'시작부터'라는 조건이 붙어

123456의 경우 그룹이 1개,

12345678의 경우 그룹이 2개가 된다.

('시작부터'라는 조건이 없다면, 123456 은 1234, 2345, 3456으로 총 3개의 그룹이다.)

 

따라서

1(B)2(B)3(B)4(b)●(B)●(B)5(B)6(B)7(b)는

(1234)(b)(●●56)(B)7(b)가 된다. 

편의상 그룹 내 boundary는 생략했다. (포함되지 않는다.)

 

다음으로, 이후 범위에 아무 값이나 1개 이상이 있는이 의미하는 바는

(1234)(b1)(●●56)(B2)7(b3)

에서 (b1), (B2)의 경우 뒤에 각각 ●567, 7으로 1개 이상의 값이 존재하며,

(b3)의 경우 뒤에 값이 존재하지 않는다.

따라서 해당 조건에 맞는 값은 (b1), (B2)가 된다.

 

이렇게 찾은 값을 원하는 대로 바꾸어 주면 된다.

 

 

 

식이 좀 기괴하긴 한 것 같다.

 

### 요약

* match를 이용할 때 정규식  /[0-9●]{1,4}/g

* replace를 이용할 때 정규식 /(? <=^(. {4})+)(?=.+)/g

### 결론

* formatter에 들어오기 전에 문자가 숫자와 ●로만 이루어져 있다면. any를 이용하는 것이 나은 것 같다. 

* 해당 포맷터는 앞에서부터 '일정한 개수' 별로 구분자를 삽입하는 데 이용할 수 있다. (MM/YY 형식 등)

* 1,000과 같은 money formatter로는 이용할 수 없다 (대신 replace에서 이용한 정규식을 변형하면 구현 가능하다.)

* (? <=Something) 은 지원 안 하는 브라우저도 있다.  

* 그러니 행복하게 match 쓰자..

 

### 참고 링크

stackoverflow.com/questions/6259515/how-can-i-split-a-string-into-segments-of-n-characters