본문으로 건너뛰기
AICosmus

Where tech meets the everyday — AI, fintech, swimming, and cars.

AICosmus

Where tech meets the everyday — AI, fintech, swimming, and cars.

  • 홈
  • IT기술
    • RAG
    • GRPC
    • Kotlin
    • LLM
    • 금융 IT
    • 에이전트
    • 제로Trust
    • 자동화
  • 일상
    • 자동차
    • 경제/재테크
    • 생활정보
  • About
    • Contact
    • Terms of Service
    • Disclaimer
    • Privacy – Policy
  • 홈
  • IT기술
    • RAG
    • GRPC
    • Kotlin
    • LLM
    • 금융 IT
    • 에이전트
    • 제로Trust
    • 자동화
  • 일상
    • 자동차
    • 경제/재테크
    • 생활정보
  • About
    • Contact
    • Terms of Service
    • Disclaimer
    • Privacy – Policy
opencode 커스텀 에이전트 JSON과 Markdown 정의 방식 비교
IT기술

[opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기] 2/12화: opencode 커스텀 에이전트 만들기 2가지 방법 — JSON vs Markdown 완전 비교 (2026)

By AICosmus
2026년 07월 30일 8 Min Read
0

이 글은 「opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기」 2일차입니다.

시즌 1 3일차에서 opencode.json 설정 파일을 처음 열어봤던 기억이 나시나요? 오늘은 그 파일 안에서 opencode 커스텀 에이전트를 직접 정의하고, 두 번째 방법인 Markdown 파일 기반 정의까지 나란히 비교해봅니다.

어제 1일차에서는 Primary(Build·Plan)와 Subagent(General·Explore·Scout) 5계층 구조를 해부했습니다. 오늘은 그 구조 위에 내가 만든 에이전트를 올려보는 첫 실습입니다.

오늘의 핵심 3가지

  • 두 갈래 정의 방식 — opencode.json의 agent 키 vs .opencode/agents/*.md YAML frontmatter
  • 스코프와 폴백 순서 — 프로젝트 로컬 → 글로벌, 어디에 두느냐가 에이전트의 생존 범위를 결정
  • default_agent 설정 — 대화 시작 시 어떤 에이전트가 먼저 응답할지 지정하는 법
opencode.json vs Markdown 에이전트 정의 방식 비교 - opencode 커스텀 에이전트

왜 두 가지 방식이 존재하는가

opencode는 에이전트를 정의하는 경로를 의도적으로 두 개 열어뒀습니다. 하나는 구조화된 JSON, 다른 하나는 자유로운 Markdown입니다. 이건 취향의 문제가 아니라, 각 방식이 해결하는 문제가 다릅니다.

opencode.json의 agent 키는 에이전트의 메타데이터(모델·권한·도구·설명)를 선언적으로 관리하기에 최적입니다. 반면 .opencode/agents/*.md는 시스템 프롬프트 본문을 길고 풍부하게 작성할 때 빛납니다. 실전에서는 두 방식을 섞어 씁니다 — JSON으로 뼈대를 잡고, Markdown으로 프롬프트를 외부화하는 식이죠.

방식 1 — opencode.json의 agent 키

기본 구조

opencode.json(또는 .opencode.json)은 프로젝트 루트에 놓이는 설정 파일입니다. 여기에 "agent" 키를 추가하면 opencode 커스텀 에이전트가 등록됩니다.

{
  "$schema": "https://opencode.ai/schema.json",
  "agent": {
    "CodeReviewer": {
      "model": "anthropic/claude-sonnet-4-1",
      "description": "코드 리뷰 전문가. PR diff를 분석하고 개선점을 제안한다.",
      "instructions": "당신은 시니어 코드 리뷰어입니다. 다음 원칙을 따르세요:\n1. 버그 가능성이 있는 패턴을 우선 지적\n2. 성능 영향이 큰 변경을 두 번째로 검토\n3. 스타일/컨벤션은 마지막에 언급",
      "tools": ["Read", "Glob", "Grep", "Agent"],
      "permission": {
        "allow": ["Read", "Glob", "Grep"],
        "deny": ["Write", "Edit", "Bash"]
      }
    }
  }
}

이 한 블록이면 CodeReviewer라는 Subagent가 생깁니다. Primary 에이전트가 코드 리뷰 관련 요청을 받으면 description을 보고 자동으로 이 에이전트에게 위임하거나, 사용자가 @CodeReviewer로 직접 호출할 수 있습니다.

주요 필드 해설

  • model — provider/model-id 형식. 생략하면 프로젝트 기본 모델을 상속합니다.
  • description — Primary가 위임 판단에 사용하는 핵심 텍스트. 짧고 명확하게 써야 자동 위임 정확도가 올라갑니다.
  • instructions — 시스템 프롬프트. JSON 안에 넣으므로 줄바꿈은 \n으로 이스케이프해야 합니다.
  • tools — 이 에이전트가 사용할 수 있는 도구 목록.
  • permission — allow/ask/deny 세 등급으로 도구 접근을 제어합니다.

JSON 방식의 장점과 한계

장점: 한 파일에서 여러 에이전트를 한눈에 관리. 버전 관리(git diff)가 깔끔. IDE 자동완성과 $schema 검증 지원.

한계: 시스템 프롬프트가 길어지면 JSON 이스케이프 지옥. 50줄짜리 프롬프트를 \n으로 연결한 한 줄 문자열로 관리하는 건 고통입니다.

방식 2 — .opencode/agents/*.md (Markdown 정의)

기본 구조

프로젝트 루트에 .opencode/agents/ 디렉토리를 만들고, 에이전트 하나당 Markdown 파일 하나를 둡니다. 파일 이름(확장자 제외)이 에이전트 이름이 됩니다.

# .opencode/agents/CodeReviewer.md

---
model: anthropic/claude-sonnet-4-1
description: "코드 리뷰 전문가. PR diff를 분석하고 개선점을 제안한다."
tools:
  - Read
  - Glob
  - Grep
  - Agent
permission:
  allow:
    - Read
    - Glob
    - Grep
  deny:
    - Write
    - Edit
    - Bash
---

당신은 시니어 코드 리뷰어입니다. 다음 원칙을 따르세요:

## 리뷰 우선순위

1. **버그 가능성** — null 참조, 경계 조건, 동시성 이슈를 가장 먼저 지적합니다.
2. **성능 영향** — O(n²) 루프, 불필요한 I/O, 메모리 누수 가능성을 검토합니다.
3. **스타일/컨벤션** — 네이밍, 포매팅, 프로젝트 컨벤션 위반은 마지막에 언급합니다.

## 피드백 형식

- 각 지적은 **파일:라인** 형태로 위치를 명시합니다.
- 심각도를 🔴 Critical / 🟡 Warning / 🔵 Info 로 구분합니다.
- 수정 제안은 코드 블록으로 제시합니다.

YAML frontmatter(두 개의 --- 사이)가 메타데이터, 그 아래 본문 전체가 시스템 프롬프트입니다. JSON에서는 \n 지옥이던 프롬프트가 여기서는 자연스러운 Markdown으로 펼쳐집니다.

opencode 에이전트 파일 배치 디렉토리 구조

Markdown 방식의 장점과 한계

장점: 프롬프트를 마크다운 문법 그대로 작성. 헤딩·목록·코드블록을 자유롭게 사용. 에이전트별 파일 분리로 협업 시 충돌 최소화.

한계: 에이전트가 10개 넘어가면 디렉토리 관리 부담. 전체 에이전트 목록을 한 눈에 보려면 ls .opencode/agents/를 쳐야 합니다.

opencode 커스텀 에이전트 — 두 방식 완전 비교표

같은 에이전트를 두 방식으로 정의할 때의 차이를 정리합니다.

항목 opencode.json agent 키 .opencode/agents/*.md
에이전트 이름 JSON 키 이름 파일명 (확장자 제외)
메타데이터 JSON 필드 YAML frontmatter
시스템 프롬프트 instructions 문자열 (이스케이프 필요) frontmatter 아래 Markdown 본문
프롬프트 외부화 {file:./prompts/review.md} 패턴 본문 자체가 프롬프트 (추가 외부화 가능)
여러 에이전트 관리 한 파일에 N개 객체 N개 파일
IDE 지원 JSON Schema 자동완성 Markdown 프리뷰
git 충돌 가능성 높음 (한 파일 수정) 낮음 (파일 분리)
적합한 상황 에이전트 3개 이하, 프롬프트 짧을 때 에이전트 많거나 프롬프트 길 때
우선순위 로딩 시 먼저 읽힘 같은 이름일 경우 md가 오버라이드

핵심 포인트: 같은 이름의 에이전트가 양쪽에 정의되어 있으면 .opencode/agents/*.md가 우선합니다. 이건 의도된 설계로, JSON에서 기본 뼈대를 잡고 Markdown에서 프롬프트를 오버라이드하는 계층 패턴을 가능하게 합니다.

스코프 — 글로벌 vs 프로젝트 로컬

두 개의 설정 위치

opencode 커스텀 에이전트는 두 곳에 놓을 수 있습니다.

  • 글로벌: ~/.config/opencode/ (Linux/macOS) 또는 %APPDATA%/opencode/ (Windows)
    • ~/.config/opencode/opencode.json의 agent 키
    • ~/.config/opencode/agents/*.md
  • 프로젝트 로컬: 프로젝트 루트
    • ./opencode.json (또는 ./.opencode.json)의 agent 키
    • ./.opencode/agents/*.md

폴백 순서 (해상도 체인)

opencode가 에이전트를 찾는 순서는 다음과 같습니다. 가장 구체적인(로컬) 정의가 이깁니다.

1. .opencode/agents/에이전트명.md   (프로젝트 로컬 Markdown)  ← 최우선
2. opencode.json → agent.에이전트명  (프로젝트 로컬 JSON)
3. ~/.config/opencode/agents/에이전트명.md  (글로벌 Markdown)
4. ~/.config/opencode/opencode.json → agent.에이전트명  (글로벌 JSON)
5. opencode 내장 에이전트 (General, Explore, Scout 등)       ← 최후 폴백

이 순서가 왜 중요할까요? 예를 들어 글로벌에 범용 CodeReviewer를 두고, 특정 프로젝트에서는 해당 프로젝트의 코딩 컨벤션을 반영한 CodeReviewer.md를 오버라이드할 수 있습니다. 글로벌은 기본값, 로컬은 특수화 — 이 패턴이 12일차 도메인 특화 시스템의 기초가 됩니다.

opencode 에이전트 스코프 폴백 순서 다이어그램

실전 권장 — 어디에 무엇을

  • 글로벌에 두기 좋은 것: 프로젝트에 무관한 범용 유틸리티 에이전트 (예: Translator, DocWriter, GitHelper)
  • 프로젝트 로컬에 두기 좋은 것: 해당 코드베이스의 아키텍처·컨벤션·기술 스택에 특화된 에이전트 (예: SpringReviewer, ReactOptimizer)
  • 양쪽에 두기 좋은 것: 글로벌에서 골격을 정의하고, 로컬에서 프롬프트만 오버라이드 (예: 글로벌 SecurityAuditor + 로컬에서 OWASP Top 10 중 이 프로젝트에서 특히 주의할 3가지 추가)

default_agent — 대화의 첫 타석 결정

opencode를 실행하면 기본적으로 Build Primary 에이전트가 대화를 받습니다. 이걸 바꾸고 싶다면 default_agent를 설정합니다.

{
  "$schema": "https://opencode.ai/schema.json",
  "default_agent": "CodeReviewer",
  "agent": {
    "CodeReviewer": {
      "model": "anthropic/claude-sonnet-4-1",
      "description": "코드 리뷰 전문가",
      "instructions": "..."
    }
  }
}

default_agent에 커스텀 에이전트 이름을 넣으면, opencode 시작 시 해당 에이전트가 Primary 역할로 대화를 시작합니다. 주의할 점은 이 설정이 Primary를 교체하는 것이 아니라, 기본 활성 에이전트를 지정하는 것이라는 점입니다. 내장 Primary(Build·Plan)는 여전히 존재하고, 사용자가 @Build로 전환할 수 있습니다.

이 기능은 특정 프로젝트에서 항상 특정 역할로 시작하고 싶을 때 유용합니다. 예를 들어 코드 리뷰만 전담하는 저장소라면 매번 @CodeReviewer를 부르는 대신 default_agent로 고정하면 됩니다.

실습 — 첫 커스텀 에이전트를 양쪽 방식으로 만들기

실제로 손에 잡히는 에이전트를 만들어봅시다. 시나리오: API 스펙 검증 에이전트 — REST API 코드를 읽고 OpenAPI 3.1 스펙과의 불일치를 찾아주는 에이전트입니다.

Step 1 — JSON으로 빠르게 프로토타이핑

프로젝트 루트의 opencode.json에 추가합니다.

{
  "$schema": "https://opencode.ai/schema.json",
  "agent": {
    "APISpecChecker": {
      "model": "anthropic/claude-sonnet-4-1",
      "description": "REST API 구현 코드와 OpenAPI 스펙 파일을 비교하여 불일치를 찾는 검증 에이전트",
      "instructions": "당신은 API 스펙 검증 전문가입니다. 사용자가 요청하면 코드의 라우트 핸들러와 OpenAPI(Swagger) YAML/JSON 스펙을 비교하여 불일치를 보고합니다. 경로·메서드·파라미터·응답 스키마·상태 코드를 체크합니다.",
      "tools": ["Read", "Glob", "Grep", "Agent"],
      "permission": {
        "allow": ["Read", "Glob", "Grep"],
        "deny": ["Write", "Edit", "Bash"]
      }
    }
  }
}

이 상태로 opencode를 실행하면 이미 동작합니다. @APISpecChecker를 호출하거나, “API 스펙이랑 코드가 맞는지 확인해줘”라고 말하면 Primary가 description을 보고 자동 위임합니다.

Step 2 — Markdown으로 프롬프트 확장

프롬프트를 더 정교하게 만들고 싶어졌습니다. JSON의 instructions를 지우고, .opencode/agents/APISpecChecker.md를 만듭니다.

# .opencode/agents/APISpecChecker.md

---
model: anthropic/claude-sonnet-4-1
description: "REST API 구현 코드와 OpenAPI 스펙 파일을 비교하여 불일치를 찾는 검증 에이전트"
tools:
  - Read
  - Glob
  - Grep
  - Agent
permission:
  allow:
    - Read
    - Glob
    - Grep
  deny:
    - Write
    - Edit
    - Bash
---

당신은 API 스펙 검증 전문가입니다.

## 검증 대상

사용자가 요청하면 다음 두 소스를 비교합니다:

1. **구현 코드** — 라우트 핸들러, 컨트롤러, 미들웨어
2. **OpenAPI 스펙** — `openapi.yaml`, `openapi.json`, 또는 `swagger.*` 파일

## 검증 체크리스트

| 항목 | 검증 내용 |
|------|----------|
| 경로 | 스펙에 정의된 모든 경로가 코드에 구현되어 있는가? 반대는? |
| HTTP 메서드 | GET/POST/PUT/DELETE 등 메서드 일치 여부 |
| 경로 파라미터 | `{id}` 같은 path parameter 이름·타입 일치 |
| 쿼리 파라미터 | required/optional 구분, 기본값 일치 |
| 요청 바디 | Content-Type, 스키마 필드, 필수/선택 구분 |
| 응답 스키마 | 상태 코드별 응답 바디 스키마 일치 |
| 인증 | securitySchemes 와 실제 미들웨어 일치 |

## 보고 형식

```
## API 스펙 검증 결과

### 🔴 불일치 (Critical)
- **POST /users** — 스펙: 201 Created / 코드: 200 OK 반환
- **GET /orders/{id}** — 스펙에 존재하나 코드에 라우트 없음

### 🟡 주의 (Warning)  
- **GET /products** — 쿼리 파라미터 `category` 스펙은 required, 코드는 optional

### ✅ 일치 확인
- GET /healthz, POST /auth/login, ...
```

## 제약

- 파일을 읽기만 하고 수정하지 않습니다.
- 스펙 파일이 없으면 사용자에게 위치를 물어봅니다.
- 스펙 버전(OpenAPI 2.0/3.0/3.1)을 먼저 확인하고 버전별 차이를 고려합니다.

JSON 한 줄이었던 프롬프트가 구조화된 체크리스트와 보고 형식 템플릿으로 확장됐습니다. 이 상태에서 opencode를 재시작하면, md 파일이 JSON 정의를 오버라이드하여 동일한 APISpecChecker가 더 정교한 프롬프트로 동작합니다.

Step 3 — 하이브리드 패턴 (권장)

실전에서 가장 깔끔한 패턴은 JSON에서는 agent 키를 제거하고, Markdown 파일만 사용하는 것입니다. 메타데이터와 프롬프트가 한 파일에 있으니 관리 포인트가 줄어듭니다.

반대로, 프롬프트가 짧은 유틸리티 에이전트(예: “이 코드의 라인 수를 세줘” 수준)는 JSON에만 한 줄로 두는 것이 오히려 효율적입니다.

정리하면:

  • 프롬프트 5줄 이하 → JSON만 사용
  • 프롬프트 5줄 초과 → Markdown 파일 사용
  • 둘 다 존재 → Markdown이 오버라이드 (의도적 계층화가 아니라면 혼란 유발)

프롬프트 외부화 — {file:} 패턴 미리보기

JSON 방식에서도 프롬프트를 길게 쓸 수 있는 우회 방법이 있습니다. {file:} 패턴입니다.

{
  "agent": {
    "APISpecChecker": {
      "model": "anthropic/claude-sonnet-4-1",
      "description": "REST API 스펙 검증 에이전트",
      "instructions": "{file:./prompts/api-spec-checker.md}",
      "tools": ["Read", "Glob", "Grep"]
    }
  }
}

./prompts/api-spec-checker.md에 프롬프트 본문을 두면, opencode가 로딩 시 파일 내용으로 치환합니다. 이 패턴은 3일차(시스템 프롬프트 심화)에서 본격적으로 다루겠지만, 오늘 알아둘 것은 JSON과 Markdown 방식 모두에서 프롬프트 외부화가 가능하다는 점입니다.

실전 디렉토리 구조 예시

오늘 만든 것을 포함해, 프로젝트에 에이전트를 3개 세팅한 디렉토리 구조입니다.

my-project/
├── opencode.json                  # default_agent + 간단한 에이전트(JSON)
├── .opencode/
│   └── agents/
│       ├── APISpecChecker.md      # 스펙 검증 (프롬프트 긴 버전)
│       ├── TestWriter.md          # 테스트 작성 에이전트
│       └── DocGenerator.md        # 문서 생성 에이전트
├── prompts/                       # (선택) {file:} 외부화용
│   └── shared-conventions.md
└── src/
    └── ...

opencode.json에는 default_agent와 간단한 유틸리티 에이전트만 두고, 프롬프트가 풍부한 에이전트는 .opencode/agents/에 Markdown으로 분리했습니다. 이 구조가 팀 협업에서도 git 충돌을 최소화하는 실전 패턴입니다.

동작 확인 — 에이전트가 잘 등록됐는지 체크

opencode를 실행한 뒤 다음과 같이 확인할 수 있습니다.

# opencode 실행 후 에이전트 목록 확인
# 대화창에서 @를 입력하면 등록된 에이전트 자동완성 목록이 표시됩니다.

# 또는 직접 호출 테스트:
@APISpecChecker 이 프로젝트의 API 스펙과 구현 코드를 비교해줘

등록이 정상이면 @ 입력 시 APISpecChecker가 자동완성 후보에 나타나고, 호출 시 Markdown에 정의한 시스템 프롬프트가 적용된 응답이 돌아옵니다. 만약 나타나지 않는다면 아래 Gotcha 코너를 확인하세요.

Gotcha 미니 코너 — 파일명 대소문자 함정

⚠️ .opencode/agents/의 파일명이 곧 에이전트 이름입니다. apispecchecker.md와 APISpecChecker.md는 다른 에이전트로 등록됩니다.

Windows에서는 파일 시스템이 대소문자를 구분하지 않아서 두 파일이 공존할 수 없지만, macOS/Linux에서는 공존 가능합니다. 팀원 중 OS가 다르면 “내 환경에선 되는데 네 환경에선 안 돼” 문제가 발생합니다.

해결책: 에이전트 파일명은 PascalCase로 통일하고, 팀 위키에 네이밍 규칙을 명시하세요. opencode.json의 agent 키 이름과 md 파일명을 정확히 일치시키는 것도 잊지 마세요.

# ✅ 좋은 예
.opencode/agents/APISpecChecker.md    → 에이전트 이름: APISpecChecker
.opencode/agents/TestWriter.md        → 에이전트 이름: TestWriter

# ❌ 나쁜 예
.opencode/agents/api-spec-checker.md  → 에이전트 이름: api-spec-checker (호출 시 어색)
.opencode/agents/test_writer.md       → 에이전트 이름: test_writer (컨벤션 불일치)

오늘 만든 것 정리

  • opencode.json의 agent 키로 에이전트를 JSON 선언 — 빠른 프로토타이핑
  • .opencode/agents/APISpecChecker.md로 동일 에이전트를 Markdown 재정의 — 풍부한 프롬프트
  • default_agent로 대화 시작 에이전트 지정
  • 글로벌 vs 로컬 폴백 순서 이해

아직 프롬프트 내용 자체는 “일단 동작하는” 수준입니다. 내일 3일차에서는 좋은 프롬프트와 나쁜 프롬프트의 실제 차이를 코드 리뷰 에이전트로 대조 실험하며, {file:} 외부화와 모델별 프롬프트 전략(Claude vs GPT)까지 다룹니다. 오늘 만든 에이전트의 프롬프트를 한 단계 끌어올리는 시간이 될 겁니다.

opencode 공식 문서의 에이전트 설정 가이드도 함께 참고하시면 오늘 내용이 더 선명해집니다.


📚 시리즈: opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기 (총 12화 중 2화)
◀ 이전 1화  (다음 차수는 아직 게시되지 않았습니다)

Tags:

AI 에이전트opencode 설정opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기-2화opencode 커스텀 에이전트opencode.json연재:opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기코딩 에이전트
작성자

AICosmus

Follow Me
다른 기사
Lazygit 터미널 Git TUI 도구 화면
Previous

Lazygit 사용법 총정리 — 8가지 핵심 기능과 실전 가이드

댓글 없음! 첫 댓글을 남겨보세요.

답글 남기기 응답 취소

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

최신 글

  • [opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기] 2/12화: opencode 커스텀 에이전트 만들기 2가지 방법 — JSON vs Markdown 완전 비교 (2026)
  • Lazygit 사용법 총정리 — 8가지 핵심 기능과 실전 가이드
  • 제로트러스트 랜섬웨어 방어 5단계 실전 가이드
  • gRPC 로드밸런싱 3가지 전략과 실전 구현 가이드
  • [opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기] 1/12화: opencode 에이전트 아키텍처 완전 해부 — 2026 Primary·Subagent 5계층 구조

최신 댓글

  1. [온프레미스 AI Assistant 아키텍처 — Qwen3·Qwen3-VL 14일 설계] 9/14화: 온프레미스 RAG 파이프라인 — bge-m3·Qdrant 자체 호스팅 실전의 Dockerfile 최적화 실전 가이드 — 빌드·크기·보안 총정리 - AICosmus
  2. RAG 평가 프레임워크, 답변 품질을 수치로 측정하는 법의 RAG 리랭킹 가이드, 검색 결과 정확도 높이는 법 - AICosmus
  3. RAG 평가 프레임워크, 답변 품질을 수치로 측정하는 법의 RAG 리랭킹 가이드, 검색 결과 정확도 높이는 법 - AICosmus
  4. gRPC Interceptor 완벽 가이드: 인증부터 로깅까지의 gRPC 데드라인과 재시도 정책으로 장애 전파 차단하기 - AICosmus
  5. gRPC Interceptor 완벽 가이드: 인증부터 로깅까지의 gRPC 데드라인과 재시도 정책으로 장애 전파 차단하기 - AICosmus
  • About
  • Contact
  • Disclaimer
  • Privacy - Policy
  • Terms of Service
Copyright 2026 — AICosmus. All rights reserved. Blogsy WordPress Theme