본문 바로가기

FrontEnd

styled-components와 flexbox를 이용한 2D 레이아웃 디자인

반응형

styled-components의 기본 빌딩 블록인 Box 컴포넌트를 잘 활용하는 방법을 알아봅니다.

styled-components

Flexbox

플렉스박스는 하나의 레이아웃 모드입니다.

플렉스 레이아웃의 기본 아이디어는

사용 가능한 공간을 가장 잘 채울 수 있도록 플렉스 아이템의 너비/높이(및 순서)를 변경할 수 있는 기능을 컨테이너에 제공하는 것입니다

플렉스 컨테이너는 플렉스 아이템을을 확장하여 사용 가능한 여유 공간을 채우거나 항목을 축소하여 오버플로를 방지합니다.

 

기억해야할 것은, 플렉스 컨테이너는 일반적인 플로우 레이아웃의 인라인, 블록 모드에 따라 렌더링 되지만,

컨테이너 내부에서는 플로우 레이아웃과 다른 배치 알고리즘을 사용한다는 것입니다.

이는 블록 내에 아이템을 채우고 남은 공간을 어떻게 적합하게 채울지에 관한 내용으로,

플렉스 컨테이너는 너비, 높이, 순서를 변경할 수 있습니다.

 

다음은 플렉스 아이템에 적용할 수 있는 값입니다.

flex-basis

가용 공간에서 아이템이 차지할 축 방향 크기의 기준이 됩니다.

이 값보다 축 방향 크기가 크다면(높이나 너비) 커진거고, 아니면 작아진 것입니다.

flex-grow

0이면 해당 플렉스 아이템의 크기가 커지지 않습니다.

0보다 크다면, 다른 아이템들의 flex-grow값과 합산하여 가용 공간이 증가했을 때,

flex-grow값/flex-grow 합산값에 비례적으로 가용 공간을 이용해 크기를 키웁니다.

flwx-shrink

0이면 해당 플렉스 아이템의 크기가 커지지 않습니다.

0보다 크다면, 다른 아이템들의 flex-grow값과 합산하여 가용 공간이 감소했을 때,

flex-shrink값/flex-shrink 합산값에 비례적으로 줄어든 가용공간에 따라 크기를 줄입니다.


행방향 레이아웃, 열방향 레이아웃

아래 내용을 이해하기 위해 중요한 것은,

justify-contents나 align-items이 아닌 flex item이 남는 공간을 어떻게 차지하는가를 이해하는 것입니다.

 

먼저 Row 컴포넌트를 봅시다.

중요한 속성은 다음과 같습니다.

  • width : 100%을 사용하여 한 줄을 전부 사용하는 것을 가정합니다.
  • flex-wrap : wrap;
    • 해당 속성은 row 내에서의 오버플로우를 방지합니다.
  • display:flex; flex-direction:row;
    • 컨테이너를 행 방향 플렉스박스로 만들어주는 속성입니다.

(주 : span은 빠른 구현을 위해 style interpolation을 사용하였으나, 유틸리티 클래스로 변경해도 좋을 것 같습니다.

보간 방법은 성능 상 단점이 많습니다.)

const Row = styled.div<{ rawSpan?: number }>`
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  width: 100%;
  flex-grow: ${(props) => props.rawSpan ?? 1};
`;

그 다음 Col 컴포넌트를 봅시다.

중요한 속성은 다음과 같습니다.

  • flex-basis : 100%을 사용하여, 아이템이 하나만 있을 경우, row의 공간을 100% 차지하도록 합니다.
    • 아이템이 여러개 있을 경우 동일한 크기의 공간을 나눠갖습니다.
  • display:flex; flex-direction:column;
    • 컨테이너를 열 방향 플렉스박스로 만들어주는 속성입니다.

(주 : span은 빠른 구현을 위해 style interpolation을 사용하였으나, 유틸리티 클래스로 변경해도 좋을 것 같습니다.

보간 방법은 성능 상 단점이 많습니다.)

const Col = styled.div<{ colSpan?: number }>`
  display: flex;
  flex-direction: column;
  flex-basis: 100%;
  flex: 1;
  flex-grow: ${(props) => props.colSpan ?? 1};
`;

마지막으로 flex-grow를 이용하면, flex basis가 동일하므로, 공간이 충분하다면 flex-grow 비율대로 공간을 나눠갖도록 할 수 있습니다.

공간이 충분하지 않을 경우 flex-grow 값이 작은 요소가 더 많은 공간을 차지할 수 있습니다.

또한 flex-grow를 0으로 설정하면, flex-basis와 상관없이, 해당 열의 너비는 컨텐츠 크기만을 유지하는 것을 볼 수 있습니다.

 

이를 응용하면 아래와 같은 마크업은 아래 그림과 같이 그려집니다.

    <AppWrapper>
      <Row className="red">
        <span>a</span> <span>b</span>
        <span> 가나다라마바사</span>
      </Row>
      <Row>
        <Col className="blue" colSpan={2}>
          test
        </Col>
        <Col className="blue">
          test testtesttesttesttesttesttesttesttesttesttest
          testtesttesttesttesttesttesttesttesttesttesttesttesttest
        </Col>
      </Row>
      <Row>
        <Col className="blue">a</Col>
        <Col className="yellow">b</Col>
        <Col className="green">c</Col>
      </Row>
      <Row rawSpan={3}>
        <Col className="green">g</Col>
        <Col className="red" colSpan={2}>
          b
        </Col>
        <Col className="yellow">y</Col>
      </Row>
    </AppWrapper>

위 마크업이 그려진 결과

  • Row 내에 col 없이 요소를 렌더링하면, 일반적인 플로우 레이아웃 모드처럼 요소들을 배치할 수 있습니다.
  • Row 내에 Col 리스트를 배치하면, flex-basis 덕택에 전체 너비를 유동적으로 나누어 가져가게 됩니다.

이를 응용하면 Flexbox를 이용해 복잡한 레이아웃도 쉽게 구현할 수 있습니다.

참고

https://dev.to/drews256/ridiculously-easy-row-and-column-layouts-with-flexbox-1k01

 

반응형