기술개선 검토 ISMPDEV-108 · Epic ISMPDEV-69

BE/FE 공통 문서·스킬 공통화 방안 검토

두 레포에 같은 규칙 문서와 스킬이 복사돼 있고 한쪽을 고쳐도 다른 쪽에 넘어가지 않습니다. 실패 39건을 전수 조사해 원인을 나누고, 후보 방식 다섯 개를 실제로 돌려 비교했습니다.

작성 2026-07-30 기준 ref origin/dev 조사한 편집 이벤트 39건 실행 검증 6건
01

무엇이 문제인가#

티켓 배경을 처음 보는 분을 위한 설명입니다.

ISMP는 aprojfrontend(Next.js)와 aprojbackend(Spring) 두 개의 독립된 Bitbucket 레포로 개발합니다. 모노레포가 아니라 각각 별도 레포이고 개발에 참여하는 사람은 6명입니다.

양쪽 .claude/ 아래에는 브랜치·커밋·changelog 규칙 문서와 Claude Code 스킬이 들어 있습니다. 이 중 파일 8개가 두 레포에 같은 이름으로 복사돼 있습니다. 규칙은 하나인데 사본이 둘이라 한쪽을 고치면 다른 쪽도 똑같이 고쳐야 합니다. 그 규칙은 문서 안에 주석으로만 적혀 있습니다.

복사 자체가 문제는 아닙니다. 레포마다 따로 발전하는 게 맞는 스킬도 있습니다. 다만 실제로 양쪽이 같이 써야 하는 자산은 전파가 빠지면서 사고가 반복됐습니다. 문서·스킬 말고도 도메인 용어집, 기획 결정사항, .claude/team-roster.md 처럼 공용으로 봐야 할 자산이 더 있습니다.

지금 상태내용
양쪽에 같은 이름으로 있는 파일8개 — 전부 내용이 다름
BE에만 있는 자산hook 3개, settings.json, convention-sync·liquibase-changeset-writer·release-finalize 스킬, doc/ 18개 파일(ADR 6건 포함)
FE에만 있는 자산branch-start의 첫 push 안내 한 줄이 전부
team-roster.md양쪽 origin/dev에 없음. BE 미머지 브랜치에만 존재

FE가 가진 자산은 BE의 부분집합입니다. 흐름은 거의 전부 BE에서 FE 방향이라 FE 쪽이 뒤처집니다. FE에 없는 doc/ 트리에는 아키텍처 규칙과 코딩 컨벤션, 결정 기록이 들어 있어서 FE 개발자는 이 내용을 볼 수 없습니다.

02

원인은 하나가 아니라 셋#

8개 파일의 편집 이력을 origin/dev 계보로 전수 확인했습니다. 39건 중 24건만 넘어갔고 못 넘어간 15건은 원인이 셋으로 갈립니다.

PR이 리뷰에서 멈춤

FE PR #50이 2026-07-01부터 열린 채로 29일. 미러 커밋은 BE와 같은 날, 한 건은 16초 차이로 제대로 작성돼 있었습니다. 머지만 안 됐습니다.

→ 정체 PR 알림, 리뷰 SLA

편집자가 상대 레포를 안 만짐

못 넘어간 편집은 전부 FE .claude/를 건드리지 않는 사람이 작성했습니다. 양쪽을 다 만지는 사람은 한 명뿐입니다.

→ 단일 출처로 합치기. 이것만이 근본 대책

한쪽만 고치고 끝

FE 44a4dbf가 문서 3개를 고쳤지만 BE에는 안 넘어갔습니다. 8일 경과.

→ PR을 실패시키는 드리프트 검사

셋을 한 번에 덮는 대책은 없습니다. 특히 가장 큰 47%는 미러링 규율을 강화해도 줄지 않습니다. 규율은 이미 지켜졌고 리뷰가 멈춰 있었을 뿐입니다.

측정값결과
편집 이벤트 (2026-06-22 이후, mainline)39
전파 성공 (같은 날 19 + 다음 날 5)24
전파 실패, 현재까지 미해소15 (38.5%)
2일 넘게 지연됐다가 전파된 사례0 — 같은 날 아니면 영원히 안 됨
미러 동기화를 목적으로 만든 커밋지금까지 0건
규칙 문장 자체가 깨져 있음

미러를 지시하는 문장이 BE branch-convention.mdbranch-start/SKILL.md에서 “백엔드에 동일 미러가 있다”로 적혀 있습니다. BE 파일이 자기 자신을 미러라고 가리키는 셈입니다. 파일이 만들어진 날부터 35일째 그대로입니다. 같은 문장이 FE는 8개 중 6개, BE는 8개 중 4개 파일에만 있습니다.

따로 처리가 필요한 건

CLAUDE.md를 두 레포가 다르게 취급합니다. FE는 정상 추적하는데 BE는 origin/dev에 팀 파일이 있으면서 .gitignore:36에도 등재돼 있고 로컬은 개인 메모입니다. BE 개발자가 팀이 합의한 규칙을 못 읽고 있을 수 있습니다.

더 급한 쪽은 데이터 손실입니다. 로컬 체크아웃이 CLAUDE.md가 추적되기 전 시점이면 git pull 한 번에 개인 메모가 경고 없이 덮어써집니다. gitignore된 파일에는 git의 덮어쓰기 보호가 걸리지 않아서 샌드박스에서 재현했을 때 충돌도 경고도 없이 조용히 사라졌습니다. 방식 결정과 별개로 팀에 먼저 알려야 합니다.

03

이미 갖춰져 있는 것#

방식을 고르기 전에 알아야 할 두 가지입니다. 새로 만들 필요가 없는 부분이 꽤 있습니다.

PR 게이트는 이미 돌고 있습니다

Bitbucket Pipelines는 워크스페이스 109개 레포 중 표본 10개 어디에도 설정된 적이 없습니다. 대신 사내 Jenkins가 ismp-be-pr-testismp-fe-pr-test build status를 모든 PR에 올립니다. 최근 머지된 PR 10건 전부 SUCCESSFUL이고 가장 최근 건은 오늘 올라왔습니다. 절차는 PR-GATES-SETUP.md에 정리돼 있습니다.

Bitbucket의 “Minimum successful builds” 머지 체크는 외부에서 올린 status도 그대로 받습니다. 드리프트 검사는 기존 Jenkins 잡 셸에 몇 줄 추가하면 됩니다. 새 CI도, 새 자격증명도 필요하지 않습니다. 참고로 팀은 과거 Pipelines PR 체크를 도입했다가 Jenkins로 옮기면서 제거했습니다.

사내에 모노레포 선례가 있습니다

같은 워크스페이스의 프론트엔드 모노레포가 2023-02-02부터 3년 반째 돌고 있습니다. Yarn PnP 워크스페이스에 packages/common-* 8개와 고객사 앱 11개가 들어 있고 태그는 <고객사>-20260120, <고객사>_20260212 처럼 컴포넌트 접두사에 날짜를 붙이는 방식입니다.

ISMP가 모노레포로 갈 때 가장 걸리는 태그 충돌을 사내에서 이미 이 방식으로 해결했습니다. 다만 이 레포에는 .claude 자산이 없어서 선례는 코드 구조와 릴리스 태깅까지만 해당합니다. 워크스페이스에서 Claude Code 자산을 가진 프로젝트는 표본 기준 ISMP뿐입니다.

04

방식 다섯 개#

샌드박스 git 저장소, 실제 Claude Code 바이너리, 실제 Bitbucket API로 다섯 개를 모두 돌려봤습니다. 아래 단점은 문헌 인용이 아니라 재현된 결과입니다.

1 · git submodule

제외 권고

공용 레포를 특정 커밋에 고정해 마운트합니다. 파일이 아니라 포인터를 커밋합니다.

장점
  • 사본이 물리적으로 없는 구조적 단일 출처
  • 어느 버전을 쓰는지 SHA로 고정됨
단점 — 6가지 모두 재현
  • 평범한 git clone빈 디렉터리에 exit 0, git status도 깨끗. 스킬이 아무 증상 없이 사라집니다
  • git pull은 내용을 갱신하지 않습니다. 신호는 M <path> 한 줄
  • git add -A 한 번이 공유 자산을 이전 버전으로 되돌립니다. PR diff는 Subproject commit 두 줄이라 무엇이 사라졌는지 안 보입니다
  • 완화책 submodule.recurse.git/config에만 들어갑니다. git이 읽는 설정 파일 중 워킹트리에 있는 건 없어서 레포로 배포할 방법이 없고 6명이 각자 한 번씩 설정해야 합니다
  • 소비 레포는 .gitmodules와 포인터만 추적합니다. git grep에 안 걸리고 Bitbucket UI는 링크 렌더링도 불안정합니다(BCLOUD-21720 미해결)
  • submodule 안은 detached HEAD라 그 자리에서 고친 내용이 다음 update에 날아갈 수 있습니다

사람이 Bitbucket에서 읽어야 한다는 요건 자체를 만족하지 못합니다

2 · git subtree

보완 필요

공용 레포 내용을 실제 파일로 병합해 커밋합니다. 팀원에게는 그냥 파일로 보입니다.

장점
  • 평범한 clone이 파일을 그대로 받습니다. 팀원이 익힐 절차가 없습니다
  • submodule의 무증상 계열 문제가 전부 사라집니다
  • Bitbucket UI와 git grep 정상 동작
단점
  • 가져온 사본을 직접 고쳐도 조용히 성공하고 상류는 모릅니다
  • 이탈을 알려주는 git subtree status 같은 명령이 없습니다
  • 상류 커밋은 squash 커밋 트레일러에만 남습니다. git log --grep으로 꺼낼 수는 있지만 디스크에 파일이 없어서 CI가 읽을 게 없습니다
  • 새로 clone하면 remote 설정이 사라집니다

읽기 쪽은 해결하고 쓰기 쪽은 남깁니다. 검사를 얹으면 3안과 같아집니다 · 핀을 파일로 남기려면 git-subrepo

3 · 공용 레포 + 복사 + 드리프트 검사

즉시 채택 권고

공용 레포를 단일 출처로 두고 각 레포에 복사합니다. 커밋 핀과 파일별 체크섬을 기록하고 CI가 검사합니다.

장점
  • 읽기·쓰기 양쪽 실패를 다 막은 유일한 방식. 변조는 파일명까지 지목하고 뒤처짐은 몇 커밋인지까지 보고합니다
  • 파일이 평범하게 추적되니 UI·grep·CI가 모두 정상
  • 붙일 CI가 이미 있습니다. 기존 Jenkins 잡에 몇 줄
  • 점진적이고 되돌릴 수 있습니다
단점
  • 검사를 직접 만들어야 합니다. git이 제공하지 않습니다
  • 감지 기반이라 사본을 직접 고치는 것 자체는 막지 못합니다
  • 공용 수정 1건마다 레포별 반영 PR이 필요합니다
  • 차단력이 요금제에 걸립니다. 5절 참고

매니페스트는 자기 해시를 제외해야 합니다. 기존 드리프트는 동결하고 신규만 실패시킵니다 — 팀이 이미 쓰는 ArchUnit FreezingArchRule과 같은 원리. 실패 메시지에는 고치는 명령을 함께 출력합니다

4 · Claude Code 플러그인 마켓플레이스

스킬 한정

공용 스킬과 hook을 플러그인으로 묶어 사설 마켓플레이스에서 배포합니다.

장점
  • 사설 Bitbucket에서 동작합니다. URL 끝에 .git을 붙이면 개발자의 기존 git 자격증명으로 클론됩니다
  • --scope project가 커밋 가능한 .claude/settings.json에 기록해 팀에 자동 배포됩니다
  • 유일하게 소비 레포 PR 없이 갱신이 퍼집니다
  • 버전 고정이 됩니다. extraKnownMarketplaces의 git 소스에 ref로 태그를 지정하고 릴리스는 태그를 옮기면 됩니다
단점
  • Claude Code 자산만 배포합니다. 컨벤션 문서·용어집·로스터·ADR은 사람이 Bitbucket에서 읽어야 하는데 플러그인 안에서는 안 보입니다
  • 백그라운드 자동 갱신이 간헐적으로 실패합니다. 백그라운드 git pull은 credential helper를 끄기 때문에 사설 레포 인증이 안 됩니다
  • plugin.jsonversion을 적으면 커밋을 올려도 갱신이 조용히 막힙니다. 사내용은 아예 비워두는 게 맞습니다
  • Claude 전용이라 다른 에이전트를 쓰는 사람에게는 의미가 없습니다

문서 절반을 덮지 못해서 단독 해법이 되기 어렵습니다

5 · 모노레포 통합

정식 검토 권고

두 레포를 하나로 합치고 frontend/·backend/ 하위 디렉터리로 둡니다. 동기화 문제가 사라집니다.

장점
  • 파일이 하나뿐이라 동기화 문제가 없어집니다
  • Claude Code가 공통 규칙과 컴포넌트별 규칙을 기본으로 지원합니다. 루트 스킬은 항상 보이고 backend/ 파일을 건드리면 backend: 접두사가 붙은 스킬이 나타나 같은 이름이어도 더 구체적인 쪽을 씁니다
  • 세션 컨텍스트가 커지지 않습니다. FE 작업 중에는 BE 규칙을 아예 불러오지 않습니다
  • BE에만 있던 convention-sync·ADR·hook이 FE에도 열립니다
  • FE/BE 계약 변경을 PR 하나로 처리합니다
  • 태그 충돌이 3건뿐이고 전부 2025-11 초기 설치 때입니다. 이후 8개월간 0건이고 접두사 태그 선례도 사내에 있습니다
  • 레포가 작습니다. 합쳐서 23MB, 같은 프로젝트(APJ), 병합할 CI 설정도 없습니다
단점
  • 되돌리기가 어렵습니다. 사실상 한 방향 결정입니다
  • main의 의미가 흐려집니다. 한쪽을 릴리스하면 상대의 미배포 커밋 132건·174건이 같이 올라갑니다. 경로별 릴리스 도구가 현재 스킬에 없습니다
  • 태그 네임스페이스에 잡음이 쌓입니다. 사내 선례에서도 113개 중 상당수가 test·backup 태그입니다
  • 경로별 쓰기 권한이 없습니다. .bitbucket/CODEOWNERS로 담당자를 추천할 수는 있지만 승인을 요구하지는 못합니다
  • dev 브랜치와 PR 목록, 로그가 섞입니다
  • hook은 루트 settings.json에만 로드되므로 한곳에 모아야 합니다. 다만 이 팀 hook은 이미 경로로 스스로 필터링하므로 command 경로 3줄 수정이면 끝입니다
  • 합치기만 하고 통합을 안 하면 비용만 내고 문제는 남습니다

먼저 답할 질문: FE/BE 릴리스를 계속 따로 낼 것인가

그 밖에 확인한 방법

  • Renovate + Vendir. GitHub Actions 없이 Bitbucket Cloud에서 돌아가는 드리프트 검사입니다. vendir.yml에 어느 레포의 어느 ref에서 무엇을 가져올지 적어두면 Renovate가 상류 변경을 감지해 소비 레포에 PR을 올립니다. 파일을 조용히 덮지 않고 PR로 만들어주는 점이 장점입니다
  • 공유 체크아웃을 --add-dir로 붙이기. 사본을 만들지 않고 옆 디렉터리의 규칙을 읽는 방법입니다. 공식 문서에만 있고 사용 사례를 찾지 못해, 스킬과 hook까지 실리는지는 확인이 필요합니다
  • Confluence를 원본으로 두기. 용어집·로스터·기획 결정사항은 이미 Confluence에 있으니 그대로 두고 MCP로 읽는 편이 낫습니다. 반대로 커밋·브랜치·changelog 규칙은 파일로 둬야 합니다. CI가 오프라인에서 읽어야 하고 PR에서 함께 리뷰해야 하고 git grep으로 찾는 게 실제 사용 방식이기 때문입니다
05

시작 전에 확인할 것#

추측하지 않고 남긴 항목입니다.

요금제 — 관리자 확인 필요

머지 체크로 실제 차단하려면 Premium이 필요합니다. Free와 Standard는 동작이 같아서 빨간 표시만 뜨고 그대로 머지됩니다. 둘 중 어느 쪽인지는 중요하지 않습니다. Premium인지 아닌지만 확인하면 됩니다. Workspace settings의 Billing에서 볼 수 있습니다. Premium은 1인당 월 $7.25이라 6명이면 연 $522 정도입니다.

Premium이어도 완전히 못 막는 것은 아닙니다. 일반 개발자는 막히지만 레포·워크스페이스 관리자는 제약을 수정하거나 지울 수 있습니다. 6명 팀에서 대부분이 관리자라면 Premium이 사주는 것은 차단이 아니라 속도 제한과 감사 기록입니다.

Premium이 아니면 검사는 경고로만 뜹니다. Forge 앱이나 마켓플레이스 앱으로 우회할 방법도 없습니다. 전부 같은 머지 체크를 거쳐서 같은 제약을 물려받습니다.

드리프트 검사를 붙일 때 걸릴 것들

  • 머지 체크는 마지막 커밋의 모든 status를 봅니다. FAILED 하나가 남아 있으면 뒤에 성공이 와도 통과하지 못합니다. 같은 key로 덮어써야 하는데 팀이 쓰는 ismp-be-pr-test·ismp-fe-pr-test는 이미 안정적인 key라 이 함정은 피해 있습니다
  • status가 없으면 통과가 아니라 실패입니다. 새 key를 만들면 그 status가 항상 올라와야 하고 웹훅이 한 번 빠지면 PR이 막힙니다. 기존 key에 합치는 편이 안전하지만 그러면 빨간 표시 하나에 원인이 둘 섞입니다
  • INPROGRESSFAILED만큼 막습니다. 잡이 중간에 죽으면 가리킬 실패도 없이 PR이 멈추니 종료 시 반드시 최종 상태를 쓰게 해야 합니다
  • PR 코멘트를 올리려면 pullrequest:write가 따로 필요합니다. 현재 문서에 적힌 repository:write에는 PR 권한이 포함되지 않습니다

확정하지 않은 것

  • 전파 성패가 작성자에 따라 갈린다는 인과 — 39건 표본에서 나온 추론이라 가설로 둡니다. 작성자 분포 자체는 확인됐습니다
  • 하위 디렉터리 스킬이 지연 로드되는 동작 — 빌드 2.1.220에서 확인한 결과입니다. Claude Code를 올릴 때 다시 봐야 합니다
  • BE pr-prep에 있는 한글 깨짐 검사를 FE도 필요로 하는지 — 양쪽 다 같은 API로 한글 PR 본문을 올리니 필요해 보이지만 FE에서 실제로 깨진 기록은 찾지 못했습니다
  • ISMP가 조직에서 유일한 Claude Code 사용처인지 — 표본 기준입니다. 109개를 전부 보지는 않았습니다
06

제안하는 순서#

되돌릴 수 있는 것부터 하고 한 방향 결정은 팀 합의 뒤에 합니다.

  1. 요금제 확인, CLAUDE.md 범위 확정, 데이터 손실 공지

    검사의 차단력이 요금제에 걸립니다. BE의 .gitignore:36 등재를 유지할지, 즉 CLAUDE.md를 공용 자산으로 볼지도 여기서 정합니다. 오래된 체크아웃에서 git pull하면 개인 메모가 사라지는 문제는 먼저 팀에 알립니다.

    관리자 · 10분
  2. 정체 PR 알림

    공백의 47%를 만든 원인입니다. FE PR #50이 29일째 열려 있는 걸 아무도 몰랐다는 게 문제의 본질입니다.

    1일 · 공백 47% 해소
  3. 기존 Jenkins 잡에 드리프트 검사 추가

    커밋 핀과 체크섬 매니페스트를 씁니다. 지금 벌어진 드리프트는 동결하고 새로 생기는 이탈만 실패시킵니다. 첫날부터 전원이 막히지 않게 하려면 이 방식이어야 합니다.

    1~2일 · 공백 20% 추가 해소
  4. 8개 파일 차이 정리와 자산 분류

    어느 방식을 택해도 필요한 작업입니다. 완전 공용 / 공통 + 레포별 / 레포 전용으로 나눕니다. 파일에 박힌 레포 이름은 양쪽 합쳐 29곳이고 전부 제목 줄 자기참조라 이 한 줄만 변수로 빼면 됩니다. FE가 뒤처진 ISMPDEV 키 안내와 BE에만 있는 한글 깨짐 검사도 이때 맞춥니다.

    방식과 무관한 선행 작업
  5. 공유 세션 — 모노레포 정식 검토

    물어야 할 것은 하나로 좁혀집니다. FE/BE 릴리스를 계속 따로 낼 것인가. 따로 유지가 확고하면 3안으로 굳히고 어차피 같이 나가는 경우가 많으면 모노레포 쪽 이점이 커집니다. 실제 쟁점은 되돌릴 수 없다는 점과 미배포 커밋 132건·174건입니다.

    티켓 완료 조건