<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>오늘도 공부</title>
    <link>https://javaexpert.tistory.com/</link>
    <description>AI,Flutter,Node등 다양한 분야를 연구하는 블로그입니다</description>
    <language>ko</language>
    <pubDate>Fri, 28 Aug 2026 12:57:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>행복한 수지아빠</managingEditor>
    <image>
      <title>오늘도 공부</title>
      <url>https://tistory1.daumcdn.net/tistory/649446/attach/3ffb00b63198428d8082dda2b9aef56d</url>
      <link>https://javaexpert.tistory.com</link>
    </image>
    <item>
      <title>RAG 다음 단계: LLM Wiki</title>
      <link>https://javaexpert.tistory.com/1821</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LLM을 개인 지식관리나 리서치에 활용할 때 가장 흔한 구조는 RAG(Retrieval-Augmented Generation)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서들을 저장해 두고, 사용자가 질문하면 관련 문서 조각을 검색한 뒤 LLM에게 전달한다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;Documents
   &amp;darr;
Chunking
   &amp;darr;
Embedding / Index
   &amp;darr;
Retrieval
   &amp;darr;
LLM
   &amp;darr;
Answer
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 한 가지 구조적인 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;매번 지식을 다시 조립해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100개의 문서를 읽고 5개의 자료를 연결해 어떤 결론을 만들었다고 하더라도, 다음 질문에서는 다시 검색하고 다시 조합해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 입장에서는 이전에 수행했던 분석이 하나의 지식 구조로 축적되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Karpathy가 공개한 LLM Wiki 아이디어는 바로 이 문제를 다르게 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 단순하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 문서를 검색하는 것에 그치지 않고, LLM이 지속적으로 관리하는 Markdown Wiki를 중간 계층에 둔다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Raw Sources
      &amp;darr;
     LLM
      &amp;darr;
Persistent Wiki
      &amp;darr;
     Query
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Wiki는 단순한 요약 저장소가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 자료가 들어올 때마다 기존 지식과 비교하고, 관련 문서를 업데이트하고, 모순을 기록하고, 새로운 연결을 만들어내는 &lt;b&gt;지속적으로 진화하는 지식베이스&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. RAG와 LLM Wiki의 가장 큰 차이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 RAG에서는 원본 자료가 지식의 중심이다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;질문
 &amp;darr;
원본 문서 검색
 &amp;darr;
관련 Chunk 추출
 &amp;darr;
LLM이 즉석에서 종합
 &amp;darr;
답변
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI Agent에 대해 100개의 글을 저장했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 다음 질문을 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP와 Agent Skill은 어떤 관계인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 시스템에서는 매번 MCP와 Agent Skill 관련 문서를 검색하고 LLM이 새롭게 관계를 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 LLM Wiki에서는 이미 다음과 같은 페이지가 존재할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;wiki/
├── ai-agent.md
├── mcp.md
├── agent-skill.md
├── tool-use.md
├── context-engineering.md
└── agent-architecture.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각 문서는 서로 연결된다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# MCP

MCP는 LLM Agent가 외부 도구와 데이터를
표준화된 방식으로 연결하기 위한 프로토콜이다.

관련 개념:

- [Agent Skill](agent-skill.md)
- [Tool Use](tool-use.md)
- [Context Engineering](context-engineering.md)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 MCP 관련 글이 들어오면 LLM은 단순히 source-103.md를 추가하는 것으로 끝내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;mcp.md
agent-skill.md
tool-use.md
agent-architecture.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등을 함께 수정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 지식이 &lt;b&gt;누적된다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 이 구조는 사실상 &amp;lsquo;지식 컴파일&amp;rsquo;에 가깝다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 개발 관점에서 보면 재미있는 비유가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Raw Source를 소스 코드라고 생각해보자.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Raw Sources
   =
Source Code
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 컴파일러 역할을 한다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;LLM
 =
Compiler
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Wiki는 컴파일된 지식이다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;Wiki
 =
Compiled Knowledge
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Articles
Papers
Videos
Meetings
Documents
     │
     ▼
┌───────────────┐
│      LLM      │
│ Knowledge     │
│ Compiler      │
└───────────────┘
     │
     ▼
┌────────────────────┐
│ Persistent Wiki    │
│                    │
│ Concepts           │
│ Entities           │
│ Comparisons        │
│ Relationships      │
│ Synthesis          │
└────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG가 필요할 때마다 지식을 해석하는 &lt;b&gt;인터프리터형 접근&lt;/b&gt;이라면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM Wiki는 미리 지식을 구조화하는 &lt;b&gt;컴파일형 접근&lt;/b&gt;이라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 실제 컴파일러와 동일한 개념은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 시스템 설계 관점에서는 꽤 유용한 모델이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 시스템은 세 개의 계층으로 구성된다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM Wiki의 기본 구조에는 세 가지 핵심 레이어가 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① Raw Sources&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 수집한 원본 자료다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;raw/
├── article-openai-agents.md
├── anthropic-agent-skills.md
├── mcp-spec.md
├── paper-context-engineering.pdf
└── assets/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 중요한 원칙이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LLM은 Raw Source를 수정하지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 자료는 Source of Truth 역할을 한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;raw = immutable
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 요약이나 해석이 생겨도 원본을 다시 확인할 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 두 번째 계층은 LLM이 관리하는 Wiki다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wiki는 사람이 직접 작성하는 공간이라기보다는 &lt;b&gt;LLM이 유지보수하는 코드베이스&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 구조가 만들어질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;wiki/
├── README.md
│
├── concepts/
│   ├── ai-agent.md
│   ├── context-engineering.md
│   ├── rag.md
│   └── agent-memory.md
│
├── technologies/
│   ├── mcp.md
│   ├── agent-skills.md
│   └── vector-database.md
│
├── companies/
│   ├── openai.md
│   ├── anthropic.md
│   └── google.md
│
└── comparisons/
    └── rag-vs-llm-wiki.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM이 담당하는 작업은 상당히 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 자료를 읽으면 다음 작업을 수행한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;요약
&amp;darr;
개념 추출
&amp;darr;
관련 페이지 탐색
&amp;darr;
기존 주장과 비교
&amp;darr;
페이지 수정
&amp;darr;
Cross-reference 생성
&amp;darr;
README 업데이트
&amp;darr;
Git commit
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 자료가 들어오면서 10~15개의 문서가 동시에 수정될 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 개인 Wiki에서는 이런 유지보수가 가장 큰 부담이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM Wiki에서는 이 유지보수 자체를 Agent에게 맡긴다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 세 번째 계층: AGENTS.md&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 가장 중요한 파일은 Wiki가 아닐 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent에게 지식베이스 관리 규칙을 알려주는 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex라면:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;AGENTS.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code라면:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태로 둘 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 일종의 &lt;b&gt;Knowledge Base Constitution&lt;/b&gt; 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 규칙을 정의할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# Wiki Rules

## Source Rules

- raw/ 파일은 절대 수정하지 않는다.
- 모든 중요한 주장에는 Source를 연결한다.
- Source 없는 추론은 명시적으로 표시한다.

## Page Rules

각 페이지는 다음 구조를 사용한다.

1. Summary
2. Key Concepts
3. Relationships
4. Evidence
5. Open Questions
6. Sources

## Link Rules

Wikilink를 사용하지 않는다.

잘못된 예:

[[MCP]]

올바른 예:

[MCP](mcp.md)

## Update Rules

새로운 Source가 기존 주장과 충돌하면
기존 내용을 삭제하지 말고 contradiction을 기록한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일의 품질에 따라 Wiki의 품질도 크게 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 LLM Wiki 구축에서 중요한 것은 단순한 Prompt Engineering보다 &lt;b&gt;Knowledge Governance&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 가장 중요한 Operation: Ingest&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 자료가 들어오면 Agent는 단순 요약보다 더 많은 작업을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 자료가 추가됐다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;raw/anthropic-agent-skills-2026.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 수행하는 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Source 읽기
     &amp;darr;
핵심 주장 추출
     &amp;darr;
기존 Wiki 검색
     &amp;darr;
관련 개념 찾기
     &amp;darr;
기존 주장과 비교
     &amp;darr;
새 페이지 생성 또는 기존 페이지 수정
     &amp;darr;
Cross-reference 업데이트
     &amp;darr;
README 업데이트
     &amp;darr;
Git Commit
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git commit은 예를 들어 다음과 같이 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;ingest: Anthropic Agent Skills architecture

- added agent-skills.md
- updated ai-agent.md
- updated mcp.md
- added skill-vs-tool comparison
- linked Anthropic architecture sources
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Commit 자체가 지식베이스의 변경 기록이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. log.md보다 Git History&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 아이디어에서는 Wiki 변경 기록을&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;log.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 저장하는 방법이 제안됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이후 버전에서는 Git 자체를 로그 시스템으로 사용하는 쪽으로 개선됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 명확하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Agent가 동시에 작업하면 append-only 파일은 충돌하기 쉽다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Agent A &amp;rarr; log.md 마지막 줄 수정
Agent B &amp;rarr; log.md 마지막 줄 수정

&amp;rarr; Merge Conflict
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git은 이미 변경 이력을 관리하기 위한 시스템이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;git log --oneline -- wiki/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만 실행해도 Wiki의 전체 변화 과정을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 페이지 변화는&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;git log --follow -p wiki/mcp.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 추적할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설계는 꽤 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 지식 History 시스템을 만드는 대신 &lt;b&gt;Git 자체를 Temporal Memory로 활용한다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. README.md는 Wiki의 Router 역할을 한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wiki가 커지면 Agent가 매번 모든 Markdown 파일을 읽을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Root에&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# AI Engineering Wiki

## Agent Architecture

- [AI Agent](concepts/ai-agent.md)
  Agent architecture overview

- [Agent Memory](concepts/agent-memory.md)
  Persistent and temporary memory mechanisms

## Protocols

- [MCP](technologies/mcp.md)
  Model Context Protocol

## Retrieval

- [RAG](concepts/rag.md)
  Retrieval Augmented Generation
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문이 들어오면 Agent는 먼저 README를 읽는다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Question
   &amp;darr;
README
   &amp;darr;
Relevant Pages
   &amp;darr;
Sources
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 수백 페이지 정도까지는 Vector DB 없이도 꽤 효율적인 탐색이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;README는 사실상 &lt;b&gt;LLM용 Knowledge Router&lt;/b&gt; 역할을 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Wiki가 커지면 계층 구조를 만든다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 복잡한 디렉터리를 만드는 것은 권장되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 다음처럼 Flat 구조로 시작한다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;wiki/
├── README.md
├── mcp.md
├── rag.md
├── agent.md
├── context-engineering.md
└── agent-memory.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지가 늘어나면서 자연스럽게 Cluster가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;agent.md
agent-memory.md
agent-tools.md
agent-planning.md
agent-evaluation.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시점에 Agent가 다음 구조를 제안할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;wiki/
└── agents/
    ├── README.md
    ├── architecture.md
    ├── memory.md
    ├── tools.md
    ├── planning.md
    └── evaluation.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Wiki 구조 변경 자체도 Agent가 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업을 원문에서는 Codebase의 &lt;b&gt;Refactoring / Compaction&lt;/b&gt;과 유사하게 본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. Wiki에도 기술 부채가 생긴다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지식베이스 역시 시간이 지나면 기술 부채와 비슷한 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;중복 페이지
오래된 주장
서로 충돌하는 설명
고립된 페이지
너무 긴 문서
잘못된 분류
깨진 링크
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 LLM Wiki에서는 두 가지 유지보수 작업이 중요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lint&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 탐지한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;contradiction
stale claim
orphan page
missing link
missing concept
source gap
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Compact&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 실제로 수정한다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;merge
rewrite
split
delete
move
restructure
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발에 대응시키면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Wiki Lint
&amp;asymp;
Static Analysis
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;Wiki Compact
&amp;asymp;
Refactoring
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상당히 개발자 친화적인 Knowledge Management 모델이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 질문 역시 지식이 된다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시스템의 중요한 특징 중 하나는 Query 결과도 Wiki에 다시 저장할 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 묻는다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP와 Agent Skill 중 어느 것을 먼저 도입해야 하는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 기존 자료를 분석해 비교 문서를 만든다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;comparisons/
└── mcp-vs-agent-skills.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 질문에서는 이 분석을 다시 처음부터 하지 않아도 된다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;Source
   &amp;darr;
Wiki

Question
   &amp;darr;
Analysis
   &amp;darr;
Wiki

Question
   &amp;darr;
Analysis
   &amp;darr;
Wiki
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Source뿐 아니라 &lt;b&gt;사고 과정에서 만들어낸 분석 결과 자체도 Knowledge Asset으로 축적된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 일반적인 Chat History와 가장 큰 차이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 검색은 처음부터 Vector DB가 필요하지 않다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자는 지식베이스라고 하면 바로 다음 스택을 떠올린다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Embedding
Vector DB
Hybrid Search
Reranker
RAG Pipeline
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 LLM Wiki에서는 처음부터 이런 인프라가 필요하지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;README
grep
ripgrep
find
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정도로 충분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;rg &quot;MCP&quot; wiki/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 Markdown 전용 Local Search Tool을 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문에서는 qmd 같은 도구가 예로 언급된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wiki가 커지면 다음 단계로 발전시킬 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Stage 1

README + grep


Stage 2

BM25 search


Stage 3

BM25 + Vector Search


Stage 4

Hybrid Search + Reranker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 검색 인프라를 처음부터 과도하게 구축할 필요가 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. Git + Markdown이 중요한 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 Markdown과 Git은 우연한 선택이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Markdown은:&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;LLM이 읽기 쉽다
LLM이 수정하기 쉽다
diff 확인이 쉽다
Git과 궁합이 좋다
Obsidian/GitHub에서 바로 렌더링된다
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git은:&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Versioning
Diff
Rollback
Branch
Merge
History
Collaboration
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 별도의 Knowledge Management Database가 없어도 상당히 강력한 시스템을 만들 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. Obsidian의 역할&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 Obsidian은 지식을 만드는 시스템이라기보다 &lt;b&gt;Knowledge IDE&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문의 표현을 개발자 관점으로 정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;Obsidian
=
IDE

LLM Agent
=
Programmer

Wiki
=
Codebase

Raw Sources
=
Input Data

AGENTS.md
=
Coding Rules

Git
=
Version Control
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Obsidian Graph View를 이용하면&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;Concept
 &amp;harr;
Entity
 &amp;harr;
Source
 &amp;harr;
Analysis
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계를 시각적으로 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. 실제로 구현한다면&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 단순한 MVP는 다음 정도로 충분하다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;knowledge/
│
├── AGENTS.md
│
├── raw/
│   ├── source-001.md
│   ├── source-002.md
│   └── assets/
│
└── wiki/
    ├── README.md
    ├── mcp.md
    ├── rag.md
    └── agent-memory.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent에게 다음 네 가지 Command만 정의해도 된다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/ingest
/query
/lint
/compact
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;/ingest&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;새 Source 분석
&amp;rarr; Wiki 업데이트
&amp;rarr; Cross-reference
&amp;rarr; README 수정
&amp;rarr; Commit
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;/query&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;README 탐색
&amp;rarr; 관련 Wiki 탐색
&amp;rarr; 필요 시 Raw Source 확인
&amp;rarr; 답변 생성
&amp;rarr; 가치 있는 분석이면 Wiki 저장
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;/lint&lt;/h3&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;중복
모순
오래된 주장
고립 페이지
깨진 링크
Source 부족
검사
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;/compact&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;중복 페이지 Merge
긴 페이지 Split
Directory 재구성
Stale 정보 제거
Cross-reference 수정
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도만으로도 상당히 강력한 개인 지식 시스템을 만들 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. 더 발전시키면 Agentic Knowledge Pipeline이 된다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 자동화하면 다음 형태까지 발전할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Web
RSS
GitHub
YouTube
Papers
Slack
Meetings
   │
   ▼
Source Collector
   │
   ▼
raw/
   │
   ▼
Ingest Agent
   │
   ├─ Entity Extraction
   ├─ Concept Extraction
   ├─ Contradiction Detection
   ├─ Relationship Discovery
   └─ Source Verification
   │
   ▼
Wiki
   │
   ├─ README
   ├─ Concepts
   ├─ Entities
   ├─ Comparisons
   └─ Synthesis
   │
   ▼
Git
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 Scheduler를 붙이면 주기적으로&lt;/p&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;lint
compact
link check
stale claim check
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 단순한 Wiki가 아니라 &lt;b&gt;Agent가 유지보수하는 지식 운영 시스템&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. RAG를 없애자는 아이디어는 아니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점이 하나 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM Wiki와 RAG는 경쟁 관계일 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 두 구조를 조합하는 것이 자연스럽다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;               ┌─ Wiki Search
Question ──────┤
               └─ Raw Source RAG
                       │
                       ▼
                      LLM
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wiki에는 이미 정리된 지식이 들어 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Raw Source RAG는 다음 용도로 사용한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;원문 검증
정확한 인용
세부 데이터 확인
새로운 정보 탐색
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 현실적인 구조는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Wiki + RAG&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 될 가능성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wiki가 Semantic Memory라면 Raw Source는 Evidence Store에 가까운 역할을 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;18. 이 아이디어의 진짜 핵심&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표면적으로 보면 이 시스템은 단순하다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;Markdown
+
Git
+
LLM
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 중요한 것은 저장 기술이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 RAG에서는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Knowledge = Documents
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM Wiki에서는&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;Knowledge =
Documents
+
Relationships
+
Synthesis
+
History
+
Questions
+
Analysis
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 지식의 단위를 문서에서 &lt;b&gt;지속적으로 업데이트되는 지식 구조&lt;/b&gt;로 바꾸는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 시스템의 핵심은 Wiki 자체라기보다 다음 개념에 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LLM이 지식을 소비하는 Agent에서 지식을 지속적으로 유지보수하는 Agent로 바뀐다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 시대의 개인 지식관리 시스템을 단순히&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;문서 저장
&amp;rarr; Embedding
&amp;rarr; Vector Search
&amp;rarr; 답변
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로만 생각할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 단계 더 나아가면&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Source
   &amp;darr;
LLM
   &amp;darr;
Structured Knowledge
   &amp;darr;
Continuous Maintenance
   &amp;darr;
Git History
   &amp;darr;
Accumulated Intelligence
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Codex, Claude Code 같은 Coding Agent가 등장하면서 이 방식은 구현하기 훨씬 쉬워졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Markdown Wiki는 일종의 코드베이스가 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM Agent는 그 코드베이스를 관리하는 개발자가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 인간은 직접 Wiki를 정리하는 대신,&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;좋은 자료를 찾고
좋은 질문을 하고
중요한 방향을 결정하는 것
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 집중한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이 아이디어의 가장 흥미로운 지점은 &lt;b&gt;LLM에게 더 많은 문서를 검색하게 하는 것이 아니라, 한 번 이해한 지식을 다시 잃어버리지 않게 만드는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG가 LLM에게 외부 기억을 제공했다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM Wiki는 그 기억을 &lt;b&gt;시간이 지날수록 구조화하고 축적하는 방법&lt;/b&gt;에 더 가깝다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1821</guid>
      <comments>https://javaexpert.tistory.com/1821#entry1821comment</comments>
      <pubDate>Thu, 27 Aug 2026 16:04:30 +0900</pubDate>
    </item>
    <item>
      <title>Grok Bot과 Kimi K3로 배우는 Graph &amp;amp; Loop Engineering</title>
      <link>https://javaexpert.tistory.com/1820</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트 시스템의 문제는 모델 성능만으로 해결되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영 환경에서 더 자주 문제가 되는 것은 다음 세 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업이 실패했을 때 &lt;b&gt;누가 다시 시작할 것인가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;여러 에이전트가 사용하는 &lt;b&gt;공유 상태를 어디에 저장할 것인가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;실제 외부 시스템을 변경하기 전에 &lt;b&gt;누가 승인할 것인가&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트를 여러 개 붙여 놓는다고 안정적인 시스템이 만들어지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 것은 &lt;b&gt;에이전트 군집(Swarm)&lt;/b&gt; 자체가 아니라, 에이전트가 어떤 작업을 수행해야 하는지 판단하고 결과를 검증하며 다시 작업으로 되돌리는 &lt;b&gt;제어 시스템(Control System)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 다음과 같은 구조를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Grok Bot
   │
   │ Outer Loop
   ▼
Coordinator
   │
   ├── Knowledge Graph
   ├── DAG
   ├── Context Pack
   ├── Policy / Approval
   └── Append Log
            │
            ▼
        Kimi Code
            │
            ▼
            K3
       Agent / Swarm
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 역할은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Grok Bot&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 실행 흐름인 Outer Loop를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Kimi Code + K3&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번의 제한된 작업 Round에서 분석, 코딩, 조사 같은 깊은 작업을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Knowledge Graph&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처를 가진 장기 지식을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DAG&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 작업을 어떤 순서로 실행할지 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Context Pack&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 에이전트에게 전달되는 컨텍스트를 제한한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Policy Layer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구와 외부 작업에 대한 권한을 통제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Append Log&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 상태 전환과 실행 이력을 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 이해하면 단순한 AI Agent를 넘어 &lt;b&gt;장기간 반복해서 운영할 수 있는 Agent Harness&lt;/b&gt;를 설계할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. Loop Engineering&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&quot;계속해&quot;는 Loop가 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM에게 다음과 같이 지시한다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;문제가 해결될 때까지 계속 작업해.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 제대로 된 Loop가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신뢰할 수 있는 Loop에는 최소한 네 가지 요소가 필요하다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;Owner
Worker
Verifier
Stop Rule
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 구조다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Coordinator
     │
     ▼
 Worker
     │
     ▼
Verifier
     │
 ┌───┴────┐
PASS    REVISE
 │         │
END        └──── Worker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Coordinator는 전체 작업을 소유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker는 하나의 후보 결과를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Verifier는 미리 정의된 Acceptance Test를 검사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패하면 전체 작업을 처음부터 다시 설명하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Verifier가 실패시킨 조건만 Worker에게 다시 전달한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 가장 단순한 3-Agent Loop&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 수십 개의 Agent를 만들 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 세 개부터 시작하는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Coordinator
Worker
Verifier
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Coordinator의 기본 동작은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. source event로부터 stable job_id 생성

2. Worker에게 candidate 생성 요청

3. Verifier가
   PASS
   REVISE
   BLOCKED
   중 하나를 반환

4. REVISE이면
   실패한 테스트만 Worker에게 전달

5. 최대 3 Round 실행

6. 동일한 결과가 반복되면 중단

7. 필요한 Source가 없거나 오래됐으면 중단

8. 외부 작업은 Draft 상태 유지

9. 실제 실행 전
   target + payload를 사용자에게 표시

10. 모든 handoff를 append log에 기록
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 매우 중요한 개념이 하나 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Round와 Retry는 다르다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API timeout이 발생했다고 새로운 Round가 되어서는 안 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Round 1
   │
   ├─ attempt 1 &amp;rarr; timeout
   ├─ attempt 2 &amp;rarr; rate limit
   └─ attempt 3 &amp;rarr; success
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 여전히 &lt;b&gt;Round 1&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 Round는 Verifier가 새로운 정보를 생성했을 때만 시작한다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Round 1
Worker
 &amp;darr;
Verifier
 &amp;darr;
Failed Check

Round 2
Worker
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Kimi K3를 Inner Worker로 사용하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 시스템의 제어권을 Kimi에게 주는 방식보다는 Kimi가 &lt;b&gt;한 번의 제한된 작업만 수행하도록 만드는 것&lt;/b&gt;이 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Grok Coordinator

     &amp;darr; task.json

Kimi K3
&quot;한 번만 작업&quot;

     &amp;darr; candidate.json

Grok Verifier
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kimi Worker는 다음 정도의 책임만 가진다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;입력 읽기
&amp;darr;
분석
&amp;darr;
필요한 경우 Sub-Agent 사용
&amp;darr;
Candidate 생성
&amp;darr;
종료
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 시스템 변경은 하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Worker Output을 Contract로 만든다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent의 결과를 자연어만으로 전달하면 자동화하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Output Contract를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;job_id&quot;: &quot;demo-001&quot;,
  &quot;round&quot;: 1,
  &quot;status&quot;: &quot;CANDIDATE&quot;,
  &quot;summary&quot;: &quot;...&quot;,
  &quot;evidence&quot;: [],
  &quot;failed_checks&quot;: [],
  &quot;next_step&quot;: &quot;...&quot;,
  &quot;requires_human_approval&quot;: false
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 결과를 받은 이후다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bridge가 반드시 다음 작업을 수행한다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;LLM Output
    &amp;darr;
JSON Parse
    &amp;darr;
Schema Validation
    &amp;darr;
Canonical JSON
    &amp;darr;
SHA-256
    &amp;darr;
candidate.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM이 직접 fingerprint를 계산하게 하지 않는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템이 동일한 방식으로 계산해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. Output Fingerprint&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent Loop에서 자주 발생하는 문제 중 하나가 &lt;b&gt;같은 답을 조금씩 표현만 바꾸며 반복하는 현상&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 감지하기 위해 결과를 fingerprint한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 데이터가 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;status&quot;: &quot;CANDIDATE&quot;,
  &quot;summary&quot;: &quot;...&quot;,
  &quot;evidence&quot;: [],
  &quot;failed_checks&quot;: []
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 semantic payload를 canonical JSON으로 만든다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;canonical JSON
      &amp;darr;
SHA-256
      &amp;darr;
output_fingerprint
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이전 Round와 비교한다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;Round 1
fingerprint = abc123

Round 2
fingerprint = abc123
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일하다면:&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;NO_PROGRESS
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 중단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 무한 Loop를 막는 중요한 Stop Rule이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. Idempotency&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화 Agent가 가장 위험한 순간은 외부 작업을 수행할 때다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;이메일 발송
결제
SNS 게시
파일 삭제
Production Deploy
계정 권한 변경
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 timeout이 발생하면 Agent는 다음과 같은 상황을 알 수 없다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;요청 실패
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인지&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;실제로는 성공했지만
응답만 받지 못함
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인지 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Idempotency Key가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Round용 Key:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;job_id + round + input_sha256
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 외부 작업용 Key:&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;job_id + &quot;:commit&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;job_483:commit
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. External Effect의 안전한 실행&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 외부 작업은 다음 순서로 처리한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Approval
   &amp;darr;
Claim idempotency key
   &amp;darr;
Execute
   &amp;darr;
Verify Live State
   &amp;darr;
Store Receipt
   &amp;darr;
Completion Marker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료 여부를 먼저 기록하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 실제 시스템 상태를 확인한 다음 기록해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이메일이라면:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;send()
 &amp;darr;
provider receipt 확인
 &amp;darr;
message ID 저장
 &amp;darr;
completed
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 자동 Loop Controller&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 흐름을 의사 코드로 만들면 다음과 비슷하다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;job_id = stable_id(source_event)

if completion_marker exists:
    return DUPLICATE

effect = read_effect(job_id + &quot;:commit&quot;)

if effect == CLAIMED:
    reconcile live state

for round in 1..3:

    write task.json

    run Kimi

    validate result

    fingerprint =
        sha256(canonical_payload)

    append event log

    verdict =
        Grok verifier(result)

    if verdict == PASS:

        if external effect 없음:
            complete
            stop

        else:
            request approval

            claim idempotency key

            execute

            verify live state

            save receipt

            complete

            stop

    if verdict == BLOCKED:
        stop

    if fingerprint ==
       previous_fingerprint:

        stop NO_PROGRESS

    send failed checks
    to next round

stop MAX_ROUNDS
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 Loop Engineering의 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Graph Engineering&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 구분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent 시스템에서 흔히 &quot;Graph&quot;라고 부르는 것은 사실 두 종류다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;Knowledge Graph
Execution Graph
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘은 완전히 다른 역할을 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. Knowledge Graph&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Graph는 시스템이 장기간 기억해야 할 사실을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;Kimi Code
   │
   ├── supports &amp;rarr; Agent
   │
   ├── supports &amp;rarr; AgentSwarm
   │
   └── uses &amp;rarr; K3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 중요한 것은 관계 자체가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Claim에 &lt;b&gt;Source가 연결되어 있어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;Claim
   │
   └── source_id
          │
          ▼
       Source
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: &quot;claim:kimi-swarm&quot;,
  &quot;kind&quot;: &quot;claim&quot;,
  &quot;label&quot;: &quot;Kimi Code includes AgentSwarm&quot;,
  &quot;source_ids&quot;: [
    &quot;src:kimi-tools&quot;
  ],
  &quot;status&quot;: &quot;verified&quot;,
  &quot;rev&quot;: 1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. Source Provenance&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source도 별도의 Entity로 관리한다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: &quot;src:kimi-tools&quot;,
  &quot;url&quot;: &quot;...&quot;,
  &quot;retrieved_at&quot;:
    &quot;2026-08-26T00:00:00Z&quot;,
  &quot;sha256&quot;: &quot;&amp;lt;content-hash&amp;gt;&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 단순히&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;Agent가 그렇게 말했다&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 아니라&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;Claim
 &amp;darr;
Source
 &amp;darr;
Source Hash
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조가 만들어진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. Knowledge Graph 최소 Schema&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실전에서는 다음 필드 정도면 충분하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;id
kind
type
source_ids
status
rev
confidence
observed_at
valid_from
valid_to
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;status는 다음과 같이 운영할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;proposed
verified
rejected
superseded
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 LLM이 추론한 관계라면:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;confidence
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 별도로 기록하는 것이 좋다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 그래프는 처음부터 Neo4j일 필요가 없다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVP에서는 오히려 JSONL이 편하다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;graphops/

├── kg/
│   ├── schema.json
│   ├── nodes.jsonl
│   ├── edges.jsonl
│   ├── sources.jsonl
│   ├── events.jsonl
│   ├── inbox/
│   └── snapshots/
│
├── dag/
│   └── pipeline.yaml
│
├── runs/
└── artifacts/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSONL의 장점은 명확하다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;inspect
append
diff
hash
backup
Git
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 쉽다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. Single Writer 원칙&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Agent가 동시에 Knowledge Graph를 수정하면 충돌하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 구조가 좋다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Research Agent ─┐
Analysis Agent ─┼─&amp;rarr; kg/inbox
Crawler Agent ──┘

                    &amp;darr;

            Knowledge Steward

                    &amp;darr;

             authoritative KG
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent는 직접 Graph를 수정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 Patch를 제출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 반영은 &lt;b&gt;Knowledge Steward 하나만 수행한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 Single Writer Pattern이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. Obsidian을 Human View로 사용할 수도 있다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSONL을 Source of Truth로 유지하면서 사람이 보기 좋은 형태를 별도로 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;Knowledge Graph
      │
      ├─ nodes.jsonl
      ├─ edges.jsonl
      │
      └─ Markdown Generator
              &amp;darr;
          Obsidian Vault
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 Node를 하나의 Markdown 파일로 만든다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;---
id: product:kimi-code
kind: product
status: verified
---

# Kimi Code

## Relations

- [[Agent]]
- [[AgentSwarm]]
- [[K3]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 사용하는 데이터와 사람이 읽는 UI를 분리하는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. DAG는 Execution Graph다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Graph와 DAG를 혼동하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Graph:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;무엇을 알고 있는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;무엇을 먼저 실행해야 하는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;observe
   &amp;darr;
 plan
 ┌─┴─────┐
 &amp;darr;       &amp;darr;
research compare
 └───┬───┘
     &amp;darr;
   review
     &amp;darr;
    act
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 YAML로 표현하면:&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;nodes:

  - id: observe
    needs: []

  - id: plan
    needs:
      - observe

  - id: research
    needs:
      - plan

  - id: compare
    needs:
      - plan

  - id: review
    needs:
      - research
      - compare

  - id: act
    needs:
      - review
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. Ready Frontier&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG Scheduler는 현재 실행 가능한 Node를 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;observe ✓
plan    ✓

research pending
compare  pending
review   pending
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Ready Frontier는:&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;research
compare
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 작업은 서로 의존하지 않으므로 동시에 실행할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;       ┌&amp;rarr; Research Agent
Plan ──┤
       └&amp;rarr; Compare Agent
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 AgentSwarm을 활용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;18. AgentSwarm은 DAG 전체를 관리하면 안 된다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swarm에게 다음과 같이 맡기면 위험하다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;전체 프로젝트 알아서 끝내.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 대신 Dispatcher가 Ready Node만 계산한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;DAG Dispatcher
       &amp;darr;
Ready Frontier
       &amp;darr;
AgentSwarm
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;autohotkey&quot;&gt;&lt;code&gt;ready_nodes = [
  research,
  compare,
  benchmark
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Agent에게는 자신의 Node 정보만 전달한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;19. Node State&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 실행 Node에는 상태가 필요하다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;pending
   &amp;darr;
running
   &amp;darr;
succeeded
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패하면:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;running
   &amp;darr;
failed
   &amp;darr;
retry
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 Node가 영구 실패하면 자식은:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;blocked
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;run_id&quot;: &quot;2026-08-26-001&quot;,
  &quot;nodes&quot;: {
    &quot;research&quot;: {
      &quot;status&quot;: &quot;succeeded&quot;,
      &quot;attempt&quot;: 1,
      &quot;output_sha256&quot;: &quot;...&quot;
    },
    &quot;review&quot;: {
      &quot;status&quot;: &quot;pending&quot;,
      &quot;attempt&quot;: 0
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;20. Knowledge Graph + DAG 결합&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘을 합치면 다음 구조가 된다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Knowledge Graph
      │
      ▼
Immutable Snapshot
      │
      ▼
     DAG
      │
      ▼
 Ready Frontier
      │
      ▼
Kimi Agents / Grok Bots
      │
      ▼
 Candidate Patch
      │
      ▼
 Knowledge Steward
      │
      ▼
Knowledge Graph
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Loop가 생긴다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;KNOW
 &amp;darr;
PLAN
 &amp;darr;
EXECUTE
 &amp;darr;
VERIFY
 &amp;darr;
LEARN
 &amp;darr;
KNOW
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 Graph + Loop Engineering이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;21. Context Engineering&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 실패하는 또 하나의 이유는 Context가 계속 커지는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 대화 내역을 매번 전달하면 다음 문제가 발생한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Context 증가
 &amp;darr;
관련 없는 정보 증가
 &amp;darr;
Attention 분산
 &amp;darr;
Instruction 충돌
 &amp;darr;
Agent Drift
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Context Pack을 따로 관리한다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;context/

├── mission.md
├── constraints.md
├── definitions.md
├── state.md
└── acceptance-tests.md
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;22. Context Pack의 역할&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;mission.md&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이 시스템이 해결하려는 문제
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;constraints.md&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;절대 하면 안 되는 작업
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;definitions.md&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;프로젝트 용어 정의
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;state.md&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;현재 상태
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;acceptance-tests.md&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;완료 판정 기준
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent에게 과거 전체 대화를 주는 대신 필요한 Operational Context만 준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;23. Handoff Envelope&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent 사이의 작업도 자유로운 메시지보다는 Contract가 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;run_id&quot;: &quot;run_01&quot;,

  &quot;task&quot;: &quot;...&quot;,

  &quot;allowed_paths&quot;: [
    &quot;/project/src&quot;
  ],

  &quot;allowed_tools&quot;: [
    &quot;Read&quot;,
    &quot;Grep&quot;
  ],

  &quot;prohibited_actions&quot;: [
    &quot;delete&quot;,
    &quot;publish&quot;
  ],

  &quot;acceptance_tests&quot;: [],

  &quot;approval_boundary&quot;:
    &quot;external_effect&quot;,

  &quot;expected_artifact&quot;:
    &quot;analysis.json&quot;,

  &quot;next_owner&quot;:
    &quot;verifier&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 &lt;b&gt;Handoff Envelope&lt;/b&gt;라고 생각하면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;24. Harness Engineering&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 Capability를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 LLM 자체가 시스템의 통제 장치는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;K3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 Model이다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Kimi Code
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 K3를 실행하는 Harness다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Grok Bot
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은 또 다른 Runtime/Control Layer다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음처럼 이해하는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Model
   &amp;darr;
Inner Harness
   &amp;darr;
Outer Harness
   &amp;darr;
External Systems
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;25. Nested Harness&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 구조에서는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Grok Bot
Outer Harness
      │
      ▼
Kimi Code
Inner Harness
      │
      ▼
K3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에는 중요한 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Outer Harness가 Kimi Code 프로세스를 실행하도록 승인했다고 해서 &lt;b&gt;그 안에서 발생하는 모든 Tool Call까지 승인한 것은 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 Nested Harness Gap이라고 볼 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;26. 이중 Policy Layer&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 권한 정책도 두 군데 필요하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Outer Policy
Grok Bot

+

Inner Policy
Kimi Code
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Outer Policy:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Publish &amp;rarr; approval
Delete &amp;rarr; approval
Send &amp;rarr; approval
Production change &amp;rarr; approval
Purchase &amp;rarr; approval
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Inner Policy:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Read &amp;rarr; allow
Grep &amp;rarr; allow

Write &amp;rarr; ask
Edit &amp;rarr; ask
Bash &amp;rarr; ask

rm -rf &amp;rarr; deny
mail.send &amp;rarr; deny
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;27. Least Privilege&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent에게 필요한 권한만 주는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Research Agent에게는:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Read
Search
Browser
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정도만 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 권한은 필요하지 않다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Write
Delete
Send
Deploy
Payment
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker Profile을 역할별로 나누는 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;28. Read-only Agent부터 시작한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 구축할 때는 다음 Agent 하나면 충분하다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;Read
Grep
Glob
Agent
AgentSwarm
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제외:&lt;/p&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;Bash
Write
Edit
Write-capable MCP
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Agent가 안정적으로 동작한 다음 하나씩 권한을 늘리는 것이 안전하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;29. Append Log&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent 시스템에서는 일반 Application Log보다 상태 전환 기록이 특히 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;v&quot;: 1,
  &quot;ts&quot;: &quot;2026-08-26T12:00:00Z&quot;,
  &quot;run_id&quot;: &quot;run_01&quot;,
  &quot;actor&quot;: &quot;kimi&quot;,
  &quot;event&quot;: &quot;tool.request&quot;,
  &quot;tool&quot;: &quot;Bash&quot;,
  &quot;decision&quot;: &quot;ask&quot;,
  &quot;status&quot;: &quot;pending&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기록할 이벤트 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;handoff.created
tool.request
approval.result
tool.result
checkpoint
handoff.completed
recovery.started
recovery.completed
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;30. Hash Chain&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Append Log를 조금 더 강하게 만들려면 이전 Record Hash를 포함할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Record 1
 hash=A

Record 2
 prev=A
 hash=B

Record 3
 prev=B
 hash=C
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;R1 &amp;rarr; R2 &amp;rarr; R3 &amp;rarr; R4
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 로그가 변경되면 이후 Hash Chain이 깨진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이것만으로 동일한 OS 사용자가 로그를 수정하는 것을 완전히 막을 수 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 시스템이라면 Chain Head를 외부 Immutable Storage에도 저장하는 구조를 고려할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;31. 로그에 Secret을 넣으면 안 된다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 데이터는 Operational Log에서 제거한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;API Token
Cookie
Password
OAuth Secret
Private Key
Raw credential
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 필요한 경우 Hash를 기록한다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;args_sha256&quot;: &quot;...&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;32. 전체 시스템 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 구조를 하나로 연결하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;                 ┌──────────────────┐
                 │     Human        │
                 │    Approval      │
                 └────────┬─────────┘
                          │
                 ┌────────▼─────────┐
                 │    Grok Bot      │
                 │ Outer Controller │
                 └────────┬─────────┘
                          │
        ┌─────────────────┼───────────────────┐
        │                 │                   │
        ▼                 ▼                   ▼
 Knowledge Graph      DAG Scheduler       Policy Gate
        │                 │
        │                 ▼
        │          Ready Frontier
        │                 │
        │                 ▼
        │           ┌───────────┐
        │           │ Kimi Code │
        │           └─────┬─────┘
        │                 │
        │                 ▼
        │                K3
        │          ┌──────┴──────┐
        │          │             │
        │        Agent        AgentSwarm
        │          │             │
        └──────────┴──────┬──────┘
                          │
                          ▼
                    Candidate Patch
                          │
                          ▼
                       Verify
                          │
                          ▼
                 Knowledge Steward
                          │
                          ▼
                  Knowledge Graph
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;33. 여기서 가장 중요한 것은 Swarm이 아니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 Agent 프레임워크가 강조하는 것은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;10 Agents
50 Agents
100 Agents
300 Agents
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 운영 시스템에서 더 중요한 것은 Agent 숫자가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 구조다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Task Contract
State
Verification
Return Path
Stop Condition
Approval Boundary
Evidence
Audit Log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 100개여도 이 구조가 없으면 시스템은 불안정해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Agent가 세 개뿐이어도 이 구조가 있으면 상당히 강력한 자동화 시스템을 만들 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;34. 추천 구축 순서&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 Knowledge Graph와 DAG와 Swarm을 모두 구축하는 것은 권장하지 않는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stage 1 &amp;mdash; Single Worker&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Coordinator
 &amp;darr;
Worker
 &amp;darr;
Verifier
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건:&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Read-only
3 Round Limit
Human Approval
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stage 2 &amp;mdash; Output Contract&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Agent 결과를:&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;JSON Schema
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;validate
hash
persist
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stage 3 &amp;mdash; Loop Controller&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;Retry
Stop Rule
Fingerprint
Idempotency
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stage 4 &amp;mdash; DAG&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;독립적인 작업을 발견하면 DAG를 만든다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;research
compare
benchmark
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 작업을 병렬 처리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stage 5 &amp;mdash; AgentSwarm&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ready Frontier에 여러 Node가 있을 때만 Swarm을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Ready Frontier
     &amp;darr;
AgentSwarm
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swarm이 전체 시스템을 통제하게 하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stage 6 &amp;mdash; Knowledge Graph&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계에서 장기 Memory를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건은 하나다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;모든 Claim에는 Source가 있어야 한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stage 7 &amp;mdash; External Actions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에 다음 기능을 추가한다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Send
Publish
Deploy
Delete
Payment
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 반드시:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;Human Approval
+
Idempotency
+
Live Verification
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 적용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;35. 실제 MVP 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 만든다면 이 정도면 충분하다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;agent-system/

├── AGENTS.md

├── context/
│   ├── mission.md
│   ├── constraints.md
│   ├── state.md
│   └── acceptance-tests.md

├── handoffs/
│   ├── inbox/
│   └── outbox/

├── runs/
│   └── &amp;lt;job_id&amp;gt;/
│       ├── task.json
│       ├── candidate.json
│       ├── verdict.json
│       └── events.jsonl

├── policy/
│   ├── tools.md
│   └── approvals.md

└── artifacts/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계에서는 Knowledge Graph도 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG도 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swarm도 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 다음 Loop가 정확하게 동작하는지 확인한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;Task
 &amp;darr;
Worker
 &amp;darr;
Candidate
 &amp;darr;
Verifier
 &amp;darr;
PASS / REVISE
 &amp;darr;
State
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;36. 다음 단계&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 안정되면:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;Loop
 &amp;darr;
DAG
 &amp;darr;
Swarm
 &amp;darr;
Knowledge Graph
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순으로 확장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대 방향으로 가면 시스템 복잡도가 빠르게 증가한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;37. 핵심 원칙&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전체 아키텍처를 다섯 문장으로 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Models give capability.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 시스템에게 능력을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Loops give persistence.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Loop는 작업이 실패해도 다시 진행할 수 있게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Graphs give memory and order.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Graph는 기억을, DAG는 실행 순서를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Context gives the correct frame.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Engineering은 Agent가 지금 필요한 정보만 보게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Harnesses give control.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Harness는 Agent가 무엇을 할 수 있고 무엇을 해서는 안 되는지 통제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 신뢰할 수 있는 AI Agent 시스템의 핵심은 더 강력한 모델이나 더 많은 Agent가 아니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Capability
     +
Loop
     +
Graph
     +
Context
     +
Harness
     +
Policy
     +
Evidence
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 갖춰졌을 때 비로소 AI Agent를 단순한 챗봇이 아니라 &lt;b&gt;지속적으로 업무를 수행하는 운영 시스템&lt;/b&gt;으로 발전시킬 수 있다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1820</guid>
      <comments>https://javaexpert.tistory.com/1820#entry1820comment</comments>
      <pubDate>Thu, 27 Aug 2026 12:37:16 +0900</pubDate>
    </item>
    <item>
      <title>Flutter Signals 실전 가이드</title>
      <link>https://javaexpert.tistory.com/1819</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Riverpod과 무엇이 다르고, 언제 사용해야 하는가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기준일: 2026년 8월 26일&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지확인된 최신 버전&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;signals / signals_flutter&lt;/td&gt;
&lt;td&gt;7.1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;riverpod / flutter_riverpod&lt;/td&gt;
&lt;td&gt;3.4.2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals 7.1.0은 세밀한 반응성, 자동 의존성 추적, 지연 계산, 부분적인 위젯 재빌드를 중심으로 설계되어 있습니다. Riverpod 3.4.2는 반응형 캐시, 데이터 바인딩, 비동기 상태, 의존성 주입, 생명주기와 테스트 오버라이드를 함께 다루는 프레임워크에 가깝습니다. (&lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;Dart packages&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 먼저 결론부터&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals와 Riverpod은 모두 Flutter 상태를 반응형으로 관리하지만, &lt;b&gt;추상화 수준이 다릅니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Signals
└─ 값을 반응형으로 만드는 핵심 프리미티브

Riverpod
├─ 상태 관리
├─ 의존성 주입
├─ 비동기 캐시
├─ 생명주기
├─ 스코프
├─ 오버라이드
└─ 테스트 컨테이너&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals 공식 문서도 Signals를 완성형 애플리케이션 프레임워크가 아니라, 다른 DI 방식과 함께 사용할 수 있는 &lt;b&gt;핵심 반응성 라이브러리&lt;/b&gt;로 설명합니다. Riverpod은 공식적으로 반응형 캐싱 및 데이터 바인딩 프레임워크를 표방합니다. (&lt;a href=&quot;https://dartsignals.dev/guides/dependency-injection&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 실무적인 선택을 요약하면 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황우선 검토&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;화면 내부의 간단한 상태&lt;/td&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;편집기, 게임 UI, 실시간 수치처럼 자주 바뀌는 상태&lt;/td&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존에 GetIt, Provider 등 DI가 구축된 프로젝트&lt;/td&gt;
&lt;td&gt;Signals 추가 도입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API 요청, 캐시, 재시도, 무효화가 많은 앱&lt;/td&gt;
&lt;td&gt;Riverpod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테스트에서 구현체를 자주 교체해야 하는 앱&lt;/td&gt;
&lt;td&gt;Riverpod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기능과 팀 규모가 큰 애플리케이션&lt;/td&gt;
&lt;td&gt;Riverpod 또는 혼합 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미 Riverpod으로 안정적으로 운영 중인 프로젝트&lt;/td&gt;
&lt;td&gt;Riverpod 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Riverpod의 인프라와 Signals의 세밀한 UI 반응성이 모두 필요&lt;/td&gt;
&lt;td&gt;Riverpod + Signals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존 Riverpod 프로젝트를 Signals로 전면 교체해야 할 일반적인 이유는 크지 않습니다.&lt;/b&gt; Signals가 필요한 화면이나 기능부터 제한적으로 넣는 편이 위험이 작습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Signals란 무엇인가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals의 핵심은 &lt;b&gt;값을 읽은 곳을 자동으로 기억한다&lt;/b&gt;는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;Signal&amp;lt;T&amp;gt;
    │
    ├── Computed&amp;lt;R&amp;gt;
    │       │
    │       ├── UI
    │       └── 다른 Computed
    │
    └── Effect&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 count.value를 SignalBuilder 안에서 읽으면, Signals가 해당 SignalBuilder와 count 사이의 의존성을 자동으로 연결합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 count.value가 변경되면 count를 읽었던 SignalBuilder만 다시 빌드됩니다. 의존성을 직접 등록하는 ref.watch, addListener, notifyListeners와 같은 코드가 필요하지 않습니다. Signals는 이러한 자동 의존성 추적과 부분 재빌드를 핵심 기능으로 제공합니다. (&lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;Dart packages&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 설치 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서는 통합 패키지인 signals를 설치하는 방식이 권장됩니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;flutter pub add signals&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 코드에서는 다음 경로로 가져옵니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;import 'package:signals/signals_flutter.dart';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;signals 통합 패키지는 실행 환경에 따라 Dart용 또는 Flutter용 바인딩을 제공하며, Flutter 전용 패키지인 signals_flutter를 직접 설치하는 방법도 있습니다. 현재 Flutter용 최신 안정 버전은 7.1.0입니다. (&lt;a href=&quot;https://dartsignals.dev/reference/install&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Signals의 네 가지 핵심 요소&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.1 signal: 변경 가능한 상태&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;signal()은 값을 담는 반응형 컨테이너를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;final count = signal(0);

void increment() {
  count.value++;
}

void reset() {
  count.value = 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 읽기와 쓰기 모두 .value를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;print(count.value);

count.value = 10;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 .value를 읽었다고 해서 항상 구독되는 것은 아닙니다. SignalBuilder, SignalWidget, computed, effect처럼 &lt;b&gt;반응성 컨텍스트 내부에서 읽었을 때&lt;/b&gt; 의존성이 등록됩니다. (&lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;Dart packages&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.2 computed: 파생 상태&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 상태로부터 계산되는 값은 computed()로 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;final count = signal(0);

final doubled = computed(() {
  return count.value * 2;
});

final isEven = computed(() {
  return count.value.isEven;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 때는 일반 Signal과 마찬가지로 .value를 읽습니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;print(doubled.value);
print(isEven.value);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;computed에는 다음 특성이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;읽힐 때 계산되는 지연 평가 방식입니다.&lt;/li&gt;
&lt;li&gt;의존성이 변경되지 않았다면 이전 계산 결과를 재사용합니다.&lt;/li&gt;
&lt;li&gt;콜백 내부에서 읽은 Signal을 자동으로 추적합니다.&lt;/li&gt;
&lt;li&gt;조건문에 따라 실제로 읽는 Signal이 바뀌면 의존성도 동적으로 조정됩니다.&lt;/li&gt;
&lt;li&gt;네트워크 요청이나 다른 Signal 변경 같은 부수 효과를 넣지 않는 것이 원칙입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서도 computed 콜백을 순수 함수로 유지하고, 데이터베이스&amp;middot;네트워크 작업이나 다른 Signal 쓰기를 피하도록 설명합니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/core/computed&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.3 effect: 상태 변화에 따른 부수 효과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI를 만드는 것이 아니라 로깅, 저장, 분석 이벤트 등의 작업을 실행해야 한다면 effect()를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;q&quot;&gt;&lt;code&gt;final count = signal(0);

final stopEffect = effect(() {
  debugPrint('현재 count: ${count.value}');
});

count.value++;
count.value++;

// 더 이상 effect가 필요하지 않을 때
stopEffect();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;effect() 안에서 읽은 Signal이 변경되면 콜백이 다시 실행됩니다. effect()는 정리 함수도 반환하므로, 기능이나 위젯의 수명이 끝날 때 호출해야 합니다. (&lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;Dart packages&lt;/a&gt;)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;잘못된 예: 순환 업데이트&lt;/h3&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;final count = signal(0);

effect(() {
  count.value++;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 count를 읽고 다시 변경하므로 effect가 자신을 계속 실행하는 순환 구조가 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수정 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 경우 상태 변경 로직을 effect 바깥의 명령 메서드로 옮기는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;final count = signal(0);

void increment() {
  count.value++;
}

final stopEffect = effect(() {
  debugPrint('count changed: ${count.value}');
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특수한 상황에서는 peek()이나 untracked()로 구독 없이 값을 읽을 수 있지만, 일반적인 상태 흐름에서는 .value가 우선입니다. (&lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;Dart packages&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.4 batch: 여러 상태를 한 번에 변경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 관련된 Signal을 연속으로 변경할 때는 batch()를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;final firstName = signal('Kim');
final lastName = signal('Minsoo');

final fullName = computed(
  () =&amp;gt; '${firstName.value} ${lastName.value}',
);

void updateName() {
  batch(() {
    firstName.value = 'Park';
    lastName.value = 'Jisoo';
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batch() 안에서 여러 Signal을 변경하면 콜백이 끝나는 시점에 관련 구독자와 effect가 정리된 상태로 갱신됩니다. 중간 상태마다 effect가 반복 실행되는 상황을 줄일 수 있습니다. (&lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;Dart packages&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. Flutter UI에서 Signals 사용하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals 7에서는 다음 세 가지가 중심 API입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API사용 시점&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SignalBuilder&lt;/td&gt;
&lt;td&gt;기존 위젯 안의 일부 영역만 반응형으로 만들 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SignalWidget&lt;/td&gt;
&lt;td&gt;StatelessWidget 전체가 Signal을 사용할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SignalStatefulWidget&lt;/td&gt;
&lt;td&gt;StatefulWidget 생명주기와 Signal 반응성이 모두 필요할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 자료에서 자주 보이는 Watch 위젯과 SignalsMixin은 현재 deprecated 상태입니다. 신규 코드는 SignalBuilder, SignalWidget, SignalStatefulWidget을 기준으로 작성하는 것이 맞습니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/widgets/watch&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.1 SignalBuilder: 가장 명확한 부분 재빌드&lt;/h2&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';

final count = signal(0);
final doubled = computed(() =&amp;gt; count.value * 2);

class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Signals Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text('이 텍스트는 count를 구독하지 않습니다.'),

            SignalBuilder(
              builder: (context) {
                return Column(
                  children: [
                    Text('Count: ${count.value}'),
                    Text('Doubled: ${doubled.value}'),
                  ],
                );
              },
            ),

            const SizedBox(height: 16),

            ElevatedButton(
              onPressed: () =&amp;gt; count.value++,
              child: const Text('증가'),
            ),
          ],
        ),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SignalBuilder는 builder 안에서 읽은 Signal을 동적으로 추적합니다. 값이 바뀌면 주변 CounterPage 전체가 아니라 해당 SignalBuilder가 반환하는 영역이 다시 빌드됩니다. 공식 문서도 가능한 한 작은 하위 영역에 배치하는 패턴을 권장합니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/widgets/signal-builder&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.2 SignalWidget: 위젯 자체를 반응형으로 만들기&lt;/h2&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class CounterText extends SignalWidget {
  const CounterText({super.key});

  @override
  Widget build(BuildContext context) {
    return Text(
      '현재 값: ${count.value}',
      style: Theme.of(context).textTheme.headlineMedium,
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SignalWidget의 build() 안에서 동기적으로 읽은 Signal은 자동 구독됩니다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class CounterPanel extends SignalWidget {
  const CounterPanel({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: ${count.value}'),
        Text('Doubled: ${doubled.value}'),
        Text('Even: ${count.value.isEven}'),
      ],
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count가 변경되면 CounterPanel 요소가 다시 빌드됩니다. 위젯 내부의 아주 작은 부분만 갱신해야 한다면 SignalWidget 전체 대신 SignalBuilder를 더 아래쪽에 배치하는 편이 적합합니다. (&lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;Dart packages&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.3 SignalStatefulWidget: 로컬 상태와 생명주기&lt;/h2&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class SearchPage extends SignalStatefulWidget {
  const SearchPage({super.key});

  @override
  State&amp;lt;SearchPage&amp;gt; createState() =&amp;gt; _SearchPageState();
}

class _SearchPageState extends State&amp;lt;SearchPage&amp;gt; {
  final query = signal('');
  late final void Function() stopLoggingEffect;

  @override
  void initState() {
    super.initState();

    stopLoggingEffect = effect(() {
      debugPrint('검색어 변경: ${query.value}');
    });
  }

  @override
  void dispose() {
    stopLoggingEffect();
    query.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              onChanged: (value) {
                query.value = value;
              },
            ),
            const SizedBox(height: 16),
            Text('현재 검색어: ${query.value}'),
          ],
        ),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SignalStatefulWidget는 State의 build()가 실행되는 동안 동기적으로 읽은 Signal을 추적합니다. 비동기 콜백, 이벤트 리스너 또는 지연 작업 안에서 나중에 읽은 값은 build() 의존성으로 등록되지 않습니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/extensions/signal-stateful-widget&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. Controller 또는 Store 패턴으로 사용하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 변수로 Signal을 바로 선언하면 예제는 간단하지만, 규모가 커지면 상태 변경 위치를 추적하기 어려워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 프로젝트에서는 다음과 같이 변경 가능한 Signal을 비공개로 두고, 외부에는 ReadonlySignal을 노출하는 구조가 적합합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;import 'package:signals/signals_flutter.dart';

class CounterController {
  final Signal&amp;lt;int&amp;gt; _count = signal(0);

  late final Computed&amp;lt;int&amp;gt; _doubled = computed(
    () =&amp;gt; _count.value * 2,
  );

  ReadonlySignal&amp;lt;int&amp;gt; get count =&amp;gt; _count;
  ReadonlySignal&amp;lt;int&amp;gt; get doubled =&amp;gt; _doubled;

  void increment() {
    _count.value++;
  }

  void decrement() {
    _count.value--;
  }

  void reset() {
    _count.value = 0;
  }

  void dispose() {
    _doubled.dispose();
    _count.dispose();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI는 Signal 값을 직접 수정하지 않고 Controller 메서드를 호출합니다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class CounterView extends SignalWidget {
  const CounterView({
    required this.controller,
    super.key,
  });

  final CounterController controller;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: ${controller.count.value}'),
        Text('Doubled: ${controller.doubled.value}'),
        ElevatedButton(
          onPressed: controller.increment,
          child: const Text('증가'),
        ),
        ElevatedButton(
          onPressed: controller.reset,
          child: const Text('초기화'),
        ),
      ],
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReadonlySignal은 외부 소비자가 .value를 직접 변경하지 못하게 하고, 상태 변경을 Controller 메서드로 모으는 데 사용됩니다. 공식 문서에서도 private Signal과 public ReadonlySignal을 결합하는 방식을 단방향 상태 흐름 패턴으로 소개합니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/core/readonly&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 비동기 API 호출&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals는 futureSignal()을 통해 Future를 AsyncState 형태로 표현할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;final userId = signal(1);

final user = futureSignal(() async {
  // await 이전에 동기적으로 읽었으므로 의존성으로 추적됩니다.
  final id = userId.value;

  return userRepository.fetchUser(id);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;userId.value가 바뀌면 user FutureSignal이 무효화되고 새로운 요청을 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;userId.value = 2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI에서는 AsyncState.map()으로 로딩, 성공, 오류를 분기할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;class UserView extends SignalWidget {
  const UserView({super.key});

  @override
  Widget build(BuildContext context) {
    return user.value.map(
      loading: () {
        return const CircularProgressIndicator();
      },
      data: (value) {
        return Text(value.name);
      },
      error: (error, stackTrace) {
        return Text('사용자 조회 실패: $error');
      },
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FutureSignal은 다음 작업도 제공합니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;user.refresh(); // 기존 데이터를 유지하면서 다시 요청
user.reload();  // 기존 상태를 버리고 Loading부터 다시 시작
user.reset();   // 초기 상태로 되돌림&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Future 콜백 안에서 Signal을 의존성으로 만들려면 기본적으로 await 이전에 동기적으로 읽어야 합니다. await 이후 읽은 Signal까지 추적해야 할 경우에는 AsyncSignalOptions의 명시적 dependencies 설정이 필요합니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/async/future&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. Riverpod의 현재 권장 사용 방식&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod 3에서 사용자 액션에 따라 변경되는 상태는 Notifier 또는 AsyncNotifier가 중심입니다. 예전 코드에서 많이 사용되던 StateNotifier보다 현재 공식 문서의 권장 방향에 가깝습니다. (&lt;a href=&quot;https://riverpod.dev/docs/concepts2/providers&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Riverpod 카운터 예제&lt;/h2&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider =
    NotifierProvider&amp;lt;CounterNotifier, int&amp;gt;(
  CounterNotifier.new,
);

class CounterNotifier extends Notifier&amp;lt;int&amp;gt; {
  @override
  int build() {
    return 0;
  }

  void increment() {
    state++;
  }

  void reset() {
    state = 0;
  }
}

class CounterPage extends ConsumerWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);

    return Scaffold(
      body: Center(
        child: Text('Count: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref
              .read(counterProvider.notifier)
              .increment();
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod에서는 다음 역할이 명시적으로 구분됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// UI 또는 다른 Provider가 상태를 구독
final value = ref.watch(counterProvider);

// 상태 변경 객체를 한 번 읽음
final notifier = ref.read(counterProvider.notifier);

// 상태 변경에 따른 부수 효과
ref.listen(counterProvider, (previous, next) {
  debugPrint('$previous -&amp;gt; $next');
});

// 캐시를 버리고 다시 계산
ref.invalidate(counterProvider);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ref.watch는 선언적인 구독, ref.listen은 부수 효과, ref.read는 버튼 클릭과 같은 명령 실행에 사용됩니다. (&lt;a href=&quot;https://riverpod.dev/docs/concepts2/refs&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Signals와 Riverpod 상세 비교&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교 항목SignalsRiverpod&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기본 성격&lt;/td&gt;
&lt;td&gt;세밀한 반응성 프리미티브&lt;/td&gt;
&lt;td&gt;반응형 캐싱 및 데이터 바인딩 프레임워크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태 정의&lt;/td&gt;
&lt;td&gt;signal(value)로 객체 생성&lt;/td&gt;
&lt;td&gt;Provider, NotifierProvider 등 선언&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태 읽기&lt;/td&gt;
&lt;td&gt;.value&lt;/td&gt;
&lt;td&gt;ref.watch(provider)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태 변경&lt;/td&gt;
&lt;td&gt;.value =, Controller 메서드&lt;/td&gt;
&lt;td&gt;Notifier 메서드와 state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;의존성 추적&lt;/td&gt;
&lt;td&gt;반응성 컨텍스트에서 읽은 Signal을 자동 추적&lt;/td&gt;
&lt;td&gt;ref.watch로 의존성을 명시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파생 상태&lt;/td&gt;
&lt;td&gt;computed()&lt;/td&gt;
&lt;td&gt;Provider 안에서 다른 Provider를 ref.watch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;부수 효과&lt;/td&gt;
&lt;td&gt;effect(), SignalEffect&lt;/td&gt;
&lt;td&gt;ref.listen()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI 연결&lt;/td&gt;
&lt;td&gt;SignalBuilder, SignalWidget&lt;/td&gt;
&lt;td&gt;ConsumerWidget, Consumer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비동기 상태&lt;/td&gt;
&lt;td&gt;FutureSignal, AsyncState&lt;/td&gt;
&lt;td&gt;FutureProvider, AsyncNotifier, AsyncValue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;캐시&lt;/td&gt;
&lt;td&gt;반응성 값 자체의 계산 캐시 중심&lt;/td&gt;
&lt;td&gt;Provider 결과 캐시가 핵심 개념&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DI&lt;/td&gt;
&lt;td&gt;별도 DI, 생성자 주입, SignalProvider 등 선택&lt;/td&gt;
&lt;td&gt;Provider 그래프 자체가 DI 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스코프&lt;/td&gt;
&lt;td&gt;직접 설계하거나 SignalProvider 활용&lt;/td&gt;
&lt;td&gt;ProviderScope, ProviderContainer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구현 교체&lt;/td&gt;
&lt;td&gt;생성자 주입 또는 사용 중인 DI에서 처리&lt;/td&gt;
&lt;td&gt;Provider override가 내장됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자동 정리&lt;/td&gt;
&lt;td&gt;Signal 옵션, Provider 또는 명시적 dispose&lt;/td&gt;
&lt;td&gt;autoDispose, ref.onDispose, keepAlive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파라미터 상태&lt;/td&gt;
&lt;td&gt;직접 Factory 또는 Store 구조 설계&lt;/td&gt;
&lt;td&gt;family 또는 codegen 파라미터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테스트&lt;/td&gt;
&lt;td&gt;Controller를 일반 Dart 객체로 테스트 가능&lt;/td&gt;
&lt;td&gt;ProviderContainer.test, override 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코드 생성&lt;/td&gt;
&lt;td&gt;필수 아님&lt;/td&gt;
&lt;td&gt;선택 사항&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DevTools&lt;/td&gt;
&lt;td&gt;초기 단계의 Signals DevTools 제공&lt;/td&gt;
&lt;td&gt;전용 DevTools와 이전 상태 검사 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals의 의존성은 .value를 읽는 순간 자동으로 구성됩니다. Riverpod은 ref.watch를 통해 Provider 간 의존성을 코드에 명시하며, Provider의 반환값을 캐시합니다. (&lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;Dart packages&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod은 Provider별 자동 정리, ref.onDispose, keepAlive, 스코프별 override, 테스트 전용 ProviderContainer를 기본 설계에 포함합니다. Signals에서는 Signal 자체의 생명주기나 사용 중인 DI 컨테이너의 생명주기에 맞춰 관리해야 합니다. SignalProvider가 직접 생성한 Signal은 위젯 트리에서 제거될 때 dispose됩니다. (&lt;a href=&quot;https://riverpod.dev/docs/concepts2/auto_dispose&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 재빌드 방식 비교&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Signals&lt;/h2&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;SignalBuilder(
  builder: (context) {
    return Text('${count.value}');
  },
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;builder 안에서 실제로 읽은 Signal을 자동 추적합니다. count가 변경되면 해당 SignalBuilder만 다시 빌드됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Riverpod&lt;/h2&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class CountText extends ConsumerWidget {
  const CountText({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);

    return Text('$count');
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod은 counterProvider의 출력값이 변경되면 CountText를 다시 빌드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 일부 속성만 구독하고 싶다면 select를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;final userName = ref.watch(
  userProvider.select((user) =&amp;gt; user.name),
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod 공식 문서는 select가 불필요한 재빌드를 줄일 수 있지만, 읽기 비용과 코드 복잡도가 조금 추가되므로 성능 측정 없이 일괄 적용하지 말라고 설명합니다. (&lt;a href=&quot;https://riverpod.dev/docs/how_to/select&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;성능에 대한 정확한 해석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals는 반응성 단위를 잘게 나누기 쉽습니다. 그러나 이것만으로 모든 애플리케이션에서 Riverpod보다 빠르다고 결론내릴 수는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능은 다음 요소의 영향을 함께 받습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SignalBuilder 또는 Consumer의 배치 위치&lt;/li&gt;
&lt;li&gt;상태 객체의 크기&lt;/li&gt;
&lt;li&gt;변경 빈도&lt;/li&gt;
&lt;li&gt;위젯 트리 깊이&lt;/li&gt;
&lt;li&gt;리스트 및 이미지 렌더링 비용&lt;/li&gt;
&lt;li&gt;API 호출 및 직렬화 비용&lt;/li&gt;
&lt;li&gt;파생 상태 계산량&lt;/li&gt;
&lt;li&gt;불변 객체 생성량&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 실무에서는 대표 화면을 대상으로 Flutter DevTools의 rebuild 통계와 프레임 시간을 측정한 후 판단하는 것이 적절합니다. Riverpod 공식 문서 역시 최적화 전에 벤치마크할 것을 권고합니다. (&lt;a href=&quot;https://riverpod.dev/docs/how_to/select&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 생명주기 비교&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Signals&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 Signal은 애플리케이션 종료까지 유지할 수 있지만, 기능 단위 상태라면 소유자가 명확해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;class EditorStore {
  final title = signal('');
  final content = signal('');

  void dispose() {
    title.dispose();
    content.dispose();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class EditorHostState extends State&amp;lt;EditorHost&amp;gt; {
  late final EditorStore store;

  @override
  void initState() {
    super.initState();
    store = EditorStore();
  }

  @override
  void dispose() {
    store.dispose();
    super.dispose();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 SignalProvider가 Signal 생성과 정리를 담당하게 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;SignalProvider&amp;lt;MySignal&amp;gt;(
  create: () =&amp;gt; MySignal(),
  child: const FeaturePage(),
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 이미 만든 인스턴스를 제공하고 Provider가 dispose하지 않게 하려면 .value 생성자를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;SignalProvider&amp;lt;MySignal&amp;gt;.value(
  value: existingSignal,
  child: const FeaturePage(),
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SignalProvider의 기본 생성자는 내부에서 생성한 Signal의 생명주기를 관리하고, .value 생성자는 외부 인스턴스의 생명주기에 관여하지 않습니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/widgets/signal-provider&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Riverpod&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;final searchProvider = Provider&amp;lt;SearchService&amp;gt;(
  isAutoDispose: true,
  (ref) {
    final service = SearchService();

    ref.onDispose(service.dispose);

    return service;
  },
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod codegen을 사용하면 자동 정리가 기본값이며, codegen을 사용하지 않을 때는 isAutoDispose: true로 활성화할 수 있습니다. 마지막 리스너가 제거된 뒤 한 프레임 동안 다시 사용되지 않으면 Provider 상태가 제거되고 ref.onDispose가 호출됩니다. (&lt;a href=&quot;https://riverpod.dev/docs/concepts2/auto_dispose&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 테스트와 의존성 교체&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Signals 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller가 Flutter에 의존하지 않는다면 일반 Dart 단위 테스트로 검증할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;void main() {
  test('increment increases count', () {
    final controller = CounterController();

    controller.increment();

    expect(controller.count.value, 1);

    controller.dispose();
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Repository를 생성자로 받도록 만들면 테스트 구현체를 직접 전달할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class LoginController {
  LoginController(this.repository);

  final AuthRepository repository;
  final loading = signal(false);

  Future&amp;lt;void&amp;gt; login() async {
    // ...
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;final controller = LoginController(
  FakeAuthRepository(),
);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Riverpod 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod은 Container와 Provider override가 정식 API로 포함되어 있습니다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;void main() {
  test('provider override example', () {
    final container = ProviderContainer.test(
      overrides: [
        authRepositoryProvider.overrideWithValue(
          FakeAuthRepository(),
        ),
      ],
    );

    final repository = container.read(
      authRepositoryProvider,
    );

    expect(repository, isA&amp;lt;FakeAuthRepository&amp;gt;());
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Provider는 ProviderScope 또는 ProviderContainer에서 다른 값이나 구현으로 교체할 수 있습니다. 이 기능은 테스트뿐 아니라 개발&amp;middot;운영 환경별 구현 교체에도 활용됩니다. (&lt;a href=&quot;https://riverpod.dev/docs/concepts2/overrides&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 복잡한 의존성 그래프에서 테스트마다 Repository, API, 인증 상태를 교체해야 한다면 Riverpod의 override 체계가 더 구조적입니다. Signals는 일반 객체 테스트가 단순하지만, DI 및 스코프 체계는 프로젝트가 직접 선택해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. Riverpod 코드를 Signals로 옮길 때의 대응 관계&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RiverpodSignals에서의 유사 개념&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NotifierProvider&lt;/td&gt;
&lt;td&gt;Controller 또는 Store + private Signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ref.watch(provider)&lt;/td&gt;
&lt;td&gt;반응성 컨텍스트에서 signal.value 읽기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider 파생값&lt;/td&gt;
&lt;td&gt;computed()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ref.listen()&lt;/td&gt;
&lt;td&gt;effect()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ref.read(provider.notifier).method()&lt;/td&gt;
&lt;td&gt;Controller 메서드 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FutureProvider&lt;/td&gt;
&lt;td&gt;futureSignal()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AsyncValue&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;AsyncState&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ref.invalidate()&lt;/td&gt;
&lt;td&gt;FutureSignal의 reload, refresh, reset 또는 직접 상태 초기화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;autoDispose&lt;/td&gt;
&lt;td&gt;SignalProvider, 명시적 dispose, Signal 옵션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;family&lt;/td&gt;
&lt;td&gt;파라미터별 Controller Factory 또는 별도 DI 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider override&lt;/td&gt;
&lt;td&gt;생성자 주입, DI override, SignalProvider.value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ProviderScope&lt;/td&gt;
&lt;td&gt;별도 DI Scope 또는 SignalProvider 트리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 &lt;b&gt;완전한 일대일 변환이 아니라는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Riverpod의 Provider는 단순 계산 함수만 의미하지 않습니다. ProviderContainer 내부의 캐시, 생명주기, 의존성 그래프와 함께 동작합니다. 따라서 Riverpod의 Provider를 무조건 computed()로 바꾸면 캐시 무효화와 스코프 의미가 달라질 수 있습니다. (&lt;a href=&quot;https://riverpod.dev/docs/concepts2/providers&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. Riverpod과 Signals를 함께 사용하는 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals 공식 문서에는 Riverpod Provider가 Signal을 생성해 제공하는 예제가 포함되어 있습니다. 즉, 두 라이브러리는 기술적으로 배타적인 관계가 아닙니다. (&lt;a href=&quot;https://dartsignals.dev/guides/dependency-injection&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 실무에서는 다음과 같이 역할을 구분할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Riverpod
├─ API Client
├─ Repository
├─ 로그인 세션
├─ 앱 설정
├─ 원격 데이터 캐시
└─ 기능별 Store 생성 및 생명주기

Signals
├─ 화면 입력 상태
├─ 편집기 선택 상태
├─ 드래그 좌표
├─ 확대&amp;middot;축소 배율
├─ 재생 위치
└─ 자주 변경되는 파생 UI 값&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;혼합 예제&lt;/h2&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;final editorRepositoryProvider =
    Provider&amp;lt;EditorRepository&amp;gt;((ref) {
  return EditorRepositoryImpl();
});

final editorStoreProvider = Provider&amp;lt;EditorStore&amp;gt;((ref) {
  final repository = ref.watch(
    editorRepositoryProvider,
  );

  final store = EditorStore(repository);

  ref.onDispose(store.dispose);

  return store;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod으로 Store 생성과 수명을 관리하고, Store 내부에서는 Signals를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class EditorStore {
  EditorStore(this.repository);

  final EditorRepository repository;

  final zoom = signal(1.0);
  final selectedLayerId = signal&amp;lt;String?&amp;gt;(null);
  final isSaving = signal(false);

  late final canZoomIn = computed(
    () =&amp;gt; zoom.value &amp;lt; 4.0,
  );

  void zoomIn() {
    if (!canZoomIn.value) {
      return;
    }

    zoom.value += 0.1;
  }

  void selectLayer(String id) {
    selectedLayerId.value = id;
  }

  void dispose() {
    canZoomIn.dispose();
    zoom.dispose();
    selectedLayerId.dispose();
    isSaving.dispose();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI에서는 Riverpod으로 Store 인스턴스를 받고, SignalBuilder로 필요한 영역만 구독합니다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class EditorToolbar extends ConsumerWidget {
  const EditorToolbar({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final store = ref.watch(editorStoreProvider);

    return SignalBuilder(
      builder: (context) {
        return Row(
          children: [
            Text(
              '${(store.zoom.value * 100).round()}%',
            ),
            IconButton(
              onPressed: store.canZoomIn.value
                  ? store.zoomIn
                  : null,
              icon: const Icon(Icons.zoom_in),
            ),
          ],
        );
      },
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 이 구조에서는 Riverpod이 애플리케이션 수준의 의존성과 생명주기를 담당하고, Signals가 기능 내부의 세밀한 반응성을 담당합니다. 만화 편집기, 오디오 플레이어, 지도, 게임 UI처럼 빈번한 상태 변경이 있는 프로젝트에 적용하기 좋은 형태입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. Signals 사용 시 주의할 점&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.1 build() 안에서 Signal을 생성하지 않기&lt;/h2&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@override
Widget build(BuildContext context) {
  // 잘못된 위치
  final count = signal(0);

  return Text('${count.value}');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위젯이 다시 빌드될 때마다 새로운 Signal 인스턴스가 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signal은 다음 위치 중 하나에 두는 것이 적절합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Controller 또는 Store 필드&lt;/li&gt;
&lt;li&gt;State 객체의 필드&lt;/li&gt;
&lt;li&gt;Provider 또는 DI Container&lt;/li&gt;
&lt;li&gt;애플리케이션 수명과 동일한 제한적 전역 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals는 build() 안에서 Signal을 생성하는 패턴을 탐지하는 별도 lint 패키지도 제공합니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_lint&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.2 SignalWidget을 지나치게 크게 만들지 않기&lt;/h2&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class EntireHomePage extends SignalWidget {
  // 수많은 Signal을 읽는 매우 큰 위젯
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 위젯에서 여러 Signal을 읽으면 그중 하나만 바뀌어도 해당 위젯 요소가 다시 빌드됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 빈도가 다른 영역은 작은 위젯이나 SignalBuilder로 분리하는 편이 적절합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;Column(
  children: [
    const StaticHeader(),
    SignalBuilder(
      builder: (_) =&amp;gt; CounterSection(),
    ),
    SignalBuilder(
      builder: (_) =&amp;gt; ProgressSection(),
    ),
  ],
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals 공식 문서도 부분 재빌드를 위해 SignalBuilder를 작은 하위 영역에 배치하는 패턴을 설명합니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/widgets/signal-builder&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.3 effect에서 상태를 무분별하게 변경하지 않기&lt;/h2&gt;
&lt;pre class=&quot;ceylon&quot;&gt;&lt;code&gt;effect(() {
  total.value = price.value * quantity.value;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값이 파생 상태라면 effect보다 computed()가 적합합니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;final total = computed(
  () =&amp;gt; price.value * quantity.value,
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;effect는 로깅, 파일 저장, 분석 이벤트, 외부 시스템 호출처럼 반응형 그래프 밖으로 나가는 작업에 사용하는 편이 좋습니다. 파생 상태는 computed로 표현해야 지연 계산과 캐시 특성을 활용할 수 있습니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/core/computed&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.4 List를 제자리에서 수정할 때 주의하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 기존 List 인스턴스를 제자리에서 수정하면 일반적인 값 비교만으로 변경이 감지되지 않을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;final todos = signal&amp;lt;List&amp;lt;Todo&amp;gt;&amp;gt;([]);

todos.value.add(newTodo);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 객체를 할당하는 방식이 명확합니다.&lt;/p&gt;
&lt;pre class=&quot;ceylon&quot;&gt;&lt;code&gt;todos.value = [
  ...todos.value,
  newTodo,
];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 강제 갱신이 필요한 경우 다음 API를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;todos.value.add(newTodo);
todos.set(todos.value, force: true);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals에는 ListSignal, MapSignal, SetSignal과 같은 반응형 컬렉션도 포함되어 있습니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/core/signal&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.5 오래된 Watch 예제를 그대로 사용하지 않기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 예제:&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Watch(
  (context) =&amp;gt; Text('$count'),
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 방식:&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;SignalBuilder(
  builder: (context) {
    return Text('${count.value}');
  },
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signals 7 기준으로 Watch는 deprecated이며 SignalBuilder가 대체 API입니다. 또한 Signals 7의 SignalBuilder는 builder:라는 이름 있는 파라미터를 사용합니다. (&lt;a href=&quot;https://dartsignals.dev/packages/signals_flutter/widgets/watch&quot;&gt;dartsignals.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. 추천 프로젝트 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] Signals를 단순 전역 변수 모음으로 사용하기보다 기능별 Store 구조를 잡는 것이 유지보수에 유리합니다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;lib/
├─ core/
│  ├─ network/
│  ├─ storage/
│  └─ di/
│
├─ features/
│  └─ cart/
│     ├─ data/
│     │  ├─ cart_api.dart
│     │  └─ cart_repository_impl.dart
│     │
│     ├─ domain/
│     │  ├─ cart_item.dart
│     │  └─ cart_repository.dart
│     │
│     ├─ application/
│     │  └─ cart_store.dart
│     │
│     └─ presentation/
│        ├─ cart_page.dart
│        └─ cart_total_view.dart
│
└─ main.dart&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cart_store.dart는 다음 원칙으로 구성할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;class CartStore {
  CartStore(this.repository);

  final CartRepository repository;

  final Signal&amp;lt;List&amp;lt;CartItem&amp;gt;&amp;gt; _items = signal([]);

  final Signal&amp;lt;bool&amp;gt; _loading = signal(false);

  final Signal&amp;lt;Object?&amp;gt; _error = signal(null);

  ReadonlySignal&amp;lt;List&amp;lt;CartItem&amp;gt;&amp;gt; get items =&amp;gt; _items;

  ReadonlySignal&amp;lt;bool&amp;gt; get loading =&amp;gt; _loading;

  ReadonlySignal&amp;lt;Object?&amp;gt; get error =&amp;gt; _error;

  late final Computed&amp;lt;int&amp;gt; itemCount = computed(
    () =&amp;gt; _items.value.length,
  );

  late final Computed&amp;lt;int&amp;gt; totalPrice = computed(
    () =&amp;gt; _items.value.fold(
      0,
      (sum, item) =&amp;gt; sum + item.price * item.quantity,
    ),
  );

  Future&amp;lt;void&amp;gt; load() async {
    batch(() {
      _loading.value = true;
      _error.value = null;
    });

    try {
      _items.value = await repository.getItems();
    } catch (error) {
      _error.value = error;
    } finally {
      _loading.value = false;
    }
  }

  void remove(String itemId) {
    _items.value = _items.value
        .where((item) =&amp;gt; item.id != itemId)
        .toList();
  }

  void dispose() {
    itemCount.dispose();
    totalPrice.dispose();
    _items.dispose();
    _loading.dispose();
    _error.dispose();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 규칙은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;변경 가능한 Signal은 비공개로 둡니다.&lt;/li&gt;
&lt;li&gt;외부에는 ReadonlySignal을 노출합니다.&lt;/li&gt;
&lt;li&gt;UI는 Signal을 직접 수정하지 않고 Store 메서드를 호출합니다.&lt;/li&gt;
&lt;li&gt;파생값은 computed로 표현합니다.&lt;/li&gt;
&lt;li&gt;외부 부수 효과만 effect에 배치합니다.&lt;/li&gt;
&lt;li&gt;Store를 생성한 계층이 Store의 정리도 담당합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. 최종 선택 기준&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Signals가 잘 맞는 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태 모델이 비교적 단순합니다.&lt;/li&gt;
&lt;li&gt;코드 생성 없이 빠르게 개발하고 싶습니다.&lt;/li&gt;
&lt;li&gt;화면의 일부 값이 자주 변경됩니다.&lt;/li&gt;
&lt;li&gt;편집기, 플레이어, 게임, 지도처럼 세밀한 갱신이 중요합니다.&lt;/li&gt;
&lt;li&gt;이미 GetIt, Provider 또는 자체 DI가 있습니다.&lt;/li&gt;
&lt;li&gt;Controller를 일반 Dart 객체로 구성하고 싶습니다.&lt;/li&gt;
&lt;li&gt;React, SolidJS, Preact 스타일의 반응성에 익숙합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Riverpod이 잘 맞는 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Repository와 API 의존성이 많습니다.&lt;/li&gt;
&lt;li&gt;비동기 상태와 캐시가 핵심입니다.&lt;/li&gt;
&lt;li&gt;로그인 세션이나 앱 설정처럼 여러 화면이 공유하는 상태가 많습니다.&lt;/li&gt;
&lt;li&gt;Provider 무효화와 자동 정리가 중요합니다.&lt;/li&gt;
&lt;li&gt;테스트에서 의존성을 자주 교체합니다.&lt;/li&gt;
&lt;li&gt;기능별 Scope가 필요합니다.&lt;/li&gt;
&lt;li&gt;여러 개발자가 동일한 아키텍처 규칙을 따라야 합니다.&lt;/li&gt;
&lt;li&gt;DevTools에서 전체 Provider 상태를 관찰하고 싶습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Riverpod은 모든 Provider 상태를 확인하고 이전 상태를 살펴볼 수 있는 전용 Flutter DevTools 확장과 Riverpod 전용 lint/refactor 기능을 제공합니다. (&lt;a href=&quot;https://riverpod.dev/&quot;&gt;riverpod.dev&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;18. 종합 평가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] Signals는 Riverpod의 단순 대체재라기보다, &lt;b&gt;Riverpod보다 낮은 수준에서 반응성을 제공하는 도구&lt;/b&gt;로 보는 것이 정확합니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;Signals가 주로 답하는 질문
&amp;ldquo;이 값이 바뀌었을 때 필요한 UI만 어떻게 반응시킬 것인가?&amp;rdquo;

Riverpod이 주로 답하는 질문
&amp;ldquo;상태를 어디서 생성하고, 캐시하고, 공유하고,
언제 제거하며, 테스트에서 어떻게 교체할 것인가?&amp;rdquo;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 기존 Riverpod 프로젝트에서는 다음 전략이 가장 현실적입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 현재 Riverpod 구조는 유지
2. 빈번하게 갱신되는 복잡한 화면을 선별
3. 해당 기능 내부에만 Signals Store 도입
4. Riverpod이 Store 생성과 dispose 담당
5. 실제 rebuild 및 frame 성능 측정
6. 효과가 확인된 영역만 점진적으로 확대&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 전체가 API 중심의 일반적인 업무 앱이라면 Riverpod만으로 충분한 경우가 많습니다. 반대로 만화 편집기, 실시간 자막, 오디오 재생 위치, 지도 이동, 드래그&amp;middot;리사이즈, 게임 HUD처럼 &lt;b&gt;짧은 시간에 여러 값이 반복적으로 바뀌는 화면&lt;/b&gt;에서는 Signals 또는 Riverpod과 Signals의 혼합 구조를 검토할 가치가 있습니다.&lt;/p&gt;</description>
      <category>스터디/Flutter</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1819</guid>
      <comments>https://javaexpert.tistory.com/1819#entry1819comment</comments>
      <pubDate>Wed, 26 Aug 2026 14:17:28 +0900</pubDate>
    </item>
    <item>
      <title>Grok Bot으로 &amp;lsquo;문제 발견 &amp;rarr; 검증 &amp;rarr; 제품 출시 &amp;rarr; 판매&amp;rsquo; 자동화 시스템 구축하기</title>
      <link>https://javaexpert.tistory.com/1818</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 AI에게 &amp;ldquo;돈 될 만한 사업 아이디어를 찾아줘&amp;rdquo;라고 요청하는 것과, &lt;b&gt;인터넷에서 실제 사업자의 문제를 지속적으로 수집하고 검증한 뒤 제품 출시까지 연결하는 시스템&lt;/b&gt;을 만드는 것은 완전히 다른 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chris가 공개한 시스템의 핵심은 하나다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아이디어를 생성하지 말고, 시장에서 반복해서 관찰되는 문제를 찾아라.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Reddit 글이나 한 사람의 불평만으로 제품을 만들지 않는다. 서로 독립된 여러 데이터 소스에서 동일한 문제가 발견되었을 때만 후보로 승격한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;인터넷 데이터 수집
       &amp;darr;
문제 추출
       &amp;darr;
Embedding / Clustering
       &amp;darr;
복수 Source Confirmation
       &amp;darr;
Scoring
       &amp;darr;
사람의 승인
       &amp;darr;
사업 전용 Agent 생성
       &amp;darr;
Landing Page + Checkout
       &amp;darr;
시장 검증
       &amp;darr;
제품 구축
       &amp;darr;
Outreach / 판매
       &amp;darr;
매출&amp;middot;비용 측정
       &amp;darr;
Kill / Scale
       &amp;darr;
결과를 다시 Scoring에 반영
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;AI 사업 아이디어 생성기가 아니라 AI 기반 Revenue Discovery Engine&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 가장 중요한 설계 원칙: Single Source를 믿지 않는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시스템에서 가장 중요한 것은 LLM 자체가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cross-source confirmation&lt;/b&gt;, 즉 서로 다른 종류의 데이터가 동일한 문제를 가리키는지를 확인하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Reddit에서 다음 이야기가 나왔다고 하자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HVAC 업체가 갑자기 결원이 생겼을 때 면허가 있는 대체 기술자를 구하기 어렵다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것 하나만으로는 사업 아이디어가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 동시에 다음 데이터가 발견된다면 이야기가 달라진다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Reddit
&amp;darr;
HVAC 업체 사장이 긴급 대체 인력을 찾기 어렵다고 말함

Indeed
&amp;darr;
비슷한 업무를 담당할 Dispatcher / Technician 채용 다수

Upwork
&amp;darr;
긴급 dispatch 관련 반복 업무 의뢰

Podcast
&amp;darr;
HVAC 운영자가 같은 문제 언급

Google News
&amp;darr;
폭염으로 HVAC 수요 급증
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 하나의 문제가 여러 시스템에서 독립적으로 검증된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chris의 시스템은 이를 &lt;b&gt;Convergence&lt;/b&gt;라고 본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 왜 데이터 수집 시스템을 5개로 나누는가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Scraper가 인터넷을 뒤지는 구조가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 시스템마다 다른 종류의 증거를 담당한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;System 1 &amp;mdash; 사람들이 이미 돈을 쓰는 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 가치 있는 신호다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집 대상 예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Upwork&lt;/li&gt;
&lt;li&gt;Fiverr&lt;/li&gt;
&lt;li&gt;Indeed&lt;/li&gt;
&lt;li&gt;App Store 리뷰&lt;/li&gt;
&lt;li&gt;SaaS 1~3점 리뷰&lt;/li&gt;
&lt;li&gt;Flippa&lt;/li&gt;
&lt;li&gt;Acquire&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 찾는 것은 단순한 불만이 아니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;이 문제를 해결하기 위해 이미 돈이 나가고 있는가?&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 회사가 특정 반복 업무를 처리하기 위해 연봉 $45,000 직원을 고용하고 있다면 그 업무에는 이미 시장 가격이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 SaaS 리뷰에 다음과 같은 내용이 있다면 강한 신호다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;매월 $130를 내고 있는데
내가 필요한 것은 기능 하나뿐이다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 새로운 SaaS 전체를 만드는 것이 아니라 그 사람이 사용하는 &lt;b&gt;핵심 기능 하나만 떼어낸 Micro SaaS&lt;/b&gt;가 기회가 될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. System 2 &amp;mdash; 전문가와 운영자들이 이야기하는 방향&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 데이터는 Podcast와 YouTube다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문에서는 다음 프로그램을 예로 들었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;My First Million&lt;/li&gt;
&lt;li&gt;This Week in Startups&lt;/li&gt;
&lt;li&gt;Lenny's Podcast&lt;/li&gt;
&lt;li&gt;AI Daily Brief&lt;/li&gt;
&lt;li&gt;Greg Isenberg 관련 콘텐츠&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 중요한 것은 프로그램 이름이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신이 공략하려는 Vertical에 맞춰 채널을 선택해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 건설업을 대상으로 한다면&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;SaaS Podcast
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Contractor Podcast
HVAC Podcast
Construction Business Podcast
Roofing Operator Channel
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 데이터가 훨씬 가치 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 단순하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;YouTube RSS
     &amp;darr;
새 Video 감지
     &amp;darr;
Transcript 수집
     &amp;darr;
LLM Extraction
     &amp;darr;
Problem / Tool / Idea 추출
     &amp;darr;
기존 Cluster와 비교
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Podcast에서 발견된 아이디어는 독립적으로 Candidate가 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시스템에서는 &lt;b&gt;Confirmation Source&lt;/b&gt; 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Podcast에서만 언급
&amp;rarr; Watching

Reddit 문제 + Podcast 언급
&amp;rarr; Score 상승

Job Posting + Review + Podcast
&amp;rarr; Candidate 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. System 3 &amp;mdash; Timing Catalyst 탐지&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사업에서는 문제 자체만큼 중요한 것이 타이밍이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 이벤트가 발생할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;새로운 규제
보험 정책 변경
폭풍
폭염
가뭄
Permit 정책 변경
보조금 정책
License 규정
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것들이 기존 문제와 결합하면 갑자기 구매 의사가 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 System 3는 다른 시스템보다 자주 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문의 예시는 2시간 간격이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집 후보:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Google News RSS
County Permit Portal
Business Registration
Contractor License Board
NOAA Weather
Regulation Update
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 데이터는 다음 네 가지다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;Who
어떤 사업자가 영향을 받는가?

Where
어느 지역인가?

Urgency
얼마나 급한가?

Window
기회가 얼마 동안 지속되는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;강력한 우박 예보
       &amp;darr;
Roofing Contractor
       &amp;darr;
향후 2~4주 수요 증가
       &amp;darr;
Lead / Scheduling / Financing 문제 증가
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Cluster와 Catalyst가 연결되면 즉시 Alert를 발생시킨다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. System 4 &amp;mdash; 사업자의 실제 불만&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reddit과 전문 Forum을 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여기에는 강력한 필터가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집하지 않는 데이터:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;직원 불평
일반 소비자 불만
정치
동기부여 글
성공담
잡담
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾는 것은 Owner 또는 Operator가 말하는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 문장이 강한 Signal이 된다.&lt;/p&gt;
&lt;pre class=&quot;smalltalk&quot;&gt;&lt;code&gt;&quot;I wish there was...&quot;

&quot;Is there a tool that...&quot;

&quot;We currently pay...&quot;

&quot;I spend hours every week...&quot;

&quot;Alternative to...&quot;

&quot;Why is there no...&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추출 결과는 최대한 단순하게 만든다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;who: HVAC owner
problem: last-minute licensed technician replacement
vertical: HVAC
severity: 5
quote: &quot;...&quot;
source: URL
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전문 산업 Forum은 Reddit보다 더 높은 Signal을 가질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Plumbing Forum에 글을 쓰는 사람은 실제 Plumbing Business 운영자일 가능성이 일반 Reddit보다 높기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. System 5 &amp;mdash; 무엇이 빠르게 성장하는가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막은 Launch / Trend 감지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Product Hunt Top 20
GitHub Trending
Show HN
Ask HN
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 단순히 숫자를 저장하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1,000 stars
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;출시 48시간
1,000 stars
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 훨씬 의미 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 반드시:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;traction
+
age
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 함께 저장한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 수집 데이터와 사람이 보는 데이터를 분리한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문의 좋은 설계 중 하나가 이 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Machine Layer와 Human Layer를 분리한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Machine Layer&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite 또는 Supabase.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;raw_items
problems
problem_embeddings
clusters
cluster_sources
enrichments
verdicts
outcomes
run_logs
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 스키마:&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;raw_items
 ├ id
 ├ source_type
 ├ source_url
 ├ raw_text
 ├ author
 └ collected_at

problems
 ├ id
 ├ raw_item_id
 ├ problem
 ├ vertical
 ├ severity
 ├ dollar_amount
 └ embedding

clusters
 ├ id
 ├ canonical_problem
 ├ vertical
 ├ score
 ├ status
 └ updated_at
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vector Search를 사용해 새로운 Problem이 기존 Cluster와 유사한지 판단한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. Human Layer는 Obsidian&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chris는 사람이 보는 인터페이스로 Obsidian을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;00-Inbox
01-Daily
02-Candidates
03-Watching
04-Proposals
05-Active
06-Outcomes
07-Dossiers
08-Killed
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cluster 하나가 Markdown 파일 하나가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;---
cluster_id: hvac-0021
score: 84.9
systems:
  - jobs
  - forum
vertical: hvac
status: candidate
verdict:
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본문:&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# Problem

HVAC 업체가 긴급 Call-out 상황에서
면허가 있는 대체 기술자를 찾기 어렵다.

## Evidence

Source A
Quote
URL

Source B
Quote
URL

## Diagnostic

...

## Enrichment

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 매우 중요한 규칙이 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 변경한 Frontmatter가 항상 우선한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 Obsidian을 업데이트하기 전에 반드시 기존 Frontmatter를 다시 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래야 사람이&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;status: killed
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 변경한 아이디어를 AI가 다시 Candidate로 되돌려 놓는 문제를 방지할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 핵심은 Clustering&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수백 개 글을 그대로 사람이 읽는 것은 자동화가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;576 Raw Items
&amp;darr;
72 Problems
&amp;darr;
57 Clusters
&amp;darr;
1 Candidate
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 압축해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 Problem이 들어오면:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Problem
   &amp;darr;
Embedding
   &amp;darr;
Vector Search
   &amp;darr;
Existing Cluster?
   ├ YES &amp;rarr; attach
   └ NO  &amp;rarr; new cluster
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 추가로 LLM 판단을 넣을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Semantic Similarity
+
LLM Classification
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 단계를 같이 쓰는 것이 안정적이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. Scoring Engine&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문의 가장 중요한 아이디어 중 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Score는 단순한 LLM 느낌이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 높은 Weight는&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Distinct Systems Confirming
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음처럼 설계할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Score =

35% Source Diversity
20% Existing Spend Evidence
15% Severity
10% Recurrence
10% Growth Velocity
5% Timing Catalyst
5% Founder Preference
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Reddit 30개
Podcast 0
Jobs 0
Reviews 0

&amp;rarr; Source Diversity = 낮음
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Reddit 2
Indeed 4
Upwork 3
App Review 5

&amp;rarr; Source Diversity = 높음
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후자가 훨씬 높은 점수를 받는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 인간 피드백까지 Scoring에 넣는다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 Candidate를 판단할 때 세 가지 Verdict를 내린다.&lt;/p&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;Pursue
Watch
Kill
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 반드시 이유를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;Kill

Reason:
시장 규모는 충분하지만
고객 확보 비용이 너무 높을 가능성이 큼.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 데이터가 쌓이면 다음 후보 평가에도 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;최근 30개 Verdict
&amp;darr;
선호 Pattern
&amp;darr;
Scoring Adjustment
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 시스템이 시간이 지날수록&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;시장에 뭐가 필요한가?&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿐 아니라&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;내가 어떤 사업을 실제로 하는가?&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까지 학습한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. Candidate 기준&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문의 예시는 매우 보수적이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Score &amp;ge; 70
+
2 Systems

&amp;rarr; Candidate
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Score &amp;ge; 85
+
3 Systems
+
Verified Numbers

&amp;rarr; Build Proposal
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 50개 문제 중 1~2개만 올라오게 만드는 것이 목적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이디어가 많이 나오는 것은 좋은 시스템이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;버릴 것을 많이 버리는 시스템이 좋은 시스템이다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. Dashboard&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 판단하는 화면은 복잡할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;홈 화면:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Problem                           Vertical   Score  Sources
──────────────────────────────────────────────────────────
Licensed technician shortage     HVAC        84.9     3
Insurance claim rework           Dental      81.2     2
Marketplace payout fee           SaaS        73.7     1
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클릭하면:&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;Problem

Why this matters

Evidence
 ├ Quote
 ├ Source
 └ URL

Enrichment

Score Breakdown

[Pursue]
[Watch]
[Kill]

Reason: __________
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 것은 데이터 시각화보다 &lt;b&gt;Decision Interface&lt;/b&gt;라는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. Pursue를 누르면 새로운 Agent를 만든다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Candidate Agent와 Business Agent를 분리한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Discovery Agent
       &amp;darr;
Human Pursue
       &amp;darr;
Business Agent 생성
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Business Agent가 받는 Context:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Problem
Evidence
Quotes
Source URLs
Vertical
Competitors
Pricing Evidence
Customer List
Catalysts
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각각 독립적으로 관리한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Business A
 ├ Revenue
 ├ Cost
 └ Customers

Business B
 ├ Revenue
 ├ Cost
 └ Customers
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사업별 P&amp;amp;L을 섞지 않는 것이 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. 제품부터 만들지 않는다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pursue한 뒤 바로 제품 개발을 시작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;b&gt;Test&lt;/b&gt;를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Evidence
&amp;darr;
Landing Page
&amp;darr;
Price
&amp;darr;
Checkout
&amp;darr;
Traffic
&amp;darr;
Payment Intent
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Landing Page에는 실제 Evidence에서 나온 표현을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사업자가&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;I spend three hours every morning rescheduling technicians.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 반복해서 말한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마케팅 문구를 새로 만들어내기보다 문제 표현 자체를 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 실제 외부 공개 시에는 저작권&amp;middot;개인정보&amp;middot;플랫폼 약관을 별도로 검토해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. Waitlist보다 Checkout&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문에서 상당히 강조하는 부분이다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;Waitlist
=
관심

Checkout
=
구매 의도
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Landing Page 단계부터 가격을 제시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;Emergency HVAC Dispatch Assistant

$299 / month
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 결제 단계까지 이동하는지를 측정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 실제 제품이 완성되지 않았다면 결제 처리 방식과 소비자 보호, 환불, 표시&amp;middot;광고 관련 법적 의무를 반드시 검토해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. 제품은 네 가지 형태 중 하나로 분류한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 검증되었다고 무조건 SaaS를 만들 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문에서는 네 가지 Shape로 분류한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① Tool&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 쓰는 Software에 대한 불만이 반복될 때.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;기존 제품 전체를 복제
X

불만이 집중된 기능 하나
O
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Micro SaaS 형태다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② Service&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Job Posting 또는 Freelance Brief가 반복될 때.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업이 이미 사람에게 맡기고 있는 Outcome을 자동화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Permit Runner

&amp;darr;

AI + Workflow Automation

&amp;darr;

Permit Processing Service
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ Physical Product&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 Screen 밖에서 발생한다면 물리 상품도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;작업 현장
매장
창고
차량
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;④ Information Product&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복적으로 동일한 질문이 등장하고 해결책이 지식이라면 정보 상품이다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Guide
Template
Checklist
Database
Course
Report
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;18. 첫 번째 고객은 이미 데이터베이스에 있다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 상당히 흥미로운 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Problem Discovery 과정에서 이미 다음 사람들을 찾았다.&lt;/p&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;문제를 말한 사람
Job Posting을 올린 회사
Freelance Job을 발주한 사람
부정적 SaaS Review 작성자
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 고객 Discovery와 Problem Discovery가 동시에 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 첫 Outreach 대상은:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Cluster Evidence
        &amp;darr;
Prospect List
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 생성한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;19. 그러나 Outreach는 자동 실행하지 않는다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문에서도 사람에게 연락하거나 돈을 쓰는 행동에는 별도 승인을 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 Permission Model이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Agent 자동 허용&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Research
Problem extraction
Clustering
Landing page 작성
Prospect list 작성
Draft 작성
Dashboard 업데이트
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Human Approval 필요&lt;/h2&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Email 전송
DM 전송
광고 집행
우편 발송
결제
가격 변경
환불
송금
외부 게시
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;AI can prepare.
Human authorizes irreversible actions.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;20. 모든 사업에는 Kill Metric이 있어야 한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사업을 시작한 뒤 AI가 계속 낙관적으로 해석하는 것을 막기 위한 장치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;14일

300명 Landing Page 방문

Checkout Click &amp;lt; 3

Payment = 0

&amp;rarr; Kill
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Outreach 100
Reply &amp;lt; 5
Qualified Lead &amp;lt; 2

&amp;rarr; Kill
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품을 만들기 전에 Kill Metric을 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해야 결과를 사후적으로 합리화하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;21. Outcome이 다시 Discovery Engine으로 돌아간다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 시스템에서 가장 중요한 Feedback Loop다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Idea
&amp;darr;
Build
&amp;darr;
Sell
&amp;darr;
Outcome
&amp;darr;
Scoring Engine
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Roofing Lead Tool
Score 88
&amp;darr;
Launch
&amp;darr;
0 Sales
&amp;darr;
Killed
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 유사한 Candidate가 나타나면:&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;Previous failure pattern
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 반영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;HVAC Dispatch
Score 86
&amp;darr;
5 Customers
&amp;darr;
MRR 발생
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한다면 비슷한 특성을 가진 문제의 Score를 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 시스템이 점점 자신의 경험 데이터를 갖게 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;22. 전체 Cron 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;07:00
System 1
Jobs / Reviews / Freelance

08:00
System 4
Forums / Reddit

08:30
System 5
Product Hunt / GitHub / HN

Every 2 Hours
System 3
News / Regulation / Weather / Permit

Event Driven
System 2
YouTube / Podcast RSS

23:00
Embedding
Clustering
Scoring
Verdict Learning

23:30
Obsidian Update
Dashboard Update

07:30
Morning Brief
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;23. Morning Brief&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람에게는 Raw Data를 전달하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Morning Brief &amp;mdash; 2026-08-26

NEW CANDIDATE

HVAC Emergency Technician Marketplace

Score: 86
Systems: 3

Evidence
- Job postings
- HVAC forum
- Upwork

Why now
Heat wave increased emergency callouts.

Expected customer
10~50 technician HVAC shop.

NEXT ACTION

Review proposal.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무것도 변하지 않았다면:&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;No meaningful changes today.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄로 끝낸다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;24. 구현한다면 Grok에 종속시킬 필요는 없다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 Grok Bot을 중심으로 설명하지만 구조 자체는 특정 LLM과 독립적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 구조로 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;                    ┌───────────────┐
                    │ Data Sources  │
                    └───────┬───────┘
                            &amp;darr;
                 ┌───────────────────┐
                 │ Collector Workers │
                 └─────────┬─────────┘
                           &amp;darr;
                     PostgreSQL
                           +
                     pgvector
                           &amp;darr;
                 Problem Extractor
                           &amp;darr;
                     Cluster Engine
                           &amp;darr;
                    Scoring Engine
                           &amp;darr;
               ┌────────────────────┐
               │ Decision Dashboard │
               └─────────┬──────────┘
                         &amp;darr;
                      Pursue
                         &amp;darr;
                 Business Agent
                         &amp;darr;
             Landing / Checkout Test
                         &amp;darr;
                   Sales Outcome
                         &amp;darr;
                 Feedback Engine
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 다음 중 무엇이든 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;GPT
Claude
Gemini
Grok
Local LLM
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 자산은 모델이 아니라:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;데이터
+
Cluster history
+
Verdicts
+
Outcomes
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;25. 현실적으로 가장 먼저 만들어야 할 MVP&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 글의 모든 기능을 구현하는 것은 과도하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVP라면 다음 6개만 있으면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1단계&lt;/h3&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Reddit / Forum
Jobs
App Reviews
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3개 Source만 수집한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2단계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM으로 Problem extraction.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3단계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pgvector로 Clustering.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4단계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 세 요소만 Scoring.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Source diversity
Dollar evidence
Severity
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5단계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Decision Dashboard.&lt;/p&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;Pursue
Watch
Kill
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6단계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pursue하면 자동으로:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Landing Page Draft
Pricing Proposal
Customer List
Validation Plan
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까지만 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 결제&amp;middot;연락&amp;middot;광고는 사람이 승인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도만 만들어도 원문 시스템의 핵심 가설을 검증할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서 가장 중요한 것은 &amp;ldquo;Grok으로 사업을 자동으로 여러 개 돌릴 수 있다&amp;rdquo;는 화려한 문장이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 중요한 부분은 그 앞단의 구조다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Search
&amp;darr;
Evidence
&amp;darr;
Convergence
&amp;darr;
Filter
&amp;darr;
Human Decision
&amp;darr;
Experiment
&amp;darr;
Revenue
&amp;darr;
Outcome
&amp;darr;
Learning
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 AI 사업 아이디어 시스템은:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;AI
&amp;darr;
아이디어 100개
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 끝난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시스템은 반대로:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;인터넷 수백 개 Signal
&amp;darr;
실제 문제 수십 개
&amp;darr;
Cluster 수십 개
&amp;darr;
검증된 Candidate 1~2개
&amp;darr;
시장 테스트
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 목표로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이것을 단순한 &lt;b&gt;AI 사업 자동화 Bot&lt;/b&gt;이라고 보는 것보다,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Continuous Problem Discovery + Evidence-based Venture Factory&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI Revenue Discovery Engine&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라고 보는 것이 기술적으로 훨씬 정확하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 구조에서 장기적으로 가장 가치 있는 것은 Agent도 Dashboard도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 축적되는 핵심 자산은 다음 네 가지다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 어떤 문제가 반복해서 발생했는가
2. 어떤 증거가 실제 매출로 이어졌는가
3. 어떤 아이디어를 왜 죽였는가
4. 어떤 유형의 사업이 실제로 돈을 벌었는가
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 데이터가 계속 축적되면 단순한 인터넷 모니터링 시스템이 아니라 &lt;b&gt;자신의 사업 경험을 학습하는 개인용 Venture Intelligence System&lt;/b&gt;으로 발전할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 :&lt;/p&gt;
&lt;figure id=&quot;og_1787712863435&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;X에서 Chris(@everestchris6) 님&quot; data-og-description=&quot;how to build revenue systems with grok bot (FULL GUIDE)&quot; data-og-host=&quot;x.com&quot; data-og-source-url=&quot;https://x.com/everestchris6/status/2092324002740224436&quot; data-og-url=&quot;https://x.com/everestchris6/status/2092324002740224436&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TTXys/dJMb9c9Puo4/4RXe2y78TxNHUsfaPShzMk/img.jpg?width=2048&amp;amp;height=819&amp;amp;face=0_0_2048_819,https://scrap.kakaocdn.net/dn/bhAGOC/dJMb9efvEhm/1NnfOlSuHSzGUKdGMNUXlk/img.jpg?width=2048&amp;amp;height=819&amp;amp;face=0_0_2048_819&quot;&gt;&lt;a href=&quot;https://x.com/everestchris6/status/2092324002740224436&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://x.com/everestchris6/status/2092324002740224436&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TTXys/dJMb9c9Puo4/4RXe2y78TxNHUsfaPShzMk/img.jpg?width=2048&amp;amp;height=819&amp;amp;face=0_0_2048_819,https://scrap.kakaocdn.net/dn/bhAGOC/dJMb9efvEhm/1NnfOlSuHSzGUKdGMNUXlk/img.jpg?width=2048&amp;amp;height=819&amp;amp;face=0_0_2048_819');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;X에서 Chris(@everestchris6) 님&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;how to build revenue systems with grok bot (FULL GUIDE)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;x.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1818</guid>
      <comments>https://javaexpert.tistory.com/1818#entry1818comment</comments>
      <pubDate>Wed, 26 Aug 2026 09:34:35 +0900</pubDate>
    </item>
    <item>
      <title>Growth Lab 분석: Coding Agent를 &amp;lsquo;성장팀&amp;rsquo;으로 바꾸는 파일 기반 Growth OS</title>
      <link>https://javaexpert.tistory.com/1817</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;figure id=&quot;og_1787628857545&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - tsingyuai/growth-lab: An end-to-end growth tool that understands the product, fetch the data it needs, researches the m&quot; data-og-description=&quot;An end-to-end growth tool that understands the product, fetch the data it needs, researches the market, executes campaigns, and reviews results to improve the next round of growth. 从代码到市场的开源端到端增长工具...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/tsingyuai/growth-lab&quot; data-og-url=&quot;https://github.com/tsingyuai/growth-lab&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dNj0cU/dJMb9eT6Tpl/Gufv3R1DuffeSmXF5KqkKk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bmn2px/dJMb8XSnFgd/cmelpsfIRPlWwmvBZfzbd0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/tsingyuai/growth-lab&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/tsingyuai/growth-lab&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dNj0cU/dJMb9eT6Tpl/Gufv3R1DuffeSmXF5KqkKk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bmn2px/dJMb8XSnFgd/cmelpsfIRPlWwmvBZfzbd0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - tsingyuai/growth-lab: An end-to-end growth tool that understands the product, fetch the data it needs, researches the m&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An end-to-end growth tool that understands the product, fetch the data it needs, researches the market, executes campaigns, and reviews results to improve the next round of growth. 从代码到市场的开源端到端增长工具...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 이용한 마케팅 도구는 이미 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글을 작성하는 AI도 있고, 경쟁사를 조사하는 AI도 있으며, SNS 콘텐츠를 생성하거나 SEO 키워드를 분석해 주는 서비스도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 대부분은 하나의 작업만 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 실제 Growth 업무가 하나의 작업으로 끝나지 않는다는 것이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;제품 이해
&amp;rarr; 사용자와 시장 조사
&amp;rarr; 성장 기회 발견
&amp;rarr; 실행
&amp;rarr; 결과 측정
&amp;rarr; 분석
&amp;rarr; 다음 실험
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사이클을 계속 반복해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈소스 프로젝트 &lt;b&gt;Growth Lab&lt;/b&gt;은 바로 이 문제를 Coding Agent를 이용해 해결하려는 프로젝트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab은 자신을 자연어로 제품 운영과 사용자 성장 루프를 실행하는 도구로 설명한다. 현재 Codex와 Claude Code를 Runtime으로 사용하고, 제품 분석부터 시장 조사, 콘텐츠&amp;middot;SEO 실행, 결과 회고까지 하나의 지속적인 루프로 연결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub: &lt;a href=&quot;https://github.com/tsingyuai/growth-lab?utm_source=chatgpt.com&quot;&gt;tsingyuai/growth-lab&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 아이디어는 의외로 단순하다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab의 구조를 한 줄로 정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;대화 = Control Plane
Codex / Claude Code = Runtime
Model = 성장 루프
Collector = 데이터 수집
Executor = 실행
Memory = 실행 기록
SOUL.md = 제품에 대한 장기 이해
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 SaaS 방식과 상당히 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 복잡한 Workflow Engine을 구축하는 대신 Coding Agent가 이미 가지고 있는 파일 읽기, 코드 수정, 웹 검색, 브라우저 조작, 명령 실행 능력을 그대로 Runtime으로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Growth Lab 자체가 거대한 애플리케이션이라기보다는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Coding Agent가 Growth Engineer처럼 행동하도록 만들어 놓은 방법론 + 도구 + Memory 묶음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 더 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 가장 중요한 구조: Model &amp;rarr; Collector &amp;rarr; Executor &amp;rarr; Memory&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab을 이해하려면 이 네 가지 개념을 먼저 봐야 한다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;                  ┌──────────────┐
                  │    Model     │
                  │ 성장 전략/루프 │
                  └──────┬───────┘
                         │
                Observe / Decide
                         │
            ┌────────────▼────────────┐
            │        Collector        │
            │ 시장&amp;middot;검색&amp;middot;콘텐츠 데이터 수집 │
            └────────────┬────────────┘
                         │
                       Evidence
                         │
                  ┌──────▼───────┐
                  │    Model     │
                  │    Decide    │
                  └──────┬───────┘
                         │
                       Action
                         │
            ┌────────────▼────────────┐
            │        Executor         │
            │ 페이지&amp;middot;콘텐츠&amp;middot;이미지&amp;middot;배포 │
            └────────────┬────────────┘
                         │
                       Result
                         │
                  ┌──────▼───────┐
                  │    Memory    │
                  │ 데이터&amp;middot;결과&amp;middot;다음 행동 │
                  └──────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model은 개별 도구가 아니라 &lt;b&gt;하나의 완전한 성장 사이클&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 문서에서도 Model을 Observation &amp;rarr; Action &amp;rarr; Review 루프로 정의하고 있으며, 각 Model은 자신의 전용 Memory namespace를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 공개된 주요 Model은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;models/
├── onboard-growth-lab
├── run-seo-page-loop
└── xhs-replicate
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 것은 단순히 Skill을 여러 개 만들어놓은 것이 아니라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 SEO Model은&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;키워드 찾아줘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 끝나는 게 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 정의는 다음 사이클을 갖는다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;Read Memory
&amp;rarr; Observe
&amp;rarr; Decide
&amp;rarr; Act
&amp;rarr; Review
&amp;rarr; Write Memory
&amp;rarr; Next Observation
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 이전 실험 결과를 읽고, 현재 검색 시장을 다시 조사하고, 하나의 행동을 선택한 다음, 실제 페이지를 만들고 배포한 후 결과까지 다시 Memory에 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 Growth Lab의 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. SOUL.md: AI가 제품 자체를 기억한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab에는 특이한 파일이 하나 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;SOUL.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 대략 다음과 같은 빈 템플릿이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Product
- Name
- One-line description
- Product form
- Stage

Users and situations
- Current users
- Candidate users
- Situations of need
- Core job to be done

Value
- Problem solved
- Core value proposition
- Differentiation

Product reality
- Available capabilities
- Website and channels
- Analytics and data

Evidence and hypotheses
- Verified facts
- Active hypotheses
- Open questions
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 설계 원칙이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 처음 제품을 보고 모든 것을 상상해서 채워 넣지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품 코드를 읽고 확인할 수 있는 사실, 사용자가 직접 알려준 사실, 아직 검증되지 않은 가설을 구분한다. 프로젝트의 Runtime 지침 역시 증거가 없는 내용을 사실로 쓰지 말라고 명시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 시간이 지나면서 다음처럼 제품 이해가 쌓인다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1차 실행
제품 코드 분석
&amp;darr;
제품이 무엇을 하는지 확인

2차 실행
시장 조사
&amp;darr;
가능성이 높은 사용자 발견

3차 실행
SEO 실험
&amp;darr;
어떤 검색 의도가 실제 트래픽을 만드는지 발견

4차 실행
콘텐츠 실험
&amp;darr;
어떤 가치 제안에 반응하는지 발견
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 중 장기적으로 유효한 제품 지식은 다시 SOUL.md에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 이 방식은 일반적인 LLM의 대화 Memory라기보다 &lt;b&gt;제품에 대한 Agent-managed knowledge state&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Memory는 채팅 기록이 아니라 Growth 실험 로그다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Model은 별도의 Memory를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 SEO라면&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;memory/
└── run-seo-page-loop/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샤오홍슈 성장 루프라면&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;memory/
└── xhs-replicate/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에는 단순한 대화 요약이 아니라 다음 내용이 기록된다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;관찰 데이터
시장 조사 결과
선택한 전략
실행한 작업
게시 URL
실행 시점
Baseline
성과 데이터
분석
다음 행동
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Growth Agent는 다음 실행에서 과거 실험을 다시 읽을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;지난번에 무엇을 했지?
        &amp;darr;
어떤 결과가 나왔지?
        &amp;darr;
그때 세운 다음 가설은 무엇이었지?
        &amp;darr;
이번에는 무엇을 시험해야 하지?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 단발성 Prompt 사용과 가장 큰 차이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Collector는 Agent의 &amp;lsquo;눈&amp;rsquo; 역할을 한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 저장소의 collectors/에는 다음과 같은 수집 기능이 존재한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;bing-webmaster
media-crawler
media-crawler-bilibili
media-crawler-douyin
media-crawler-kuaishou
media-crawler-tieba
media-crawler-weibo
media-crawler-zhihu
research-product
research-seo-demand
xiaohongshu-mcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collector의 역할은 명확하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Agent가 의사결정을 내리는 데 필요한 Evidence를 가져오는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 SEO라면&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;검색 수요
검색 결과
경쟁 페이지
SERP 구조
현재 Ranking
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 데이터를 수집할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘텐츠 Growth에서는&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;경쟁 콘텐츠
고성과 콘텐츠
트렌드
콘텐츠 구조
사용자 반응
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등을 조사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Agent가 직접 검색이나 브라우저를 사용할 수 있다면 굳이 별도의 Client를 만들지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab은 Runtime이 이미 가진 검색&amp;middot;브라우저&amp;middot;파일&amp;middot;코드 기능을 우선 활용하도록 설계되어 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. Executor는 실제 행동을 담당한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collector가 조사 담당이라면 Executor는 실행 담당이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 공개된 Executor에는 다음과 같은 것들이 있다.&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;create-seo-page
generate-image
indexnow
review-seo-page
review-seo-performance
screenshot-assets
xhs-render-cards
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할이 상당히 구체적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 SEO 루프에서는 다음 흐름이 만들어진다.&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;research-seo-demand
        &amp;darr;
create-seo-page
        &amp;darr;
generate-image
        &amp;darr;
review-seo-page
        &amp;darr;
제품 테스트
        &amp;darr;
Deploy
        &amp;darr;
IndexNow
        &amp;darr;
review-seo-performance
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 SEO Model의 Skill에도 이 실행 순서가 정의되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 특히 흥미로운 부분은 review-seo-page다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 단순히&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이 글 괜찮아?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 묻는 구조가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경쟁 페이지와 검색 의도, 정보 밀도, 사용자 가치, 증거, 차별화 등을 기준으로 페이지를 다시 검토하도록 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEO 페이지를 만들기 전에 후보 Query마다 상위 3~5개 주요 페이지를 분석하도록 요구하며, 검색량이나 검색 결과 제목만 보고 바로 콘텐츠를 생성하는 것을 금지하고 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. SEO 자동화가 아니라 &amp;lsquo;SEO 학습 루프&amp;rsquo;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 SEO AI 도구의 구조는 흔히 이렇다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;Keyword
&amp;darr;
AI Article
&amp;darr;
Publish
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab은 훨씬 길다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;기존 Memory 확인
&amp;darr;
실제 검색 수요 조사
&amp;darr;
SERP 분석
&amp;darr;
경쟁 페이지 구조 분석
&amp;darr;
Information Gap 발견
&amp;darr;
하나의 Action 결정
&amp;darr;
페이지 구현
&amp;darr;
페이지 검수
&amp;darr;
배포
&amp;darr;
IndexNow
&amp;darr;
성과 측정
&amp;darr;
Memory 저장
&amp;darr;
다음 Action
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 차이는 마지막 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Publish가 종료 지점이 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성과를 다시 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음을 본다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Discovery
Ranking
CTR
Search Intent Fit
Content usefulness
Conversion
AI visibility
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 결과를 기반으로&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;페이지 개선
Snippet 변경
Evidence 추가
Conversion 수정
새 Supporting Page 생성
관찰 기간 연장
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중 하나를 다음 행동으로 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 Growth Loop다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. SNS 콘텐츠도 같은 구조로 처리한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 구현된 또 다른 대표 Model은 중국 SNS 플랫폼 샤오홍슈(Xiaohongshu)용 xhs-replicate다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 과정은 5단계다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;① Idea 탐색
&amp;darr;
② 참고 콘텐츠 선택 + 구조 분석
&amp;darr;
③ 실제 제품 내용 적용
&amp;darr;
④ 이미지 생성 + 자동 검사
&amp;darr;
⑤ 게시 결과 회수 + 회고
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에는 의외로 세밀한 규칙들이 들어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 인기 게시물을 참고한다고 해서 그대로 복제하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skill에서는 다음과 같은 요소만 이전하도록 제한한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;정보 계층
Proof 영역 비율
읽기 리듬
정보 밀도
Hook 구조
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 다음 요소는 복제하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;브랜드
원문 Copy
제품 UI
고유 Screenshot
고유 장식
정확한 Composition
전체 카드 순서
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 제품 기능이 없는 내용 역시 만들어내지 않도록 제한한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 참고 콘텐츠가 7장짜리 카드라고 해도 실제 제품 정보가 6장밖에 없다면 6장만 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 콘텐츠 게시 전에 자동 검사도 수행한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;check-compliance.py
check-banned-phrases.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전자는 과장 표현, 외부 유도, 허위 보장 등의 표현을 검사하고, 후자는 반복적인 AI 문체를 줄이기 위한 검사다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 사람이 완전히 빠지는 시스템은 아니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab이 흥미로운 또 다른 이유가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 것을 자동화하려 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 계정에 민감한 게시 작업에서는 Human-assisted publishing 방식을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;최종 콘텐츠
이미지
채널
게시 시점
설정
링크
게시 방법
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 포함하는 Publication Package를 만들고 사람이 실제 플랫폼 UI에서 게시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤 게시 URL이나 Screenshot을 다시 Agent에게 전달해 후속 분석을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샤오홍슈 Model 역시 자동 게시를 하지 않고 실제 게시 결과를 24시간, 48시간, 7일 시점에 Memory에 저장하도록 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 목표는&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Human 제거
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 아니라&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Human 판단이 필요한 곳만 남기기
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 또 하나의 중요한 결정: 데이터베이스가 없다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEO Model에는 꽤 과감한 설계 원칙이 명시돼 있다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;Create no fixed schema,
database,
dashboard,
workflow state,
or task queue.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 별도의&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PostgreSQL
Workflow DB
Queue
Dashboard
State Machine
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 기본 구조로 만들지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 파일 시스템이 상태를 담당한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;SOUL.md
SKILL.md
references/
memory/
outputs/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 이는 Coding Agent가 이미 파일 시스템을 자유롭게 탐색하고 수정할 수 있다는 전제에서는 상당히 합리적인 선택이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 서비스라면 상태를 다음처럼 구현해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Application
&amp;darr;
API
&amp;darr;
Database
&amp;darr;
Workflow Engine
&amp;darr;
Queue
&amp;darr;
Worker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab은 많은 부분을 다음으로 압축한다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;Coding Agent
&amp;darr;
Files
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 구조가 극도로 단순해진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 그래서 실제 사용법도 단순하다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치는 저장소를 Clone하는 것으로 시작한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;git clone https://github.com/tsingyuai/growth-lab.git
cd growth-lab
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 Codex 또는 Claude Code에서 해당 디렉터리를 연다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 자연어로 이야기한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 제품을 분석해줘.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 제품의 첫 번째 성장 루프를 실행해줘.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;검색 수요를 조사해서
가장 가능성 높은 SEO 페이지 하나를 만들어줘.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;최근 Growth 결과를 분석하고
다음 실험을 실행해줘.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent는 필요한 Model을 찾아 해당 SKILL.md를 읽고, 필요한 Collector와 Executor를 선택해 실행하도록 설계되어 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. API Key 관리도 Agent 방식이다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 정보는 repository 내부에 저장하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.env.local
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 Process Environment Variable을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.env와 .env.local은 Git에서 제외하도록 구성되어 있으며, 필요한 기능에 대한 Credential만 선택적으로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 샤오홍슈 수집은 API Key 방식이 아니라 Local MCP 서비스와 QR 로그인을 사용하고, 로그인 상태 역시 저장소 밖에 저장하도록 설계돼 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 역시 Git repository 자체를 Growth Workspace로 사용하기 위한 설계다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 이 프로젝트에서 가장 주목할 부분&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] Growth Lab에서 가장 흥미로운 부분은 SEO나 SNS 기능 자체보다 &lt;b&gt;Agent Application Architecture&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 Agent 시스템을 만들 때 흔히 다음부터 구현한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Workflow Builder
Agent DB
Vector DB
Task Queue
Scheduler
Admin Dashboard
Tool Router
Memory Server
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab은 반대로 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 Coding Agent가 가지고 있는 능력을 최대한 활용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Reasoning      &amp;rarr; Codex / Claude Code
Tool Calling   &amp;rarr; Coding Agent
Browser        &amp;rarr; Runtime
Code Editing   &amp;rarr; Runtime
Execution      &amp;rarr; Runtime
Memory         &amp;rarr; Files
Knowledge      &amp;rarr; Markdown
Methodology    &amp;rarr; Skill
Integration    &amp;rarr; Collector / Executor
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 시스템에서 정말 필요한 부분만 남는다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;무엇을 관찰해야 하는가
무엇을 판단해야 하는가
어떤 행동을 해야 하는가
결과를 어떻게 측정해야 하는가
무엇을 다음 실행에 기억해야 하는가
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Agent 인프라보다 &lt;b&gt;업무 방법론을 코드화하는 것&lt;/b&gt;에 집중한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 내가 이 프로젝트를 다시 정의한다면&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] Growth Lab을 단순히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI Growth Tool&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라고 부르는 것보다 다음 표현이 더 정확하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Coding Agent 위에서 실행되는 파일 기반 Growth Operating System&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 구조는 다음 공식으로 설명할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;Agent Runtime
+
Domain Skills
+
Tools
+
Persistent Memory
+
Real-world Feedback
=
Learning Growth Agent
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 중요한 것은 마지막의 Real-world Feedback이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM에게 계속 콘텐츠를 생성시킨다고 시스템이 발전하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab은 다음 사이클을 만들려고 한다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;AI가 가설 생성
&amp;darr;
실제 행동
&amp;darr;
실제 사용자 반응
&amp;darr;
데이터 수집
&amp;darr;
Memory
&amp;darr;
다음 판단
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 반복되면 단순한 콘텐츠 생성기가 아니라 제품별 Growth history를 가진 Agent가 만들어진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Growth Lab의 핵심은 AI에게 마케팅 글을 잘 쓰게 하는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Coding Agent가 제품을 이해하고 &amp;rarr; 시장을 관찰하고 &amp;rarr; 행동하고 &amp;rarr; 실제 결과를 학습하도록 만드는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 복잡한 Agent 플랫폼 대신 상당히 단순한 구조를 선택했다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;SOUL
    제품에 대한 장기 이해

Model
    Observe &amp;rarr; Act &amp;rarr; Review 방법론

Collector
    세상을 관찰하는 도구

Executor
    실제 행동을 수행하는 도구

Memory
    실제 실험과 결과의 기록

Codex / Claude Code
    전체를 실행하는 Runtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 공개 구현은 SEO 페이지 성장과 샤오홍슈 콘텐츠 성장에 집중되어 있다. README 역시 현재 SEO 페이지 루프와 샤오홍슈 수집&amp;middot;제작&amp;middot;회고 워크플로우를 주요 구현 기능으로 설명하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 그러나 구조 자체는 훨씬 범용적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 같은 패턴으로&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;YouTube Growth Model
App Store ASO Model
Instagram Growth Model
Product Hunt Launch Model
Email Marketing Model
Affiliate Growth Model
Community Growth Model
Paid Ads Model
Retention Model
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Model을 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 Growth Lab이 보여주는 아이디어는 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI Agent 시대에는 애플리케이션을 전부 새로 만들 필요가 없을 수도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Coding Agent 자체를 Runtime으로 사용하고, 우리가 정말 잘 만들어야 할 것은 그 위에서 실행되는 &lt;b&gt;업무 방법론, 도구, 그리고 실제 결과를 기억하는 Feedback Loop&lt;/b&gt;일 수 있다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1817</guid>
      <comments>https://javaexpert.tistory.com/1817#entry1817comment</comments>
      <pubDate>Tue, 25 Aug 2026 12:34:36 +0900</pubDate>
    </item>
    <item>
      <title>Kiro cli 기본 사용방법 정리</title>
      <link>https://javaexpert.tistory.com/1816</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 설치 &amp;rarr; 로그인 &amp;rarr; 프로젝트에서 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS/Linux에서는 공식 설치 명령이 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;curl -fsSL https://cli.kiro.dev/install | bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;kiro-cli login&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 상태 확인:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;kiro-cli whoami&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트로 이동해서:&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;cd ~/projects/my-project

kiro-cli&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 현재 디렉터리를 workspace로 삼아 Kiro가 실행됩니다. 일반적인 대화형 사용은 브라우저 로그인을 쓰며, API Key는 주로 CI/CD와 headless 모드용입니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 가장 기본적인 사용법은 그냥 자연어&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후에는 별도의 명령 문법을 배울 필요 없이 요청하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&amp;gt; 이 프로젝트 구조를 분석해줘.

&amp;gt; 현재 인증 시스템이 어떻게 동작하는지 설명해줘.

&amp;gt; 로그인 API에서 발생할 수 있는 보안 문제를 찾아줘.

&amp;gt; 이 기능을 구현해줘.

&amp;gt; 테스트까지 작성하고 실행해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro는 필요에 따라 파일을 읽고, 코드를 수정하고, shell 명령을 실행합니다. 위험하거나 권한이 필요한 작업에는 승인 절차가 들어갑니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/setup/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. @파일은 반드시 알아두기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 파일을 정확하게 대상으로 지정할 때는 @를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;&amp;gt; @src/api/routes.ts 코드를 리뷰해줘.

&amp;gt; @package.json 을 보고 현재 기술 스택을 설명해줘.

&amp;gt; @src/auth/login.ts 에서 발생할 수 있는 버그를 찾아줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tab 자동완성도 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능은 꽤 중요합니다. 에이전트가 알아서 프로젝트 전체를 뒤지게 하는 것보다 &lt;b&gt;내가 원하는 파일을 명시적으로 context로 주는 것&lt;/b&gt;이 빠르고 명확한 경우가 많습니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/setup/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Shift + Tab &amp;mdash; Plan Mode&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 작업에서는 바로 코딩을 시키기보다 이것을 많이 쓰는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Shift + Tab&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 Plan mode가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;[plan] &amp;gt; 기존 JWT 인증 시스템을 OAuth + JWT 구조로 변경해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 코드를 바꾸는 대신 먼저 구현 계획을 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. 현재 인증 구조 분석
2. OAuth provider abstraction 설계
3. DB schema 변경
4. API route 수정
5. refresh token 처리
6. migration
7. 테스트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계획을 확인한 다음 실행 단계로 넘어갈 수 있습니다. 공식 문서에서도 복잡한 변경 전에 Plan mode를 사용하는 흐름을 안내합니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/setup/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작은 수정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;버튼 색깔 바꿔줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 일반 모드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;큰 수정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;인증 구조 전체 리팩터링
결제 시스템 추가
DB schema 변경&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; Shift+Tab 권장&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. /spec &amp;mdash; Kiro를 쓰는 중요한 이유 중 하나&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상당히 큰 기능이라면 Plan보다 한 단계 더 체계적인 &lt;b&gt;Spec workflow&lt;/b&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;/spec new auth-system&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고:&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;JWT 인증 + Role 기반 권한 시스템을 구현하고 싶어.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro는 다음 구조를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;.kiro/specs/auth-system/

├── requirements.md
├── design.md
└── tasks.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름은:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;요구사항
   &amp;darr;
설계
   &amp;darr;
Task 분해
   &amp;darr;
구현
   &amp;darr;
검증&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/v3/specs/?utm_source=chatgpt.com&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Spec 보기:&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;/spec&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Spec 계속하기:&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;/spec auth-system&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task 전체 실행:&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;/spec run auth-system&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro의 Spec은 CLI와 IDE가 같은 .kiro/specs/를 사용하므로 CLI에서 만들고 IDE에서 이어서 작업할 수도 있습니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/v3/specs/?utm_source=chatgpt.com&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 구분한다면:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업사용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;작은 수정&lt;/td&gt;
&lt;td&gt;일반 Prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;약간 복잡&lt;/td&gt;
&lt;td&gt;Shift+Tab Plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;큰 Feature&lt;/td&gt;
&lt;td&gt;/spec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반복하면서 완료까지 밀어붙이기&lt;/td&gt;
&lt;td&gt;/goal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. /goal &amp;mdash; &quot;될 때까지 반복해&quot;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro CLI에서 특히 알아둘 만한 기능입니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/goal 로그인 API를 리팩터링하고 모든 테스트가 통과하도록 해줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro가 한 번 작업하고 끝나는 것이 아니라:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;구현
 &amp;darr;
테스트
 &amp;darr;
실패
 &amp;darr;
수정
 &amp;darr;
다시 테스트
 &amp;darr;
완료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식으로 반복합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 최대 iteration은 5회입니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/goal --max 10 Jest 테스트를 전부 Vitest로 마이그레이션하고 모든 테스트가 통과하도록 해줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중단:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/goal clear&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 &lt;b&gt;완료 조건을 구체적으로 써주는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋지 않은 예:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/goal 코드를 개선해줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 예:&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;/goal auth 모듈을 리팩터링하고
npm test가 모두 통과하며
TypeScript error가 0개가 되도록 해줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서도 /goal에서는 &amp;ldquo;all tests pass&amp;rdquo;, &amp;ldquo;no TypeScript errors&amp;rdquo; 같은 명확한 acceptance criteria를 주는 것을 권장합니다. (&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. !명령어 &amp;mdash; AI를 거치지 않고 바로 Shell 실행&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLI 안에서:&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;!npm run build&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;!npm test&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;diff&quot;&gt;&lt;code&gt;!git status&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 하면 Kiro에게 &amp;ldquo;이 명령을 실행해줘&amp;rdquo;라고 요청하는 것이 아니라 &lt;b&gt;직접 Shell 명령을 실행&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;&amp;gt; 로그인 기능 수정해줘.

Kiro 작업 완료

&amp;gt; !npm test&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 쓰면 편합니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/chat/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. /context &amp;mdash; AI가 무엇을 보고 있는지 확인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 에이전트에서 아주 중요한 명령입니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;/context show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 context에 어떤 파일이 들어가 있고 얼마나 token을 사용하는지 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 추가:&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;/context add src/app.js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Glob도 됩니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;/context add &quot;src/**/*.ts&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제거:&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;/context remove src/app.js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 제거:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/context clear&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로젝트가 커지면:&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;/context add &quot;src/auth/**/*.ts&quot;
/context add &quot;src/db/**/*.ts&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 &lt;b&gt;현재 작업에 필요한 영역만 넣는 방식&lt;/b&gt;이 유용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. .kiro/steering/ &amp;mdash; 프로젝트 규칙을 Kiro에게 기억시키기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 다음처럼 말하고 싶지 않을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;TypeScript 사용해
pnpm 사용해
Vitest 사용해
Server Component 우선해
any 사용하지 마&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 프로젝트 규칙을 Steering에 넣습니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;.kiro/
└── steering/
    └── project.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# Project Rules

- Next.js 15 + TypeScript
- Package manager: pnpm
- Database: PostgreSQL
- ORM: Drizzle
- Testing: Vitest
- Avoid `any`
- Prefer server components
- API responses follow REST conventions&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro는 workspace의 .kiro/steering/ 내용을 프로젝트 컨텍스트로 사용할 수 있습니다. 전역 규칙은:&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;~/.kiro/steering/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 둘 수 있고, workspace 규칙이 global 규칙보다 우선합니다. (&lt;a href=&quot;https://kiro.dev/docs/steering/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AGENTS.md도 지원합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;project/
├── AGENTS.md
├── src/
└── .kiro/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro는 프로젝트 루트의 AGENTS.md도 자동으로 인식합니다. (&lt;a href=&quot;https://kiro.dev/docs/steering/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. /model &amp;mdash; 모델 변경&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 모델 선택:&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;/model&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 모델 지정:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/model claude-opus-4.6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 모델을 기본값으로:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;/model set-current-as-default&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 CLI에서는 Tab completion도 지원합니다. (&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. /agent &amp;mdash; 작업별 전문 Agent 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 항상 같은 방식으로 코드 리뷰를 한다면:&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;/agent create code-reviewer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명을 지정할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;/agent create code-reviewer -D &quot;Security and code quality reviewer&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent 목록:&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;/agent list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전환:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;/agent swap code-reviewer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편집:&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;/agent edit code-reviewer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 Agent 지정:&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;/agent set-default code-reviewer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 전용 Agent:&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;.kiro/agents/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 Agent:&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;~/.kiro/agents/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 저장됩니다. 현재 공식 문서는 JSON과 Markdown 형식을 모두 지원한다고 설명합니다. (&lt;a href=&quot;https://kiro.dev/docs/custom-agents/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의할 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Custom Agent에서는 Steering이 자동으로 포함되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 Agent configuration에:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;resources&quot;: [
    &quot;file://.kiro/steering/**/*.md&quot;
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 넣어야 합니다. 이 부분은 놓치기 쉽습니다. (&lt;a href=&quot;https://kiro.dev/docs/steering/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. Skills &amp;mdash; 반복 작업을 /명령으로 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro도 Agent Skills를 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트용:&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;.kiro/skills/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역:&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;~/.kiro/skills/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;.kiro/skills/pr-review/
└── SKILL.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 CLI에서:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/pr-review&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro는 Skill description과 요청이 맞으면 자동으로 활성화할 수도 있습니다. (&lt;a href=&quot;https://kiro.dev/docs/skills/?utm_source=chatgpt.com&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Steering
= 프로젝트 규칙

Agent
= 역할

Skill
= 작업 절차&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 이해하면 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;.kiro/

steering/
  coding-rules.md

agents/
  backend.md
  reviewer.md

skills/
  pr-review/
  deploy/
  db-migration/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 구조가 가능합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. /tools &amp;mdash; 권한 관리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro가 계속:&lt;/p&gt;
&lt;pre class=&quot;apache&quot;&gt;&lt;code&gt;Allow?
[y/n]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 물어보면 도구를 신뢰하도록 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Tool 확인:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/tools&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 Tool trust:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;/tools trust write&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 승인 필요:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;/tools untrust write&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 trust:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;/tools trust-all&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/tools reset&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 trust-all보다는 필요한 권한만 주는 방식이 안전합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. /chat, /rewind, /compact&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 개도 실제 사용 빈도가 높습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 대화:&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;/chat new&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 작업 계속:&lt;/p&gt;
&lt;pre class=&quot;hsp&quot;&gt;&lt;code&gt;/chat resume&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLI 시작할 때 바로 resume:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;kiro-cli chat --resume&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 session:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;kiro-cli chat --resume-id SESSION_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://kiro.dev/docs/cli/chat/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화가 엉뚱한 방향으로 갔다면:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/rewind&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 시점:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/rewind 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 대화를 파괴하는 것이 아니라 &lt;b&gt;그 지점에서 새로운 conversation branch를 만듭니다.&lt;/b&gt; (&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context가 너무 길어졌다면:&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;/compact&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화 핵심을 요약하고 context 공간을 확보합니다. (&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. /guide &amp;mdash; 명령어를 외울 필요가 없게 해주는 기능&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 처음 Kiro CLI를 사용할 때 상당히 유용합니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/guide&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고:&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;How do I configure MCP?

이 프로젝트에 적합한 steering을 만들어줘.

code review agent를 만들어줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 물을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Guide Agent는 현재 설치된 Kiro CLI에 맞는 문서를 바탕으로 설명하고 .kiro/ 설정 파일까지 만들어줄 수 있습니다. 원래 Agent로 돌아갈 때는 Shift+Tab 또는 /agent swap을 사용할 수 있습니다. (&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. MCP&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub, DB, API, 각종 외부 도구를 붙일 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 상태:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/mcp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 문제:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;/mcp auth my-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그아웃:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;/mcp logout my-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro의 MCP는 외부 데이터나 도구를 Agent에게 연결하는 역할입니다. (&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. Headless &amp;mdash; CI/CD 자동화&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kiro CLI는 터미널 대화 없이 한 번 실행하는 것도 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;kiro-cli chat --no-interactive \
  &quot;이 프로젝트의 보안 문제를 찾아줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Read/grep만 허용:&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;kiro-cli chat \
  --no-interactive \
  --trust-tools=read,grep \
  &quot;src의 TODO를 전부 찾아줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Tool 허용:&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;kiro-cli chat \
  --no-interactive \
  --trust-all-tools \
  &quot;테스트를 만들고 실행해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://kiro.dev/docs/cli/headless/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CI에서는:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export KIRO_API_KEY=ksk_xxxxx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;git diff | kiro-cli chat \
  --no-interactive \
  --trust-tools=read,grep \
  &quot;이 변경사항을 코드 리뷰해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 PR Review 자동화도 가능합니다. 공식 문서 역시 CI에서는 API Key를 secret으로 저장하고 --trust-all-tools보다 필요한 tool만 지정하는 것을 권장합니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/headless/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;18. 알아두면 좋은 단축키&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키기능&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shift+Tab&lt;/td&gt;
&lt;td&gt;Plan mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shift+Enter&lt;/td&gt;
&lt;td&gt;여러 줄 Prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+J&lt;/td&gt;
&lt;td&gt;여러 줄 입력, 대부분 Terminal 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+G&lt;/td&gt;
&lt;td&gt;Subagent monitor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+O&lt;/td&gt;
&lt;td&gt;긴 Shell output 펼치기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+S&lt;/td&gt;
&lt;td&gt;명령/context 검색 및 queue steering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Esc&lt;/td&gt;
&lt;td&gt;Agent 작업 취소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tab&lt;/td&gt;
&lt;td&gt;autocomplete / 승인 세부 옵션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+D&lt;/td&gt;
&lt;td&gt;CLI 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://kiro.dev/docs/reference/slash-commands/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;제가 생각하는 Kiro CLI의 핵심 사용 흐름&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 프로젝트를 열었으면 다음 순서가 가장 이해하기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;cd project
     &amp;darr;
kiro-cli
     &amp;darr;
/guide
     &amp;darr;
프로젝트 분석
     &amp;darr;
.kiro/steering 작성
     &amp;darr;
@파일 + 일반 Prompt
     &amp;darr;
복잡하면 Shift+Tab
     &amp;darr;
큰 Feature면 /spec
     &amp;darr;
반복 실행이 필요하면 /goal
     &amp;darr;
테스트
     &amp;darr;
/rewind 또는 수정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &lt;b&gt;Steering + Plan + Spec + Goal + Skills&lt;/b&gt; 이 다섯 개를 이해하면 Kiro CLI를 단순한 Claude Code/Codex류의 &amp;ldquo;AI 터미널&amp;rdquo;이 아니라 &lt;b&gt;프로젝트 개발 워크플로우 시스템&lt;/b&gt;으로 사용할 수 있습니다. Kiro 공식 문서도 CLI의 핵심 능력으로 Steering, Hooks, MCP, Custom Agents, Skills, Sub-agents, Models, Permissions 등을 같은 기반 기능으로 설명하고 있습니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이것만 외워도 됩니다&lt;/h2&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 시작
kiro-cli

# 도움말
/guide

# 파일 지정
@src/auth.ts

# 계획
Shift+Tab

# 대형 기능
/spec new feature-name
/spec run feature-name

# 자율 반복
/goal --max 10 목표 + 완료조건

# Context
/context show

# 모델
/model

# Agent
/agent list
/agent swap agent-name

# 세션
/chat new
/chat resume

# 되돌리기
/rewind

# Context 압축
/compact

# 권한
/tools

# 비용 확인
/usage

# Shell
!npm test&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도가 &lt;b&gt;Kiro CLI 입문 시 실제로 먼저 익힐 80~90%의 기능&lt;/b&gt;이라고 보면 됩니다. 특히 Kiro 특유의 기능을 하나만 꼽는다면 /spec, 그리고 최근 CLI에서 실전성이 높은 기능을 꼽는다면 /goal입니다. (&lt;a href=&quot;https://kiro.dev/docs/cli/v3/specs/?utm_source=chatgpt.com&quot;&gt;Kiro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kiro.dev/docs/cli/?utm_source=chatgpt.com&quot;&gt;Kiro CLI 공식 문서&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://kiro.dev/docs/cli/reference/slash-commands/?utm_source=chatgpt.com&quot;&gt;Kiro CLI Slash Commands&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1816</guid>
      <comments>https://javaexpert.tistory.com/1816#entry1816comment</comments>
      <pubDate>Tue, 25 Aug 2026 12:21:04 +0900</pubDate>
    </item>
    <item>
      <title>시니어 엔지니어에서 스태프 엔지니어로 성장</title>
      <link>https://javaexpert.tistory.com/1815</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;시니어 엔지니어에서 스태프 엔지니어(Staff Engineer)로 역할이 확장될 때 가장 크게 달라지는 점 중 하나는 &lt;b&gt;주어진 문제를 잘 푸는 것만으로는 충분하지 않다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스태프 레벨에서는 한 단계 더 나아가야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;우리 팀과 조직이 지금 무엇을 해결해야 하는가?&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lalit Maganti가 자신의 글 How I Find Problems to Solve as a Staff Engineer에서 설명하는 핵심도 바로 여기에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그가 강조하는 방식은 의외로 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회의실에 앉아 억지로 &amp;ldquo;전략적인 아이디어&amp;rdquo;를 만들어내는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직에서 매일 발생하는 문제를 지속적으로 관찰하고, 충분한 시간 동안 축적한 뒤, 서로 다른 문제 사이에서 &lt;b&gt;공통된 구조(Common Shape)&lt;/b&gt;를 발견하는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 전략은 빈 문서에서 시작되지 않는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 엔지니어가 스태프 엔지니어가 되면 일정 시간을 따로 확보해 다음과 같은 질문을 고민해야 한다고 생각한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우리 조직의 가장 중요한 문제는 무엇인가?&lt;/li&gt;
&lt;li&gt;다음 분기에 무엇을 만들어야 하는가?&lt;/li&gt;
&lt;li&gt;어떤 프로젝트를 제안해야 하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Maganti는 이런 방식으로 좋은 문제를 찾는 경우가 거의 없다고 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 자신을 &lt;b&gt;스펀지처럼 만든다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일상적인 업무 속에서 흘러다니는 정보를 계속 흡수한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 곳이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회의&lt;/li&gt;
&lt;li&gt;Slack이나 사내 채팅&lt;/li&gt;
&lt;li&gt;이메일&lt;/li&gt;
&lt;li&gt;발표&lt;/li&gt;
&lt;li&gt;장애 대응&lt;/li&gt;
&lt;li&gt;버그 분석&lt;/li&gt;
&lt;li&gt;다른 팀의 불만&lt;/li&gt;
&lt;li&gt;개발자가 사용하는 우회 방법&lt;/li&gt;
&lt;li&gt;반복적으로 발생하는 운영 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 들었을 때는 별것 아닌 문제처럼 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 바로 해결하려 하지 않고 머릿속에 남겨두면 시간이 지나면서 일부 문제는 사라지고, 일부 문제는 다른 문제와 연결되기 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 어느 순간 이런 질문을 할 수 있게 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이 문제들이 사실은 같은 원인에서 발생하는 것 아닐까?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스태프 엔지니어의 문제 발견 능력은 여기서 시작된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 요청(Request)이 아니라 문제(Problem)를 수집한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 자신의 문제를 항상 정확하게 설명하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분은 문제 대신 &lt;b&gt;해결책을 요청한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 이렇게 말할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이 버튼을 화면 위쪽에 고정할 수 있게 해주세요.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 그대로 기능 요구사항으로 받아들이면 다음과 같은 구현이 시작된다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Request
&amp;darr;
버튼 고정 기능
&amp;darr;
개발
&amp;darr;
배포
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 스태프 엔지니어는 한 단계 더 들어간다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Request
&amp;darr;
왜 필요한가?
&amp;darr;
어떤 작업을 하려고 하는가?
&amp;darr;
현재 방식은 왜 불편한가?
&amp;darr;
다른 팀도 같은 문제가 있는가?
&amp;darr;
Root Problem
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문도 달라진다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;왜 이 기능이 필요한가요?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;지금은 이 작업을 어떻게 하고 있나요?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이 기능이 생기면 어떤 문제가 해결되나요?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;기존 기능으로는 어느 정도까지 해결할 수 있나요?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청을 구현하는 것과 문제를 이해하는 것은 완전히 다른 활동이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스태프 엔지니어에게 더 중요한 것은 후자다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 직접 워크플로를 관찰한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 중요해 보이면 Maganti는 단순히 인터뷰만 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 사용자가 문제를 겪는 과정을 직접 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 개발팀이 특정 디버깅 문제를 이야기한다면 옆에 앉아서 다음 과정을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Bug 발생
&amp;darr;
로그 검색
&amp;darr;
Trace 분석
&amp;darr;
특정 데이터 찾기
&amp;darr;
도구 설정 변경
&amp;darr;
반복 작업
&amp;darr;
원인 발견
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하면 자신도 같은 버그를 직접 분석해 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 관찰이 중요한 이유는 사용자가 말한 문제와 실제 문제가 다를 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;필터 기능이 필요합니다.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 말하지만 실제로 관찰해 보면 필요한 것은&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;매번 같은 분석 환경을 다시 만드는 작업을 없애는 것&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;Requested Solution &amp;ne; Actual Need
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일 가능성이 상당히 높다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 조직을 넓게 보는 사람에게서 신호를 얻는다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신이 경험한 문제만 관찰해서는 조직 전체의 패턴을 발견하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Maganti는 다음과 같은 사람들과 의도적으로 이야기한다고 설명한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;핵심 시스템 담당자&lt;/li&gt;
&lt;li&gt;여러 팀을 지원하는 엔지니어&lt;/li&gt;
&lt;li&gt;플랫폼 엔지니어&lt;/li&gt;
&lt;li&gt;인프라 엔지니어&lt;/li&gt;
&lt;li&gt;downstream 시스템을 잘 아는 사람&lt;/li&gt;
&lt;li&gt;여러 조직의 문제를 반복해서 접하는 사람&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들은 일종의 &lt;b&gt;Problem Router&lt;/b&gt; 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 팀에서는&lt;/p&gt;
&lt;pre class=&quot;dns&quot;&gt;&lt;code&gt;Problem A
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 보였던 것이 다른 팀에서는&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Problem B
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여러 팀을 보는 사람은 이미 이렇게 인식하고 있을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Team A &amp;rarr; Problem A ┐
Team B &amp;rarr; Problem B ├&amp;rarr; Root Problem X
Team C &amp;rarr; Problem C ┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스태프 엔지니어에게 조직 내 네트워크가 중요한 이유도 단순히 정치적 영향력을 얻기 위해서가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;더 많은 문제 신호에 접근하기 위해서다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 문제를 바로 프로젝트로 만들지 않는다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 원칙이 하나 나온다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제를 충분히 쌓이게 둔다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maganti는 과거 특정 팀의 요청에 너무 빨리 반응했다가 실패했던 경험을 소개한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 팀이 특정 기능을 강하게 원했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기능을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 정작 배포했을 때 거의 사용하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 사이 팀의 우선순위가 바뀌었거나, 애초에 일회성 조사 때문에 필요했던 기능이었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험에서 중요한 구분이 나온다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;요청의 강도 &amp;ne; 문제의 중요도
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가 매우 적극적으로 요구한다고 해서 반드시 중요한 문제는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 문제를 처음 발견했을 때 바로 개발 프로젝트로 만들지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 일종의 &lt;b&gt;Problem Backlog&lt;/b&gt;에 넣어둔다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 문제에도 증거가 누적되어야 한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 지나면서 같은 문제가 다시 나타나는지 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 처음에는 이렇게 보인다.&lt;/p&gt;
&lt;pre class=&quot;dns&quot;&gt;&lt;code&gt;Team A &amp;rarr; Problem A
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 달 뒤 다른 팀에서도 나타난다.&lt;/p&gt;
&lt;pre class=&quot;dns&quot;&gt;&lt;code&gt;Team A &amp;rarr; Problem A
Team B &amp;rarr; Problem A
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 형태로 또 나타난다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Team C &amp;rarr; Problem B
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 분석해 보면 사실 이런 구조일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Problem A ┐
Problem A ├&amp;rarr; Root Problem X
Problem B ┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때부터 문제의 중요도가 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 문제의 우선순위는 단순한 목소리의 크기가 아니라 &lt;b&gt;증거의 누적&lt;/b&gt;으로 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 간단하게 모델링하면 다음과 같이 생각할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;Problem Priority
=
Frequency
&amp;times; Number of Teams
&amp;times; Severity
&amp;times; Strategic Relevance
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 공식이 중요한 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 문제를 처음 발견한 순간보다 &lt;b&gt;반복해서 발견되는 패턴&lt;/b&gt;을 중요하게 보는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 가장 중요한 순간: Common Shape 찾기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maganti가 설명하는 과정에서 가장 중요한 단계가 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;여러 문제의 공통된 형태를 발견하는 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Perfetto 사례가 이를 잘 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Perfetto는 시스템의 동작 기록을 타임라인으로 분석하는 성능 디버깅 도구다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 팀이 다음과 같은 기능을 요청했다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Team A
&amp;rarr; 특정 Track을 항상 위에 고정하고 싶다.

Team B
&amp;rarr; 다른 Track을 고정하고 싶다.

Team C
&amp;rarr; 특정 위치로 자동 Zoom하고 싶다.

Team D
&amp;rarr; 특정 Aggregation을 자동으로 보고 싶다.

Team E
&amp;rarr; Bookmarklet으로 직접 자동화하고 있다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로 보면 모두 다른 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 하나씩 대응하면 이런 제품이 된다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Feature A
Feature B
Feature C
Feature D
Feature E
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 충분히 문제를 쌓아놓고 보면 공통점이 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 팀이 원한 것은 개별 기능이 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자의 업무 방식에 맞게 &lt;b&gt;Perfetto를 개인화하고 확장할 수 있는 능력&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 문제의 구조가 바뀐다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Track 고정 ┐
Auto Zoom  │
Aggregation├&amp;rarr; UI 확장성
Automation │
Bookmarklet┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 Common Shape다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 좋은 플랫폼은 기능을 대신 만들어주지 않는다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 통찰은 플랫폼 엔지니어링에서 특히 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 접근은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;Team A 요청 &amp;rarr; Platform Team 개발
Team B 요청 &amp;rarr; Platform Team 개발
Team C 요청 &amp;rarr; Platform Team 개발
Team D 요청 &amp;rarr; Platform Team 개발
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서는 플랫폼 팀이 병목이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 늘어날수록 유지보수 비용도 계속 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 공통 문제를 발견하면 구조가 달라진다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Team A ┐
Team B │
Team C ├&amp;rarr; Extension Platform
Team D │
Team E ┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플랫폼팀이 모든 기능을 구현하는 대신 다른 팀이 자신의 요구를 해결할 수 있는 &lt;b&gt;Primitive&lt;/b&gt;를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maganti가 Perfetto에서 도달한 방향도 이런 형태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글에 따르면 그는 이후 UI 동작을 자동화할 수 있는 &lt;b&gt;Macros&lt;/b&gt;와 이를 팀 간 공유할 수 있는 &lt;b&gt;Extension Servers&lt;/b&gt;를 설계했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Build Features
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Build Capability
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 문제의 추상화 수준이 올라간 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이는 매우 크다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 그러나 &amp;lsquo;멋진 추상화&amp;rsquo;는 증거가 아니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 위험한 함정이 하나 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔지니어는 우아한 설계를 좋아한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 문제를 하나의 구조로 설명할 수 있는 순간 이런 생각이 들기 쉽다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;찾았다.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Maganti는 경고한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통된 구조는 어디까지나 가설이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Elegant Architecture &amp;ne; Correct Problem
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그가 소개한 또 다른 Perfetto 사례에서는 대용량 trace 공유 문제와 반복 query 문제를 하나의 투명한 캐시 시스템으로 해결하려 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 두 문제를 하나로 해결하는 매우 우아한 설계처럼 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 RFC와 프로토타입을 만들면서 실제로는 서로 다른 문제라는 사실이 드러났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 글에 따르면 해결책은 분리됐다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Repeated Query
&amp;rarr; Warm Session

Large Trace Reopen
&amp;rarr; Streaming Table Export
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사례에서 중요한 것은 &lt;b&gt;추상화를 사랑하지 않는 태도&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔지니어가 만든 개념보다 실제 문제가 더 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 구현 전에 Pressure Test 한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Common Shape을 발견했다고 바로 개발하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계는 가설 검증이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maganti는 상황에 따라 세 가지 방식으로 접근한다고 설명한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;① 작고 안전한 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 구현한다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Low Risk
+
Clear Value
&amp;darr;
Ship
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;② 불확실한 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버려도 되는 프로토타입을 만든다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Hypothesis
&amp;darr;
Throwaway Prototype
&amp;darr;
Feedback
&amp;darr;
Technical Risk
&amp;darr;
Decision
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입의 목적은 제품을 만드는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;틀렸다는 사실을 싸게 발견하는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;③ 크지만 확신이 높은 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RFC, 설계 검토, 조직 설득까지 들어간다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Problem
&amp;darr;
Evidence
&amp;darr;
Proposal
&amp;darr;
RFC
&amp;darr;
1:1
&amp;darr;
Presentation
&amp;darr;
Feedback
&amp;darr;
Refinement
&amp;darr;
Implementation
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스태프 엔지니어의 중요한 역할 중 하나가 바로 이 단계다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 스스로의 아이디어도 설득해야 한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pressure Test의 목적은 다른 사람을 설득하는 것만이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신도 설득해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 질문을 계속 던져야 한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이 문제가 정말 중요한가?

여러 팀에게 발생하는가?

기존 도구로 해결할 수 없는가?

우리가 해결해야 하는 문제인가?

기술적으로 가능한가?

유지보수 비용은 감당할 수 있는가?

지금 해결해야 하는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 프로젝트를 포기할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것은 실패가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 큰 시스템을 만들기 전에 잘못된 가설을 제거한 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 타이밍이 틀렸다면 문제를 버리지 않는다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 아이디어라도 조직의 현재 우선순위와 맞지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 바로 실행하지 않고 보관한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Good Problem
+
Wrong Timing
&amp;darr;
Park
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 환경이 바뀌면 다시 꺼낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Org Priority Change
&amp;darr;
Existing Problem Evidence
&amp;darr;
Ready Proposal
&amp;darr;
Fast Execution
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 스태프 레벨에서 상당히 중요한 능력이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 새로운 아이디어를 만드는 것이 아니라 &lt;b&gt;이미 충분히 연구된 문제를 적절한 순간에 실행하는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 반드시 자신이 구현할 필요는 없다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스태프 엔지니어의 영향력은 코드량으로 측정되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 발견하고 구조화한 뒤 다른 팀이 구현하게 만들 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Problem Discovery
&amp;darr;
Problem Framing
&amp;darr;
Solution Direction
&amp;darr;
Org Alignment
&amp;darr;
Another Engineer Implements
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우에도 상당한 기술적 영향력을 만들어낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스태프 엔지니어의 역할은&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;I built X
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 점차&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;X became the right thing for the organization to build
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 확대된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. 문제 해결은 새로운 문제를 가져오는 플라이휠을 만든다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에는 흥미로운 네트워크 효과가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가의 문제를 진지하게 듣고 실제로 해결해 주면 사람들이 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 다음번에는 문제가 더 일찍 공유된다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;문제 발견
&amp;darr;
좋은 질문
&amp;darr;
문제 해결
&amp;darr;
신뢰 증가
&amp;darr;
더 많은 문제 공유
&amp;darr;
조직 가시성 증가
&amp;darr;
더 좋은 문제 발견
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 하나의 &lt;b&gt;Problem Discovery Flywheel&lt;/b&gt;이 만들어진다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Listen
  &amp;darr;
Understand
  &amp;darr;
Solve
  &amp;darr;
Trust
  &amp;darr;
More Signals
  &amp;darr;
Better Patterns
  &amp;darr;
Better Problems
  ↺
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경력이 올라갈수록 스태프 엔지니어가 중요한 문제를 더 빨리 발견하는 이유도 단순히 경험이 많기 때문만은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직 안에서 문제 신호가 그 사람에게 모이기 시작하기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. Staff Engineer의 핵심 루프&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 내용을 하나의 루프로 압축하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;① Listen
조직의 문제를 듣는다
        &amp;darr;
② Dig
요청 뒤의 실제 문제를 파고든다
        &amp;darr;
③ Observe
실제 워크플로를 본다
        &amp;darr;
④ Accumulate
문제를 바로 해결하지 않고 축적한다
        &amp;darr;
⑤ Connect
반복되는 문제 사이의 연결을 찾는다
        &amp;darr;
⑥ Abstract
Common Shape을 발견한다
        &amp;darr;
⑦ Pressure Test
인터뷰&amp;middot;RFC&amp;middot;Prototype으로 검증한다
        &amp;darr;
⑧ Build / Park / Kill
구현하거나 보류하거나 폐기한다
        &amp;darr;
⑨ Earn Trust
문제를 해결하면서 신뢰를 만든다
        &amp;darr;
⑩ More Signals
더 많은 문제 정보가 들어온다
        ↺
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 루프가 반복될수록 개인의 영향력은 커진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. Senior와 Staff의 차이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글의 관점에서 Senior Engineer와 Staff Engineer의 차이를 단순화하면 다음과 같이 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Senior EngineerStaff Engineer&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;어려운 문제를 해결한다&lt;/td&gt;
&lt;td&gt;어떤 문제를 해결해야 하는지 찾는다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주어진 프로젝트를 성공시킨다&lt;/td&gt;
&lt;td&gt;프로젝트 자체를 만든다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;요구사항을 구현한다&lt;/td&gt;
&lt;td&gt;요구사항 뒤의 문제를 찾는다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;한 팀의 문제를 본다&lt;/td&gt;
&lt;td&gt;여러 팀의 패턴을 본다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature를 만든다&lt;/td&gt;
&lt;td&gt;Capability를 만든다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation 중심&lt;/td&gt;
&lt;td&gt;Problem Framing 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;프로젝트 영향&lt;/td&gt;
&lt;td&gt;조직 영향&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 실제 조직마다 역할 정의는 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Maganti 자신도 글에서 자신의 경험이 Google과 같은 대규모 조직의 인프라&amp;middot;개발자 도구 팀처럼 엔지니어의 bottom-up 자율성이 큰 환경에 기반한다고 전제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 모든 회사에서 동일한 방식으로 적용된다고 볼 수는 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. 이 접근법을 실제 업무 시스템으로 만들면&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 개인의 직관에만 의존할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 &lt;b&gt;Problem Discovery System&lt;/b&gt;으로 구조화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문제를 발견할 때 다음 정보를 기록한다.&lt;/p&gt;
&lt;pre class=&quot;vhdl&quot;&gt;&lt;code&gt;Problem
 ├─ Source
 ├─ Team
 ├─ Context
 ├─ Requested Solution
 ├─ Actual Problem
 ├─ Frequency
 ├─ Severity
 ├─ Workaround
 ├─ Similar Problems
 ├─ Evidence
 └─ Status
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 상태를 관리한다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Observed
&amp;darr;
Repeated
&amp;darr;
Pattern Found
&amp;darr;
Investigating
&amp;darr;
Validated
&amp;darr;
Proposed
&amp;darr;
Building
&amp;darr;
Solved
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 아이디어 관리가 아니라 &lt;b&gt;문제 관리&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 아이디어 백로그가&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;What should we build?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 기록한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Problem Backlog는&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;What keeps hurting people?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품팀이나 플랫폼팀이라면 후자가 훨씬 강력한 자산이 될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서 가장 중요한 메시지는 &amp;ldquo;스태프 엔지니어는 전략 회의를 많이 해야 한다&amp;rdquo;가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 반대에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 전략적 문제는 일상적인 업무와 분리된 곳에서 갑자기 등장하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;사람들의 불편
+
반복되는 작업
+
우회 방법
+
여러 팀의 요청
+
기술적 제약
+
시간
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것들이 충분히 축적될 때 중요한 패턴이 보이기 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 스태프 엔지니어의 역할은 그 수많은 신호 속에서&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;서로 다른 문제처럼 보이지만 사실 같은 문제다.&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 순간을 발견하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 스태프 엔지니어의 핵심 경쟁력은 단순히 더 어려운 코드를 작성하는 능력만이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제를 수집하고, 문제를 기다리고, 문제를 연결하고, 올바른 추상화 수준에서 문제를 다시 정의하는 능력.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 능력이 개인의 기술적 영향력을 팀에서 조직으로 확장시킨다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1815</guid>
      <comments>https://javaexpert.tistory.com/1815#entry1815comment</comments>
      <pubDate>Tue, 25 Aug 2026 12:12:07 +0900</pubDate>
    </item>
    <item>
      <title>AI 애플리케이션을 잘 만드는 개발자는 무엇이 다른가</title>
      <link>https://javaexpert.tistory.com/1814</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Andrew Ng의 AI Engineering Skills Map ① &amp;mdash; Building and Deploying AI Applications&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 애플리케이션 개발은 기존 소프트웨어 개발과 상당히 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 프로그램에서는 같은 입력을 넣으면 대부분 예상 가능한 결과가 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 LLM이나 머신러닝 모델은 그렇지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 프롬프트를 사용하더라도 답변이 달라질 수 있고, 새로운 데이터에 대해 어떤 결과가 나올지 완벽하게 예측하기도 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 AI 엔지니어링에서 중요한 것은 단순히 &lt;b&gt;LLM API를 호출하는 능력&lt;/b&gt;이 아닙니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예측하기 어려운 AI 컴포넌트를 이용해 신뢰할 수 있는 시스템을 만드는 능력&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng는 이를 위해 필요한 역량을 다음 6가지로 정리합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;LLM Foundations&lt;/li&gt;
&lt;li&gt;Grounding Models with Data&lt;/li&gt;
&lt;li&gt;Building Agentic Systems&lt;/li&gt;
&lt;li&gt;Evaluation-Driven Development&lt;/li&gt;
&lt;li&gt;Operating in Production&lt;/li&gt;
&lt;li&gt;Machine Learning Foundations&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 살펴보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. LLM Foundations&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LLM이 어떻게 동작하는지 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM을 제대로 활용하려면 먼저 모델의 기본 동작을 이해해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;GPT API를 호출할 줄 안다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정도로는 부족합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소한 다음 개념을 이해해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tokenization&lt;/li&gt;
&lt;li&gt;Context Window&lt;/li&gt;
&lt;li&gt;Sampling&lt;/li&gt;
&lt;li&gt;Reasoning&lt;/li&gt;
&lt;li&gt;Knowledge Cutoff&lt;/li&gt;
&lt;li&gt;Cache&lt;/li&gt;
&lt;li&gt;Tool Calling&lt;/li&gt;
&lt;li&gt;Multimodal Model&lt;/li&gt;
&lt;li&gt;Fine-tuning&lt;/li&gt;
&lt;li&gt;Self-hosting&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 모델에게 매우 긴 문서를 통째로 Context Window에 넣을 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 항상 좋은 선택은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context가 길어질수록&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비용이 증가하고&lt;/li&gt;
&lt;li&gt;응답 시간이 늘어나며&lt;/li&gt;
&lt;li&gt;중요한 정보가 묻힐 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 개발자는 판단해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전체 데이터를 Context에 넣을 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;필요한 데이터만 검색해서 가져오게 할 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 판단을 하기 위해 LLM의 기본 구조를 이해해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Grounding Models with Data&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI에게 좋은 데이터를 제공하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM의 성능은 모델 자체만으로 결정되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 어떤 Context를 제공하느냐가 매우 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 LLM 애플리케이션에서는 주로 &lt;b&gt;RAG + Vector Search&lt;/b&gt; 구조가 많이 사용되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 구조는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자 질문&lt;br /&gt;&amp;darr;&lt;br /&gt;Embedding&lt;br /&gt;&amp;darr;&lt;br /&gt;Vector Search&lt;br /&gt;&amp;darr;&lt;br /&gt;관련 문서 검색&lt;br /&gt;&amp;darr;&lt;br /&gt;LLM Context에 삽입&lt;br /&gt;&amp;darr;&lt;br /&gt;답변 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 지금의 Grounding 기술은 훨씬 다양해졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 특성에 따라 다른 구조를 선택할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Vector Database&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서나 자연어 검색에 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사내 문서&lt;/li&gt;
&lt;li&gt;매뉴얼&lt;/li&gt;
&lt;li&gt;고객 문의&lt;/li&gt;
&lt;li&gt;기술 문서&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Knowledge Graph&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개체와 관계가 중요한 데이터에 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자 &amp;rarr; 회사 &amp;rarr; 프로젝트 &amp;rarr; 제품&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 관계 중심의 정보를 표현할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Structured Data&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRM이나 ERP 같은 구조화된 데이터는 SQL이나 Semantic Layer를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고객 정보&lt;/li&gt;
&lt;li&gt;주문 기록&lt;/li&gt;
&lt;li&gt;재고&lt;/li&gt;
&lt;li&gt;매출&lt;/li&gt;
&lt;li&gt;사용자 활동&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 질문은 이것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 데이터를 AI에게 어떤 방식으로 제공하는 것이 가장 좋은가?&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 데이터를 한 번 연결했다고 끝나는 것도 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PDF, HTML, 이미지, 문서 등을 AI가 사용할 수 있는 형태로 변환하고 지속적으로 업데이트해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 AI 시대에는 &lt;b&gt;Data Engineering과 Context Engineering의 경계도 점점 가까워지고 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Building Agentic Systems&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI에게 일을 맡기는 시스템 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 LLM 애플리케이션은 이런 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자&lt;br /&gt;&amp;rarr; Prompt&lt;br /&gt;&amp;rarr; LLM&lt;br /&gt;&amp;rarr; Answer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Agentic System은 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 리서치 에이전트라면 다음과 같이 움직일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;질문 분석&lt;br /&gt;&amp;darr;&lt;br /&gt;검색&lt;br /&gt;&amp;darr;&lt;br /&gt;자료 읽기&lt;br /&gt;&amp;darr;&lt;br /&gt;추가 검색 결정&lt;br /&gt;&amp;darr;&lt;br /&gt;정보 정리&lt;br /&gt;&amp;darr;&lt;br /&gt;초안 작성&lt;br /&gt;&amp;darr;&lt;br /&gt;검증&lt;br /&gt;&amp;darr;&lt;br /&gt;최종 답변&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 LLM은 단순히 답변을 생성하는 것이 아니라&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다음에 무엇을 해야 할지 결정합니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng는 Agentic System을 크게 두 종류로 볼 수 있다고 설명합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Workflow&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미리 정해진 순서대로 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Research &amp;rarr; Summarize &amp;rarr; Write &amp;rarr; Review&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Agent Loop&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 상황에 따라 다음 행동을 스스로 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Think&lt;br /&gt;&amp;rarr; Tool&lt;br /&gt;&amp;rarr; Observe&lt;br /&gt;&amp;rarr; Think&lt;br /&gt;&amp;rarr; Tool&lt;br /&gt;&amp;rarr; Observe&lt;br /&gt;&amp;rarr; Answer&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Agent를 만들 때 필요한 판단&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agentic System을 설계할 때는 의외로 많은 결정을 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 작업을 연결할 것인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A &amp;rarr; B &amp;rarr; C&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 작업을 병렬 처리할 것인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Search A&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Search B&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Search C&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 검색이 서로 의존하지 않는다면 동시에 실행할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 작업을 코드로 처리할 것인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 것을 LLM에게 맡길 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 계산이나 규칙 기반 작업은 일반 코드가 더 적합할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 Tool을 제공할 것인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 사용할 수 있는 도구는 다양합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Web Search&lt;/li&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;API&lt;/li&gt;
&lt;li&gt;MCP&lt;/li&gt;
&lt;li&gt;CLI&lt;/li&gt;
&lt;li&gt;Browser&lt;/li&gt;
&lt;li&gt;Sandbox&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Memory와 Context 관리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 오랫동안 작업하면 Context가 계속 커집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음도 설계해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Short-term Memory&lt;/li&gt;
&lt;li&gt;Long-term Memory&lt;/li&gt;
&lt;li&gt;Session Memory&lt;/li&gt;
&lt;li&gt;Context Compression&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;Retrieval&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 긴 작업에서는 모든 대화를 계속 Context에 넣는 방식보다 필요한 정보만 유지하거나 다시 검색하는 구조가 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;언제 Multi-Agent를 사용할 것인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 문제에 Multi-Agent가 필요한 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 문제는 Single Agent가 더 효율적일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 역할이 명확히 나뉘는 작업에서는 Multi-Agent 구조를 고려할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 콘텐츠 제작 시스템을 이렇게 나눌 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Research Agent&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자료 조사&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Writer Agent&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초안 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Fact Checker&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Editor Agent&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문장 개선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Publisher Agent&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시 형식 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 바로 &lt;b&gt;Agent Orchestration&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Evaluation-Driven Development&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 개발에서 가장 중요한 기술 중 하나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng는 AI 시스템 개발에서 중요한 역량으로 &lt;b&gt;Eval과 Error Analysis&lt;/b&gt;를 특히 강조합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발에서 흔히 발생하는 문제는 이것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트를 수정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괜찮아 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 수정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 개발하면 개선이 실제로 일어나고 있는지 알기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 AI 시스템에서는 다음 루프가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Build&lt;br /&gt;&amp;darr;&lt;br /&gt;Run&lt;br /&gt;&amp;darr;&lt;br /&gt;Evaluate&lt;br /&gt;&amp;darr;&lt;br /&gt;Error Analysis&lt;br /&gt;&amp;darr;&lt;br /&gt;Improve&lt;br /&gt;&amp;darr;&lt;br /&gt;Run Again&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 &lt;b&gt;Evaluation-Driven Development&lt;/b&gt;라고 볼 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Eval은 어떻게 할까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가 방법은 여러 가지가 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Deterministic Evaluation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 명확하게 검사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;JSON 형식이 맞는가?
필수 필드가 있는가?
숫자 범위가 맞는가?
URL 형식이 올바른가?
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. LLM-as-a-Judge&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 LLM에게 결과를 평가하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;이 답변이 사용자의 질문에 정확히 답했는지
1~5점으로 평가하세요.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Human Evaluation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 직접 평가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음과 같은 요소는 사람 평가가 중요할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자연스러움&lt;/li&gt;
&lt;li&gt;유용성&lt;/li&gt;
&lt;li&gt;브랜드 톤&lt;/li&gt;
&lt;li&gt;창의성&lt;/li&gt;
&lt;li&gt;사용자 만족도&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;중요한 것은 Eval 자체도 평가해야 한다는 것&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eval을 만들었다고 끝나는 것이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eval 자체가 실제 품질을 제대로 측정하고 있는지도 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 LLM Judge가 좋은 답변과 나쁜 답변을 제대로 구분하지 못한다면 평가 시스템 역시 개선해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실제 AI 시스템은 다음과 같이 움직입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI System&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Eval System&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Eval의 정확성 검증&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Eval 개선&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI System 개선&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 평가 시스템 역시 하나의 제품입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. Operating in Production&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI를 실제 서비스로 운영하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prototype과 Production은 완전히 다른 문제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Demo에서는 잘 작동하던 AI가 실제 사용자에게 공개되면 예상하지 못했던 문제가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이상한 질문&lt;/li&gt;
&lt;li&gt;매우 긴 입력&lt;/li&gt;
&lt;li&gt;Prompt Injection&lt;/li&gt;
&lt;li&gt;잘못된 검색 결과&lt;/li&gt;
&lt;li&gt;API 장애&lt;/li&gt;
&lt;li&gt;모델 업데이트&lt;/li&gt;
&lt;li&gt;비용 폭증&lt;/li&gt;
&lt;li&gt;응답 지연&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등이 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Production에서는 &lt;b&gt;Observability&lt;/b&gt;가 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;무엇을 관찰해야 할까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 다음을 추적할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Quality&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;답변 품질&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Latency&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 시간&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cost&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Token 비용&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Error&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패율&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tool Usage&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tool 호출 성공률&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Retrieval&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 정확도&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Security&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prompt Injection이나 데이터 유출 위험&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;AI 시스템에는 Drift도 존재한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스를 운영하다 보면 사용자의 질문 유형이 달라질 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 업데이트될 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 내용도 바뀝니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 시간이 지나면서 기존 Eval 성능이 떨어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Monitoring + Regression Test&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 모델이나 프롬프트를 적용하기 전에 기존 Eval Dataset을 다시 실행해서 성능이 떨어지지 않았는지 확인하는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;비용과 Latency도 AI Engineering이다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 서비스를 대규모로 운영하면 작은 비용 차이가 큰 차이를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 한 요청에 1센트가 든다고 가정해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 100만 요청이면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;$10,000 / day&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Production에서는 모델 정확도만 볼 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 요소를 함께 고려해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Quality&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cost&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Latency&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 가지 사이의 트레이드오프가 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 개선하기 위해 다음과 같은 방법을 사용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Smaller Model&lt;/li&gt;
&lt;li&gt;Model Routing&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Distillation&lt;/li&gt;
&lt;li&gt;Fine-tuning&lt;/li&gt;
&lt;li&gt;Prompt Optimization&lt;/li&gt;
&lt;li&gt;Context Reduction&lt;/li&gt;
&lt;li&gt;Workflow Simplification&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. Machine Learning Foundations&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LLM 시대에도 ML 기본기가 필요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM이 등장하면서 Machine Learning을 공부할 필요가 없어졌다고 생각할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Andrew Ng는 반대로 이야기합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 LLM 엔지니어들은 대부분 Machine Learning과 Deep Learning에 대한 기본적인 이해를 가지고 있다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 개념은 여전히 중요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Supervised Learning&lt;/li&gt;
&lt;li&gt;Reinforcement Learning&lt;/li&gt;
&lt;li&gt;Training / Validation / Test&lt;/li&gt;
&lt;li&gt;Bias&lt;/li&gt;
&lt;li&gt;Variance&lt;/li&gt;
&lt;li&gt;Overfitting&lt;/li&gt;
&lt;li&gt;Error Analysis&lt;/li&gt;
&lt;li&gt;Data Engineering&lt;/li&gt;
&lt;li&gt;Accuracy&lt;/li&gt;
&lt;li&gt;Precision / Recall&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 LLM도 결국 확률적 Machine Learning 시스템이기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Bias와 Variance 사고방식&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI 시스템의 성능이 낮을 때 단순히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;프롬프트를 더 잘 써야겠다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 판단해서는 안 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 원인을 분석해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모델 문제인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 능력이 부족한가?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 문제인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context가 부족한가?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Retrieval 문제인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 문서를 검색하는가?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Prompt 문제인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지시가 명확하지 않은가?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tool 문제인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 잘못된 Tool을 사용하는가?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Evaluation 문제인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가 기준 자체가 잘못되어 있는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 바로 머신러닝에서 오랫동안 사용해 온 &lt;b&gt;Error Analysis 사고방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;결국 AI Engineering은 반복의 기술이다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시스템을 만드는 과정을 한 줄로 표현하면 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Build &amp;rarr; Measure &amp;rarr; Analyze &amp;rarr; Improve &amp;rarr; Repeat&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 소프트웨어에서는 설계를 먼저 충분히 한 뒤 구현하는 방식이 비교적 잘 작동했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AI 시스템은 결과가 불확실하기 때문에 실제로 만들어 보고 결과를 확인하면서 다음 행동을 결정해야 하는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 AI 엔지니어에게 중요한 능력은&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;첫 번째 버전을 완벽하게 만드는 능력&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과를 보고 다음 실험을 정확하게 결정하는 능력&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6가지 역량을 하나의 시스템으로 보면&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng가 설명한 6가지 역량은 서로 독립적이지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 연결됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LLM Foundations&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Grounding&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 Context를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Agentic Systems&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 Tool을 사용하며 작업하도록 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Evaluation&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 측정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Production&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 환경에서 관찰하고 운영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Machine Learning Foundations&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 원인을 분석하고 개선한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다시 처음으로 돌아갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 결국 하나의 거대한 &lt;b&gt;Feedback Loop&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;실전 학습 로드맵으로 바꾼다면&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용을 공부 순서로 바꾸면 다음과 같이 구성할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STEP 1. LLM 기본기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Token&lt;/li&gt;
&lt;li&gt;Context Window&lt;/li&gt;
&lt;li&gt;Prompt&lt;/li&gt;
&lt;li&gt;Structured Output&lt;/li&gt;
&lt;li&gt;Tool Calling&lt;/li&gt;
&lt;li&gt;Multimodal&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STEP 2. Grounding&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Embedding&lt;/li&gt;
&lt;li&gt;Vector DB&lt;/li&gt;
&lt;li&gt;RAG&lt;/li&gt;
&lt;li&gt;Hybrid Search&lt;/li&gt;
&lt;li&gt;Knowledge Graph&lt;/li&gt;
&lt;li&gt;Structured Data&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STEP 3. Agent&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tool&lt;/li&gt;
&lt;li&gt;Workflow&lt;/li&gt;
&lt;li&gt;Agent Loop&lt;/li&gt;
&lt;li&gt;Memory&lt;/li&gt;
&lt;li&gt;MCP&lt;/li&gt;
&lt;li&gt;Multi-Agent&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STEP 4. Evaluation&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Golden Dataset&lt;/li&gt;
&lt;li&gt;Deterministic Eval&lt;/li&gt;
&lt;li&gt;LLM Judge&lt;/li&gt;
&lt;li&gt;Human Eval&lt;/li&gt;
&lt;li&gt;Error Analysis&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STEP 5. Production&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Tracing&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Guardrails&lt;/li&gt;
&lt;li&gt;CI/CD&lt;/li&gt;
&lt;li&gt;Cost Optimization&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STEP 6. Machine Learning&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Supervised Learning&lt;/li&gt;
&lt;li&gt;Deep Learning&lt;/li&gt;
&lt;li&gt;Bias / Variance&lt;/li&gt;
&lt;li&gt;Error Analysis&lt;/li&gt;
&lt;li&gt;Data Engineering&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;앞으로 AI 개발자의 핵심 경쟁력&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발에서 중요한 것은 가장 최신 모델을 가장 빨리 사용하는 것만은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 계속 바뀝니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPT, Claude, Gemini와 같은 모델뿐만 아니라 새로운 모델과 Agent Framework도 계속 등장할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그 위에서 반복되는 핵심 질문은 크게 달라지지 않습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 모델을 사용할 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 데이터를 Context로 제공할 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 Tool을 연결할 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 Workflow를 구성할 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 좋은지 어떻게 평가할 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패 원인은 무엇인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Production에서 비용과 Latency를 어떻게 관리할 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 체계적으로 답할 수 있는 사람이 AI Engineering을 잘하는 개발자에 가까워질 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng가 제시한 &lt;b&gt;Building and Deploying AI Applications&lt;/b&gt;의 핵심 역량은 다음 6가지입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. LLM Foundations&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM의 동작 원리를 이해하고 적절한 모델과 기능을 선택하는 능력.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Grounding Models with Data&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG, Vector Search, Knowledge Graph, Structured Data 등을 이용해 모델에 적절한 Context를 제공하는 능력.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Building Agentic Systems&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Workflow, Agent Loop, Tool, Memory, MCP, Multi-Agent 등을 설계하는 능력.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Evaluation-Driven Development&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eval과 Error Analysis를 이용해 AI 시스템을 체계적으로 개선하는 능력.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Operating in Production&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observability, Security, Cost, Latency, Regression Test 등을 관리하는 능력.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Machine Learning Foundations&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bias, Variance, Error Analysis, Data Engineering 등 머신러닝의 기본 원리를 이해하는 능력.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 AI Engineering의 핵심은 단순합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;신뢰하기 어려운 AI 컴포넌트를 이용해 신뢰할 수 있는 시스템을 만드는 것.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 과정은 한 번의 완벽한 설계가 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Build &amp;rarr; Eval &amp;rarr; Error Analysis &amp;rarr; Improve&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 끊임없이 반복하는 과정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시대에는 코드를 작성하는 능력뿐 아니라 &lt;b&gt;모델, 데이터, Agent, Evaluation, Production을 하나의 시스템으로 설계하는 능력&lt;/b&gt;이 개발자의 중요한 경쟁력이 되고 있습니다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1814</guid>
      <comments>https://javaexpert.tistory.com/1814#entry1814comment</comments>
      <pubDate>Mon, 24 Aug 2026 11:58:57 +0900</pubDate>
    </item>
    <item>
      <title>AI 시대 개발자가 반드시 갖춰야 할 4가지 역량</title>
      <link>https://javaexpert.tistory.com/1813</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;1402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DwDBM/dJMcafgKjxX/FDQ6xtHkZk6DmSd3InPfuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DwDBM/dJMcafgKjxX/FDQ6xtHkZk6DmSd3InPfuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DwDBM/dJMcafgKjxX/FDQ6xtHkZk6DmSd3InPfuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDwDBM%2FdJMcafgKjxX%2FFDQ6xtHkZk6DmSd3InPfuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1122&quot; height=&quot;1402&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;1402&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Andrew Ng의 「AI Engineering Skills Map」이 말하는 개발자의 새로운 기준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 소프트웨어를 만드는 방식을 완전히 바꾸고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불과 몇 년 전만 해도 개발자의 핵심 역량은 프로그래밍 언어, 프레임워크, 데이터베이스, 서버 아키텍처 등을 얼마나 잘 다루느냐에 집중되어 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 생성형 AI와 코딩 에이전트가 빠르게 발전하면서 상황이 달라졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 중요한 것은 단순히 &lt;b&gt;코드를 얼마나 잘 작성하는가&lt;/b&gt;가 아닙니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI를 이용해 무엇을 만들 것인지 결정하고, AI가 제대로 만들도록 설계하고 검증하는 능력&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점이 점점 더 중요해지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng는 최근 **「The AI Engineering Skills Map」**을 공개하면서 앞으로 개발자에게 중요한 AI 엔지니어링 역량을 크게 네 가지로 정리했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그의 팀은 10,000개 이상의 채용 공고를 분석하고, AI 전문가&amp;middot;채용 담당자&amp;middot;리크루터 인터뷰와 설문조사 등을 종합해 이 역량 지도를 만들었다고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 다음 네 가지입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;AI 애플리케이션 구축과 배포&lt;/li&gt;
&lt;li&gt;소프트웨어 엔지니어링 기본기&lt;/li&gt;
&lt;li&gt;코딩 에이전트 활용&lt;/li&gt;
&lt;li&gt;무엇을 만들지 결정하는 능력, Shaping the Build&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 살펴보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. AI 애플리케이션을 만들고 배포하는 능력&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Building and Deploying AI Applications&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 소프트웨어와 AI 소프트웨어 사이에는 중요한 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;출력의 불확실성&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 프로그램에서는 동일한 입력과 조건이라면 결과를 어느 정도 예측할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 LLM에 프롬프트를 입력하면 매번 조금씩 다른 답이 나올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머신러닝 모델 역시 새로운 데이터에 어떤 예측을 내놓을지 완벽하게 미리 알 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 AI 애플리케이션 개발자는 단순히 API를 호출할 줄 아는 것으로는 부족합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 기술을 이해해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM&lt;/li&gt;
&lt;li&gt;Prompt Engineering&lt;/li&gt;
&lt;li&gt;Context Engineering&lt;/li&gt;
&lt;li&gt;RAG&lt;/li&gt;
&lt;li&gt;Agentic Workflow&lt;/li&gt;
&lt;li&gt;Machine Learning&lt;/li&gt;
&lt;li&gt;Deep Learning&lt;/li&gt;
&lt;li&gt;AI Evaluation&lt;/li&gt;
&lt;li&gt;Error Analysis&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;Guardrails&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Andrew Ng가 강조하는 것이 &lt;b&gt;Eval과 Error Analysis&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시스템은 이런 구조로 개선됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI 실행 &amp;rarr; 결과 수집 &amp;rarr; 평가 &amp;rarr; 오류 분류 &amp;rarr; 개선 &amp;rarr; 다시 평가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 고객 상담 AI가 있다고 생각해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;답변을 잘하도록 프롬프트를 수정한다&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 방식으로 개발하면 한계가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 실제 질문 1,000개를 모아서 다음과 같이 분류할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정확한 답변&lt;/li&gt;
&lt;li&gt;사실 오류&lt;/li&gt;
&lt;li&gt;질문 의도 오해&lt;/li&gt;
&lt;li&gt;정보 부족&lt;/li&gt;
&lt;li&gt;지나치게 긴 답변&lt;/li&gt;
&lt;li&gt;정책 위반&lt;/li&gt;
&lt;li&gt;RAG 검색 실패&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각각의 오류 유형이 얼마나 발생하는지 측정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때부터 AI 개발은 감각적인 프롬프트 수정이 아니라 &lt;b&gt;데이터 기반 엔지니어링&lt;/b&gt;이 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 소프트웨어 엔지니어링 기본기는 오히려 더 중요해진다&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Software Engineering Fundamentals&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시대가 되면서 종종 이런 이야기가 나옵니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이제 코딩을 몰라도 AI가 프로그램을 만들어준다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부분적으로는 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 복잡한 서비스를 개발할수록 오히려 소프트웨어 엔지니어링 기본기가 중요해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 개발에는 항상 여러 가지 트레이드오프가 존재하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 시스템을 설계할 때 다음을 동시에 고려해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비용&lt;/li&gt;
&lt;li&gt;확장성&lt;/li&gt;
&lt;li&gt;안정성&lt;/li&gt;
&lt;li&gt;속도&lt;/li&gt;
&lt;li&gt;보안&lt;/li&gt;
&lt;li&gt;개인정보 보호&lt;/li&gt;
&lt;li&gt;유지보수성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트에게 단순히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;사용자 100만 명이 사용할 서비스를 만들어줘.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 요청하는 것과&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;읽기 트래픽이 쓰기 트래픽보다 20배 많고, PostgreSQL을 primary datastore로 사용하며 Redis caching을 적용하고 stateless API 서버를 horizontal scaling할 수 있도록 설계해줘.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 요청하는 것은 결과가 완전히 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 개발자는 AI에게 정확한 &lt;b&gt;Engineering Context&lt;/b&gt;를 제공할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 다음 기본기가 여전히 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시스템 설계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Client / Server 구조&lt;/li&gt;
&lt;li&gt;API 설계&lt;/li&gt;
&lt;li&gt;Load Balancing&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Queue&lt;/li&gt;
&lt;li&gt;Distributed System&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터베이스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SQL / NoSQL&lt;/li&gt;
&lt;li&gt;Index&lt;/li&gt;
&lt;li&gt;Transaction&lt;/li&gt;
&lt;li&gt;Replication&lt;/li&gt;
&lt;li&gt;Partitioning&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Backend&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Error Handling&lt;/li&gt;
&lt;li&gt;Background Job&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Infrastructure&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Cloud&lt;/li&gt;
&lt;li&gt;CI/CD&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Scaling&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 만들어줄수록 개발자는 오히려 &lt;b&gt;AI가 내린 기술적 선택이 적절한지 판단할 수 있어야 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Coding Agent를 사용하는 능력&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Using Coding Agents&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 개발자의 중요한 역량 중 하나는 단순한 AI 코딩 도구 사용이 아니라 &lt;b&gt;Coding Agent Management&lt;/b&gt;가 될 가능성이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 에이전트를 잘 사용하는 개발자는 다음을 이해합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Context 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트에게 모든 정보를 한꺼번에 제공한다고 좋은 결과가 나오는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 정보를 필요한 순간에 제공해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음을 관리해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 구조&lt;/li&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;Coding Convention&lt;/li&gt;
&lt;li&gt;API Specification&lt;/li&gt;
&lt;li&gt;Database Schema&lt;/li&gt;
&lt;li&gt;기존 구현&lt;/li&gt;
&lt;li&gt;Test&lt;/li&gt;
&lt;li&gt;Constraints&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Prompt Engineering &amp;rarr; Context Engineering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 중심이 이동합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Planning과 Execution을 분리한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 작업을 바로 코딩하도록 요청하는 것보다 다음과 같이 나누는 방법이 효과적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요구사항 분석&lt;br /&gt;&amp;darr;&lt;br /&gt;구현 계획&lt;br /&gt;&amp;darr;&lt;br /&gt;작업 분할&lt;br /&gt;&amp;darr;&lt;br /&gt;코드 구현&lt;br /&gt;&amp;darr;&lt;br /&gt;테스트&lt;br /&gt;&amp;darr;&lt;br /&gt;검증&lt;br /&gt;&amp;darr;&lt;br /&gt;수정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 코딩 에이전트를 단순한 코드 생성기가 아니라 &lt;b&gt;작업을 수행하는 엔지니어처럼 관리하는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Verification Loop를 만들어야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 작성하는 것보다 더 중요한 것은 코드가 올바른지 확인하는 과정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 루프를 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Agent&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Test&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Verifier&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Agent&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Test&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재검증&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 만들면 사람이 모든 코드를 일일이 확인하지 않아도 에이전트가 일정 부분 스스로 문제를 수정할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 이제 개발자는 &quot;무엇을 만들 것인가&quot;까지 결정해야 한다&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shaping the Build&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 이번 Skills Map에서 가장 중요한 변화라고 볼 수 있는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 개발 프로세스에서는 흔히 역할이 이렇게 나뉘었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기획자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 만들지 결정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디자이너&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 설계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개발자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AI 코딩 에이전트가 구현 영역을 빠르게 자동화하면서 개발자의 역할이 위쪽으로 이동하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 개발자가 이런 질문에도 답할 수 있어야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 문제를 해결해야 하는가?&lt;/li&gt;
&lt;li&gt;고객에게 실제 필요한 기능은 무엇인가?&lt;/li&gt;
&lt;li&gt;어떤 기능을 제거해도 되는가?&lt;/li&gt;
&lt;li&gt;MVP에는 무엇만 넣어야 하는가?&lt;/li&gt;
&lt;li&gt;지금 빠르게 만들어야 하는가?&lt;/li&gt;
&lt;li&gt;아니면 안정성을 위해 천천히 개발해야 하는가?&lt;/li&gt;
&lt;li&gt;어떤 지표로 성공을 판단할 것인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 개발자에게 &lt;b&gt;Product Sense&lt;/b&gt;가 요구됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;개발자의 역할이 바뀌고 있다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 개발자의 업무 흐름을 단순화하면 이랬습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Requirement&lt;br /&gt;&amp;rarr; Design&lt;br /&gt;&amp;rarr; Coding&lt;br /&gt;&amp;rarr; Test&lt;br /&gt;&amp;rarr; Deploy&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시대에는 조금 다른 구조가 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Problem Discovery&lt;br /&gt;&amp;darr;&lt;br /&gt;Product Definition&lt;br /&gt;&amp;darr;&lt;br /&gt;Spec&lt;br /&gt;&amp;darr;&lt;br /&gt;Agent Orchestration&lt;br /&gt;&amp;darr;&lt;br /&gt;AI Coding&lt;br /&gt;&amp;darr;&lt;br /&gt;Automated Evaluation&lt;br /&gt;&amp;darr;&lt;br /&gt;Human Review&lt;br /&gt;&amp;darr;&lt;br /&gt;Deployment&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 사람이 담당하는 영역은 점점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Coding&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Decision Making + Architecture + Evaluation&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쪽으로 이동합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;앞으로 중요한 것은 Agent가 아니라 Agent를 관리하는 능력이다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발 도구는 앞으로 계속 좋아질 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 도구 이름 자체를 외우는 것은 장기적인 경쟁력이 되기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Claude Code를 사용할 수도 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex나 Cursor를 사용할 수도 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내년에는 전혀 다른 Coding Agent가 등장할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 중요한 것은 그 아래에 있는 공통 원리를 이해하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Context Management&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 필요한 정보를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Task Decomposition&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 문제를 작은 작업으로 나눈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Agent Orchestration&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 에이전트에게 작업을 분배한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Verification&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트와 Eval로 결과를 검증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Feedback Loop&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류를 발견하고 다시 에이전트에게 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 원리를 알고 있다면 사용하는 AI 도구가 달라져도 빠르게 적응할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;AI Engineering Skills Map을 학습 로드맵으로 바꾸면&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng의 네 가지 영역을 실제 공부 순서로 확장하면 다음과 같은 구조를 만들 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LEVEL 1 &amp;mdash; Software Engineering&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 개발의 기반을 익힙니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Programming&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;API&lt;/li&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Cloud&lt;/li&gt;
&lt;li&gt;System Design&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LEVEL 2 &amp;mdash; AI Application&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 애플리케이션의 핵심 구성 요소를 배웁니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM API&lt;/li&gt;
&lt;li&gt;Structured Output&lt;/li&gt;
&lt;li&gt;Tool Calling&lt;/li&gt;
&lt;li&gt;Context Engineering&lt;/li&gt;
&lt;li&gt;Embedding&lt;/li&gt;
&lt;li&gt;Vector Database&lt;/li&gt;
&lt;li&gt;RAG&lt;/li&gt;
&lt;li&gt;Guardrails&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LEVEL 3 &amp;mdash; AI Evaluation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시스템을 측정하고 개선하는 방법을 익힙니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dataset&lt;/li&gt;
&lt;li&gt;Eval&lt;/li&gt;
&lt;li&gt;LLM-as-a-Judge&lt;/li&gt;
&lt;li&gt;Error Analysis&lt;/li&gt;
&lt;li&gt;Regression Test&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LEVEL 4 &amp;mdash; Agent Engineering&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 LLM 호출에서 에이전트 시스템으로 확장합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tool Use&lt;/li&gt;
&lt;li&gt;Planning&lt;/li&gt;
&lt;li&gt;Memory&lt;/li&gt;
&lt;li&gt;Agent Loop&lt;/li&gt;
&lt;li&gt;Multi-Agent&lt;/li&gt;
&lt;li&gt;Workflow&lt;/li&gt;
&lt;li&gt;Human-in-the-Loop&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LEVEL 5 &amp;mdash; Coding Agent Engineering&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 함께 실제 소프트웨어를 개발합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Specification&lt;/li&gt;
&lt;li&gt;Context Management&lt;/li&gt;
&lt;li&gt;Agent Planning&lt;/li&gt;
&lt;li&gt;Code Generation&lt;/li&gt;
&lt;li&gt;Test Automation&lt;/li&gt;
&lt;li&gt;Verifier&lt;/li&gt;
&lt;li&gt;Agent Orchestration&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LEVEL 6 &amp;mdash; Shaping the Build&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 단계에서는 단순 개발을 넘어 제품을 설계합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Problem Discovery&lt;/li&gt;
&lt;li&gt;Product Sense&lt;/li&gt;
&lt;li&gt;User Interview&lt;/li&gt;
&lt;li&gt;MVP&lt;/li&gt;
&lt;li&gt;Rapid Prototyping&lt;/li&gt;
&lt;li&gt;Business Context&lt;/li&gt;
&lt;li&gt;Metrics&lt;/li&gt;
&lt;li&gt;Iteration&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;결국 개발자는 '코드를 만드는 사람'에서 '시스템을 만드는 사람'으로 이동한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng의 AI Engineering Skills Map에서 중요한 메시지는 AI 기술 몇 가지를 새롭게 배우라는 것만은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자의 역할 자체가 변하고 있다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내가 어떻게 코드를 작성할 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 중요한 질문이었다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;무엇을 만들어야 하는가?&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI에게 어떤 Context를 제공해야 하는가?&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어떻게 작업을 분리해야 하는가?&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어떻게 결과가 제대로 나왔는지 검증할 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 더 중요한 질문이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 개발자의 핵심 역량은 다음과 같이 이동하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Code Writing&lt;br /&gt;&amp;rarr; Context Engineering&lt;br /&gt;&amp;rarr; Agent Orchestration&lt;br /&gt;&amp;rarr; Evaluation&lt;br /&gt;&amp;rarr; Architecture&lt;br /&gt;&amp;rarr; Product Decision&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 능력이 사라지는 것이 아니라, 코딩을 포함하는 &lt;b&gt;더 높은 수준의 엔지니어링 능력&lt;/b&gt;이 필요해지는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng가 제시한 AI Engineering의 핵심은 네 가지입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;① Building and Deploying AI Applications&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM, RAG, Agent, Eval 등을 활용해 예측하기 어려운 AI 시스템을 측정하고 개선하는 능력.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;② Software Engineering Fundamentals&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Architecture, Database, Security, Scaling 등 좋은 기술적 판단을 내릴 수 있는 기본기.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;③ Using Coding Agents&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 Context를 제공하고 작업을 분할하며 테스트와 검증 루프를 만들어 개발을 자동화하는 능력.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;④ Shaping the Build&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 주어진 기능을 구현하는 것이 아니라 고객의 문제를 발견하고 제품과 MVP 자체를 설계하는 능력.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 네 가지를 관통하는 기반은 하나입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Continuous Learning &amp;mdash; 계속 배우고 자신의 개발 방식을 업데이트하는 것.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI Engineering 시대의 좋은 개발자는 가장 많은 코드를 작성하는 사람이 아닐지도 모릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제를 정의하고, 좋은 시스템을 설계하고, AI 에이전트를 지휘하고, 결과를 검증하면서 제품을 끝까지 만들어낼 수 있는 사람.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Andrew Ng의 Skills Map은 개발자의 역할이 그 방향으로 이동하고 있음을 보여주는 하나의 중요한 프레임워크입니다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1813</guid>
      <comments>https://javaexpert.tistory.com/1813#entry1813comment</comments>
      <pubDate>Mon, 24 Aug 2026 11:56:07 +0900</pubDate>
    </item>
    <item>
      <title>DuckDB 2.0, 작은 분석 DB가 서버를 향하기 시작했다</title>
      <link>https://javaexpert.tistory.com/1812</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;파일 하나 열어 분석하던 DuckDB가 이제 PostgreSQL과 S3, 서버의 영역까지 넘보고 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 분석을 하다 보면 항상 비슷한 고민을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSV 파일 몇 개를 분석하기 위해 데이터베이스 서버를 구축하기에는 너무 무겁다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 Python과 Pandas만으로 처리하기에는 데이터가 커지면서 메모리와 성능이 부담스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사이에서 빠르게 성장한 데이터베이스가 &lt;b&gt;DuckDB&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB는 별도의 데이터베이스 서버를 운영하지 않고도 애플리케이션 프로세스 안에서 SQL 분석을 수행할 수 있는 데이터베이스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 2026년 가을 출시가 예정된 &lt;b&gt;DuckDB 2.0&lt;/b&gt;에서는 이 정체성이 꽤 크게 확장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 개발팀은 이번 버전을 설명하면서 이렇게 방향을 잡았다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난해가 Lakehouse의 해였다면, 이번 릴리스는 DuckDB가 서버가 되는 해의 시작이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0은 단순한 성능 업데이트라기보다는 &lt;b&gt;DuckDB가 어디까지 사용될 수 있는지 범위를 넓히는 버전&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v1.5 이후 10,000개 이상의 커밋이 들어갔고, SQL Parser와 Storage Format 같은 내부 구조까지 상당 부분 손봤다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 가장 큰 변화, DuckDB가 서버가 된다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 DuckDB를 설명할 때 빠지지 않았던 특징이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;In-process Database.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL이나 MySQL처럼 별도 데이터베이스 서버에 접속하는 구조가 아니라 프로그램 안에 DuckDB 엔진을 넣어 사용하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 DuckDB 2.0부터 여기에 새로운 선택지가 생긴다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Quack Protocol&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB의 새로운 네트워크 프로토콜인 &lt;b&gt;Quack&lt;/b&gt;을 이용하면 DuckDB 데이터베이스를 네트워크를 통해 제공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서는 대략 다음과 같이 DuckDB를 서비스할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;CALL quack_serve(
    token = 'my_token'
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 DuckDB에서는 이 서버에 접속한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;ATTACH 'quack:server.example.com'
    AS qk (TOKEN 'my_token');

CONNECT qk;

SELECT count(*)
FROM events;

DISCONNECT;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 부분은 SELECT가 클라이언트에서 수행되는 것이 아니라 &lt;b&gt;서버에서 실행되고 결과만 전달된다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB가 단순히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 노트북 안에서 Parquet을 빠르게 분석하는 DB&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크를 통해 여러 시스템이 접근하는 분석 엔진&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 확장되기 시작한 셈이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 더 재미있는 것은 CONNECT다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CONNECT는 DuckDB 서버에만 사용하는 기능이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL에도 연결할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;CONNECT 'postgres://localhost/mydb';

SELECT count(*)
FROM orders;

DISCONNECT;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 재미있는 점은 DuckDB가 PostgreSQL 테이블 전체를 가져와서 분석하는 것이 아니라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 &lt;b&gt;Remote Pushdown Optimizer&lt;/b&gt;가 SQL을 PostgreSQL이나 MySQL 쪽으로 전달해 가능한 연산을 원격 서버에서 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조적으로 보면 이런 그림이 가능해진다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;Application
     │
   DuckDB
     │
 ┌───┼─────────┐
 │   │         │
 ▼   ▼         ▼
S3 PostgreSQL MySQL
 │
Parquet
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 이렇게 보면 DuckDB가 단순한 데이터 저장소라기보다 &lt;b&gt;여러 데이터 소스를 SQL로 연결하는 경량 분석 계층&lt;/b&gt;으로 활용될 가능성이 더 커진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. JSON을 위한 새로운 무기, VARIANT&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 데이터 시스템에서 JSON을 피하기는 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그도 JSON이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 데이터도 JSON이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI Agent의 결과도 JSON인 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 JSON이 편리한 대신 분석 효율이 좋지 않은 경우가 많다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB가 해결책으로 밀고 있는 것이 &lt;b&gt;VARIANT 타입&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 팀은 이를 사실상&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON on steroids&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VARIANT는 한 컬럼 안에 서로 다른 구조의 데이터를 저장할 수 있다는 점에서는 JSON과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 문자열 형태의 JSON을 그대로 보관하는 방식이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB가 데이터 안의 공통 구조를 자동으로 찾아 내부적으로 분해해 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 별도의 스키마를 미리 정의하지 않아도 압축과 쿼리 실행 효율을 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE events (
    payload VARIANT
);

INSERT INTO events
VALUES (
    '{&quot;user&quot;:{&quot;id&quot;:42,&quot;tags&quot;:[&quot;a&quot;,&quot;b&quot;]}}'
    ::JSON::VARIANT
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 구조가 조금씩 변하는 로그 데이터나 이벤트 스트림 같은 데이터에 어울리는 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 팀은 v2.0 이후 일반 JSON 타입의 내부 구현을 VARIANT 기반으로 바꾸는 것도 검토하고 있다고 밝혔다. 다만 이는 v2.0 이후 계획이며 확정 일정은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 드디어 Trigger가 들어왔다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버형 데이터베이스에 가까워지면서 자연스럽게 추가된 기능도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;Trigger&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0은 다음 기능들을 제공할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BEFORE / AFTER&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FOR EACH ROW / FOR EACH STATEMENT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OLD / NEW TABLE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Trigger&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RETURNING&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DROP TRIGGER&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 데이터가 변경될 때 자동으로 Audit Log를 남길 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;UPDATE
   │
   ▼
Target Table
   │
 Trigger
   ▼
Audit Table
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 DuckDB가 단발성 분석 작업 중심이었다면, Trigger는 &lt;b&gt;장시간 동작하는 DuckDB 서비스&lt;/b&gt;라는 새로운 사용 방식과 상당히 잘 맞는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. AI 시대에 재미있는 기능, NEAREST JOIN&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 DuckDB 2.0의 SQL 변화 중 눈에 띄는 것은 NEAREST JOIN이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자 embedding과 상품 embedding이 있다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT
    q.user_id,
    t.product_id
FROM users q
INNER JOIN products t
APPROX NEAREST 2
BY SIMILARITY
array_cosine_similarity(
    q.embedding,
    t.embedding
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL JOIN 문법 자체로 &lt;b&gt;Top-K Similarity Search&lt;/b&gt;를 수행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 이런 작업을 위해 별도의 Vector DB나 검색 시스템을 고려해야 하는 경우가 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이것만으로 DuckDB가 전문 Vector Database를 대체한다고 말할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 작은 AI 서비스나 로컬 RAG, 데이터 분석 파이프라인처럼&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;Metadata
+
Embedding
+
Analytics
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 한곳에서 처리하려는 상황에서는 꽤 흥미로운 선택지가 될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. SQL 자체도 훨씬 강력해졌다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0에서는 SQL 기능도 대거 추가된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 CTE 안에서 DELETE를 실행한 뒤 바로 다른 테이블로 넣을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;WITH moved AS MATERIALIZED (
    DELETE FROM staging
    RETURNING *
)

INSERT INTO archive
SELECT *
FROM moved;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Nested Schema&lt;/li&gt;
&lt;li&gt;$variable 문법&lt;/li&gt;
&lt;li&gt;json_set&lt;/li&gt;
&lt;li&gt;json_insert&lt;/li&gt;
&lt;li&gt;json_replace&lt;/li&gt;
&lt;li&gt;json_remove&lt;/li&gt;
&lt;li&gt;Recursive CTE의 USING KEY&lt;/li&gt;
&lt;li&gt;SQL 표준 FETCH FIRST&lt;/li&gt;
&lt;li&gt;UNNEST in GROUP BY&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등이 추가되거나 개선된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL 하나만으로 데이터 처리 파이프라인을 구성할 수 있는 영역이 조금씩 넓어지는 모습이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. S3를 많이 쓴다면 Async I/O가 중요하다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 데이터 분석 환경에서는 데이터가 로컬 디스크보다 S3 같은 Object Storage에 존재하는 경우가 많다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;S3
 │
 ├─ parquet
 ├─ parquet
 ├─ parquet
 └─ parquet
      │
      ▼
    DuckDB
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB는 이전에도 S3 데이터를 병렬로 읽을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 I/O 자체는 동기식 접근이라는 제약이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0에서는 엔진 전반에 &lt;b&gt;Asynchronous I/O&lt;/b&gt;가 도입된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 I/O 처리와 Query Processing이 독립적으로 확장될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 설명에서도 로컬 저장장치보다는 특히 &lt;b&gt;네트워크 스토리지에서 더 큰 효과를 기대할 수 있다&lt;/b&gt;고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원 범위도 Parquet에서 시작해 CSV와 DuckDB 자체 파일 포맷으로 확대되고 있으며, 비동기 Parquet 쓰기도 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] S3 + Parquet 기반 분석 파이프라인을 사용하는 개발자라면 DuckDB 2.0에서 가장 체감하기 쉬운 변화 중 하나가 될 가능성이 높다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 쿼리 엔진도 상당히 빨라졌다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0에서는 기존 SQL을 수정하지 않아도 성능 향상을 얻을 수 있도록 Optimizer와 Execution Engine에도 많은 변경이 들어갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Join 아래로 Partial Aggregate Pushdown&lt;/li&gt;
&lt;li&gt;중복 Aggregate 재사용&lt;/li&gt;
&lt;li&gt;Recursive CTE Engine 재작성&lt;/li&gt;
&lt;li&gt;메모리 초과 Aggregate의 Disk Spill&lt;/li&gt;
&lt;li&gt;Partition-aware Query Planning&lt;/li&gt;
&lt;li&gt;Row Group Pruning 확대&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 블로그의 한 마이크로 벤치마크가 재미있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100만 개 Edge를 대상으로 Recursive CTE를 실행한 테스트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전실행시간&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB 1.5.4&lt;/td&gt;
&lt;td&gt;4.90초&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB 2.0 Preview&lt;/td&gt;
&lt;td&gt;0.12초&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 &lt;b&gt;40배 차이&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이는 특정 Recursive CTE 마이크로벤치마크 결과이므로 DuckDB 2.0의 모든 쿼리가 40배 빨라진다는 뜻은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. Storage Format도 2.0으로 바뀐다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0에서는 기본 Storage Format도 &lt;b&gt;v2.0.0&lt;/b&gt;으로 변경된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변화는 꽤 내부적이지만 실제 운영에서는 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Column Metadata를 Lazy Loading 방식으로 변경하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DICT_FSST 문자열 압축을 기본으로 사용하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Delete 정보를 더 작게 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 데이터를 읽을 때 Corruption Validation도 강화된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 팀의 설명에 따르면 특히 &lt;b&gt;큰 Index와 매우 많은 Column을 가진 Table을 열 때 로딩 속도와 메모리 사용량이 개선된다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. PostgreSQL Parser와도 작별한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB는 오랫동안 PostgreSQL에서 파생된 SQL Parser를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0에서는 이것도 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체 개발한 &lt;b&gt;PEG 기반 SQL Parser&lt;/b&gt;가 도입된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서 SQL 문법이 갑자기 달라지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호환성을 유지하는 것이 목표다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 내부적으로는 꽤 중요한 변화다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Extension이 SQL Grammar 자체에 개입할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 장기적으로는 DuckDB Extension이 단순한 함수 추가 수준을 넘어 &lt;b&gt;새로운 SQL 문법 자체를 제공하는 형태&lt;/b&gt;까지 확장될 가능성이 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. Extension 생태계도 크게 달라진다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB Extension 개발에는 한 가지 번거로운 문제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 버전이 바뀌면 Extension을 다시 빌드해야 하는 경우가 많았다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0에서는 C API가 크게 정비된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 상당 부분에 &lt;b&gt;Stable ABI&lt;/b&gt;를 제공할 계획이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 대략 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;My Extension
      │
      ▼
Stable C ABI
      │
      ▼
DuckDB
1.x &amp;rarr; 2.x &amp;rarr; ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Extension 개발자가 DuckDB 릴리스마다 Extension을 다시 맞춰 빌드해야 하는 부담을 줄이는 것이 목표다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rust 기반 Extension을 위한 binding도 작업 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나 재미있는 기능이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업이나 개발자가 &lt;b&gt;자신의 Extension Repository를 직접 운영&lt;/b&gt;할 수 있게 된다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE EXTENSION REPOSITORY my_repo
FROM 'https://extensions.example.org';

INSTALL my_ext
FROM my_repo;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RSA Public Key를 이용해 Extension을 검증하는 구조도 함께 제공될 예정이다. 해당 기능은 공식 프리뷰 게시 시점에는 일부가 개발 중이라고 명시되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업 내부 DuckDB Extension Store 같은 것도 가능해지는 셈이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;결국 DuckDB 2.0에서 가장 중요한 변화는 무엇일까&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 하나만 고르라면 Quack이나 Async I/O일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 더 중요한 것은 DuckDB의 &lt;b&gt;포지션 변화&lt;/b&gt;라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전의 DuckDB를 단순화하면 이랬다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;Application
    │
    ▼
 DuckDB
    │
    ▼
Local File
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0이 그리고 있는 그림은 상당히 다르다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;                ┌─ PostgreSQL
                │
Application ─ DuckDB ─ MySQL
                │
                ├─ S3 / Parquet
                │
                ├─ DuckLake
                │
                └─ Remote DuckDB
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;JSON / VARIANT
Vector Similarity
Trigger
Extension
Async I/O
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까지 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 이런 방향이 이어진다면 DuckDB는 단순한 Embedded Analytics Database를 넘어 &lt;b&gt;애플리케이션과 여러 데이터 저장소 사이에 놓이는 경량 분석 엔진&lt;/b&gt;이라는 역할을 더 강하게 가져갈 가능성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 PostgreSQL이나 MySQL을 대체하는 데이터베이스가 된다고 단정하기에는 이르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 흥미로운 점은 &lt;b&gt;기존 데이터베이스를 없애려 하기보다 연결하려 한다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL은 그대로 두고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터는 S3에 그대로 두고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parquet도 그대로 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 필요한 순간 DuckDB가 그 위에서 SQL을 실행한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;AI 개발자에게 DuckDB 2.0이 흥미로운 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 AI 서비스를 만들다 보면 데이터 구조가 꽤 복잡해진다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;사용자 데이터 &amp;rarr; PostgreSQL

로그 &amp;rarr; JSON

분석 데이터 &amp;rarr; Parquet / S3

Embedding &amp;rarr; Vector

AI 결과 &amp;rarr; JSON

실험 데이터 &amp;rarr; CSV
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 각각 다른 도구를 붙이는 것이 자연스러웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] DuckDB 2.0의 여러 기능을 조합하면 일부 규모의 시스템에서는 이 데이터들을 하나의 SQL 분석 계층에서 다루는 아키텍처를 고려해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;S3 + Parquet + PostgreSQL + JSON + Embedding&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조합을 사용하는 개발자라면 DuckDB 2.0은 꽤 주목할 만하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;DuckDB는 더 이상 단순한 '작은 DB'만은 아니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0 프리뷰를 읽으면서 가장 인상적이었던 부분은 기능의 개수가 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB는 여전히 가볍게 사용할 수 있는 In-process Database라는 장점을 유지하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 동시에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Server&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Remote Query&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Object Storage&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Lakehouse&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Semi-structured Data&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Vector Search&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Extension Ecosystem&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영역까지 조금씩 확장하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 로드맵에도 Quack 안정화, Async I/O, Rust Extension 지원, Iceberg&amp;middot;Delta&amp;middot;Lance&amp;middot;DuckLake 개선 등이 포함되어 있다. 다만 로드맵 항목은 변경될 수 있으며 특정 시점 출시가 보장된 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 2.0의 정식 버전은 2026년 가을 출시가 예정되어 있다. 정확한 세부 기능과 Breaking Changes는 정식 Release Announcement에서 다시 확인할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 방향만큼은 꽤 명확해 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DuckDB는 파일을 분석하는 작은 SQL 엔진에서, 데이터가 있는 곳으로 직접 찾아가는 SQL 엔진으로 확장되고 있다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1812</guid>
      <comments>https://javaexpert.tistory.com/1812#entry1812comment</comments>
      <pubDate>Fri, 21 Aug 2026 13:08:51 +0900</pubDate>
    </item>
    <item>
      <title>휴먼 디자이너를 AI 디자인 에이전트로 복제</title>
      <link>https://javaexpert.tistory.com/1811</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;figure id=&quot;og_1787278562749&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;X의 Karan 님(@kmeanskaran)&quot; data-og-description=&quot;System Design for Agent Systems (Part 1)&quot; data-og-host=&quot;x.com&quot; data-og-source-url=&quot;https://x.com/kmeanskaran/status/2090436724250026177&quot; data-og-url=&quot;https://x.com/kmeanskaran/status/2090436724250026177&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/VKZjA/dJMb9kmtorz/SwRZyQzKqkKg8oNyfSttH0/img.jpg?width=1230&amp;amp;height=492&amp;amp;face=0_0_1230_492&quot;&gt;&lt;a href=&quot;https://x.com/kmeanskaran/status/2090436724250026177&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://x.com/kmeanskaran/status/2090436724250026177&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/VKZjA/dJMb9kmtorz/SwRZyQzKqkKg8oNyfSttH0/img.jpg?width=1230&amp;amp;height=492&amp;amp;face=0_0_1230_492');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;X의 Karan 님(@kmeanskaran)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;System Design for Agent Systems (Part 1)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;x.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;디자이너의 머릿속을 복제해 누구나 90점 이상의 UI를 만들게 한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 아이디어를 한 문장으로 정리하면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;디자이너가 직접 모든 화면을 만드는 대신, 디자이너의 판단 기준과 작업 프로세스를 코드&amp;middot;문서&amp;middot;디자인 시스템으로 구조화하고 AI가 이를 매번 참조하도록 만든다.&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 단순한 &lt;b&gt;AI UI 생성기&lt;/b&gt;가 아니라 &lt;b&gt;&amp;lsquo;디자이너의 사고 과정 자체를 시스템화&amp;rsquo;&lt;/b&gt;한 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 이런 시스템을 만들었나&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 NEWT 개발 조직은 대략 다음과 같았습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Before

3개 팀

팀마다
PM 1명
엔지니어 약 3명
디자이너 1명&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 AI 도입 이후 개발 속도가 빨라지고 엔지니어의 풀스택화가 진행되면서 소규모 팀을 많이 만들 수 있게 되었습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;After

8개 팀

그중
3개 팀 &amp;rarr; 디자이너 있음
5개 팀 &amp;rarr; 디자이너 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 &lt;b&gt;개발팀 수는 늘어나지만 디자이너 수는 그대로&lt;/b&gt;라는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자이너 한 명이 여러 팀을 담당할 수도 있지만,&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;모든 화면 직접 디자인
&amp;darr;
모든 화면 검토
&amp;darr;
수정
&amp;darr;
다시 검토&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 반복하는 것은 현실적으로 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 개발 속도를 위해 디자인 품질을 포기할 수도 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이들이 내린 결론은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI 품질이 흔들리는 이유는 디자인 판단 기준이 디자이너의 머릿속에만 있기 때문이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디자이너 머릿속의 판단 기준을 밖으로 꺼내 AI가 읽을 수 있게 만들자.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 접근입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 목표&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 목표는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;PM / 엔지니어
        &amp;darr;
Claude Code
        &amp;darr;
NEWT 디자인 기준 참조
        &amp;darr;
90점 수준 UI 생성
        &amp;darr;
디자이너 리뷰
        &amp;darr;
확정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 디자이너의 역할을&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Before

디자인 제작자&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;After

디자인 기준 설계자
+
최종 리뷰어&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 바꾸는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 반드시 구현하고 싶었던 5가지&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① Figma가 아니라 Claude Code로 UI를 만들 수 있어야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PM이나 엔지니어가 Figma를 능숙하게 다루는 데에는 진입장벽이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 회사에서는 Claude Code를 전사적으로 사용하고 있기 때문에 직군에 관계없이 쉽게 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 디자인 작업 인터페이스를&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Figma&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 아니라&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Claude Code&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 설정했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. ② 디자이너의 사고 과정을 그대로 재현&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자이너는 보통 바로 화면부터 그리지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 과정은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;문제 이해
&amp;darr;
PRD 확인
&amp;darr;
기존 UI 조사
&amp;darr;
경쟁사 조사
&amp;darr;
여러 디자인안 작성
&amp;darr;
비교
&amp;darr;
추천안 결정
&amp;darr;
세부 UX 검토
&amp;darr;
디자인 제안&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 일반적인 AI UI 생성은&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;프롬프트
&amp;darr;
UI 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 끝나는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 NEWT에서는 &lt;b&gt;디자인 결과물만 생성하지 않고 디자인 사고 과정 자체를 AI Workflow로 만들었습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. ③ 디자인 리뷰하기 쉬운 결과물을 만든다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI 이미지만 던져주면 디자이너가 리뷰하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 질문이 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;왜 이 화면을 만들었지?

경쟁사는 어떻게 하고 있지?

사용자가 어떤 행동을 해야 하지?

이 버튼을 누르면 어떻게 되지?

이 상태에서는 어떤 UI가 나오지?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 최종 결과물에는 단순 화면뿐 아니라&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;문제 정의
경쟁사 조사
설계 근거
디자인 후보
추천안
상세 사양&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 모두 포함합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 &lt;b&gt;한 장의 HTML 문서&lt;/b&gt;로 만들어집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. ④ 디자인 단계에서는 구현을 잊되, 바로 구현으로 연결&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 상당히 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인 단계에서&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;API
DB
서버 상태
데이터 연결&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 것을 고민하기 시작하면 UI 탐색 범위가 제한됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 디자인 환경에서는 이런 구현 요소를 제거합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 디자인이 확정된 후 다시 UI를 개발하면 비효율적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디자인할 때 사용하는 UI 컴포넌트와 실제 서비스에서 사용하는 UI 컴포넌트를 동일하게 만들었습니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 핵심 아키텍처&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 시스템에는 2개의 핵심 저장소가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;newt-design-system
        +
newt-spec&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할은 명확하게 나뉩니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;newt-design-system

&quot;어떻게 디자인해야 하는가&quot;
디자인의 기준 / Single Source of Truth&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;newt-spec

&quot;무엇을 만들어야 하는가&quot;
PRD / 기능 명세 / 실제 디자인 작업&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. newt-design-system&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;newt-design-system/
│
├── tokens/
│   └── newt.tokens.json
│
├── packages/ui/
│   └── src/components/
│
├── design-mock/
│   ├── components/
│   ├── screens/
│   ├── design/contracts/
│   ├── foundations/
│   └── src/
│
├── docs/
│   └── communication-design/
│
├── plugin/
│
└── storybook/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 UI 컴포넌트 라이브러리가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 NEWT 디자인과 관련된 모든 정보를 모읍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;색상
폰트
여백
UI Component
UX 철학
UX Writing
브랜드 디자인
Communication Design
Figma Plugin
Storybook&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NEWT 디자인의 Single Source of Truth&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Design Token&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 컬러나 여백을 각각의 화면에서 AI가 마음대로 결정하지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;tokens/newt.tokens.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로는&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;color&quot;: {
    &quot;primary&quot;: &quot;...&quot;,
    &quot;text&quot;: &quot;...&quot;,
    &quot;background&quot;: &quot;...&quot;
  },
  &quot;spacing&quot;: {
    &quot;sm&quot;: &quot;...&quot;,
    &quot;md&quot;: &quot;...&quot;,
    &quot;lg&quot;: &quot;...&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 형태가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 AI 역시 임의 값을 생성하는 것이 아니라 기존 토큰을 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실제 제품과 디자인 시안이 같은 UI Component 사용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시스템에서 가장 중요한 결정 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 디자인 시안을 일반 HTML로 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 문제가 발생했습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;화면 A Button
17px

화면 B Button
16px

화면 C Button
padding 약간 다름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 계속 비슷하지만 조금씩 다른 컴포넌트를 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;packages/ui&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 만들고 실제 서비스와 디자인 mock이 모두 동일한 컴포넌트를 사용하도록 변경했습니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;               packages/ui
                    │
           ┌────────┴────────┐
           │                 │
실제 NEWT 서비스       design-mock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 디자인 시안 단계부터 실제 서비스와 동일한 버튼, 카드, 입력창 등이 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 상당히 중요한 포인트입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. design-mock&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;design-mock은 디자인 실험 공간입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 앱으로 만들어져 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;API
Backend
DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 연결하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI/UX만 집중해서 검토합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 디자인을 4단계로 구조화&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인을 대략 다음과 같은 계층으로 나눕니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;Page
 └─ Section
      └─ Domain Component
            └─ UI Component&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 여행상품 상세 화면이라면&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;TourDetailPage

 ├─ HeroSection
 ├─ PriceSection
 │    ├─ PriceCard
 │    │    ├─ Text
 │    │    ├─ Badge
 │    │    └─ Button
 │
 └─ ScheduleSection&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 실제 코드와 공유하는 것은 UI Component만&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흥미로운 부분입니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;UI Component&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만 실제 제품과 공유합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;Domain Component
Section
Page&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 디자인 mock 전용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 실제 제품에서는 이 부분들이 비즈니스 로직에 따라 다시 구성되기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 &lt;b&gt;이름과 구조는 동일하게 맞춥니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 디자인팀과 개발팀이&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;PriceSection
TourHero
PriceCard&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 동일한 언어로 이야기할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. Component Contract&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 UI 컴포넌트를 정확하게 이해하려면 단순 TSX 코드만으로 부족합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;design/contracts/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안에 컴포넌트의 기계 판독 가능한 사양을 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 개념적으로&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Button

Variant
- Primary
- Secondary
- Text

Size
- Small
- Medium
- Large

Usage
Primary action은 페이지당 하나를 권장

Avoid
위험 작업에서 Primary 사용 금지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;AI용 디자인 명세서&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. MCP를 이용해 AI가 디자인 시스템을 읽는다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 MCP가 등장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;design-mock은 Next.js 웹앱인 동시에&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;design-system MCP Server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code가 이 MCP를 이용해 NEWT 디자인 시스템을 검색합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI가&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여행 상세페이지의 가격 영역을 수정해줘.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 요청을 받으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 디자인 시스템을 읽지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;get_section(&quot;price&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 방식으로 필요한 부분만 가져옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼이 필요하면&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;get_ui_component(&quot;Button&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 호출합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. 필요한 만큼만 가져오는 것이 중요&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NEWT에는 화면이 매우 많기 때문에 전체 디자인 시스템을 매번 LLM Context에 넣는 것은 비효율적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 검색 단위를 나눕니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;Page
Section
Domain Component
UI Component
Token
UX Writing&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude가 필요한 디자인 정보만 가져올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 일종의&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Design RAG&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조라고 볼 수도 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. 회사 공용 MCP&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 중요한 것이&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;reiwatravel-mcp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 프로젝트마다 MCP를 설치해야 한다면&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;설정 파일 추가
Server 실행
Token 설정
권한 설정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등이 필요해서 사람들이 잘 사용하지 않게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 사내 공통 MCP 환경을 구축했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 회사 직원은&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;어떤 Repo
어떤 프로젝트
어떤 직군&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서도 NEWT Design System을 불러올 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;18. 두 번째 저장소 newt-spec&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이곳에는&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;PRD
기능 정의
UX Specification
Design Workflow&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 중요한 AI Skill이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 것이&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;design-builder&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;19. design-builder Skill 구조&lt;/h1&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;design-builder/
│
├── SKILL.md
│
├── references/
│   ├── principles.md
│   ├── proposal-rules.md
│   ├── checklist.md
│   └── ...
│
├── scripts/
│
└── templates/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 가장 중요한 것은&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;SKILL.md
+
references/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;20. SKILL.md = 디자이너의 작업 프로세스&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SKILL.md에는 Step 0~8까지 디자인 프로세스를 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Claude에게&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;디자인해.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 요청하는 것이 아니라&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Step 0
문제 파악

Step 1
자료 읽기

Step 2
모호한 점 확인

Step 3
조사

Step 4
디자인안 제작

Step 5
비교

Step 6
추천

Step 7
문서화

Step 8
PR 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 &lt;b&gt;강제 Workflow&lt;/b&gt;를 실행시키는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;21. references = 디자이너의 머릿속&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 이 글의 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;references/의 약 11개 파일에 디자인 판단 기준이 들어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;principles.md

UI 생성 시 반드시 지켜야 하는
10개의 디자인 원칙&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;proposal-rules.md

디자인 제안서 작성 규칙&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;checklist.md

완료 조건&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude는 체크리스트를 모두 만족하기 전까지 디자인 작업이 완료된 것으로 판단하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;디자이너의 경험
&amp;darr;
문서화
&amp;darr;
AI 규칙
&amp;darr;
매 디자인 작업 적용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;22. 실제 AI 디자인 Workflow&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 디자인은 대략 다음 순서로 진행됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 1. 전제 확인&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;어떤 화면인가?
어떤 문제가 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 2. 자료 확인&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PRD
Design System
기존 화면
UX Guide&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 읽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 3. 모호한 부분 해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 질문합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;결제 버튼은 항상 노출되어야 하나요?

로그인하지 않은 사용자도 가격을 볼 수 있나요?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계에 중요한 불확실성을 하나씩 제거합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 4. 조사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude가&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;사내 기존 화면
+
경쟁사 화면&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 조사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Screenshot
출처
패턴
장단점&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 5. 여러 디자인안 제작&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;Option A
기존 패턴 유지형

Option B
정보 계층 개선형

Option C
전환 최적화형&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 실제 NEWT 컴포넌트를 이용해 작동하는 화면으로 만듭니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;23. AI가 추천안까지 선택&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 3개를 만들고 끝내지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;추천안: B

이유:
- 정보 구조가 명확함
- 기존 NEWT 패턴과 일치
- 구현 변경이 작음
- 모바일에서도 안정적&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 판단을 내립니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;24. 최종 결과는 하나의 HTML&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 결과는 이런 식입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Design Proposal

1. Problem
2. PRD
3. Existing UI
4. Competitor Research
5. Findings

6. Design Option A
7. Design Option B
8. Design Option C

9. Recommended Design

10. Interaction Spec
11. UX Writing
12. Edge Cases

13. Implementation Notes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 것이 &lt;b&gt;한 페이지 HTML&lt;/b&gt;에 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 디자이너는 화면만 보는 것이 아니라&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 이런 디자인이 나왔는지&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까지 동시에 검토할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;25. GitHub PR + Vercel Preview&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인도 코드처럼 PR을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Claude Code
&amp;darr;
Design HTML
&amp;darr;
Git Commit
&amp;darr;
Pull Request
&amp;darr;
Vercel Preview
&amp;darr;
Designer Review&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리뷰어는 URL 하나만 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vercel Preview에서 실제 화면을 보고 댓글도 남길 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;26. 디자이너 승인&lt;/h1&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;AI 디자인
&amp;darr;
Designer Review
&amp;darr;
수정
&amp;darr;
Approve
&amp;darr;
구현&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 &lt;b&gt;AI가 디자이너를 없애는 시스템이 아니라는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 90점 정도를 목표로 하고,&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;마지막 10점&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 디자이너가 판단합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;27. 아직 AI에게 맡기기 어려운 것&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글에서도 한계를 명확히 인정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전히 새로운 UX를 만드는 0&amp;rarr;1 디자인&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은 아직 어렵다고 이야기합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;기존에 전혀 없던 기능
새로운 Interaction
새로운 User Journey
브랜드의 중요한 Experience&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;AI &amp;rarr; 아이디어 / 참고안
Designer &amp;rarr; Figma에서 최종 설계&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방식을 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;28. 그래서 역할을 구분한다&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI에 잘 맞는 작업&lt;/h3&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;기존 화면 개선
기존 패턴 확장
일상적인 UI 변경
폼 추가
Section 변경
정보 구조 개선&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디자이너가 직접 해야 할 가능성이 높은 작업&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;새로운 서비스
새로운 UX
핵심 Experience
브랜드 핵심 화면
0&amp;rarr;1 제품&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;29. Figma의 역할도 바뀐다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Figma = 디자인의 Master&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 새로운 구조에서는&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Design System Code
+
design-mock main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Master가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Figma는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;필요할 때 쓰는 디자인 도구&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 바뀝니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;30. 확정 디자인은 main으로 Merge&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;승인된 디자인은&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;design-mock/main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 다음 Claude 작업에서 이 디자인을 다시 참조할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;AI 디자인
&amp;darr;
사람 리뷰
&amp;darr;
승인
&amp;darr;
Design System 축적
&amp;darr;
다음 AI 디자인의 Reference&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 &lt;b&gt;학습 루프&lt;/b&gt;가 생깁니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;31. 이 시스템의 진짜 핵심&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Design System은&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인간이 읽는 문서&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 시스템에서는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI가 매번 읽는 실행 가능한 지식베이스&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 문제는&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Design System 작성
&amp;darr;
사람이 안 봄
&amp;darr;
오래됨
&amp;darr;
실제 제품과 달라짐
&amp;darr;
아무도 안 봄&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라는 악순환이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 사용하면 반대가 됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;AI가 매 작업 참조
&amp;darr;
Design System 중요도 증가
&amp;darr;
계속 업데이트
&amp;darr;
더 정확한 AI 결과
&amp;darr;
사용 증가&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;32. 앞으로의 목표&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 UI Design System이 중심이지만 앞으로는 범위를 확장하려고 합니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;Product UI
+
UX Writing
+
Brand
+
Communication Design
+
Advertisement
+
Landing Page&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까지 모두 하나의 Single Source에 넣는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;reiwatravel-mcp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 통해 AI에게 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 구조는 이렇게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;               NEWT Design SSOT
                     │
       ┌─────────────┼─────────────┐
       │             │             │
      UI           Brand       UX Writing
       │             │             │
       └─────────────┼─────────────┘
                     │
                     MCP
                     │
             AI / Claude Code
                     │
       ┌─────────────┼─────────────┐
       │             │             │
      App            LP           Ads&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;이 글에서 가장 중요한 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 이 글을 시스템 관점에서 압축하면 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;                Designer Brain
                     │
                     ▼
           ┌───────────────────┐
           │ Design Knowledge │
           └───────────────────┘
                     │
       ┌─────────────┼─────────────┐
       │             │             │
    Tokens       Components     Principles
       │             │             │
   UX Writing     Contracts     Checklist
       │             │             │
       └─────────────┼─────────────┘
                     │
                    MCP
                     │
                     ▼
                Claude Code
                     │
                     ▼
                 SKILL.md
                     │
                     ▼
             Design Workflow
                     │
         ┌───────────┼───────────┐
         │           │           │
      Research     Design      Review Doc
         │           │           │
         └───────────┼───────────┘
                     ▼
               Vercel Preview
                     │
                     ▼
               Designer Review
                     │
                     ▼
                   Merge
                     │
                     ▼
              Design SSOT 갱신&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;특히 주목할 만한 7가지&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글의 핵심은 &lt;b&gt;Claude Code로 UI를 만든다&lt;/b&gt;는 것 자체가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 중요한 것은 다음 7가지입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;디자인 시스템을 AI가 읽을 수 있는 형태로 만든다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실제 서비스와 디자인 Mock이 동일한 UI 컴포넌트를 사용한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Component Contract로 AI에게 사용법까지 알려준다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MCP로 필요한 디자인 정보만 검색해서 가져온다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SKILL.md로 디자이너의 작업 순서를 강제한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Designer Principles와 Checklist를 파일로 명문화한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;승인된 결과를 다시 SSOT에 넣어 다음 AI가 사용하게 한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;일반적인 AI UI 생성과 차이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 AI UI 제작NEWT 방식&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;프롬프트 &amp;rarr; 화면&lt;/td&gt;
&lt;td&gt;PRD &amp;rarr; 조사 &amp;rarr; 분석 &amp;rarr; 여러 안 &amp;rarr; 추천&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI가 임의 UI 생성&lt;/td&gt;
&lt;td&gt;Design System 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;임의 CSS&lt;/td&gt;
&lt;td&gt;Design Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비슷한 버튼 새로 생성&lt;/td&gt;
&lt;td&gt;실제 Button Component 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;결과 이미지 중심&lt;/td&gt;
&lt;td&gt;작동하는 TSX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;맥락 없음&lt;/td&gt;
&lt;td&gt;PRD + 조사 + 설계 근거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI 출력 후 종료&lt;/td&gt;
&lt;td&gt;Designer Review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;결과가 사라짐&lt;/td&gt;
&lt;td&gt;승인 결과 SSOT 편입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Figma가 Master&lt;/td&gt;
&lt;td&gt;코드 기반 Design System이 Master&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;이 시스템을 한 단계 더 추상화하면&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이것은 단순한 &lt;b&gt;AI Design System&lt;/b&gt;보다 더 큰 개념입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Design System
+
RAG
+
MCP
+
Agent Skill
+
Workflow
+
Code Component
+
Human Review
+
Git
+
CI/CD&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 합친 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저라면 이것을&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Design Engineering Agent System&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI-Native Design Operating System&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 철학은 이것입니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;좋은 프롬프트를 만드는 것
        X

좋은 디자인을 반복해서 만들 수 있는
환경을 만드는 것
        O&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 글 마지막의 메시지도 사실 여기에 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디자이너가 한 화면을 잘 디자인하면 한 화면만 좋아지지만, 디자인 판단 기준 하나를 시스템에 추가하면 모든 팀의 AI 출력이 동시에 좋아진다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 이 방식에서 가장 큰 레버리지입니다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1811</guid>
      <comments>https://javaexpert.tistory.com/1811#entry1811comment</comments>
      <pubDate>Fri, 21 Aug 2026 11:16:09 +0900</pubDate>
    </item>
    <item>
      <title>Reddit Sales System 구축하기</title>
      <link>https://javaexpert.tistory.com/1810</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용은 원문을 기준으로 번역&amp;middot;정리한 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1787213647180&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;X의 Chris님(@everestchris6)&quot; data-og-description=&quot;how to build a reddit sales system (FULL GUIDE)&quot; data-og-host=&quot;x.com&quot; data-og-source-url=&quot;https://x.com/everestchris6/status/2090097270805193202&quot; data-og-url=&quot;https://x.com/everestchris6/status/2090097270805193202&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bQ0yJ1/dJMb88GmbxR/IqHKy5ZkBcGytOytrVtGFk/img.jpg?width=2048&amp;amp;height=819&amp;amp;face=0_0_2048_819&quot;&gt;&lt;a href=&quot;https://x.com/everestchris6/status/2090097270805193202&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://x.com/everestchris6/status/2090097270805193202&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bQ0yJ1/dJMb88GmbxR/IqHKy5ZkBcGytOytrVtGFk/img.jpg?width=2048&amp;amp;height=819&amp;amp;face=0_0_2048_819');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;X의 Chris님(@everestchris6)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;how to build a reddit sales system (FULL GUIDE)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;x.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Reddit Sales System 전체 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글의 핵심은 &lt;b&gt;Reddit에서 직접 자동으로 글을 올리는 봇을 만드는 것이 아니라, Reddit을 계속 읽고 분석해서 &amp;ldquo;사람이 최종 클릭&amp;middot;게시만 하는 영업 시스템&amp;rdquo;을 만드는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 구조는 2개의 에이전트로 나뉩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Reddit
  &amp;darr;
RSS 수집
  &amp;darr;
게시글/댓글 DB
  &amp;darr;
Subreddit별 사용자 프로필
  &amp;darr;
┌──────────────────────┐
│ Agent 1              │
│ 시장/문제/상품 발굴 │
└──────────────────────┘
          +
┌──────────────────────┐
│ Agent 2              │
│ 잠재고객/스레드 발굴│
│ 답변/게시글 작성     │
└──────────────────────┘
          &amp;darr;
Dashboard
          &amp;darr;
사람이 검토 &amp;rarr; 직접 Reddit에 게시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 원칙은 &lt;b&gt;에이전트가 Reddit 계정에 로그인하거나 자동 게시하지 않는 것&lt;/b&gt;입니다. 에이전트는 읽기, 분석, 답변 작성, 우선순위 지정까지만 하고 실제 게시 행위는 사람이 자신의 브라우저에서 한다는 방식입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 Reddit인가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문의 주장은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reddit에는 거의 모든 문제와 관심사에 대응하는 커뮤니티가 있기 때문에, 사람들이 실제로 사용하는 표현과 실제 불만을 관찰하기 좋은 채널이라는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 광고 키워드나 검색량보다 다음과 같은 데이터를 직접 볼 수 있다는 점을 강조합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사람들이 반복해서 질문하는 문제&lt;/li&gt;
&lt;li&gt;이미 써본 제품과 실패 이유&lt;/li&gt;
&lt;li&gt;돈을 지불할 정도로 절박한 문제&lt;/li&gt;
&lt;li&gt;문제를 표현하는 실제 단어&lt;/li&gt;
&lt;li&gt;어떤 글이 추천을 받고 어떤 글이 묻히는지&lt;/li&gt;
&lt;li&gt;커뮤니티별 문화와 말투&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 단순 Reddit 마케팅 시스템이 아니라 &lt;b&gt;시장조사 + Voice of Customer + 콘텐츠 마케팅 시스템&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Reddit 데이터 수집&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문에서는 별도의 스크레이퍼 대신 Reddit의 공개 RSS를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;https://www.reddit.com/r/startups/.rss&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시글에서는 대략 다음 정보를 가져온다고 설명합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;title
author
url
body
timestamp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;댓글에서는:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;username
comment text
post
permalink
timestamp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 지속적으로 데이터를 저장해 과거 이력을 누적합니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;30분마다
    &amp;darr;
새 Post 수집
새 Comment 수집
    &amp;darr;
DB 저장
    &amp;darr;
기존 데이터와 중복 제거&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문은 동일 IP에서 요청 속도를 낮게 유지하고 429가 나오면 즉시 반복 요청하지 말라고 제안합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Hermes Agent 구축&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문에서는 Nous Research의 Hermes를 중심 에이전트로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성은 대략:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Hermes Agent
     &amp;darr;
Railway
     &amp;darr;
Persistent Storage
     &amp;darr;
Telegram Bot&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Telegram은 알림용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;08:00

오늘 Reddit에서
잠재고객 후보 12건을 발견했습니다.

고우선순위 답변: 4건
게시글 초안: 5건

Dashboard &amp;rarr;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트는 파일과 분석 결과를 Persistent Storage에 계속 보존합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Subreddit Profile&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시스템에서 상당히 중요한 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 subreddit마다 Markdown 파일 하나를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;profiles/
 ├─ smallbusiness.md
 ├─ entrepreneur.md
 ├─ startups.md
 └─ saas.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 내용은 대략 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Demographics&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추정 연령, 지역, 직업, 사업 단계 등을 기록합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 추정인 경우 명확하게 추정이라고 표시합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Psychographics&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 원하는지, 무엇을 두려워하는지, 어떤 유형의 사람으로 보이기 싫어하는지 등을 분석합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Language&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 중요한 데이터입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;problem phrase:
&quot;getting clients is killing me&quot;

solution phrase:
&quot;outbound that doesn't feel spammy&quot;

failed solution:
&quot;I tried cold email and got nothing&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들이 실제로 사용하는 표현을 저장합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Previous attempts&lt;/h3&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Tried:
- cold email
- LinkedIn outreach
- paid ads

Complaints:
- expensive
- low reply rate
- felt spammy&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Content patterns&lt;/h3&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Upvoted
- 구체적인 경험
- 숫자
- 실패 경험
- 과정 공개

Buried
- 지나친 자기 홍보
- 링크
- AI 느낌의 글&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rules&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크 허용 여부, 자기 홍보 정책, 관리자 규칙 등을 기록합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tone&lt;/h3&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;post length
sentence style
personal/story driven
formal/informal
header usage&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매일 이 파일을 업데이트합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. Agent 1 &amp;mdash; 상품이 없을 때&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent 1은 &lt;b&gt;무엇을 팔지 찾는 에이전트&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 작업은 문제 시장을 찾는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문 기준 필터는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;긴급한 문제인가?
        &amp;darr;
사람들이 이미 돈을 쓰고 있는가?
        &amp;darr;
기존 해결책에 불만이 있는가?
        &amp;darr;
Reddit에서 접근 가능한가?
        &amp;darr;
해결책을 만드는 데 전문 자격이 필요한가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같이 분석합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NicheProblemUrgencyExisting spendReddit access&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Freelancer&lt;/td&gt;
&lt;td&gt;Client acquisition&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;매우 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaaS founder&lt;/td&gt;
&lt;td&gt;User acquisition&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Productivity&lt;/td&gt;
&lt;td&gt;Focus&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;매우 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의료&amp;middot;법률&amp;middot;안전 등 잘못된 조언으로 피해가 생길 수 있는 분야는 따로 표시하라고 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 반복되는 문제 찾기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일정 기간 데이터를 모은 후 반복되는 질문을 찾습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;지난 30일

&quot;How do I get my first 10 customers?&quot;
47회

&quot;Cold email isn't working&quot;
31회

&quot;How much should I charge?&quot;
26회&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각 문제에 대해:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;발생 빈도
좌절 강도
현재 사용 중인 해결책
현재 돈을 쓰고 있는지
실제 댓글 예시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등을 분석합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;AI가 억지로 상품 아이디어를 만들지 않는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 문제가 없으면:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 수집한 데이터에는 만들 만한 상품이 없다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 판단하게 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 상품 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복적인 문제를 발견하면 가이드 형태의 디지털 상품을 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문 예시는 약:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;25~30페이지&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Step 1
무엇을 해야 하는가

Step 2
왜 이것이 작동하는가

Step 3
보통 어디에서 실패하는가

Step 4
기존에 사람들이 시도했던 방법은 왜 실패했는가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 &lt;b&gt;Subreddit Profile의 실제 언어를 사용&lt;/b&gt;한다는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. Landing Page 자동 생성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 판매 페이지를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;Headline
&amp;darr;
고객이 실제 사용한 문제 표현

What's inside
&amp;darr;
구체적 콘텐츠

Who it's for
&amp;darr;
대상

Who it's NOT for
&amp;darr;
비대상

Buy
&amp;darr;
Checkout&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 원문에서는 다음과 같은 조작을 하지 말라고 명시합니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;가짜 후기 X
가짜 사용자 수 X
가짜 판매량 X
허위 수치 X&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Agent 2 &amp;mdash; 이미 상품이 있을 때&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent 2는 &lt;b&gt;상품을 판매할 수 있는 Reddit 대화를 찾는 에이전트&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 product.md 같은 파일을 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;product.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Product
Price

Problem solved

Ideal customer

Not for

Features / contents

Limitations

Customer objections

Honest responses

Real Reddit quotes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일을 모든 답변 작성 전에 참고합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 구매 가능성이 높은 Thread 탐색&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집한 Reddit 게시글과 댓글을 분석해서:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이 사람이 우리 제품이 해결하는 문제를 겪고 있는가?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 판단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 키워드 검색이 아니라 의미 기반 매칭을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품 문제가:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;cold email reply rate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어도 사용자는:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;sent 500 emails and got two replies&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 말할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Semantic Search 또는 LLM classification을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점수:&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1 = 관련성 거의 없음
2 = 약간 관련
3 = 관련
4 = 강한 관련
5 = 현재 해결책을 찾는 중&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;4+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만 답변 후보로 올립니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 자동 Reply 초안&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4점 이상이면 답변을 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 원문의 중요한 철학이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상품을 직접 언급하지 않습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋지 않은 형태:&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;제가 만든 SaaS를 한번 써보세요.
https://...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장 구조:&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;당신 상황에서는 먼저 X를 확인하는 게 좋습니다.

제가 비슷한 상황에서 봤던 가장 흔한 문제는 Y였습니다.

1. ...
2. ...
3. ...

특히 Z는 피하는 게 좋습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 도움 &amp;rarr; 신뢰 &amp;rarr; 자연스러운 관심 &amp;rarr; Profile/DM&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라는 Reddit 특유의 구조를 활용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. Reply 규칙&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문에서는 다음 규칙을 줍니다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;해당 subreddit 말투 사용

실제 질문에 답하기

Product mention 금지

Link 금지

Thread의 다른 댓글 길이와 비슷하게 작성

마케팅 표현 금지

느낌표 금지

&quot;Great question&quot;으로 시작하지 않기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dashboard에는:&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;Reply
Copy button
Open Reddit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 표시됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 매일 게시글 5개 생성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트는 매일 5개의 글을 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘텐츠 전략은 흥미롭습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품의 핵심 방법을 숨기지 말고 그냥 공개하라.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;90% 가치 제공
0% 링크
0% 상품 언급&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시글 끝에는:&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;질문 있으면 답변하겠습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정도의 표현만 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각 글마다:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이 글이 삭제될 가능성
왜 삭제될 수 있는지
어떻게 수정하면 되는지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도 분석합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. DM은 자동화하지 않음&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Private DM은 RSS에서 읽을 수 없기 때문에 원문 시스템에서는 사람이 직접 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 DM은 매우 중요한 시장조사 데이터라고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 DM은 다시:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Subreddit Profile
Product Profile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 반영합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. Dashboard 1 &amp;mdash; Product Discovery&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 대시보드는 상품 발굴용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;──────────────────────
Reddit Product Radar
──────────────────────

Trending Problems

1. Getting first customers
██████████ 47

&quot;I'm stuck at zero customers&quot;
&quot;cold email isn't working&quot;

2. Pricing
██████ 29

3. Ads too expensive
████ 18&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Community Changes

r/startups
&amp;uarr; AI marketing complaints +24%

r/smallbusiness
&amp;uarr; cold outreach complaints

r/entrepreneur
&amp;darr; interest in paid ads&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등을 보여줍니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. Dashboard 2 &amp;mdash; Sales&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 실제 영업용입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;──────────────────────
Reddit Sales Queue
──────────────────────

URGENT

r/startups
posted 42 min ago

Problem:
&quot;No one replies to my cold emails&quot;

Match score
★★★★★

[Copy Reply]

[Open Reddit]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 아래에:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;READY

Replies 12

Posts 5

Posted today 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와 같은 큐를 표시합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. Account Health&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계정 상태도 별도 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Account A

Karma       842
Age         14 months
Removals    0
Warnings    0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Removal이 발생하면:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Warning

r/startups
Post removed

Possible reason:
self promotion rule&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 표시합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;18. 전체 스케줄&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문 시스템의 운영 주기는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Every 30 min
Reddit RSS 수집
       &amp;darr;
새 게시글/댓글 저장
       &amp;darr;
Potential Buyer Matching
       &amp;darr;
Reply Draft&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밤:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Night

오늘 데이터 분석
&amp;darr;
Subreddit Profile 업데이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아침:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Morning

5개 Posts 작성
&amp;darr;
Reply Queue 정리
&amp;darr;
Dashboard 업데이트
&amp;darr;
Telegram 알림&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주간:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Weekly

Problem Analysis
&amp;darr;
새로운 시장 변화
&amp;darr;
새로운 반복 문제
&amp;darr;
Product Opportunity&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;19. 원문에서 추천하는 모델 역할&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문 작성자가 제시한 모델 구성입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할원문 추천&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;전체 Orchestration&lt;/td&gt;
&lt;td&gt;Claude Opus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feed 읽기 / Matching&lt;/td&gt;
&lt;td&gt;Claude Haiku&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;글 / 댓글 / Community Profile&lt;/td&gt;
&lt;td&gt;Claude Sonnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;대규모 댓글 분석&lt;/td&gt;
&lt;td&gt;Gemini Flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard / Landing Page 개발&lt;/td&gt;
&lt;td&gt;Kimi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 모델명과 버전은 원문의 주장 그대로이며 현재 실제 제공 모델&amp;middot;가격&amp;middot;성능은 별도 확인이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 설계 관점에서는 모델명을 고정하기보다는 역할을 다음처럼 분리하는 것이 더 핵심입니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Orchestrator LLM
        │
 ┌──────┼────────┐
 ▼      ▼        ▼
Cheap   Writer   Coding
Model   Model    Model
 │       │        │
Feed    Posts    Dashboard
Match   Replies  Landing&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;20. 이 시스템의 핵심 데이터&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서 가장 가치 있는 부분은 사실 Dashboard도, RSS도 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 다음 3개의 데이터 자산입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;① Reddit Raw Data

posts
comments
threads

        &amp;darr;

② Community Intelligence

subreddit profiles

        &amp;darr;

③ Customer/Product Intelligence

problems
language
objections
failed solutions
buying intent&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 쌓일수록:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Market Research
      &amp;darr;
Product Discovery
      &amp;darr;
Content
      &amp;darr;
Lead Discovery
      &amp;darr;
Sales&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 하나의 파이프라인이 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;21. 제가 이 시스템을 다시 설계한다면&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 원문을 그대로 구현하기보다는 &lt;b&gt;Reddit Sales System&lt;/b&gt;이라기보다 아래처럼 &lt;b&gt;Reddit Intelligence Platform&lt;/b&gt;으로 만드는 편이 확장성이 더 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;                Reddit RSS
                    │
                    ▼
             Collector Workers
                    │
                    ▼
              PostgreSQL
                    │
          ┌─────────┴─────────┐
          ▼                   ▼
      Vector DB           Analytics
          │                   │
          ▼                   ▼
 Problem Detection      Trend Detection
          │                   │
          └─────────┬─────────┘
                    ▼
                LLM Layer
                    │
       ┌────────────┼────────────┐
       ▼            ▼            ▼
 Community      Buyer Intent   Content
 Intelligence    Matching      Generator
       │            │            │
       └────────────┼────────────┘
                    ▼
                Dashboard
                    │
             Human Approval
                    │
                    ▼
                  Reddit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 기능을 5개 모듈로 나누는 것이 좋습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Community Radar&lt;/b&gt;&lt;br /&gt;커뮤니티 분위기, 문제, 표현, 트렌드 분석&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Problem Radar&lt;/b&gt;&lt;br /&gt;반복 문제와 사업기회 탐색&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Lead Radar&lt;/b&gt;&lt;br /&gt;제품과 관련된 구매 의도 게시글 탐색&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Content Copilot&lt;/b&gt;&lt;br /&gt;답변과 게시글 초안 작성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sales Queue&lt;/b&gt;&lt;br /&gt;사람이 직접 확인하고 Reddit으로 이동해 게시&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들면 Reddit뿐 아니라 이후:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Reddit
Hacker News
X
Product Hunt
Indie Hackers
GitHub Issues
Discord
YouTube Comments&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 데이터 소스를 붙일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이 글의 핵심 아이디어를 한 문장으로 요약하면:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI에게 Reddit 영업을 시키는 것이 아니라, Reddit에서 고객의 문제&amp;middot;언어&amp;middot;구매의도를 계속 수집하고 AI가 &amp;ldquo;지금 누구에게 무엇을 말해야 하는지&amp;rdquo; 준비하게 한 뒤 최종 행동은 사람이 하는 시스템입니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 제품 개발 관점에서는 단순 영업 자동화보다 &lt;b&gt;「시장 문제 발견 &amp;rarr; 제품 아이디어 &amp;rarr; 제품 생성 &amp;rarr; 잠재고객 발견 &amp;rarr; 콘텐츠 생성 &amp;rarr; 판매 &amp;rarr; 다시 고객 데이터 축적」을 하나의 루프로 묶었다는 점&lt;/b&gt;이 가장 활용 가치가 높습니다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1810</guid>
      <comments>https://javaexpert.tistory.com/1810#entry1810comment</comments>
      <pubDate>Thu, 20 Aug 2026 17:14:41 +0900</pubDate>
    </item>
    <item>
      <title>GPT-5.6 Luna&amp;middot;Terra&amp;middot;Sol 가격과 성능 비교 &amp;mdash; 어떤 모델을 써야 할까?</title>
      <link>https://javaexpert.tistory.com/1809</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI의 GPT 모델을 API로 서비스에 적용할 때 가장 고민되는 부분은 결국 두 가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;어떤 모델이 가장 성능이 좋은가?&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;그 성능에 얼마를 지불해야 하는가?&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 OpenAI의 GPT-5.6 계열은 크게 &lt;b&gt;Luna, Terra, Sol&lt;/b&gt; 세 가지 모델로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 정리하면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Luna = 저렴한 대량 처리 모델&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Terra = 가격과 성능의 균형 모델&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Sol = 최고 수준의 복잡한 추론 모델&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 세 모델 모두 추론 강도를 조절할 수 있기 때문에 서비스의 성격에 따라 상당히 세밀한 비용 최적화가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 GPT-5.6 Luna, Terra, Sol의 &lt;b&gt;API 가격, 추론 성능, 벤치마크, 그리고 실제 서비스에서 어떻게 모델을 선택하면 좋은지&lt;/b&gt; 정리해본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. GPT-6이 아니라 GPT-5.6이다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 모델명을 정확하게 구분할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna, Terra, Sol은 현재 &lt;b&gt;GPT-6 모델이 아니라 GPT-5.6 계열&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 다음과 같이 이해하면 쉽다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;GPT-5.6
 ├─ Luna
 ├─ Terra
 └─ Sol
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 모델은 같은 GPT-5.6 계열이지만 가격과 추론 성능에서 큰 차이가 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. GPT-5.6 모델별 API 가격&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 8월 19일 기준 가격은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델입력 100만 토큰캐시 입력출력 100만 토큰&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 Luna&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;$0.02&lt;/td&gt;
&lt;td&gt;$1.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 Terra&lt;/td&gt;
&lt;td&gt;$2.00&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;$12.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 Sol&lt;/td&gt;
&lt;td&gt;$5.00&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;$30.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격만 보면 차이가 상당히 크다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Luna &amp;rarr; Terra&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력과 출력 모두 약 &lt;b&gt;10배&lt;/b&gt; 비싸진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Terra &amp;rarr; Sol&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 &lt;b&gt;2.5배&lt;/b&gt; 비싸진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Luna &amp;rarr; Sol&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무려 &lt;b&gt;25배&lt;/b&gt; 차이가 난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 단순 API 비용만 생각하면 Luna가 압도적으로 저렴하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Luna, Terra, Sol의 포지션&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 쉽게 구분하면 다음과 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPT-5.6 Luna&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대량 처리 + 비용 최적화 모델&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같은 작업에 적합하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서 요약&lt;/li&gt;
&lt;li&gt;번역&lt;/li&gt;
&lt;li&gt;정보 분류&lt;/li&gt;
&lt;li&gt;간단한 챗봇&lt;/li&gt;
&lt;li&gt;데이터 가공&lt;/li&gt;
&lt;li&gt;검색 결과 정리&lt;/li&gt;
&lt;li&gt;대량 콘텐츠 처리&lt;/li&gt;
&lt;li&gt;간단한 AI Agent&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 호출량이 많은 서비스라면 가장 먼저 고려할 수 있는 모델이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPT-5.6 Terra&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성능과 비용의 균형형 모델&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna보다 훨씬 높은 비용이 들지만 복잡한 작업에서 안정적인 성능을 기대할 수 있는 위치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 코딩 에이전트&lt;/li&gt;
&lt;li&gt;복잡한 문서 분석&lt;/li&gt;
&lt;li&gt;업무 자동화&lt;/li&gt;
&lt;li&gt;데이터 분석&lt;/li&gt;
&lt;li&gt;리서치&lt;/li&gt;
&lt;li&gt;코드 리뷰&lt;/li&gt;
&lt;li&gt;디버깅&lt;/li&gt;
&lt;li&gt;다단계 Agent Workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 SaaS나 AI Agent 서비스를 만든다면 가장 범용적인 모델이 될 가능성이 높다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPT-5.6 Sol&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최고 수준의 복잡한 추론 모델&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 비싸지만 어려운 문제를 풀어야 할 때 사용하는 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 영역은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 시스템 설계&lt;/li&gt;
&lt;li&gt;고난도 프로그래밍&lt;/li&gt;
&lt;li&gt;대규모 코드 분석&lt;/li&gt;
&lt;li&gt;수학 문제&lt;/li&gt;
&lt;li&gt;과학적 추론&lt;/li&gt;
&lt;li&gt;장시간 Agent 작업&lt;/li&gt;
&lt;li&gt;복잡한 의사결정&lt;/li&gt;
&lt;li&gt;멀티스텝 리서치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 &lt;b&gt;비용보다 결과 품질이 중요한 작업&lt;/b&gt;에 사용하는 모델이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 추론 강도도 조절할 수 있다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPT-5.6의 재미있는 점은 모델만 선택하는 것이 아니라 &lt;b&gt;Reasoning Effort&lt;/b&gt;, 즉 추론 강도까지 선택할 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원되는 단계는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;none
low
medium
high
xhigh
max
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Luna라고 해서 항상 단순한 모델처럼 동작하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Luna + none
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 사용하면 매우 저렴하고 빠른 작업 처리가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Luna + high
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 사용하면 Luna에서도 훨씬 깊은 추론을 수행하도록 설정할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. Reasoning을 높이면 API 단가도 올라갈까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분이 하나 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reasoning을&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;low
&amp;rarr; medium
&amp;rarr; high
&amp;rarr; xhigh
&amp;rarr; max
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 높인다고 해서 &lt;b&gt;토큰 단가 자체가 변경되는 것은 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 비용은 증가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 모델이 문제를 해결하기 위해 사용하는 &lt;b&gt;Reasoning Token&lt;/b&gt;이 증가할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;토큰 가격
=
동일
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이지만&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;사용 토큰 수
=
증가 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하기 때문에 최종 API 비용은 올라갈 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 모델별 추론 성능 비교&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개된 벤치마크 기준으로 모델 간 성능을 비교하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BenchmarkLunaTerraSol&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Artificial Analysis Intelligence Index&lt;/td&gt;
&lt;td&gt;51.2&lt;/td&gt;
&lt;td&gt;55.0&lt;/td&gt;
&lt;td&gt;&lt;b&gt;58.9&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coding Agent Index&lt;/td&gt;
&lt;td&gt;74.6&lt;/td&gt;
&lt;td&gt;77.4&lt;/td&gt;
&lt;td&gt;&lt;b&gt;80.0&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agents' Last Exam&lt;/td&gt;
&lt;td&gt;50.3%&lt;/td&gt;
&lt;td&gt;50.4%&lt;/td&gt;
&lt;td&gt;&lt;b&gt;52.7%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPQA Diamond&lt;/td&gt;
&lt;td&gt;92.3%&lt;/td&gt;
&lt;td&gt;92.9%&lt;/td&gt;
&lt;td&gt;&lt;b&gt;94.6%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FrontierMath Tier 1&amp;ndash;3&lt;/td&gt;
&lt;td&gt;78.6%&lt;/td&gt;
&lt;td&gt;84.9%&lt;/td&gt;
&lt;td&gt;&lt;b&gt;89.0%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FrontierMath Tier 4&lt;/td&gt;
&lt;td&gt;58.5%&lt;/td&gt;
&lt;td&gt;68.3%&lt;/td&gt;
&lt;td&gt;&lt;b&gt;83.0%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BrowseComp&lt;/td&gt;
&lt;td&gt;83.3%&lt;/td&gt;
&lt;td&gt;87.5%&lt;/td&gt;
&lt;td&gt;&lt;b&gt;90.4%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSWorld 2.0&lt;/td&gt;
&lt;td&gt;45.6%&lt;/td&gt;
&lt;td&gt;50.2%&lt;/td&gt;
&lt;td&gt;&lt;b&gt;62.6%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 상당히 흥미로운 특징이 나타난다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 쉬운 문제에서는 Luna도 상당히 강하다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격은 Luna와 Sol이 무려 25배 차이가 난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 전체 Intelligence Index를 보면&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Luna   51.2
Terra  55.0
Sol    58.9
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격 차이에 비해 일반적인 벤치마크 차이는 생각보다 크지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 단순한 작업에서 무조건 Sol을 사용하는 것은 비용 효율이 떨어질 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 어려운 문제에서는 Sol의 차이가 커진다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 문제 난도가 높아질수록 Sol의 강점이 분명하게 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 사례가 FrontierMath Tier 4다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Luna   58.5%
Terra  68.3%
Sol    83.0%
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna와 Sol의 차이가 무려 &lt;b&gt;24.5%p&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSWorld 2.0에서도 비슷하다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Luna   45.6%
Terra  50.2%
Sol    62.6%
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 일반적인 작업에서는 Luna도 충분히 강하지만, &lt;b&gt;매우 복잡한 Agent 작업이나 고난도 추론에서는 Sol의 가치가 커지는 구조&lt;/b&gt;라고 볼 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 가격 대비 성능을 계산해보면 Luna가 상당히 강하다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격과 Intelligence Index를 단순 비교해보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Luna&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$0.20 / 1M tokens
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intelligence Index&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;51.2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Terra&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$2.00 / 1M tokens
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intelligence Index&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;55.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sol&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$5.00 / 1M tokens
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intelligence Index&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;58.9
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna &amp;rarr; Terra로 넘어가면서 비용은 약 10배 증가하지만 Intelligence Index는 약 7% 정도 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sol까지 올라가면 Luna 대비 가격은 25배지만 Intelligence Index 차이는 약 15% 수준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 벤치마크 하나만으로 모델의 실제 성능을 평가할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;대량 API 서비스에서는 Luna의 가격 경쟁력이 매우 크다&lt;/b&gt;는 점은 눈여겨볼 만하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 그렇다면 실제 서비스에서는 어떤 모델을 써야 할까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 서비스에서 모델을 하나만 사용하는 것보다 작업 난도에 따라 모델을 나누는 구조가 비용 효율적일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음처럼 구성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업모델Reasoning&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;단순 분류&lt;/td&gt;
&lt;td&gt;Luna&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;요약&lt;/td&gt;
&lt;td&gt;Luna&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;번역&lt;/td&gt;
&lt;td&gt;Luna&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;일반 챗봇&lt;/td&gt;
&lt;td&gt;Luna&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복잡한 질문&lt;/td&gt;
&lt;td&gt;Luna&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;업무 자동화&lt;/td&gt;
&lt;td&gt;Terra&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI 코딩&lt;/td&gt;
&lt;td&gt;Terra&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복잡한 디버깅&lt;/td&gt;
&lt;td&gt;Terra&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;시스템 설계&lt;/td&gt;
&lt;td&gt;Sol&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;고난도 연구&lt;/td&gt;
&lt;td&gt;Sol&lt;/td&gt;
&lt;td&gt;xhigh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;매우 어려운 문제&lt;/td&gt;
&lt;td&gt;Sol&lt;/td&gt;
&lt;td&gt;max&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 가장 흥미로운 구조는 AI Model Routing&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 개인적으로 가장 효율적인 아키텍처는 세 모델을 계층적으로 사용하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자의 모든 요청을 처음부터 Sol에게 보내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Luna가 처리한다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;사용자 요청
      &amp;darr;
    Luna
      &amp;darr;
처리 가능?
   &amp;darr;       &amp;darr;
 YES       NO
 &amp;darr;          &amp;darr;
응답       Terra
             &amp;darr;
          처리 가능?
          &amp;darr;       &amp;darr;
         YES      NO
          &amp;darr;        &amp;darr;
         응답      Sol
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식을 &lt;b&gt;Model Routing&lt;/b&gt; 또는 &lt;b&gt;Model Escalation&lt;/b&gt; 구조라고 볼 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. Luna &amp;rarr; Terra &amp;rarr; Sol 자동 승격&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 실제 AI 서비스를 만든다면 다음과 같은 구조도 고려할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Level 1&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Luna + low
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 일반 요청 처리&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Level 2&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Luna + high
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 어려운 문제&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Level 3&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Terra + high
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 Agent / Coding&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Level 4&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Sol + high
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고난도 문제&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Level 5&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Sol + max
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 문제 해결 시도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 비용을 상당히 세밀하게 제어할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. AI Coding Agent에도 이 구조를 적용할 수 있다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 예를 들어 AI 개발 플랫폼을 만든다고 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 코드 작업은 Luna가 처리한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;변수명 변경
README 수정
간단한 API 작성
테스트 코드 생성
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 복잡하면 Terra로 승격한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;버그 분석
리팩터링
DB 설계
API 구조 변경
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 어려운 작업은 Sol에게 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;전체 시스템 설계
복잡한 성능 문제
Race Condition
Distributed System
대규모 Architecture 변경
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 만들면 모든 개발 작업에 비싼 모델을 사용하는 것을 피할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. 컨텍스트 길이도 상당히 크다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 모델 모두 최대 약&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1.05M tokens
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수준의 컨텍스트를 지원하고 최대 출력은&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;128K tokens
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 대규모 코드베이스 분석이나 긴 문서를 처리하는 Agent를 만드는 데도 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 매우 긴 컨텍스트를 사용할 경우 별도의 Long Context 가격 정책이 적용될 수 있으므로 대규모 문서를 항상 전체 입력하는 방식보다는 검색&amp;middot;RAG&amp;middot;Context Engineering과 함께 사용하는 것이 비용 측면에서 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. 결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPT-5.6 Luna, Terra, Sol을 한 문장으로 정리하면 다음과 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Luna&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가격 대비 성능 최강&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대량 서비스와 일반적인 AI 기능에 적합하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Terra&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI Agent와 코딩의 균형점&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능과 가격을 모두 고려해야 하는 업무형 서비스에 적합하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sol&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최고 난도의 문제 해결&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비용보다 결과 품질이 중요한 복잡한 작업에 적합하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 실제 서비스를 만든다면 가장 흥미로운 전략은 특정 모델 하나를 선택하는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 오히려&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Luna
 &amp;darr;
Terra
 &amp;darr;
Sol
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태의 &lt;b&gt;다단계 AI Model Router&lt;/b&gt;를 만드는 것이 비용과 성능을 동시에 관리하는 데 유리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 요청은 저렴한 Luna가 처리하고, Luna가 해결하지 못하는 문제만 Terra로 넘긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 해결이 어렵다면 마지막으로 Sol을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델의 성능이 계속 높아지는 시대에는 결국&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;가장 좋은 모델을 사용하는 것&amp;rdquo;보다&lt;br /&gt;&amp;ldquo;어떤 문제에 어떤 모델을 배치하는가&amp;rdquo;가 더 중요해질 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 GPT-5.6 Luna, Terra, Sol을 바라볼 때 가장 중요한 포인트다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기준일: 2026년 8월 19일&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ API 가격과 모델 정책은 변경될 수 있으므로 실제 개발 및 서비스 적용 전 OpenAI 공식 API 문서에서 최신 가격과 모델 사양을 다시 확인하는 것이 좋다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1809</guid>
      <comments>https://javaexpert.tistory.com/1809#entry1809comment</comments>
      <pubDate>Wed, 19 Aug 2026 09:50:23 +0900</pubDate>
    </item>
    <item>
      <title>현재 한국의 외환&amp;middot;신용&amp;middot;채권&amp;middot;자금흐름&amp;middot;부동산&amp;middot;부채 현황에 대해 확인</title>
      <link>https://javaexpert.tistory.com/1808</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;2026년 8월 14일 18:11 KST 기준&lt;/b&gt;으로 다시 확인했습니다. 환율&amp;middot;채권&amp;middot;주식은 오늘 시장 데이터, CDS는 8월 13일 최신치, 부동산은 8월 10일 기준 주간통계, 가계부채는 최신 공식 총액과 7월 대출 흐름을 사용했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  현재 한국 경제 위기 신호등&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지표판정현재 확인값핵심 해석&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;  &lt;b&gt;원/달러 환율&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;  &lt;b&gt;주의 &amp;darr; 개선&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;1,418.3원&lt;/b&gt; 8/14 종가&lt;/td&gt;
&lt;td&gt;6월 1,561.5원까지 갔던 극단적 원화 약세에서는 크게 회복&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt; ️ &lt;b&gt;한국 CDS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;  &lt;b&gt;정상&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;5년물 &lt;b&gt;22.63bp&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;현재 가장 중요한 긍정 신호. 국가 신용위험 급등은 관찰되지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  &lt;b&gt;국고채 금리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;  &lt;b&gt;위험&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;10년 &lt;b&gt;4.31%&lt;/b&gt;, 30년 &lt;b&gt;4.66%&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;현재 6개 중 가장 주의해야 할 지표 중 하나&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  &lt;b&gt;외국인 자금&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;  &lt;b&gt;주의 &amp;uarr; 회복&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;7월 주식 &lt;b&gt;-62.6억 달러&lt;/b&gt;, 8/14 하루 &lt;b&gt;+3.04조원 순매수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;대규모 이탈 뒤 최근 강하게 복귀 중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  &lt;b&gt;부동산&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;  &lt;b&gt;위험&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;전국 주간 &lt;b&gt;+0.08%&lt;/b&gt;, 서울 &lt;b&gt;+0.21%&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;붕괴 위험이 아니라 &lt;b&gt;과열&amp;middot;부채 연계 위험&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  &lt;b&gt;가계부채&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;  &lt;b&gt;위험&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1분기 &lt;b&gt;1,993.1조원&lt;/b&gt;, 7월 대출 &lt;b&gt;+6.2조원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;이미 높은 부채에 올해 대출 증가가 다시 빨라짐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;종합 판정:   &lt;b&gt;주의 &amp;mdash; 다만 빨간불에 가까운 주의&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 판단으로는 &lt;b&gt;현재 한국이 외환위기&amp;middot;국가부도형 위기에 들어간 상태라고 볼 데이터는 아닙니다.&lt;/b&gt; 가장 중요한 이유가 CDS입니다. 한국 5년 CDS가 8월 13일 기준 &lt;b&gt;22.63bp&lt;/b&gt;입니다. 참고로 2022년 시장 불안기에는 70bp까지 올라간 적이 있었습니다. 현재 국제시장이 한국 정부의 채무불이행 위험을 급격히 가격에 반영하고 있는 상황과는 상당한 차이가 있습니다. (&lt;a href=&quot;https://www.worldgovernmentbonds.com/cds-historical-data/south-korea/5-years/?utm_source=chatgpt.com&quot;&gt;World Government Bonds&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;b&gt;국내의 금리&amp;middot;부동산&amp;middot;가계부채라는 세 축은 분명히 위험 신호&lt;/b&gt;가 켜져 있습니다. 그래서 저는 전체를   정상으로 보기는 어렵습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;①   환율 &amp;mdash;   주의, 하지만 빠르게 개선 중&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8월 14일 서울 외환시장 원/달러 종가는 &lt;b&gt;1,418.3원&lt;/b&gt;입니다. (&lt;a href=&quot;https://english.mofe.go.kr/&quot;&gt;재정경제부&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 방향입니다. 원화는 6월 한때 &lt;b&gt;1달러=1,561.5원&lt;/b&gt;까지 떨어져 17년 만의 약세 수준을 기록했는데, 7월에는 8% 이상 반등했습니다. 정부의 달러 매도 개입도 있었고 7월 말 1,418원까지 회복했습니다. (&lt;a href=&quot;https://www.reuters.com/world/asia-pacific/south-korea-conducts-rare-dollar-selling-intervention-source-says-2026-07-30/?utm_source=chatgpt.com&quot;&gt;Reuters&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 현재 환율은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1,561 &amp;rarr; 1,418&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 상당히 정상화됐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] &lt;b&gt;환율만 보면 위기 방향이 아니라 위기에서 멀어지는 방향&lt;/b&gt;입니다. 다만 1,400원대 자체가 낮은 환율은 아니며, 한국은행도 최근 원화 수준이 여전히 인플레이션 압력 요인이 될 수 있다고 평가했습니다. (&lt;a href=&quot;https://www.reuters.com/world/asia-pacific/bank-korea-likely-raise-interest-rates-further-outgoing-deputy-chief-says-2026-08-11/?utm_source=chatgpt.com&quot;&gt;Reuters&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;판정:   주의 &amp;rarr;   방향으로 개선 중&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;②  ️ CDS &amp;mdash;   정상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 6개 가운데 &lt;b&gt;가장 중요하게 보는 지표&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국 5년물 CDS 최신치는 8월 13일 기준 &lt;b&gt;22.63bp&lt;/b&gt;입니다. (&lt;a href=&quot;https://www.worldgovernmentbonds.com/cds-historical-data/south-korea/5-years/?utm_source=chatgpt.com&quot;&gt;World Government Bonds&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDS는 쉽게 말하면&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;한국 정부가 빚을 못 갚을 위험에 대해 보험료를 얼마나 내야 하느냐&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 국제시장이 가격으로 표현한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 30년 국채금리가 사상 최고인데 &lt;b&gt;CDS가 20bp 초반이라는 조합은 상당히 중요합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 현재 30년 금리 상승을 시장이 주로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;한국 국가부도 위험 &amp;uarr;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 보고 있다기보다,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인플레이션 + 장기금리 + 국채 공급 + 장기채 수급 + 기간 프리미엄&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제로 가격에 반영하고 있을 가능성이 더 높습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;판정:   정상&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;③   국고채 &amp;mdash;   위험&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기는 확실히 안 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8월 14일 한국 국고채 10년물은 약 **4.31%**입니다. 1년 전보다 약 &lt;b&gt;1.48%p 높습니다.&lt;/b&gt; (&lt;a href=&quot;https://tradingeconomics.com/south-korea/government-bond-yield?utm_source=chatgpt.com&quot;&gt;Trading Economics&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;30년물은 &lt;b&gt;4.66%&lt;/b&gt; 수준이고, 8월 12일에는 &lt;b&gt;4.67%&lt;/b&gt;까지 올라 &lt;b&gt;30년물이 도입된 2012년 이후 최고 수준&lt;/b&gt;을 기록했습니다. (&lt;a href=&quot;https://tradingeconomics.com/south-korea/30-year-bond-yield?utm_source=chatgpt.com&quot;&gt;Trading Economics&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 현재 기준금리는 **2.75%**입니다. (&lt;a href=&quot;https://www.bok.or.kr/eng/main/main.do?utm_source=chatgpt.com&quot;&gt;한국은행&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 대략&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기준금리 2.75%&lt;br /&gt;&amp;rarr; 3년 3.80%&lt;br /&gt;&amp;rarr; 5년 4.05%&lt;br /&gt;&amp;rarr; 10년 4.31%&lt;br /&gt;&amp;rarr; 30년 4.66%&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태입니다. 3년&amp;middot;5년물 최신치는 정부 경제지표에서도 확인됩니다. (&lt;a href=&quot;https://english.mofe.go.kr/&quot;&gt;재정경제부&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 이것은 한국의 &lt;b&gt;장기 자금조달 여건이 상당히 빡빡해지고 있다는 경고&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정부뿐 아니라 회사채 &amp;rarr; 기업대출 &amp;rarr; PF &amp;rarr; 주택담보대출 금리에도 간접적으로 영향을 줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;판정:   위험&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 중요한 구분은:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  국채금리 위험 &amp;ne;   국가부도 위험&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다. CDS가 이를 구분해 주고 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;④   외국인 자금 &amp;mdash;   주의, 최근 급격히 회복&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기는 재미있는 변화가 나타나고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7월에는 외국인이 한국 주식을 &lt;b&gt;62.6억 달러 순매도&lt;/b&gt;했습니다. 그것도 &lt;b&gt;3개월 연속 순유출&lt;/b&gt;이었습니다. (&lt;a href=&quot;https://www.reuters.com/world/china/taiwan-south-korea-drive-asian-equity-outflows-july-ai-worries-bite-2026-08-11/?utm_source=chatgpt.com&quot;&gt;Reuters&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 최근 방향이 반대로 바뀌었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;8월 14일 하루만 유가증권시장에서 외국인이 3.04조원을 순매수&lt;/b&gt;했고, 코스피는 2.42% 상승한 6,977.94로 마감했습니다. (&lt;a href=&quot;https://en.yna.co.kr/view/AEN20260814008051320&quot;&gt;연합뉴스&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5~7월: 외국인 이탈&lt;br /&gt;&amp;rarr; 8월: 외국인 다시 매수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 전환이 나타나고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 따라서 현재 상황을 &lt;b&gt;&amp;ldquo;외국인이 한국을 탈출하고 있다&amp;rdquo;&lt;/b&gt;고 표현하는 것은 최신 데이터와 맞지 않습니다. 그렇다고 몇 거래일의 매수만으로 3개월간의 자금유출 추세가 완전히 끝났다고 확인할 수도 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;판정:   주의 &amp;rarr; 개선 중&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⑤   부동산 &amp;mdash;   위험&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기는 &lt;b&gt;폭락 위험이 아니라 과열 위험&lt;/b&gt;이라는 점이 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국부동산원 8월 둘째 주(8월 10일 기준) 자료를 보면 전국 아파트 가격은 한 주 동안 &lt;b&gt;+0.08%&lt;/b&gt;, 서울은 &lt;b&gt;+0.21%&lt;/b&gt; 상승했습니다. 서울 상승률은 직전 주 +0.26%보다는 낮아졌습니다. (&lt;a href=&quot;https://www.yna.co.kr/amp/view/AKR20260813088751003&quot;&gt;연합뉴스&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 내부적으로 변화가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강남구는 &lt;b&gt;-0.02%&lt;/b&gt;, 서초구는 &lt;b&gt;-0.04%&lt;/b&gt;로 하락 전환했지만 중랑 +0.46%, 성북 +0.43%, 서대문 +0.41%, 중구&amp;middot;강북 +0.40% 등 중저가 지역은 상당한 상승세를 이어갔습니다. (&lt;a href=&quot;https://www.yna.co.kr/amp/view/AKR20260813088751003&quot;&gt;연합뉴스&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정부도 현재 주택시장을 심각한 과열 문제로 보고 주택공급&amp;middot;금융대책을 발표한 상태입니다. (&lt;a href=&quot;https://www.reuters.com/world/asia-pacific/south-korea-unveils-package-boost-housing-supply-support-young-buyers-2026-08-13/?utm_source=chatgpt.com&quot;&gt;Reuters&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 따라서 현재 부동산의 위험은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;집값이 무너진다&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;높은 금리에도 집값 상승 &amp;rarr; 다시 대출 증가 &amp;rarr; 가계부채 증가&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쪽입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;판정:   위험&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⑥   가계부채 &amp;mdash;   위험&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국은행이 현재 공식적으로 발표한 최신 분기 총액은 2026년 1분기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가계신용은 &lt;b&gt;1,993.1조원&lt;/b&gt;, 전분기보다 &lt;b&gt;14.0조원 증가&lt;/b&gt;했습니다. 이 가운데 가계대출이 1,865.8조원입니다. (&lt;a href=&quot;https://www.bok.or.kr/portal/bbs/B0000501/view.do?depth=201264&amp;amp;menuNo=201264&amp;amp;nttId=10098042&amp;amp;programType=newsData&amp;amp;relate=Y&quot;&gt;한국은행&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2분기 공식 가계신용은 아직 발표 전이며 &lt;b&gt;8월 19일 발표 예정&lt;/b&gt;입니다. 따라서 지금 2분기 총액을 확정적으로 말할 수는 없습니다. (&lt;a href=&quot;https://www.bok.or.kr/portal/stats/statsPublictSchdul/listCldr.do?date=2020-06&amp;amp;menuNo=200775&amp;amp;utm_source=chatgpt.com&quot;&gt;한국은행&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 더 빠르게 나오는 월별 금융권 대출을 보면:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4월 +3.5조&lt;br /&gt;&amp;rarr; 5월 +9.3조&lt;br /&gt;&amp;rarr; 6월 +8.3조&lt;br /&gt;&amp;rarr; 7월 +6.2조원&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다. 올해 &lt;b&gt;1~7월 누적으로 +35.3조원&lt;/b&gt; 늘었습니다. (&lt;a href=&quot;https://www.asiae.co.kr/en/article/2026081410240887044?utm_source=chatgpt.com&quot;&gt;아시아경제&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 두 달 증가 속도가 떨어진 것은 긍정적이지만, 금융당국도 7월 증가액이 과거 평균보다 여전히 높은 수준이라고 보고 있습니다. (&lt;a href=&quot;https://v.daum.net/v/xYHGF4sAv7?utm_source=chatgpt.com&quot;&gt;다음 뉴스&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;판정:   위험&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래서 제가 현재 가장 걱정하는 것은 이 연결입니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;부동산 가격 상승&lt;/b&gt;&lt;br /&gt;&amp;darr;&lt;br /&gt;&lt;b&gt;주담대&amp;middot;가계대출 증가&lt;/b&gt;&lt;br /&gt;&amp;darr;&lt;br /&gt;&lt;b&gt;가계부채 2,000조원 부근&lt;/b&gt;&lt;br /&gt;&amp;darr;&lt;br /&gt;&lt;b&gt;장기 국채금리 4%대 중후반&lt;/b&gt;&lt;br /&gt;&amp;darr;&lt;br /&gt;&lt;b&gt;가계&amp;middot;기업 이자부담 증가&lt;/b&gt;&lt;br /&gt;&amp;darr;&lt;br /&gt;&lt;b&gt;소비&amp;middot;투자 위축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시나리오입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 지금 한국의 핵심 위험은 &lt;b&gt;&amp;ldquo;외환위기&amp;rdquo;보다는 부동산 + 부채 + 고금리의 조합&lt;/b&gt;에 더 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 실제 외환&amp;middot;국가신용 위기로 발전하고 있다면 &lt;b&gt;원화 급락 + CDS 급등 + 외국인 지속 이탈 + 국채금리 급등&lt;/b&gt;이 동시에 나타나는지를 봐야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;환율   &amp;rarr; 개선&lt;br /&gt;CDS  &lt;br /&gt;외국인   &amp;rarr; 개선&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;국채  &lt;br /&gt;부동산  &lt;br /&gt;가계부채  &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;&amp;ldquo;한국이 당장 위험하다&amp;rdquo;보다는 &amp;ldquo;대외위기는 아직 아니지만 내부 금융불균형의 압력이 상당히 높다&amp;rdquo;&lt;/b&gt;가 현재 데이터에 더 맞는 표현입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 처음 보여주신 &lt;b&gt;30년 국채 4.6%대&lt;/b&gt;는 무시할 숫자는 아닙니다. 앞으로는 &lt;b&gt;CDS가 같이 올라가기 시작하는지&lt;/b&gt;를 보는 게 핵심입니다. 30년물은 계속 높은데 CDS가 20bp대에 머물면 장기금리 문제에 가깝고, &lt;b&gt;CDS까지 빠르게 튀기 시작하면 상황의 성격이 달라집니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 지표들은 변동이 빨라서 매일 같은 기준으로 점검하면 변화가 훨씬 잘 보입니다.&lt;/p&gt;</description>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1808</guid>
      <comments>https://javaexpert.tistory.com/1808#entry1808comment</comments>
      <pubDate>Fri, 14 Aug 2026 18:16:53 +0900</pubDate>
    </item>
    <item>
      <title>AI가 코드를 쓰는 시대, 이제 필요한 것은 &amp;lsquo;소프트웨어 공장&amp;rsquo;이다 (하네스)</title>
      <link>https://javaexpert.tistory.com/1807</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구가 빠르게 발전하면서 소프트웨어를 만드는 속도는 이전과 비교하기 어려울 정도로 빨라졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cursor, Claude Code, Codex와 같은 도구를 사용하면 개발자는 몇 시간 만에 기능을 구현하고, 화면을 만들고, 테스트 코드까지 작성할 수 있다. 예전에는 며칠 걸리던 작업이 몇 시간 안에 끝나는 경우도 이제 낯설지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 여기서 새로운 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드를 만드는 속도는 빨라졌지만, 좋은 소프트웨어를 지속적으로 배포하는 속도까지 같은 비율로 빨라진 것은 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만들어내는 코드가 많아질수록 리뷰해야 할 코드도 늘어난다. 품질 편차도 커지고, 어느 순간부터는 사람이 전체 시스템을 제대로 이해하지 못하는 상황까지 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 AI 개발의 다음 경쟁력은 &amp;lsquo;코드를 얼마나 빨리 생성할 수 있는가&amp;rsquo;가 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI가 만든 결과물을 어떻게 안정적으로 검증하고 지속적으로 제품으로 내보낼 것인가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 이동하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 지점에서 다시 주목받는 개념이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;소프트웨어 팩토리(Software Factory)&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소프트웨어 팩토리란 무엇인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 팩토리를 단순하게 표현하면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 개발 공정을 설계하고, 여러 AI 에이전트가 그 공정 안에서 반복적으로 소프트웨어를 생산하는 개발 체계&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 중요한 단어는 &amp;lsquo;AI&amp;rsquo;보다 오히려 &lt;b&gt;공정(Process)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 AI 코딩은 개발자가 AI에게 이렇게 요청하는 형태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;로그인 기능 만들어줘.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 코드를 작성하고, 개발자는 결과를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 소프트웨어 팩토리에서는 작업이 조금 다르게 흘러간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 요구사항이 표준화된 형태로 입력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 만들어야 하는지, 어디까지 구현해야 하는지, 어떤 조건을 만족해야 하는지, 어떤 테스트를 통과해야 하는지가 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 여러 에이전트가 역할을 나눠 작업한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 에이전트는 구현하고, 다른 에이전트는 테스트한다. 또 다른 에이전트는 보안이나 코드 품질을 검사할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 마지막으로 모든 결과물이 동일한 품질 검사를 통과한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건을 만족한 변경만 실제 서비스에 반영된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조를 단순화하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요구사항 &amp;rarr; 표준화된 Spec &amp;rarr; 작업 Queue &amp;rarr; AI Agent &amp;rarr; 검증 &amp;rarr; 리뷰 &amp;rarr; 배포 &amp;rarr; 사용자 피드백 &amp;rarr; 다시 작업 Queue&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 루프가 하나가 아니라 여러 개 동시에 돌아가는 것이 소프트웨어 팩토리의 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 코딩과 소프트웨어 팩토리는 다르다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 조직이 개발자들에게 AI 코딩 도구를 제공하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AI 도구를 많이 사용하는 조직이 반드시 AI 개발을 잘하는 조직은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 10명이 각자 AI를 사용하면 코드 생산량은 크게 증가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 그다음이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리뷰해야 할 Pull Request가 급증하고, 테스트해야 할 기능도 늘어난다. 각 개발자가 서로 다른 방식으로 AI를 사용하면서 코드 스타일이나 설계 방식도 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이런 상황이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드 생산량은 10배 늘었는데 검증 능력은 그대로다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 AI는 오히려 새로운 병목을 만들어낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 소프트웨어 팩토리는 단순히 AI에게 일을 많이 시키는 개념이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 결과물이 반드시 일정한 공정을 통과하도록 만드는 것이 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공방의 품질과 공장의 품질&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 팩토리를 이해하는 데 좋은 비유가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;공방&amp;rsquo;과 &amp;lsquo;공장&amp;rsquo;의 차이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공방에서는 장인의 실력이 제품의 품질을 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 개발자가 있으면 좋은 코드가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 공장에서는 조금 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품의 품질이 특정 작업자의 능력에만 의존하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 자재를 사용했는지, 어떤 공정을 통과했는지, 어떤 검사를 통과했는지가 품질의 근거가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 역시 같은 방향으로 이동할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 이런 질문이 중요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이 코드는 누가 만들었나?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 다음 질문이 더 중요해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이 코드는 어떤 기준을 만족했고, 어떤 검사를 통과했나?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 작성했든 AI가 작성했든 같은 품질 기준을 통과하도록 만드는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소프트웨어 팩토리의 네 가지 조건&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 팩토리를 운영하려면 최소한 네 가지 요소가 필요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 입력의 표준화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 자유로운 자연어만 던지는 방식으로는 안정적인 결과를 얻기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 요청 자체가 일정한 구조를 가져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 단순히&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;구글 로그인 만들어줘.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 요청하는 것이 아니라,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 목표&lt;/li&gt;
&lt;li&gt;구현 범위&lt;/li&gt;
&lt;li&gt;기술적 제약&lt;/li&gt;
&lt;li&gt;Acceptance Criteria&lt;/li&gt;
&lt;li&gt;테스트 요구사항&lt;/li&gt;
&lt;li&gt;보안 요구사항&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등을 명확하게 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 좋은 코드를 요구하기 전에 &lt;b&gt;좋은 작업 정의를 제공하는 것&lt;/b&gt;이 먼저다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 공통 검증&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 작성한 코드와 AI가 작성한 코드가 다른 검사를 받아서는 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 변경은 동일한 품질 기준을 통과해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 항목들이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unit Test&lt;/li&gt;
&lt;li&gt;Integration Test&lt;/li&gt;
&lt;li&gt;E2E Test&lt;/li&gt;
&lt;li&gt;Security Scan&lt;/li&gt;
&lt;li&gt;Performance Test&lt;/li&gt;
&lt;li&gt;Accessibility&lt;/li&gt;
&lt;li&gt;디자인 시스템 준수&lt;/li&gt;
&lt;li&gt;API 호환성&lt;/li&gt;
&lt;li&gt;코드 품질 검사&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만들었다는 이유만으로 더 느슨한 기준을 적용해서는 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 코드 생성량이 커질수록 자동 검증의 비중은 더 중요해진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 결과의 측정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공장은 측정할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 팩토리 역시 마찬가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 지표를 볼 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발 사이클 타임&lt;/li&gt;
&lt;li&gt;PR Merge 시간&lt;/li&gt;
&lt;li&gt;테스트 실패율&lt;/li&gt;
&lt;li&gt;배포 후 버그 발생률&lt;/li&gt;
&lt;li&gt;Rollback 비율&lt;/li&gt;
&lt;li&gt;보안 문제 발생률&lt;/li&gt;
&lt;li&gt;Agent 성공률&lt;/li&gt;
&lt;li&gt;Human Intervention 비율&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 AI 모델이 좋은지보다 더 중요한 질문은 이것일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어떤 개발 루프가 가장 안정적으로 좋은 결과를 만들어내는가.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 추적 가능성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성형 AI는 같은 입력을 주더라도 항상 같은 결과를 만들지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 완벽한 재현성보다는 &lt;b&gt;어떻게 만들어졌는지를 추적할 수 있는 구조&lt;/b&gt;가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 하나의 변경 사항에 다음 정보가 남을 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 Task에서 시작됐는지&lt;/li&gt;
&lt;li&gt;어떤 Prompt를 사용했는지&lt;/li&gt;
&lt;li&gt;어떤 모델을 사용했는지&lt;/li&gt;
&lt;li&gt;어떤 Agent가 실행했는지&lt;/li&gt;
&lt;li&gt;어떤 코드 버전을 기반으로 했는지&lt;/li&gt;
&lt;li&gt;어떤 테스트를 통과했는지&lt;/li&gt;
&lt;li&gt;누가 승인했는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기록은 문제가 발생했을 때 매우 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 개발에 더 깊게 들어올수록 &lt;b&gt;코드 자체뿐 아니라 생성 과정 역시 개발 자산&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결국 핵심은 Harness다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 AI Agent 개발에서 자주 등장하는 개념이 &lt;b&gt;Harness&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Harness는 AI Agent가 일하는 환경과 규칙 전체를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 AI에게 제공하는 &amp;lsquo;작업장&amp;rsquo;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에는 다음과 같은 요소가 포함될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Context&lt;/li&gt;
&lt;li&gt;Rules&lt;/li&gt;
&lt;li&gt;Tools&lt;/li&gt;
&lt;li&gt;Permissions&lt;/li&gt;
&lt;li&gt;Memory&lt;/li&gt;
&lt;li&gt;Runtime&lt;/li&gt;
&lt;li&gt;Tests&lt;/li&gt;
&lt;li&gt;Quality Gate&lt;/li&gt;
&lt;li&gt;Definition of Done&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델이 아무리 좋아도 이런 구조가 없다면 결과 품질은 흔들릴 수밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 모델이 조금 부족하더라도 좋은 Harness가 있다면 일정 수준 이상의 결과를 반복적으로 만들어낼 가능성이 높아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 앞으로 AI 개발에서 중요한 경쟁력은 모델을 선택하는 능력보다 &lt;b&gt;Agent가 안정적으로 일할 수 있는 작업 환경을 설계하는 능력&lt;/b&gt;이 될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;거대한 AI 하나보다 작은 루프 여러 개&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI Agent를 활용하는 방식에서도 변화가 나타나고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 하나의 강력한 Agent에게 긴 작업을 맡기는 방식이 주목받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이 서비스를 처음부터 끝까지 만들어줘.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 작업이 길어질수록 Context가 복잡해지고 오류가 누적될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 최근에는 작업을 작게 나누고 짧은 Agent 실행을 반복하는 방식도 많이 논의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task 1을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 Git이나 파일에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Task에서는 새로운 Context로 Agent를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다시 결과를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작은 루프가 반복된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 소프트웨어 팩토리는 하나의 거대한 AI 개발자가 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;수많은 작은 AI 작업 루프가 연결된 개발 조직&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;디자인 역시 공장의 품질 기준이 된다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 팩토리가 생산하는 것은 코드가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 실제로 사용하는 제품이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 디자인 역시 검증 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 규칙을 자동으로 검사할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정된 Color Token을 사용했는가&lt;/li&gt;
&lt;li&gt;Spacing 규칙을 지켰는가&lt;/li&gt;
&lt;li&gt;Typography가 Design System과 일치하는가&lt;/li&gt;
&lt;li&gt;Loading 상태가 존재하는가&lt;/li&gt;
&lt;li&gt;Empty State가 정의되어 있는가&lt;/li&gt;
&lt;li&gt;Error State가 존재하는가&lt;/li&gt;
&lt;li&gt;Keyboard Navigation이 가능한가&lt;/li&gt;
&lt;li&gt;문구 Tone &amp;amp; Manner가 규칙을 따르는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 디자인 시스템은 단순한 Figma 파일이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI와 사람이 모두 읽을 수 있는 제품 품질 규칙&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인도 개발 이전 단계의 작업물이 아니라 배포를 결정하는 Quality Gate의 일부가 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가장 위험한 문제, &amp;lsquo;이해 부채&amp;rsquo;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발이 빠르게 확산되면서 새로운 형태의 기술 부채도 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;이해 부채(Understanding Debt)&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 작성하는 속도가 사람이 코드를 이해하는 속도보다 빨라지면 이런 상황이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 계속 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트도 통과한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스도 정상적으로 돌아간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 어느 순간 문제가 발생했을 때 아무도 해당 코드가 왜 그렇게 만들어졌는지 설명하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드량과 사람이 실제로 이해하고 있는 코드 사이의 간격이 계속 커지는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 많은 코드를 만들어낼수록 이 문제는 더 빠르게 커질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 모든 개발을 무조건 자동화하는 것이 좋은 전략이라고 보기는 어렵다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;같은 AI가 코드와 테스트를 만들 때 생기는 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발에서 특히 조심해야 할 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Agent가 기능을 구현하고 그 기능의 테스트까지 작성하는 경우다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 요구사항을 잘못 이해했다고 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 잘못 이해한 요구사항을 기준으로 코드를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 같은 이해를 바탕으로 테스트도 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 이런 상황이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드는 잘못됐지만 테스트는 통과한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초록색 체크가 반드시 올바른 소프트웨어를 의미하지 않는 이유다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 중요한 기능에서는 구현과 검증을 분리하거나, 원래 Specification을 기준으로 독립적인 검증을 수행하는 구조가 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모든 것을 자동화할 필요는 없다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 팩토리에서 중요한 것은 &amp;lsquo;사람을 없애는 것&amp;rsquo;이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 작업에 사람이 필요한지 결정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화하기 쉬운 작업이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 영향 범위가 작고 실패 여부를 기계적으로 판단할 수 있는 변경이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 사람이 반드시 판단해야 할 작업도 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증&lt;/li&gt;
&lt;li&gt;결제&lt;/li&gt;
&lt;li&gt;개인정보&lt;/li&gt;
&lt;li&gt;권한&lt;/li&gt;
&lt;li&gt;데이터베이스 Migration&lt;/li&gt;
&lt;li&gt;Public API&lt;/li&gt;
&lt;li&gt;핵심 Architecture&lt;/li&gt;
&lt;li&gt;중요한 보안 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 영역은 잘못됐을 때 영향이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 AI가 구현하더라도 사람이 최종 판단을 하는 것이 합리적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표는 &amp;lsquo;Human 0%&amp;rsquo;가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기계가 검증할 수 있는 것은 자동화하고, 판단이 필요한 곳에는 인간을 남기는 것.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경계를 설계하는 것이 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 시대의 진짜 병목은 코드 생성이 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델과 컴퓨팅 자원을 늘리면 코드 생성량은 계속 늘릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사람의 Attention은 무한하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QA 인력도 무한하지 않고, Security Review나 Architecture Review에 사용할 수 있는 시간 역시 제한되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 AI 개발에서 가장 희소한 자원은 코드가 아니라 &lt;b&gt;검증 능력&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 앞으로 개발 조직이 고민해야 할 질문도 바뀔 가능성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;어떤 AI가 코드를 가장 잘 쓰는가?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;우리는 AI가 만든 결과를 얼마나 빠르고 정확하게 검증할 수 있는가?&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 더 중요한 질문이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발자의 역할도 달라질 수 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 더 많이 작성하게 된다고 해서 개발자가 필요 없어지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 역할의 중심이 이동할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 개발자의 핵심 업무가 직접 코드를 작성하는 것이었다면 앞으로는 다음과 같은 일이 더 중요해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 만들어야 하는지 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Acceptance Criteria를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 사용할 Context와 Tool을 설계한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화할 수 있는 검증 기준을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 Architecture를 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 최종 품질을 책임진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 개발자의 역할이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Coder&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Software Factory Designer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 확장되는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI를 잘 쓰는 회사보다 &amp;lsquo;AI가 일하는 시스템&amp;rsquo;을 잘 만드는 회사&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구는 빠르게 평준화될 가능성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 모델은 누구나 사용할 수 있고, 좋은 코딩 Agent 역시 많은 조직이 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 차이는 어디에서 만들어질까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 조직 내부에 쌓인 다음과 같은 것들이 경쟁력이 될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좋은 Specification을 만드는 방법&lt;/li&gt;
&lt;li&gt;좋은 Prompt와 Context&lt;/li&gt;
&lt;li&gt;개발 규칙&lt;/li&gt;
&lt;li&gt;Design System&lt;/li&gt;
&lt;li&gt;테스트 체계&lt;/li&gt;
&lt;li&gt;Quality Gate&lt;/li&gt;
&lt;li&gt;Security Policy&lt;/li&gt;
&lt;li&gt;Agent Workflow&lt;/li&gt;
&lt;li&gt;실패 사례와 Feedback Loop&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것들은 단순히 AI 모델을 바꾸는 것으로 얻을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직이 시행착오를 반복하면서 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이런 구조가 갖춰진 조직에서는 AI Agent가 하나 늘어날 때 단순히 개발자 한 명이 늘어나는 것이 아니라 &lt;b&gt;생산라인 하나가 추가되는 효과&lt;/b&gt;가 나타날 수도 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;앞으로 중요한 질문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발의 초반 경쟁은 &amp;lsquo;누가 AI로 더 빨리 코드를 쓰는가&amp;rsquo;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그 단계는 빠르게 지나가고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 더 중요한 질문은 이것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 하루에 수백 개의 변경을 만들어낸다면 우리는 그 결과를 어떻게 믿을 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 팩토리는 그 질문에 대한 하나의 답이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 더 많은 코드를 작성시키는 것이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;좋은 코드만 살아남는 개발 공정을 만드는 것.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시대의 소프트웨어 경쟁력은 결국 코드 생성량이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;얼마나 안정적으로 검증하고, 얼마나 반복적으로 좋은 제품을 출하할 수 있는 시스템을 가지고 있는가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 결정될 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쩌면 앞으로 가장 중요한 개발 능력은 코드를 잘 작성하는 능력만이 아닐지도 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI가 좋은 소프트웨어를 계속 만들어낼 수 있도록 &amp;lsquo;공장&amp;rsquo;을 설계하는 능력.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것이 소프트웨어 팩토리가 다시 주목받는 이유다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1807</guid>
      <comments>https://javaexpert.tistory.com/1807#entry1807comment</comments>
      <pubDate>Fri, 14 Aug 2026 17:59:46 +0900</pubDate>
    </item>
    <item>
      <title>Appwrite 뜯어보기: 오픈소스 BaaS는 내부에서 어떻게 동작할까?</title>
      <link>https://javaexpert.tistory.com/1806</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;웹이나 모바일 앱 하나를 만들 때 실제로 필요한 백엔드 기능은 생각보다 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입과 로그인, 데이터베이스, 파일 업로드, 권한 관리, 서버 함수, 실시간 통신, 이메일과 푸시 알림, 웹훅, 배포 환경까지 준비해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개별 서비스를 조합해 직접 만들 수도 있지만, Appwrite는 이 영역을 하나의 개발 플랫폼으로 묶으려는 오픈소스 프로젝트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite는 공식 README에서 자신을 &lt;b&gt;웹&amp;middot;모바일&amp;middot;AI 애플리케이션을 위한 오픈소스 개발 플랫폼&lt;/b&gt;으로 설명한다. Auth, Databases, Storage, Functions, Messaging, Sites, Realtime 등을 하나의 플랫폼에서 제공하며, Appwrite Cloud를 이용하거나 직접 서버에 Self-hosting할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Appwrite에서 더 흥미로운 부분은 기능 목록이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 많은 기능을 하나의 백엔드 플랫폼 안에서 어떻게 구조화했는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 코드를 살펴보면 Appwrite는 꽤 흥미로운 답을 보여준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Appwrite는 단순한 API 서버가 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite를 가장 단순하게 보면 다음과 같은 구조다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Web / Mobile / Server Application
                │
                ▼
             Appwrite
                │
        ┌───────┼────────┐
        │       │        │
       Auth  Database  Storage
        │       │        │
        ├──── Functions ─┤
        │                │
     Realtime         Messaging
        │                │
        └───── Sites ────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 이런 기능을 REST API나 SDK를 통해 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 README 기준 주요 제품은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Auth &amp;mdash; 로그인, 세션, OAuth, MFA, 사용자 인증&lt;/li&gt;
&lt;li&gt;Databases &amp;mdash; 데이터 저장, Query, Index, Relationship&lt;/li&gt;
&lt;li&gt;Storage &amp;mdash; 파일 업로드&amp;middot;다운로드와 이미지 처리&lt;/li&gt;
&lt;li&gt;Functions &amp;mdash; 이벤트 또는 스케줄 기반 서버 코드 실행&lt;/li&gt;
&lt;li&gt;Messaging &amp;mdash; Email, SMS, Push&lt;/li&gt;
&lt;li&gt;Sites &amp;mdash; 웹 애플리케이션 호스팅&lt;/li&gt;
&lt;li&gt;Realtime &amp;mdash; 실시간 데이터 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite는 REST뿐 아니라 WebSocket과 GraphQL도 지원한다고 아키텍처 문서에서 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Appwrite를 단순히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Firebase를 오픈소스로 다시 만든 프로젝트&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정도로 이해하면 내부 구조에서 배울 수 있는 많은 부분을 놓치게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite는 오히려 &lt;b&gt;여러 백엔드 기능을 하나의 플랫폼으로 구성하는 방법을 보여주는 대규모 백엔드 시스템 사례&lt;/b&gt;로 보는 편이 더 흥미롭다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 핵심은 '모놀리스 + 마이크로서비스' 혼합 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite의 아키텍처에서 가장 먼저 눈에 들어오는 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 CONTRIBUTING 문서에는 현재 Appwrite 구조가 &lt;b&gt;Monolithic Architecture와 Microservice Architecture의 조합&lt;/b&gt;이라고 명시되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 API는 하나의 애플리케이션으로 구성한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;              ┌─────────────────────┐
Client ─────▶ │    Appwrite API     │
              │                     │
              │ Auth                │
              │ Database            │
              │ Storage             │
              │ Functions           │
              │ Teams               │
              │ Users               │
              │ Sites               │
              └─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 시간이 많이 걸리거나 독립적으로 처리할 수 있는 작업은 Worker로 분리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Docker Compose에는 다음과 같은 Worker들이 별도 컨테이너로 존재한다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;appwrite-worker-webhooks
appwrite-worker-deletes
appwrite-worker-databases
appwrite-worker-builds
appwrite-worker-jobs
appwrite-worker-screenshots
appwrite-worker-certificates
appwrite-worker-functions
appwrite-worker-mails
appwrite-worker-notifications
appwrite-worker-messaging
appwrite-worker-migrations
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 전체 구조를 단순화하면 다음에 가깝다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;                         ┌──────────────┐
Internet ───────────────▶│   Traefik    │
                         └──────┬───────┘
                                │
                    ┌───────────┴──────────┐
                    ▼                      ▼
             Appwrite API           Appwrite Realtime
                    │
                    ▼
             Application Logic
                    │
          ┌─────────┼─────────┐
          ▼         ▼         ▼
      Database    Redis      Storage
                    │
                    ▼
                 Queue
                    │
       ┌────────────┼──────────────┐
       ▼            ▼              ▼
   Functions     Webhooks       Messaging
     Worker        Worker          Worker
       │
       ▼
 OpenRuntimes
 Executor
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;README도 API Layer에서는 캐시를 활용하고, 무거운 작업은 Background Worker로 넘긴다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Appwrite의 설계 방향을 명확하게 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모든 기능을 처음부터 마이크로서비스로 분리하지 않았다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CONTRIBUTING 문서에 따르면 핵심 API를 모놀리스로 유지한 이유 중 하나는 작은 팀이 더 빠르게 개발하기 위해서였다. 대신 Worker와 내부 서비스는 별도 컨테이너로 나누어 독립적으로 확장할 수 있도록 구성했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. PHP인데 일반적인 PHP 서버와 조금 다르다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite 서버의 중심 언어는 PHP다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 일반적인&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Nginx
  &amp;darr;
PHP-FPM
  &amp;darr;
Laravel
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태와는 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 main의 composer.json은 PHP &amp;gt;= 8.5.0과 Swoole 6 확장을 요구하고 있다. 또한 상당수의 핵심 구성요소가 utopia-php 패키지로 나뉘어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 의존성을 보면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;PHP &amp;gt;= 8.5
Swoole 6
Utopia Platform
Utopia Database
Utopia Queue
Utopia Cache
Utopia Storage
Utopia Messaging
Utopia WebSocket
GraphQL PHP
Redis Extension
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite 내부 개발 가이드 역시 Swoole 기반 비동기 런타임과 Utopia PHP Framework를 핵심 Stack으로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 컨테이너의 기본 실행 명령도 흥미롭다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;CMD [ &quot;php&quot;, &quot;app/http.php&quot; ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도 PHP-FPM이 아니라 Appwrite 애플리케이션 자체가 HTTP 서버 프로세스로 동작하는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 거대한 Controller 대신 'Module + Action'&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 백엔드에서는 기능이 증가하면서 Controller가 비대해지는 문제가 자주 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite 역시 과거에는&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;app/controllers/api/[service].php
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태의 큰 Controller 파일을 사용했지만, 유지보수 문제가 생기면서 신규 개발을 HTTP Module 구조로 이전하고 있다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;src/Appwrite/Platform/Modules/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 아래에 기능별 Module이 존재한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Account
Avatars
Databases
Functions
Health
Notifications
Projects
Sites
Storage
Teams
Tokens
Users
VCS
Webhooks
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Appwrite Platform 클래스가 이 모듈들을 하나씩 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Module 내부는 대략 다음 패턴을 따른다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Modules/
  Teams/
    Module.php

    Services/
      Http.php
      Workers.php
      Tasks.php

    Http/
      Teams/
        Create.php
        Get.php
        Update.php
        Delete.php
        XList.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 개발 가이드에서는 HTTP Endpoint의 경로까지 파일 구조에 반영하도록 규칙을 정의하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;POST /v1/teams
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API는&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;Modules/Teams/Http/Teams/Create.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 구현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 코드를 보면 하나의 Action에서 HTTP Method, Path, Scope, Event, Audit, SDK Metadata, Parameter, Dependency Injection까지 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로 단순화하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;class Create extends Action
{
    public function __construct()
    {
        $this
            -&amp;gt;setHttpMethod('POST')
            -&amp;gt;setHttpPath('/v1/teams')
            -&amp;gt;label('event', 'teams.[teamId].create')
            -&amp;gt;label('scope', 'teams.write')
            -&amp;gt;inject('dbForProject')
            -&amp;gt;inject('queueForEvents')
            -&amp;gt;callback($this-&amp;gt;action(...));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 눈여겨볼 점은 하나의 Endpoint 정의가 단순 Controller 함수가 아니라는 것이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;HTTP Route
+
Validation
+
Authorization Scope
+
Audit
+
Event
+
SDK Metadata
+
Dependency Injection
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모두가 하나의 Action Contract에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 구현과 SDK 생성 규칙을 최대한 동일한 구조에서 관리하려는 방식이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 무거운 작업은 Queue 뒤로 넘긴다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite 내부에서 중요한 구성요소 중 하나가 Event Queue다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Event.php에는 다양한 Queue 이름이 정의되어 있다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;v1-database
v1-deletes
v1-audits
v1-mails
v1-notifications
v1-functions
v1-webhooks
v1-certificates
v1-builds
v1-jobs
v1-screenshots
v1-messaging
v1-executions
v1-migrations
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Event가 발생하면 Publisher를 이용해 Queue에 Payload를 넣는다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;return $this-&amp;gt;publisher-&amp;gt;enqueue(
    $queue,
    $payload
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Event 구현에서도 Queue 생성 &amp;rarr; Payload 구성 &amp;rarr; Publisher enqueue 과정이 확인된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 구조적으로 보면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;API Request
    │
    ▼
Business Logic
    │
    ├────────────▶ Database
    │
    ▼
Generate Event
    │
    ▼
Queue
    │
    ├────▶ Webhook Worker
    ├────▶ Function Worker
    ├────▶ Messaging Worker
    ├────▶ Build Worker
    └────▶ Delete Worker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 Team 생성 API에서도 데이터베이스 작업 후 queueForEvents에 이벤트 관련 Parameter를 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 패턴은 대규모 백엔드에서 매우 자주 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 기다릴 필요가 없는 작업을 동기 HTTP Request에서 분리하면 API와 Background Processing을 별도로 운영할 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. Redis는 단순 캐시 이상으로 중요하다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite Compose에는 Redis가 별도 서비스로 포함되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 설정에서는 Redis 7.4 Alpine 이미지를 사용하며 최대 메모리와 LRU 정책도 설정되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite 개발 가이드에서는 Redis의 역할을 다음처럼 정리한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Cache
Queue
Pub/Sub
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 전체 Appwrite 구조를 이해하려면 Redis를 단순한 캐시 서버라고 생각하기보다는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서비스와 Worker 사이를 연결하는 인메모리 인프라&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 관점에서 보는 것이 더 적절하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 데이터베이스도 하나만 사용하는 구조가 아니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 8월 현재 main의 개발 환경 설정은 특히 흥미롭다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.env에는 기본 Platform Database와 DocumentsDB, VectorsDB가 서로 다른 Adapter를 사용할 수 있도록 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 기본 설정은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Main DB
PostgreSQL

DocumentsDB
MongoDB

VectorsDB
PostgreSQL
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Redis는 별도로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Compose에도 MariaDB, MongoDB, PostgreSQL이 각각 정의되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 현재 main에는&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;DocumentsDB
TablesDB
VectorsDB
Embeddings
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 데이터베이스 기능이 세분화되고 있다. VectorsDB에는 Collection과 Transaction을 포함한 별도 HTTP API 구조도 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에는 별도의 Embedding 서비스도 포함되어 있다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;appwrite-embedding:
  image: appwrite/embedding:0.1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 Embedding Model 설정에는 nomic-embed-text가 지정되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 현재 개발 브랜치에서는 기존 BaaS 기능뿐 아니라 &lt;b&gt;Vector Database와 Embedding을 위한 AI 애플리케이션 인프라까지 코드베이스에 포함되고 있는 것&lt;/b&gt;을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, main 브랜치의 기능은 최신 정식 릴리스와 반드시 동일하다고 볼 수 없으므로 버전별 기능 확인은 별도로 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. Serverless Function은 OpenRuntimes가 담당한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite Functions도 Appwrite API 프로세스 안에서 직접 실행하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Compose에는 별도의&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;openruntimes-executor
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 main에서는 openruntimes/executor:0.25.4 이미지를 사용하며 Functions와 Sites의 Runtime을 실행할 수 있도록 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Build&amp;middot;Job 처리를 위한&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;orchestrator-jobs
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스도 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 구조를 단순화하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Developer
    │
    ▼
Upload Function
    │
    ▼
Appwrite API
    │
    ▼
Build Worker
    │
    ▼
Jobs / Build Infrastructure
    │
    ▼
OpenRuntimes Executor
    │
    ▼
Isolated Runtime
    │
    ├─ Node
    ├─ PHP
    ├─ Python
    └─ ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;README에서는 현재 Functions 제품이 여러 Runtime을 지원한다고 안내하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite가 단순 API 서버를 넘어 플랫폼으로 복잡해지는 이유 중 하나가 바로 이 Compute Layer다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Realtime도 API 서버와 분리되어 있다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Realtime 역시 별도의 컨테이너다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;appwrite-realtime:
  entrypoint: realtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Traefik은&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;/v1/realtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청을 Appwrite Realtime 서비스로 라우팅한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 HTTP API와 WebSocket 연결을 같은 프로세스에 모두 몰아넣는 구조가 아니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;               Traefik
                  │
         ┌────────┴─────────┐
         │                  │
     REST / GraphQL      WebSocket
         │                  │
   Appwrite API      Appwrite Realtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Realtime 연결과 일반 API 요청의 부하 특성이 다르다는 점을 고려한 서비스 분리가 소스 구조에서도 확인된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 앞단에는 Traefik이 있다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Self-hosted Compose 환경에서 외부 트래픽의 진입점은 Traefik이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Compose에서는 Traefik 3.6이 사용되고 있으며 HTTP 80과 HTTPS 443 Entry Point를 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Traefik이 요청을 구분해&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;API
Console
Realtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등으로 라우팅한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 구조를 한 번 더 정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;                    Internet
                       │
                       ▼
                   Traefik
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
      Appwrite      Realtime      Console
        API
          │
          ▼
       Modules
          │
     ┌────┴────┐
     ▼         ▼
 Database     Redis
               │
               ▼
             Queue
               │
      ┌────────┼─────────┐
      ▼        ▼         ▼
 Functions  Webhooks  Messaging
 Worker      Worker     Worker
      │
      ▼
 OpenRuntimes
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite를 이해하는 데 가장 중요한 그림도 사실 이것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. Self-hosting이 가능한 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite는 Docker 기반 배포를 핵심 설치 방식으로 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;README에서는 Docker Compose뿐 아니라 Kubernetes, Docker Swarm, Rancher 같은 Container Orchestration 환경에서도 운영할 수 있다고 안내한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile에는 Storage 관련 디렉터리도 명시적으로 구성되어 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/storage/uploads
/storage/imports
/storage/cache
/storage/config
/storage/certificates
/storage/functions
/storage/debug
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에서는 추가로 Functions, Sites, Builds 등을 Volume으로 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Self-hosted Appwrite를 운영한다면 Appwrite API 컨테이너 하나만 관리한다고 생각하기보다는&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Reverse Proxy
Database
Redis
Realtime
Workers
Executor
Build infrastructure
Persistent Storage
Certificate
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 포함한 &lt;b&gt;하나의 백엔드 플랫폼 Stack&lt;/b&gt;을 운영한다고 이해하는 편이 정확하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 오픈소스 라이선스는 BSD 3-Clause&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite Server Repository는 BSD 3-Clause License를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이선스 전문에는 소스 및 바이너리 형태의 재배포와 수정 사용을 허용하면서 Copyright Notice와 Disclaimer 유지 등의 조건을 명시하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업에서 직접 Self-hosting하거나 소스를 분석하려는 경우에도 비교적 접근하기 쉬운 오픈소스 라이선스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 상용 제품에 수정&amp;middot;재배포하려는 경우에는 Appwrite Server뿐 아니라 함께 사용하는 별도 이미지와 외부 프로젝트들의 라이선스도 각각 확인할 필요가 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. Appwrite 코드에서 배울 수 있는 다섯 가지&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite를 직접 사용할 계획이 없더라도 백엔드 개발자 입장에서 참고할 만한 설계 요소가 많다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① 처음부터 모든 것을 Microservice로 만들 필요는 없다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite는 핵심 API를 Monolith로 유지하면서 Worker를 독립 서비스로 분리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Core Business Logic
        │
        ▼
     Monolith

Heavy / Async Job
        │
        ▼
   Independent Worker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 현실적인 분리 방법을 보여준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② Side Effect는 Queue 뒤로 분리할 수 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메일, Function 실행, Webhook, Messaging, Delete 같은 작업은 별도 Queue와 Worker로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 서버와 Background Processing의 책임을 분리하는 실제 대규모 사례로 살펴볼 만하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ 기능 단위 Module 구조가 중요하다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite는 Account, Databases, Functions, Storage, Sites, Teams처럼 도메인별 Module을 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 커질수록 단순한&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;controllers/
services/
repositories/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조보다&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Modules/
  Teams/
  Storage/
  Functions/
  Databases/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 기능 자체를 상위 경계로 두는 방식이 코드 탐색에 유리한 경우가 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;④ API 규칙을 코드 구조까지 연결한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite의 신규 HTTP Action 파일은 기본적으로&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Create
Get
Update
Delete
XList
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태로 제한하는 규칙을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL, Directory, Action Class, SDK Operation이 최대한 동일한 Resource Model을 표현하도록 만드는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 API를 오랫동안 유지할 때 Naming Convention과 Directory Convention 자체가 Architecture의 일부가 될 수 있다는 좋은 사례다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⑤ BaaS도 결국 분산 시스템이다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 Appwrite에서 간단하게&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;account.create(...)
databases.create(...)
storage.createFile(...)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 API를 호출하지만 그 뒤에는&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Reverse Proxy
API
Authorization
Database
Cache
Queue
Worker
Runtime
Storage
Realtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 연결되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BaaS 제품이 사용자에게 복잡성을 감춰준다고 해서 시스템 자체가 단순한 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite Repository는 그 복잡성을 실제 코드 수준에서 관찰할 수 있다는 점에서 공부할 가치가 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. 그렇다면 Appwrite는 어떤 프로젝트에 어울릴까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 구성상 Appwrite는 다음과 같은 요구사항을 가진 애플리케이션에서 검토할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;사용자 인증이 필요하다
+
Database가 필요하다
+
파일 Storage가 필요하다
+
Realtime 기능이 필요하다
+
Backend Function이 필요하다
+
Messaging이 필요하다
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Cloud 서비스뿐 아니라 Self-hosted 선택지도 필요하다면 Appwrite의 구조를 살펴볼 이유가 충분하다. Cloud와 Self-hosted 두 방식을 모두 제공한다는 점은 공식 README에서도 명시되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Self-hosted를 선택한다면 Docker Compose에서 확인할 수 있듯 Appwrite API뿐 아니라 Redis, 데이터베이스, Worker, Executor 등 여러 인프라 컴포넌트를 함께 운영하게 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. Appwrite Repository를 공부한다면 이 순서가 좋다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 수많은 소스 파일을 읽는 것은 비효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 순서가 구조를 이해하기 쉽다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. README.md
        &amp;darr;
2. docker-compose.yml
        &amp;darr;
3. composer.json
        &amp;darr;
4. src/Appwrite/Platform/Appwrite.php
        &amp;darr;
5. src/Appwrite/Platform/Modules
        &amp;darr;
6. 특정 HTTP Action 하나
        &amp;darr;
7. Event / Queue
        &amp;darr;
8. Worker
        &amp;darr;
9. OpenRuntimes Executor
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Team 생성 하나만 따라가도 많은 구조를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;POST /v1/teams
       │
       ▼
Teams/Create.php
       │
       ├── Validation
       ├── Authorization
       ├── Database
       ├── Audit Metadata
       └── Event
                │
                ▼
              Queue
                │
                ▼
              Worker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식으로 하나의 요청을 끝까지 추적하면 거대한 Repository를 훨씬 빠르게 이해할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;마무리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite를 겉에서 보면 Auth, Database, Storage를 제공하는 편리한 Backend-as-a-Service다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 내부를 살펴보면 훨씬 더 흥미롭다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PHP + Swoole
        │
        ▼
Modular Monolith API
        │
        ▼
Redis / Queue
        │
        ▼
Independent Workers
        │
        ├── Messaging
        ├── Webhook
        ├── Functions
        ├── Build
        └── Migration
        │
        ▼
OpenRuntimes
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 모든 구성요소를 Docker 환경에서 하나의 개발 플랫폼으로 묶는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Appwrite에서 가장 인상적인 부분은 &quot;기능이 많다&quot;는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복잡한 백엔드 플랫폼을 하나의 거대한 마이크로서비스 집합으로 시작하지 않고, 핵심 API의 응집도는 유지하면서 비동기 작업과 실행 인프라를 점진적으로 분리했다는 점&lt;/b&gt;이다. 이 설계 배경은 Appwrite의 공식 아키텍처 설명과 현재 컨테이너 구조에서 직접 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 백엔드, BaaS, Event-driven Architecture, Worker Architecture, Serverless Runtime에 관심이 있다면 Appwrite는 단순히 사용해 볼 오픈소스를 넘어 &lt;b&gt;실제 프로덕션급 백엔드 플랫폼의 구조를 공부할 수 있는 Repository&lt;/b&gt;로 살펴볼 만하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;분석 정보&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Repository: appwrite/appwrite&lt;/li&gt;
&lt;li&gt;분석 기준: 2026-08-12&lt;/li&gt;
&lt;li&gt;분석 브랜치: main&lt;/li&gt;
&lt;li&gt;분석 시점 최신 main Commit: ac639005607c71d8a05253b38f3d7398bd298075&lt;/li&gt;
&lt;li&gt;확인 시점 최신 정식 Release: 1.9.6&lt;/li&gt;
&lt;li&gt;License: BSD 3-Clause&lt;/li&gt;
&lt;li&gt;Repository: &lt;a href=&quot;https://github.com/appwrite/appwrite&quot;&gt;https://github.com/appwrite/appwrite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 분석 파일&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;README.md
CONTRIBUTING.md
AGENTS.md
composer.json
Dockerfile
docker-compose.yml
.env

src/Appwrite/Platform/Appwrite.php
src/Appwrite/Platform/Modules/
src/Appwrite/Event/Event.php
src/Appwrite/Platform/Modules/Teams/Http/Teams/Create.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ main 브랜치는 개발이 계속 진행되는 브랜치이므로 향후 디렉터리 구조, Runtime, Database 구성 및 서비스 버전은 변경될 수 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1786505093298&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - appwrite/appwrite: Appwrite&amp;reg; - complete cloud infrastructure for your web, mobile and AI apps. Including Auth, Databas&quot; data-og-description=&quot;Appwrite&amp;reg; - complete cloud infrastructure for your web, mobile and AI apps. Including Auth, Databases, Storage, Functions, Messaging, Hosting, Realtime and more - appwrite/appwrite&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/appwrite/appwrite&quot; data-og-url=&quot;https://github.com/appwrite/appwrite&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yVo1l/dJMb8U89IPH/ZZvtjK9aKjO9pAnyorDeC0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/EQrXe/dJMb9eftRtM/M2pRqDKBFejbP6Vktvmlo0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/1UamP/dJMb9dHD4Wd/sn4yBZwSZha63A4SHEeLck/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://github.com/appwrite/appwrite&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/appwrite/appwrite&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yVo1l/dJMb8U89IPH/ZZvtjK9aKjO9pAnyorDeC0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/EQrXe/dJMb9eftRtM/M2pRqDKBFejbP6Vktvmlo0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/1UamP/dJMb9dHD4Wd/sn4yBZwSZha63A4SHEeLck/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - appwrite/appwrite: Appwrite&amp;reg; - complete cloud infrastructure for your web, mobile and AI apps. Including Auth, Databas&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Appwrite&amp;reg; - complete cloud infrastructure for your web, mobile and AI apps. Including Auth, Databases, Storage, Functions, Messaging, Hosting, Realtime and more - appwrite/appwrite&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1806</guid>
      <comments>https://javaexpert.tistory.com/1806#entry1806comment</comments>
      <pubDate>Wed, 12 Aug 2026 12:25:00 +0900</pubDate>
    </item>
    <item>
      <title>안드레 카파시가 말하는 &amp;lsquo;어떤 분야에서 전문가가 되는 법&amp;rsquo;</title>
      <link>https://javaexpert.tistory.com/1804</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uMgp9/dJMcagT4RlX/KtKFSUsoDSrmX8Yj3EGUsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uMgp9/dJMcagT4RlX/KtKFSUsoDSrmX8Yj3EGUsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uMgp9/dJMcagT4RlX/KtKFSUsoDSrmX8Yj3EGUsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuMgp9%2FdJMcagT4RlX%2FKtKFSUsoDSrmX8Yj3EGUsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;566&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 공부부터 하지 말고, &lt;b&gt;프로젝트부터 시작한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무언가를 처음부터 끝까지 체계적으로 전부 공부한 뒤 시작하려 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 &lt;b&gt;구체적인 프로젝트 하나를 정하고 직접 완성해본다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행하다가 모르는 것이 나오면 그때 필요한 내용을 배운다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Project &amp;rarr; 문제 발생 &amp;rarr; 필요한 공부 &amp;rarr; 해결 &amp;rarr; 다음 문제&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;너비 우선 학습(Breadth-first)&lt;/b&gt;보다 &lt;b&gt;깊이 우선 학습(Depth-first)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 웹 개발을 배운다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML &amp;rarr; CSS &amp;rarr; JS &amp;rarr; React &amp;rarr; DB &amp;rarr; 서버...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 모두 공부하고 프로젝트를 시작하는 것이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 만들고 싶은 서비스 시작 &amp;rarr; 로그인 필요 &amp;rarr; 인증 공부 &amp;rarr; DB 필요 &amp;rarr; DB 공부 &amp;rarr; 배포 필요 &amp;rarr; 배포 공부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 &lt;b&gt;필요할 때 배우는 On-demand Learning&lt;/b&gt;을 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 배운 것을 반드시 &lt;b&gt;자기 언어로 다시 설명한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 보고 이해했다고 끝내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배운 것을&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로 정리하고&lt;/li&gt;
&lt;li&gt;다른 사람에게 설명하고&lt;/li&gt;
&lt;li&gt;블로그에 쓰거나&lt;/li&gt;
&lt;li&gt;개발 노트로 남기고&lt;/li&gt;
&lt;li&gt;&amp;ldquo;왜 이렇게 동작하는가?&amp;rdquo;를 자기 말로 설명한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명하지 못한다면 아직 완전히 이해하지 못한 부분이 있다는 신호로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 흐름은:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배운다 &amp;rarr; 사용한다 &amp;rarr; 설명한다 &amp;rarr; 다시 이해한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 반복하면서 지식이 단순한 정보가 아니라 &lt;b&gt;자기 지식&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 다른 사람과 비교하지 말고 &lt;b&gt;과거의 나와 비교한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전문가와 자신의 현재 실력을 비교하면 끝이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교 기준을&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;저 사람보다 잘하나?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 아니라&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;3개월 전의 나보다 무엇을 더 할 수 있게 되었나?&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 하나씩 쌓이면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 1 &amp;rarr; 프로젝트 2 &amp;rarr; 프로젝트 3 &amp;rarr; &amp;hellip;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 프로젝트가 이전보다 조금 더 어려워지고, 해결할 수 있는 문제의 범위가 넓어진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결국 하나의 루프로 정리하면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;① 만들고 싶은 프로젝트를 정한다&lt;/b&gt;&lt;br /&gt;&amp;rarr; &lt;b&gt;② 일단 만든다&lt;/b&gt;&lt;br /&gt;&amp;rarr; &lt;b&gt;③ 막히는 부분을 공부한다&lt;/b&gt;&lt;br /&gt;&amp;rarr; &lt;b&gt;④ 직접 해결한다&lt;/b&gt;&lt;br /&gt;&amp;rarr; &lt;b&gt;⑤ 배운 것을 자기 언어로 정리한다&lt;/b&gt;&lt;br /&gt;&amp;rarr; &lt;b&gt;⑥ 다음에는 조금 더 어려운 프로젝트를 만든다&lt;/b&gt;&lt;br /&gt;&amp;rarr; &lt;b&gt;⑦ 이전의 나와 비교한다&lt;/b&gt;&lt;br /&gt;&amp;rarr; 반복&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;한 문장으로 요약&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전문가가 되려면 모든 것을 공부하려 하지 말고, 실제 프로젝트를 깊게 파고들며 필요한 것을 그때그때 배우고, 배운 것을 자신의 언어로 설명하면서 이전의 나보다 계속 발전하면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 개발 공부에 적용하면 &lt;b&gt;&amp;ldquo;공부 &amp;rarr; 프로젝트&amp;rdquo;가 아니라 &amp;ldquo;프로젝트 &amp;rarr; 공부 &amp;rarr; 해결 &amp;rarr; 기록 &amp;rarr; 더 어려운 프로젝트&amp;rdquo;&lt;/b&gt;의 순서라는 것이 핵심입니다.&lt;/p&gt;</description>
      <category>개발상식</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1804</guid>
      <comments>https://javaexpert.tistory.com/1804#entry1804comment</comments>
      <pubDate>Mon, 10 Aug 2026 14:02:50 +0900</pubDate>
    </item>
    <item>
      <title>핫한 prime-agent 훝어보기</title>
      <link>https://javaexpert.tistory.com/1803</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심을 한 문장으로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 코딩 에이전트가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LLM &amp;rarr; tool call &amp;rarr; 결과 &amp;rarr; LLM &amp;rarr; tool call&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방식이라면 Prime Agent는 오히려&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LLM &amp;rarr; 지속되는 Python 런타임 &amp;rarr; 코드로 도구/데이터/서브에이전트 제어 &amp;rarr; 결과 축적 &amp;rarr; 다음 행동&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라는 구조를 중심으로 설계했습니다. 모델에 기본적으로 노출되는 핵심 실행 환경도 &lt;b&gt;persistent IPython kernel&lt;/b&gt;이며, 파일 읽기&amp;middot;수정, shell 실행, skill 호출, subagent 생성까지 Python에서 프로그래밍하듯 조합하게 합니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/rlm.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;아키텍처를 단순화하면&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;                  ┌─────────────────────┐
                  │      User / TUI     │
                  └──────────┬──────────┘
                             │
                     Prime Agent Daemon
                             │
              ┌──────────────▼─────────────┐
              │       Root Agent           │
              │                            │
              │ Persistent IPython Kernel  │
              │ variables / functions      │
              │ files / shell / skills     │
              └──────┬─────────┬───────────┘
                     │         │
                rlm(...)       │ skills
                     │         │
             ┌───────▼───┐ ┌──▼──────────┐
             │ Subagent A│ │ Python Skill│
             └───────────┘ └─────────────┘
                     │
               ┌─────▼─────┐
               │Subagent A-1│
               └────────────┘

         &amp;darr; persistent state &amp;darr;

 JSONL history
 Python state
 goals
 schedules
 heartbeat
 memories
 skills
 subagent definitions
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 구현에서도 TUI와 실행 프로세스를 분리하고, daemon supervisor가 세션&amp;middot;worker&amp;middot;routing&amp;middot;복구를 관리하며, 각 worker가 root agent와 scheduler, IPython kernel, RLM children을 보유합니다. 그래서 터미널 UI를 닫아도 worker가 살아 있으면 작업이 계속될 수 있습니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/architecture.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. RLM이 가장 중요한 부분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 RLM은 단순히 &amp;ldquo;에이전트를 여러 개 띄운다&amp;rdquo;는 의미가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prime Agent에서 rlm(...)은 Python 함수처럼 취급됩니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;api = await rlm(
    &quot;API 코드를 분석하고 보안 문제를 찾아라&quot;,
    name=&quot;api-reviewer&quot;
)

tests = await rlm(
    &quot;테스트 커버리지를 분석해라&quot;,
    name=&quot;test-reviewer&quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 호출은 단순 LLM API 호출이 아니라 &lt;b&gt;독립적인 Prime Agent 세션&lt;/b&gt;을 하나 생성합니다. child에게도 자체 context, session directory, 모델, 필요 시 IPython kernel이 있으며 결과는 단순 함수 return으로 돌려주는 게 아니라 agent-to-agent messaging을 통해 전달할 수 있습니다. (&lt;a href=&quot;https://www.primeintellect.ai/blog/prime-agent&quot;&gt;Prime Intellect&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 중요한 것은 child가 일회용이 아니라는 점입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;children = await rlm.list_subagents()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 기존 child를 다시 찾은 뒤,&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;await agent_message.send(
    &quot;방금 수정한 코드까지 다시 검사해&quot;,
    receiver_role=&quot;child&quot;,
    receiver_name=&quot;api-reviewer&quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 이전에 일했던 에이전트에게 추가 작업을 줄 수 있습니다. child registry는 compaction이나 kernel restart, parent restoration 이후에도 복구하도록 설계돼 있습니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/rlm.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 꽤 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 패턴이&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;manager
 ├─ agent A &amp;rarr; 종료
 ├─ agent B &amp;rarr; 종료
 └─ agent C &amp;rarr; 종료
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라면 Prime Agent가 지향하는 것은&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;manager
 ├─ frontend specialist ──────┐
 ├─ backend specialist ───────┤ 장기간 유지
 ├─ test specialist ──────────┤
 └─ security specialist ──────┘
             &amp;uarr;
       필요할 때 재호출
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 더 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Persistent Python이 상당히 좋은 아이디어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 tool-calling에서는 이런 데이터를 매번 모델 context에 다시 넣는 경우가 많습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;50개 파일 검색
&amp;rarr; 결과 30KB
&amp;rarr; LLM에게 전달
&amp;rarr; 다시 grep
&amp;rarr; 또 LLM에게 전달
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prime Agent에서는 Python에 그대로 남겨둘 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;files = ...
results = ...
failed_tests = ...
dependency_graph = ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음 turn에서도 해당 변수와 함수들을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서상 Python state는 tool call과 context compaction을 넘어 유지됩니다. 즉 LLM의 context window를 &lt;b&gt;작업 메모리 전체로 사용하지 않고&lt;/b&gt;, 상당 부분을 외부 Python environment에 밀어놓는 것입니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/rlm.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 RLM의 핵심 철학입니다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;LLM context
=
모든 정보를 집어넣는 창고
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 아니라&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;LLM context
=
현재 판단에 필요한 정보

Python environment
=
거대한 외부 작업 메모리
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 역할을 분리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장시간 코딩 작업에서는 상당히 합리적인 설계입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. &amp;ldquo;Self-improving&amp;rdquo;의 정확한 의미&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기가 가장 오해하기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Prime Agent가 작업하면서 GPT나 Claude의 neural weights를 재학습시키는 것은 아닙니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 Continual Harness라는 별도의 상태를 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 설명에서는 이를&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;H = (&amp;rho;, G, K, M)

&amp;rho; = prompts
G = subagents
K = skills
M = memory
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태로 정의하고 있습니다. 이 상태에 대해 create/read/update/delete가 가능합니다. (&lt;a href=&quot;https://www.primeintellect.ai/blog/prime-agent&quot;&gt;Prime Intellect&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 에이전트가 여러 번 작업해보니&lt;/p&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;우리 프로젝트에서는 migration 수정 후
반드시 pnpm prisma generate를 실행해야 한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 패턴을 발견했다고 합시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 그냥 대화 중 발견한 사실입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다가 /refine이 trajectory를 분석해 이것을&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Memory
또는
Prompt Note
또는
Skill
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 승격시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 반복 작업이라면:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;migration-check/
 ├── SKILL.md
 ├── pyproject.toml
 └── src/
     └── migration_check/
         └── __init__.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Python-backed skill까지 만들 수 있습니다. Prime Agent는 instruction-only SKILL.md뿐 아니라 실제 Python package를 persistent IPython에 설치하는 형태도 공식 지원합니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/skills.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 정확하게 표현하면&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모델이 개선되는 것이 아니라 에이전트의 작업 환경과 operating procedure가 경험을 통해 개선됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 보는 게 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 오히려 이 방식이 실용적으로 중요하다고 봅니다. 모델 fine-tuning 없이도 실제 프로젝트에서 반복적으로 발생하는 노하우를 축적할 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Continual Harness가 재미있는 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 AGENTS.md, CLAUDE.md, SKILL.md 방식은 사람이 관리합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;사람
&amp;darr;
실수 발견
&amp;darr;
AGENTS.md 수정
&amp;darr;
다음 작업부터 개선
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prime Agent는 이 루프의 일부를 agent에게 넘기려 합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Agent 작업

       &amp;darr;

성공 / 실패 trajectory

       &amp;darr;

/refine

       &amp;darr;

┌──────────────┐
│ Prompt Note  │
│ Memory       │
│ Skill        │
│ Subagent Spec│
└──────────────┘

       &amp;darr;

다음 작업

       &amp;darr;

더 나은 Harness
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/refine은 현재 trajectory를 보고 가능한 한 작은 CRUD 변경을 적용하도록 설계되어 있으며, base system prompt 자체는 수정하지 않습니다. refinement history를 남기고 이전 상태로 rollback하는 구조도 있습니다. (&lt;a href=&quot;https://www.primeintellect.ai/blog/prime-agent&quot;&gt;Prime Intellect&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이것은 사실상 &lt;b&gt;&amp;ldquo;agent-level continual learning&amp;rdquo;을 weights 밖에서 구현하려는 접근법&lt;/b&gt;이라고 이해하면 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 장시간 자율 작업은 꽤 제대로 설계되어 있습니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prime Agent에는 서로 다른 역할의 기능이 따로 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능역할&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Persistent Goal&lt;/td&gt;
&lt;td&gt;끝날 때까지 목표 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Autonomous Mode&lt;/td&gt;
&lt;td&gt;다음 작업을 계속 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heartbeat&lt;/td&gt;
&lt;td&gt;일정 간격으로 agent 깨우기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schedule&lt;/td&gt;
&lt;td&gt;특정 시점/cron 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subagent&lt;/td&gt;
&lt;td&gt;병렬 전문 작업&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compaction&lt;/td&gt;
&lt;td&gt;오래된 context 압축&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSONL persistence&lt;/td&gt;
&lt;td&gt;세션 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daemon&lt;/td&gt;
&lt;td&gt;UI와 실행 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 heartbeat는 사용자용 /heartbeat와 agent가 직접 만드는 rlm_heartbeat가 분리되어 있습니다. prime-agent schedule은 one-time 또는 cron prompt를 별도로 예약할 수 있습니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/long-running-agents.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이런 구조가 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;Goal:
&quot;서비스 전체 성능을 개선하고 테스트까지 통과시켜라.&quot;

09:00
main agent 시작

09:05
├── backend-agent
├── frontend-agent
├── db-agent
└── benchmark-agent

09:30 heartbeat
&quot;benchmark 결과 확인&quot;

10:00
benchmark-agent &amp;rarr; main
&quot;API P95 820ms&quot;

main
&amp;rarr; backend-agent
&quot;DB query 최적화&quot;

11:00
test gate 실행

FAILED

main
&amp;rarr; 수정

12:00
test gate

PASSED

goal.complete()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Autonomous mode에는 max turns, token budget, wall-clock timeout과 quality gate를 설정할 수 있고, gate가 실패하면 그 출력을 agent에게 다시 넘겨 후속 작업을 수행하게 할 수 있습니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/long-running-agents.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 단순 cron 기반 AI script와 큰 차이입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. Codex / Claude Code와 가장 다른 부분&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 보기에는 차이를 이렇게 잡는 것이 가장 정확합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex / Claude Code 계열Prime Agent&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;중심 abstraction&lt;/td&gt;
&lt;td&gt;Agent + tools&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Agent + persistent REPL&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tool 사용&lt;/td&gt;
&lt;td&gt;개별 tool calling&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Python program&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;subagent&lt;/td&gt;
&lt;td&gt;task delegation&lt;/td&gt;
&lt;td&gt;&lt;b&gt;persistent agent process&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;context&lt;/td&gt;
&lt;td&gt;모델 context 중심&lt;/td&gt;
&lt;td&gt;&lt;b&gt;context + Python state&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장기 작업&lt;/td&gt;
&lt;td&gt;continuation 중심&lt;/td&gt;
&lt;td&gt;&lt;b&gt;daemon + goal + schedule + heartbeat&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;학습&lt;/td&gt;
&lt;td&gt;rules/memory&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Continual Harness CRUD&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;skill&lt;/td&gt;
&lt;td&gt;instructions/scripts&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Python package 가능&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A2A&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;&lt;b&gt;native messaging&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 그래서 Prime Agent의 진짜 경쟁 포인트는 모델 자체의 코딩 능력보다 &lt;b&gt;&amp;ldquo;에이전트를 운영하는 OS/runtime&amp;rdquo;&lt;/b&gt;에 있습니다. 이 해석은 위 아키텍처와 공식 문서에 나타난 설계 패턴을 기반으로 한 것입니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/architecture.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 의외로 모델 선택도 자유롭습니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 공식 provider 문서에는 OpenAI, Anthropic, Gemini, DeepSeek, Groq, Cerebras, OpenRouter, Fireworks, MiniMax, Kimi, Xiaomi MiMo 등 상당히 많은 provider가 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 OAuth 방식으로 &lt;b&gt;ChatGPT Plus/Pro(Codex), Claude Pro/Max, GitHub Copilot&lt;/b&gt;도 지원한다고 문서화돼 있습니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/providers.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;Prime Agent
       │
       ├─ GPT
       ├─ Claude
       ├─ Gemini
       ├─ GLM
       ├─ MiniMax
       ├─ Kimi
       └─ OpenRouter models
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 harness 실험이 가능합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 주의할 부분도 큽니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 문제는 &lt;b&gt;보안&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prime Agent 공식 문서도 IPython kernel과 worker가 security sandbox가 아니라고 명시하고 있습니다. 모델이 생성한 Python과 shell command가 기본적으로 사용자 OS 권한으로 실행됩니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서:&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;Prime Agent
&amp;darr;
persistent Python
&amp;darr;
shell
&amp;darr;
filesystem
&amp;darr;
skills
&amp;darr;
subagents
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 agent의 힘이 커질수록 prompt injection이나 악성 repository, 악성 skill의 피해 범위도 커질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 장시간 autonomous mode를 쓸 거라면 &lt;b&gt;Docker/VM/devcontainer 같은 별도 sandbox 안에서 돌리는 방식&lt;/b&gt;이 더 적합하다고 판단합니다. [Inference] 이는 공식 문서가 untrusted repository/instruction에 external sandbox 또는 restricted environment 사용을 권고한다는 점을 바탕으로 한 운영상 권고입니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 벤치마크는 흥미롭지만 아직 조심해서 봐야 합니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prime Intellect는 자체 발표에서 Prime Agent를 Opus 5, GPT-5.6 Sol, GLM-5.2 등과 조합해 장문맥 benchmark를 테스트했고, 여러 항목에서 native harness와 경쟁하거나 앞서는 결과를 보고했습니다. ARC-AGI-3에서도 높은 결과를 발표했습니다. (&lt;a href=&quot;https://www.primeintellect.ai/blog/prime-agent&quot;&gt;Prime Intellect&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이것은 &lt;b&gt;Prime Intellect 자체 평가 결과&lt;/b&gt;입니다. 독립적인 대규모 재현 결과와 동일하게 취급해서는 안 됩니다. 또한 회사도 현재 모델들이 Prime Agent harness 자체를 대상으로 훈련된 것은 아니라고 밝히고 있습니다. (&lt;a href=&quot;https://www.primeintellect.ai/blog/prime-agent&quot;&gt;Prime Intellect&lt;/a&gt;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 이 프로젝트에서 제가 가장 높게 보는 부분&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 개인적으로 기술적으로 가장 중요한 순서를 꼽으면 &lt;b&gt;Persistent REPL &amp;rarr; persistent subagent &amp;rarr; Continual Harness &amp;rarr; daemon/scheduler&lt;/b&gt; 순입니다. 이는 기능의 기반 의존관계를 기준으로 한 판단입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 이 조합:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Persistent Python
       +
Persistent Subagents
       +
Persistent Memory
       +
Executable Skills
       +
Heartbeat
       +
Goal
       +
Scheduler
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 만들어내는 결과는 단순히&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;AI가 코드를 잘 짠다.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 아니라&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;AI 개발팀이 프로젝트 안에 계속 상주한다.&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 가까워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 하나의 프로젝트에:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;main-agent
│
├── architect
├── backend
├── flutter
├── security
├── test
└── devops
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 장기간 유지하면서 각 agent에게 프로젝트별 memory와 skill을 축적시키는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 잘 발전하면 &lt;b&gt;Claude Code/Codex의 경쟁 제품&lt;/b&gt;보다는 오히려 &lt;b&gt;AI Agent Runtime / Agent OS / autonomous engineering runtime&lt;/b&gt;이라는 분류가 더 적합해질 가능성이 있습니다. [Inference] 이는 현재 daemon, persistent session, RLM child lifecycle, scheduling, Continual Harness 구조를 종합한 판단입니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/architecture.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주목할 가치가 상당히 큰 레포입니다.&lt;/b&gt; 특히 RLM = recursive call 정도로 보면 핵심을 놓칩니다. 진짜 포인트는 &lt;b&gt;LLM이 Python을 자기 작업용 컴퓨터처럼 사용하고, 그 안에서 다른 LLM 세션까지 함수처럼 조직하며, 수행 과정에서 얻은 노하우를 memory/skill/subagent spec으로 축적하는 것&lt;/b&gt;입니다. (&lt;a href=&quot;https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/rlm.md&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Prime Agent가 말하는 &amp;ldquo;self-improving&amp;rdquo;도 &lt;b&gt;모델 자체 self-training이 아니라 harness의 self-improvement&lt;/b&gt;라고 이해하는 게 정확합니다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1803</guid>
      <comments>https://javaexpert.tistory.com/1803#entry1803comment</comments>
      <pubDate>Mon, 10 Aug 2026 11:02:13 +0900</pubDate>
    </item>
    <item>
      <title>OpenAI Agents SDK</title>
      <link>https://javaexpert.tistory.com/1802</link>
      <description>&lt;h1&gt;챗봇 하나를 만드는 일과 인공지능(AI) 에이전트 팀을 만드는 일은 다르다.&lt;/h1&gt;
&lt;p&gt;챗봇은 질문을 받고 답을 돌려주면 끝난다. 에이전트는 다르다. 필요한 도구를 고르고, 작업 결과를 다시 읽고, 다른 전문가에게 일을 넘긴 뒤 조건을 만족할 때까지 다음 행동을 결정해야 한다. 정작 어려운 부분은 모델 호출보다 그 주변의 실행 흐름에 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/openai/openai-agents-python&quot;&gt;OpenAI Agents SDK&lt;/a&gt;는 바로 이 흐름을 파이썬 코드로 다루기 위한 오픈소스 소프트웨어 개발 키트(Software Development Kit, SDK)다. 거대한 워크플로 엔진을 먼저 배우지 않아도 &lt;code&gt;Agent&lt;/code&gt;, &lt;code&gt;Runner&lt;/code&gt;, 도구, handoff, guardrail 같은 몇 가지 개념만으로 에이전트 애플리케이션을 구성할 수 있다.&lt;/p&gt;
&lt;p&gt;이 프로젝트를 한 문장으로 정리하면 이렇다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;OpenAI Agents SDK는 AI가 답변만 생성하는 단계를 넘어, 도구를 사용하고 다른 에이전트와 협업하며 작업을 끝낼 때까지 실행하는 런타임이다.&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;모델보다 중요한 것은 실행 흐름이다&lt;/h2&gt;
&lt;p&gt;일반적인 대규모 언어 모델(Large Language Model, LLM) 애플리케이션은 입력을 모델에 보내고 결과를 받는다.&lt;/p&gt;
&lt;p&gt;에이전트 애플리케이션이라면 그 사이에 여러 단계가 들어간다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;사용자 입력
  ↓
에이전트가 다음 행동 판단
  ↓
도구 실행 또는 다른 에이전트로 handoff
  ↓
실행 결과를 다시 모델에 전달
  ↓
가드레일과 출력 형식 검증
  ↓
최종 결과 또는 다음 반복&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 반복 구조를 직접 구현하려면 도구 호출, 오류 처리, 대화 이력, 상태 저장, 스트리밍, 실행 중단과 재개까지 관리해야 한다. Agents SDK의 &lt;code&gt;Runner&lt;/code&gt;가 이 반복을 맡는다. 개발자에게 남는 일은 각 에이전트의 역할과 사용할 수 있는 도구를 선언하는 것이다.&lt;/p&gt;
&lt;p&gt;핵심은 모델에게 더 긴 프롬프트를 주는 데 있지 않다. &lt;strong&gt;모델이 어떤 범위 안에서 무엇을 할 수 있는지 코드로 정의하는 것&lt;/strong&gt;, 그것이 출발점이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;네 가지 개념으로 시작한다&lt;/h2&gt;
&lt;h3&gt;1. Agent: 역할과 권한을 가진 작업자&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Agent&lt;/code&gt;는 단순한 모델 별칭이 아니다. 역할과 실행 조건을 다음과 같이 한데 묶는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이름과 업무 지침&lt;/li&gt;
&lt;li&gt;사용할 모델&lt;/li&gt;
&lt;li&gt;호출할 수 있는 도구&lt;/li&gt;
&lt;li&gt;업무를 넘길 수 있는 다른 에이전트&lt;/li&gt;
&lt;li&gt;입력과 출력을 검사하는 가드레일&lt;/li&gt;
&lt;li&gt;구조화된 출력 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;가장 작은 에이전트부터 살펴보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from agents import Agent, Runner

agent = Agent(
    name=&amp;quot;Assistant&amp;quot;,
    instructions=&amp;quot;질문에 짧고 정확한 한국어로 답하세요.&amp;quot;,
)

result = Runner.run_sync(
    agent,
    &amp;quot;멀티 에이전트 시스템이 필요한 이유를 한 문장으로 설명해 줘.&amp;quot;,
)

print(result.final_output)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;동기 실행이 필요하지 않다면 비동기 &lt;code&gt;Runner.run()&lt;/code&gt;이나 스트리밍 실행을 선택할 수 있다. 네트워크 요청과 도구 실행이 많은 실제 서비스에서는 비동기 방식이 자연스럽다.&lt;/p&gt;
&lt;h3&gt;2. Tool: 답변을 행동으로 바꾸는 연결점&lt;/h3&gt;
&lt;p&gt;에이전트가 사내 데이터 조회, 계산, 검색, 파일 처리 같은 일을 하려면 도구가 필요하다. Agents SDK는 파이썬 함수의 타입 정보를 읽어 도구 스키마를 만들고, Pydantic 기반 검증을 적용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from agents import Agent, Runner
from agents.decorators import tool


@tool
def get_order_status(order_id: str) -&amp;gt; str:
    &amp;quot;&amp;quot;&amp;quot;주문 번호로 현재 배송 상태를 조회한다.&amp;quot;&amp;quot;&amp;quot;
    return f&amp;quot;{order_id}: 배송 준비 중&amp;quot;


agent = Agent(
    name=&amp;quot;Order Support&amp;quot;,
    instructions=&amp;quot;주문 문의를 처리하세요. 필요한 경우 주문 조회 도구를 사용하세요.&amp;quot;,
    tools=[get_order_status],
)

result = Runner.run_sync(agent, &amp;quot;ORDER-1024의 상태를 확인해 줘.&amp;quot;)
print(result.final_output)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;도구는 일반 함수에만 머물지 않는다. AI 애플리케이션과 외부 도구를 표준 방식으로 연결하는 Model Context Protocol(MCP) 서버, OpenAI 호스팅 도구, 다른 에이전트, 그리고 실험적인 로컬 Codex CLI까지 연결할 수 있다.&lt;/p&gt;
&lt;h3&gt;3. Handoff: 적합한 전문가에게 실행권 넘기기&lt;/h3&gt;
&lt;p&gt;멀티 에이전트 구성에서 중요한 것은 에이전트의 수가 아니라 &lt;strong&gt;책임의 경계&lt;/strong&gt;다.&lt;/p&gt;
&lt;p&gt;결제 문의와 기술 장애를 하나의 프롬프트에 모두 넣을 수도 있다. 하지만 업무별 정책, 도구, 출력 형식이 달라진다면 전문 에이전트를 나누는 편이 관리하기 쉽다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from agents import Agent, Runner

billing_agent = Agent(
    name=&amp;quot;Billing Specialist&amp;quot;,
    handoff_description=&amp;quot;결제, 청구서, 환불 문의를 담당합니다.&amp;quot;,
    instructions=&amp;quot;결제 정책에 따라 문의를 처리하세요.&amp;quot;,
)

technical_agent = Agent(
    name=&amp;quot;Technical Specialist&amp;quot;,
    handoff_description=&amp;quot;로그인, 오류, 성능 문제를 담당합니다.&amp;quot;,
    instructions=&amp;quot;증상을 확인하고 재현 가능한 진단 절차를 안내하세요.&amp;quot;,
)

triage_agent = Agent(
    name=&amp;quot;Triage&amp;quot;,
    instructions=&amp;quot;문의 내용을 분류해 적합한 전문가에게 넘기세요.&amp;quot;,
    handoffs=[billing_agent, technical_agent],
)

result = Runner.run_sync(
    triage_agent,
    &amp;quot;결제는 됐는데 구독 기능이 활성화되지 않았어요.&amp;quot;,
)

print(result.final_output)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Handoff가 일어나면 다음 에이전트가 실행 흐름을 이어받는다. 반대로 총괄 에이전트가 하위 에이전트의 결과를 받아 직접 최종 답변을 작성해야 한다면, 전문 에이전트를 ‘agent as tool’ 형태로 등록할 수 있다.&lt;/p&gt;
&lt;h3&gt;4. Guardrail: 모델 앞뒤에 세우는 검증선&lt;/h3&gt;
&lt;p&gt;가드레일은 모델이 스스로 지켜 주기를 기대하는 프롬프트 문장이 아니다. 애플리케이션 실행 경로에 포함되는 검사다.&lt;/p&gt;
&lt;p&gt;예를 들어 다음 조건을 코드로 분리할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;입력에 개인정보나 금지된 요청이 포함됐는가&lt;/li&gt;
&lt;li&gt;사용자의 질문이 에이전트의 업무 범위에 속하는가&lt;/li&gt;
&lt;li&gt;결과가 요구한 타입과 필드를 만족하는가&lt;/li&gt;
&lt;li&gt;도구에 전달할 인자가 정책을 위반하지 않는가&lt;/li&gt;
&lt;li&gt;특정 조건에서는 사람의 승인이 필요한가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;검증에 실패하면 실행을 중단하거나 별도 처리 경로로 보낼 수 있다. 생성형 모델의 불확실성을 없애지는 못하지만, 실패를 발견하고 통제할 위치를 만들어 준다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;실제 서비스에 필요한 주변 기능&lt;/h2&gt;
&lt;p&gt;Agents SDK의 강점은 멀티 에이전트 데모를 만드는 데서 끝나지 않는다. 운영 환경에 필요한 기능도 실행 런타임 주변에 모여 있다.&lt;/p&gt;
&lt;h3&gt;세션과 대화 상태&lt;/h3&gt;
&lt;p&gt;여러 실행 사이에서 대화 이력을 유지할 수 있다. 저장 방식도 하나로 고정되지 않는다. 기본 SQLite 구성뿐 아니라 SQLAlchemy, Redis, 암호화 세션과 사용자 정의 저장소를 선택할 수 있다.&lt;/p&gt;
&lt;p&gt;세션은 ‘AI의 기억’이라는 추상적인 표현보다 &lt;strong&gt;다음 실행에 어떤 대화 상태를 다시 공급할 것인가&lt;/strong&gt;에 가깝다. 서비스에서는 사용자별 격리, 보존 기간, 암호화와 삭제 정책을 함께 설계해야 한다.&lt;/p&gt;
&lt;h3&gt;Human-in-the-loop&lt;/h3&gt;
&lt;p&gt;환불, 배포, 파일 삭제, 외부 메시지 전송처럼 되돌리기 어려운 행동은 사람의 승인을 기다리게 만들 수 있다. 실행을 중단하고 상태를 저장한 뒤, 승인 또는 거절 결과를 받아 이어서 실행하는 방식이다.&lt;/p&gt;
&lt;p&gt;이 기능의 목적은 AI가 모든 것을 자동 처리하게 만드는 데 있지 않다. &lt;strong&gt;자동화할 부분과 사람이 책임질 부분을 분리하는 장치&lt;/strong&gt;에 가깝다.&lt;/p&gt;
&lt;h3&gt;Tracing&lt;/h3&gt;
&lt;p&gt;에이전트가 어떤 판단을 거쳐 어느 도구를 호출했고, 언제 다른 에이전트로 넘어갔는지 추적할 수 있다. 모델 호출 시간, 도구 실행, 오류와 토큰 사용량을 살펴보며 병목과 실패 지점을 찾는 데 유용하다.&lt;/p&gt;
&lt;p&gt;멀티 에이전트 시스템은 최종 답변만 봐서는 문제의 원인을 찾기 어렵다. 추적 정보가 있다면 잘못된 분류인지, 도구 오류인지, handoff 이후의 문맥 손실인지 구분할 수 있다.&lt;/p&gt;
&lt;h3&gt;Sandbox Agent&lt;/h3&gt;
&lt;p&gt;코드 저장소를 읽고 명령을 실행하거나 파일을 수정하는 작업에는 실제 작업공간이 필요하다. &lt;code&gt;SandboxAgent&lt;/code&gt;는 로컬 Unix 환경, Docker 또는 호스팅 샌드박스와 연결해 격리된 작업을 수행한다.&lt;/p&gt;
&lt;p&gt;파일 접근 범위, 네트워크 사용, 명령 실행 권한은 명확히 제한해야 한다. 일반 텍스트 에이전트에 셸 권한을 무심코 붙이지 않고 작업공간과 권한을 별도 경계로 다루는 이유가 여기에 있다.&lt;/p&gt;
&lt;h3&gt;Realtime Agent와 Voice Pipeline&lt;/h3&gt;
&lt;p&gt;저지연 음성 대화에는 WebSocket 기반 &lt;code&gt;RealtimeAgent&lt;/code&gt;를 사용할 수 있다. 음성 인식, 일반 에이전트 실행, 음성 합성을 단계별로 조합하고 싶다면 &lt;code&gt;VoicePipeline&lt;/code&gt;을 선택할 수 있다.&lt;/p&gt;
&lt;p&gt;두 기능은 비슷해 보이지만 구조가 다르다. Realtime Agent는 지속적인 실시간 세션에 가깝다. Voice Pipeline은 음성 인식(Speech-to-Text, STT) → 에이전트 → 음성 합성(Text-to-Speech, TTS) 처리 흐름에 가깝다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;OpenAI 모델만 사용할 수 있는 것은 아니다&lt;/h2&gt;
&lt;p&gt;프로젝트 README는 이 SDK를 provider-agnostic, 즉 특정 모델 제공자에만 묶이지 않는 구조로 설명한다. OpenAI Responses API(Application Programming Interface)와 Chat Completions API뿐 아니라 OpenAI 호환 엔드포인트와 외부 어댑터도 연결할 수 있다는 뜻이다.&lt;/p&gt;
&lt;p&gt;예를 들어 DeepSeek API를 기본 모델 클라이언트로 지정할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import os

from openai import AsyncOpenAI
from agents import (
    Agent,
    Runner,
    set_default_openai_client,
    set_tracing_disabled,
)

client = AsyncOpenAI(
    api_key=os.environ[&amp;quot;DEEPSEEK_API_KEY&amp;quot;],
    base_url=&amp;quot;https://api.deepseek.com&amp;quot;,
)

set_default_openai_client(
    client,
    use_for_tracing=False,
)
set_tracing_disabled(True)

agent = Agent(
    name=&amp;quot;DeepSeek Assistant&amp;quot;,
    instructions=&amp;quot;정확하고 간결한 한국어로 답하세요.&amp;quot;,
    model=&amp;quot;deepseek-v4-flash&amp;quot;,
)

result = Runner.run_sync(agent, &amp;quot;에이전트와 챗봇의 차이를 설명해 줘.&amp;quot;)
print(result.final_output)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드는 연결 형태를 보여 주는 예제이며, 이 글을 작성한 환경에서는 실제 DeepSeek API 키를 사용한 호출까지 검증하지 않았다.&lt;/p&gt;
&lt;p&gt;2026년 8월 10일 기준으로 DeepSeek의 Responses API는 &lt;code&gt;deepseek-v4-flash&lt;/code&gt;를 지원하지만 일부 OpenAI 기능과 완전히 같지는 않다. &lt;code&gt;previous_response_id&lt;/code&gt;, 서버 측 conversation 저장, 파일·이미지 입력, MCP와 일부 호스팅 도구에는 제약이 있다. 모델 제공자를 바꿀 때는 ‘요청 형식이 비슷하다’는 사실과 ‘기능이 동일하다’는 판단을 구분해야 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;설치하고 첫 실행까지&lt;/h2&gt;
&lt;p&gt;이 저장소의 현재 패키지는 Python 3.10 이상을 요구한다. 아래 절차는 macOS 또는 Linux의 표준 셸 환경을 기준으로 한다.&lt;/p&gt;
&lt;h3&gt;1. 가상환경과 패키지 준비&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install openai-agents&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;확인:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python -c &amp;quot;import agents; print(agents.__version__)&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;버전 번호가 출력되면 패키지를 불러올 수 있는 상태다.&lt;/p&gt;
&lt;p&gt;실패한다면 현재 셸에서 &lt;code&gt;.venv&lt;/code&gt;가 활성화됐는지, &lt;code&gt;python --version&lt;/code&gt;이 3.10 이상인지 먼저 확인한다.&lt;/p&gt;
&lt;h3&gt;2. API 키 설정&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export OPENAI_API_KEY=&amp;quot;발급받은_API_키&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실제 키를 소스 코드나 Git 저장소에 저장하지 않는다. 서비스 환경에서는 운영체제의 비밀 저장소나 배포 플랫폼의 secret 기능을 사용한다.&lt;/p&gt;
&lt;h3&gt;3. 예제 실행&lt;/h3&gt;
&lt;p&gt;다음 내용을 &lt;code&gt;hello_agent.py&lt;/code&gt;로 저장한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from agents import Agent, Runner

agent = Agent(
    name=&amp;quot;Assistant&amp;quot;,
    instructions=&amp;quot;친절하고 정확한 한국어로 답하세요.&amp;quot;,
)

result = Runner.run_sync(agent, &amp;quot;오늘 해야 할 일을 세 단계로 정리해 줘.&amp;quot;)
print(result.final_output)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실행:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python hello_agent.py&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;정상이라면 모델이 생성한 답변이 터미널에 출력된다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Missing credentials&lt;/code&gt; 오류가 나오면 &lt;code&gt;OPENAI_API_KEY&lt;/code&gt;가 현재 셸에 설정됐는지 확인한다. &lt;code&gt;401&lt;/code&gt; 오류가 나오면 키의 유효성과 API 프로젝트 권한을 확인한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;이 SDK가 잘 맞는 경우&lt;/h2&gt;
&lt;p&gt;다음 조건이라면 Agents SDK의 장점이 분명하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모델이 여러 도구를 선택하며 반복적으로 작업해야 한다&lt;/li&gt;
&lt;li&gt;역할이 다른 전문 에이전트에게 업무를 위임해야 한다&lt;/li&gt;
&lt;li&gt;입력, 출력과 도구 호출을 별도로 검증해야 한다&lt;/li&gt;
&lt;li&gt;대화 상태와 장기 실행을 관리해야 한다&lt;/li&gt;
&lt;li&gt;중요한 행동 전에 사람의 승인이 필요하다&lt;/li&gt;
&lt;li&gt;실행 경로를 추적하고 디버깅해야 한다&lt;/li&gt;
&lt;li&gt;실제 파일과 명령을 다루는 격리 작업공간이 필요하다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반대로 한 번의 요청으로 짧은 텍스트를 생성하고 끝나는 기능이라면 Responses API나 Chat Completions API를 직접 호출하는 편이 단순할 수 있다. 에이전트 수가 많다고 좋은 구조가 되는 것도 아니다. 책임과 권한이 분리되지 않은 에이전트는 이름만 여러 개인 하나의 복잡한 프롬프트가 되기 쉽다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;AI 팀을 만든다는 것&lt;/h2&gt;
&lt;p&gt;멀티 에이전트 시스템을 소개할 때는 종종 조직도 같은 그림이 먼저 등장한다. 총괄 에이전트 아래에 조사, 개발, 검토 에이전트를 배치한 모습은 제법 그럴듯하다.&lt;/p&gt;
&lt;p&gt;그렇다면 에이전트를 많이 배치할수록 결과도 좋아질까? 실제 품질을 결정하는 것은 숫자가 아니다.&lt;/p&gt;
&lt;p&gt;누가 어떤 정보를 볼 수 있는지, 어떤 도구를 실행할 수 있는지부터 정해야 한다. 언제 다른 에이전트에게 넘길지, 실패하면 어디에서 중단할지, 마지막 결과를 누가 책임질지도 빠질 수 없다. OpenAI Agents SDK는 이런 결정을 파이썬 코드로 옮길 수 있게 해 준다.&lt;/p&gt;
&lt;p&gt;복잡한 프레임워크 없이 시작할 수 있다는 말이 설계까지 필요 없다는 뜻은 아니다. 오히려 작은 문법으로 책임과 경계를 또렷하게 드러낼 수 있다는 뜻에 가깝다.&lt;/p&gt;
&lt;p&gt;AI에게 일을 시키는 시대를 지나 AI 팀을 운영하려 한다면, 이 프로젝트는 꽤 현실적인 출발점이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/openai/openai-agents-python&quot;&gt;OpenAI Agents SDK Python GitHub 저장소&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://openai.github.io/openai-agents-python/&quot;&gt;OpenAI Agents SDK 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pypi.org/project/openai-agents/&quot;&gt;OpenAI Agents SDK PyPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api-docs.deepseek.com/guides/responses_api/&quot;&gt;DeepSeek Responses API 호환성 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;검증 기준&lt;/h2&gt;
&lt;p&gt;이 글의 프로젝트 기능과 설치 조건은 2026년 8월 10일에 다음 로컬 소스를 기준으로 확인했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;저장소 브랜치: &lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;확인 커밋: &lt;code&gt;54cc7d93&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;패키지 버전: &lt;code&gt;openai-agents 0.19.4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;패키지 요구 조건: Python 3.10 이상&lt;/li&gt;
&lt;li&gt;로컬 확인 환경: macOS, Python 3.13.7, &lt;code&gt;uv&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;저장소 설치와 Python import는 로컬에서 검증했다. 글에 포함한 OpenAI 및 DeepSeek API 호출 예제는 실제 자격 증명을 사용해 실행하지 않았으므로 API 응답 결과는 미검증이다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1802</guid>
      <comments>https://javaexpert.tistory.com/1802#entry1802comment</comments>
      <pubDate>Mon, 10 Aug 2026 10:30:17 +0900</pubDate>
    </item>
    <item>
      <title>메인은 GPT-5.6, 서브에이전트는 DeepSeek V4 Flash로 설정하기</title>
      <link>https://javaexpert.tistory.com/1801</link>
      <description>&lt;h1&gt;GPT-5.6은 감독하고, DeepSeek V4 Flash 서브에이전트가 작업하는 Codex 구성법&lt;/h1&gt;
&lt;p&gt;이 구성에서 실제 일은 &lt;code&gt;deepseek_v4_flash&lt;/code&gt; 자식이 합니다. 자식은 요청을 받아 파일을 조사하고, 필요한 코드를 바꾸고, 관련 검증까지 수행합니다. 메인 GPT-5.6은 같은 결과물을 다시 작성하지 않고 작업 범위·변경 내역·검증 근거를 확인한 뒤 승인하거나 보완 작업만 지시합니다.&lt;/p&gt;
&lt;p&gt;이 글은 Codex의 메인을 GPT-5.6으로 유지하면서, &lt;code&gt;deepseek_v4_flash&lt;/code&gt;라는 전용 자식 worker가 DeepSeek V4 Flash로 작업을 소유하게 만드는 방법을 다룹니다. 부모는 조율자와 최종 검토자이고, 자식은 실행자입니다.&lt;/p&gt;
&lt;p&gt;핵심은 API 키를 에이전트 파일마다 복사하지 않는 것입니다. 키는 provider에 한 번만 연결하고, 서브에이전트는 그 provider를 명시적으로 선택합니다. Codex는 커스텀 provider와 커스텀 에이전트 파일을 지원합니다. &lt;a href=&quot;https://developers.openai.com/codex/config-reference&quot;&gt;Codex 설정 레퍼런스&lt;/a&gt;, &lt;a href=&quot;https://developers.openai.com/codex/subagents&quot;&gt;서브에이전트 문서&lt;/a&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;이 글의 예시는 macOS/Linux 기준입니다. API 키를 문서, Git 저장소, 스크린샷, 채팅 로그에 남기지 마세요. 이미 노출된 키라면 먼저 폐기하고 새 키를 발급받는 편이 안전합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;먼저 확인할 것&lt;/h2&gt;
&lt;p&gt;이 구성에는 다음이 필요합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;최신 Codex&lt;/li&gt;
&lt;li&gt;ChatGPT 또는 OpenAI 인증으로 동작하는 GPT-5.6 메인 모델&lt;/li&gt;
&lt;li&gt;DeepSeek API 키와 사용 가능한 잔액/권한&lt;/li&gt;
&lt;li&gt;&lt;code&gt;deepseek-v4-flash&lt;/code&gt; 모델 접근 권한&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DeepSeek의 현재 모델 ID와 기본 API URL은 공식 모델 문서에서 확인하는 편이 좋습니다. 모델 이름이나 API 형식은 바뀔 수 있으므로, 다른 블로그의 값을 그대로 복사하기보다 공식 문서를 우선합니다. &lt;a href=&quot;https://api-docs.deepseek.com/quick_start/pricing/&quot;&gt;DeepSeek Models &amp;amp; Pricing&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;1. 설정부터 백업한다&lt;/h2&gt;
&lt;p&gt;provider 설정은 사용자 전역 설정에 들어가므로, 먼저 원본을 따로 보관합니다. 아래 명령은 삭제하거나 덮어쓰지 않고 복사본만 만듭니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/.codex/backups
backup_stamp=&amp;quot;$(date +%Y%m%d-%H%M%S)&amp;quot;
backup_dir=&amp;quot;$HOME/.codex/backups/before-deepseek-$backup_stamp&amp;quot;

mkdir -p &amp;quot;$backup_dir&amp;quot;
cp -p ~/.codex/config.toml &amp;quot;$backup_dir/config.toml&amp;quot;

if [ -d ~/.codex/agents ]; then
  ditto ~/.codex/agents &amp;quot;$backup_dir/agents&amp;quot;
fi

chmod -R go-rwx &amp;quot;$backup_dir&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;config.toml&lt;/code&gt;에 토큰이 직접 들어 있을 가능성이 있다면 백업도 민감 정보입니다. 클라우드 드라이브나 Git에 올리지 말고, 소유자만 읽을 수 있도록 둡니다.&lt;/p&gt;
&lt;h2&gt;2. API 키는 provider에 한 번만 연결한다&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;~/.codex/config.toml&lt;/code&gt;의 기존 메인 모델 설정은 유지합니다. 그 아래에 DeepSeek provider만 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;# 메인 모델은 기존 GPT-5.6/OpenAI 설정을 그대로 둡니다.
model = &amp;quot;gpt-5.6-sol&amp;quot;
model_provider = &amp;quot;openai&amp;quot;

[model_providers.deepseek]
name = &amp;quot;DeepSeek&amp;quot;
base_url = &amp;quot;https://api.deepseek.com&amp;quot;
wire_api = &amp;quot;responses&amp;quot;
env_key = &amp;quot;DEEPSEEK_API_KEY&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;env_key&lt;/code&gt;는 키 자체가 아니라 환경 변수의 이름입니다. Codex의 provider 설정은 환경 변수로 API 키를 받도록 지원하며, 직접 토큰을 TOML에 쓰는 방식보다 안전합니다. &lt;a href=&quot;https://developers.openai.com/codex/config-reference&quot;&gt;Codex provider 설정&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;macOS 데스크톱 앱이 키를 읽도록 하려면 현재 로그인 세션에 환경 변수를 설정한 뒤 Codex를 완전히 재시작합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;launchctl setenv DEEPSEEK_API_KEY &amp;quot;발급받은_키&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 명령은 현재 로그인 세션을 위한 설정입니다. 영구 보관이 필요하다면 운영체제 키체인이나 조직의 비밀 관리 방식을 사용하면 됩니다. 셸 시작 파일에 실제 키를 평문으로 적는 방법은 피하는 것이 좋습니다.&lt;/p&gt;
&lt;h2&gt;3. 자식에게 실행권한을 주고, 부모는 감독만 하게 한다&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;agents.default_subagent_model&lt;/code&gt;만 바꾸면 모델 이름만 기본값으로 바뀌고 provider까지 안전하게 고정되지 않습니다. 메인 OpenAI provider로 DeepSeek 모델을 호출하는 실수를 피하려면, provider를 함께 지정한 커스텀 에이전트를 만듭니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/.codex/agents/deepseek-v4-flash.toml&lt;/code&gt; 파일을 만듭니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;name = &amp;quot;deepseek_v4_flash&amp;quot;
description = &amp;quot;Fast DeepSeek V4 Flash worker for bounded implementation, documentation, and verification tasks.&amp;quot;
model = &amp;quot;deepseek-v4-flash&amp;quot;
model_provider = &amp;quot;deepseek&amp;quot;
sandbox_mode = &amp;quot;workspace-write&amp;quot;

developer_instructions = &amp;quot;&amp;quot;&amp;quot;
Own the bounded task delegated by the parent from inspection through
implementation and focused verification. Modify only files needed for the task,
preserve unrelated user changes, and never access or change credentials. Return
a concise summary of changed files, verification evidence, and any limitation.
&amp;quot;&amp;quot;&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;workspace-write&lt;/code&gt;는 자식에게 현재 작업공간 안에서 구현할 권한을 줍니다. 자식은 요청을 받아 구현과 검증을 마칠 때까지 작업을 소유하고, 부모는 결과가 요구사항과 맞는지 확인합니다. 자격 증명·전역 설정·배포 권한은 자식의 범위에서 제외하는 편이 좋습니다.&lt;/p&gt;
&lt;p&gt;Codex는 &lt;code&gt;~/.codex/agents/&lt;/code&gt; 아래의 각 TOML 파일을 커스텀 에이전트로 로드하며, 그 파일의 &lt;code&gt;model&lt;/code&gt; 설정은 부모 설정보다 우선합니다. &lt;a href=&quot;https://developers.openai.com/codex/subagents&quot;&gt;커스텀 에이전트 설정 방식&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;4. 부모는 작업을 넘기고, 결과만 검토한다&lt;/h2&gt;
&lt;p&gt;Codex를 완전히 종료한 뒤 다시 엽니다. 부모에게는 다음처럼 역할을 분명히 요청합니다. 부모는 직접 파일을 고치지 않고, 자식이 작업한 결과와 검증 기록을 검토합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;deepseek_v4_flash 자식에게 이 작업을 전부 맡겨 주세요.
자식이 관련 파일을 조사하고 구현한 뒤, 필요한 테스트를 실행해 결과를 보고하게 하세요.
부모는 파일을 직접 수정하지 말고 변경 파일, 테스트 결과, 요구사항 충족 여부만 검토해 승인 또는 보완 사항을 알려 주세요.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자식에게 원래 사용자 요청 또는 그 요청을 정확히 요약한 작업 지시가 전달돼야 합니다. 부모가 결과물을 대신 쓸 필요는 없지만, 자식이 무엇을 완성해야 하는지 알 수 있어야 하기 때문입니다.&lt;/p&gt;
&lt;p&gt;부모가 확인할 항목은 세 가지면 충분합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;자식이 수정한 파일이 작업 범위 안에 있는가&lt;/li&gt;
&lt;li&gt;자식이 실행한 검증이 변경 내용과 맞는가&lt;/li&gt;
&lt;li&gt;결과가 사용자의 요청을 충족하는가&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. provider와 worker를 각각 검증한다&lt;/h2&gt;
&lt;p&gt;먼저 DeepSeek provider 자체를 확인합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex exec --strict-config --sandbox workspace-write \
  --model deepseek-v4-flash \
  --config &amp;#39;model_provider=&amp;quot;deepseek&amp;quot;&amp;#39; \
  &amp;#39;Create a small Markdown file, read it back, and report the title and line count.&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;성공 기준은 자식이 파일을 만들고, 스스로 읽어 확인한 결과를 반환하는 것입니다. 이후 부모가 같은 파일을 읽어 제목·줄 수·변경 범위를 독립적으로 확인하면 작성과 감독이 분리됩니다.&lt;/p&gt;
&lt;p&gt;커스텀 에이전트를 통한 자동 위임은 Codex 버전과 실행 환경에 따라 작업 본문 전달이 실패할 수 있습니다. 실제 업무에 쓰기 전에 반드시 자식이 전달받은 작업을 자신의 말로 요약하고, 해당 파일을 실제로 수정·검증했는지 확인하세요. 작업 지시가 비어 있다면 provider 문제가 아니라 handoff 문제이므로, 성공한 것처럼 다음 단계로 넘어가면 안 됩니다.&lt;/p&gt;
&lt;h2&gt;6. 문제가 생기면 원본으로 되돌린다&lt;/h2&gt;
&lt;p&gt;테스트가 실패했거나 메인 모델 선택이 달라졌다면, Codex를 종료한 상태에서 백업본의 &lt;code&gt;config.toml&lt;/code&gt;과 &lt;code&gt;agents&lt;/code&gt; 폴더를 복원합니다. 복원 전 현재 파일도 별도로 복사해 두면 원인을 비교할 수 있습니다.&lt;/p&gt;
&lt;p&gt;복원 후에는 &lt;code&gt;codex doctor&lt;/code&gt;로 다음 두 가지를 확인합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;config.toml parse ok&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;메인 모델이 의도한 GPT-5.6과 &lt;code&gt;openai&lt;/code&gt; provider로 표시되는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DeepSeek 모델을 못 찾는다는 메타데이터 경고가 나올 수 있습니다. 호출이 성공하더라도 복잡한 도구 사용이나 장기 작업에서 동작이 달라질 수 있으므로, 처음에는 작은 기능 하나처럼 검증 범위가 분명한 작업부터 맡기는 편이 안전합니다.&lt;/p&gt;
&lt;h2&gt;어떤 작업을 DeepSeek 서브에이전트에 맡길까&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DeepSeek 자식이 소유할 작업&lt;/th&gt;
&lt;th&gt;GPT-5.6 부모가 관리할 작업&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;관련 코드 조사, 구현, 단위·통합 테스트&lt;/td&gt;
&lt;td&gt;요구사항을 작업 단위로 나누기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문서 작성과 코드 정리&lt;/td&gt;
&lt;td&gt;변경 범위와 사용자 의도 대조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;오류 재현, 원인 분석, 수정안 적용&lt;/td&gt;
&lt;td&gt;테스트 근거와 회귀 위험 검토&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반복되는 기능 작업&lt;/td&gt;
&lt;td&gt;보안·배포·데이터 변경의 최종 승인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;부모가 자식의 결과를 다시 구현하면 분업의 이점이 사라집니다. 부모는 결과를 믿는 것이 아니라, 변경 파일과 검증 근거를 살펴 결과를 판정합니다. 이 역할 분리가 비용을 줄이면서도 품질을 통제하는 핵심입니다.&lt;/p&gt;
&lt;h2&gt;마무리&lt;/h2&gt;
&lt;p&gt;이 구성의 목표는 GPT-5.6을 구현자로 쓰는 것이 아닙니다. GPT-5.6은 맥락·우선순위·최종 책임을 유지하고, DeepSeek V4 Flash 자식은 실제 작업과 검증을 끝까지 수행합니다. provider에는 키를 한 번만 연결하고, 에이전트 파일에는 worker의 역할·모델·권한만 남기면 관리와 복구가 단순해집니다.&lt;/p&gt;</description>
      <category>개발상식</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1801</guid>
      <comments>https://javaexpert.tistory.com/1801#entry1801comment</comments>
      <pubDate>Thu, 6 Aug 2026 13:41:49 +0900</pubDate>
    </item>
    <item>
      <title>Cerebras는 어떻게 사내 지식 검색 시스템을 구축했을까?</title>
      <link>https://javaexpert.tistory.com/1800</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;PostgreSQL 하나로 구현한 엔터프라이즈 RAG 아키텍처 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 생성형 AI가 기업 업무에 빠르게 도입되면서, 사내 문서와 대화 기록을 AI가 검색하고 활용할 수 있도록 만드는 &lt;b&gt;엔터프라이즈 지식 검색 시스템&lt;/b&gt;이 주목받고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 실제 기업 환경에서 RAG 시스템을 운영하는 일은 생각보다 단순하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업의 지식은 한곳에 정리되어 있지 않기 때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업무 대화는 Slack에서 이루어진다.&lt;/li&gt;
&lt;li&gt;소스 코드는 GitHub에 저장된다.&lt;/li&gt;
&lt;li&gt;프로젝트 일정과 이슈는 Jira에서 관리된다.&lt;/li&gt;
&lt;li&gt;공식 문서는 Google Drive나 Notion에 존재한다.&lt;/li&gt;
&lt;li&gt;중요한 결정은 회의록이나 이메일에 남는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 데이터가 여러 서비스에 분산된 상황에서, AI가 사용자의 질문에 정확한 답을 제공하려면 단순한 벡터 검색 이상의 설계가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 AI 반도체 기업 &lt;b&gt;Cerebras&lt;/b&gt;가 공개한 사내 지식 검색 시스템인 &lt;b&gt;Cerebras Knowledge&lt;/b&gt;의 구조를 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 네 가지를 중심으로 분석한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;전용 벡터 데이터베이스 없이 PostgreSQL을 선택한 이유&lt;/li&gt;
&lt;li&gt;Slack 데이터를 검색 가능한 지식으로 변환하는 방법&lt;/li&gt;
&lt;li&gt;키워드&amp;middot;벡터&amp;middot;희소성&amp;middot;신선도를 결합한 하이브리드 검색&lt;/li&gt;
&lt;li&gt;인간용 UI와 AI 에이전트용 MCP 도구를 분리한 설계&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. Cerebras Knowledge란 무엇인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras Knowledge는 Cerebras 내부의 여러 데이터 소스를 통합 검색하기 위해 구축된 사내 지식 시스템이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제공된 기사에 따르면 이 시스템은 출시 후 약 3개월 만에 하루 15,000건 이상의 질문을 처리하는 수준으로 사용되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문을 보내는 주체도 사람만이 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 직원&lt;/li&gt;
&lt;li&gt;개발자&lt;/li&gt;
&lt;li&gt;사내 자동화 시스템&lt;/li&gt;
&lt;li&gt;AI 에이전트&lt;/li&gt;
&lt;li&gt;코딩 에이전트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Cerebras Knowledge는 단순한 사내 챗봇이 아니라 인간과 AI가 함께 사용하는 &lt;b&gt;조직 지식 인프라&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 사내 RAG 시스템은 다음과 같은 흐름을 따른다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Slack &amp;middot; GitHub &amp;middot; Jira &amp;middot; 문서
        &amp;darr;
데이터 수집 및 청크 분할
        &amp;darr;
임베딩 생성
        &amp;darr;
벡터 데이터베이스 저장
        &amp;darr;
유사도 검색
        &amp;darr;
LLM 답변 생성
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras의 구조도 큰 흐름은 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 다음 두 가지에서 차별점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, Pinecone이나 전용 검색 클러스터 대신 PostgreSQL을 중심으로 시스템을 구성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째, Slack 메시지를 그대로 임베딩하지 않고, 검색에 적합한 형태로 정규화한 후 저장했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 왜 사내 지식 검색은 어려운가?&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.1 지식은 작성하기 편한 장소에서 만들어진다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 기업이 지식 관리 문제를 해결하기 위해 다음과 같은 목표를 세운다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 정보를 하나의 플랫폼에 모으자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 흔히 &lt;b&gt;Single Source of Truth&lt;/b&gt;, 즉 단일 진실 공급원이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론적으로는 합리적인 접근이다. 하지만 실제 조직에서는 제대로 실현되기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들은 정보를 관리하기 좋은 장소보다, 업무를 처리하기 편한 장소에 기록하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 개발팀은 Slack에서 기술적인 결정을 내리고, GitHub Pull Request에서 구현 방향을 토론하며, Jira 이슈에 진행 상태를 남긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보를 모두 특정 지식 관리 플랫폼으로 다시 옮기는 작업은 지속되기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 현실적인 사내 지식 검색 시스템은 모든 사용자의 작성 습관을 바꾸기보다, 기존 도구에 흩어진 정보를 수집하고 검색할 수 있어야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.2 Slack 메시지는 일반 문서와 다르다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack 데이터는 RAG에서 다루기 까다로운 대표적인 비정형 데이터다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 문서는 하나의 문단이나 페이지에 비교적 완전한 의미가 담겨 있다. 반면 Slack 메시지는 대화의 전후 맥락에 의존한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 메시지가 있다고 가정해 보자.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;A: 이번 장애 원인이 뭐였나요?

B: 캐시 만료 정책 때문입니다.

C: 확인했습니다.

B: 정확히는 Redis TTL이 배포 과정에서 기본값으로 변경됐습니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메시지를 개별 청크로 저장하면 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 만료 정책 때문입니다라는 메시지는 질문과 분리될 경우 무엇을 설명하는지 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인했습니다와 같은 짧은 메시지는 검색 가치가 거의 없지만, 임베딩 유사도에 따라 검색 결과 상단에 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 가장 중요한 기술적 설명이 스레드 중간이나 마지막에 위치할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Slack 데이터를 메시지 단위로 단순 벡터화하면 다음과 같은 문제가 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전후 맥락 손실&lt;/li&gt;
&lt;li&gt;짧은 메시지의 과도한 노출&lt;/li&gt;
&lt;li&gt;검색 가치가 낮은 응답 포함&lt;/li&gt;
&lt;li&gt;중요한 장문 설명 누락&lt;/li&gt;
&lt;li&gt;질문과 답변 관계 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras는 이를 해결하기 위해 Slack 데이터를 검색 전에 가공하는 파이프라인을 구축했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. PostgreSQL 하나로 검색 시스템 구성하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras Knowledge에서 가장 눈에 띄는 선택은 전용 벡터 데이터베이스를 사용하지 않았다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제공된 기사에 따르면 검색 인프라는 PostgreSQL을 중심으로 구성되어 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키워드 검색: PostgreSQL Full Text Search와 GIN 인덱스&lt;/li&gt;
&lt;li&gt;벡터 검색: pgvector&lt;/li&gt;
&lt;li&gt;벡터 인덱스: HNSW&lt;/li&gt;
&lt;li&gt;구조화 데이터 및 메타데이터: 일반 PostgreSQL 컬럼&lt;/li&gt;
&lt;li&gt;원문 데이터: 동일 데이터베이스에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 RAG 서비스에서는 다음과 같이 여러 저장소를 분리하는 경우가 많다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;PostgreSQL
- 사용자
- 권한
- 문서 메타데이터

Vector DB
- 임베딩
- 벡터 검색

Elasticsearch
- 키워드 검색
- 로그 검색
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Cerebras 방식은 이를 하나의 PostgreSQL 시스템으로 단순화한다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;PostgreSQL
- 원문
- 정규화된 문서
- 임베딩
- 키워드 인덱스
- 메타데이터
- 시간 정보
- 데이터 출처
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.1 PostgreSQL 중심 설계의 장점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영 복잡도 감소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 종류가 늘어나면 배포, 백업, 모니터링, 장애 대응 체계도 복잡해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL 하나를 사용하면 다음 항목을 일관되게 관리할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백업&lt;/li&gt;
&lt;li&gt;복구&lt;/li&gt;
&lt;li&gt;마이그레이션&lt;/li&gt;
&lt;li&gt;트랜잭션&lt;/li&gt;
&lt;li&gt;접근 권한&lt;/li&gt;
&lt;li&gt;모니터링&lt;/li&gt;
&lt;li&gt;데이터 정합성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 동기화가 단순하다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 원문은 PostgreSQL에 있고 임베딩은 별도 벡터 DB에 저장되어 있다면, 문서 수정 시 두 시스템을 모두 갱신해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 다음과 같은 오류가 발생할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원문은 최신인데 벡터는 이전 버전인 상태&lt;/li&gt;
&lt;li&gt;삭제된 문서의 벡터가 남아 있는 상태&lt;/li&gt;
&lt;li&gt;문서는 존재하지만 임베딩 생성에 실패한 상태&lt;/li&gt;
&lt;li&gt;재처리 중 중복 벡터가 생성된 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일 데이터베이스에 원문과 임베딩을 저장하면 이러한 동기화 문제를 줄일 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발자의 학습 비용이 낮다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL은 대부분의 백엔드 엔지니어가 사용 경험을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 벡터 데이터베이스의 쿼리 문법, 운영 방식, 백업 정책, 클러스터 구조를 추가로 학습하지 않아도 된다는 장점이 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.2 PostgreSQL만으로 충분한가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 RAG 시스템에 PostgreSQL이 적합한 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 상황에서는 전용 검색 시스템이 더 적절할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수억 개 이상의 임베딩을 검색해야 하는 경우&lt;/li&gt;
&lt;li&gt;초당 검색 요청이 매우 높은 경우&lt;/li&gt;
&lt;li&gt;복잡한 언어별 형태소 분석이 필요한 경우&lt;/li&gt;
&lt;li&gt;여러 지역에 걸친 분산 검색이 필요한 경우&lt;/li&gt;
&lt;li&gt;벡터 인덱스를 독립적으로 확장해야 하는 경우&lt;/li&gt;
&lt;li&gt;고급 필터링과 랭킹 기능이 필요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 사내 지식 검색처럼 데이터 규모가 통제 가능하고, 메타데이터 필터링과 정합성이 중요한 환경에서는 PostgreSQL이 실용적인 선택이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &amp;ldquo;전용 벡터 DB가 항상 필요하다&amp;rdquo;가 아니라, 시스템 규모와 운영 조건에 따라 저장소를 선택해야 한다는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Slack 스레드를 검색 가능한 문서로 변환하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras는 Slack 메시지를 그대로 임베딩하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 스레드 전체를 LLM에 입력하고, 검색에 적합한 구조화 문서로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로는 다음과 같은 처리 과정이다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Slack 원본 스레드
        &amp;darr;
대화 분석
        &amp;darr;
핵심 주제 추출
        &amp;darr;
질문과 답변 관계 복원
        &amp;darr;
기술적 결론 요약
        &amp;darr;
예상 검색 질문 생성
        &amp;darr;
정규화 문서 저장
        &amp;darr;
임베딩 생성
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 원본 Slack 스레드가 다음과 같다고 하자.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;A: 배포 이후 API 응답이 느려진 이유가 뭔가요?

B: DB 문제는 아닌 것 같습니다.

C: 로그를 보니 캐시 적중률이 20%대로 내려갔습니다.

B: 배포 스크립트에서 Redis TTL 환경 변수가 누락됐네요.

A: 기본값이 적용된 건가요?

B: 네. 수정 후 캐시 적중률이 정상화됐습니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 검색용 문서로 변환하면 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;제목:
배포 이후 API 응답 지연과 Redis TTL 환경 변수 누락

요약:
배포 스크립트에서 Redis TTL 환경 변수가 누락되면서
기본 만료 시간이 적용되었고, 캐시 적중률이 약 20%대로 감소했다.
환경 변수를 복원한 후 캐시 적중률과 API 응답 속도가 정상화되었다.

예상 검색 질문:
- 배포 이후 API 응답이 느려진 이유는 무엇인가?
- Redis 캐시 적중률이 감소한 원인은 무엇인가?
- TTL 환경 변수 누락으로 발생한 장애 사례가 있는가?

관련 키워드:
Redis, TTL, 캐시 적중률, 배포 스크립트, API 지연
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 변환하면 사용자가 원문의 정확한 표현을 기억하지 못하더라도 관련 내용을 찾기 쉬워진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.1 예상 질문을 생성하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩 검색은 질문과 문서가 의미적으로 유사할수록 잘 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 원본 문서는 사실을 설명하는 서술형 문장이고, 사용자는 의문형으로 검색하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문서에는 다음과 같이 기록되어 있을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Redis TTL 환경 변수가 누락되어 기본값이 적용됐다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 다음과 같이 질문할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;지난번 배포 후 API가 느려진 이유가 뭐였지?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 문장은 동일한 사건을 가리키지만 사용하는 단어와 문장 구조가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색용 예상 질문을 미리 생성하면 사용자 질문과 임베딩 공간에서 가까운 표현을 확보할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 흔히 다음과 같은 이름으로 불린다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Question Generation&lt;/li&gt;
&lt;li&gt;Hypothetical Question Indexing&lt;/li&gt;
&lt;li&gt;Query-oriented Chunk Enrichment&lt;/li&gt;
&lt;li&gt;Document Expansion&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.2 원문도 별도로 보존한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규화된 요약만 저장하면 LLM이 생략한 세부 정보가 손실될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 원본 Slack 텍스트도 함께 저장해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;knowledge_chunks
├── source_type
├── source_id
├── thread_id
├── raw_content
├── normalized_content
├── summary
├── hypothetical_questions
├── keywords
├── embedding
├── author_ids
├── channel_id
├── created_at
├── updated_at
└── metadata
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 단계에서는 정규화된 문서와 예상 질문을 벡터 검색에 활용하고, 원문은 키워드 검색과 최종 근거 제공에 활용할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 긴 Slack 스레드를 처리하는 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 전체를 하나의 청크로 만드는 방법도 완전하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드가 지나치게 길면 중간에 있는 중요한 정보가 임베딩에서 희석될 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 하나의 스레드에 다음 내용이 모두 포함되어 있을 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장애 발생 보고&lt;/li&gt;
&lt;li&gt;원인 분석&lt;/li&gt;
&lt;li&gt;임시 대응&lt;/li&gt;
&lt;li&gt;코드 수정&lt;/li&gt;
&lt;li&gt;재발 방지 대책&lt;/li&gt;
&lt;li&gt;배포 결과&lt;/li&gt;
&lt;li&gt;후속 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스레드를 하나의 벡터로 표현하면 개별 주제의 의미가 약해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras는 긴 스레드에서 특정 조건을 충족하는 연속 발언을 별도 청크로 분리하는 방식을 사용한 것으로 소개된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 한 사용자가 연속해서 작성한 장문의 기술 설명은 독립적인 지식 단위일 가능성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적인 분할 기준은 다음과 같이 설계할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일 작성자의 연속 메시지&lt;/li&gt;
&lt;li&gt;일정 글자 수 이상의 메시지 묶음&lt;/li&gt;
&lt;li&gt;코드 블록을 포함한 설명&lt;/li&gt;
&lt;li&gt;URL이나 문서 링크를 포함한 설명&lt;/li&gt;
&lt;li&gt;특정 기술 용어가 반복되는 구간&lt;/li&gt;
&lt;li&gt;질문에 대한 직접적인 원인 또는 해결책&lt;/li&gt;
&lt;li&gt;최종 결정이나 합의를 나타내는 문장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 기반으로 스레드에서 여러 종류의 청크를 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;autohotkey&quot;&gt;&lt;code&gt;Slack Thread
├── 스레드 전체 요약 청크
├── 원인 분석 청크
├── 기술 설명 청크
├── 해결 방법 청크
└── 최종 결정 청크
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 한 가지 청킹 방식만 사용하는 것보다 검색 recall을 높이는 데 유리하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 검색할 때 네 가지 신호를 함께 사용한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras Knowledge는 사용자의 질문이 들어왔을 때 하나의 검색 방식만 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 검색 신호를 병렬로 계산한 후 결과를 결합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기사에서 소개된 주요 신호는 다음 네 가지다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;키워드 검색&lt;/li&gt;
&lt;li&gt;벡터 검색&lt;/li&gt;
&lt;li&gt;IDF 기반 희귀 단어 가중치&lt;/li&gt;
&lt;li&gt;정보 신선도&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.1 키워드 검색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키워드 검색은 사용자가 입력한 단어가 문서에 직접 포함되어 있는지를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 검색에 강하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정확한 클래스 이름&lt;/li&gt;
&lt;li&gt;오류 코드&lt;/li&gt;
&lt;li&gt;티켓 번호&lt;/li&gt;
&lt;li&gt;프로젝트 이름&lt;/li&gt;
&lt;li&gt;사람 이름&lt;/li&gt;
&lt;li&gt;API 엔드포인트&lt;/li&gt;
&lt;li&gt;라이브러리 이름&lt;/li&gt;
&lt;li&gt;커밋 해시&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ERR_CONNECTION_RESET
JIRA-1842
payment-webhook
RedisClusterManager
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 값은 의미 기반 임베딩보다 정확한 문자열 매칭이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL에서는 tsvector, tsquery, GIN 인덱스를 사용해 전체 텍스트 검색을 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적인 SQL은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT
    id,
    normalized_content,
    ts_rank(search_vector, query) AS keyword_score
FROM knowledge_chunks,
     plainto_tsquery('simple', :query) AS query
WHERE search_vector @@ query
ORDER BY keyword_score DESC
LIMIT 50;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.2 벡터 검색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터 검색은 표현이 다르더라도 의미가 비슷한 문서를 찾는 데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 다음과 같이 질문한다고 하자.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;서비스 배포 후 갑자기 느려졌던 사건
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서에는 다음 표현만 존재할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;릴리스 이후 캐시 적중률 저하로 API latency가 증가했다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 일치하는 단어는 적지만 의미는 유사하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pgvector를 사용하면 다음과 같은 형태로 검색할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT
    id,
    normalized_content,
    1 - (embedding &amp;lt;=&amp;gt; :query_embedding) AS vector_score
FROM knowledge_chunks
ORDER BY embedding &amp;lt;=&amp;gt; :query_embedding
LIMIT 50;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.3 IDF 기반 희귀 단어 가중치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IDF는 &lt;b&gt;Inverse Document Frequency&lt;/b&gt;, 즉 역문서 빈도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 문서에 반복해서 등장하는 단어는 검색 가치가 낮고, 일부 문서에만 등장하는 희귀 단어는 검색 가치가 높다고 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Slack에는 다음과 같은 표현이 매우 자주 나타난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확인했습니다&lt;/li&gt;
&lt;li&gt;감사합니다&lt;/li&gt;
&lt;li&gt;알겠습니다&lt;/li&gt;
&lt;li&gt;가능합니다&lt;/li&gt;
&lt;li&gt;좋습니다&lt;/li&gt;
&lt;li&gt;진행하겠습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 단어가 포함된 메시지는 검색 결과에서 높은 우선순위를 받을 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 다음 표현은 특정 문제와 직접 연결될 가능성이 높다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NCCL timeout&lt;/li&gt;
&lt;li&gt;HNSW index&lt;/li&gt;
&lt;li&gt;memory fragmentation&lt;/li&gt;
&lt;li&gt;TTL override&lt;/li&gt;
&lt;li&gt;compiler regression&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IDF 신호를 사용하면 검색 가치가 낮은 상투적 표현을 억제하고, 기술적으로 구체적인 문장을 우대할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.4 정보 신선도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내 지식은 시간이 지나면서 변경된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6개월 전의 배포 절차보다 지난주에 개정된 배포 절차가 더 정확할 가능성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 검색 결과에 시간 가중치를 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 감쇠 함수를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;freshness_score = exp(-&amp;lambda; &amp;times; document_age)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 document_age는 문서 생성 또는 갱신 후 경과 시간이며, &amp;lambda;는 오래된 정보의 점수를 얼마나 빠르게 낮출지를 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 최신 문서가 항상 정답인 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래된 장애 회고, 기술적 결정 기록, 설계 배경은 시간이 지나도 가치가 유지될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 데이터 유형에 따라 신선도 가중치를 다르게 적용하는 것이 안전하다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;운영 절차       &amp;rarr; 신선도 가중치 높음
현재 담당자 정보 &amp;rarr; 신선도 가중치 높음
장애 회고       &amp;rarr; 신선도 가중치 중간
아키텍처 결정   &amp;rarr; 신선도 가중치 중간
역사적 기록     &amp;rarr; 신선도 가중치 낮음
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. RRF로 검색 결과를 통합한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키워드 검색과 벡터 검색은 각각 다른 점수 체계를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 키워드 검색 점수는 0.72, 벡터 유사도는 0.84, 신선도 점수는 0.56처럼 계산될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점수들을 단순히 더하면 각 점수의 범위와 분포가 달라 결과가 왜곡될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras는 여러 랭킹을 결합하기 위해 &lt;b&gt;RRF, Reciprocal Rank Fusion&lt;/b&gt;을 사용하는 것으로 소개된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RRF는 검색 결과의 절대 점수보다 각 검색 방식에서 몇 위에 올랐는지를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 식은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;RRF Score(d) = &amp;Sigma; 1 / (k + rankᵢ(d))
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;d: 문서&lt;/li&gt;
&lt;li&gt;rankᵢ(d): i번째 검색 방식에서 해당 문서의 순위&lt;/li&gt;
&lt;li&gt;k: 상위 결과의 점수 차이를 완화하는 상수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 어떤 문서가 다음 순위를 기록했다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;키워드 검색: 2위
벡터 검색: 5위
신선도 랭킹: 3위
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k = 60이면 다음과 같이 계산할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;RRF = 1 / 62 + 1 / 65 + 1 / 63
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RRF의 장점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 점수 정규화가 필요하지 않다.&lt;/li&gt;
&lt;li&gt;서로 다른 검색 엔진의 결과를 쉽게 결합할 수 있다.&lt;/li&gt;
&lt;li&gt;특정 검색 방식 하나에 과도하게 의존하지 않는다.&lt;/li&gt;
&lt;li&gt;구현이 단순하다.&lt;/li&gt;
&lt;li&gt;하이브리드 검색의 안정성이 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 소형 리랭커로 최종 문서를 선별한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RRF로 검색 결과를 합친 뒤에도 상위 문서가 모두 질문에 적합한 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 상위 후보에 대해 리랭커를 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 검색 파이프라인은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;사용자 질문
    &amp;darr;
키워드 검색 Top 50
벡터 검색 Top 50
IDF 랭킹 Top 50
신선도 랭킹 Top 50
    &amp;darr;
RRF 통합
    &amp;darr;
상위 30~50개 후보
    &amp;darr;
소형 리랭커
    &amp;darr;
최종 Top 10
    &amp;darr;
LLM 컨텍스트 구성
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리랭커는 질문과 각 문서를 함께 입력받아 관련성을 직접 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터 검색이 질문과 문서를 각각 독립적으로 임베딩하는 bi-encoder 방식이라면, 리랭커는 질문과 문서를 동시에 비교하는 cross-encoder 방식으로 구현되는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리랭커는 일반적으로 벡터 검색보다 정확하지만 계산 비용이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 전체 문서를 리랭킹하지 않고, 1차 검색으로 좁힌 후보에만 적용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 검색된 청크에 주변 문맥을 다시 붙인다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색용 청크는 정확한 검색을 위해 작게 분할해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 LLM에 제공하는 컨텍스트는 충분한 문맥을 포함해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 검색에 사용한 청크와 생성 모델에 제공하는 청크를 동일하게 구성할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 검색 결과가 Slack 스레드 중간의 기술 설명이라면, 최종적으로 LLM에 전달할 때는 다음 내용을 추가할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초 질문&lt;/li&gt;
&lt;li&gt;검색된 핵심 메시지&lt;/li&gt;
&lt;li&gt;바로 앞 메시지&lt;/li&gt;
&lt;li&gt;바로 뒤 메시지&lt;/li&gt;
&lt;li&gt;스레드 최종 결론&lt;/li&gt;
&lt;li&gt;연결된 문서나 코드 링크&lt;/li&gt;
&lt;li&gt;작성자와 작성 시점&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;context expansion&lt;/b&gt;, &lt;b&gt;neighbor expansion&lt;/b&gt;, &lt;b&gt;parent-child retrieval&lt;/b&gt; 등의 방식으로 구현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;작은 청크로 검색
        &amp;darr;
관련 청크 선택
        &amp;darr;
부모 문서 또는 주변 청크 로드
        &amp;darr;
확장된 컨텍스트를 LLM에 전달
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 검색 정확도와 답변 완성도를 동시에 확보하는 데 유리하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. MCP에는 완성형 답변이 아니라 검색 도구를 공개한다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras Knowledge는 인간이 사용하는 웹 인터페이스뿐 아니라 AI 에이전트가 호출할 수 있는 MCP 인터페이스도 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 설계 원칙은 AI 에이전트에게 다음과 같은 단일 도구를 제공하지 않는다는 점이다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;answer_company_question(question)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 보다 작은 검색 도구를 제공한다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;search_slack(query)
search_code(query)
search_documents(query)
search_jira(query)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 도구를 흔히 &lt;b&gt;primitive tool&lt;/b&gt;, 즉 기본 단위 도구라고 부를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 사용자의 요청을 분석하고 필요한 도구를 스스로 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 질문이 들어왔다고 하자.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;결제 웹훅 중복 처리 문제가 왜 발생했고 현재 코드는 어떻게 수정됐나요?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트는 다음과 같은 순서로 동작할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;1. search_slack
   - 장애 당시 논의와 원인 검색

2. search_jira
   - 관련 이슈와 후속 작업 검색

3. search_code
   - 현재 구현 코드와 커밋 검색

4. 결과 비교
   - 과거 원인과 현재 수정 상태를 연결

5. 최종 답변 생성
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 시스템이 모든 질문을 한 번에 해결하려 하기보다, 에이전트가 조합할 수 있는 작은 기능을 제공하는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.1 인간용 인터페이스와 AI용 인터페이스의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인간 사용자는 일반적으로 다음과 같은 경험을 원한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;질문 입력&lt;/li&gt;
&lt;li&gt;답변 확인&lt;/li&gt;
&lt;li&gt;근거 문서 열기&lt;/li&gt;
&lt;li&gt;추가 질문&lt;/li&gt;
&lt;li&gt;검색 결과 필터링&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 AI 에이전트는 다음을 필요로 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조화된 입력과 출력&lt;/li&gt;
&lt;li&gt;데이터 출처 구분&lt;/li&gt;
&lt;li&gt;검색 범위 지정&lt;/li&gt;
&lt;li&gt;페이지네이션&lt;/li&gt;
&lt;li&gt;메타데이터 필터&lt;/li&gt;
&lt;li&gt;재현 가능한 결과&lt;/li&gt;
&lt;li&gt;근거 식별자&lt;/li&gt;
&lt;li&gt;원문 조회 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 인간용 챗봇 API와 AI 에이전트용 검색 API를 분리하는 것은 합리적인 접근이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 전체 아키텍처 예시&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras 사례를 참고해 사내 지식 검색 시스템을 구성하면 다음과 같은 구조를 설계할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;┌─────────────────────────────────────┐
│            데이터 소스              │
│ Slack &amp;middot; GitHub &amp;middot; Jira &amp;middot; Drive       │
└──────────────────┬──────────────────┘
                   │
                   ▼
┌─────────────────────────────────────┐
│          수집 및 동기화 계층         │
│ Webhook &amp;middot; Polling &amp;middot; Incremental Sync│
└──────────────────┬──────────────────┘
                   │
                   ▼
┌─────────────────────────────────────┐
│          전처리 파이프라인           │
│ 정규화 &amp;middot; 중복 제거 &amp;middot; 권한 추출      │
│ 스레드 요약 &amp;middot; 질문 생성 &amp;middot; 청크 분할 │
└──────────────────┬──────────────────┘
                   │
                   ▼
┌─────────────────────────────────────┐
│             PostgreSQL              │
│ 원문 &amp;middot; 요약 &amp;middot; 메타데이터 &amp;middot; 임베딩   │
│ Full Text Search &amp;middot; pgvector &amp;middot; HNSW │
└──────────────────┬──────────────────┘
                   │
                   ▼
┌─────────────────────────────────────┐
│             검색 계층               │
│ Keyword &amp;middot; Vector &amp;middot; IDF &amp;middot; Freshness │
│ RRF &amp;middot; Reranker &amp;middot; Context Expansion │
└──────────────────┬──────────────────┘
                   │
          ┌────────┴────────┐
          ▼                 ▼
┌─────────────────┐ ┌─────────────────┐
│   인간용 Web UI  │ │ AI Agent &amp;middot; MCP  │
│ 검색 &amp;middot; 채팅      │ │ search_slack    │
│ 출처 &amp;middot; 피드백    │ │ search_code     │
└─────────────────┘ └─────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 실제 구현 시 가장 어려운 문제: 권한 관리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기사에서도 한계로 지적된 부분이 권한 관리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내 데이터는 모든 직원에게 동일하게 공개되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 접근 제한이 존재할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비공개 Slack 채널&lt;/li&gt;
&lt;li&gt;경영진 전용 문서&lt;/li&gt;
&lt;li&gt;인사 평가 자료&lt;/li&gt;
&lt;li&gt;법무 관련 문서&lt;/li&gt;
&lt;li&gt;특정 프로젝트 참여자 전용 GitHub 저장소&lt;/li&gt;
&lt;li&gt;고객 계약 자료&lt;/li&gt;
&lt;li&gt;보안 사고 보고서&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 서비스의 검색 API를 직접 사용하는 경우에는 해당 서비스의 권한 체계를 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자의 Slack 토큰으로 Slack 검색 API를 호출하면, 사용자가 접근 가능한 채널만 검색된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 데이터를 별도 PostgreSQL에 복제하면 권한 모델도 함께 복제해야 한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;원본 데이터 복제
      +
권한 정보 복제
      +
사용자 그룹 동기화
      +
검색 시 권한 필터링
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 하나라도 누락되면 민감한 정보가 노출될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12.1 권한 필터링 기본 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 지식 청크에 접근 제어 정보를 저장할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;knowledge_chunks
├── allowed_user_ids
├── allowed_group_ids
├── source_channel_id
├── source_repository_id
├── visibility
└── classification_level
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색할 때는 벡터 유사도 계산만 수행하는 것이 아니라, 현재 사용자의 권한 조건을 함께 적용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT
    id,
    normalized_content,
    1 - (embedding &amp;lt;=&amp;gt; :query_embedding) AS score
FROM knowledge_chunks
WHERE
    visibility = 'public'
    OR :user_id = ANY(allowed_user_ids)
    OR allowed_group_ids &amp;amp;&amp;amp; :user_group_ids
ORDER BY embedding &amp;lt;=&amp;gt; :query_embedding
LIMIT 20;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 검색 후 결과를 제거하는 것이 아니라, 검색 단계부터 권한 조건을 적용해야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 후 필터링 방식은 다음과 같은 문제를 일으킬 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;권한 없는 문서가 상위 결과를 차지함&lt;/li&gt;
&lt;li&gt;권한 필터링 후 결과가 부족함&lt;/li&gt;
&lt;li&gt;검색 점수나 로그를 통해 정보가 간접 노출됨&lt;/li&gt;
&lt;li&gt;리랭커나 LLM에 민감한 문서가 전달될 가능성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12.2 삭제와 권한 변경도 동기화해야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 수집보다 더 어려운 것은 삭제와 권한 변경이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 상황을 처리해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공개 채널이 비공개 채널로 변경됨&lt;/li&gt;
&lt;li&gt;사용자가 프로젝트에서 제외됨&lt;/li&gt;
&lt;li&gt;GitHub 저장소 접근 권한이 제거됨&lt;/li&gt;
&lt;li&gt;문서가 삭제됨&lt;/li&gt;
&lt;li&gt;Slack 메시지가 수정됨&lt;/li&gt;
&lt;li&gt;민감 정보가 포함되어 원문이 삭제됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 시스템에 이전 데이터가 남아 있다면 원본에서 권한이 제거되더라도 계속 검색될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 동기화 시스템은 생성 이벤트뿐 아니라 다음 이벤트를 추적해야 한다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;Create
Update
Delete
Permission Change
Membership Change
Visibility Change
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 하나의 거대한 지식 기반은 계속 유지될 수 있을까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 모든 데이터를 하나의 지식 기반에 저장하는 방식이 단순하고 효율적일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 데이터와 사용 사례가 증가하면 하나의 검색 랭킹으로 모든 질문을 처리하기 어려워질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 질문은 서로 다른 검색 전략이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;코드 오류 원인을 찾아줘.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;지난 분기 영업 전략을 알려줘.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;휴가 신청 절차가 어떻게 되나요?
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;dns&quot;&gt;&lt;code&gt;고객 A와 체결한 계약 조건을 찾아줘.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 검색은 함수명, 경로, 심볼, 커밋 관계가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인사 규정 검색은 최신 공식 문서와 정확한 조항이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영업 자료 검색은 고객, 산업, 시점, 담당 조직이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 시스템이 성장하면 도메인별 검색 구성이 필요할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Engineering Knowledge
- GitHub
- Jira
- 기술 Slack
- 설계 문서

Sales Knowledge
- CRM
- 제안서
- 고객 미팅 기록
- 영업 Slack

HR Knowledge
- 사내 규정
- 복지 문서
- 인사 공지

Legal Knowledge
- 계약서
- 정책
- 컴플라이언스 문서
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 상위 에이전트가 질문을 분류한 후 적절한 검색 도구를 선택하도록 구성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;사용자 질문
      &amp;darr;
도메인 라우터
      &amp;darr;
Engineering / Sales / HR / Legal
      &amp;darr;
도메인별 검색
      &amp;darr;
통합 답변
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 도메인을 분리할수록 권한 관리와 운영 복잡도는 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 처음부터 과도하게 분리하기보다, 검색 품질과 데이터 규모를 관찰하면서 단계적으로 분리하는 것이 현실적이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. 실무에 적용할 수 있는 핵심 교훈&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.1 벡터 데이터베이스부터 선택하지 않는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 시스템을 구축할 때 기술 스택부터 결정하는 경우가 많다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;Pinecone을 사용할까?
Weaviate를 사용할까?
Milvus를 사용할까?
Elasticsearch를 사용할까?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 검색 품질을 결정하는 핵심은 데이터베이스 제품보다 다음 요소일 가능성이 높다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 데이터의 품질&lt;/li&gt;
&lt;li&gt;청크 분할 방식&lt;/li&gt;
&lt;li&gt;메타데이터 설계&lt;/li&gt;
&lt;li&gt;질문과 문서의 표현 차이&lt;/li&gt;
&lt;li&gt;권한 필터링&lt;/li&gt;
&lt;li&gt;최신 정보 반영&lt;/li&gt;
&lt;li&gt;검색 결과 결합&lt;/li&gt;
&lt;li&gt;리랭킹&lt;/li&gt;
&lt;li&gt;근거 문맥 확장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소규모 또는 중간 규모의 사내 지식 시스템이라면 PostgreSQL과 pgvector로 먼저 검증하는 접근도 가능하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.2 채팅 데이터는 반드시 대화 단위로 처리한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack이나 Teams 메시지를 개별 메시지 단위로 임베딩하는 방식은 피하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소한 다음 처리가 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 단위 병합&lt;/li&gt;
&lt;li&gt;질문과 답변 관계 복원&lt;/li&gt;
&lt;li&gt;짧은 응답 제거&lt;/li&gt;
&lt;li&gt;장문 설명 별도 추출&lt;/li&gt;
&lt;li&gt;결론과 결정 사항 요약&lt;/li&gt;
&lt;li&gt;예상 검색 질문 생성&lt;/li&gt;
&lt;li&gt;원문과 정규화 문서 병행 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.3 검색은 하나의 방식으로 해결하지 않는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터 검색만으로는 다음 항목을 정확히 찾기 어렵다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오류 코드&lt;/li&gt;
&lt;li&gt;프로젝트명&lt;/li&gt;
&lt;li&gt;함수명&lt;/li&gt;
&lt;li&gt;티켓 번호&lt;/li&gt;
&lt;li&gt;파일 경로&lt;/li&gt;
&lt;li&gt;사람 이름&lt;/li&gt;
&lt;li&gt;버전 번호&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 키워드 검색만으로는 표현이 다른 질문을 찾기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실무에서는 하이브리드 검색이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;Keyword
+ Vector
+ Metadata
+ Freshness
+ RRF
+ Reranker
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.4 검색용 청크와 답변용 컨텍스트를 분리한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색에는 작은 청크가 유리하지만, 답변에는 충분한 문맥이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 구조가 효과적이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;작은 청크로 검색
&amp;rarr; 부모 문서 확인
&amp;rarr; 주변 문맥 확장
&amp;rarr; 최종 컨텍스트 구성
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.5 AI 에이전트에는 작고 명확한 도구를 제공한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 도구는 하나의 거대한 답변 API보다 기능별 검색 도구로 제공하는 것이 활용성이 높다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;search_slack
search_code
search_jira
fetch_document
get_thread
get_commit
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트가 목적에 맞게 도구를 조합할 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.6 권한 관리는 나중에 추가할 기능이 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업용 RAG에서 권한 관리는 부가 기능이 아니라 핵심 아키텍처다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 모델을 설계할 때부터 다음 항목이 포함되어야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 ID&lt;/li&gt;
&lt;li&gt;그룹 ID&lt;/li&gt;
&lt;li&gt;조직 ID&lt;/li&gt;
&lt;li&gt;원본 서비스 권한&lt;/li&gt;
&lt;li&gt;문서 공개 범위&lt;/li&gt;
&lt;li&gt;검색 시 ACL 필터&lt;/li&gt;
&lt;li&gt;권한 변경 동기화&lt;/li&gt;
&lt;li&gt;삭제 전파&lt;/li&gt;
&lt;li&gt;검색 및 열람 감사 로그&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. 단계별 구축 전략&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras 사례를 참고하되 처음부터 모든 기능을 구현할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 단계적으로 구축할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1단계: 기본 검색 MVP&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;pgvector&lt;/li&gt;
&lt;li&gt;문서 수집&lt;/li&gt;
&lt;li&gt;기본 청킹&lt;/li&gt;
&lt;li&gt;벡터 검색&lt;/li&gt;
&lt;li&gt;출처 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2단계: 하이브리드 검색&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PostgreSQL Full Text Search&lt;/li&gt;
&lt;li&gt;키워드와 벡터 검색 병렬 실행&lt;/li&gt;
&lt;li&gt;RRF 통합&lt;/li&gt;
&lt;li&gt;메타데이터 필터&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3단계: Slack 최적화&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 단위 수집&lt;/li&gt;
&lt;li&gt;LLM 요약&lt;/li&gt;
&lt;li&gt;예상 질문 생성&lt;/li&gt;
&lt;li&gt;장문 메시지 별도 청킹&lt;/li&gt;
&lt;li&gt;원문 보존&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4단계: 검색 품질 향상&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리랭커&lt;/li&gt;
&lt;li&gt;신선도 점수&lt;/li&gt;
&lt;li&gt;IDF 기반 노이즈 억제&lt;/li&gt;
&lt;li&gt;컨텍스트 확장&lt;/li&gt;
&lt;li&gt;사용자 피드백 수집&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5단계: 엔터프라이즈 권한&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 및 그룹 동기화&lt;/li&gt;
&lt;li&gt;문서 ACL 저장&lt;/li&gt;
&lt;li&gt;검색 단계 권한 필터링&lt;/li&gt;
&lt;li&gt;삭제와 권한 변경 동기화&lt;/li&gt;
&lt;li&gt;감사 로그&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6단계: AI 에이전트 연동&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MCP 서버&lt;/li&gt;
&lt;li&gt;search_slack&lt;/li&gt;
&lt;li&gt;search_code&lt;/li&gt;
&lt;li&gt;search_documents&lt;/li&gt;
&lt;li&gt;원문 조회 도구&lt;/li&gt;
&lt;li&gt;도메인별 검색 라우팅&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. 결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cerebras의 사내 지식 검색 사례에서 가장 중요한 점은 특정한 벡터 데이터베이스나 최신 AI 모델을 사용했다는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 다음과 같은 기본 원칙을 충실히 적용했다는 점이 중요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 작성되는 장소에서 수집한다.&lt;/li&gt;
&lt;li&gt;Slack 대화를 검색 가능한 문서로 정규화한다.&lt;/li&gt;
&lt;li&gt;키워드 검색과 벡터 검색을 함께 사용한다.&lt;/li&gt;
&lt;li&gt;희귀 단어와 최신 정보를 랭킹에 반영한다.&lt;/li&gt;
&lt;li&gt;RRF로 여러 검색 결과를 결합한다.&lt;/li&gt;
&lt;li&gt;소형 리랭커로 최종 관련성을 평가한다.&lt;/li&gt;
&lt;li&gt;검색 후 주변 문맥을 다시 확장한다.&lt;/li&gt;
&lt;li&gt;인간용 인터페이스와 AI 에이전트용 도구를 구분한다.&lt;/li&gt;
&lt;li&gt;운영 복잡도를 낮추기 위해 PostgreSQL을 중심으로 구성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사례는 RAG 시스템의 성능이 단순히 임베딩 모델이나 벡터 데이터베이스 성능으로 결정되지 않는다는 사실을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 사내 지식 시스템을 만들기 위해서는 검색 기술뿐 아니라 데이터 전처리, 동기화, 권한 관리, 사용자 경험, 에이전트 인터페이스를 하나의 시스템으로 설계해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 초기 단계에서는 복잡한 분산 아키텍처보다 다음과 같은 단순한 구성으로 시작하는 것이 현실적일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;PostgreSQL
+ pgvector
+ Full Text Search
+ RRF
+ Reranker
+ 권한 필터
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 위에 Slack 정규화, 코드 검색, MCP, 도메인 라우팅을 단계적으로 추가하면 운영 가능한 엔터프라이즈 지식 기반으로 발전시킬 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 개념&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Retrieval-Augmented Generation, RAG&lt;/li&gt;
&lt;li&gt;PostgreSQL Full Text Search&lt;/li&gt;
&lt;li&gt;GIN Index&lt;/li&gt;
&lt;li&gt;pgvector&lt;/li&gt;
&lt;li&gt;HNSW&lt;/li&gt;
&lt;li&gt;Inverse Document Frequency, IDF&lt;/li&gt;
&lt;li&gt;Reciprocal Rank Fusion, RRF&lt;/li&gt;
&lt;li&gt;Cross-Encoder Reranker&lt;/li&gt;
&lt;li&gt;Hypothetical Question Generation&lt;/li&gt;
&lt;li&gt;Context Expansion&lt;/li&gt;
&lt;li&gt;Model Context Protocol, MCP&lt;/li&gt;
&lt;li&gt;Access Control List, ACL&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발상식</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1800</guid>
      <comments>https://javaexpert.tistory.com/1800#entry1800comment</comments>
      <pubDate>Wed, 5 Aug 2026 16:29:57 +0900</pubDate>
    </item>
    <item>
      <title>#   한눈에 정리하는 23가지 핵심 디자인 패턴 (Design Patterns for Humans)</title>
      <link>https://javaexpert.tistory.com/1799</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;&amp;quot;디자인 패턴을 머릿속에 쏙쏙 들어오게!&amp;quot;&lt;/strong&gt;&lt;br&gt;누구나 한번쯤 머리가 아팠을 디자인 패턴, 직관적인 &lt;strong&gt;현실 세계 비유&lt;/strong&gt;와 &lt;strong&gt;JavaScript(Node.js) &amp;amp; Python&lt;/strong&gt; 예제 코드로 가장 쉽고 명쾌하게 정리했습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;  목차 (Table of Contents)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt; ️ 생성 패턴 (Creational)&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;  구조 패턴 (Structural)&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;  행동 패턴 (Behavioral)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-1-simple-factory-%EC%89%AC%EC%9A%B4-%EA%B3%B5%EC%9E%A5&quot;&gt;  Simple Factory&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-1-adapter-%EC%96%B4%EB%8C%91%ED%84%B0&quot;&gt;  Adapter&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-1-chain-of-responsibility-%EC%B1%85%EC%9E%84-%EC%82%AC%EC%8A%AC&quot;&gt;  Chain of Responsibility&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-2-factory-method-%EA%B3%B5%EC%9E%A5-%EB%A9%94%EC%84%9C%EB%93%9C&quot;&gt;  Factory Method&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-2-bridge-%EB%B8%8C%EB%A6%AC%EC%A7%80&quot;&gt;  Bridge&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-2-command-%EC%BB%A4%EB%A7%A8%EB%93%9C&quot;&gt;  Command&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-3-abstract-factory-%EC%B6%94%EC%83%81-%EA%B3%B5%EC%9E%A5&quot;&gt;  Abstract Factory&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-3-composite-%EC%BB%B4%ED%8F%AC%EC%A7%80%ED%8A%B8&quot;&gt;  Composite&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-3-iterator-%EB%B0%98%EB%B3%B5%EC%9E%90&quot;&gt;➿ Iterator&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-4-builder-%EB%B9%8C%EB%8D%94&quot;&gt;  Builder&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-4-decorator-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0&quot;&gt;☕ Decorator&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-4-mediator-%EC%A4%91%EC%9E%AC%EC%9E%90&quot;&gt;  Mediator&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-5-prototype-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85&quot;&gt;  Prototype&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-5-facade-%ED%8D%BC%EC%82%AC%EB%93%9C&quot;&gt;  Facade&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-5-memento-%EB%A9%94%EB%A9%98%ED%86%A0&quot;&gt;  Memento&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-6-singleton-%EC%8B%B1%EA%B8%80%ED%86%A4&quot;&gt;  Singleton&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-6-flyweight-%ED%94%8C%EB%9D%BC%EC%9D%B4%EC%9B%A8%EC%9D%B4%ED%8A%B8&quot;&gt;  Flyweight&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-6-observer-%EC%98%B5%EC%A0%80%EB%B2%84&quot;&gt;  Observer&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-7-proxy-%ED%94%84%EB%A1%9D%EC%8B%9C&quot;&gt;  Proxy&lt;/a&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-7-visitor-%EB%B0%A9%EB%AC%B8%EC%9E%90&quot;&gt;  Visitor&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-8-strategy-%EC%A0%84%EB%9E%B5&quot;&gt;  Strategy&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-9-state-%EC%83%81%ED%83%9C&quot;&gt;  State&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=&quot;#-10-template-method-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%84%9C%EB%93%9C&quot;&gt;  Template Method&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;  디자인 패턴 들어가기 (Introduction)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;[!IMPORTANT]&lt;br&gt;&lt;strong&gt;디자인 패턴이란?&lt;/strong&gt;&lt;br&gt;자주 발생하는 문제들에 대한 &lt;strong&gt;일반적이고 재사용 가능한 해결책(가이드라인)&lt;/strong&gt;입니다. 가져다 바로 꽂아 넣는 플러그인이나 라이브러리가 아니라, &lt;strong&gt;상황을 풀어나가기 위한 디자인 템플릿&lt;/strong&gt;에 가깝습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;⚠️ 사용 시 주의사항 (Be Careful)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;만능 해결책(Silver Bullet)이 아닙니다.&lt;/strong&gt; 패턴을 억지로 적용하려고 하면 오히려 설계가 어그러집니다.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;문제에 맞춘 해결책입니다.&lt;/strong&gt; 해결책에 맞춰 문제를 발굴하는 과도한 설계를 경계하세요.&lt;/li&gt;
&lt;li&gt;  적재적소에 올바르게 쓰면 훌륭한 구원자가 되지만, 잘못 남용하면 &lt;strong&gt;코드가 악몽의 난장판&lt;/strong&gt;이 될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1&gt; ️ 생성 디자인 패턴 (Creational Design Patterns)&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 요약:&lt;/strong&gt; 객체 또는 연관된 객체 그룹을 &lt;strong&gt;어떻게 인스턴스화할 것인가&lt;/strong&gt;에 집중하는 패턴입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;  1. Simple Factory (쉬운 공장)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;집을 짓다가 문이 필요할 때 직접 목수 옷을 입고 나무와 톱을 들고 문을 짤 수도 있지만, &lt;strong&gt;공장에 주문해서 배송받는 편이 훨씬 깔끔&lt;/strong&gt;합니다. 문을 만드는 복잡한 과정이나 먼지를 뒤집어쓸 필요가 없기 때문입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;클라이언트에게 인스턴스화 로직을 노출하지 않고 인스턴스를 만들어 제공합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class WoodenDoor {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  getWidth() {
    return this.width;
  }

  getHeight() {
    return this.height;
  }
}

class DoorFactory {
  static makeDoor(width, height) {
    return new WoodenDoor(width, height);
  }
}

// 사용 예시
const door = DoorFactory.makeDoor(100, 200);
console.log(`Width: ${door.getWidth()}, Height: ${door.getHeight()}`);&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from abc import ABC, abstractmethod

class Door(ABC):
    @abstractmethod
    def get_width(self) -&amp;gt; float: pass

    @abstractmethod
    def get_height(self) -&amp;gt; float: pass

class WoodenDoor(Door):
    def __init__(self, width: float, height: float):
        self._width = width
        self._height = height

    def get_width(self) -&amp;gt; float: return self._width
    def get_height(self) -&amp;gt; float: return self._height

class DoorFactory:
    @staticmethod
    def make_door(width: float, height: float) -&amp;gt; Door:
        return WoodenDoor(width, height)

# 사용 예시
door1 = DoorFactory.make_door(100, 200)
print(f&amp;quot;Width: {door1.get_width()}, Height: {door1.get_height()}&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;언제 사용하나요?&lt;/strong&gt;&lt;br&gt;단순 생성을 넘어 약간의 초기화 로직이나 설정 작업이 필요할 때, 해당 생성을 전용 팩토리 클래스로 묶어 코드 중복을 방지할 때 사용합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;  2. Factory Method (공장 메서드)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;총괄 채용 매니저 혼자 모든 직군의 면접을 진행하는 것은 불가능합니다. 면접 진행이라는 전체 큰 틀은 갖고 있되, &lt;strong&gt;구체적으로 어떤 면접관을 세울지는 각 분야(개발, 마케팅) 팀장에게 위임&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;객체 생성 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지는 &lt;strong&gt;서브클래스에 위임&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Developer {
  askQuestions() {
    console.log(&amp;#39;디자인 패턴에 대해 질문합니다!&amp;#39;);
  }
}

class CommunityExecutive {
  askQuestions() {
    console.log(&amp;#39;커뮤니티 빌딩에 대해 질문합니다!&amp;#39;);
  }
}

class HiringManager {
  // Factory Method (하위 클래스에서 구현)
  makeInterviewer() {
    throw new Error(&amp;#39;makeInterviewer() 메서드를 구현해야 합니다.&amp;#39;);
  }

  takeInterview() {
    const interviewer = this.makeInterviewer();
    interviewer.askQuestions();
  }
}

class DevelopmentManager extends HiringManager {
  makeInterviewer() {
    return new Developer();
  }
}

class MarketingManager extends HiringManager {
  makeInterviewer() {
    return new CommunityExecutive();
  }
}

// 사용 예시
const devManager = new DevelopmentManager();
devManager.takeInterview(); // 디자인 패턴에 대해 질문합니다!

const marketingManager = new MarketingManager();
marketingManager.takeInterview(); // 커뮤니티 빌딩에 대해 질문합니다!&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from abc import ABC, abstractmethod

class Interviewer(ABC):
    @abstractmethod
    def ask_questions(self): pass

class Developer(Interviewer):
    def ask_questions(self):
        print(&amp;quot;디자인 패턴에 대해 질문합니다!&amp;quot;)

class CommunityExecutive(Interviewer):
    def ask_questions(self):
        print(&amp;quot;커뮤니티 빌딩에 대해 질문합니다!&amp;quot;)

class HiringManager(ABC):
    @abstractmethod
    def make_interviewer(self) -&amp;gt; Interviewer: pass

    def take_interview(self):
        interviewer = self.make_interviewer()
        interviewer.ask_questions()

class DevelopmentManager(HiringManager):
    def make_interviewer(self) -&amp;gt; Interviewer:
        return Developer()

# 사용 예시
dev_manager = DevelopmentManager()
dev_manager.take_interview()&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;언제 사용하나요?&lt;/strong&gt;&lt;br&gt;상위 클래스가 자신이 생성해야 하는 객체의 구체적인 클래스를 미리 알 수 없을 때 유용합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;  3. Abstract Factory (추상 공장)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;목재 문에는 목수가 필요하고, 철제 문에는 용접공이 필요합니다. 문이라는 제품과 설치 기사라는 전문가 사이에 끈끈한 연관성/의존성이 존재할 때 이를 &lt;strong&gt;그룹 단위로 세트로 제공&lt;/strong&gt;하는 매장 개념입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;연관되거나 의존적인 객체들의 패밀리를 구체적인 클래스 지정 없이 생성할 수 있는 &lt;strong&gt;&amp;quot;공장들의 공장&amp;quot;&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class WoodenDoor {
  getDescription() { console.log(&amp;#39;목재 문입니다.&amp;#39;); }
}
class IronDoor {
  getDescription() { console.log(&amp;#39;철제 문입니다.&amp;#39;); }
}

class Carpenter {
  getDescription() { console.log(&amp;#39;목재 문 전문가입니다.&amp;#39;); }
}
class Welder {
  getDescription() { console.log(&amp;#39;철제 문 전문가입니다.&amp;#39;); }
}

// 추상 공장 역
class WoodenDoorFactory {
  makeDoor() { return new WoodenDoor(); }
  makeFittingExpert() { return new Carpenter(); }
}

class IronDoorFactory {
  makeDoor() { return new IronDoor(); }
  makeFittingExpert() { return new Welder(); }
}

// 사용 예시
const woodenFactory = new WoodenDoorFactory();
const door = woodenFactory.makeDoor();
const expert = woodenFactory.makeFittingExpert();

door.getDescription();   // 목재 문입니다.
expert.getDescription(); // 목재 문 전문가입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from abc import ABC, abstractmethod

class Door(ABC):
    @abstractmethod
    def get_description(self): pass

class WoodenDoor(Door):
    def get_description(self): print(&amp;quot;목재 문입니다.&amp;quot;)

class DoorFittingExpert(ABC):
    @abstractmethod
    def get_description(self): pass

class Carpenter(DoorFittingExpert):
    def get_description(self): print(&amp;quot;목재 문 전문가입니다.&amp;quot;)

class DoorFactory(ABC):
    @abstractmethod
    def make_door(self) -&amp;gt; Door: pass
    @abstractmethod
    def make_fitting_expert(self) -&amp;gt; DoorFittingExpert: pass

class WoodenDoorFactory(DoorFactory):
    def make_door(self) -&amp;gt; Door: return WoodenDoor()
    def make_fitting_expert(self) -&amp;gt; DoorFittingExpert: return Carpenter()

# 사용 예시
factory = WoodenDoorFactory()
door = factory.make_door()
expert = factory.make_fitting_expert()
door.get_description()
expert.get_description()&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;언제 사용하나요?&lt;/strong&gt;&lt;br&gt;연관된 제품군을 묶어서 생성해야 하고, 서로 호환되지 않는 제품들이 섞이는 것을 방지하고 싶을 때 사용합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;  4. Builder (빌더)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;패스트푸드점에서 &amp;quot;세트 하나 주세요&amp;quot;는 단순 생성(Simple Factory)이지만, 수제 샌드위치 매장에서 &lt;strong&gt;&amp;quot;빵은 아티잔, 치즈는 체다, 소스는 칠리 추가해주세요&amp;quot;&lt;/strong&gt;처럼 복잡한 옵션을 단계별로 조합하는 것이 빌더입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;복잡한 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 합니다 (망원경 생성자 안티패턴 방지).&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Burger {
  constructor(builder) {
    this.size = builder.size;
    this.cheese = builder.cheese || false;
    this.pepperoni = builder.pepperoni || false;
    this.lettuce = builder.lettuce || false;
    this.tomato = builder.tomato || false;
  }
}

class BurgerBuilder {
  constructor(size) {
    this.size = size;
  }

  addCheese() {
    this.cheese = true;
    return this;
  }

  addPepperoni() {
    this.pepperoni = true;
    return this;
  }

  addTomato() {
    this.tomato = true;
    return this;
  }

  build() {
    return new Burger(this);
  }
}

// 사용 예시 (메서드 체이닝)
const burger = new BurgerBuilder(14)
  .addPepperoni()
  .addCheese()
  .build();&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Burger:
    def __init__(self, builder):
        self.size = builder.size
        self.cheese = builder.cheese
        self.pepperoni = builder.pepperoni

class BurgerBuilder:
    def __init__(self, size: int):
        self.size = size
        self.cheese = False
        self.pepperoni = False

    def add_cheese(self):
        self.cheese = True
        return self

    def add_pepperoni(self):
        self.pepperoni = True
        return self

    def build(self) -&amp;gt; Burger:
        return Burger(self)

# 사용 예시
burger = BurgerBuilder(14).add_cheese().add_pepperoni().build()&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;언제 사용하나요?&lt;/strong&gt;&lt;br&gt;인자(Parameter)가 엄청 많거나 생성이 여러 단계로 나누어져 선택적 옵션이 많을 때 쓰면 코드가 훨씬 깔끔해집니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;  5. Prototype (프로토타입)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;복제 양 돌리를 생각해 보세요! 처음부터 하나하나 다시 키우고 설계하는 것보다 &lt;strong&gt;완성된 원본을 복제(Clone)&lt;/strong&gt;해서 살짝 수정하는 것이 훨씬 빠르고 비용이 적게 듭니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;기존 객체를 복제(Cloning)하여 새로운 객체를 생성하는 패턴입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Sheep {
  constructor(name, category = &amp;#39;Mountain Sheep&amp;#39;) {
    this.name = name;
    this.category = category;
  }

  setName(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }

  clone() {
    return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
  }
}

// 사용 예시
const original = new Sheep(&amp;#39;Jolly&amp;#39;);
const cloned = original.clone();
cloned.setName(&amp;#39;Dolly&amp;#39;);

console.log(original.getName()); // Jolly
console.log(cloned.getName());   // Dolly&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import copy

class Sheep:
    def __init__(self, name: str, category: str = &amp;quot;Mountain Sheep&amp;quot;):
        self.name = name
        self.category = category

    def clone(self):
        return copy.deepcopy(self)

# 사용 예시
original = Sheep(&amp;quot;Jolly&amp;quot;)
cloned = original.clone()
cloned.name = &amp;quot;Dolly&amp;quot;

print(original.name) # Jolly
print(cloned.name)   # Dolly&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  6. Singleton (싱글톤)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;한 나라에는 한 시점에 오직 &lt;strong&gt;단 한 명의 대통령&lt;/strong&gt;만 존재해야 합니다. 필요할 때마다 대통령을 새로 찍어낼 수는 없습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;클래스의 인스턴스가 애플리케이션 내에서 &lt;strong&gt;단 하나만 존재&lt;/strong&gt;하도록 보장하고, 어디서든 접근할 수 있는 전역 접점을 제공합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class President {
  constructor() {
    if (President.instance) {
      return President.instance;
    }
    this.name = &amp;#39;President&amp;#39;;
    President.instance = this;
  }
}

// 사용 예시
const p1 = new President();
const p2 = new President();

console.log(p1 === p2); // true&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class President:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# 사용 예시
p1 = President()
p2 = President()
print(p1 is p2) # True&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h1&gt;  구조 디자인 패턴 (Structural Design Patterns)&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 요약:&lt;/strong&gt; 객체들을 큰 구조로 합성하거나, 서로 호환되지 않는 인터페이스를 연결하여 &lt;strong&gt;효율적인 관계를 형성&lt;/strong&gt;하는 것에 관한 패턴입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;  1. Adapter (어댑터)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;SD 카드의 데이터를 노트북에 옮기고 싶은데 노트북에는 MicroSD 슬롯만 있다면? &lt;strong&gt;카드 리더기(어댑터)&lt;/strong&gt;를 끼워서 형태를 바꿔 통신합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;호환되지 않는 인터페이스를 가진 객체들이 함께 일할 수 있도록 &lt;strong&gt;중간에서 변환 역할을 감싸주는(Wrapper)&lt;/strong&gt; 패턴입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class AfricanLion {
  roar() {}
}

class Hunter {
  hunt(lion) {
    lion.roar();
  }
}

class WildDog {
  bark() {
    console.log(&amp;#39;Woof woof!&amp;#39;);
  }
}

// 어댑터 클래스
class WildDogAdapter {
  constructor(dog) {
    this.dog = dog;
  }

  roar() {
    this.dog.bark();
  }
}

// 사용 예시
const dog = new WildDog();
const adapter = new WildDogAdapter(dog);
const hunter = new Hunter();
hunter.hunt(adapter); // Woof woof!&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Lion:
    def roar(self): pass

class Hunter:
    def hunt(self, lion: Lion):
        lion.roar()

class WildDog:
    def bark(self):
        print(&amp;quot;Woof woof!&amp;quot;)

class WildDogAdapter(Lion):
    def __init__(self, dog: WildDog):
        self.dog = dog

    def roar(self):
        self.dog.bark()

# 사용 예시
dog = WildDog()
adapter = WildDogAdapter(dog)
hunter = Hunter()
hunter.hunt(adapter) # Woof woof!&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  2. Bridge (브리지)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;웹사이트에 &amp;#39;소개 페이지&amp;#39;, &amp;#39;홈 페이지&amp;#39;가 있고 각 페이지는 &amp;#39;다크 테마&amp;#39;, &amp;#39;라이트 테마&amp;#39;를 적용할 수 있습니다. 상속으로 만들면 2x2=4개의 클래스를 만들어야 하지만, &lt;strong&gt;페이지(추상화)와 테마(구현)를 브리지로 분리하여 합성&lt;/strong&gt;하면 클래스가 2+2=4개로 확장성이 훨씬 좋아집니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;추상화(Abstraction)와 구현(Implementation)을 분리&lt;/strong&gt;하여 두 계층이 독립적으로 변형될 수 있도록 만드는 패턴입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class DarkTheme {
  getColor() { return &amp;#39;Dark Black&amp;#39;; }
}

class LightTheme {
  getColor() { return &amp;#39;Off White&amp;#39;; }
}

class AboutPage {
  constructor(theme) {
    this.theme = theme;
  }

  getContent() {
    return `About page in ${this.theme.getColor()}`;
  }
}

// 사용 예시
const darkPage = new AboutPage(new DarkTheme());
console.log(darkPage.getContent()); // About page in Dark Black&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class DarkTheme:
    def get_color(self): return &amp;quot;Dark Black&amp;quot;

class AboutPage:
    def __init__(self, theme):
        self.theme = theme

    def get_content(self):
        return f&amp;quot;About page in {self.theme.get_color()}&amp;quot;

# 사용 예시
page = AboutPage(DarkTheme())
print(page.get_content())&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  3. Composite (컴포지트)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;회사는 개별 개발자(단일 객체)들로 구성되기도 하고, 개발팀(객체 그룹)으로 구성되기도 합니다. 대표님이 &amp;quot;이번 달 급여 총액 얼마야?&amp;quot;라고 물었을 때 &lt;strong&gt;단일 직원이나 팀 전체나 똑같이 &lt;code&gt;getSalary()&lt;/code&gt;로 일관되게 조회&lt;/strong&gt;할 수 있어야 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;객체들을 &lt;strong&gt;트리 구조&lt;/strong&gt;로 구성하여 개별 객체와 복합 객체를 클라이언트 입장에서 동일하게 다룰 수 있게 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Developer {
  constructor(name, salary) {
    this.name = name;
    this.salary = salary;
  }

  getSalary() {
    return this.salary;
  }
}

class Organization {
  constructor() {
    this.employees = [];
  }

  addEmployee(employee) {
    this.employees.push(employee);
  }

  getSalary() {
    return this.employees.reduce((total, e) =&amp;gt; total + e.getSalary(), 0);
  }
}

// 사용 예시
const org = new Organization();
org.addEmployee(new Developer(&amp;#39;John&amp;#39;, 1000));
org.addEmployee(new Developer(&amp;#39;Jane&amp;#39;, 1200));

console.log(&amp;#39;Total Salary:&amp;#39;, org.getSalary()); // 2200&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from abc import ABC, abstractmethod

class Employee(ABC):
    @abstractmethod
    def get_salary(self) -&amp;gt; float: pass

class Developer(Employee):
    def __init__(self, name: str, salary: float):
        self.name = name
        self.salary = salary

    def get_salary(self) -&amp;gt; float: return self.salary

class Organization(Employee):
    def __init__(self):
        self.employees = []

    def add_employee(self, employee: Employee):
        self.employees.append(employee)

    def get_salary(self) -&amp;gt; float:
        return sum(e.get_salary() for e in self.employees)

# 사용 예시
org = Organization()
org.add_employee(Developer(&amp;quot;John&amp;quot;, 1000))
org.add_employee(Developer(&amp;quot;Jane&amp;quot;, 1200))
print(&amp;quot;Total:&amp;quot;, org.get_salary())&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;☕ 4. Decorator (데코레이터)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;기본 에스프레소 커피에 우유 토핑을 얹으면 라떼가 되고, 휘핑크림을 또 얹으면 모카가 됩니다. 기본 제품의 틀을 변경하지 않고 &lt;strong&gt;필요한 옵션을 겉에 겹겹이 포장(감싸서) 추가&lt;/strong&gt;하는 느낌입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;객체에 동적으로 새로운 책임(기능)을 추가하는 패턴으로, &lt;strong&gt;서브클래싱(상속)의 유연한 대안&lt;/strong&gt;을 제공합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class SimpleCoffee {
  getCost() { return 10; }
  getDescription() { return &amp;#39;Simple coffee&amp;#39;; }
}

class MilkCoffee {
  constructor(coffee) {
    this.coffee = coffee;
  }

  getCost() {
    return this.coffee.getCost() + 2;
  }

  getDescription() {
    return `${this.coffee.getDescription()}, milk`;
  }
}

// 사용 예시
let coffee = new SimpleCoffee();
coffee = new MilkCoffee(coffee);
console.log(coffee.getCost()); // 12
console.log(coffee.getDescription()); // Simple coffee, milk&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Coffee:
    def get_cost(self) -&amp;gt; float: return 10.0
    def get_description(self) -&amp;gt; str: return &amp;quot;Simple coffee&amp;quot;

class MilkCoffee:
    def __init__(self, coffee: Coffee):
        self._coffee = coffee
    def get_cost(self) -&amp;gt; float: return self._coffee.get_cost() + 2.0
    def get_description(self) -&amp;gt; str: return self._coffee.get_description() + &amp;quot;, milk&amp;quot;

# 사용 예시
coffee = SimpleCoffee()
coffee_with_milk = MilkCoffee(coffee)
print(coffee_with_milk.get_cost()) # 12.0&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  5. Facade (퍼사드)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;컴퓨터를 켤 때 메인보드 전원 공급, CPU 클럭 가동, 램 전압 체크 등 복잡한 과정을 일일이 조작하지 않습니다. 그저 &lt;strong&gt;전원 버튼 하나(Facade)&lt;/strong&gt;만 누르면 내부 복잡한 서브시스템이 일괄 실행됩니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;서브시스템의 복잡한 인터페이스 집합에 대해 &lt;strong&gt;통합된 단순한 고수준 인터페이스를 제공&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Computer {
  getElectricShock() { console.log(&amp;#39;Ouch!&amp;#39;); }
  makeSound() { console.log(&amp;#39;Beep beep!&amp;#39;); }
  showLoadingScreen() { console.log(&amp;#39;Loading..&amp;#39;); }
}

class ComputerFacade {
  constructor(computer) {
    this.computer = computer;
  }

  turnOn() {
    this.computer.getElectricShock();
    this.computer.makeSound();
    this.computer.showLoadingScreen();
  }
}

// 사용 예시
const facade = new ComputerFacade(new Computer());
facade.turnOn();&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Computer:
    def get_electric_shock(self): print(&amp;quot;Ouch!&amp;quot;)
    def make_sound(self): print(&amp;quot;Beep beep!&amp;quot;)
    def show_loading_screen(self): print(&amp;quot;Loading..&amp;quot;)

class ComputerFacade:
    def __init__(self, computer: Computer):
        self.computer = computer

    def turn_on(self):
        self.computer.get_electric_shock()
        self.computer.make_sound()
        self.computer.show_loading_screen()

# 사용 예시
facade = ComputerFacade(Computer())
facade.turn_on()&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  6. Flyweight (플라이웨이트)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;찻집에서 손님이 100명 왔을 때 매번 완전히 새로운 찻잔 객체를 100개 찍어내기보다, &lt;strong&gt;동일한 종류의 차(상태가 같은 객체)는 재사용하여 메모리와 자원을 아끼는 방식&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;많은 수의 유사한 객체를 효율적으로 공유하여 &lt;strong&gt;메모리 사용량을 절감&lt;/strong&gt;시키는 패턴입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class KarakTea {}

class TeaMaker {
  constructor() {
    this.availableTea = {};
  }

  make(preference) {
    if (!this.availableTea[preference]) {
      this.availableTea[preference] = new KarakTea();
    }
    return this.availableTea[preference];
  }
}

// 사용 예시
const maker = new TeaMaker();
const tea1 = maker.make(&amp;#39;less sugar&amp;#39;);
const tea2 = maker.make(&amp;#39;less sugar&amp;#39;);

console.log(tea1 === tea2); // true (객체 공유)&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class KarakTea:
    pass

class TeaMaker:
    def __init__(self):
        self._available_tea = {}

    def make(self, preference: str) -&amp;gt; KarakTea:
        if preference not in self._available_tea:
            self._available_tea[preference] = KarakTea()
        return self._available_tea[preference]

# 사용 예시
maker = TeaMaker()
tea1 = maker.make(&amp;quot;less sugar&amp;quot;)
tea2 = maker.make(&amp;quot;less sugar&amp;quot;)
print(tea1 is tea2) # True&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  7. Proxy (프록시)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;연구실 문을 그냥 열 수도 있지만, &lt;strong&gt;보안 출입 카드키(Proxy)&lt;/strong&gt;를 거쳐야만 문이 열리도록 출입 통제 기능을 대리하는 것입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;다른 객체에 대한 &lt;strong&gt;접근을 제어하거나 대리자 역할&lt;/strong&gt;을 수행하는 객체를 제공합니다 (보안, 캐싱, 지연 로딩 등).&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class LabDoor {
  open() { console.log(&amp;#39;Opening lab door&amp;#39;); }
  close() { console.log(&amp;#39;Closing lab door&amp;#39;); }
}

class SecuredDoor {
  constructor(door) {
    this.door = door;
  }

  open(password) {
    if (password === &amp;#39;secret&amp;#39;) {
      this.door.open();
    } else {
      console.log(&amp;#39;Access denied!&amp;#39;);
    }
  }

  close() {
    this.door.close();
  }
}

// 사용 예시
const door = new SecuredDoor(new LabDoor());
door.open(&amp;#39;wrong&amp;#39;);  // Access denied!
door.open(&amp;#39;secret&amp;#39;); // Opening lab door&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class LabDoor:
    def open(self): print(&amp;quot;Opening lab door&amp;quot;)

class SecuredDoor:
    def __init__(self, door: LabDoor):
        self.door = door

    def open(self, password: str):
        if password == &amp;quot;secret&amp;quot;:
            self.door.open()
        else:
            print(&amp;quot;Access denied!&amp;quot;)

# 사용 예시
door = SecuredDoor(LabDoor())
door.open(&amp;quot;wrong&amp;quot;)  # Access denied!
door.open(&amp;quot;secret&amp;quot;) # Opening lab door&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h1&gt;  행동 디자인 패턴 (Behavioral Design Patterns)&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 요약:&lt;/strong&gt; 객체 간의 &lt;strong&gt;책임 분배, 알고리즘 제어 흐름, 그리고 통신 방식&lt;/strong&gt;을 다루는 패턴입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;  1. Chain of Responsibility (책임 사슬)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;결제 시스템에서 먼저 &lt;strong&gt;계좌 A(잔액 부족) ➔ 카드 B(잔액 부족) ➔ 카카오페이 C(성공)&lt;/strong&gt;로 순차적으로 처리 권한을 넘겨가며 결제를 완료하는 구조입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;요청을 처리할 수 있는 객체들을 사슬(Chain) 형태로 연결하여, &lt;strong&gt;요청이 처리될 때까지 사슬을 따라 전달&lt;/strong&gt;시킵니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Account {
  setNext(account) {
    this.successor = account;
  }

  pay(amountToPay) {
    if (this.canPay(amountToPay)) {
      console.log(`Paid ${amountToPay} using ${this.name}`);
    } else if (this.successor) {
      console.log(`Cannot pay using ${this.name}. Proceeding..`);
      this.successor.pay(amountToPay);
    } else {
      console.log(&amp;#39;None of the accounts have enough balance&amp;#39;);
    }
  }

  canPay(amount) {
    return this.balance &amp;gt;= amount;
  }
}

class Bank extends Account {
  constructor(balance) {
    super();
    this.name = &amp;#39;Bank&amp;#39;;
    this.balance = balance;
  }
}

class Paypal extends Account {
  constructor(balance) {
    super();
    this.name = &amp;#39;Paypal&amp;#39;;
    this.balance = balance;
  }
}

// 사용 예시
const bank = new Bank(100);
const paypal = new Paypal(200);

bank.setNext(paypal);
bank.pay(150); // Paid 150 using Paypal&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Account:
    def __init__(self, balance: float):
        self.balance = balance
        self.next_account = None

    def set_next(self, account):
        self.next_account = account

    def pay(self, amount: float):
        if self.balance &amp;gt;= amount:
            print(f&amp;quot;Paid {amount} using {self.__class__.__name__}&amp;quot;)
        elif self.next_account:
            self.next_account.pay(amount)
        else:
            print(&amp;quot;Cannot pay!&amp;quot;)

# 사용 예시
bank = Account(100)
paypal = Account(200)
bank.set_next(paypal)
bank.pay(150) # Paid 150 using Paypal&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  2. Command (커맨드)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;손님이 주문서(Command)를 작성해서 웨이터(Invoker)에게 주면, 웨이터는 주문 내용을 신경 쓰지 않고 요리사(Receiver)에게 그대로 전달만 합니다. &lt;strong&gt;요청 자체가 독립된 객체&lt;/strong&gt;가 됩니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;요청 자체를 &lt;strong&gt;독립적인 객체로 캡슐화&lt;/strong&gt;하여 요청 매개변수화, 작업 취소(Undo), 큐 저장 등을 가능하게 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Bulb {
  turnOn() { console.log(&amp;#39;Light on&amp;#39;); }
  turnOff() { console.log(&amp;#39;Light off&amp;#39;); }
}

class TurnOnCommand {
  constructor(bulb) {
    this.bulb = bulb;
  }

  execute() {
    this.bulb.turnOn();
  }
}

class RemoteControl {
  submit(command) {
    command.execute();
  }
}

// 사용 예시
const bulb = new Bulb();
const cmd = new TurnOnCommand(bulb);
const remote = new RemoteControl();
remote.submit(cmd); // Light on&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Bulb:
    def turn_on(self): print(&amp;quot;Light on&amp;quot;)
    def turn_off(self): print(&amp;quot;Light off&amp;quot;)

class TurnOnCommand:
    def __init__(self, bulb: Bulb): self.bulb = bulb
    def execute(self): self.bulb.turn_on()

class RemoteControl:
    def submit(self, command): command.execute()

# 사용 예시
bulb = Bulb()
cmd = TurnOnCommand(bulb)
remote = RemoteControl()
remote.submit(cmd) # Light on&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;➿ 3. Iterator (반복자)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;라디오의 &amp;#39;다음 채널&amp;#39; 버튼을 누르면 내부 회로 구조가 복잡하게 어찌 되어 있든 &lt;strong&gt;단순히 순서대로 다음 채널로 넘어가서 청취&lt;/strong&gt;할 수 있습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;컬렉션의 내부 표현 방식을 노출하지 않고 &lt;strong&gt;그 안의 요소들에 순차적으로 접근&lt;/strong&gt;할 수 있는 방법을 제공합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class StationList {
  constructor() {
    this.stations = [];
  }

  addStation(freq) {
    this.stations.push(freq);
  }

  // Symbol.iterator 구현
  *[Symbol.iterator]() {
    for (const station of this.stations) {
      yield station;
    }
  }
}

// 사용 예시
const stations = new StationList();
stations.addStation(89.0);
stations.addStation(101.5);

for (const station of stations) {
  console.log(station);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class StationList:
    def __init__(self):
        self.stations = []

    def add_station(self, freq: float):
        self.stations.append(freq)

    def __iter__(self):
        return iter(self.stations)

# 사용 예시
stations = StationList()
stations.add_station(89.0)
stations.add_station(101.5)
for s in stations:
    print(s)&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  4. Mediator (중재자)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;채팅방(Mediator)이 존재하여 모든 유저가 서로 1:1로 직접 연결될 필요 없이 &lt;strong&gt;채팅방 서버 하나만을 통해 메시지를 송수신&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;객체 간의 복잡한 상호작용을 &lt;strong&gt;중재자 객체에 캡슐화&lt;/strong&gt;하여 객체 간의 결합도(Coupling)를 획기적으로 낮춥니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class ChatRoom {
  showMessage(user, message) {
    console.log(`[${user.getName()}]: ${message}`);
  }
}

class User {
  constructor(name, chatMediator) {
    this.name = name;
    this.chatMediator = chatMediator;
  }

  getName() {
    return this.name;
  }

  send(message) {
    this.chatMediator.showMessage(this, message);
  }
}

// 사용 예시
const mediator = new ChatRoom();
const john = new User(&amp;#39;John&amp;#39;, mediator);
john.send(&amp;#39;Hello!&amp;#39;); // [John]: Hello!&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class ChatRoom:
    def show_message(self, user, message: str):
        print(f&amp;quot;[{user.name}]: {message}&amp;quot;)

class User:
    def __init__(self, name: str, mediator: ChatRoom):
        self.name = name
        self.mediator = mediator

    def send(self, message: str):
        self.mediator.show_message(self, message)

# 사용 예시
chat = ChatRoom()
john = User(&amp;quot;John&amp;quot;, chat)
john.send(&amp;quot;Hello!&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  5. Memento (메멘토)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;텍스트 에디터에서 글을 쓰다가 &lt;strong&gt;&lt;code&gt;Ctrl + Z&lt;/code&gt; (Undo)&lt;/strong&gt;를 누르면 이전 저장 시점(스냅샷)으로 글의 상태가 롤백됩니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;캡슐화를 저해하지 않으면서 &lt;strong&gt;객체의 내부 상태를 스냅샷으로 저장하여 나중에 복원&lt;/strong&gt;할 수 있게 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Memento {
  constructor(content) {
    this.content = content;
  }

  getContent() {
    return this.content;
  }
}

class Editor {
  constructor() {
    this.content = &amp;#39;&amp;#39;;
  }

  type(text) {
    this.content += text;
  }

  save() {
    return new Memento(this.content);
  }

  restore(memento) {
    this.content = memento.getContent();
  }
}

// 사용 예시
const editor = new Editor();
editor.type(&amp;#39;Hello World&amp;#39;);
const saved = editor.save();

editor.type(&amp;#39; Extra text&amp;#39;);
editor.restore(saved);
console.log(editor.content); // Hello World&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Memento:
    def __init__(self, state: str): self._state = state
    def get_state(self): return self._state

class Editor:
    def __init__(self): self.content = &amp;quot;&amp;quot;
    def type(self, text: str): self.content += text
    def save(self): return Memento(self.content)
    def restore(self, memento: Memento): self.content = memento.get_state()

# 사용 예시
editor = Editor()
editor.type(&amp;quot;Hello World&amp;quot;)
saved = editor.save()
editor.type(&amp;quot; Extra text&amp;quot;)
editor.restore(saved)
print(editor.content) # Hello World&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  6. Observer (옵저버)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;YouTube 채널 구독과 같습니다. 채널에 새 영상이 업로드(상태 변경)되면 &lt;strong&gt;구독(Subscriber/Observer) 중인 시청자들에게 알림&lt;/strong&gt;이 자동으로 보내집니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;객체의 상태 변화가 있을 때 이를 구독하고 있는 &lt;strong&gt;관찰자들에게 자동으로 알림을 전달&lt;/strong&gt;하는 일대다 의존 관계 패턴입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class JobSeeker {
  constructor(name) {
    this.name = name;
  }

  notify(jobTitle) {
    console.log(`Hi ${this.name}, new job posted: ${jobTitle}`);
  }
}

class EmploymentAgency {
  constructor() {
    this.observers = [];
  }

  attach(observer) {
    this.observers.push(observer);
  }

  addJob(jobTitle) {
    this.observers.forEach(obs =&amp;gt; obs.notify(jobTitle));
  }
}

// 사용 예시
const agency = new EmploymentAgency();
agency.attach(new JobSeeker(&amp;#39;Alice&amp;#39;));
agency.addJob(&amp;#39;Backend Developer&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class JobSeeker:
    def __init__(self, name: str): self.name = name
    def notify(self, job_title: str): print(f&amp;quot;Hi {self.name}, new job: {job_title}&amp;quot;)

class EmploymentAgency:
    def __init__(self): self.observers = []
    def attach(self, observer: JobSeeker): self.observers.append(observer)
    def add_job(self, title: str):
        for obs in self.observers: obs.notify(title)

# 사용 예시
agency = EmploymentAgency()
agency.attach(JobSeeker(&amp;quot;Alice&amp;quot;))
agency.add_job(&amp;quot;Backend Developer&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  7. Visitor (방문자)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;관광객(Visitor)이 해외 국가를 방문할 때 현지 법이나 규칙을 바꾸지 않고도 &lt;strong&gt;관광객 자신의 고유한 동작(사진 찍기, 쇼핑하기 등)을 수행&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;객체 구조의 클래스 코드를 변경하지 않고 &lt;strong&gt;새로운 연산/기능을 외부 방문자 클래스에 추가&lt;/strong&gt;할 수 있게 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Monkey {
  shout() {
    console.log(&amp;#39;Ooh oo aa aa!&amp;#39;);
  }

  accept(operation) {
    operation.visitMonkey(this);
  }
}

class SpeakOperation {
  visitMonkey(monkey) {
    monkey.shout();
  }
}

// 사용 예시
const monkey = new Monkey();
const speak = new SpeakOperation();
monkey.accept(speak); // Ooh oo aa aa!&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Monkey:
    def shout(self): print(&amp;quot;Ooh oo aa aa!&amp;quot;)
    def accept(self, operation): operation.visit_monkey(self)

class SpeakOperation:
    def visit_monkey(self, monkey: Monkey): monkey.shout()

# 사용 예시
monkey = Monkey()
speak = SpeakOperation()
monkey.accept(speak)&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  8. Strategy (전략)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;정렬할 데이터 양에 따라 &lt;strong&gt;작은 데이터는 버블 정렬, 큰 데이터는 퀵 정렬&lt;/strong&gt;로 알고리즘 전략을 런타임에 유연하게 변경합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;동일 계열의 &lt;strong&gt;알고리즘들을 개별 클래스로 캡슐화&lt;/strong&gt;하여 런타임에 동적으로 교체해서 사용할 수 있게 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class BubbleSort {
  sort(dataset) {
    console.log(&amp;#39;Bubble sort applied&amp;#39;);
    return dataset.sort();
  }
}

class QuickSort {
  sort(dataset) {
    console.log(&amp;#39;Quick sort applied&amp;#39;);
    return dataset.sort();
  }
}

class Sorter {
  constructor(strategy) {
    this.strategy = strategy;
  }

  sort(dataset) {
    return this.strategy.sort(dataset);
  }
}

// 사용 예시
const sorter = new Sorter(new BubbleSort());
sorter.sort([3, 1, 2]);&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class BubbleSort:
    def sort(self, data):
        print(&amp;quot;Bubble sort applied&amp;quot;)
        return sorted(data)

class QuickSort:
    def sort(self, data):
        print(&amp;quot;Quick sort applied&amp;quot;)
        return sorted(data)

class Sorter:
    def __init__(self, strategy): self.strategy = strategy
    def sort(self, data): return self.strategy.sort(data)

# 사용 예시
sorter = Sorter(BubbleSort())
sorter.sort([3, 1, 2])&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  9. State (상태)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;스마트폰에서 똑같이 화면을 누르더라도 &lt;strong&gt;잠금 상태, 통화 상태, 카메라 작동 상태&lt;/strong&gt;에 따라 각각 완전히 다른 동작을 수행하는 것과 같습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;객체의 &lt;strong&gt;내부 상태 변경에 따라 객체의 행동을 변경&lt;/strong&gt;하여, 마치 객체의 클래스가 바뀐 것처럼 동작하게 만듭니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class StateIdle {
  pickUp() {
    console.log(&amp;#39;Picked up&amp;#39;);
    return new StatePickedUp();
  }
}

class StatePickedUp {
  pickUp() {
    console.log(&amp;#39;Already picked up&amp;#39;);
    return this;
  }
}

class Phone {
  constructor() {
    this.state = new StateIdle();
  }

  pickUp() {
    this.state = this.state.pickUp();
  }
}

// 사용 예시
const phone = new Phone();
phone.pickUp(); // Picked up
phone.pickUp(); // Already picked up&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class StateIdle:
    def pick_up(self):
        print(&amp;quot;Picked up&amp;quot;)
        return StatePickedUp()

class StatePickedUp:
    def pick_up(self):
        print(&amp;quot;Already picked up&amp;quot;)
        return self

class Phone:
    def __init__(self): self.state = StateIdle()
    def pick_up(self): self.state = self.state.pick_up()

# 사용 예시
phone = Phone()
phone.pick_up() # Picked up&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h3&gt;  10. Template Method (템플릿 메서드)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;현실 예시:&lt;/strong&gt;&lt;br&gt;건축 공정에서 &lt;strong&gt;&lt;code&gt;기초 공사 ➔ 벽 세우기 ➔ 지붕 얹기&lt;/code&gt;의 큰 틀(스켈레톤)&lt;/strong&gt;은 일정하지만, 벽을 콘크리트로 할지 목재로 할지의 개별 세부 단계는 상황에 맞춰 다르게 구현합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;한 줄 정의:&lt;/strong&gt;&lt;br&gt;알고리즘의 &lt;strong&gt;골격(뼈대)을 상위 클래스의 메서드에 정의&lt;/strong&gt;하고, 일부 세부 단계는 하위 클래스에서 오버라이딩하도록 미룹니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;  프로그래밍 예제&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;JavaScript (Node.js) 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Builder {
  // 템플릿 메서드
  build() {
    this.test();
    this.assemble();
  }

  test() {
    throw new Error(&amp;#39;test() 메서드를 구현해야 합니다.&amp;#39;);
  }

  assemble() {
    throw new Error(&amp;#39;assemble() 메서드를 구현해야 합니다.&amp;#39;);
  }
}

class AndroidBuilder extends Builder {
  test() {
    console.log(&amp;#39;Testing Android code&amp;#39;);
  }

  assemble() {
    console.log(&amp;#39;Assembling APK&amp;#39;);
  }
}

// 사용 예시
const builder = new AndroidBuilder();
builder.build();&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;&lt;b&gt;Python 예제 보기&lt;/b&gt;&lt;/summary&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from abc import ABC, abstractmethod

class Builder(ABC):
    # Template method
    def build(self):
        self.test()
        self.assemble()

    @abstractmethod
    def test(self): pass
    @abstractmethod
    def assemble(self): pass

class AndroidBuilder(Builder):
    def test(self): print(&amp;quot;Testing Android code&amp;quot;)
    def assemble(self): print(&amp;quot;Assembling APK&amp;quot;)

# 사용 예시
builder = AndroidBuilder()
builder.build()&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h2&gt;  마무리하며 (Wrap-Up)&lt;/h2&gt;
&lt;p&gt;이것으로 핵심 23가지 디자인 패턴 정리를 마칩니다!&lt;br&gt;디자인 패턴은 코드를 무조건 복잡하게 만드는 도구가 아니라, &lt;strong&gt;팀원 간의 공통 언어를 형성하고 유지보수성을 극대화&lt;/strong&gt;하기 위한 약속입니다. 적재적소에 유연하게 활용해 보세요!&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  기여 및 피드백 (Contribution)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;  이슈 제보 (Report issues)&lt;/li&gt;
&lt;li&gt;  개선 푸시 리퀘스트 (Open Pull Request)&lt;/li&gt;
&lt;li&gt;  주변 공유 및 전달 (Spread the word)&lt;/li&gt;
&lt;li&gt;  작성자 인사하기: &lt;a href=&quot;https://twitter.com/kamrify&quot;&gt;&lt;img src=&quot;https://img.shields.io/twitter/url/https/twitter.com/kamrify.svg?style=social&amp;label=Follow%20%40kamrify&quot; alt=&quot;Twitter URL&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  라이선스 (License)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://creativecommons.org/licenses/by/4.0/&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/License-CC%20BY%204.0-lightgrey.svg&quot; alt=&quot;License: CC BY 4.0&quot;&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발상식</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1799</guid>
      <comments>https://javaexpert.tistory.com/1799#entry1799comment</comments>
      <pubDate>Wed, 5 Aug 2026 14:41:11 +0900</pubDate>
    </item>
    <item>
      <title>AI 네이티브 앱 제작 프로세스</title>
      <link>https://javaexpert.tistory.com/1798</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;1821&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ1CpM/dJMcadXfVET/ISFk7mTHFesmMY83yT89W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ1CpM/dJMcadXfVET/ISFk7mTHFesmMY83yT89W1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ1CpM/dJMcadXfVET/ISFk7mTHFesmMY83yT89W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ1CpM%2FdJMcadXfVET%2FISFk7mTHFesmMY83yT89W1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;864&quot; height=&quot;1821&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;1821&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;사용자 문제 정의부터 디자인, 통합 명세, 구현, 검증까지 연결하는 실무 가이드&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;1. 문서 개요&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;목적&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 문서는 AI를 활용해 앱을 만들 때 곧바로 코드를 작성하지 않고, 사용자 문제와 제품 기준을 먼저 구체화하는 작업 절차를 설명한다. 독자는 이 절차를 따라 다음 산출물을 순서대로 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;사용자 문제 정의&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;design.md&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;핵심 화면 시안&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;통합 기획서 &lt;/span&gt;&lt;span&gt;spec.html&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;전체 화면 목업 &lt;/span&gt;&lt;span&gt;design.html&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;구현된 애플리케이션&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;완료 상태는 제품 요구사항, 디자인, 데이터 구조, 구현 코드가 서로 일치하고 핵심 사용자 흐름을 실행할 수 있는 상태다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;대상 독자&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;AI 코딩 도구로 웹 또는 모바일 앱을 만들려는 기획자와 개발자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;제품 아이디어를 실제 서비스 명세로 구체화하려는 1인 개발자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;AI가 생성한 화면과 코드를 검수해야 하는 제품 책임자&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;범위&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 문서는 제품 기획과 제작 순서를 다룬다. 특정 AI 도구의 설치 방법, 명령어, 요금, 배포 환경 설정은 다루지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;미검증 범위:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 이 문서는 사용자가 제공한 영상 요약을 기술 문서 형식으로 재구성한 것이다. 영상 원본, 도구의 현재 기능, 실제 구현 결과는 별도로 검증하지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;2. 핵심 원칙&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AI 네이티브 앱 제작의 핵심은 코딩 속도가 아니라 &lt;/span&gt;&lt;b&gt;&lt;span&gt;AI가 판단할 수 있는 기준을 먼저 제공하는 것&lt;/span&gt;&lt;/b&gt;&lt;span&gt;이다. 전체 흐름은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;아이디어
&amp;rarr; 사용자 문제 정의
&amp;rarr; 디자인 원칙 수립
&amp;rarr; 핵심 화면 탐색
&amp;rarr; 제품&amp;middot;디자인&amp;middot;기술 명세 통합
&amp;rarr; 전체 화면과 상태 설계
&amp;rarr; 구현 전 질의응답
&amp;rarr; 실제 개발
&amp;rarr; 검증과 반복 수정
&amp;rarr; 명세&amp;middot;디자인&amp;middot;코드 동기화
&amp;rarr; 배포&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 과정에서 AI는 여러 대안을 빠르게 생성한다. 사람은 해결할 문제, 디자인의 취향, 기능의 우선순위, 데이터 구조와 최종 품질 기준을 결정한다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;3. 예제 서비스: TasteMaker&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;TasteMaker는 영화, TV 프로그램, 게임처럼 여러 서비스에 흩어진 개인 취향을 한 페이지에 정리하고 공유하는 프로필 서비스다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;사용자 문제&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;영화, TV 프로그램, 게임 기록이 서로 다른 서비스에 나뉘어 있어 사용자가 자신의 취향 전체를 한 페이지로 보여주기 어렵다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;주요 기능&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;좋아하는 영화&amp;middot;TV 프로그램&amp;middot;게임 표시&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;최근 리뷰와 별점 표시&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사용자가 만든 콘텐츠 목록 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공개 프로필 공유&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;회원가입과 온보딩&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;콘텐츠 추가&amp;middot;수정&amp;middot;정렬&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 예제는 이후 단계에서 어떤 산출물을 만들어야 하는지 설명하기 위한 기준으로 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;4. 단계별 제작 절차&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;4.1 사용자 문제 정의&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;목표&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기능 목록을 작성하기 전에 제품이 해결할 사용자 문제를 한 문장으로 정의한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;작성 항목&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;이 제품을 사용할 사람은 누구인가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사용자는 현재 어떤 상황에서 불편을 겪는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;기존 해결 방법은 왜 충분하지 않은가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;제품을 사용한 뒤 무엇이 달라져야 하는가?&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사업화를 전제로 한다면 다음 항목도 조사한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;실제로 같은 문제를 겪는 사용자가 있는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;직접 또는 간접 경쟁 서비스는 무엇인가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사용자가 비용을 지불할 가능성이 있는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;지속 가능한 수익 모델을 구성할 수 있는가?&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;개인용 앱이나 내부 도구는 시장성 조사를 줄일 수 있지만, 사용자와 완료 상태는 명확히 정의해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;산출물 예시&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;[대상 사용자]는 [현재 상황]에서 [문제]를 겪는다.
기존에는 [대체 방법]을 사용하지만 [한계]가 있다.
이 제품은 [핵심 가치]를 제공해 [기대 결과]를 만든다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;완료 기준&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;특정 기능명을 사용하지 않고도 문제를 설명할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;대상 사용자와 사용 상황이 드러난다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;제품이 제공할 변화가 한 문장으로 정리된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사업용 제품이라면 경쟁 서비스와 지불 가능성을 조사할 계획이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;4.2 디자인 레퍼런스 수집과 &lt;/span&gt;&lt;span&gt;design.md&lt;/span&gt;&lt;span&gt; 작성&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;목표&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;ldquo;예쁘게 만들어 달라&amp;rdquo;는 추상적인 요청을 시각적 기준과 구현 가능한 디자인 규칙으로 바꾼다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;절차&lt;/span&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;Mobbin, Dribbble, 실제 앱 화면 등에서 레퍼런스를 수집한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각 레퍼런스에서 채택할 요소와 제외할 요소를 구분한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;제품의 콘텐츠와 사용 맥락에 맞는 디자인 원칙을 정한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;결정한 내용을 &lt;/span&gt;&lt;span&gt;design.md&lt;/span&gt;&lt;span&gt;에 기록한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;레퍼런스는 방향을 찾는 자료로만 사용하고 화면을 그대로 복제하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;design.md&lt;/span&gt;&lt;span&gt; 권장 구조&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# Design Direction

## Design Principles
- 콘텐츠가 인터페이스보다 먼저 보이게 한다.
- 장식 요소는 사용자의 판단을 방해하지 않는 범위에서 사용한다.

## Color
- 배경색:
- 기본 텍스트:
- 보조 텍스트:
- 강조색:
- 상태 색상:

## Typography
- 제목:
- 본문:
- 보조 정보:

## Spacing and Layout
- 기본 간격 단위:
- 최대 콘텐츠 너비:
- 카드 간격:

## Components
- 버튼:
- 카드:
- 입력창:
- 모달 및 시트:

## Content Density
- 한 화면에 노출할 정보량:
- 모바일 축약 규칙:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;TasteMaker의 디자인 방향은 인터페이스를 조용하고 단순하게 유지하고, 영화와 게임의 커버 이미지가 화면의 주요 색상을 담당하게 하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;완료 기준&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;색상, 타이포그래피, 간격, 카드와 버튼 규칙이 정의되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;레퍼런스에서 가져올 원칙과 복제하지 않을 요소가 구분되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;AI가 서로 다른 화면에서도 같은 시각 언어를 적용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;4.3 핵심 화면 탐색&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;목표&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;전체 화면을 만들기 전에 제품의 가치와 디자인 방향을 가장 잘 보여주는 화면 두 개를 설계한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;TasteMaker에서는 다음 화면을 먼저 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;비로그인 사용자가 보는 랜딩 페이지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;영화&amp;middot;TV 프로그램&amp;middot;게임 취향을 보여주는 공개 프로필&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;절차&lt;/span&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;design.md&lt;/span&gt;&lt;span&gt;와 사용자 문제를 AI 디자인 도구에 제공한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;서로 다른 레이아웃과 테마의 시안을 요청한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각 시안을 콘텐츠 우선순위와 사용성 기준으로 비교한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;적합한 방향을 하나 선택한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;불필요한 요소를 제거하고 배치, 밀도, 문구를 반복 수정한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;시안 변형 예시는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;행 중심 레이아웃&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;그리드 중심 에디토리얼 레이아웃&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;라이트 모드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;다크 모드&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;피드백 작성 원칙&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;ldquo;더 예쁘게&amp;rdquo;처럼 평가 기준이 없는 표현보다 대상과 변경 내용을 함께 적는다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;[대상 화면 또는 컴포넌트]에서
[현재 문제]가 있으므로
[구체적인 변경]을 적용한다.
[변경 후 확인할 기준]은 다음과 같다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;사용하지 않는 팔로우 버튼을 삭제한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;즐겨찾기 콘텐츠를 한 행에 6개 배치한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;콘텐츠 행에 좌우 이동 버튼을 추가한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리뷰는 작은 카드 그리드 대신 전체 너비의 한 줄 구조로 표시한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공개 프로필 왼쪽에 섹션 빠른 이동 메뉴를 배치한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;랜딩 페이지에서 실제 공개 프로필 예시를 크게 보여준다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;완료 기준&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;선택한 화면이 제품의 핵심 가치를 설명한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;콘텐츠 우선순위와 화면 밀도가 정리되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;두 핵심 화면이 같은 컴포넌트와 시각 규칙을 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;선택하지 않은 시안과 선택 이유를 기록했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;4.4 통합 기획서 &lt;/span&gt;&lt;span&gt;spec.html&lt;/span&gt;&lt;span&gt; 작성&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;목표&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;제품 요구사항, 디자인 시스템, 기술 설계를 하나의 기준 문서로 통합한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;spec.html&lt;/span&gt;&lt;span&gt;은 최소한 Product, Design, Tech의 세 영역으로 구성한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Product 영역&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;사용자 문제와 대상 사용자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;제품 목표와 성공 기준&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;핵심 기능과 우선순위&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;화면별 요구사항&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;기본 상태와 예외 상태&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사용자 권한과 접근 범위&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Design 영역&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;디자인 원칙&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;색상과 타이포그래피&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;간격과 레이아웃 규칙&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공통 UI 컴포넌트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;컴포넌트별 상태와 사용 규칙&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;반응형 동작&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Tech 영역&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;프런트엔드와 백엔드 기술&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;인증 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;데이터베이스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;데이터 스키마와 관계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;외부 API와 의존성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;오류 처리와 로딩 전략&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;구현 제약과 미결정 사항&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;공통 컴포넌트 정의&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;컴포넌트마다 다음 항목을 기록한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;이름&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;코드와 디자인에서 공통으로 사용할 이름&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;목적&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;사용자가 이 컴포넌트로 수행하는 작업&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;변형&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;크기, 강조 수준, 용도별 형태&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;상태&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;기본, 호버, 포커스, 비활성, 로딩, 오류&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;콘텐츠 규칙&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;허용 길이, 줄바꿈, 생략 방식&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;접근성&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;키보드 조작, 레이블, 대비 등&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;데이터 스키마 정의&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;데이터 모델마다 다음 항목을 명시한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;엔터티 이름과 역할&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;필드명, 데이터 형식, 필수 여부&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;고유값과 기본값&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;엔터티 간 관계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;생성&amp;middot;수정&amp;middot;삭제 규칙&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공개 정보와 비공개 정보&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;정렬과 검색에 필요한 인덱스&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;스키마가 확정되지 않은 항목은 추정하여 채우지 않고 &lt;/span&gt;&lt;span&gt;미결정&lt;/span&gt;&lt;span&gt;으로 표시한 뒤 구현 전에 질문한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;완료 기준&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;모든 핵심 기능이 특정 화면 또는 사용자 흐름에 연결된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공통 컴포넌트의 이름과 상태가 정의되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;데이터 스키마가 화면의 입력&amp;middot;출력과 연결되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;미결정 사항과 결정 책임자가 구분되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;4.5 전체 화면과 상태 설계&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;목표&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;정상 상태뿐 아니라 실제 사용 중 나타날 수 있는 빈 화면, 오류, 권한, 긴 콘텐츠와 작은 화면까지 설계한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;TasteMaker의 화면 목록&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;랜딩 페이지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공개 프로필&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;본인 프로필 편집&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;콘텐츠 추가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리뷰 작성&amp;middot;수정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;전체 영화&amp;middot;게임 목록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;콘텐츠 상세 시트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;회원가입과 온보딩&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사용자 핸들 선택&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;좋아하는 콘텐츠 6개 선택&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;프로필 공유&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;화면별 필수 상태&lt;/span&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;상태&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;확인할 내용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;기본&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;대표 데이터가 있을 때의 정상 화면&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;빈 화면&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;데이터가 없을 때의 안내와 다음 행동&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;최초 가입&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;사용자가 처음 진입했을 때의 안내&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;로딩&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;대기 중인 영역과 사용자 조작 가능 범위&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;오류&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;오류 설명, 재시도, 안전한 이탈 방법&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;검색 결과 없음&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;검색어 수정 또는 초기화 방법&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;저장 전&amp;middot;후&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;미저장 변경과 저장 성공 여부&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;권한 없음&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;접근 제한 이유와 가능한 다음 행동&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;긴 콘텐츠&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;긴 제목, 리뷰, 사용자 이름 처리&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;작은 화면&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;모바일 배치, 축약, 탐색 방식&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;화면 명세 템플릿&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;화면 이름:
사용자 목표:
진입 조건:
주요 데이터:
주요 행동:
공통 컴포넌트:
정상 상태:
빈 상태:
로딩 상태:
오류 상태:
권한 조건:
모바일 규칙:
완료 조건:&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;완료 기준&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;모든 핵심 사용자 흐름에 시작점과 종료점이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각 화면에 필요한 데이터와 사용자 행동이 정의되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;빈 상태, 로딩, 오류, 권한 없음 상태를 확인했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;긴 콘텐츠와 모바일 화면에서 레이아웃이 깨지지 않는 기준이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;4.6 구현 전 검토와 실제 개발&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;목표&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코딩 에이전트가 임의로 결정해야 하는 부분을 줄이고, 합의된 명세와 디자인을 코드로 옮긴다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;입력 파일&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;spec.html&lt;/span&gt;&lt;span&gt;: 제품, 디자인, 기술 요구사항&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;design.html&lt;/span&gt;&lt;span&gt;: 전체 화면과 상태의 목업&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;구현 전 요청 예시&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;spec.html과 design.html을 검토하세요.
아직 구현하지 말고, 두 문서가 충돌하거나 구현에 필요한 정보가
빠진 부분을 질문 목록으로 정리하세요.

각 질문에는 다음 내용을 포함하세요.
1. 관련 화면 또는 요구사항
2. 현재 모호한 점
3. 결정하지 않았을 때 구현에 미치는 영향
4. 선택 가능한 대안&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;구현 순서&lt;/span&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;코딩 에이전트의 질문에 답하고 미결정 사항을 확정한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;핵심 사용자 흐름부터 수직으로 구현한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;로컬 환경에서 화면과 동작을 확인한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;디자인과 다른 부분, 누락 기능, 상태 처리를 수정한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;인증과 데이터베이스를 연결한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;실제 데이터의 생성, 조회, 수정, 삭제를 확인한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;변경된 요구사항을 &lt;/span&gt;&lt;span&gt;spec.html&lt;/span&gt;&lt;span&gt;과 &lt;/span&gt;&lt;span&gt;design.html&lt;/span&gt;&lt;span&gt;에 반영한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;검수 항목&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;리뷰 영역 등 필수 콘텐츠가 빠지지 않았는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;카드의 호버 상태처럼 상호작용 조건이 디자인과 일치하는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;좌우 이동, 정렬, 필터 등 탐색 동작이 요구사항과 일치하는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;여백, 글꼴, 정렬, 반응형 동작이 디자인과 일치하는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;인증 상태에 따라 접근 권한이 올바르게 달라지는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;입력한 데이터가 실제 데이터베이스에 저장되고 다시 조회되는가?&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;완료 기준&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;구현 전 질문에 답했으며 남은 미결정 사항이 표시되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;핵심 사용자 흐름을 로컬 환경에서 처음부터 끝까지 실행할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;요구사항, 디자인, 구현의 차이를 기록하고 해소했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;코드 변경으로 요구사항이 달라졌다면 문서도 함께 갱신했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;5. 산출물 간 추적 관계&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;각 산출물은 독립 문서가 아니라 다음 단계의 입력이다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;산출물&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;포함할 내용&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;다음 단계에서의 용도&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;문제 정의&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;사용자, 상황, 문제, 기대 변화&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;기능과 우선순위 판단&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;design.md&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;디자인 원칙과 시각 규칙&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;핵심 화면 시안 생성&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;핵심 화면&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;선택한 레이아웃과 콘텐츠 구조&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;상세 요구사항 발견&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;spec.html&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;제품&amp;middot;디자인&amp;middot;기술 명세&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;전체 화면 설계와 구현 기준&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;design.html&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;전체 화면과 상태&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;구현 결과의 시각적 기준&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;애플리케이션 코드&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;실제 동작&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;테스트와 배포 대상&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;요구사항이 바뀌면 영향을 받는 산출물을 함께 수정한다. 예를 들어 프로필에서 즐겨찾기 개수가 6개에서 8개로 바뀌었다면 Product 요구사항, 컴포넌트 규칙, 화면 목업, 데이터 검증 규칙과 구현 코드를 모두 확인한다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;6. 단계별 품질 게이트&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다음 단계로 넘어가기 전에 각 질문에 답한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;문제 정의 완료&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대상 사용자가 구체적인가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사용 상황과 문제가 한 문장으로 설명되는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기능이 아니라 사용자의 변화를 중심으로 작성했는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;디자인 방향 완료&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;레퍼런스의 채택&amp;middot;제외 기준이 있는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;색상, 글꼴, 간격, 컴포넌트 원칙이 있는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;제품 콘텐츠가 시각적 장식보다 우선하는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;핵심 화면 완료&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;최소 두 가지 이상의 대안을 비교했는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;선택 이유를 설명할 수 있는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;불필요한 요소를 제거했는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;통합 명세 완료&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기능이 화면과 사용자 흐름에 연결되는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;공통 컴포넌트와 상태가 정의되어 있는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;데이터 스키마와 권한 규칙이 정의되어 있는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;미결정 사항을 질문 목록으로 분리했는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;전체 디자인 완료&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;정상&amp;middot;빈&amp;middot;로딩&amp;middot;오류 상태가 있는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;권한 없음과 검색 결과 없음 상태가 있는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;긴 콘텐츠와 모바일 화면을 확인했는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;구현 완료&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;핵심 흐름을 실제로 실행했는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;저장한 데이터를 다시 조회할 수 있는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;명세와 디자인의 차이를 수정했는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li data-checked=&quot;false&quot; data-task-list-item=&quot;true&quot;&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;변경된 코드와 문서를 동기화했는가?&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;7. 자주 발생하는 실패와 대응&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;추상적인 디자인 요청&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;증상:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 화면마다 전형적인 색상과 구성요소가 반복되고 제품의 개성이 드러나지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;확인:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;design.md&lt;/span&gt;&lt;span&gt;에 콘텐츠 우선순위, 제외할 스타일, 컴포넌트 규칙이 있는지 점검한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;대응:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 실제 레퍼런스에서 채택할 원칙을 뽑고 색상, 간격, 밀도와 컴포넌트 상태를 명시한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;첫 번째 시안을 완성품으로 사용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;증상:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 불필요한 버튼, 배지, 문구가 남고 콘텐츠 구조가 사용자 목표와 맞지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;확인:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 서로 다른 구조의 대안을 비교했는지, 삭제 중심의 피드백을 진행했는지 확인한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;대응:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 레이아웃 대안을 만든 뒤 콘텐츠 우선순위, 정보 밀도와 사용자 행동을 기준으로 하나를 선택한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;정상 화면만 설계&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;증상:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 실제 데이터가 없거나 오류가 발생했을 때 화면과 다음 행동이 정의되지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;확인:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 화면 명세에 빈 상태, 로딩, 오류, 권한과 모바일 항목이 있는지 확인한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;대응:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 상태 매트릭스를 만들고 각 상태의 메시지, 행동과 복구 경로를 정의한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;화면마다 다른 컴포넌트 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;증상:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 같은 역할의 버튼과 카드가 화면마다 다른 크기, 색상, 동작을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;확인:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 컴포넌트 이름, 변형과 상태가 &lt;/span&gt;&lt;span&gt;spec.html&lt;/span&gt;&lt;span&gt;에 정의되어 있는지 확인한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;대응:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 공통 컴포넌트 목록을 만들고 디자인과 코드에서 같은 이름을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;문서와 코드 불일치&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;증상:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 구현은 변경됐지만 명세나 목업에는 이전 동작이 남아 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;확인:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 변경 사항이 제품 요구사항, 디자인, 데이터와 코드 중 어디에 영향을 주는지 추적한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;대응:&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 구현 작업의 완료 조건에 관련 문서 갱신을 포함한다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;8. 운영 원칙&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;기획에 충분한 시간 배정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;제공된 영상은 전체 제작 시간의 최소 50%를 사전 기획에 투자하라고 제안한다. 이 비율은 이 문서에서 독립적으로 검증하지 않았다. 실무에서는 데이터베이스 구조, 인증 방식, 화면 구조, 핵심 사용자 흐름과 공통 컴포넌트처럼 변경 영향이 큰 결정을 구현 전에 우선 검토한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;구체적인 반복 피드백&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AI의 첫 결과는 검토할 초안으로 취급한다. 피드백에는 대상, 문제, 변경 내용과 확인 기준을 포함한다. 시각적 취향만 말하지 않고 사용자 목표와 제품 원칙을 근거로 판단한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;단일 기준 문서 유지&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;spec.html&lt;/span&gt;&lt;span&gt;과 &lt;/span&gt;&lt;span&gt;design.html&lt;/span&gt;&lt;span&gt;을 구현의 기준으로 사용한다. 코드에서 요구사항이 바뀌면 관련 문서도 같은 작업 범위에서 수정한다. 문서와 코드가 충돌하면 어느 쪽이 최신 결정인지 확인한 뒤 한쪽을 임의로 덮어쓰지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;9. 최종 완료 정의&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다음 조건을 모두 충족하면 제작 프로세스가 완료된 것으로 판단한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;해결할 사용자 문제와 대상 사용자가 명확하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;선택한 디자인 방향과 선택 이유가 문서화되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;전체 기능, 화면, 상태, 컴포넌트와 데이터 스키마가 정의되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;구현 전에 모호한 요구사항을 질문하고 결정했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;핵심 사용자 흐름이 실제 환경에서 동작한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;빈 상태, 로딩, 오류, 권한과 모바일 화면을 검증했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;명세, 디자인과 코드가 같은 요구사항을 반영한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;배포 전 테스트 결과와 남은 미검증 항목이 기록되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;10. 결론&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AI 네이티브 앱 제작은 AI에게 한 번의 명령으로 완성품을 요청하는 과정이 아니다. 사용자 문제를 정의하고, 디자인의 판단 기준을 만들고, 핵심 화면으로 제품 구조를 탐색한 뒤, 제품&amp;middot;디자인&amp;middot;기술 명세를 구현 가능한 수준으로 연결하는 과정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AI는 대안 생성과 구현 속도를 높이는 역할을 맡는다. 사람은 어떤 문제를 해결할지, 어떤 화면이 더 적합한지, 무엇을 삭제할지, 어떤 데이터 구조와 품질 기준을 채택할지 결정한다. 따라서 최종 품질은 코드 생성 속도뿐 아니라 기획, 디자인 판단, 명세 작성과 검수 능력에 달려 있다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1798</guid>
      <comments>https://javaexpert.tistory.com/1798#entry1798comment</comments>
      <pubDate>Mon, 3 Aug 2026 10:14:31 +0900</pubDate>
    </item>
    <item>
      <title>Codex에서 GPT-5.6 Luna를 서브에이전트로 사용하는 방법</title>
      <link>https://javaexpert.tistory.com/1797</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서는 GPT-5.6 Sol을 메인 오케스트레이터로 사용하면서 GPT-5.6 Luna를 서브에이전트로 실행하는 로컬 설정을 기록한 운영 가이드다. Codex 업데이트나 설정 초기화 후 Luna 서브에이전트가 보이지 않을 때 이 문서를 따라 다시 설정하고 검증한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;현재 확인된 정상 구성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 8월 1일 기준으로 다음 구성이 실제 실행까지 확인됐다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메인 모델: &lt;code&gt;gpt-5.6-sol&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;서브에이전트 모델: &lt;code&gt;gpt-5.6-luna&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Luna 다중 에이전트 프로토콜: &lt;code&gt;v2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Luna 추론 수준: &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;xhigh&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;실제 검증 결과: Luna 자식 에이전트가 생성되고 &lt;code&gt;LUNA_SUBAGENT_OK&lt;/code&gt;를 반환함&lt;/li&gt;
&lt;li&gt;검증 당시 데스크톱 내장 Codex: &lt;code&gt;0.146.0-alpha.9.2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;검증 당시 터미널 Codex: &lt;code&gt;0.146.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시작하기 전에: Codex 경로 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서의 명령은 macOS와 Linux의 Bash/Zsh 셸을 기준으로 한다. 먼저 현재 터미널에서 Codex 데이터 디렉터리를 &lt;code&gt;CODEX_DIR&lt;/code&gt; 변수로 지정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export CODEX_DIR=&quot;${CODEX_HOME:-$HOME/.codex}&quot;
printf 'Codex directory: %s\n' &quot;$CODEX_DIR&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;CODEX_HOME&lt;/code&gt;을 따로 설정했다면 그 경로를 사용한다.&lt;/li&gt;
&lt;li&gt;별도 설정이 없다면 현재 사용자의 기본 경로인 &lt;code&gt;$HOME/.codex&lt;/code&gt;를 사용한다.&lt;/li&gt;
&lt;li&gt;이후 터미널을 새로 열었다면 위 명령을 다시 실행한다.&lt;/li&gt;
&lt;li&gt;Windows에서는 Codex 데이터 디렉터리를 확인한 뒤 PowerShell 환경 변수와 경로 표기법에 맞게 명령을 바꿔야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서에서 사용하는 파일은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$CODEX_DIR/config.toml
$CODEX_DIR/models_cache.json
$CODEX_DIR/models-luna-v2.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;models_cache.json&lt;/code&gt;은 Codex가 내려받은 원본 모델 카탈로그다. 이 파일을 직접 수정하지 않고 복사본인 &lt;code&gt;models-luna-v2.json&lt;/code&gt;에서 Luna 항목만 &lt;code&gt;v2&lt;/code&gt;로 바꾼다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정은 로컬 모델 카탈로그 오버라이드다. Codex 업데이트로 공식 카탈로그나 서브에이전트 동작 방식이 바뀌면 더 이상 필요하지 않거나 작동하지 않을 수 있다. 업데이트 후에는 반드시 실제 자식 에이전트 실행까지 확인한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 현재 설정 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 다음 명령을 실행한다.&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;grep -nE 'model_catalog_json|multi_agent' &quot;$CODEX_DIR/config.toml&quot;

jq '.models[]
  | select(.slug == &quot;gpt-5.6-luna&quot;)
  | {
      slug,
      multi_agent_version,
      supported_reasoning_levels
    }' &quot;$CODEX_DIR/models-luna-v2.json&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상이라면 다음 두 조건을 만족해야 한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;model_catalog_json = &quot;/absolute/path/to/codex-home/models-luna-v2.json&quot;
multi_agent = true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna 모델 정보에는 다음 값이 있어야 한다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;slug&quot;: &quot;gpt-5.6-luna&quot;,
  &quot;multi_agent_version&quot;: &quot;v2&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이 없거나 값이 다르면 다음 절차로 다시 만든다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Luna V2 모델 카탈로그 다시 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 원본 모델 캐시와 현재 설정을 백업한다.&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;mkdir -p &quot;$CODEX_DIR/backups/luna-v2-setup&quot;

cp &quot;$CODEX_DIR/models_cache.json&quot; \
   &quot;$CODEX_DIR/backups/luna-v2-setup/models_cache.json&quot;

cp &quot;$CODEX_DIR/config.toml&quot; \
   &quot;$CODEX_DIR/backups/luna-v2-setup/config.toml&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 카탈로그를 복사하고 Luna 항목만 &lt;code&gt;v2&lt;/code&gt;로 변경한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;cp &quot;$CODEX_DIR/models_cache.json&quot; \
   &quot;$CODEX_DIR/models-luna-v2.json&quot;

python3 - &amp;lt;&amp;lt;'PY'
import json
import os
from pathlib import Path

codex_dir = Path(os.environ[&quot;CODEX_DIR&quot;]).expanduser().resolve()
catalog_path = codex_dir / &quot;models-luna-v2.json&quot;
data = json.loads(catalog_path.read_text(encoding=&quot;utf-8&quot;))
models = data.get(&quot;models&quot;, [])

matches = [
    model
    for model in models
    if model.get(&quot;slug&quot;) == &quot;gpt-5.6-luna&quot;
]

if len(matches) != 1:
    raise SystemExit(
        f&quot;gpt-5.6-luna 항목이 정확히 1개여야 합니다. 현재: {len(matches)}개&quot;
    )

matches[0][&quot;multi_agent_version&quot;] = &quot;v2&quot;

temporary_path = catalog_path.with_suffix(&quot;.json.tmp&quot;)
temporary_path.write_text(
    json.dumps(data, ensure_ascii=False, indent=2) + &quot;\n&quot;,
    encoding=&quot;utf-8&quot;,
)
temporary_path.replace(catalog_path)

print(&quot;gpt-5.6-luna multi_agent_version=v2 설정 완료&quot;)
PY&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령이 Luna 항목을 찾지 못하면 작업을 중단한다. 다른 모델을 Luna로 추정해 바꾸면 안 된다. 먼저 Codex를 업데이트하거나 &lt;code&gt;models_cache.json&lt;/code&gt;에 Luna가 제공되는지 확인한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. &lt;code&gt;config.toml&lt;/code&gt; 연결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$CODEX_DIR/config.toml&lt;/code&gt;의 최상위 영역에 다음 설정을 둔다. 첫 번째 &lt;code&gt;[섹션]&lt;/code&gt;이 시작되기 전에 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 현재 사용자에게 맞는 설정 한 줄을 출력한다.&lt;/p&gt;
&lt;pre class=&quot;scilab&quot;&gt;&lt;code&gt;printf 'model_catalog_json = &quot;%s/models-luna-v2.json&quot;\n' &quot;$CODEX_DIR&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력된 절대경로를 복사해 &lt;code&gt;config.toml&lt;/code&gt;에 넣는다. 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;model = &quot;gpt-5.6-sol&quot;
model_catalog_json = &quot;/absolute/path/to/codex-home/models-luna-v2.json&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 &lt;code&gt;[features]&lt;/code&gt; 섹션에는 다음 값을 추가하거나 확인한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[features]
multi_agent = true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의 사항:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;model_catalog_json&lt;/code&gt;을 &lt;code&gt;[features]&lt;/code&gt;나 &lt;code&gt;[projects]&lt;/code&gt; 아래에 넣으면 안 된다.&lt;/li&gt;
&lt;li&gt;TOML 문자열 안에서는 &lt;code&gt;$HOME&lt;/code&gt;이나 &lt;code&gt;$CODEX_DIR&lt;/code&gt; 같은 셸 변수가 자동으로 확장되지 않는다. 반드시 앞의 &lt;code&gt;printf&lt;/code&gt; 명령으로 확인한 절대경로를 넣는다.&lt;/li&gt;
&lt;li&gt;같은 키를 두 번 선언하면 TOML 로딩 오류가 날 수 있다.&lt;/li&gt;
&lt;li&gt;기존 &lt;code&gt;[features]&lt;/code&gt; 섹션이 있으면 새 섹션을 하나 더 만들지 말고 그 안에 추가한다.&lt;/li&gt;
&lt;li&gt;메인 모델을 Sol로 유지하려면 &lt;code&gt;model = &quot;gpt-5.6-sol&quot;&lt;/code&gt;을 그대로 둔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정을 저장한 뒤 파싱 상태를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;codex doctor --summary&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;config.load&lt;/code&gt;가 실패한다면 앱을 재시작하기 전에 &lt;code&gt;config.toml&lt;/code&gt;의 중복 키와 섹션 위치부터 고친다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Codex 완전 재시작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중인 Codex는 시작 시 읽은 모델 카탈로그와 도구 스키마를 계속 사용할 수 있다. 설정 파일만 바꿔서는 이미 열린 작업에 Luna가 나타나지 않을 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;진행 중인 작업을 마친다.&lt;/li&gt;
&lt;li&gt;ChatGPT/Codex 데스크톱 앱을 &lt;code&gt;⌘Q&lt;/code&gt;로 완전히 종료한다.&lt;/li&gt;
&lt;li&gt;터미널에서 별도 &lt;code&gt;codex&lt;/code&gt; 프로세스가 실행 중이면 함께 종료한다.&lt;/li&gt;
&lt;li&gt;데스크톱 앱을 다시 실행한다.&lt;/li&gt;
&lt;li&gt;기존 작업이 아니라 새 작업에서 Luna 서브에이전트를 검증한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강제 종료나 macOS 재부팅은 필수 조건이 아니다. 앱을 정상적으로 완전히 종료했다가 다시 여는 것으로 충분하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Luna 서브에이전트 실제 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna를 확실히 사용하려면 서브에이전트 생성 시 모델을 명시해야 한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;model: gpt-5.6-luna
reasoning_effort: max
fork_turns: none&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;fork_turns&lt;/code&gt;는 &lt;code&gt;none&lt;/code&gt; 또는 최근 턴 수처럼 제한된 값을 사용한다. 전체 대화 기록을 상속하는 생성 방식은 부모 모델과 추론 수준을 그대로 물려받으므로 Luna 모델 오버라이드를 적용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 Codex에 요청할 때는 다음과 같이 작성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;메인 Sol이 작업을 조율하고, 실제 하위 작업은
gpt-5.6-luna / reasoning max 서브에이전트로 실행해 줘.
Luna 생성 시 모델을 명시하고 fork_turns는 none 또는 제한된 턴 수를 사용해 줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna는 &lt;code&gt;max&lt;/code&gt;까지 지원한다. &lt;code&gt;ultra&lt;/code&gt;를 지정하면 모델 검증 단계에서 거부될 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 빠른 스모크 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 작업에서 다음과 같이 요청한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;gpt-5.6-luna, reasoning max로 읽기 전용 서브에이전트 한 개를 생성해 줘.
그 에이전트는 다른 도구를 사용하지 말고 LUNA_SUBAGENT_OK만 반환하게 해 줘.
실행 후 자식 세션의 실제 model과 reasoning_effort도 확인해 줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공 판정은 다음 세 조건을 모두 만족해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자식 생성 요청이 모델 검증 단계에서 거부되지 않는다.&lt;/li&gt;
&lt;li&gt;자식이 &lt;code&gt;LUNA_SUBAGENT_OK&lt;/code&gt;를 반환한다.&lt;/li&gt;
&lt;li&gt;자식 rollout의 &lt;code&gt;turn_context&lt;/code&gt;에 &lt;code&gt;model=&quot;gpt-5.6-luna&quot;&lt;/code&gt;와 &lt;code&gt;reasoning_effort=&quot;max&quot;&lt;/code&gt;가 기록된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 자식 세션에서 모델 기록을 찾으려면 다음 명령을 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;grep -R -l '&quot;model&quot;:&quot;gpt-5.6-luna&quot;' \
  &quot;$CODEX_DIR/sessions&quot; |
  tail -n 5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾은 rollout 파일에서 &lt;code&gt;turn_context&lt;/code&gt;를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;jq -c '
  select(.type == &quot;turn_context&quot;)
  | {
      model: .payload.model,
      reasoning_effort:
        .payload.collaboration_mode.settings.reasoning_effort
    }
' /절대/경로/rollout-파일.jsonl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 결과 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;model&quot;: &quot;gpt-5.6-luna&quot;,
  &quot;reasoning_effort&quot;: &quot;max&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 자기 모델을 말로 주장하는 것만으로는 충분하지 않다. 반드시 자식 세션의 &lt;code&gt;turn_context&lt;/code&gt;를 확인한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 문제가 생겼을 때&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;gpt-5.6-luna&lt;/code&gt;가 허용 모델 목록에 없음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 항목을 순서대로 확인한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;model_catalog_json&lt;/code&gt; 경로가 정확한가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;models-luna-v2.json&lt;/code&gt;에 Luna 항목이 있는가&lt;/li&gt;
&lt;li&gt;Luna의 &lt;code&gt;multi_agent_version&lt;/code&gt;이 &lt;code&gt;v2&lt;/code&gt;인가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[features]&lt;/code&gt;의 &lt;code&gt;multi_agent&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt;인가&lt;/li&gt;
&lt;li&gt;설정 변경 후 앱을 완전히 재시작했는가&lt;/li&gt;
&lt;li&gt;재시작 후 만든 새 작업에서 테스트했는가&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Luna를 지정했는데 Sol 자식이 실행됨&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 원인이 가장 흔하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성 요청에서 &lt;code&gt;model&lt;/code&gt;을 생략함&lt;/li&gt;
&lt;li&gt;부모의 전체 대화 기록을 그대로 상속함&lt;/li&gt;
&lt;li&gt;자동 오케스트레이션에 맡기고 Luna 사용을 명시하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna를 강제하려면 &lt;code&gt;model=gpt-5.6-luna&lt;/code&gt;와 제한된 &lt;code&gt;fork_turns&lt;/code&gt;를 함께 지정한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;reasoning_effort&lt;/code&gt;가 거부됨&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Luna에는 &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;xhigh&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt; 중 하나를 사용한다. &lt;code&gt;ultra&lt;/code&gt;는 사용하지 않는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;업데이트 후 다시 작동하지 않음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트된 &lt;code&gt;$CODEX_DIR/models_cache.json&lt;/code&gt;에서 Luna의 공식 &lt;code&gt;multi_agent_version&lt;/code&gt;을 먼저 확인한다.&lt;/p&gt;
&lt;pre class=&quot;autohotkey&quot;&gt;&lt;code&gt;jq '.models[]
  | select(.slug == &quot;gpt-5.6-luna&quot;)
  | {
      slug,
      multi_agent_version,
      supported_reasoning_levels
    }' &quot;$CODEX_DIR/models_cache.json&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공식 값이 이미 &lt;code&gt;v2&lt;/code&gt;라면 별도 오버라이드가 필요하지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;공식 값이 &lt;code&gt;v1&lt;/code&gt;이면 이 문서의 절차로 별도 카탈로그를 다시 만든다.&lt;/li&gt;
&lt;li&gt;Luna 항목 자체가 없으면 임의로 모델 항목을 만들지 않는다.&lt;/li&gt;
&lt;li&gt;모델은 보이지만 생성 도구가 거부한다면 현재 Codex 버전의 서브에이전트 도구 스키마가 Luna를 허용하는지 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 원상 복구&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 오버라이드를 제거하려면 앱을 완전히 종료한 뒤 &lt;code&gt;config.toml&lt;/code&gt;에서 다음 줄을 삭제한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;model_catalog_json = &quot;/absolute/path/to/codex-home/models-luna-v2.json&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;models-luna-v2.json&lt;/code&gt;은 즉시 삭제하지 말고 보관해도 된다. 그다음 앱을 다시 실행하면 Codex가 기본 모델 카탈로그를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백업한 설정으로 되돌려야 할 때는 앱을 종료한 상태에서 백업 내용을 확인한 뒤 복원한다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;cp &quot;$CODEX_DIR/backups/luna-v2-setup/config.toml&quot; \
   &quot;$CODEX_DIR/config.toml&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복원 후에는 &lt;code&gt;codex doctor --summary&lt;/code&gt;로 설정 파싱 여부를 확인하고 앱을 다시 실행한다.&lt;/p&gt;</description>
      <category>개발상식</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1797</guid>
      <comments>https://javaexpert.tistory.com/1797#entry1797comment</comments>
      <pubDate>Sat, 1 Aug 2026 18:40:46 +0900</pubDate>
    </item>
    <item>
      <title>PixelRAG: PDF와 이미지를 화면 그대로 검색하는 시각 자료용 RAG</title>
      <link>https://javaexpert.tistory.com/1796</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG는 PDF, 이미지, 웹페이지를 텍스트로 변환하지 않고 &lt;b&gt;화면 이미지 자체를 검색 대상으로 사용하는 시각 자료용 RAG(Retrieval-Augmented Generation) 프로젝트&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 문서 RAG는 PDF에서 텍스트를 추출한 뒤 문단 단위로 나누어 검색합니다. 반면 PixelRAG는 문서 페이지를 이미지로 저장하고, 비전&amp;middot;언어 임베딩 모델을 이용해 질문과 관련된 페이지를 찾습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 다음과 같이 시각적 구조가 중요한 문서에 적합합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;표와 차트가 많은 보고서&lt;/li&gt;
&lt;li&gt;그림과 설명이 결합된 교재&lt;/li&gt;
&lt;li&gt;PPT를 변환한 PDF&lt;/li&gt;
&lt;li&gt;인포그래픽&lt;/li&gt;
&lt;li&gt;제품 매뉴얼&lt;/li&gt;
&lt;li&gt;스캔 문서&lt;/li&gt;
&lt;li&gt;시스템 구성도와 설계 도면&lt;/li&gt;
&lt;li&gt;웹페이지 UI&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG는 완성된 문서 채팅 서비스라기보다, 이러한 자료를 검색하기 위한 &lt;b&gt;픽셀 기반 RAG 파이프라인&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 핵심 처리 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG의 기본 처리 흐름은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;PDF&amp;middot;이미지&amp;middot;웹페이지 입력
          &amp;darr;
페이지를 스크린샷 또는 이미지 타일로 변환
          &amp;darr;
Qwen3-VL-Embedding-2B로 이미지 임베딩 생성
          &amp;darr;
FAISS 또는 Qdrant에 벡터 저장
          &amp;darr;
사용자 질문을 벡터로 변환
          &amp;darr;
질문과 관련된 페이지 이미지 검색
          &amp;darr;
검색된 이미지를 비전 언어 모델이 분석
          &amp;darr;
답변과 출처 페이지 제공
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 문서를 OCR 텍스트로 변환한 뒤 검색하는 것이 아니라, &lt;b&gt;문서의 시각적 표현을 유지한 상태에서 검색한다는 점&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 일반 문서 RAG와의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분일반 문서 RAGPixelRAG&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;문서 처리&lt;/td&gt;
&lt;td&gt;PDF에서 텍스트 추출&lt;/td&gt;
&lt;td&gt;PDF를 페이지 이미지로 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;검색 단위&lt;/td&gt;
&lt;td&gt;문장 또는 문단&lt;/td&gt;
&lt;td&gt;페이지 또는 이미지 타일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;임베딩 대상&lt;/td&gt;
&lt;td&gt;텍스트&lt;/td&gt;
&lt;td&gt;이미지와 텍스트의 시각적 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;표&amp;middot;차트 처리&lt;/td&gt;
&lt;td&gt;추출 과정에서 구조가 깨질 수 있음&lt;/td&gt;
&lt;td&gt;원래 배치와 형태 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OCR 의존성&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;상대적으로 낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주요 대상&lt;/td&gt;
&lt;td&gt;텍스트 중심 문서&lt;/td&gt;
&lt;td&gt;교재&amp;middot;보고서&amp;middot;도면&amp;middot;인포그래픽&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;검색 결과&lt;/td&gt;
&lt;td&gt;관련 텍스트 조각&lt;/td&gt;
&lt;td&gt;관련 페이지 또는 이미지 영역&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 RAG에서는 표의 행과 열, 이미지와 설명의 위치 관계, 페이지 레이아웃 등이 텍스트 추출 과정에서 사라질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG는 페이지를 이미지로 다루기 때문에 이러한 시각적 관계를 유지할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 검색 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 PDF가 있다고 가정하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지내용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1페이지&lt;/td&gt;
&lt;td&gt;회사 소개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2페이지&lt;/td&gt;
&lt;td&gt;PixelRAG 시스템 구성도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3페이지&lt;/td&gt;
&lt;td&gt;매출 현황 표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4페이지&lt;/td&gt;
&lt;td&gt;제품 이미지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 다음과 같이 질문합니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;PixelRAG 시스템 구조도를 보여줘.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG는 OCR로 추출한 문장을 찾는 대신, 질문과 시각적으로 관련된 &lt;b&gt;2페이지 이미지를 검색 결과로 반환&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 예제에서도 PDF를 인덱싱한 후 다음과 같은 질문으로 구성도가 포함된 페이지를 검색합니다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Overview of PixelRAG and the diagram
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 PixelRAG가 담당하는 핵심 역할은 다음과 같이 정리할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 질문과 가장 관련된 PDF 페이지 또는 이미지 영역을 시각적으로 검색한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. PDF를 업로드하면 바로 질문할 수 있는가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적으로 구현할 수 있지만, 질문하기 전에 &lt;b&gt;문서 인덱싱 과정&lt;/b&gt;이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG는 사용자가 질문할 때마다 PDF 전체를 처음부터 분석하지 않습니다. 문서를 최초로 등록할 때 페이지별 임베딩을 생성하고, 이후에는 만들어진 벡터 인덱스를 이용해 관련 페이지를 검색합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 작업 순서는 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 입력 문서 지정&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;source:
  type: local
  path: ./paper.pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 이미지 임베딩 모델 지정&lt;/h3&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;embed:
  model: Qwen/Qwen3-VL-Embedding-2B
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.3 인덱스 생성&lt;/h3&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;pixelrag index build
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.4 검색 API 실행&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;pixelrag serve \
  --index-dir ./paper_index \
  --port 30001
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 생성이 완료되면 애플리케이션은 사용자의 질문을 검색 API로 전달하고, 관련성이 높은 페이지 이미지를 받을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 README에 기재된 예시 기준으로 샘플 PDF 인덱싱 시간은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple M 시리즈: 약 3분&lt;/li&gt;
&lt;li&gt;GPU 환경: 약 1분&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 크기, 페이지 수, 이미지 해상도, 하드웨어 사양에 따라 실제 처리 시간은 달라질 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. PixelRAG가 담당하는 범위&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG의 주요 역할은 &lt;b&gt;관련 이미지 검색&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색된 페이지를 읽고 자연어 답변을 생성하려면 별도의 비전 언어 모델(VLM)을 연결해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 모델을 답변 생성 단계에 사용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Qwen3-VL&lt;/li&gt;
&lt;li&gt;GPT 비전 지원 모델&lt;/li&gt;
&lt;li&gt;Claude 비전 지원 모델&lt;/li&gt;
&lt;li&gt;기타 이미지 입력을 지원하는 멀티모달 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 문서 채팅 시스템에서는 PixelRAG와 답변 생성 모델의 역할이 구분됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성 요소담당 역할&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PixelRAG&lt;/td&gt;
&lt;td&gt;질문과 관련된 페이지 또는 이미지 영역 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;벡터 데이터베이스&lt;/td&gt;
&lt;td&gt;이미지 임베딩 저장 및 유사도 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비전 언어 모델&lt;/td&gt;
&lt;td&gt;검색된 이미지의 내용 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM&lt;/td&gt;
&lt;td&gt;최종 답변 구성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;프런트엔드&lt;/td&gt;
&lt;td&gt;파일 업로드, 질문 입력, 답변 및 출처 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 문서 채팅 서비스 구성 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatPDF와 같은 서비스를 만들려면 PixelRAG 외에 업로드, 작업 관리, 답변 생성, 출처 표시 기능을 추가해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Next.js 파일 업로드 화면
          &amp;darr;
PDF 원본 저장
          &amp;darr;
인덱싱 작업 등록
          &amp;darr;
페이지 이미지 생성
          &amp;darr;
PixelRAG 임베딩 및 인덱스 생성
          &amp;darr;
사용자 질문 입력
          &amp;darr;
관련 페이지 이미지 검색
          &amp;darr;
비전 언어 모델이 페이지 분석
          &amp;darr;
답변과 PDF 페이지 출처 표시
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요한 주요 구성 요소&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;파일 업로드 기능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;사용자가 PDF나 이미지를 업로드할 수 있어야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;원본 파일 저장소&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;업로드된 문서를 로컬 디스크나 객체 스토리지에 보관합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비동기 인덱싱 작업&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;파일 업로드 후 백그라운드 작업으로 페이지 변환과 임베딩 생성을 처리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벡터 데이터베이스&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;소규모 서비스는 FAISS, 다중 사용자 서비스는 Qdrant와 같은 벡터 데이터베이스를 검토할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색 API&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;사용자 질문을 받아 관련 페이지와 이미지 영역을 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;답변 생성 모델&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;검색된 페이지 이미지를 읽고 질문에 맞는 답변을 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;출처 표시 기능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;답변과 함께 문서명, 페이지 번호, 원본 페이지 이미지를 표시합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 적합한 활용 사례&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG는 다음과 같은 서비스에 활용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;교육 자료 검색&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;교재의 그림, 문법표, 수학 도형, 과학 실험 이미지가 포함된 페이지를 검색할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보고서 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매출 그래프, 통계표, 조직도, 비교 차트가 포함된 페이지를 찾아 분석할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기술 문서 검색&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 구성도, 네트워크 다이어그램, UI 화면, 설치 절차가 포함된 페이지를 검색할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스캔 문서 검색&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트 추출 품질이 낮은 스캔 문서를 페이지 이미지 상태로 검색할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹페이지 UI 검색&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹페이지를 화면 이미지로 수집한 뒤 특정 메뉴, 버튼, 카드 또는 화면 구성을 검색하는 데 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 고려해야 할 사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG를 실제 서비스에 적용할 때는 다음 항목을 함께 검토해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서별 인덱싱 시간&lt;/li&gt;
&lt;li&gt;페이지 이미지 저장 공간&lt;/li&gt;
&lt;li&gt;GPU 또는 Apple Silicon 사용 여부&lt;/li&gt;
&lt;li&gt;임베딩 모델의 메모리 요구량&lt;/li&gt;
&lt;li&gt;여러 사용자의 인덱스 분리 방식&lt;/li&gt;
&lt;li&gt;질문별 검색 결과 수&lt;/li&gt;
&lt;li&gt;비전 언어 모델 호출 비용&lt;/li&gt;
&lt;li&gt;답변에 표시할 페이지 출처&lt;/li&gt;
&lt;li&gt;문서 삭제 시 이미지와 인덱스를 함께 제거하는 절차&lt;/li&gt;
&lt;li&gt;표와 차트의 수치 정확성 검증&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 이미지 기반 답변은 검색된 페이지의 내용을 비전 모델이 다시 해석하는 구조이므로, 중요한 수치나 표 데이터는 원본 페이지와 함께 제공하는 것이 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG는 PDF나 이미지를 OCR 텍스트로 변환해 검색하는 프로젝트가 아닙니다. 문서의 페이지와 화면 영역을 이미지 상태로 임베딩하고, 사용자 질문과 관련된 시각 자료를 검색하는 프로젝트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 이해할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PixelRAG는 PDF&amp;middot;이미지&amp;middot;웹페이지의 시각적 구조를 유지하면서, 질문과 관련된 페이지나 이미지 영역을 찾아주는 픽셀 기반 검색 시스템이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표, 차트, 교재, 인포그래픽, 시스템 구성도처럼 &lt;b&gt;텍스트만 추출하면 정보 구조가 손실되는 문서&lt;/b&gt;에서 특히 활용 가치가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 완성형 문서 채팅 서비스를 구축하려면 PixelRAG에 파일 업로드 UI, 문서 저장소, 인덱싱 작업 관리, 비전 언어 모델, 답변 생성 및 출처 표시 기능을 추가해야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/StarTrail-org/PixelRAG&quot;&gt;PixelRAG GitHub 저장소&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/StarTrail-org/PixelRAG/blob/main/README.md&quot;&gt;PixelRAG README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/StarTrail-org/PixelRAG/blob/main/CLAUDE.md&quot;&gt;PixelRAG CLAUDE.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1796</guid>
      <comments>https://javaexpert.tistory.com/1796#entry1796comment</comments>
      <pubDate>Thu, 30 Jul 2026 11:33:10 +0900</pubDate>
    </item>
    <item>
      <title>LLM 추론 엔지니어링 튜토리얼</title>
      <link>https://javaexpert.tistory.com/1795</link>
      <description>&lt;h1&gt;LLM 추론 엔지니어링 튜토리얼&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이미 만들어진 AI 모델을 GPU 서버에 올리고, 수많은 사용자에게 빠르고 안정적으로 제공하면서 비용까지 최적화하는 기술을 배우는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정을 마치면 다음 결과물을 갖게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vLLM과 SGLang으로 구축한 OpenAI 호환 추론 서버&lt;/li&gt;
&lt;li&gt;처리량 중심, 지연시간 중심, 긴 문맥 중심의 서빙 파이프라인 3종&lt;/li&gt;
&lt;li&gt;비용&amp;middot;지연시간&amp;middot;품질을 함께 고려하는 모델 라우터&lt;/li&gt;
&lt;li&gt;사용자별 토큰 예산 및 요청 제어 API&lt;/li&gt;
&lt;li&gt;Prometheus&amp;middot;Grafana 기반 운영 대시보드&lt;/li&gt;
&lt;li&gt;Kubernetes 자동 확장 환경&lt;/li&gt;
&lt;li&gt;1,000개 이상의 동시 요청을 재현하는 부하 시험&lt;/li&gt;
&lt;li&gt;재현 가능한 공개 벤치마크 보고서&lt;/li&gt;
&lt;li&gt;요청당 원가와 손익분기점을 계산하는 비용 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;대상 독자&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python과 REST API의 기본 개념을 아는 개발자&lt;/li&gt;
&lt;li&gt;로컬 LLM 실행에서 실제 서비스 운영으로 넘어가려는 사람&lt;/li&gt;
&lt;li&gt;모델 연구보다 서빙&amp;middot;성능&amp;middot;비용 최적화에 관심이 있는 사람&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;권장 학습 기간&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주 5일, 하루 1.5&lt;/p&gt;
&lt;p&gt;&lt;del&gt;2시간을 기준으로 &lt;b&gt;12주&lt;/b&gt;를 권장한다. GPU가 한 대여도 1&lt;/del&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8주차 대부분을 학습할 수 있다. 1,000 동시 요청과 Kubernetes 다중 GPU 실습은 클라우드 GPU를 짧게 빌려 수행해도 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;전체 학습 지도&lt;/h1&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;단계&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;기간&lt;/th&gt;
&lt;th&gt;핵심 주제&lt;/th&gt;
&lt;th&gt;대표 결과물&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;2일&lt;/td&gt;
&lt;td&gt;환경과 측정 기준&lt;/td&gt;
&lt;td&gt;기준 성능표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;Ollama&amp;middot;LM Studio&amp;middot;LiteLLM&lt;/td&gt;
&lt;td&gt;로컬 API 게이트웨이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;2주&lt;/td&gt;
&lt;td&gt;vLLM&amp;middot;SGLang&lt;/td&gt;
&lt;td&gt;서빙 파이프라인 2종&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;Paged Attention&amp;middot;KV 캐시&lt;/td&gt;
&lt;td&gt;긴 문맥 서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;양자화&lt;/td&gt;
&lt;td&gt;정밀도별 비교표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;추측 디코딩&lt;/td&gt;
&lt;td&gt;draft/target 비교 실험&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;라우터&amp;middot;토큰 예산&lt;/td&gt;
&lt;td&gt;정책 기반 라우터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;관측 가능성&lt;/td&gt;
&lt;td&gt;Grafana 대시보드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;부하 시험&lt;/td&gt;
&lt;td&gt;1,000 동시 요청 보고서&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;Kubernetes&amp;middot;HPA&lt;/td&gt;
&lt;td&gt;자동 확장 배포&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;ONNX&amp;middot;TensorRT&amp;middot;WebLLM&lt;/td&gt;
&lt;td&gt;엣지 데모&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1주&lt;/td&gt;
&lt;td&gt;비용 경제성&amp;middot;공개 벤치마크&lt;/td&gt;
&lt;td&gt;최종 포트폴리오&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;0. 준비와 기준선&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0.1 권장 환경&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로컬 학습용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ubuntu 22.04/24.04 또는 WSL2&lt;/li&gt;
&lt;li&gt;Python 3.11 또는 프로젝트가 지원하는 버전&lt;/li&gt;
&lt;li&gt;Docker와 Docker Compose&lt;/li&gt;
&lt;li&gt;RAM 64GB 권장, 최소 32GB&lt;/li&gt;
&lt;li&gt;SSD 여유 공간 200GB 이상 권장&lt;/li&gt;
&lt;li&gt;NVIDIA GPU 24GB 이상 권장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AMD 32GB GPU도 개념 학습, Ollama, llama.cpp 계열, 일부 SGLang&amp;middot;ROCm 실험에 사용할 수 있다. 다만 CUDA 전용 커널, TensorRT-LLM, FP8 및 일부 양자화 경로는 동일하게 재현되지 않을 수 있다. 따라서 이 과정에서는 &lt;b&gt;AMD로 가능한 실습&lt;/b&gt;과 &lt;b&gt;CUDA 환경에서 별도로 확인할 실습&lt;/b&gt;을 구분한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클라우드 실습용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 GPU: L4, L40S, A100, H100급 중 예산에 맞는 인스턴스&lt;/li&gt;
&lt;li&gt;다중 GPU: 마지막 2주에만 시간 단위로 대여&lt;/li&gt;
&lt;li&gt;Kubernetes: 로컬 &lt;code&gt;kind&lt;/code&gt;/&lt;code&gt;k3d&lt;/code&gt;로 구조를 익힌 뒤 GPU 노드에서 검증&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0.2 저장소 구조&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;llm-inference-lab/
├── README.md
├── models/
├── configs/
├── servers/
│   ├── vllm/
│   ├── sglang/
│   └── router/
├── loadtest/
├── observability/
│   ├── prometheus/
│   └── grafana/
├── kubernetes/
├── edge/
├── results/
└── reports/&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0.3 가장 먼저 정의할 지표&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;지표&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;주의점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TTFT&lt;/td&gt;
&lt;td&gt;요청부터 첫 토큰까지 걸린 시간&lt;/td&gt;
&lt;td&gt;대화형 UX에 중요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ITL&lt;/td&gt;
&lt;td&gt;토큰 사이의 평균 지연시간&lt;/td&gt;
&lt;td&gt;스트리밍 체감 속도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TPOT&lt;/td&gt;
&lt;td&gt;출력 토큰 1개당 처리시간&lt;/td&gt;
&lt;td&gt;ITL과 정의를 통일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E2E latency&lt;/td&gt;
&lt;td&gt;요청 전체 완료 시간&lt;/td&gt;
&lt;td&gt;입력&amp;middot;출력 길이를 함께 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;td&gt;초당 처리한 요청 또는 토큰&lt;/td&gt;
&lt;td&gt;요청/초와 토큰/초를 구분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Goodput&lt;/td&gt;
&lt;td&gt;SLO를 만족한 유효 처리량&lt;/td&gt;
&lt;td&gt;단순 처리량보다 운영에 유용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queue time&lt;/td&gt;
&lt;td&gt;대기열에서 기다린 시간&lt;/td&gt;
&lt;td&gt;GPU 계산 시간과 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache hit rate&lt;/td&gt;
&lt;td&gt;재사용된 prefix/KV 비율&lt;/td&gt;
&lt;td&gt;동일 프롬프트 비율과 함께 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error rate&lt;/td&gt;
&lt;td&gt;전체 요청 중 실패 비율&lt;/td&gt;
&lt;td&gt;타임아웃과 서버 오류를 구분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost/request&lt;/td&gt;
&lt;td&gt;요청 한 건의 추론 원가&lt;/td&gt;
&lt;td&gt;GPU 유휴시간도 포함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0.4 기준선 실험&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 모델, 동일한 데이터셋, 동일한 입력&amp;middot;출력 길이로 아래 표를 채운다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;| 엔진 | 모델 | 정밀도 | 동시성 | 입력/출력 토큰 | TTFT p50/p95 | ITL p50/p95 | tok/s | GPU 메모리 |
|---|---|---|---:|---:|---:|---:|---:|---:|&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;측정 스크립트를 두 번 실행해 결과 편차를 확인했다.&lt;/li&gt;
&lt;li&gt;모델명, revision, 엔진 버전, GPU, 드라이버를 기록했다.&lt;/li&gt;
&lt;li&gt;워밍업 요청을 측정값에서 분리했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 로컬 도구로 추론 흐름 익히기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.1 Ollama&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 목표는 모델 다운로드, 메모리 적재, 프롬프트 요청, 스트리밍 응답의 흐름을 이해하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ollama pull qwen3:4b
ollama run qwen3:4b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 확인:&lt;/p&gt;
&lt;pre class=&quot;flix&quot;&gt;&lt;code&gt;curl http://localhost:11434/api/generate \
  -d '{&quot;model&quot;:&quot;qwen3:4b&quot;,&quot;prompt&quot;:&quot;KV cache를 한 문장으로 설명해 줘.&quot;,&quot;stream&quot;:false}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 항목:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 요청과 두 번째 요청의 시간 차이&lt;/li&gt;
&lt;li&gt;모델이 메모리에 남아 있을 때의 변화&lt;/li&gt;
&lt;li&gt;입력 길이가 늘어날 때 TTFT 변화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.2 LM Studio&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUI에서 모델별 메모리 사용량과 오프로딩 설정을 비교한다. 로컬 서버 기능을 켠 뒤 OpenAI 호환 클라이언트로 호출한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from openai import OpenAI

client = OpenAI(base_url=&quot;http://localhost:1234/v1&quot;, api_key=&quot;local&quot;)
response = client.chat.completions.create(
    model=&quot;local-model&quot;,
    messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;continuous batching을 설명해 줘.&quot;}],
)
print(response.choices[0].message.content)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.3 LiteLLM&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LiteLLM은 여러 모델 제공자 또는 로컬 서버를 하나의 API 형태로 연결하는 게이트웨이 실습에 사용한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# configs/litellm.yaml
model_list:
  - model_name: local-fast
    litellm_params:
      model: openai/local-model
      api_base: http://localhost:1234/v1
      api_key: local&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;litellm --config configs/litellm.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;미니 프로젝트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ollama와 LM Studio 중 사용 가능한 서버를 선택하고, 실패하면 다른 서버로 전환하는 작은 게이트웨이를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;/v1/chat/completions&lt;/code&gt;로 두 개 이상의 백엔드에 요청했다.&lt;/li&gt;
&lt;li&gt;모델 로딩 시간과 순수 생성 시간을 분리했다.&lt;/li&gt;
&lt;li&gt;타임아웃과 연결 실패를 로그에서 구분했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. vLLM과 SGLang&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령의 옵션은 릴리스에 따라 달라질 수 있다. 실습 전에 각 프로젝트의 공식 최신 CLI 문서를 확인한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.1 vLLM 서버&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 가상환경에서 설치한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;python -m venv .venv-vllm
source .venv-vllm/bin/activate
python -m pip install --upgrade pip
pip install vllm&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 시작:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;vllm serve Qwen/Qwen3-4B-Instruct-2507 \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype auto \
  --api-key local-token&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청:&lt;/p&gt;
&lt;pre class=&quot;scilab&quot;&gt;&lt;code&gt;curl http://localhost:8000/v1/chat/completions \
  -H &quot;Authorization: Bearer local-token&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d '{
    &quot;model&quot;:&quot;Qwen/Qwen3-4B-Instruct-2507&quot;,
    &quot;messages&quot;:[{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;Paged Attention이 필요한 이유는?&quot;}],
    &quot;max_tokens&quot;:128
  }'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증:&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;curl http://localhost:8000/health
curl http://localhost:8000/metrics&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.2 SGLang 서버&lt;/h2&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;python -m venv .venv-sglang
source .venv-sglang/bin/activate
python -m pip install --upgrade pip
pip install &quot;sglang[all]&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;python -m sglang.launch_server \
  --model-path Qwen/Qwen3-4B-Instruct-2507 \
  --host 0.0.0.0 \
  --port 30000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 프롬프트 묶음을 vLLM과 SGLang에 보내 다음을 비교한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 요청 TTFT&lt;/li&gt;
&lt;li&gt;동시성 1, 8, 32, 128의 처리량&lt;/li&gt;
&lt;li&gt;반복 prefix가 있을 때의 처리량&lt;/li&gt;
&lt;li&gt;긴 입력과 짧은 출력&lt;/li&gt;
&lt;li&gt;짧은 입력과 긴 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.3 파이프라인 3종&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;A. 처리량 우선&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연속 배칭 활성화&lt;/li&gt;
&lt;li&gt;GPU 메모리 사용률을 점진적으로 높여 탐색&lt;/li&gt;
&lt;li&gt;동시 요청 수를 늘리며 최대 goodput 측정&lt;/li&gt;
&lt;li&gt;p95 지연시간 SLO를 넘으면 직전 설정으로 복귀&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;B. 대화형 지연시간 우선&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청당 출력 상한 설정&lt;/li&gt;
&lt;li&gt;대형 배치보다 짧은 대기열 선호&lt;/li&gt;
&lt;li&gt;스트리밍 사용&lt;/li&gt;
&lt;li&gt;TTFT p95를 핵심 SLO로 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;C. 긴 문맥 우선&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;prefix caching 또는 Radix Cache 활용&lt;/li&gt;
&lt;li&gt;최대 문맥 길이를 실제 제품 요구에 맞춰 제한&lt;/li&gt;
&lt;li&gt;KV 캐시 부족 시 선점&amp;middot;재계산&amp;middot;퇴출 현상 관찰&lt;/li&gt;
&lt;li&gt;긴 요청이 짧은 요청을 막지 않도록 스케줄링 정책 실험&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 엔진의 결과를 같은 표 형식으로 기록했다.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;어느 엔진이 항상 빠르다&amp;rdquo;가 아니라 워크로드별 승자를 설명했다.&lt;/li&gt;
&lt;li&gt;서버 시작 명령과 전체 환경 정보를 보고서에 남겼다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Paged Attention과 KV 캐시&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.1 먼저 이해할 계산&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transformer의 KV 캐시 메모리는 모델 구조와 데이터형에 따라 달라진다. 개념적으로는 다음 항목에 비례한다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;KV cache bytes
&amp;asymp; batch &amp;times; sequence_length &amp;times; layers
  &amp;times; 2(K,V) &amp;times; KV_heads &amp;times; head_dim &amp;times; bytes_per_element&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Grouped Query Attention을 사용하는 모델은 attention head 수 대신 KV head 수가 중요하다. 모델 설정 파일의 &lt;code&gt;num_key_value_heads&lt;/code&gt;, &lt;code&gt;num_hidden_layers&lt;/code&gt;, &lt;code&gt;head_dim&lt;/code&gt; 또는 이에 해당하는 값을 직접 확인한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.2 Paged Attention&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Paged Attention은 요청별 KV 캐시를 고정된 큰 연속 공간으로 잡는 대신 블록 단위로 관리해 단편화와 낭비를 줄이는 접근이다. 운영 관점에서는 다음 질문으로 연결한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블록 크기가 메모리 낭비와 관리 비용에 어떤 영향을 주는가?&lt;/li&gt;
&lt;li&gt;요청이 끝난 뒤 블록이 얼마나 빨리 재사용되는가?&lt;/li&gt;
&lt;li&gt;여러 요청이 동일 prefix를 공유할 때 어느 정도 절약되는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.3 캐시 퇴출 실험&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 가지 트래픽을 만든다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;짧은 대화 100개&lt;/li&gt;
&lt;li&gt;동일한 시스템 프롬프트를 공유하는 요청 100개&lt;/li&gt;
&lt;li&gt;긴 문서 질의 20개와 짧은 대화 100개의 혼합&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 실험에서 다음을 기록한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KV 캐시 사용률&lt;/li&gt;
&lt;li&gt;prefix cache hit rate&lt;/li&gt;
&lt;li&gt;preemption 또는 recomputation 횟수&lt;/li&gt;
&lt;li&gt;대기열 길이&lt;/li&gt;
&lt;li&gt;TTFT p50/p95/p99&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.4 퇴출 정책 설계 과제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 점수로 직접 정책을 설계한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;eviction_score =
  0.40 &amp;times; idle_time_normalized
  + 0.25 &amp;times; cache_size_normalized
  + 0.20 &amp;times; recompute_cost_inverse
  + 0.15 &amp;times; tenant_priority_inverse&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 공식은 학습용 예시다. 가중치를 바꾸며 다음 사례를 비교한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LRU&lt;/li&gt;
&lt;li&gt;가장 큰 캐시 우선&lt;/li&gt;
&lt;li&gt;재계산 비용이 낮은 항목 우선&lt;/li&gt;
&lt;li&gt;유료 사용자 우선 보존&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시가 부족해지는 시점을 그래프로 찾았다.&lt;/li&gt;
&lt;li&gt;hit rate가 높아져도 p95가 나빠질 수 있는 조건을 설명했다.&lt;/li&gt;
&lt;li&gt;퇴출 정책의 장단점을 실제 측정값으로 비교했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 양자화: INT4, FP8, AWQ, GPTQ&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.1 구분&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;th&gt;핵심 성격&lt;/th&gt;
&lt;th&gt;주로 확인할 것&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FP8&lt;/td&gt;
&lt;td&gt;낮은 정밀도의 부동소수점 연산&lt;/td&gt;
&lt;td&gt;하드웨어 지원, 처리량, 품질&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INT4&lt;/td&gt;
&lt;td&gt;4비트 정수 기반 가중치 표현&lt;/td&gt;
&lt;td&gt;메모리 절감, 커널 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWQ&lt;/td&gt;
&lt;td&gt;activation을 고려한 weight-only 양자화&lt;/td&gt;
&lt;td&gt;실제 프롬프트 품질&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPTQ&lt;/td&gt;
&lt;td&gt;2차 정보 근사를 이용한 사후 학습 양자화&lt;/td&gt;
&lt;td&gt;양자화 설정과 커널 호환성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWQ와 GPTQ는 &amp;ldquo;파일이 더 작다&amp;rdquo;만으로 평가하지 않는다. 엔진과 GPU에 맞는 커널이 없으면 메모리는 줄어도 속도가 느려질 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.2 비교 실험&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일 모델 계열에서 BF16/FP16, FP8, AWQ INT4, GPTQ INT4를 준비한다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;| 형식 | 모델 크기 | VRAM idle/peak | TTFT p95 | tok/s | 정확도 | 비고 |
|---|---:|---:|---:|---:|---:|---|&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;품질 평가는 최소 세 층으로 나눈다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;짧은 지식&amp;middot;상식 100문항&lt;/li&gt;
&lt;li&gt;한국어 지시 이행 50문항&lt;/li&gt;
&lt;li&gt;실제 서비스 프롬프트 50개&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.3 선택 규칙&lt;/h2&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;if GPU가 FP8을 효율적으로 지원하고 품질 저하가 허용 범위:
    FP8 후보
elif VRAM이 가장 큰 제약이고 지원 커널이 검증됨:
    AWQ 또는 GPTQ INT4 후보
else:
    BF16/FP16 기준선 유지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 절감률과 처리량 향상률을 따로 기록했다.&lt;/li&gt;
&lt;li&gt;정확도 평균뿐 아니라 실패 유형을 분류했다.&lt;/li&gt;
&lt;li&gt;현재 장비에서 실제로 지원하지 않는 경로를 &lt;code&gt;미검증&lt;/code&gt;으로 표시했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 추측 디코딩&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.1 개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 draft 모델이 여러 후보 토큰을 먼저 제안하고 target 모델이 이를 검증한다. 목표는 출력 품질을 유지하면서 토큰 사이 지연시간을 낮추는 것이다. 성능 향상은 모델 조합, acceptance rate, 배치 크기, 메모리 대역폭과 워크로드에 따라 달라진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.2 실험 설계&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;변수&lt;/th&gt;
&lt;th&gt;값 예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;target model&lt;/td&gt;
&lt;td&gt;7B~14B instruct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;draft model&lt;/td&gt;
&lt;td&gt;같은 tokenizer를 쓰는 소형 모델&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;speculative tokens&lt;/td&gt;
&lt;td&gt;1, 3, 5, 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동시성&lt;/td&gt;
&lt;td&gt;1, 8, 32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;출력 길이&lt;/td&gt;
&lt;td&gt;32, 256, 1,024&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집 항목:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;draft acceptance rate&lt;/li&gt;
&lt;li&gt;speculative efficiency&lt;/li&gt;
&lt;li&gt;ITL p50/p95&lt;/li&gt;
&lt;li&gt;전체 처리량&lt;/li&gt;
&lt;li&gt;추가 GPU 메모리&lt;/li&gt;
&lt;li&gt;비추측 기준선 대비 속도 변화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.3 중단 조건&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 중 하나면 해당 설정을 채택하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;p95 ITL이 기준선보다 나빠짐&lt;/li&gt;
&lt;li&gt;draft 모델 메모리 때문에 동시성이 감소함&lt;/li&gt;
&lt;li&gt;acceptance rate가 낮아 추가 계산이 이익을 상쇄함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최소 2개 draft/target 조합을 비교했다.&lt;/li&gt;
&lt;li&gt;단일 요청과 높은 동시성에서 결과가 다른 이유를 설명했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 모델 라우터와 토큰 예산&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.1 라우팅 목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 &amp;ldquo;가장 좋은 모델&amp;rdquo;을 고르는 장치가 아니라 요청의 품질 하한, 지연시간 SLO, 비용 상한을 동시에 만족하는 후보를 선택하는 정책 계층이다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;flowchart TD
    A[&quot;사용자 요청&quot;] --&amp;gt; B[&quot;정책&amp;middot;예산 검사&quot;]
    B --&amp;gt; C[&quot;난이도&amp;middot;길이 분류&quot;]
    C --&amp;gt; D{&quot;후보 모델&quot;}
    D --&amp;gt; E[&quot;저비용 모델&quot;]
    D --&amp;gt; F[&quot;저지연 모델&quot;]
    D --&amp;gt; G[&quot;고품질 모델&quot;]
    E --&amp;gt; H[&quot;응답&amp;middot;비용 기록&quot;]
    F --&amp;gt; H
    G --&amp;gt; H&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.2 최소 데이터 모델&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from dataclasses import dataclass

@dataclass
class ModelProfile:
    name: str
    input_cost_per_million: float
    output_cost_per_million: float
    p95_ttft_ms: float
    p95_itl_ms: float
    quality_score: float
    max_context: int&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.3 점수 함수&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;def route_score(
    quality: float,
    latency_ms: float,
    estimated_cost: float,
    quality_weight: float = 0.5,
    latency_weight: float = 0.3,
    cost_weight: float = 0.2,
) -&amp;gt; float:
    return (
        quality_weight * quality
        - latency_weight * latency_ms
        - cost_weight * estimated_cost
    )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 구현에서는 단위가 다른 값을 그대로 더하지 말고 0~1 범위로 정규화한다. 또한 품질 하한과 비용 상한을 먼저 적용한 후 남은 후보에 점수를 계산한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.4 토큰 예산&lt;/h2&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;def output_budget(
    plan_limit: int,
    used_tokens: int,
    requested_max_tokens: int,
    reserve_tokens: int = 256,
) -&amp;gt; int:
    remaining = max(0, plan_limit - used_tokens - reserve_tokens)
    return min(requested_max_tokens, remaining)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수 정책:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자&amp;middot;팀&amp;middot;API 키별 일/월 토큰 한도&lt;/li&gt;
&lt;li&gt;요청당 최대 입력&amp;middot;출력 토큰&lt;/li&gt;
&lt;li&gt;시스템 프롬프트와 도구 호출 토큰 포함 여부&lt;/li&gt;
&lt;li&gt;스트리밍 도중 예산 소진 처리&lt;/li&gt;
&lt;li&gt;재시도와 fallback 요청의 중복 과금 처리&lt;/li&gt;
&lt;li&gt;예약량과 실제 사용량의 정산&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.5 라우터 평가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 규칙, latency 기반, cost 기반, 품질 포함 다목적 라우터를 같은 요청 집합으로 비교한다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;| 정책 | 성공률 | 품질 | p95 지연 | 평균 원가 | SLO 위반률 |
|---|---:|---:|---:|---:|---:|&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라우팅 이유를 요청 로그에서 재현할 수 있다.&lt;/li&gt;
&lt;li&gt;예산 초과 요청은 GPU에 도달하기 전에 거절된다.&lt;/li&gt;
&lt;li&gt;fallback이 무한 반복되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 관측 가능성: Prometheus와 Grafana&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.1 반드시 수집할 네 종류&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요청&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청 수, 성공 수, 오류 수&lt;/li&gt;
&lt;li&gt;입력&amp;middot;출력 토큰&lt;/li&gt;
&lt;li&gt;모델, tenant, 상태 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지연시간&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TTFT&lt;/li&gt;
&lt;li&gt;ITL/TPOT&lt;/li&gt;
&lt;li&gt;E2E&lt;/li&gt;
&lt;li&gt;queue time&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엔진&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 중&amp;middot;대기 중 요청&lt;/li&gt;
&lt;li&gt;KV 캐시 사용률과 hit rate&lt;/li&gt;
&lt;li&gt;preemption&lt;/li&gt;
&lt;li&gt;batch 크기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자원&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU 사용률, 메모리, 전력&lt;/li&gt;
&lt;li&gt;CPU, RAM, 네트워크&lt;/li&gt;
&lt;li&gt;Pod 재시작과 OOM&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.2 카디널리티 경고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prometheus label에 &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;request_id&lt;/code&gt;, 전체 프롬프트를 넣지 않는다. 사용자 단위 상세 기록은 로그 또는 분석 저장소에 보내고, 메트릭은 plan, model, region처럼 제한된 값만 사용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.3 대시보드 구성&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서비스 건강: RPS, error rate, p95 E2E&lt;/li&gt;
&lt;li&gt;사용자 경험: TTFT와 ITL&lt;/li&gt;
&lt;li&gt;GPU 효율: utilization, memory, tokens/s&lt;/li&gt;
&lt;li&gt;스케줄러: running/waiting requests, queue time&lt;/li&gt;
&lt;li&gt;캐시: KV 사용률, prefix hit rate, preemption&lt;/li&gt;
&lt;li&gt;비용: 시간당 비용, 요청당 비용, 1M token당 비용&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.4 알림 예시&lt;/h2&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;groups:
  - name: llm-serving
    rules:
      - alert: LLMHighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~&quot;5..&quot;}[5m]))
          /
          clamp_min(sum(rate(http_requests_total[5m])), 1)
          &amp;gt; 0.02
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: &quot;LLM API 5xx 비율이 2%를 초과했습니다.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메트릭 이름은 실제 애플리케이션에서 노출하는 이름에 맞춰 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;느린 응답이 queue, prefill, decode 중 어디서 발생했는지 구분한다.&lt;/li&gt;
&lt;li&gt;대시보드의 수치와 원시 요청 로그를 표본 대조했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 1,000+ 동시 요청 부하 시험&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.1 도구&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;k6&lt;/code&gt;: HTTP 부하와 시나리오 작성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Locust&lt;/code&gt;: Python 기반 사용자 행동 모델&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vegeta&lt;/code&gt;: 단순 HTTP 처리량 확인&lt;/li&gt;
&lt;li&gt;엔진별 benchmark 도구: 순수 추론 성능 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 스트리밍 응답은 일반 HTTP 완료 시간만 측정하면 TTFT와 ITL을 놓칠 수 있다. SSE 청크의 도착 시간을 기록하는 전용 클라이언트를 준비한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.2 단계적 증가&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1 &amp;rarr; 8 &amp;rarr; 32 &amp;rarr; 64 &amp;rarr; 128 &amp;rarr; 256 &amp;rarr; 512 &amp;rarr; 1,000 &amp;rarr; 1,500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계에서 최소 5분간 안정 상태를 측정한다. 오류율 또는 p95가 SLO를 넘으면 더 높은 단계로 진행하지 않고 원인을 찾는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.3 현실적인 요청 분포&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;유형&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;비율&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;입력 토큰&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;출력 토큰&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;짧은 질의&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;50%&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;64~256&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;32~128&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;일반 대화&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;30%&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;256~1,024&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;128~512&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;긴 문서&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;15%&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;4K~16K&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;128~512&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;긴 생성&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;5%&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;256~1K&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1K~4K&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.4 보고서&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동시성별 offered load와 completed throughput&lt;/li&gt;
&lt;li&gt;p50/p95/p99 TTFT, ITL, E2E&lt;/li&gt;
&lt;li&gt;timeout, 429, 5xx&lt;/li&gt;
&lt;li&gt;GPU 사용률과 메모리&lt;/li&gt;
&lt;li&gt;queue length&lt;/li&gt;
&lt;li&gt;SLO를 만족하는 최대 goodput&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1,000 연결을 열었다는 사실과 1,000 요청이 성공했다는 사실을 구분했다.&lt;/li&gt;
&lt;li&gt;클라이언트 CPU&amp;middot;네트워크가 병목이 아닌지 확인했다.&lt;/li&gt;
&lt;li&gt;실패 지점과 안전 운영 한계를 명시했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Kubernetes와 GPU 자동 확장&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.1 학습 순서&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Deployment와 Service&lt;/li&gt;
&lt;li&gt;GPU resource request&lt;/li&gt;
&lt;li&gt;readiness/liveness probe&lt;/li&gt;
&lt;li&gt;rolling update&lt;/li&gt;
&lt;li&gt;node selector, taint, toleration&lt;/li&gt;
&lt;li&gt;HPA&lt;/li&gt;
&lt;li&gt;Prometheus custom metrics&lt;/li&gt;
&lt;li&gt;queue 기반 확장&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.2 Deployment 골격&lt;/h2&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: llm-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: llm-server
  template:
    metadata:
      labels:
        app: llm-server
    spec:
      containers:
        - name: server
          image: your-registry/llm-server:VERSION
          ports:
            - containerPort: 8000
          resources:
            limits:
              nvidia.com/gpu: 1
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 60
            periodSeconds: 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 태그와 health endpoint는 실제 배포물에 맞춰 검증한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.3 HPA 골격&lt;/h2&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: llm-server
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: llm-server
  minReplicas: 1
  maxReplicas: 8
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 600
  metrics:
    - type: Pods
      pods:
        metric:
          name: llm_waiting_requests
        target:
          type: AverageValue
          averageValue: &quot;4&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시는 custom metrics adapter가 &lt;code&gt;llm_waiting_requests&lt;/code&gt;를 제공한다고 가정한다. GPU Pod는 모델 로딩 시간이 길기 때문에 CPU 사용률만으로 확장하면 늦을 수 있다. queue length, waiting requests, 예상 대기시간 같은 선행 지표를 비교한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.4 운영 실험&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0&amp;rarr;100 요청 급증&lt;/li&gt;
&lt;li&gt;10분 동안 점진 증가&lt;/li&gt;
&lt;li&gt;한 Pod 강제 종료&lt;/li&gt;
&lt;li&gt;새 모델 이미지 rolling update&lt;/li&gt;
&lt;li&gt;노드 하나 장애&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새 Pod가 준비되기 전에는 트래픽을 받지 않는다.&lt;/li&gt;
&lt;li&gt;scale-down 중 진행 중 요청의 종료 정책을 검증했다.&lt;/li&gt;
&lt;li&gt;모델 다운로드 시간이 확장 지연에 포함됨을 측정했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 엣지 배포&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.1 ONNX Runtime&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 encoder 또는 소형 생성 모델을 ONNX로 변환해 CPU, DirectML, CUDA 실행 공급자를 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 항목:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 shape와 동적 shape&lt;/li&gt;
&lt;li&gt;graph optimization&lt;/li&gt;
&lt;li&gt;INT8 양자화&lt;/li&gt;
&lt;li&gt;cold start와 warm latency&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.2 TensorRT 계열&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CUDA 환경에서만 별도 트랙으로 진행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔진 빌드 시간과 실행 시간 구분&lt;/li&gt;
&lt;li&gt;지원 precision 확인&lt;/li&gt;
&lt;li&gt;고정/동적 shape profile&lt;/li&gt;
&lt;li&gt;GPU 아키텍처와 엔진 호환성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AMD 장비에서는 이 트랙을 개념 학습 후 클라우드 NVIDIA GPU에서 검증한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.3 WebLLM&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 WebGPU로 소형 모델을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;측정 항목:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초 모델 다운로드 크기와 시간&lt;/li&gt;
&lt;li&gt;브라우저별 WebGPU 지원&lt;/li&gt;
&lt;li&gt;prefill/decode 속도&lt;/li&gt;
&lt;li&gt;탭 전환과 메모리 회수&lt;/li&gt;
&lt;li&gt;개인정보가 서버로 전송되지 않는 로컬 처리 범위&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.4 엣지 의사결정표&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;조건&lt;/th&gt;
&lt;th&gt;후보&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;브라우저 오프라인 실행&lt;/td&gt;
&lt;td&gt;WebLLM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데스크톱/모바일 네이티브&lt;/td&gt;
&lt;td&gt;ONNX Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NVIDIA 서버 최대 성능&lt;/td&gt;
&lt;td&gt;TensorRT 계열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;개발 편의와 폭넓은 모델&lt;/td&gt;
&lt;td&gt;llama.cpp/Ollama 계열&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 작은 모델을 서버와 엣지에서 비교했다.&lt;/li&gt;
&lt;li&gt;다운로드 비용, cold start, 전력, 개인정보를 성능표에 포함했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 추론 비용과 단위 경제성&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.1 기본 공식&lt;/h2&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;GPU hourly cost
= 인스턴스 비용 + 스토리지 + 네트워크 + 관측 비용

effective tokens per hour
= measured tokens/s &amp;times; 3,600 &amp;times; utilization &amp;times; success_rate

cost per 1M tokens
= GPU hourly cost / effective tokens per hour &amp;times; 1,000,000

cost per request
= input_token_cost + output_token_cost
  + routing + storage + network + retry overhead&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.2 손익분기점&lt;/h2&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;gross margin per request
= revenue per request - variable cost per request

break-even requests per month
= monthly fixed cost / gross margin per request&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.3 반드시 포함할 숨은 비용&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;낮은 사용률과 유휴 GPU&lt;/li&gt;
&lt;li&gt;모델 로딩과 재배포 시간&lt;/li&gt;
&lt;li&gt;실패&amp;middot;재시도&amp;middot;fallback&lt;/li&gt;
&lt;li&gt;긴 system prompt&lt;/li&gt;
&lt;li&gt;사용하지 않고 남은 예약 용량&lt;/li&gt;
&lt;li&gt;로그&amp;middot;메트릭&amp;middot;네트워크&lt;/li&gt;
&lt;li&gt;엔지니어 운영 시간&lt;/li&gt;
&lt;li&gt;품질 저하로 발생하는 재질문&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.4 실습&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 시나리오를 비교한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;단일 고성능 GPU 자체 서빙&lt;/li&gt;
&lt;li&gt;여러 저가 GPU 자체 서빙&lt;/li&gt;
&lt;li&gt;외부 API 사용&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트래픽이 낮음/보통/높음일 때 월 비용과 gross margin을 계산한다. 자체 서빙은 최고 처리량이 아니라 실제 평균 사용률로 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가격과 성능 수치의 기준일을 기록했다.&lt;/li&gt;
&lt;li&gt;최선/기준/최악 시나리오를 분리했다.&lt;/li&gt;
&lt;li&gt;비용 절감이 품질과 SLO에 미치는 영향을 함께 제시했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 최종 프로젝트&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 요구사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 아키텍처를 구현한다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;flowchart TD
    A[&quot;클라이언트&quot;] --&amp;gt; B[&quot;API&amp;middot;토큰 예산&quot;]
    B --&amp;gt; C[&quot;모델 라우터&quot;]
    C --&amp;gt; D[&quot;vLLM 풀&quot;]
    C --&amp;gt; E[&quot;SGLang 풀&quot;]
    D --&amp;gt; F[&quot;Prometheus&quot;]
    E --&amp;gt; F
    F --&amp;gt; G[&quot;Grafana&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenAI 호환 chat completions API&lt;/li&gt;
&lt;li&gt;최소 2개 모델 또는 2개 배포&lt;/li&gt;
&lt;li&gt;비용&amp;middot;지연시간&amp;middot;품질 기반 라우팅&lt;/li&gt;
&lt;li&gt;사용자별 일일 토큰 예산&lt;/li&gt;
&lt;li&gt;스트리밍&lt;/li&gt;
&lt;li&gt;요청&amp;middot;토큰&amp;middot;오류&amp;middot;비용 기록&lt;/li&gt;
&lt;li&gt;Prometheus 메트릭&lt;/li&gt;
&lt;li&gt;Grafana 대시보드&lt;/li&gt;
&lt;li&gt;부하 시험&lt;/li&gt;
&lt;li&gt;Docker Compose 로컬 실행&lt;/li&gt;
&lt;li&gt;선택: Kubernetes와 HPA&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서빙 파이프라인&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;처리량 최적화 vLLM&lt;/li&gt;
&lt;li&gt;prefix 공유에 최적화한 SGLang&lt;/li&gt;
&lt;li&gt;양자화 또는 추측 디코딩을 적용한 실험 파이프라인&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공개 벤치마크 필수 정보&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;date:
git_commit:
model_id:
model_revision:
engine:
engine_version:
container_image:
gpu:
gpu_count:
driver:
cuda_or_rocm:
cpu:
ram:
prompt_dataset:
input_length_distribution:
output_length_distribution:
concurrency:
warmup:
duration:
sampling_parameters:&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 평가표&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;영역&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;배점&lt;/th&gt;
&lt;th&gt;통과 기준&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;재현성&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;20&lt;/td&gt;
&lt;td&gt;다른 사람이 명령대로 실행 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;성능 측정&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;20&lt;/td&gt;
&lt;td&gt;TTFT&amp;middot;ITL&amp;middot;goodput&amp;middot;오류 포함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;캐시&amp;middot;배칭&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;15&lt;/td&gt;
&lt;td&gt;적용 전후 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;라우팅&amp;middot;예산&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;15&lt;/td&gt;
&lt;td&gt;결정 이유와 제한 동작 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관측&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10&lt;/td&gt;
&lt;td&gt;병목 위치를 대시보드에서 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;부하 시험&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10&lt;/td&gt;
&lt;td&gt;현실적인 분포와 안전 중단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비용&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10&lt;/td&gt;
&lt;td&gt;요청당 원가와 손익분기점&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;매일 공부 루틴&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;90분 버전&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;15분: 공식 문서 또는 논문 한 절 읽기&lt;/li&gt;
&lt;li&gt;45분: 하나의 변수만 바꾸는 실험&lt;/li&gt;
&lt;li&gt;15분: 그래프와 표 업데이트&lt;/li&gt;
&lt;li&gt;10분: 실패 원인 기록&lt;/li&gt;
&lt;li&gt;5분: 다음 실험의 가설 작성&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실험 노트 양식&lt;/h2&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 실험 제목

## 가설

## 고정 조건

## 변경 변수

## 실행 명령

## 예상 결과

## 실제 결과

## 해석

## 미검증 항목

## 다음 실험&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;논문과 자료를 읽는 법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 출시 뉴스보다 다음 질문에 답하는 자료를 우선한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 이동량을 어떻게 줄였는가?&lt;/li&gt;
&lt;li&gt;prefill과 decode를 어떻게 나눴는가?&lt;/li&gt;
&lt;li&gt;배칭과 스케줄링이 tail latency에 어떤 영향을 주는가?&lt;/li&gt;
&lt;li&gt;KV 캐시를 어떻게 배치&amp;middot;압축&amp;middot;퇴출하는가?&lt;/li&gt;
&lt;li&gt;품질을 유지하면서 연산량을 어떻게 줄였는가?&lt;/li&gt;
&lt;li&gt;어떤 하드웨어와 워크로드에서만 이득이 있었는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽을 주제:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PagedAttention&lt;/li&gt;
&lt;li&gt;continuous batching&lt;/li&gt;
&lt;li&gt;prefix caching와 RadixAttention&lt;/li&gt;
&lt;li&gt;speculative decoding, Medusa, EAGLE 계열&lt;/li&gt;
&lt;li&gt;disaggregated prefill/decode&lt;/li&gt;
&lt;li&gt;KV cache quantization과 eviction&lt;/li&gt;
&lt;li&gt;tensor/pipeline/data/expert parallelism&lt;/li&gt;
&lt;li&gt;goodput과 SLO-aware scheduling&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문 요약에는 반드시 &lt;b&gt;기준선, 하드웨어, 모델, 입력&amp;middot;출력 길이, 동시성, 한계&lt;/b&gt;를 기록한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;자주 발생하는 실패&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CUDA/ROCm 메모리 부족&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인 순서:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다른 프로세스가 GPU 메모리를 쓰는지 확인&lt;/li&gt;
&lt;li&gt;최대 문맥 길이와 동시 요청 수 축소&lt;/li&gt;
&lt;li&gt;GPU memory utilization 설정 점검&lt;/li&gt;
&lt;li&gt;양자화 모델로 기준선 재구성&lt;/li&gt;
&lt;li&gt;CPU offload는 속도 저하를 포함해 다시 측정&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처리량은 높은데 응답이 느림&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 높은 배치 크기로 총 처리량은 올라갔지만 queue time 또는 TTFT가 악화됐을 가능성이 있다. 대기시간과 실제 GPU 실행 시간을 분리해 확인한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPU 사용률이 낮음&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 입력 준비, 토크나이징, 네트워크, 작은 배치 또는 부하 생성기가 병목일 가능성이 있다. GPU만 보지 말고 CPU, queue, batch size, 클라이언트 사용률을 함께 확인한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;양자화했는데 느려짐&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 현재 GPU&amp;middot;엔진에 최적화된 커널이 없거나 dequantization 비용이 이득을 상쇄했을 가능성이 있다. 동일 동시성과 동일 길이에서 커널 및 정밀도별 결과를 비교한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HPA가 늦게 확장됨&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] CPU 사용률이 대기열 증가보다 늦게 반응하거나 모델 로딩 시간이 긴 것이 원인일 수 있다. waiting requests와 예상 대기시간을 선행 지표 후보로 검증한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;공식 문서&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.vllm.ai/&quot;&gt;vLLM 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.vllm.ai/en/stable/serving/online_serving/&quot;&gt;vLLM OpenAI 호환 서버&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.vllm.ai/en/stable/design/metrics/&quot;&gt;vLLM 메트릭&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.sglang.ai/&quot;&gt;SGLang 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.litellm.ai/docs/routing&quot;&gt;LiteLLM 라우팅&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/&quot;&gt;Kubernetes Horizontal Pod Autoscaling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://prometheus.io/docs/&quot;&gt;Prometheus 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://grafana.com/docs/&quot;&gt;Grafana 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://onnxruntime.ai/docs/&quot;&gt;ONNX Runtime 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nvidia.github.io/TensorRT-LLM/&quot;&gt;NVIDIA TensorRT-LLM 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webllm.mlc.ai/docs/&quot;&gt;WebLLM 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.ollama.com/&quot;&gt;Ollama 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;시작 체크리스트&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; GPU와 드라이버 정보를 기록했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 모델과 엔진 버전을 고정했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; TTFT, ITL, throughput, goodput 정의를 정했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Ollama 또는 LM Studio API를 호출했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; vLLM 서버를 실행했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; SGLang 서버를 실행했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 동일 워크로드로 두 엔진을 비교했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; KV 캐시 사용률과 cache hit rate를 관찰했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 양자화 전후 메모리&amp;middot;속도&amp;middot;품질을 비교했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 추측 디코딩의 acceptance rate를 측정했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 모델 라우터와 토큰 예산을 구현했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Prometheus와 Grafana를 연결했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 1,000 동시 요청 시험을 수행했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Kubernetes HPA를 검증했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 요청당 원가와 손익분기점을 계산했다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 모든 명령과 환경을 공개 보고서에 기록했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;검증 범위와 주의사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서는 학습 사이트의 구조와 실습 설계를 제공한다. 이 환경에는 GPU가 연결되어 있지 않아 vLLM, SGLang, CUDA, ROCm, Kubernetes GPU 배포 명령을 실제 실행하지 않았다. 따라서 각 실습의 성공 결과는 &lt;b&gt;미검증&lt;/b&gt;이며, 사용하는 GPU&amp;middot;드라이버&amp;middot;엔진 릴리스의 공식 문서에서 지원 조합과 CLI 옵션을 확인해야 한다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1795</guid>
      <comments>https://javaexpert.tistory.com/1795#entry1795comment</comments>
      <pubDate>Wed, 29 Jul 2026 14:35:33 +0900</pubDate>
    </item>
    <item>
      <title>Unity CLI 에 대해 알아보자</title>
      <link>https://javaexpert.tistory.com/1794</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Unity CLI란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Unity CLI&lt;/b&gt;는 Unity Hub 화면을 열지 않고 터미널에서 Unity 개발환경을 관리하는 공식 명령줄 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 역할은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity Editor 설치&lt;/li&gt;
&lt;li&gt;Android&amp;middot;iOS&amp;middot;WebGL 모듈 설치&lt;/li&gt;
&lt;li&gt;설치된 Editor 목록 확인&lt;/li&gt;
&lt;li&gt;프로젝트에 맞는 Unity 버전으로 실행&lt;/li&gt;
&lt;li&gt;Unity 계정 로그인&lt;/li&gt;
&lt;li&gt;CLI 업데이트&lt;/li&gt;
&lt;li&gt;CI/CD 환경 자동 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Unity Hub의 CLI 기능과 달리, &lt;b&gt;독립된 unity 네이티브 실행 파일&lt;/b&gt;로 제공되므로 CI 서버나 AI 에이전트가 호출하기에 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 글 작성 시점 기준으로 &lt;b&gt;Unity CLI와 Unity Pipeline 모두 experimental 단계&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 설치&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;macOS / Linux&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;curl -fsSL https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.sh \
  | UNITY_CLI_CHANNEL=beta bash
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Windows PowerShell&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;$env:UNITY_CLI_CHANNEL='beta'
irm https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.ps1 | iex
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 후 터미널을 다시 열고 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;unity --version
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Unity Editor 설치&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최신 LTS 설치&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;unity install lts
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정 버전 설치&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;unity install 6000.3.7f1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;플랫폼 모듈과 함께 설치&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;unity install lts -m android ios webgl
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기존 Editor에 모듈 추가&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;unity install-modules -e 6000.3.7f1 -m android ios
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 모듈 추가가 가능한 Editor가 다음 방식으로 설치된 경우에 한정된다는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity Hub로 설치&lt;/li&gt;
&lt;li&gt;Unity CLI로 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수동으로 복사하거나 직접 배치한 Editor는 모듈 추가 대상이 아닐 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Unity 프로젝트 실행&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;unity open ./MyProject
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;open은 생략할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;unity ./MyProject
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity CLI가 프로젝트의 설정을 읽고 해당 프로젝트에 필요한 Unity Editor 버전을 찾아 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 프로젝트마다 Unity 버전이 다를 때 직접 Editor 실행 경로를 지정하는 작업을 줄일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 인증과 업데이트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unity 계정 로그인&lt;/h3&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;unity auth login
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로그인 상태 확인&lt;/h3&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;unity auth status
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unity CLI 업데이트&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;unity upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤드리스 서버나 CI 환경에서는 환경변수를 이용한 서비스 계정 인증도 지원하는 구조입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. CI/CD 활용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity CLI는 자동화 도구가 결과를 읽기 쉽도록 구조화된 출력 형식을 지원합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JSON 출력&lt;/h3&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;unity editors --format json
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TSV 출력&lt;/h3&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;unity editors --format tsv
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표준 출력과 오류 출력이 분리되고 종료 코드도 명확합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 코드의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;성공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;오류&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;130&lt;/td&gt;
&lt;td&gt;사용자 또는 작업 취소&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인 질문 없이 자동 설치할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;unity install lts \
  -m android ios \
  --accept-eula \
  --yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경이나 인증 문제를 진단할 때는 다음 명령을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;unity doctor
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Actions 같은 CI에서는 다음 작업을 자동화할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;저장소 체크아웃
&amp;rarr; Unity CLI 설치
&amp;rarr; 지정 Unity 버전 설치
&amp;rarr; 플랫폼 모듈 설치
&amp;rarr; 인증
&amp;rarr; 테스트
&amp;rarr; 빌드
&amp;rarr; 결과 및 종료 코드 확인
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. Unity CLI와 Unity Pipeline의 차이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 도구의 역할을 구분하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분Unity CLIUnity Pipeline&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;주요 목적&lt;/td&gt;
&lt;td&gt;Unity 설치 및 실행환경 관리&lt;/td&gt;
&lt;td&gt;실행 중인 Editor&amp;middot;Player 외부 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor 설치&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;담당하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모듈 설치&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;담당하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;프로젝트 실행&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;실행 후 내부 조작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Play 모드 제어&lt;/td&gt;
&lt;td&gt;Pipeline을 통해 가능&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unity API 호출&lt;/td&gt;
&lt;td&gt;직접적인 핵심 기능 아님&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C# 코드 실행&lt;/td&gt;
&lt;td&gt;직접적인 핵심 기능 아님&lt;/td&gt;
&lt;td&gt;eval 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI 에이전트 연동&lt;/td&gt;
&lt;td&gt;환경 관리 담당&lt;/td&gt;
&lt;td&gt;실제 Editor 조작 담당&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Unity CLI
= Unity 개발환경을 설치하고 프로젝트를 실행하는 도구

Unity Pipeline
= 실행 중인 Unity Editor와 Player를 외부에서 조작하는 도구
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Unity Pipeline 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 실행한 상태에서 다음 명령으로 Pipeline 패키지를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;unity pipeline install
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity Pipeline은 제공된 글 기준으로 &lt;b&gt;Unity 6.0 LTS 이상&lt;/b&gt;을 대상으로 하며, 로컬 API를 통해 Unity CLI와 Editor를 연결합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 가능한 명령 목록을 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;unity command
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 명령을 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;unity command &amp;lt;command-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 프로젝트에 등록된 테스트나 에디터 도구를 다음처럼 호출하는 구조입니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;unity command run-tests
unity command enter-play-mode
unity command generate-assets
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 실제 명령 이름은 프로젝트에 등록된 명령에 따라 달라집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 프로젝트 전용 CLI 명령 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 정적 메서드에 CliCommand 속성을 붙이면 프로젝트 전용 명령을 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;using Unity.Pipeline.Commands;
using UnityEngine;

public static class MyPipelineCommands
{
    [CliCommand(&quot;greet&quot;, &quot;Log a greeting and return its length&quot;)]
    public static int Greet(
        [CliArg(&quot;name&quot;, &quot;Who to greet&quot;, Required = true)] string name)
    {
        Debug.Log($&quot;Hello, {name}!&quot;);
        return name.Length;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서는 다음처럼 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;unity command greet --name World
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 복잡한 등록 코드 없이 속성이 붙은 메서드가 자동으로 검색됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식으로 다음 기능을 CLI 명령으로 공개할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;씬 생성 및 변경&lt;/li&gt;
&lt;li&gt;GameObject 배치&lt;/li&gt;
&lt;li&gt;프리팹 생성&lt;/li&gt;
&lt;li&gt;에셋 임포트&lt;/li&gt;
&lt;li&gt;테스트 실행&lt;/li&gt;
&lt;li&gt;프로젝트 검증&lt;/li&gt;
&lt;li&gt;빌드&lt;/li&gt;
&lt;li&gt;Play 모드 진입&lt;/li&gt;
&lt;li&gt;커스텀 에디터 도구 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. Unity Editor 안에서 C# 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity Pipeline은 실행 중인 Editor 내부에서 C# 표현식이나 코드를 실행하는 eval 기능을 제공합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unity 버전 확인&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;unity command eval &quot;return Application.version;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Play 모드 상태 확인&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;unity command eval \
  &quot;return UnityEditor.EditorApplication.isPlaying;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;C# 파일 실행&lt;/h3&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;unity command eval_file &quot;path/to/script.cs&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 Roslyn으로 컴파일되어 Unity Editor의 메인 스레드에서 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 전체 프로젝트를 다시 컴파일하거나 도메인을 리로드하지 않고도 Unity API를 호출할 수 있다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 eval은 임의의 Unity API를 실행할 수 있으므로 보안상 매우 강력한 기능입니다. 제공된 글에 따르면 실행 시 보안 토큰이 필요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 개발용 Player 제어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity Pipeline 런타임 컴포넌트를 Development Build에 포함하면 Editor뿐 아니라 실행 중인 게임 Player도 제어할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;unity command --runtime &amp;lt;player-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 활용 사례는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Player 로그 수집&lt;/li&gt;
&lt;li&gt;게임 상태 조회&lt;/li&gt;
&lt;li&gt;런타임 API 호출&lt;/li&gt;
&lt;li&gt;자동 QA 수행&lt;/li&gt;
&lt;li&gt;개발용 Player 코드 핫리로드&lt;/li&gt;
&lt;li&gt;특정 위치나 게임 상태 재현&lt;/li&gt;
&lt;li&gt;테스트 캐릭터 또는 데이터를 주입&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능은 기본적으로 비활성화되어 있고 로컬호스트 접속을 전제로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 배포용 본 게임보다는 다음 환경에 적합합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발용 빌드&lt;/li&gt;
&lt;li&gt;내부 QA 빌드&lt;/li&gt;
&lt;li&gt;자동 테스트용 빌드&lt;/li&gt;
&lt;li&gt;에이전트 검증용 빌드&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. AI 에이전트와의 연동&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity CLI와 Pipeline이 중요한 이유는 AI가 단순히 C# 코드만 작성하는 수준에서 벗어나 &lt;b&gt;실제로 Unity를 실행하고 결과를 검증할 수 있는 기반&lt;/b&gt;을 제공하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 AI 코딩 흐름은 보통 다음과 같았습니다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;AI가 코드 작성
&amp;rarr; 사람이 Unity Editor 실행
&amp;rarr; 사람이 컴파일 오류 확인
&amp;rarr; 사람이 Play 모드 실행
&amp;rarr; 결과를 다시 AI에게 설명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity CLI와 Pipeline을 연결하면 다음 흐름으로 확장할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;AI가 프로젝트 분석
&amp;rarr; Unity 프로젝트 실행
&amp;rarr; C# 코드 수정
&amp;rarr; Unity 컴파일 상태 확인
&amp;rarr; 테스트 실행
&amp;rarr; Play 모드 진입
&amp;rarr; 로그 및 상태 조회
&amp;rarr; 오류 분석
&amp;rarr; 코드 재수정
&amp;rarr; 다시 테스트
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 JSON 출력, 공통 종료 코드, 커스텀 CLI 명령이 있기 때문에 AI가 Unity 화면을 이미지로 분석하지 않고도 구조화된 결과를 받을 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. MCP와 연결했을 때의 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 다음 구조는 제공된 기능 설명을 기반으로 정리한 일반적인 연동 방식입니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Claude Code / Codex / Gemini CLI / 자체 AI 에이전트
                    &amp;darr;
                 MCP 서버
                    &amp;darr;
               Unity CLI
                    &amp;darr;
             Unity Pipeline
                    &amp;darr;
       Unity Editor 또는 Development Player
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 도구는 예를 들어 다음 형태로 설계할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;unity_open_project
unity_get_commands
unity_execute_command
unity_eval
unity_run_tests
unity_enter_play_mode
unity_get_console_logs
unity_build_project
unity_inspect_scene
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 eval을 AI에게 직접 무제한 공개하는 것은 위험할 수 있으므로, 실무에서는 허용된 작업만 수행하는 커스텀 명령을 만드는 편이 더 안전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[CliCommand(&quot;validate-scene&quot;, &quot;Validate the active scene&quot;)]
public static ValidationResult ValidateScene()
{
    // 허용된 검증 작업만 수행
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. 가장 유용한 실무 활용 사례&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자동 개발환경 구성&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;unity install 6000.3.7f1 -m android --accept-eula --yes
unity auth login
unity ./MyProject
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자동 테스트&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;unity command run-tests
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로젝트 상태 검사&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;unity command validate-project
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI가 생성한 씬 검증&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;AI가 씬 생성 코드 작성
&amp;rarr; Unity CLI로 프로젝트 실행
&amp;rarr; Pipeline 명령으로 씬 생성
&amp;rarr; Play 모드 실행
&amp;rarr; 로그 확인
&amp;rarr; 실패 시 재수정
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모바일 빌드 자동화&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Unity 설치
&amp;rarr; Android 또는 iOS 모듈 설치
&amp;rarr; 프로젝트 열기
&amp;rarr; 테스트
&amp;rarr; 빌드 명령 실행
&amp;rarr; 빌드 산출물 확인
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. 주의할 점&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;아직 실험 단계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity CLI와 Unity Pipeline 모두 experimental이므로 명령 형식이나 API가 변경될 가능성이 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;eval 보안&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eval은 Unity Editor 안에서 임의의 C# 코드를 실행할 수 있습니다. 신뢰할 수 없는 입력이나 외부 네트워크에 직접 노출하면 위험합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Editor 메인 스레드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pipeline 코드가 Editor 메인 스레드에서 실행되므로 장시간 작업을 동기적으로 실행하면 Editor가 멈춘 것처럼 보일 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;커스텀 명령 우선&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 연동에서는 자유로운 eval보다 명시적으로 검증된 CliCommand를 제공하는 편이 적절합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;권장:
unity command build-android
unity command validate-scene
unity command run-game-tests

주의:
unity command eval &quot;&amp;lt;AI가 생성한 임의 코드&amp;gt;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발용 Player에서만 사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임 제어 기능은 배포용 빌드가 아니라 Development Build와 QA 환경 중심으로 사용하는 것이 적합합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;핵심 요약&lt;/h1&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;Unity CLI
- Unity Editor 설치
- 플랫폼 모듈 설치
- 프로젝트 실행
- 계정 인증
- CI 환경 구성

Unity Pipeline
- 실행 중인 Editor 제어
- 커스텀 CLI 명령 실행
- C# 코드 실행
- Play 모드와 테스트 제어
- Development Player 제어

두 도구를 결합하면
AI가 Unity 프로젝트를 수정하는 데서 끝나지 않고
실행&amp;middot;테스트&amp;middot;로그 확인&amp;middot;재수정까지 자동화할 수 있다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 변화는 &lt;b&gt;Unity Hub를 CLI로 대체하는 것 자체가 아니라, Unity Editor를 AI 에이전트가 검증 가능한 형태로 외부 제어할 수 있게 되었다는 점&lt;/b&gt;입니다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1794</guid>
      <comments>https://javaexpert.tistory.com/1794#entry1794comment</comments>
      <pubDate>Wed, 29 Jul 2026 09:22:50 +0900</pubDate>
    </item>
    <item>
      <title>ChatGPT Sites 내부에선 어떻게 돌아갈까?</title>
      <link>https://javaexpert.tistory.com/1793</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT의 Sites 기능은 단순히 HTML 파일을 만들어 다운로드해 주는 기능이 아닙니다. 대화에서 웹사이트를 기획하고 코드를 작성한 뒤, 빌드&amp;middot;버전 저장&amp;middot;호스팅&amp;middot;외부 URL 연결까지 처리하는 관리형 웹 배포 서비스에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 구조를 먼저 한 문장으로 정리하면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT의 Work VM은 웹사이트를 만드는 작업실이고, Sites는 완성된 소스와 빌드 산출물을 별도의 관리형 Worker 환경에 배포해 외부에서 접속할 수 있게 하는 서비스입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 작업에 사용한 VM이 종료되거나 정리되더라도, 이미 배포된 사이트는 별도의 운영 환경에서 계속 제공될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 생성된 사이트도 로컬 작업 폴더가 정리된 후에도 Sites 서비스에 다음 정보가 남아 있었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이트 프로젝트 정보&lt;/li&gt;
&lt;li&gt;공개 주소&lt;/li&gt;
&lt;li&gt;공개 범위&lt;/li&gt;
&lt;li&gt;Git 커밋 기준 소스 버전&lt;/li&gt;
&lt;li&gt;전체 소스 아카이브&lt;/li&gt;
&lt;li&gt;배포된 운영 버전&lt;/li&gt;
&lt;li&gt;사이트 대표 이미지&lt;/li&gt;
&lt;li&gt;접근 권한 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 원리를 이해하려면 Work VM, Sites, Vinext, Vite, Cloudflare Worker의 역할을 분리해서 봐야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. ChatGPT Sites는 하나의 프로그램이 아니라 전체 배포 과정이다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites를 하나의 웹 프레임워크나 서버 프로그램으로 생각하면 구조가 혼란스러워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites는 대략 다음 기능을 하나로 묶은 서비스입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사이트 프로젝트 생성&lt;/li&gt;
&lt;li&gt;작업용 소스 코드 준비&lt;/li&gt;
&lt;li&gt;이미지와 정적 파일 관리&lt;/li&gt;
&lt;li&gt;React&amp;middot;Next.js 코드 빌드&lt;/li&gt;
&lt;li&gt;Worker용 서버 코드 생성&lt;/li&gt;
&lt;li&gt;빌드 산출물 검증&lt;/li&gt;
&lt;li&gt;소스 버전 저장&lt;/li&gt;
&lt;li&gt;운영 환경 배포&lt;/li&gt;
&lt;li&gt;chatgpt.site 주소 연결&lt;/li&gt;
&lt;li&gt;공개&amp;middot;비공개 접근 권한 관리&lt;/li&gt;
&lt;li&gt;환경변수와 API 키 관리&lt;/li&gt;
&lt;li&gt;데이터베이스와 파일 저장소 연결&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 흐름은 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;886&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ge7nr/dJMcadvYXR9/hLjVoW2DNMxzd0wI9DsNoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ge7nr/dJMcadvYXR9/hLjVoW2DNMxzd0wI9DsNoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ge7nr/dJMcadvYXR9/hLjVoW2DNMxzd0wI9DsNoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGe7nr%2FdJMcadvYXR9%2FhLjVoW2DNMxzd0wI9DsNoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;247&quot; height=&quot;886&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;886&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 Work VM은 최종 운영 서버가 아닙니다. 코드를 만들고 검사하는 임시 개발환경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 사이트는 Sites가 저장하고 배포한 별도의 버전으로 서비스됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Work VM은 웹사이트가 계속 실행되는 서버가 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT Work 환경에서 사이트를 만들면 /workspace/sites/... 같은 작업 경로에 소스 코드가 준비됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 확인한 WMU 사이트도 다음 경로로 복원됐습니다.&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;/workspace/sites/wmu-global-ai-hub
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 폴더에는 일반적인 웹 프로젝트처럼 다음 파일들이 들어 있었습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;wmu-global-ai-hub/
├─ app/
│  ├─ page.tsx
│  ├─ layout.tsx
│  ├─ globals.css
│  └─ chatgpt-auth.ts
├─ public/
│  ├─ hero-women-network.png
│  └─ favicon.svg
├─ worker/
│  └─ index.ts
├─ build/
│  └─ sites-vite-plugin.ts
├─ scripts/
│  ├─ build-verified.sh
│  └─ validate-artifact.sh
├─ .openai/
│  └─ hosting.json
├─ vite.config.ts
└─ package.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT는 이 작업 폴더에서 다음 작업을 수행합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React 컴포넌트 작성&lt;/li&gt;
&lt;li&gt;CSS 디자인&lt;/li&gt;
&lt;li&gt;이미지 파일 배치&lt;/li&gt;
&lt;li&gt;모바일 반응형 적용&lt;/li&gt;
&lt;li&gt;버튼과 모달 같은 상호작용 구현&lt;/li&gt;
&lt;li&gt;빌드 실행&lt;/li&gt;
&lt;li&gt;빌드 산출물 검사&lt;/li&gt;
&lt;li&gt;배포할 소스 버전 확정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 외부 사용자가 이 작업 폴더에 직접 접속하는 것은 아닙니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fxp7Q/dJMcadvYXsP/QxJorUdRGKQP9930hb6ubk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fxp7Q/dJMcadvYXsP/QxJorUdRGKQP9930hb6ubk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fxp7Q/dJMcadvYXsP/QxJorUdRGKQP9930hb6ubk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFxp7Q%2FdJMcadvYXsP%2FQxJorUdRGKQP9930hb6ubk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;158&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Work VM은 개발자 PC와 비슷하고, Sites 운영 환경은 Vercel&amp;middot;Cloudflare Workers 같은 배포 플랫폼과 비슷한 역할을 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. VM이 정리됐는데 사이트가 남아 있었던 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 분석 과정에서 로컬 /workspace를 검색했을 때 처음에는 사이트 소스와 .openai/hosting.json이 발견되지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 작업공간이 자동 정리된 상태였기 때문입니다. 그러나 Sites 서비스에는 WMU Global AI Hub 프로젝트가 남아 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인된 원격 정보는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목확인된 상태&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;프로젝트&lt;/td&gt;
&lt;td&gt;WMU Global AI Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공개 주소&lt;/td&gt;
&lt;td&gt;wmu-global-ai-hub.tommykim1981.chatgpt.site&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공개 범위&lt;/td&gt;
&lt;td&gt;Public&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;저장된 버전&lt;/td&gt;
&lt;td&gt;1개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;소스 기준&lt;/td&gt;
&lt;td&gt;Git 커밋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;소스 아카이브&lt;/td&gt;
&lt;td&gt;약 3.3MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;커스텀 도메인&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;운영 환경변수&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D1 데이터베이스&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R2 파일 저장소&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites가 저장한 원격 버전을 통해 사이트 체크아웃을 다시 복원할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Sites에는 최소한 다음 세 가지 계층이 존재한다고 이해할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 로컬 작업본
   ChatGPT가 현재 수정하는 파일

2. 저장된 소스 버전
   특정 Git 커밋과 소스 아카이브

3. 운영 배포본
   외부 URL에서 서비스되는 실행 버전
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 가지는 서로 같아 보이지만 목적이 다릅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로컬 작업본&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 편집 중인 코드입니다. 아직 외부 사용자에게 공개되지 않은 변경사항이 포함될 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;저장된 소스 버전&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 시점의 소스를 변경 불가능한 버전으로 저장한 것입니다. 나중에 복원하거나 배포 이력을 확인하는 기준이 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영 배포본&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장된 버전을 실제 운영 환경에서 실행하도록 배포한 결과입니다. 사용자가 브라우저에서 보는 것은 이 운영 배포본입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. .openai/hosting.json은 어떤 역할을 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites 프로젝트에는 .openai/hosting.json이라는 파일이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트에서 확인된 형태는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;d1&quot;: null,
  &quot;project_id&quot;: &quot;사이트 프로젝트 식별자&quot;,
  &quot;r2&quot;: null
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에 웹사이트 전체 설정이 들어 있는 것은 아닙니다. Sites 서비스와 로컬 소스 코드를 연결하는 프로젝트 표식에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 필드의 의미는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드역할&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;project_id&lt;/td&gt;
&lt;td&gt;현재 코드가 어느 Sites 프로젝트에 속하는지 식별&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d1&lt;/td&gt;
&lt;td&gt;Sites가 연결한 D1 데이터베이스 바인딩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r2&lt;/td&gt;
&lt;td&gt;Sites가 연결한 R2 파일 저장소 바인딩&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 WMU 사이트는 랜딩 페이지이기 때문에 D1과 R2가 모두 null입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스나 업로드 기능을 추가하면 Sites가 해당 리소스를 만들고 논리적 바인딩을 이 파일과 연결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 API 키나 비밀번호를 hosting.json에 넣지 않는다는 것입니다. 비밀정보는 Sites의 운영 환경변수 기능으로 관리해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 실제 사이트는 어떤 기술로 만들어졌을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NextJs로 만든 사이트의 package.json을 확인한 결과 주요 구성은 다음과 같았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술역할&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React 19&lt;/td&gt;
&lt;td&gt;UI 컴포넌트 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js 16&lt;/td&gt;
&lt;td&gt;페이지와 서버 기능 구성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vite 8&lt;/td&gt;
&lt;td&gt;개발 서버와 프로덕션 빌드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vinext&lt;/td&gt;
&lt;td&gt;Next.js 기능을 Vite&amp;middot;Worker 환경에 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare Vite Plugin&lt;/td&gt;
&lt;td&gt;Worker 환경 빌드와 로컬 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrangler&lt;/td&gt;
&lt;td&gt;Cloudflare Worker 개발 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;타입이 있는 JavaScript 개발&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tailwind CSS&lt;/td&gt;
&lt;td&gt;CSS 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drizzle ORM&lt;/td&gt;
&lt;td&gt;D1 같은 SQL 데이터베이스 연결 준비&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중에서 가장 낯선 기술은 Vinext입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Vinext는 플랫폼이 아니라 호환 계층이다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vinext는 호스팅 서비스나 클라우드 플랫폼 이름이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 애플리케이션의 API와 실행 방식을 Vite 위에서 다시 구현해, Cloudflare Workers 같은 환경에서 실행할 수 있게 만드는 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloudflare의 공식 Vinext 저장소는 Vinext를 Next.js API 표면을 Vite 위에서 재구현하는 플러그인으로 설명합니다. App Router, Pages Router, React Server Components, Server Actions, Middleware, Route Handler, ISR과 정적 내보내기 등을 지원하며, Cloudflare Workers와 가장 깊게 통합됩니다. &lt;a href=&quot;https://github.com/cloudflare/vinext&quot;&gt;Cloudflare Vinext 공식 저장소&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 관계는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCea3J/dJMcaiqBMMT/eR6oFlmcnkBVPEhTdVXrSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCea3J/dJMcaiqBMMT/eR6oFlmcnkBVPEhTdVXrSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCea3J/dJMcaiqBMMT/eR6oFlmcnkBVPEhTdVXrSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCea3J%2FdJMcaiqBMMT%2FeR6oFlmcnkBVPEhTdVXrSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;817&quot; height=&quot;158&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 코드를 그대로 Cloudflare Worker에 넣는다고 자동으로 실행되지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 다음과 같은 자체 실행 규칙을 가지고 있기 때문입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 기반 라우팅&lt;/li&gt;
&lt;li&gt;App Router&lt;/li&gt;
&lt;li&gt;서버 컴포넌트&lt;/li&gt;
&lt;li&gt;클라이언트 컴포넌트&lt;/li&gt;
&lt;li&gt;Route Handler&lt;/li&gt;
&lt;li&gt;서버 렌더링&lt;/li&gt;
&lt;li&gt;데이터 캐시&lt;/li&gt;
&lt;li&gt;이미지 최적화&lt;/li&gt;
&lt;li&gt;서버 액션&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vinext는 이런 Next.js의 기능을 Vite 빌드와 Worker의 요청 처리 방식에 맞춰 연결합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트의 vite.config.ts에는 실제로 다음 플러그인이 등록되어 있었습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import vinext from &quot;vinext&quot;;
import { defineConfig } from &quot;vite&quot;;
import { sites } from &quot;./build/sites-vite-plugin&quot;;

export default defineConfig({
  plugins: [
    vinext(),
    sites(),
  ],
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 각 플러그인의 역할은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;vinext()
&amp;rarr; Next.js 호환 기능과 라우팅을 Vite 빌드에 연결

sites()
&amp;rarr; 빌드 산출물에 Sites 배포 메타데이터를 포함

cloudflare()
&amp;rarr; Worker 런타임과 바인딩을 Vite에 연결
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 왜 Next.js인데 next build가 아니라 Vinext를 사용할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 Next.js 프로젝트는 다음 명령으로 빌드합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;next build
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 Sites 프로젝트는 다음과 같은 방식으로 빌드됩니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;vinext build
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vinext 공식 설명에 따르면, Vinext는 next build 결과물을 가져와 변환하는 방식이 아니라 Next.js API를 Vite 위에서 다시 구현하는 방식입니다. &lt;a href=&quot;https://github.com/cloudflare/vinext&quot;&gt;Cloudflare Vinext 공식 저장소&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 개념적으로 다음과 같은 차이가 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반적인 Next.js 배포&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;Next.js 코드
&amp;rarr; next build
&amp;rarr; Node.js 또는 Vercel용 결과물
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sites에서 확인된 구조&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Next.js 코드
&amp;rarr; Vinext
&amp;rarr; Vite
&amp;rarr; Cloudflare Worker 호환 결과물
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식의 장점은 개발자가 React와 Next.js 스타일로 사이트를 작성하면서도 최종 실행 환경을 Worker 구조로 만들 수 있다는 점입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 빌드하면 어떤 산출물이 만들어질까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트의 빌드 검증 스크립트는 최소한 다음 두 파일이 있어야 배포 가능한 산출물로 인정합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;dist/server/index.js
dist/.openai/hosting.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 파일의 역할은 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dist/server/index.js&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker에서 실행할 서버 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 모듈은 기본 내보내기 객체를 제공하고, 그 안에 호출 가능한 fetch() 함수가 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적인 형태는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export default {
  async fetch(request, env, ctx) {
    return new Response(&quot;Hello&quot;);
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dist/.openai/hosting.json&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 빌드가 어느 Sites 프로젝트와 연결되는지 알려 주는 배포 메타데이터입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 자산&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 다음 파일들이 빌드 산출물에 포함될 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;dist/
├─ server/
│  └─ index.js
├─ assets/
│  ├─ JavaScript 번들
│  ├─ CSS
│  ├─ 폰트
│  └─ 이미지
└─ .openai/
   ├─ hosting.json
   └─ drizzle/
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Drizzle 데이터베이스 마이그레이션이 있다면 dist/.openai/drizzle/도 함께 포함됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. Worker 서버 코드는 왜 필요할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트가 단순한 랜딩 페이지라면 HTML, CSS, JavaScript와 이미지만 있어도 충분해 보입니다. 그런데 Sites 프로젝트에는 왜 Worker 서버 코드가 포함될까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 배포 형식을 정적 사이트로 제한하지 않고, 서버 렌더링과 백엔드 기능까지 처리할 수 있도록 만들기 위해서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker 서버 코드는 다음 기능을 담당할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;들어온 HTTP 요청 처리&lt;/li&gt;
&lt;li&gt;URL별 페이지 라우팅&lt;/li&gt;
&lt;li&gt;서버 컴포넌트 실행&lt;/li&gt;
&lt;li&gt;서버 렌더링&lt;/li&gt;
&lt;li&gt;API Route 실행&lt;/li&gt;
&lt;li&gt;로그인 사용자 확인&lt;/li&gt;
&lt;li&gt;환경변수 접근&lt;/li&gt;
&lt;li&gt;데이터베이스 조회&lt;/li&gt;
&lt;li&gt;파일 저장소 접근&lt;/li&gt;
&lt;li&gt;외부 API 호출&lt;/li&gt;
&lt;li&gt;이미지 최적화&lt;/li&gt;
&lt;li&gt;오류 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloudflare Workers에서는 외부 HTTP 요청이 fetch() 핸들러로 전달됩니다. 이 함수는 Request, 환경 바인딩인 env, 실행 컨텍스트인 ctx를 받아 Response를 반환합니다. &lt;a href=&quot;https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/&quot;&gt;Cloudflare Workers Fetch Handler 공식 문서&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export default {
  async fetch(request, env, ctx) {
    return new Response(&quot;응답&quot;);
  },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트에서도 같은 형태를 확인할 수 있었습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;const worker = {
  async fetch(request, env, ctx) {
    return handler.fetch(request, env, ctx);
  },
};

export default worker;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 handler는 Vinext가 제공하는 Next.js App Router 실행기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Worker가 직접 모든 페이지를 구성하는 것이 아니라 Vinext의 핸들러에게 요청을 전달합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1047&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbvfMC/dJMcafU1OnA/78fPqlcvjCDOkgWKk31mlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbvfMC/dJMcafU1OnA/78fPqlcvjCDOkgWKk31mlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbvfMC/dJMcafU1OnA/78fPqlcvjCDOkgWKk31mlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbvfMC%2FdJMcafU1OnA%2F78fPqlcvjCDOkgWKk31mlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1047&quot; height=&quot;54&quot; data-origin-width=&quot;1047&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 정적 파일과 Worker는 어떻게 함께 동작할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites의 배포물은 정적 자산과 서버 코드를 함께 포함할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;배포 버전
├─ 정적 자산
│  ├─ 이미지
│  ├─ CSS
│  ├─ 브라우저 JavaScript
│  └─ 폰트
└─ Worker 서버
   ├─ 페이지 라우팅
   ├─ 서버 렌더링
   ├─ API
   └─ 외부 서비스 연동
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloudflare Workers는 HTML, CSS, 이미지 같은 정적 자산을 Worker와 함께 배포할 수 있습니다. Cloudflare는 이런 정적 파일을 캐시하고 브라우저에 제공할 수 있다고 설명합니다. &lt;a href=&quot;https://developers.cloudflare.com/workers/static-assets/&quot;&gt;Cloudflare Workers Static Assets 공식 문서&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker 코드에서는 env.ASSETS 바인딩을 통해 정적 파일을 직접 가져올 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;return env.ASSETS.fetch(request);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloudflare 공식 문서에서도 Assets 바인딩을 사용해 env.ASSETS.fetch() 형태로 정적 자산을 가져올 수 있다고 설명합니다. &lt;a href=&quot;https://developers.cloudflare.com/workers/static-assets/binding/&quot;&gt;Cloudflare Assets Binding 공식 문서&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트에서는 이미지 최적화 경로가 들어오면 다음과 같은 처리를 합니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;if (url.pathname === &quot;/_vinext/image&quot;) {
  return handleImageOptimization(request, {
    fetchAsset: (path) =&amp;gt;
      env.ASSETS.fetch(
        new Request(new URL(path, request.url))
      ),
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 요청 흐름은 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfRxRB/dJMb99UJ98j/Ovvbfnnk5E5IukKKGTmFE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfRxRB/dJMb99UJ98j/Ovvbfnnk5E5IukKKGTmFE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfRxRB/dJMb99UJ98j/Ovvbfnnk5E5IukKKGTmFE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfRxRB%2FdJMb99UJ98j%2FOvvbfnnk5E5IukKKGTmFE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;554&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트가 대부분 정적이더라도 하나의 Worker 배포 안에서 정적 자산과 동적 요청을 통합해 처리할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 모든 페이지 요청이 Worker를 거쳐야 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 라우팅 우선순위와 캐시 정책은 Sites의 비공개 배포 설정에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로는 다음 두 방식이 가능합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 자산 우선 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 생성된 CSS, JavaScript, 이미지 요청은 정적 자산 계층에서 바로 반환하고, 동적 요청만 Worker로 전달합니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;정적 파일 요청 &amp;rarr; CDN&amp;middot;Assets
동적 페이지 요청 &amp;rarr; Worker
API 요청 &amp;rarr; Worker
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Worker가 요청을 분류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 요청이 Worker 진입점으로 전달되고, Worker가 정적 자산과 페이지 요청을 구분합니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;모든 요청 &amp;rarr; Worker &amp;rarr; Assets 또는 서버 핸들러
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 프로젝트 소스에서는 Worker가 이미지 최적화 요청을 직접 분기하고, 나머지 요청은 Vinext 핸들러로 전달하는 것을 확인했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. Sites는 백엔드도 만들 수 있을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능합니다. Worker 서버 코드가 포함되는 가장 중요한 이유 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites에서 구현할 수 있는 백엔드 기능은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능구현 방식&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;REST API&lt;/td&gt;
&lt;td&gt;Next.js Route Handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;폼 접수&lt;/td&gt;
&lt;td&gt;POST /api/contact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;외부 AI 호출&lt;/td&gt;
&lt;td&gt;Worker에서 fetch()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;회원별 페이지&lt;/td&gt;
&lt;td&gt;로그인 헤더 확인 후 렌더링&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;게시물 저장&lt;/td&gt;
&lt;td&gt;D1 데이터베이스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미지 업로드&lt;/td&gt;
&lt;td&gt;R2 저장소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;결제 Webhook&lt;/td&gt;
&lt;td&gt;공개 API 경로에서 수신&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Telegram Webhook&lt;/td&gt;
&lt;td&gt;Telegram이 Sites API로 POST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이메일 발송&lt;/td&gt;
&lt;td&gt;외부 이메일 API 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관리자 기능&lt;/td&gt;
&lt;td&gt;인증 + D1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;다국어 콘텐츠&lt;/td&gt;
&lt;td&gt;URL&amp;middot;헤더&amp;middot;데이터에 따라 렌더링&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문의 폼 API는 다음처럼 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export async function POST(request: Request) {
  const form = await request.json();

  if (!form.email || !form.message) {
    return Response.json(
      { error: &quot;필수 항목이 없습니다.&quot; },
      { status: 400 },
    );
  }

  return Response.json({
    ok: true,
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서는 다음 주소로 요청할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;POST /api/contact
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites가 배포된 후에는 다음과 같은 외부 주소가 됩니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;POST https://사이트주소.chatgpt.site/api/contact
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. Worker에서 외부 API도 호출할 수 있을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker는 표준 fetch()를 이용해 외부 HTTP API를 호출할 수 있습니다. Cloudflare 공식 문서도 Workers 내부에서 Fetch API를 이용해 HTTP 리소스를 비동기로 가져올 수 있다고 설명합니다. &lt;a href=&quot;https://developers.cloudflare.com/workers/runtime-apis/fetch/&quot;&gt;Cloudflare Workers Fetch API 공식 문서&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 DeepSeek API를 호출하는 백엔드는 다음과 같은 구조로 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export async function POST(request: Request) {
  const { message } = await request.json();

  const response = await fetch(
    &quot;https://api.deepseek.com/chat/completions&quot;,
    {
      method: &quot;POST&quot;,
      headers: {
        Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`,
        &quot;Content-Type&quot;: &quot;application/json&quot;,
      },
      body: JSON.stringify({
        model: &quot;deepseek-v4-flash&quot;,
        messages: [
          {
            role: &quot;user&quot;,
            content: message,
          },
        ],
      }),
    },
  );

  const result = await response.json();
  return Response.json(result);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 이용하면 다음 기능을 만들 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹사이트 AI 상담&lt;/li&gt;
&lt;li&gt;콘텐츠 자동 요약&lt;/li&gt;
&lt;li&gt;번역&lt;/li&gt;
&lt;li&gt;챗봇&lt;/li&gt;
&lt;li&gt;상품 설명 생성&lt;/li&gt;
&lt;li&gt;한국어 학습 피드백&lt;/li&gt;
&lt;li&gt;퀴즈 자동 생성&lt;/li&gt;
&lt;li&gt;관리자용 데이터 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 API 키를 브라우저 코드에 직접 넣으면 안 됩니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;잘못된 구조
브라우저 &amp;rarr; DeepSeek API
API 키가 사용자에게 노출될 수 있음

권장 구조
브라우저 &amp;rarr; Sites Worker &amp;rarr; DeepSeek API
API 키는 Worker 환경변수에서만 사용
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. API 키와 운영 환경변수는 어디에 저장할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트 소스에 API 키를 직접 작성하면 Git 기록과 빌드 산출물에 비밀정보가 남을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 코드는 피해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;const apiKey = &quot;실제 API 키&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites에는 운영용 환경변수를 별도로 등록하는 기능이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적인 구조는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pH5oF/dJMcajpqLMB/MIjo1NaAei3ee2ppZpC9K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pH5oF/dJMcajpqLMB/MIjo1NaAei3ee2ppZpC9K1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pH5oF/dJMcajpqLMB/MIjo1NaAei3ee2ppZpC9K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpH5oF%2FdJMcajpqLMB%2FMIjo1NaAei3ee2ppZpC9K1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;158&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수는 일반 값과 비밀값으로 구분할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;일반 환경변수
PUBLIC_SITE_NAME=WMU Global Hub

비밀 환경변수
DEEPSEEK_API_KEY=...
TELEGRAM_BOT_TOKEN=...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀값은 소스 코드나 hosting.json에 넣지 않고 Sites의 운영 설정에 보관해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트는 현재 외부 API를 사용하지 않기 때문에 운영 환경변수가 등록되어 있지 않았습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. D1 데이터베이스는 어떤 역할을 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D1은 Cloudflare Workers에서 사용하는 서버리스 SQL 데이터베이스입니다. SQLite와 호환되는 방식으로 데이터를 저장하고 조회할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloudflare 공식 문서에 따르면 Worker는 D1 바인딩을 통해 SQL 쿼리를 실행할 수 있습니다. &lt;a href=&quot;https://developers.cloudflare.com/d1/worker-api/&quot;&gt;Cloudflare D1 Worker API 공식 문서&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites에서 D1을 연결하면 다음 기능을 만들 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회원 프로필&lt;/li&gt;
&lt;li&gt;문의 내역&lt;/li&gt;
&lt;li&gt;게시판&lt;/li&gt;
&lt;li&gt;학습 진도&lt;/li&gt;
&lt;li&gt;퀴즈 문제와 답안&lt;/li&gt;
&lt;li&gt;포인트 내역&lt;/li&gt;
&lt;li&gt;예약 데이터&lt;/li&gt;
&lt;li&gt;상품 정보&lt;/li&gt;
&lt;li&gt;주문 상태&lt;/li&gt;
&lt;li&gt;AI 대화 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker에서는 보통 다음과 같은 방식으로 접근합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export default {
  async fetch(request, env) {
    const result = await env.DB
      .prepare(&quot;SELECT * FROM users WHERE id = ?&quot;)
      .bind(&quot;user-123&quot;)
      .first();

    return Response.json(result);
  },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloudflare는 바인딩을 통해 Worker가 D1 같은 플랫폼 리소스에 접근한다고 설명합니다. &lt;a href=&quot;https://developers.cloudflare.com/workers/runtime-apis/bindings/&quot;&gt;Cloudflare Workers Bindings 공식 문서&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트의 설정에는 D1이 연결되어 있지 않았습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;d1&quot;: null
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 실제 회원&amp;middot;게시물&amp;middot;포인트 데이터를 저장하지 않는 소개용 사이트이기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16. R2 파일 저장소는 어떤 역할을 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2는 이미지, 동영상, PDF, 첨부파일 같은 객체 파일을 저장하는 용도입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites에 R2를 연결하면 다음 기능을 구현할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회원 프로필 이미지&lt;/li&gt;
&lt;li&gt;강의 자료 업로드&lt;/li&gt;
&lt;li&gt;PDF 교재 저장&lt;/li&gt;
&lt;li&gt;상품 이미지&lt;/li&gt;
&lt;li&gt;라이브커머스 영상&lt;/li&gt;
&lt;li&gt;사용자 제출 과제&lt;/li&gt;
&lt;li&gt;AI 생성 이미지&lt;/li&gt;
&lt;li&gt;관리자 첨부파일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 이미지와 사용자 업로드 파일은 구분해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 이미지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트를 만들 때부터 프로젝트에 포함된 파일입니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;public/hero-women-network.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트를 다시 배포해야 이미지가 변경됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용자 업로드 파일&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트 운영 중 사용자가 올리는 파일입니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;사용자 &amp;rarr; Worker 업로드 API &amp;rarr; R2 저장
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트를 다시 빌드하지 않아도 파일을 추가하고 삭제할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트는 현재 R2를 사용하지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;r2&quot;: null
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17. ChatGPT 로그인도 사용할 수 있을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 프로젝트에는 chatgpt-auth.ts라는 인증 보조 코드가 포함되어 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 Sites 실행환경이 전달하는 인증 헤더를 읽어 사용자 이메일과 이름을 확인하는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인된 주요 헤더는 다음과 같았습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;oai-authenticated-user-email
oai-authenticated-user-full-name
oai-authenticated-user-full-name-encoding
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적인 동작은 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NfvJd/dJMcadJxvWE/FyhdeIkCJ1lkUGCv1pq451/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NfvJd/dJMcadJxvWE/FyhdeIkCJ1lkUGCv1pq451/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NfvJd/dJMcadJxvWE/FyhdeIkCJ1lkUGCv1pq451/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNfvJd%2FdJMcadJxvWE%2FFyhdeIkCJ1lkUGCv1pq451%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;925&quot; height=&quot;54&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트 코드에서는 다음처럼 사용자를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;const email = requestHeaders.get(
  &quot;oai-authenticated-user-email&quot;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증되지 않은 사용자는 로그인 경로로 이동시킬 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;/signin-with-chatgpt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 WMU 사이트의 접근 모드는 Public이기 때문에 누구나 사이트를 열 수 있습니다. 다만 사이트 일부 기능만 로그인 사용자에게 제공하는 식으로 확장할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18. 공개 사이트와 비공개 사이트는 무엇이 다를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites는 프로젝트별로 접근 정책을 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 WMU 사이트는 공개 모드입니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;access_mode: public
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개 사이트는 외부 사용자가 chatgpt.site 주소로 접속할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비공개 또는 제한된 사이트라면 특정 사용자나 그룹만 접근하도록 구성할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;공개 사이트
&amp;rarr; 누구나 URL로 접속

제한된 사이트
&amp;rarr; 인증된 사용자만 접속

사용자 지정 접근
&amp;rarr; 허용된 사용자&amp;middot;그룹만 접속
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 정책은 애플리케이션 코드와 별도로 Sites 배포 계층에서 관리됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이는 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 코드에 로그인 페이지가 있어도 Sites 접근 정책이 Public이면 기본 사이트 주소 자체는 공개될 수 있습니다. 반대로 애플리케이션이 공개 페이지처럼 보여도 Sites 접근 정책이 제한적이면 허용된 사용자만 진입할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;19. chatgpt.site 주소는 어떻게 연결될까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites에서 프로젝트를 만들면 프로젝트별 슬러그를 기반으로 운영 URL이 만들어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트는 다음 주소를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;https://wmu-global-ai-hub.tommykim1981.chatgpt.site
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소 구성은 대략 다음처럼 이해할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;프로젝트 슬러그
        +
사용자 또는 소유자 식별 영역
        +
chatgpt.site
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites는 이 주소를 배포된 운영 버전과 연결합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biy8sg/dJMcagl1aDU/K84R5GM7dqeakfiRM3MmG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biy8sg/dJMcagl1aDU/K84R5GM7dqeakfiRM3MmG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biy8sg/dJMcagl1aDU/K84R5GM7dqeakfiRM3MmG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbiy8sg%2FdJMcagl1aDU%2FK84R5GM7dqeakfiRM3MmG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;868&quot; height=&quot;54&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 직접 Nginx를 설정하거나 SSL 인증서를 설치할 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] DNS, TLS 인증서, 라우팅과 배포 대상 연결은 Sites의 관리형 인프라에서 처리되는 것으로 보입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;20. 커스텀 도메인도 연결할 수 있을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites 서비스에는 프로젝트별 커스텀 도메인을 조회하고 관리하는 구조가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트에는 현재 커스텀 도메인이 연결되어 있지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;Custom domains: 없음
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 현재는 기본 chatgpt.site 주소만 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 도메인이 지원되는 프로젝트라면 개념적으로 다음과 같이 연결할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;기본 주소
wmu-global-ai-hub....chatgpt.site

사용자 도메인
www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 주소가 동일한 Sites 프로젝트의 운영 배포본으로 연결되는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 DNS 설정과 지원 범위는 Sites의 현재 도메인 연결 정책을 확인해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;21. Sites 배포는 어떤 단계를 거칠까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites 배포는 단순히 폴더를 서버에 복사하는 작업이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인된 프로젝트 구조를 기준으로 보면 다음 단계로 나뉩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1단계: 소스 준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React&amp;middot;Next.js 코드와 이미지, CSS, Worker 진입점, 배포 메타데이터를 준비합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2단계: 로컬 빌드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vinext가 Next.js 호환 기능을 해석하고 Vite가 Worker용 산출물을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;vinext build
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3단계: 산출물 검증&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 검증 스크립트는 다음 조건을 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;dist/server/index.js가 존재하는가?
dist/.openai/hosting.json이 존재하는가?
서버 모듈이 ESM 형식인가?
기본 내보내기에 fetch()가 있는가?
hosting.json이 유효한 JSON인가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증 코드의 핵심은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const worker = await import(workerUrl.href);

if (
  !worker.default ||
  typeof worker.default.fetch !== &quot;function&quot;
) {
  throw new Error(
    &quot;Worker 진입점이 올바르지 않습니다.&quot;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4단계: 소스 커밋 저장&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포할 소스를 특정 Git 커밋으로 확정합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5단계: 불변 버전 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 커밋과 소스 아카이브를 Sites의 저장된 버전으로 만듭니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6단계: 운영 배포&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장된 버전을 관리형 Worker 환경에 배포합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7단계: 배포 상태 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 상태가 성공인지 실패인지 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;pending
&amp;rarr; building
&amp;rarr; publishing
&amp;rarr; succeeded 또는 failed
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8단계: 운영 URL 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공한 운영 배포본을 사이트의 현재 공개 URL과 연결합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;22. 소스 저장과 배포는 왜 분리되어 있을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 버전과 운영 배포를 분리하면 같은 소스를 다시 배포하거나 이전 버전으로 돌아갈 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/55Hm5/dJMcagl1aG5/pNqBo6DBXeK72g9DXeC6tK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/55Hm5/dJMcagl1aG5/pNqBo6DBXeK72g9DXeC6tK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/55Hm5/dJMcagl1aG5/pNqBo6DBXeK72g9DXeC6tK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F55Hm5%2FdJMcagl1aG5%2FpNqBo6DBXeK72g9DXeC6tK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;262&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 버전 3에서 문제가 발생하면 이전에 저장된 버전 2를 다시 운영에 배포하는 구조를 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스만 저장됐다고 외부에 공개되는 것도 아니고, 로컬 코드가 수정됐다고 운영 사이트가 즉시 바뀌는 것도 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 사이트가 바뀌려면 수정된 소스를 새 버전으로 저장하고 그 버전을 배포해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;23. Sites와 일반 VPS는 무엇이 다를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 차이는 서버 프로세스의 유지 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분ChatGPT Sites일반 VPS&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;실행 방식&lt;/td&gt;
&lt;td&gt;요청 기반 Worker&lt;/td&gt;
&lt;td&gt;상시 실행 프로세스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;서버 관리&lt;/td&gt;
&lt;td&gt;관리형&lt;/td&gt;
&lt;td&gt;사용자가 직접 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;포트 개방&lt;/td&gt;
&lt;td&gt;일반적으로 불필요&lt;/td&gt;
&lt;td&gt;직접 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx 설정&lt;/td&gt;
&lt;td&gt;일반적으로 불필요&lt;/td&gt;
&lt;td&gt;직접 구성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL&lt;/td&gt;
&lt;td&gt;관리형 URL에 연결&lt;/td&gt;
&lt;td&gt;직접 또는 인증서 자동화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정적 파일&lt;/td&gt;
&lt;td&gt;Assets로 배포&lt;/td&gt;
&lt;td&gt;디스크&amp;middot;Nginx 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Worker 요청 처리&lt;/td&gt;
&lt;td&gt;Node&amp;middot;Python 서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터베이스&lt;/td&gt;
&lt;td&gt;D1 등 바인딩&lt;/td&gt;
&lt;td&gt;PostgreSQL&amp;middot;MySQL 직접 운영&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파일 저장&lt;/td&gt;
&lt;td&gt;R2&lt;/td&gt;
&lt;td&gt;로컬 디스크&amp;middot;S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장기 실행 데몬&lt;/td&gt;
&lt;td&gt;부적합&lt;/td&gt;
&lt;td&gt;적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Telegram 폴링&lt;/td&gt;
&lt;td&gt;부적합&lt;/td&gt;
&lt;td&gt;적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Telegram Webhook&lt;/td&gt;
&lt;td&gt;적합&lt;/td&gt;
&lt;td&gt;적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenClaw Gateway&lt;/td&gt;
&lt;td&gt;부적합&lt;/td&gt;
&lt;td&gt;적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;일반 TCP 서버&lt;/td&gt;
&lt;td&gt;부적합&lt;/td&gt;
&lt;td&gt;적합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites는 웹 요청을 받아 짧게 처리하고 응답하는 서비스에 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 VPS는 프로세스를 계속 실행해야 하는 프로그램에 적합합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;24. 왜 Work VM에서는 Telegram 봇이 멈췄는데 Sites 웹사이트는 계속 열릴까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 Work VM에서 Hermes와 OpenClaw를 실행해 Telegram 봇을 연결했지만, 장기 실행 중 다음 오류가 확인됐습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Network access to
https://api.telegram.org:443
was blocked by policy
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hermes나 OpenClaw의 프로그램 문제가 아니라 Work VM의 장기 실행 프로세스와 외부 네트워크 정책 때문이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Telegram 폴링 봇은 다음 구조입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djM9Tm/dJMcaheeDkh/e1mZYu2ktkSK08ar3cju91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djM9Tm/dJMcaheeDkh/e1mZYu2ktkSK08ar3cju91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djM9Tm/dJMcaheeDkh/e1mZYu2ktkSK08ar3cju91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjM9Tm%2FdJMcaheeDkh%2Fe1mZYu2ktkSK08ar3cju91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;245&quot; height=&quot;470&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로그램은 다음 조건을 만족해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스가 계속 살아 있어야 함&lt;/li&gt;
&lt;li&gt;Telegram 서버에 반복 접속해야 함&lt;/li&gt;
&lt;li&gt;네트워크 연결이 유지돼야 함&lt;/li&gt;
&lt;li&gt;메모리와 세션을 계속 유지해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Sites는 요청 기반입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXZBFe/dJMcafU1OCg/p5m4VJlTuDjz5IRU7wlxp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXZBFe/dJMcafU1OCg/p5m4VJlTuDjz5IRU7wlxp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXZBFe/dJMcafU1OCg/p5m4VJlTuDjz5IRU7wlxp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXZBFe%2FdJMcafU1OCg%2Fp5m4VJlTuDjz5IRU7wlxp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;177&quot; height=&quot;470&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker가 24시간 하나의 프로세스로 실행되는 것이 아니라, 요청이 들어왔을 때 실행될 수 있는 코드로 배포됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Work VM이 없어도 운영 사이트가 계속 응답할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;25. Telegram을 Sites와 연결하는 방법은 없을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenClaw 전체 Gateway를 Sites에 실행하는 방식은 적합하지 않습니다. 하지만 Telegram Webhook 방식의 경량 챗봇은 만들 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;폴링 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;봇이 Telegram 서버에 계속 질문합니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;새 메시지 있습니까?
새 메시지 있습니까?
새 메시지 있습니까?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장기 실행 프로세스가 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Webhook 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Telegram이 새 메시지가 생겼을 때 Sites API로 알려 줍니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;Telegram
&amp;rarr; POST /api/telegram/webhook
&amp;rarr; Sites Worker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 구조는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oDzPK/dJMcaiYny4O/Wez7YDF8L2VZWBInasofnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oDzPK/dJMcaiYny4O/Wez7YDF8L2VZWBInasofnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oDzPK/dJMcaiYny4O/Wez7YDF8L2VZWBInasofnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoDzPK%2FdJMcaiYny4O%2FWez7YDF8L2VZWBInasofnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;258&quot; height=&quot;494&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 Worker가 계속 실행될 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Telegram이 메시지를 전달할 때만 Worker가 실행됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;26. Telegram Webhook 백엔드는 어떻게 구성할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적인 API 경로는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;POST /api/telegram/webhook
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리 순서는 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Telegram이 메시지 업데이트를 전송&lt;/li&gt;
&lt;li&gt;요청이 Telegram에서 온 것인지 검증&lt;/li&gt;
&lt;li&gt;메시지를 보낸 사용자 ID 확인&lt;/li&gt;
&lt;li&gt;허용된 사용자만 처리&lt;/li&gt;
&lt;li&gt;메시지 내용 추출&lt;/li&gt;
&lt;li&gt;DeepSeek API 호출&lt;/li&gt;
&lt;li&gt;답변 내용 정리&lt;/li&gt;
&lt;li&gt;Telegram sendMessage API 호출&lt;/li&gt;
&lt;li&gt;Telegram에 HTTP 200 응답&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 코드 구조는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export async function POST(request: Request) {
  const update = await request.json();

  const message = update.message;
  if (!message?.text) {
    return Response.json({ ok: true });
  }

  const answer = await askDeepSeek(message.text);

  await sendTelegramMessage(
    message.chat.id,
    answer,
  );

  return Response.json({ ok: true });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek 호출 함수는 다음과 같이 분리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;async function askDeepSeek(message: string) {
  const response = await fetch(
    &quot;https://api.deepseek.com/chat/completions&quot;,
    {
      method: &quot;POST&quot;,
      headers: {
        Authorization:
          `Bearer ${process.env.DEEPSEEK_API_KEY}`,
        &quot;Content-Type&quot;: &quot;application/json&quot;,
      },
      body: JSON.stringify({
        model: &quot;deepseek-v4-flash&quot;,
        messages: [
          {
            role: &quot;system&quot;,
            content:
              &quot;질문에 자연스러운 한국어로 답하세요.&quot;,
          },
          {
            role: &quot;user&quot;,
            content: message,
          },
        ],
      }),
    },
  );

  const result = await response.json();
  return result.choices[0].message.content;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Telegram 발송 함수는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;async function sendTelegramMessage(
  chatId: number,
  text: string,
) {
  await fetch(
    `https://api.telegram.org/bot${
      process.env.TELEGRAM_BOT_TOKEN
    }/sendMessage`,
    {
      method: &quot;POST&quot;,
      headers: {
        &quot;Content-Type&quot;: &quot;application/json&quot;,
      },
      body: JSON.stringify({
        chat_id: chatId,
        text,
      }),
    },
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀값은 Sites 운영 환경변수에 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;DEEPSEEK_API_KEY
TELEGRAM_BOT_TOKEN
TELEGRAM_WEBHOOK_SECRET
ALLOWED_TELEGRAM_USER_ID
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;27. Sites에서 OpenClaw 전체를 실행하기 어려운 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenClaw는 단순한 API 함수가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenClaw Gateway는 다음과 같은 상태를 계속 유지합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Telegram&amp;middot;Discord 등 채널 연결&lt;/li&gt;
&lt;li&gt;세션&lt;/li&gt;
&lt;li&gt;대화 기록&lt;/li&gt;
&lt;li&gt;작업 큐&lt;/li&gt;
&lt;li&gt;도구 실행&lt;/li&gt;
&lt;li&gt;파일 접근&lt;/li&gt;
&lt;li&gt;장기 실행 작업&lt;/li&gt;
&lt;li&gt;백그라운드 이벤트&lt;/li&gt;
&lt;li&gt;주기적 작업&lt;/li&gt;
&lt;li&gt;Gateway WebSocket&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 프로그램은 일반적으로 다음 환경이 적합합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS Lightsail&lt;/li&gt;
&lt;li&gt;일반 VPS&lt;/li&gt;
&lt;li&gt;EC2&lt;/li&gt;
&lt;li&gt;Docker 서버&lt;/li&gt;
&lt;li&gt;Railway의 장기 실행 서비스&lt;/li&gt;
&lt;li&gt;Fly.io VM&lt;/li&gt;
&lt;li&gt;집 PC&lt;/li&gt;
&lt;li&gt;Mac mini&lt;/li&gt;
&lt;li&gt;NAS&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites의 Worker는 HTTP 요청을 받아 처리하는 백엔드에는 적합하지만, OpenClaw 전체 Gateway를 계속 실행하는 서버를 대체하는 용도는 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 OpenClaw의 일부 기능을 API 형태로 분리하면 Sites와 조합할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Sites
&amp;rarr; 사용자 화면과 Webhook

외부 VPS
&amp;rarr; OpenClaw Gateway

Sites와 VPS
&amp;rarr; HTTPS API로 연결
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;28. Worker 백엔드의 상태는 어디에 저장할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker 코드의 전역 변수나 메모리에 중요한 상태를 계속 보관하면 안 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 구조는 신뢰하기 어렵습니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;let userPoints = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청마다 실행 인스턴스가 달라지거나 재시작될 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태는 용도에 맞는 저장소에 넣어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 유형적합한 저장소&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;회원&amp;middot;게시물&amp;middot;포인트&lt;/td&gt;
&lt;td&gt;D1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미지&amp;middot;PDF&amp;middot;동영상&lt;/td&gt;
&lt;td&gt;R2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;간단한 설정&amp;middot;캐시&lt;/td&gt;
&lt;td&gt;KV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실시간 상태&amp;middot;동시성&lt;/td&gt;
&lt;td&gt;Durable Objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비밀 API 키&lt;/td&gt;
&lt;td&gt;운영 환경변수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정적 사이트 이미지&lt;/td&gt;
&lt;td&gt;프로젝트의 public/&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트는 현재 실제 운영 데이터를 저장하지 않으므로 별도 저장소를 사용하지 않습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;29. 서버 렌더링은 무엇이며 왜 필요할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 렌더링은 브라우저에서 모든 화면을 만든 뒤 보여 주는 대신, 서버가 먼저 HTML을 만들어 반환하는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYOl21/dJMcagNcy01/JytWTXaIDdK23BAfT0XLG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYOl21/dJMcagNcy01/JytWTXaIDdK23BAfT0XLG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYOl21/dJMcagNcy01/JytWTXaIDdK23BAfT0XLG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYOl21%2FdJMcagNcy01%2FJytWTXaIDdK23BAfT0XLG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;949&quot; height=&quot;54&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 렌더링은 다음 상황에 유용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그인 사용자별 화면&lt;/li&gt;
&lt;li&gt;데이터베이스를 조회한 페이지&lt;/li&gt;
&lt;li&gt;검색엔진이 읽어야 하는 콘텐츠&lt;/li&gt;
&lt;li&gt;첫 화면을 빠르게 제공해야 하는 경우&lt;/li&gt;
&lt;li&gt;API 키를 브라우저에 노출하지 않아야 하는 경우&lt;/li&gt;
&lt;li&gt;서버에서 권한을 검사해야 하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vinext는 Next.js의 App Router와 React Server Components를 Worker 환경에서 실행할 수 있도록 연결합니다. &lt;a href=&quot;https://github.com/cloudflare/vinext&quot;&gt;Cloudflare Vinext 공식 저장소&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;30. 정적 사이트라면 Worker를 빼도 되지 않을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적으로 완전한 정적 사이트라면 HTML, CSS, JavaScript, 이미지만 배포하는 구조도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Sites의 표준 프로젝트는 다음 이유로 Worker 형식을 기본으로 사용하는 것으로 보입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정적 사이트와 동적 사이트를 같은 배포 규격으로 관리할 수 있음&lt;/li&gt;
&lt;li&gt;나중에 API를 추가하기 쉬움&lt;/li&gt;
&lt;li&gt;서버 렌더링 지원&lt;/li&gt;
&lt;li&gt;ChatGPT 인증 연결&lt;/li&gt;
&lt;li&gt;이미지 최적화&lt;/li&gt;
&lt;li&gt;D1&amp;middot;R2 바인딩&lt;/li&gt;
&lt;li&gt;Next.js Route Handler 지원&lt;/li&gt;
&lt;li&gt;공개&amp;middot;비공개 접근 정책 적용&lt;/li&gt;
&lt;li&gt;동적 오류 페이지와 라우팅&lt;/li&gt;
&lt;li&gt;외부 API 호출&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 지금은 정적인 랜딩 페이지라도 나중에 회원, 게시판, AI 챗봇, 문의 저장 기능을 붙일 수 있는 기반을 갖고 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;31. 현재 WMU 사이트는 정적 사이트인가, 동적 사이트인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히는 &lt;b&gt;정적 콘텐츠 중심의 Worker 애플리케이션&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘텐츠 자체는 대부분 코드에 고정되어 있습니다.&lt;/p&gt;
&lt;pre class=&quot;d&quot;&gt;&lt;code&gt;const pillars = [
  {
    title: &quot;배움이 가능성이 되는 곳&quot;,
    body: &quot;AI 리터러시부터...&quot;,
  },
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스나 외부 API는 사용하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다음 동적 기능은 브라우저 React 상태로 작동합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모바일 메뉴 열기&lt;/li&gt;
&lt;li&gt;핵심 서비스 탭 전환&lt;/li&gt;
&lt;li&gt;스토리 모달 열기와 닫기&lt;/li&gt;
&lt;li&gt;스크롤 이동&lt;/li&gt;
&lt;li&gt;반응형 레이아웃&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 형식은 Worker이기 때문에 앞으로 서버 기능을 추가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음처럼 구분하는 것이 정확합니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;콘텐츠 성격
&amp;rarr; 대부분 정적

브라우저 UI
&amp;rarr; 일부 동적

배포 방식
&amp;rarr; Worker 애플리케이션

현재 백엔드 데이터
&amp;rarr; 없음

백엔드 확장 가능성
&amp;rarr; 있음
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;32. 이미지 파일은 어디에 있고 어떻게 제공될까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WMU 사이트의 메인 이미지는 다음 파일입니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;public/hero-women-network.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서는 다음처럼 참조합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.hero-art {
  background-image:
    url(&quot;/hero-women-network.png&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드할 때 이 파일은 정적 Assets에 포함됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 사용자가 이미지를 요청하면 Sites의 배포 환경에서 해당 정적 자산을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 이미지 최적화 경로를 사용하는 경우 Worker는 원본 이미지를 Assets에서 읽고 이미지 처리 기능을 거쳐 반환할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Worker 코드에는 다음 환경 바인딩이 정의돼 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;interface Env {
  ASSETS: Fetcher;
  DB: D1Database;
  IMAGES: {
    input(stream: ReadableStream): unknown;
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 역할은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바인딩역할&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ASSETS&lt;/td&gt;
&lt;td&gt;정적 이미지&amp;middot;CSS&amp;middot;JS 파일 접근&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB&lt;/td&gt;
&lt;td&gt;D1 데이터베이스 접근&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IMAGES&lt;/td&gt;
&lt;td&gt;이미지 크기&amp;middot;포맷 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 사이트에서 DB를 실제로 사용하는 것은 아니지만 표준 Worker 템플릿에는 연결 형태가 준비되어 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;33. Sites는 Cloudflare Pages와 같은 것일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전히 같다고 단정할 수는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인된 프로젝트는 다음 Cloudflare 기술을 사용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cloudflare Vite Plugin&lt;/li&gt;
&lt;li&gt;Wrangler&lt;/li&gt;
&lt;li&gt;Worker fetch() 진입점&lt;/li&gt;
&lt;li&gt;Static Assets 바인딩&lt;/li&gt;
&lt;li&gt;이미지 처리 바인딩&lt;/li&gt;
&lt;li&gt;선택적 D1&lt;/li&gt;
&lt;li&gt;선택적 R2&lt;/li&gt;
&lt;li&gt;nodejs_compat 호환 플래그&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Cloudflare Workers 호환 배포 구조인 것은 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 사용자의 개인 Cloudflare 계정에 직접 Worker가 생성됐다고 확인되지는 않았습니다. Sites가 자체적으로 관리하는 Cloudflare 기반 인프라에 배포하는 구조로 보는 것이 적절합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 일반적으로 다음 작업을 직접 하지 않습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cloudflare 계정 연결&lt;/li&gt;
&lt;li&gt;Wrangler 로그인&lt;/li&gt;
&lt;li&gt;Worker 이름 생성&lt;/li&gt;
&lt;li&gt;DNS 레코드 설정&lt;/li&gt;
&lt;li&gt;TLS 인증서 설정&lt;/li&gt;
&lt;li&gt;Assets 버킷 설정&lt;/li&gt;
&lt;li&gt;D1 리소스 ID 직접 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 Sites 서비스가 대신 관리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;34. 배포한 사이트는 Work VM이 꺼져도 계속 동작할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인된 WMU 사례에서는 로컬 작업공간이 없어졌는데도 Sites의 운영 URL과 원격 버전이 유지됐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이미 성공적으로 배포된 사이트의 운영은 Work VM 프로세스에 의존하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 다음은 구분해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;계속 동작하는 것&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 배포된 사이트&lt;/li&gt;
&lt;li&gt;정적 이미지와 CSS&lt;/li&gt;
&lt;li&gt;Worker 페이지 응답&lt;/li&gt;
&lt;li&gt;배포된 API 경로&lt;/li&gt;
&lt;li&gt;연결된 D1&amp;middot;R2&lt;/li&gt;
&lt;li&gt;운영 환경변수&lt;/li&gt;
&lt;li&gt;공개 URL&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Work VM이 다시 필요한 것&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스 수정&lt;/li&gt;
&lt;li&gt;새 이미지 생성&lt;/li&gt;
&lt;li&gt;기능 추가&lt;/li&gt;
&lt;li&gt;디자인 변경&lt;/li&gt;
&lt;li&gt;새 버전 빌드&lt;/li&gt;
&lt;li&gt;새 배포 생성&lt;/li&gt;
&lt;li&gt;로컬 미리보기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트 수정이 필요하면 원격 저장 버전에서 소스를 다시 복원한 뒤 변경하고 새 버전을 배포할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;35. Sites로 만들기 적합한 서비스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites는 다음과 같은 프로젝트에 잘 맞습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;콘텐츠와 소개 사이트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회사 홈페이지&lt;/li&gt;
&lt;li&gt;브랜드 사이트&lt;/li&gt;
&lt;li&gt;행사 안내&lt;/li&gt;
&lt;li&gt;포트폴리오&lt;/li&gt;
&lt;li&gt;투자 소개&lt;/li&gt;
&lt;li&gt;제품 랜딩 페이지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 기반 웹앱&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대시보드&lt;/li&gt;
&lt;li&gt;관리자 화면&lt;/li&gt;
&lt;li&gt;교육 플랫폼&lt;/li&gt;
&lt;li&gt;퀴즈 서비스&lt;/li&gt;
&lt;li&gt;게시판&lt;/li&gt;
&lt;li&gt;신청&amp;middot;접수 시스템&lt;/li&gt;
&lt;li&gt;포인트 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI 기능이 포함된 사이트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 상담&lt;/li&gt;
&lt;li&gt;번역&lt;/li&gt;
&lt;li&gt;문서 요약&lt;/li&gt;
&lt;li&gt;콘텐츠 생성&lt;/li&gt;
&lt;li&gt;문제 생성&lt;/li&gt;
&lt;li&gt;학습 피드백&lt;/li&gt;
&lt;li&gt;이미지 분석 결과 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Webhook 기반 연동&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Telegram 챗봇&lt;/li&gt;
&lt;li&gt;결제 결과 수신&lt;/li&gt;
&lt;li&gt;GitHub 이벤트&lt;/li&gt;
&lt;li&gt;외부 서비스 알림&lt;/li&gt;
&lt;li&gt;폼 접수 후 이메일 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;36. Sites에 적합하지 않은 서비스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 기능은 일반 VPS나 전용 백엔드가 더 적합합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenClaw Gateway 전체 실행&lt;/li&gt;
&lt;li&gt;Telegram 무한 폴링&lt;/li&gt;
&lt;li&gt;임의 포트의 TCP 서버&lt;/li&gt;
&lt;li&gt;SSH 서버&lt;/li&gt;
&lt;li&gt;장기간 실행되는 Python 프로세스&lt;/li&gt;
&lt;li&gt;시스템 패키지를 계속 사용하는 데몬&lt;/li&gt;
&lt;li&gt;서버 로컬 디스크에 의존하는 프로그램&lt;/li&gt;
&lt;li&gt;장기 GPU 작업&lt;/li&gt;
&lt;li&gt;지속적인 WebSocket 서버 상태&lt;/li&gt;
&lt;li&gt;Docker Compose 전체 스택&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloudflare Workers 플랫폼 자체에는 Queues, Workflows, Durable Objects, Cron 같은 다양한 기능이 있지만, ChatGPT Sites가 그 기능을 모두 직접 노출한다고 확인된 것은 아닙니다. Sites에서 제공되는 실제 기능 범위 안에서 설계해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;37. Sites를 이해하기 위한 가장 쉬운 비유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites를 식당으로 비유하면 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Work VM은 주방 테스트 공간&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메뉴를 개발함&lt;/li&gt;
&lt;li&gt;재료를 준비함&lt;/li&gt;
&lt;li&gt;맛을 확인함&lt;/li&gt;
&lt;li&gt;접시 구성을 수정함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;저장된 사이트 버전은 확정된 레시피&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 시점의 레시피를 보관함&lt;/li&gt;
&lt;li&gt;이전 레시피로 돌아갈 수 있음&lt;/li&gt;
&lt;li&gt;누가 무엇을 배포했는지 기준이 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Worker는 주문 처리 직원&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주문이 들어올 때 실행됨&lt;/li&gt;
&lt;li&gt;요청을 해석함&lt;/li&gt;
&lt;li&gt;정적 파일이나 서버 결과를 반환함&lt;/li&gt;
&lt;li&gt;필요한 경우 데이터베이스와 외부 API를 호출함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Assets는 미리 준비된 음식과 재료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;이미지&lt;/li&gt;
&lt;li&gt;폰트&lt;/li&gt;
&lt;li&gt;정적 HTML&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;D1은 주문&amp;middot;회원 장부&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회원&lt;/li&gt;
&lt;li&gt;게시물&lt;/li&gt;
&lt;li&gt;포인트&lt;/li&gt;
&lt;li&gt;신청 내역&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;R2는 창고&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지&lt;/li&gt;
&lt;li&gt;PDF&lt;/li&gt;
&lt;li&gt;영상&lt;/li&gt;
&lt;li&gt;업로드 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;chatgpt.site는 식당 주소&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 사용자가 찾아오는 공개 주소&lt;/li&gt;
&lt;li&gt;현재 운영 버전과 연결됨&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;38. 전체 구조를 한 장으로 정리하면&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;886&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv9jku/dJMcagGqEQe/wzO86mjlL6mIAot4gTAxpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv9jku/dJMcagGqEQe/wzO86mjlL6mIAot4gTAxpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv9jku/dJMcagGqEQe/wzO86mjlL6mIAot4gTAxpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv9jku%2FdJMcagGqEQe%2FwzO86mjlL6mIAot4gTAxpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1005&quot; height=&quot;886&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;886&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;39. 자주 묻는 질문&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ChatGPT가 만든 사이트는 VM 안에서 계속 실행되나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아닙니다. VM은 사이트를 만들고 빌드하는 작업환경입니다. 성공적으로 배포된 운영 사이트는 별도의 Sites 운영 환경에서 제공됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;VM 폴더가 삭제되면 사이트도 없어지나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 WMU 사례에서는 로컬 폴더가 없어져도 원격 소스 버전과 운영 URL이 유지됐습니다. 원격 버전에서 체크아웃을 다시 복원할 수 있었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sites는 정적 사이트만 만들 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아닙니다. 정적 자산과 Worker 서버 코드가 함께 배포될 수 있어 API, 서버 렌더링, 인증, 데이터베이스와 외부 API 호출을 구현할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Worker는 일반 Node.js 서버인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아닙니다. 계속 실행되는 Node.js 프로세스가 아니라 HTTP 요청을 fetch() 핸들러로 처리하는 서버리스 런타임입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;백엔드 API를 만들 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능합니다. Next.js Route Handler 또는 Worker 코드를 이용해 REST API와 Webhook을 만들 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터베이스도 가능한가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites에서 D1 바인딩을 연결할 수 있는 구조가 확인됐습니다. 현재 WMU 사이트는 데이터베이스를 사용하지 않습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이미지 업로드도 가능한가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2 저장소를 연결하면 구현할 수 있는 구조입니다. 현재 WMU 사이트는 프로젝트에 포함된 정적 이미지만 사용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DeepSeek API를 연결할 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker의 fetch()로 외부 API를 호출하고, API 키를 Sites의 비밀 환경변수에 저장하는 구조로 구현할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Telegram 봇도 만들 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Webhook 방식의 Telegram 봇은 적합합니다. 반면 계속 실행되는 폴링 방식이나 OpenClaw 전체 Gateway는 Sites보다 VPS가 적합합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sites에 OpenClaw를 설치할 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenClaw의 전체 Gateway는 장기 실행 프로세스와 지속적인 채널 연결이 필요하므로 Worker 실행 모델과 맞지 않습니다. 일부 기능을 API 또는 Webhook으로 분리하는 방식은 가능합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용자의 Cloudflare 대시보드에서 Worker를 볼 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 수 없습니다. 현재 프로젝트는 Cloudflare Worker 호환 기술을 사용하지만, 사용자의 개인 Cloudflare 계정에 직접 배포됐다는 정보는 확인되지 않았습니다. Sites가 관리하는 인프라에 배포되는 구조로 이해하는 것이 적절합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;40. 결론: Sites는 &amp;lsquo;코드 생성기&amp;rsquo;보다 &amp;lsquo;관리형 웹 배포 시스템&amp;rsquo;에 가깝다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT Sites의 핵심은 예쁜 웹페이지 코드를 생성하는 데서 끝나지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트가 외부에서 실제로 작동하려면 다음 과정이 모두 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;기획
&amp;rarr; 디자인
&amp;rarr; React&amp;middot;Next.js 구현
&amp;rarr; Vinext&amp;middot;Vite 빌드
&amp;rarr; Worker 산출물 생성
&amp;rarr; 정적 Assets 패키징
&amp;rarr; 산출물 검증
&amp;rarr; 소스 버전 저장
&amp;rarr; 운영 배포
&amp;rarr; 공개 URL 연결
&amp;rarr; 접근 정책 적용
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sites는 이 과정을 하나의 작업 흐름으로 묶습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Work VM은 사이트를 만드는 공간이고, Vinext는 Next.js를 Vite&amp;middot;Worker 환경에 연결하며, Worker는 외부 요청을 처리합니다. 정적 파일은 Assets로 제공되고, 필요한 경우 D1&amp;middot;R2&amp;middot;환경변수&amp;middot;인증&amp;middot;외부 API를 연결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Sites는 단순한 정적 홈페이지 제작 도구라기보다 다음에 더 가깝습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React와 Next.js로 만든 프런트엔드와 Worker 백엔드를 빌드하고, 버전으로 저장하고, 관리형 주소와 권한을 붙여 외부에 배포하는 ChatGPT 내장형 풀스택 호스팅 시스템&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 서버 프로그램을 Sites에서 실행할 수 있는 것은 아닙니다. 웹 요청에 따라 실행되는 API와 Webhook에는 적합하지만, OpenClaw Gateway처럼 계속 살아 있어야 하는 프로그램은 VPS나 장기 실행 컨테이너가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹사이트, AI 상담, 교육 플랫폼, 신청 시스템, 관리자 페이지, Telegram Webhook처럼 요청 중심으로 동작하는 서비스라면 Sites의 구조를 활용할 수 있습니다. 반대로 지속적인 프로세스와 자유로운 서버 제어가 핵심이라면 일반 클라우드 서버를 선택해야 합니다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1793</guid>
      <comments>https://javaexpert.tistory.com/1793#entry1793comment</comments>
      <pubDate>Fri, 24 Jul 2026 15:03:48 +0900</pubDate>
    </item>
    <item>
      <title>AI가 &amp;lsquo;도구&amp;rsquo;가 아니라 &amp;lsquo;팀원&amp;rsquo;이 되는 회사 (Buzz)</title>
      <link>https://javaexpert.tistory.com/1792</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;잭 도시의 Block이 공개한 협업 플랫폼 &amp;lsquo;Buzz&amp;rsquo;가 보여주는 미래&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이미 업무에서 인공지능을 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메일을 정리하고, 문서를 요약하고, 코드를 작성하고, 회의 내용을 정리한다. 그러나 현재의 AI는 대부분 업무를 돕는 &lt;b&gt;보조 도구&lt;/b&gt;에 머물러 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 AI에게 질문한다.&lt;br /&gt;AI가 답변을 만든다.&lt;br /&gt;그리고 실제 업무는 다시 사람이 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AI가 단순히 질문에 답하는 도구가 아니라, 프로젝트에 직접 참여하는 팀원이라면 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 팀 채팅방에 들어오고, 과거 대화를 검색하고, 코드를 수정하고, 테스트를 실행하고, 리뷰를 요청한다. 사람은 AI가 수행한 작업을 확인하고 승인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 7월, 잭 도시가 이끄는 Block이 공개한 오픈소스 협업 플랫폼 &lt;b&gt;Buzz&lt;/b&gt;는 바로 이런 업무 환경을 지향한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz를 한 문장으로 표현하면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack의 팀 대화, GitHub의 코드 관리, CI/CD 자동화, 사내 검색, AI 에이전트를 하나로 통합한 업무 플랫폼&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 여러 서비스를 한 화면에 모은 제품은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz가 던지는 더 중요한 질문은 이것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 앞으로 회사에서 어떤 지위로 일하게 될 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;흩어진 업무 도구를 하나로 연결하다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 대부분의 개발 조직은 여러 개의 서비스를 동시에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack이나 Microsoft Teams에서 대화하고, GitHub에서 코드를 관리한다. GitHub Actions나 Jenkins로 테스트와 배포를 자동화하고, Notion이나 Confluence에 문서를 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구도 별도로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 프로젝트를 진행하지만 업무 기록은 여러 공간에 흩어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원이 Slack에서 기능 개발 방향을 논의한다. 개발자는 GitHub에 코드를 올린다. 테스트 결과는 다른 자동화 도구에 저장된다. 왜 이런 결정을 내렸는지는 문서나 과거 메시지를 다시 찾아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 지나면 코드 자체는 남지만, 그 코드가 만들어진 배경은 쉽게 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 이 모든 활동을 하나의 업무 공간에서 연결하려 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀 대화&lt;/li&gt;
&lt;li&gt;프로젝트 채널&lt;/li&gt;
&lt;li&gt;코드 저장소&lt;/li&gt;
&lt;li&gt;코드 변경과 리뷰&lt;/li&gt;
&lt;li&gt;자동화 워크플로&lt;/li&gt;
&lt;li&gt;AI 에이전트의 작업&lt;/li&gt;
&lt;li&gt;검색과 감사 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람과 AI가 같은 공간에서 대화하고, 작업하고, 결과를 남기는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트를 정식 팀원처럼 다룬다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz에서 가장 눈에 띄는 개념은 AI 에이전트의 위치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 협업 서비스에서 AI는 대개 채팅방에 연결된 봇이다. 질문을 받으면 답변하거나, 특정 명령을 실행하는 정도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 AI 에이전트를 조금 다르게 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람과 마찬가지로 AI 에이전트도 고유한 신원과 암호화 키를 가질 수 있다. 참여할 수 있는 채널과 접근할 수 있는 프로젝트가 정해지고, 어떤 작업을 수행했는지 활동 기록이 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 버그 수정 에이전트는 다음과 같은 과정을 처리할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;팀 채널에 등록된 오류 내용을 확인한다.&lt;/li&gt;
&lt;li&gt;관련 프로젝트의 과거 대화를 검색한다.&lt;/li&gt;
&lt;li&gt;코드 저장소를 분석한다.&lt;/li&gt;
&lt;li&gt;수정 코드를 작성한다.&lt;/li&gt;
&lt;li&gt;테스트를 실행한다.&lt;/li&gt;
&lt;li&gt;수정안을 제출한다.&lt;/li&gt;
&lt;li&gt;사람 개발자에게 리뷰를 요청한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 AI가 모든 회사 정보에 자유롭게 접근하는 것이 아니라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트도 사람과 마찬가지로 허용된 채널과 저장소에만 접근하도록 구성할 수 있다. 특정 프로젝트에 참여시키고 싶다면 해당 에이전트를 프로젝트 채널에 초대하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 AI를 단순한 기능이 아니라 &lt;b&gt;권한과 책임 범위를 가진 업무 주체&lt;/b&gt;로 취급하는 접근이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Git 브랜치 하나가 프로젝트 채팅방이 된다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz가 제시하는 흥미로운 방식 가운데 하나는 Git 브랜치와 채팅 채널을 연결하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 새로운 기능을 만들기 위해 브랜치를 생성하면, 해당 브랜치 전용 프로젝트 채널을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 결제 기능을 개발한다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;feature/payment-api라는 브랜치가 만들어지면 관련 채널에서 다음 활동이 함께 진행된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 요구사항 논의&lt;/li&gt;
&lt;li&gt;코드 변경&lt;/li&gt;
&lt;li&gt;AI 에이전트의 분석&lt;/li&gt;
&lt;li&gt;테스트 결과&lt;/li&gt;
&lt;li&gt;코드 리뷰&lt;/li&gt;
&lt;li&gt;수정 요청&lt;/li&gt;
&lt;li&gt;최종 승인&lt;/li&gt;
&lt;li&gt;병합 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 환경에서는 이 정보가 Slack, GitHub Pull Request, 자동화 로그 등에 나뉘어 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz에서는 코드와 대화가 같은 프로젝트 흐름 안에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 문제가 발생했을 때 단순히 &amp;ldquo;어떤 코드가 변경됐는가&amp;rdquo;만 보는 것이 아니라, &amp;ldquo;왜 이 코드를 변경했는가&amp;rdquo;까지 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트 입장에서도 큰 차이가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드만 읽는 것이 아니라 코드가 작성된 배경, 관련 대화, 테스트 결과, 리뷰 의견을 함께 이해할 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;대화와 코드가 하나의 조직 기억이 된다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업이 AI를 업무에 적용할 때 가장 어려운 문제 중 하나는 정보가 분산돼 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 제대로 답변하려면 여러 장소에서 자료를 가져와야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Slack의 대화&lt;/li&gt;
&lt;li&gt;GitHub의 코드&lt;/li&gt;
&lt;li&gt;Notion의 문서&lt;/li&gt;
&lt;li&gt;Jira의 이슈&lt;/li&gt;
&lt;li&gt;CI/CD 테스트 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 서비스의 데이터를 수집하고, 검색 시스템에 넣고, 서로 연결해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 처음부터 대화와 코드 활동을 같은 이벤트 구조로 저장하는 방향을 택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 메시지, 코드 변경, 리뷰 승인, 워크플로 실행, AI 작업 등이 하나의 연속된 기록으로 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 서비스 장애가 발생했을 때 AI에게 이렇게 물을 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 오류가 과거에도 발생한 적이 있는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 단순히 문서에서 비슷한 문장을 찾는 데 그치지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거의 장애 대화, 당시 수정된 코드, 테스트 결과, 작업한 사람, 재발 방지 조치까지 함께 찾아낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 축적된 기록은 회사의 &lt;b&gt;조직 기억&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 퇴사하거나 담당자가 바뀌어도 프로젝트가 어떤 과정을 거쳐 현재 모습이 됐는지 추적할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모든 작업에 서명이 붙는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 Nostr 프로토콜의 이벤트 구조를 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 작성, 코드 변경, 리뷰 승인, 워크플로 실행과 같은 활동이 이벤트로 기록되고, 각 이벤트에는 작업을 수행한 주체의 암호화 서명이 붙는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 다음 내용을 확인할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;누가 작업했는가&lt;/li&gt;
&lt;li&gt;사람인가, AI 에이전트인가&lt;/li&gt;
&lt;li&gt;어떤 권한으로 작업했는가&lt;/li&gt;
&lt;li&gt;어떤 요청에서 작업이 시작됐는가&lt;/li&gt;
&lt;li&gt;어떤 코드가 변경됐는가&lt;/li&gt;
&lt;li&gt;누가 검토하고 승인했는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 실제 코드를 수정하고 업무를 수행하는 환경에서는 결과만큼이나 과정이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 어떤 정보를 참고했는지, 어떤 권한을 사용했는지, 최종적으로 누가 승인했는지가 명확하게 남아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 AI 에이전트 시대의 협업에서 &lt;b&gt;감사 가능성&lt;/b&gt;과 &lt;b&gt;책임 추적&lt;/b&gt;이 중요한 요소가 될 것임을 보여준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정말 탈중앙화된 플랫폼일까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 오픈소스와 자기주권적인 운영 방식을 강조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업이나 조직은 Buzz를 자신의 서버에 설치할 수 있다. 대화, 코드, 파일과 같은 데이터를 외부 SaaS 기업에 맡기지 않고 직접 관리하는 것도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이를 완전한 P2P 탈중앙 네트워크로 이해해서는 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Buzz의 각 커뮤니티는 하나의 중앙 릴레이 서버를 중심으로 작동한다. 사용자의 읽기와 쓰기, 검색, 이벤트 처리도 해당 릴레이가 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;릴레이끼리 데이터를 자동으로 교환하거나 복제하는 완전한 분산형 구조는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Buzz가 말하는 탈중앙화는 다음 의미에 더 가깝다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 SaaS 기업에 종속되지 않는다.&lt;/li&gt;
&lt;li&gt;조직이 자체 서버를 운영할 수 있다.&lt;/li&gt;
&lt;li&gt;조직이 자신의 데이터와 도메인을 소유한다.&lt;/li&gt;
&lt;li&gt;사용자가 암호화 신원을 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;소스 코드가 공개돼 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 시스템 내부가 완전히 탈중앙화돼 있다기보다, 여러 조직이 각자의 서버를 소유할 수 있는 &lt;b&gt;분산 배포형 오픈소스 플랫폼&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;아직은 완성된 기업용 제품이 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 흥미로운 비전을 제시하지만, 현재는 개발 단계에 있는 프로젝트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 채널, 개인 메시지, 공동 캔버스, 검색, 감사 로그, 데스크톱 앱, Git 호스팅, 워크플로와 같은 기능은 구현돼 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 모바일 앱, 푸시 알림, 일부 승인 워크플로와 고급 에이전트 기능은 계속 개발되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack, GitHub, Notion, Jenkins처럼 오랫동안 검증된 서비스를 한 번에 대체할 만큼 안정적인지는 아직 확인이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체 호스팅 방식에도 책임이 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직은 직접 서버를 운영하고, 보안을 관리하고, 데이터를 백업하고, 장애와 업데이트에 대응해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈소스라는 이유만으로 운영 비용과 관리 부담까지 사라지는 것은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Buzz가 보여주는 것은 제품보다 조직의 변화다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] Buzz의 의미는 단순히 새로운 협업 서비스 하나가 등장했다는 데 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 중요한 변화는 AI를 바라보는 관점에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 기업은 AI를 직원이 사용하는 도구로 본다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;직원
&amp;rarr; AI에게 질문
&amp;rarr; AI가 답변
&amp;rarr; 직원이 실제 업무 수행
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz가 지향하는 구조에서는 AI가 업무 과정 안으로 직접 들어온다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사람과 AI가 같은 프로젝트에 참여
&amp;rarr; AI가 대화와 코드를 확인
&amp;rarr; AI가 작업 수행
&amp;rarr; 사람이 검토하고 승인
&amp;rarr; 전체 과정이 기록으로 보존
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] 앞으로의 회사에서는 AI가 단순히 생산성을 높이는 프로그램이 아니라, 특정 역할을 맡아 반복적으로 일하는 디지털 구성원이 될 가능성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 AI는 버그를 분석하고, 다른 AI는 코드를 리뷰할 수 있다. 또 다른 AI는 문서를 정리하고 프로젝트 진행 상황을 요약할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 모든 작업을 직접 처리하기보다, 목표와 권한을 설정하고 결과를 검토하는 역할로 이동할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 회사에 필요한 것은 더 강력한 챗봇 하나가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 어떤 정보에 접근할 수 있는지, 어떤 행동을 할 수 있는지, 누가 결과를 승인하는지 관리할 수 있는 새로운 업무 시스템이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 바로 그 문제를 먼저 설계하려는 시도다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 시대의 협업 도구가 갖춰야 할 조건&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Inference] Buzz의 구조를 통해 앞으로의 AI 협업 플랫폼에 필요한 조건을 정리해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, AI 에이전트마다 독립적인 신원이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 AI가 같은 계정과 권한을 공유하면 어떤 에이전트가 어떤 작업을 했는지 구분하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째, 최소 권한 원칙이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 필요 이상의 대화와 코드에 접근하지 못하도록 프로젝트 단위의 권한을 부여해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셋째, 모든 실행 과정이 기록돼야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 어떤 요청을 받고, 어떤 자료를 참고하고, 무엇을 변경했는지 추적할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넷째, 중요한 작업에는 사람의 승인이 남아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 작업을 수행하더라도 배포, 결제, 데이터 삭제처럼 위험도가 높은 행동은 사람이 검토하는 구조가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다섯째, 대화와 실행 기록이 분리되지 않아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 업무 맥락을 이해하려면 코드뿐 아니라 그 코드가 만들어진 이유까지 볼 수 있어야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 팀원이 출근하는 시대&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz가 성공적으로 정착할지는 아직 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 협업 도구를 대체하기에는 부족한 기능이 있고, 자체 서버 운영에 따른 부담도 존재한다. Block 내부에서 효과적으로 사용되더라도 다른 기업에서도 같은 방식이 통할지는 더 지켜봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Buzz가 던진 문제는 분명하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 실제 업무를 수행하기 시작하면 회사는 더 이상 AI를 단순한 플러그인이나 채팅 기능으로만 관리할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게도 신원이 필요하고, 권한이 필요하며, 활동 기록과 책임 범위가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람과 AI가 함께 일하는 회사는 단순히 기존 업무 도구에 AI 버튼 하나를 추가한 모습이 아닐 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화, 코드, 권한, 실행, 승인, 기록이 모두 연결된 새로운 업무 환경이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buzz는 그 미래를 완성한 제품은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AI가 도구의 자리를 넘어 &lt;b&gt;팀원의 자리로 이동할 때 회사의 운영체제는 어떻게 바뀌어야 하는가&lt;/b&gt;라는 질문을 가장 구체적으로 보여주는 사례 중 하나다.&lt;/p&gt;
&lt;figure id=&quot;og_1784680875319&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Buzz &amp;mdash; Your people, your agents, your project &amp;mdash; all in one place.&quot; data-og-description=&quot;Come test the early stages with us.&quot; data-og-host=&quot;buzz.xyz&quot; data-og-source-url=&quot;https://buzz.xyz/&quot; data-og-url=&quot;https://buzz.xyz&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/elTqtS/dJMb86oaJls/EYGMn1zGFwfbag4KHxSgA1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/CEZyf/dJMb88Gibmd/rQBHyLHwTI04VmR9oi4mVK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://buzz.xyz/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://buzz.xyz/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/elTqtS/dJMb86oaJls/EYGMn1zGFwfbag4KHxSgA1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/CEZyf/dJMb88Gibmd/rQBHyLHwTI04VmR9oi4mVK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Buzz &amp;mdash; Your people, your agents, your project &amp;mdash; all in one place.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Come test the early stages with us.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;buzz.xyz&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1792</guid>
      <comments>https://javaexpert.tistory.com/1792#entry1792comment</comments>
      <pubDate>Wed, 22 Jul 2026 09:41:30 +0900</pubDate>
    </item>
    <item>
      <title>파라미터 대신 &amp;lsquo;작업의 지평선&amp;rsquo;을 확장한 AI 모델 Agents-A1</title>
      <link>https://javaexpert.tistory.com/1791</link>
      <description>&lt;h1&gt;35B 모델이 거대 AI를 따라잡는 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인공지능 모델의 발전은 오랫동안 하나의 방향으로 설명됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 많은 데이터를 학습하고, 더 많은 연산을 투입하고, 더 많은 파라미터를 가진 모델을 만든다. 모델이 커질수록 더 복잡한 문제를 해결할 수 있다는 것이 대규모 언어 모델의 기본적인 성장 공식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 InternScience가 공개한 &lt;b&gt;Agents-A1&lt;/b&gt;은 조금 다른 질문을 던진다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델의 크기를 계속 키우는 대신, AI가 한 번에 수행할 수 있는 작업의 길이를 늘리면 어떨까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1의 기술 보고서 제목은 이 질문을 그대로 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;Scaling the Horizon, Not the Parameters&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터가 아니라 &amp;lsquo;지평선&amp;rsquo;을 확장한다는 의미다. 여기서 지평선, 즉 Horizon은 AI가 하나의 목표를 해결하기 위해 계획하고, 검색하고, 도구를 사용하고, 결과를 확인하고, 실패를 수정하는 전체 작업 길이를 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 총 35B 규모의 Mixture-of-Experts 모델로, 장시간 검색, 엔지니어링, 과학 연구, 복합 지시 이행, 도구 호출에 특화되어 있다. 공식 프로젝트는 최대 256K 컨텍스트와 여섯 가지 에이전트 평가 영역을 제시한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Agents-A1은 무엇이 다른가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 챗봇 모델은 사용자의 질문을 받은 뒤 답변을 생성한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;질문 &amp;rarr; 추론 &amp;rarr; 답변
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1이 목표로 하는 작업은 조금 더 길다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;목표 이해
&amp;rarr; 문제 분해
&amp;rarr; 검색 도구 실행
&amp;rarr; 검색 결과 확인
&amp;rarr; 추가 정보 판단
&amp;rarr; 코드 실행
&amp;rarr; 오류 확인
&amp;rarr; 전략 수정
&amp;rarr; 결과 검증
&amp;rarr; 최종 답변
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 좋은 문장을 만드는 것뿐 아니라 &lt;b&gt;여러 단계의 행동을 이어가며 실제 작업을 끝내는 능력&lt;/b&gt;을 강화한 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 프로젝트는 Agents-A1이 복잡한 목표를 실행 가능한 하위 단계로 나누고, 중간 결과에 따라 전략을 수정하며, API&amp;middot;검색 엔진&amp;middot;코드 인터프리터 같은 외부 도구를 호출할 수 있도록 설계됐다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이는 생각보다 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 모델이 &amp;ldquo;이 문제를 해결하려면 웹 검색과 데이터 분석이 필요합니다&amp;rdquo;라고 설명하는 데 그친다면, 에이전트 모델은 검색 도구를 호출하고, 결과를 비교하고, 필요한 계산을 수행한 뒤 최종 결론까지 도달해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 이처럼 &lt;b&gt;오래 이어지는 작업 과정 자체를 학습 대상으로 삼았다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;평균 4만 5천 토큰의 작업 과정을 학습하다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1 연구진은 외부 지식, 행동, 관찰 결과, 검증 결과를 연결하는 장기 작업 인프라를 구축했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 인프라에서는 단순한 질문과 정답만 저장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 어떤 행동을 선택했는지, 도구를 실행한 뒤 무엇을 관찰했는지, 결과가 맞았는지, 다음 행동을 어떻게 수정했는지까지 하나의 궤적으로 만든다. 연구진이 구축한 에이전트 궤적의 평균 길이는 약 45K 토큰이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습은 세 단계로 진행됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 여러 영역의 에이전트 행동을 폭넓게 학습하는 지도 미세조정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 검색, 과학, 엔지니어링 등 각 영역에 특화된 교사 모델을 별도로 훈련하는 단계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째는 여러 교사 모델의 능력을 하나의 모델로 이전하는 다중 교사 온폴리시 증류다. 연구진은 이를 통해 서로 다른 여섯 가지 에이전트 능력을 하나의 35B 모델에 통합했다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 표현하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;검색 전문 교사
과학 연구 전문 교사
엔지니어링 전문 교사
도구 사용 전문 교사
지시 이행 전문 교사
        &amp;darr;
Agents-A1 하나로 통합
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 무작정 크게 만드는 대신, &lt;b&gt;각 분야에서 길고 복잡한 작업을 수행한 경험을 효율적으로 증류한 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;35B 모델이 1조 파라미터급과 경쟁한다는 의미&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1을 소개하는 문구 가운데 가장 눈에 띄는 표현은 &amp;ldquo;35B 모델로 1조 파라미터급 성능에 도달했다&amp;rdquo;는 주장이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표현은 주의해서 해석해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1이 모든 영역에서 1조 파라미터급 모델보다 뛰어나다는 뜻은 아니다. 공식 평가표를 보면 특정 장기 작업과 과학 연구, 복합 지시 이행에서 매우 강한 결과를 기록한 반면, 일부 코딩&amp;middot;머신러닝&amp;middot;검색 벤치마크에서는 더 큰 상용 모델보다 낮은 점수를 기록했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 모델 카드가 보고한 대표 결과는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SEAL-0: 56.36&lt;/li&gt;
&lt;li&gt;GAIA: 96.04&lt;/li&gt;
&lt;li&gt;BrowseComp: 75.51&lt;/li&gt;
&lt;li&gt;FrontierScience-Olympiad: 79.0&lt;/li&gt;
&lt;li&gt;FrontierScience-Research: 40.0&lt;/li&gt;
&lt;li&gt;IFBench: 80.61&lt;/li&gt;
&lt;li&gt;IFEval: 94.82&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 SEAL-0, HiPhO, FrontierScience, IFBench 등에서는 비교 대상 대형 모델보다 높은 결과가 보고됐다. 반면 SciCode에서는 Agents-A1이 44.33, GPT-5.5가 56.1이었고, MLE-Lite에서는 Agents-A1이 43.94, GPT-5.5가 72.73이었다. BrowseComp 역시 Agents-A1의 75.51보다 비교 대상 대형 모델들의 점수가 더 높았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 정확한 표현은 다음에 가깝다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 모든 능력에서 초거대 모델을 대체하는 모델이 아니라, 일부 장기 에이전트 작업에서 훨씬 큰 모델과 경쟁할 수 있는 효율적인 특화 모델이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점이 Agents-A1의 진짜 의미다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;일반 추론 모델과는 무엇이 다른가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론 모델은 어려운 수학 문제나 논리 문제를 해결하기 위해 내부 계산을 길게 수행한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;문제 &amp;rarr; 긴 내부 추론 &amp;rarr; 답변
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트 모델은 내부 추론과 함께 외부 환경을 활용한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;문제
&amp;rarr; 계획
&amp;rarr; 도구 호출
&amp;rarr; 결과 관찰
&amp;rarr; 계획 수정
&amp;rarr; 추가 도구 호출
&amp;rarr; 검증
&amp;rarr; 답변
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 추론 모델의 중심이 &lt;b&gt;생각의 깊이&lt;/b&gt;라면 에이전트 모델의 중심은 &lt;b&gt;생각과 행동의 연결&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 함수 호출과 외부 도구 연동을 기본적으로 지원한다. 공식 실행 예제에서는 검색&amp;middot;코드 실행&amp;middot;API 호출에 사용할 수 있는 도구 호출 파서가 제공되며, SGLang과 vLLM을 통해 OpenAI 호환 API 서버로 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모델 하나를 실행했다고 해서 자동으로 웹 검색이나 파일 수정이 가능한 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 어떤 도구를 사용할지 판단하는 두뇌에 가깝다. 실제 검색, 브라우저 조작, 터미널 실행, 파일 읽기와 쓰기는 별도의 에이전트 런타임이 담당해야 한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Agents-A1
    &amp;darr; 도구 호출 요청
에이전트 프레임워크
    &amp;darr;
웹 검색&amp;middot;브라우저&amp;middot;Python&amp;middot;터미널&amp;middot;파일 시스템
    &amp;darr; 실행 결과
Agents-A1
    &amp;darr; 다음 행동 결정
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Agents-A1의 성능을 제대로 활용하려면 단순 채팅 UI보다 &lt;b&gt;도구가 연결된 실행 환경&lt;/b&gt;이 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;장문맥 모델과 장기 에이전트 모델은 다르다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 공식 실행 예제에서 최대 262,144토큰의 컨텍스트 길이를 지원한다. 이미지 입력을 포함하는 멀티모달 모델이며, 텍스트 전용 실행 시에는 비전 인코더를 제외해 KV 캐시 공간을 확보하는 옵션도 제공된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 긴 컨텍스트를 지원한다고 해서 반드시 긴 작업을 잘 수행하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 문서를 한꺼번에 입력할 수 있는 능력과 여러 단계의 작업 목표를 끝까지 유지하는 능력은 서로 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 장문맥 모델은 긴 자료를 읽을 수 있어도 다음과 같은 문제를 보일 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 조사한 내용을 반복한다.&lt;/li&gt;
&lt;li&gt;중간에 원래 목표를 잊는다.&lt;/li&gt;
&lt;li&gt;이전 도구의 오류를 반영하지 못한다.&lt;/li&gt;
&lt;li&gt;여러 지시 조건 가운데 일부를 놓친다.&lt;/li&gt;
&lt;li&gt;잘못된 방향으로 진행하면서도 작업을 계속한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1이 강조하는 것은 단순한 컨텍스트 크기가 아니라, &lt;b&gt;긴 행동 궤적 안에서 목표&amp;middot;관찰&amp;middot;검증 결과를 유지하는 능력&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 논문 제목에서 말하는 Horizon Scaling의 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Agents-A1의 장점&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 중형 모델로 구현한 강력한 장기 작업 능력&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;35B급 모델이 일부 장기 검색과 과학 연구 벤치마크에서 훨씬 큰 모델과 경쟁한 것은 의미가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초거대 모델을 직접 운영하기 어려운 연구소나 기업에도 비교적 현실적인 에이전트 모델 선택지가 생겼기 때문이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 도구 호출을 전제로 설계된 모델&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 검색, API, 코드 인터프리터, 외부 작업 환경과 연동하는 함수 호출을 지원한다. 단순 질의응답보다 조사&amp;middot;분석&amp;middot;실행을 반복하는 워크플로에 적합하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 과학&amp;middot;연구형 작업에서 강한 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 평가에서는 FrontierScience-Research, FrontierScience-Olympiad, HiPhO 같은 과학 연구형 평가에서 강한 성능이 보고됐다. 특히 FrontierScience-Research에서는 비교표에 포함된 대형 모델보다 높은 점수를 기록했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 복잡한 지시 조건을 잘 유지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IFEval과 IFBench에서 높은 점수가 보고됐다. 이는 단순히 질문에 답하는 것보다 출력 형식, 금지 조건, 단계별 요구사항처럼 여러 제약을 동시에 지켜야 하는 작업에서 중요하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 오픈소스 생태계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 가중치와 일부 평가 코드, 기술 보고서가 공개돼 있다. 라이선스는 Apache 2.0이며, Hugging Face Transformers, SGLang, vLLM 형식으로 사용할 수 있다. 공식 모델 카드에는 llama.cpp, Ollama, LM Studio 등에서 사용할 수 있는 양자화 모델 탐색 경로도 제공된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 4B 경량 모델도 제공&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;InternScience는 2026년 7월 14일 Agents-A1-4B를 공개했다. 공식 결과에서는 4B 모델도 일부 검색&amp;middot;과학&amp;middot;지시 이행 벤치마크에서 비슷한 크기의 기본 모델보다 높은 성능을 기록했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;35B 모델이 서버나 고성능 워크스테이션용이라면, 4B 모델은 개인용 로컬 에이전트 실험에 더 가까운 선택지다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Agents-A1의 단점과 주의할 점&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 모든 분야에서 최고 성능은 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 장기 검색과 과학 연구, 복합 지시 이행에 강하지만 모든 코딩&amp;middot;엔지니어링 작업에서 최고는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 평가에서도 SciCode, MLE-Lite, HLE with tools, BrowseComp 등 일부 항목은 대형 프런티어 모델보다 낮았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &amp;ldquo;35B 모델이 1조 파라미터 모델을 완전히 대체했다&amp;rdquo;는 식의 해석은 과장에 가깝다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 벤치마크와 실제 업무 사이에는 차이가 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 평가는 공개된 에이전트 평가 프레임워크를 통해 일부 재현이 가능하다. 다만 벤치마크 성능이 기업 내부 시스템, 브라우저 자동화, 복잡한 파일 작업, 장시간 코딩 프로젝트에서 동일하게 나타난다고 단정할 수는 없다. 공식 모델 카드 역시 비교 대상 모델의 기존 보고값과 자체 평가값을 함께 사용했다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도입 전에는 실제 업무 데이터와 도구 환경을 이용한 별도 검증이 필요하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 모델만으로는 에이전트가 완성되지 않는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 도구를 선택하고 다음 행동을 결정할 수 있지만, 실제 도구 실행 시스템은 별도로 구축해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한 관리, 재시도, 브라우저 상태, 오류 복구, 비용 제한, 사용자 승인, 로그 기록 같은 기능은 에이전트 프레임워크와 애플리케이션이 담당해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 장기 작업은 비용과 시간이 커진다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 작업은 짧은 질문보다 더 많은 토큰과 도구 호출을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트가 길어질수록 첫 응답까지 걸리는 시간이 증가하고, 검색과 코드 실행을 반복하면 전체 작업 시간도 길어진다. &amp;ldquo;더 작은 모델&amp;rdquo;이 반드시 &amp;ldquo;더 저렴한 작업&amp;rdquo;을 의미하지는 않는다. 에이전트가 얼마나 많은 단계를 수행하는지도 전체 비용을 결정한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 로컬 실행에는 여전히 높은 사양이 필요하다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;35B급 모델은 양자화를 사용하더라도 일반 노트북에서 가볍게 실행하는 모델은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 PC에서는 GGUF나 다른 양자화 버전을 사용하거나, 더 작은 Agents-A1-4B를 선택하는 편이 현실적이다. 원본 모델을 긴 컨텍스트와 비전 기능까지 포함해 서비스하려면 충분한 GPU 메모리와 서빙 환경이 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;어떤 작업에 적합할까&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1이 잘 맞을 가능성이 높은 작업은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 웹사이트를 조사하는 심층 리서치&lt;/li&gt;
&lt;li&gt;논문 검색과 근거 비교&lt;/li&gt;
&lt;li&gt;코드를 작성하고 실행하고 수정하는 반복 작업&lt;/li&gt;
&lt;li&gt;과학&amp;middot;공학 분야의 도구 기반 문제 해결&lt;/li&gt;
&lt;li&gt;여러 제약 조건이 포함된 복잡한 보고서 작성&lt;/li&gt;
&lt;li&gt;API를 연속 호출하는 업무 자동화&lt;/li&gt;
&lt;li&gt;긴 프로젝트를 단계별로 수행하는 연구 에이전트&lt;/li&gt;
&lt;li&gt;이미지와 문서를 함께 분석하는 멀티모달 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 간단한 번역, 짧은 요약, 일상 대화, 단순 질의응답처럼 한 번의 응답으로 끝나는 작업에서는 Agents-A1의 장점이 크게 드러나지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업에는 더 작은 4B&amp;middot;7B급 모델이 빠르고 효율적일 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Agents-A1이 보여주는 새로운 방향&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1의 가장 중요한 성과는 특정 벤치마크 점수 하나가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모델이 보여주는 것은 &lt;b&gt;모델 성능을 키우는 방법이 파라미터 증가만은 아니라는 가능성&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로의 AI 경쟁은 다음과 같이 바뀔 수 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;더 큰 모델
&amp;rarr; 더 긴 작업을 수행하는 모델

더 많은 지식
&amp;rarr; 지식을 찾고 검증하는 능력

더 좋은 답변
&amp;rarr; 실제 도구를 사용해 일을 끝내는 능력
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 Agents-A1 하나로 초거대 모델의 시대가 끝난 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 코딩, 범용 추론, 창작, 세계 지식, 안정성 등에서는 대형 프런티어 모델이 여전히 강하다. Agents-A1의 공식 결과에서도 영역별 성능 차이가 분명하게 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 Agents-A1은 중요한 질문을 남긴다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI의 능력을 키우기 위해 모델 자체를 계속 크게 만들어야 할까?&lt;br /&gt;아니면 작은 모델이 더 오래 생각하고, 더 많은 도구를 사용하고, 더 긴 작업을 경험하도록 만드는 편이 효과적일까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agents-A1은 후자의 가능성을 보여주는 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 몸집으로 모든 문제를 해결하는 모델은 아니다. 하지만 검색하고, 실행하고, 관찰하고, 수정하며 끝까지 작업을 이어가는 능력에서는 기존 중형 오픈 모델보다 한 단계 진화한 방향을 제시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 단순히 답을 생성하는 도구에서 실제 업무를 수행하는 에이전트로 이동하는 시점에, Agents-A1이 주목받는 이유도 바로 여기에 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;한 줄 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Agents-A1은 파라미터 수를 무작정 늘리는 대신, AI가 계획하고 도구를 사용하며 작업을 이어가는 &amp;lsquo;지평선&amp;rsquo;을 확장한 35B 오픈 에이전트 모델이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 분야에서 초거대 모델을 이기는 것은 아니지만, 장기 검색&amp;middot;과학 연구&amp;middot;복합 지시 이행에서는 모델 크기 대비 매우 강한 성능을 보여준다. 앞으로 로컬 AI와 기업용 에이전트가 어떤 방향으로 발전할지를 보여주는 흥미로운 사례다.&lt;/p&gt;</description>
      <category>AI/추천 오픈소스</category>
      <author>행복한 수지아빠</author>
      <guid isPermaLink="true">https://javaexpert.tistory.com/1791</guid>
      <comments>https://javaexpert.tistory.com/1791#entry1791comment</comments>
      <pubDate>Tue, 21 Jul 2026 13:48:33 +0900</pubDate>
    </item>
  </channel>
</rss>