정정권
Right to Rectification
부정확한 개인정보를 정정 요청할 권리. GDPR 제16조 및 개인정보보호법 제36조에서 보장.
Right to Rectification
부정확한 개인정보를 정정 요청할 권리. GDPR 제16조 및 개인정보보호법 제36조에서 보장.
정정권(Right to Rectification)은 GDPR 제16조에서 보장하는 정보주체의 기본 권리로, 자신에 관한 부정확한 개인정보를 지체 없이 수정할 것을 컨트롤러에게 요청할 수 있습니다. 또한 처리 목적을 고려하여 불완전한 개인정보를 보충할 권리도 포함됩니다. 한국 개인정보보호법 제36조도 "개인정보의 정정/삭제"라는 제목 아래 동일한 권리를 규정합니다.
GDPR 제5조 제1항 (d)호는 개인정보의 "정확성" 원칙을 규정하며, 부정확한 개인정보는 지체 없이 삭제 또는 정정되어야 합니다. 이는 정정권의 법적 근거가 됩니다. AI 시스템에서 특히 중요한 이유는 부정확한 데이터로 학습된 모델이 정보주체에게 불이익을 줄 수 있기 때문입니다. 채용 AI가 잘못된 경력 정보로 후보자를 탈락시키거나, 신용평가 AI가 오류 데이터로 대출을 거절할 수 있습니다.
정정 요청 시 컨트롤러의 의무는 GDPR 제19조에서 추가로 규정됩니다. 정정이 이루어지면 개인정보를 공개한 각 수신자에게 정정 사실을 통지해야 합니다(불가능하거나 과도한 노력이 필요한 경우 제외). 정보주체가 요청하면 수신자 정보도 제공해야 합니다. 한국 개인정보보호법 제36조 제3항도 정정/삭제된 경우 제3자에게 알려야 함을 규정합니다.
정정 요청에 대한 응답 기한은 GDPR 기준 1개월 이내이며, 복잡한 경우 2개월 연장 가능합니다(제12조). 한국 개인정보보호법은 10일 이내 처리를 원칙으로 합니다. 정정 요청을 거부하거나 처리를 지연하면 GDPR 기준 최대 2,000만 유로 또는 전 세계 연간 매출액의 4%의 과징금 대상이 됩니다. 정보주체는 거부 시 감독기관에 민원을 제기할 권리가 있습니다.
"""
GDPR Article 16 정정권(Right to Rectification) 처리 시스템
Article 19 수신자 통지 의무 포함
"""
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from typing import Optional
import uuid
class RectificationStatus(Enum):
"""정정 요청 처리 상태"""
PENDING = "pending"
VERIFIED = "verified"
RECTIFIED = "rectified"
REJECTED = "rejected"
NOTIFICATION_SENT = "notification_sent"
class DataCategory(Enum):
"""개인정보 카테고리"""
IDENTITY = "identity" # 이름, 생년월일 등
CONTACT = "contact" # 연락처, 주소
EMPLOYMENT = "employment" # 직장, 직위
FINANCIAL = "financial" # 금융정보
HEALTH = "health" # 건강정보 (민감정보)
DERIVED = "derived" # AI 추론 데이터
@dataclass
class DataRecipient:
"""데이터 수신자 정보 (Art 19 통지 대상)"""
recipient_id: str
name: str
contact_email: str
data_categories_shared: list[DataCategory]
notified: bool = False
notification_date: Optional[datetime] = None
@dataclass
class RectificationRequest:
"""정정 요청"""
request_id: str = field(default_factory=lambda: str(uuid.uuid4()))
subject_id: str = ""
field_name: str = ""
current_value: str = ""
requested_value: str = ""
category: DataCategory = DataCategory.IDENTITY
reason: str = ""
evidence_provided: bool = False
status: RectificationStatus = RectificationStatus.PENDING
created_at: datetime = field(default_factory=datetime.now)
processed_at: Optional[datetime] = None
rejection_reason: Optional[str] = None
class RectificationHandler:
"""
GDPR Article 16 정정권 처리기
Article 19 수신자 통지 의무 구현
"""
# Art 16: 정정 요청 처리 기한 (GDPR: 1개월)
RESPONSE_DEADLINE_DAYS = 30
# 정정이 불가능한 카테고리 (법적 기록 등)
NON_RECTIFIABLE_CATEGORIES = {
"government_id_number", # 법적으로 변경 불가
"transaction_history", # 회계 기록
"audit_log" # 감사 로그
}
def __init__(self):
self.requests: dict[str, RectificationRequest] = {}
self.user_data: dict[str, dict] = {}
self.recipients: dict[str, list[DataRecipient]] = {} # subject_id -> recipients
def register_recipient(
self,
subject_id: str,
recipient: DataRecipient
) -> None:
"""데이터 수신자 등록 (Art 19 통지 대상)"""
if subject_id not in self.recipients:
self.recipients[subject_id] = []
self.recipients[subject_id].append(recipient)
def submit_rectification_request(
self,
subject_id: str,
field_name: str,
current_value: str,
requested_value: str,
category: DataCategory,
reason: str,
evidence_provided: bool = False
) -> dict:
"""정정 요청 제출"""
# 정정 불가 필드 검증
if field_name in self.NON_RECTIFIABLE_CATEGORIES:
return {
"success": False,
"error": f"'{field_name}'은 법적 이유로 정정할 수 없습니다",
"legal_basis": "법적 보관 의무 또는 기록 무결성 요건"
}
request = RectificationRequest(
subject_id=subject_id,
field_name=field_name,
current_value=current_value,
requested_value=requested_value,
category=category,
reason=reason,
evidence_provided=evidence_provided
)
self.requests[request.request_id] = request
# 민감정보는 추가 검증 필요
requires_verification = category == DataCategory.HEALTH
return {
"success": True,
"request_id": request.request_id,
"deadline": request.created_at + timedelta(days=self.RESPONSE_DEADLINE_DAYS),
"requires_additional_verification": requires_verification,
"legal_basis": "GDPR Article 16, 개인정보보호법 제36조"
}
def verify_and_process(
self,
request_id: str,
verified: bool,
verifier_notes: str = ""
) -> dict:
"""정정 요청 검증 및 처리"""
request = self.requests.get(request_id)
if not request:
return {"success": False, "error": "요청을 찾을 수 없음"}
if not verified:
request.status = RectificationStatus.REJECTED
request.rejection_reason = verifier_notes
request.processed_at = datetime.now()
return {
"success": True,
"status": "rejected",
"reason": verifier_notes,
"subject_notification": {
"required": True,
"content": "정정 요청 거부 사유 및 이의제기 방법 안내 필요"
}
}
# 데이터 정정 수행
request.status = RectificationStatus.RECTIFIED
request.processed_at = datetime.now()
# 사용자 데이터 업데이트
if request.subject_id not in self.user_data:
self.user_data[request.subject_id] = {}
old_value = self.user_data[request.subject_id].get(request.field_name)
self.user_data[request.subject_id][request.field_name] = request.requested_value
# Art 19: 수신자 통지 필요 여부 확인
notification_result = self._notify_recipients(request)
return {
"success": True,
"status": "rectified",
"changes": {
"field": request.field_name,
"old_value": old_value,
"new_value": request.requested_value
},
"processing_time_days": (request.processed_at - request.created_at).days,
"recipient_notifications": notification_result
}
def _notify_recipients(self, request: RectificationRequest) -> dict:
"""
GDPR Article 19: 수신자에게 정정 통지
정보가 공개된 각 수신자에게 알려야 함 (불가능/과도한 노력 제외)
"""
recipients = self.recipients.get(request.subject_id, [])
notified = []
skipped = []
for recipient in recipients:
# 해당 카테고리 데이터를 공유받은 수신자만 통지
if request.category not in recipient.data_categories_shared:
continue
# 실제 구현에서는 이메일/API 통지 수행
recipient.notified = True
recipient.notification_date = datetime.now()
notified.append({
"recipient_id": recipient.recipient_id,
"name": recipient.name,
"notification_sent": True
})
return {
"total_recipients": len(recipients),
"notified": len(notified),
"notifications": notified,
"article_19_compliance": True,
"subject_can_request_list": True # 정보주체가 수신자 목록 요청 가능
}
def complete_incomplete_data(
self,
subject_id: str,
field_name: str,
supplementary_data: str,
supplementary_statement: str = ""
) -> dict:
"""
Art 16 후반부: 불완전한 데이터 보충
처리 목적을 고려하여 추가 진술 등을 통해 보충
"""
if subject_id not in self.user_data:
self.user_data[subject_id] = {}
current = self.user_data[subject_id].get(field_name, "")
# 불완전한 데이터 보충
self.user_data[subject_id][field_name] = supplementary_data
# 추가 진술이 있는 경우 별도 저장
if supplementary_statement:
statement_key = f"{field_name}_supplementary_statement"
self.user_data[subject_id][statement_key] = supplementary_statement
return {
"success": True,
"field": field_name,
"previous_value": current,
"completed_value": supplementary_data,
"supplementary_statement_recorded": bool(supplementary_statement),
"legal_basis": "GDPR Article 16 (completion of incomplete data)"
}
def get_recipient_list(self, subject_id: str) -> list[dict]:
"""정보주체에게 수신자 목록 제공 (Art 19)"""
recipients = self.recipients.get(subject_id, [])
return [
{
"name": r.name,
"categories_shared": [c.value for c in r.data_categories_shared],
"notified_of_rectification": r.notified
}
for r in recipients
]
# 사용 예시
if __name__ == "__main__":
handler = RectificationHandler()
# 데이터 수신자 등록
handler.register_recipient(
subject_id="user-001",
recipient=DataRecipient(
recipient_id="partner-001",
name="Partner Analytics Inc.",
contact_email="dpo@partner.com",
data_categories_shared=[DataCategory.CONTACT, DataCategory.EMPLOYMENT]
)
)
# 정정 요청 제출
result = handler.submit_rectification_request(
subject_id="user-001",
field_name="job_title",
current_value="Software Engineer",
requested_value="Senior Software Engineer",
category=DataCategory.EMPLOYMENT,
reason="승진 후 직위가 업데이트되지 않음",
evidence_provided=True
)
print(f"요청 ID: {result['request_id']}")
print(f"처리 기한: {result['deadline']}")
# 정정 처리
process_result = handler.verify_and_process(
request_id=result['request_id'],
verified=True
)
print(f"정정 완료: {process_result['changes']}")
print(f"수신자 통지: {process_result['recipient_notifications']['notified']}건")
# 출력: 수신자 통지: 1건