Flexbox를 처음 배울 때 가장 헷갈리는 부분이 뭘까. 아마도 여러 정렬 속성들이 정확히 언제, 어느 축에서 동작하는지 구분하지 못하는 것일 거다. justify-content, align-items, align-self가 각각 뭐 하는 건지, 그리고 왜 원하는 방향으로 정렬이 안 되는지 모르는 채 속성값을 이것저것 막 바꿔보던 경험 말이다.

다만 대부분의 개발자들은 Flexbox 축의 개념을 정확히 이해하지 못한 채 인터넷에서 가져온 코드를 복사해 붙이고, 나중에 다시 같은 상황이 되면 또 헤맨다. 이번에는 주축(Main Axis)과 교차축(Cross Axis)이 정확히 뭔지, 각 정렬 속성이 어느 축에서 동작하는지, 그리고 실전에서 자주 만나는 정렬 오류를 어떻게 고칠 수 있는지 완벽하게 정리해서 소개하겠다.

 

Flexbox 축 개념 이해하기

Flexbox는 두 개의 축으로 동작한다. 하나는 주축(Main Axis)이고, 다른 하나는 교차축(Cross Axis)이다. 기본값으로 flex-direction이 row일 때는 주축이 왼쪽에서 오른쪽으로 가로 방향이고, 교차축은 위에서 아래로 세로 방향이다. flex-direction을 column으로 바꾸면 주축과 교차축이 180도 회전한다.

주축에서의 정렬은 justify-content로 제어하고, 교차축에서의 정렬은 align-items 또는 align-self로 제어한다. 이 구분을 못 하면 원하는 위치로 요소가 정렬되지 않는 것처럼 보인다.

속성 대상 축 적용 대상 기본값
justify-content 주축 (Main Axis) 모든 자식 요소 flex-start
align-items 교차축 (Cross Axis) 모든 자식 요소 stretch
align-self 교차축 (Cross Axis) 개별 자식 요소 auto (align-items 상속)

 

justify-content 완벽 가이드

justify-content는 주축 방향으로 자식 요소들을 어떻게 배치할지 결정한다. flex-direction이 row일 때는 가로 정렬을, column일 때는 세로 정렬을 한다. 가장 흔한 값들을 정리해보면 다음과 같다.

flex-start는 주축의 시작점에 붙인다. flex-end는 끝점에 붙인다. center는 중앙에 정렬한다. space-between은 첫 번째와 마지막 요소를 끝에 붙이고 나머지는 균등 배분한다. space-around는 모든 요소 양옆에 동일한 여백을 준다. space-evenly는 모든 여백을 균등하게 나눈다.

<!-- 잘못된 코드: justify-content로 세로 정렬 시도 -->
<div style="display: flex; flex-direction: column; justify-content: center; height: 300px; border: 1px solid #ccc;">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</div>

<!-- 결과: 아이템이 세로로는 정렬되지 않고 계속 맨 위에 붙어있음 -->

✗ 위 코드에서 문제는 뭘까. flex-direction을 column으로 설정하면 주축이 위에서 아래로 변경된다. 그래서 justify-content: center는 주축(세로)의 중앙에 정렬해야 하는데, 요소들이 움직이지 않는다면 height 값이 없거나 부모 요소의 높이가 자식 요소의 높이와 같다는 뜻이다. 위 코드에 height: 300px을 줬으므로 이론상 정렬되어야 하는데, 만약 정렬이 안 된다면 부모 요소의 높이가 실제로는 자동 계산되고 있다는 뜻이다.

<!-- 올바른 코드: 부모의 높이를 명확히 설정하고 justify-content 사용 -->
<div style="display: flex; flex-direction: column; justify-content: center; height: 300px; border: 1px solid #ccc;">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</div>

<!-- 결과: 세 개의 아이템이 300px 컨테이너의 중앙에 정렬됨 -->

✓ 이 코드는 주축이 명확히 세로인 상황에서 높이 제약이 있으므로 justify-content: center가 정확히 동작한다. 핵심은 주축이 어느 방향인지 파악하고, 그 방향의 컨테이너 크기가 자식 요소보다 크다는 것을 확인하는 것이다.

 

align-items와 align-self의 차이

align-items는 교차축에서 모든 자식 요소를 한 번에 정렬한다. align-self는 특정 자식 요소 하나만 다르게 정렬할 때 사용한다. align-items: center를 쓰면 모든 자식이 교차축 중앙에 정렬되지만, 한 요소만 다르게 하려면 그 요소에 align-self: flex-end를 붙이면 된다.

<!-- 잘못된 코드: align-items로 가로 정렬 시도 -->
<div style="display: flex; align-items: center; width: 100%;">
  <div style="width: 100px; background: #f0f0f0;">Left</div>
  <div style="width: 100px; background: #e0e0e0;">Center</div>
  <div style="width: 100px; background: #d0d0d0;">Right</div>
</div>

<!-- 결과: 세 개 박스가 가로로 정렬되지 않고 그냥 세로로 쌓임. align-items는 교차축이므로 가로 정렬이 안 됨 -->

✗ flex-direction이 기본값(row)이므로 주축은 가로, 교차축은 세로다. align-items: center는 교차축(세로) 중앙 정렬만 한다. 가로로 세 박스를 나열하려면 justify-content를 써야 한다.

<!-- 올바른 코드: justify-content로 가로 정렬, align-items로 세로 정렬 -->
<div style="display: flex; justify-content: center; align-items: center; height: 200px; width: 100%;">
  <div style="width: 100px; height: 100px; background: #f0f0f0;">Left</div>
  <div style="width: 100px; height: 100px; background: #e0e0e0;">Center</div>
  <div style="width: 100px; height: 100px; background: #d0d0d0;">Right</div>
</div>

<!-- 결과: 세 개 박스가 200px 높이의 컨테이너에서 가로-세로 완벽히 중앙 정렬됨 -->

✓ 이제 주축(가로)과 교차축(세로)을 동시에 제어한다. justify-content가 가로를 정렬하고, align-items가 세로를 정렬한다. 게다가 컨테이너에 높이 제약이 있으므로 정렬이 눈에 띄게 보인다.

 

한 요소만 다르게 정렬하기

때로는 대부분의 요소는 한 위치에, 특정 요소 하나만 다른 위치에 정렬해야 한다. 그럴 때 align-self를 쓴다. margin: auto를 조합하면 더욱 강력해진다.

<!-- 잘못된 코드: 마지막 요소를 오른쪽 끝에 붙이려고 justify-content 변경 -->
<div style="display: flex; justify-content: space-between; width: 100%;">
  <div style="width: 80px; background: #f0f0f0;">Home</div>
  <div style="width: 80px; background: #e0e0e0;">About</div>
  <div style="width: 80px; background: #d0d0d0;">Profile</div>
  <div style="width: 80px; background: #c0c0c0;">Logout</div>
</div>

<!-- 결과: 네 개 요소가 양쪽 끝에 붙고 중간에 여백이 생김. 세 개는 왼쪽, 하나는 오른쪽 정렬 불가 -->

✗ space-between은 첫 번째와 마지막을 끝에 붙이므로 처음 세 개와 마지막 하나를 분리하기 어렵다. 네비게이션에서 처음 세 개 메뉴는 왼쪽에, 로그아웃 버튼은 오른쪽에 놓고 싶은데 이 방법으로는 안 된다.

<!-- 올바른 코드: margin-left: auto로 마지막 요소 분리 -->
<div style="display: flex; width: 100%;">
  <div style="width: 80px; background: #f0f0f0;">Home</div>
  <div style="width: 80px; background: #e0e0e0;">About</div>
  <div style="width: 80px; background: #d0d0d0;">Profile</div>
  <div style="width: 80px; background: #c0c0c0; margin-left: auto;">Logout</div>
</div>

<!-- 결과: 처음 세 개는 왼쪽 끝에 붙고, Logout은 오른쪽 끝에 붙음 -->

✓ margin-left: auto는 Flexbox에서 마술처럼 작동한다. 왼쪽에 최대한 여백을 주라는 뜻이므로 요소가 오른쪽으로 밀려난다. 이 방법이 justify-content나 align-self보다 훨씬 실용적이다.

 

주의사항과 흔한 실수

잘못: flex-direction: column일 때 justify-content로 가로 정렬 시도
주축이 세로로 바뀌므로 justify-content는 세로만 제어한다. 가로 정렬은 align-items나 align-content를 써야 한다.

올바름: 주축과 교차축을 먼저 파악한 후 적절한 속성 선택
flex-direction이 column이면 justify-content는 세로, align-items는 가로를 제어한다는 것을 항상 기억하자.

잘못: 부모 요소에 크기 제약이 없는데 정렬 속성만 추가
자식 요소보다 컨테이너가 크지 않으면 정렬해도 눈에 띄지 않는다. 특히 justify-content나 align-items가 먹지 않는다고 느낄 때는 부모의 width나 height를 확인해야 한다.

올바름: 먼저 부모의 크기를 설정한 후 정렬 속성 적용
브라우저 개발자 도구로 부모 요소의 실제 크기를 확인하고, 필요하면 명시적으로 width나 height를 설정하자.

잘못: align-items와 align-self를 헷갈려서 개별 요소에 align-items 사용
align-items는 부모에만 적용되는 속성이다. 개별 자식을 제어하려면 align-self를 써야 한다.

올바름: 전체 자식을 정렬할 때는 align-items, 한 요소만 다르게 할 때는 align-self
부모 규칙으로 기본 정렬을 정한 후, 예외가 필요한 요소에만 align-self를 붙이면 코드가 깔끔하고 유지보수가 쉽다.

 

실전 예제: 하이브리드 레이아웃

가장 흔한 패턴은 헤더 영역을 Flexbox로 만드는 것이다. 로고, 메뉴, 사용자 정보를 한 줄에 배치하되, 메뉴는 왼쪽에, 사용자 정보는 오른쪽에 놓는 경우다.

<header style="display: flex; justify-content: space-between; align-items: center; padding: 16px 24px; background: #333; color: #fff;">
  <div style="font-weight: bold; font-size: 18px;">Logo</div>
  <nav style="display: flex; gap: 20px;">
    <a href="#" style="color: #fff; text-decoration: none;">Home</a>
    <a href="#" style="color: #fff; text-decoration: none;">About</a>
    <a href="#" style="color: #fff; text-decoration: none;">Contact</a>
  </nav>
  <div style="display: flex; gap: 10px; align-items: center;">
    <span>User</span>
    <button style="padding: 8px 16px; background: #0066cc; color: #fff; border: none; border-radius: 4px; cursor: pointer;">Logout</button>
  </div>
</header>

이 코드에서는 외부 header가 justify-content: space-between으로 로고와 사용자 정보를 양쪽 끝에 붙인다. 내부 nav는 gap: 20px으로 메뉴 간격을 균등하게 만든다. 모든 요소가 align-items: center로 세로 중앙 정렬된다.

 

마무리

Flexbox는 현대 웹 레이아웃의 핵심이다. justify-content와 align-items의 차이, 주축과 교차축의 개념을 이해하는 것만으로도 90%의 정렬 문제를 해결할 수 있다. 작은 개념의 정확한 이해가 모여서 복잡한 레이아웃도 쉽게 만들 수 있다는 점을 잊지 말자. 이 글의 축 개념 부분을 참고해 자신의 코드를 다시 검토하면, 지금까지 왜 정렬이 안 됐는지 명확히 보일 것이다.