🥳 200만 유저의 친구 ‘이루다’ 기술로 AI 캐릭터를 자유롭게 만들어보세요 ‘핑퐁 스튜디오’ 보러가기

Tech

2022 개인정보 가명·익명처리 기술 경진대회 참여 후기

'성동구소재회사근무중인20대남성개발자4명'팀의 대상 비법

이정민 김성훈 김성환 최기원 | 2023년 02월 01일 | #Engineering

스캐터랩에 다니고 있는 개발자 이정민, 최기원, 김성훈, 김성환은 과학기술정보통신부가 주최하고 한국인터넷진흥원(KISA)이 주관한 2022 개인정보 가명·익명처리 기술 경진대회(PIDICON)에 “성동구소재회사근무중인20대남성개발자4명” 팀으로 참가하여 일반부 대상(과학기술정보통신부 장관상)을 수상했습니다.

시상식 사진

PIDICON은 주어진 시나리오에 따라 재현 데이터(synthetic data)를 가명·익명처리하고 그 처리 기법의 우수성을 겨루는 대회입니다. 이 대회를 준비하면서 저희 팀은 가명·익명처리를 많이 공부했고 그 내용을 글로 정리해 보았습니다. 공부한 내용은 대회 참가자뿐만 아니라 개인정보를 다루는 개발자에게도 도움이 되니 편하게 읽어주세요.

목차

가명처리와 익명처리

가명처리라는 개념은 2020년 8월 데이터3법(개인정보 보호법, 정보통신망법, 신용정보법)이 시행되면서 국내에 널리 퍼지게 됩니다. 그만큼 가명처리와 개인정보 보호법 사이에는 밀접한 연관이 있으며, 가명·익명처리 개념을 잘 이해하려면 개인정보 보호법 또한 잘 이해해야 합니다. 우선은 개인정보, 가명정보, 익명정보를 법에서는 어떻게 정의했는지 살펴보겠습니다.

개인정보

개인정보 보호법 제2조 제1호는 “개인정보”를 다음과 같이 정의합니다.

개인정보 보호법 제2조 제1호

이 정의에 따르면 살아 있는 개인을 식별할 수만 있다면 내용과 형태의 제한 없이 모두 개인정보로 간주될 수 있습니다. 휴대전화 번호 뒤 4자리처럼 그 자체로는 식별 가능성이 매우 떨어지더라도 다른 정보와 결합되어 특정 개인을 알아볼 수 있는 상황에서는 개인정보가 될 수 있습니다. 특히 IT 서비스의 경우 사용자가 직접 제공하지 않았어도 시스템에서 생성하는 접속 로그 등도 개인정보가 될 수 있다는 점을 유의해야 합니다.

가명정보

개인정보 보호법 제2조 제1호 다목을 보면 “가명정보”를 개인정보의 하위 유형으로 정의하고 있습니다. 가명정보는 특정 개인을 알아볼 수 없도록 개인정보를 가명처리한 결과물이지만 암호화 키나 알고리즘 등의 “추가정보”를 활용하면 개인을 알아볼 수도 있습니다. 이러한 가명정보의 재식별 위험성 때문에 여전히 개인정보 보호법에서는 가명정보를 개인정보의 일종으로서 보호 대상으로 명시하고 있습니다.

그렇다면 개인정보랑 가명정보는 무슨 차이가 있는 걸까요? 가명정보는 개인정보 보호법 제3장 제3절(가명정보 처리에 관한 특례)의 적용 대상이 되어 가명처리되지 않은 개인정보보다 완화된 제약을 받습니다. 일례로, 가명정보는 정보주체의 동의 없이도 처리(이용, 제공 등)할 수 있습니다. 단 그 처리 목적이 통계작성, 과학적 연구, 공익적 기록보존 등으로 제한되며 가명정보의 원본인 개인정보를 수집할 때는 정보주체의 동의가 필요합니다.

익명정보

익명정보는 시간·비용·기술 등을 합리적으로 고려했을 때 다른 정보를 사용하여도 더 이상 개인을 알아볼 수 없는 정보입니다(개인정보보호위원회, 2022, p. 7). 따라서 추가정보를 쓰면 재식별이 가능한 가명정보와는 달리 익명정보는 애초에 개인정보가 아닙니다. 개인정보 보호법에서 익명정보를 직접 정의하지는 않지만 제3조(개인정보 보호 원칙) 제7항에서 간접적으로 그 흔적을 확인할 수 있습니다.

⑦ 개인정보처리자는 개인정보를 익명 또는 가명으로 처리하여도 개인정보 수집목적을 달성할 수 있는 경우 익명처리가 가능한 경우에는 익명에 의하여, 익명처리로 목적을 달성할 수 없는 경우에는 가명에 의하여 처리될 수 있도록 하여야 한다.

안전성 대 유용성

많은 현대인들은 개인정보 유출 사고 등을 접하면서 프라이버시 침해가 염려스럽지만, 그렇다고 개인정보를 제공해서 얻는 맞춤형 혜택을 무시할 수도 없는 상황에 처해 있습니다. 개인정보를 가명처리하는 과정에서도 비슷한 모순이 발생합니다. 개인정보의 식별 가능성을 최대한 낮추기 위해 정보를 최대한 없애다 보면, 결국 과학적 연구 같은 원래 목적을 달성하지 못할 정도로 쓸모없는 정보만 남을 수도 있습니다.

이처럼 프라이버시 보호와 유용성은 상충하는 관계이기에 그 균형을 잘 맞추어야 합니다. 대회 중에는 팀원들끼리 서로 다른 입장이 되어 악마의 변호를 하며 많은 토론의 시간을 가졌습니다.

이 대회에서는 가명·익명처리 결과물을 안전성과 유용성 두 기준으로 평가함으로써 이러한 프라이버시의 양면성을 반영했습니다. 안전성과 유용성을 평가하는 지표에는 무엇이 있는지 개략적으로 소개하겠습니다.

안전성 평가 지표

비식별 정보(가명정보, 익명정보)의 안전성이란 곧 재식별의 위험성이 얼마나 큰지를 의미합니다. 이때 재식별의 위험성은 데이터 자체 정보뿐만 아니라 비식별 정보의 처리 환경 또한 종합적으로 고려해야 됩니다.

안전성을 정량적으로 평가하는 한 가지 방법으로는 k-익명성이 있습니다. k-익명성은 프라이버시 보호 모델의 한 가지 사례로 자세한 설명은 익명처리 기법 절에서 이어 하겠습니다. k-익명성 지표는 가명처리에는 적용하기 어렵고 모든 프라이버시 위협을 제거하는 건 아니라는 한계점이 있습니다. 데이터를 안전하게 활용하려면 일부 지표를 기준에 만족시키는 데 그치지 않고 재식별 위험성을 다방면에서 검토해야 합니다.

유용성 평가 지표

원본 데이터셋과 비식별 데이터셋의 통계값이 다르다면, 그만큼의 정보가 오염되었다고 볼 수 있습니다. 이 아이디어를 이용하면 원본과 비식별 데이터셋의 지표 차이를 유용성 평가 지표로 쓸 수 있습니다. 예를 들어 평균의 차이를 활용할 수 있습니다. 숫자를 버림하는 방식으로 비식별화를 했다면 원본 데이터에 비해 가명처리 데이터의 평균이 상대적으로 낮게 측정될 것입니다.

저희 팀은 유용성 평가 지표를 언제든 바로 측정할 수 있도록 스크립트를 작성하였습니다. 스크립트 작성에는 Python의 데이터 분석 라이브러리 pandas를 사용했습니다. (앞으로 등장하는 코드에서도 pandas 라이브러리를 사용합니다.)

import pandas as pd

raw_df = pd.read_csv("원본_데이터셋.csv")
deid_df = pd.read_csv("비식별처리_데이터셋.csv")

mean_diff = abs(raw_df["2022년 소득"].mean() - deid_df["2022년 소득"].mean())
print("Mean difference :", mean_diff)

유용성 평가 지표는 평균 외에도 여러 가지를 생각할 수 있습니다. 이때 비식별 정보의 활용 목적에 따라서 적절한 지표를 선정하는 것이 중요합니다. 이번 대회에서는 원본 속성 벡터와 비식별 속성 벡터의 코사인 유사도(Cosine Similarity), 원본 데이터셋 대비 익명 데이터셋의 레코드 비율(Anonymisation Ratio)을 비롯한 9개의 지표가 정량 평가에 쓰였습니다. 다른 유용성 측정 지표도 궁금하다면 김동현(2022)의 유용성 측정 방법 연구를 참고하는 것을 추천드립니다.

가명처리 기법

개인정보보호위원회(2022, pp. 32-51)의 가명정보 처리 가이드라인에는 다양한 가명·익명처리 기술을 구체적인 예시와 함께 소개하고 있습니다. 그 중 대회에서 가장 유용했던 범주화, 상하단코딩, 부분 총계 기술을 소개하겠습니다. 이 절에서 소개하는 가명처리 기법은 익명처리 과정에서도 똑같이 적용될 수 있습니다.

범주화와 상하단 코딩

범주화(categorization)은 특정 정보를 좀 더 넓은 부류나 범위로 대체하는 기술입니다(예: 초등학생 → 학생, 25세 → 20대). 정해진 형식을 갖고 있는 대부분의 숫자형, 문자형 데이터는 범주화 기술만 적용해도 상당히 안전한 가명정보를 만들 수 있습니다.

범주를 어떻게 설정할지 판단하기 위해서는 원본 데이터셋에 대한 탐색적 데이터 분석(EDA, Exploratory Data Analysis)이 필요합니다. 그 예시로 나이를 적절한 수준으로 범주화하기 위한 EDA 과정을 코드로 표현해보겠습니다.

df = pd.read_csv("원본_데이터셋.csv")

df["나이"].describe()  # 개수, 평균, 표준편차, 사분위수 확인

def age_to_interval(age: int):
    """나이를 5단위로 구간화합니다.

    예시: age_to_interval(24) == '[20,25)'
    """
    l = age // 5 * 5
    r = l + 5
    return f"[{l},{r})"

deid_age = df["나이"].map(age_to_interval)

deid_age.value_counts()  # 도수분포 확인

위 코드는 describe() 메서드로 나이의 통계적 분포를 확인하고, 나이를 5단위 구간으로 범주화하는 age_to_interval 함수를 정의한 다음, map() 메서드로 원본 데이터셋의 나이를 가명처리하는 과정을 보여줍니다. 구간을 문자열로 표현할 때 양 끝 값의 포함관계를 헷갈리지 않도록 반열린 구간([a,b) = {a ≤ x < b} )으로 표현하였습니다.

만약 value_counts() 메서드로 구간별 데이터 개수를 확인했는데, 고령자 구간(예: [90, 95))이 희소하게 나와 재식별 위험성이 높다면 어떻게 해야 할까요? 이때는 양 끝단의 정보를 별도의 범주로 분리하는 상하단코딩 기법을 적용할 수 있습니다. 상하단코딩은 범주화 기술의 하위 개념으로 수치형 데이터에서 드물게 나타나는 양 끝단의 정보(이상치, outlier)를 범주화하는 기법입니다. 아래 코드는 5단위 구간화와 상하단코딩을 모두 적용한 예시입니다.

def age_to_interval(age: int):
    """나이를 5단위로 구간화하되, 20세 미만과 80세 이상은 별도로 처리합니다."""
    if age < 20: return "[0,20)"
    if age >= 80: return "[80,inf)"
    l = age // 5 * 5
    r = l + 5
    return f"[{l},{r})"

부분 총계

총계처리(Aggregation)은 해당 데이터 전체를 대표하는 통계값(평균, 최댓값, 최솟값 등)을 사용하는 것입니다. 부분 총계(Micro Aggregation)은 데이터를 기준에 따라 분할하고, 분할된 그룹별로 총계처리하는 것을 뜻합니다. 총계처리는 여러 행의 데이터를 하나의 값으로 압축하기 때문에 유용성이 크게 손실될 수 있습니다. 특히 여러 행을 단일한 값으로 대체하게 되면 분산과 같은 산포도에 치명적인 영향을 줄 수 있기 때문에 조심해야 합니다.

다음은 성별, 나이가 일치하는 그룹별로 부분 총계 처리를 하는 예시입니다.

"""성별, 나이가 일치하는 그룹의 2022년 소득을 평균으로 부분 총계처리하는 예시"""
x_columns = ["성별", "나이"]
y_column = "2022년 소득"

group_mean = df.groupby(x_columns, as_index=False)[y_column].mean()
deid_income = df[x_columns].merge(group_mean, how="left", on=x_columns)[y_column]

익명처리 기법

가장 유명하고 이번 대회에서도 사용한 익명처리 기법으로는 프라이버시 보호 모델이 있습니다. 프라이버시 보호 모델은 재식별 공격을 막아내는 수준을 정량화할 수 있습니다. 이 절에서는 프라이버시 보호 모델을 이해하기 위해 필요한 사전 지식(개인정보 유형 구분)과, 대표적인 프라이버시 보호 모델인 k-익명성을 적용하는 방법을 차례로 설명하겠습니다.

개인정보 유형 구분

개인정보보호위원회(2022)의 가명정보 처리 가이드라인에서는 개인정보유형을 식별정보와 식별가능정보로 구분합니다. 식별정보는 성명, 휴대전화 번호, 주민등록번호 등 식별을 목적으로 생성된 정보로, 비식별 과정에서 삭제되어야 합니다. 식별가능정보는 성별, 나이, 거주 지역 등 개인을 알아볼 수 있는 정보로, 다른 항목과 결합해서 식별 가능성이 높아진다면 추가적인 비식별 처리가 필요할 수도 있습니다.

식별가능정보는 다시 준식별자, 민감속성, 비민감속성의 3가지로 구분됩니다.

특정 정보가 어떤 개인정보 유형에 속하는지는 맥락에 따라 달라질 수 있습니다. 어떤 정보가 개인을 식별하는 데 쓰일지, 노출되면 위험한 정보가 무엇인지는 익명처리자가 직접 정해야 합니다. 위 예시에서는 신용등급을 민감속성으로 간주했지만, 만약 신용정보를 외부 데이터로 사용할 수 있는 환경이라면 준식별자로 볼 수도 있습니다.

추가로 민감속성은 개인정보 보호법 제23조에서 정의하는 “민감정보”와는 다른 개념입니다. 이름도 비슷하고 정보주체의 사생활을 현저히 침해할 우려가 있는 개인정보라는 데에서는 유사하지만, 법적인 민감정보는 법 또는 시행령에 명시적으로 정의된 범위로만 한정됩니다.

k-익명성

k-익명성은 연결 공격(linkage attack)을 막는데 활용할 수 있는 프라이버시 보호 모델입니다. 연결 공격은 준식별자로 공개된 데이터와 익명정보를 연결시키고 이를 통해 익명정보에 숨어 있던 민감속성을 드러내는 방법입니다.

관련된 유명한 사례로는 넷플릭스가 공개한 시청 이력 데이터에서 일부 사용자를 재식별한 연구가 있습니다(Narayanan, A. & Shmatikov, V., 2006). 넷플릭스의 데이터는 익명처리되어 있었지만, 다른 영화 정보 서비스 IMDb의 공개된 영화 리뷰와 대조해서 IMDb의 일부 사용자를 넷플릭스의 익명 데이터와 연결할 수 있었습니다. 해당 유저가 넷플릭스에 남긴 영화 평점을 통해 정치적·성적 성향 같은 민감한 정보까지 짐작할 수 있었습니다.

k-익명성은 이러한 공격을 방어하는 완벽한 방법은 아니지만, 익명처리를 위한 최소한의 수준을 판단하는 데에는 도움이 됩니다. 예를 들어 다음과 같이 익명화된 데이터가 있다고 가정해 봅시다.

구분 나이 성별 우편번호 직업코드 연봉(만원)
1 22 남성 04766 22222 4986
2 26 남성 04820 22239 3450
3

여기서 준식별자는 나이, 성별, 우편번호, 직업코드고, 민감정보는 연봉입니다. 이 데이터만으로는 각 행이 누구의 연봉인지 식별하기 어렵습니다. 하지만 다른 데이터를 통해 22세이고, 남성이며, 우편번호가 04766인 지역에 거주하고 있고, 직업 코드가 22222인 특정 개인을 알아냈다면, 1번 데이터와 연결되어서 그 사람의 연봉이 4986만원이라는 사실을 합리적으로 추론할 수 있게 됩니다.

동일한 데이터를 익명화 수준을 높여서 다음과 같이 각 열의 일부 정보를 *표로 삭제했다고 가정해봅시다.

구분 나이 성별 우편번호 직업코드 연봉(만원)
1 2* 남성 04*** 22*** 4986
2 2* 남성 04*** 22*** 3450
3

이제 특정 개인의 정보를 알고 있다고 하더라도, 1번 행의 주인인지 2번 행의 주인인지는 확신할 수 없게 되었습니다. 식별 가능성이 1/2로 줄어든 것입니다.

비식별 처리된 준식별자의 집합(위 예시에서는 나이, 성별, 우편번호, 직업코드)이 완전히 똑같은 모임을 동질 집합(equivalence class)이라고 부릅니다. 전체 데이터셋에서 가장 작은 동질 집합 크기를 k값이라고 부르고, 이것이 안전성을 측정하는 지표가 됩니다. 만약 k=5라면 어떤 레코드에서도 개인이 식별될 가능성이 1/5(20%) 이하임을 보장할 수 있습니다.

재식별 가능성을 판단할 때는 k값뿐만 아니라 데이터 자체의 특성도 같이 고려해야 합니다. k=5인 익명 데이터의 재식별 가능성은 20%로 상당히 높은 수준이지만, 해당 데이터가 전체 회원 중 1%를 랜덤 샘플링해서 만들었다면 실질적인 재식별 가능성은 0.2%로 낮아집니다.

(해당 사용자가 샘플링되었을 확률) * (동질 집합 내에서 해당 사용자가 식별될 확률) = 0.01 * 0.2 = 0.002

다음은 pandas를 이용하여 데이터프레임의 k값을 측정하고, k≥5를 만족시키기 위해서 크기가 작은 동질집합 데이터를 삭제하는 코드의 예시입니다. 대회에서는 평가를 위해 특정 레코드를 삭제하더라도 레코드 번호를 보존해야 했기 때문에, drop() 하는 대신 삭제할 레코드의 모든 컬럼을 N/A로 덮어씌웠습니다.

x_columns = ["성별", "나이"]  # 준식별자
k = df.groupby(x_columns).size().min()  # k값 측정

group_size = df.groupby(x_columns, as_index=False).size()
safe_record = df[x_columns].merge(group_size, how="left", on=x_columns)["size"] >= 5

# k값이 5 미만인 동질집합에 속한 레코드 제거
deid_df = df.copy()
deid_df.loc[~safe_record] = np.full(deid_df.loc[~safe_record].shape, np.nan)

기타 프라이버시 모델

l-다양성, t-근접성 등 이 글에서 설명하지 않은 다른 프라이버시 보호 모델도 있습니다. 관련 내용이 궁금하다면 행정자치부 등 관계부처 협동의 비식별 조치 가이드라인을 같이 참고하시면 좋습니다(2016, pp. 36-41). 비록 해당 가이드라인은 개인정보보호위원회(2022)의 가명정보 처리 가이드라인 발간 이후로 현행법과 맞지 않는 부분이 생겨 더 이상 활용되지는 않으나, 가명처리가 아닌 익명처리 관점에서는 여전히 중요한 정보를 많이 제공합니다.

프라이버시 모델을 활용해서 최적의 비식별 수준을 계산하는 연구도 오래전부터 이루어져 왔으며, 그에 따라 ARX와 같은 오픈소스 익명화 도구도 개발되어 있습니다. 다만, 이러한 소프트웨어는 보통 연구 목적으로 개발되어 실무에 쓰기 어려울 때도 있습니다. 대신 저희 팀은 개발자에게 친숙한 파이썬 코드로 익명처리를 수행하기로 결정했습니다.

결론

스캐터랩에서 일하면서 개발자 또한 프라이버시 보호에 많은 관심을 쏟는 것이 중요하다는 사실을 배웠습니다. 서비스를 운영하는 과정에서 생성되는 크고 복잡한 데이터를 안전하게 활용하려면 정책을 잘 세우는 것도 중요하지만, 데이터 흐름에 대한 기술적인 이해도 필요합니다. 개발자는 코드를 통해서 개인정보가 어떤 환경에서 무슨 방식으로 수집되고 처리되는지 가장 잘 이해할 수 있습니다. 거기에 프라이버시에 대한 지식까지 습득한다면 안전하면서도 유용한 개인정보·가명정보 처리 시스템을 설계해낼 수 있을 겁니다.

이러한 깨달음을 바탕으로 개인정보관리사(CPPG) 자격증을 공부하는 등 개인정보를 더 깊이 이해하기 위해 노력하고, 나아가 해당 지식을 실무에 접목시킨 경험 덕분에 과학기술정보통신부 장관상이라는 좋은 성과를 얻게 된 것 같아 뿌듯합니다. 덕분에 가명처리와 익명처리를 톺아보는 글도 쓰게 되었는데, 이 글이 개인정보 보호에 입문하려는 사람에게 많은 도움이 되었으면 좋겠습니다.

참고 자료

스캐터랩이 직접 전해주는
AI에 관한 소식을 받아보세요

능력있는 현업 개발자, 기획자, 디자이너가
지금 스캐터랩에서 하고 있는 일, 세상에 벌어지고 있는 흥미로운 일들을 알려드립니다.