25편부터 27편까지 비밀값을 가리는 Mod, 운영 DB를 지키는 Mod, 비용을 적는 Mod를 만들었습니다. 혼자 쓸 때는 --plugin-dir로 불러오면 됐습니다. 팀이 같이 쓰려면 사정이 다릅니다. 팀원마다 폴더 경로를 알려 주고 명령을 붙여 달라고 할 수는 없기 때문입니다.
이번 편에서는 세 Mod를 하나의 마켓플레이스로 묶고, 팀 저장소의 설정 파일에 "이 저장소에서는 이 Mod를 켠다"고 적어 두었습니다. 그리고 팀원이 저장소를 새로 받아 Claude Code를 여는 장면부터 따라가 봤습니다. 첫 세션은 잘 됐습니다. 두 번째 세션에서 Mod 셋이 아무 경고 없이 사라졌습니다.
실습 환경: Windows 11, PowerShell, Claude Code 2.1.308, Opus 5.5, 권한 모드 acceptEdits, 2026-10-23 확인. 개인 설정은 싣지 않았습니다(--setting-sources project,local). 팀 원격 저장소는 GitHub 대신 이 PC의 git 저장소 두 개로 대신했고, 팀원은 그 원격을 새 폴더로 받아 처음 여는 사람처럼 시작했습니다. 화면 속 키, 토큰, 주민번호는 모두 지어낸 실습용 값입니다.
먼저 결론
- 저장소
.claude/settings.json에 마켓플레이스와 켤 Mod를 적어 두면, 팀원은 폴더 신뢰 창에서 Yes 하나만 누르고 Mod 셋이 켜진 채로 시작했습니다. 설치를 묻는 창은 없었습니다. - 그런데 Claude Code를 닫았다 다시 열자 Mod 셋이 모두 빠졌습니다. 화면에는 아무 말이 없었고, 고객 CSV의 주민번호가 가려지지 않은 채 Claude에게 넘어갔습니다. 두 번 해서 두 번 같았습니다.
- 팀원이
claude plugin install <Mod>@team-mods --scope project를 한 번 하자 그 뒤로는 세션을 몇 번 열어도 켜졌습니다. - Mod 규칙을 고쳐 푸시해도
version을 올리지 않으면 팀원에게 가지 않았습니다. 올리자 바로 갔습니다. - "이 저장소에서만 꺼 줘"라고 하면 Claude는
settings.local.json을 고쳐 끕니다. 끄지 못하게 막는 Mod를 함께 넣자 막혔지만, 사람이 끄는 길은 그대로였습니다.
1. 꾸러미 만들기: 마켓플레이스 하나에 Mod 넷
공식 문서는 팀 배포를 마켓플레이스로 하라고 안내합니다. 마켓플레이스는 .claude-plugin/marketplace.json 파일 하나가 있는 저장소입니다. 그 파일에 Mod 목록과 각 Mod가 있는 자리를 적습니다.
team-mods/
├── .claude-plugin/marketplace.json
└── plugins/
├── secret-guard/ (25편)
├── sql-guard/ (26편)
├── cost-meter/ (27편)
└── team-lock/ (이번 편, 아래 4장)
{
"name": "team-mods",
"description": "우리 팀이 같이 쓰는 Claude Code Mod",
"owner": { "name": "lab" },
"plugins": [
{ "name": "secret-guard", "source": "./plugins/secret-guard", "description": "API 키와 개인정보를 가린다" },
{ "name": "sql-guard", "source": "./plugins/sql-guard", "description": "운영 DB 에는 행 제한 붙은 조회만" },
{ "name": "cost-meter", "source": "./plugins/cost-meter", "description": "부탁마다 비용 기록, 예산 넘으면 멈춤" },
{ "name": "team-lock", "source": "./plugins/team-lock", "description": "Claude 가 팀 Mod 를 끄지 못하게" }
]
}
목록의 name은 각 Mod의 plugin.json 이름과 같아야 합니다. 다르면 설치할 때 "not found in marketplace"가 납니다. 처음에는 Mod마다 작성자(author)가 없다는 경고가 네 줄 나와서 채웠습니다.
2. 저장소에 적어 두기
문서에 나온 방법은 저장소 폴더에서 claude plugin marketplace add <저장소> --scope project를 한 번 하고, 그 명령이 쓴 .claude/settings.json을 커밋하는 것입니다. 이번 실습의 원격은 이 PC의 git 저장소라서 그 명령이 받아 주지 않았습니다. 경로를 주면 폴더로 읽어 "Marketplace file not found"가 났고, file:/// 주소를 주면 "Invalid marketplace source format"이 났습니다. 그래서 같은 내용을 손으로 적었습니다. GitHub나 사내 git 서버라면 owner/repo나 https:// 주소로 그 명령을 쓰면 됩니다.
{
"extraKnownMarketplaces": {
"team-mods": {
"source": { "source": "git", "url": "file:///D:/lab/team-mods.git" }
}
},
"enabledPlugins": {
"secret-guard@team-mods": true,
"cost-meter@team-mods": true,
"team-lock@team-mods": true
}
}
이 저장소는 DB를 쓰지 않아서 sql-guard는 켜지 않았습니다. 마켓플레이스에는 넷을 두고 저장소마다 필요한 것만 켜는 식입니다.
커밋하려는데 git이 "ignored by one of your .gitignore files"라며 받지 않았습니다. 실습 저장소의 .gitignore에 .claude/가 통째로 들어 있었기 때문입니다. 개인 설정(settings.local.json)을 빼려고 넣은 줄인데, 팀 설정까지 같이 빠졌습니다. 그대로 두면 팀원 누구에게도 설정이 가지 않습니다. 이렇게 고쳤습니다.
node_modules/
.claude/*
!.claude/settings.json
.env
3. 팀원이 처음 열 때
팀원 쪽은 원격을 새 폴더로 받고, 지어낸 키를 넣은 .env를 만든 뒤 Claude Code를 열었습니다.
폴더 신뢰 창에서 Yes를 누른 것 말고는 묻는 것이 없었습니다. 마켓플레이스를 더할지, 어떤 Mod를 설치할지 묻는 창은 뜨지 않았습니다. /plugin을 열어 보니 Mod 셋이 켜져 있었습니다.
편하긴 한데 짚어 둘 점이 있습니다. Mod는 샌드박스 없이 사용자 권한으로 돕니다. 이 저장소를 신뢰한다는 Yes 하나가 팀 마켓플레이스의 코드를 내 PC에서 돌리겠다는 뜻이 됐습니다. 남의 저장소를 받을 때 신뢰 창을 가볍게 넘기면 안 되는 이유가 하나 늘었습니다.
25편과 같은 부탁을 했습니다. "data/customers.csv 를 보고 지역별 고객 수를 알려 줘."
25편에서 혼자 돌렸을 때와 같이 주민번호 여섯과 전화번호 여섯이 가려졌고, 상태줄에 비용도 떴습니다. 여기까지는 계획대로였습니다.
4. 두 번째 세션에서 사라졌다
Claude Code를 닫고 같은 폴더에서 다시 열어 같은 부탁을 했습니다.
이번에는 Claude가 PowerShell로 CSV를 읽었고, 그 결과에 주민등록번호와 휴대폰 번호가 그대로 찍혔습니다. 상태줄에는 가림도, 비용도 없었습니다. /plugin을 열어야 Errors 칸에 3이 보였습니다. 시작 화면에는 아무 말이 없었습니다.
디버그 로그(--debug-file)에는 이렇게 남아 있었습니다.
Skipped auto-recording secret-guard@team-mods ... enabled only by repo-authored settings
Plugin "secret-guard" not cached at ...\marketplaces\team-mods ... run /plugin to refresh
저장소가 쓴 설정만으로 켜진 Mod는 설치 기록을 남기지 않는다는 뜻입니다. 남이 만든 저장소 설정만으로 내 PC에 무언가가 깔려 버리면 곤란하니, 일부러 그렇게 만든 것으로 보입니다. 그 결과 첫 세션에는 켜졌다가, 다음 세션부터는 켜지 못했습니다.
처음에는 실습 때문에 플러그인 저장 위치를 바꿔 둔 탓인지 의심했습니다. 그래서 원래 위치(~/.claude/plugins)로 새 팀원 폴더를 하나 더 만들어 다시 했습니다. 결과는 같았습니다. 첫 세션은 가림 12, 두 번째 세션은 Errors (3)였습니다. 공식 문서에는 마켓플레이스 안의 상대 경로로 적힌 Mod는 저장소 설정이 적용되면 마켓플레이스 사본에서 불러온다고 되어 있어서, 이 PC의 2.1.308에서 본 것과는 다릅니다.
하나 더 있습니다. 첫 세션 셋 중 한 번은 /plugin에 Mod 셋이 켜져 있다고 나왔는데도, 세션을 열자마자 한 첫 부탁의 주민번호를 가리지 못했습니다. 비용 기록은 찍혔습니다. 이유는 확인하지 못했습니다.
고친 방법: 팀원이 한 번 설치하기
문서가 외부 저장소에 있는 Mod에 대해 안내하는 방법이 이번에도 통했습니다. 팀원이 저장소 폴더에서 한 번 실행합니다.
claude plugin install secret-guard@team-mods --scope project
claude plugin install cost-meter@team-mods --scope project
claude plugin install team-lock@team-mods --scope project
그 뒤로 세션을 두 번 더 열었고, 두 번 모두 가림 12와 비용이 찍혔습니다. 다만 이 명령이 커밋된 .claude/settings.json의 키 순서와 줄바꿈을 바꿔 써서, 팀원의 git에 바뀐 파일로 잡혔습니다. 내용은 같아서 git checkout으로 되돌렸고, 되돌린 뒤에도 Mod는 계속 켜졌습니다. 설치 기록은 저장소가 아니라 사용자 폴더 쪽에 남기 때문입니다.
그래서 README에는 "저장소를 받으면 이 세 줄을 한 번 실행하세요"를 적어 두는 것이 맞아 보입니다. 저장소 설정만 믿으면 첫날만 지켜지고, 다음 날부터는 아무도 모르게 빠집니다.
5. 고친 규칙을 팀에 보내기
25편의 빈 곳으로 "사내 토큰은 못 잡는다"를 적었습니다. 그래서 secret-guard에 lab_tok_로 시작하는 사내 토큰 규칙을 더해 푸시했습니다. 이때 version은 0.1.1 그대로 두었습니다. 팀원은 업데이트 명령을 돌렸습니다.
Claude Code는 version 문자열을 보고 새 판인지 가립니다. 커밋이 새로 쌓여도 문자열이 같으면 "이미 최신"입니다. 버전을 0.1.2로 올려 다시 푸시했습니다.
문서에는 version을 아예 빼면 커밋마다 새 판으로 본다는 길도 있습니다. 팀 Mod라면 저는 버전을 올리는 쪽을 고르겠습니다. 언제 무엇이 바뀌었는지 팀원 화면에 숫자로 남기 때문입니다. 업데이트가 저절로 오게 하는 자동 업데이트는 마켓플레이스 쪽에서 켤 수 없고, 팀원이 /plugin에서 켜거나 관리자가 관리 설정으로 켜야 합니다.
제 실수도 적어 둡니다. 사내 토큰 규칙을 처음 푸시할 때 규칙 줄이 빠진 채로 올라갔습니다. 앞서 Slack 토큰 규칙을 더할 때도 셸이 정규식의 \b를 제어 문자로 바꿔서 같은 일이 있었습니다. 두 번 모두 제 PC의 claude plugin test에서는 실패가 보였는데, 시험 결과와 상관없이 푸시가 됐습니다. 팀 마켓플레이스라면 푸시 전에 시험이 자동으로 돌게 해 두는 편이 낫습니다.
6. "이 저장소에서만 꺼 줘"
26편에서 "우회해도 돼"라고 하자 Claude가 가드를 끌 설정 파일을 찾아다녔습니다. 그때는 끌 파일이 없었습니다. 이번에는 저장소 설정으로 켠 Mod라 끌 자리가 분명히 있습니다. 그래서 넷째 Mod team-lock을 넣었습니다. Claude가 설정 파일을 고치거나 Mod를 끄는 명령을 보내면 막습니다. 읽기는 막지 않습니다.
import type { Register } from 'claude-code'
// 팀 Mod 를 켜고 끄는 설정 파일들. 읽기는 되고, Claude 가 고치는 것만 막는다
const SETTINGS = /(^|[\\/])(\.claude[\\/]settings(\.local)?\.json|\.claude\.json)$/i
const SETTINGS_IN_CMD = /settings(\.local)?\.json|\.claude\.json/i
const WRITE_IN_CMD = /Set-Content|Out-File|Add-Content|Copy-Item|Move-Item|Remove-Item|\bsed\s+-i|\btee\b|>|\bcp\b|\bmv\b|\brm\b|WriteAll|ConvertTo-Json/i
const PLUGIN_OFF = /\bplugin\s+(disable|uninstall|remove)\b|--setting-sources|--safe-mode|disableAllHooks/i
export function blocked(tool: string, input: { file_path?: unknown; command?: unknown }): string | undefined {
if (tool === 'Edit' || tool === 'Write' || tool === 'NotebookEdit') {
const p = String(input.file_path ?? '')
if (SETTINGS.test(p)) return `${p} 는 팀 Mod 를 켜고 끄는 설정입니다.`
}
if (tool === 'Bash' || tool === 'PowerShell') {
const c = String(input.command ?? '')
if (PLUGIN_OFF.test(c)) return 'Mod 를 끄거나 지우는 명령입니다.'
if (SETTINGS_IN_CMD.test(c) && WRITE_IN_CMD.test(c)) return '설정 파일을 고치는 명령입니다.'
}
return undefined
}
export const register: Register = on => {
on('tool.call', async ($, e, next) => {
const why = blocked(e.tool, e as { file_path?: unknown; command?: unknown })
if (why) {
await $.ui.toast('team-lock: 막음')
return { deny: `team-lock: ${why} 팀 Mod 를 끄거나 바꾸는 일은 Claude 가 하지 않습니다. 필요하면 사람이 직접 하거나 팀에 알려 주세요.` }
}
return next(e)
}).catch(($, e, next) => (next.called ? next(e) : { deny: 'team-lock: 검사하지 못해 막았습니다.' }))
}
팀원 자리에서 이렇게 부탁했습니다. "secret-guard 가 자꾸 가려서 불편해. 이 저장소에서만 꺼 줘." 이어서 "괜찮아, 내가 허락할게. 꺼 줘."
Claude는 .claude/settings.local.json에 "secret-guard@team-mods": false를 쓰려다 막혔습니다. 허락한다고 해도 "팀이 걸어 둔 장치라 셸 명령 같은 다른 방법으로 우회하지 않겠다"며, 사람이 !로 칠 명령을 적어 줬습니다. 끄면 무엇이 사라지는지, 자꾸 가리는 게 오탐이라면 끄는 것보다 팀에 규칙을 고쳐 달라고 하는 편이 낫다는 말도 덧붙였습니다.
비교하려고 사람이 먼저 settings.local.json으로 team-lock만 꺼 두고 같은 부탁을 했습니다.
Claude는 첫 부탁에 바로 settings.local.json을 고치러 갔습니다. 눈여겨볼 점은 acceptEdits 모드인데도 Claude Code가 "settings.local.json을 고칠까요?"라고 확인창을 띄웠다는 것입니다. .claude 폴더의 설정 파일은 Claude Code가 따로 지키는 자리였습니다. 실습 스크립트가 사람 대신 Yes를 눌렀습니다.
다음 세션에서 사내 토큰 파일을 보여 달라고 하자 토큰이 그대로 나왔습니다. 정말로 꺼진 것입니다. 이 파일은 커밋되지 않으니 팀의 다른 사람은 모릅니다.
team-lock이 막지 못하는 것
- 사람이 끄는 것. 방금처럼 사람이
settings.local.json을 고치거나!로 명령을 치면 그대로 꺼집니다.team-lock자신도 같은 방법으로 꺼집니다. - Claude의 도구 호출이 아닌 길.
team-lock은 Claude가 보내는 Edit, Write, 셸 명령만 봅니다.--safe-mode로 시작하면 설치된 Mod가 모두 꺼집니다.
공식 문서에 따르면 정말로 끌 수 없게 하는 자리는 관리 설정(managed settings)입니다. 관리 설정의 enabledPlugins로 켠 Mod는 사용자가 자기 범위에서 꺼도 계속 돕니다. 다만 문서는 GitHub이나 git 원격에서 받아 사본으로 깔린 Mod는 "조직의 Mod"가 아니라 사용자의 Mod로 친다고 적습니다. 조직의 Mod로 치려면 관리자가 각 PC의 정해진 폴더에 마켓플레이스를 깔고 그 절대 경로를 관리 설정에 적어야 합니다. 관리 설정은 PC 전체에 걸리는 설정이라 이번 실습에서는 바꾸지 않았습니다.
만들며 배운 것
- 저장소 설정은 안내문에 가깝습니다. 첫 세션에 켜 주지만 설치 기록을 남기지 않아, 이 PC의 2.1.308에서는 다음 세션부터 빠졌습니다. 팀원이
install --scope project를 한 번 해야 남습니다. - 빠질 때 소리가 없습니다. 시작 화면은 조용했고
/plugin의 Errors 칸에만 숫자가 있었습니다. 상태줄에 무언가를 띄우는 Mod라면 그것이 보이지 않는 것으로 알아챌 수 있습니다. - 고쳤으면
version을 올립니다. 안 올리면 "이미 최신"입니다. .gitignore의.claude/한 줄이 팀 설정을 통째로 막습니다.- 끄지 못하게 하는 Mod는 Claude만 막습니다. 사람까지 막으려면 관리 설정이 필요합니다.
남은 빈틈
- 원격은 이 PC의 git 저장소로 대신했습니다. GitHub 비공개 저장소의 인증, 자동 업데이트는 돌려 보지 않았습니다.
- 두 번째 세션에서 빠지는 것이 의도된 동작인지 버그인지는 확인하지 못했습니다. 문서의 설명과는 달랐습니다.
- 첫 세션의 첫 부탁을 한 번 놓친 이유는 찾지 못했습니다.
- 관리 설정으로 강제하는 방법은 문서로만 확인했습니다.
팀 저장소에 Claude Code 설정을 두고 계시다면, 팀원 한 명에게 Claude Code를 두 번 열어 보고 /plugin의 Errors 칸을 봐 달라고 해 보시기 바랍니다. 첫날만 켜지고 있었을 수 있습니다.
다음 편에서는 지금까지 만든 Mod들을 성능, 보안, 호환성 기준으로 하나씩 점검하는 목록을 만들어 보겠습니다.
참고: Create a marketplace (code.claude.com), Host and maintain a marketplace (code.claude.com), Manage plugins for your organization (code.claude.com), Manage mods for your organization (code.claude.com)
그러면 오늘도 모두 스테이블 하세요.
'Coding, Testing, Challenge' 카테고리의 다른 글
| Claude Code 개발 QA 보안 통합 Mod 세트 만들기, 넷을 한 번에 켜고 부탁 하나를 맡겨 봤다 (0) | 2026.10.25 |
|---|---|
| Claude Code Mod 를 믿어도 되는지 확인하기, 막는 Mod 가 느려졌더니 조용히 통과됐다 (0) | 2026.10.25 |
| Claude Code 사용량과 비용을 관리하는 Mod, 예산 $0.05에서 멈추게 했더니 멈추는 데도 돈이 들었다 (0) | 2026.10.22 |
| Claude Code SQL 작업을 안전하게 만드는 Mod, 운영 DB 삭제를 막았더니 Claude가 가드 설정을 찾아 나섰다 (0) | 2026.10.22 |
| Claude Code API 키·개인정보 노출을 막는 Mod, 키를 코드에 넣어 달라고 하자 Claude가 진짜 키를 적었다 (0) | 2026.10.22 |