검색/풀스택 모노레포에서 공유 패키지를 망가뜨리지 않고 코드를 고친다

풀스택 모노레포에서 공유 패키지를 망가뜨리지 않고 코드를 고친다

앱과 공유 패키지가 함께 있는 모노레포를 AI가 고칠 때 쓴다. 변경을 알맞은 모듈에 두고, 생성되는 번들을 다시 빌드하고, 조직별 데이터를 섞지 않으며, 테스트 결과를 붙인 풀 리퀘스트로 넘긴다.

이 AI 규칙을 사용하려면
## 각 변경을 그 일을 맡은 모듈에 둔다 편집하기 전에 workspace를 파악한다. 제품 application, 재사용 가능한 UI package, 공유 business logic, data schema와 migration, type package, config preset, documentation, deployment asset, test harness를 구분한다. 기능별 동작은 해당 기능을 소유한 application 또는 feature module에 둔다. 재사용 가능한 domain logic과 shared type은 사용자가 명확한 package에 둔다. infrastructure와 deployment 부속 자료는 runtime source와 분리한다. unit test는 검증 대상인 안정된 logic과 같은 위치에 두고, browser flow specification은 application의 end-to-end 영역에 둔다. package 변경을 피하려는 이유만으로 shared logic을 application 내부에 복제해서는 안 된다. dependency 방향을 따르고 shared package가 변경되면 모든 downstream 사용자를 검증한다. ## 미리 빌드된 패키지를 다시 빌드하고 오래된 캐시를 피한다 사전에 compile되어 application의 public asset으로 복사되는 package를 식별한다. 실행 중인 application은 package의 source가 아니라 distribution output을 import할 수 있으므로, source만 편집하면 변경 사항이 관찰되지 않을 수 있다. 사전 compile된 package 또는 해당 bundle에 영향을 주는 dependency를 변경한 뒤에는 다음을 수행한다. 1. 해당 package에서 알려진 distribution output과 복사된 artifact만 제거한다. 2. 영향을 받는 dependency graph에서는 build system cache를 우회한다. 3. 공식 workspace task를 통해 package를 다시 build한다. 4. browser cache를 새로 고치거나 비활성화한다. 5. 다시 build한 asset이 여전히 로드되지 않을 때만 development server를 재시작한다. 복사된 bundle이나 생성된 artifact를 절대 직접 편집하지 않는다. cache 무효화는 검증 절차의 일부로 취급하며, source 변경이 실패했다는 증거로 취급하지 않는다. ## 공유 린트와 타입 규칙을 따른다 local override 대신 repository의 공유 lint, formatting, compiler preset을 사용한다. 기존의 indentation, line width, quote, semicolon, import group 규칙을 따른다. component와 component folder의 이름을 일관되게 정하고, function과 variable에는 lower camel case를 사용하며, 진정한 constant에만 upper snake case를 사용한다. resolution과 import 순서가 안정적으로 유지되도록 mock을 지정된 mock 위치에 둔다. 명확성이 유지되는 경우 inferred type을 우선하고 shared domain type을 사용하며, type이 없는 우회 수단은 피한다. component props는 immutable한 것으로 취급한다. component의 역할을 집중시키고 effect의 범위를 좁게 유지하며 hook dependency를 올바르게 관리한다. memoization은 습관이 아니라 측정된 필요에 근거해 사용한다. ## 서버 로직을 나누고 캐시 키를 하나의 팩토리로 만든다 route composition, service logic, feature module을 서로 구분한다. server action은 service를 호출하고 일관된 성공 또는 error envelope를 반환해야 한다. context provider는 provider 밖에서 사용될 때 명확하게 실패해야 하며, 변경 가능한 reference가 아닌 안정된 snapshot을 사용해 effect를 정리해야 한다. 한 request 안의 중복 작업에는 request-scoped memoization을 사용한다. request를 넘나드는 고비용 작업에는 승인된 shared cache abstraction 또는 명시적인 distributed cache를 사용한다. repository가 거부한 불안정한 framework cache API를 도입해서는 안 된다. 모든 cache key는 정식 key factory를 통해 생성한다. user, tenant, environment, locale 또는 feature mode가 호환되지 않는 entry를 공유하지 않도록 결과를 바꿀 수 있는 모든 차원을 key에 포함해야 한다. ## 화면 문구는 번역을 거치고, 시간대를 언어와 따로 다룬다 user에게 표시되는 모든 text는 translation function을 거치게 하고, 기존의 소문자 dot 구분 key 규칙을 따른다. 하나의 source locale file만 수동으로 작성하는 유일한 번역 source로 유지한다. 생성된 locale file을 절대 직접 편집하지 않는다. source text를 변경한 뒤에는 localization generator와 key validator를 실행한다. user에게 표시되는 날짜와 시간은 shared helper를 통해 rendering한다. 표시 locale은 application language의 source of truth에서 가져오며, 암묵적인 browser default에서 가져와서는 안 된다. locale과 time zone은 서로 독립적으로 취급한다. locale은 formatting을 제어하고 time zone은 표현되는 시점을 제어한다. locale에서 time zone을 추론해서는 안 된다. 제품이 time zone의 source of truth를 정의하지 않는 한 기존 stored value의 의미를 유지한다. storage, API, export, integration, log에는 명시적인 UTC 또는 ISO 표현과 같이 안정적이고 현지화되지 않은 형식을 사용한다. ## 조직별 데이터를 섞지 않고 쿼리를 빠르게 유지한다 모든 data operation의 범위를 organization, environment 등 repository의 tenant 경계로 제한한다. inactive 또는 삭제된 record가 다른 query 경로를 통해 다시 나타나지 않도록 soft-deletion filter를 일관되게 적용한다. 개수를 구할 때는 offset 방식 pagination argument 대신 조건이 적용된 count query를 사용한다. 서로 독립적인 count query와 data query는 동시에 실행한다. 대규모 dataset에는 cursor pagination을 우선하고, 시간으로 filtering할 때는 index가 설정된 ownership field를 포함한다. schema와 migration 변경은 영향을 받는 모든 query 경로와 함께 검토한다. data isolation은 여러 영역에 걸친 불변 조건이다. tenant predicate 누락은 사소한 performance 문제가 아니라 잠재적인 data 경계 장애다. ## 커버리지 숫자가 아니라 사용자 위험으로 테스트를 고른다 coverage 비율이 아니라 신뢰도를 최적화한다. - 중요한 user journey, UI regression, component 동작에는 browser end-to-end test를 사용한다. - validator, transformer, evaluator, 계산, edge case와 같은 안정된 pure logic에는 unit test를 사용한다. - release 준비 상태는 staging에서 수동으로 확인하고, 중요한 문제에는 bug 수정, backport, 재test의 순환 절차를 적용한다. browser test는 범위를 집중시키고 적절하게 이름을 붙이며, 느린 suite를 명확히 표시한다. network와 storage 경계는 shared helper를 통해 mock한다. coverage를 높이기 위해서만 fragile component snapshot, markup assertion, 구현 세부 사항 test, timing hack, 지나치게 큰 flow, 낮은 가치의 test를 추가해서는 안 된다. 검토 전에 표준 unit suite를 실행하고, 중요한 flow가 변경되면 coverage를 확인하며, journey risk에는 직접 영향을 받는 end-to-end suite를 실행한다. ## 문서와 컴포넌트 예시를 최신으로 유지한다 documentation system의 file contract, metadata field, heading rule, 승인된 step 또는 callout component를 따른다. 제한된 edition의 content에는 새로운 badge나 문구를 만들지 말고 표준 notice를 표시한다. component example은 component 옆에 두고 public entry point를 통해 import한다. control은 repository의 표준 group으로 구성한다. default state, 지원되는 경우 disabled state와 icon state, 모든 variant, 의미 있는 edge case를 포함한다. UI를 build하거나 검토하기 전에 영향을 받는 화면의 design context를 불러온다. shared design guidance는 code convention을 보완하며 token, component, motion, visual quality의 source of truth로 유지된다. ## CI 워크플로에 필요한 권한만 준다 automation token에는 각 workflow에 필요한 permission만 부여한다. hosted Linux job은 승인된 runner hardening step으로 시작하여 project code가 실행되기 전에 network와 process 동작을 제한한다. dependency lock을 정식 기준으로 유지하고 repository가 선택한 package manager를 통해 workspace command를 실행한다. task graph, shared environment 또는 cache metadata를 우회하는 일회성 install command나 build command는 피한다. workflow를 변경할 때는 event trigger, permission, secret 노출, cache key, artifact, failure propagation을 검증한다. downstream step의 성공이 실패한 prerequisite를 숨겨서는 안 된다. ## 테스트 결과와 스크린샷을 붙여 풀 리퀘스트를 넘긴다 완료하기 전에 dead code와 사용하지 않는 import를 제거하고, 정식 lint command를 실행하며, 변경을 입증하는 최소 test set과 직접 영향을 받는 더 넓은 flow를 실행한다. commit의 범위를 집중시키고 repository의 commit convention을 따른다. pull request에는 문제를 설명하고 해결책을 요약하며, 근거가 되는 issue 또는 specification을 연결하고, migration 또는 environment 변경을 명시해야 한다. UI 작업에는 시각적 근거를 첨부하고 관련 lint, test, build, migration command의 결과를 기록한다. 검토자는 diff만으로 작업을 재구성하지 않고도 무엇이 변경되었는지, 왜 해당 module에 속하는지, 생성된 결과물을 어떻게 갱신했는지, 어떤 risk를 검증했는지 판단할 수 있어야 한다.