넘겨받을 커머스 시스템의 실사를 마감하며, 운영 가능 축의 Red 를 인수 거절 사유가 아니라 계약 조건 넷으로 바꾸기로 했다. 질문은 이 코드베이스를 외주 유지보수 형태로 넘겨받아 운영할 수 있는가 하나였고, 결론은 「코드와 서비스는 실재하고 가동 중이지만, 운영을 넘겨받기 위한 전제가 갖춰져 있지 않다. 넘겨받을 수는 있으나 계약에 조건을 박아야 한다」였다.
Table of contents
Open Table of contents
배경 — 세 축으로 나눈 이유
실사를 「좋다·나쁘다」 한 줄로 끝내면 무엇을 고쳐야 넘겨받을 수 있는지가 남지 않는다. 그래서 판정을 세 축으로 나눴다.
| 축 | 질문 | 판정 |
|---|---|---|
| ① 시스템 구성 완비 | 넘겨받을 자산이 목록에 모두 있는가 | Yellow |
| ② 코드베이스 실재 | 코드가 실제로 있고 돌아가는가 | Yellow |
| ③ 유지보수 운영 가능 | 넘겨받은 뒤 우리가 운영할 수 있는 상태인가 | Red |
한 줄 판정 대신 축을 나누면 「코드는 있는데 운영은 못 한다」 같은 결론을 그대로 적을 수 있다. 한 줄 판정이었다면 코드가 실재한다는 사실이 운영 위험을 가렸을 것이다.
방법 — 현장이 아니라 콘솔과 화면을 실측했다
이번 실사는 현장 실사가 아니라, 넘겨받은 접근 권한으로 직접 조회할 수 있는 것을 전부 직접 봤다.
- Apple Developer·App Store Connect·Play Console·Firebase(RTDB·Firestore)·GCP 콘솔 직접 조회
- 백오피스 실제 화면과 그 화면의 네트워크 요청 분석
- 무형자산 문서, API·DB 명세 CSV 93건, GCP 청구서 3개월, Slack export(151MB)의 메타데이터
ghCLI 로 GitHub 조직의 권한과 저장소 실측
열지 않을 것도 미리 정했다. Slack 채널 본문, 저장소 46개의 코드 전문, Firestore 34개 컬렉션의 문서 전수는 목적 없이 열면 끝이 없으므로, 특정 질문이 생겼을 때 그 질문으로만 판다. RTDB 의 customers 전체 조회도 하지 않았다. 사용량 과금 요금제이고 RTDB 가 가장 큰 비용 항목이라, 콘솔에서 전량을 불러오면 과금과 성능 양쪽에 위험하다. 이 범위 설정이 실사에서 가장 중요한 결정이었고, 다 보려고 했다면 정해진 기간 안에 판정이 나오지 않았을 것이다.
측정 — 실제 스택과 자산 목록의 차이
콘솔과 청구서로 다시 그린 스택은 다음과 같다.
- 프론트: Vue.js 웹 · iOS(Swift) · Android(Kotlin)
- 백엔드: GKE 위의 Node.js 와 중량 기반 사후 과금 엔진
- 결제: 해시 생성과 주문 번호 로직이 전부 PHP 에 있는 결제 허브
- 백오피스: React·TypeScript 와 Go API 로 된 고객·주문 관리, PHP 로 된 출고·재고 관리
- 데이터: RTDB · Firestore · Cloud SQL · MongoDB
- 분석: BigQuery 와 BI Engine, Azure Databricks
- 그 밖의 GCP: App Engine · Memorystore Redis · Cloud Functions · Pub/Sub · Secret Manager · Cloud Scheduler
넘겨받은 자산 목록과 대조하니 Cloud SQL·MongoDB·App Engine·Redis·BigQuery·Azure Databricks, 그리고 관리자 도메인 하나까지 일곱 개가 어느 자료에도 없었다. 전부 청구서와 응답 코드, 채널 설명에서 나왔다. 자산 목록이 실제보다 좁으면 이관 계획의 기초가 흔들리므로 축 ①이 Yellow 가 됐다. 정보보호 인증(ISMS)이 2026년 3월에 만료됐다는 사실도 같은 축에 들어갔다.
원인 — 운영 가능 축이 Red 가 된 네 가지
축 ③을 Red 로 만든 것은 코드 품질이 아니라, 넘겨받은 뒤 운영을 시작하기 위한 전제였다.
- 비밀 값이 git 과 코드에 평문으로 있다. App Store Connect 전권을 가진 ASC API key, GCP 서비스 계정 키 4개, Slack Bot Token·Webhook 9건이다. 노출된 채로 넘겨받으면 옛 관계자가 계속 접근할 수 있다
- 키 로테이션 절차가 없다. 바꿔 본 적이 없는 키는 바꾸는 순간 무엇이 깨질지 아무도 모른다
- 권한이 한 사람을 거친다. Apple·Play·결제 허브·라우팅 데이터 권한이 전부 상대 쪽 기술 책임자 한 사람에게 모여 있다
- 같은 계정을 쓰는 관계사가 있다. Google Workspace·Firestore·Slack 세 시스템을 다른 사업과 공유하고 있어 통째로 옮길 수 없다
축 ②도 깨끗하지 않았다. 서버가 카드 번호 16자리와 유효기간, 카드 비밀번호를 직접 받는 API 가 실재한다. 이 구조는 PCI DSS 의 SAQ-D 범위에 들어가는데 인증은 이미 만료돼 있다.
판단 — 판정을 계약 조건으로 바꾸기
판정이 Red 라고 해서 넘겨받지 않는 것은 아니고, 이 판정을 다루는 선택지는 둘이었다.
- Red 를 이유로 인수 전에 상대가 고쳐 오게 한다
- 넘겨받되, Red 의 원인을 인계 완료 조건과 계약 조건으로 바꿔 적는다
2번이 맞다고 본 이유는 Red 의 원인 네 가지가 모두 넘겨받는 쪽이 직접 해야 의미가 있는 일이기 때문이다. 비밀 값 교체는 상대가 해 주면 새 값이 다시 상대 손을 거친다. 대신 조건을 계약에 박지 않으면 넘겨받은 뒤에 협조를 얻을 근거가 없다. 그래서 권고를 네 개로 정리했다.
- 비밀 값 전면 교체를 인계 완료 조건으로 둔다
- 한 사람을 거치는 권한에 대해, 그 사람의 협조 범위와 기간을 서면으로 정한다
- 공유 중인 세 시스템의 분리 방식과 비용 분담을 정한다
- 이관 직후 PCI DSS 범위 진단을 한다
이 선택의 대가는 넘겨받는 순간부터 Red 상태의 시스템을 우리가 운영한다는 점이고, 조건이 이행되기 전까지 생기는 사고도 우리 몫이 된다.
결과 — 판정이 계약 조건이 된 뒤
- 인수 후 첫 과제 목록. Red 의 원인 넷(비밀 값 교체·한 사람을 거치는 권한·공유 중인 세 시스템·PCI DSS 범위 진단)이 그대로 인계 완료 조건과 넘겨받은 뒤의 첫 과제가 됐다
- 자산 범위의 정정. 청구서와 대조해 목록에 없던 데이터 저장소 등 일곱 가지를 찾아, 이관 계획을 그 범위로 다시 잡았다
- 감수한 위험. 넘겨받는 순간부터 Red 상태의 시스템을 운영하므로, 조건이 이행되기 전의 사고는 운영 쪽 몫이다
정리
- 실사는 축을 나눠 판정한다. 「코드는 있는데 운영은 못 한다」를 그대로 적을 수 있어야 한다
- 무엇을 열지 않을지를 먼저 정한다. 목적 없는 전수 조회는 판정을 늦추고, 사용량 과금 환경에서는 비용과 성능을 위협한다
- 자산 목록은 청구서와 대조한다. 목록에 없던 데이터 저장소가 청구서에서 나온다
- Red 는 거절 사유가 아니라 계약 조건의 재료다. 넘겨받는 쪽이 직접 해야 하는 일은 넘겨받은 뒤에 하도록 적는다