Skip to content

플렉스박스 플렉스 컨테이너를 만들 때 무슨 일이 벌어질까?

그동안 그리드 레이아웃을 주로 설명했는데 이번에는 플렉스박스를 자세히 들여다보려고 한다. 플렉스박스가 어떤 목적으로 만들어졌는지, 어떤 기능이 좋은지, 왜 레이아웃 구성에 사용해서는 안 되는지 알아보자. 특히 스타일시트에 display: flex를 입력하면 정확히 어떤 일이 벌어지는지 세세하게 들여다볼 예정이다.

플렉스 컨테이너 하나 주세요!

플렉스박스를 사용하려면 플렉스 컨테이너container가 될 요소가 필요하다. CSS에 display: flex를 선언하자.


우선 display: flex가 정확히 어떤 의미일까? 디스플레이 모듈 명세 레벨 3에서는 display: flex를 디스플레이 속성의 모든 값이 내부 디스플레이 모델과 외부 디스플레이 모델 두 가지 조합이라고 설명한다. 웹 디자이너가 display: flex를 입력하면 display: block flex가 선언된다. 이 경우 플렉스 컨테이너 외부 디스플레이 형식은 block으로 설정되어 컨테이너 자체가 일반적인 블록 요소처럼 움직이도록 한다. 내부 디스플레이 형식은 flex로 컨테이너 안에 들어가는 아이템이 플렉스 레이아웃의 일부가 되도록 한다.

display: flex에 대해 심도 있게 생각해본 적은 없더라도 이해는 할 수 있을 것이다. 플렉스 컨테이너는 페이지상에서 다른 블록과 똑같이 움직인다. 만약 플렉스 컨테이너 뒤에 문단을 작성한다면 두 요소는 블록이 작동하는 것과 같은 익숙한 모습을 보여줄 것이다.

컨테이너에 inline-flex 값을 부여하면 플렉스 컨테이너가 인라인 요소처럼 움직이고 자식들은 플렉스 레이아웃에 들어가도록 display: inline flex의 효과를 낼 수 있다. 인라인 플렉스 컨테이너 내부에 있는 자식들은 플렉스 컨테이너의 자식과 동일하게 움직인다. 두 컨테이너 사이에 차이가 있다면 컨테이너 자체가 전체적인 레이아웃에서 움직이는 방식이다.


요소에 외부 디스플레이 방식을 지정하는 개념은 요소 스스로 페이지 전체에 박스로서 어떻게 동작할지 결정하고 (내부 디스플레이 방식을 통해) 자식들의 작동 방식까지 결정하기 때문에 꽤 유용하게 쓸 수 있다. 이 개념은 CSS가 사용하는 모든 박스에 적용이 가능하다. 이 요소는 어떻게 움직일까? 이 요소의 자식들은 어떻게 표시될까? 그 답은 외부와 내부 디스플레이 모델에서 찾을 수 있다.

행? 열?

플렉스 컨테이너를 만들었다면 이번엔 초깃값이 활약할 차례다. 다른 속성을 입력하지 않을 경우 플렉스 아이템들은 행으로 나열된다. 이건 flex-direction 요소의 초깃값이 row이기 때문이다. 따로 값을 지정하지 않으면 행이 선택되는 것이다.

flex-direction은 주축의 방향을 설정한다. flex-direction이 가질 수 있는 다른 값들은 아래와 같다.

  • column
  • row-reverse
  • column-reverse

행을 따라 배치하기로 결정했다면 아이템들은 인라인 영역의 출발선에 첫 번째 아이템을 놓는 것을 시작으로 소스에 나온 순서대로 자리잡는다. 명세서에서는 이 출발선을 main-start라 부른다.

main-start는 인라인 영역의 출발선에 위치한다. (이미지 확대)

만약 column으로 방향을 지정했다면 아이템들은 블록 영역의 출발선부터 놓이기 시작해 열을 만들 것이다.

main-start는 블록 영역의 출발선이다. (이미지 확대)

row-reverse를 사용할 경우 main-startmain-end의 위치가 뒤바뀌어 아이템들이 역순으로 놓이게 된다.

main-start가 인라인 영역의 끝에 위치한다. (이미지 확대)

column-reverse 역시 같은 방식으로 작동한다. 이 값들은 ‘배치 순서를 바꾸는 것’처럼 보이지만 main-start 위치를 옮겨 아이템을 배치하는 방향을 바꿀 뿐이란 사실을 기억하자. 화면에는 아이템들이 역순으로 표시되지만 사실 컨테이너의 반대편부터 놓기 시작한 것뿐이다.

또 이런 값을 사용할 때 그 효과는 시각적으로만 적용된다는 것 역시 잊지 말아야 한다. 아이템들은 끝선부터 배열됐을 뿐 순서는 입력된 대로 유지되어 스크린 리더를 사용하거나 탭 버튼을 누를 때는 원래 순서가 적용된다. 순서를 바꾸고 싶다면 소스에서 변경하면 된다.

플렉스박스의 두 축

플렉스박스의 중요한 특징인 주축main axis을 행에서 열로 바꾸는 기능을 봤다. 내가 그리드 레이아웃의 정렬 같은 기능을 더 이해하기 쉽다고 생각하는 이유는 이 주축 변경 때문이다. 두 가지 영역 모두 제어하는 그리드를 사용한다면 양 축을 모두 같은 방법으로 조정할 수 있다. 반면 플렉스박스는 주축을 제어하느냐 교차축cross axis을 제어하느냐에 따라 다르기 때문에 조금 더 복잡하다.

사실 주축은 이미 한 번 봤다. flex-direction 속성값으로 설정한 축이 바로 주축이다. 여기서 선택되지 않은 축은 교차축이 된다. flex-direction: row를 설정했을 때 주축은 행의 방향이 되며 교차축은 열의 방향이 된다. flex-direction:column을 설정할 경우 주축은 열이 되며 교차축은 행을 따라 움직인다. 플렉스박스가 가진 또 다른 중요한 특징은 스크린이라는 물리적 공간에 얽매이지 않는다는 점이다. 그래서 플렉스박스를 사용할 때 행은 왼쪽에서 오른쪽으로 향하고, 열은 위에서 아래로 향한다고 말하지 않는다. 그 이유는 늘 그렇게 움직이지 않기 때문이다.

쓰기 모드

위에서 행과 열을 설명할 때 블록과 인라인 영역을 언급했다. 이 글은 가로쓰기 모드를 사용하는 영어로 작성됐다. 가로쓰기 모드를 사용한다는 것은 플렉스박스에 행 값을 설정할 때 플렉스 아이템들이 가로로 표시된다는 걸 의미한다. 이 경우 main-start는 영어가 시작하는 왼쪽에 위치하게 된다.

만약 아랍어처럼 오른쪽에서 시작하는 언어를 사용한다면 시작선은 오른쪽에 자리할 것이다.


여기서 플렉스 컨테이너를 선언만 하고 아무런 변경도 하지 않았다면 플렉스박스 초깃값이 아이템들을 자동적으로 오른쪽부터 왼쪽으로 배치할 것이다. 인라인 방향에서의 시작선은 사용하는 쓰기 모드의 시작 위치와 같다.

만약 세로쓰기 언어를 사용한다면 텍스트가 작성되는 행의 방향이 세로이기 때문에 행은 세로로 나타날 것이다. 확인하고 싶다면 플렉스 컨테이너에 writing-mode 속성을 부여하고 값을 vertical-lr로 설정해보자. 이때 flex-directionrow로 설정하면 아이템들은 수직으로 배치된다.


행은 main-start가 왼쪽에서 오른쪽으로 향하는 수평으로 나타날 수 있고 main-start가 위에서부터 시작하는 수직으로도 나타날 수 있다. 수평 텍스트에 익숙해진 우리가 세로로 된 행을 받아들이기 어렵더라도 flex-direction의 값은 여전히 row다.

아이템들을 블록 영역에 배치하고 싶다면 flex-direction 값을 column이나 column-reverse로 설정해보자. 그러면 영어(나 아랍어)에서 아이템들이 컨테이너 꼭대기부터 차곡차곡 쌓이는 모습을 확인할 수 있을 것이다.

세로쓰기 모드에서는 블록이 배치되는 방향을 따라 블록 영역이 페이지 전체를 차지하게 된다. writing-mode의 값을 vertical-lr로 설정한 뒤 열을 불러온다면 블록들은 세로 방향을 기준으로 왼쪽에서 오른쪽으로 배치될 것이다.


하지만 블록이 어느 방향으로 표현되건 column을 사용한다면 블록 영역을 제어하는 것이다.

행과 열이 다른 물리적 방향으로도 움직일 수 있음을 이해한다면 그리드와 플렉스박스에서 사용되는 용어를 이해하는 데 도움이 될 것이다. 플렉스박스와 그리드는 문서의 쓰기 방식에 대한 어떤 추측도 하지 않으므로 ‘상하’, ‘좌우’ 같은 표현을 사용하지 않는다. CSS의 속성 대부분은 점차 쓰기 모드에 영향을 받도록 변해가고 있다. 만약 나머지 CSS 요소들까지 같은 방식으로 움직이게 만드는 속성과 값에 관심이 있다면 CSS 논리적 속성과 값 이해하기를 읽어볼 것을 권한다.

쓰기 모드에 대해 요약하면 다음과 같으니 기억해두자.

  • flex-direction: row
    • 주축 = 인라인 영역
    • main-start = 쓰기 모드에서 문장이 시작하는 지점
    • 교차축 = 블록 영역
  • flex-direction: column
    • 주축 = 블록 영역
    • main-start = 쓰기 모드에서 블록이 나타나는 지점
    • 교차축 = 인라인 영역

 

초기 정렬

display: flex를 선언할 때 일어나는 또 다른 부분을 살펴보자. 몇몇 초깃값이 그 주인공이다. 추후 다른 글에서 정렬에 대해 다룰 예정이지만 display: flex를 자세히 알기 위해서는 초깃값을 확인해야 한다.

참고: 그리드의 박스 정렬 명세가 결국 플렉스박스 명세를 대체하게 되겠지만 플렉스박스 명세가 설명하듯 정렬 속성들이 플렉스박스 명세에서 시작됐다는 걸 알아두자.

주축 정렬

justify-content의 초깃값은 flex-start로 설정되어 있다. 다음 CSS 코드를 보자.


이 코드가 플렉스 아이템들이 플렉스 컨테이너의 시작선에 놓이는 이유다. 그리고 동시에 row-reverse를 설정하면 같은 선이 끝선으로 바뀌고 맞은 편이 축의 시작 지점으로 바뀌는 원인이기도 하다.

justify-로 시작되는 정렬 속성은 모두 플렉스박스의 주축에 적용된다. 그래서 justify-content는 주축 정렬을 통해 플렉스 아이템들을 시작 지점에 정렬한다.

justify-content가 가질 수 있는 다른 값들은 아래와 같다.

  • flex-end
  • center
  • space-around
  • space-between
  • space-evenly (박스 정렬에서 추가됨)

이 값들은 플렉스 컨테이너 내부의 여유 공간 배치에 관여한다. 어떤 값을 선택하느냐에 따라 아이템들의 위치와 간격이 바뀌게 된다. 예를 들어 justify-content: space-between을 입력할 경우 컨테이너의 여백은 각 아이템에 똑같이 나뉘는 식이다. 하지만 이런 효과를 보기 위해서는 빈 영역이 필요하다. (아이템들이 배치된 후 여유 공간이 남지 않는) 꽉 찬 플렉스 컨테이너를 만들었다면 justify-content는 아무 역할도 하지 않는다.

flex-directioncolumn으로 설정하면 아래와 같은 결과가 나온다. 플렉스 컨테이너의 높이를 지정하지 않으면 여유 공간이 생기지 않기 때문에 justify-content: space-between은 아무 일도 하지 못한다. 컨테이너에 아이템을 배치하고 남을 정도의 높이를 부여한다면 justify-content: space-between이 효과를 발휘한다.


교차축 정렬

일렬로 된 플렉스 컨테이너는 내부에 담긴 박스들 사이의 배치를 조절하는 교차축 정렬을 할 수 있다. 다음 예시에서는 박스 하나가 다른 박스들보다 많은 내용을 담고 있다. 이에 한 속성이 다른 박스들에게 같은 높이로 늘어나라고 지시한다. 바로 stretch를 초깃값으로 갖고 있는 align-items 속성이다.


플렉스박스에서 align-으로 시작하는 정렬 속성들은 모두 교차축 정렬을 제어하며 align-items는 플렉스 축 안에 위치한 아이템들을 정렬한다. align-items가 갖는 다른 값은 다음과 같다.

  • flex-start
  • flex-end
  • center
  • baseline

모든 박스들이 가장 높은 박스를 따라 늘어나는 걸 원치 않는다면 align-items: flex-start를 사용해 교차축의 시작선에 맞출 수 있다.


플렉스 아이템의 초깃값

플렉스 내부 아이템 역시 초깃값을 갖는다.

  • flex-grow: 0
  • flex-shrink: 1
  • flex-basis: auto

위의 값은 주축에 남는 공간을 채우기 위해서 아이템들의 크기가 늘어나도록 만들지 않는다. 만약 flex-grow 속성이 양수 값을 갖는다면 남는 공간을 채우기 위해 커질 것이다.

대신 flex-shrink1로 설정되어 작아질 수 있다. 그래서 아주 좁은 플렉스 컨테이너를 만들 경우 내부 아이템들은 컨테이너를 넘어가지 않도록 하기 위해 줄어들게 된다. 박스에 내용을 표시할 공간이 남아 있다면 보통은 내용물이 박스 밖으로 나오기보다 내부에 있길 바라기 때문에 이는 꽤 실용적이다.

기본값으로만 최상의 레이아웃을 얻고 싶다면 flex-basisauto로 설정하는 것이 좋다. auto는 ‘내용을 다 담을 만큼 크게’란 의미로 생각하면 된다. 아래 예시는 컨테이너 안을 채우는 플렉스 아이템 중 하나가 다른 것들보다 많은 양의 내용을 담고 있어 더 많은 공간을 차지하는 경우다.


이것이 플렉스박스가 지닌 유연성이다. 플렉스 아이템에 flex-basis 속성이 자동으로 설정되어 있고, 크기가 특정되지 않았다면, 기본 크기는 max-content의 크기와 같아진다. 이는 아이템이 늘어난 뒤 다른 아이템을 감싸지 않을 때 갖는 크기다. 이 경우 각 아이템에서 일정 비율만큼 공간이 줄어들게 되는데 이에 대해서는 플렉스박스 명세서에서 확인할 수 있다.

“참고: 크기를 줄일 때는 플렉스 shrink 속성에 플렉스의 기본 크기가 곱해진다. 이렇게 아이템이 줄어들 수 있는 비율만큼 크기를 줄이면 큰 아이템이 눈에 띄게 줄어들기 전에 작은 아이템이 사라지는 일이 없게 된다.”

최종 레이아웃이 완성될 때 더 큰 아이템의 공간이 적게 줄어든다. 아래 두 스크린샷은 똑같은 속성으로 만든 페이지다. 하지만 첫 스크린샷에는 세 번째 박스가 적은 양의 내용물을 가지고 있어 공간을 좀 더 공평하게 나눠 갖는다.

 

아이템들은 더 큰 아이템에 많은 공간을 주기 위해 줄어든다. (이미지 확대)

 

플렉스박스는 웹 디자이너가 다른 CSS 코드를 작성하지 않아도 합리적인 결과물을 얻을 수 있게 한다. 내용이 긴 아이템이 있다면 공간을 균등하게 나눠 한 줄에 두세 단어씩 적어 높이가 길어지게 만들기보다 내용물을 모두 적을 수 있도록 많은 공간을 할당하는 식이다. 이런 부분이 플렉스박스를 실제로 사용하게 만드는 핵심적인 이유다. 플렉스박스는 아이템들을 (한 방향으로) 유연하게 내용 중심으로 배치할 때 가장 좋은 선택이다. 이번에 잠시 살펴봤지만 차후 이 알고리즘에 대해서 자세하게 다룰 예정이다.

 

요약

display:flex를 선언하면 어떤 일이 일어나는지 알아보기 위해 플렉스박스의 초깃값을 다뤘다. 여기서 다룬 몇 가지 속성은 플렉스 레이아웃의 주요 기능에 속한다.

플렉스 레이아웃은 유연하다. 플렉스는 기본적으로 최고의 가독성을 위해 박스의 크기를 조정한다. 플렉스 레이아웃은 쓰기 모드를 감지해 행과 열의 방향을 결정한다. 플렉스 레이아웃은 공간을 분배하는 방식을 선택해 주축 방향 아이템들을 정렬한다. 또한 교차축에서 아이템들의 위치를 조정해 플렉스 줄 위 아이템들을 정렬한다. 중요한 것은 플렉스 레이아웃이 내용물의 크기를 감지해 표시하기 가장 좋은 방법을 선택한다는 점이다. 조만간 이런 기능에 대해 깊이 있게 다룰 예정이다. 더불어 언제, 왜, 플렉스박스를 선택해야 하는가에 대해서도 이야기하려고 한다.

 

도서 소개

레이철 앤드루의 신간 『새로운 CSS 레이아웃』
그리드를 사용한 레이아웃을 기존 레이아웃과 비교해서 살펴볼 수 있습니다. 예제를 통해 그리드 레이아웃이 어떻게 활용되는지 확인해보세요. 레이아웃만을 다룬 ‘그리드 레이아웃’ 전문서 『새로운 CSS 레이아웃』입니다. 본질의 웹 디자인 시대 흐름에 뒤처지지 않으려면 이 책을 꼭 읽어야 합니다!!
저작권 정보이 글은 Smashing Magazine 기사를 번역한 것입니다. 저작권자의 정당한 허락을 받은 저작물로 한국어판 저작권은 웹액츄얼리에 있습니다. 웹액츄얼리의 서면 동의 없이 무단 전재, 복제를 금합니다. 원본은 What Happens When You Create A Flexbox Flex Container?에서 확인할 수 있습니다.
참여를 기다립니다!국내 웹 디자인계 발전에 도움 주실 분을 찾습니다. 최신 웹 기술에 관심 많은 프론트엔드 개발자이면서, 영어를 우리말로 옮기는 데 어려움 없는 분의 연락을 기다립니다. 번역물에 대한 소정의 번역료를 지급합니다. 메일로 연락주시면 안내드리겠습니다.
books@webactually.com


맨위로