Communitygithub.com

react-native-patterns

React Native 및 Expo 앱 패턴 — Expo Router 네비게이션, 상태 분리(서버/클라이언트/경로/폼), TanStack Query와 Zod를 사용한 데이터 패칭, 성능 최적화 리스트, NativeWind/StyleSheet 스타일링, 네이티브 API, 보안 저장소. React Native / Expo 화면, 컴포넌트, 네비게이션, 데이터 계층을 구축하거나 편집할 때 사용합니다.

react-native-patterns란 무엇인가요?

이 스킬은 React Native와 Expo를 사용하여 프로덕션 수준의 앱을 구축하기 위한 실용적인 패턴을 제공합니다. Expo Router를 이용한 파일 기반 라우팅, 서버/클라이언트/경로/폼 상태 분리, TanStack Query와 Zod를 결합한 데이터 패칭 및 검증, FlatList/FlashList를 활용한 가상화 리스트, NativeWind 또는 StyleSheet를 통한 일관된 스타일링, expo-location 및 expo-secure-store 같은 네이티브 API 사용법을 다룹니다. 이 스킬은 Expo 관리 워크플로우(Expo Router, EAS, expo-* 모듈)와 New Architecture(Expo SDK 55+ 기본)를 기반으로 하며, 웹 브라우저 DOM(<div>, URL 바, 웹 데이터 패칭 기본값)을 가정하지 않습니다. 또한 TanStack Query가 서버 상태를 소유하게 하고, Zod로 모든 외부 입력(API 응답, 경로 매개변수, 푸시 페이로드)을 검증하며, 로딩/오류/빈 상태를 항상 렌더링하고, 리스트를 가상화하는 등의 모범 사례를 강조합니다. React Native, Cursor, Codex 등 다양한 AI 에이전트 플랫폼에서 사용할 수 있도록 설계되었습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/react-native-patterns

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

React Native / Expo 패턴

Expo로 프로덕션 React Native 앱을 구축하기 위한 실용적인 패턴입니다. 네비게이션, 상태, 데이터 패칭, 리스트, 스타일링 및 네이티브 API를 다룹니다. rules/react-native/ 규칙 세트와 함께 사용됩니다. 규칙은 무엇을 강제해야 하는지를 말하고, 이 스킬은 어떻게 하는지를 보여줍니다.

아래에 언급된 라이브러리들(NativeWind, Zustand/Jotai, TanStack Query)은 예시로 보여주는 일반적이고 잘 정립된 옵션입니다. 중요한 것은 특정 패키지보다 패턴 자체이며, 동등한 기능을 하는 어떤 라이브러리든 사용 가능합니다. Zod는 ECC의 기존 typescript/ 규칙과 일관성을 유지하기 위해 검증에 사용됩니다.

이 패턴들은 관리되는 Expo 워크플로우(Expo Router, EAS, expo-* 모듈)와 New Architecture(최근 Expo SDK의 기본값, SDK 55+부터 필수)를 가정합니다. 브라우저 DOM은 가정하지 않습니다. React Native에는 <div>, URL 표시줄, 웹 데이터 패칭 기본값이 없습니다.

활성화 시기

다음과 같은 경우 이 스킬을 사용하십시오:

  • React Native / Expo 화면, 컴포넌트, 네비게이션 구축 또는 편집
  • Expo Router(파일 기반 app/ 디렉토리)로 라우팅 설정
  • 상태가 어디에 속하는지 결정(서버 캐시 vs 클라이언트 저장소 vs 경로 파라미터 vs 폼)
  • TanStack Query로 데이터 패칭을 연결하고 Zod로 응답 검증
  • 길거나 무거운 리스트 렌더링
  • 스타일링 접근 방식 선택 또는 적용(NativeWind 또는 StyleSheet)
  • 네이티브 장치 API(카메라, 위치, 알림) 또는 보안 저장소 접근
  • 모바일 특정 이슈에 대한 RN 코드 검토

여기서 웹/React-DOM 패턴(URL-as-state, <div>, SWR-for-browser)은 React Native에 적용되지 않으므로 사용하지 마십시오.

핵심 개념

프로젝트 구조 (Expo Router)

app/ 디렉토리 아래 파일 기반 라우팅. 라우트 파일은 얇게 유지하십시오. 라우트 파일은 파라미터를 읽고 검증한 다음, components/ 또는 features/에 있는 스크린 컴포넌트에 위임합니다.

app/
  _layout.tsx          # 루트 스택
  (tabs)/
    _layout.tsx        # 탭 네비게이터
    index.tsx          # 홈
  user/[id].tsx        # 동적 라우트
components/
features/
  user/UserProfile.tsx

네비게이션: 라우트 파라미터 검증

딥 링크와 동적 라우트는 신뢰할 수 없는 문자열을 전달합니다. 사용 전에 Zod로 검증하십시오.

// app/user/[id].tsx
import { useLocalSearchParams, router } from 'expo-router'
import { z } from 'zod'
import { UserProfile } from '@/features/user/UserProfile'

const Params = z.object({ id: z.string().uuid() })

export default function UserRoute() {
  const parsed = Params.safeParse(useLocalSearchParams())
  if (!parsed.success) {
    router.replace('/not-found')
    return null
  }
  return <UserProfile userId={parsed.data.id} />
}

상태: 관심사 분리 유지

서버 데이터를 클라이언트 저장소에 복제하지 마십시오. 각 관심사는 자신의 집이 있습니다.

관심사일반적인 선택
서버 상태 (원격 데이터)서버 캐시 라이브러리 (TanStack Query, SWR)
클라이언트/UI 상태가벼운 저장소 (Zustand, Jotai) 또는 Context
라우트/네비게이션 상태Expo Router 파라미터
폼 상태폼 라이브러리 (예: React Hook Form) + 스키마 검증
비밀 / 토큰expo-secure-store
비-비밀 지속성AsyncStorage / MMKV

상태가 진정으로 공유될 필요가 있을 때까지는 지역 useState를 선호하십시오.

데이터 패칭: 캐시 라이브러리 + Zod

useEffect에서 fetch를 사용하는 대신 서버 캐시 라이브러리(TanStack Query, SWR)를 사용하십시오. 경계에서 검증하고 스키마에서 타입을 추론하십시오. 로딩, 오류, 빈 상태를 명시적으로 처리하십시오. (예제는 TanStack Query를 사용합니다.)

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { z } from 'zod'

const User = z.object({ id: z.string(), email: z.string().email() })
type User = z.infer<typeof User>

export function useUser(id: string) {
  return useQuery({
    queryKey: ['user', id],
    queryFn: async (): Promise<User> => User.parse(await api.getUser(id)),
  })
}

export function useUpdateEmail(id: string) {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (email: string) => api.updateEmail(id, email),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['user', id] }),
  })
}

리스트: 가상화, ScrollView에서 큰 배열을 map하지 마십시오

import { FlatList } from 'react-native'

<FlatList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}          // 메모이제이션
  initialNumToRender={10}
  windowSize={5}
/>

크거나 이질적인 리스트에는 FlashList(Shopify)를 사용하십시오.

스타일링: 하나의 시스템 선택

StyleSheet.create()는 프레임워크 네이티브 옵션입니다; 유틸리티 클래스 라이브러리(예: NativeWind)는 일반적인 대안입니다. 하나를 선택하고 일관성을 유지하십시오. 핫 경로의 JSX에서 스타일 객체를 인라인으로 빌드하지 마십시오.

// NativeWind
<View className="p-4 rounded-2xl bg-white">
  <Text className="text-base font-semibold">안녕하세요</Text>
</View>

// StyleSheet
const styles = StyleSheet.create({ card: { padding: 16, borderRadius: 16, backgroundColor: '#fff' } })
<View style={styles.card}>...</View>

네이티브 API: 훅으로 감싸고, 효과 정리

Expo SDK 호출 및 구독을 JSX가 아닌 use* 훅 내부에 유지하십시오. 항상 정리하십시오.

import { useEffect, useState } from 'react'
import * as Location from 'expo-location'

type LocationState =
  | { status: 'loading' }
  | { status: 'denied' }
  | { status: 'granted'; coords: Location.LocationObjectCoords }

export function useCurrentLocation() {
  // 상태를 추적하고, 좌표만 추적하지 마십시오. UI가 "여전히 로딩 중"과
  // "권한 거부됨"을 구분하여 실행 가능한 메시지를 표시할 수 있도록 합니다.
  const [state, setState] = useState<LocationState>({ status: 'loading' })

  useEffect(() => {
    let active = true
    ;(async () => {
      const { status } = await Location.requestForegroundPermissionsAsync()
      if (status !== 'granted') {
        if (active) setState({ status: 'denied' })
        return
      }
      const pos = await Location.getCurrentPositionAsync({})
      if (active) setState({ status: 'granted', coords: pos.coords })
    })()
    return () => { active = false }   // 언마운트 후 오래된 결과 무시
  }, [])

  return state
}

토큰을 위한 보안 저장소

import * as SecureStore from 'expo-secure-store'

await SecureStore.setItemAsync('auth_token', token)   // Keychain / Keystore
const token = await SecureStore.getItemAsync('auth_token')

코드 예제

전체 화면: 라우트 → 쿼리 → 리스트 → 상태

// app/(tabs)/orders.tsx
import { memo, useCallback } from 'react'
import { FlatList, Text, View } from 'react-native'
import { useQuery } from '@tanstack/react-query'
import { z } from 'zod'

const OrderSchema = z.object({ id: z.string(), total: z.number(), status: z.string() })
const OrdersSchema = z.array(OrderSchema)
type Order = z.infer<typeof OrderSchema>

function useOrders() {
  return useQuery({
    queryKey: ['orders'],
    queryFn: async () => OrdersSchema.parse(await api.listOrders()),
  })
}

// 리렌더링 간에 참조가 안정적이도록 메모이제이션 (리스트 지침 참조).
const OrderRow = memo(function OrderRow({ item }: { item: Order }) {
  return (
    <View className="px-4 py-3 border-b border-neutral-200">
      <Text className="font-medium">#{item.id}</Text>
      <Text className="text-neutral-500">{item.status} · ${item.total}</Text>
    </View>
  )
})

export default function OrdersScreen() {
  const { data, isLoading, isError, refetch, isRefetching } = useOrders()
  const renderItem = useCallback(({ item }: { item: Order }) => <OrderRow item={item} />, [])

  if (isLoading) return <Centered><Text>로딩 중…</Text></Centered>
  if (isError) return <Centered><Text accessibilityRole="alert">주문을 불러올 수 없습니다.</Text></Centered>
  if (!data?.length) return <Centered><Text>아직 주문이 없습니다.</Text></Centered>

  return (
    <FlatList
      data={data}
      keyExtractor={(o) => o.id}
      onRefresh={refetch}
      refreshing={isRefetching}
      renderItem={renderItem}
    />
  )
}

폼: React Hook Form + Zod 리졸버

import { useForm, Controller } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { TextInput, Button, Text } from 'react-native'

const Schema = z.object({ email: z.string().email('유효하지 않은 이메일') })
type FormValues = z.infer<typeof Schema>

export function EmailForm({ onSubmit }: { onSubmit: (v: FormValues) => void }) {
  const { control, handleSubmit, formState: { errors } } = useForm<FormValues>({
    resolver: zodResolver(Schema),
    defaultValues: { email: '' },
  })

  return (
    <>
      <Controller
        control={control}
        name="email"
        render={({ field: { value, onChange, onBlur } }) => (
          <TextInput
            value={value}
            onChangeText={onChange}
            onBlur={onBlur}
            autoCapitalize="none"
            keyboardType="email-address"
            accessibilityLabel="이메일 주소"
          />
        )}
      />
      {errors.email && <Text accessibilityRole="alert">{errors.email.message}</Text>}
      <Button title="저장" onPress={handleSubmit(onSubmit)} />
    </>
  )
}

안티 패턴

// 잘못됨: ScrollView 내부에서 큰 배열을 map (가상화 없음, 버벅거림, 높은 메모리)
<ScrollView>{items.map((i) => <Row key={i.id} item={i} />)}</ScrollView>
// 올바름: FlatList / FlashList

// 잘못됨: 서버 데이터를 클라이언트 저장소에 복사 (두 출처, 오래된 데이터)
const useStore = create((set) => ({ users: [], setUsers: (u) => set({ users: u }) }))
useEffect(() => { getUsers().then(setUsers) }, [])
// 올바름: useQuery가 서버 상태를 소유; 필요한 것을 파생

// 잘못됨: AsyncStorage에 토큰 저장 (암호화되지 않음)
await AsyncStorage.setItem('auth_token', token)
// 올바름: expo-secure-store

// 잘못됨: 딥 링크 파라미터 신뢰
const { id } = useLocalSearchParams(); fetchUser(id)
// 올바름: 사용 전에 Zod로 검증

// 잘못됨: 핫 경로에서 매 렌더링마다 재생성되는 인라인 스타일 객체
<View style={{ padding: 16, backgroundColor: '#fff' }} />
// 올바름: 모듈 범위에서 StyleSheet.create, 또는 NativeWind className

// 잘못됨: 번들에 실제 비밀 탑재
const STRIPE_SECRET = 'sk_live_...'
// 올바름: 권한 있는 호출은 서버 측에서 유지; 백엔드 규칙으로 보호된 공개 키만 탑재

모범 사례

  • 라우트 파일은 얇게 유지; 로직은 스크린 컴포넌트와 use* 훅에 넣습니다.
  • 모든 외부 입력(API 응답, 라우트 파라미터, 푸시 페이로드)을 Zod로 검증합니다.
  • TanStack Query가 서버 상태를 소유하게 하고; 클라이언트 저장소는 작게 유지합니다.
  • 항상 로딩, 오류, 빈 상태를 렌더링합니다. 폴백 없이 스피너만 표시하지 마십시오.
  • 리스트를 가상화; renderItem을 메모이제이션; 안정적인 keyExtractor를 제공합니다.
  • 애니메이션에는 react-native-reanimated 사용 (UI 스레드); JS 스레드에서 무거운 작업을 피합니다.
  • 토큰은 expo-secure-store에 저장; 인증을 위해 클라이언트를 신뢰하지 마십시오.
  • 처음부터 safe areas, Dynamic Type, 접근성 역할/레이블을 존중합니다.
  • 릴리스 전에 모든 네이티브 의존성의 New Architecture 호환성을 확인합니다.

관련 스킬

  • frontend-patterns — React/Next.js (웹) 패턴; 공유 React 개념에 유용하지만, DOM 특화.
  • coding-standards — RN 코드에 적용되는 TypeScript/JavaScript 관용구.
  • tdd-workflow, e2e-testing — 테스트 프로세스 (RN용 Jest + React Native Testing Library, Maestro/Detox 사용).
  • security-review — 위의 RN 번들/비밀 지침을 보완하는 일반 보안 체크리스트.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

관련 스킬