검색/다른 웹 프레임워크와 똑같이 동작하는 호환성 계층을 만든다

다른 웹 프레임워크와 똑같이 동작하는 호환성 계층을 만든다

다른 웹 프레임워크의 동작을 여러 런타임에서 다시 구현할 때 쓴다. 먼저 그 프레임워크의 테스트를 옮겨 오고, 요청 처리 순서와 런타임 경계를 지키며, 파일 경로 표기를 통일하고, 모든 검사를 통과한 pull request로만 병합한다.

이 AI 규칙을 사용하려면
## 따라 할 프레임워크의 테스트와 코드를 명세로 삼는다 호환성을 근거의 문제로 다룬다. 대상 프레임워크의 테스트와 구현은 직관보다 더 신뢰성 있게 예상 동작을 정의한다. 모든 기능 또는 버그 수정에서 다음을 수행한다. 1. 관련 이름, 오류, 유효하지 않은 입력, 경계 사례를 포함하여 업스트림 단위 테스트 및 엔드투엔드 테스트 스위트를 폭넓게 검색한다. 2. 해당 구현을 읽고 순서, 기본값, 실패 시 동작을 이해한다. 3. 코드만으로 동작이 의도된 것인지 설명되지 않으면 issue나 이전 설계 논의를 검색한다. 4. 변경을 구현하기 전에 관련 사례를 이식하고, 이식한 각 사례를 해당 업스트림 테스트에 연결한다. 5. 변경 설명에 근거를 기록한다. 소스와 테스트를 빠르게 검색하려면 로컬 shallow clone을 사용한다. clone을 사용할 수 없거나 질문에 현재 논의가 필요한 경우에만 원격 코드 검색을 사용한다. 보안, 배포 또는 플랫폼상의 이유로 호환성 계층이 의도적으로 달라야 한다면 그 결정을 명시적으로 기록한다. 우발적인 차이를 기능인 것처럼 가장해서는 안 된다. ## 실패하는 테스트를 먼저 쓰고, 개발·운영·edge 진입점을 함께 고친다 먼저 초점을 맞춘 실패 테스트를 작성한 다음, 검증된 계약을 충족하는 최소 변경을 구현한다. - 변경한 하위 시스템의 테스트 파일과 직접 결합된 테스트를 실행한다. - 통합 동작에는 fixture 입력을 추가하고, 예제는 사용자 대상 시연용으로 유지한다. - 반복 작업 중에는 테스트 이름 필터를 사용한다. - 변경이 공유 라우팅, 빌드 인프라, 가상 모듈 또는 공통 테스트 헬퍼에 영향을 주지 않는 한 전체 스위트 실행은 CI에 맡긴다. - 공유 인프라가 바뀌면 로컬 검증을 영향받는 횡단 범위로만 확장한다. 검증을 소유권에 맞춘다. shim에는 shim 테스트가, 라우팅에는 라우트 순서 테스트가, 캐시에는 캐시 테스트가, 빌드 또는 배포 변경에는 빌드 테스트가 필요하다. 요청 처리는 개발, 프로덕션, 서버 렌더링, edge의 별도 진입점에 존재하는 경우가 많다. 같은 동작을 소유하는 모든 구현을 확인하고, 알려진 중복을 남기지 말고 같은 변경에서 동등성 차이를 수정한다. ## 런타임 경계에서는 요청 정보를 명시적으로 넘기고, 생성 코드는 얇게 유지한다 분리된 빌드 또는 렌더링 환경은 독립적인 모듈 그래프와 모듈 상태를 가질 수 있다. 한 환경에서 요청 상태를 설정하면 다른 환경도 바뀐다고 절대 가정하지 않는다. - pathname, 매개변수, 헤더, cookie, 기타 요청 컨텍스트를 환경 경계를 넘어 명시적으로 전달한다. - 렌더링 직전에 컨텍스트를 초기화하고 이후에 정리한다. - 요청 범위 상태와 작은 공개 파사드를 무거운 구현과 분리한다. - 스트리밍 소비와 정리 시점을 모두 검증한다. 생성된 진입 모듈에는 라우트별 import, manifest, 얇은 연결 코드만 포함해야 한다. 요청 오케스트레이션, 캐시 정책, 메서드 디스패치, 리디렉션, 오류 처리, 헤더 병합과 그 밖의 테스트 가능한 동작은 일반적인 타입 지정 모듈로 옮긴다. 추출한 헬퍼를 직접 단위 테스트한다. 생성 코드 테스트는 큰 구현 문자열을 일치시키지 말고 위임과 연결을 검증해야 한다. 기반 bundler plugin과 호환성 계층 사이에 명확한 소유권 경계를 유지한다. plugin은 변환, 직렬화, 다중 환경 빌드, CSS 처리, hydration 부트스트랩을 소유할 수 있고, 호환성 계층은 라우팅, 요청 수명 주기, 레이아웃 구성, 탐색, 캐시, API shim을 소유한다. ## 파일 경로 표기를 통일하고, 다중 환경 빌드 파이프라인 전체로 빌드한다 모듈 ID나 생성된 import로 사용하는 파일 시스템 경로에는 운영 체제를 아우르는 하나의 정규 구분자 정책이 필요하다. - 소스 코드에서는 네이티브 의미론을 보존하면서 정규 식별자를 반환하는 경로 추상화를 사용한다. - process 작업 디렉터리, 해석된 모듈, 실제 경로, glob 출력, bundler가 보고한 ID처럼 외부에서 온 값만 정규화한다. - 정규 경로 계층이 이미 생성한 값을 반복해서 정규화하지 않는다. - URL 역슬래시 무해화와 파일 시스템 경로 처리를 분리한다. - 실제로 필요한 네이티브 경로 예외는 모두 문서화하고 lint로 검사한다. 테스트에서는 네이티브 경로 API로 입력을 구성하여 Windows가 실제 역슬래시를 제공하게 한다. 예상 출력만 정규화한다. 슬래시만으로 구성한 크로스 플랫폼 테스트는 Windows 정규화 회귀를 감지할 수 없다. 프로덕션 빌드는 프레임워크 plugin의 완전한 다중 환경 builder 파이프라인을 사용해야 한다. 일반적인 단일 환경 빌드 호출은 필요한 server, renderer, client 단계를 알림 없이 건너뛸 수 있다. 도구 체인의 메이저 버전이 바뀌면 현재 transformer, dependency optimizer, bundler option 계열을 우선한다. 레거시 option 이름은 마이그레이션 대상으로 취급한다. 가상 모듈에서는 루트 접두사가 붙은 ID, 이미 해석된 null 접두사, 물리적 모듈 위치가 없을 때의 절대 import도 명시적으로 처리해야 한다. ## 일반 요청을 가볍게 유지하고, 캐시 동작을 분명히 정한다 기능별 런타임을 일반 요청 경로에서 제외한다. - 선택적 동작을 dynamic import하기 전에 저비용 요청 또는 라우트 metadata로 적용 여부를 판별한다. - 런타임 코드가 필요하지 않으면 type-only import를 사용한다. - JavaScript handler가 실행되기 전에 bundler hook을 필터링하여 의존성, 생성된 런타임, prebundle, 적용될 수 없는 구문 대상을 제외한다. - 가벼운 상태 및 등록 모듈을 캐시, 탐색, 렌더링 구현과 분리한다. - 계산한 캐시 키와 요청 파생 값을 재사용한다. - 불변 표현식과 테이블을 외부로 끌어올린다. - 가능하면 결정론적 파싱과 asset 선택을 빌드 시점으로 옮긴다. 소유권, 무효화, 런타임 동등성이 명확해지기 전에는 캐시를 추가하지 않는다. 더 작은 모듈 그래프가 오래된 코드, 서로 다른 런타임 조건 또는 손상된 플랫폼 bundle을 초래한다면 이점이 아니다. 스토리지 handler를 단순하게 유지하고 증분 재생성 정책을 그 상위에 둔다. stale-while-revalidate 동작, single-flight 재생성, 재검증 metadata, tag 무효화를 각각 별도로 정의한다. 시간이 만료된 항목은 재생성 중에 오래된 상태로 반환해도 되지만, 명시적으로 무효화된 항목은 제거해야 한다. backend 선택을 선언적으로 만들고, 안전한 기본값을 유지하며, 선택적 adapter는 처음 사용하기 전에 등록한다. ## 따라 할 프레임워크와 같은 순서로 요청을 처리한다 대상 프레임워크의 요청 수명 주기를 순서가 있는 계약으로 모델링한다. 1. 정적 설정 헤더는 원래 요청을 기준으로 평가한다. 2. 정적 리디렉션은 원래 요청을 기준으로 평가한다. 3. middleware가 실행되어 rewrite, redirect 또는 헤더 수정을 수행할 수 있다. 4. 파일 시스템 처리 전 rewrite는 middleware 처리 후 상태를 기준으로 평가한다. 5. 정적 라우트와 파일 시스템 라우트를 해석한다. 6. 파일 시스템 처리 후 rewrite를 실행한다. 7. 동적 라우트를 해석한다. 8. fallback rewrite를 실행한다. 최종 응답이 동등해 보인다는 이유만으로 단계를 옮기지 않는다. middleware가 요청을 변경하면 조건부 일치 결과가 달라질 수 있다. 알려진 동등성 차이를 숨기지 말고 기록한다. 어떤 정적 asset이 middleware를 우회하고 어떤 공개 파일이 middleware를 통과하는지와 같은 의도적 구분을 보존한다. 변화하는 API에서는 호환성 계약이 두 방식 모두를 요구할 때만 이전 및 신규 접근 패턴을 지원한다. 예를 들어 해석된 필드가 추가된 promise는 await을 사용한 매개변수 접근과 직접 매개변수 접근을 모두 지원할 수 있다. 공개 API import를 사용하는 서드파티 라이브러리는 alias 또는 확장자 정규화를 통해 동작할 수 있다. 빌드 plugin이나 내부 API에 의존하는 라이브러리에는 명시적 통합이 필요하며, 설치하고 import하는 것만으로는 충분하지 않다. ## lockfile을 우회하지 않고, 검사를 통과한 pull request로만 병합한다 무인 설치 중에는 frozen lockfile을 절대 우회하지 않는다. 의존성 해석에 lockfile 변경이 정말 필요하다면 작업을 중지하고, 사용자에게 해당 작업을 직접 수행하거나 승인해 달라고 요청한다. 정확한 버전과 integrity hash는 공급망 경계다. 보호된 기본 브랜치에 직접 push하거나, 브랜치 보호를 우회하거나, 관리자 merge를 강제하지 않는다. 다음 순서를 사용한다. 1. 범위를 좁힌 feature branch를 만든다. 2. 일관성 있는 최소 변경을 수행하고 초점을 맞춘 검사를 실행한다. 3. 리포지터리가 요구하는 메시지 규칙에 따라 commit한다. 4. 브랜치를 push하고 pull request를 연다. 5. 필수 format, type, unit, end-to-end 검사가 모두 통과할 때까지 기다린다. 6. 실패를 우회하지 말고 수정한다. 7. 승인된 방법으로 merge하고 브랜치를 삭제한다. 릴리스 자동화가 conventional commit에서 changelog metadata를 도출하게 한다. 의도적인 소급 재분류에만 문서화된 override 방법을 사용하고, 생성된 릴리스 산출물을 수동으로 편집하지 않는다. 대규모 리팩터링에는 각각 하나의 응집된 경계를 추출하고, 초점을 맞춘 헬퍼 테스트를 추가하며, 위임 검증을 업데이트하는 작은 stacked change를 사용한다. 신뢰할 수 없는 기여의 CI는 secret 없이 읽기 전용 권한으로 실행해야 한다. secret이 포함된 preview workflow는 권한을 가진 maintainer가 신원 확인으로 보호된 command를 통해 명시적으로 실행하지 않는 한 fork를 건너뛰어야 한다. 배포된 모든 example에는 build matrix 등록과 예상 route, status, content를 검증하는 smoke test가 필요하다. 필수 검사와 배포 후 근거가 통과한 뒤에만 릴리스 또는 배포가 완료된다.