콘텐츠 빌더 에이전트 만들기
콘텐츠 빌더 에이전트 만들기 (Build a content builder agent)
브랜드 메모리, 스킬, 서브에이전트, 이미지 생성을 갖춘 콘텐츠 작성 에이전트를 만들어 보세요.
개요 (Overview)
이 가이드는 Deep Agents를 사용해 콘텐츠 작성 에이전트를 처음부터 만드는 방법을 보여줍니다.
만들 에이전트는 다음과 같이 동작합니다:
AGENTS.md와 스킬 폴더에서 브랜드 목소리와 작업 흐름 규칙을 로드합니다.web_search로 특화된 서브에이전트에 웹 리서치를 위임합니다.- 로드된 스킬을 따라 블로그 또는 소셜 콘텐츠를 초안으로 작성합니다.
- Gemini로 커버 또는 소셜 이미지를 생성하고 프로젝트 디렉터리 아래에 파일을 저장합니다.
이 튜토리얼의 코드는 이미지 생성 도구와 파일 시스템 백엔드를 연결해 에이전트가 프로젝트 디렉터리 아래에서 게시물, 리서치 노트, 이미지를 읽고 쓸 수 있게 합니다. 전체 실행 가능한 프로젝트는 content-builder-agent 예시를 참조하세요.
핵심 개념 (Key concepts)
이 튜토리얼이 다루는 내용:
- 장기 메모리 for TODO
- 스킬 for TODO
- 서브에이전트 for TODO
- 파일 시스템 백엔드 for 파일 읽기와 쓰기
- 검색과 이미지 생성을 위한 커스텀 도구
사전 요구 사항 (Prerequisites)
API 키:
- Anthropic (Claude) 또는 다른 프로바이더 API 키
gemini-2.5-flash-image로 이미지 생성을 위한 Google (Gemini) 키- 웹 검색용 Tavily (무료 티어)
- 추적용 LangSmith (선택 사항)
Node.js 18 이상.
설정 (Setup)
```bash yarn wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
yarn add deepagents @langchain/core @langchain/anthropic @google/generative-ai tavily zod tsx
```
</CodeGroup>
`content_writer.ts`를 실행하려면 `tsx`를 추가하세요. `--input-type=module` 플래그는 `--eval`, `--print`, 또는 stdin에만 적용되며 스크립트 파일 경로에는 적용되지 않습니다.
`createDeepAgent`가 사용하는 기본 Claude 모델을 LangChain이 로드할 수 있도록 `@langchain/anthropic`을 설치하세요.
구성 파일 추가 (Add configuration files)
이 예시는 동작을 메모리, 스킬, 서브에이전트 정의의 세 가지 종류의 파일에 담습니다.
```markdown expandable wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# Content Writer Agent
You are a content writer for a technology company. Your job is to create engaging, informative content that educates readers about AI, software development, and emerging technologies.
## Brand Voice
- **Professional but approachable**: Write like a knowledgeable colleague, not a textbook
- **Clear and direct**: Avoid jargon unless necessary; explain technical concepts simply
- **Confident but not arrogant**: Share expertise without being condescending
- **Engaging**: Use concrete examples, analogies, and stories to illustrate points
## Writing Standards
1. **Use active voice**: "The agent processes requests" not "Requests are processed by the agent"
2. **Lead with value**: Start with what matters to the reader, not background
3. **One idea per paragraph**: Keep paragraphs focused and scannable
4. **Concrete over abstract**: Use specific examples, numbers, and case studies
5. **End with action**: Every piece should leave the reader knowing what to do next
## Content Pillars
Our content focuses on:
- AI agents and automation
- Developer tools and productivity
- Software architecture and best practices
- Emerging technologies and trends
## Formatting Guidelines
- Use headers (H2, H3) to break up long content
- Include code examples where relevant (with syntax highlighting)
- Add bullet points for lists of 3+ items
- Keep sentences under 25 words when possible
- Include a clear call-to-action at the end
## Research Requirements
Before writing on any topic:
1. Use the `researcher` subagent for in-depth topic research
2. Gather at least 3 credible sources
3. Identify the key points readers need to understand
4. Find concrete examples or case studies to illustrate concepts
```
이 에이전트가 여러분의 톤, 핵심 주제, 포맷 규칙을 따르게 하려면 `AGENTS.md`의 텍스트를 수정하세요.
`skills/blog-post/SKILL.md`를 만들고 장문 게시물 작성, SEO 콘텐츠 최적화, 커버 이미지 생성에 대한 정보가 담긴 다음 텍스트를 복사하세요.
````md expandable wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
---
name: blog-post
description: Writes and structures long-form blog posts, creates tutorial outlines, and optimizes content for SEO with cover image generation. Use when the user asks to write a blog post, article, how-to guide, tutorial, technical writeup, thought leadership piece, or long-form content.
---
# Blog Post Writing Skill
## Research First (Required)
**Before writing any blog post, you MUST delegate research:**
1. Use the `task` tool with `subagent_type: "researcher"`
2. In the description, specify BOTH the topic AND where to save:
```
task(
subagent_type="researcher",
description="Research [TOPIC]. Save findings to research/[slug].md"
)
```
Example:
```
task(
subagent_type="researcher",
description="Research the current state of AI agents in 2025. Save findings to research/ai-agents-2025.md"
)
```
3. After research completes, read the findings file before writing
## Output Structure (Required)
**Every blog post MUST have both a post AND a cover image:**
```
blogs/
└── <slug>/
├── post.md # The blog post content
└── hero.png # REQUIRED: Generated cover image
```
Example: A post about "AI Agents in 2025" → `blogs/ai-agents-2025/`
**You MUST complete both steps:**
1. Write the post to `blogs/<slug>/post.md`
2. Generate a cover image using `generate_image` and save to `blogs/<slug>/hero.png`
**A blog post is NOT complete without its cover image.**
## Blog Post Structure
Every blog post should follow this structure:
### 1. Hook (Opening)
- Start with a compelling question, statistic, or statement
- Make the reader want to continue
- Keep it to 2-3 sentences
### 2. Context (The Problem)
- Explain why this topic matters
- Describe the problem or opportunity
- Connect to the reader's experience
### 3. Main Content (The Solution)
- Break into 3-5 main sections with H2 headers
- Each section covers one key point
- Include code examples, diagrams, or screenshots where helpful
- Use bullet points for lists
### 4. Practical Application
- Show how to apply the concepts
- Include step-by-step instructions if applicable
- Provide code snippets or templates
### 5. Conclusion & CTA
- Summarize key takeaways (3 bullets max)
- End with a clear call-to-action
- Link to related resources
## Cover Image Generation
After writing the post, generate a cover image using the `generate_cover` tool:
```
generate_cover(prompt="A detailed description of the image...", slug="your-blog-slug")
```
The tool saves the image to `blogs/<slug>/hero.png`.
### Writing Effective Image Prompts
Structure your prompt with these elements:
1. **Subject**: What is the main focus? Be specific and concrete.
2. **Style**: Art direction (minimalist, isometric, flat design, 3D render, watercolor, etc.)
3. **Composition**: How elements are arranged (centered, rule of thirds, symmetrical)
4. **Color palette**: Specific colors or mood (warm earth tones, cool blues and purples, high contrast)
5. **Lighting/Atmosphere**: Soft diffused light, dramatic shadows, golden hour, neon glow
6. **Technical details**: Aspect ratio considerations, negative space for text overlay
### Example Prompts
**For a technical blog post:**
```
Isometric 3D illustration of interconnected glowing cubes representing AI agents, each cube has subtle circuit patterns. Cubes connected by luminous data streams. Deep navy background (#0a192f) with electric blue (#64ffda) and soft purple (#c792ea) accents. Clean minimal style, lots of negative space at top for title. Professional tech aesthetic.
```
**For a tutorial/how-to:**
```
Clean flat illustration of hands typing on a keyboard with abstract code symbols floating upward, transforming into lightbulbs and gears. Warm gradient background from soft coral to light peach. Friendly, approachable style. Centered composition with space for text overlay.
```
**For thought leadership:**
```
Abstract visualization of a human silhouette profile merging with geometric neural network patterns. Split composition - organic watercolor texture on left transitioning to clean vector lines on right. Muted sage green and warm terracotta color scheme. Contemplative, forward-thinking mood.
```
## SEO Considerations
- Include the main keyword in the title and first paragraph
- Use the keyword naturally 3-5 times throughout
- Keep the title under 60 characters
- Write a meta description (150-160 characters)
## Quality Checklist
Before finishing:
- [ ] Post saved to `blogs/<slug>/post.md`
- [ ] Hero image generated at `blogs/<slug>/hero.png`
- [ ] Hook grabs attention in first 2 sentences
- [ ] Each section has a clear purpose
- [ ] Conclusion summarizes key points
- [ ] CTA tells reader what to do next
````
다음으로 `skills/social-media/SKILL.md`를 만들고 소셜 미디어 게시물 초안 작성과 동반 이미지 생성에 대한 정보가 담긴 다음 텍스트를 복사하세요:
````md expandable wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
---
name: social-media
description: Drafts engaging social media posts, writes hooks, suggests hashtags, creates thread structures, and generates companion images. Use when the user asks to write a LinkedIn post, tweet, Twitter/X thread, social media caption, social post, or repurpose content for social platforms.
---
# Social Media Content Skill
## Research First (Required)
**Before writing any social media content, you MUST delegate research:**
1. Use the `task` tool with `subagent_type: "researcher"`
2. In the description, specify BOTH the topic AND where to save:
```
task(
subagent_type="researcher",
description="Research [TOPIC]. Save findings to research/[slug].md"
)
```
Example:
```
task(
subagent_type="researcher",
description="Research renewable energy trends in 2025. Save findings to research/renewable-energy.md"
)
```
3. After research completes, read the findings file before writing
## Output Structure (Required)
**Every social media post MUST have both content AND an image:**
**LinkedIn posts:**
```
linkedin/
└── <slug>/
├── post.md # The post content
└── image.png # REQUIRED: Generated visual
```
**Twitter/X threads:**
```
tweets/
└── <slug>/
├── thread.md # The thread content
└── image.png # REQUIRED: Generated visual
```
Example: A LinkedIn post about "prompt engineering" → `linkedin/prompt-engineering/`
**You MUST complete both steps:**
1. Write the content to the appropriate path
2. Generate an image using `generate_image` and save alongside the post
**A social media post is NOT complete without its image.**
## Platform Guidelines
### LinkedIn
**Format:**
- 1,300 character limit (show more after ~210 chars)
- First line is crucial - make it hook
- Use line breaks for readability
- 3-5 hashtags at the end
**Tone:**
- Professional but personal
- Share insights and learnings
- Ask questions to drive engagement
- Use "I" and share experiences
**Structure:**
```
[Hook - 1 compelling line]
[Empty line]
[Context - why this matters]
[Empty line]
[Main insight - 2-3 short paragraphs]
[Empty line]
[Call to action or question]
#hashtag1 #hashtag2 #hashtag3
```
### Twitter/X
**Format:**
- 280 character limit per tweet
- Threads for longer content (use 1/🧵 format)
- No more than 2 hashtags per tweet
**Thread Structure:**
```
1/🧵 [Hook - the main insight]
2/ [Supporting point 1]
3/ [Supporting point 2]
4/ [Example or evidence]
5/ [Conclusion + CTA]
```
## Image Generation
Every social media post needs an eye-catching image. Use the `generate_social_image` tool:
```
generate_social_image(prompt="A detailed description...", platform="linkedin", slug="your-post-slug")
```
The tool saves the image to `<platform>/<slug>/image.png`.
### Social Image Best Practices
Social images need to work at small sizes in crowded feeds:
- **Bold, simple compositions** - one clear focal point
- **High contrast** - stands out when scrolling
- **No text in image** - too small to read, platforms add their own
- **Square or 4:5 ratio** - works across platforms
### Writing Effective Prompts
Include these elements:
1. **Single focal point**: One clear subject, not a busy scene
2. **Bold style**: Vibrant colors, strong shapes, high contrast
3. **Simple background**: Solid color, gradient, or subtle texture
4. **Mood/energy**: Match the post tone (inspiring, urgent, thoughtful)
### Example Prompts
**For an insight/tip post:**
```
Single glowing lightbulb floating against a deep purple gradient background, lightbulb made of interconnected golden geometric lines, rays of soft light emanating outward. Minimal, striking, high contrast. Square composition.
```
**For announcements/news:**
```
Abstract rocket ship made of colorful geometric shapes launching upward with a trail of particles. Bright coral and teal color scheme against clean white background. Energetic, celebratory mood. Bold flat illustration style.
```
**For thought-provoking content:**
```
Two overlapping translucent circles, one blue one orange, creating a glowing intersection in the center. Represents collaboration or intersection of ideas. Dark charcoal background, soft ethereal glow. Minimalist and contemplative.
```
## Content Types
### Announcement Posts
- Lead with the news
- Explain the impact
- Include link or next step
### Insight Posts
- Share one specific learning
- Explain the context briefly
- Make it actionable
### Question Posts
- Ask a genuine question
- Provide your take first
- Keep it focused on one topic
## Quality Checklist
Before finishing:
- [ ] Post saved to `linkedin/<slug>/post.md` or `tweets/<slug>/thread.md`
- [ ] Image generated alongside the post
- [ ] First line hooks attention
- [ ] Content fits platform limits
- [ ] Tone matches platform norms
- [ ] Has clear CTA or question
- [ ] Hashtags are relevant (not generic)
````
이 지침은 에이전트가 먼저 `researcher` 서브에이전트를 호출하고, `blogs/`, `linkedin/`, 또는 `tweets/` 아래에 마크다운을 쓰고, 이미지에 대해 `generate_cover` 또는 `generate_social_image`를 호출하도록 합니다.
나중에 에이전트를 만들 때 스킬 폴더를 지정하면, 그 스킬 폴더들의 `SKILLS.md` 파일 frontmatter가 시스템 프롬프트에 로드되어 작업이 스킬 설명과 일치할 때 에이전트가 스킬을 사용할 수 있게 됩니다.
스크립트 만들기 (Build the script)
프로젝트 루트에 content_writer.ts를 만드세요. 다음 섹션들은 순서대로 하나의 파일에 속합니다.
```ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { tool } from "@langchain/core/tools";
import * as z from "zod";
import * as fs from "node:fs";
import * as path from "node:path";
const EXAMPLE_DIR = path.dirname(new URL(import.meta.url).pathname);
const webSearch = tool(
async ({ query, maxResults = 5, topic = "general" }) => {
const apiKey = process.env.TAVILY_API_KEY;
if (!apiKey) return { error: "TAVILY_API_KEY not set" };
try {
const { TavilyClient } = await import("tavily");
const client = new TavilyClient({ apiKey });
return client.search(query, { maxResults, topic });
} catch (e) {
return { error: `Search failed: ${e}` };
}
},
{
name: "web_search",
description: "Search the web for current information.",
schema: z.object({
query: z.string().describe("The search query (be specific and detailed)"),
maxResults: z
.number()
.optional()
.describe("Number of results to return (default: 5)"),
topic: z
.enum(["general", "news"])
.optional()
.describe('"general" for most queries, "news" for current events'),
}),
},
);
const generateCover = tool(
async ({ prompt, slug }) => {
try {
const { GoogleGenerativeAI } = await import("@google/generative-ai");
const genai = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY ?? "");
const model = genai.getGenerativeModel({
model: "gemini-2.5-flash-image",
});
const result = await model.generateContent(prompt);
const part = result.response.candidates?.[0]?.content?.parts?.find(
(p) => p.inlineData,
);
if (!part?.inlineData) return "No image generated";
const outputPath = path.join(EXAMPLE_DIR, "blogs", slug, "hero.png");
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, Buffer.from(part.inlineData.data, "base64"));
return `Image saved to ${outputPath}`;
} catch (e) {
return `Error: ${e}`;
}
},
{
name: "generate_cover",
description: "Generate a cover image for a blog post.",
schema: z.object({
prompt: z
.string()
.describe("Detailed description of the image to generate."),
slug: z
.string()
.describe("Blog post slug. Image saves to blogs/<slug>/hero.png"),
}),
},
);
const generateSocialImage = tool(
async ({ prompt, platform, slug }) => {
try {
const { GoogleGenerativeAI } = await import("@google/generative-ai");
const genai = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY ?? "");
const model = genai.getGenerativeModel({
model: "gemini-2.5-flash-image",
});
const result = await model.generateContent(prompt);
const part = result.response.candidates?.[0]?.content?.parts?.find(
(p) => p.inlineData,
);
if (!part?.inlineData) return "No image generated";
const outputPath = path.join(EXAMPLE_DIR, platform, slug, "image.png");
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, Buffer.from(part.inlineData.data, "base64"));
return `Image saved to ${outputPath}`;
} catch (e) {
return `Error: ${e}`;
}
},
{
name: "generate_social_image",
description: "Generate an image for a social media post.",
schema: z.object({
prompt: z
.string()
.describe("Detailed description of the image to generate."),
platform: z.string().describe('Either "linkedin" or "tweets"'),
slug: z
.string()
.describe("Post slug. Image saves to <platform>/<slug>/image.png"),
}),
},
);
```
<CodeGroup>
```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent, FilesystemBackend } from "deepagents";
function createContentWriter() {
const researcherSubagent = {
name: "researcher",
description:
"Research subagent with web search capability. Delegate research tasks here.",
systemPrompt:
"You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.",
tools: [webSearch],
};
return createDeepAgent({
model: "google-genai:gemini-3.6-flash",
memory: ["./AGENTS.md"],
skills: ["./skills/"],
tools: [generateCover, generateSocialImage],
subagents: [researcherSubagent],
backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }),
});
}
```
```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent, FilesystemBackend } from "deepagents";
function createContentWriter() {
const researcherSubagent = {
name: "researcher",
description:
"Research subagent with web search capability. Delegate research tasks here.",
systemPrompt:
"You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.",
tools: [webSearch],
};
return createDeepAgent({
model: "openai:gpt-5.5",
memory: ["./AGENTS.md"],
skills: ["./skills/"],
tools: [generateCover, generateSocialImage],
subagents: [researcherSubagent],
backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }),
});
}
```
```ts Anthropic theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent, FilesystemBackend } from "deepagents";
function createContentWriter() {
const researcherSubagent = {
name: "researcher",
description:
"Research subagent with web search capability. Delegate research tasks here.",
systemPrompt:
"You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.",
tools: [webSearch],
};
return createDeepAgent({
model: "anthropic:claude-sonnet-5",
memory: ["./AGENTS.md"],
skills: ["./skills/"],
tools: [generateCover, generateSocialImage],
subagents: [researcherSubagent],
backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }),
});
}
```
```ts OpenRouter theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent, FilesystemBackend } from "deepagents";
function createContentWriter() {
const researcherSubagent = {
name: "researcher",
description:
"Research subagent with web search capability. Delegate research tasks here.",
systemPrompt:
"You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.",
tools: [webSearch],
};
return createDeepAgent({
model: "openrouter:z-ai/glm-5.2",
memory: ["./AGENTS.md"],
skills: ["./skills/"],
tools: [generateCover, generateSocialImage],
subagents: [researcherSubagent],
backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }),
});
}
```
```ts Fireworks theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent, FilesystemBackend } from "deepagents";
function createContentWriter() {
const researcherSubagent = {
name: "researcher",
description:
"Research subagent with web search capability. Delegate research tasks here.",
systemPrompt:
"You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.",
tools: [webSearch],
};
return createDeepAgent({
model: "fireworks:accounts/fireworks/models/glm-5p2",
memory: ["./AGENTS.md"],
skills: ["./skills/"],
tools: [generateCover, generateSocialImage],
subagents: [researcherSubagent],
backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }),
});
}
```
```ts Baseten theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent, FilesystemBackend } from "deepagents";
function createContentWriter() {
const researcherSubagent = {
name: "researcher",
description:
"Research subagent with web search capability. Delegate research tasks here.",
systemPrompt:
"You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.",
tools: [webSearch],
};
return createDeepAgent({
model: "baseten:zai-org/GLM-5.2",
memory: ["./AGENTS.md"],
skills: ["./skills/"],
tools: [generateCover, generateSocialImage],
subagents: [researcherSubagent],
backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }),
});
}
```
```ts Ollama theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent, FilesystemBackend } from "deepagents";
function createContentWriter() {
const researcherSubagent = {
name: "researcher",
description:
"Research subagent with web search capability. Delegate research tasks here.",
systemPrompt:
"You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.",
tools: [webSearch],
};
return createDeepAgent({
model: "ollama:north-mini-code-1.0",
memory: ["./AGENTS.md"],
skills: ["./skills/"],
tools: [generateCover, generateSocialImage],
subagents: [researcherSubagent],
backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }),
});
}
```
</CodeGroup>
const agent = createContentWriter();
const result = await agent.invoke({
messages: [{ role: "user", content: task }],
config: { configurable: { threadId: "content-builder-demo" } },
});
const messages = result.messages ?? [];
for (const msg of messages) {
if (msg.content) console.log(msg.content);
}
```
에이전트 실행 (Run the agent)
프로젝트 디렉터리에서:
npx tsx content_writer.ts
프롬프트를 추가 인자로 전달하세요:
npx tsx content_writer.ts Write a blog post about prompt engineering
LANGSMITH_API_KEY가 설정되어 있으면 LangSmith에서 실행을 검사할 수 있습니다.
출력 (Output)
성공하면 에이전트가 프로젝트 루트(예시 디렉터리) 아래에 산출물을 작성합니다. 예:
blogs/
└── prompt-engineering/
├── post.md
└── hero.png
research/
└── prompt-engineering.md
경로는 SKILL.md의 스킬 지침을 따릅니다.
전체 코드 (Full code)
GitHub에서 Rich 기반 스트리밍 UI를 포함한 완전한 content-builder-agent 예시를 살펴보세요.
다음 단계 (Next steps)
AGENTS.md를 수정해 브랜드 목소리와 리서치 요구 사항을 변경하세요- 새 콘텐츠 유형에 대해
skills/<name>/SKILL.md아래에 스킬을 추가하세요 subagents.yaml에 서브에이전트를 추가하고load_subagents에 도구를 등록하세요- 더 깊은 구성은 Subagents, Skills, Customization을 읽어보세요
더 알아보기
- 이 문서를 MCP로 연결하면 Claude, VSCode 등에서 실시간 답변을 받을 수 있어요.
- GitHub에서 이 페이지 편집하기 또는 이슈 제출하기.