일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- rn
- motion.div
- no-permission-handler-detected
- Promise
- javascript
- await
- debounce
- Hash-table
- promise.all
- private-access-to-photos
- React-Native업데이트
- named type
- react-native-permissions
- react-native-image-picker
- react-native
- RN업데이트
- Swift
- RN새로운아키텍쳐
- hydration mismatch
- async
- react animation
- Throttle
- 비동기
- animation
- axios
- react
- ios
- CS
- react native
- RN아키텍쳐
- Today
- Total
하루살이 개발일지
Expo-router 의 개념과 사용 본문
React Native 공식문서를 보면 알 수 있다시피 Expo를 강추하고 있다.
그래서 Expo를 이용해 프로젝트르 세팅하는 중 라우터 구현 방식에 대한 기술적 의사결정이 필요했다. 지금까지는 React navigation만 사용했는데, Expo Router도 한번 사용해 보고싶었다.
우선 Expo router란, React Native와 웹 앱에서 사용할 수 있는 파일 기반 라우팅 라이브러리이다. iOS, Android, 웹에서 동일한 라우팅 구조를 사용할 수 있다. 파일 시스템을 기반으로 동작하며, 디렉터리에 파일을 추가하면 해당 파일이 자동으로 라우트로 등록된다.
Expo Router의 특징
Expo router는 다음과 같은 특징이 있다.
- 네이티브 지원: React Navigation을 기반으로 하여 플랫폼별 최적화
- 공유 가능: 각 화면이 자동으로 딥 링크를 지원
- 오프라인 지원: 네트워크 없이도 동작하며 새 버전은 자동 업데이트
- 최적화: 필요한 시점에 화면 로드, 빠른 개발 환경 제공
- 유니버설: Android, iOS, 웹에서 통합된 구조 제공
- 검색 가능: 웹에서 정적 렌더링, 네이티브 앱은 유니버설 링크 지원
Expo router와 React Navigation 비교
우선 React Navigation은 코드 기반 라우팅을 사용한다. 그래서 모든 경로(route)를 코드로 명시적으로 정의해야 하며, 복잡한 커스터마이징에 용이하다. 빈면 Expo Router는 파일 기반 라우팅을 사용하는데, 이는 Next.js의 라우팅 방식과 유사하다. Expo Router는 라우팅 설정이 간단하며 디렉토리에 파일을 추가하기만 하면 자동으로 라우트로 등록된다.
또한 React Navigation은 웹 플랫폼에 대해서는 @react-navigation/web을 추가로 설정해야 하지만, Expo Router는 iOS, Android, 웹을 한번에 처리할 수 있다.
React Navigation | Expo Router | |
라우팅 방식 | 코드 기반 라우팅 | 파일 기반 라우팅 |
플랫폼 통합 | 웹 추가 설정 필요 | iOS, Android, Web 통합 |
딥 링크 | 수동 설정 | 기본 제공 (자동 설정) |
SEO | 별도 설정 | 웹에서 정적 렌더링 및 검색 엔진 인덱싱 |
네이티브 최적화 | 완전 네이티브 지원 가능 | React Navigation 기반 |
사용 상황 | 복잡한 네비게이션 로직이 필요한 경우 Expo 사용하지 않는 프로젝트 |
간단하게 라우팅 설정할 때 Expo 기반의 프로젝트 iOS, Android, 웹에서 통합된 라우팅이 필요한 경우 |
Expo Router로 페이지 만들기
Expo Router는 파일 기반 라우팅 방식을 사용한다.
즉, app/ 디렉토리 안에 파일을 생성하면, 해당 파일이 자동으로 앱의 라우터로 등록된다.
index.tsx 파일은 해당 폴더의 기본 경로를 의미하는데, 예를 들어 app/settings/index.tsx는 /settings 경로로 매칭된다.
app/
├── index.tsx # '/' 경로
├── home.tsx # '/home' 경로
├── [user].tsx # '/expo' 또는 '/evanbacon' 같은 동적 경로
├── settings/
│ ├── index.tsx # '/settings' 경로
│ ├── _layout.tsx
페이지 정의 방식
Expo Router에서 페이지는 app/ 디렉토리 안의 파일에서 React 컴포넌트를 export default로 내보내는 방식으로 정의된다.
(export const 사용 시 페이지로 인식 안됨)
// app/index.tsx
import { Text } from 'react-native';
export default function Page() {
return <Text>Top-level page</Text>;
}
플랫폼별 확장자 지원
Expo Router 3.5.x부터는 플랫폼별 파일 확장자를 지원한다.
app/
├── _layout.tsx # 모든 플랫폼에서 사용
├── _layout.web.tsx # 웹에서만 사용
├── index.tsx # 모든 플랫폼에서 사용
├── about.tsx # 모든 플랫폼에서 사용
├── about.web.tsx # 웹에서만 사용
- _layout.web.tsx → 웹 전용 레이아웃
- _layout.tsx → iOS, Android 등 모든 플랫폼에서 사용
- about.web.tsx → 웹 전용 페이지
- about.tsx → 모든 플랫폼에서 사용
또한, 기본 파일 없이 플랫폼별 확장자만 사용하면 오류가 발생한다. 즉, about.web.tsx가 있다면 about.tsx도 있어야 한다.
동적 라우트 (Dynamic Routes)
파일명에 대괄호 [ ]를 사용하면 동적 경로를 만들 수 있다.
app/
├── blog/
│ ├── [slug].tsx # '/blog/123' 같은 경로
│ ├── [...rest].tsx # '/blog/123/settings' 같은 다중 경로
- app/blog/[slug].tsx → /blog/123 같은 동적 경로를 지원
- app/blog/[...rest].tsx → /blog/123/settings 같은 다중 경로를 지원
이때, 정확한 경로가 먼저 매칭되는데, 예를 들어 /blog/bacon.tsx 파일이 있다면 /blog/bacon이 먼저 매칭되고, 그다음 /blog/[id].tsx가 실행된다.
동적 경로에서 값 가져오기
동적 경로의 값은 useLocalSearchParams()를 사용하여 가져올 수 있다.
// app/blog/[slug].tsx
import { useLocalSearchParams } from 'expo-router';
import { Text } from 'react-native';
export default function Page() {
const { slug } = useLocalSearchParams();
return <Text>Blog post: {slug}</Text>;
}
이렇게 하면 /blog/123에서 slug 값이 123으로 전달된다.
비 라우트(Non-route) 파일
app/ 디렉토리 안에는 모든 파일이 라우트 또는 레이아웃이어야 한다.
즉 components, hooks 같은 비라우트 파일은 app/ 디렉토리 바깥에 위치해야 한다.
🚫 잘못된 구조
app/
├── profile.tsx
├── ProfileImageComponent.tsx # ❌ 비라우트 파일이 app 폴더에 있음 (오류 발생 가능)
✅ 올바른 구조
app/
├── profile.tsx
components/
├── ProfileImageComponent.tsx # ✅ 올바른 위치
이유: 라우트와 비라우트 파일이 섞이면 프로젝트가 비구조적으로 변할 수 있기 때문
추천 프로젝트 구조
Expo Router에서는 라우트는 네비게이션 패턴 기준 / 컴포넌트는 기능(feature) 기준으로 정리하는 것을 추천하고 있다. 컴포넌트와 라우트를 분리하면 유지보수가 쉬워진다는 장점이 있다.
app/
├── sign-in/
├── sign-out/
├── profile/
├── tasks/
components/
├── authentication/
├── tasks/
│ ├── overview/
├── profile/
│ ├── [user]/
- sign-in 및 sign-out은 인증 관련 페이지
- components/authentication/에는 인증 관련 UI 컴포넌트
- profile/[user]는 프로필 기능 관련 페이지
경로 별칭 (Path Aliases)
경로 별칭을 사용하면 코드가 더 깔끔해질 수 있다. 이와 같은 경로 별칭들은 tsconfig.json에서 설정할 수 있다.
예를 들어, components/Container.tsx를 app/index.tsx에서 가져온다고 했을 때 ->
기존 방식
import { Container } from '../../components/Container';
경로 별칭 사용
import { Container } from '~/components/Container';
예약어 (Reserved Keywords)
Expo Router는 React Navigation을 기반으로 동작하므로, 특정 단어(예약어)는 동적 경로에서 사용할 수 없다.
🚫 사용 금지 단어 : screen, params, key
즉, app/[screen].tsx 같은 경로는 사용할 수 없다.
<정리>
1. 파일 기반 라우팅을 사용하여 app/ 폴더 내부 파일이 자동으로 경로가 됨
2. 플랫폼별 확장자(.web.tsx, .ios.tsx 등)를 사용하여 플랫폼별 페이지 제공 가능
3. 동적 라우트 ([slug].tsx, [...rest].tsx 등)를 사용하여 유연한 경로 설정 가능
4. 비라우트 파일은 app/ 디렉토리 안에 둘 수 없음 (components/ 같은 별도 디렉토리에 저장)
5. 라우트와 컴포넌트를 분리할 것
6. 경로 별칭을 활용하여 import 경로를 단순화할 것
7. 예약어(screen, params, key)는 동적 라우트에서 사용 불가능
Expo Router에서 페이지 이동하기 (Navigation)
1. Link 태그란
Expo Router에서는 웹의 <a> 태그처럼 동작하는 <Link> 컴포넌트를 사용하여 페이지 간 이동을 한다. (href 속성 사용)
[기본 사용법]
import { View } from 'react-native';
import { Link } from 'expo-router';
export default function Page() {
return (
<View>
<Link href="/about">About</Link>
<Link href="/user/bacon">View user</Link>
</View>
);
}
- /about 페이지로 이동하는 링크 생성
- /user/bacon 경로로 이동하는 링크 생성 (동적 라우트)
2. 버튼으로 네비게이션
<Link> 컴포넌트는 기본적으로 텍스트 링크를 위해 설계되어 있어, 자식 요소를 <Text> 컴포넌트로 감싸게 된다. 이 때문에 <Button>, <Pressable>, <TouchableOpacity> 같은 요소를 감싸는 경우, 예상과 다르게 동작할 수 있다.
이 동작을 변경하려면 asChild 속성을 사용하면 되는데, asChild를 적용하면 <Link>가 직접 자식을 감싸는 대신 첫 번째 자식 요소로 <Link>의 역할을 그대로 전달한다.
이때, 자식 컴포넌트는 onPress 및 onClick 속성을 지원해야 하며 href와 role 속성도 함께 전달된다.
import { Pressable, Text } from 'react-native';
import { Link } from 'expo-router';
export default function Page() {
return (
<Link href="/other" asChild>
<Pressable>
<Text>Go to Other Page</Text>
</Pressable>
</Link>
);
}
- asChild를 사용하면 <Link>가 <Pressable>을 직접 감싸지 않고, 네비게이션 기능만 추가한다.
[asChild 속성을 적용하지 않으면?]
아래 코드는 각각 /new로 이동하고자 하는 버튼과 /details 로 이동하려는 버튼을 <Link> 태그로 감싼 코드이다.
/new로 이동하려는 버튼은 asChild 속성을 부여하지 않았고, /details 경로로 이동하고자 하는 버튼에는 asChild 속성을 부여했다.
아래의 <Button> 은 TouchableOpacity를 기반으로 만들어진 커스텀 컴포넌트이다.
// app/index.ts
<>
<Stack.Screen options={{ title: 'Home' }} />
<Container>
<ScreenContent path="app/index.tsx" title="Home" />
<Link href={{ pathname: '/new', params: { fruit: 'cherry' } }}>
<Button title="Go new route" />
</Link>
<Link href={{ pathname: '/details' }} asChild>
<Button title="Go with details route" />
</Link>
</Container>
</>
위의 코드를 보면 asChild 속성의 유무에 따라 UI가 달라지는 것을 확인할 수 있다. 또한 각각의 버튼의 동작도 기대한 것과 달랐다.
asChild 속성을 부여하지 않은 버튼은 클릭해도 /new로 이동하지 않았고, /details로 이동하는 버튼은 정상적으로 동작했다.
1. <Link>가 <Button>을 감싸는 경우 (asChild 없음)
<Link href={{ pathname: '/new', params: { fruit: 'cherry' } }}>
<Button title="Go new route" />
</Link>
Expo Router의 <Link>는 기본적으로 <Text>를 감싸도록 되어 있기 때문데, 위 코드를 실행하면, React Native에서는 내부적으로 이렇게 렌더링된다 :
<Text>
<TouchableOpacity>
<Text>Go new route</Text>
</TouchableOpacity>
</Text>
여기서 문제가 발생하는 지점은 바깥쪽 <Text>가 <TouchableOpacity>를 감싸면서 클릭 이벤트(onPress)가 <Link>로 전달되지 않는 것이다. React Native에서는 TouchableOpacity가 <Text> 내부에 있을 때 onPress 이벤트가 정상적으로 버블링(전파)되지 않을 가능성이 크다. 즉,
- 버튼을 클릭하면 TouchableOpacity의 onPress가 실행됨
- 하지만 onPress 이벤트가 <Text>의 부모(Link)로 전달되지 않음
- 결국 <Link>가 href="/new"를 실행할 기회를 얻지 못함
- 결과적으로 /new 경로로 이동하지 않음
결론적으로, 버튼의 onPress 이벤트가 <Link>로 전달되지 않는 이유는 <Text>가 이벤트 흐름을 가로막기 때문이다.
마찬가지로 스타일이 깨지는 이유 또한 <Text>가 부모 요소로 추가되면서 발생할 수 있다.
2. asChild를 적용한 경우
<Link href={{ pathname: '/details' }} asChild>
<Button title="Go with details route" />
</Link>
asChild 속성을 부여하면 <Link>가 <Text>를 감싸지 않게 된다. 이 경우 <Link>가 감싸고 있는 <Button>을 자신처럼 동작하도록 만든다. 즉, <Button> 자체가 <Link>처럼 동작하도록 변환한다.
이는 <Button>의 onPress가 내부적으로 <Link>의 href를 실행하도록 변경되며, 따라서 버튼을 클릭했을 때 /details 경로로 정상적으로 이동하게 된다.
마찬가지로 스타일이 유지되는 이유 또한 <Link>가 <Button>을 감싸지 않기 때문에 불필요한 스타일 계층이 사라지므로 <Button>의 스타일이 그대로 유지된다.
즉 React Native 환경에서 asChild를 사용하지 않으면 <Link>가 가진 특징 때문에 이벤트 흐름이 방해될 수 있어 <Link>의 네비게이션 기능이 실행되지 않을 수 있다. 반면 asChild를 사용하면 <Button> 자체가 <Link>처럼 동작하게 되어 정상적으로 이동한다.
3. 네비게이션 구조
Expo Router는 스택(Stack) 기반 네비게이션을 사용한다. 즉, 이전 페이지 위에 새로운 페이지를 추가하는 방식이다.
예를 들어,
- /feed 페이지에서 /profile로 이동하면 → ['/feed', '/profile'] 이렇게 스택이 쌓인다.
- /settings로 이동하면 → ['/feed', '/profile', '/settings'] 이렇게 스택이 쌓인다.
웹과 비슷하지만 네이티브에서는 여러 개의 독립적인 자식 스택을 가질 수 있는데, 이를 병렬 라우팅(Parallel Routing)이라고 한다.
병렬 라우팅 예제
위 예제에서 app/_layout은 <Tabs /> 네비게이터를 사용하고, 다른 두 개의 레이아웃 파일은 <Stack />을 렌더링한다.
즉, 탭 네비게이션을 사용하는 부모 스택 안에 두 개의 독립적인 스택이 존재하는 구조인데, 이러한 구조에서 페이지를 이동하면 하나의 단일 스택이 아니라, 두 개의 개별적인 스택이 유지되는 방식이 된다.
예를 들어, 사용자가 /apple → /orange를 방문한 후, /carrot → /potato로 이동했다면, 히스토리는 다음과 같이 유지된다.
[['/apple', '/orange'], ['/carrot', '/potato']]
즉, 각 네비게이션 그룹(fruit, vegetable)은 서로 독립적인 히스토리를 가진다.
[스택 네비게이션과 back의 복잡성]
스택에서 앞으로 이동(페이지 추가)은 간단한데, 뒤로 가기(이전 페이지로 이동)는 히스토리 구조에 따라 다르게 동작할 수 있다.
모바일 앱에서 자주 발생하는 문제 :
- 특정 링크를 클릭하여 앱이 실행되었을 때, 뒤로 가기를 누르면 히스토리에 없는 화면들이 먼저 나오고 나서 앱이 종료되는 경우가 있다.
- 이는 스택이 독립적으로 유지되기 때문인데, 각 스택의 히스토리가 개별적으로 관리되기 때문에 발생한다.
- 즉, 단순히 back()을 실행하면 예상하지 못한 페이지가 먼저 나올 수도 있다.
스택의 push와 pop 개념
- 새로운 페이지로 이동하는 것은 push (스택에 추가)
- 뒤로 가기는 pop (스택에서 제거)
[Expo Router의 뒤로 가기 기능]
Expo Router는 스택 네비게이션에서 뒤로 가기를 처리할 수 있도록 여러 가지 네비게이션 함수를 제공한다.
back()
- 현재 스택에서 이전 페이지로 이동
- 동일한 스택 내에서만 작동하며, 다른 스택으로는 이동하지 않는다.
dismiss()
- 현재 스택 내에서만 뒤로 가기
- back()과 유사하지만, 특정한 모달 또는 임시 화면을 닫는 데 자주 사용
dismissTo(route)
- 특정 경로까지 스택을 정리하면서 이동
- 방문한 적이 있던 경로라면 해당 경로까지 스택을 제거하며 돌아감
- 만약 경로가 현재 스택에 없다면, 새로운 페이지를 푸시(push)하여 이동
- 즉, 뒤로 가기뿐만 아니라 앞으로도 이동할 수 있는 유연한 함수
replace(route)
- 기존 스택을 유지한 채, 현재 페이지를 새로운 페이지로 교체
- 예를 들어, ['/home', '/page1'] 상태에서 replace('/page2')를 하면 ['/home', '/page2']가 됨 (뒤로 가기하면 /home으로 이동)
4. 경로 이동하는 방법들
1. 절대 경로
<Link href="/profile/settings">Go to Settings</Link>
2. 상대 경로
<Link href="../settings">Go to Settings</Link>
3. 객체 형태로 동적 파라미터 전달
<Link href={{ pathname: '/user/[id]', params: { id: 'bacon' } }}>
View User
</Link>
5. 상대 경로 (Relative Navigation)
- Relative URL이란 ./로 시작하는 URL을 의미한다.( ./article 또는 ./article/ 같은 거)
- 현재 화면의 URL을 document URL이라고 부르는데, 이건 보통 마지막에 /가 붙지 않는 URL이다.
- relative URL은 현재 사용자가 보고 있는 URL(=document url)을 기준으로 경로를 계산한다.
예를 들어, 현재 /route/v1 페이지에 있다면,
- 이 페이지의 document URL은 /route/v1이다.
- 즉, ./article 같은 relative URL을 사용하면, /route/article로 이동한다.
- 여기서 중요한 점은 v1 뒤에 /가 없기 때문에, URL이 v1을 파일처럼 인식하고 ./article을 v1의 형제 경로로 해석한다는 것이다.
(+참고)
웹 서버에서 경로를 해석할 때, 보통 URL을 폴더(디렉터리) 기반으로 볼지, 파일 기반으로 볼지에 따라 해석 방식이 달라진다.
디렉터리 기반인 경우는 URL이 /route/v1/ (마지막 / 포함) 인데,
- 이 URL은 /route/v1이 디렉터리(폴더)로 인식된다.
- 따라서 ./article을 붙이면, 그 폴더 안에서 article을 찾는다.
- 결과: /route/v1/article
파일 기반으로 보는 경우는 URL이 /route/v1 (마지막 / 없음) 인데,
- 이 URL은 v1이 파일처럼 인식될 수도 있다.
- 따라서 ./article을 붙이면, 현재 URL과 같은 위치에서 article을 찾는다.
- 결과: /route/article (즉, v1과 같은 디렉터리 수준에서 해석됨)
[relativeToDirectory 옵션이 필요한 이유]
보통 index.tsx 같은 파일이 현재 URL을 렌더링하고 있을 때, 원하는 동작은 현재 URL을 하나의 폴더처럼 해석하는 것이다.
하지만 relative URL이 기본적으로 현재 URL을 파일처럼 해석하는 방식 때문에 예상한 경로가 나오지 않을 수도 있다.
이 문제를 해결하기 위해 relativeToDirectory: true 옵션을 사용하면, 현재 URL을 디렉터리처럼 간주하여 relative URL을 해석할 수 있다.
relativeToDirectory 옵션을 사용했을 때와 사용하지 않았을 때 :
6. Link에서 네비게이션 방식 변경
기본적으로 <Link>는 push 방식이다. 즉, 현재 페이지 위에 새로운 페이지를 추가한다.
<Link push href="/feed">Go to Feed</Link>
<Link href="/feed">Go to Feed</Link>
위 두 개의 <Link>는 동일한 동작을 한다.
push 속성을 명시하지 않더라도 기본값이 true이므로 router.push('/feed')와 같은 동작을 한다.
//replace와 동일하게 동작
<Link href="/feed" push={false}>Go to Feed</Link>
<Link>에서 push={false}로 설정하면 router.replace() 방식으로 동작하게 된다. 즉 router.replace('/feed')와 동일하다.
<Link replace href="/feed">Login</Link>
직접 replace 속성을 부여할 수도 있다.
7. Imperative Navigation (함수를 이용한 네비게이션)
router 객체를 사용하면 컴포넌트 외부에서도 네비게이션을 실행할 수 있다.
예시: 로그아웃 시 특정 페이지로 이동
import { router } from 'expo-router';
export function logout() {
router.replace('/login'); // 로그인 페이지로 이동
}
이 방식은 버튼 클릭이 아닌 이벤트(예: 로그아웃)에서 네비게이션이 필요할 때 유용하다.
8. 자동 타입 지원 (TypeScript)
Expo Router는 TypeScript를 사용하면 자동으로 경로를 인식하여 타입을 지원한다.
<Link href="/profile/settings">Go to Settings</Link>
위처럼 href="/profile/settings" 입력 시, 유효한 경로가 아니라면 TypeScript에서 오류를 표시해준다.
9. 웹에서 동작 방식
Expo Router는 웹에서 실행될 때 기본적으로 <a> 태그를 사용할 수 있지만, <a> 태그를 사용하면 전체 페이지가 새로고침되면서 서버 네비게이션(Server-side navigation)이 수행된다. 즉, 페이지가 변경될 때 React가 관리하는 상태(State)가 초기화되고, 불필요한 네트워크 요청이 발생하여 속도가 느려진다. 이를 해결하기 위해, Expo Router의 <Link> 컴포넌트는 클라이언트 사이드 네비게이션(Client-side navigation)을 수행한다. 즉, 페이지 이동 시 전체 페이지를 새로고침하지 않고, 현재 앱의 상태를 유지하면서 빠르게 전환할 수 있다.
또한, <Link>는 웹에서 실행될 때 target, rel, download 같은 웹 전용 속성을 자동으로 <a> 태그에 전달할 수 있다. 이렇게 하면 웹에서 <a> 태그처럼 동작하면서도, React의 장점을 유지한 채 빠른 페이지 이동이 가능하다.
Expo Router의 클라이언트 사이드 네비게이션은 싱글 페이지 애플리케이션(SPA)에서 동작할 수 있으며, 정적 렌더링(Static Rendering)에서도 활용할 수 있다.
<정리>
- 기본적인 페이지 이동은 <Link href="경로">를 사용
- 버튼을 네비게이션 링크로 만들려면 asChild 사용 (속성 부여할때와 안할때 기능이 달라질 수 있음)
- 스택 기반 네비게이션. push, replace, back 활용
- dismissTo()를 사용하면 특정 경로까지 스택을 정리 가능
- 동적 경로는 { pathname: '/user/[id]', params: { id: 'bacon' } } 형식으로 사용
- router.push(), router.replace() 같은 API를 활용해 네비게이션 가능
- TypeScript 사용 시 자동 완성 기능 제공
- 웹과 네이티브에서 동일하게 동작하지만, 웹에서는 클라이언트 사이드 내비게이션이 기본
출처 : Expo Docs
'앱개발 > React-Native' 카테고리의 다른 글
Git 워크플로우 및 커밋/푸시 컨벤션 설정 (husky + commitizen) (2) | 2024.12.30 |
---|---|
생체인증을 통한 전자서명 (RSA 암호화와 SHA-256 해싱 알고리즘) (1) | 2024.12.29 |
React Native Reanimated에 대해서 (4) | 2024.09.01 |
[RN] Deep Link란? (0) | 2024.06.13 |
React Native Reanimated란? (0) | 2024.04.17 |