매일 하나의 미션을 수행하고, 오늘의 경험을 텍스트와 이미지로 기록하는 모바일 서비스
- 개발 기간: 2025.11.01 ~ 2025.12.17
- 개발 인원: 2명
- 성과: SCSC(서울대 컴퓨터 연구회) 고등학생 온라인 해커톤 최종 4위
- 저장소: React Native 클라이언트 · 백엔드
OIL은 반복되는 일상에 작은 변화를 만드는 일일 미션 서비스입니다. 사용자는 원하는 시간에 오늘의 미션을 받고, 제한 시간 동안 미션을 수행한 뒤 텍스트와 사진으로 경험을 남길 수 있습니다. 완료한 미션과 기록은 다시 열어보며 자신의 변화를 확인할 수 있습니다.
| 기능 | 설명 |
|---|---|
| 회원가입·로그인 | 이메일 기반 인증과 알림 수신 여부, 미션 수신 시간, 닉네임을 설정합니다. |
| 오늘의 미션 | 서버에서 오늘의 미션을 조회하고 타이머와 함께 수행합니다. |
| 수행 기록 | 미션 결과를 텍스트와 이미지로 작성해 완료 처리합니다. |
| 완료 이력 | 완료한 미션 목록과 각 미션의 기록·완료 시점을 조회합니다. |
| 알림 | 미션 수신 시간과 알림 허용 여부를 변경하고 FCM 푸시를 처리합니다. |
| 구성원 | 역할 | 담당 범위 |
|---|---|---|
| 정명우 (tccmw) | Frontend | React Native 앱 구조와 UI/UX, 인증, 미션 수행·기록·이력, REST API 연동, FCM 알림 |
| hefxpzwk | Backend | 인증·사용자·미션·알림 REST API와 서버 비즈니스 로직 |
프론트엔드에서는 화면 퍼블리싱에 그치지 않고 회원가입부터 미션 수행, 기록 작성, 완료 이력 조회까지 이어지는 모바일 사용자 흐름 전체를 설계하고 구현했습니다.
| 분류 | 기술 | 선정 이유 |
|---|---|---|
| App | React Native, React | 하나의 코드베이스로 Android와 iOS 네이티브 기능을 사용하고 빠르게 MVP를 구현하기 위해 선택했습니다. |
| Language | TypeScript | 화면 간 파라미터와 API 응답 형식을 명시해 런타임 오류를 줄이기 위해 사용했습니다. |
| Navigation | React Navigation | 인증 흐름은 Stack, 핵심 화면은 Bottom Tab으로 분리해 화면 구조와 이동 책임을 명확히 했습니다. |
| HTTP | Axios, React Native Config | 공통 Base URL·timeout을 한 곳에서 관리하고 실행 환경별 API 주소를 코드와 분리했습니다. |
| Storage | React Native Encrypted Storage | Access Token과 Refresh Token 등 인증 정보를 일반 저장소가 아닌 암호화 저장소에 보관했습니다. |
| Push | Firebase Cloud Messaging | 로그인 시 조건부 디바이스 토큰 등록과 포그라운드·알림 진입 상태의 메시지 처리를 구현하기 위해 사용했습니다. |
| Media | React Native Image Picker | 미션 수행 결과를 기기 갤러리에서 선택하고 기록 화면에서 바로 확인할 수 있도록 사용했습니다. |
| UI | React Native SVG, Reanimated | 미션 진행도와 상태 변화를 모바일 화면에서 직관적으로 표현하기 위해 사용했습니다. |
- 문제: 회원가입, 시간 설정, 기록 작성 화면에서 키보드가 입력 영역을 가리고 여러 화면을 오가며 사용 맥락이 끊길 수 있었습니다.
- 해결:
KeyboardAwareScrollView기반 공통DismissKeyboardView를 만들고, 휠 형태의 시간 선택 컴포넌트와 Stack/Bottom Tab 내비게이션을 조합했습니다. - 결과: 입력 중에도 현재 화면과 다음 행동을 확인할 수 있고, 인증부터 미션 기록까지 일관된 이동 흐름을 유지했습니다.
- 문제: 토큰을 일반 저장소에 보관하면 노출 위험이 있고, 화면마다 URL·timeout·인증 헤더를 직접 관리하면 통신 코드가 복잡해집니다.
- 해결: 로그인 응답의 토큰을
EncryptedStorage에 저장하고, 환경 변수 기반 Axios 인스턴스와 기능별 API 모듈을 분리했습니다. 인증 요청은 저장된 Access Token을 읽어 Bearer 헤더로 전달합니다. - 결과: 화면 로직과 서버 통신 책임을 분리하고, 인증이 필요한 미션·알림 API의 호출 방식을 일관되게 유지했습니다.
- 문제: 미션 수행, 텍스트·이미지 작성, 완료 처리, 이력 조회가 분리되면 사용자가 자신의 결과를 추적하기 어렵습니다.
- 해결: 오늘의 미션과 타이머를 연결하고, 완료 모달에서 텍스트와 이미지를 받은 뒤 완료 API를 호출했습니다. 완료 목록과 상세 기록 API를 연결해 과거 결과를 다시 확인할 수 있게 했습니다.
- 결과: 미션 조회 → 수행 → 기록 → 완료 → 회고가 하나의 사용자 흐름으로 이어졌습니다.
- 문제: 로그인 시 알림 동의와 OS 권한을 확인해야 하고, 실행 중 수신과 알림을 눌러 진입한 경우의 처리가 달랐습니다.
- 해결: 로그인 성공 후 로컬 알림 동의와 OS 권한이 모두 있을 때만 FCM 토큰을 발급해 서버에 등록했습니다. 포그라운드 메시지는 알림창으로 표시하고, 백그라운드 또는 종료 상태에서 알림을 눌러 진입하면 Navigation Ref를 통해 홈 화면으로 이동하도록 구성했습니다.
- 결과: 로그인, 푸시 수신, 알림 진입으로 이어지는 현재 앱의 실행 경로를 연결했습니다.
아래 다이어그램은 앱의 구성을 레이어 단위로 단순화한 개요이며, 실제 호출 순서는 이어지는 데이터 흐름 설명을 기준으로 합니다.
flowchart LR
U["사용자"] --> N["React Navigation<br/>Stack + Bottom Tab"]
N --> P["화면·컴포넌트"]
P --> A["기능별 API 모듈<br/>auth / mission / alarm"]
A --> X["Axios Instance<br/>Base URL + timeout"]
X --> B["OIL REST API"]
P <--> S["EncryptedStorage<br/>Access·Refresh Token / 알림 동의"]
P --> I["Image Picker<br/>미션 기록 이미지"]
F["Firebase Cloud Messaging"] --> H["Push Hooks·Service"]
H --> P
P --> E["외부 명언 API"]
현재 main 브랜치의 실제 데이터 흐름은 다음과 같습니다.
- 로그인:
SignIn이 로그인 API를 호출하고 Access Token, Refresh Token, 닉네임을EncryptedStorage.auth에 저장합니다. - 푸시 토큰 등록: 로그인 직후
getFcmToken이EncryptedStorage.alarm의 동의 여부와 OS 권한을 확인합니다. 조건을 만족하면 FCM 토큰을 받아 Push Token API로 서버에 등록합니다. - 오늘의 미션:
BottomTabNavigator가 저장된 Access Token으로 오늘의 미션을 조회하고, 응답을MainPage와TaskPage에 props로 전달합니다. - 미션 완료 기록:
MainPage의 로컬 타이머가 완료 상태를 만들면 기록 모달이 텍스트와 기기 이미지 URI를 받습니다. 모달은 저장된auth와missions값을 읽어 완료 API를 호출합니다. - 완료 이력:
MainPage와TaskPage가 완료 목록 API를 호출하고, 항목 선택 시 상세 API의 응답을 기록 모달에 표시합니다. - 명언:
MainPage와MyPage가 Advice API 모듈을 호출합니다. 이 모듈은 공통 Axios 인스턴스에 외부 API의 절대 URL을 전달합니다. - 푸시 수신: 포그라운드 메시지는
useForegroundPush가 알림창으로 표시합니다. 백그라운드 또는 종료 상태에서 연 알림은App.tsx가 받아 Navigation Ref로 홈 화면에 연결합니다. - 알림 변경:
ChangeAlarmPage가 미션 수신 시간과 알림 활성 상태를 서버·로컬 저장소에 반영한 뒤 서버의 Push Token 삭제 API를 호출합니다.
usePushToken은 앱 시작 시 저장된 로그인 정보와 알림 동의를 확인해 토큰을 동기화하며, 토큰 갱신 시에도 서버 값을 갱신합니다. 알림 비활성화와 로그아웃 시에는 서버 및 로컬 FCM 토큰을 정리합니다. 기록 완료에 필요한EncryptedStorage.missions를 저장하는 코드는 현재 저장소에서 확인되지 않습니다.
.
├── App.tsx # 앱 진입점, Stack Navigation, 푸시 이벤트
├── src
│ ├── api # Axios 인스턴스와 기능별 REST API
│ ├── components # 입력, 버튼, 시간 선택, 기록 모달
│ ├── hooks # 타이머와 푸시 처리
│ ├── navigation # Bottom Tab과 Navigation Ref
│ ├── pages
│ │ ├── auth # 로그인·회원가입·초기 설정
│ │ └── main # 오늘의 미션·과제 이력·마이페이지·알림
│ └── services # FCM 토큰 서비스
└── types # Navigation 등 공통 타입
- Node.js 20 이상
- npm
- React Native 0.83 개발 환경
- Android: Android Studio, Android SDK, JDK 17 이상
- iOS: macOS, Xcode, CocoaPods
자세한 네이티브 환경 설정은 React Native 개발 환경 설정 문서를 참고하세요.
git clone https://github.com/team-OIL/OIL-RN.git
cd OIL-RN
npm ci프로젝트 루트에 .env 파일을 만들고 백엔드 API 주소를 입력합니다.
API_BASE_URL=https://api.example.com| 변수 | 필수 | 설명 |
|---|---|---|
API_BASE_URL |
예 | OIL 백엔드 REST API의 Base URL |
실제 서버 주소나 비밀값이 포함된
.env파일은 저장소에 커밋하지 마세요.
FCM을 사용하는 네이티브 빌드에는 플랫폼별 Firebase 설정도 필요합니다.
- Android:
android/app/google-services.json - iOS:
- Xcode OIL 타깃의
PRODUCT_BUNDLE_IDENTIFIER와 동일한 Bundle ID로 Firebase iOS 앱을 등록합니다. - Firebase Console에서 받은 파일을
ios/OIL/GoogleService-Info.plist에 둡니다. OIL 타깃의 Resources에는 이 경로가 이미 연결되어 있습니다. - Apple Developer에서 해당 App ID의 Push Notifications를 활성화하고 APNs 인증 키를 Firebase Console의 Cloud Messaging 설정에 업로드합니다.
- Xcode OIL 타깃의
npm run ios는 빌드 전에 iOS Firebase 파일과 필수 키를 검사합니다. 실제 푸시 수신은 APNs 서명이 필요한 실제 iOS 기기에서 확인해야 합니다.
Metro 개발 서버를 실행합니다.
npm start새 터미널에서 대상 플랫폼을 실행합니다.
# Android
npm run android
# iOS
bundle install
cd ios
bundle exec pod install --repo-update
cd ..
npm run ios푸시 알림은 Google Play Services를 지원하는 Android 에뮬레이터 또는 실제 Android 기기, APNs 프로비저닝이 적용된 실제 iOS 기기에서 확인합니다.
npm run lint
npm test| 영역 | 코드 |
|---|---|
| 앱 진입점·화면 이동·푸시 진입 | App.tsx, navigation |
| 인증·초기 사용자 설정 | auth pages, auth API |
| 오늘의 미션·타이머 | MainPage.tsx, useTimer.tsx |
| 기록 작성·완료 이력 | imageModel.tsx, TaskPage.tsx, Mission API |
| API 공통 설정·토큰 관리 | axios.tsx, pushToken.service.ts |
| 알림 설정·FCM 처리 | ChangeAlarmPage.tsx, push hooks |
- 모바일 입력·화면 UX: #3 키보드 최적화, #5 스크롤 최적화, #31 과제 타이머
- 인증·API 구조: #46 Axios 기본 설정, #47 로그인 API, #67 토큰 저장
- 미션 기록·조회: #57 완료 기록 모달, #73 완료 목록, #75 미션 상세, #76 오늘의 미션, #78 미션 완료
- 알림: #86 알림 변경 화면, #88 알림 설정 API, #90 FCM 푸시, #92 푸시 토큰 삭제
전체 작업 이력은 OIL-RN Pull Requests에서 확인할 수 있습니다.