소수점 2자리를 가진 숫자를 표현하기 위해 지난 번에는 Quasar Input 컴포넌트가 기본으로 제공하는 mask 속성을 활용했다.
그러나 mask 속성의 단점은 입력이 조금 부자연스럽고 점 위치가 고정된다는 것이다.
그래서 이 부분을 바꿔달라는 요청이 들어왔고 mask를 버렸다...!
요구사항
점 위치가 고정된 것이 불편하다. 사용자가 직접 .을 입력한 곳에서 소수점이 입력되길 바란다.
소수점은 2자리로 고정한다.
Quasar의 기본 속성을 이용할 수 없는 이유
- mask
- #은 숫자를 의미한다. 그렇기 때문에 #.##은 숫자 소수점 2자리의 규칙을 가지는 아주 편리한 도구였지만, 요구사항을 충족하기 위해서는 더 이상 사용할 수 없다.
- type="number"
- 타입을 숫자로 한정하게 되면 우선 문자 등의 입력을 막을 수 있다. 그러나 소수점을 입력했을 때 유효한 값을 입력해달라는 안내 문구(툴팁)가 뜬다.
- 또한 소수점 자릿수를 제한하기 위해서는 정규표현식이 필요하다.
구현 시 고려사항
- 숫자와 .을 제외한 어떤 문자도 입력할 수 없다
- 최소 한 자리 이상의 숫자로 시작한다 (.으로 시작 불가)
- .이 입력된 이후에는 숫자가 입력된다 (입력되지 않을 시에는 뒤에 00을 추가해준다)
- .은 최대 한 번만 나올 수 있다
- . 이후의 숫자는 최대 2개 나올 수 있다 (소수점 2자리까지 표기)
구현
const inputValue = computed({
get: () => {
const value = props.value?.toString();
// 숫자와 .만 입력 가능
let newVal = value?.replace(/^[^0-9]|[^\d.]/g, '');
// 조건에 부합하는 숫자(형태의 문자열)인지 확인
const regExp = /^\d+(?:\.\d{0,2})?|\d*\./g;
const match = newVal?.match(regExp);
return match ? match[0] : newVal;
},
set: (value) => {
emits('update:inputBoxValue', value);
},
});
위 코드의 실행 순서는 다음과 같다.
1. 숫자와 .이 아닌 입력은 삭제한다
2. match를 이용해 소수점 2자리 형태의 숫자만 남긴다.
정규표현식을 좀 더 풀어서 설명하자면
/^[^0-9]|[^\d.]/g
- ^는 문자열의 시작을 의미
- [ ] 내부의 ^는 Not을 의미
- ^[^0-9]는 숫자로 시작하지 않는 모든 문자를 의미
- | 는 OR를 의미
- \d는 숫자를 의미
- [^\d.]은 숫자와 . 조합으로 이루어지지 않은 모든 문자를 의미
즉, 숫자와 .이 아닌 입력을 의미한다
String.prototype.replace
대상 문자열과 인수로 전달받은 정규 표현식과의 매칭되는 부분을 인수로 전달받은 문자열로 교체해준다.
즉, 위의 정규표현식에 해당하는 숫자와 .이 아닌 모든 입력을 공백으로 바꿔준다. (=제거한다)
/^\d+(?:\.\d{0,2})?|\d*\./g
- ^는 문자열의 시작을 의미
- \d는 숫자를 의미
- +는 앞선 패턴이 최소 한 번 이상 반복
- ^\d+는 최소 하나 이상의 숫자로 시작하는 것을 의미
- ?:는 비캡처링 그룹 지정 (괄호로 묶인 묶음에 대한 그룹화된 결과를 제공하지 않음)
- \.은 특수문자 .을 의미
- {m,n}은 최소 m번, 최대 n번의 패턴 반복을 의미
- \.\d{0,2}는 소수점 2자리를 의미
- ?는 바로 앞의 패턴이 최대 한 번 이상 반복 → 0번 또는 1번 (있거나 없거나)
- |는 OR를 의미
- *는 앞선 패턴이 일치하지 않거나 한 번 이상 반복 = {0,}
즉, 숫자로만 이루어져 있거나, 소수점 2자리인 경우를 의미한다.
String.prototype.match
대상 문자열과 인수로 전달받은 정규 표현식과의 매칭 결과를 배열로 반환한다.
이때 g 플래그가 지정되면 모든 매칭 결과를 배열로 반환한다.
➡ match[0]은 위의 조건을 만족하는 입력 결과
또한 모든 수를 소수점 2자리로 표현하기 위해서 input 컴포넌트에서 focus out 될 때 실행되는 @blur 이벤트를 활용했다.
// focus out 시 실행되는 함수
const handleBlur = () => {
if (props.mask !== 'decimal') return;
let newVal = inputValue.value || 0;
const period = inputValue.value?.indexOf('.');
if (period === -1) newVal += '.00';
else {
const decimal = inputValue.value?.match(/(?:\.)(\d*)$/) as RegExpMatchArray;
if (decimal[1].length < 2) newVal += '0'.repeat(2 - decimal[1].length);
}
emits('update:inputBoxValue', newVal);
};
입력 값이 없을 시에는 기본으로 0을 지정해줬다. (|| 사용)
indexOf 메서드를 이용해 .의 유무와 위치를 확인한 후,
- 없을 때는 .00을 붙여 소수점 2자리를 맞춰주고
- 있을 때는 필요한 만큼만 0을 이어 붙였다.
우선은 요구사항을 충족한 것 같다는 생각이 들지만,,,, 코드 개선 작업을 이어나갈 것 같다.
'Javascript' 카테고리의 다른 글
input 자동완성 해제 (autocomplete) (0) | 2024.07.12 |
---|---|
자바스크립트로 만든 로또 번호 자동 생성기 (0) | 2024.05.17 |
"b" + "a" + +"a" + "a" = 'baNaNa' ??!!!!! (0) | 2024.04.18 |
[ Javascript ] 문자열을 숫자로 형변환하는 3가지 방법 (0) | 2024.03.28 |
[ Javascript ] 첨부파일 다운로드 | Blob (0) | 2024.03.28 |