리액트 네이티브 강좌. 6편 - Flexbox - basis, shrink, grow 및 레이아웃 전략

안녕하세요!

리액트 네이티브 강좌 6편입니다.

편의를 위해 전체 강좌 링크도 넣었습니다.

  1. 리액트 네이티브 강좌. 1편 - 소개 및 Expo 살펴보기

  2. 리액트 네이티브 강좌. 2편 - 핵심 컴포넌트 가이드 - 뷰, 텍스트, 이미지부터 커스텀 컴포넌트까지

  3. 리액트 네이티브 강좌. 3편 - 스타일링 - 스타일시트, 박스 모델, 그림자, 그리고 상속

  4. 리액트 네이티브 강좌. 4편 - Flexbox - 기본 개념과 flexDirection 설정

  5. 리액트 네이티브 강좌. 5편 - Flexbox - 정렬 및 레이아웃 속성

  6. 리액트 네이티브 강좌. 6편 - Flexbox - basis, shrink, grow 및 레이아웃 전략

  7. 리액트 네이티브 강좌. 7편 - 동적인 사용자 인터페이스 구현하기 - Dimensions API와 플랫폼별 코드 작성법

  8. 리액트 네이티브 강좌. 8편 - 리스트 렌더링하기 그리고 FlatList, SectionList 사용하기

  9. 리액트 네이티브 강좌. 9편 - Input and Forms with Switch, KeyboardAvoidingView, Form Validation

  10. 리액트 네이티브 강좌. 10편 - Networking 다루기, 데이터 fetching, loading state, error handling


** 목 차 **


플렉스 기준(Flex Basis)

이번에는 React Native에서 flexBasis 속성에 대해 알아보겠습니다.

flexBasis 속성은 컨테이너 내의 여분의 공간이 분배되기 전에 플렉스 아이템의 초기 크기를 결정합니다.

이는 플렉스 레이아웃에서 heightwidth 속성을 사용하는 대안으로 작용합니다.

UI를 통해 더 잘 이해해보겠습니다.

먼저, 아무것도 지정하지 않은 상태의 코드와 스타일입니다.

import { View, StyleSheet } from "react-native";
import Box from "../components/Box";

const App = () => {
  return (
    <View style={styles.container}>
      <Box style={{ backgroundColor: "blue" }}>Box 1</Box>
      <Box style={{ backgroundColor: "green" }}>Box 2</Box>
      <Box style={{ backgroundColor: "red" }}>Box 3</Box>
      <Box style={{ backgroundColor: "purple" }}>Box 4</Box>
      <Box style={{ backgroundColor: "orange" }}>Box 5</Box>
      <Box style={{ backgroundColor: "pink" }}>Box 6</Box>
      <Box style={{ backgroundColor: "yellow" }}>Box 7</Box>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: 64,
    borderWidth: 6,
    borderColor: "red",
  },
});

export default App;

해당되는 스크린샷도 아래와 같습니다.

기본적으로 플렉스 아이템은 박스 모델에 따라 초기 높이가 결정됩니다.

아이템의 높이는 콘텐츠 크기와 패딩에 따라 수직 방향으로 결정됩니다.

하지만 특정 아이템을 의도적으로 더 높게 설정하고 싶다면 어떻게 해야 할까요?

이러한 경우에 flexBasis 속성을 사용할 수 있습니다.

코드에서 Box 3flexBasis를 140으로 설정해보겠습니다.

<Box style={{ backgroundColor: "red", flexBasis: 140 }}>Box 3</Box>

UI를 보면 Box 3가 다른 박스들보다 두 배 높게 표시된 것을 볼 수 있습니다.

이제, 왜 height 속성을 사용하지 않고 flex basis를 사용하는지 궁금할 것입니다.

Box 4에 높이를 140으로 설정해 보겠습니다.

<Box style={{ backgroundColor: "red", flexBasis: 140 }}>Box 3</Box>
<Box style={{ backgroundColor: "purple", height: 140 }}>Box 4</Box>

위 그림을 보시면 Box 3Box 4의 높이가 똑같습니다.

결국 결과는 거의 동일합니다.

하지만 중요한 차이점이 있습니다.

Box 3Box 4가 컨테이너 내의 사용 가능한 공간을 차지하도록 설정하고 싶다면 어떻게 해야 할까요?

즉, Box 7 밑으로 조금의 여백이 있는데 이 공간까지 사용하고 싶을 때 말입니다.

이를 위해 flex 값을 1로 설정합니다.

<Box style={{ backgroundColor: "red", flexBasis: 140, flex: 1 }}>
  Box 3
</Box>
<Box style={{ backgroundColor: "purple", height: 140, flex: 1 }}>
  Box 4
</Box>

파일을 저장하고 UI를 확인해보면, Box 3Box 4보다 더 높게 표시된 것을 알 수 있습니다.

이는 사용 가능한 공간이 flexBasis를 기준으로 비례적으로 분배되기 때문이지, height 속성을 기준으로 분배되지 않기 때문입니다.

또한, flexBasis는 부모 컨테이너의 flexDirectionrow일 경우 아이템의 초기 너비를 설정한다는 점도 주목할 만합니다.

즉, flexBasis 속성은 플렉스 아이템의 초기 크기를 설정하는 데 사용됩니다.

요약하자면, flexBasis 속성은 컨테이너 내에서 여분의 공간이 분배되기 전에 플렉스 아이템의 초기 크기를 설정하는 데 사용됩니다.


플렉스 축소(Flex Shrink)

이번에는 React Native에서 flexShrink 속성에 대해 알아보겠습니다.

flexShrink 속성은 컨테이너의 자식 요소들이 컨테이너 크기를 초과할 때, 주축을 따라 얼마나 줄어들지를 결정합니다.

이 축소 계수는 컨테이너 내의 다른 아이템들과 상대적입니다.

UI를 통해 flexShrink 개념을 명확히 이해하기 위해 코드 몇 가지를 수정하겠습니다.

우선, 일부 박스를 주석 처리하고 두 개의 박스만 렌더링하여 간단하게 만듭니다.

각 박스 텍스트에 "shrink"라는 단어를 추가하고, 컨테이너의 flexDirectionrow로 변경하며, alignItemsflex-start로 설정하여 두 박스를 가로로 배치합니다.

마지막으로 컨테이너의 너비를 300으로 설정합니다.

import { View, StyleSheet } from "react-native";
import Box from "../components/Box";

const App = () => {
  return (
    <View style={styles.container}>
      <Box style={{ backgroundColor: "blue" }}>Box 1 shrink</Box>
      <Box style={{ backgroundColor: "green" }}>Box 2 shrink</Box>
      {/* <Box style={{ backgroundColor: "red" }}>Box 3</Box>
      <Box style={{ backgroundColor: "purple" }}>Box 4</Box>
      <Box style={{ backgroundColor: "orange" }}>Box 5</Box>
      <Box style={{ backgroundColor: "pink" }}>Box 6</Box>
      <Box style={{ backgroundColor: "yellow" }}>Box 7</Box> */}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: "row",
    alignItems: "flex-start",
    width: 300,
    marginTop: 64,
    borderWidth: 6,
    borderColor: "red",
  },
});

export default App;

이 결과로 두 아이템이 가로로 정렬되지만, 컨테이너의 너비가 300이기 때문에 오버플로우 됩니다.

초기 상태에서 모든 플렉스 아이템은 기본적으로 flexShrink 값이 0입니다.

그래서 아이템들이 컨테이너를 넘치게 됩니다.

하지만 flexShrink에 양의 값을 설정하여 축소 동작을 제어할 수 있습니다.

예를 들어, 두 번째 박스에 flexShrink: 1을 설정해보겠습니다.

<Box style={{ backgroundColor: "blue" }}>Box 1 shrink</Box>
<Box style={{ backgroundColor: "green", flexShrink: 1 }}>
  Box 2 shrink
</Box>

이렇게 설정하면 두 번째 박스가 컨테이너 내에 맞도록 축소됩니다.

두 박스 모두에 flexShrink를 설정하면, 두 박스는 동일하게 축소되어 컨테이너에 맞게 됩니다.

<Box style={{ backgroundColor: "blue", flexShrink: 1 }}>Box 1 shrink</Box>
<Box style={{ backgroundColor: "green", flexShrink: 1 }}>
  Box 2 shrink
</Box>

축소 계수는 다른 아이템들과 상대적입니다.

예를 들어, 두 번째 박스에 flexShrink: 2를 설정하면 첫 번째 박스보다 두 배 더 많이 축소됩니다.

<Box style={{ backgroundColor: "blue", flexShrink: 1 }}>Box 1 shrink</Box>
<Box style={{ backgroundColor: "green", flexShrink: 2 }}>
  Box 2 shrink
</Box>

이렇게 설정하면 두 번째 박스의 너비가 첫 번째 박스보다 더 적게 표시됩니다.

요약하자면, flexShrink 속성은 플렉스 아이템의 기본 크기가 플렉스 컨테이너보다 클 때 아이템들이 어떻게 동작할지를 결정합니다.

기본적으로 모든 플렉스 아이템의 flexShrink 값은 0이므로 아이템들이 컨테이너를 넘치게 됩니다.

하지만 flexShrink에 양의 값을 설정하면 아이템들이 필요할 때 축소되며, 축소 비율은 컨테이너 내의 다른 아이템들과 상대적입니다.


플렉스 증가(Flex Grow)

이번에는 React Native에서 flexGrow 속성에 대해 알아보겠습니다.

flexGrow 속성은 플렉스 컨테이너 내에서 여분의 공간이 있을 때 아이템이 얼마나 많은 공간을 차지해야 하는지를 결정합니다.

flexShrink와 유사하게, flexGrow 계수는 컨테이너 내의 다른 아이템들과 상대적입니다.

UI를 통해 flexGrow가 어떻게 작동하는지 이해해보겠습니다.

기본적으로 플렉스 아이템은 콘텐츠를 담기 위해 필요한 공간만 차지합니다.

따라서 컨테이너 내에 여분의 공간이 생깁니다.

하지만 플렉스 아이템이 성장하여 남은 공간을 채우도록 하고 싶은 경우가 있습니다.

기본적으로 모든 플렉스 아이템은 flexGrow 값이 0으로 설정되어 있어 여분의 공간을 사용하지 않습니다.

이 동작을 변경하려면 양의 값을 사용하여 flexGrow 속성을 설정할 수 있습니다.

먼저, Box 5flexGrow를 0으로 설정하고 파일을 저장해보겠습니다.

import { View, StyleSheet } from "react-native";
import Box from "../components/Box";

const App = () => {
  return (
    <View style={styles.container}>
      <Box style={{ backgroundColor: "blue" }}>Box 1</Box>
      <Box style={{ backgroundColor: "green" }}>Box 2</Box>
      <Box style={{ backgroundColor: "red" }}>Box 3</Box>
      <Box style={{ backgroundColor: "purple" }}>Box 4</Box>
      <Box style={{ backgroundColor: "orange", flexGrow: 0 }}>Box 5</Box>
      <Box style={{ backgroundColor: "pink" }}>Box 6</Box>
      <Box style={{ backgroundColor: "yellow" }}>Box 7</Box>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: 64,
    borderWidth: 6,
    borderColor: "red",
  },
});

export default App;

파일을 저장하면 변화가 없음을 알 수 있습니다.

이는 0이 기본값이기 때문입니다.

이제 Box 5flexGrow를 1로 설정하고 다시 저장해보겠습니다.

<Box style={{ backgroundColor: "orange", flexGrow: 1 }}>Box 5</Box>

이제 Box 5가 남은 공간을 차지하도록 성장한 것을 볼 수 있습니다.

동일한 설정을 Box 6에도 적용해보겠습니다.

<Box style={{ backgroundColor: "orange", flexGrow: 1 }}>Box 5</Box>
<Box style={{ backgroundColor: "pink", flexGrow: 1 }}>Box 6</Box>

이제 Box 5Box 6가 여분의 공간을 균등하게 나눠 차지하는 것을 볼 수 있습니다.

예를 들어, 100픽셀의 여분 공간이 있다면 두 아이템은 각각 50픽셀씩 성장합니다.

Box 6flexGrow 값을 1에서 3으로 변경해보겠습니다.

<Box style={{ backgroundColor: "pink", flexGrow: 3 }}>Box 6</Box>

이제 Box 6Box 5보다 세 배 더 많은 공간을 차지하는 것을 볼 수 있습니다.

즉, Box 5가 25픽셀 성장한다면, Box 6는 75픽셀 성장하게 됩니다.

모든 아이템이 여분의 공간을 균등하게 차지하도록 하려면, 모든 플렉스 아이템에 flexGrow를 1로 설정할 수 있습니다.

<View style={styles.container}>
  <Box style={{ backgroundColor: "blue" }}>Box 1</Box>
  <Box style={{ backgroundColor: "green" }}>Box 2</Box>
  <Box style={{ backgroundColor: "red" }}>Box 3</Box>
  <Box style={{ backgroundColor: "purple" }}>Box 4</Box>
  <Box style={{ backgroundColor: "orange" }}>Box 5</Box>
  <Box style={{ backgroundColor: "pink" }}>Box 6</Box>
  <Box style={{ backgroundColor: "yellow" }}>Box 7</Box>
</View>

Box 컴포넌트에서 box 스타일시트를 수정합니다.

const styles = StyleSheet.create({
  box: {
    backgroundColor: "white",
    padding: 20,
    flexGrow: 1,
  },
  text: {
    fontSize: 24,
    fontWeight: "bold",
    textAlign: "center",
  },
});

이렇게 하면 모든 아이템이 남은 공간을 균등하게 차지하여 더 깔끔한 레이아웃을 얻을 수 있습니다.

요약하자면, flexGrow 속성은 플렉스 컨테이너 내에서 아이템이 차지해야 할 여분의 공간을 결정합니다.

flexGrow 계수는 컨테이너 내의 다른 아이템들과 상대적입니다.

기본값인 0은 아이템이 성장하지 않음을 의미합니다.

모든 플렉스 아이템에 flexGrow를 1로 설정하면 여분의 공간을 균등하게 차지합니다.

마지막으로, flex 속성에 대해 처음 배운 내용을 기억하실 것입니다.

flex 속성을 양의 숫자로 설정하면, 이는 동일한 숫자로 flexGrow를 설정한 것과 같습니다.

하지만 flex 속성은 또한 flexShrink를 1로, flexBasis를 0으로 암묵적으로 설정합니다.

따라서 아래와 같은 flex 값이 있다고 하면,

flex : 양수1

이론적으로 위 코드는 아래 코드와 같습니다.

flexGrow: 양수1, flexShrink: 1, flexBasis: 0

예를 들어 flex: 1flex grow: 1, flex shrink: 1, flex basis: 0과 동일한 결과를 얻습니다.


상대 레이아웃(Relative Layout)과 절대 레이아웃(Absolute Layout)

이번에는 React Native에서 두 가지 중요한 레이아웃 타입인 relativeabsolute 레이아웃에 대해 알아보겠습니다.

이 레이아웃들은 요소가 부모 컨테이너 내에서 어떻게 위치되는지를 정의하는 position 속성에 기반합니다.

position 속성에는 relativeabsolute 두 가지 가능한 값이 있습니다.

먼저, relative 레이아웃을 살펴보겠습니다.

position 속성의 기본값은 relative입니다.

이 레이아웃에서는 요소가 레이아웃의 정상적인 흐름에 따라 위치합니다.

요소는 원래 위치에 유지되며, top, right, bottom, left 값을 사용하여 그 위치에서 오프셋될 수 있습니다.

중요한 점은 이 오프셋이 다른 형제 요소나 부모 요소의 위치에 영향을 미치지 않는다는 것입니다.

반면에 absolute 레이아웃에서는 요소가 레이아웃의 정상적인 흐름에 참여하지 않습니다.

대신, 형제 요소들과 독립적으로 배치됩니다.

요소의 위치는 top, right, bottom, left 값에 의해 결정되며, 이는 부모 컨테이너를 기준으로 한 특정 좌표를 지정합니다.

이제 VS Code로 돌아가 UI를 통해 이를 이해해보겠습니다.

먼저, 각 박스를 정사각형으로 변환하여 widthheight를 100으로 설정하겠습니다.

// Box 컴포넌트에서

const styles = StyleSheet.create({
  box: {
    backgroundColor: "white",
    padding: 20,
    width: 100,
    height: 100,
  },
  text: {
    fontSize: 24,
    fontWeight: "bold",
    textAlign: "center",
  },
});

이렇게 하면 UI에 7개의 정사각형 박스가 표시됩니다.

상대 레이아웃(Relative Layout)

먼저 기본값인 상대 레이아웃을 살펴보겠습니다.

컨테이너의 요소들은 레이아웃의 정상적인 흐름에 따라 배치됩니다.

요소를 오프셋하면 항상 원래 위치를 기준으로 오프셋됩니다.

<View style={styles.container}>
  <Box style={{ backgroundColor: "blue", top: 75, left: 75 }}>Box 1</Box>
  <Box style={{ backgroundColor: "green" }}>Box 2</Box>
  <Box style={{ backgroundColor: "red" }}>Box 3</Box>
  <Box style={{ backgroundColor: "purple", top: 75, left: 75 }}>Box 4</Box>
  <Box style={{ backgroundColor: "orange" }}>Box 5</Box>
  <Box style={{ backgroundColor: "pink" }}>Box 6</Box>
  <Box style={{ backgroundColor: "yellow" }}>Box 7</Box>
</View>

위 코드를 보면 Box 1Box 4가 각각 top: 75left: 75로 오프셋되었습니다.

UI를 보면 두 박스가 원래 위치에서 오프셋된 것을 볼 수 있으며, 이 오프셋이 다른 형제 요소의 위치에 영향을 미치지 않습니다.

절대 레이아웃(Absolute Layout)

이제 absolute 위치를 지정해보겠습니다.

이제 Box 4absolute 위치를 지정하고, top을 100, left를 100으로 설정해보겠습니다.

<Box
  style={{
    backgroundColor: "purple",
    position: "absolute",
    top: 100,
    left: 100,
  }}
>
  Box 4
</Box>

UI를 보면 Box 4가 부모 컨테이너의 왼쪽 위 모서리를 기준으로 100픽셀 떨어진 위치에 배치된 것을 볼 수 있습니다.

또한, Box 4가 레이아웃의 정상적인 흐름에 참여하지 않기 때문에 Box 3 다음에 Box 5가 이어지며, Box 4가 원래 있던 공간에 빈 공간이 남아 있지 않습니다.

요약

상대 레이아웃은 반응성과 적응성이 중요한 경우에 사용하기 좋습니다.

이는 다양한 화면 크기와 방향을 처리하는 데 더 유지보수 가능하고 유연한 접근 방식을 제공합니다.

절대 레이아웃은 UI 구성 요소의 위치와 크기를 정확하게 제어해야 할 때, 그리고 고정된 좌표로 사용자 정의 애니메이션을 만들 때 유용합니다.

지금까지 React Native에서 레이아웃에 대해 알아봤습니다.

그래서 플렉스박스 모델을 사용하여 요소를 배치하는 방법과 플렉스박스가 제공하는 다양한 속성들, 그리고 상대 레이아웃과 절대 레이아웃의 차이점을 이해할 수 있었습니다.

그럼.