웹 개발을 하다 보면 요소의 위치를 조정해야 할 일이 수없이 많다. 그런데 CSS position 속성을 정확히 이해하지 못한 채 top, left, right, bottom 값만 무작정 조정하다가 레이아웃이 엉망이 되는 경험을 누구나 한 번쯤 해봤을 것이다. 실무에서는 position의 종류에 따라 요소가 배치되는 기준점(containing block)이 완전히 달라지는데, 이를 모르고 있으면 z-index 꼬임, 예상과 다른 위치 배치, 스크롤 시 겹침 문제 등이 계속 반복된다. 이번에는 position의 각 값이 정확히 무엇인지, 언제 어떤 값을 써야 하는지, 그리고 자주 실수하는 경우들을 완벽하게 정리해서 소개하겠다.

 

position 속성의 5가지 값 이해하기
position 속성은 요소가 문서 흐름(normal flow)에서 어떻게 배치될지를 결정한다. 각 값마다 레이아웃 기준점과 동작 방식이 완전히 다르다.
값 설명 기준점(containing block) 문서 흐름
static 기본값. 위치 이동 불가 부모 요소 정상 흐름(flow)
relative 자신의 원래 위치 기준으로 이동 자신의 원래 위치 정상 흐름 유지
absolute 가장 가까운 position이 static이 아닌 부모 기준으로 이동 부모의 padding box 흐름에서 제거
fixed 뷰포트 기준으로 고정 뷰포트 흐름에서 제거
sticky 스크롤되면 fixed처럼 고정(최신 브라우저) 스크롤 컨테이너 정상 흐름 유지

 

각 값의 실제 동작과 예제
1. position: static (기본값)

모든 요소의 기본값이다. top, left 같은 위치 속성을 아무리 줘도 작동하지 않는다. 정상적인 문서 흐름을 따른다.

<!-- ✗ 잘못된 방법 -->
<div style="position: static; top: 50px; left: 50px;">
  위치가 이동하지 않는다
</div>

<!-- ✓ 올바른 방법 -->
<div style="position: relative; top: 50px; left: 50px;">
  위치가 50px 아래, 50px 오른쪽으로 이동한다
</div>
2. position: relative (상대 위치)

자신의 원래 있던 위치를 기준으로 이동한다. 중요한 점은 원래 차지하던 공간은 여전히 예약되어 있다는 것이다. 즉, 문서 흐름에 영향을 주지 않으면서 시각적으로만 이동한다.

<!-- ✗ 잘못된 경우: absolute의 기준점이 될 부모를 만들려고 static인 부모 사용 -->
<div style="position: static;">
  <div style="position: absolute; top: 0; left: 0;">
    이 요소는 부모를 무시하고 body를 기준으로 배치된다
  </div>
</div>

<!-- ✓ 올바른 방법: relative를 부모로 주기 -->
<div style="position: relative;">
  <div style="position: absolute; top: 0; left: 0;">
    이제 이 부모를 기준으로 배치된다
  </div>
</div>
3. position: absolute (절대 위치)

가장 자주 오용되는 값이다. position이 static이 아닌 가장 가까운 부모 요소를 기준으로 배치된다. 부모가 없으면 body(정확히는 document)를 기준으로 한다. 절대 위치 요소는 문서 흐름에서 완전히 제거되므로 다른 요소가 그 자리를 채운다.

<!-- ✗ 흔한 실수: 부모가 static인 경우 -->
<div class="parent" style="position: static;">
  <div class="child" style="position: absolute; top: 10px; left: 10px;">
    부모의 왼쪽 10px 아래에 올 거라 생각하지만
    실제로는 화면(body) 기준으로 배치된다
  </div>
</div>

<!-- ✓ 올바른 방법 -->
<div class="parent" style="position: relative;"> <!-- 부모에 relative 반드시 필요 -->
  <div class="child" style="position: absolute; top: 10px; left: 10px;">
    이제 부모 기준으로 정확히 배치된다
  </div>
</div>
4. position: fixed (고정 위치)

뷰포트(보이는 화면)를 기준으로 고정된다. 스크롤해도 움직이지 않는다. 네비게이션 바, 플로팅 버튼 같은 요소에 쓰인다. 부모의 position 값에 무관하게 항상 뷰포트 기준이다.

<!-- ✗ 잘못된 경우: transform이 있는 부모 하위에 fixed 배치 -->
<div style="transform: translateZ(0);">
  <div style="position: fixed; bottom: 0; right: 0;">
    transform이 있는 부모가 새로운 stacking context를 만들어서
    fixed가 실제로는 fixed처럼 작동하지 않을 수 있다
  </div>
</div>

<!-- ✓ 올바른 방법: body 바로 하위에 배치 -->
<div style="position: fixed; bottom: 0; right: 0;">
  플로팅 버튼
</div>
5. position: sticky (끈기 있는 위치)

sticky는 상대적으로 최신 기능이다(IE 미지원). 처음에는 relative처럼 작동하다가 스크롤되면 fixed처럼 고정된다. 부모 요소 범위 내에서만 고정되므로 테이블 헤더, 섹션 제목 같은 요소에 유용하다.

<!-- ✗ 잘못된 경우: sticky가 작동하지 않는 구조 -->
<div style="overflow: hidden;"> <!-- overflow 속성이 새 stacking context 생성 -->
  <div style="position: sticky; top: 0;">
    스크롤 범위가 제한되어 sticky가 제대로 작동하지 않음
  </div>
</div>

<!-- ✓ 올바른 방법 -->
<div>
  <div style="position: sticky; top: 0;">
    테이블 헤더처럼 스크롤하면 고정됨
  </div>
  <div>스크롤 콘텐츠</div>
</div>

 

실무에서 자주 실수하는 경우
1. absolute 자식을 정가운데 배치하려고 할 때

부모 기준으로 정가운데 배치하려면 부모가 relative여야 한다. 많은 개발자가 이를 빼먹고 top, left 값만 조정한다.

/* ✗ 잘못된 코드 */
.parent {
  width: 400px;
  height: 400px;
  /* position이 없음 = static */
}

.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* 부모가 static이므로 body 기준으로 배치됨 */
}

/* ✓ 올바른 코드 */
.parent {
  position: relative; /* 이것이 핵심 */
  width: 400px;
  height: 400px;
}

.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* 이제 부모의 정가운데에 배치됨 */
}
2. fixed 요소가 부모 범위를 벗어날 때

fixed는 뷰포트 기준이므로 부모의 width 제약을 받지 않는다. 부모가 max-width를 가진 컨테이너라면 fixed 자식이 화면 전체로 확장된다.

<!-- ✗ 문제 상황 -->
<div style="max-width: 1200px; margin: 0 auto;">
  <div style="position: fixed; bottom: 0; left: 0; right: 0; height: 50px;">
    화면 전체 너비의 플로팅 바
  </div>
</div>

<!-- ✓ 해결 방법: fixed 요소를 부모 밖으로 이동 -->
<div style="max-width: 1200px; margin: 0 auto;">
  콘텐츠
</div>
<div style="position: fixed; bottom: 0; left: 0; right: 0; height: 50px;">
  플로팅 바를 별도로 배치
</div>
3. sticky가 작동하지 않을 때

sticky가 잘 작동하려면 부모 요소가 overflow: visible이어야 하고, 부모의 높이가 sticky 요소보다 커야 한다. overflow: hidden이나 overflow: auto가 있으면 sticky의 범위가 제한된다.

/* ✗ sticky가 작동하지 않는 경우 */
.container {
  overflow: auto; /* 문제: 이 컨테이너 내에서만 sticky 고정됨 */
  height: 200px;
}

.sticky-header {
  position: sticky;
  top: 0;
}

/* ✓ 올바른 구조 */
body {
  overflow: visible; /* 기본값 */
}

.sticky-header {
  position: sticky;
  top: 0; /* 뷰포트 기준 top으로부터 0px에 고정 */
}

.scrollable-content {
  /* overflow는 여기만 */
}

 

z-index와 position의 관계

z-index는 position이 static이 아닐 때만 작동한다. static인 요소에 아무리 z-index를 줘도 효과가 없다. 또한 부모의 z-index가 낮으면 자식이 아무리 높은 z-index를 가져도 다른 부모의 요소 앞으로 나올 수 없다.

/* ✗ 잘못된 코드 */
.background {
  position: static;
  z-index: 999; /* 작동하지 않음 */
}

.overlay {
  position: relative;
  z-index: 1;
}

/* ✓ 올바른 코드 */
.background {
  position: relative; /* position이 있어야 z-index 작동 */
  z-index: 1;
}

.overlay {
  position: relative;
  z-index: 2;
}

/* 또는 더 간단하게 */
.overlay {
  position: relative;
  z-index: 10; /* 부모 z-index와 무관하게 상대적으로 위에 배치 */
}

 

정리

CSS position은 페이지 레이아웃의 핵심이다. static은 기본값일 뿐이고, relative는 자신의 원래 위치 기준, absolute는 부모(relative가 있어야 함) 기준, fixed는 뷰포트 기준, sticky는 부모 범위 내에서 고정이라는 점을 확실히 기억하자. 이 글의 비교표와 실수 사례를 참고해 position 속성을 올바르게 적용하면, 레이아웃 오류로 인한 시행착오를 크게 줄일 수 있을 것이다.