관리자 페이지나 화면 모니터링 로그를 개발하다가 사용자의 이메일, 전화번호, 이름 같은 민감한 개인정보가 그대로 화면에 노출되는 상황을 경험해봤을까?
관리자 페이지나 시스템 로그 파일에 개인정보를 날것(Raw Data) 그대로 남겨두는 것은 개인정보보호법 및 ISMS 인증 기준 위반일 뿐만 아니라 심각한 데이터 유출 사고로 이어질 수 있다. 다만 매번 화면을 그릴 때마다 정규식을 직접 작성하여 이메일이나 전화번호를 마스킹하는 것은 여간 번거로운 일이 아니다.
이번에는 문자열 유형(이메일, 전화번호, 이름)을 자동으로 감지하거나 지정하여 안전하게 마스킹해주는 PHP 서포트 함수가 정확히 뭔지, 왜 필요한지, 어떻게 작성해서 실무에 활용하는지 완벽하게 정리해서 소개하겠다.

 

1. 개인정보 마스킹의 필요성과 유형별 처리 규칙

개인정보 마스킹은 데이터를 식별할 수 없도록 일부 문자열을 '*'(아스타리스크) 등의 대체 문자로 가리는 작업이다.
실무에서 가장 자주 다루는 데이터 형태는 이름, 전화번호, 이메일 세 가지다. 각각의 문자열은 구조와 길이가 다르기 때문에 일률적으로 처리하면 한글 깨짐이나 이메일 도메인 손상 등의 문제가 발생할 수 있다.

데이터 유형원본 데이터 예시마스킹 처리 결과마스킹 처리 규칙
이름 (한글/영문)홍길동 / 남궁길동홍*동 / 남궁**동외자(2자)는 뒷글자, 3자 이상은 성과 마지막 글자 외 중앙 마스킹
전화번호010-1234-5678010-****-5678중간 자릿수(3~4자리) 전체 마스킹 (하이픈 유무 무관)
이메일user1234@domain.comus****@domain.com아이디 앞 2자리 유지 후 남은 아이디 마스킹, 도메인 보존

 

2. 자주 하는 실수 - 잘못된 마스킹 처리법

다만 대부분의 개발자들은 단순 `substr()` 함수를 사용하거나 고정된 길이로 문자열을 자르는 방식을 사용하곤 한다. 이 경우 멀티바이트(한글) 문자가 깨지거나 이메일 아이디가 짧은 경우 도메인 영역까지 가려지는 치명적인 버그가 생긴다.

✗ 잘못된 코드 (한글 깨짐 및 짧은 이메일 처리 오류)

<?php
// 1. 단순 substr 사용 시 한글 깨짐 발생
$name = "홍길동";
$masked_name = substr($name, 0, 1) . "*" . substr($name, 2);
// 출력: 홍* (한글이 UTF-8 3바이트이므로 문자가 깨짐)

// 2. 고정 길이 마스킹 시 아이디가 짧으면 도메인 영역이 침범됨
$email = "ab@domain.com";
$parts = explode('@', $email);
$masked_email = substr($parts[0], 0, 2) . "***@" . $parts[1];
// 출력: ab***@domain.com (아이디 'ab'가 그대로 노출된 채 별표만 붙음)
?>

✓ 올바른 코드 (mb_string 및 유연한 길이 체크 적용)

<?php
// 1. mb_substr 함수를 사용하여 멀티바이트 한글 안전 처리
$name = "홍길동";
$length = mb_strlen($name, 'UTF-8');
$masked_name = mb_substr($name, 0, 1, 'UTF-8') . '*' . mb_substr($name, 2, 1, 'UTF-8');
// 출력: 홍*동

// 2. 이메일 아이디 길이에 맞춘 가변 마스킹
$email = "ab@domain.com";
list($id, $domain) = explode('@', $email);
$id_len = mb_strlen($id, 'UTF-8');
$masked_id = ($id_len <= 2) ? mb_substr($id, 0, 1, 'UTF-8') . '*' : mb_substr($id, 0, 2, 'UTF-8') . str_repeat('*', $id_len - 2);
$masked_email = $masked_id . '@' . $domain;
// 출력: a*@domain.com
?>

 

3. 실전 PHP 서포트 함수 구현 | mask_user_data()

실무에서 유용하게 쓸 수 있도록 자동 타입 감지 기능과 개별 타입 지정 기능을 갖춘 공통 서포트 함수 `mask_user_data()`를 구현해보자.

<?php
/**
 * 개인정보 문자열(이름, 전화번호, 이메일)을 감지하여 안전하게 마스킹 처리하는 서포트 함수
 * 
 * @param string $string 마스킹할 원본 문자열
 * @param string $type 마스킹 유형 ('auto', 'name', 'phone', 'email')
 * @return string 마스킹 완료된 문자열
 */
function mask_user_data($string, $type = 'auto') {
    $string = trim((string)$string);
    if (empty($string)) {
        return '';
    }

    // 자동 타입 판별
    if ($type === 'auto') {
        if (filter_var($string, FILTER_VALIDATE_EMAIL)) {
            $type = 'email';
        } elseif (preg_match('/^[0-9]{2,3}-?[0-9]{3,4}-?[0-9]{4}$/', $string)) {
            $type = 'phone';
        } else {
            $type = 'name';
        }
    }

    switch ($type) {
        case 'email':
            $parts = explode('@', $string, 2);
            if (count($parts) !== 2) return $string;
            $id = $parts[0];
            $domain = $parts[1];
            $len = mb_strlen($id, 'UTF-8');
            
            if ($len <= 2) {
                $masked_id = mb_substr($id, 0, 1, 'UTF-8') . '*';
            } else {
                $masked_id = mb_substr($id, 0, 2, 'UTF-8') . str_repeat('*', $len - 2);
            }
            return $masked_id . '@' . $domain;

        case 'phone':
            // 숫자만 추출
            $clean_phone = preg_replace('/[^0-9]/', '', $string);
            $len = strlen($clean_phone);
            
            if ($len === 11) { // 01012345678
                return preg_replace('/^(d{3})(d{4})(d{4})$/', '$1-****-$3', $clean_phone);
            } elseif ($len === 10) { // 0101235678 또는 0212345678
                if (substr($clean_phone, 0, 2) === '02') {
                    return preg_replace('/^(d{2})(d{4})(d{4})$/', '$1-****-$3', $clean_phone);
                }
                return preg_replace('/^(d{3})(d{3})(d{4})$/', '$1-***-$3', $clean_phone);
            }
            return $string; // 규격에 맞지 않으면 원본 반환

        case 'name':
        default:
            $len = mb_strlen($string, 'UTF-8');
            if ($len <= 1) {
                return $string;
            } elseif ($len === 2) {
                return mb_substr($string, 0, 1, 'UTF-8') . '*';
            } else {
                $first = mb_substr($string, 0, 1, 'UTF-8');
                $last = mb_substr($string, -1, 1, 'UTF-8');
                $middle = str_repeat('*', $len - 2);
                return $first . $middle . $last;
            }
    }
}
?>

 

4. 서포트 함수 활용 및 출력 결과

작성한 `mask_user_data()` 서포트 함수를 실제 회원 데이터 출력 및 로그 기록 시점에 적용하는 방법이다.

<?php
$user_list = [
    ['name' => '홍길동', 'phone' => '010-1234-5678', 'email' => 'hong@example.com'],
    ['name' => '남궁성', 'phone' => '01098765432', 'email' => 'ab@test.co.kr'],
    ['name' => 'John Doe', 'phone' => '02-123-4567', 'email' => 'johndoe1004@gmail.com']
];

foreach ($user_list as $user) {
    echo "이름: " . mask_user_data($user['name']) . "
"; echo "전화번호: " . mask_user_data($user['phone']) . "
"; echo "이메일: " . mask_user_data($user['email']) . "
"; echo "-----------------------------------
"; } ?>
실행 결과 출력값
이름: 홍*동
전화번호: 010-****-5678
이메일: ho**@example.com
-----------------------------------
이름: 남*성
전화번호: 010-****-5432
이메일: a*@test.co.kr
-----------------------------------
이름: J******e
전화번호: 02-***-4567
이메일: jo********@gmail.com
-----------------------------------

 

5. 주의사항 및 흔한 실수

서포트 함수를 개발할 때 흔히 범하는 실수 중 하나는 DB에 저장되는 원본 데이터 자체를 마스킹하여 저장하는 것이다.

✗ 잘못된 사용법 (데이터베이스 변경)

<?php
// DB에 저장할 데이터 자체를 마스킹하면 원본 복구가 불가능해짐!
$masked_email = mask_user_data($_POST['email']);
$db->query("UPDATE users SET email = '{$masked_email}' WHERE id = 1");
?>

✓ 올바른 사용법 (출력 및 로그 레이어에서만 적용)

<?php
// DB에는 원본 데이터를 그대로 유지하고, 뷰(HTML)나 로그파일에 기록할 때만 서포트 함수 사용
$user = $db->findUser(1);

// 화면 출력 시점에만 호출
echo "<td>" . htmlspecialchars(mask_user_data($user['email'])) . "</td>";

// 시스템 로그 기록 시점에만 호출
error_log("User logged in: " . mask_user_data($user['phone']));
?>

 

6. 마무리

개인정보 마스킹은 웹 서비스의 보안과 사용자 신뢰를 지키기 위한 핵심 기본기다. 개발 편의를 이유로 로그나 화면에 날것의 데이터를 출력하던 작은 습관을 바꾸는 것이 모여서 서비스 전체의 강력한 보안을 만든다는 점을 잊지 말자. 이 글의 `mask_user_data()` 서포트 함수 코드를 참고해 공통 라이브러리 파일에 추가해 두면, 개인정보 유출 리스크 없이 안전하고 깔끔한 웹 서비스를 구축할 수 있을 것이다.