3주 전, 요가 스튜디오를 운영하는 친구를 위해 예약 위젯을 만들었습니다. 다른 일을 하는 중이라 빠르게 승인한 계획, 실행, 그리고 대충 훑어보고 넘어간 초록색 체크 표시가 있는 버전 카드가 있었죠. 화요일에 친구가 다른 스튜디오 사이트도 같은 빌드를 사용해도 되냐고 물었습니다. 허락하기 전에 3주 전 "검증됨"이 실제로 무엇을 의미했는지 확인하러 돌아갔고, 그때 처음으로 체크 표시를 그냥 믿는 대신 이 기록 중 하나를 실제로 읽어봤습니다.
이 기록은 버전 카드 바로 위, 미리보기와 코드 액션 옆에 있습니다. 재배포하거나 롤백할 때 가는 바로 그 위치죠. 제일 먼저 눈에 띈 건, 이것이 대화 전체가 아니라 그 버전 하나에만 한정된다는 점이었습니다. 저는 깨진 날짜 선택기를 고치느라 다섯 번이나 빌드를 반복했고, 이 기록이 전체 왕복 과정을 이야기해줄 거라고 어느 정도 기대했습니다. 그렇지 않았습니다. 버전 4의 기록은 오직 버전 4만 설명합니다. 버전 2가 로그인 폼이 조용히 실패한 채로 출시됐다는 기억도 없고, 버전 5가 버전 3에서 생긴 문제를 조용히 고쳤다는 것도 알려주지 않습니다. 각 기록은 스냅샷일 뿐, 차이점(diff)도 변경 이력(changelog)도 아닙니다. 릴리스별로 무엇이 바뀌었는지 알고 싶다면 완전히 다른 뷰를 봐야 합니다. 이 기록은 오직 "이 버전이 괜찮은가"에만 답합니다.
아래로 스크롤하면 기록은 6개의 행으로 나뉩니다:
| 레이어 | 통과 기준 |
|---|---|
| 기능/브라우저 내 실행 | 빌드가 실제 브라우저에서 실행되었고, 상호작용이 테스트됨 (게임이라면 실제로 플레이해봄) |
| 코드 리뷰 | 읽기 전용 리뷰어가 파일과 동작으로 입증 가능한 결함을 발견하지 못함 |
| 보안 | 인젝션 취약점, 노출된 비밀 값, 안전하지 않은 패턴이 발견되지 않음 |
| 링크 및 SEO | 깨진 링크 없음, 메타데이터·robots·사이트맵이 정상 |
| 접근성 | 자동화된 axe 검사에서 위반 사항이 발견되지 않음 |
| 적합성(Conformance) | 빌드가 승인된 계획에서 약속한 내용을 포함함 |
여섯 개 모두 초록색이었고, 제 첫 반응은 아마 대부분의 사람들이 가질 법한 것과 같은 잘못된 반응이었습니다: 보안이 통과했으니 안전하고, 접근성이 통과했으니 접근 가능하다는 것이었죠. 두 해석 모두 각 검사가 실제로 하는 일과 부합하지 않습니다. 보안 검사는 표면적인 문제들 — 쿼리에 문자열을 그대로 이어 붙이는 것, 클라이언트 번들에 노출된 API 키, 사용자 입력에 대한 eval — 이 없다는 것을 의미합니다. 침투 테스터가 하루 종일 매달린 것과는 다릅니다. 제 친구의 스튜디오는 이 위젯으로 결제를 받지 않고 이름과 시간대만 받으니 이 정도 수준이면 충분했습니다. 결제 흐름이었다면 그 이상을 원했을 겁니다.
접근성 항목은 실제로 멈춰서 찾아보게 만든 유일한 항목이었습니다. "axe 통과"라는 말은 철저해 보이지만 실제로는 그렇지 않기 때문입니다. 내부에서 실행되는 자동화 엔진 axe는 WCAG 성공 기준의 3분의 1에서 절반 정도를 안정적으로 잡아냅니다: 누락된 대체 텍스트, 나쁜 대비 비율, 라벨 없는 입력 필드, 명백한 ARIA 오용 같은 것들이죠. 하지만 제가 요청한 커스텀 날짜 선택기 드롭다운이 스크린 리더로 사용 가능한지, 다단계 예약 흐름에서 탭으로 이동할 때 포커스가 적절한 곳에 떨어지는지, 초록색과 노란색으로 표시한 "확정" 대 "대기" 상태가 적록색맹인 사람에게 문제가 되는지는 알려주지 못합니다. 이런 건 보조 기술을 실제로 끄고 켜가며 사람이 직접 빌드를 살펴봐야 합니다. axe는 실질적인 신호이지 무의미한 것은 아닙니다. 접근성의 맞춤법 검사기 층이지, 편집자는 아닙니다.
적합성 항목은 관료적으로 들려서 거의 건너뛸 뻔했습니다 — "계획이 약속한 내용을 포함함"이라니, 제가 정신이 딴 데 팔린 채로 승인했던 계획이 이메일 확인을 요구했는지 SMS만 요구했는지 정말로 기억나지 않았다는 걸 떠올리기 전까지는요. 이 레이어는 빌드를 제 실제 의도가 아니라 계획과 대조해서 확인하는 것이고, 통과했다는 건 빌드가 제가 승인한 내용과 일치한다는 뜻이지, 제가 진짜 의도했던 것과 반드시 일치한다는 뜻은 아닙니다. 기능적으로 탄탄하고 안전한데도 시간 압박 속에 기능이 조용히 빠져서 이 레이어에서 실패한 빌드에 대해 들은 적이 있습니다. 대화 자체가 다소 엉성했더라도, 빌드를 그 대화에 정직하게 유지시켜주는 레이어입니다.
여섯 개 행 아래에는 두 개의 묶음으로 나뉜 더 긴 목록이 있었고, 여기에 가장 많은 시간을 썼습니다. 필수 수정 항목들은 현재 빌드에 잘못된 부분이 아니라, 영수증 같은 것입니다. 한 줄에는 리뷰 레이어가 날짜 문자열이 쿼리에 그대로 삽입되는 경우를 발견했고, 이 버전이 완료로 표시되기 전에 이미 패치되었다고 적혀 있었습니다. 저는 열린 상처를 보고 있던 게 아니라 흉터를 보고 있었던 겁니다. 필수 수정 항목을 살아있는 경고로 읽으면 이미 닫힌 문제를 걱정하며 시간을 낭비하게 되니 이 구분은 중요합니다.
권고 목록은 더 길었고, 대부분은 병합을 막고 싶지 않으면서 동료의 코드를 리뷰할 때 제가 직접 했을 법한 말들이었습니다: "반복되는 슬롯 렌더링 블록을 공유 컴포넌트로 추출하는 것을 고려하세요", "이 엔드포인트에는 속도 제한이 없는데, 내부용 예약 도구로는 괜찮지만 공개로 전환한다면 재고할 가치가 있습니다." 그 목록에는 결함이 하나도 없었습니다. 계획과 코드만 가지고 검증자가 내린 판단들이었고, 요가 스튜디오의 내부 예약 시스템에는 그 판단들 모두 합리적인 쪽으로 떨어졌습니다. 만약 제 친구의 스튜디오가 이 위젯을 50개 지점 페이지에 내장한 프랜차이즈였다면 속도 제한 항목에는 반박하고 싶었을 겁니다. 이 분류는 검증자가 추측만 할 수 있는 맥락에 좌우되며, 추측이 틀린 것 같다면 제대로 된 대응은 라벨을 최종적인 것으로 받아들이는 게 아니라 채팅에서 그렇게 말하는 것입니다.
깨끗한 필수 수정 열 옆에 상당히 긴 권고 목록을 보면서 문득 깨달은 건, 이 길이를 나쁜 소식으로 거의 잘못 읽을 뻔했다는 점입니다. 그렇지 않습니다. 권고 항목이 하나도 없는 빌드는 좁은 범위의 검사를 받았거나 운이 좋았던 것이고, "고려해보세요" 항목이 잔뜩 쌓여 있으면서 필수 수정에는 남은 게 없는 빌드는 실제로 꼼꼼히 검토된 빌드입니다. 권고 열은 진짜 문제가 사라진 뒤에 남아야 할 것들입니다.
이 기록이 3주나 지난 것이었기에, 판정을 신뢰하기 전에 억지로라도 확인한 또 다른 것은 어떤 레이어가 실제로 실행되었는지였습니다. 여기서는 여섯 개 모두 존재했지만, 이후에 접근성이 통과나 실패로 표시되지 않고 목록에서 그냥 빠져 있는 빌드를 본 적이 있습니다. 이는 중요하지 않아서 건너뛴 것과는 다르며, 해당 사이트 유형이나 플래그 설정에서는 그 검사가 실행되지 않았다는 신호입니다. 부재를 조용한 통과로 읽는 것은 대충 훑어볼 때 이 형식이 유발하기 딱 좋은 실수입니다.
요가 스튜디오의 예약 흐름이 실제로 전환되는지, 사람들이 시간대 선택 단계에서 이탈하는지, 그냥 Calendly로 링크만 거는 대신 커스텀 위젯을 만든다는 아이디어 자체가 애초에 맞는 선택이었는지는 아무것도 말해주지 않았습니다. 검증은 빌드가 약속한 대로 작동한다는 것을 증명하지, 그 약속이 옳은 약속이었는지는 증명하지 않습니다. 이건 별개의 질문이며, 저는 모든 레이어를 깔끔하게 통과한 빌드가 실제 사용자에게는 실패하는 걸 지켜본 적이 있습니다. "정확히 작동한다"와 "올바른 문제를 해결한다"는 바라는 만큼 겹치지 않기 때문입니다. 두 번째 질문에 실제로 답하는 부분은 측정 루프이며, 둘은 함께 읽어야 합니다. 아무도 예약하지 않는 기능에 대한 깨끗한 검증 기록이라도, 여전히 아무도 예약하지 않는 기능일 뿐입니다.



