⚖️ AI 규제/윤리

자동화된 의사결정

Automated Decision-Making

인간 개입 없이 AI가 자동으로 결정을 내리는 것. GDPR 제22조와 EU AI Act에서 규제하는 핵심 개념.

상세 설명

자동화된 의사결정(Automated Decision-Making, ADM)은 인간의 개입 없이 기술적 수단만으로 개인에 관한 결정을 내리는 것을 의미합니다. GDPR 제22조 제1항은 정보주체가 "자신에게 법적 효력을 미치거나 유사하게 중대한 영향을 미치는 프로파일링 등 자동화된 처리에만 기반한 결정의 대상이 되지 않을 권리"를 규정합니다. 이는 AI 시대의 핵심적인 개인 권리 보호 조항입니다.

제22조가 적용되려면 세 가지 조건이 충족되어야 합니다. 첫째, 결정이 오로지 자동화된 처리에만 기반해야 합니다(solely automated). 인간이 실질적으로 개입한다면 적용되지 않습니다. 둘째, 프로파일링을 포함할 수 있습니다. 셋째, 법적 효력이나 유사하게 중대한 영향(significantly affects)이 있어야 합니다. 예를 들어, 대출 거절, 채용 탈락, 보험료 인상 등이 해당합니다.

제22조의 예외는 세 가지입니다: (a) 계약 체결/이행에 필요한 경우, (b) EU 또는 회원국 법률에 의해 허용된 경우, (c) 정보주체의 명시적 동의가 있는 경우. 그러나 예외가 적용되더라도 컨트롤러는 최소한 (1) 인간 개입 요청권, (2) 의견 표명권, (3) 결정 이의 제기권을 보장해야 합니다. 특수 범주 데이터(인종, 건강 등)에 기반한 자동화된 결정은 더 엄격한 조건이 적용됩니다.

EU AI Act는 GDPR의 ADM 규정을 보완합니다. 고위험 AI(채용, 신용 평가, 사법 등)에는 투명성, 인간 감독, 위험 관리 요건이 추가됩니다. 한국 개인정보보호법 제37조의2도 자동화된 결정에 대한 거부권과 설명 요구권을 규정합니다. 위반 시 GDPR 기준 최대 2,000만 유로 또는 전 세계 연간 매출의 4% 과징금이 부과될 수 있습니다. 실무에서는 '의미 있는 인간 개입(meaningful human involvement)'을 증명하는 것이 핵심입니다.

코드 예제

GDPR 제22조를 준수하는 자동화된 의사결정 시스템 구현 예제입니다.

"""
자동화된 의사결정(ADM) 시스템
GDPR 제22조 및 개인정보보호법 제37조의2 준수
"""
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Dict, List, Optional, Any
import logging


class DecisionType(Enum):
    """결정 유형"""
    FULLY_AUTOMATED = "fully_automated"      # 완전 자동화 (제22조 적용)
    HUMAN_ASSISTED = "human_assisted"         # 인간 보조 결정
    HUMAN_DECIDED = "human_decided"           # 인간 결정


class LegalBasis(Enum):
    """제22조 예외 법적 근거"""
    CONTRACT_NECESSITY = "contract"           # 계약 체결/이행에 필요
    LEGAL_AUTHORIZATION = "legal"             # 법률에 의해 허용
    EXPLICIT_CONSENT = "consent"              # 명시적 동의
    NOT_APPLICABLE = "n/a"                    # 예외 없음


class ImpactLevel(Enum):
    """결정의 영향 수준"""
    LOW = "low"                               # 낮은 영향
    MODERATE = "moderate"                      # 중간 영향
    SIGNIFICANT = "significant"                # 중대한 영향 (제22조 적용)
    LEGAL_EFFECT = "legal_effect"             # 법적 효력


@dataclass
class AutomatedDecision:
    """자동화된 결정 기록"""
    decision_id: str
    subject_id: str
    decision_type: DecisionType
    model_name: str
    input_data: Dict
    output: Any
    confidence: float
    explanation: str
    impact_level: ImpactLevel
    legal_basis: Optional[LegalBasis] = None
    human_reviewer: Optional[str] = None
    human_review_notes: Optional[str] = None
    created_at: datetime = field(default_factory=datetime.now)
    objection_requested: bool = False
    objection_reason: Optional[str] = None


class GDPRArticle22Handler:
    """
    GDPR 제22조 준수 핸들러

    주요 기능:
    - 자동화된 결정 적법성 검증
    - 인간 개입 요청 처리
    - 의견 표명 및 이의 제기 처리
    - 결정 설명 제공
    """

    # 중대한 영향 분야
    SIGNIFICANT_DOMAINS = [
        "credit_scoring",      # 신용 평가
        "employment",          # 채용/고용
        "insurance",           # 보험
        "social_benefits",     # 사회 급여
        "education",           # 교육 입학
        "health",              # 의료
        "legal"                # 법적 결정
    ]

    def __init__(self, audit_logger):
        self.audit_logger = audit_logger
        self.decisions: Dict[str, AutomatedDecision] = {}
        self.logger = logging.getLogger("gdpr_article22")

    def register_decision(
        self,
        decision: AutomatedDecision,
        domain: str
    ) -> Dict:
        """
        자동화된 결정 등록 및 제22조 적용 여부 확인

        Returns:
            제22조 적용 여부 및 필요 조치
        """
        # 제22조 적용 여부 판단
        article22_applies = self._check_article22_applicability(
            decision, domain
        )

        self.decisions[decision.decision_id] = decision

        result = {
            "decision_id": decision.decision_id,
            "article_22_applies": article22_applies,
            "decision_type": decision.decision_type.value,
            "impact_level": decision.impact_level.value
        }

        if article22_applies:
            # 법적 근거 검증
            legal_basis_valid = self._validate_legal_basis(decision)

            result.update({
                "legal_basis": decision.legal_basis.value if decision.legal_basis else None,
                "legal_basis_valid": legal_basis_valid,
                "required_safeguards": self._get_required_safeguards(),
                "warning": None if legal_basis_valid else
                    "GDPR 제22조 위반 가능성: 유효한 법적 근거 없이 완전 자동화된 중대 결정"
            })

        self.audit_logger.log_event(
            event_type="ADM_DECISION_REGISTERED",
            decision_id=decision.decision_id,
            article_22_applies=article22_applies,
            impact_level=decision.impact_level.value
        )

        return result

    def _check_article22_applicability(
        self,
        decision: AutomatedDecision,
        domain: str
    ) -> bool:
        """
        GDPR 제22조 적용 여부 확인

        적용 조건:
        1. 완전 자동화된 결정 (solely automated)
        2. 중대한 영향 또는 법적 효력
        """
        # 완전 자동화가 아니면 미적용
        if decision.decision_type != DecisionType.FULLY_AUTOMATED:
            return False

        # 중대한 영향 또는 법적 효력 확인
        significant_impact = (
            decision.impact_level in [ImpactLevel.SIGNIFICANT, ImpactLevel.LEGAL_EFFECT] or
            domain in self.SIGNIFICANT_DOMAINS
        )

        return significant_impact

    def _validate_legal_basis(self, decision: AutomatedDecision) -> bool:
        """법적 근거 유효성 검증"""
        if not decision.legal_basis:
            return False

        if decision.legal_basis == LegalBasis.NOT_APPLICABLE:
            return False

        # 각 법적 근거에 대한 추가 검증 필요
        # (실제 구현에서는 동의 기록, 계약 문서 등 확인)
        return True

    def _get_required_safeguards(self) -> List[str]:
        """제22조 제3항: 필수 보호조치"""
        return [
            "정보주체의 인간 개입 요청권 보장",
            "정보주체의 의견 표명권 보장",
            "결정에 대한 이의 제기권 보장"
        ]

    def request_human_intervention(
        self,
        decision_id: str,
        subject_id: str,
        reason: str
    ) -> Dict:
        """
        인간 개입 요청 (제22조 제3항)

        정보주체는 자동화된 결정에 대해
        인간 개입을 요청할 권리가 있음
        """
        decision = self.decisions.get(decision_id)

        if not decision:
            raise ValueError(f"Decision not found: {decision_id}")

        if decision.subject_id != subject_id:
            raise PermissionError("Unauthorized: subject_id mismatch")

        # 결정 유형을 인간 검토 필요로 변경
        decision.decision_type = DecisionType.HUMAN_ASSISTED
        decision.objection_requested = True
        decision.objection_reason = reason

        self.audit_logger.log_event(
            event_type="HUMAN_INTERVENTION_REQUESTED",
            decision_id=decision_id,
            reason=reason
        )

        return {
            "status": "intervention_requested",
            "decision_id": decision_id,
            "message": "인간 검토자가 귀하의 결정을 재검토할 예정입니다.",
            "expected_timeline": "영업일 기준 5일 이내"
        }

    def express_view(
        self,
        decision_id: str,
        subject_id: str,
        view: str,
        supporting_documents: List[str] = None
    ) -> Dict:
        """
        의견 표명 (제22조 제3항)

        정보주체는 자동화된 결정에 대해
        자신의 의견을 표명할 권리가 있음
        """
        decision = self.decisions.get(decision_id)

        if not decision or decision.subject_id != subject_id:
            raise ValueError("Invalid decision or subject")

        self.audit_logger.log_event(
            event_type="VIEW_EXPRESSED",
            decision_id=decision_id,
            view_summary=view[:100] + "..." if len(view) > 100 else view,
            has_documents=bool(supporting_documents)
        )

        return {
            "status": "view_recorded",
            "decision_id": decision_id,
            "message": "귀하의 의견이 기록되었습니다. 검토 후 연락드리겠습니다."
        }

    def contest_decision(
        self,
        decision_id: str,
        subject_id: str,
        grounds: str
    ) -> Dict:
        """
        결정 이의 제기 (제22조 제3항)

        정보주체는 자동화된 결정에
        이의를 제기할 권리가 있음
        """
        decision = self.decisions.get(decision_id)

        if not decision or decision.subject_id != subject_id:
            raise ValueError("Invalid decision or subject")

        decision.objection_requested = True
        decision.objection_reason = grounds

        self.audit_logger.log_event(
            event_type="DECISION_CONTESTED",
            decision_id=decision_id,
            grounds_summary=grounds[:100]
        )

        return {
            "status": "contest_received",
            "decision_id": decision_id,
            "case_number": f"CONTEST-{decision_id[:8]}",
            "message": "이의 제기가 접수되었습니다. 재심사를 진행합니다.",
            "expected_response": "30일 이내"
        }

    def get_decision_explanation(
        self,
        decision_id: str,
        subject_id: str
    ) -> Dict:
        """
        결정 설명 제공 (제13-15조, Recital 71)

        정보주체는 자동화된 결정의 로직,
        중요성, 예상 결과에 대한 설명을 받을 권리가 있음
        """
        decision = self.decisions.get(decision_id)

        if not decision or decision.subject_id != subject_id:
            raise ValueError("Invalid decision or subject")

        return {
            "decision_id": decision_id,
            "decision_date": decision.created_at.isoformat(),
            "decision_outcome": str(decision.output),
            "confidence_level": f"{decision.confidence:.0%}",
            "explanation": {
                "logic_summary": decision.explanation,
                "key_factors": self._extract_key_factors(decision.input_data),
                "significance": self._get_impact_description(decision.impact_level),
                "possible_consequences": self._get_consequences(decision.output)
            },
            "your_rights": [
                "인간 검토 요청 (제22조 제3항)",
                "의견 표명 (제22조 제3항)",
                "결정 이의 제기 (제22조 제3항)"
            ]
        }

    def _extract_key_factors(self, input_data: Dict) -> List[str]:
        """결정에 영향을 미친 주요 요소 추출"""
        return [f"{k}: {v}" for k, v in list(input_data.items())[:5]]

    def _get_impact_description(self, level: ImpactLevel) -> str:
        descriptions = {
            ImpactLevel.LOW: "이 결정은 귀하에게 경미한 영향을 미칩니다.",
            ImpactLevel.MODERATE: "이 결정은 귀하에게 중간 수준의 영향을 미칩니다.",
            ImpactLevel.SIGNIFICANT: "이 결정은 귀하에게 중대한 영향을 미칩니다.",
            ImpactLevel.LEGAL_EFFECT: "이 결정은 귀하에게 법적 효력이 있습니다."
        }
        return descriptions.get(level, "영향 수준을 평가할 수 없습니다.")

    def _get_consequences(self, output: Any) -> List[str]:
        """결정의 예상 결과"""
        return [
            "이 결정에 따라 서비스/혜택이 결정됩니다.",
            "이의 제기 시 재검토가 가능합니다."
        ]


# 사용 예시
if __name__ == "__main__":
    import uuid

    logging.basicConfig(level=logging.INFO)

    class MockAuditLogger:
        def log_event(self, **kwargs):
            print(f"AUDIT: {kwargs}")

    handler = GDPRArticle22Handler(MockAuditLogger())

    # 자동화된 신용 결정 등록
    decision = AutomatedDecision(
        decision_id=str(uuid.uuid4())[:8],
        subject_id="user_12345",
        decision_type=DecisionType.FULLY_AUTOMATED,
        model_name="credit_model_v3",
        input_data={
            "income": 50000,
            "debt_ratio": 0.3,
            "credit_history_years": 5
        },
        output="DENIED",
        confidence=0.85,
        explanation="부채 비율이 기준(0.25)을 초과함",
        impact_level=ImpactLevel.SIGNIFICANT,
        legal_basis=LegalBasis.CONTRACT_NECESSITY
    )

    # 결정 등록 및 제22조 적용 확인
    result = handler.register_decision(decision, "credit_scoring")
    print(f"Article 22 applies: {result['article_22_applies']}")

    # 결정 설명 요청
    explanation = handler.get_decision_explanation(
        decision.decision_id,
        "user_12345"
    )
    print(f"Explanation: {explanation['explanation']['logic_summary']}")

    # 인간 개입 요청
    intervention = handler.request_human_intervention(
        decision.decision_id,
        "user_12345",
        "추가 소득 자료가 있어 재검토 요청"
    )
    print(f"Intervention status: {intervention['status']}")

실무 대화

PM: 우리 대출 심사 AI가 완전 자동화되어 있는데, GDPR 제22조 문제가 있나요?
개발자: 네, 대출 거절은 '중대한 영향'에 해당하므로 제22조가 적용됩니다. 완전 자동화된 결정은 원칙적으로 금지되지만, 계약 필요성이나 명시적 동의가 있으면 예외입니다.
PM: 우리는 약관에 동의를 받고 있는데요?
개발자: '명시적 동의'는 일반 약관 동의와 다릅니다. 별도로 자동화된 결정에 대한 동의를 받아야 해요. 그리고 동의가 있어도 인간 개입 요청권, 의견 표명권, 이의 제기권을 보장해야 합니다. 최소 1명은 AI 결정을 검토하는 게 안전합니다.
면접관: GDPR 제22조의 'solely automated'와 'significantly affects'를 설명해주세요.
지원자: 'Solely automated'는 인간이 실질적으로 개입하지 않고 기술만으로 결정이 내려지는 것입니다. 형식적인 승인만 하는 rubber-stamping은 인간 개입으로 인정되지 않습니다. 'Significantly affects'는 법적 효력은 아니지만 그에 준하는 영향을 말하며, 대출 거절, 채용 탈락, 보험료 인상 등이 해당합니다.
면접관: 제22조 예외가 적용되어도 필요한 보호조치는 무엇인가요?
지원자: 제22조 제3항에 따라 세 가지 권리를 보장해야 합니다. 첫째, 인간 개입 요청권입니다. 둘째, 자신의 의견을 표명할 권리입니다. 셋째, 결정에 이의를 제기할 권리입니다. 이 세 가지는 예외 적용 시에도 반드시 보장되어야 합니다.
리뷰어: 이 채용 AI 시스템에서 자동 탈락 로직이 있네요. 제22조 위반입니다.
작성자: 하지만 HR팀이 최종 승인하는데요?
리뷰어: AI가 탈락시킨 후보는 HR이 검토도 안 하잖아요. 그건 'solely automated'입니다. HR이 AI 추천을 무비판적으로 수용하는 것도 실질적 인간 개입이 아니에요. 모든 탈락 결정에 인간 검토를 추가하거나, 지원자에게 인간 검토 요청 기능을 제공해야 합니다.
작성자: 알겠습니다. 탈락 결정에 인간 검토 단계를 추가하고, 지원자 포털에 이의 제기 기능도 넣겠습니다.

주의사항

실질적 인간 개입 필요

'Solely automated'를 피하려면 인간 개입이 실질적이어야 합니다. 단순히 AI 결정을 승인만 하는 'rubber-stamping'은 인정되지 않습니다. 인간 검토자가 (1) AI 결정을 이해하고, (2) 관련 데이터를 검토하며, (3) 필요시 결정을 변경할 권한과 능력이 있어야 합니다. 검토 시간, 변경 비율 등을 모니터링하여 형식적 검토가 아님을 증명해야 합니다.

명시적 동의의 요건

제22조 예외로서의 '명시적 동의'는 일반 약관 동의와 다릅니다. GDPR 제4조 제11호의 요건을 충족해야 합니다: (1) 자유롭게 주어진 동의, (2) 구체적이고 정보에 입각한 동의, (3) 명확한 동의 표시. 자동화된 결정에 대해 별도의 동의 절차가 필요하며, 동의 철회 시에도 서비스를 제공해야 합니다.

특수 범주 데이터 제한

인종, 민족, 정치적 견해, 종교, 건강, 성적 지향 등 특수 범주 데이터(제9조)에 기반한 자동화된 결정은 더 엄격합니다. 명시적 동의나 중대한 공익이 있어도 적절한 보호조치가 필수입니다. 많은 경우 이러한 데이터를 AI 의사결정에 사용하는 것 자체를 피하는 것이 바람직합니다.

더 배우기