
솔직히 저는 float 레이아웃이 잘못됐다는 걸 한참 동안 몰랐습니다. 사이드바가 본문 아래로 밀려나는 현상을 며칠째 붙잡고 있으면서도 clearfix를 빠뜨린 탓인지, 너비 계산이 틀린 건지 갈피를 잡지 못했습니다. 지금 돌아보면 그 며칠이 꽤 소모적인 시간이었는데, 문제는 기술적인 이해 부족이 아니라 "이 방식 자체가 맞는가"라는 질문을 너무 늦게 던졌다는 데 있었습니다.
그러다 Flexbox를 처음 써본 날, 부모 요소 하나에 display: flex 한 줄을 추가했더니 자식 요소들이 나란히 붙어버렸습니다. 너무 간단해서 오히려 뭔가 빠진 게 있는 줄 알고 코드를 두세 번 다시 확인했습니다. float 시절의 저는 clearfix, 너비 계산, overflow 처리까지 머릿속에 함께 굴리면서 레이아웃을 짰는데, Flexbox는 그 전부를 한 줄로 압축해버렸습니다. 그 충격은 생각보다 컸습니다. 그리고 솔직히 조금 허탈했습니다.
주축과 교차축, 개념부터 잡아야 실수가 줄어든다
Flexbox를 처음 배울 때 가장 먼저 막히는 부분이 주축(main axis)과 교차축(cross axis) 개념입니다. 주축이란 flex 컨테이너 안에서 자식 요소들이 나열되는 기본 방향을 말하고, 교차축은 그 방향에 수직으로 교차하는 축을 말합니다. flex-direction이 기본값인 row일 때는 주축이 가로, 교차축이 세로가 됩니다.
여기서 헷갈리기 시작합니다. justify-content는 주축 방향 정렬을 담당하고, align-items는 교차축 방향 정렬을 담당합니다. 글로 읽으면 이해가 되는 것 같은데, flex-direction을 column으로 바꾸는 순간 축이 뒤집힙니다. 주축이 세로가 되고 교차축이 가로가 되기 때문에, justify-content와 align-items가 하는 역할도 뒤바뀝니다.
제가 직접 써봤는데, 이 부분에서 실수가 반복됐습니다. 외웠다고 생각했는데 막상 코드를 짜면 방향이 꼬이는 경험이 계속됐습니다. 나중에 왜 이게 이렇게 헷갈리는지 생각해봤는데, "justify는 가로, align은 세로"라고 기억하다가 flex-direction이 바뀌면 그 고정 기억이 오히려 방해가 됩니다. justify와 align을 각각의 축과 묶어서 기억하는 게 아니라, 어느 축이 주축인지를 먼저 파악하는 습관이 필요합니다. 저는 이 관점 전환이 꽤 오래 걸렸습니다.
공식 문서를 먼저 읽는 방식보다 실제 레이아웃 문제를 먼저 던지고 거기서 역으로 개념을 익히는 게 훨씬 효과적이라고 생각합니다. 저는 티스토리 사이드바 문제를 직접 해결하면서 이 개념을 체득했는데, 그쪽이 MDN 문서를 처음부터 읽는 것보다 훨씬 머릿속에 잘 남았습니다. MDN Web Docs에서도 Flexbox 관련 속성을 단독으로 설명하기보다 실제 사용 맥락과 함께 기술하고 있는데, 그래도 처음 접하는 분들에게는 작은 레이아웃 예제부터 직접 만들어보는 편이 더 빠릅니다. 여기서 한 가지 비판을 보태자면, Flexbox 튜토리얼 대부분이 "이 속성은 이런 값을 가진다"는 형태로 나열되어 있어서 실제로 레이아웃을 부수고 고치는 경험을 주지 않습니다. 그 구조가 오히려 이해를 늦춥니다.
align-items의 기본값이 stretch라는 점도 직접 겪어봤는데, 사이드바가 본문 길이만큼 쭉 늘어나는 현상이 바로 여기서 비롯됩니다. 따로 선언하지 않으면 자식 요소가 교차축 방향으로 컨테이너를 꽉 채웁니다. 한참 뒤에 이 이유를 알았을 때 허탈했습니다. 한 줄이면 해결되는 문제였으니까요. 그런데 동시에 이건 stretch가 왜 기본값인지를 이해해야 납득이 갑니다. 높이가 서로 다른 자식 요소들이 나란히 있을 때 레이아웃 균형을 유지하려면 stretch가 자연스러운 기본값이기 때문입니다. 내가 원하지 않는 동작이라도, 그 이유를 알면 당황하지 않습니다.
flex-shrink와 반응형, 실제 레이아웃에서 쓴 방식
주축과 교차축을 이해했다면 다음은 실제 레이아웃 조립입니다. 티스토리 블로그 기준으로 광고, 본문, 사이드바 3단 구성을 만들 때 제가 쓴 방식은 이렇습니다. 광고 영역과 사이드바는 너비를 고정하고, 본문에만 flex: 1을 줍니다.
flex: 1이란 flex-grow: 1, flex-shrink: 1, flex-basis: 0을 한꺼번에 지정하는 단축 속성입니다. 쉽게 말해 "남은 공간을 이 요소가 전부 가져가라"는 의미입니다. 피자 한 판을 세 사람이 나눠 먹을 때, 광고와 사이드바가 이미 자기 조각을 가져간 뒤 남은 판 전체를 본문이 챙기는 구조입니다. 처음에 flex-grow 개념이 잘 안 잡혔는데, 이렇게 생각하니 바로 이해됐습니다.
반대로 flex-shrink: 0이란 화면 너비가 줄어들어도 해당 요소의 크기를 줄이지 않겠다는 선언입니다. 광고 영역에 이 값을 주지 않으면 브라우저 창을 좁혔을 때 광고 컨테이너가 덩달아 찌그러집니다. 애드센스 광고의 경우 컨테이너 너비가 의도치 않게 바뀌면 CLS(Cumulative Layout Shift) 수치가 올라갑니다. CLS란 페이지 로딩 중 눈에 보이는 요소가 갑자기 이동하거나 크기가 바뀌는 정도를 수치화한 지표로, Google의 Core Web Vitals 평가 항목 중 하나입니다. 광고 컨테이너에 min-height를 지정해두면 광고가 로딩되기 전에 레이아웃이 흔들리는 현상을 줄이는 데 도움이 됩니다.
이 부분에서 개인적으로 한 가지 더 말하고 싶은 게 있습니다. flex-shrink: 0을 습관적으로 모든 고정 요소에 붙이는 분들이 있는데, 모바일에서 화면이 극단적으로 좁아질 때는 그 고정값이 레이아웃을 오히려 망가뜨릴 수 있습니다. 반응형 설계에서 "고정"이라는 단어는 항상 조건부여야 합니다. 어느 구간까지 고정인지를 명확히 하지 않으면, flex-shrink: 0이 달린 요소가 화면 밖으로 튀어나가는 상황이 생깁니다.
모바일 대응은 생각보다 훨씬 단순합니다. 미디어쿼리 안에서 flex-direction을 column으로 바꿔주면, 가로로 나란히 놓였던 세 영역이 세로로 쌓입니다. 제가 처음 브라우저 창을 좌우로 늘리고 줄이면서 광고는 고정되고 본문만 유동으로 늘어나는 걸 확인했을 때, 이렇게 자연스럽게 반응형이 구현된다는 게 신기해서 한동안 창 크기를 계속 조절해봤습니다. float 시절에 이 작업이 얼마나 번거로웠는지 떠오르면서 씁쓸하기도 했습니다.
다만 Flexbox가 만능은 아닙니다. 1차원 레이아웃, 즉 가로 한 줄 또는 세로 한 줄 배치에는 강하지만, 행과 열을 동시에 제어해야 하는 2차원 구조에서는 코드가 복잡해집니다. 그런 경우에는 CSS Grid를 쓰는 게 낫습니다. "Flexbox는 1차원, Grid는 2차원"이라는 기준 하나만 잡아두면 실무에서 어떤 걸 꺼낼지 판단이 빨라집니다. 이 기준도 완벽하진 않습니다. 실무에서는 두 가지를 혼용하는 경우가 훨씬 많고, 어느 하나를 고집하다 보면 불필요하게 복잡해지는 경우도 생깁니다. 도구는 상황에 맞게 꺼내 써야 합니다.
Flexbox는 개념을 한번 제대로 잡아두면 그 이후 레이아웃 작업 속도가 눈에 띄게 달라집니다. 저는 지금 float을 거의 쓰지 않고 Flexbox를 먼저 꺼내는 게 자연스러운 루틴이 됐습니다. 처음에 주축과 교차축 개념이 어색하더라도, 간단한 3단 레이아웃을 직접 만들면서 flex-shrink와 align-items를 하나씩 건드려보는 것이 가장 빠른 길입니다. 읽는 것보다 손으로 깨지면서 익히는 쪽이 훨씬 오래 남습니다.
참고:
MDN Web Docs — CSS Flexible Box Layout
CSS-Tricks — A Complete Guide to Flexbox
Google Search Central — Core Web Vitals