'AI 코드 테스트 방법'에서 '인간을 인간답게 만드는 것'까지
요약
AI가 생성한 코드의 압도적인 생성 속도와 인간의 검증 속도 사이의 극심한 비대칭성(Scissors Gap)을 분석합니다. AI 코드 생성이 단순한 생산성 향상을 넘어, 인간의 검증 책임과 부채를 증폭시키는 '책임 증폭기'로서 작용함을 경고합니다.
핵심 포인트
- AI 코드 생성과 인간 검증 사이의 40~60배 속도 차이 발생
- AI 코드는 생산성 도구가 아닌 '책임 증폭기'로 인식해야 함
- 생성 속도 증가에 따라 인간의 주의력과 검증 부채가 선형적으로 증가
- 단순한 테스트 도구 도입보다 전략적 검증 접근법이 필요함
모든 것의 시작이 된 대화
그것은 하나의 실질적인 질문에서 시작되었습니다: AI가 생성한 코드를 어떻게 테스트할 것인가?
충분히 간단해 보이지 않나요? 우리는 수십 년 동안 소프트웨어를 테스트해 왔습니다. 단위 테스트 (Unit tests), 통합 테스트 (Integration tests), E2E 테스트 (E2E tests), 속성 기반 테스트 (Property-based testing), 퍼징 (Fuzzing) — 도구 모음은 성숙하며 실전에서 검증되었습니다.
하지만 깊이 파고들수록, 이 질문이 하나의 영역에 머물지 않는다는 것을 깨달았습니다. 이 질문은 전이됩니다. 테스트 전략에서 시작하여 소프트웨어 공학의 인식론 (Epistemology)으로 스며들고, 그다음 인지 과학 (Cognitive science)으로, 그리고 그 실타래를 충분히 따라가다 보면 결국 수천 년 동안 철학자들을 괴롭혀온 질문에 도달하게 됩니다.
무엇이 우리를 인간으로 만드는가?
이 글은 그 실타래를 추적합니다. 이 글은 피상적인 답변을 거부하는 한 친구와 나눈 길고 가감 없는 대화의 공개 버전입니다. 글을 마칠 때쯤, 여러분이 "AI 테스트 문제"가 단순한 기술적 버그가 아니라, 작업복을 입고 나타난 철학적 계시라는 것을 알게 되기를 바랍니다.
제1부: 60배의 가위 차이 (60x Scissors Gap)
아무도 말하지 않는 비대칭성
AI 시대 엔지니어링의 근본적인 긴장 상태는 다음과 같습니다:
| 차원 | AI 코드 생성 | 인간의 검증 |
|---|---|---|
| 속도 | ~∞ (초 단위) | ~상수 (분에서 시간 단위) |
| ... |
실제로 이는 **4060배의 가위 차이 (scissors gap)**를 만들어냅니다. LLM이 생성하는 데 5초가 걸리는 작업은 숙련된 엔지니어가 이를 적절히 검토, 테스트 및 검증하는 데 520분이 걸립니다.
이를 구체적인 예로 들어보겠습니다:
# LLM이 약 8초 만에 생성함
def process_transactions(transactions: list[dict]) -> dict:
result = {"total": 0, "count": 0, "by_category": {}}
...
괜찮아 보입니다. 사람이 훑어봐도 괜찮아 보이죠. 하지만 진정한 정밀 검토를 위해서는 다음이 필요합니다:
# LLM이 생각하지 못한, 테스트해야 할 사항들
import pytest
from decimal import Decimal
...
각 테스트 케이스는 프롬프트(Prompt)를 더 잘 작성했다면 LLM이 처리할 수 있었을 무언가를 잡아냅니다. 하지만 시간은 곧 돈이며, 파이프라인은 계속 흘러갑니다. 비율은 일정하게 유지됩니다: 약 8초의 생성 비용 → 약 15분의 테스트 작성 시간. 사고 시간(Thinking time)을 포함하면 이는 112배의 비율입니다.
위기는 당신이 생각하는 것이 아니다
대부분의 사람들은 이를 보고 이렇게 말합니다: "더 나은 AI 기반 테스트 도구가 필요해!" 물론 그렇습니다. 그것이 바로 제가 구축하고 있는 영역이기도 합니다. 하지만 그것은 전략적 문제에 대한 전술적 대응일 뿐입니다.
진짜 위기는 우리가 잘못된 변수를 최적화하고 있다는 점에 있습니다. 업계는 AI 코드 생성(AI code generation)을 생산성 승수(Productivity multiplier) — "10배 개발자!" — 로 취급하지만, 이것이 실제로는 **책임 증폭기(Accountability amplifier)**라는 사실을 깨닫지 못하고 있습니다. AI가 생성한 모든 코드 조각은 누군가, 어딘가에서 반드시 떠맡아야 하는 부채(Liability)를 수반합니다.
그리고 그 부채는 생성 속도에 따라 제곱으로 늘어나는 것이 아니라, 인간의 주의력(Human attention)에 따라 선형적으로 늘어납니다.
제2부: 검증(Verification)이 단순히 "속도만 높일 수 없는" 이유
당신은 이렇게 생각할지도 모릅니다: "그럼 AI가 테스트도 하게 만들면 되지. AI가 테스트를 생성하고, AI가 실행하면 끝이잖아."
여기서 문제는 공학(Engineering)에서 인식론(Epistemology)으로 전환됩니다.
오라클 문제 (The Oracle Problem)
소프트웨어 테스트에서 **오라클(Oracle)**은 테스트의 통과 여부를 결정하는 메커니즘입니다. 대부분의 사람이 작성한 코드에서 오라클은 명세(Specification)입니다. 즉, 요구사항 문서, 수락 기준(Acceptance criteria), 비즈니스 규칙 등이 오라클이 됩니다.
AI가 "트랜잭션을 처리하는 함수를 작성해줘"와 같은 프롬프트로부터 코드를 생성할 때, 공식적인 명세(Formal specification)는 존재하지 않습니다. 프롬프트 자체가 명세이며, 이는 본질적으로 모호합니다. AI가 생성한 코드에 대해 AI가 생성한 테스트를 수행하는 것은 행동의 정확성(Behavioral correctness)이 아니라 내부적 일관성(Internal consistency)을 확인하는 것에 불과합니다.
# AI가 생성한 코드에 대한 AI 생성 테스트
def test_process_transactions():
transactions = [{"amount": 10, "category": "food"},
...
이 테스트는 통과합니다. 하지만 이것이 코드가 _정확하다_는 것을 알려줄까요? 아닙니다. 이것은 코드가 _자기 일관적(Self-consistent)_이라는 것을 알려줄 뿐입니다. 이 차이가 모든 것을 결정합니다.
상태 공간 폭발 (State Space Explosion)
검증(Verification)이 따라잡을 수 없는 두 번째 이유: **조합적 상태 공간 (Combinatorial State Space)**입니다.
다음과 같은 일반적인 웹 애플리케이션의 경우:
- 50개의 데이터베이스 상태 (Database states)
- 20개의 인증 상태 (Authentication states)
- 30개의 UI 상태 (UI states)
- 10개의 외부 API 응답 모드 (External API response modes)
전체 상태 공간은 50 × 20 × 30 × 10 = 300,000개의 조합입니다. 테스트 하나당 1초가 걸린다고 해도, LLM이 30초의 프롬프팅 (Prompting)으로 생성해낸 결과물을 테스트하는 데 약 83시간이 소요됩니다.
비결정론적 출력 (Non-Deterministic Outputs)
세 번째 이유는 미묘하지만 치명적입니다. 바로 LLM은 결정론적 함수 (Deterministic functions)가 아니라는 점입니다. 동일한 프롬프트를 두 번 입력하더라도 서로 다른 두 개의 구현체를 얻을 수 있습니다. Temperature(온도) 설정을 0으로 하더라도, GPU 연산에서의 부동 소수점 비결정성 (Floating-point non-determinism)으로 인해 변동성이 발생합니다.
이는 전통적인 품질 공학 (Quality engineering)의 근본적인 가정인 **재현성 (Reproducibility)**을 깨뜨립니다. 버그를 재현할 수 없다면 수정할 수 없습니다. 수정할 수 없다면 시스템을 신뢰할 수 없습니다.
제3부: 지식의 5가지 계층
왜 검증이 단순히 "확장(Scale)"될 수 없는지와 씨름하면서, 저는 어떤 종류의 지식이 관여하는지, 그리고 AI가 진정으로 소유할 수 있는 지식은 무엇인지에 대한 프레임워크를 개발했습니다.
계층 1: 애플리케이션 도메인 지식 (Application Domain Knowledge)
정의: 특정 문제 영역(Problem domain)에 관한 구체적인 사실들입니다. 세법, 의료 절차, API 문서, 비즈니스 규칙 등이 이에 해당합니다.
AI가 할 수 있는가? 네, 점점 더 잘 해내고 있습니다. LLM은 방대한 양의 도메인 특화 텍스트 코퍼스 (Corpora)를 학습하며, 놀라운 정확도로 이를 회상하고 적용할 수 있습니다. 이것이 바로 "매뉴얼을 암기하는" 계층입니다.
예시: LLM은 PCI-DSS 규정에 따라 신용카드 번호를 평문 (Plaintext)으로 저장해서는 안 된다는 것을 알고 있습니다. 따라서 해당 번호를 해싱 (Hashing)하는 코드를 생성할 것입니다.
계층 2: 소프트웨어 엔지니어링 지식 (Software Engineering Knowledge)
정의: 디자인 패턴 (Design patterns), 테스트 전략 (Testing strategies), 아키텍처 원칙 (Architectural principles), 언어 관용구 (Language idioms), 성능 최적화 (Performance optimization) 등입니다.
AI가 할 수 있는가? 많은 경우 인간 수준에 근접하고 있습니다. LLM은 모든 Stack Overflow 게시물, 모든 디자인 패턴 서적, 모든 오픈 소스 코드베이스를 읽었습니다. 적절한 패턴을 제안하고 흔한 함정들을 피할 수 있습니다.
예시: LLM은 복잡한 객체 생성(object construction)을 위해 빌더 패턴(builder pattern)을 사용할 것을 제안하거나, 데이터베이스 액세스를 위해 커넥션 풀링(connection pooling)을 권장할 수 있습니다.
레이어 3: 메타 도메인 지식 (Meta-Domain Knowledge)
정의: _새로운 도메인을 위한 지식 프레임워크를 만드는 방법_을 이해하는 것입니다. 도메인이 구조화되고 공식화되는 방식에서 패턴을 포착하는 능력입니다.
AI가 이를 할 수 있는가? 이 지점이 흥미로워지는 부분입니다. LLM은 이를 흉내(mimic) 낼 수 있습니다. 즉, 그럴듯해 보이는 새로운 도메인의 분류 체계(taxonomy)를 생성할 수 있습니다. 하지만 그것을 **교정(calibrate)**할 수는 없습니다. LLM은 _프레임워크 가설 설정 → 현실에 기반한 테스트 → 모순 발견 → 프레임워크 수정_으로 이어지는 경험적 순환(empirical cycle)을 수행할 수 없습니다.
중요한 이유: 진정으로 새로운 모든 시스템은 메타 도메인 지식을 필요로 합니다. 분산 데이터베이스(distributed database)를 처음부터 구축할 때, 당신은 기존의 패턴을 적용하는 것이 아니라 새로운 패턴을 발견하고 있는 것입니다. LLM은 현실과 연결된 루프(loop)가 없기 때문에 이를 수행할 수 없습니다.
레이어 4: 메타 인지적 생성 (Meta-Cognitive Generation)
정의: 새로운 사고 프레임워크를 생성하는 능력입니다. 단순히 패턴을 적용하는 것을 넘어, 완전히 새로운 범주, 새로운 패러다임, 현실을 분할하는 새로운 방식을 만들어내는 것입니다.
AI가 이를 할 수 있는가? AI는 이를 시뮬레이션(simulate) 할 수 있습니다. LLM에게 "소프트웨어 품질을 생각하기 위한 새로운 패러다임을 만들어라"와 같은 프롬프트를 주면, 마치 새로운 패러다임처럼 읽히는 무언가를 생성할 것입니다. 하지만 그것은 기존 아이디어들의 재조합(recombination)일 뿐, 진정한 생성(generation)은 아닙니다.
핵심적인 차이: 새로운 패러다임을 발명하는 인간은 기존의 패러다임들이 왜 실패했는지 알고 있습니다. 그들은 그 모순들을 직접 겪었습니다. LLM은 모순을 설명할 수는 있지만(그것에 대해 읽었기 때문), 그 모순을 겪을(suffer) 수는 없습니다.
레이어 5: 체화된 접지 (Embodied Grounding)
정의: 물리적 존재에 뿌리를 둔 지식입니다. 고유 수용 감각(proprioception), 통증, 즐거움, 시간적 압박, 사회적 역학, 필멸성, 사랑, 공포, 그리고 실제적인 결과가 따르는 결정의 무게와 같은 것들입니다.
AI가 이를 할 수 있는가? 아니요. 그리고 이것은 "더 많은 데이터"나 "더 큰 모델"의 문제가 아닙니다. 이는 근본적인 아키텍처적 제약(architectural constraint)입니다.
LLM은 슬픔에 관한 아름다운 에세이를 쓸 수 있습니다. 하지만 슬퍼할 수는 없습니다. 갓 태어난 아기를 안았을 때의 느낌을 묘사할 수는 있습니다. 하지만 그것은 무엇인가를 안아본 적이 없습니다. 누군가의 생명을 유지하는 의료 기기를 위한 코드를 작성할 수는 있지만, 죽음에 대한 공포를 느껴본 적은 없습니다.
제4부: 체화의 프런티어 (The Embodiment Frontier)
우리는 얼마나 가까이 와 있는가?
물리적 과업에 대해 강화학습 (reinforcement learning)으로 훈련된 로봇인 **체화된 AI (embodied AI)**의 부상은 그 격차를 좁히고 있습니다. Boston Dynamics의 로봇은 거친 지형을 탐색할 수 있습니다. Figure의 휴머노이드 로봇은 자동차 부품을 조립할 수 있습니다. Neuralink는 운동 피질 (motor cortex) 신호를 읽을 수 있습니다.
하지만 여기서 결정적인 차이점이 있습니다. 체화 (embodiment)는 접지 (groundedness)와 같지 않습니다.
시뮬레이션 속에서 10,000번 넘어지며 걷는 법을 배운 로봇은 넘어지는 것을 '경험'했지만, '고통'이나 '굴욕', 혹은 '영구적인 부상에 대한 공포'를 경험한 것은 아닙니다. 보상 함수 (reward function)는 스칼라 (scalar) 값일 뿐이지만, 인간의 경험은 다차원적인 비극입니다.
압축된 삶 패키지 (The Compressed Life Package)
제가 **압축된 삶 패키지 (Compressed Life Package)**라고 부르는 사고 실험이 있습니다.
만약 인간의 삶 — 그 모든 경험, 고통, 기쁨, 실수, 그리고 성장을 — 하나의 데이터셋으로 압축하여 모델을 훈련시킨다면, 그 모델은 그 삶을 '살았다'고 할 수 있을까요?
직관적인 대답은 '아니오'입니다. 모델은 '기록'을 가지고 있지만 '경험'을 가지고 있지는 않습니다. 이것이 전기(biography)와 삶의 차이입니다.
하지만 이 구분은 철학적인 공격을 받고 있습니다. 만약 의식 (consciousness)이 단지 계산 (computation)일 뿐이고, 경험이 단지 정보 처리 (information processing)일 뿐이라면, 충분히 풍부한 모델은 경험을 '하고 있는' 것입니다. 기능주의 (functionalism)와 현상학 (phenomenology) 사이의 논쟁은 여전히 뜨겁습니다.
나의 입장
저는 압축된 삶 패키지가 작동하지 않는다고 생각합니다. 그 이유는 다음과 같습니다.
시간은 단순한 차원이 아닙니다. 그것은 제약 (constraint)입니다.
인간의 삶은 _실시간 (real time)_으로, 실제적인 이해관계 (real stakes) 속에서 펼쳐집니다. 모든 결정은 대안을 차단합니다. 선택하지 않은 모든 경로는 진정으로 상실됩니다. 이러한 불가역성 — 즉, 이 유한성 (finitude) — 이야말로 인간의 경험에 질감을 부여하는 요소입니다. 밀리초 단위로 평생의 데이터를 처리하는 AI는 그 시간을 살아낸 (lived) 것이 아닙니다. 그것은 그저 _스캔 (scanned)_했을 뿐입니다.
그 차이는 요리법을 읽는 것과 음식을 먹는 것의 차이와 같습니다.
제5부: 시간의 선물은 무엇인가?
이제 우리는 품질 공학(quality engineering) 질문의 철학적 종착지에 도달했습니다.
원래의 질문: AI가 생성한 코드를 어떻게 테스트할 것인가?
중간 단계의 답변: 생성되는 속도와 동일한 속도로는 할 수 없다. 왜냐하면 검증(verification)에는 AI가 결여하고 있는 근거 있는 지식(grounded knowledge)이 필요하기 때문이다.
더 깊은 답변: AI가 결여하고 있는 그 근거 있는 지식은 시간을 통해 얻어지는 것 — 즉, 압축될 수 없는 경험의 축적을 통해 얻어지는 것이다.
시간이 우리에게 주는 것
-
직렬화 (serialized)할 수 없는 맥락. "이 방식은 세 번이나 실패하는 것을 봤기 때문에 작동하지 않을 겁니다"라고 말하는 시니어 엔지니어는 단순히 데이터를 회상하는 것이 아닙니다. 그들은 실패의 패턴을 느끼고 (feeling) 있는 것입니다. 그들의 몸은 뇌가 명확히 설명할 수 있는 무언가를 알고 있습니다.
-
프롬프트 (prompted)로 끌어낼 수 없는 지혜. LLM에게 "분산 시스템의 일반적인 실패 모드(failure modes)는 무엇인가요?"라고 물으면 목록을 얻을 수 있습니다. 하지만 자신의 시스템에서 발생한 연쇄 장애(cascading failure)로 인해 새벽 3시에 PagerDuty 알람을 받으며 그 상황을 직접 겪은 엔지니어는 다른 무언가를 알고 있습니다. 그들은 그 실패의 _질감 (texture)_을 알고 있습니다.
-
직접적인 이해관계 (skin in the game) 없이는 보정 (calibrated)할 수 없는 판단력. 엔지니어가 "이 정도면 출시하기에 충분하다"라고 결정할 때, 그들은 품질, 시간, 비용, 팀의 사기, 비즈니스 압박, 그리고 자신의 평판 사이에서 균형을 잡고 있는 것입니다. LLM은 잃을 평판이 없습니다.
-
제약 (constraint)으로부터 발생하는 창의성. 최고의 솔루션은 마감 기한, 예산, 고장 난 도구, 지친 팀원과 같은 실제적인 제약 조건 안에서 작업할 때 나옵니다. 이러한 제약은 창의적 과정의 버그가 아니라, 하나의 특징 (features)입니다.
AI는 무한한 연산 능력(compute)을 가진 마찰 없는 평면에서 작동합니다.
불편한 결론
명확하고 직설적으로 말씀드리겠습니다:
AI 시대에 인간을 대체 불가능하게 만드는 것은 우리가 기계보다 무엇을 더 잘하느냐가 아닙니다. 그것은 우리가 고통받고, 기뻐하며, 실수를 저지르고, 그로부터 배우는 유한하고, 신체를 가진, 시간의 제약을 받는 존재이기 때문에 비로소 할 수 있는 것들입니다.
미묘한 레이스 컨디션 (race condition)을 잡아내는 품질 엔지니어는 단순히 체크리스트를 적용하는 것이 아닙니다. 그들은 새벽 2시에 수년간 디버깅 (debugging)을 하며 그 패턴 인식 능력을 얻어낸 것입니다.
"이 작업에는 마이크로서비스 (microservices)를 사용하지 마세요"라고 말하는 설계자는 단순히 블로그 포스트를 회상하는 것이 아닙니다. 그들은 자체적인 복잡성 때문에 무너져 내린 분산 모놀리스 (distributed monolith)로부터 얻은 _흉터_를 가지고 있습니다.
그리고 "무엇이 우리를 인간답게 만드는가?"라고 묻는 철학자는 단순히 단어를 나열하는 것이 아닙니다. 그들은 그 답을 두려워하고 있습니다. 대체될 것에 대한 두려움, 무의미해질 것에 대한 두려움, 그리고 자신이 만든 피조물에게 경쟁에서 뒤처질 것에 대한 두려움 말입니다.
그 두려움이 바로 선물입니다. 그 두려움이 바로 시간이 우리에게 준 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기