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건 중 15건이 전파되지 않았고
원인이 셋으로 갈립니다.
47%7 / 15
33%5 / 15
20%3 / 15
PR이 리뷰에서 멈춤
FE PR #50이 2026-07-01부터 열린 채로 29일. 미러 커밋은 BE와 같은 날 제대로 작성돼 있었고 머지만 안 됐습니다.
→ 정체 PR 알림
편집자가 상대 레포를 안 만짐
전파가 안 된 편집은 전부 FE .claude/를 건드리지 않는 사람이 작성했습니다.
→ 단일 출처로 합치기
한쪽만 고치고 끝
FE 44a4dbf가 문서 3개를 고쳤지만 BE에는 안 넘어갔습니다.
→ 드리프트 검사
공유 방식을 정해서 없어지는 것은 가운데 33%입니다. 나머지 절반 이상은 리뷰 운영과 검사 장치의
문제라 어느 방식을 골라도 남습니다. 그래서 6절의 권고는 방식 선택과 나머지 둘을 나눠서 다룹니다.
03
이미 갖춰져 있는 것
방식을 고르기 전에 알아야 할 두 가지입니다. 새로 만들 필요가 없는 부분이 꽤 있습니다.
PR 게이트가 이미 있다
사내 Jenkins가 모든 PR에 build status(ismp-be-pr-test·ismp-fe-pr-test)를
올립니다. Bitbucket 머지 체크는 외부에서 올린 status도 그대로 받으니
드리프트 검사는 기존 Jenkins 잡에 몇 줄만 더하면 됩니다. 새 CI도 자격증명도 필요하지 않습니다.
Bitbucket Pipelines는 쓰지 않습니다. 절차는 PR-GATES-SETUP.md에 있습니다.
사내에 모노레포 선례가 있습니다
같은 워크스페이스의 frontend-monorepo가 2023-02-02부터 3년 반째 돌고 있습니다.
Yarn PnP 워크스페이스에 packages/common-* 8개와 고객사 앱 11개가 들어 있고
태그는 samsungcard-20260120, shinhanlife_20260212 처럼
컴포넌트 접두사에 날짜를 붙이는 방식입니다.
ISMP가 모노레포로 갈 때 가장 걸리는 태그 충돌을 사내에서 이미 이 방식으로 해결했습니다.
다만 선례가 닿는 범위는 코드 구조와 릴리스 태깅까지입니다.
04
방식 다섯 개
샌드박스 git 저장소, 실제 Claude Code 바이너리, 실제 Bitbucket API로 다섯 개를 모두 돌려봤습니다.
아래 단점은 문헌 인용이 아니라 재현된 결과입니다.
칩 읽는 법
칩이 두 개씩 붙어 있습니다. 앞은 권고 여부(추천 · 조건부 · 비추천),
뒤는 착수 조건입니다.
추천은 3안과 5안 둘이고 차이는 지금 시작할 수 있는지입니다.
3안은 오늘 착수해도 되고 되돌릴 수 있습니다. 5안은 되돌리기 어려워서 팀이 먼저
“FE/BE 릴리스를 계속 따로 낼 것인가”에 답해야 합니다.
두 안은 배타적이지 않습니다 — 3안으로 지금 막고 5안은 공유 세션에서 결정하면 됩니다.
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.json에 version을 적으면 커밋을 올려도 갱신이 조용히 막힙니다. 사내용은 아예 비워두는 게 맞습니다
- Claude 전용이라 다른 에이전트를 쓰는 사람에게는 의미가 없습니다
문서 절반을 덮지 못해서 단독 해법이 되기 어렵습니다
5 · 모노레포 통합
추천팀 결정 먼저
두 레포를 하나로 합치고 frontend/·backend/ 하위 디렉터리로 둡니다. 동기화 문제가 사라집니다.
항상 로드
해당 폴더 파일 접근 시 로드
루트에서만 로드
- aproj/
- ├─ CLAUDE.md
- 항상 공용 코어 규칙
- ├─ .claude/
- │ ├─ commit-convention.md
- 커밋·브랜치·changelog 규칙 — 사본 없이 하나
- │ ├─ branch-convention.md
- │ ├─ changelog-format.md
- │ ├─ team-roster.md
- 지금 미머지 브랜치에만 있는 파일
- │ ├─ skills/
- 항상 공용 스킬 —
branch-start, pr-prep 등
- │ ├─ settings.json
- 루트만 hook 배선을 여기 한곳에 모음
- │ └─ hooks/
- 3개 — 경로로 스스로 걸러서 BE 파일에만 발동
- ├─ doc/
- ADR·아키텍처·코딩 컨벤션 — 지금은 BE만 볼 수 있음
- ├─ frontend/
- │ ├─ CLAUDE.md
- 접근 시 FE 전용 규칙 — 스택·명령·주의점
- │ ├─ .claude/skills/
- 접근 시 FE 전용 스킬 →
frontend:<이름>
- │ ├─ CHANGELOG.md · changelog.d/
- 컴포넌트별로 따로 유지
- │ └─ src/ …
- └─ backend/
- ├─ CLAUDE.md
- 접근 시 BE 전용 규칙 — 레이어·jOOQ·Liquibase
- ├─ .claude/skills/
- 접근 시 BE 전용 스킬 →
backend:<이름>
- ├─ CHANGELOG.md · changelog.d/
- 컴포넌트별로 따로 유지
- └─ src/ismp/ …
같은 이름의 스킬이 루트와 하위에 다 있으면 둘이 함께 나타나고 지금 고치는 파일이 있는 쪽을 씁니다.
backend/ 파일을 만지면 backend:branch-start가 루트 branch-start보다 우선합니다.
릴리스 태그는 fe-20260730·be-20260730처럼 컴포넌트 접두사를 붙입니다.
장점
- 파일이 하나뿐이라 동기화 문제가 없어집니다
- 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에 합치는 편이 안전하지만 그러면 빨간 표시 하나에 원인이 둘 섞입니다
INPROGRESS도 FAILED만큼 막습니다. 잡이 중간에 죽으면 가리킬 실패도 없이 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
제안하는 순서
되돌릴 수 있는 것부터 하고 한 방향 결정은 팀 합의 뒤에 합니다.
-
요금제 확인, CLAUDE.md 범위 확정, 데이터 손실 공지
검사의 차단력이 요금제에 걸립니다. BE의 .gitignore:36 등재를 유지할지, 즉 CLAUDE.md를 공용 자산으로 볼지도 여기서 정합니다. 그리고 오래된 체크아웃에서 git pull하면 개인 메모가 경고 없이 덮어써집니다. gitignore된 파일에는 git의 덮어쓰기 보호가 걸리지 않아서 충돌도 경고도 없이 사라지니, 팀에 먼저 알려야 합니다.
관리자 · 10분
-
정체 PR 알림
공백의 47%를 만든 원인입니다. FE PR #50이 29일째 열려 있는 걸 아무도 몰랐다는 게 문제의 본질입니다.
1일 · 공백 47% 해소
-
기존 Jenkins 잡에 드리프트 검사 추가
커밋 핀과 체크섬 매니페스트를 씁니다. 지금 벌어진 드리프트는 동결하고 새로 생기는 이탈만 실패시킵니다. 첫날부터 전원이 막히지 않게 하려면 이 방식이어야 합니다.
1~2일 · 공백 20% 추가 해소
-
8개 파일 차이 정리와 자산 분류
어느 방식을 택해도 필요한 작업입니다. 완전 공용 / 공통 + 레포별 / 레포 전용으로 나눕니다. 파일에 박힌 레포 이름은 양쪽 합쳐 29곳이고 전부 제목 줄 자기참조라, 이 한 줄만 변수로 빼면 됩니다. FE가 뒤처진 ISMPDEV 키 안내와 BE에만 있는 한글 깨짐 검사도 이때 맞춥니다.
방식과 무관한 선행 작업
-
공유 세션 — 모노레포 정식 검토
물어야 할 것은 하나로 좁혀집니다. FE/BE 릴리스를 계속 따로 낼 것인가. 따로 유지가 확고하면 3안으로 굳히고 어차피 같이 나가는 경우가 많으면 모노레포 쪽 이점이 커집니다. 실제 쟁점은 되돌릴 수 없다는 점과 미배포 커밋 132건·174건입니다.
티켓 완료 조건