상황

크리덴셜(외부 시스템 토큰, API 키)을 전역 설정 한 곳에 두고 모두가 공유하던 시스템을, 사용자별 개인 설정으로 전환하는 마이그레이션이었다. 예를 들어 이슈 트래커 PAT는 팀 공용 계정 명의로 전역에 등록되어 있었고, 이 토큰으로 발행된 일감은 전부 그 공용 계정 앞으로 쌓이고 있었다.

개인화 전환 자체는 방향이 명확했다. 문제는 전환 시점에 이미 존재하는 크리덴셜을 어떻게 처리하느냐였다.

문제

기존 크리덴셜의 마이그레이션 경로는 두 가지가 있다.

  1. 자동 귀속: 기존 크리덴셜을 특정 사용자(관리자, 최초 등록자 등)의 개인 설정으로 옮긴다. 사용자 마찰이 없다.
  2. 폐기 + 재입력: 기존 크리덴셜을 버리고 각자 새로 등록하게 한다. 데이터가 깨끗하지만 마찰이 있다.

자동 귀속이 매력적으로 보이지만, 함정이 있다. 공용 계정으로 쓰이던 크리덴셜은 데이터만으로 실소유자를 확정할 수 없다. 등록한 사람, 실제로 쓰던 사람들, 공용 계정 명의 — 셋 중 누구에게 귀속해도 근거가 없는 추측이다.

그리고 크리덴셜은 단순 저장값이 아니라 파생 정책의 근거다. 이 시스템에서 크리덴셜의 소유자는 이후 담당자 자동 지정, 활동 기록 귀속의 기준이 된다. 오귀속 하나가 저장 시점의 오류로 끝나지 않고, 그 크리덴셜로 발행되는 모든 일감의 담당자 데이터로 전파된다. 잘못 귀속의 비용이 재입력 마찰보다 비대칭적으로 크다.

접근과 판단

판단 기준을 하나로 세웠다: 크리덴셜의 소유자를 데이터만으로 확정할 수 있는가.

flowchart TD
    A[기존 전역 크리덴셜] --> B{"소유자를 데이터만으로<br/>확정할 수 있는가?"}
    B -- "확정 불가<br/>(공용 계정 PAT)" --> C[폐기 + 각자 재입력]
    B -- "성격을 재정의 가능<br/>(admin 등록 LLM 키)" --> D[시스템 공용 크리덴셜로 이관]
    C --> E["파생 정책 오염 차단<br/>(담당자 자동 지정, 활동 기록)"]
    D --> F[소유자 개념 자체를 제거<br/>귀속 문제 소멸]

같은 마이그레이션 안에서 크리덴셜마다 결론이 갈렸다.

  • 공용 계정으로 쓰이던 이슈 트래커 PAT: 소유자 확정 불가 → 폐기 + 재입력. 추측 귀속은 어떤 후보를 골라도 파생 정책을 오염시킨다.
  • 관리자가 등록한 LLM API 키: 개인 소유로 귀속할 필요가 없다. "시스템 공용 크리덴셜"로 성격을 재정의하면 소유자 문제 자체가 사라진다 → 이관.

즉 규칙은 "전부 폐기"가 아니다. 확정 불가면 폐기, 재정의 가능하면 이관 — 크리덴셜의 성격에 따라 분기한다.

또 하나의 판단은 전환기 임시 단계를 만들지 않은 것이다. 원래 계획에는 전역/개인 설정이 공존하는 중간 구조(전환기용 UI)가 있었지만, 만들었다 버릴 코드라는 점이 명확해 목표 구조로 직행했다. 회의에서 정해진 중간 단계라도, 구현 시점에 존재 이유가 사라졌다면 생략을 판단할 수 있다.

구현

폐기+재입력의 핵심 과제는 마찰 흡수다. 별도의 마이그레이션 공지나 전용 화면을 만드는 대신, 기존 실패 경로를 온보딩 경로로 전환했다.

  1. 기존 전역 크리덴셜 폐기 후, 해당 기능 사용 시 인가 에러가 발생한다.
  2. 인가 에러 화면에서 "개인 설정에서 토큰을 등록하세요"로 안내한다 — 에러가 곧 재등록 진입점이 된다.
  3. 개인 토큰 등록 시, 공용 계정 명의로 기발행된 일감의 담당자를 본인으로 일괄 변경할지 확인하는 모달을 띄운다 — 과거 데이터의 귀속을 시스템 추측이 아니라 사용자 본인의 확인으로 처리한다.

자동 귀속에서 시스템이 추측으로 결정하던 것을, 재입력 플로우에서는 사용자가 명시적으로 확정한다. 귀속의 근거가 "추측"에서 "본인 승인"으로 바뀌는 것이 이 설계의 본질이다.

결과

  • 소유자 불명 크리덴셜의 오귀속이 원천 차단됐고, 담당자 자동 지정·활동 기록 등 파생 정책이 깨끗한 소유 데이터 위에서 동작하게 됐다.
  • 재입력 마찰은 인가 에러 → 안내 → 등록 → 일괄 변경 확인의 자연스러운 경로로 흡수되어, 별도 마이그레이션 안내 없이 전환이 완료됐다.
  • 전환기 임시 UI를 생략해 만들었다 버리는 코드가 발생하지 않았다.

배운 것

  • 귀속 불가 데이터는 추측으로 채우지 말고 소유자에게 되물어라. 크리덴셜처럼 파생 정책의 근거가 되는 데이터는, 오귀속 비용이 재입력 마찰보다 항상 크다. 마찰은 UX로 흡수할 수 있지만 오염된 귀속은 데이터 전반으로 전파된 뒤에는 되돌리기 어렵다.
  • 판별 규칙은 재사용 가능하다: 전역→개인 마이그레이션에서 (1) 소유자를 데이터만으로 확정 가능한가 묻고, (2) 불가면 폐기 + 온보딩 플로우로 재입력을 흡수하고, (3) 성격을 재정의할 수 있으면(개인 소유 → 시스템 공용) 귀속 문제 자체를 소멸시킨다. 도구나 도메인과 무관하게 적용된다.
  • 인가 에러는 실패 화면이 아니라 온보딩 진입점이 될 수 있다. 마이그레이션 전용 UI를 만드는 대신 기존 에러 경로에 안내를 얹으면, 전환기 코드 없이 사용자가 자연스럽게 새 구조로 이동한다.
  • 회의에서 결정된 중간 단계라도 구현 시점에 다시 검증하라. 목표 구조로 직행할 수 있다면, 임시 단계는 결정이 아니라 부채다.