Expanded: 개념 및 Flutter에서의 작동 방식

저자: IT Sectr 게시일: 2026-07-03 읽는 시간: 6 분

Expanded는 자식 요소가 Flex 컨테이너에서 사용 가능한 모든 공간을 채우도록 강제하는 Flutter 위젯입니다. Row나 Column이 자식들 사이에 공간을 분배할 때, Expanded는 유연하지 않은 요소를 고려한 후 남은 여유 공간을 차지합니다. 공식 Flutter 문서(2026)에 따르면 Expanded는 가장 많이 사용되는 레이아웃 위젯 중 하나입니다: 백분율 기반 인터페이스, 카드, 목록 및 패널 구축에 적용됩니다. 주요 장점은 치수를 명시적으로 지정하지 않고 flex 계수를 기반으로 너비나 높이를 자동 계산하는 것입니다.

핵심 포인트

  • Expanded는 Row, Column 또는 Flex에서 자식을 확장하여 사용 가능한 모든 공간을 채우는 레이아웃 위젯입니다
  • Flex 계수는 각 Expanded가 받을 여유 공간의 비율을 결정합니다
  • Expanded는 사용 가능한 모든 공간을 강제로 채운다는 점에서 Flexible과 다릅니다
  • Stack은 Expanded를 지원하지 않으며, Row, Column 및 Flex만 지원합니다
  • 오류: Flex 컨테이너 외부에서 Expanded를 사용하면 LayoutException이 발생합니다

Expanded란

Expanded는 자식을 받아 Flex 컨테이너(Row, Column, Flex)에서 사용 가능한 공간을 채우도록 확장시키는 래퍼 위젯입니다. Expanded는 자체적인 시각적 표현이 없습니다 — 자식 요소에 전달되는 제약(constraints)만 관리합니다.

Expanded의 핵심에는 Flutter 프레임워크가 제공하는 Flex 메커니즘이 있습니다. Row나 Column이 자식의 크기를 계산할 때, 먼저 유연하지 않은 요소(고정 크기의 일반 위젯)에 공간을 할당한 후, 나머지 공간을 Expanded와 Flexible 자식들 사이에 flex 계수에 비례하여 분배합니다.

Expanded는 Flexible.fit = FlexFit.tight를 사용합니다. 이는 Expanded의 자식이 자신의 크기가 더 작더라도 할당된 모든 공간을 차지해야 함을 의미합니다. 자식이 할당된 영역보다 작을 수 있는 Fit.loose의 Flexible과 달리, Expanded는 콘텐츠를 강제로 확장합니다.

요약: Expanded는 Flutter에서 비례 레이아웃을 만드는 기본 도구입니다. 요소가 사용 가능한 모든 너비나 높이를 보장적으로 채워야 할 때 사용하세요.

Expanded vs Flexible: 주요 차이점

ExpandedFlexible은 모두 Flex 컨테이너에서 공간을 분배하지만, 근본적으로 다르게 동작합니다. 차이점은 fit 속성에 있으며, 이는 자식이 사용 가능한 공간보다 작을 수 있는지 여부를 결정합니다.

Expanded는 엄격한 동작 모델을 가집니다: fit이 FlexFit.tight입니다. 이는 자식 위젯이 사용 가능한 공간과 동일한 제약을 받는다는 것을 의미합니다 — 더도 말고 덜도 아닙니다. 자식이 더 작으면 확장되고, 더 크면 축소됩니다. Flexible은 FlexFit.loose를 사용하여 자식 요소가 할당된 공간 내에서 모든 크기를 가질 수 있도록 합니다.

실제로: Row에 flex=1인 두 개의 Expanded가 있으면 각각 정확히 너비의 절반을 차지합니다. 이를 fit.loose의 Flexible으로 대체하면, 각 자식은 자신의 크기가 더 작을 경우 절반보다 작아질 수 있으며 — 나머지 공간은 비어 있습니다.

요약: 엄격한 비례 레이아웃에는 Expanded를 사용하고, 자식이 사용 가능한 공간보다 작을 수 있는 경우에는 Flexible을 사용하세요. 확실하지 않다면 Expanded로 시작하세요 — 더 예측 가능한 동작을 제공합니다.

Flex 계수: 공간 분배 방식

Flex 계수(flex 매개변수)는 Expanded가 받을 사용 가능한 공간의 비율을 결정하는 숫자 값입니다. 기본값은 flex=1이며, 이는 모든 Expanded 자식 간에 균등한 분배를 의미합니다.

분배 알고리즘은 세 단계로 작동합니다. 첫째: Flutter는 모든 유연하지 않은 자식의 내재적 크기를 계산합니다. 둘째: 나머지 공간을 모든 flex 계수의 합으로 나눕니다. 셋째: 각 Expanded는 자신의 flex 계수에 하나의 flex 단위 크기를 곱한 값과 같은 몫을 받습니다.

예를 들어, 너비 300px의 Row에 두 개의 Expanded가 있습니다: 첫 번째는 flex=2, 두 번째는 flex=1입니다. 여유 공간(300px에서 유연하지 않은 자식을 뺀 값)은 3부분으로 나뉩니다. 첫 번째가 2/3를 받고, 두 번째가 1/3을 받습니다. 유연하지 않은 자식이 없으면 첫 번째는 200px, 두 번째는 100px을 받습니다.

Flex 계수는 양의 정수일 수 있습니다. 값 0은 특수한 경우입니다: flex=0인 Expanded는 일반적인 유연하지 않은 위젯처럼 동작하여 자식의 크기에 따라 최소 공간을 차지합니다. 이 동작은 Expanded가 없는 것과 동일합니다.

요약: flex 계수는 유연한 레이아웃을 구축하는 열쇠입니다. 동일한 열에는 1:1, 메인 영역과 사이드바 영역이 있는 패널에는 2:1, 좁은 탐색이 있는 인터페이스에는 3:1 비율을 사용하세요.

Expanded 코드 예제

예제 1은 세 개의 동일한 열을 만들기 위해 Row에서 Expanded의 기본 사용법을 보여줍니다.

dart
Row(
  children: [
    Expanded(
      child: Container(
        color: Colors.red,
      ),
    ),
    Expanded(
      child: Container(
        color: Colors.green,
      ),
    ),
    Expanded(
      child: Container(
        color: Colors.blue,
      ),
    ),
  ],
)

이 예제에서 세 개의 Expanded(기본적으로 flex=1)가 Row의 너비를 균등하게 나눕니다. 컨테이너는 Row에 설정된 전체 높이로 확장되고 동일한 몫을 차지합니다. 이 접근 방식은 비례 분배로 열 기반 레이아웃을 만드는 데 편리합니다.

예제 2는 사이드바와 메인 콘텐츠가 있는 레이아웃을 만들기 위해 다른 flex 계수를 사용하는 방법을 보여줍니다.

dart
Row(
  children: [
    Expanded(
      flex: 1,
      child: Container(
        color: Colors.grey.200,
        child: const Center(
          child: Text('사이드바'),
        ),
      ),
    ),
    Expanded(
      flex: 3,
      child: Container(
        color: Colors.white,
        child: const Center(
          child: Text('콘텐츠 영역'),
        ),
      ),
    ),
  ],
)

두 번째 예제는 사이드바(25%)와 메인 콘텐츠 영역(75%)이 있는 전형적인 레이아웃을 만듭니다. flex=1과 flex=3은 1:3 비율을 제공합니다. 화면 너비가 변경되면 비율이 자동으로 유지됩니다 — 이것이 반응형 인터페이스의 기초입니다.

요약: 다른 flex 계수를 가진 Expanded는 반응형 레이아웃을 구축하는 표준 도구입니다. Column에서 Expanded를 결합하면 수평 및 수직 비례 배열을 모두 만들 수 있습니다.

Expanded 사용 시 일반적인 실수

실수 1: Row, Column 또는 Flex 외부에서 Expanded 사용. Stack, ListView 또는 다른 Flex가 아닌 컨테이너에 Expanded를 직접 배치하면 Flutter는 "Expanded widgets must be placed inside Flex widgets" 메시지와 함께 LayoutException을 발생시킵니다. 해결책: 부모 위젯을 확인하세요 — Row, Column 또는 Flex만 가능합니다.

실수 2: shrinkWrap을 지정하지 않고 ListView 내부에 Expanded 배치. ListView는 기본적으로 스크롤 방향으로 무한한 높이를 가지며 Expanded는 자신의 몫을 계산할 수 없습니다. 해결책: shrinkWrap: true를 사용하거나 ListView를 고정 높이의 SizedBox로 감싸세요.

실수 3: 총 flex가 사용 가능한 공간을 초과하는 여러 Expanded. 제한된 너비의 Row에 Expanded와 큰 내재적 크기의 위젯이 함께 있으면 오버플로가 발생할 수 있습니다. 해결책: 유연하지 않은 자식의 최소 크기를 확인하고 필요시 FittedBox 또는 Flex fit을 사용하세요.

실수 4: Row 내부의 Column에 중첩된 Expanded. Column 내부의 Expanded가 Row 내부에 다른 Expanded를 포함하면 제약 충돌이 발생할 수 있습니다. 해결책: 중첩 깊이를 제한하거나 중간 수준에서 명시적으로 치수를 설정하기 위해 ConstrainedBox를 사용하세요.

요약: Expanded는 올바른 부모 컨텍스트가 필요합니다. Expanded가 Row, Column 또는 Flex에 직접 배치되었는지 항상 확인하고, 명시적인 치수 없이 중첩을 피하세요.

자주 묻는 질문

Expanded와 SizedBox.expand의 차이점은 무엇인가요?

SizedBox.expand는 외부 제약에 의해 설정된 치수로 자식을 강제로 확장하지만, flex 분배에는 참여하지 않습니다. Expanded는 Flex 컨테이너 내에서만 작동하며 자식 간의 여유 공간 분배 시 flex 계수를 고려합니다.

Stack 내부에서 Expanded를 사용할 수 있나요?

아니요. Stack은 Flex 컨테이너가 아니며, 내부에서 Expanded를 사용하면 LayoutException이 발생합니다. Stack에서 자식을 확장하려면 Positioned.fill 또는 BoxConstraints.expand를 사용하세요.

flex 계수를 지정하지 않으면 어떻게 되나요?

기본적으로 flex=1입니다. 명시적인 flex 없이 모든 Expanded는 여유 공간의 동등한 몫을 받습니다. Row에 flex 없는 세 개의 Expanded가 있으면 각각 사용 가능한 공간의 정확히 3분의 1을 차지합니다.

Expanded를 너비에만 적용하고 높이에는 적용하지 않으려면?

수평 확장을 위해 Expanded를 Row에 배치하세요. 수직 확장을 위해 Column 내부에서 Expanded를 사용하세요. 치수를 제한해야 하는 경우 Expanded 또는 그 자식을 SizedBox로 감싸세요.

Expanded는 Wrap이나 Flex 위젯과 함께 작동하나요?

Expanded는 Row와 Column을 하위 클래스로 포함하는 Flex와 함께 작동합니다. Wrap은 Flex 컨테이너가 아니며 Expanded를 지원하지 않습니다. Wrap에서 공간을 분배하려면 RunAlignment와 같은 다른 방법을 사용하세요.

요약

  • Expanded는 Flex 컨테이너에서 사용 가능한 모든 공간을 채우도록 자식을 강제로 확장하는 위젯입니다
  • Flex 계수는 여러 Expanded 간의 여유 공간 분배 비율을 결정합니다
  • Flexible과의 차이점: Expanded는 tight fit(강제 채움), Flexible은 loose fit(선택적)을 사용합니다
  • 오류: Row, Column 또는 Flex 외부에 Expanded를 배치하면 LayoutException이 발생합니다
  • 반응성: 화면 크기가 변경되어도 레이아웃 비율이 자동으로 유지됩니다
  • 권장사항: Flutter에서 열 및 패널 레이아웃의 기본 도구로 Expanded를 사용하세요

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기