⚖️ AI 규제/윤리

표준계약조항

Standard Contractual Clauses (SCC)

개인정보 국외이전 시 사용하는 표준 계약 조건

상세 설명

표준계약조항(Standard Contractual Clauses, SCC)은 EU 집행위원회가 승인한 계약 조항으로, 적정성 결정이 없는 제3국으로의 개인정보 이전에 대한 법적 근거를 제공합니다. GDPR 제46조 제2항 (c)호에 따라 SCC를 체결하면 별도의 감독기관 승인 없이 개인정보를 제3국으로 이전할 수 있습니다. 2021년 6월 EU 집행위원회는 새로운 SCC(Commission Implementing Decision (EU) 2021/914)를 채택하여 다양한 이전 시나리오를 다루는 모듈형 구조를 도입했습니다.

새로운 SCC는 네 가지 모듈로 구성됩니다: Module 1(컨트롤러 to 컨트롤러), Module 2(컨트롤러 to 프로세서), Module 3(프로세서 to 프로세서), Module 4(프로세서 to 컨트롤러). 각 모듈은 데이터 전송 당사자의 역할에 따라 적합한 의무와 보호 조치를 규정합니다. AI 시스템 개발 시 글로벌 클라우드 서비스를 이용하거나 해외 AI 모델 제공업체와 협력하는 경우, 주로 Module 2가 적용됩니다. 프로세서가 다른 프로세서(서브프로세서)에게 데이터를 위탁하는 경우에는 Module 3이 필요합니다.

Schrems II 판결(2020년) 이후 SCC 사용 시 이전 영향 평가(Transfer Impact Assessment, TIA)가 필수가 되었습니다. SCC 제14조에 따라 데이터 수입자의 소재국 법령이 SCC 준수에 미치는 영향을 평가해야 하며, 필요시 보충적 조치(supplementary measures)를 적용해야 합니다. 보충적 조치로는 강력한 암호화, 가명처리, 데이터 분할 저장, 정부 접근 제한을 위한 계약 조항 등이 있습니다. EDPB(유럽 데이터보호 이사회)는 보충적 조치에 관한 구체적인 가이드라인을 제공하고 있습니다.

한국 개인정보보호법 제28조의8에서도 표준개인정보보호지침에 따른 계약 체결을 국외이전의 적법 근거로 인정합니다. 개인정보보호위원회가 고시한 표준계약서는 GDPR SCC와 유사한 구조를 가지며, 한국 기업이 해외로 개인정보를 이전할 때 사용할 수 있습니다. 다만 GDPR SCC와 한국 표준계약서가 동일하지 않으므로, 양방향 데이터 흐름이 있는 경우 두 규제 체계 모두를 충족하는 계약 구조가 필요합니다.

코드 예제

Python으로 SCC 기반 데이터 이전을 관리하고 이전 영향 평가를 추적하는 시스템입니다.

from dataclasses import dataclass, field
from datetime import date, datetime
from enum import Enum
from typing import Optional
import hashlib
import json

class SCCModule(Enum):
    """SCC 모듈 유형 (2021/914)"""
    MODULE_1 = "controller_to_controller"
    MODULE_2 = "controller_to_processor"
    MODULE_3 = "processor_to_processor"
    MODULE_4 = "processor_to_controller"

class TransferRiskLevel(Enum):
    """이전 영향 평가 위험 수준"""
    LOW = "low"  # 적정성 결정국 수준
    MEDIUM = "medium"  # 보충적 조치로 해결 가능
    HIGH = "high"  # 보충적 조치로도 불충분할 수 있음
    PROHIBITIVE = "prohibitive"  # 이전 불가

class SupplementaryMeasure(Enum):
    """보충적 조치 유형"""
    STRONG_ENCRYPTION = "strong_encryption_in_transit_at_rest"
    PSEUDONYMISATION = "pseudonymisation_before_transfer"
    SPLIT_PROCESSING = "split_processing_across_jurisdictions"
    CONTRACTUAL_COMMITMENTS = "enhanced_contractual_commitments"
    GOVERNMENT_ACCESS_NOTIFICATION = "government_access_notification"
    AUDIT_RIGHTS = "expanded_audit_rights"

@dataclass
class DataImporter:
    """데이터 수입자 (제3국 소재)"""
    name: str
    country_code: str
    country_name: str
    has_adequacy_decision: bool
    adequacy_scope: Optional[str] = None  # 예: "Data Privacy Framework"
    surveillance_law_concerns: list[str] = field(default_factory=list)

@dataclass
class SCCAgreement:
    """SCC 계약"""
    agreement_id: str
    module: SCCModule
    data_exporter: str  # EU 소재 기업
    data_importer: DataImporter
    execution_date: date
    data_categories: list[str]
    data_subjects: list[str]
    transfer_purpose: str
    supplementary_measures: list[SupplementaryMeasure] = field(default_factory=list)
    tia_completed: bool = False
    tia_date: Optional[date] = None
    tia_risk_level: Optional[TransferRiskLevel] = None
    valid: bool = True

    def get_agreement_hash(self) -> str:
        """계약 식별 해시"""
        content = f"{self.agreement_id}|{self.data_exporter}|{self.data_importer.name}"
        return hashlib.sha256(content.encode()).hexdigest()[:12]

@dataclass
class TransferImpactAssessment:
    """이전 영향 평가 (TIA)"""
    tia_id: str
    scc_agreement_id: str
    assessment_date: date
    destination_country: str
    laws_assessed: list[str]
    access_requests_history: int  # 최근 정부 접근 요청 수
    legal_remedies_available: bool
    effective_supplementary_measures: list[SupplementaryMeasure]
    risk_level: TransferRiskLevel
    assessor: str
    next_review_date: date
    notes: str = ""

class SCCTransferManager:
    """SCC 기반 국외이전 관리 시스템"""

    # 주요국 위험 프로필 (예시)
    COUNTRY_RISK_PROFILES = {
        "US": {
            "has_adequacy": True,
            "adequacy_scope": "Data Privacy Framework (DPF)",
            "concerns": ["FISA 702", "EO 12333"],
            "dpf_certified_required": True
        },
        "CN": {
            "has_adequacy": False,
            "concerns": ["National Security Law", "Data Security Law", "PIPL government access"],
            "base_risk": TransferRiskLevel.HIGH
        },
        "JP": {
            "has_adequacy": True,
            "adequacy_scope": "Full adequacy",
            "concerns": [],
            "base_risk": TransferRiskLevel.LOW
        },
        "KR": {
            "has_adequacy": True,
            "adequacy_scope": "Full adequacy (2021)",
            "concerns": [],
            "base_risk": TransferRiskLevel.LOW
        },
        "IN": {
            "has_adequacy": False,
            "concerns": ["IT Act surveillance provisions"],
            "base_risk": TransferRiskLevel.MEDIUM
        }
    }

    def __init__(self):
        self.agreements: dict[str, SCCAgreement] = {}
        self.tia_records: dict[str, TransferImpactAssessment] = {}

    def determine_scc_module(
        self,
        exporter_role: str,
        importer_role: str
    ) -> SCCModule:
        """당사자 역할에 따른 SCC 모듈 결정"""

        role_map = {
            ("controller", "controller"): SCCModule.MODULE_1,
            ("controller", "processor"): SCCModule.MODULE_2,
            ("processor", "processor"): SCCModule.MODULE_3,
            ("processor", "controller"): SCCModule.MODULE_4,
        }

        key = (exporter_role.lower(), importer_role.lower())
        if key not in role_map:
            raise ValueError(f"Invalid role combination: {exporter_role} -> {importer_role}")

        return role_map[key]

    def create_scc_agreement(
        self,
        data_exporter: str,
        data_importer: DataImporter,
        module: SCCModule,
        data_categories: list[str],
        transfer_purpose: str
    ) -> SCCAgreement:
        """SCC 계약 생성"""

        agreement = SCCAgreement(
            agreement_id=f"SCC-{datetime.now().strftime('%Y%m%d')}-{len(self.agreements)+1:04d}",
            module=module,
            data_exporter=data_exporter,
            data_importer=data_importer,
            execution_date=date.today(),
            data_categories=data_categories,
            data_subjects=["employees", "customers"],  # 예시
            transfer_purpose=transfer_purpose
        )

        self.agreements[agreement.agreement_id] = agreement
        return agreement

    def conduct_transfer_impact_assessment(
        self,
        agreement_id: str,
        laws_assessed: list[str],
        access_requests: int,
        legal_remedies: bool,
        assessor: str
    ) -> TransferImpactAssessment:
        """이전 영향 평가 (TIA) 수행 - Schrems II 요건"""

        agreement = self.agreements.get(agreement_id)
        if not agreement:
            raise ValueError(f"Agreement {agreement_id} not found")

        importer = agreement.data_importer
        country_profile = self.COUNTRY_RISK_PROFILES.get(importer.country_code, {})

        # 위험 수준 결정
        if country_profile.get("has_adequacy"):
            # 적정성 결정이 있는 경우
            if country_profile.get("dpf_certified_required"):
                # US DPF: 인증 여부 확인 필요
                risk_level = TransferRiskLevel.LOW  # 인증된 경우
            else:
                risk_level = TransferRiskLevel.LOW
        else:
            # 적정성 결정이 없는 경우
            base_risk = country_profile.get("base_risk", TransferRiskLevel.MEDIUM)
            concerns = country_profile.get("concerns", [])

            if len(concerns) >= 2 and not legal_remedies:
                risk_level = TransferRiskLevel.HIGH
            elif access_requests > 10:  # 빈번한 정부 접근
                risk_level = TransferRiskLevel.HIGH
            else:
                risk_level = base_risk

        # 필요한 보충적 조치 결정
        supplementary = self._determine_supplementary_measures(risk_level, country_profile)

        tia = TransferImpactAssessment(
            tia_id=f"TIA-{agreement_id}",
            scc_agreement_id=agreement_id,
            assessment_date=date.today(),
            destination_country=importer.country_code,
            laws_assessed=laws_assessed,
            access_requests_history=access_requests,
            legal_remedies_available=legal_remedies,
            effective_supplementary_measures=supplementary,
            risk_level=risk_level,
            assessor=assessor,
            next_review_date=date(date.today().year + 1, date.today().month, date.today().day)
        )

        # 계약에 TIA 정보 업데이트
        agreement.tia_completed = True
        agreement.tia_date = date.today()
        agreement.tia_risk_level = risk_level
        agreement.supplementary_measures = supplementary

        self.tia_records[tia.tia_id] = tia
        return tia

    def _determine_supplementary_measures(
        self,
        risk_level: TransferRiskLevel,
        country_profile: dict
    ) -> list[SupplementaryMeasure]:
        """위험 수준에 따른 보충적 조치 결정"""

        measures = []

        if risk_level in [TransferRiskLevel.MEDIUM, TransferRiskLevel.HIGH]:
            measures.append(SupplementaryMeasure.STRONG_ENCRYPTION)

            if risk_level == TransferRiskLevel.HIGH:
                measures.append(SupplementaryMeasure.PSEUDONYMISATION)
                measures.append(SupplementaryMeasure.GOVERNMENT_ACCESS_NOTIFICATION)

        measures.append(SupplementaryMeasure.AUDIT_RIGHTS)
        measures.append(SupplementaryMeasure.CONTRACTUAL_COMMITMENTS)

        return measures

    def validate_transfer(self, agreement_id: str, data_record: dict) -> dict:
        """실제 데이터 이전 검증"""

        agreement = self.agreements.get(agreement_id)
        if not agreement:
            return {"allowed": False, "reason": "SCC agreement not found"}

        if not agreement.valid:
            return {"allowed": False, "reason": "SCC agreement no longer valid"}

        if not agreement.tia_completed:
            return {"allowed": False, "reason": "Transfer Impact Assessment not completed"}

        if agreement.tia_risk_level == TransferRiskLevel.PROHIBITIVE:
            return {"allowed": False, "reason": "Transfer prohibited due to high risk"}

        # 보충적 조치 적용 확인
        if agreement.tia_risk_level == TransferRiskLevel.HIGH:
            if SupplementaryMeasure.PSEUDONYMISATION in agreement.supplementary_measures:
                if not self._is_pseudonymised(data_record):
                    return {
                        "allowed": False,
                        "reason": "Data must be pseudonymised before transfer"
                    }

        return {
            "allowed": True,
            "agreement_id": agreement_id,
            "module": agreement.module.value,
            "destination": agreement.data_importer.country_code,
            "supplementary_measures_applied": [m.value for m in agreement.supplementary_measures]
        }

    def _is_pseudonymised(self, data: dict) -> bool:
        """데이터 가명처리 여부 확인"""
        direct_identifiers = ["name", "email", "phone", "ssn", "address"]
        return not any(key in data for key in direct_identifiers)

    def get_transfer_summary(self) -> dict:
        """전체 이전 현황 요약"""

        by_country = {}
        by_module = {}
        by_risk = {}

        for agreement in self.agreements.values():
            country = agreement.data_importer.country_code
            by_country[country] = by_country.get(country, 0) + 1

            module = agreement.module.value
            by_module[module] = by_module.get(module, 0) + 1

            if agreement.tia_risk_level:
                risk = agreement.tia_risk_level.value
                by_risk[risk] = by_risk.get(risk, 0) + 1

        return {
            "total_agreements": len(self.agreements),
            "by_destination_country": by_country,
            "by_scc_module": by_module,
            "by_risk_level": by_risk,
            "tia_completion_rate": f"{sum(1 for a in self.agreements.values() if a.tia_completed)/len(self.agreements)*100:.1f}%"
        }

# 사용 예시
if __name__ == "__main__":
    manager = SCCTransferManager()

    # 데이터 수입자 정의 (미국 클라우드 서비스)
    us_processor = DataImporter(
        name="CloudAI Inc.",
        country_code="US",
        country_name="United States",
        has_adequacy_decision=True,
        adequacy_scope="Data Privacy Framework",
        surveillance_law_concerns=["FISA 702"]
    )

    # SCC 계약 생성
    module = manager.determine_scc_module("controller", "processor")
    agreement = manager.create_scc_agreement(
        data_exporter="EU Tech GmbH",
        data_importer=us_processor,
        module=module,
        data_categories=["customer_profiles", "usage_data"],
        transfer_purpose="AI model training and inference"
    )

    # TIA 수행
    tia = manager.conduct_transfer_impact_assessment(
        agreement_id=agreement.agreement_id,
        laws_assessed=["FISA 702", "EO 12333", "CLOUD Act"],
        access_requests=2,
        legal_remedies=True,
        assessor="DPO Team"
    )

    print(f"위험 수준: {tia.risk_level.value}")
    print(f"보충적 조치: {[m.value for m in tia.effective_supplementary_measures]}")

실무 대화

법무팀:

"미국 클라우드 서비스를 AI 학습에 사용하려면 어떤 이전 근거가 필요한가요?"

DPO:

"미국은 Data Privacy Framework(DPF)에 따른 부분 적정성 결정이 있어요. 먼저 해당 서비스 제공자가 DPF 인증을 받았는지 확인하세요. 인증 기업이면 추가 조치 없이 이전 가능하지만, 아니라면 SCC Module 2를 체결하고 TIA를 수행해야 합니다."

기술팀:

"TIA에서 어떤 점을 평가해야 하나요?"

DPO:

"FISA 702나 CLOUD Act 같은 감시법이 우리 데이터 보호에 미치는 영향을 평가해야 해요. 정부 접근 요청 이력, 이용 가능한 법적 구제책, 필요한 보충적 조치를 문서화합니다. 전송 중/저장 시 암호화, 가명처리, 감사권 등의 보충적 조치를 계약에 포함시켜야 합니다."

면접관:

"Schrems II 판결 이후 SCC 사용 시 달라진 점은 무엇인가요?"

지원자:

"Schrems II 이전에는 SCC 체결만으로 충분했지만, 이후에는 이전 영향 평가(TIA)가 필수가 되었습니다. SCC 제14조에 따라 데이터 수입자 국가의 법령이 SCC 준수를 저해하는지 평가하고, 필요시 암호화, 가명처리 등 보충적 조치를 적용해야 합니다. 또한 2021년 새 SCC는 모듈형 구조로 개편되어 다양한 이전 시나리오를 더 정확히 다룰 수 있게 되었습니다."

면접관:

"SCC 네 가지 모듈의 차이점을 간단히 설명해주세요."

지원자:

"Module 1은 컨트롤러 간, Module 2는 컨트롤러에서 프로세서로, Module 3은 프로세서 간, Module 4는 프로세서에서 컨트롤러로의 이전에 사용됩니다. 가장 흔한 것은 Module 2로, EU 기업이 해외 클라우드 서비스에 처리를 위탁할 때 적용됩니다. 각 모듈은 당사자 역할에 맞는 의무를 규정합니다."

시니어:

"SCC 검증 로직에서 DPF 인증 확인이 빠져 있네요. 미국으로 이전할 때 DPF 인증 여부에 따라 요건이 다르잖아요."

주니어:

"맞아요. DPF 인증 기업이면 적정성 결정 하에 이전이 가능하고, 아니면 SCC+TIA가 필요하죠. 데이터 수입자 프로필에 dpf_certified 필드를 추가하고 검증 로직을 분기시키겠습니다."

시니어:

"그리고 TIA 재검토 주기도 관리해야 해요. 일반적으로 연 1회 또는 법령 변경 시 재평가가 필요한데, next_review_date 필드만 있고 실제 알림 로직은 없네요."

주니어:

"검토 기한 임박 시 알림을 보내는 스케줄러와, 수입국 법령 변경 뉴스를 모니터링하는 기능을 추가하겠습니다. Schrems II 같은 판결이 나오면 영향받는 모든 TIA를 재검토 대상으로 플래깅해야겠네요."

주의사항

  • TIA 필수 수행: Schrems II 이후 SCC 체결만으로는 불충분합니다. 수입국의 감시법, 정부 접근 가능성, 이용 가능한 법적 구제책을 평가하는 이전 영향 평가(TIA)를 반드시 수행하고 문서화해야 합니다.
  • 보충적 조치 적용: TIA 결과 위험이 식별되면 암호화, 가명처리, 계약상 강화된 의무 등 보충적 조치를 적용해야 합니다. 조치가 효과적이지 않다면 이전을 중단해야 할 수 있습니다.
  • 정기적 재검토: TIA와 SCC 계약은 일회성이 아닙니다. 수입국의 법령 변경, 판례 변화, 새로운 감시 프로그램 도입 등 상황 변화에 따라 정기적으로 재평가하고 필요시 계약을 업데이트해야 합니다.

더 배우기