소프트웨어 3.0 시대를 맞이하며
Blog

소프트웨어 3.0 시대를 맞이하며

Viva Republica
2026.01.26
·Service·by 이호민
#Software 3.0#LLM#Agent#Harness#Architecture

핵심 포인트

  • 1Andrej Karpathy가 제시한 Software 3.0 시대에는 자연어 프롬프트가 프로그램이 되지만, LLM은 혼자서 외부 시스템에 접근하거나 복잡한 작업을 처리할 수 없어 Harness라는 도구와 환경이 필수적입니다.
  • 2Claude Code의 MCP, Skills, Sub-agent, Slash Command와 같은 Harness 구성요소들은 전통적인 레이어드 아키텍처와 유사하며, 특히 에이전트는 Human-in-the-Loop(HITL)를 통해 불확실한 상황에서 사용자에게 질문하여 판단을 위임할 수 있습니다.
  • 3Software 3.0 시대로의 전환에도 불구하고 레이어 분리, SRP(단일 책임 원칙), 추상화 등 기존 소프트웨어 설계 원칙은 여전히 중요하며, 토큰 사용량 관리와 Skill 폭발 방지가 핵심적인 고려사항입니다.

이 문서는 Andrej Karpathy가 제시한 Software 3.0 시대의 도래와 함께, LLM(Large Language Model)의 잠재력을 실질적인 애플리케이션으로 구현하기 위한 'Harness' 개념의 중요성을 설명합니다. 특히 Anthropic의 Claude Code를 Harness의 구체적인 사례로 들며, 기존 Software 1.0의 아키텍처 원칙이 LLM 기반 에이전트 설계에 어떻게 재적용되는지 상세히 분석하고, 새로운 패러다임이 가져오는 변화와 주의할 점을 제시합니다.

1. Software 3.0 시대의 정의와 Harness의 필요성
Karpathy는 소프트웨어의 진화를 세 단계로 나눕니다:

  • Software 1.0: Python, Java, C++ 등으로 명시적인 로직을 작성하는 'How(어떻게)'의 시대입니다.
  • Software 2.0: 딥러닝 부상과 함께 데이터를 기반으로 모델을 학습시켜 신경망의 가중치가 프로그램이 되는 시대입니다.
  • Software 3.0: LLM에 자연어로 'What(무엇을)' 원하는지 말하면 되는 시대로, 프롬프트가 곧 프로그램이 됩니다.

그러나 LLM은 그 자체로는 파일 시스템 접근, API 호출, 데이터베이스 접근 등 실제 업무에 필요한 외부 시스템 연동 능력이 부족합니다. 또한, Context Window 제한, Memory 관리의 어려움, 환각(Hallucination), 도메인 지식 부족, 상태 관리 불가 등의 본질적인 한계를 가집니다. 이러한 LLM의 한계를 보완하고 실제 작업을 수행하는 에이전트로 만들기 위해 필요한 도구와 환경을 'Harness'라고 명명합니다. Harness는 말의 힘을 제어하는 마구(馬具)처럼 LLM의 강력한 능력을 효과적으로 활용하게 해줍니다.

2. Claude Code: LLM을 위한 Harness의 실제 구현
Claude Code는 Anthropic이 개발한 CLI 기반 코딩 에이전트로, Claude 모델을 위한 대표적인 Harness입니다. Claude Code가 제공하는 기능들은 다음과 같습니다:

  • 파일 시스템 접근: LLM이 코드를 읽고 쓸 수 있게 합니다.
  • 터미널 실행: LLM이 외부 명령어를 실행할 수 있게 합니다.
  • MCP (Model Context Protocol): 외부 시스템(API, DB 등)과의 연결을 추상화하여 LLM이 외부 자원을 활용할 수 있도록 합니다.
  • Sub-agent: 복잡한 작업을 분할 처리할 수 있도록 논리적인 작업 단위를 제공합니다.
  • Slash Command: 사용자 의도를 특정 워크플로우로 라우팅하는 진입점 역할을 합니다 (예: /review, /refactor).
  • Skills: 단일 책임 원칙(SRP)에 따라 재사용 가능한 기능 단위로, 특정 작업을 수행합니다 (예: "코드 리뷰하기", "테스트 생성하기").
  • Hooks: 특정 이벤트 발생 시 자동화를 트리거합니다.

이러한 기능들은 Claude라는 LLM 엔진을 실제 작업을 수행하는 지능형 에이전트로 전환시키는 Harness의 역할을 합니다.

3. Software 1.0 아키텍처 관점에서 Harness 이해하기
Harness의 구성 요소들은 Software 1.0의 전통적인 레이어드 아키텍처 개념과 유사합니다.

  • Slash Command = Controller: 사용자 요청의 진입점으로, 특정 워크플로우를 트리거합니다.
  • Sub-agent = Service Layer: 여러 Skill을 조합하여 복잡한 워크플로우를 완성하며, 각각 독립적인 Context를 가집니다.
  • Skills = Domain Component (SRP): "코드 리뷰하기", "테스트 생성하기"처럼 단일하고 명확한 역할을 담당하며, SRP 원칙을 따릅니다.
  • MCP = Infrastructure / Adapter: 외부 시스템(DB, API, 파일 시스템)과의 연동을 담당하며, Repository Pattern이나 Adapter Pattern처럼 내부 로직과 외부 구현 간의 추상화를 제공합니다.
  • CLAUDE.md = package.json / pom.xml: 프로젝트의 기술 스택, 코딩 컨벤션, 빌드 명령어 등 잘 변하지 않는 원칙을 정의합니다.

4. 전통적인 안티패턴과 코드 스멜의 재적용
Software 1.0에서 발생했던 안티패턴과 코드 스멜이 에이전트 설계에서도 유사하게 나타납니다.

  • God Class → God Skill: 하나의 Skill이 모든 기능을 처리하는 거대하고 비대한 형태.
  • Spaghetti Code → Spaghetti CLAUDE.md: 구조 없이 모든 지시사항이 뒤섞여 혼란스러운 상태.
  • Tight Coupling → MCP 없는 하드코딩: 외부 curl 호출처럼 직접적인 의존성으로 인해 변경에 취약한 구조.
  • Leaky Abstraction → Sub-agent가 MCP 내부 구현을 인지: 추상화 경계가 무너져 재사용이 어렵고 복잡도가 증가하는 문제.
  • Circular Dependency → Skill 간 순환 호출: A→B→C→A와 같은 순환 참조로 무한 루프 위험.
  • 코드 스멜: Feature Envy (Skill이 다른 Skill의 데이터 과도 참조), Duplication (비슷한 프롬프트 중복), Long Method (Sub-agent가 다수의 Skill을 연속 호출).

5. 결정적인 차이: Human-in-the-Loop (HITL)
전통적인 아키텍처에서는 모든 분기가 미리 정의되어야 하며, 불확실한 상황에서는 예외를 던지거나 개발자가 임의로 결정해야 합니다. 그러나 에이전트는 HITL을 통해 작업 중간에 사용자에게 질문하고 판단을 위임할 수 있습니다. 즉, Exception이 Question으로 바뀌는 것입니다.

  • 언제 질문할 것인가: 되돌리기 어려운 작업, 여러 선택지가 있는 경우, 비용/리스크가 큰 결정.
  • 언제 알아서 할 것인가: 안전하게 반복 가능한 작업, 합의된 컨벤션이 있는 경우, 되돌리기 쉬운 작업.
좋은 에이전트는 "언제 질문할지"를 아는 에이전트입니다.

6. Software 1.0 개발자가 3.0으로 가는 길
Software 3.0 시대에도 기존의 엔지니어링 원칙은 여전히 중요합니다.

  • 버릴 것: 모든 로직을 명시적으로 작성해야 한다는 강박, 모든 예외 상황을 미리 정의하려는 시도, LLM을 단순히 '똑똑한 자동 완성'으로만 보는 시각.
  • 가져갈 것: 레이어 분리, 단일 책임 원칙(SRP), 추상화, 의존성 관리, 인터페이스 설계, 테스트 가능성, 디버깅 전략, 코드 리뷰, 점진적 개선.
결과적으로 도구는 바뀌었지만, 좋은 설계의 원칙(응집도, 결합도, 추상화)은 변하지 않습니다.

7. 비유가 숨기는 것들: 실제 적용 시 주의할 점
레이어드 아키텍처 비유는 이해를 돕지만, LLM 에이전트만의 고유한 특성을 숨길 수 있습니다.

  • 토큰은 메모리다: 전통적인 서버에서 RAM을 걱정하듯, 에이전트에서는 '토큰'을 걱정해야 합니다. Context Window는 작업 메모리이며, 토큰 사용량은 메모리 점유율에 해당합니다. CLAUDE.md, Skills, 대화 히스토리, MCP 응답 등 모든 것이 Context Window에 쌓이므로, OOM(Out Of Memory)처럼 토큰 폭발을 예방해야 합니다. 결정적 로직은 scripts로 분리하여 LLM의 토큰 소모를 줄이는 것이 좋습니다.
  • Skill 분리의 딜레마: 클래스 폭발과 디미터의 법칙: SRP를 맹목적으로 따르다 보면 클래스 폭발처럼 Skill이 난립할 수 있습니다. Claude는 시작 시 모든 Skill의 메타데이터를 로드하므로, Skill이 많으면 Context를 과도하게 점유합니다. 디미터의 법칙(Law of Demeter)을 적용하여 SKILL.md는 진입점만 제공하고, 세부 지식은 references/에 위임하는 Facade 패턴과 유사한 구조를 사용하는 것이 효율적입니다. references/ 안의 파일들은 Claude가 필요하다고 판단할 때만 Context에 로드됩니다.

8. 실전 팁: Setup & Config 패턴
Slash Command를 활용하면 HITL과 자동화를 자연스럽게 조합할 수 있습니다. /setup과 같은 명령어로 에이전트가 환경을 자동 감지하되, 애매한 부분은 질문하여 사용자의 판단을 요청하는 방식으로 초기 설정을 진행하는 것이 효과적입니다.

결론적으로, Software 3.0 시대의 개발은 코드를 직접 작성하는 것에서 코드를 조립하고 지시하는 패러다임으로 변화하고 있습니다. 그러나 이러한 '조립'의 원칙은 기존의 엔지니어링 개념과 크게 다르지 않으며, 애플리케이션이 사용자에게 질문할 수 있는 새로운 가능성을 열었다는 점이 핵심적인 변화입니다.