harness/plugins/zioinfo/skills/zio-harness/references/mobile.md
DESKTOP-TKLFCPR\ython 6caf43e1ed feat!: v2.0.0 — 4개 플러그인 zioinfo 단일 통합 + 최신 플러그인 기술 적용
- harness·zio-harness·proposal-builder·zioinfo → plugins/zioinfo (git mv 히스토리 보존)
- 스킬 4·커맨드 3(/zioinfo:pmo·proposal·wiki)·에이전트 15·graphify 훅·knowledge 통합
- 신규: /zioinfo:wiki (graphify LLM wiki — graphify-out/wiki/ 커뮤니티별 아티클)
- 신규: ZIO WISE 테마 (themes/zioinfo.json, experimental)
- manifest 최신화: $schema·displayName(ZIO INFOTECH Suite)·experimental.themes
- marketplace.json 단일 엔트리, 루트 plugin.json 제거
- CLAUDE.md·PROJECT_MAP·docs/plugins.md·README 3종·CHANGELOG·설치가이드 pptx 재구성

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 20:16:36 +09:00

5.0 KiB

Mobile App 개발 가이드

스택 가정

  • React Native (Expo 또는 bare workflow) + TypeScript
  • 네비게이션: React Navigation v6
  • 상태 관리: Zustand (웹과 공유 가능)
  • API: Axios + TanStack Query
  • 스타일: StyleSheet (네이티브), NativeWind (Tailwind 기반)

Flutter를 사용하는 경우: references/flutter.md 생성 필요 (별도 확장)


폴더 구조

mobile/
├── src/
│   ├── screens/          # 화면 단위 컴포넌트
│   │   ├── auth/         # 인증 화면 (Login, Register)
│   │   ├── home/         # 홈 화면
│   │   └── profile/      # 프로필 화면
│   ├── navigation/       # 네비게이터 정의
│   │   ├── RootNavigator.tsx
│   │   ├── AuthNavigator.tsx
│   │   └── MainNavigator.tsx
│   ├── components/       # 재사용 컴포넌트
│   │   ├── ui/           # 기본 UI (Button, Input, Card)
│   │   └── features/     # 도메인별 컴포넌트
│   ├── api/              # API 클라이언트 + 훅 (웹과 동일 패턴)
│   ├── store/            # Zustand stores (웹과 코드 공유 가능)
│   ├── types/            # 타입 정의
│   ├── utils/            # 유틸리티
│   └── constants/        # 상수 (colors, sizes, etc.)
├── android/              # Android 네이티브
├── ios/                  # iOS 네이티브
├── app.json              # Expo 설정
└── package.json

네비게이션 구조

// src/navigation/RootNavigator.tsx
export function RootNavigator() {
  const { token } = useAuthStore();

  return (
    <NavigationContainer>
      {token ? <MainNavigator /> : <AuthNavigator />}
    </NavigationContainer>
  );
}

// src/navigation/MainNavigator.tsx
const Tab = createBottomTabNavigator<MainTabParams>();

export function MainNavigator() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
    </Tab.Navigator>
  );
}

// 타입 안전한 네비게이션
type MainTabParams = {
  Home: undefined;
  Profile: { userId: string };
};

화면 컴포넌트 패턴

// src/screens/home/HomeScreen.tsx
import { NativeStackScreenProps } from '@react-navigation/native-stack';

type Props = NativeStackScreenProps<MainTabParams, 'Home'>;

export function HomeScreen({ navigation }: Props) {
  const { data: posts, isLoading } = usePosts();

  if (isLoading) return <LoadingSpinner />;

  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={posts}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <PostCard
            post={item}
            onPress={() => navigation.navigate('PostDetail', { id: item.id })}
          />
        )}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#fff' },
});

API 연동 (웹과 코드 공유)

웹(React)과 모바일은 동일한 API 훅 패턴을 사용한다. 공유 패키지 또는 복사 방식으로 관리:

// src/api/client.ts — 웹과 동일한 패턴
const api = axios.create({
  baseURL: Config.API_URL,  // react-native-config 사용
  timeout: 10000,
});

환경 변수는 react-native-config 사용:

# .env
API_URL=http://localhost:8080

로컬 저장소

import AsyncStorage from '@react-native-async-storage/async-storage';

// 토큰 저장
await AsyncStorage.setItem('token', token);

// 토큰 읽기
const token = await AsyncStorage.getItem('token');

// Zustand 영속성 (zustand/middleware의 persist)
const useAuthStore = create(
  persist<AuthState>(
    (set) => ({ ... }),
    { name: 'auth-store', storage: createJSONStorage(() => AsyncStorage) }
  )
);

푸시 알림

// Expo Notifications 사용 시
import * as Notifications from 'expo-notifications';

async function registerForPushNotifications() {
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== 'granted') return null;
  
  const token = await Notifications.getExpoPushTokenAsync({
    projectId: Constants.expoConfig?.extra?.eas?.projectId,
  });
  
  // 서버에 토큰 등록
  await api.post('/api/v1/users/push-token', { token: token.data });
  return token.data;
}

빌드 및 배포

# 개발 실행
npx expo start

# Android 빌드 (EAS Build)
eas build --platform android --profile preview

# iOS 빌드
eas build --platform ios --profile preview

# 스토어 제출
eas submit --platform android
eas submit --platform ios

컨벤션

항목 규칙
화면 파일 {Name}Screen.tsx
네비게이터 {Name}Navigator.tsx
스타일 파일 하단에 StyleSheet.create({})
네이티브 모듈 src/native/ 폴더에 분리
플랫폼별 코드 .ios.tsx / .android.tsx 확장자