[css] 자식 요소들이 일정 비율을 유지하도록 하기 (flex, flex-grow, flex-shrink)

todo list를 만들며 1) 타이틀 영역 2) 유저 입력 영역 3) 할일 목록 영역이 일정 비율을 유지하고, 목록이 영역을 넘어가도 전체 영역이 커지지 않고 스크롤하도록 하는 구조를 구성해보고 싶었다.

 

 

첫 시도

처음엔 웹브라우저 크기가 달라지는 것만 생각하여 flex-grow를 활용했다.

.mainContainer {
    display: flex;
    justify-content: center;
    flex-direction: column;
    align-items: center;
}

.title {
    flex-grow: 1;
}

.userInput {
    flex-grow: 1;
}

.listBody {
    flex-grow: 7;
}

하지만 내 예상과 다른 결과가 나왔다.. 목록에 할일을 추가할수록 점점 목록 영역이 차지하는 영역이 커졌다.

왜지?!! flex-grow를 다시 정확히 찾아보았다.

 

 

flex 속성들

flex-grow

  • flex-grow: 0 (기본값)  -> 컨테이너의 공간이 남아도 기본 크기를 유지한다.

flex-item 요소가 flex-container 요소 내부에서 할당 가능한 공간의 정도를 선언한다.

처음 사용할 때는 딱 이정도로 이해하고 있었다. 하지만 고려하지 못한 부분이 있었다.

바로 할당하는 공간이 flex-item들이 차지하고 남은 영역을 분배한다는 점이다.

 

컨테이너의 넓이를 1000px, 각 아이템들의 넓이를 100px로 지정한 상태다.

즉, 여기서 flex-grow를 지정하면 남은 700px을 어떻게 차지할지를 설정하는 것이 된다.

 

.container {
    width: 1000px;
    height: 100px;
    display: flex;
    flex-direction: row;
    background-color: lightgray;
}

.item1 {
    background-color: orange;
    width: 100px;
    flex-grow: 1;
}

.item2 {
    background-color: yellowgreen;
    width: 100px;
    flex-grow: 2;
}

.item3 {
    background-color: skyblue;
    width: 100px;
    flex-grow: 4;
}

 

 

예시로 적용해보았다.

아이템 1, 2, 3에 순서대로 1, 2, 4만큼의 비율을 설정하는 경우 남은 700을 1 : 2 : 4의 비율로 할당받는다.

 

첫 시도처럼 flex-grow만 설정해준 경우에는 할일 목록이 기본적으로 차지하는 비중은 늘어나는데, 할당받을 나머지 공간은 줄어드니 목록 영역이 점점 더 많은 공간을 차지하게 된 것이다.

 

 

flex-shrink

  • flex-shrink: 1 (기본값)  -> 컨테이너의 너비가 아이템보다 작으면 너비에 맞춰 줄어든다.
  • flex-shrink: 0  -> 컨테이너의 너비가 아이템보다 작아도 크기가 줄어들지 않는다.

아이템이 컨테이너를 벗어난 너비를 분배해서 줄인다. 아이템이 컨테이너 영역을 넘어선다는 전제가 있어야하기에 flex-wrap 속성이 적용되어있으면 적용되지 않는다!

또한 기본값이 1이어서, 컨테이너를 넘어서는 경우 직접 정의하지 않아도 자동으로 아이템이 축소된다. 축소되지 않게 하려면 flex-shrink: 0을 설정해줘야한다.

 

 

1000px 넓이의 컨테이너에 각각 500px 넓이의 아이템들을 넣어주었다.

flex-shrink: 0을 설정해주어 아이템들이 원래의 너비대로 자리를 차지하고 있다.

즉, 컨테이너를 벗어난 너비는 500px이고 그 500px을 줄이기위해 각 아이템이 얼마만큼의 비율을 축소할 것인가를 설정하는 것이다.

 

.item1 {
    background-color: orange;
    width: 500px;
    height: 100px;
    flex-shrink: 1;
}

.item2 {
    background-color: yellowgreen;
    width: 500px;
    height: 100px;
    flex-shrink: 0;
}

.item3 {
    background-color: skyblue;
    width: 500px;
    height: 100px;
    flex-shrink: 4;
}

1:0:4 비율을 지정해주었다. 0만큼의 비율을 줄여야하는 item2는 그대로 넓이가 500px이다.

item3은 줄여야하는 500px에서 4 / (1 + 0 +  4)만큼을 줄여 100px이 되었다.

 

 

flex-basis

  • flex-basis: auto (기본값)  -> 아이템의 컨텐츠 크기(주축 기준)를 의미
  • flex-basis: 0  -> flex-grow, flex-shrink의 설정 비율대로 비율이 조정됨

아이템의 주축방향 기본 크기를 설정한다. flex-direction: row인 경우 넓이, flex-direction: column인 경우 높이를 지정한다.

아이템이 설정값보다 작다면, 설정한 값으로 맞춰지고 아이템이 설정값보다 크다면 그대로 유지된다. 기본값은 auto다.

flex-grow는 flex-basis를 제외한 남은 공간을 나누어 가지도록 하기에 flex-basis: 0을 설정해두면 컨테이너의 모든 영역을 지정한 비율로 나눠가지는 것이 된다!

 

flex

위 세가지를 한번에 설정할 수 있는 축약형 속성이다.

flex: 1을 설정하면 flex-grow: 1, flex-shrink: 1, flex-basis: 0이된다.

 

후에 자주 사용되는 값들을 정리해봐야겠다.

 

 

적용 및 해결

.mainContainer {
    display: flex;
    justify-content: center;
    flex-direction: column;
    align-items: center;
}

.title {
    flex: 1;  /* flex-grow: 1, flex-shrink: 1, flex-basis: 0 */
}

.userInput {
    flex: 1;  /* flex-grow: 1, flex-shrink: 1, flex-basis: 0 */
}

.listBody {
    flex: 7;  /* flex-grow: 7, flex-shrink: 1, flex-basis: 0 */
}

 

각 아이템에 flex-grow가 아닌 flex를 설정해주었다.

flex-grow만 설정했을 때는 flex-basis가 auto로, 각 아이템의 컨텐츠 영역을 제외한 나머지 공간을 나눠가지게 된다.

할일 목록 영역이 사용자 입력에 따라 컨텐츠 영역이 커지면서, 나눠가질 공간의 크기가 계속 달라지는 것이 문제였는데 아이템들의 컨텐츠 영역 크기에 상관없이 컨테이너의 모든 부분을 flex-grow 비율에 맞게 나눠가지게 된 것이다!

 

원래 의도대로 타이틀, 입력, 목록 부분이 지정한 비율만큼 컨테이너의 영역을 차지하게 되었다!