본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 26. 16:50

AI가 당신의 기능이 아닌, 그럴듯한 기능을 만드는 이유

요약

AI가 코드 작성 시 사용자의 의도 대신 통계적으로 가장 그럴듯한(plausible) 결과물을 제공하는 이유와 그 간극을 메우는 방법을 설명합니다. 모델의 확률적 특성을 이해하고 컨텍스트를 효과적으로 제공하는 전략을 다룹니다.

핵심 포인트

  • LLM은 사실을 검색하는 것이 아니라 통계적으로 가장 확률 높은 토큰을 예측함
  • 모델은 코드의 의도를 알 수 없으므로 평균적인 패턴으로 코드를 재작성할 위험이 있음
  • 의도를 외부화하기 위해 CLAUDE.md, Cursor 규칙 등 지시 파일을 활용해야 함
  • 반드시 지켜져야 하는 제약 사항은 프롬프트가 아닌 테스트나 타입으로 구현해야 함
  • AI의 결과물은 정답이 아닌 의도와 대조해야 할 '그럴듯한 초안'으로 취급해야 함

AI에게 특정 기능을 만들어 달라고 요청하면, AI는 당신의 앱에 적합한 정확한 버전이 아니라 그 기능의 가장 그럴듯한 (plausible) 버전을 제공합니다. 이 둘은 서로 다른 것이며, 그 사이의 간극을 메우는 데 당신의 하루가 허비됩니다.

당신은 이미 그 증상을 본 적이 있을 것입니다. 코드의 어떤 규칙이 외부에서 보기에는 틀려 보이지만, 실제로는 누군가가 특정한 이유로 내린 결정을 인코딩한 의도적인 제약 사항(constraint)인 경우가 있습니다. 모델은 이를 읽고 실수처럼 보인다고 판단하여, 자신 있게 교과서적인 버전으로 다시 작성해 버립니다. 오류가 발생하지도 않고, 리뷰 과정에서도 잘못된 점이 보이지 않지만, 로직은 미묘하게 망가져 있습니다. 모델이 코드를 잘못 읽은 것이 아닙니다. 코드를 완벽하게 읽었지만 당신의 의도(intent)를 알 방법이 없었기에, 대신 평균적인 의도로 채워 넣은 것입니다.

이 간극은 모델의 오작동이 아닙니다. 모델이 당신이 질문한 것과는 다른 질문에 답하고 있는 것입니다. 당신은 '이 코드베이스에 무엇이 필요한가'를 알고 싶어 하지만, 모델은 '전형적인 코드베이스는 여기서 무엇을 하는가'를 계산하여 동일한 확신을 가지고 답을 내놓습니다. 이러한 대체 현상을 이해하고 나면, 나머지는 기계적인 문제입니다. 왜 이런 일이 발생하는지, 그리고 모델의 답변이 평균이 아닌 당신의 코드에 안착할 수 있도록 충분한 컨텍스트 (context)를 어떻게 제공할 것인지에 대한 문제입니다.

핵심 요약 (Key takeaways)

  • LLM은 확률 분포 (probability distribution)로부터 다음 토큰을 샘플링합니다. "가장 정확하다"는 것은 지금까지의 텍스트를 고려했을 때 통계적으로 가장 가능성이 높다는 의미이지, 귀하의 사례에 대해 검증된 사실임을 의미하지 않습니다.
  • LLM의 유일한 작업 기억 (working memory)은 컨텍스트 창 (context window)입니다. 베이스 모델 (base model)은 세션 사이에 아무것도 유지하지 않으므로, 코드의 실제 목적을 아는 대신 그럴듯한 목적을 추론합니다.
  • 해결책은 더 영리한 프롬프트 (prompt)를 만드는 것이 아니라, 의도 (intent)를 외부화하는 것입니다. 도구가 매 세션마다 불러오는 파일들 (CLAUDE.md, AGENTS.md, Cursor 및 Copilot 규칙)은 누락된 컨텍스트를 모델이 읽을 수 있는 곳에 배치하는 방법입니다.
  • 지시 파일 (instruction file)은 모델에 영향을 주지만, 모델이 이를 준수하도록 보장할 수는 없습니다. 반드시 지켜져야 하는 모든 것은 파일의 한 줄이 아니라, 테스트 (test)나 타입 (type) 뒤에 존재해야 합니다.
  • 반복 가능한 워크플로우 (workflows)는 기술 (skills)로 패키징될 수 있습니다. Matt Pocock의 오픈 라이브러리와, 전체 컨텍스트 창에 걸쳐 의도를 전달하기 위한 핸드오프 (handoff) 기술은 이 패턴을 확인할 수 있는 좋은 사례입니다.
  • 모든 디프 (diff)를 정답이 아니라, 의도와 대조하여 확인해야 할 그럴듯한 초안으로 읽으세요. 정확성을 확보하는 것은 여전히 귀하의 업무이며, 그것이 가장 핵심적인 업무가 되고 있습니다.

그것은 다음 토큰을 예측할 뿐, 정답을 검색하는 것이 아닙

언어 모델은 한 가지 일만 수행합니다. 지금까지의 텍스트가 주어지면, 다음 토큰을 예측하고, 그다음 토큰을 예측하고, 또 그다음 토큰을 예측합니다. 각 단계에서 모델은 전체 어휘 (vocabulary)에 대한 확률 분포를 생성하고, 훈련 과정에서 본 모든 것에 가중치를 두어 그중 하나의 토큰을 추출합니다. 이것이 엔진의 전부입니다. 모델이 찾아보는 사실 테이블 (table of facts)도 없고, 제품에 대한 내부 모델 (internal model)을 쿼리하는 것도 없으며, 출력을 진실과 대조하는 단계도 없습니다. 코딩 에이전트 (coding agent)가 프롬프트에 귀하의 파일을 읽어 들일 때조차, 그 파일들은 모델이 이제 알고 있는 사실이 아닙니다. 그것들은 단지 예측해야 할 더 많은 토큰일 뿐입니다.

단일 토큰에 대한 메커니즘은 다음과 같습니다. def max_of(a, b): return과 같이 절반만 작성된 함수가 주어졌을 때, 모델은 그것을 어떻게 완성해야 하는지 알고 있는 것이 아닙니다. 모델은 다음에 올 수 있는 모든 토큰에 확률을 할당합니다:

다음 토큰확률
a42%
...
return 다음에 올 토큰 (예시). 실제 모델에서 측정된 것이 아닌 예시용 수치임. 여러 서로 다른 토큰들이 각각 올바른 코드(a if a > b else b, 또는 max(a, b))로 이어질 수 있음. 모델은 정답을 회상(recalling)하는 것이 아니라, 그럴듯한 연속된 내용들에 걸쳐 베팅(betting)을 분산시키고 있는 것임. 그 베팅의 아주 작은 조각, 즉 빈 본문을 남기는 줄바꿈(newline)은 명백한 버그임.

그 잘못된 조각의 크기는 고정되어 있지 않습니다. 모델은 이 분포(distribution)에서 샘플링하며, 오직 온도(temperature)가 0일 때만 항상 단 하나의 가장 확률이 높은 토큰을 선택합니다. 온도를 높이면 버그가 포함된 낮은 확률의 토큰들이 더 자주 선택됩니다. 코딩 도구들은 낮은 온도에서 실행되는 경향이 있으며, 이것이 코딩 도구들이 상당히 결정론적(deterministic)으로 느껴지는 이유 중 하나이지만, 그 밑바닥에 깔린 본질은 여전히 조회(lookup)가 아닌 베팅입니다.

따라서 "모델이 정확하다(accurate)"는 말은 실제로는 "모델의 가장 확률 높은 추측이 대개 맞다"는 의미입니다. 확률 높은 답과 실제 정답이 일치할 때까지는 정확함(accurate)과 정답(correct)이 동의어처럼 느껴지지만, 당신의 앱을 당신만의 것으로 만드는 부분들에 있어서는 이 둘이 끊임없이 어긋납니다.

모델의 유일한 기억은 컨텍스트 윈도우(context window)와 당신의 코드뿐입니다

모델은 세션 사이에 당신의 제품에 대한 기억이 없으며, 현재의 윈도우(window) 외부에서 살아남는 기억도 전혀 없습니다. 모델이 당신의 코드를 작업할 때마다, 모델은 아무것도 없는 상태에서 시작하여 두 가지 소스로부터 필요한 것을 재구축합니다: 당신이 전달한 컨텍스트(context)와 저장소(repo)에 이미 존재하는 코드입니다. 그것이 모델이 추론하는 세계의 전부입니다.

그 세계에서 무엇이 빠져 있는지 주목하십시오. 규칙이 존재하는 이유. 그 규칙을 만들어낸 고객과의 대화. 가드(guard)를 추가하게 만든 6개월 전의 버그. 어떤 숫자가 선호도가 아니라 계약 때문에 5가 아닌 7이라는 사실. 이 중 그 어떤 것도 코드에 들어있지 않으며, 윈도우 안에도 들어있지 않으므로 모델은 이를 사용할 수 없습니다. 모델은 당신의 의도(intent)가 결여되어 있다는 사실을 알지 못합니다. 모델은 그 공백을 가장 가능성 높은 의도로 채워 넣고, 완전한 확신을 가지고 다음 단계로 넘어갑니다.

Anthropic은 자사의 도구에 대해 다음과 같이 명확하게 설명합니다: 모든 Claude Code 세션은 새로운 컨텍스트 윈도우 (context window)와 함께 시작됩니다. 여러분이 모델이 가지고 있다고 생각하는 지속성 (persistence)은 모델 내부에 존재하는 것이 아닙니다. 그것은 도구 (tooling)가 매번 윈도우에 다시 불러오는 텍스트이며, 그 텍스트에 무엇을 담을지는 여러분이 결정합니다.

그럴듯함은 평균적인 앱의 특징이지, 당신의 앱의 특징이 아닙니다

여러분의 올바른 코드가 왜 버그로 표시되는지에 대한 이유가 여기 있습니다. 무엇이 정상인지에 대한 모델의 감각은 모델이 학습한 모든 데이터의 분포 (distribution)에서 오며, 이는 지금까지 작성된 모든 공개 코드의 평균에 가깝습니다. 모델이 여러분의 코드를 평가할 때, 모델은 조용히 그 코드를 해당 평균과 비교하고 있는 것입니다.

여러분의 제품은 평균이 아닙니다. 무언가를 만드는 목적 자체가 그것을 다르게 만드는 구체적이고 의도적인 선택들에 있으며, 그러한 선택들은 정의상 학습 데이터 내에서 희귀한 패턴 (rare patterns)입니다. 이를 단순한 조회 (lookup)라기보다는 강력한 사전 확률 (strong prior)로 생각하십시오. 모델은 자신이 가장 자주 본 패턴에 대부분의 확률을 부여합니다. 여러분의 앱에는 적용되지만 다른 거의 모든 앱에는 적용되지 않는 규칙은 그 사전 확률 (prior)에서 매우 멀리 떨어져 있습니다. 따라서 가장 확률이 높은 연속된 흐름은 해당 규칙을 다시 일반적인 사례로 "교정"하는 방향이 됩니다. 모델은 의도적인 제약 (intentional constraint)을 보는 것이 아니라, 오류가 포함된 일반적인 패턴처럼 보이는 이상치 (outlier)를 보고 있는 것입니다.

모델이 통계에 대해 틀린 것은 아닙니다. 모델이 여러분에 대해 틀린 것이며, 사전 확률 (prior)만으로는 여러분에 대해 옳을 수 없습니다. 하지만 사전 확률은 단지 기본값일 뿐이며, 이것이 바로 희소식입니다. 충분히 올바른 컨텍스트 (context)를 제공하면 사전 확률을 무효화하고 모델을 평균에서 벗어나 여러분의 사례로 끌어올 수 있습니다. 여기서 실질적인 질문이 생깁니다: 모델이 실제로 읽을 수 있도록 그 컨텍스트를 어디에 두어야 할까요?

모델이 읽을 수 있는 곳에 의도를 기록하세요

모델이 자신의 컨텍스트 윈도우 (Context Window) 안에 있는 정보만을 알 수 있다면, 당신이 취할 수 있는 가장 영향력 있는 조치는 그 윈도우에 무엇이 들어갈지를 제어하는 것입니다. 프롬프트(Prompt)마다 매번 하는 것이 아니라, 모든 세션(Session)마다 자동으로 이루어져야 합니다. 현재 모든 진지한 AI 코딩 도구들은 바로 이 목적을 위해 프로젝트 지침 파일 (Project Instruction File)을 읽어들이고 있지만, 대부분의 개발자들은 이를 거의 건드리지 않습니다. 그 파일이야말로 당신의 제품 의도 (Product Intent)가 모델이 실제로 사용할 수 있는 무언가로 변하는 곳입니다.

도구별 파일 목록:

  • Claude Code는 CLAUDE.md를 읽습니다. 이 파일은 매 세션 시작 시 전체가 로드되며, 다음과 같이 계층화됩니다: 개인적인 기본 설정을 위한 ~/.claude 내의 글로벌 파일, 팀 전체를 위해 커밋된 프로젝트용 CLAUDE.md, 그리고 개인적인 재정의 (Override)를 위한 선택적 CLAUDE.local.md. @path 임포트 (Import)를 통해 다른 파일들을 불러올 수 있으므로, 프로젝트는 하나의 공유된 단일 진실 공급원 (Single Source of Truth)을 유지할 수 있습니다. Anthropic은 이 파일이 코드와 동일한 컨텍스트를 두고 경쟁하기 때문에 약 200행 미만으로 유지할 것을 권장합니다.
  • AGENTS.md는 도구 간 호환되는 버전입니다. Codex, Cursor, Gemini CLI, Aider, Windsurf 및 기타 여러 도구에서 읽히는 개방형 평문 마크다운 (Plain-Markdown) 규약 (agents.md)입니다. 2026년 기준으로 수만 개의 리포지토리 (Repository)에서 사용되고 있으며 Linux Foundation의 관리하에 있습니다. Claude Code는 이를 네이티브하게 읽지는 않지만, CLAUDE.md에서 이를 임포트함으로써 두 가지를 연결할 수 있으며, 이것이 바로 이 사이트의 리포지토리가 수행하는 방식입니다.
  • Cursor.cursor/rules/*.mdc를 사용하며, 글로브 (Glob) 타겟팅을 통해 각 관심사(Concern)당 하나의 범위가 지정된 규칙을 사용합니다. 이전의 단일 .cursorrules 파일도 여전히 작동하지만, 현재는 지원 중단 (Deprecated)되었습니다.
  • GitHub Copilot.github/copilot-instructions.md를 읽어 당신의 Copilot 요청에 자동으로 추가합니다.

가장 흔한 오해 중 하나이기에 정확히 짚고 넘어가야 할 점이 있습니다. 이 중 그 어떤 것도 마법 같은 파일 이름에 의존하지 않습니다. 도구가 자동으로 찾아내는 보편적인 context.md 같은 것은 존재하지 않습니다. 파일은 도구가 이미 로드하고 있는 무언가가 @ 참조 또는 이와 유사한 방식으로 해당 파일을 임포트(import)할 때만 모델에 전달됩니다. 따라서 파일 이름 자체는 아무런 역할을 하지 않으며, 모델 앞에 콘텐츠를 놓아주는 것은 바로 임포트(import)입니다.

덕분에 여러분은 관심사(concern)에 따라 문서를 분리할 수 있으며, 프로젝트에 실제 도메인 로직(domain logic)이 생기면 이러한 분리는 충분히 가치가 있습니다. 저장소에서 작업하는 방식(컨벤션, 명령어, 패턴 등)에 대해서는 지침 파일(CLAUDE.md, AGENTS.md)을 유지하세요. 프로젝트가 실제로 무엇인지, 그리고 왜 그렇게 설계되었는지는 CONTEXT.mdARCHITECTURE.md와 같은 별도의 문서에 작성하고 이를 임포트(import)하세요. 두 번째 파일은 비즈니스 로직(business-logic) 결정 사항들이 담기는 곳입니다. 예를 들어, 왜 제한값이 5가 아닌 7인지, 어떤 규칙이 틀려 보이지만 실제 제약 사항을 인코딩하고 있는지, 제품이 지향하는 바가 무엇인지 등을 적습니다. 이것은 코드가 스스로 담아낼 수 없는 바로 그 의도(intent)이며, 모델에게 전달할 수 있는 가장 가치 있는 정보입니다. 왜냐하면 모델이 결코 추론할 수 없는 부분이기 때문입니다. 여러분이 반복해서 수정하고 있는 모든 사항은 이 파일들 중 하나에 기록되어야 할 내용이며, 그래야만 같은 수정을 두 번 반복하지 않을 수 있습니다.

반복 가능한 부분을 기술(skills)로 패키징하기

정적인 지침(instructions) 파일은 프로젝트에 대해 항상 유효한 사실들을 다룹니다. 반면 기술(skill)은 반복 가능한 워크플로우(workflow)를 다룹니다. 즉, 하나의 작업을 수행하는 방법을 설명하는 작은 마크다운(Markdown) 파일이며, 에이전트(agent)는 사용자가 요청하거나 특정 작업이 적합하다고 판단될 때 이를 실행합니다.

Matt Pocock은 github.com/mattpocock/skills에 TDD, 버그 진단, 토론을 PRD나 이슈(issues)로 변환하는 기능 등 공개된 기술 세트를 유지하고 있습니다. npx skills@latest add mattpocock/skills 명령어로 이를 설치할 수 있습니다.

그중 하나인 handoff는 이 글의 내용과 직접적으로 연결됩니다. 세션의 컨텍스트 윈도우 (context window)가 가득 차고 품질이 저하될 때, 이를 하나의 문서로 압축하여 새로운 에이전트 (agent)가 작업을 이어갈 수 있도록 합니다. 이 기능은 해당 문서를 워크스페이스 (workspace) 대신 운영체제 (OS)의 임시 디렉토리에 작성하며, 다음 에이전트가 호출해야 할 스킬 (skills) 목록을 나열하고, 기존 아티팩트 (artifacts)를 복사하는 대신 경로로 참조하며, 비밀 정보 (secrets)를 삭제(redact)합니다.

이는 한 단계 상위 개념인 지침 파일 (instructions file)과 동일한 방식입니다. 모델이 무언가를 잊어버리려 할 때, 무엇을 유지할지 당신이 결정하는 것입니다.

업무의 중심이 의도(intent)를 소유하는 것으로 이동하다

이러한 요소들을 종합해 보면 업무의 형태가 변했음을 알 수 있습니다. 이제 모델이 타이핑의 상당 부분을 담당하며, 희소해진 것은 모델이 구조적으로 할 수 없는 부분, 즉 소프트웨어가 무엇을 해야 하는지, 그리고 왜 그래야 하는지를 아는 것입니다. 그것은 언제나 엔지니어링 (engineering)의 어려운 부분이었습니다. 이제 그것이 남겨진 업무의 대부분이 되었습니다.

실제로는 세 가지 습관이 필요하며, 이 글에서는 이미 앞의 두 가지를 다루었습니다.

  • 의도 (intent)를 외부화하기: 도구가 읽을 수 있는 파일과 테스트로 의도를 외부화하여, 모델이 당신만이 아는 내용을 추측하는 일을 멈추게 하세요.
  • 유지되어야 할 사항을 강제하기: 작성된 지침만으로는 모델의 준수를 보장할 수 없으므로, 빌드 (build) 시 실행되는 테스트나 타입 (type)을 통해 반드시 지켜져야 할 사항을 강제하세요.
  • 모든 디프 (diff)를 초안으로 읽기: 확신에 찬 디프는 확률적인 주장이지, 정확성에 대한 주장이 아닙니다. 중요한 리뷰는 "이것이 좋은 코드인가"가 아닙니다. 그것은 거의 항상 좋은 코드처럼 보이기 때문입니다. 중요한 것은 "이것이 우리가 실제로 의도한 것인가"이며, 이는 오직 당신만이 답할 수 있습니다. 모델이 결코 전달받지 못한 입력값(input)을 당신만이 가지고 있기 때문입니다.

이러한 도구들로부터 최대한의 성과를 얻는 것은 영리한 프롬프트 (prompts)를 만드는 것보다, 당신의 소프트웨어가 무엇을 위한 것인지에 대한 명확한 그림을 유지하고 그 그림을 모델이 읽을 수 있는 곳에 두는 것에 가깝습니다. 모델은 그럴듯함 (plausible)을 최적화합니다. 그것을 정확하게 유지하는 것은 당신의 일입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0