본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 30. 10:48

Skills가 '프로젝트 전용'이 되는 문제를 3계층 설정 병합으로 해결한 이야기

요약

Claude Code Skills 사용 시 발생하는 프로젝트 간 설정 중복 문제를 '3계층 설정 병합' 방식으로 해결하는 방법을 다룹니다. 스킬 내장 기본값, 글로벌 설정, 프로젝트 설정을 딥 머지(Deep merge)하여 코드 수정 없이 유연하게 설정을 관리하는 설계 패턴을 소개합니다.

핵심 포인트

  • 하드코딩된 프로젝트 고유값을 제거하여 스킬 재사용성 향상
  • 기본값, 글로벌, 프로젝트 3계층의 우선순위 기반 설정 병합
  • jq의 딥 머지를 활용해 중첩된 객체의 특정 키만 선택적 재정의
  • 공유 라이브러리를 통한 스킬 관리 효율화 및 유지보수 비용 절감

스킬을 20개쯤 작성하니, 복사 붙여넣기 이식이 시작되었다

Claude Code Skills를 20개 정도 작성했을 무렵, 문득 깨달았습니다. 프로젝트 A에서 만든 스킬을 프로젝트 B로 복사해서 붙여넣고 있는 제 자신을 말이죠.

GA4의 속성 ID(Property ID)를 수동으로 고쳐 쓰고, 출력 디렉터리를 수정하고, SNS 게시 설정(SNS post settings)을 조정하고——.

그건 자동화 도구를 수동으로 이식해서 무엇 하려는 건가.

근본적인 문제는 스크립트 안에 프로젝트 고유값이 하드코딩(Hard-coded)되어 있다는 점입니다:

PROPERTY_ID="123456789" # GA4의 속성 ID
OUTPUT_DIR="./claudedocs" # 리포트 출력 위치
SITE="sc-domain:example.com" # GSC의 사이트 URL

스킬이 늘어날수록 이식 비용이 쌓입니다. 그리고 이식한 곳에서 버그 수정을 가해도 원래의 스킬에는 반영되지 않습니다. 스킬이 분열됩니다.

3계층 설정 병합(3-layer config merge)으로 해결하기

해결책은 "스킬 본체에 프로젝트 고유값을 적지 않는다"라는 한 점으로 집약됩니다. 이를 실현하는 메커니즘이 3계층 설정 병합입니다.

병합 우선순위

스킬이 설정을 읽을 때, 3개의 계층이 딥 머지(Deep merge)됩니다:

스킬 내장 기본값 ← 글로벌 설정 ← 프로젝트 설정
(최저 우선순위) (사용자 공통) (최우선 순위)
계층경로용도
글로벌 (Global)~/.config/skills/config.json모든 프로젝트 공통 (타임존, 언어 등)
프로젝트 (Project)<project>/skill-config.json프로젝트 고유 (API ID, 출력 위치 등)
기본값 (Default)스크립트 내 초기값미설정 시 폴백 (Fallback)

구현

공유 라이브러리의 load_skill_config 함수를 source 하는 것만으로 사용할 수 있습니다:

source "$SKILLS_DIR/_lib/common.sh"
config=$(load_skill_config "ga-analyzer")
property_id=$(echo "$config" | jq -r '.property_id')
...

내부에서는 jq* 연산자로 딥 머지(Deep merge)를 수행합니다:

# 글로벌과 프로젝트를 병합 (프로젝트가 우선함)
echo "$global_cfg" | jq --argjson proj "$project_cfg" '. * $proj'

왜 딥 머지(Deep merge)인가

섀로우 머지(Shallow merge, 중첩된 객체를 덮어쓰는 방식)로는 곤란한 상황이 있습니다.

SNS 게시 설정을 예로 들어보겠습니다. 글로벌 설정에서 "X와 LinkedIn을 기본 ON"으로 설정하면서도, 특정 프로젝트에서는 "LinkedIn만 OFF"로 하고 싶을 때:

// ~/.config/skills/config.json
{
"sns-announce": {
...
// projectB/skill-config.json
{
"sns-announce": {
...

섀로우 머지(Shallow merge)를 사용하면 platforms 객체 전체가 프로젝트 설정으로 덮어씌워져서, x 설정이 사라집니다. 딥 머지(Deep merge)라면 수정한 키만 덮어씌워집니다:

// 병합 결과
{
"sns-announce": {
...

스킬이 늘어나도 설정의 재정의가 필요 없게 됩니다. 소소한 설계이지만, 스킬이 30개를 넘어갈 때쯤 "해두길 잘했다"라고 느낍니다.

_lib/ 공유 라이브러리로 중복 코드를 제로로 만들기

설정 병합을 실현하는 load_skill_config를 각 스킬의 스크립트에 복사해서 붙여넣고 있다면 본말전도입니다. _lib/ 디렉터리에 공통 처리를 집약합니다:

skills/
├── _lib/
│ ├── common.sh # Bash 공통 함수
...

각 스킬은 맨 앞줄에서 1줄 source 하기만 하면 됩니다:

#!/usr/bin/env bash
source "$(dirname "$0")/../../_lib/common.sh"
require_cmd jq # jq가 없으면 즉시 에러
...

왜 이렇게 작성하는가

require_cmdrequire_gh_auth...

를 맨 앞에 두는 데에는 이유가 있습니다. 스크립트가 도중에 실패하면 원인 파악에 시간이 걸립니다. 전제 조건의 결여는 서두에서 즉시 검출하는 것이 디버깅 비용을 낮춥니다.

Python 스킬도 Bash 버전과 동일하게 config.json을 읽습니다:

from _lib.config import load_skill_config
config = load_skill_config("ga-analyzer")
property_id = config.get("property_id")

설정의 이중 관리가 발생하지 않습니다. 언어를 바꿔도 설정 파일은 하나라는 단순함이 중요합니다.

실전에서 깨달은 점

글로벌과 프로젝트의 구분에서 오는 고민

"타임존 (Timezone)"은 글로벌. "GA4 프로퍼티 ID (GA4 Property ID)"는 프로젝트 고유. 이 구분은 명확합니다.

고민되는 지점은 "SNS 게시 대상"입니다. 개인적으로는 모든 프로젝트에서 X(구 Twitter)에 게시하지만, 클라이언트 프로젝트에서는 SNS 게시를 비활성화하고 싶을 때가 있습니다. 이 케이스는 기본값(Default)을 글로벌로 정의하고, 예외를 프로젝트에서 덮어쓰기 (Override) 하는 패턴으로 대처할 수 있습니다.

jq가 필수

common.shhas_jq 체크에서 폴백 (Fallback) 처리가 실행되지만, 딥 머지 (Deep merge)는 jq가 없으면 작동하지 않습니다. nix를 사용한다면 nix profile install nixpkgs#jq로 설치할 수 있습니다.

Git 관리 외에서는 설정을 읽을 수 없음

load_skill_configgit rev-parse --show-toplevel로 프로젝트 루트를 특정합니다. Git 리포지토리(Repository) 외부에서 실행하면 프로젝트 설정을 읽을 수 없어 글로벌 설정만 적용됩니다. "설정이 반영되지 않는다"고 느껴진다면 먼저 git status를 확인하십시오.

요약: 나는 이렇게 사용하고 있다

"스킬 본체에 프로젝트 고유값을 작성하지 않는다"라는 원칙을 철저히 지킴으로써, 스킬은 포터블한 자산 (Portable Asset) 이 됩니다.

글로벌 설정을 한 번 작성해 두면, 새 프로젝트에서는 프로젝트 고유값만 추가하면 됩니다. 복사 붙여넣기 식의 이식 작업이 사라지고, 버그 수정은 모든 프로젝트에 자동으로 파급됩니다.

수치로 말하자면, 5개 프로젝트에서 70개 이상의 스킬을 공유하고 있는 지금, 새로운 프로젝트에 설정을 추가하는 것은 skill-config.json에 5~10줄을 쓰는 것만으로 완료됩니다.

원문 기사

이 기사는 Skills를 "어떤 프로젝트에서도 동작하는" 포터블 자산으로 만드는 설계 의 핵심 부분을 재구성한 것입니다. 원문 기사에서는 다음과 같은 내용을 더 다루고 있습니다:

  • 외부 스킬 (skills.sh 등)의 심링크 (Symlink) 통합과 .gitignore 자동 관리 구현
  • 레거시 폴백 (Legacy Fallback): 구형 형식의 설정 파일을 일제히 이전하지 않고 계속 작동시키는 메커니즘
  • 5개 프로젝트 공유 설정의 전체 모습 (글로벌 vs 프로젝트 고유의 실제 구분)

이 기사는 playpark Blog로부터 전재되었습니다.

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0