들어가며

React Native와 Expo를 활용해 iOS에서 동작하는 간단한 ToDo 앱을 만들어보기로 했다. 평소에는 웹 개발을 주로 해왔기 때문에 네이티브 앱을 직접 만들어본 경험은 없었다. 그래서 이번엔 한 번 진짜 네이티브 구조를 직접 구현해 보자는 목표로 시작했다. 생각보다 빠르게 앱을 구성할 수 있었고, 그 과정에서 겪은 시행착오들을 글로 정리해 본다.
왜 ToDo였을까?
ToDo는 거의 모든 웹 개발 학습과 실무에서 기본이 되며, 동시에 확장성도 뛰어난 기능이다.
CRUD(Create, Read, Update, Delete)의 기본적인 흐름을 전부 포함하고 있어서, 컴포넌트 구성, 상태 관리, 입력/출력/삭제 로직을 체계적으로 연습하기에 딱 좋은 구조다. 또한 이후 필터링, 정렬, 알림, 네트워크 연동 등으로 얼마든지 발전시킬 수 있다는 점에서도 좋은 연습용 프로젝트라고 생각했다.
Expo를 선택한 이유

개발을 할 때 가장 자주 마주치는 문제 중 하나는 개발 환경 설정이다. 플랫폼 별로 다르고, 툴 설치부터 SDK 설정까지 예상치 못한 변수도 많다. 그래서 React Native를 시도하기 전에는 “Xcode는 꼭 설치해야 하지 않을까?”, “시뮬레이터는 어떻게 실행하지?” 같은 고민부터 들었다.
그런데 Expo는 이 모든 걱정을 거의 날려줬다. Expo는 React Native 프로젝트를 더 쉽게 실행하고 배포할 수 있게 도와주는 프레임워크이자 툴킷이다. 그 안에는 개발 서버, 빌드 도구, 디버깅 환경, 그리고 테스트 앱까지 포함되어 있다.
- 설치와 실행이 빠르고,
- iOS 시뮬레이터 없이도 Expo Go 앱으로 테스트 가능하며,
- OTA 업데이트 기능까지 지원하니 배포도 편리하다.

앱은 iOS에서 테스트했으며, App Store에 있는 Expo Go 앱을 설치한 뒤, QR코드를 스캔해서 바로 실행해 볼 수 있었다. 생각보다 빠르게 구동되었고, 앱 전환이나 렌더링에서 버벅거림이 없었던 것도 인상적이었다. 무엇보다 노트북에서 npx expo start 한 줄로 실행 환경이 즉시 만들어지는 것도 인상 깊었다. 원래는 Xcode를 반드시 설치해야 하지 않을까 걱정했는데, 개발에 대한 접근성이 이렇게 좋을 줄은 몰랐다.
Stitch로 시작한 디자인

기능 구현을 시작하기 전, 디자인 템플릿을 빠르게 만들고 싶었다. 마침 구글에서 최근에 공개한 도구인 Stitch를 한 번 사용해볼까 싶어서 도입해 봤다.
Stitch는 내가 원하는 UI를 말로 설명하면, 이를 바탕으로 깔끔한 디자인 시안을 빠르게 만들어준다. 특히 좋았던 점은:
- 피그마 + HTML 형태로 즉시 결과물을 받을 수 있다는 점
- Tailwind 기반의 구조여서 React Native 스타일링에도 참고하기 좋았던 점
- 대화형으로 원하는 UI 방향을 수정 요청할 수 있다는 점
아직 완벽하게 내 마음대로 되지는 않지만, 코드와 이미지가 자동 생성되고, 실시간으로 디자인을 조율해 나갈 수 있는 방식은 꽤 인상 깊었다. 앞으로 발전이 기대되는 도구다.
기본 기능 설계
처음 만들고자 했던 기능은 아주 기본적인 ToDo 기능이다:
- 할 일 입력
- 리스트 렌더링
- 체크박스 토글
- 수정 및 삭제 기능
// 주요 상태 구조
{
id: string,
text: string,
completed: boolean,
editing: boolean
}
이걸 기반으로 TodoItem, TodoInput, TodoToolbar, TodoResetButton까지 4개의 컴포넌트로 쪼개고, index.tsx에서 이들을 조합하는 방식으로 구조화했다.
또한 앱 구조는 Expo Router 기반으로 자동 구성되는데, app/(tabs)/index.tsx 파일이 탭 내 홈 화면이 되고, 각 파일이 경로가 되는 방식이어서 처음 써보는 나도 금방 이해할 수 있었다.

React 기반의 파일 기반 라우팅을 그대로 가져온 덕분에, 프레임워크를 쓴다는 느낌이 강하게 들었고, 디렉토리 구조만 잘 맞추면 화면 전환이 자연스럽게 동작하는 점이 좋았다.
FlatList 중심 구조로 리팩토링하기
처음에는 ParallaxScrollView 안에 FlatList를 넣었는데, 다음과 같은 경고가 떴다:
VirtualizedLists should never be nested inside plain ScrollViews
FlatList는 성능 최적화를 위해 가상 리스트(VirtualizedList) 구조를 사용하는데, 같은 방향의 스크롤을 중첩해서 사용하면 가상화가 깨지기 때문이다.
그래서 구조를 완전히 바꿨다. FlatList를 최상위 컨테이너로 사용하고, 상단에는 ListHeaderComponent, 하단에는 ListFooterComponent를 활용하는 방식으로 리팩토링했다.
이 과정에서 React Native에서 기본적으로 제공하는 View, Text, TextInput, FlatList, Pressable 같은 컴포넌트들이 얼마나 직관적인지도 함께 체감할 수 있었다. HTML과 유사하지만 스타일링은 Flexbox 기반이라 조금 다르게 접근해야 했다.
또 한 가지 놀라웠던 점은 useBottomTabBarHeight() 같은 훅이 기본으로 준비돼 있다는 것이었다. 삭제 버튼처럼 화면 하단에 고정된 요소를 배치할 때, 탭 바의 높이를 자동으로 계산해서 위치를 조절할 수 있었고, 이걸 단 한 줄의 코드로 해결할 수 있다는 점이 무척 인상 깊었다. 기존 React 프로젝트에서는 이런 경우 직접 수치를 조정하거나 레이아웃을 트릭으로 구성했었기 때문에, 확실히 UI 프레임워크가 설계되어 있다는 느낌을 받았다.
상태 관리에 대한 인상
이번 프로젝트를 진행하면서 또 하나 크게 느낀 점은, React Native가 기본적으로 React 기반이기 때문에 useState 같은 상태 관리 방식이 그대로 적용된다는 점이었다. 웹에서 하던 방식 그대로 useState로 리스트를 구성하고, 업데이트와 삭제까지 자연스럽게 구현할 수 있었다. 막연하게 "앱 개발은 어렵다"라고 생각했었는데, 구조를 한 번 이해하고 나니 생각보다 익숙한 방식으로 작업이 진행되었다.
삭제 버튼을 화면 하단에 고정하기
리스트 아래쪽에 Delete Selected 버튼이 위치했는데, 스크롤을 내릴 때 같이 내려가면서 버튼이 화면에서 사라지는 UX 문제가 있었다. 이를 해결하기 위해 삭제 버튼을 position: 'absolute'로 분리해 화면 하단에 고정시켰다.
<View style={{
position: 'absolute',
bottom: 20,
left: 16,
right: 16,
zIndex: 10,
}}>
<TodoToolbar />
</View>
또한 FlatList에 paddingBottom을 넉넉히 넣어 버튼이 가려지지 않도록 처리했다.
탭 바와 겹치는 문제 해결하기
Expo Router에서 기본 탭 네비게이션을 쓰면 화면 하단에 BottomTabNavigator가 자동으로 붙는다. 그런데 고정된 삭제 버튼이 이 탭 바와 겹치는 문제가 발생했다.
이를 해결하기 위해 @react-navigation/bottom-tabs의 useBottomTabBarHeight() 훅을 사용해 탭 바 높이를 계산하고, 삭제 버튼의 bottom 값을 그만큼 올려주었다.
const tabBarHeight = useBottomTabBarHeight();
<View style={{ bottom: tabBarHeight + 16 }} />
전체 초기화 기능 추가
TodoResetButton 컴포넌트를 만들어 TodoInput 아래에 배치했다. 클릭하면 전체 할 일을 초기화하는 간단한 기능이지만, 기능적으로 깔끔하게 마무리되는 느낌을 줬다.
const resetAll = () => setTodos([]);
마무리하며

처음엔 "간단하게 만들어보자"고 시작한 프로젝트였지만, 생각보다 UI에서 고려할 게 많았고, React Native가 자체적으로 이런 부분을 고려해서 제공해 주는 훅이나 레이아웃 구성 방식이 많다는 것도 인상적이었다. 버튼 위치, 리스트 최적화, 탭바 대응 등 앱의 기본 구성 요소 하나하나가 사용자 경험에 큰 영향을 준다는 걸 직접 느낄 수 있었다.
또한 처음 경험한 Stitch 디자인 도구나 React Native의 FlatList 구조, App Router 기반의 자동 라우팅 구조 덕분에 네이티브 앱 구조를 조금 더 이해하게 되었고, 앞으로 더 복잡한 앱 구조를 만들어보고 싶은 욕심도 생겼다.
개인적으로는 처음 접한 React Native라는 도구가 생각보다 익숙했고, 웹 개발자 입장에서 크게 이질감이 없다는 점이 특히 좋았다. 이 프로젝트를 시작으로 다양한 모바일 앱 구조를 실험해보고 싶어졌다.
'DEV > FE' 카테고리의 다른 글
[npm deep dive] 2장 (3) | 2025.07.26 |
---|---|
Next.js + MDX 블로그 만들기 - 1 (0) | 2025.05.31 |
네이버 2025 공채 1차 면접까지 회고 (6) | 2025.05.17 |
Tanstack Table 쓰기 전에 공식문서 읽어보기(번역과 함께) (0) | 2025.04.04 |