CSS position 완전 이해: relative·absolute·fixed·sticky 실습

··설명 약 7분·작성: 해비·블로그 소개

CSS relative, absolute, fixed, sticky의 배치 기준은 어떻게 다른가요?

relative는 원래 공간을 유지하고 absolute는 기준 조상에 맞춰 배치됩니다. fixed는 기본적으로 화면에 고정되며 sticky는 스크롤 중 지정 위치에 붙습니다. 배치 기준과 남는 공간을 구분합니다.

적용 조건·범위: absolute 요소는 일반 flow에서 빠지고 가장 가까운 positioned ancestor 등을 기준으로 위치를 계산합니다.

공식 문서 참고: MDN: relative·absolute·fixed·sticky의 배치 기준

독립 실습: 직접 만들고 확인하기

CSS position 완전 이해: relative·absolute·fixed·sticky 실습 · 독립 실습

문서를 스크롤하며 absolute 배지, sticky 제목과 fixed 문의 링크의 위치를 비교하세요.

예제 크게 보기 (새 탭)

독자 질문

카드의 NEW 배지는 카드 안에, 상단 안내는 스크롤 시 위에, 문의 링크는 화면 오른쪽 아래에 어떻게 고정할까요?

직접 만들 화면

높이가 긴 소개 화면에서 카드 배지·sticky 안내·fixed 문의 링크가 서로 다른 기준으로 움직이는 모습

새 폴더의 index.html과 style.css에 각각 저장해 브라우저에서 엽니다.

<!doctype html>
<html lang="ko">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>CSS position 완전 이해: relative·absolute·fixed·sticky 실습</title><link rel="stylesheet" href="./style.css"></head>
<body>
<header class="demo-header">TaskFlow 위치 실습</header>
<main class="demo-main">
  <article class="demo-card">
    <span class="demo-badge">NEW</span>
    <h2>빠른 작업 추가</h2>
    <p>해야 할 일을 한곳에 정리합니다.</p>
  </article>
  <p class="demo-spacer">아래로 스크롤하며 위치를 비교하세요.</p>
  <h2 id="contact">문의 영역</h2>
</main>
<a class="demo-help" href="#contact">문의</a>
</body>
</html>
.demo-header { position: sticky; top: 0; z-index: 1; padding: 16px; background: #eee; }
.demo-main { min-height: 160vh; padding: 24px; }
.demo-card { position: relative; max-width: 360px; padding: 24px; border: 1px solid #777; }
.demo-badge { position: absolute; top: 12px; right: 12px; background: #fff; }
.demo-spacer { margin-top: 80vh; }
.demo-help { position: fixed; right: 16px; bottom: 16px; padding: 12px; background: #222; color: #fff; }

확인할 결과와 실제 검증 상태

스크롤 전후 배지는 카드 안에 남고 안내는 상단에 붙으며 문의 링크는 화면에 남아야 합니다. .demo-card의 position: relative를 제거하면 배지 기준점이 카드 밖으로 바뀔 수 있습니다. 360px과 키보드 Tab으로 문의 링크가 내용을 가리지 않는지도 직접 확인하세요.

실제 브라우저·DevTools 조작 결과: 미확인. 위 내용은 코드에서 예상되는 결과와 독자가 수행할 확인 절차입니다.

다음 학습 연결

05회차에서는 위치 실습에 이어 JavaScript 없이 접고 펼치는 FAQ를 추가합니다.

static

기본값입니다.

요소는 normal flow 안에서 앞뒤 요소가 차지하는 공간을 고려해 배치됩니다.

.card {
  position: static;
}

대부분의 일반 레이아웃은 static + Flex/Grid로 해결합니다.

relative

relative는 원래 자리의 공간을 유지하면서 자신을 기준으로 위치를 조정할 수 있습니다.

더 자주 쓰는 목적은 absolute 자식의 기준점이 되는 것입니다.

.card {
  position: relative;
}

absolute

absolute 요소는 일반 flow에서 빠지고 가장 가까운 positioned ancestor 등을 기준으로 위치를 계산합니다.

.card {
  position: relative;
}


.card__badge {
  position: absolute;
  top: 12px;
  right: 12px;
}

badge는 card를 기준으로 배치됩니다.

부모에 relative가 없다면 예상과 다른 조상 또는 viewport에 가까운 기준을 사용할 수 있습니다.

배지는 기준이 되는 카드 안에 배치하고, 고정 버튼은 기준 영역과 콘텐츠 가림 여부를 함께 확인합니다.
개념 설명용 일러스트. 배지는 기준이 되는 카드 안에 배치하고, 고정 버튼은 기준 영역과 콘텐츠 가림 여부를 함께 확인합니다. 이미지 원본 열기

fixed

기본적으로 viewport를 기준으로 고정됩니다. 다만 조상의 transform이 none이 아니거나 contain 같은 속성이 기준 영역을 만들면 그 조상을 기준으로 동작할 수 있으므로 fixed가 예상과 다르게 움직일 때 부모 CSS도 확인합니다.

.help-button {
  position: fixed;
  right: 24px;
  bottom: 24px;
}

스크롤해도 같은 화면 위치에 남습니다.

중요한 콘텐츠를 가리거나 mobile viewport에서 버튼이 겹치지 않는지 확인합니다.

sticky

sticky는 normal flow에 참여하다가 scroll container 안에서 지정 위치에 도달하면 고정처럼 동작합니다.

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

sticky가 작동하지 않으면 다음을 확인합니다.

  • top 등 inset 값이 있는가
  • 실제로 스크롤되는 공간이 있는가
  • ancestor overflow가 예상과 다른 scroll container를 만들지 않았는가

독립 예제

<article class="demo-card">
  <span class="demo-card__badge">
    NEW
  </span>
  <h2>기능 카드</h2>
  <p>position 예제입니다.</p>
</article>
.demo-card {
  position: relative;
  padding: 24px;
  border: 1px solid #ddd;
}


.demo-card__badge {
  position: absolute;
  top: 12px;
  right: 12px;
}

자주 발생하는 오류

absolute 요소가 엉뚱한 곳에 붙습니다

기준 부모에 position: relative가 있는지 확인합니다.

sticky가 안 됩니다

scroll container와 top 값을 확인합니다.

fixed 요소가 본문을 가립니다

작은 viewport에서 실제 위치를 확인하고 필요하면 safe spacing을 확보합니다.

학습 완료 체크리스트

  • static·relative·absolute·fixed·sticky를 구분할 수 있습니다.
  • absolute의 기준이 되는 containing block을 이해했습니다.
  • sticky가 동작하지 않는 원인을 기본적으로 진단할 수 있습니다.
  • 일반 레이아웃은 Flex/Grid를 우선할 수 있습니다.

독립 실습은 여기서 마칠 수 있습니다. 아래 선택 실습은 TaskFlow 프로젝트 파일로 진행합니다.

선택 실습: 할 일 앱에 적용하기

TaskFlow는 간단한 서비스 화면에서 시작해 할 일 앱으로 이어 만드는 예제입니다. 이 선택 실습에서는 HTML과 CSS로 이번 화면을 만듭니다.

TaskFlow 카드 badge와 sticky header, fixed 도움 버튼을 만들어 position의 역할을 분리합니다.

선택: 연재 프로젝트와 전체 자료

연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.

TaskFlow 프로젝트 적용

CSS position 완전 이해: relative·absolute·fixed·sticky 실습 · 단계별 TaskFlow

TaskFlow 누적 4단계입니다. 문서를 스크롤하며 absolute 배지, sticky 제목과 fixed 문의 링크의 위치를 비교하세요.

예제 크게 보기 (새 탭)

실행 가능한 시작본과 이번 단계

이번 단계 시작 ZIP을 새 폴더에 압축 해제합니다. 이 ZIP은 직전 단계의 완료 파일과 같으며 앞 글의 코드를 따로 모을 필요가 없습니다. 이번 단계 완료 ZIP은 다른 폴더에 풀어 비교합니다.

첫 기능 카드에 NEW 배지를 붙이고 본문 밖 도움 링크와 sticky header를 적용합니다. 한쪽 테두리 장식 대신 전체 테두리로 구분합니다.

  1. index.html: 이 파일의 기존 내용을 아래 완료 파일 전체와 대조하여 교체합니다. 다른 파일은 유지합니다.
  2. style.css: 이 파일의 기존 내용을 아래 완료 파일 전체와 대조하여 교체합니다. 다른 파일은 유지합니다.

짧은 코드 예제는 변경 위치를 설명합니다. 파일 전체를 작성하거나 교체할 때는 완료 파일 전체를 사용하세요. 생략 표시가 있는 부분 코드를 파일 전체로 저장하지 않습니다.

실행: ZIP의 index.html을 브라우저에서 엽니다. 외부 서버나 npm 설치 없이 로컬 HTML·CSS·이미지를 사용합니다. 문의 폼은 서버 전송을 연결하지 않은 문서 예제입니다. 연습 값만 사용하고 입력 형식을 확인합니다.

직접 확인할 결과: 배지는 카드 기준, 문의 링크는 viewport 기준입니다. 문의 링크의 이동 대상이 존재합니다.

전체 파일·코드 보기

완료 파일 전체

index.html 전체 파일

<!DOCTYPE html>
<html lang="ko">
 <head>
  <meta charset="utf-8"/>
  <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
  <title>
   TaskFlow
  </title>
  <link href="./style.css" rel="stylesheet"/>
 </head>
 <body>
  <header class="site-header">
   <div class="page site-header__inner">
    <a class="brand" href="#home">
     TaskFlow
    </a>
    <nav aria-label="주요 메뉴" class="nav">
     <a href="#features">
      기능
     </a>
     <a href="#contact">
      문의
     </a>
    </nav>
   </div>
  </header>
  <main class="page" id="home">
   <section>
    <h1>
     해야 할 일을 한눈에 정리하세요
    </h1>
    <p>
     TaskFlow는 오늘의 작업을 빠르게 정리하는 서비스입니다.
    </p>
   </section>
   <section id="features">
    <h2>
     주요 기능
    </h2>
    <div class="feature-list">
     <article class="card feature-card">
      <span class="feature-card__badge">
       NEW
      </span>
      <h3>
       빠른 작업 추가
      </h3>
      <p>
       해야 할 일을 바로 기록할 수 있습니다.
      </p>
     </article>
     <article class="card">
      <h3>
       간단한 분류
      </h3>
      <p>
       해야 할 일을 구분합니다.
      </p>
     </article>
     <article class="card">
      <h3>
       진행 확인
      </h3>
      <p>
       완료 여부를 확인합니다.
      </p>
     </article>
    </div>
   </section>
   <section id="contact">
    <h2>
     문의하기
    </h2>
    <form>
     <div class="form-field">
      <label for="name">
       이름
      </label>
      <input autocomplete="name" id="name" name="name" type="text"/>
     </div>
     <div class="form-field">
      <label for="email">
       이메일
      </label>
      <input autocomplete="email" id="email" name="email" type="email"/>
     </div>
     <div class="form-field">
      <label for="message">
       문의 내용
      </label>
      <textarea id="message" name="message" rows="5"></textarea>
     </div>
     <button type="submit">
      문의 보내기
     </button>
    </form>
   </section>
  </main>
  <footer class="site-footer">
   <div class="page">
    <p>
     © TaskFlow
    </p>
   </div>
  </footer>
  <a class="help-button" href="#contact">
   문의
  </a>
 </body>
</html>

style.css 전체 파일

:root {
  color: #171717;
  background: #f5f5f5;
}


*,
*::before,
*::after {
  box-sizing: border-box;
}


body {
  margin: 0;
  font-family:
    Arial,
    sans-serif;
  line-height: 1.6;
  color: #171717;
  background: #f5f5f5;
}


.page {
  width: min(
    100% - 32px,
    960px
  );
  margin-inline: auto;
}


.site-header,
.site-footer {
  padding-block: 20px;
}


main {
  padding-block: 48px;
}


section + section {
  margin-top: 64px;
}


.card {
  padding: 24px;
  border: 1px solid #d7d7d7;
  background: #fff;
}


form {
  display: grid;
  gap: 16px;
}


.form-field {
  display: grid;
  gap: 8px;
}


input,
textarea {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid #b8b8b8;
  background: #fff;
}


button {
  min-height: 44px;
  padding: 10px 16px;
  border: 1px solid #171717;
  background: #171717;
  color: #fff;
}

a { color: inherit; }
input, textarea, button { font: inherit; }
h1 { font-size: 32px; font-weight: 700; }
h2 { font-size: 24px; }
h3 { font-size: 20px; }

.site-header__inner {
  display: flex;
  flex-direction: column;
  gap: 12px;
}


.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}


.feature-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}


@media (min-width: 48rem) {
  .site-header__inner {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }


  .feature-list {
    flex-direction: row;
  }


  .feature-list > * {
    flex: 1;
  }
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: #f5f5f5;
  border: 1px solid #ddd;
}


.feature-card {
  position: relative;
}


.feature-card__badge {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: 4px 8px;
  border: 1px solid #171717;
  background: #fff;
  font-size: 12px;
}


.help-button {
  position: fixed;
  right: 16px;
  bottom: 16px;
  min-width: 44px;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid #171717;
  background: #171717;
  color: #fff;
  text-decoration: none;
}

위 파일과 ZIP의 동명 파일은 동일합니다. 회차별 README·CHANGES 문서는 ZIP에 포함합니다. 생성 산출물·node_modules·실제 환경 변수 파일은 포함하지 않습니다.

index.html

기능 카드 badge:

<article class="card feature-card">
  <span class="feature-card__badge">
    NEW
  </span>
  <h3>빠른 작업 추가</h3>
  <p>
    해야 할 일을 바로 기록할 수 있습니다.
  </p>
</article>

페이지 하단 도움 링크:

<a
  class="help-button"
  href="#contact"
>
  문의
</a>

style.css

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: #f5f5f5;
  border-bottom: 1px solid #ddd;
}


.feature-card {
  position: relative;
}


.feature-card__badge {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: 4px 8px;
  border: 1px solid #171717;
  background: #fff;
  font-size: 12px;
}


.help-button {
  position: fixed;
  right: 16px;
  bottom: 16px;
  min-width: 44px;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid #171717;
  background: #171717;
  color: #fff;
  text-decoration: none;
}

시작본과 이번 변경

CSS Flex와 미디어 쿼리 입문: 반응형 카드 페이지 만들기의 완료 체크포인트를 시작본으로 사용합니다.

index.html 카드에 배지를, 본문 뒤에 도움 링크를 추가합니다. style.css에서 카드 relative·배지 absolute·헤더 sticky·도움 링크 fixed를 설정하고 작은 화면에서 가림을 검사합니다.

연속 프로젝트 선수 조건

  • normal flow와 Flexbox 기본을 알고 있으면 좋습니다.
  • position은 일반 레이아웃을 대체하는 도구가 아니라는 전제에서 진행합니다.

이전 체크포인트

publishing-step-03-flex-responsive

이번 목표

TaskFlow 카드 badge와 sticky header, fixed 도움 버튼을 만들어 position의 역할을 분리합니다.

실행 확인

  • badge가 각 card 안에서 오른쪽 위에 붙는가
  • header가 scroll 시 상단에 유지되는가
  • fixed 도움 버튼이 콘텐츠를 가리지 않는가
  • 360px viewport에서도 버튼이 화면 밖으로 나가지 않는가

완료 체크포인트

publishing-step-04-position

다음 학습

다음 글에서는 JavaScript 없이 details와 summary를 사용해 native FAQ disclosure를 추가합니다.

글 소개·학습 준비·전체 목차
CSS position 완전 이해: relative·absolute·fixed·sticky 실습 대표 일러스트

웹 퍼블리싱 · 4 / 15 · 필수 학습 · CSS position

relative는 원래 자리의 공간을 유지하고, absolute는 기준 조상에 맞춰 흐름 밖에 배치됩니다. fixed는 기본적으로 화면에 고정되고, sticky는 스크롤 영역 안에서 지정한 위치에 도달하면 붙습니다.

normal flow로 배치하기 어려운 특정 UI를 기준 좌표에 맞춰 배치할 때 사용합니다. 이번 글에서는 relative·absolute·fixed·sticky가 어떤 기준으로 움직이고 문서 흐름에 어떤 영향을 주는지 구분합니다.

이번 글에서 배우는 내용

  • position: static
  • relative
  • absolute
  • fixed
  • sticky
  • containing block
  • top/right/bottom/left
  • position을 레이아웃 대체 수단으로 쓰지 않는 기준

먼저 알아야 할 내용

CSS 박스 모델과 normal flow, 선택자를 알면 됩니다. 아래 문서 한 개의 스크롤로 세 위치 기준을 비교합니다.

목차

이 글이 도움이 되었나요?

조회 중

읽음 기록

읽음 — / 전체 15개 · 필수 13개

이 글을 쓴 해비

웹사이트를 제작·운영하고, 개인 서비스와 자동화·AI 도구를 만듭니다. 직접 만들며 배운 내용과 문제 해결 과정을 BlogFlow에 기록합니다.

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기