React Native BottomSheet 안에서의 컨테이란 무엇일까?

eeensu·4일 전

React Native

목록 보기
59/61

컨테이너(container)는 바텀시트가 그 안에서 위아래로 움직이는 "무대(영역)"다. 시트 자체가 아니라, 시트를 담고 있으면서 시트가 차지할 수 있는 공간의 범위를 정의하는 바깥 껍데기라고 보면 된다.

바텀시트는 허공에서 슬라이드하는 게 아니라 반드시 어떤 영역 안에서 움직인다. 그 영역이 컨테이너다. 컨테이너의 크기(height)와 화면 가장자리로부터의 여백(offset)이 정해져야 "시트가 어디까지 올라올 수 있는지"를 계산할 수 있다.



1. 시각적 계층 구조

바텀시트는 여러 겹으로 쌓인 구조다. 바깥에서 안쪽 순서로 보면 이렇다.

┌─────────────────────────────────┐
│ Container (무대)                 │  ← 시트가 움직일 수 있는 전체 영역
│  ┌───────────────────────────┐  │
│  │ Backdrop (뒷배경/딤)      │  │  ← 뒤를 어둡게 덮는 레이어 (선택)
│  └───────────────────────────┘  │
│                                  │
│  ┌───────────────────────────┐  │
│  │ Sheet (시트 본체)         │  │  ← 실제로 올라오는 패널
│  │  ┌─────────────────────┐  │  │
│  │  │ Handle (손잡이)     │  │  │  ← 위쪽 그립 바
│  │  ├─────────────────────┤  │  │
│  │  │ Content (콘텐츠)    │  │  │  ← 안에 넣은 내용
│  │  └─────────────────────┘  │  │
│  └───────────────────────────┘  │
└─────────────────────────────────┘

여기서 핵심은 컨테이너와 시트는 서로 다른 것이라는 점이다. 컨테이너는 고정된 무대이고, 시트는 그 무대 안에서 오르내리는 패널이다. 사람들이 흔히 "바텀시트"라고 부르는 건 보통 시트 본체지만, 위치 계산의 기준이 되는 건 컨테이너다.



2. 컨테이너는 실제 컴포넌트다

개념일 뿐 아니라 라이브러리 내부에 BottomSheetContainer라는 실제 컴포넌트로 존재한다. BottomSheetBackdrop, BottomSheetBackgroundContainer 등과 함께 시트를 구성하는 내부 부품 중 하나다.

이 컨테이너 컴포넌트가 하는 일은 크게 두 가지다.

  • 시트가 얹힐 영역을 화면에 잡아두는 것 (레이아웃 확보)
  • 그 영역의 크기와 위치를 측정해 내부 계산에 넘기는 것 (onLayout 기반 측정)


3. 컨테이너 = 무대, 시트 = 배우

세 가지를 구분하면 개념이 선명해진다.

구분역할비유
Container시트가 움직일 수 있는 전체 영역무대
Sheet실제로 오르내리는 패널배우
Content시트 안에 넣은 내용배우가 든 소품

배우(시트)의 위치는 항상 무대(컨테이너)를 기준으로 잡힌다. snapPoints={['50%']}의 50%는 화면이 아니라 컨테이너 높이의 50%다. 무대가 커지면 같은 "절반 지점"도 실제로는 달라지는 것이다.



4. 일반 BottomSheet의 컨테이너

기본 BottomSheet는 컨테이너가 자신이 놓인 부모 뷰 안에 렌더된다. 즉 부모 뷰의 영역이 곧 무대가 된다.

그래서 부모 뷰가 화면 전체를 차지하지 않으면, 시트도 그 좁은 영역 안에서만 움직인다. 부모가 화면의 절반짜리 뷰라면 바텀시트도 그 절반 안에 갇혀 어색하게 보일 수 있다. 일반 BottomSheet를 쓸 때는 대개 flex: 1로 화면을 꽉 채우는 부모 안에 두는 이유가 여기에 있다.

// 컨테이너 = 이 부모 View의 영역
<View style={{ flex: 1 }}>
  <BottomSheet index={1} snapPoints={['50%', '90%']}>
    <BottomSheetView>{/* content */}</BottomSheetView>
  </BottomSheet>
</View>


5. BottomSheetModal의 컨테이너

BottomSheetModal은 컨테이너가 @gorhom/portal로 감싸져 앱의 최상단에 렌더된다. 컴포넌트 트리상 어디에 선언하든, 실제로는 모든 화면 위를 덮는 별도의 무대로 올라간다.

이 덕분에 모달 시트는 부모 뷰의 크기나 위치에 갇히지 않고 항상 화면 전체 위에 얹힌다. 대신 이 포털을 받아줄 BottomSheetModalProvider를 앱 상단에 두어야 한다.

// Provider가 포털 호스트를 제공 → 컨테이너가 최상단에 렌더됨
<BottomSheetModalProvider>
  <BottomSheetModal ref={ref} snapPoints={['50%']}>
    <BottomSheetView>{/* content */}</BottomSheetView>
  </BottomSheetModal>
</BottomSheetModalProvider>

두 방식의 컨테이너 차이를 정리하면 이렇다.

컨테이너가 렌더되는 위치무대의 크기
BottomSheet부모 뷰 안부모 뷰 영역
BottomSheetModal포털을 통해 앱 최상단화면 전체


6. 컨테이너가 하는 일: 위치 계산의 기준

컨테이너가 중요한 이유는, 시트의 모든 위치가 화면 절대 좌표가 아니라 컨테이너를 기준으로 계산되기 때문이다.

  • snap point 환산 : '50%', '90%' 같은 값이 컨테이너 height를 기준으로 픽셀 위치로 바뀐다.
  • 상한 결정 : 시트가 최대로 올라올 수 있는 높이가 컨테이너에서 나온다.
  • 안전영역 회피 : 컨테이너의 offset(top/left/right/bottom)으로 상태바·노치·제스처바를 피한다.

이 "컨테이너의 크기와 여백" 정보가 바로 containerLayoutState(= { height, offset })다. 값을 직접 넘기지 않으면 컨테이너 컴포넌트가 onLayout으로 스스로 측정하는데, 이때 리렌더가 한 번 더 발생한다. 컨테이너 크기를 미리 알고 있다면 값을 주입해 이 측정 과정을 건너뛸 수 있다.



7. New Architecture 관점

  • 컨테이너의 크기·위치는 Reanimated SharedValue로 관리되어 UI 스레드에서 소비된다. New Architecture의 동기 레이아웃 흐름과 잘 맞는 구조다.
  • 모달 컨테이너는 포털로 최상단에 렌더되므로, react-navigation의 네이티브 스택 모달 등과 겹칠 때 스택 컨텍스트(z-순서) 문제가 생길 수 있다. Provider와 GestureHandlerRootView의 배치 위치를 함께 점검하는 것이 좋다.
  • 컨테이너 offsetreact-native-safe-area-context의 인셋과 연결하면, 노치·제스처 바 영역까지 정확히 반영된 무대를 얻을 수 있다.


요약

  • 컨테이너는 시트가 그 안에서 움직이는 무대(영역)이며, 시트 본체와는 구분된다.
  • 라이브러리 내부에 BottomSheetContainer라는 실제 컴포넌트로 존재한다.
  • 일반 BottomSheet는 부모 뷰 안에, BottomSheetModal은 포털을 통해 앱 최상단에 컨테이너를 렌더한다.
  • 컨테이너의 크기(height)와 여백(offset)이 곧 containerLayoutState이며, 시트의 모든 snap 위치가 이 값을 기준으로 계산된다.
profile
안녕하세요! 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글