배경 및 목표

개인정보 파기 요청이 들어올 때마다 개발자가 운영 DB에 직접 DML을 실행하고 있었습니다. 요청은 Slack으로 오고, 실행 기록은 Jira 댓글이나 Slack 스레드에만 남았습니다.

Slack 운영 요청 채널에서 “개인정보 파기” 키워드로 요청을 전수 수집한 결과, 36개월 동안 59건이 접수돼 있었고, 최근 3개월은 9건으로 월 평균 1.5 3.0건으로 상승했습니다.

왜 중요했는가

수동 DML은 세 가지 문제를 만들고 있었습니다.

  • 되돌릴 수 없는 작업에 검증 단계가 없었습니다. 조건을 잘못 적어도 실행 전에 걸러 줄 장치가 없었습니다.
  • 다른 사람이 수행하기 어려웠습니다. 요청한 개인만 실행할 수 있었고, 담당자가 아니면 “지난번에 어떤 쿼리를 날렸는지”를 매번 물어봐야 했습니다.
  • 증적이 흩어져 있었습니다. 무엇을 언제 얼마나 지웠는지가 Slack 스레드에만 남아, 파기확인서 요청이 올 때마다 기록을 다시 모아야 했습니다.

제약

제약선택지에 미친 영향
되돌릴 수 없는 작업실행 전에 대상 건수를 확정하는 프리뷰와, 요청자와 승인자를 분리하는 결재를 필수로 두었습니다
개발자 아닌 사람도 써야 함운영 화면에서 요청과 결재, 증적 확인이 모두 끝나야 합니다
진행 중인 요청이 계속 들어옴유형별로 나눠 당장 필요한 것부터 구현하고, 급하지 않은 유형은 보류했습니다

목표

  • 유형별로 제각각이던 파기 작업을 애플리케이션 로직 하나로 균일화합니다.
  • 되돌릴 수 없는 실행 앞에 프리뷰, 결재, 실행, 증적의 고정된 흐름을 둡니다.
  • 개발자가 아니어도 권한만 있으면 요청하고, 승인 아래 실행까지 할 수 있게 합니다.

맡은 역할과 판단

구분내용
담당 범위요청 수집과 문제 정의부터 아키텍처 설계, 구현, QA까지 단독으로 담당했습니다
직접 내린 판단1. 요청 유형을 나눠 자동화할 것과 보류할 것을 정하고, 보류한 유형은 서버에서 막음
2. 백오피스가 남의 데이터를 직접 지우지 않고 각 서비스의 내부 전용 API로만 호출
3. 요청자와 승인자를 분리하는 2인 관여를 화면이 아니라 서버에서 강제
협업, 합의아키텍처 문서를 팀에 공유하는 과정에서, 민감정보를 지우는 작업인 만큼 승인과 결재 절차, 어드민 권한 체계가 필요하다는 의견이 나왔습니다. 처음에는 후순위였던 두 항목을 해결 대상으로 올려 설계에 넣었습니다

해결 방법과 해결 후보군

문제를 찾은 시점부터 마무리까지 밟은 과정입니다.

flowchart LR
    S1["Slack MCP로<br/>요청 전수 조사와 유형화"]
    S2["아키텍처 문서 작성"]
    S3["작업 티켓 분해와<br/>DAG 구성"]
    S4["작업 병렬 수행"]
    S5["티켓 기반<br/>테스트 케이스 구성"]
    S6["브라우저 E2E 자동화<br/>N회 반복"]

    S1 --> S2 --> S3 --> S4 --> S5 --> S6
    S6 -->|문제 발견 시| S4

1. 자동화할 유형과 보류할 유형 나누기

수집한 요청을 유형별로 나눠, 조건이 명확하고 반복되는 유형부터 자동화했습니다. 요청 빈도가 낮고 증적 비교가 성립하지 않는 유형 하나는 보류했습니다.

보류한 유형은 화면에서 탭을 감추는 것으로 끝내지 않고, 요청 타입과 프리뷰, 실행을 서버에서 모두 막았습니다. 화면은 우회할 수 있지만 API는 그렇지 않기 때문입니다.

2. 결재와 승인 프로세스

민감한 정보인 만큼 결재 - 승인 프로세스를 두어 필수 리뷰어 승인 하에 파기가 수행되도록 검증 구간을 두었습니다.

sequenceDiagram
    participant R as 요청자
    participant BO as 파기 백오피스
    participant S as 대상 서비스
    participant N as Slack
    participant A as 승인자

    R->>BO: 1. 프리뷰 (조건 입력)
    BO->>S: 대상 건수 조회
    S-->>BO: 건수 · 파기 예정 시각
    BO-->>R: 지울 대상을 실행 전에 확인

    R->>BO: 2. 파기 요청서 생성
    Note over BO: 조건과 건수를 이 시점에 굳힘
    BO->>N: 3. 승인자에게 알림

    A->>BO: 4. 승인 / 거절
    Note over BO: 요청자 ≠ 승인자 강제

    R->>BO: 5. 실행 (요청자 본인만)
    BO->>S: 파기 호출
    S-->>BO: 증적 번호 · 처리 건수
    BO->>N: 6. 같은 스레드에 증적 회신

결재와 실행은 상태를 따로 뒀습니다. 승인은 됐지만 아직 실행되지 않은 상태를 구분해야 하기 때문입니다. 권한 없는 결재, 이미 끝난 요청서의 재승인, 승인되지 않은 요청서의 실행, 남의 요청 실행, 재실행은 모두 서버가 막습니다.

권한할 수 있는 일
조회 권한요청서와 증적 조회
실행 권한요청과 실행 (조회 포함)
결재 권한승인과 거절 (조회 포함, 실행 권한은 없음)
최상위 권한전 기능과 권한 부여, 회수

실행 권한이 결재 권한을 포함하지 않게 나눴습니다. 요청자와 결재자가 같으면 스스로 요청하고, 승인하여 수행이 가능하기 때문입니다.

후보설명채택 여부
화면에서만 버튼을 감춤권한 없는 사람에게 버튼을 숨김❌ API 직접 호출로 우회 가능
서버가 판정하고 화면은 그 값을 사용인가를 서버에 두고 화면은 받아 씀✅ 화면을 우회해도 같은 판정을 받음

권한 부여와 회수도 화면에서 합니다. 다만 최상위 권한은 화면으로 부여할 수 없고, 자기 자신이나 마지막 한 명의 최상위 권한을 회수하는 것도 서버가 거절합니다. 부여와 회수, 결재, 실행은 모두 감사 로그에 행위자와 함께 남습니다.

3. 개인정보 파기 아키텍처

파기 대상은 여러 서비스에 흩어져 있습니다. 백오피스가 타 서비스의 데이터를 직접 건드리지 않고, 각 서비스가 노출한 내부 전용 API로만 호출하도록 했습니다. 화면에서 들어온 요청이 인가를 지나 프리뷰, 요청서, 결재, 실행, 증적 순으로 넘어가고, 각 단계가 남기는 기록은 저장소와 감사 로그에 쌓입니다.

flowchart LR
    UI["운영 화면"]
    SL["Slack"]

    subgraph BO["파기 백오피스"]
        AUTH["인가"]
        PRE["프리뷰"]
        REQ["요청서"]
        APV["결재"]
        EXE["실행"]
        CERT["증적과 종결"]
        AUD["감사 로그"]
    end

    subgraph ST["저장소"]
        PDB[("권한")]
        RDB[("요청서와 증적")]
    end

    subgraph EXT["대상 서비스"]
        A["서비스 A"]
        B["서비스 B"]
        C["서비스 C"]
        D["서비스 D 보류"]
    end

    UI --> AUTH
    AUTH --> PDB
    AUTH --> PRE
    AUTH --> APV
    AUTH --> EXE
    PRE -->|1 대상 건수 조회| EXT
    PRE --> REQ
    REQ -->|2 조건 고정| RDB
    REQ -->|3 알림| SL
    APV -->|4 승인 거절| RDB
    EXE -->|5 파기 호출| EXT
    EXE --> CERT
    CERT -->|6 증적 회신| SL
    CERT --> RDB
    AUD --> RDB

백오피스의 관리 대상은 요청서와 확인서, 권한뿐입니다. 지워야 할 데이터는 각 서비스의 비즈니스 로직으로 지웁니다. 이렇게 하면 대상이 늘어도 백오피스는 API 명세만 알면 되고, 파기 로직이 바뀐다던지, 추가되던지 하여도 백오피스를 고치지 않습니다.

4. 공통 스키마, API 명세 작성 후 병렬로 진행

권한과 결재 테이블, 파기 API 명세를 선행 작업으로 분리하고, 서비스별 내부 API 작업 순으로 진행했습니다. 선행이 머지되면 의존성이 풀린 작업을 다시 병렬로 여는 방식을 반복해, 착수부터 마지막 수정까지 9일이 걸렸습니다.

5. LLM으로 QA 케이스를 뽑고, 브라우저에서 데이터까지 검증

구현이 끝난 뒤 LLM으로 테스트 매트릭스를 산출하고 전 항목을 다섯 번 반복 실행했습니다. 화면 조작에서 끝내지 않고, 브라우저 자동화로 화면을 조작한 뒤 그 결과를 API와 데이터 상태로 대조했습니다.

검증 항목방법
화면브라우저 자동화로 입력과 버튼, 상세 확인
서버화면 검증을 우회해 API 직접 호출
데이터실행 후 같은 조건으로 재조회해 파기 여부 확인
시각예약 시각이 UTC로 정확히 저장됐는지 교차 확인
로그감사 로그에 누가 무엇을 했는지 남는지 확인

결과

지표기존개선
파기 실행 방식개발자가 운영 DB에 수동 DML백오피스 화면에서 요청과 결재, 실행
실행 가능한 사람운영 DB 권한을 가진 개발자권한을 부여받은 사람 누구나
실행 전 검증없음프리뷰로 대상 건수 확정 후 승인자 결재
2인 관여없음요청자와 승인자를 분리, 실행은 요청자 본인만
증적Slack 스레드와 Jira 댓글에 흩어짐요청서와 확인서로 조회, 감사 로그에 행위자 기록
  • 파기 유형이 같은 흐름을 타게 되어, 유형이 늘어도 흐름은 그대로입니다.
  • 권한 체계를 함께 만든 덕에 개발자가 모든 요청을 처리하지 않아도 됩니다. 어드민 접근 권한이 있으면 요청할 수 있고, 승인 아래 실행까지 가능합니다.