프로그램 개발서

카드번호 마스킹 처리 html, js 예제 본문

Javascript

카드번호 마스킹 처리 html, js 예제

rairen 2023. 7. 11. 17:52

2023.08.24 : 카드번호 위치 및 마스킹 표시 문자, 하이픈 표시여부, 하이픈 사용시 하이픈 위치 지정 설정 추가

2023.08.29 : 설정에 따라 다른 처리되는 것을 확인하기 위한 코드로 변경

 

카드번호 마스킹 처리 예제 코드

 

- html

<div class="container py-5">
      <div class="card-group">
        <div class="card">
          <div class="card-header">
            <h4 class="h4">카드번호 마스킹</h4>
          </div>
          <div class="card-body">
            <div class="row justify-content-center">
              <div class="col-auto">
                <label>번호 길이[최소, 최대]</label>
                <div class="input-group">
                  <input type="text" class="form-control onlyNumber" id="numberMaxLength" name="numberMaxLength" value="16" size="2">
                </div>
              </div>

              <div class="col-auto vr px-0"></div>
              <div class="col-auto">
                <label>마스킹 시작자리</label>
                <div class="input-group">
                  <input type="text" class="form-control onlyNumber" id="positionStart" name="positionStart" value="7" size="2">
                </div>
              </div>

              <div class="col-auto vr px-0"></div>
              <div class="col-auto">
                <label>마스킹 처리길이</label>
                <div class="input-group">
                  <input type="text" class="form-control onlyNumber" id="positionLength" name="positionLength" value="6" size="2">
                </div>
              </div>

              <div class="col-auto vr px-0"></div>
              <div class="col-auto ">
                <label>실제 처리할 데이터 구분</label>
                <div class="input-group">
                  <div class="form-check form-check-inline">
                    <input type="radio" class="form-check-input" id="procValueView" name="procValue" value="view" checked="">
                    <label class="form-check-label" for="procValueView">마스크 처리 후</label>
                  </div>
                  <div class="form-check form-check-inline">
                    <input type="radio" class="form-check-input" id="procValueHide" name="procValue" value="hide">
                    <label class="form-check-label" for="procValueHide">마스크 처리 전</label>
                  </div>
                </div>
              </div>

              <div class="col-auto vr px-0"></div>
              <div class="col-auto ">
                <label>하이픈(-) 사용여부</label>
                <div class="input-group">
                  <div class="form-check form-check-inline">
                    <input type="radio" class="form-check-input" id="isHipenUseTrue" name="isHipenUse" value="true" checked="">
                    <label class="form-check-label" for="isHipenUseTrue">사용</label>
                  </div>
                  <div class="form-check form-check-inline">
                    <input type="radio" class="form-check-input" id="isHipenUseFalse" name="isHipenUse" value="false">
                    <label class="form-check-label" for="isHipenUseFalse">사용안함</label>
                  </div>
                </div>
              </div>
            </div>

            <hr class="hr">
            <div class="card-text">
              <label for="view-card-number">사용자가 입력하고 마스킹 처리되는 입력란</label>
              <input type="text" class="form-control" id="view-card-number" minlength="15" maxlength="16">
            </div>
            <div class="card-text">
              <label for="hide-card-number">사용자가 입력한 번호 그대로 가지고 있는 입력란</label>
              <input type="text" class="form-control" id="hide-card-number" readonly="">
            </div>
            <div class="card-text">
              <label for="real-card-number">실제로 백단으로 보낼 입력란</label>
              <input type="text" class="form-control" id="real-card-number" readonly="">
            </div>
          </div>
        </div>
      </div>
    </div>

- javascript

      function onlyNumber(className = 'onlyNumber'){
        className = "." + className;
        let onlyNumberEl = document.querySelectorAll(className);
        onlyNumberEl.forEach(function(el, idx){
          el.addEventListener('keydown', function (KeyboardEvent) {
            if(KeyboardEvent.which >= 48 && KeyboardEvent.which <= 57){
              return true;
            }
            else if(KeyboardEvent.which >= 96 && KeyboardEvent.which <= 105){
              return true;
            }
            else{
              KeyboardEvent.preventDefault();
              KeyboardEvent.stopPropagation();
              return false;
            }
          });
        });
      }
      onlyNumber();
      
      /**
       * 카드번호 마스킹
       */
      let [start, end] = [0, 0];// 입력 위치 확인하기위한 변수

      let viewCardEl = document.querySelector('#view-card-number');//마스크 처리 된 값을 보여줄 요소
      let hideCardEl = document.querySelector('#hide-card-number');//마스크 처리하기 전 사용자가 입력한 값을 보유할 요소
      let realCardEl = document.querySelector('#real-card-number');//처리단으로 보낼 값을 보유한 요소

      // 마스킹 설정
      let maskRange      = [7, 13];    // 마스킹 처리 범위[시작, 끝] ex) 4이후부터 8까지
      let maskText       = 'x';        // 마스킹 처리할 문자
      let procValue      = 'view';     // 어떤 값으로 처리하지 구분할 값 ['view', 'hide']
      let isHipenUse     = true;       // 하이픈(-) 사용 여부
      let hipenPosition  = [5, 10, 15];// 하이픈 위치
      let inputMinLength = 15;         // 입력제한
      let inputMaxLength = 16;         // 입력제한

      viewCardEl.setAttribute('minLength', inputMinLength);
      viewCardEl.setAttribute('maxLength', inputMaxLength);

      // 선택 시 해당 위치 저장
      function valueSelect(e){
          start = e.target.selectionStart;
          end   = e.target.selectionEnd;
      }

      // 중간 잘라내기
      function valueSlice(str, start, end, midStr){
          const front = str.slice(0, start);
          const back  = str.slice(end);
          if (midStr) return front + midStr + back;
          else return front + back;
      }

      // 입력 시 마스킹 처리하기
      function maskCardNumber(e){
        // 입력제한 있는 경우
        if(typeof inputMaxLength !== 'undefined'){
          // 하이픈 사용하고 하이픈 들어가는 개수가 있는 경우
          if(typeof isHipenUse !== 'undefined' && isHipenUse === true && typeof hipenPosition !== 'undefined' && hipenPosition.length > 0){
            e.target.setAttribute('maxLength', parseInt(inputMaxLength) + parseInt(hipenPosition.length));
          }
          else{
            e.target.setAttribute('maxLength', parseInt(inputMaxLength));
          }
        }

        const data      = e.data;     // 입력 데이터
        const inputType = e.inputType;// 입력 방식
        let position = e.target.selectionStart;// 입력 후 시작위치

        // 삭제
        if(inputType === 'deleteContentBackward'){
          // 기본
          if (start === end) {
            hideCardEl.value = valueSlice(hideCardEl.value, position, position + 1);
          }
          // 드래그 삭제
          else {
            hideCardEl.value = valueSlice(hideCardEl.value, start, end);
          }
        }
        // 입력
        else if (parseInt(data) >= 0 && parseInt(data) <= 9){
          if (start === end) {
            hideCardEl.value = valueSlice(hideCardEl.value, position - 1, position - 1, data);
          }
          // 드래그 후 입력
          else {
            hideCardEl.value = valueSlice(hideCardEl.value, start, end, data);
          }
          // 위치저장
          start = position;
          end   = position;
        }

        console.log(maskRange);
        let tempMaskRange = maskRange;
        // 하이픈 사용 시
        // 마스크 처리 부분은
        if (typeof isHipenUse !== 'undefined' && isHipenUse === true) {
          if(hideCardEl.value !== ''){
            // 하이픈 넣어주면서 생기는 커서 위치 pos 조정
            hideCardEl.value = hideCardEl.value.replaceAll('-', '').match(/[0-9*]{1,4}/g)?.join('-');
          }
          // 하이픈 위치 저장 변수 체크하기 위해 반복
          for(let i = 0; i < hipenPosition.length; i++){
            // 입력 후 위치가 하이픈 넣을 위치면 하이픈 다음으로 옮겨주기 위헤
            // 위치 값 + 1
            if(position === hipenPosition[i]){
              position = position + 1;
            }

            // 마스크 범위 수정
            if((hipenPosition[i] - 1) === maskRange[0]){
              tempMaskRange[0] = maskRange[0] + i + 1;
            }
            if((hipenPosition[i] - 1) === maskRange[1]){
              tempMaskRange[1] = maskRange[1] + i + 1;
            }
          }
        }
        
        const value = hideCardEl.value;
        const splitValue = value.split('');
        const maskValue = splitValue.map((v, i) => {
          if(typeof isHipenUse !== 'undefined' && isHipenUse === true){
              if (i >= parseInt(tempMaskRange[0]) && i <= parseInt(tempMaskRange[1]) && v !== '-') return maskText;
              else return v;
          }
          else{
              if (i >= tempMaskRange[0] && i <= tempMaskRange[1]) return maskText;
              else return v;
          }
        }).join('');
        // 치환 값 넣기
        e.target.value = maskValue;

        // 바뀐 위치 원상복구
        e.target.selectionStart = position;
        e.target.selectionEnd   = position;

        // 어떤 값을 실제 처리단으로 넘길 값으로 할지 구분함.
        if(typeof procValue !== 'undefined'){
          switch (procValue) {
            case 'view':
              realCardEl.value = viewCardEl.value;
              break;
            case 'hide':
              realCardEl.value = hideCardEl.value;
              break;
          }
        }
        else{
          realCardEl.value = hideCardEl.value;
        }
      }
      
      // 이벤트 바인딩
      viewCardEl.addEventListener('input', maskCardNumber);// 입력 시
      viewCardEl.addEventListener('select', valueSelect);  // 선택 시
      
      // 번호 최대길이
      let numberMaxLengthEl = document.querySelector('#numberMaxLength');
      numberMaxLengthEl.addEventListener('keyup', function(){
        let maxLength = this.value;
        inputMaxLength = maxLength;
        viewCardEl.setAttribute('maxLength', maxLength);
      });
      
      // 마스크 위치
      let positionStartEl = document.querySelector('#positionStart');
      positionStartEl.addEventListener('keyup', function(){
        let positionStartData = this.value;
        if(isHipenUse !== true){
          positionStartData = (positionStartData - 1);
        }
        maskRange[0] = parseInt(maskRange[0]);
      });

      // 마스크 길이
      let positionLengthEl = document.querySelector('#positionLength');
      positionLengthEl.addEventListener('keyup', function(){
        let positionLengthData = this.value;
        maskRange[1] = maskRange[0] + positionLengthData;
      });

      let isHipenUseEl = document.querySelectorAll('input[name="isHipenUse"]');
      isHipenUseEl.forEach(function(el, idx){
        el.addEventListener('click', function () {
          isHipenUse = (this.value === 'true');
          maskRange[0] = (this.value === 'true') ? parseInt(positionStartEl.value) : parseInt(positionStartEl.value) - 1;
          maskRange[1] = maskRange[0] + parseInt(positionLengthEl.value) - 1;
        });
      });
      
      let procValueEl = document.querySelectorAll('input[name="procValue"]');
      procValueEl.forEach(function(el, idx){
        el.addEventListener('click', function () {
          procValue = this.value;
        });
      });

 

티스토리 삽입 예제

카드번호 마스킹


코드펜 예제

See the Pen Card Number Mask Script by rairen (@rairen) on CodePen.

반응형