React
에이전트 협업 팀을 위한 React 코딩 컨벤션입니다. ui, widget, page 세 레이어의 소유 경계, 컴포넌트 조립, 핸들러와 프롭 계약, 서버 데이터와 상태의 출처, 렌더와 이펙트의 수명, 성능 판단과 문서화를 다룹니다. TypeScript 규칙을 항상 함께 따르고, 클래스 계약이나 스타일시트를 바꿀 때는 CSS 규칙도 함께 봅니다. React 19, 19.2 전용 API는 프로젝트의 지원 버전을 확인한 뒤 적용합니다.
함께 보는 규칙
- TypeScript 규칙은 항상 함께 봅니다.
- CSS 규칙은 다음 변경이 있을 때 함께 봅니다. 클래스 계약, 스타일시트, 스타일 표면을 변경한다.
규칙
1. 소유와 경계
ui, widget, page 세 레이어의 소유 경계가 분명해야 코드를 예측 가능하게 배치할 수 있습니다. 레이어와 역할에 맞춰 이름과 폴더를 정하고, 가져오기는 하위 레이어로만 향합니다. 공통 라우트 프레임은 Outlet 그룹으로 묶고 화면 아래 소유자는 한 겹으로 유지합니다. 생명주기는 해당 컴포넌트가 관리하고 순수 계산은 훅으로 감싸지 않습니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
1.1 컴포넌트를 ui, widget, page 소유 레이어로 나눕니다 | 공용 책임과 화면 전용 책임이 같은 레이어에 섞이지 않습니다 |
| 1.2 파일 이름과 심볼에 레이어 접두사를 붙입니다 | 파일 하나만 봐도 어느 레이어 소유인지 드러납니다 |
| 1.3 추출한 파일은 소유자 아래 역할 폴더에 둡니다 | 추출한 파일의 소유자와 역할을 경로에서 확인할 수 있습니다 |
| 1.4 컴포넌트 가져오기는 레이어와 소유자 경계를 지킵니다 | 공개 범위를 벗어난 가져오기를 막아 컴포넌트의 소유 관계를 유지합니다 |
1.5 공통 라우트 프레임은 Outlet 그룹으로 묶습니다 | 라우트 중첩을 폴더로 드러내면서 화면 아래 한 겹 소유 경계를 지킵니다 |
| 1.6 순수 계산을 감싸는 화면 전용 훅을 만들지 않습니다 | 실제 상태, 생명주기, 컨텍스트가 필요한 경우에만 React 훅을 사용합니다 |
| 1.7 라이브러리 생명주기는 소유 컴포넌트에서 관리합니다 | 외부 라이브러리의 생명주기와 실행 흐름을 소유 컴포넌트에서 확인할 수 있습니다 |
2. 서버 데이터 흐름
쿼리와 뮤테이션의 바인딩 이름에 API 출처를 드러냅니다. 응답 가공은 query.select처럼 출처에 가까운 곳에서 처리하고, 실패 처리와 무효화는 호출한 곳에서 맡습니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
2.1 쿼리, 뮤테이션 바인딩에 response, mutation 접두사를 붙입니다 | 지역 바인딩 이름으로 생성된 API 훅을 쉽게 찾을 수 있습니다 |
2.2 응답 가공은 query.select에서 합니다 | 응답 가공을 쿼리에 모아 화면이 원본 구조에 의존하지 않게 합니다 |
2.3 여러 쿼리를 합칠 때는 combine을 씁니다 | 여러 응답의 가공 위치를 통일하고 화면 본문의 별칭을 줄입니다 |
| 2.4 응답, 뮤테이션, 스토어는 JSX까지 원본 이름으로 읽습니다 | 별칭을 추적하지 않고 사용처에서 값의 출처를 확인할 수 있습니다 |
| 2.5 뮤테이션 실패는 호출한 자리에서 처리합니다 | 저장 실패를 놓치지 않고 호출한 자리에서 처리합니다 |
2.6 뮤테이션이 바꾼 쿼리는 invalidateQueries로 다시 맞춥니다 | 저장으로 바뀐 서버 상태를 관련 쿼리에 반영합니다 |
3. 타입과 계약
React 핸들러 타입과 래퍼가 제공하는 프롭 계약을 선언에 명시합니다. 라이브러리 래퍼는 허용할 프롭 범위를 좁히고 구조에 맞는 방법으로 전달합니다. 일반 TypeScript 타입 규칙은 동반 스킬이 다루고 여기서는 React 문맥만 봅니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
| 3.1 핸들러와 래퍼 프롭스 타입은 기존 계약에서 가져옵니다 | 같은 시그니처를 직접 다시 적어 생기는 계약 불일치를 막습니다 |
| 3.2 라이브러리 래퍼는 필요한 프롭만 공개합니다 | 화면의 라이브러리 의존성을 제한하고 교체 시 수정 범위를 줄입니다 |
| 3.3 래퍼에 DOM 속성을 열 때는 세 단계를 차례로 시도합니다 | 프롭 타입 충돌을 해결하면서 필요한 DOM 속성과 이벤트를 유지합니다 |
3.4 래퍼 형태를 고르고 조건이 맞을 때만 {...props}로 넘깁니다 | 각 프롭이 전달되는 요소를 코드에서 확인할 수 있습니다 |
4. 조립 전략
공용 컴포넌트는 단일, 합성, 명시적 변형 중 구조를 먼저 고르고 공개할 부품을 정합니다. 불리언 프롭으로 모드를 늘리지 않고, 정적 조립에는 렌더 프롭 대신 children을 씁니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
| 4.1 단일, 합성, 변형 중 필요한 구조를 고릅니다 | 필요한 확장 범위에 맞춰 단순한 컴포넌트 구조를 선택합니다 |
| 4.2 합성 부품은 조립에 필요한 것만 공개합니다 | 내부 구조를 공개 계약과 분리해 이후 변경 범위를 줄입니다 |
| 4.3 공용 컴포넌트의 모드를 불리언 조합으로 늘리지 않습니다 | 모드별 분기와 조합을 컴포넌트 구조에서 확인할 수 있습니다 |
4.4 정적 조립에서는 렌더 프롭 대신 children을 씁니다 | 실행 문맥이 필요 없는 조립을 JSX 구조로 바로 읽을 수 있습니다 |
5. 컴포넌트 구조와 JSX
프롭스 계약은 컴포넌트 바로 위에 선언하고 값은 props.로 읽어 출처를 남깁니다. 본문은 훅, 핸들러, 이펙트, 반환 순으로 읽힙니다. JSX 안에는 동작을 숨기지 않고, 컴포넌트를 컴포넌트 안에서 정의하지 않습니다. ref는 실제 명령형 계약이 있을 때만 열고, Activity는 숨긴 상태와 DOM을 보존해야 할 때만 씁니다. Fragment와 조건부 렌더링은 형태를 하나로 고정합니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
5.1 프롭스는 구조분해하지 않고 props로 읽습니다 | 값이 프롭스에서 왔다는 사실이 쓰는 자리마다 그대로 남습니다 |
| 5.2 컴포넌트 안에서 컴포넌트를 정의하지 않습니다 | 렌더마다 컴포넌트 타입을 다시 만들어 생기는 재마운트와 상태 초기화를 막습니다 |
| 5.3 JSX 안 로직은 이름 붙인 핸들러로 뺍니다 | 부수효과, 분기, 비동기 흐름을 일반 코드 흐름에서 읽습니다 |
5.4 직접 제어해야 할 때만 ref 프롭을 엽니다 | 사용하지 않는 명령형 계약이 공용 컴포넌트에 늘어나는 것을 막습니다 |
5.5 상태를 보존해야 할 때만 Activity로 숨깁니다 | 상태 보존과 초기화 요구에 맞는 렌더 방식을 선택합니다 |
5.6 프롭스 interface는 컴포넌트 바로 위에 선언합니다 | 계약과 구현을 일정한 순서로 읽을 수 있습니다 |
5.7 <> 단축 문법 대신 Fragment를 그대로 씁니다 | Fragment를 검색하고 변경 내역에서 식별하기 쉽습니다 |
| 5.8 JSX 분기는 조건을 각각 적습니다 | 각 요소 바로 앞에 표시 조건이 남아 화면 분기를 바로 읽을 수 있습니다 |
| 5.9 본문은 훅, 핸들러, 이펙트, 반환 순으로 배치합니다 | 컴포넌트마다 훅, 핸들러, 이펙트를 같은 순서로 찾을 수 있습니다 |
5.10 widget, ui 안의 부품은 런타임 경계가 있을 때만 파일로 나눕니다 | 자체 책임이 있는 부품만 분리해 소유자 안 파일 수와 구조를 읽기 쉽게 유지합니다 |
6. 화면 파일 규율
라우트 진입 파일에 화면 흐름을 드러내고, 상태나 비동기를 직접 소유한 섹션만 추출합니다. 파생값은 사용처에서 계산하고 필요가 확인되기 전에 분리하지 않습니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
| 6.1 라우트 진입 파일은 화면 흐름 조립에 집중합니다 | 진입 파일만 봐도 화면 흐름을 따라갈 수 있습니다 |
| 6.2 화면 코드를 미리 추상화하지 않습니다 | 추측에 따른 추출을 줄이고 실제 재사용 경계에 맞춰 코드를 배치합니다 |
| 6.3 런타임 경계가 있는 섹션만 화면 지역 컴포넌트로 추출합니다 | 화면 흐름을 유지하면서 자체 책임이 있는 섹션만 분리합니다 |
| 6.4 파생값은 쓰는 자리 가까이에 둡니다 | 파생값의 출처를 유지하고 화면 상단의 별칭과 준비 코드를 줄입니다 |
7. 런타임 경계
화면 표시를 막는 로딩과 오류는 경계에서 처리합니다. Suspense와 오류 경계의 위치를 정하고, 경계가 처리할 분기는 화면 본문에 남기지 않습니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
7.1 Suspense 경계는 섹션 소유자에 둡니다 | 초기 로딩을 섹션 소유자의 경계에서 처리합니다 |
| 7.2 화면 본문에 초기 로딩, 실패 분기를 추가하지 않습니다 | 초기 로딩과 실패는 경계가 맡고 화면 본문에는 데이터가 있는 경로만 남습니다 |
| 7.3 오류 뒤에도 유지할 화면 범위에 맞춰 경계를 둡니다 | 쿼리 실패를 정해진 경계에서 처리하고 필요한 화면을 유지합니다 |
8. 상태 소유와 갱신
상태는 값의 수명과 소유자에 맞는 도구로 고르고, 파생값은 저장하지 않고 렌더에서 계산해야 합니다. 여러 화면이 공유하는 판단만 전역 스토어에 두고, 이전 상태에 의존하는 갱신은 함수형으로 씁니다. 이펙트의 반응형 값은 의존성에 남기고, 최신 값을 읽기만 하는 콜백은 useEffectEvent로 분리합니다. URL 상태의 바인딩 이름을 통일해 서버 응답과 구분합니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
| 8.1 파생값은 렌더 중에 계산합니다 | 지금 입력으로 구할 수 있는 값은 상태에 두지 않고 렌더에서 계산합니다 |
| 8.2 상태 도구는 값의 소유자와 수명으로 고릅니다 | 로컬, 공유, 서버, URL 상태의 소유자를 구분합니다 |
| 8.3 여러 화면이 함께 쓰는 파생 판단만 스토어로 올립니다 | 같은 도메인 판별 로직이 여러 화면에 퍼지지 않습니다 |
8.4 이전 상태로 계산하는 갱신은 함수형 setState로 씁니다 | 이전 상태에 의존하는 갱신에서 오래된 값을 사용하는 오류를 막습니다 |
8.5 비반응형 이펙트 콜백은 useEffectEvent로 분리합니다 | 콜백은 최신 값을 읽고 이펙트는 구독 조건의 변화에만 반응합니다 |
| 8.6 URL 파서, 파싱 결과, 플랫폼 객체의 이름을 구분합니다 | 주소가 소유한 상태, 플랫폼 객체, 서버 응답이 이름만으로 구분됩니다 |
9. 이벤트와 상호작용 흐름
이벤트 핸들러는 정해진 이름을 쓰고 추가 인자는 커링으로 전달합니다. 사용자 액션에 따른 처리는 이펙트가 아닌 핸들러에서 실행합니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
9.1 핸들러 이름에 handle 접두사를 붙입니다 | 이벤트 흐름을 이름으로 검색할 수 있습니다 |
| 9.2 이벤트 외 추가 인자는 커링으로 넘깁니다 | 추가 인자 전달만을 위한 JSX 인라인 래퍼를 줄입니다 |
| 9.3 사용자 액션은 이펙트가 아니라 핸들러에서 실행합니다 | 사용자 액션이 무관한 이펙트 재실행으로 반복되는 것을 막습니다 |
10. 렌더 성능
메모이제이션은 필요를 확인한 경우에만 적용합니다. 실제로 무거운 초기화와 갱신만 초기화 함수, 트랜지션, 지연 값으로 미룹니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
10.1 확인한 이유가 없으면 useMemo, useCallback, memo를 쓰지 않습니다 | 효과를 확인하지 않은 방어적 useMemo, useCallback, memo를 막습니다 |
| 10.2 무거운 초기값 계산은 초기화 함수로 감쌉니다 | 무거운 초기 상태 계산이 이후 렌더에서 반복되지 않습니다 |
| 10.3 측정한 근거가 있을 때만 무거운 렌더를 미룹니다 | 측정한 렌더 병목에만 트랜지션과 지연 값을 적용합니다 |
11. 접근성
조작할 수 있는 요소에는 스크린 리더와 테스트가 찾을 수 있는 이름을 제공합니다. 보이는 글자를 이름으로 사용하고, 글자가 없으면 대체 이름을 지정합니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
| 11.1 클릭, 입력 요소에는 접근 가능한 이름을 붙입니다 | 스크린 리더와 테스트가 요소를 이름으로 찾을 수 있습니다 |
12. 문서화와 주석
문서 주석의 형식과 태그, 붙일 선언은 동반 스킬인 convention-typescript가 정합니다. 이 섹션에서는 React 전용 선언의 문서화와 JSX 자식 위치의 주석 형식을 정합니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
| 12.1 React의 주요 경계 선언에 JSDoc 주석을 붙입니다 | 공통 문서화 기준에 React 전용 선언을 추가해 누락을 막습니다 |
| 12.2 JSX 안 주석은 여러 줄 블록으로 씁니다 | JSX 주석 형식을 통일해 화면 구역의 역할을 쉽게 읽을 수 있습니다 |
13. 도구 설정
React 전용 검사는 biome 도메인 설정으로 지정하고, 도구가 검사하지 못하는 항목은 리뷰에서 확인합니다. 섹션 페이지
| 규칙 | 요지 |
|---|---|
13.1 biome의 react 도메인과 추가 검사를 설정합니다 | 자동 검사 범위와 리뷰에서 판단할 범위를 구분합니다 |