
지난 글에서 디자인까지 마쳤으니, 이번엔 본격적으로 개발 환경을 세팅하고 프로젝트 구조를 잡는 과정을 담아보려 한다.
RN에 Expo를 함께 사용할거라 이 명령어로 프로젝트를 만들었다.
npx create-expo-app@latest
그리고 테일윈드가 너무 좋아서 바~~~로 테일윈드까지 설치 해버렸습니다.
npm install nativewind tailwindcss
React Native에서 스타일을 적용하는 방법은 크게 두 가지인데, 기본적으로 제공하는 StyleSheet를 사용하거나 NativeWind처럼 테일윈드 기반 라이브러리를 사용하는 방법이 있다.
StyleSheet는 애니메이션처럼 동적으로 스타일 값이 바뀌는 경우에 더 유리하지만, 스타일을 확인하려면 정의해둔 곳을 찾아 계속 왔다갔다 해야 한다는 단점이 있다.
반면 테일윈드는 태그 바로 옆에 클래스명으로 스타일이 다 보이니까 훨씬 직관적이었다. 그래서 복잡한 애니메이션에는 StyleSheet를 쓰고 기본적인 UI 스타일 작업에는 테일윈드를 쓰기로 정했다.
이후에는 NativeWind 사용을 위해 초기 파일을 설정해주었다.
이 블로그를 참고해서 현재 프로젝트에서 숨김 처리 되어있는 babel.config.js + metro.config.js 이 파일을 꺼내야 한다.
tailwind.config.js
module.exports = {
content: [
'./app/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}',
],
presets: [require('nativewind/preset')],
theme: {
extend: {
colors: {
primary: '#5B0E14',
secondary: '#F1E194',
ivory: '#F9F5EB',
'primary-light': '#8B3A3A',
'primary-dark': '#4A1515',
white: '#F5F5F5',
black: '#333333'
},
},
},
plugins: [],
};
색상은 지난 글에서 Stitch로 잡아둔 디자인 시스템을 그대로 가져왔다.
global.css
@tailwind base;
@tailwind components;
@tailwind utilities;
babel.config.js
module.exports = function(api) {
api.cache(true);
return {
presets: [
['babel-preset-expo', { jsxImportSource: 'nativewind' }],
'nativewind/babel',
],
};
};
metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withNativeWind } = require('nativewind/metro');
const config = getDefaultConfig(__dirname);
module.exports = withNativeWind(config, { input: './global.css' });
nativewind-env.d
/// <reference types="nativewind/types" />
tsconfig.json
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": ["./*"]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts",
// 추가한 두 줄
"**/*.css",
"nativewind-env.d.ts"
]
}
휴,,, 일단 라이브러리를 위한 초기 설정을 다 해주었다.
구조를 먼저 잡아야지... 하면서도 디자인이 머릿속에 생생할 때 바로 화면을 만들어보고 싶어서 냅다 기본 구조에 코드 먼저 작성해봤다.ㅋㅋㅋㅋㅋ
그러다 모달 부분에서 조금 막혔었는데 이 블로그의 글을 보고 참고해 아주 부드러운 모달을 완성할 수 있었다.

파일이 더 많아지기 전에 구조를 잡아야 할 것 같아서 여기저기 서치해서 구조를 정했다.
구조를 짜면서 헷갈렸던 부분이 두 가지 있었다.
1. screens/ 폴더가 필요할까?
여러 RN 프로젝트 구조를 보면 screens/ 폴더가 있는 경우가 많아서 나도 넣어야 하나 싶었는데, 알고 보니 React Navigation을 직접 쓸 때 필요한 폴더였다. Expo Router는 app/ 폴더 자체가 라우팅이랑 스크린 역할을 동시에 하기 때문에 따로 만들 필요가 없었다.
2. app/과 src/를 분리해야 할까?
Expo Router를 쓰면 app/ 폴더가 라우팅 역할을 고정으로 담당하기 때문에, 여기에 비즈니스 로직까지 섞이면 나중에 관리가 힘들어질 것 같았다. 그래서 라우팅은 app/에, 나머지 로직은 전부 src/ 안에 넣는 구조로 분리하기로 했다.
그렇게 해서 최종적으로 정한 구조가 이거다!
my-app/
├── app/ # Expo Router 라우팅
│ ├── (tabs)/ # 탭 네비게이션
│ ├── (modals)/ # 모달 그룹
│ ├── products/ # 완성품 관련 라우트
│ ├── materials/ # 재료 관련 라우트
│ └── _layout.tsx # 루트 레이아웃
│
├── src/ # 비즈니스 로직
│ ├── components/ # 컴포넌트
│ ├── hooks/ # 커스텀 훅
│ ├── services/ # API 통신
│ ├── store/ # Zustand store
│ ├── types/ # 타입 정의
│ ├── utils/ # 유틸 함수
│ └── constants/ # 상수 (색상, 설정 등)
│
├── assets/ # 정적 파일
│
├── app.json
├── tailwind.config.js
└── package.json
이제 이 구조대로 본격적으로 개발을 시작해보려 한다! 다음 글에서는 실제 화면 구현 과정을 담아볼 예정이다.