우리 앱을 처음 쓰는 사람의 눈으로 봐 달라고 하면 어떨까요? 처음 쓰는 사람은 딱 한 번만 처음이라서, 두 번째부터는 이미 아는 사람이 됩니다. 그래서 처음 쓰는 사람을 만들어 봤어요. 사내에서 만들고 있는 AI 학습 코칭 앱 로나를, 앱 코드는 한 줄도 안 고치고 바깥에서 나흘 동안 테스트한 기록입니다.
발단 — 손이 버전을 못 따라갔다
처음 이틀은 손으로 했어요. 앱 번들을 풀어 코드를 읽고, 앱이 띄우는 로컬 서버에 조회 요청만 보내 보고, 이상한 동작을 결함 문서로 남겼습니다. 열 건쯤 모였을 때 문제가 보였어요. 조사한 나흘 동안 앱이 네 버전을 지났거든요. 손으로 한 조사는 버전이 바뀌면 다시 처음입니다.
속도가 문제였어요. 코딩 에이전트 덕에 만드는 쪽은 몇 배로 빨라졌는데, 검증하는 쪽은 여전히 사람 손이에요. 버전 하나에 고친 게 한두 군데가 아니라 기능이 통째로 바뀌어 올라오니, 사람이 클릭해 보는 QA로는 따라갈 수가 없었습니다. 만드는 쪽이 에이전트면 검증하는 쪽도 에이전트여야 속도가 맞겠다 싶었어요.
지난 글에서 코드는 이해의 캐시이고 엄밀함은 평가 쪽으로 옮겨가야 한다는 얘기를 했었죠. 그걸 모듈 하나가 아니라 앱 하나에 통째로 적용해 보면 어떻게 될까 싶었어요. 규칙은 하나입니다. 앱 코드는 고치지 않는다. 바깥에서 써 보고, 지켜보고, 기록만 한다. 앱은 개발팀이 만들고, 저는 그 앱을 처음 쓰는 사람이 겪는 일을 증거와 함께 넘기는 역할이에요.
설계 세션에서 우선순위를 하나 뒤집었습니다. 처음엔 "찾아 둔 결함이 다음 버전에서 다시 나는지 지키는 일"을 먼저 하려 했는데, 그건 이미 아는 것만 지킵니다. 모르는 결함을 찾는 일이 1순위가 됐어요. 그러려면 앱을 실제로 써 보는 누군가가 필요합니다. 첫 표본은 좁게 잡았어요. 초보 한 명, 과제 하나. 변수를 하나씩 늘려야 뭐가 앱 문제이고 뭐가 학습자 차이인지 가를 수 있으니까요.
설계 — 초보 한 명을 만들다
그냥 코딩 에이전트에게 "이 앱 QA 좀 해 줘"라고 하면 안 되냐고 물으실 수 있어요. 그렇게 하면 뭘 본 건지 알 수가 없어요. 로나는 혼자 도는 앱이 아니라 Codex 앱이나 Claude 앱과 붙어서 도는 앱이거든요. 사용자는 로나에서 "코칭 시작"을 누르고, 실제 대화는 코딩 에이전트 앱 안에서 코치(그 안에서 도는 AI)와 하고, 진행 상황은 다시 로나의 진행표로 돌아옵니다. 그러니 테스트 대상이 앱 하나가 아니라 앱 두 개와 그 사이의 연결, 그리고 그 안에서 도는 AI까지 넷이에요. 게다가 테스트를 시킬 에이전트가 테스트 대상의 엔진이기도 합니다. Codex에게 시키면 Codex 위에서 도는 코치를 Codex가 검사하는 셈이죠. 누가 학습자를 연기하고, 누가 코치를 맡고, 누가 판정하는지를 갈라 두지 않으면 결과를 읽을 수 없어요. 그래서 설계가 필요했습니다.
코드보다 먼저 용어집을 만들었어요. 봇이 쓰는 말이 흔들리면 판정도 흔들리니까요. 열다섯 개 중 글에 필요한 다섯만 옮깁니다.
📎 페르소나 — 가상 학습자의 프로필이에요. AI 숙련도, 쓰는 도구, 가져온 과제까지 정해 두면 말투와 판단 기준이 따라옵니다. 시나리오는 시작 조건·목표·대화 턴 상한·기대 결과를 적은 대본이고, 시나리오에 적힌 "눌러도 되는 버튼" 목록이 곧 그 런의 승인 범위예요. 페르소나 하나가 시나리오 하나를 끝까지 한 번 수행한 것을 런이라 부릅니다. 런이 끝나면 시나리오의 기대 결과와 대조해 검사 항목마다 통과·실패를 가르는데 그 묶음이 판정이고, 런 도중에 학습자가 겪은 관찰 하나하나가 소견이에요. 판정은 자동이고 소견은 사람이 읽고 결함·개선점·의문 중 하나로 분류합니다.
봇이 연기할 첫 페르소나는 AI 도구 초보예요. ChatGPT 웹은 써 봤지만 터미널·Git·MCP·스킬이라는 말은 처음 듣는 사람입니다. (직업은 30대 마케터로 뒀는데, 말투를 정하는 장치일 뿐 중요한 건 초보라는 점이에요.) 모르는 용어가 나오면 되묻고, 두 번 못 알아들으면 "그냥 넘어가도 되나요?"라고 합니다. 오류 메시지를 보면 스스로 고치려 하지 않고 그대로 복사해서 코치에게 보여줘요. 결과물은 "내 눈에 대시보드처럼 보이면" 승인합니다. 코드 품질은 판단 못 하고요. 규칙을 하나 더 박아 뒀는데, 코치가 "진행표를 앱에 연결해도 될까요?"라고 물으면 승인하게 했어요. (초보의 자연스러운 반응은 아니라서, 이 규칙이 발동한 턴은 기록에 따로 표시합니다.) 과제는 한 문장으로 고정했습니다.
그리고 이 초보를 앱에 앉히는 드라이버를 만들었어요. 앱 안의 코치를 띄우고, 학습자의 발화는 코치와 다른 엔진이 만들게 했습니다. Codex가 가르치면 Claude가 학습자를 연기하는 식이에요. (같은 엔진끼리면 서로 잘 통해서 초보가 걸리는 데를 그냥 넘어갈까 봐 그랬는데, 대조 실험은 안 한 설계 가설입니다.) 판정은 시나리오에 적어 둔 기대 결과로 자동입니다.
하나 더, 게이트를 달았습니다. 이 앱은 저도 실제로 쓰는데, 봇이 데스크톱 앱을 직접 조작하는 방식은 제 마우스와 키보드를 같이 써요. 그래서 제 진짜 코칭이 최근 30분 안에 움직였으면 런을 거부하게 했습니다. (Claude 쪽은 통제권을 통째로 가져가고 Codex 쪽은 키보드만 쓰는데, 샌드박스가 파일·네트워크 격리라 입력까지는 못 막았어요. 봇 자신의 활동과 사람 활동을 가르는 게 어려워서 이 게이트는 여러 번 고쳐졌습니다.)
누가 누구를 보는지 한 장으로 그리면 이렇습니다. 아래 줄이 사용자가 지나가는 경로를 재현한 구성이에요. 대상은 넷이지만 QA 에이전트가 붙는 관측 지점은 학습자·코치·로나 앱 셋이고, 학습자 발화를 넣고 코치 응답과 진행표를 읽습니다. 그리고 QA 에이전트 자신은 리뷰어 둘이 봅니다.
이 구성으로 첫날 오후에 첫 런이 돌았어요. 손으로 이틀 동안 열 건을 찾았 던 자리에서, 봇을 돌린 사흘 동안 결함 열 건이 더 나왔습니다.
봇도 틀린다 — 그래서 봇도 리뷰를 받는다
규칙을 하나 박았어요. 봇 자체(방법론·문서·도구)를 바꿀 때마다 Codex와 Claude에 각각 독립 리뷰를 시키고, 둘이 같은 자리를 짚은 지적을 우선 반영합니다. 프롬프트는 "반박 목록만, 칭찬·요약 금지, 근거 없는 추정은 추정이라고 써라"예요. 사흘 동안 열세 번 돌렸는데, 두 리뷰어가 같은 파일·행을 짚은 곳은 회당 세 곳에서 열두 곳 사이였고 각자 짚은 자리 전체에 비하면 일부였습니다. (파일·행 단위로 센 거라 거친 숫자예요. 방향만 봐 주세요.)
리뷰가 잡아 준 봇의 오류가 꽤 됩니다. 학습자 역할이 코치의 마무리 질문에 "끝"이라고 답해 버려서 완주 판정을 놓친 것, 판정이 증거 없이도 통과를 낼 수 있는 경로, 병렬로 돌릴 때 전역 슬롯이 하나라 검사 항목 두 개가 믿을 수 없게 되는 문제 같은 것들이요. 마지막 건 고치는 대신 판정 결과에 "신뢰 불가"라고 찍히게 했습니다. 못 믿는 숫자를 믿는 척하는 게 제일 나쁘니까요.
반전 — "완주 불가"는 측정 방식이 만든 결과였다
무인 런을 돌리면 Codex 코치는 앱이 그 코칭을 자기 것으로 알아보지 못해서 끝까지 못 가는 걸로 보였어요. 결함 문서에도 그렇게 적혀 있었고요. 그런데 실제 데스크톱 앱을 화면 조작으로 돌려 보니 Codex도 Claude도 한 사이클을 완주했습니다. (완주는 코치가 완료 처리를 호출한 것이에요. 앱 진행표가 끝까지 갔는지와는 별개입니다.)
추적해 보니 무인 런의 연결 실패 중 일부는 비대화형 실행에 앱의 훅이 돌지 않는 조건이 만든 결과로 보여요. (그 조건을 지금은 같은 머신에서 재현할 수 없어서 확정은 못 했습니다.) 실제 앱에서는 훅이 돌고, 연결이 흔들려도 코치가 재시도하거나 "이어하기"로 넘어갑니다. 그래서 결함 몇 건은 "무인 조건 한정"으로 범위를 다시 적었어요. 전부가 그랬던 건 아닙니다. 진행표 동기화 문제는 실제 앱에서도 그대로 재현됐고, 그건 진짜 앱의 결함으로 남았어요. 지난 글에서는 검사 대상인 원본 코드가 틀렸었는데, 이번에 틀린 건 검사 결과 쪽이었습니다. 결함이 아니라 "완주 못 한다"는 판정이요.
그래서 QA 방식을 하나로 두지 않고, 사람 손이 얼마나 드는지로 넷으로 나눴어요.
첫째 층 무인 런이 제일 많이 돌았어요. 마흔한 번 중 서른세 번이 여기예요. 빠르고 병렬로 돌릴 수 있지만 화면에서만 보이는 문제는 못 봅니다. 둘째 층은 코딩 에이전트 앱 안의 대화창이 학습자 역할을 맡는 방식인데, 실사용자는 못 하는 사전 호출이 필요해요. 셋째 층의 절대 규칙은 조작하는 앱과 조작당하는 앱이 달라야 한다는 겁니다. 자기 자신을 조작하면 포커스가 충돌해요. Codex 앱이 Claude 앱을 몰고, 그 반대도요. 넷째 층 사람 런 셋은 팀원이 처음 쓰는 사람처럼 직접 받은 것으로(봇의 페르소나 대본을 따른 건 아닙니다), 실경로를 확인하는 기준 표본이에요. 결과물이 엉뚱한 폴더에 저장된 것, 대화창이 둘 뜬 것은 지금까지는 사람 런에서만 관측됐습니다. (봇이 원리상 못 보는 건 아니고, 무인 런에서는 안 났을 뿐이에요.)
결과 — 나흘, 코칭 마흔네 번
제목이 속도였으니 속도부터 볼게요. 나흘 동안 앱 버전이 세 번 바뀌었고, 봇은 세 번 다 따라갔습니다. 버전이 올라올 때마다 새 기준선을 뜨고, 같은 페르소나를 다시 앉히고, 검사가 붙은 결함의 상태를 버전별로 다시 판정했어요. 손으로 하던 이틀 동안은 버전 하나도 못 따라갔던 걸요. 봇 한 번 앉는 데 30분 남짓이라 하루에 두 버전도 됐습니다. "처음 쓰는 사람은 한 번만 처음"이라는 문제가, 버전마다 처음 쓰는 사람을 다시 앉히는 걸로 풀린 거예요.
표의 분모만 짚어 둘게요. 44회에는 방식을 어겨 폐기한 봇 런 한 번이 들어 있고, 완주율의 분모 21은 끝까지 가는 시나리오를 돌린 봇 런만 센 겁니다. 소요 시간 두 줄은 봇 대 사람 비교가 아니라 봇 런의 절대 소요로 읽어 주세요. 사람 값은 셋뿐이고 읽고 답하는 시간이 들어 있어서 참고치 예요.
완주율 52%는 낮아 보이죠. 못 간 열 번은 앱과 코치 사이의 연결·진행표 층에서 막힌 것이지 코칭 내용 때문은 아니었어요. 그래서 핵심 발견이 이 한 줄입니다. 코칭 자체는 되고, 걸리는 건 코칭 내용이 아니라 진행표 동기화다. 소견 서른일곱 건 중 결함 스무 건은 각각 파일 하나로, 버전별 상태와 다시 판정할 검사 스크립트를 달고 개발팀에 넘어갔어요. 보고서 한 장과 코치 지침 수정 명세 여덟 항목도 같이요. 그 제안 가운데 실제로 효과를 확인한 건 "첫 호출 전에 사용자에게 허락을 한 번 묻기" 하나뿐이고, 나머지는 고친 뒤 다시 재야 합니다. 보고서는 봇 리뷰 때와 달리 서로 다른 모델 셋에게 반박시켜 분모와 추정치 라벨을 고쳤고요.
1순위를 "모르는 결함 찾기"로 뒤집을 수 있었던 것도 이 구조 덕이에요. 찾은 결함을 지키는 일은 검사 스크립트가 버전마다 자동으로 해 주니까요. 앱 코드를 건드리지 않았으니 앱이 몇 번을 올라와도 버릴 게 없습니다. 화면을 짚는 층만 UI가 바뀌면 다시 손봐야 해요.
아직 못 본 것
솔직히 적어 둘 한계가 있어요. 처음 쓰는 사람을 만들어 놓고 정작 처음 설치하는 온보딩은 봇이 제대로 못 봤습니다. 첫 설치는 이 계정의 설정을 지워야 재현돼서 보류했고, 화면 조작 층으로 끝까지 보는 시나리오는 써 두고 아직 안 돌렸어요. 그리고 앱을 쓸 사람은 여러 유형일 텐데 이번엔 초보 프로필 하나, 만들 주제도 뉴스 대시보드 하나였습니다. 다른 사람이 다른 걸 만들려 할 때 어디서 걸리는지는 아직 모릅니다.
그래서 다음은 이겁니다. 온보딩 시나리오를 화면 조작 층으로 끝까지 돌리고, 페르소나를 늘립니다. 숙련도가 다른 사람, 다른 과제를 가져온 사람, 다른 엔진을 쓰는 사람을 하나씩 앉혀 볼 거예요. 앱이 올라올 때마다 같은 절차가 돌게 해 뒀으니, 이제 늘릴 건 앉힐 사람의 종류입니다.
그래서 무엇이 남았나
- QA가 버전 속도를 따라갑니다. 손으로는 이틀에 한 버전도 못 따라갔는데, 봇은 나흘 동안 세 번 바뀐 버전을 세 번 다 다시 돌았어요.
- 봇의 오류는 두 겹으로 거릅니다. 봇을 고칠 때마다 서로 다른 모델 둘의 교차 리뷰로 판정 버그를, 실경로 런으로 봇의 결론을 걸렀어요. 이 두 겹이 QA 절차 안에 붙박이로 들어갔습니다.
- 측정 방식이 결과를 만듭니다. 무인 런만 믿었으면 결함의 범위를 잘못 적어 보고했을 겁니다. 실제 경로를 한 층 이상 두세요.
- 사람 런은 대체되는 게 아니라 배치됩니다. 봇이 넓게 훑고, 사람은 실경로를 확인하는 자리에 세 번만 앉았어요.