IT/Front-End

데이터(Data) 마스킹(Masking) 처리하기

라임웨일 2022. 8. 2. 10:54
반응형

 

Api를 통해서 받아온 사용자의 데이터를 노출할 때 정보보호를 위해 특정 데이터는 전체를 보여주지 않고 일부를 가려서(Masking) 보여줘야 하는 경우가 발생합니다.

가장 좋은 방법은 Back-End서버에서 데이터가 가공처리되어 전달하고 Front에서는 전달받은 데이터를 화면에 그대로 노출시켜 주는 게 가장 좋은 방법이지만 상황에 따라서 Front에서 데이터를 가공해야 할 경우도 발생합니다. 이럴 때 공통 마스킹 Function을 만들어서 사용하면 굉장히 유용하게 사용할 수 있습니다. 

 

💡 마스킹 처리 함수

  /********************************************
  * 공통 마스킹
  * checkNull : 데이터 Check => 공백 | undefined | null
  *
  * ※) 이메일 마스킹
  * ex 1) emailEndHide
  * 원본 데이터 : abcdefg12345@naver.com, 변경 데이터 : ab**********@naver.com
  * ex 2) emailEndShow
  * 원본 데이터 : abcdefg12345@naver.com, 변경 데이터 : ab**********@naver.com
  *
  * ※) 휴대폰 번호 마스킹
  * ex1) 원본 데이터 : 01012345678, 변경 데이터 : 010****5678
  * ex2) 원본 데이터 : 010-1234-5678, 변경 데이터 : 010-****-5678
  * ex3) 원본 데이터 : 0111234567, 변경 데이터 : 011***4567
  * ex4) 원본 데이터 : 011-123-4567, 변경 데이터 : 011-***-4567
  *
  * ※) 주민번호 resident
  * ex1) 원본 데이터 : 990101-1234567, 변경 데이터 : 990101-1******
  * ex2) 원본 데이터 : 9901011234567, 변경 데이터 : 9901011******
  *
  * ※) 이름 마스킹
  * ex1) 원본 데이터 : 김철수, 변경 데이터 : 김철*
  * ex2) 원본 데이터 : 대한민국, 변경 데이터 : 대한**
  * ex3) 원본 데이터 : 한국, 변경 데이터 : 한*
  * 
  * * ※) 계좌번호 마스킹
  * 자리수에 따라서 마스킹 노출 다름 :  substring 변경  
  *********************************************/
  
  
let maskingFunc = {
  checkNull: function (str) {
    if (typeof str === "undefined" || str === null || str === "") {
      return true;
    }
    else {
      return false;
    }
  },

  emailEndHide: function (str) {
    let emailStr = str.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi);

    if (this.checkNull(str)) {
      return str;
    }

    let strLength = emailStr.toString().split('@')[0].length - 4;
    return str.toString().replace(new RegExp('.(?=.{0,' + strLength + '}@)', 'g'), '*').replace(/.{6}$/, "******");
  },
  emailEndShow: function (str) {
    let emailStr = str.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi);
    if (this.checkNull(str) == true || this.checkNull(emailStr) == true) {
      return str;
    }

    let strLength = emailStr.toString().split('@')[0].length - 4;
    return str.toString().replace(new RegExp('.(?=.{0,' + strLength + '}@)', 'g'), '*');
  },
  phone: function (str) {
    let phoneStr;
    let maskingStr;

    if (this.checkNull(str)) {
      return str;
    }

    // 1) -가 없는 경우
    if (str.toString().split('-').length != 3) {
      phoneStr = str.length < 11 ? str.match(/\d{10}/gi) : str.match(/\d{11}/gi);

      if (str.length < 11) {
        maskingStr = str.toString().replace(phoneStr, phoneStr.toString().replace(/(\d{3})(\d{3})(\d{4})/gi, '$1***$3'));
      }
      else {
        maskingStr = str.toString().replace(phoneStr, phoneStr.toString().replace(/(\d{3})(\d{4})(\d{4})/gi, '$1****$3'));
      }
    }
    // 2) -가 있는 경우
    else {
      phoneStr = str.match(/\d{2,3}-\d{3,4}-\d{4}/gi);
      if (/-[0-9]{3}-/.test(phoneStr)) {
        maskingStr = str.toString().replace(phoneStr, phoneStr.toString().replace(/-[0-9]{3}-/g, "-***-"));
      } else if (/-[0-9]{4}-/.test(phoneStr)) { // 2.2) 00-0000-0000
        maskingStr = str.toString().replace(phoneStr, phoneStr.toString().replace(/-[0-9]{4}-/g, "-****-"));
      }
    }

    return maskingStr;
  },  
  resident: function (str) {
    let residentStr;
    let maskingStr;

    if (this.checkNull(str)) {
      return str;
    }

    // - 이 있는 경우
    if (str.length === 14) {
      residentStr = str.match(/(?:[0-9]{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[1,2][0-9]|3[0,1]))-[1-4]{1}[0-9]{6}\b/gi);
      maskingStr = str.toString().replace(residentStr, residentStr.toString().replace(/(-?)([1-4]{1})([0-9]{6})\b/gi, "$1$2******"));
    }
    // - 이 없는 경우
    if (str.length === 13) {
      residentStr = str.match(/\d{13}/gi);
      maskingStr = str.toString().replace(residentStr, residentStr.toString().replace(/([0-9]{6})$/gi, "******"));
    }
    return maskingStr;
  },

  name: function (str) {
    let maskingStr;

    if (this.checkNull(str)) {
      return str;
    }

    if (str.length < 3) {
      maskingStr = str.replace(/(?<=.{1})./gi, "*");
    }
    if (str.length === 3) {
      maskingStr = str.replace(/(?<=.{2})./gi, "*");
    }
    if (str.length > 3) {
      maskingStr = str.replace(/(?<=.{2})./gi, "*");
    }

    return maskingStr;
  },
  account: function (str) {
      let maskingStr;

      if (this.checkNull(str) == true) {
        return str;
      }

      if (str.length <= 8) {
        maskingStr =
          str.substring(0, 2) +
          str.substring(3, 7).replace(/[0-9a-zA-Z]/g, '*') +
          str.substring(6, str.length);
      } else if (str.length >= 9 && str.length <= 10) {
        maskingStr =
          str.substring(0, 2) +
          str.substring(3, 8).replace(/[0-9a-zA-Z]/g, '*') +
          str.substring(7, str.length);
      } else if (str.length > 10) {
        maskingStr =
          str.substring(0, 3) +
          str.substring(3, 8).replace(/[0-9a-zA-Z]/g, '*') +
          str.substring(8, str.length);
      }
      return maskingStr;
    },
}

 

✔ 사용 예시

maskingFunc.emailEndHide("abcdefg12345@naver.com"); // "abc*********@nav******"
maskingFunc.emailEndShow("abcdefg12345@naver.com"); // "abc*********@naver.com"
maskingFunc.phone("0111234567"); // "011***4567"
maskingFunc.phone("011-123-4567"); // "011-***-4567"
maskingFunc.phone("01012345678"); // "010****5678"
maskingFunc.phone("010-1234-5678"); // "010-****-5678"
maskingFunc.resident("9901011234567"); // "9901011******"
maskingFunc.resident("990101-1234567"); // "990101-1******"
maskingFunc.name("한국"); // "한*"
maskingFunc.name("김철수"); // "김철*"
maskingFunc.name("대한민국"); // "대한**"
maskingFunc.account("1233333333333"); // "123*****33333"

 

✔ 반복문에서 사용 예시

반복문에서도 사용가능하고 React처럼 Render가 이루어지고 화면이 그려지는 경우에도 마스킹 함수를 만들어놓고 아래 예시처럼 사용하면 정상적으로 데이터가 마스킹 처리되어 화면에 보여집니다.

{userData.map((item, idx) => (
  <tr>
    <td>{item.name}</td>
    <td>{maskingFunc.account(item.account)}</td>
  </tr>
))}

 

참고 : https://goddaehee.tistory.com/236

반응형