문제 상황
Claude Code를 사용하면서 다음과 같은 불편함을 느낌:
- 매번 같은 설명 반복: “이 프로젝트는 블로그고, 스타일은 이렇고…”
- 일관성 없는 결과물: 이전에 요청한 방식과 다르게 작성됨
- 수동 작업 반복: 블로그 포스트 생성할 때마다 Front Matter 작성
이런 반복 작업을 줄이고 일관된 결과물을 얻고 싶었음.
해결 방법
Claude Code는 다음 세 가지 방법으로 커스터마이징할 수 있음:
- CLAUDE.md: 프로젝트 컨텍스트와 작업 가이드 문서화
- Custom Skill: 반복 작업을 스크립트화
- MCP Server: 외부 도구 연동
1. CLAUDE.md 활용법
CLAUDE.md란?
프로젝트 루트에 위치하여 Claude Code에게 프로젝트 컨텍스트를 제공하는 Markdown 문서.
Claude Code는 대화 시작 시 자동으로 이 파일을 읽고 컨텍스트를 파악함.
파일 위치
1
2
3
4
5
| project-root/
├── CLAUDE.md ← 여기에 생성
├── README.md
├── package.json
└── src/
|
기본 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # 프로젝트명
## 프로젝트 개요
(프로젝트 설명)
## 기술 스택
- Frontend: React, TypeScript
- Backend: Node.js, Express
## 작업 시 주의사항
### 코드 스타일
- 함수형 컴포넌트 사용
- TypeScript strict mode
### 네이밍 규칙
- 컴포넌트: PascalCase
- 함수: camelCase
## 자주 하는 작업
### 1. 새 컴포넌트 생성
(템플릿이나 가이드)
### 2. API 엔드포인트 추가
(가이드라인)
|
실전 예시: 블로그 프로젝트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| # 기술 블로그 프로젝트
## 글 작성 스타일
### 선호하는 스타일
- 간결하고 직관적 ("~임", "~됨" 체)
- Before/After 코드 비교 포함
- 실제 에러 로그 포함
### 피해야 할 스타일
- ❌ "이번 포스팅에서는..."
- ❌ 과도한 감탄사
## Front Matter 형식
~~~yaml
---
title: "[카테고리] 제목"
date: YYYY-MM-DD HH:MM:SS +09:00
categories: [Category1, Category2]
tags: [Tag1, Tag2]
---
~~~
## 회사 정보 보호
실제 프로젝트명이나 회사 정보는 절대 노출 금지.
다음과 같이 일반화:
- 프로젝트명: "서비스 A", "서비스 B"
- 경로: `/home/user/app`
|
활용 효과
Before (CLAUDE.md 없을 때):
1
2
3
| 나: 블로그 포스트 작성해줘
Claude: [일반적인 형식으로 작성]
나: 아니 우리 블로그 스타일은... (긴 설명)
|
After (CLAUDE.md 있을 때):
1
2
| 나: 블로그 포스트 작성해줘
Claude: [CLAUDE.md 스타일에 맞춰 자동 작성]
|
2. Custom Skill 작성법
Custom Skill이란?
프로젝트별로 자주 하는 작업을 명령어로 만들어서 /skill-name 형태로 호출할 수 있음.
Skill 폴더 구조
1
2
3
4
5
6
7
8
9
| project-root/
└── .claude/
└── skills/
├── blog-post/
│ ├── skill.ts
│ └── template.md
└── component/
├── skill.ts
└── template.tsx
|
기본 Skill 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| // .claude/skills/blog-post/skill.ts
import { Skill } from '@anthropic/claude-code';
export const blogPostSkill: Skill = {
name: 'create-blog-post',
description: '새 블로그 포스트 생성',
async execute(args: string[]) {
const [title, category] = args;
if (!title) {
throw new Error('Usage: /create-blog-post "포스트 제목" "카테고리"');
}
const date = new Date().toISOString().split('T')[0];
const fileName = `${date}-${title.toLowerCase().replace(/\s+/g, '-')}.md`;
const template = `---
title: "[${category || 'Tech'}] ${title}"
description:
date: ${new Date().toISOString().slice(0, 19).replace('T', ' ')} +09:00
categories: [${category || 'Tech'}]
tags: []
---
## 문제 상황
---
## 해결 방법
---
### 도움이 되셨길 바랍니다! 😀
`;
await this.writeFile(`_posts/${fileName}`, template);
return `Created: _posts/${fileName}`;
}
};
|
Skill 등록
1
2
3
4
5
6
7
8
| // .claude/skills/index.ts
import { blogPostSkill } from './blog-post/skill';
import { componentSkill } from './component/skill';
export const skills = [
blogPostSkill,
componentSkill,
];
|
Skill 사용 예시
1
2
3
4
| # 블로그 포스트 생성
/create-blog-post "Claude Code 활용법" "Tool"
# 결과: _posts/2026-01-04-claude-code-활용법.md 생성됨
|
3. 고급 Skill: 인터랙티브 입력
문제
단순 파라미터만으로는 복잡한 작업을 처리하기 어려움.
해결: Prompt 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
| // .claude/skills/blog-post-interactive/skill.ts
export const interactiveBlogPostSkill: Skill = {
name: 'blog-post',
description: '대화형 블로그 포스트 생성',
async execute() {
// 1. 제목 입력받기
const title = await this.prompt('포스트 제목을 입력하세요:');
// 2. 카테고리 선택
const category = await this.select('카테고리를 선택하세요:', [
'Trouble Shooting',
'Design Pattern',
'Tool',
'Study'
]);
// 3. 태그 입력
const tagsInput = await this.prompt('태그를 입력하세요 (쉼표로 구분):');
const tags = tagsInput.split(',').map(t => t.trim());
// 4. 템플릿 선택
const templateType = await this.select('템플릿을 선택하세요:', [
'Troubleshooting',
'Tutorial',
'Comparison'
]);
const template = this.getTemplate(templateType, { title, category, tags });
const date = new Date().toISOString().split('T')[0];
const fileName = `${date}-${this.slugify(title)}.md`;
await this.writeFile(`_posts/${fileName}`, template);
return `Created: _posts/${fileName}`;
},
getTemplate(type: string, data: any): string {
const templates = {
Troubleshooting: `---
title: "[${data.category}] ${data.title}"
description:
date: ${new Date().toISOString().slice(0, 19).replace('T', ' ')} +09:00
categories: [${data.category}]
tags: [${data.tags.join(', ')}]
---
## 문제 상황
### 오류 메시지
\`\`\`
\`\`\`
---
## 원인
---
## 해결 방법
### Before
\`\`\`typescript
\`\`\`
### After
\`\`\`typescript
\`\`\`
---
## 결과
---
### 도움이 되셨길 바랍니다! 😀
`,
Tutorial: `---
title: "${data.title}"
description:
date: ${new Date().toISOString().slice(0, 19).replace('T', ' ')} +09:00
categories: [${data.category}]
tags: [${data.tags.join(', ')}]
---
## 개요
---
## 사전 준비
---
## 단계별 가이드
### 1.
### 2.
---
## 마치며
### 도움이 되셨길 바랍니다! 😀
`
};
return templates[type] || templates.Troubleshooting;
},
slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^a-z0-9가-힣]+/g, '-')
.replace(/^-+|-+$/g, '');
}
};
|
사용 예시
1
2
3
4
5
6
7
8
9
| /blog-post
# 대화형으로 입력
포스트 제목을 입력하세요: AG-Grid v33 마이그레이션
카테고리를 선택하세요: [2] Design Pattern
태그를 입력하세요: AG-Grid, Migration, TypeScript
템플릿을 선택하세요: [1] Troubleshooting
Created: _posts/2026-01-04-ag-grid-v33-마이그레이션.md
|
4. MCP (Model Context Protocol) 활용
MCP란?
Claude Code와 외부 도구를 연결하는 프로토콜.
예: GitHub API, Notion API, Database 등과 연동 가능.
MCP Server 설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // .claude/mcp_config.json
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_your_token_here"
}
},
"notion": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-notion"],
"env": {
"NOTION_TOKEN": "secret_your_token_here"
}
}
}
}
|
MCP를 활용한 Skill
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| // GitHub 이슈를 블로그 포스트로 변환
export const issueToBlogSkill: Skill = {
name: 'issue-to-blog',
description: 'GitHub 이슈를 블로그 포스트로 변환',
async execute(args: string[]) {
const [issueNumber] = args;
// MCP를 통해 GitHub API 호출
const issue = await this.mcp.github.getIssue({
owner: 'username',
repo: 'blog',
issue_number: parseInt(issueNumber)
});
const template = `---
title: "[Trouble Shooting] ${issue.title}"
description: ${issue.body.split('\n')[0]}
date: ${new Date().toISOString().slice(0, 19).replace('T', ' ')} +09:00
categories: [Trouble Shooting]
tags: []
---
${issue.body}
---
### 도움이 되셨길 바랍니다! 😀
`;
const fileName = `${new Date().toISOString().split('T')[0]}-${this.slugify(issue.title)}.md`;
await this.writeFile(`_posts/${fileName}`, template);
return `Created from issue #${issueNumber}: _posts/${fileName}`;
}
};
|
5. Hooks 활용
Hook이란?
특정 이벤트가 발생했을 때 자동으로 실행되는 스크립트.
설정 파일
1
2
3
4
5
6
7
8
| // .claude/settings.json
{
"hooks": {
"beforeToolCall": "scripts/before-tool.sh",
"afterToolCall": "scripts/after-tool.sh",
"onFileWrite": "scripts/on-file-write.sh"
}
}
|
Hook 예시: 자동 포맷팅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #!/bin/bash
# scripts/on-file-write.sh
FILE=$1
# Markdown 파일이면 Prettier로 포맷팅
if [[ $FILE == *.md ]]; then
npx prettier --write "$FILE"
fi
# TypeScript 파일이면 ESLint 자동 수정
if [[ $FILE == *.ts ]] || [[ $FILE == *.tsx ]]; then
npx eslint --fix "$FILE"
fi
|
Hook 예시: 자동 커밋
1
2
3
4
5
6
7
8
9
10
11
12
| #!/bin/bash
# scripts/after-tool.sh
TOOL=$1
RESULT=$2
# 블로그 포스트 생성 후 자동 커밋
if [[ $TOOL == "Write" ]] && [[ $RESULT == *"_posts/"* ]]; then
FILE=$(echo $RESULT | grep -o "_posts/[^']*")
git add "$FILE"
git commit -m "docs: Add blog post $(basename $FILE)"
fi
|
실전 예시: 블로그 워크플로우
전체 프로세스
1
2
3
4
5
6
7
8
9
10
11
| 1. 아이디어 정리 (GitHub Issue)
↓
2. /issue-to-blog [이슈번호]
↓
3. Claude가 초안 작성
↓
4. 수정 요청 ("더 간결하게", "코드 예시 추가")
↓
5. Hook이 자동으로 포맷팅 & 커밋
↓
6. GitHub Pages 자동 배포
|
디렉토리 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| blog/
├── CLAUDE.md # 프로젝트 가이드
├── .claude/
│ ├── settings.json # Hook 설정
│ ├── mcp_config.json # MCP 설정
│ ├── skills/
│ │ ├── index.ts
│ │ ├── blog-post/
│ │ │ ├── skill.ts
│ │ │ └── templates/
│ │ │ ├── troubleshooting.md
│ │ │ ├── tutorial.md
│ │ │ └── comparison.md
│ │ └── issue-to-blog/
│ │ └── skill.ts
│ └── scripts/
│ ├── before-tool.sh
│ ├── after-tool.sh
│ └── on-file-write.sh
├── _posts/
└── package.json
|
결과
Before (수동 작업)
1
2
3
4
5
6
7
8
| 1. 포스트 파일 생성 (5분)
2. Front Matter 작성 (3분)
3. 템플릿 복사 (2분)
4. 내용 작성 (30분)
5. 포맷팅 (5분)
6. 커밋 & 푸시 (2분)
총 47분
|
After (자동화)
1
2
3
4
5
6
| 1. /blog-post 실행 (30초)
2. 내용 작성 (Claude가 초안 생성) (5분)
3. 수정 요청 (5분)
4. 자동 포맷팅 & 커밋 (0초)
총 10분 30초
|
시간 절감: 78%
주의할 점
1. CLAUDE.md는 간결하게
- 너무 길면 Claude가 읽지 않을 수 있음
- 핵심만 요약해서 작성
- 1,000줄 이하 권장
2. Skill은 명확한 용도로
- 한 Skill에 너무 많은 기능을 넣지 말 것
- 작고 재사용 가능한 Skill로 분리
3. MCP는 보안 주의
- API 토큰은 환경 변수로 관리
.gitignore에 mcp_config.json 추가
1
2
3
| # .gitignore
.claude/mcp_config.json
.claude/.env
|
4. Hook은 신중하게
- 자동 커밋 Hook은 의도하지 않은 커밋을 만들 수 있음
- 테스트 후 활성화 권장
추가 리소스
Claude Code의 CLAUDE.md, Custom Skill, MCP, Hooks를 활용하면 반복 작업을 크게 줄일 수 있음. 특히 블로그 포스트 작성처럼 템플릿화 가능한 작업은 자동화 효과가 매우 큼.
도움이 되셨길 바랍니다! 😀