처리정지권
Right to Restriction of Processing
개인정보 처리 정지를 요청할 권리
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}")