


저장소 규칙에 맞춰 네이티브와 웹에서 동작하는 TypeScript 앱 만들기
네이티브와 웹에서 함께 동작하는 TypeScript 앱을 만들거나 고칠 때 쓴다. AI가 저장소의 스크립트와 폴더 구성을 따르고, 공용 컴포넌트를 재사용하며, 화면에 보이는 문구를 번역할 수 있게 만들고, 영향을 받는 모든 플랫폼에서 테스트한 뒤 마무리한다.
이 AI 규칙을 사용하려면
## 테스트·lint·빌드를 저장소 스크립트로 실행하기
지원되는 버전과 명령의 기준은 패키지 매니페스트와 저장소 스크립트로 삼는다. 현재 지원되는 API를 우선 사용하고, 영향을 받는 범위에서 안전하게 수행할 수 있을 때 레거시 또는 더 이상 권장되지 않는 패턴을 교체한다.
다음 작업에는 패키지 관리자 스크립트를 사용한다.
- 개발 서버와 지원되는 각 플랫폼 시작
- 단위 테스트, lint, 타입 검사, 포매팅
- 웹 빌드와 네이티브 프로젝트 생성
저장소 스크립트가 있다면 그 아래에서 실행되는 테스트, lint, 타입 검사 또는 포매팅 도구를 절대로 직접 호출하지 않는다. 스크립트에는 직접 명령이 생략하는 설정이나 준비 과정이 포함될 수 있다.
CI가 현지화 추출 또는 컴파일 작업을 담당한다면 해당 명령을 실행하지 않는다. 번역 가능한 소스 문자열을 추가하거나 업데이트한 다음, 예약된 파이프라인이 카탈로그와 런타임 번역을 업데이트하도록 맡긴다.
코드를 변경하기 전에 대상 플랫폼, 검증에 사용할 표준 스크립트, 생성된 산출물을 로컬과 자동화 중 어디에서 관리하는지 파악한다.
## 새 화면과 컴포넌트를 알맞은 폴더에 두기
공유되는 플랫폼 중립적 UI 요소는 `components`에 배치한다. 전체 페이지 경로는 `screens`에 배치한다. 여러 화면에서 컨텍스트, 데이터 접근, 컴포넌트, 유틸리티를 결합하는 큰 기능에는 `features`를 사용한다.
비공개 컴포넌트나 상태가 있는 복잡한 화면에는 화면 디렉터리를 만들고 해당 종속 요소를 같은 위치에 배치한다. 둘 이상의 사용처에서 필요로 하기 전에는 코드를 공유 폴더로 승격하지 않는다.
서버 상태 훅, 단순한 UI 설정, 세션 상태, 영속성은 명확한 상태 모듈에 두거나 기능별 상태를 해당 사용처와 같은 위치에 둔다. 관련 없는 책임을 섞은 큰 파일은 피한다.
특정 기능에 속하지 않는 재사용 가능한 유틸리티, API 클라이언트, 포매터, 상수에는 `lib`를 사용한다. 이미 정립된 책임 영역에 맞는다면 새로운 최상위 소스 디렉터리를 추가하지 않는다.
레거시 뷰 디렉터리에 새 작업을 추가하지 않는다. 마이그레이션이 작업 범위에 포함되지 않았다면 기존 레거시 코드는 유지하되, 새 화면, 컴포넌트, 기능은 현재 구조에 배치한다.
컴포넌트 이름에는 PascalCase를, 일반 파일, 디렉터리, 변수, 함수에는 camelCase를 사용한다. 규모가 큰 모듈은 같은 수준에 컴포넌트, 훅, 유틸리티를 두고 `index.tsx` 진입점을 사용할 수 있다. 작은 컴포넌트는 파일 하나에 유지한다.
선택적인 기능 문서와 테스트는 큰 모듈과 같은 위치에 둔다. 주석에는 코드가 이미 나타내는 내용이 아니라 결정이 존재하는 이유를 설명한다. 이름 있는 선언에는 문서화 블록, 여러 줄 설명에는 블록 주석, 짧은 메모에만 줄 주석을 사용한다.
## 디자인 시스템과 기존 컴포넌트로 UI 만들기
원시 스타일이나 중복 컴포넌트를 만들기 전에 기존 디자인 시스템을 사용한다. 테마 독립적 아톰, 테마 아톰, 예외적인 원시 스타일을 다음 순서로 결합한다.
1. flex 및 레이아웃 아톰
2. 간격 아톰
3. 타이포그래피 아톰
4. 테마 색상과 표면
5. 범위를 엄격히 제한한 원시 스타일
조건부 스타일링에는 플랫폼 스타일 유틸리티를, 반응형 레이아웃에는 중단점 훅을 사용한다. 플랫폼 조건을 컴포넌트 로직 곳곳에 흩뜨리지 말고 스타일 구성 안에 유지한다.
컨트롤을 만들기 전에 공유 컴포넌트 라이브러리에서 적절한 버튼, 다이얼로그, 메뉴, 텍스트 필드 또는 타이포그래피 기본 요소를 찾는다. 해당 컴파운드 API와 기존 표준 사용법을 따른다.
컴포넌트 선언에는 다음 규칙을 적용한다.
- 키가 있는 프래그먼트가 필요하지 않다면 프래그먼트 단축 문법을 우선한다.
- 함수 선언을 우선한다.
- 매개변수 목록에서 props를 구조 분해한다.
- 가독성을 유지할 수 있다면 명시적인 인라인 props 타입을 사용한다.
- 선택적 props에는 합리적인 기본값을 지정한다.
- 재사용 가능한 컴포넌트는 가능하면 플랫폼 중립적으로 유지한다.
플랫폼 아키텍처에서 제어 입력의 성능이 좋지 않다면 `defaultValue`가 있는 비제어 텍스트 필드를 사용한다. 동작상 실제로 필요할 때만 제어 값을 사용한다.
예시에 없더라도 사용자 생성 텍스트에는 이모지가 포함될 수 있다. 사용자 콘텐츠에는 타이포그래피 시스템의 이모지 안전 동작을 활성화하고, 알려진 정적 텍스트에만 이를 생략한다.
## 화면에 보이는 모든 문구를 번역할 수 있게 만들기
사용자에게 표시되는 모든 문자열을 프로젝트의 번역 매크로나 번역 컴포넌트로 감싼다. 명사와 동사의 철자가 같은 경우처럼 한 단어에 여러 의미가 있다면 번역자 주석이나 컨텍스트를 추가한다.
번역 프레임워크가 지원하는 최신 매크로 진입점을 사용한다. 테마 설정 같은 다른 하위 시스템이 동일한 짧은 변수 이름을 이미 사용한다면 번역 함수에 별칭을 지정한다.
개수에는 번역 프레임워크의 복수형 기능을, 날짜와 시간에는 날짜 포매터를 사용한다. 프레임워크 포매팅은 활성 언어가 바뀔 때 다시 실행되어야 하지만, 로캘을 직접 지정한 포매팅은 그렇지 않은 경우가 많다.
사용자 대상 텍스트에는 타이포그래피에 맞는 올바른 Unicode 문장 부호를 우선 사용한다. 따옴표와 대시 간격을 포함하여 프로젝트의 문장 부호 규칙을 일관되게 적용한다.
생성된 로캘 출력을 수동으로 편집하지 않으며, CI가 담당하는 추출 또는 컴파일 작업을 로컬에서 실행하지 않는다. 소스 변경에는 번역 매크로나 컴포넌트로 감싼 메시지와 정확한 번역에 충분한 컨텍스트를 포함해야 한다.
## 서버 데이터, 화면 이동, 플랫폼별 파일 다루기
원격 데이터에는 기존 서버 상태 쿼리 라이브러리를, 단순한 UI 설정에는 컨텍스트 방식의 상태를 사용한다. 읽기 및 업데이트 훅을 한 쌍으로 제공하여 설정을 노출한다.
공유 키 도우미와 인수 객체를 사용하여 쿼리 키를 구성한다. 키 루트는 훅 이름과 일치해야 한다. 쿼리 훅, 뮤테이션 훅, 캐시만 변경하는 뮤테이션 도우미에는 일관된 이름을 사용한다.
공유 상수에서 데이터 유효 시간(stale time)을 선택한다. 커서 페이지네이션에는 무한 쿼리를 사용하고, 응답 커서를 다음 페이지 매개변수로 반환하며, 사용 경계에서만 페이지를 평탄화한다.
영속된 쿼리 데이터의 형태가 바뀌면 영속 버전을 올려 오래된 직렬화 데이터가 폐기된 뒤 다시 가져와지도록 한다.
뮤테이션 실패 시 다음과 같이 처리한다.
- 예상되는 네트워크 실패는 오류 로그에 중복 기록하지 않고 사용자에게 알린다.
- 타입이 지정된 프로토콜 또는 API 오류를 명시적으로 처리한다.
- 예상하지 못한 실패는 안전한 메시지와 함께 구조화된 로깅으로 보낸다.
타입이 지정된 경로 매개변수와 프로젝트의 탐색 도우미 또는 훅을 사용한다. 경로 구성, 경로 정의, 경로 타입을 동기화한다.
플랫폼 구현은 `.web`, `.native`, `.ios`, `.android` 같은 파일 이름 접미사로 나타낸다. 기본 모듈 경로를 임포트하고 번들러가 구현을 선택하게 한다. 플랫폼 파일을 선택하기 위해 조건부 런타임 임포트를 절대로 사용하지 않는다.
플랫폼 변형을 하나의 모듈 디렉터리 뒤에 그룹화하는 방식을 우선한다. 런타임 플랫폼 플래그는 동작에만 사용하고 모듈 해석에는 사용하지 않는다. 깊은 상대 경로 대신 저장소의 절대 임포트 별칭을 사용한다.
## 다이얼로그를 닫을 때의 타이밍 문제와 불필요한 메모이제이션 막기
다이얼로그가 닫힌 후 실행해야 하는 모든 작업은 다이얼로그 컨트롤의 닫기 콜백 안에서 실행한다. 먼저 닫은 직후 탐색하거나, 다른 오버레이를 열거나, 영향받는 상태를 업데이트하거나, 부모 콜백을 호출하면 닫기 애니메이션과 경합할 수 있다.
필수 순서는 다음과 같다.
1. 닫기 메서드를 호출한다.
2. 해당 메서드에 콜백을 제공한다.
3. 그 콜백 안에서 탐색, 오버레이 열기, 상태 업데이트 또는 부모 콜백 호출을 수행한다.
플랫폼별 컴파운드 컴포넌트를 준수한다. 다이얼로그 핸들이나 메뉴 컨테이너 항목은 네이티브 전용일 수 있고, 닫기 컨트롤이나 메뉴 구분선은 웹 전용일 수 있다. 각 분기를 실행하는 플랫폼을 테스트한다.
컴파일러 자동 메모이제이션이 활성화되어 있다면 `useMemo`나 `useCallback`을 미리 추가하지 않는다. 이펙트 종속성이나 React 외부 라이브러리 계약처럼 구체적인 참조 안정성 요구가 있을 때만 사용한다.
추측성 최적화를 위해 가독성을 희생하지 않는다. 수동 메모이제이션을 추가하기 전에 실제 문제를 측정하거나 명시적인 안정성 요구를 충족한다.
## 마치기 전에 영향을 받는 모든 플랫폼에서 테스트하기
완료 전에 다음을 수행한다.
1. 모든 상호작용 요소에 접근 가능한 레이블이 있고 필요한 경우 유용한 힌트가 있는지 확인한다.
2. 사용자에게 표시되는 모든 문자열이 현지화되었고 모호한 메시지에 컨텍스트가 포함되었는지 확인한다.
3. 스타일링에 디자인 시스템 아톰, 테마 값, 플랫폼 유틸리티가 일관되게 사용되었는지 확인한다.
4. 서버 상태, UI 설정, 세션 상태에 각각 의도된 메커니즘이 사용되었는지 확인한다.
5. 기존 공유 컴포넌트를 중복 생성하지 않았는지 확인한다.
6. props와 경로 매개변수에 명시적 타입이 있는지 확인한다.
7. 엔드투엔드 테스트에 필요한 위치에 안정적인 테스트 식별자를 노출한다.
8. 테스트, lint, 타입 검사, 포매팅을 위한 저장소 스크립트를 실행한다.
9. 영향을 받는 모든 네이티브 및 웹 동작을 테스트한다.
차이점을 검토하여 레거시 디렉터리의 새로운 사용, 조건부 플랫폼 임포트, 생성된 현지화 파일의 직접 편집, 비제어 입력과 제어 입력 간의 회귀, 다이얼로그가 닫힌 뒤의 경합 조건이 없는지 확인한다.
실행할 수 없었던 플랫폼이나 표준 스크립트는 누락된 전제 조건과 함께 보고한다. 단일 플랫폼 검사만으로 크로스 플랫폼 작업이 완료되었다고 주장하지 않는다.
