[CSS-10] Grid & Position Sticky

Comely·2025년 6월 6일

CSS

목록 보기
10/12

🏗️ CSS Grid 기초

브라우저 호환성

브라우저지원 버전
Chrome57+ ✅
Firefox52+ ✅
Safari10.1+ ✅
Edge16+ ✅
IE❌ (구 문법으로만 부분 지원)

참고: caniuse.com에서 정확한 호환성 정보 확인 가능

Grid 기본 개념

CSS Grid는 격자(Grid) 기반 레이아웃 시스템으로, 복잡한 2차원 레이아웃을 쉽게 구현할 수 있습니다.

📐 기본 Grid 레이아웃

간단한 Grid 생성

<div class="grid-container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
</div>
.grid-container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;  /* 4개 컬럼 */
  grid-template-rows: 100px 100px 100px;   /* 3개 행 */
  grid-gap: 10px;                         /* 간격 */
  padding: 20px;
}

.item {
  background: #007bff;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
}

Grid 단위 설명

.grid-example {
  display: grid;
  
  /* 다양한 단위 사용 */
  grid-template-columns: 
    200px          /* 고정 크기 */
    1fr            /* 비율 (1배) */
    2fr            /* 비율 (2배) */
    minmax(100px, 300px)  /* 최소/최대 크기 */
    auto;          /* 내용에 맞춤 */
  
  /* repeat 함수 사용 */
  grid-template-columns: repeat(4, 1fr);      /* 1fr을 4번 반복 */
  grid-template-columns: repeat(3, 200px);    /* 200px을 3번 반복 */
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); /* 반응형 */
}

🎯 Grid 레이아웃 방법 1: 아이템 크기 조정

그리드 라인 기반 배치

<div class="layout-container">
  <div class="header">헤더</div>
  <div class="sidebar">사이드바</div>
  <div class="main">메인 컨텐츠</div>
  <div class="footer">푸터</div>
</div>
.layout-container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: 80px 400px 80px;
  grid-gap: 10px;
  min-height: 100vh;
}

.header {
  grid-column: 1 / 5;  /* 컬럼 1번 라인부터 5번 라인까지 */
  background: #333;
  color: white;
}

.sidebar {
  grid-column: 1 / 2;  /* 컬럼 1번 라인부터 2번 라인까지 */
  grid-row: 2 / 3;     /* 행 2번 라인부터 3번 라인까지 */
  background: #f8f9fa;
}

.main {
  grid-column: 2 / 5;  /* 컬럼 2번 라인부터 5번 라인까지 */
  grid-row: 2 / 3;
  background: white;
  padding: 20px;
}

.footer {
  grid-column: 1 / 5;
  background: #6c757d;
  color: white;
}

그리드 라인 번호 이해

  1   2   3   4   5  ← 세로 라인 번호
1 ┌───┬───┬───┬───┐
  │   │   │   │   │
2 ├───┼───┼───┼───┤
  │   │   │   │   │
3 ├───┼───┼───┼───┤
  │   │   │   │   │
4 └───┴───┴───┴───┘
  ↑ 가로 라인 번호

단축 속성

.item {
  /* 기본 방식 */
  grid-column-start: 1;
  grid-column-end: 3;
  grid-row-start: 1;
  grid-row-end: 2;
  
  /* 단축 속성 */
  grid-column: 1 / 3;
  grid-row: 1 / 2;
  
  /* span 키워드 사용 */
  grid-column: span 2;  /* 2칸 차지 */
  grid-row: span 1;     /* 1칸 차지 */
}

🏷️ Grid 레이아웃 방법 2: 영역 이름 지정

Grid Areas 사용

<div class="page-layout">
  <header class="page-header">헤더</header>
  <aside class="page-sidebar">사이드바</aside>
  <main class="page-main">메인 컨텐츠</main>
  <footer class="page-footer">푸터</footer>
</div>
.page-layout {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: 80px 1fr 60px;
  grid-template-areas: 
    "header  header  header"
    "sidebar main    aside"
    "footer  footer  footer";
  min-height: 100vh;
  grid-gap: 10px;
}

.page-header {
  grid-area: header;
  background: #007bff;
  color: white;
}

.page-sidebar {
  grid-area: sidebar;
  background: #f8f9fa;
}

.page-main {
  grid-area: main;
  background: white;
  padding: 20px;
}

.page-footer {
  grid-area: footer;
  background: #6c757d;
  color: white;
}

빈 공간 표시

.grid-with-gaps {
  grid-template-areas: 
    "header header header header"
    "sidebar main main ."        /* . = 빈 공간 */
    "sidebar main main ."
    "footer footer footer footer";
}

📱 반응형 Grid 레이아웃

기본 반응형 패턴

.responsive-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  grid-gap: 20px;
  padding: 20px;
}

/* 수동 반응형 */
@media (max-width: 768px) {
  .page-layout {
    grid-template-columns: 1fr;
    grid-template-areas: 
      "header"
      "main"
      "sidebar"
      "footer";
  }
}

@media (min-width: 769px) and (max-width: 1024px) {
  .page-layout {
    grid-template-columns: 150px 1fr;
    grid-template-areas: 
      "header header"
      "sidebar main"
      "footer footer";
  }
}

🖼️ 실전 예제: 이미지 갤러리

HTML 구조

<div class="gallery-grid">
  <div class="gallery-item featured">
    <img src="https://picsum.photos/600/400?random=1" alt="Featured Image">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/300/300?random=2" alt="Image 2">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/300/300?random=3" alt="Image 3">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/300/300?random=4" alt="Image 4">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/300/300?random=5" alt="Image 5">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/300/300?random=6" alt="Image 6">
  </div>
</div>

CSS 스타일

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 200px;
  grid-gap: 15px;
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.gallery-item {
  background: #f8f9fa;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s ease;
}

.gallery-item:hover {
  transform: translateY(-5px);
}

.gallery-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* 첫 번째 이미지를 2x2로 확대 */
.gallery-item.featured {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}

/* 반응형 */
@media (max-width: 768px) {
  .gallery-grid {
    grid-template-columns: 1fr;
    grid-auto-rows: 250px;
  }
  
  .gallery-item.featured {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
  }
}

@media (min-width: 769px) and (max-width: 1024px) {
  .gallery-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  
  .gallery-item.featured {
    grid-column: 1 / 3;
    grid-row: 1 / 2;
  }
}

🏢 실전 예제: 블로그 레이아웃

<div class="blog-layout">
  <header class="blog-header">
    <h1>My Blog</h1>
    <nav>Navigation Menu</nav>
  </header>
  <aside class="blog-sidebar">
    <h3>Sidebar</h3>
    <ul>
      <li>Recent Posts</li>
      <li>Categories</li>
      <li>Archives</li>
    </ul>
  </aside>
  <main class="blog-content">
    <article>
      <h2>Blog Post Title</h2>
      <p>Blog post content goes here...</p>
    </article>
  </main>
  <footer class="blog-footer">
    <p>&copy; 2024 My Blog. All rights reserved.</p>
  </footer>
</div>
.blog-layout {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas: 
    "header header"
    "sidebar content"
    "footer footer";
  min-height: 100vh;
  grid-gap: 20px;
  padding: 20px;
}

.blog-header {
  grid-area: header;
  background: #343a40;
  color: white;
  padding: 20px;
  border-radius: 8px;
}

.blog-sidebar {
  grid-area: sidebar;
  background: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
}

.blog-content {
  grid-area: content;
  background: white;
  padding: 30px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.blog-footer {
  grid-area: footer;
  background: #6c757d;
  color: white;
  padding: 15px;
  border-radius: 8px;
  text-align: center;
}

/* 태블릿 */
@media (max-width: 1024px) {
  .blog-layout {
    grid-template-columns: 200px 1fr;
  }
}

/* 모바일 */
@media (max-width: 768px) {
  .blog-layout {
    grid-template-columns: 1fr;
    grid-template-areas: 
      "header"
      "content"
      "sidebar"
      "footer";
  }
}

📌 Position Sticky

기본 개념

position: sticky스크롤 기반 고정 요소를 만드는 속성입니다.

브라우저 지원

브라우저지원 버전
Chrome56+ ✅
Firefox32+ ✅
Safari6.1+ ✅
Edge16+ ✅
IE

기본 사용법

<body style="height: 3000px; background: #f0f0f0;">
  <div class="content-section">
    <div class="sticky-sidebar">
      <img src="product.jpg" alt="Product Image">
    </div>
    <div class="main-text">
      <h2>Product Description</h2>
      <p>Long product description that requires scrolling...</p>
      <!-- 많은 텍스트 내용 -->
    </div>
  </div>
</body>
.content-section {
  background: white;
  max-width: 1200px;
  margin: 100px auto;
  padding: 40px;
  display: flex;
  gap: 40px;
  min-height: 2000px; /* 스크롤 가능하도록 */
}

.sticky-sidebar {
  flex: 0 0 400px;
  position: sticky;
  top: 20px;          /* viewport 상단에서 20px 위치에 고정 */
  height: fit-content;
}

.sticky-sidebar img {
  width: 100%;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.main-text {
  flex: 1;
  line-height: 1.8;
}

Sticky 동작 원리

  1. 일반 상태: 요소가 원래 위치에 있음
  2. 스크롤 시: 지정된 위치(top, bottom 등)에 도달하면 고정
  3. 부모 범위: 부모 요소를 벗어나면 다시 일반 요소로 동작

고급 Sticky 예제

네비게이션 메뉴

<header class="main-header">
  <h1>Website Header</h1>
</header>

<nav class="sticky-nav">
  <ul>
    <li><a href="#home">Home</a></li>
    <li><a href="#about">About</a></li>
    <li><a href="#services">Services</a></li>
    <li><a href="#contact">Contact</a></li>
  </ul>
</nav>

<main class="main-content">
  <!-- 페이지 내용 -->
</main>
.main-header {
  background: #343a40;
  color: white;
  padding: 60px 20px;
  text-align: center;
}

.sticky-nav {
  position: sticky;
  top: 0;
  background: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  z-index: 100;
}

.sticky-nav ul {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 1200px;
  margin: 0 auto;
}

.sticky-nav li {
  flex: 1;
}

.sticky-nav a {
  display: block;
  padding: 20px;
  text-decoration: none;
  color: #333;
  text-align: center;
  transition: background 0.3s ease;
}

.sticky-nav a:hover {
  background: #f8f9fa;
}

.main-content {
  max-width: 1200px;
  margin: 0 auto;
  padding: 40px 20px;
  min-height: 2000px;
}

사이드바 목차

<div class="article-layout">
  <aside class="table-of-contents">
    <h3>목차</h3>
    <ul>
      <li><a href="#section1">섹션 1</a></li>
      <li><a href="#section2">섹션 2</a></li>
      <li><a href="#section3">섹션 3</a></li>
      <li><a href="#section4">섹션 4</a></li>
    </ul>
  </aside>
  
  <article class="article-content">
    <section id="section1">
      <h2>섹션 1</h2>
      <p>섹션 1의 내용...</p>
    </section>
    <!-- 더 많은 섹션들 -->
  </article>
</div>
.article-layout {
  display: grid;
  grid-template-columns: 250px 1fr;
  gap: 40px;
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.table-of-contents {
  position: sticky;
  top: 20px;
  height: fit-content;
  background: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #e9ecef;
}

.table-of-contents h3 {
  margin-top: 0;
  color: #495057;
}

.table-of-contents ul {
  list-style: none;
  padding: 0;
}

.table-of-contents li {
  margin-bottom: 8px;
}

.table-of-contents a {
  color: #007bff;
  text-decoration: none;
  padding: 8px 0;
  display: block;
  border-radius: 4px;
  padding-left: 12px;
  transition: all 0.3s ease;
}

.table-of-contents a:hover {
  background: #e9ecef;
  color: #0056b3;
}

.article-content {
  line-height: 1.8;
}

.article-content section {
  margin-bottom: 60px;
  min-height: 600px;
}

@media (max-width: 768px) {
  .article-layout {
    grid-template-columns: 1fr;
  }
  
  .table-of-contents {
    position: static;
    order: -1;
  }
}

🎯 Grid vs Flexbox 비교

언제 Grid를 사용할까?

/* Grid가 적합한 경우 */
.grid-suitable {
  /* 2차원 레이아웃 (행과 열 모두 제어) */
  display: grid;
  grid-template-areas: 
    "header header header"
    "sidebar main aside"
    "footer footer footer";
}

/* Flexbox가 적합한 경우 */
.flex-suitable {
  /* 1차원 레이아웃 (한 방향만 제어) */
  display: flex;
  justify-content: space-between;
  align-items: center;
}

특징 비교

특징GridFlexbox
차원2차원 (행 + 열)1차원 (행 또는 열)
용도전체 레이아웃컴포넌트 내부 정렬
복잡성복잡한 레이아웃간단한 정렬
반응형강력한 반응형 기능유연한 크기 조절

💡 실무 팁

Grid 네이밍 규칙

.layout-grid {
  grid-template-areas: 
    "header header header"
    "nav main aside"
    "footer footer footer";
}

/* 명확한 이름 사용 */
.page-header { grid-area: header; }
.page-nav { grid-area: nav; }
.page-main { grid-area: main; }
.page-aside { grid-area: aside; }
.page-footer { grid-area: footer; }

성능 최적화

/* 불필요한 재계산 방지 */
.grid-container {
  contain: layout style;
}

/* 이미지 최적화 */
.grid-item img {
  object-fit: cover;
  width: 100%;
  height: 100%;
}

접근성 고려

/* 스크린 리더를 위한 순서 유지 */
.accessible-grid {
  display: grid;
  grid-template-areas: 
    "header"
    "main"
    "sidebar"
    "footer";
}

@media (min-width: 768px) {
  .accessible-grid {
    grid-template-areas: 
      "header header"
      "sidebar main"
      "footer footer";
  }
}

CSS Grid와 Position Sticky을 활용하여 반응형인 웹 레이아웃을 구현할 수 있습니다

profile
App, Web Developer

0개의 댓글