개념
아무것도 달라지면 안 되는 프로젝트
QA가 맡는 일 대부분은 새로 생긴 것 을 검증한다. 새 기능, 바뀐 화면, 추가된 API. 그런데 성격이 정반대인 프로젝트가 주기적으로 온다.
- DB를 새 엔진으로 옮긴다
- 결제 코어를 새 시스템으로 교체한다
- 검색 색인 파이프라인을 폴링에서 CDC 방식으로 바꾼다
- 레거시 화면 수백 개를 새 프레임워크로 변환한다
- 온프레미스에서 클라우드로 이전한다
목록에 낯선 말이 몇 개 있다. 폴링 은 "바뀐 게 있나" 를 주기적으로 되묻는 방식이고, CDC(Change Data Capture)는 저장소에서 바뀐 것이 생기는 즉시 알려 주는 방식이다. 레거시 는 오래전에 만들어져 지금도 돌아가고 있는 시스템, 온프레미스 는 그 시스템을 빌린 서버가 아니라 회사가 직접 가진 장비에 두고 쓰는 것을 가리킨다.
이 프로젝트들의 성공 기준은 "아무것도 달라지지 않았다" 이다. 사용자가 변화를 느끼면 실패다. 기획서에 적힌 기대 결과가 없고, 대신 원본이 기대 결과 다.
그래서 이런 프로젝트에서 가장 어려운 일은 옮기는 작업 자체가 아니다. 어느 컨퍼런스 세션 제목이 이걸 정확히 표현한다 — "옮기는 것보다 증명이 어렵다." 옮기는 것은 개발이 하고, 증명은 QA가 한다.
무엇을 증명해야 하는가 — 등가성
증명해야 할 것은 기능적 등가성(functional equivalence) 이다. 같은 입력에 같은 결과. 그런데 이걸 완벽하게 증명하는 것은 이론적으로도 현실적으로도 불가능에 가깝다. 입력 공간이 무한하기 때문이다. 모듈 1에서 배운 "완전한 테스트는 불가능하다"가 여기서 그대로 재현된다.
그래서 실무는 근사 등가성 을 쓴다. 확인 가능한 증거를 여러 각도에서 모아 "같다"를 뒷받침하는 방식이다. 다섯 가지 기법이 있고, 대개 여러 개를 겹쳐 쓴다.
| 기법 | 무엇을 하나 | 강점 | 한계 |
|---|---|---|---|
| 이중 실행 비교(섀도우) | 신·구를 동시에 돌려 결과를 대조 | 실제 트래픽으로 검증 | 부작용 있는 작업엔 위험 |
| 리콘실리에이션 | 건수·합계·체크섬을 대조 | 전수를 값싸게 확인 | 총합이 같아도 개별이 다를 수 있음 |
| 표본 대조 | 개별 레코드를 하나씩 비교 | 정확 | 전수 불가 |
| 골든 데이터셋 재생 | 고정 입력을 양쪽에 흘려 비교 | 재현 가능·회귀 가능 | 실제 데이터의 다양성이 빠짐 |
| 지표 기반 감시 | 전환 후 운영 지표의 변화를 본다 | 놓친 것을 사후에 잡음 | 이미 사용자에게 나간 뒤 |
핵심은 이 다섯을 층으로 쌓는 것 이다. 리콘실리에이션으로 전수를 훑고, 표본으로 깊이를 보고, 이중 실행으로 실제 트래픽을 확인하고, 전환 후 지표로 감시한다. 어느 하나도 단독으로는 충분하지 않다.
이중 실행 비교 — 가장 강력하고 가장 조심스럽다
섀도우(shadow) 실행 은 실제 트래픽을 신·구 양쪽에 흘리되, 사용자에게는 기존 결과만 돌려주고 새 시스템의 결과는 비교용으로만 쓰는 방식이다.
사용자 요청
│
┌────────┴────────┐
▼ ▼
[기존 시스템] [새 시스템] ← 섀도우
│ │
응답 반환 결과 기록만
│ │
└────► 비교기 ◄───┘
│
차이 리포트QA가 설계해야 할 것은 비교기다. 그리고 비교기 설계의 90%는 다음 질문에 답하는 일이다.
어디까지 같아야 "같다"인가?
기계적으로 비교하면 거의 항상 차이가 나온다. 그리고 그 차이의 대부분은 문제가 아니다. 그래서 허용 가능한 차이 목록 을 먼저 만들어야 한다.
| 차이 유형 | 대개 허용 | 이유 |
|---|---|---|
| 정렬 순서 | 조건부 | 순서가 계약이 아니라면 허용. 계약이면 불허 |
| 타임스탬프·생성 ID | 허용 | 실행 시점이 다르므로 당연 |
| 부동소수점 말단 자리 | 조건부 | 금액이면 절대 불허 |
| 공백·개행·JSON 키 순서 | 허용 | 표현의 차이 |
| 새로 추가된 필드 | 허용 | 단, 기존 필드가 사라지면 불허 |
| null과 빈 문자열 | 불허 | 소비하는 쪽에서 다르게 동작한다 |
마지막 줄이 실제로 사고를 만든다. 새 시스템이 빈 값을 null 대신 ""
로 주기 시작하면, 그걸 받는 클라이언트에서 조건문 하나가 반대로 돈다.
표면적으로는 "값이 없다"로 같지만 코드에서는 다르다.
부작용에 대한 주의도 필요하다. 조회는 섀도우로 돌려도 안전하지만, 결제·발송·차감처럼 부작용이 있는 작업 은 새 시스템 쪽을 반드시 격리해야 한다. 실제 문자가 두 번 나가거나 재고가 두 번 차감되는 사고가 이 지점에서 난다.
리콘실리에이션 — 전수를 값싸게 훑는다
데이터 이전이라면 대사(reconciliation) 가 기본이다. 개별 레코드를 다 볼 수 없으니 집계값을 맞춘다.
확인 순서 (값싼 것부터)
① 건수 원본 1,482,930건 = 대상 1,482,930건 ?
② 합계 금액 합, 수량 합이 일치하는가
③ 그룹별 집계 상태별·기간별·유형별 건수
④ 체크섬 핵심 필드를 이어 붙여 해시 비교
⑤ 표본 대조 ①~④가 통과해도 개별을 확인①이 맞았다고 안심하면 안 된다. 건수가 같아도 내용이 뒤바뀔 수 있고, 합계가 같아도 두 레코드가 서로 값을 교환했을 수 있다. 그래서 층을 쌓는다.
③ 그룹별 집계가 특히 유용하다. 전체 건수는 맞는데 "취소 상태 건수만 1,200건 적다"가 나오면 원인 범위가 즉시 좁혀진다. 총합만 보면 상쇄되어 보이지 않는 문제가 그룹별로 보면 드러난다.
⑤ 표본을 뽑을 때는 모듈 18b(레슨 8)에서 배운 원칙을 쓴다 — 무작위가 아니라 위험 가중 이다.
- 가장 오래된 데이터(스키마가 달랐을 시기)
- 가장 최근 데이터(전환 중 유입)
- 경계값 — 최대 길이 문자열, 0, 음수, 특수문자, 이모지
- 특수 상태 — 삭제 표시, 병합된 계정, 부분 환불
- 크기가 극단적인 레코드
"이모지가 깨져서 이전됐다"는 사고가 실제로 흔하다. 문자 인코딩과 컬럼 길이가 바뀌는 경계에서 나오고, 평범한 표본에서는 절대 안 잡힌다.
테스트가 없는 레거시를 옮길 때
가장 흔한 현실 — 옛 시스템에 테스트가 없다. 그러면 "같은지"를 비교할 기준 자체가 없다.
이때의 순서는 정해져 있다.
① 특성화 테스트(characterization test)를 만든다
"옳은지"가 아니라 "지금 이렇게 동작한다"를 고정한다
↓
② 그것을 기준선(baseline)으로 삼는다
↓
③ 전환 후 같은 테스트를 새 시스템에 돌린다특성화 테스트의 핵심은 버그도 함께 고정한다 는 점이다. 기존 시스템에 버그가 있어도 일단 그대로 기록한다. 전환 프로젝트에서 버그를 함께 고치려는 시도가 가장 흔한 실패 원인이기 때문이다.
전환과 개선을 같은 릴리즈에 넣지 않는다.
결과가 달라졌을 때 그것이 전환 실패인지 의도한 개선인지 구분할 수 없게 된다. 개선은 전환이 끝나고 안정화된 뒤에 별도로 한다.
이 원칙을 지키기 어려운 순간이 반드시 온다. "어차피 옮기는 김에" 라는 말이 나올 때다. 그때 QA가 해야 할 말은 "고치지 말자"가 아니라 "따로 하자" 이다. 발견한 버그는 목록으로 남기고 전환 후 백로그로 넘긴다.
비교기가 쏟아 내는 차이를 다루는 법
비교기를 처음 돌리면 차이가 수천 건 나온다. 그 앞에서 손이 멈추는 것이 전환 프로젝트의 첫 번째 고비다. 순서를 정해 두면 하루면 정리된다.
| 순서 | 하는 일 |
|---|---|
| 1 | 유형별로 묶는다 — 같은 원인은 한 건으로 센다 |
| 2 | 허용 목록에 있는 유형을 비교기에서 끈다 |
| 3 | 남은 유형을 건수 많은 순으로 줄 세운다 |
| 4 | 각 유형에서 대표 1건만 깊게 파고 원인을 찾는다 |
| 5 | 원인을 고친 뒤 전체를 다시 돌린다 |
1번이 전부다. 차이 3,000건이 실제로는 원인 5개인 경우가 대부분이다. 묶지 않고 하나씩 보면 끝이 없고, 묶으면 다섯 개의 문제가 된다.
2번에는 함정이 있다. 허용 목록에 있다고 조용히 끄면 나중에 그 유형에서 진짜 문제가 섞여 들어와도 안 보인다. 끈 유형과 그 건수를 리포트에 남겨 두고, 건수가 갑자기 늘면 다시 들여다본다.
컷오버와 롤백
검증이 끝나면 실제로 넘어가는 순간이 온다. 전략은 대체로 넷이다.
| 전략 | 방식 | 롤백 | 적합한 상황 |
|---|---|---|---|
| 빅뱅 | 한 번에 전체 전환 | 어렵다 | 병행이 불가능한 구조 |
| 병행 운영 | 둘 다 돌리며 점진 이동 | 쉽다 | 대부분의 경우 권장 |
| 카나리 | 일부 트래픽·일부 사용자부터 | 쉽다 | 트래픽 분할이 가능할 때 |
| 듀얼 라이트 | 쓰기를 양쪽에, 읽기는 한쪽 | 중간 | 데이터 저장소 전환 |
QA가 컷오버 계획에서 반드시 확인할 것 세 가지 가 있다.
- 롤백 조건이 숫자로 정의됐는가. "문제가 생기면 되돌린다"는 조건이 아니다. "오류율 2% 초과 또는 응답시간 p95가 800ms 초과 시 즉시 롤백" 처럼 판정 가능해야 한다. 모듈 4 레슨 5의 Go/No-Go 기준과 같은 원리다.
- 롤백이 실제로 가능한가, 그리고 시험해봤는가. 되돌릴 수 있다고 문서에 적혀 있는 것과 실제로 눌러본 것은 다르다. 특히 스키마 변경이 포함되면 되돌리기가 불가능한 경우가 있다.
- 되돌렸을 때 그동안 쌓인 데이터는 어떻게 되는가. 새 시스템에서 2시간 동안 생성된 주문은 롤백 후 어디로 가는가? 이 질문에 답이 없는 롤백 계획은 계획이 아니다.
전환 후에도 끝나지 않는다
컷오버 직후가 아니라 며칠~몇 주 뒤에 드러나는 문제 가 있다.
- 월말 배치가 처음 도는 날
- 특정 조건이 처음 발생하는 날(연체 30일 도달, 구독 갱신일)
- 트래픽 피크가 처음 오는 날
- 오래된 데이터를 처음 조회하는 날
그래서 전환 검증 계획에는 감시 기간과 감시 지표 가 포함돼야 한다. 모듈 14 레슨 6의 관측성이 여기서 쓰인다 — 오류율, 응답시간, 처리 건수, 그리고 비즈니스 지표(주문 수, 결제 성공률, 검색 결과 0건 비율).
기술 지표는 멀쩡한데 비즈니스 지표가 조용히 어긋나는 경우가 실제로 있다. 검색 색인 전환에서 오류는 하나도 없는데 "검색 결과 0건" 비율이 3%에서 7%로 오른 상황 같은 것이다. 에러가 아니라 침묵으로 나타나는 실패 다.
검증 종료 조건
언제 "검증이 끝났다"고 말할 것인가. 모듈 4 레슨 4에서 배운 종료 조건을 전환 프로젝트에 맞게 쓰면 이렇다.
- 리콘실리에이션 전 항목 일치(또는 차이가 전부 허용 목록에 있음)
- 위험 가중 표본 N건 대조 완료, 미해결 차이 0건
- 섀도우 실행 M일간 불일치율 임계 이하
- 특성화 테스트 전량 통과
- 롤백 절차 리허설 완료
- 감시 지표와 임계값·감시 기간 합의 완료
마지막 두 줄을 빠뜨리는 계획이 많다. 그런데 롤백을 시험해보지 않은 전환은 사고가 났을 때 되돌릴 수 없고, 감시 계획이 없는 전환은 문제를 사용자가 먼저 발견한다.
사례 — 회원 148만 건 이전
실제 전환에서 다섯 층이 어떻게 걸러 내는지 한 흐름으로 보자. 회원 저장소를 새 엔진으로 옮기는 프로젝트였다.
| 층 | 결과 | 걸린 것 |
|---|---|---|
| ① 건수 | 1,482,930 = 1,482,930 | 통과 |
| ② 합계 | 포인트 합 일치 | 통과 |
| ③ 그룹별 | 탈퇴 상태만 1,204건 적다 | ← 여기서 처음 걸렸다 |
| ④ 체크섬 | 닉네임 필드 해시 불일치 3,117건 | 이모지·결합 문자 |
| ⑤ 표본 | 2008년 가입자 40건 중 2건 생년 깨짐 | 옛 스키마 잔재 |
①과 ②만 봤다면 통과시켰을 프로젝트다. 세 가지 결함의 원인은 전부 달랐다.
- 탈퇴 1,204건 — 이전 스크립트가 탈퇴 회원을 "삭제 표시" 로 보고 건너뛰었다. 총 건수는 다른 상태에서 같은 수가 더 들어와 상쇄되어 보였다
- 닉네임 3,117건 — 이모지와 결합 문자(한글 자모가 따로 저장된 것)가 새 인코딩에서 다른 바이트로 바뀌었다. 화면에는 비슷하게 보여 눈으로는 안 잡힌다
- 생년 2건 — 2008년에는 생년을 두 자리로 받던 시기가 있었고, 변환 규칙이 그 형식을 몰랐다
마지막 건이 ⑤ 표본의 값을 보여 준다. 148만 건 중 40건을 봐서 2건을 찾았는데, 무작위로 뽑았다면 거의 확실히 못 찾았을 것이다. "가장 오래된 데이터" 라는 기준 하나가 그 차이를 만들었다.
전환 후 감시에서도 하나가 더 나왔다. 오류율은 정상인데 로그인 실패율이 0.4%에서 1.1%로 올랐다. 비밀번호 해시 방식이 섞여 있던 구간을 변환할 때 일부가 재설정 대상으로 넘어간 것이었다. 기술 지표가 아니라 비즈니스 지표가 먼저 알려 준 전형적인 경우다.
실무에서 왜 필요한가
전환 프로젝트는 QA의 존재 이유가 가장 선명해지는 자리다. 새 기능은 "좋아졌다"로 넘어갈 수 있지만, 전환은 다르면 사고 다. 그리고 다른지 같은지를 판정할 수 있는 사람이 팀에 없으면 프로젝트는 감에 의존한다.
동시에 QA가 조직의 판단에 개입할 여지가 가장 큰 자리이기도 하다. "전환과 개선을 분리하자", "롤백을 리허설하자", "감시 기간을 2주 잡자"는 제안은 전부 QA가 하기 좋은 말이고, 받아들여지면 프로젝트의 위험이 눈에 띄게 줄어든다.
경력 관점에서도 값이 크다. 대규모 전환 검증 경험은 채용 시장에서 드물고, "무엇을 어떻게 대조했는가" 를 구체적으로 말할 수 있으면 설득력이 크다. AI가 코드 변환을 대신 해주는 시대에는 오히려 더 그렇다 — 모듈 18c 레슨 6에서 다룬 대로, 옮기는 일은 자동화되어도 증명하는 일은 남는다.
채용시장에서의 위치
전환 검증 경험은 공고에 거의 안 적히는데 면접에서는 반드시 묻는 종류의 경험이다. 적히지 않는 이유는 상시 업무가 아니라서이고, 묻는 이유는 대규모 전환을 겪어 본 조직이 그 어려움을 알기 때문이다.
| 질문 | 약한 답 | 강한 답 |
|---|---|---|
| "데이터 이전은 어떻게 검증했어요?" | "건수 맞춰 보고 샘플 확인했습니다" | "건수·합계·그룹별·체크섬·표본 다섯 층으로 쌓았고, 그룹별에서 처음 걸렸습니다" |
| "'같다'는 걸 어떻게 정의했어요?" | "결과가 동일하면요" | "허용 가능한 차이 목록을 먼저 만들었습니다. null과 빈 문자열은 불허로 뒀습니다" |
| "표본은 어떻게 뽑았어요?" | "무작위로요" | "가장 오래된 데이터·경계값·특수 상태 중심의 위험 가중으로 뽑았습니다" |
| "롤백은 준비했어요?" | "계획은 있었습니다" | "숫자 조건 3개를 정하고, 리허설을 돌리고, 롤백 시 쌓인 데이터 처리까지 정했습니다" |
| "전환하면서 버그도 고쳤어요?" | "같이 고쳤습니다" | "분리했습니다. 다르면 전환 실패인지 개선인지 구분이 안 되니까요" |
마지막 줄이 판단력을 보는 질문이다. "어차피 옮기는 김에" 를 거절한 경험은 기술보다 원칙을 지킨 경험으로 읽힌다.
직무 이름으로는 데이터 QA · 플랫폼 QA · SRE 협업 QA 쪽에서 직접 쓰이고, 금융·통신·커머스의 차세대 시스템 구축 프로젝트에서는 전환 검증 담당이 별도 역할로 뽑힌다. 그리고 코드 변환을 AI가 대신하는 비중이 커질수록 "옮긴 결과가 같은지 판정하는 사람" 의 몫은 줄지 않는다.