2๋ถ๊ธฐ TechOKR ์์ ์ผ๋ก ์ ์ ๋ ํ๋ฉด์ ํ๊ฐ ์ฑ๋ฅ ๊ฐ์ ์์ ์ ๋ด๋นํ๋ฉด์ Native Stack Navigator์ ์ ํ์ ๋์ ํ๊ฒ ๋์๋ค. ํด๋น ์์ ์ ์งํํ๊ฒ๋ ๋ฐฐ๊ฒฝ, ์ ์ฉ๊ณผ์ ์ ๋ํด ์ ๋ฆฌํ๋ฉด์ ์๋กญ๊ฒ ์๊ฒ๋ ๋ด์ฉ, ์ํ์ฐฉ์ค๋ฅผ ๊ธฐ๋กํด๋ณด๋ ค ํ๋ค.
๐ Native Stack Navigator ์์ ๋ฐฐ๊ฒฝ
Native Stack์ด๋
Native Stack Navigator๋ react navigation์์ ์ง์ํ๋ Navigator ํํ ์ค ํ๋๋ก, stack navigator์ ์ธํฐํ์ด์ค์ ์ ์ฌํ๊ฒ ์ ๊ณตํ๋ฉด์, stack navigator์ ๋ค๋ฅด๊ฒ iOS๋ UINavigationController, Android๋ Fragment๋ก Native ์์๋ฅผ ์ด์ฉํด ํ๋ฉด์ ๊ตฌํํ๊ฒ ๋๋ค.
Native ์์๋ฅผ ์ด์ฉํด ํ๋ฉด์ ๊ตฌํํ๋ฉด์ Native๊ฐ ๊ฐ์ง๊ณ ์๋ ์ฑ๋ฅ๊ณผ ํน์ง๋ค์ ์ด์ฉํ ์ ์๋ ์ฅ์ ์ ๊ฐ์ง์ง๋ง, stackNavigator์ ๋ค๋ฅด๊ฒ ์ปค์คํ ์ด ์ด๋ ค์ด ๋จ์ ์ ๊ฐ์ง๋ค.
์ Native Stack์ ๋์ ํ๊ฒ ๋์๋
Native Stack์ ์ด์ 1๋ถ๊ธฐ์ startup-time ๊ฐ์ ์์ ์ ์งํํ๋ฉด์ ๊ณ ๋ คํ๋ ๋ฐฉ๋ฒ์ค์ ์์๋ ์์ ์ผ๋ก, ๋น์์๋ ํ๋ฉด์ ํ๊ฐ ์๋๊ฐ ์ฑ ์์ ์๊ฐ์ ์ต์ ํํ๋๋ฐ ํฐ ์ํฅ์ด ์์ ๊ฒ ๊ฐ์ ๋ณด๋ฅํด ๋์๋ค.
์ดํ์ ์ผ๊ฐ์ผ๋ก ๋ฐ์ ์์ผฐ๋ ์ด์ ๋ก ๊ธฐ์กด ์ ํ์ stackNavigator๋ฅผ ์ด์ฉํ๋ฉด์ ํ๋ฉด ์ ํ๊ฐ ๋ฒ๋ฒ ์์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ๊ฐ ๊ฐํ์ ์ผ๋ก ์์๊ณ , React Native ๊ณต์๋ฌธ์์ Navigation ์์ ๊ฐ NativeStack์ ์ด์ฉํ๋ ๋ฐฉ์์ผ๋ก ์๊ฐ๋๋ ๊ฒ์ผ๋ก ์์ ๋์๊ณ (์ปค๋ฎค๋ํฐ์์ ์ฑ๋ฅ์ ์ํด ๊ถ์ฅํ๋ ๋ฐฉ๋ฒ), ํ๋ฉด์ ํ ์ ๋๋ฉ์ด์ ์ Native ์ค๋ ๋์์ ์งํํ๊ฒ ๋๋ฉด JS ์ค๋ ๋๊ฐ ๋ฐ์๊ฒ ์งํ๋ ๋์๋ ์์ ์ ์ผ๋ก ํ๋ฉด ์ ํ ์ ๋๋ฉ์ด์ ์ ๋ณด์ฅํ ์ ์์ ๊ฒ์ ๊ธฐ๋ํ๋ฉฐ ์์ ์ ์์ํ๊ฒ ๋์๋ค.
[react native ๊ณต์๋ฌธ์์ Navigating between screens]
[React Navigation v2์ ์๊ฐ๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๋น๊ต ๋ฐ์ดํฐ]
์ถ๊ฐ๋ก ํ์ฌ React Native๋ Expo๋ฅผ ๊ณต์์ ์ธ ํ๋ ์์ํฌ๋ก ์ถ์ฒํ๊ณ ์๋ ํ๋ฆ ์์ expo์์ ์ฌ์ฉํ๋ routing ์์คํ ์ธ Expo router๋ ๋์ผํ๊ฒ React Native Screens๋ฅผ ์ด์ฉํด file-based routing ๋ฐฉ์์ผ๋ก ์ง์ํ๊ณ ์๋ค.
๐ Native Stack Navigator ์ ํ์ ์ ์ฉํด๋ณด๊ธฐ
๊ธฐ์กด ์ ํ์ Stack Navigator๋ฅผ ์ฌ์ฉํ๊ณ ์์๊ธฐ ๋๋ฌธ์ Native Stack Navigator๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ๊ธฐ ์ํด์๋ Stack Navigator์ Navigation Option์ Native Stack Navigator์ ๋ง๊ฒ ๋ณ๊ฒฝํ๋ ์์ ์ด ๊ฐ์ฅ ์ค์ํ๊ฒ ์งํ๋์๋ค.
์ด๋ฌํ ์ต์ ์ค ๊ฐ์ฅ ์ค์ํ๋ ๋ถ๋ถ์ presentation ์ต์ ์ผ๋ก, presentation์ ์ด๋ป๊ฒ ์ ํ๋๋์ ๋ฐ๋ผ ํ๋ฉด์ ํ ์ ๋๋ฉ์ด์ , ํ๋ฉด ๋ ๋๋ง ์คํ์ผ์ด ๋ฌ๋ผ์ง๊ฒ ๋๋ค.
Stack Navigator์ presentation ์ต์
stack navigator์์๋ card
, modal
, transparent modal
3๊ฐ์ง ์ต์
์ ์ฌ์ฉํ ์ ์๋ค.
card
: ๊ธฐ๋ณธ ํ๋ฉด์ ํ ๋ฐฉ์์ผ๋ก, iOS์ Android์์ default OS animation์ผ๋ก ํ๋ฉด์ ํ์ด ์งํ๋๋ค.modal
: ํ๋ฉด์ด ๋ชจ๋ฌ๋ก ๋จ๋ ๋ฐฉ์์ผ๋ก, iOS์ android ๋ชจ๋ ํ๋ฉด์ด ์๋์์ ์๋ก ์ฌ๋ผ์ค๋ ๋ฐฉ์์ผ๋ก ํ๋ฉด์ด ๋จ๊ฒ ๋๋ค.transparent modal
: ๋ชจ๋ฌ๋ก ๋จ๋ ํ๋ฉด์ด์ง๋ง, ๋ฐฐ๊ฒฝ์ด ํฌ๋ช ํ๊ฒ ๋์ด์์ด ์ด์ ํ๋ฉด์ด ๋ณด์ด๊ฒ ๋๋ค.
[iOS Presentation๋ณ ํ๋ฉด์ ํ ์ ๋๋ฉ์ด์ ]
Card | Modal | Transparent Modal |
---|---|---|
[Android Presentation๋ณ ํ๋ฉด์ ํ ์ ๋๋ฉ์ด์ ]
Card | Modal | Transparent Modal |
---|---|---|
๋ํ, stack Navigator์ ๊ฒฝ์ฐ ์ผ๋ฐ View๋ก ๊ตฌํ๋๋ ํ๋ฉด์ด๊ธฐ ๋๋ฌธ์ Card <-> Modal ํ๋ฉด๊ฐ ์ด๋์ด ์์ ๋ก์ Card ์์ Modalํ๋ฉด์ด ์์ด๊ณ , Modalํ๋ฉด์ Card ํ๋ฉด์ด ๋ค์ ์์ผ ์ ์๋ค.
[Card -> Modal -> Card2 -> TransparentModal ํ๋ฉด์ ํ]
Native Stack Navigator์ presentation ์ต์
Native Stack Navigator์์๋ card
, modal
, transparent modal
, contained modal
, contained transparent modal
, full screen modal
, form sheet
7๊ฐ์ง๋ก ๊ตฌ๋ถ๋์ด ์๋ค.
iOS์์ ์ฌ์ฉํ๋ ๋ชจ๋ฌ ์คํ์ผ์ ์กฐ๊ธ ๋ ์ธ๋ถ์ ์ผ๋ก ์ค์ ํ ์ ์๊ฒ ์ง์ํ๊ณ ์๊ณ , android์์๋ ๋ชจ๋ modal ๋๋ transparentModal๋ก fallback๋์ด ์ฒ๋ฆฌ๋๋ค.
card
: ๊ธฐ๋ณธ ํ๋ฉด์ ํ ๋ฐฉ์์ผ๋ก, iOS๋ ์ค๋ฅธ์ชฝ์์ ์ผ์ชฝ์ผ๋ก ํ๋ฉด์ด ์ ํ๋๊ณ , Android๋ OS ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ํ๋ฉด์ ํ ์ ๋๋ฉ์ด์ ์ด ์งํ๋๋ค.modal
: iOS๋ ๋ค์ดํฐ๋ธ ๋ชจ๋ฌ์ฒ๋ผ ํ๋ฉด์ด ์ ์ฒด์ ์ผ๋ก ์ฌ๋ผ์ค๋ ํํ์ ๋ชจ๋ฌ์ ๊ฐ์ง๊ณ ์๋์์ ์๋ก ํ๋ฉด์ด ๋ํ๋๊ฒ ๋์ง๋ง, Android๋ card์ ๋์ผํ ์ ๋๋ฉ์ด์ ์ผ๋ก ํ๋ฉด์ด ์ ํ๋๋ค. (๊ด๋ จ ์ด์)transparent modal
: ์ด์ ํ๋ฉด์ด ๋ณด์ด๋ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ๋ณด์ด๋ ๋ชจ๋ฌํํ์ ํ๋ฉด์ด๋ค.contained modal
: iOS๋UIModalPresentationCurrentContext
๋ชจ๋ฌ ์คํ์ผ์ ์ด์ฉํด ๋ถ๋ชจ ํฌ๊ธฐ์ ๋ฐ๋ผ ์ฐจ์งํ๊ฒ ๋๋ฉฐ, Android๋ modal๊ณผ ๋์ผํ๊ฒ ์ฒ๋ฆฌ๋๋ค.contained transparent modal
: iOS๋UIModalPresentationOverCurrentContext
๋ชจ๋ฌ ์คํ์ผ์ ์ด์ฉํด ๋ถ๋ชจ ํฌ๊ธฐ์ ๋ฐ๋ผ ์ฐจ์งํ๊ฒ ๋๋ฉฐ, Android๋ transparent modal๊ณผ ๋์ผํ๊ฒ ์ฒ๋ฆฌ๋๋ค.fullScreenModal
: iOS๋UIModalPresentationFullScreen
๋ชจ๋ฌ ์คํ์ผ์ ์ด์ฉํด ์ ์ฒด ํ๋ฉด์ ์ฐจ์งํ๊ฒ ๋๊ณ , ์ ์ค์ฒ๋ก ์ ๊ฑฐ๋์ง ์๋ ํน์ง์ ๊ฐ์ง๋ค. Android๋ modal๊ณผ ๋์ผํ๊ฒ ์ฒ๋ฆฌ๋๋ค.formSheet
: iOS๋UIModalPresentationFormSheet
๋ชจ๋ฌ ์คํ์ผ์ ์ด์ฉํ๊ณ Android๋ modal๊ณผ ๋์ผํ๊ฒ ์ฒ๋ฆฌ๋๋ค.
[iOS Presentation]
card | modal |
---|---|
transparent modal | contained modal |
contained transparent modal | fullScreen modal |
formSheet | |
[android Presentation (Android 14, API Level 34)]
card | modal |
---|---|
transparent modal | contained modal |
contained transparent modal | fullScreen modal |
formSheet | |
stack Navigator์ ๋ค๋ฅด๊ฒ Native ์์๋ค์ ์ด์ฉํด ํ๋ฉด์ ๊ตฌํํ๊ธฐ ๋๋ฌธ์ ๋ชจ๋ฌ์ ํญ์ Navigation History์ ๋ง์ง๋ง์ ์์ผํ๋ ์กฐ๊ฑด์ด ์๋ค. ์ด ์กฐ๊ฑด์ ์งํค์ง ์์ผ๋ฉด Card ํ๋ฉด์ด Modal ํ๋ฉด ๋ค์ ์์ฌ ๋ณด์ด์ง ์๋ ํ์์ด ๋ฐ์ํ๋ค.
[Card -> Modal -> Card2 ํ๋ฉด์ ํ ]
์ ์์๋ฅผ ๋ณด๋ฉด Modal ์ดํ Card2๋ก ํ๋ฉด์ ํ์ ์งํํ๊ฒ ๋๋ฉด, Card2๊ฐ Modal ํ๋ฉด ๋ค์ ์์ด๊ฒ ๋์ด ๋ณด์ด์ง ์๋ ํ์์ด ๋ฐ์ํ๋ค. navigation pop์ ํด๋ฆญํ๊ฒ ๋๋ฉด, Card2๊ฐ ๋จผ์ ์ ๊ฑฐ๋๊ณ , ์ดํ Modal ํ๋ฉด์ด ์ ๊ฑฐ๋๋ ํ์์ ํ์ธํ ์ ์๋ค.
React Navigation์ ๊ฐ์ด๋์ ๋ฐ๋ฅด๋ฉด ๋ชจ๋ฌ ํ๋ฉด์ ํญ์ Navigation History์ ๋ง์ง๋ง์ ์์ผํ๋ค๊ณ ๋์ด์๋๋ฐ, ์ด๋ Native Stack Navigator์ ํน์ง์ผ๋ก ๋ณด์ฌ์ง๋ค.
์ ํ ๋ด Navigator presentation ๋ฐ์ํ๊ธฐ
๊ทธ๋ฌ๋ฉด ์ด์ Native Stack Navigator์ presentation ์ต์ ์ ์ ํ์ ์ ์ฉํด๋ณด์.
Card
Stack Navigator์ ๋์ผํ๊ฒ iOS์ Android ๋ชจ๋ ๋์ผํ๊ฒ Card
์ default Animation์ ์ ์ฉํ๊ธฐ๋ก ํ๋ค.
iOS๋ ์์ ํ ๋์ผํ๊ฒ ๋์ํด์ ํฐ ๊ณ ๋ฏผ์ด ์์์ง๋ง Android๋ ๋ณ๋์ ์ ๋๋ฉ์ด์
์ ์ฃผ์ด์ผํ ์ง ๊ณ ๋ฏผ์ด ๋์๋ค.
๊ทธ์ด์ ๋ Android์์๋ OS ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฅธ ํ๋ฉด์ ํ ์ ๋๋ฉ์ด์
์ด ์งํ๋๊ฒ ๋๊ณ ๊ธฐ์กด๊ณผ ๋ค๋ฅธ ์ ์ ๊ฒฝํ์ ๋ํ ์ฐ๋ ค๊ฐ ์์๊ธฐ ๋๋ฌธ์ด๋ค.
[Stack Navigator์ ์ ์๋ Transition Preset, Android ๋ฒ์ ๋ณ ์ ๋๋ฉ์ด์ ์ต์ ]
/**
* Standard Android navigation transition when opening or closing an Activity on Android < 9 (Oreo).
*/
export const FadeFromBottomAndroid: TransitionPreset = {
gestureDirection: "vertical",
transitionSpec: {
open: FadeInFromBottomAndroidSpec,
close: FadeOutToBottomAndroidSpec,
},
cardStyleInterpolator: forFadeFromBottomAndroid,
headerStyleInterpolator: forFade,
}
/**
* Standard Android navigation transition when opening or closing an Activity on Android 9 (Pie).
*/
export const RevealFromBottomAndroid: TransitionPreset = {
gestureDirection: "vertical",
transitionSpec: {
open: RevealFromBottomAndroidSpec,
close: RevealFromBottomAndroidSpec,
},
cardStyleInterpolator: forRevealFromBottomAndroid,
headerStyleInterpolator: forFade,
}
/**
* Standard Android navigation transition when opening or closing an Activity on Android 10 (Q).
*/
export const ScaleFromCenterAndroid: TransitionPreset = {
gestureDirection: "horizontal",
transitionSpec: {
open: ScaleFromCenterAndroidSpec,
close: ScaleFromCenterAndroidSpec,
},
cardStyleInterpolator: forScaleFromCenterAndroid,
headerStyleInterpolator: forFade,
}
์๋ GIF์์ ๊ธฐ์กด์ ๊ฐ์ด๋ฐ์์ ํผ์ ธ๋๊ฐ๋ ํ์(ScaleFromCenterAndroid)์ผ๋ก ํ๋ฉด์ด ์ ํ๋๋ค๋ฉด, Native Stack์์๋ ์ค๋ฅธ์ชฝ์์ ์ผ์ชฝ์ผ๋ก ํ๋ฉด์ด ์ ํ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ธฐ์กด ์๋๋ก์ด๋ ํ๋ฉด์ ํ | Native Stack ํ๋ฉด ์ ํ |
---|---|
๊ธฐ์กด ScaleFromCenterAndroid
์ ์ต๋ํ ์ ์ฌํ ์ ๋๋ฉ์ด์
์ผ๋ก Fade
์ ๋๋ฉ์ด์
์ ์ฒ์ ๊ณ ๋ฏผํด๋ดค์ง๋ง, ์ฌ์ ํ ๊ธฐ์กด๊ณผ ๋ค๋ฅธ๊ฒ ๋๊ปด์ง๋ ๊ฒ ๊ฐ๋ค๋ ๋๋ฃ๋ถ์ ํผ๋๋ฐฑ์ ๋ฐ์๊ณ ,
๋ฒ์ ์ ๋ง๊ฒ ํ์ค ์ ๋๋ฉ์ด์
์ ์ ์ฉํ๋ default ์ต์
์ด ์ดํ ์ ์ง๋ณด์ ์ธก๋ฉด์์ ์ข์ ๊ฒ ๊ฐ๋ค๋ ์ข์ ์กฐ์ธ์ ํด์ฃผ์
์, Card
์ต์
์ ๊ทธ๋๋ก ์ ์ฉํ๊ธฐ๋ก ํ๋ค.
Modal
์์ ์ค ๊ฐ์ฅ ์ด์๊ฐ ๋ง์๊ณ , OS๋ณ๋ก ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ ๋ถ๋ถ์ด ๋ฌ๋ผ ๊ณ ๋ฏผ์ด ๋ง์๋ ์์ ์์ญ์ด์๋ค.
OS๋ณ๋ก ๋ชจ๋ฌ ์ต์
๋ค์ ์ ๋ฆฌํด๋ณด๋ฉด iOS์์๋ modal, contained modal, fullscreen modal, formsheet
4๊ฐ์ง๊ฐ ์๊ณ , android๋ modal
ํ๊ฐ์ง ์ต์
๋ง ์ ๊ณตํ์ง๋ง ์ด์๊ฐ ์์ด์ ์ถ๊ฐ์ ์ผ๋ก ์ ๋๋ฉ์ด์
์ต์
์ ์ ์ฉํด์ผํ๋ ์ํฉ์ด์๋ค.
์ฌ๊ธฐ์ ์ถ๊ฐ์ ์ผ๋ก ๋ชจ๋ฌ์ด ํญ์ Navigation History์ ๋ง์ง๋ง์ ์์ผํ๋ ์กฐ๊ฑด์ด ์์ด์, ์ด๋ฅผ ์งํค์ง ์์ผ๋ฉด ๊ธฐ์กด ์ ํ๊ณผ ๋ค๋ฅด๊ฒ ํ๋ฉด์ด ๋ณด์ด์ง ์๋ ์ด์๊ฐ ๋ฐ์ํด, card์ modal๊ฐ ํ๋ฉด ์ ํ์ ์์ ๋๋ ๊ณ ๋ฏผํด์ผ ํ๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก๋ ์์์ ์ธ๊ธํ ์ต์
๋ค์ด ์๋ iOS์ Android ๋ชจ๋ Card
์ต์
์ slide_from_bottom
์ ๋๋ฉ์ด์
์ ์ ์ฉํ ํํ๋ก ์ ์ฉํ๊ธฐ๋ก ํ๋ค.
์ ๋ฌ๊ธ์์ด Card
๋๋ ์๋ฌธ์ด ๋ค ์ ์๊ฒ ์ง๋ง, android์ ์ด์๋ก ์ธํด ์ถ๊ฐ์ ์ธ ์ ๋๋ฉ์ด์
์ด ํ์์ ์ผ๋ก ํ์ํ ์ํฉ์ธ ์ ๊ณผ ๊ธฐ์กด ์ ํ ๋ด Navigation History ๊ด๋ฆฌ ๋ฐฉ์์ ์ ์งํด ์์
๋ฒ์์ ์ปจ๋ฒค์
์ ์งํค๋ ๊ฒ์ด ๋ ์ข๊ฒ ๋ค๋ ์ ์ ๊ณ ๋ คํด ์ ํํ๊ฒ ๋์๋ค.
๊ทธ๋ฌ๋ฉด ๊ฐ ์ต์ ๋ค์ ์ ์ฉํ์ ๋ ๋ฐ์ํ๋ ์ด์๋ค์ ์ ๋ฆฌํด๋ณด์.
Modal์ FormSheet
iOS์์๋ modal
์ formsheet
์ต์
์ ์ ์ฉํ์ ๋, ๊ธฐ์กด ์ ํ๋ด ๋ชจ๋ฌ๊ณผ ๋ค๋ฅด๊ฒ ํ๋ฉด ์ ์ฒด๋ฅผ ์ฐจ์งํ๋๊ฒ ์๋๋ผ ์ผ์ ์์ญ๋ง ์ฐจ์งํ๊ณ ์๋ก ๋ ์๋ ํํ๋ฅผ ๊ฐ์ง๊ฒ ๋๊ณ , android๋ card์ ๋์ผํ๊ฒ ํ๋ฉด ์ ํ์ด ์ด๋ฃจ์ด์ง๋ ์ด์๊ฐ ์์ด ์ฌ์ฉํ์ง ๋ชปํ๋ค.
[์ ํ๋ด ๋ชจ๋ฌ๊ณผ Modal ์ต์ ]
๊ธฐ์กด ์ ํ๋ด ๋ชจ๋ฌ ํ๋ฉด | modal iOS | modal android |
---|---|---|
[์ ํ๋ด ๋ชจ๋ฌ๊ณผ formsheet ์ต์ ]
๊ธฐ์กด ์ ํ๋ด ๋ชจ๋ฌ ํ๋ฉด | formsheet iOS | formsheet android |
---|---|---|
Contained Modal๊ณผ FullScreen Modal
android๋ card์ ๋์ผํ๊ฒ ํ๋ฉด ์ ํ์ด ์ด๋ฃจ์ด์ง๋ ์ด์๊ฐ ๋์ผํ๊ฒ ์์ง๋ง, iOS์์๋ contained modal
๊ณผ fullscreen modal
์ต์
์ ์ ์ฉํ์ ๋, ๊ธฐ์กด๊ณผ ๊ฐ์ด ํ๋ฉด์ด ์ ์ฒด๋ฅผ ์ฐจ์งํ๋ ํํ๋ก ํ๋ฉด์ด ๋ํ๋ผ ์ ์์ด ์ฌ์ฉํ๋ ค ํ๋ ์ต์
์ด์๋ค.
ํ์ง๋ง ๊ฐ ์ต์ ์ ์ ์ฉ์ ์์ด ๋ฌธ์ ์ ๋ค์ด ๊ฐ๊ฐ ์กด์ฌํ๋ค. contained Modal์ ๊ฒฝ์ฐ์๋ Navigation History์ ๋ง์ง๋ง์ ์์ผํ๋ ์กฐ๊ฑด์ ์ํด ๋ณ๋์ Nested Navigator๋ก ๋ชจ๋ฌ๋ค์ ๊ด๋ฆฌํ๋ ๋น์ฉ์ด ์ปธ๋ค๋ ์ ์ผ๋ก ์ธํด ์ฌ์ฉํ์ง ๋ชปํ๋ค.
fullscreen Modal ์ต์ ์ card,modal๊ฐ ํ๋ฉด ์ ํ ์ด์์ ๋๋ถ์ด, alert ๋ชจ๋ฌ์ ์์์ ๋์์ค ์ ์๋ ์ด์๊ฐ ์์ด ์ฌ์ฉํ์ง ๋ชปํ๋ค. ์๋๋ ํด๋น ์ํฉ์ ์ํ mimic ์ฝ๋์ด๋ค.
[fullscreen modal์์ Modal๋ก ๊ตฌํํ alert๊ฐ ๋จ์ง ์๋ ์ด์๋ฅผ ์ํ mimic ์ฝ๋]
// zustand๋ก ๊ตฌํํ ์ ์ญ ๋ชจ๋ฌ ๋
ธ์ถ ์ฝ๋
import { create } from "zustand"
export const useAlertModal = create(set => ({
visible: false,
show: () => set({ visible: true }),
hide: () => set({ visible: false }),
}))
const App = () => {
const visible = useAlertModal(state => state.visible)
const close = useAlertModal(state => state.hide)
const onPressClose = () => {
close()
}
return (
<SafeAreaView style={{ flex: 1 }}>
{visible && (
<Modal
visible={visible}
animationType={"fade"}
transparent={true}
onRequestClose={onPressClose}
>
<View
style={{
justifyContent: "center",
alignItems: "center",
backgroundColor: "white",
flex: 1,
}}
>
<Text>Modal์ด์์</Text>
<Button title="๋ซ๊ธฐ" onPress={onPressClose} />
</View>
</Modal>
)}
<NavigationContainer>
<NativeStack />
</NavigationContainer>
</SafeAreaView>
)
}
const FullscreenModal = ({ navigation }) => {
const handlePress = () => {
navigation.pop()
}
const show = useAlertModal(state => state.show)
const showAlert = () => {
show()
}
return (
<SafeAreaView style={{ flex: 1 }}>
<Button title="๋ค๋ก๊ฐ๊ธฐ" onPress={handlePress} />
<Button title="alert ๋์ฐ๊ธฐ" onPress={showAlert} />
</SafeAreaView>
)
}
Modal๋ก ๊ตฌํํ alert ์ปดํฌ๋ํธ๊ฐ ์์์ ๋จ์ง ์๋ ์ด์ |
์ด๋ฌํ ์ด์๋ค๋ก ์ธํด iOS์ Android ๋ชจ๋ Card
์ต์
์ slide_from_bottom
์ ๋๋ฉ์ด์
์ ์ ์ฉํ ํํ๋ก ์ ์ฉํ๊ธฐ๋ก ํ๋ค.
slidefrombottom ์๋ ์ด์ ํด๊ฒฐํ๊ธฐ
์ด์ ๋์ด์ ์ด์๊ฐ ์์ ๊ฒ์ด๋ผ ์๊ฐํ์ง๋ง Card
์ต์
์ slide_from_bottom
์ต์
์ ์ ์ฉํ๋ ๋ฐฉ์์๋ ์ด์๊ฐ ์์๋ค.
๋ฐ๋ก ๋ชจ๋ฌ์ด ๋จ๋ ์ ๋๋ฉ์ด์ ์๋ ์ด์๋ก iOS์์๋ duration์ ์ต์ ์ผ๋ก ์ ํ ์ ์๊ฒ ์ ๊ณตํ์ง๋ง, android์์๋ ์ปค์คํ ํ ์ ์์ด ๊ธฐ์กด๊ณผ ์ฒด๊ฐ์ด ๋ ์ ๋๋ก ๋๋ฆฌ๊ฒ ํ๋ฉด์ด ์ ํ๋๋ ์ด์๊ฐ ์์๋ค.
Android Stack Navigator Modal | Android Native Stack Card (slide_from_bottom) |
๋์ ๋๊ฒ ๋๋ฆฌ๋ค๋ ๋๋์ด ๋ค์ด ํด๋น ๋ถ๋ถ์ ํด๊ฒฐํ๊ธฐ ์ํด React Navigation ๋ด๋ถ ์ฝ๋๋ฅผ ํ๋ํ๋ ๋ถ์ํ๊ฒ ๋์๋ค.
๋จผ์ NativeStackNavigator๋ฅผ ๋ง๋ค๊ธฐ ์ํ createNativeStackNavigator
๋ฅผ ๋ณด๋ฉด createNavigatorFactory
ํฉํ ๋ฆฌ ํจ์์ NativeStackNavigator
๋ฅผ ์ธ์๋ก ๋๊ฒจ์ฃผ๋ ๋ฐฉ์์ผ๋ก NativeStackNavigator๋ฅผ ๋ง๋ค๊ณ ์๋ค.
//[์ฐธ์กฐ ์ฝ๋](https://github.com/react-navigation/react-navigation/blob/main/packages/native-stack/src/navigators/createNativeStackNavigator.tsx)
function NativeStackNavigator({...rest }: NativeStackNavigatorProps) {
...
return (
<NavigationContent>
<NativeStackView
{...rest}
state={state}
navigation={navigation}
descriptors={descriptors}
/>
</NavigationContent>
);
}
export function createNativeStackNavigator<
ParamList extends ParamListBase,
NavigatorID extends string | undefined = undefined,
TypeBag extends NavigatorTypeBagBase = {
ParamList: ParamList;
NavigatorID: NavigatorID;
State: StackNavigationState<ParamList>;
ScreenOptions: NativeStackNavigationOptions;
EventMap: NativeStackNavigationEventMap;
NavigationList: {
[RouteName in keyof ParamList]: NativeStackNavigationProp<
ParamList,
RouteName,
NavigatorID
>;
};
Navigator: typeof NativeStackNavigator;
},
Config extends StaticConfig<TypeBag> | undefined =
| StaticConfig<TypeBag>
| undefined,
>(config?: Config): TypedNavigator<TypeBag, Config> {
return createNavigatorFactory(NativeStackNavigator)(config);
}
๋ด๊ฐ ๊ถ๊ธํ๊ฑด ์คํฌ๋ฆฐ์ด ์ด๋ป๊ฒ ๋ง๋ค์ด์ง๋๋๊น ์ด์ NativeStackView ์ฝ๋ ๋ด๋ถ๋ฅผ ๋ณด๊ฒ ๋๋ฉด React Native Screens
์์ Screen, ScreenStack ์ปดํฌ๋ํธ๋ฅผ ๋ฐ์์ prop์ ์ ๋ฌํ๋ ๋ฐฉ์์ผ๋ก wrappingํ๊ณ ์๋ค๋ ๊ฒ์ ์ ์ ์์๋ค.
//[์ฐธ์กฐ ์ฝ๋](https://github.com/react-navigation/react-navigation/blob/main/packages/native-stack/src/views/NativeStackView.native.tsx):`react-navigation/packages/native-stack/src/views/NativeStackView.native.tsx`)
//...
import {
Screen,
type ScreenProps,
ScreenStack,
type StackPresentationTypes,
} from 'react-native-screens';
//...
const MaybeNestedStack = (props) => {
//...
if (isHeaderInModal) {
return (
<ScreenStack style={styles.container}>
<Screen
enabled
isNativeStack
hasLargeHeader={options.headerLargeTitle ?? false}
style={StyleSheet.absoluteFill}
>
{content}
<HeaderConfig
{...options}
route={route}
headerHeight={headerHeight}
headerTopInsetEnabled={headerTopInsetEnabled}
canGoBack
/>
</Screen>
</ScreenStack>
);
}
return content;
};
const SceneView = (props) => {
//...
return (
<Screen
key={route.key}
enabled
isNativeStack
style={StyleSheet.absoluteFill}
hasLargeHeader={options.headerLargeTitle ?? false}
customAnimationOnSwipe={animationMatchesGesture}
fullScreenSwipeEnabled={fullScreenGestureEnabled}
// ... ๊ธฐํ props
>{...}</Screen>
);
};
type Props = {
state: StackNavigationState<ParamListBase>;
navigation: NativeStackNavigationHelpers;
descriptors: NativeStackDescriptorMap;
};
export function NativeStackView({ state, navigation, descriptors }: Props) {
...
return (
<SafeAreaProviderCompat style={{ backgroundColor: colors.background }}>
<ScreenStack style={styles.container}>
{state.routes.map((route, index) => {
//...
return (
<SceneView
key={route.key}
index={index}
focused={isFocused}
descriptor={descriptor}
previousDescriptor={previousDescriptor}
nextDescriptor={nextDescriptor}
isPresentationModal={isModal}
// ๊ธฐํ props
/>
);
})}
</ScreenStack>
</SafeAreaProviderCompat>
);
}
์ค์ ์ ๋๋ฉ์ด์ ์๋๋ฅผ ๋ณ๊ฒฝํ๊ธฐ ์ํด์๋ React Navigation ์ฝ๋๊ฐ ์๋๋ผ React Native Screens ํจํค์ง ๋ด๋ถ ์ฝ๋๋ฅผ ์์ ํด์ผํ๋ ๊ฒ์ ์๊ฒ ๋์๋ค. ๊ทธ๋์ React Navigation์์ Native Stack์ ์ด์๋ React Native Screens ๋ ํฌ์ ์ฌ๋ ค๋ฌ๋ผํ๊ตฌ๋ ์ดํด๊ฐ ๋์๋ค.
์ด์ด์ React Native Screens ํจํค์ง ๋ด๋ถ ์ฝ๋๋ฅผ ๋ถ์ํ๊ฒ ๋์๊ณ , ์๋์ ๊ฐ์ด Screen ์ฝ๋๋ฅผ ๊ฐ๋จํ๊ฒ ๋ํ๋ผ ์ ์๊ณ , Screen ์ปดํฌ๋ํธ๋ ScreenNativeComponent๋ฅผ ๋ง๋ค์ด์ง๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
//[์ฐธ์กฐ ์ฝ๋](react-native-screens/src/components/Screen.tsx)
// ...
import ScreenNativeComponent from "../fabric/ScreenNativeComponent"
import ModalScreenNativeComponent from "../fabric/ModalScreenNativeComponent"
export const NativeScreen: React.ComponentType<ScreenProps> =
ScreenNativeComponent as React.ComponentType<ScreenProps>
const AnimatedNativeScreen = Animated.createAnimatedComponent(NativeScreen)
// ...
const Screen: React.FC<ScreenProps> = props => {
// ...
return <AnimatedNativeScreen {...props} />
}
export default Screen
๊ทธ๋ฆฌ๊ณ ScreenNativeComponent ์ฝ๋๋ฅผ ๋ณด๊ฒ ๋๋ฉด, Screen
์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด์ฃผ๋ ์ฝ๋๋ฅผ ์ฐพ์ ์ ์์๋ค.
์๋ ์ฝ๋๋ codegen์ ์ด์ฉํด Screen ์ปดํฌ๋ํธ๋ฅผ iOS์ Android๋ฅผ ๋น๋ํ ๋ ์๋์ผ๋ก ์์ฑํ๊ธฐ ์ํ ์ธํฐํ์ด์ค๋ค์ด ์ ์๋์ด ์๋ ์ฝ๋์ด๋ค.
์ด์ codegen์ ํตํด ๋ง๋ค์ด์ง ํด๋น ์ฝ๋ ๋ถ๋ถ์ ์ฐพ์๊ฐ๋ฉด ๋๋์ด ๋ด๊ฐ ์ํ๋ ์๋๋ก์ด๋ ์ ๋๋ฉ์ด์
์๋๋ฅผ ๋ณ๊ฒฝํ ์ ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ ์ ์์๋ค.
//[์ฐธ์กฐ์ฝ๋](react-native-screens/src/fabric/ScreenNativeComponent.ts)
import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent"
// ...
type StackPresentation =
| "push"
| "modal"
| "transparentModal"
| "fullScreenModal"
| "formSheet"
| "containedModal"
| "containedTransparentModal"
type StackAnimation =
| "default"
| "flip"
| "simple_push"
| "none"
| "fade"
| "slide_from_right"
| "slide_from_left"
| "slide_from_bottom"
| "fade_from_bottom"
| "ios"
// ...
export default codegenNativeComponent<NativeProps>("RNSScreen", {
interfaceOnly: true,
})
๋๋์ด rnscreens ๋ด๋ถ์ ScreenStack ์ฝ๋์์ ์ ๋๋ฉ์ด์
์ค์ ์ฝ๋๋ฅผ ๋ฐ๊ฒฌํ ์ ์์๋ค.
๊ทธ์ค ๋ด๊ฐ ์ฐพ๋ slide_from_bottom
์ ๋๋ฉ์ด์
์ R.anim.rns_slide_in_from_bottom
, R.anim.rns_no_animation_medium
์ผ๋ก ์ ์๋์ด ์์๋ค.
[์ฐธ๊ณ ์ฝ๋](react-native-screens/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt)
class ScreenStack(context: Context?) : ScreenContainer(context) {
...
override fun onUpdate() {
...
createTransaction().let {
// animation logic start
if (stackAnimation != null) {
if (shouldUseOpenAnimation) {
when (stackAnimation) {
StackAnimation.DEFAULT -> it.setCustomAnimations(R.anim.rns_default_enter_in, R.anim.rns_default_enter_out)
StackAnimation.NONE -> it.setCustomAnimations(R.anim.rns_no_animation_20, R.anim.rns_no_animation_20)
StackAnimation.FADE -> it.setCustomAnimations(R.anim.rns_fade_in, R.anim.rns_fade_out)
StackAnimation.SLIDE_FROM_RIGHT -> it.setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left)
StackAnimation.SLIDE_FROM_LEFT -> it.setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right)
StackAnimation.SLIDE_FROM_BOTTOM -> it.setCustomAnimations(
R.anim.rns_slide_in_from_bottom, R.anim.rns_no_animation_medium
)
StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_fade_from_bottom, R.anim.rns_no_animation_350)
StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_slide_in_from_right_ios, R.anim.rns_slide_out_to_left_ios)
}
} else {
when (stackAnimation) {
StackAnimation.DEFAULT -> it.setCustomAnimations(R.anim.rns_default_exit_in, R.anim.rns_default_exit_out)
StackAnimation.NONE -> it.setCustomAnimations(R.anim.rns_no_animation_20, R.anim.rns_no_animation_20)
StackAnimation.FADE -> it.setCustomAnimations(R.anim.rns_fade_in, R.anim.rns_fade_out)
StackAnimation.SLIDE_FROM_RIGHT -> it.setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right)
StackAnimation.SLIDE_FROM_LEFT -> it.setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left)
StackAnimation.SLIDE_FROM_BOTTOM -> it.setCustomAnimations(
R.anim.rns_no_animation_medium, R.anim.rns_slide_out_to_bottom
)
StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_no_animation_250, R.anim.rns_fade_to_bottom)
StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_slide_in_from_left_ios, R.anim.rns_slide_out_to_right_ios)
}
}
}
...
}
}
}
์ด์ ์ง์ง ๋ง์ง๋ง์ผ๋ก ํด๋น xml ํ์ผ์ ์ฐพ์ duration๋ถ๋ถ์ ์์ ํ๊ณ patch package๋ฅผ ์งํํด ์ ๋๋ฉ์ด์
์๋๋ฅผ ๋ณ๊ฒฝํ ์ ์์๋ค.
๊ธฐ์กด duration์ config_mediumAnimTime
์ผ๋ก ์ค์ ๋์ด ์์๊ณ , ํด๋น ๋ถ๋ถ์ ์์ ํ๋ฉด ์ปค์คํ
ํ๊ฒ ์์ ์ด ๊ฐ๋ฅํ๊ฒ ๋์๋ค.
animation duration์ react-native-screens์์ 20,250,350์ ๊ธฐ๋ณธ ๊ฐ์ผ๋ก ์ค์ ํด๋๊ณ ์๋ ๊ฒ์ผ๋ก ๋ณด์ฌ, 250์ผ๋ก ๋ณ๊ฒฝํด ์ ์ฉํ๊ฒ ๋์๋ค.
<!--[์ฐธ๊ณ ์ฝ๋](react-native-screens/android/src/main/res/base/anim/rns_slide_in_from_bottom.xml)-->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromYDelta="100%"
android:toYDelta="0%"
android:duration="@android:integer/config_mediumAnimTime" /> <!--250์ผ๋ก ๋ณ๊ฒฝ -->
<!--[์ฐธ๊ณ ์ฝ๋](react-native-screens/android/src/main/res/base/anim/rns_slide_out_to_bottom.xml)-->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromYDelta="100%"
android:toYDelta="0%"
android:duration="@android:integer/config_mediumAnimTime" /> <!--250์ผ๋ก ๋ณ๊ฒฝ -->
<!--[์ฐธ๊ณ ์ฝ๋] (react-native-screens/android/src/main/res/base/anim/rns_no_animation_medium.xml)-->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0"
android:toAlpha="1.0"
android:duration="@android:integer/config_mediumAnimTime"/> <!--250์ผ๋ก ๋ณ๊ฒฝ -->
๊ฐ์ ์ Android Native Stack Card (slide_from_bottom) | ๊ฐ์ ํ Android Native Stack Card (slide_from_bottom) |
์๊ฐ์ ํ์ง ๋ชปํ์ง๋ง, ์กฐ๊ธ ๋ ๋ชจ๋ฌ ์ ๋๋ฉ์ด์
์ ๊ฐ์ ํ๋ฉด ์ข์์ ๊ฒ ๊ฐ๋ค๋ ์์ฌ์ด ์๊ฐ์ด ๋ค์๋ค.
๊ธฐ์กด์๋ ๋์ ์ ์ ์ฒ์ฒํ ๋์ฐฉํ๋ ๋ฏํ ํจ๊ณผ๊ฐ ์์์ง๋ง ์ง๊ธ์ linearํ๊ฒ ํ๋ฒ์ ๋จ๊ณ ๋ซํ๋ ๊ฒ ๊ฐ์ ์ด์ํจ์ด ๋จ์์์ด๋ณด์๋ค.
์ดํ์ ๋ native์ค๋ฌ์ด ๋๋
์ ์ค ์ ์๊ฒ ํ์์
์ผ๋ก ๊ฐ์ ์์
๋ ์งํํด๋ณด๋ ค ํ๋ค.
TransparentModal
๊ธฐ์กด ์ผ๋ถ ํ๋ฉด์์ ์ฌ์ฉ๋๊ณ ์๋ transparent modal
์ต์
์ containedTransparentModal
์ ์ด์ฉํด ์ต๋ํ ์ฌ์ฉํ๋ ค ํ์ง๋ง, ๋ค์ ํ๋ฉด์ด card์ธ ๊ฒฝ์ฐ์ ์ ๋๋ฉ์ด์
์ด ๋ฒ๋ฒ
์ด๋ ๋ฏํ ์ด์๊ฐ ๋ฐ์ํ๊ฒ ๋์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ transparent modal
์ต์
๋์ ๊ธฐ์กด ํ๋ฉด์ ๋ฐํ
์ํธ ์ปดํฌ๋ํธ๋ฅผ ์ด์ฉํด ๊ตฌํํ๋ ๋ฐฉ์์ผ๋ก ์์ ํ๊ฒ ๋์๋ค.
์ด๋ ๊ฒ ์์ ํ์ ๋ ๊ธฐ์กด์ ๋ชจ๋ฌ์ด์ง๋ง ์์ํ fade in/fade out์ผ๋ก ํ๋ฉด์ ํ์ด ๋์์ง๋ง, ๋ฐํ ์ํธ๋ก ๋ณ๊ฒฝํ๋ฉด์ overlay๊ฐ ๋ณด์ด๋ ๋ชจ๋ฌ ๋๋์ด ๋ ๋ค ์ ์์ด ์ข์ ์ ํ์ด๋ผ ์๊ฐ์ด ๋ค์๋ค.
์ ํ ๋ด ๋ชจ๋ transparent modal
์ต์
์ ์ฌ์ฉํ๋ ํ๋ฉด๋ค์ ๋ค ๋ฐ๊พธ์ง๋ ๋ชปํ๊ณ , ์ด์๊ฐ ์์๋ ํ๋ฉด๋ง ์์ ํ๊ฒ ๋์์ง๋ง, ๋ค์ํ๋ฉด์ ๋ฐ๋ผ ํ๋ฉด์ ํ ์ ๋๋ฉ์ด์
์ ์ด์๊ฐ ์์ ์ ์์ด ํ์์
์ ์งํํด์ผํ ๊ฒ ๊ฐ๋ค.
์ด์๊ฐ ๋์๋ transparent modal | BottomSheet์ผ๋ก ์ ํํ transparent modal |
๊ธฐํ ์ด์: gesture handler๋ก ์ฌ์ง ๋์ด๋น๊ฒจ ๋ซ๊ธฐ ์ด์
ํด๋น ์ด์๋ ์ฒซ๋ฒ์งธ ๋ฐฐํฌ ๋ ๋กค๋ฐฑํ๊ฒ ๋ ๊ฐ์ฅ ์ปธ๋ ์ด์๋ก ์ด๋ฏธ์ง ์ฌ๋ผ์ด๋๋ฅผ ๋ณด๊ณ ๋์ด๋น๊ฒจ ํด๋น ํ๋ฉด์ popํ ํ์, ์ ํ ๋ด ๋ฉ์ธ ํผ๋ ์ค ํ๋์ธ ์์ฒญ์ ์์ฑํ๋ฉด์ ์ง์ ํ ๊ฒฝ์ฐ์ ์ง๋ฌธ์ด 10๊ฐ๋ง ๋ ๋๋ง๋๋ ์ด์๊ฐ ๋ฐ์ํ๋ค.
์ด๋ฏธ์ง๋ฅผ ๋์ด๋น๊ฒจ ์ข
๋ฅํ ์ ์๊ฒ ํ๊ธฐ ์ํด PanResponder๋ฅผ ์ด์ฉํด ๊ตฌํํ๋๋ฐ, PanResponder๋ก ๋์ด๋น๊ฒจ navigation.pop
์ ์งํํ๊ฒ ํ๋ ๋์์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ผ๋ก ๋ณด์๋ค.
ํด๋น ์ด์๋ฅผ ํด๊ฒฐํ๋ ๊ณผ์ ์์ FlatList๋ก ๊ตฌํ๋ ์์ฒญ์ ์์ฑํ๋ฉด์ ์์๊ฐ Flatlist์ initialNumToRender prop์ default ๊ฐ์ฒ๋ผ ๋ฑ 10๊ฐ๋ง ํญ์ ๋ ๋๋ง๋๋ ๊ฒ์ ๊ทผ๊ฑฐ๋ก FlatList
๋ด๋ถ ์ฝ๋๋ฅผ ํ์ธํด๋ณด๊ฒ ๋์๋ค.
Flatlist๋ VirtualizedList๋ฅผ ์์๋ฐ์ ๊ตฌํ๋์ด ์๊ณ , VirtualizedList๋ ๋ค์ ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๊ธฐ ์ํด์ Batchinator๋ฅผ ์ด์ฉํด ๋ค์ ๋ชฉ๋ก์ ๋ฐ์์ค๊ฒ๋๋ค. ์ด๋ Batchinator๋ InteractionManager์ runAfterInteractions ๋ฉ์๋๋ฅผ ์ด์ฉํด ๋ค์ ๋ชฉ๋ก์ ๋ฐ์์ค๊ฒ ๊ตฌํ๋์ด ์์๋ค.
class Batchinator {
_callback: () => void;
_delay: number;
_taskHandle: ?{cancel: () => void, ...};
constructor(callback: () => void, delayMS: number) {
this._delay = delayMS;
this._callback = callback;
}
/*
* Cleanup any pending tasks.
*
* By default, if there is a pending task the callback is run immediately. Set the option abort to
* true to not call the callback if it was pending.
*/
dispose(options: {abort: boolean, ...} = {abort: false}) {
if (this._taskHandle) {
this._taskHandle.cancel();
if (!options.abort) {
this._callback();
}
this._taskHandle = null;
}
}
schedule() {
if (this._taskHandle) {
return;
}
const timeoutHandle = setTimeout(() => {
this._taskHandle = InteractionManager.runAfterInteractions(() => {
// Note that we clear the handle before invoking the callback so that if the callback calls
// schedule again, it will actually schedule another task.
this._taskHandle = null;
this._callback();
});
}, this._delay);
this._taskHandle = {cancel: () => clearTimeout(timeoutHandle)};
}
}
module.exports = Batchinator;
์ด๋ฏธ์ง๋ฅผ ๋์ด๋น๊ฒจ ์ข ๋ฃํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ PanResponder ๋ํ ๋ด๋ถ ์ ์ผ๋ก InteractionManager๋ฅผ ์ด์ฉํ๋๋ฐ ์ด๋ gesture ๋ฐฉํด๋ฅผ ๋ง๊ธฐ์ํด JS ์ด๋ฒคํธ๋ฅผ blockingํ๊ฒ ๊ตฌํ๋์ด ์๋ค.
์ด๋ฅผ ๊ทผ๊ฑฐ๋ก ์์ธํ ๋์๊ณผ ์ถฉ๋ ๊ณผ์ ์ ํ์ ํ์ง ๋ชปํ์ง๋ง ์ด๋ฏธ์ง๋ฅผ ๋์ด๋น๊ฒจ ์ข ๋ฃํ๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ณผ์ ์์ Interaction Manager๊ฐ blocking๋์ด FlatList์ InteractionManager๊ฐ ๋์ํ์ง ๋ชปํ๊ฒ ๋ง์์ ๋ฐ์ํ๋ ์ด์๋ก ์ถ์ธกํ ์ ์์๋ค.
ํด๋น ์ด์๋ฅผ ํด๊ฒฐํ๊ฒ ์ํด์ Pan Responder์ interaction ๋์ค์ navigation.pop์ด ์งํ๋๊ฒ ํ๋๊ฒ ์๋๋ผ Interaction์ด ๋ชจ๋ ๋๋ ํ์ ์งํ๋๊ฒ ํด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์๋ค.
const panResponder = useRef(
PanResponder.create({
...,
onPanResponderGrant: () => {
// PanGesture๊ฐ ์๋ฃ๋๊ณ ๋ ์ดํ์ ํ๋ฉด ์ด๋์ ๋์์์ผ InteractionManager๊ฐ ์ถฉ๋์ ๋ง์ต๋๋ค.
InteractionManager.runAfterInteractions(() => {
navigation.goBack();
});
},
}),
).current;
โญ๏ธ ์ ์ฉ ํ ์ฑ๋ฅ ๋ถ์
์ด์ ์ ์ฉ ํ ์ฑ๋ฅ ๋ถ์์ ์งํํ ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด๋ ค ํ๋ค. ํ๋ฉด ์ ํ๊ฐ ์ฑ๋ฅ์ ๊ฐ์ ํ๋ค๋ ๋ชฉํ๋ฅผ ๊ฐ์ง๊ณ ์์ ์ ์งํํ๋ค ๋ณด๋ ์งํ์ ์ผ๋ก ๋ช ํํ๊ฒ ๋ณด์ฌ์ค ๋ฐฉ๋ฒ์ด ํฌ๊ฒ ์์ด ์ด๋ ค์์ ๋๊ผ๋ค.
๊ณ ๋ฏผ ๋์ ์ ํ๋ ๋ฐฉ๋ฒ์ ์ด 3๊ฐ์ง๋ก, ๋จผ์ ๊ฐ๋จํ๊ฒ ์ค์ ์ ํ ๋ด ๋ฉ์ธ ํผ๋๋ค์ ๋ํ ์์์ ์ฐ์ด์ ์ /ํ ๋น๊ต๋ฅผ ์งํํ๊ณ , ๋ค์์ผ๋ก ์ง์ ์ ์ด์ง๋ ์์ง๋ง ์ ๋๋ฉ์ด์ ์ฒ๋ฆฌ ๊ณผ์ ์ ํ์ํ CPU ์ฌ์ฉ๋๊ณผ memory ์ฌ์ฉ๋์ ๋ณด๊ธฐ ์ํด android์์ ์น์ light house์ฒ๋ผ ์ฑ๋ฅ์ธก์ ์ ํ ์ ์๋ flash light์ ์ด์ฉํด ๋ฉ์ธ ํผ๋๋ค์ ๋ํ ์งํ๋ฅผ ์ธก์ ํด๋ณด์๋ค. ๊ทธ๋ฆฌ๊ณ ์ถ๊ฐ์ ์ผ๋ก ํ๋ฉด stack์ด ์ต๋ 100๊ฐ๊ฐ ๋์์ ๋๋ ์ ์์ ์ผ๋ก ์ ๋๋ฉ์ด์ ์ ์ฒ๋ฆฌํ ์ ์์์ง ๋ณด๊ธฐ ์ํด stress ํ ์คํธ๋ฅผ ์งํํด๋ณด์๋ค.
์์์ ํตํ ์ /ํ ๋น๊ต
์์์ ํตํ ์ /ํ ๋น๊ต๋ ๊ฐ์ฅ ์ง๊ด์ ์ผ๋ก ๋ณด์ฌ์ค ์ ์๋ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค. ์์์ ์ฐ์ด์ ์ /ํ ๋น๊ต๋ฅผ ์งํํ๊ณ , ์ด๋ฅผ ํตํด ์ ๋๋ฉ์ด์ ์ ์์ฐ์ค๋ฌ์๊ณผ ๋๊น์ด ์๋์ง, ๋ํ ํ๋ฉด ์ ํ ์๋๊ฐ ๋นจ๋ผ์ก๋์ง ํ์ธํ ์ ์์๋ค.
์๋๋ ๋ฉ์ธ ํผ๋ ์ค ํ๋์ธ ๊ณ ๊ฐํ -> ๋ฉ์ธ ์นดํ ๊ณ ๋ฆฌ ๊น์ง ๋์ด๊ฐ๋ ๊ณผ์ ์ Android (Galaxy22)์์ ์ฐ์ ์์์ด๋ค.
AS-IS | TO-BE |
์์์ ๋น๊ตํด๋ณด์์ ๋ ์ด์ ๋ณด๋ค ์กฐ๊ธ ๋ ๋ถ๋๋ฝ๊ฒ ์ ํ๋๊ณ ํ๋ฉด์ ํ ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ฐ ๋นจ๋ผ์ง ๊ฒ์ ๋๋ ์ ์์๋ค.
FlashLight๋ฅผ ์ด์ฉํ ์ฑ๋ฅ ์ธก์
์์์ ์ฐ๋๋ฐ์์ ๋๋ด์ง ์๊ณ ์ต๋ํ ์ซ์๋ก, ์งํ๋ก ๋ถ์ํด๋ณด๋ฉด ์กฐ๊ธ ๋ ์ข์ง ์์๊น ์๊ฐํด flash light๋ฅผ ์ด์ฉํด ์ฑ๋ฅ ์ธก์ ์ ์งํํด๋ณด์๋ค. flash light์ maestro e2e ์๋ํ ์ฝ๋๋ฅผ ์ด์ฉํด ์ธก์ ํด๋ณด์๊ณ , ์ฑ์ ์์ํด์ ์์์ ์์์ผ๋ก ์ฐ์๋ ๋ฉ์ธ ํผ๋ ์๋๋ฆฌ์ค์ ๋ํด ๊ฐ๊ฐ 30๋ฒ ์งํํ์ ๋ ์ฑ๋ฅ ์งํ๋ฅผ ์ธก์ ํ๋ค.
ํ -> ๋ฉ์ธ ์นดํ ๊ณ ๋ฆฌ ์ /ํ |
---|
๋ฐ์ ์์ฒญ ๋ชฉ๋ก -> ๋ฐ์ ์์ฒญ ์์ธ ์ /ํ |
๋ฐ์ ๊ฒฌ์ ๋ชฉ๋ก -> ๋ฐ์ ๊ฒฌ์ ์์ธ -> ๊ฒฌ์ ์ ์์ธ ์ /ํ |
์ฑํ ๋ฐฉ ๋ชฉ๋ก -> ์ฑํ ๋ฐฉ AS-IS |
์ฑ๋ฅ ์งํ๋ฅผ ๋ถ์ํด๋ณด์์ ๋ ํผ๋ ๋ณ๋ก ์ค๋ฅด๋ฝ ๋ด๋ฆฌ๋ฝ ํ๋ ๋ถ๋ถ๋ค์ด ์์ง๋ง, CPU ์ฌ์ฉ๋๊ณผ Memory ์ฌ์ฉ๋์ด ๋๋ถ๋ถ ๊ฐ์ํ๊ณ ์ด๋ ์ ๋๋ฉ์ด์ ์ฒ๋ฆฌ ๊ณผ์ ์์ ๋ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌ๋๊ณ ์๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค๊ณ ๋ณผ ์ ์์๋ค.
๊ทธ๋ฆฌ๊ณ ์ถ๊ฐ์ ์ผ๋ก ๋ถ์ํด๋ณด์์ ๋ ๋ฐ์๊ฒฌ์ ๋ชฉ๋ก -> ๋ฐ์ ๊ฒฌ์ ์์ธ -> ๊ฒฌ์ ์ ์์ธ ์ ํ์์ ๋ค๋ฅธ ํผ๋๊ณผ ๋ค๋ฅด๊ฒ ์ ์ฒด์ ์ธ ์งํ ๋ชจ๋ ์ข์์ง ๊ฒ์ ๋ณผ ์ ์์๋ค. ํผ๋๊ฐ ์ฐจ์ด์ ์ ๋ณด์์ ๋ ์ธก์ ํ๋ ํผ๋ ์ค ๊ฐ์ฅ ๊ธด ํผ๋์ด์๋ค๋ ์ ์ ๊ณ ๋ คํด Navigation stack์ด ์์ผ์๋ก ๋ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์๋ค๋ ๊ฒ์ ์ ์ ์์๋ค.
์ด๋ฌํ ํน์ง์ ๋์ผ๋ก ๋ ๋ช
ํํ๊ฒ ํ์ธํด๋ณด๊ธฐ ์ํด์ Navigation stack์ด 100๊ฐ๊ฐ ๋์์ ๋๋ ์ฑ๋ฅ์ด ์ข์์ง๋์ง
ํ์ธํด๋ณด๋ ๊ฑด ์ด๋จ๊น๋ผ๋ ํธ๊ธฐ์ฌ์ด ์๊ฒจ stress ํ
์คํธ๋ฅผ ์ด์ด์ ์งํํด๋ณด์๋ค.
Stress ํ ์คํธ๋ฅผ ํตํ ์ฑ๋ฅ ํ์ธ
stress ํ ์คํธ๋ 10๊ฐ, 30๊ฐ, 50๊ฐ, 75๊ฐ, 100๊ฐ ๊น์ง stack์ ํ๋ฉด์ด ์์์ ๋ ์ด๋ป๊ฒ ํ๋ฉด์ ํ์ด ๋๋์ง๋ฅผ ์ดฌ์ํด๋ณด์๊ณ , ์์ ์๋จ์ navigation stack์ ๊ธธ์ด๋ฅผ ํ์ํด ํ์ฌ ๋ช๊ฐ stack์ด ์์๋์ง ํ์ธํ ์ ์๊ฒ ํ๋ค. ์๋ ์์์ Android Galaxy22 ๊ธฐ๊ธฐ์์ ์ธก์ ํ ๊ฒฐ๊ณผ๋ค.
Stack Navigator 10๊ฐ | Stack Navigator 50๊ฐ | Stack Navigator 100๊ฐ |
---|---|---|
Native Stack Navigator 10๊ฐ | Native Stack Navigator 50๊ฐ | Native Stack Navigator 100๊ฐ |
Stack Navigator๋ stack์ด ์์ผ์๋ก ํ๋ฉด์ ํ ์๋๊ฐ ๋๋ ค์ง๋ ๊ฒ์ ํ์ธํ ์ ์์๊ณ , Native Stack Navigator๋ stack์ด ์์ฌ๋ ์ ์ฌํ ์๋๋ก ํ๋ฉด์ ํ์ด ์งํ๋๋ ๊ฒ์ ๋ณผ ์ ์์๋ค.
์ดฌ์๊ณผ์ ์์ ๊ธฐ๊ธฐ์์ ๋๊ปด์ง๋ ๋ฐ์ด๋ Native Stack Navigator๊ฐ ๋ ๋ฎ์ ๊ฒ์ ๋๋ ์ ์์๊ณ , ์ด๋ CPU ์ฌ์ฉ๋๊ณผ Memory ์ฌ์ฉ๋์ด ์ค์ด๋ค์ด ์ฑ๋ฅ์ด ํฅ์๋์๋ค๋ ๊ฒ์ ์ฒด๊ฐํ ์ ์์๋ค.
๐ ๋ฐฐ์ด์
์ฌํ๊น์ง ์งํํ๋ ํ๋ก์ ํธ ์ค์์ ๊ฐ์ฅ ์ด์๊ฐ ๋ง์๋ ์์ ์ด์๊ณ , ๋คํํ ์ ํด๊ฒฐํด์ ํ์ฌ ์ ์ ํ์ ๋ฐ์๋์ด ์์ด ๋ฟ๋ฏํ๋ค.
๋จ์ Javascript ๋ฐํ์๋ง ๊ณ ๋ฏผํ๋๊ฒ ์๋๋ผ ๋ชจ๋ฐ์ผ ํ๋ซํผ์ ํนํ๋ ์ฑ๋ฅ ๊ฐ์ ์ ์ ์ฉํ ์ ์์ด ์ข์๊ณ , ์ ๋ง๋ค์ด์ง ์คํ ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๋ด๋ถ๊ตฌ์กฐ๋ ํํค์น๋ ์ข์ ๊ฒฝํ์ด ๋์ด ์ดํ์ React Navigation์๋ ๊ธฐ์ฌํ๊ณ ์ถ์ ๋ง์๋ ์๊ฒผ๋ค.
์ ์ฉํ๋ฉด์ ์ฑ๋ฅ์ด ๊ฐ์ ๋์๋์ง ํ์ธํ๊ธฐ ์ํด ๋ค์ํ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด๋ณด์๋๋ฐ, ์์์ ์ฐ์ด ์ /ํ ๋น๊ต๋ฅผ ํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ง๊ด์ ์ด์๋ค. ๋๋ฆ ์ซ์๋ก ํํํ๊ธฐ ์ํด ๋ ธ๋ ฅํด๋ณด์์ง๋ง ์ด๋์ ๋ ํ๊ณ๊ฐ ์์๋ค. ํ์ง๋ง, ๋ฉ๋ชจ๋ฆฌ๋ CPU ์ฌ์ฉ๋๊ณผ ๊ฐ์ด ์ด์ ์ ๊ด์ฌ์ ๋์ง ์์๋ ํ๋์จ์ด ์คํ์ ๋ํด์๋ ๊ด์ฌ์ ๊ฐ์ง๊ฒ ๋์๊ณ , ์ด๋ฅผ ํตํด ์ฑ๋ฅ์ ๊ฐ์ ํ๋๋ฐ ๋์์ด ๋์๋ค.
๋ฌผ๋ก ์ฌ์ ํ ๋จ๊ฒจ์ง ์ผ๋ค์ด ์์ง๋ง, ํ๋์ ๋ ํฐ ์ผ๊ฐ์ ์ ๋ง๋ฌด๋ฆฌํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ๊ณผ ์ฑ๋ฅ ๋ชจ๋ ๊ฐ์ ํ ์ ์์๋ ์ข์ ์์ ์ด์๋ค๊ณ ์๊ฐ๋์๋ค.