⚖️ AI 규제/윤리

처리정지권

Right to Restriction of Processing

개인정보 처리 정지를 요청할 권리

상세 설명

처리정지권(Right to Restriction of Processing)은 GDPR 제18조에서 규정하는 정보주체의 권리로, 특정 상황에서 개인정보의 처리를 일시적으로 정지하도록 요청할 수 있는 권리입니다. 삭제권(제17조)과 달리 데이터를 완전히 삭제하는 것이 아니라 저장은 유지하되 처리를 제한하는 중간 단계의 보호 수단입니다. 이 권리는 데이터 정확성 분쟁, 불법 처리 시 삭제 대신 제한 요청, 컨트롤러의 목적 종료 후 법적 청구를 위한 보존 필요, 이의권 행사 중 검증 대기 등 네 가지 상황에서 행사할 수 있습니다.

AI 시스템 맥락에서 처리정지권은 특히 중요합니다. 예를 들어, AI 기반 신용평가 시스템이 부정확한 데이터로 결정을 내렸다고 이의를 제기한 경우, 정보주체는 정확성이 확인될 때까지 해당 데이터의 AI 분석 처리를 정지시킬 수 있습니다. GDPR 제18조 제2항에 따르면 처리가 제한된 개인정보는 저장을 제외하고, 정보주체의 동의, 법적 청구의 설정/행사/방어, 다른 자연인/법인의 권리 보호, 중요한 공익을 위해서만 처리될 수 있습니다.

한국 개인정보보호법 제37조에서는 처리정지 요구권을 규정하고 있습니다. 정보주체는 개인정보처리자에게 자신의 개인정보 처리 정지를 요구할 수 있으며, 개인정보처리자는 지체 없이 처리를 정지해야 합니다. 다만 GDPR과 달리 법률에 특별한 규정이 있거나, 다른 사람의 생명/신체를 해할 우려가 있거나, 공공기관의 업무 수행에 현저한 지장을 초래하는 경우 등은 예외로 인정됩니다. 응답 기한은 요구를 받은 날로부터 10일 이내입니다.

실무적으로 처리정지는 기술적 구현이 필요합니다. 데이터베이스에 처리정지 플래그를 설정하고, 모든 데이터 처리 파이프라인에서 이 플래그를 확인하도록 해야 합니다. AI 학습 데이터셋에서 해당 데이터를 제외하고, 배치 처리나 분석 작업에서도 필터링해야 합니다. GDPR 제18조 제3항에 따르면 처리정지를 해제하기 전에 정보주체에게 반드시 알려야 하며, 위반 시 최대 2,000만 유로 또는 전세계 연매출의 4%에 해당하는 과징금이 부과될 수 있습니다.

코드 예제

Python으로 처리정지권 요청을 처리하고 데이터 처리 파이프라인에서 제한을 적용하는 시스템입니다.

from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from typing import Optional, Callable
import uuid

class RestrictionGround(Enum):
    """GDPR 제18조 처리정지 사유"""
    ACCURACY_CONTESTED = "accuracy_contested"  # 제18(1)(a): 정확성 이의
    UNLAWFUL_PROCESSING = "unlawful_processing"  # 제18(1)(b): 불법 처리
    LEGAL_CLAIMS = "legal_claims"  # 제18(1)(c): 법적 청구 목적 보존 필요
    OBJECTION_PENDING = "objection_pending"  # 제18(1)(d): 이의권 검증 중

class RestrictionStatus(Enum):
    """처리정지 상태"""
    REQUESTED = "requested"
    ACTIVE = "active"  # 처리정지 적용 중
    LIFTED = "lifted"  # 해제됨
    REJECTED = "rejected"

class AllowedProcessing(Enum):
    """처리정지 중 허용되는 처리 (제18조 제2항)"""
    STORAGE = "storage_only"  # 저장
    DATA_SUBJECT_CONSENT = "data_subject_consent"  # 정보주체 동의
    LEGAL_CLAIMS = "legal_claims_establishment"  # 법적 청구
    RIGHTS_PROTECTION = "other_persons_rights"  # 타인 권리 보호
    PUBLIC_INTEREST = "important_public_interest"  # 중요 공익

@dataclass
class RestrictionRequest:
    """처리정지 요청"""
    request_id: str = field(default_factory=lambda: f"RST-{uuid.uuid4().hex[:8].upper()}")
    data_subject_id: str = ""
    ground: RestrictionGround = RestrictionGround.ACCURACY_CONTESTED
    affected_data_categories: list[str] = field(default_factory=list)
    reason_description: str = ""
    submitted_at: datetime = field(default_factory=datetime.now)
    status: RestrictionStatus = RestrictionStatus.REQUESTED
    restricted_at: Optional[datetime] = None
    lifted_at: Optional[datetime] = None
    lift_notification_sent: bool = False

@dataclass
class DataRecord:
    """데이터 레코드 (처리정지 플래그 포함)"""
    record_id: str
    data_subject_id: str
    data_category: str
    content: dict
    is_restricted: bool = False
    restriction_request_id: Optional[str] = None
    restricted_since: Optional[datetime] = None

class ProcessingRestrictionHandler:
    """처리정지권 처리 시스템"""

    # GDPR 응답 기한: 1개월 (연장 시 최대 3개월)
    GDPR_RESPONSE_DEADLINE_DAYS = 30
    # 개인정보보호법 응답 기한: 10일
    PIPA_RESPONSE_DEADLINE_DAYS = 10

    def __init__(self, jurisdiction: str = "GDPR"):
        self.jurisdiction = jurisdiction
        self.requests: dict[str, RestrictionRequest] = {}
        self.data_store: dict[str, DataRecord] = {}
        self.processing_hooks: list[Callable] = []

    def submit_restriction_request(
        self,
        data_subject_id: str,
        ground: RestrictionGround,
        data_categories: list[str],
        reason: str
    ) -> RestrictionRequest:
        """처리정지 요청 제출"""

        request = RestrictionRequest(
            data_subject_id=data_subject_id,
            ground=ground,
            affected_data_categories=data_categories,
            reason_description=reason
        )

        self.requests[request.request_id] = request

        # 정확성 이의나 이의권 행사 중인 경우 즉시 처리정지 적용
        if ground in [RestrictionGround.ACCURACY_CONTESTED,
                      RestrictionGround.OBJECTION_PENDING]:
            self._apply_restriction(request)

        return request

    def _apply_restriction(self, request: RestrictionRequest) -> int:
        """처리정지 적용"""

        restricted_count = 0

        for record_id, record in self.data_store.items():
            if record.data_subject_id == request.data_subject_id:
                if not request.affected_data_categories or \
                   record.data_category in request.affected_data_categories:
                    record.is_restricted = True
                    record.restriction_request_id = request.request_id
                    record.restricted_since = datetime.now()
                    restricted_count += 1

        request.status = RestrictionStatus.ACTIVE
        request.restricted_at = datetime.now()

        return restricted_count

    def process_data(
        self,
        record_id: str,
        processing_purpose: AllowedProcessing,
        has_consent: bool = False
    ) -> dict:
        """데이터 처리 시도 (처리정지 확인)"""

        if record_id not in self.data_store:
            return {"allowed": False, "reason": "Record not found"}

        record = self.data_store[record_id]

        if not record.is_restricted:
            return {"allowed": True, "data": record.content}

        # 처리정지 중인 경우 허용되는 처리만 가능 (제18조 제2항)
        allowed_reasons = {
            AllowedProcessing.STORAGE: True,  # 저장은 항상 허용
            AllowedProcessing.DATA_SUBJECT_CONSENT: has_consent,
            AllowedProcessing.LEGAL_CLAIMS: True,  # 법적 청구는 별도 검증 필요
            AllowedProcessing.RIGHTS_PROTECTION: True,  # 별도 검증 필요
            AllowedProcessing.PUBLIC_INTEREST: True  # 별도 검증 필요
        }

        if allowed_reasons.get(processing_purpose, False):
            return {
                "allowed": True,
                "restricted": True,
                "processing_basis": processing_purpose.value,
                "data": record.content,
                "warning": "Data is under restriction - only limited processing allowed"
            }

        return {
            "allowed": False,
            "restricted": True,
            "reason": f"Processing restricted since {record.restricted_since}",
            "restriction_id": record.restriction_request_id,
            "allowed_processing": [p.value for p in AllowedProcessing]
        }

    def lift_restriction(
        self,
        request_id: str,
        lift_reason: str
    ) -> dict:
        """처리정지 해제 (사전 통지 필수 - 제18조 제3항)"""

        if request_id not in self.requests:
            return {"success": False, "error": "Request not found"}

        request = self.requests[request_id]

        if request.status != RestrictionStatus.ACTIVE:
            return {"success": False, "error": "Restriction not active"}

        # 정보주체에게 해제 전 통지 (필수)
        if not request.lift_notification_sent:
            notification = self._send_lift_notification(request)
            request.lift_notification_sent = True
            return {
                "success": False,
                "pending": True,
                "reason": "Data subject must be notified before lifting",
                "notification_sent": notification
            }

        # 처리정지 해제
        lifted_count = 0
        for record in self.data_store.values():
            if record.restriction_request_id == request_id:
                record.is_restricted = False
                record.restriction_request_id = None
                record.restricted_since = None
                lifted_count += 1

        request.status = RestrictionStatus.LIFTED
        request.lifted_at = datetime.now()

        return {
            "success": True,
            "request_id": request_id,
            "lifted_records": lifted_count,
            "lift_reason": lift_reason,
            "lifted_at": request.lifted_at.isoformat()
        }

    def _send_lift_notification(self, request: RestrictionRequest) -> dict:
        """처리정지 해제 전 정보주체 통지"""
        return {
            "notification_type": "restriction_lift_notice",
            "data_subject_id": request.data_subject_id,
            "message": "처리정지가 해제될 예정입니다. 이의가 있으시면 연락주세요.",
            "sent_at": datetime.now().isoformat(),
            "legal_basis": "GDPR Article 18(3)"
        }

    def get_response_deadline(self, request_id: str) -> dict:
        """응답 기한 계산"""

        request = self.requests.get(request_id)
        if not request:
            return {"error": "Request not found"}

        if self.jurisdiction == "GDPR":
            deadline_days = self.GDPR_RESPONSE_DEADLINE_DAYS
        else:  # PIPA
            deadline_days = self.PIPA_RESPONSE_DEADLINE_DAYS

        deadline = request.submitted_at + timedelta(days=deadline_days)
        remaining = (deadline - datetime.now()).days

        return {
            "request_id": request_id,
            "jurisdiction": self.jurisdiction,
            "submitted_at": request.submitted_at.isoformat(),
            "deadline": deadline.isoformat(),
            "remaining_days": max(0, remaining),
            "overdue": remaining < 0
        }

    def filter_for_ai_training(self, dataset: list[str]) -> list[str]:
        """AI 학습 데이터셋에서 처리정지 데이터 제외"""

        return [
            record_id for record_id in dataset
            if record_id in self.data_store
            and not self.data_store[record_id].is_restricted
        ]

# 사용 예시
if __name__ == "__main__":
    handler = ProcessingRestrictionHandler(jurisdiction="GDPR")

    # 테스트 데이터 추가
    handler.data_store["REC001"] = DataRecord(
        record_id="REC001",
        data_subject_id="USER123",
        data_category="credit_history",
        content={"score": 720, "history": "good"}
    )

    # 정확성 이의로 처리정지 요청
    request = handler.submit_restriction_request(
        data_subject_id="USER123",
        ground=RestrictionGround.ACCURACY_CONTESTED,
        data_categories=["credit_history"],
        reason="신용 점수가 실제와 다름"
    )
    print(f"처리정지 요청: {request.request_id}, 상태: {request.status.value}")

    # 데이터 처리 시도 (일반 목적)
    result = handler.process_data("REC001", AllowedProcessing.STORAGE)
    print(f"저장 처리: {result['allowed']}")

    # AI 학습용 필터링
    filtered = handler.filter_for_ai_training(["REC001", "REC002"])
    print(f"AI 학습 가능 데이터: {filtered}")

실무 대화

고객지원팀:

"고객이 AI 추천 시스템에서 자신의 구매 이력 처리를 정지해달라고 요청했습니다. 데이터 정확성에 이의가 있다고 하네요."

개인정보보호 담당자:

"GDPR 제18조 제1항 (a)호에 해당하는 정확성 이의 사례네요. 정확성을 확인하는 동안 즉시 처리정지를 적용해야 합니다. 삭제가 아니라 처리 제한이니 데이터는 보존하되 AI 분석에서는 제외해주세요."

데이터 엔지니어:

"추천 엔진 파이프라인에서 해당 고객 데이터를 필터링하겠습니다. 다만 다음 주 예정된 모델 재학습에서도 제외해야 하나요?"

개인정보보호 담당자:

"네, 학습 데이터셋에서도 제외해야 합니다. 처리정지 중에는 저장, 정보주체 동의가 있는 처리, 법적 청구, 타인 권리 보호, 공익 목적 외의 처리는 불가합니다. 정확성 확인 후 해제할 때는 반드시 고객에게 먼저 알려야 해요."

면접관:

"처리정지권과 삭제권의 차이점을 설명해주세요."

지원자:

"삭제권(제17조)은 데이터를 완전히 삭제하는 반면, 처리정지권(제18조)은 데이터를 보존하되 처리만 제한하는 중간 단계입니다. 처리정지는 정확성 분쟁 중일 때, 불법 처리지만 삭제 대신 제한을 원할 때, 컨트롤러는 불필요하지만 정보주체가 법적 청구를 위해 필요할 때, 이의권 행사 검증 중일 때 적용됩니다. 처리정지 중에는 저장을 제외하고 정보주체 동의, 법적 청구, 타인 권리 보호, 공익 목적으로만 처리 가능합니다."

면접관:

"처리정지 해제 시 유의사항은 무엇인가요?"

지원자:

"제18조 제3항에 따라 처리정지를 해제하기 전에 반드시 정보주체에게 통지해야 합니다. 이 통지 없이 해제하면 GDPR 위반이 됩니다. 또한 처리정지 사유가 해소되었는지 확인해야 하고, 해제 이유와 시점을 기록으로 남겨야 합니다."

시니어:

"처리정지 플래그 체크 로직이 API 레이어에만 있네요. 배치 처리나 데이터 파이프라인에서는 어떻게 되나요?"

주니어:

"아, 맞아요. Spark 작업이나 ML 파이프라인에서도 처리정지 필터가 필요하겠네요. 공통 데이터 접근 레이어에서 체크하도록 리팩토링해야겠습니다."

시니어:

"그리고 처리정지 해제 로직에서 notification_sent 플래그만 체크하고 있는데, 실제로 정보주체가 통지를 받았는지, 일정 기간이 지났는지도 확인해야 할 것 같아요. 단순히 이메일 발송 여부가 아니라 합리적인 통지 기간을 보장해야 합니다."

주니어:

"통지 후 대기 기간을 설정하고, 정보주체의 응답을 받을 수 있는 채널도 열어두겠습니다. 해제 전 이의 제기 기회를 보장해야 하니까요."

주의사항

  • 모든 처리 경로 차단: 처리정지 플래그는 API뿐 아니라 배치 처리, ETL 파이프라인, ML 학습, 분석 작업 등 모든 데이터 처리 경로에서 확인되어야 합니다. 한 곳이라도 누락되면 GDPR 위반이 됩니다.
  • 해제 전 통지 필수: 제18조 제3항에 따라 처리정지 해제 전 정보주체에게 반드시 알려야 합니다. 통지 없이 처리를 재개하면 최대 2,000만 유로 또는 매출의 4% 과징금 대상입니다.
  • 처리정지 중 허용 처리 제한: 처리정지 중에는 저장을 제외하고 정보주체 동의, 법적 청구, 타인 권리 보호, 공익 목적으로만 처리 가능합니다. 마케팅, 프로파일링, AI 학습 등 일반적인 비즈니스 목적 처리는 불가합니다.

더 배우기