검색/테넌트와 에디션을 섞지 않고 멀티테넌트 자동화 플랫폼을 만든다

테넌트와 에디션을 섞지 않고 멀티테넌트 자동화 플랫폼을 만든다

커뮤니티·엔터프라이즈·호스팅 에디션이 있는 멀티테넌트 자동화 플랫폼의 코드를 작성할 때 쓴다. 쿼리를 올바른 프로젝트로 좁히고, 외부 HTTP 요청을 안전한 클라이언트로 보내 SSRF를 막고, 의존성을 사용하는 워크스페이스에 선언하며, 모든 에디션을 테스트한 뒤에 끝낸다.

이 AI 규칙을 사용하려면
## 테넌트 데이터를 분리하고 프리미엄 코드를 커뮤니티 에디션에 넣지 않는다 테넌트 범위, 제품 에디션, 기능 접근 권한, 브랜딩을 보안과 동작에 관한 계약으로 다룬다. ### 격리를 강제한다 - 플랫폼, 프로젝트, 사용자 수준에서 소유권을 명시적으로 모델링한다. - 모든 데이터베이스 쿼리의 범위를 올바른 프로젝트 또는 플랫폼 식별자로 제한한다. 호출자가 나중에 결과를 필터링할 것이라고 절대로 기대하지 않는다. - 모든 엔드포인트에 접근 제어 선언을 요구한다. - 보호된 동작보다 먼저 백엔드 권한 부여와 기능 미들웨어를 적용하고, 이용할 수 없는 유료 기능에는 표준 `402` 응답을 반환하며, 프런트엔드 가드와 쿼리 활성화에도 같은 제한을 반영한다. ### 에디션 간 의존 방향을 유지한다 - 프리미엄 또는 호스팅 에디션은 등록된 훅이나 인터페이스를 통해 커뮤니티 코어를 확장한다. - 프리미엄 전용 애플리케이션 코드를 커뮤니티 코드로 절대로 가져오지 않는다. - 커뮤니티 기본값에는 특정 브랜드를 적용하지 않고, 호스팅 환경에는 구성된 브랜딩을 적용하며, 자체 호스팅 엔터프라이즈 브랜딩은 적절한 요금제 기능으로 제한한다. - 고객에게 보이는 동작을 커뮤니티 자체 호스팅, 엔터프라이즈 자체 호스팅, 무료 호스팅, 유료 호스팅, 엔터프라이즈 호스팅이라는 5개 경로에서 검증한다. - 고객에게 보이는 모든 이름, 색상, 로고, 로그인 화면, 메시지는 제품 브랜드를 하드코딩하지 말고 구성된 외형에 따라 결정되게 한다. ## 의존성을 워크스페이스마다 선언하고 여러 서버가 함께 쓰는 데이터를 잠금으로 보호한다 의존성의 소유 주체와 동시 데이터 처리 방식을 명시하여 로컬에서의 성공이 프로덕션 배포에서도 재현되게 한다. ### 패키지 경계를 유지한다 - 공유 라이브러리는 가볍고 프레임워크에 독립적인 기반부터 무거운 애플리케이션 수준 패키지까지 순서대로 구성한다. - 재사용 가능한 통합 기능과 실행 엔진은 가벼운 패키지에만 의존하게 하고, 필요한 애플리케이션 동작은 좁은 프레임워크 계층을 통해 제공한다. - 각 런타임 의존성은 그것을 가져오는 워크스페이스에 선언하고, 각 테스트 또는 도구 의존성은 해당 워크스페이스의 개발 의존성에 선언한다. - 루트 매니페스트는 저장소 전체 도구 또는 실제 부트스트랩 요구 사항에만 사용한다. - 버전을 일관되게 고정하고 의존성을 변경한 뒤에는 표준 설치 도구로 잠금 파일을 갱신한다. ### 영속성과 동시성을 보호한다 - 객체 매퍼가 새 엔터티를 자동으로 발견하지 못하면 명시적으로 등록한다. - 부수 효과를 전용 모듈로 분리하고 변경 작업이 성공한 뒤 명시적으로 호출한다. - 여러 서버에서 작업할 때는 분산 잠금, 큐 중복 제거 또는 `skip-locked` 의미론을 갖춘 행 잠금을 사용한다. - 관리형 서비스에서 사용할 수 없는 데이터베이스 확장 기능을 피한다. - 외부에서 제공된 객체는 데이터베이스에 저장하기 전에 정제한다. ## any와 억지 타입 캐스트를 쓰지 않고 프로젝트 공용 헬퍼를 사용한다 예측 가능한 인터페이스, 데이터 흐름, 파일 구성, 공유 유틸리티를 사용한다. ### 타입 무결성을 보존한다 - `any`를 사용하지 않는다. 정확한 타입을 사용하거나 `unknown`을 타입 가드와 함께 사용한다. - 타입 캐스팅으로 값을 억지로 통과시키지 않는다. 원본 타입을 수정하거나, 검증을 추가하거나, 값의 타입을 좁힌다. - API를 사용하기 전에 라이브러리 문서를 확인하고 사용 중단으로 표시된 항목은 모두 교체한다. - 매개변수가 1개를 초과하는 모든 함수에는 구조 분해된 단일 객체 매개변수를 사용한다. - 함수 경계를 지나는 데이터 흐름은 불변으로 유지하는 방식을 우선한다. 호출자가 소유한 객체를 변경하지 말고 새 컬렉션을 만들어 반환한다. ### 규칙을 명시한다 - 동기 및 비동기 오류 처리에는 프로젝트의 공유 결과 헬퍼를 사용한다. - 사용자에게 보이는 검증 메시지는 현지화 키로 만들고, 일반적인 폼 오류에는 공유 키를 사용하며, 필요하면 새 키를 추가한다. - 현재 브랜치를 확인한 뒤 호환 가능한 수정이나 추가에는 공유 패키지의 패치 버전을, 새 내보내기나 동작에는 마이너 버전을 올린다. - 내보내지 않는 헬퍼는 독립된 함수로 정의하고, 실행 로직을 먼저 배치하며, 내보내는 상수와 타입은 로직 사이에 섞지 말고 끝에 모은다. - 자명한 코드가 무엇을 하는지가 아니라 그 결정이 왜 존재하는지를 주석으로 남긴다. - 여러 일반 유틸리티는 이름 있는 객체 1개를 통해 내보내고, React 컴포넌트는 이름 있는 내보내기로 유지한다. ## 외부 HTTP 요청을 안전한 클라이언트로 보내 SSRF를 막고 API 규칙을 지킨다 신뢰할 수 없는 모든 경계에서 명시적인 요청 의미론과 안전한 네트워크 클라이언트를 적용한다. ### 엔드포인트를 표준화한다 - 생성과 업데이트를 위한 모든 변경에는 `POST`를 사용하고 삭제에는 `DELETE`를 사용한다. API 계약에서 제외한 경우 `PUT`이나 `PATCH`를 절대로 사용하지 않는다. - 모든 엔드포인트에 명시적인 보안 접근 구성을 제공한다. - 표준 식별자, 오류 타입, 스키마 기반, 컬렉션 헬퍼, 데이터베이스 정제 도구를 다시 구현하지 말고 사용한다. ### 서버 측 요청 위조를 방지한다 - 서버 컴포넌트에서 보내는 HTTP 요청은 승인된 안전한 클라이언트를 거치게 한다. - 사용자 입력, 관리자 구성, OAuth 엔드포인트 또는 제3자 통합에서 유래한 URL에는 원시 `fetch`나 제한 없는 클라이언트 팩터리를 절대로 사용하지 않는다. - 사설 주소, 루프백 주소, 클라우드 메타데이터 주소에 대한 필터링을 유지한다. - 타입 강제 변환, URL 검증, 외부 객체 정제를 경계에서 수행한다. ### 마이그레이션을 통제된 변경으로 다룬다 - 마이그레이션을 만들거나 수정하기 전에 저장소의 마이그레이션 운영 지침을 읽는다. - 마이그레이션 구성을 임의로 만들지 말고 문서화된 생성 및 구조 절차를 따른다. - 지원되는 관리형 데이터베이스 환경과의 호환성을 유지한다. ## 페이지의 핵심 데이터를 불러오지 못할 때만 전체 오류 대화 상자를 보여 준다 실패한 데이터가 현재 페이지에 꼭 필요한 경우에만 전체 쿼리 실패를 표시한다. ### 영향에 따라 쿼리를 분류한다 - 테이블 행이나 주요 목록 같은 페이지의 핵심 데이터에는 전체 오류 대화 상자를 표시하는 쿼리 메타데이터 플래그를 설정한다. - 기능 플래그, 메타데이터, 개별 조회, 필터 선택지, 사용자 세부 정보 같은 보조 데이터에는 이 플래그를 활성화하지 않는다. - 실패 때문에 설명할 수 없는 빈 페이지나 빈 주요 화면이 나타난다면 대화 상자를 표시한다는 판단 기준을 사용한다. - 주변 인터페이스를 계속 유용하게 쓸 수 있다면 부수적인 쿼리는 조용히 실패하게 둔다. - 해당 요금제 기능을 이용할 수 있을 때까지 기능 제한 쿼리를 비활성 상태로 유지한다. ## 테넌트 분리와 모든 에디션을 모노레포 태스크 러너로 테스트한다 모노레포의 표준 태스크 러너를 통해 범위를 좁힌 테스트를 실행하고 에디션에 따라 달라지는 경로를 검증한다. ### 코드와 데이터를 검증한다 - 공유 코드와 실행 코드에는 단위 테스트를, API 동작에는 통합 테스트를 실행한다. - 인증과 라우팅이 프로덕션 동작과 일치하도록 표준 테스트 환경 헬퍼와 요청 컨텍스트 헬퍼를 사용한다. - 테스트 데이터를 격리하고 테스트 사이에 정리한다. - 코드에서 잠금, 중복 제거 또는 `skip-locked` 데이터베이스 선택을 사용하면 동시성 테스트를 추가한다. - 다른 프로젝트나 플랫폼의 데이터가 반환될 수 없음을 입증하여 테넌트 격리를 테스트한다. ### 지원되는 명령을 사용한다 - 작업을 완료로 보기 전에 저장소에서 요구하는 lint 및 수정 명령을 실행한다. - 구성된 모노레포 태스크 러너와 패키지 필터 명령을 사용하고, 관련 없는 빌드 시스템을 호출하지 않는다. - 호스팅된 프런트엔드가 원격 백엔드를 가리킬 때는 호스팅 도메인으로 리디렉션되는 OAuth 흐름을 피하고, 리디렉션하지 않는 자격 증명이나 완전히 로컬인 백엔드를 사용한다. - 지원되는 모든 경로에서 에디션 제한, 브랜딩, 한도, 기능 이용 가능 여부를 검증한다. ## 변경에 라벨을 붙이고 필수 검사를 통과한 뒤에 끝낸다 기능 문서, 라벨, 최종 확인을 사용하여 변경을 이해할 수 있고 릴리스 가능한 상태로 만든다. ### 구현을 준비한다 - 기능을 수정하기 전에 엔터티, 서비스, 데이터 흐름, 통합 제약을 다루는 해당 기능의 문서를 읽는다. - 중요한 범분야 안전 규칙은 간결하게 유지하고 단계별 기능 작업 절차와 분리한다. - 의존성 매니페스트, 엔터티 등록, 테넌트 필터, 에디션 간 가져오기, 접근 선언, 부수 효과의 순서를 검토한다. ### 기여를 준비한다 - 기능, 버그 수정, 변경 기록에서 제외되는 내부 변경을 구분하는 주요 변경 라벨을 정확히 1개 적용한다. - 변경이 제3자 통합 또는 핵심 통합에 영향을 주면 통합 영역 라벨을 별도로 추가한다. - 잠금 파일, 패키지 버전, 현지화 키, 테스트, 생성된 결과물이 구현과 일치하는지 확인한다. - 필수 lint, 테스트, 보안 검사를 실행하고 실행할 수 없었던 검사를 보고한다. - 최종 변경 사항과 검증 결과가 뒷받침할 때까지 완료를 선언하지 않는다.