⚖️ AI 규제/윤리

정정권

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%의 과징금 대상이 됩니다. 정보주체는 거부 시 감독기관에 민원을 제기할 권리가 있습니다.

💻 코드 예제

Python GDPR 제16조/제19조 기반 정정권 처리 시스템
"""
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건

💬 실무 대화

AI가 추론한 데이터(예: 예측 신용점수)도 정정권 대상인가요?
복잡한 문제입니다. GDPR 정정권은 "사실적으로 부정확한" 데이터에 적용됩니다. AI 추론 데이터가 입력 데이터의 오류에 기인한다면 정정 대상이 될 수 있습니다. 그러나 정확한 입력에서 도출된 추론 자체는 "의견"에 가까워 정정보다는 제22조(자동화된 의사결정) 권리나 반대권이 더 적합할 수 있습니다.
파트너사에 공유한 데이터도 정정해야 하나요?
네, GDPR 제19조에 따라 개인정보를 공개한 수신자에게 정정 사실을 통지해야 합니다. 불가능하거나 과도한 노력이 필요한 경우는 예외지만, 이를 입증해야 합니다. 정보주체가 요청하면 수신자 목록도 제공해야 합니다. 데이터 공유 계약에 정정 통지 의무를 명시하는 것이 좋습니다.
GDPR 제16조 정정권의 두 가지 측면을 설명해주세요.
첫째는 부정확한 데이터의 정정으로, 사실과 다른 개인정보를 지체 없이 수정할 권리입니다. 이름, 주소, 생년월일 오류 등이 해당합니다. 둘째는 불완전한 데이터의 보충으로, 처리 목적을 고려하여 추가 진술 등을 통해 데이터를 완성할 권리입니다. 예를 들어 학력 정보에 최신 학위를 추가하는 것이 해당합니다.
정정 요청을 거부할 수 있는 경우는요?
정정권은 대체로 강력한 권리이지만, 일부 예외가 있습니다. 법적 보관 의무가 있는 기록(회계 데이터, 감사 로그), 사법 절차를 위한 증거 보존, 공익적 기록 보관 목적 등의 경우 거부가 가능합니다. 다만 거부 시에는 사유를 명확히 안내하고, 감독기관 민원 및 사법적 구제 수단을 고지해야 합니다.
NON_RECTIFIABLE_CATEGORIES를 하드코딩했는데, 이게 적절한가요?
법적 제한이 있는 필드는 명시적으로 정의하는 것이 좋습니다. 다만 관할권별로 차이가 있을 수 있으니, 설정 파일이나 데이터베이스에서 관리하고 법무팀 검토 후 업데이트하는 프로세스를 권장합니다. 또한 거부 시 구체적인 법적 근거(예: "상법 제33조 회계장부 보존 의무")를 반환하도록 개선하세요.
수신자 통지 시 실패 처리는 어떻게 해야 하나요?
좋은 지적입니다. 현재 코드는 통지 성공을 가정하고 있습니다. 실패 시 재시도 로직과 실패 기록이 필요합니다. 또한 "불가능하거나 과도한 노력"의 예외 적용 시 그 판단 근거를 문서화해야 합니다. notification_attempts, last_failure_reason, deemed_impossible 필드를 추가하고, 정보주체가 통지 상태를 조회할 수 있게 하세요.

⚠️ 주의사항

📚 더 배우기