요즘 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개 사이인 값'을 '전체'에서 찾겠다 라는 말과 같다.
최대 개수를 벗어나면, 그 이후의 값에서 또다시 해당하는 값을 찾는다.
### 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