Engineering note ยท CSS

Stacking Context ์˜ ์›๋ฆฌ

2026๋…„ 08์›” 09์ผ์ดํ˜„์ˆ˜
ํƒœ๊ทธcsshtml

z-index: 9999๋ฅผ ๋ถ€์—ฌํ•ด๋„ ์š”์†Œ๊ฐ€ ๋ถ€๋ชจ ๋ ˆ์ด์–ด ๋’ค๋กœ ๊น”๋ฆฌ๋Š” ์›์ธ์ธ ์Œ“์ž„ ๋งฅ๋ฝ(Stacking Context)์˜ ์ƒ์„ฑ ์กฐ๊ฑด๊ณผ isolation: isolate๋ฅผ ํ™œ์šฉํ•œ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ์ •๋ฆฌ.

๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด๋‚˜ ๋ชจ๋‹ฌ ํŒ์—… ์š”์†Œ์— z-index: 9999๋ฅผ ์คฌ๋Š”๋ฐ๋„, ๋‹ค๋ฅธ ์นด๋“œ๋‚˜ ๋ถ€๋ชจ ์˜์—ญ ๋’ค๋กœ ๊น”๋ ค๋ฒ„๋ฆฌ๋Š” ๊ฒฝ์šฐ๋„ ์ƒ๊ธด๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™”๋ฉด์„ 3์ฐจ์› ๋ ˆ์ด์–ด๋กœ ๊ฒน์ณ ๊ทธ๋ฆด ๋•Œ ์ ์šฉํ•˜๋Š” Stacking Context ๋•Œ๋ฌธ์ด๋‹ค.

๋ Œ๋”๋ง ์ˆœ์„œ์˜ ๊ธฐ๋ณธ ๋™์ž‘

ํŠน๋ณ„ํ•œ z-index ์„ค์ •์ด ์—†๋Š” ํ˜•์ œ ์š”์†Œ๋“ค ์‚ฌ์ด์—์„œ๋Š” ๋‚˜์ค‘ ์š”์†Œ๊ฐ€ ํ™”๋ฉด ์•ž์ชฝ์— ๊ทธ๋ ค์ง„๋‹ค.

html
<div class="box red"></div>
<div class="box blue"></div>
<div class="box green"></div>
css
.box {
  width: 100px;
  height: 100px;
}
.red {
  background: red;
  position: relative;
  top: 40px;
}
.blue {
  background: blue;
}
.green {
  background: green;
}

์›๋ž˜๋Š” ๋‚˜์ค‘์— ์„ ์–ธ๋œ .blue๊ฐ€ ์œ„์— ์™€์•ผ ํ•˜์ง€๋งŒ, .red์— position: relative๋ฅผ ๋ถ€์—ฌํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ํฌ์ง€์…˜์ด ์ง€์ •๋œ ์š”์†Œ๋ฅผ ์ผ๋ฐ˜ ํ๋ฆ„ ์š”์†Œ๋ณด๋‹ค ์•ž์ชฝ์— ๋ Œ๋”๋งํ•œ๋‹ค. ๋”ฐ๋ผ์„œ 40px ๋ฐ€๋ ค๋‚œ .red๊ฐ€ .blue ์œ„๋กœ ์˜ฌ๋ผ์˜จ๋‹ค.

Stacking Context ์ด ๊ฐ€๋‘ฌ๋ฒ„๋ฆฌ๋Š” Z์ถ• ๋ฒ”์œ„

๋ถ€๋ชจ ์Œ“์ž„ ๋งฅ๋ฝ์— ๊ฐ‡ํžŒ ์ž์‹ z-index

Stacking Context ๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ Œ๋”๋ง์„ ๊ณ„์‚ฐํ•˜๋Š” ๋…๋ฆฝ๋œ 3์ฐจ์› ๋ ˆ์ด์–ด ๊ทธ๋ฃน์ด๋‹ค. ๋ณด์ด๋Š” ๊ฑด 2d ์ด์ง€๋งŒ, ๊ณ„์‚ฐ์„ ์œ„ํ•ด์„œ 3d์˜ ๊ฐœ๋…์„ ์‚ฌ์šฉํ•œ ๊ฒƒ์ด๋‹ค.

ํ•ต์‹ฌ ๊ทœ์น™์€ ๊ฐ„๋‹จํ•˜๋‹ค. ์ž์‹ ์š”์†Œ์˜ z-index๋Š” ํ•ด๋‹น ๋ถ€๋ชจ ๋‚ด๋ถ€์—์„œ๋งŒ ์œ ํšจํ•˜๋‹ค. ์ž์‹ ์š”์†Œ์— 99999๋ฅผ ๋ถ€์—ฌํ•ด๋„, ๋ถ€๋ชจ์˜ Stacking Context ์ž์ฒด์˜ Z์ถ• ์ˆœ์„œ๊ฐ€ ๋‚ฎ์œผ๋ฉด ์™ธ๋ถ€์˜ ๋‹ค๋ฅธ ์š”์†Œ ์œ„๋กœ ์˜ฌ๋ผ๊ฐˆ ์ˆ˜ ์—†๋‹ค.

html
<header class="header">
  <div class="nav-item">
    <div class="dropdown" style="z-index: 9999;">๋“œ๋กญ๋‹ค์šด</div>
  </div>
</header>

<main class="content" style="position: relative; z-index: 2;">
  <h2>๋ฉ”์ธ ์ฝ˜ํ…์ธ </h2>
</main>
css
.header {
  position: relative;
  z-index: 1; /* ํ—ค๋” ์ „์ฒด๊ฐ€ z-index: 1 ์ธ ์Œ“์ž„ ๋งฅ๋ฝ ํ˜•์„ฑ */
}

ํ—ค๋”๊ฐ€ z-index: 1์ธ ์Œ“์ž„ ๋งฅ๋ฝ์„ ๊ฐ€์ง„ ์ˆœ๊ฐ„, ๋‚ด๋ถ€์˜ .dropdown์ด ๊ฐ€์ง„ z-index: 9999๋Š” .header ๋‚ด๋ถ€์—์„œ์˜ ์ˆœ์„œ์ผ ๋ฟ์ด๋‹ค. z-index: 2๋ฅผ ๊ฐ€์ง„ .content ์˜์—ญ๋ณด๋‹ค ํ—ค๋” ์ „์ฒด๊ฐ€ ๋’ค์— ์žˆ์–ด์„œ ๋“œ๋กญ๋‹ค์šด์€ ๋’ค๋กœ ๊ฐ€๋ ค์ง€๊ฒŒ ๋œ๋‹ค.

๋งฅ๋ฝ์ด ์ƒˆ๋กœ ์ƒ๊ธฐ๋Š” ์กฐ๊ฑด

position ๋ฟ ์•„๋‹ˆ๋ผ ํŠน์ • CSS ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด ์ƒˆ๋กœ์šด Stacking Context ์ด ํ˜•์„ฑ๋œ๋‹ค.

  • ๋ฃจํŠธ ์š”์†Œ: <html>
  • ํฌ์ง€์…˜ ๊ด€๋ จ: position์ด absolute ๋˜๋Š” relative์ด๋ฉด์„œ z-index๊ฐ€ auto๊ฐ€ ์•„๋‹Œ ์ˆซ์ž์ผ ๋•Œ, position: fixed ๋˜๋Š” sticky์ธ ์š”์†Œ
  • Flex/Grid ์ž์‹: z-index๊ฐ€ auto๊ฐ€ ์•„๋‹Œ ์ˆซ์ž์ผ ๋•Œ

๊ทธ๋ž˜ํ”ฝ ๋ ˆ์ด์–ด

๊ทธ๋ž˜ํ”ฝ ๋ ˆ์ด์–ด์—์„œ Compositing ํ•ด์•ผํ•˜๋Š” css ์†์„ฑ๋“ค์€ ๋Œ€๋ถ€๋ถ„ ์ƒˆ๋กœ์šด Stacking Context ๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค. ๋งŒ์•ฝ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ ˆ์ด์–ด๋ฅผ ๋ถ„๋ฆฌํ•˜์ง€ ์•Š๊ณ  ์ž์‹ ์š”์†Œ๋“ค์„ ํ™”๋ฉด์— ๋ฐ”๋กœ ๊ทธ๋ฆฌ๋ฉด์„œ ๊ฐ๊ฐ opacity๋ฅผ ์ ์šฉํ•˜๋ฉด ๋ Œ๋”๋ง ์™œ๊ณก์ด ๋ฐœ์ƒํ•œ๋‹ค.

  • ์˜๋„ํ•œ ๊ฒฐ๊ณผ: ๋ถ€๋ชจ์™€ ์ž์‹๋“ค์„ ํฌํ•จํ•œ ํ•˜๋‚˜์˜ ์™„์„ฑ๋œ ๋ฐ•์Šค ์ „์ฒด๊ฐ€ 50% ํˆฌ๋ช…ํ•จ.
  • ๋ ˆ์ด์–ด๋ฅผ ๋ถ„๋ฆฌํ•˜์ง€ ์•Š์•˜์„ ๋•Œ์˜ ๋ฌธ์ œ:
  • ๋ถ€๋ชจ ๋ฐฐ๊ฒฝ์ด 50% ํˆฌ๋ช…ํ•˜๊ฒŒ ๊ทธ๋ ค์ง.
  • ๊ทธ ์œ„์— ์ž์‹ 1์ด 50% ํˆฌ๋ช…ํ•˜๊ฒŒ ๊ทธ๋ ค์ง.
  • ์ž์‹ 1๊ณผ ๊ฒน์น˜๋Š” ์ž์‹ 2๊ฐ€ ๋˜ 50% ํˆฌ๋ช…ํ•˜๊ฒŒ ๊ทธ๋ ค์ง€๋ฉด์„œ ์ž์‹๋ผ๋ฆฌ ๊ฒน์น˜๋Š” ๋ถ€๋ถ„์ด ์ด์ค‘์œผ๋กœ ํˆฌ๋ช…ํ•ด์ง (25% ํˆฌ๊ณผ).

์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด ์š”์†Œ์™€ ์ž์‹๋“ค์„ ํ•˜๋‚˜์˜ ๋…๋ฆฝ๋œ Offscreen Buffer ์— ๋จผ์ € ์˜จ์ „ํ•˜๊ฒŒ ๋ Œ๋”๋งํ•œ ํ›„, ๊ทธ ๊ฒฐ๊ณผ๋ฌผ ์ „์ฒด์— ๋ถˆํˆฌ๋ช…๋„๋ฅผ ์ ์šฉํ•ด ๋ถ€๋ชจ ๋ ˆ์ด์–ด ์œ„์— ํ•ฉ์„ฑํ•ด์•ผ ํ•œ๋‹ค.

  • ๋ถˆํˆฌ๋ช…๋„: opacity๊ฐ€ 1๋ณด๋‹ค ์ž‘์€ ์š”์†Œ (opacity: 0.99๋งŒ ์ฃผ์–ด๋„ ๋ Œ๋”๋ง ๋ ˆ์ด์–ด๊ฐ€ ๋ถ„๋ฆฌ๋จ)
  • CSS ํŠธ๋žœ์Šคํผ/ํ•„ํ„ฐ: transform, filter, perspective, clip-path, mask ์†์„ฑ์ด none์ด ์•„๋‹ ๋•Œ
  • ๊ทธ๋ž˜ํ”ฝ ํ•ฉ์„ฑ: mix-blend-mode๊ฐ€ normal์ด ์•„๋‹ ๋•Œ
  • ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ฐ ๊ฒฉ๋ฆฌ: will-change ์†์„ฑ์— ์Œ“์ž„ ๋งฅ๋ฝ ์ƒ์„ฑ ์†์„ฑ์ด ๋“ค์–ด๊ฐ”์„ ๋•Œ, contain์ด layout์ด๋‚˜ paint๋ฅผ ํฌํ•จํ•  ๋•Œ
  • ๋ช…์‹œ์  ๊ฒฉ๋ฆฌ: isolation: isolate๊ฐ€ ์ง€์ •๋œ ์š”์†Œ

GPU ํ•˜๋“œ์›จ์–ด ๊ฐ€์†๊ณผ ๋ ˆ์ด์–ด ์Šน๊ฒฉ ์›๋ฆฌ๋„ ์ด์ฒ˜๋Ÿผ ๊ทธ๋ž˜ํ”ฝ ํ•ฉ์„ฑ์„ ์œ„ํ•œ ๋ ˆ์ด์–ด ๋ถ„๋ฆฌ์™€ ๊ด€๋ จ๋œ๋‹ค.

์‹ค์ˆ˜ํ•˜๊ธฐ ์‰ฌ์šด ํฌ์ธํŠธ

UI ์นด๋“œ์— ๋งˆ์šฐ์Šค ํ˜ธ๋ฒ„ ํšจ๊ณผ๋กœ transform: translateY(-4px)๋‚˜ opacity ํŠธ๋žœ์ง€์…˜์„ ๊ฑธ์–ด๋‘˜ ๋•Œ๊ฐ€ ์žˆ๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์‹œ์ž‘๋˜๋Š” ์ˆœ๊ฐ„ ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ถˆํˆฌ๋ช…๋„ ๊ณ„์‚ฐ๊ณผ 3D ํ•ฉ์„ฑ์„ ์œ„ํ•ด ์ƒˆ๋กœ์šด Stacking Context ์„ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ์นด๋“œ ๋‚ด๋ถ€์— ๋„์›Œ๋‘์—ˆ๋˜ ํˆดํŒ์ด๋‚˜ ์„œ๋ธŒ ๋ฉ”๋‰ด๊ฐ€ ์˜† ์นด๋“œ ๋ ˆ์ด์–ด์— ๊ฐ€๋ ค์ง€๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•œ๋‹ค.

css
.card {
  isolation: isolate; /* ์™ธ๋ถ€์™€ ๋ถ„๋ฆฌ๋œ ๋…๋ฆฝ ์Œ“์ž„ ๋งฅ๋ฝ ๋ช…์‹œ */
}

๋ถ€๋ชจ ์š”์†Œ์˜ z-index๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ๋‚ด๋ถ€ ์š”์†Œ๋“ค์ด ์™ธ๋ถ€ Z์ถ•์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋„๋ก ํ•˜๋ ค๋ฉด isolation: isolate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

์Šคํƒ€์ผ ๊ตฌ์กฐ์ƒ ๋ถ€๋ชจ์˜ ์Œ“์ž„ ๋งฅ๋ฝ์„ ์ œ์–ดํ•  ์ˆ˜ ์—†๋‹ค๋ฉด, ๋Œ€์ƒ UI์˜ DOM ์œ„์น˜๋ฅผ ์™„์ „ ์™ธ๋ถ€๋กœ ์˜ฎ๊ฒจ ๋ Œ๋”๋งํ•˜๋ฉด ๋œ๋‹ค.

  • React์˜ createPortal์ด๋‚˜ Vue์˜ <Teleport> ์‚ฌ์šฉ
  • HTML ํ‘œ์ค€์œผ๋กœ๋Š” <dialog> ํƒœ๊ทธ ๋˜๋Š” Popover API ํ™œ์šฉ

์ข‹์•„์š”์™€ ๋Œ“๊ธ€

๋Œ“๊ธ€ ๋‚จ๊ธฐ๊ธฐ

๋Œ“๊ธ€ 0๊ฐœ

๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.