본문 바로가기

Coding, Testing, Challenge

Claude Code 개발 QA 보안 통합 Mod 세트 만들기, 넷을 한 번에 켜고 부탁 하나를 맡겨 봤다

Claude Mods 연재 30편 대표 이미지

이 연재에서 지금까지 Mod를 하나씩 만들었습니다. 바뀐 파일을 옆 패널에 보여 주는 것, 고칠 때마다 테스트를 돌리는 것, 비밀값을 가리는 것, 운영 DB를 지키는 것입니다. 각각은 혼자서도 쓸모가 있었습니다. 마지막 편에서는 이것들을 역할별로 한 세트로 묶어, 한 번에 켜고 실제 작업 하나를 맡겨 봤습니다. 네 Mod가 한 세션에서 서로 방해하지 않고 각자 자리에서 일하는지 보려는 것입니다.

묶은 세트는 개발, QA, 보안 세 역할입니다. 개발은 git-changes(14편), QA는 review-test(24편), 보안은 secret-guard(25편)와 sql-guard(26편)입니다. 이 넷을 dev-kit이라는 마켓플레이스 하나에 모았습니다.

실습 환경: Windows 11, PowerShell, Claude Code 2.1.310, 권한 모드 acceptEdits, 2026-10-25 확인. 개인 설정은 싣지 않았습니다(--setting-sources project,local). 실습 저장소에는 지어낸 고객 CSV(주민번호와 휴대폰 포함), 가짜 알림 키, 그리고 dev DB 200명과 prod DB 50,000명의 SQLite가 들어 있습니다. 화면 속 값은 모두 실습용입니다.

먼저 결론

  • 네 Mod를 마켓플레이스 하나(dev-kit)로 묶었습니다. claude plugin validate가 세트 전체를 통과시켰습니다.
  • 세트를 켜자 Changes 패널이 열리고 네 Mod가 함께 섰습니다. "고객 CSV로 지역별 수를 세고 시험도 붙여 줘"라는 부탁 하나에 세 역할이 동시에 움직였습니다. CSV의 주민번호는 가려지고, 테스트가 돌고, 바뀐 파일이 패널에 떴기 때문입니다.
  • 그 세션 기록에 원래 주민번호는 한 번도 남지 않았습니다. secret-guard가 Claude가 읽기 전에 가렸고, Claude는 가려진 값만 보고도 집계를 끝냈습니다.
  • 이어서 운영 DB 조회를 맡기자, sql-guard가 "집계가 아닌 쿼리에는 행 제한이 필요하다"며 한 번 막았고, Claude는 집계 쿼리로 바꿔 읽기 전용으로만 셌습니다.
  • 역할이 겹치지 않게 고른 Mod들은 한 상태줄에 나란히 서서 서로를 방해하지 않았습니다. 19편에서 본 충돌과는 반대 경우였습니다.

1. 세트 조립: 마켓플레이스 하나에 Mod 넷

28편에서 팀 배포를 마켓플레이스로 한다는 것을 봤습니다. 이번에는 배포가 아니라 구성이 목적이라, 역할별로 고른 Mod 넷을 dev-kit 저장소 하나에 모으고 marketplace.json에 적었습니다. 그리고 세트 전체가 성한지 검사했습니다.

claude plugin validate 가 dev-kit 마켓플레이스를 통과시킨다

마켓플레이스 하나를 검사하면 그 안의 Mod들을 함께 봐 줍니다. 네 Mod가 모두 통과했습니다. 세트를 만들 때는 29편에서 본 점검을 Mod마다 해 두는 것이 먼저입니다. 각 Mod의 calls:에 뜻밖의 호출이 없는지, 막는 Mod라면 .catch가 붙어 있는지를 확인한 것들만 세트에 넣었기 때문입니다.

2. 한 번 켜자

세트를 켜고 실습 저장소를 열었습니다. 켜자마자 오른쪽에 Changes 패널이 열렸습니다. git-changes가 브랜치와 바뀐 파일을 거기에 그립니다. 아직 바꾼 것이 없어 "바뀐 파일 없음"이라고만 떠 있습니다.

세트를 켜자 오른쪽에 Changes 패널이 열린 모습

3. 부탁 하나에 세 역할이 함께

이제 한 부탁을 맡겼습니다. "data/customers.csv를 읽어서 지역별 고객 수를 세는 함수를 src에 추가하고 시험도 붙여 줘"입니다. 이 하나가 보안과 QA와 개발을 한꺼번에 건드립니다. CSV에는 주민번호가 들어 있고(보안), 함수에는 시험이 필요하고(QA), 파일이 새로 생기기 때문입니다(개발).

한 화면에 Changes 패널, 테스트 10/10, 가림 12 가 함께 떠 있다

한 화면에 세 역할이 모두 보입니다. 오른쪽 패널에는 새로 생긴 src/customers.js와 테스트 파일이 떴습니다(개발). 아래 상태줄에는 review-test: 테스트 10/10이 있고, Claude는 "10개 테스트 전부 통과합니다"라고 끝냈습니다(QA). 그 옆 secret-guard: 가림 12는 CSV를 읽는 동안 주민번호와 휴대폰 열두 값을 가렸다는 뜻입니다(보안).

눈여겨본 것은 Claude의 마지막 말입니다. "마스킹된 주민번호와 휴대폰 값에도 쉼표가 없어 안전"하다고 적었습니다. Claude는 가려진 값을 본 상태에서 지역만 세어 집계를 마쳤습니다. 실제로 그 세션 기록을 뒤져 보니 원래 주민번호는 한 번도 남아 있지 않았습니다. 가리는 일이 Claude가 읽기 전에 일어났기 때문입니다.

4. 이어서 운영 DB 조회

같은 세션에서 보안 Mod 하나를 더 건드려 봤습니다. "prod DB에서 지역이 서울인 고객이 몇 명인지 봐 줘"입니다.

sql-guard 가 집계가 아닌 쿼리에 행 제한을 요구하고, Claude 가 집계 쿼리로 바꿔 센다

Claude의 첫 시도가 sql-guard에 걸렸습니다. 화면에 "집계가 아닌 쿼리에는 LIMIT이 필요하다"고 남았습니다. 그러자 Claude는 전체 행을 긁어 세는 대신 집계 쿼리로 바꿔서, 지역별 수를 한 번에 셌습니다. 서울은 8,333명이었고, 조회는 읽기 전용 SELECT로만 했다고 적었습니다. 막는 것이 일을 멈추게 한 것이 아니라, 안전한 방식으로 돌아가게 만든 셈입니다. 이때 상태줄에는 가림 12, 테스트 10/10, SQL prod 세 줄이 나란히 떠 있었습니다.

세트로 묶으며 배운 것

  • 역할이 겹치지 않는 Mod들은 한 세션에서 서로를 방해하지 않았습니다. 하나는 패널을 그리고, 하나는 테스트를 돌리고, 둘은 각기 다른 자리(파일 읽기, 셸 명령)를 지켰기 때문입니다. 19편에서 본 충돌은 같은 자리를 두 Mod가 다툴 때 생겼습니다.
  • 막는 Mod는 일을 멈추게 하기보다 방향을 바꾸게 했습니다. 주민번호는 가려진 채로 집계가 됐고, 운영 조회는 집계 쿼리로 돌아갔기 때문입니다.
  • 세트의 신뢰는 Mod 하나하나의 점검에서 나왔습니다. 29편의 확인을 거친 Mod만 넣었기 때문에, 세트를 켜 놓고도 각 Mod가 조용히 통과시킬 걱정을 덜 수 있었습니다.

남은 빈틈

  • 이번 세트는 역할이 겹치지 않게 골랐습니다. 같은 자리(예: 둘 다 셸 명령을 막는)를 다투는 Mod를 함께 넣으면 순서를 정해야 하고, 그건 19편의 주제였습니다.
  • 마켓플레이스로 묶었지만 팀에 배포해 두 번째 세션까지 유지되는지는 28편에서 본 대로 설치 한 번이 더 필요합니다. 이번에는 구성만 확인했고 배포 지속성은 다시 보지 않았습니다.
  • 운영 조회를 집계로 돌린 것은 이번 한 번의 관찰입니다. 더 공격적인 요청에서도 같은 방향으로 가는지는 더 재 봐야 합니다.

여기까지가 서른 편입니다. Mod가 무엇인지 스피너에 숫자 하나 띄워 보는 것으로 시작해, 손으로 만들고, 여럿을 함께 켜고, 팀에 배포하고, 믿을 수 있는지 확인하고, 마지막으로 한 세트로 묶었습니다. 처음 한 줄과 마지막 한 세트 사이에, 막는 Mod가 조용히 통과시킨 날도 있었고 가드를 피해 다른 길로 돌아간 날도 있었습니다. 그 어긋남을 적어 둔 것이 이 연재였습니다.

직접 쓰시는 Claude Code에도 반복되는 일이 있다면, 작은 Mod 하나로 시작해 보시기 바랍니다. 켜기 전에 claude plugin validate 한 번, 그리고 역할이 겹치지 않게 고르는 것, 그 두 가지면 세트까지 어렵지 않기 때문입니다.

참고: Mods overview (code.claude.com), Create a marketplace (code.claude.com)

그러면 오늘도 모두 스테이블 하세요.