Afaik

반응형 디자인과 미디어 쿼리

중요도: ⭐⭐⭐⭐

현대 웹 개발의 필수 요소로, 모든 디바이스에서 최적의 사용자 경험을 제공하는 핵심 기술입니다.

개념

반응형 디자인에서 브레이크포인트는 다양한 화면 크기에 맞춰 레이아웃이 변경되는 지점을 의미합니다.

반응형 디자인(Responsive Design)은 다양한 디바이스와 화면 크기에서 최적의 사용자 경험을 제공하기 위해 레이아웃과 콘텐츠를 유연하게 조정하는 웹 디자인 접근 방식입니다.

일반적인 브레이크포인트

/* Mobile First 접근법 */
/* Extra small devices (phones, 576px and down) */
@media (max-width: 575.98px) {
  /* 모바일 스타일 */
}

/* Small devices (landscape phones, 576px and up) */
@media (min-width: 576px) {
  /* 큰 모바일/작은 태블릿 스타일 */
}

/* Medium devices (tablets, 768px and up) */
@media (min-width: 768px) {
  /* 태블릿 스타일 */
}

/* Large devices (desktops, 992px and up) */
@media (min-width: 992px) {
  /* 데스크톱 스타일 */
}

/* Extra large devices (large desktops, 1200px and up) */
@media (min-width: 1200px) {
  /* 큰 데스크톱 스타일 */
}

/* Extra extra large devices (1400px and up) */
@media (min-width: 1400px) {
  /* 매우 큰 화면 스타일 */
}

Custom Properties를 활용한 브레이크포인트

:root {
  --breakpoint-xs: 0;
  --breakpoint-sm: 576px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 992px;
  --breakpoint-xl: 1200px;
  --breakpoint-xxl: 1400px;
}

/* SCSS/SASS에서 사용 */
$breakpoints: (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px);

@mixin respond-to($breakpoint) {
  @if map-has-key($breakpoints, $breakpoint) {
    @media (min-width: map-get($breakpoints, $breakpoint)) {
      @content;
    }
  }
}

// 사용법
.container {
  width: 100%;
  padding: 1rem;

  @include respond-to(md) {
    padding: 2rem;
    max-width: 768px;
  }

  @include respond-to(lg) {
    max-width: 1024px;
  }
}

컨테이너 쿼리 (현대적 접근법)

/* 컨테이너 쿼리를 사용한 내재적 반응형 디자인 */
.card-container {
  container-type: inline-size;
}

@container (min-width: 300px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}

@container (max-width: 299px) {
  .card {
    display: block;
  }
}

모바일 퍼스트 vs 데스크톱 퍼스트

모바일 퍼스트 (권장)

/* 기본: 모바일 스타일 */
.navigation {
  display: block;
}

.nav-item {
  display: block;
  width: 100%;
  padding: 1rem;
}

/* 태블릿 이상 */
@media (min-width: 768px) {
  .navigation {
    display: flex;
  }

  .nav-item {
    width: auto;
    padding: 0.5rem 1rem;
  }
}

데스크톱 퍼스트

/* 기본: 데스크톱 스타일 */
.navigation {
  display: flex;
}

.nav-item {
  width: auto;
  padding: 0.5rem 1rem;
}

/* 태블릿 이하 */
@media (max-width: 767.98px) {
  .navigation {
    display: block;
  }

  .nav-item {
    display: block;
    width: 100%;
    padding: 1rem;
  }
}

고급 미디어 쿼리 기능

/* 방향 기반 */
@media (orientation: landscape) {
  .hero {
    height: 60vh;
  }
}

@media (orientation: portrait) {
  .hero {
    height: 80vh;
  }
}

/* 해상도 기반 */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
  .logo {
    background-image: url("logo@2x.png");
    background-size: contain;
  }
}

/* 선호 색상 스키마 */
@media (prefers-color-scheme: dark) {
  body {
    background-color: #121212;
    color: #ffffff;
  }
}

/* 동작 감소 선호 */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* 호버 지원 여부 */
@media (hover: hover) {
  .button:hover {
    background-color: #0056b3;
  }
}

@media (hover: none) {
  .button {
    /* 터치 디바이스용 스타일 */
  }
}

실제 반응형 레이아웃 예시

그리드 시스템

.container {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1rem;
}

.row {
  display: flex;
  flex-wrap: wrap;
  margin: 0 -0.5rem;
}

.col {
  flex: 1;
  padding: 0 0.5rem;
  margin-bottom: 1rem;
}

/* 모바일: 1열 */
@media (max-width: 767.98px) {
  .col {
    flex: 0 0 100%;
  }
}

/* 태블릿: 2열 */
@media (min-width: 768px) and (max-width: 991.98px) {
  .col-md-6 {
    flex: 0 0 50%;
  }
}

/* 데스크톱: 3열 */
@media (min-width: 992px) {
  .col-lg-4 {
    flex: 0 0 33.333333%;
  }
}

플렉스박스 활용

.card-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.card {
  flex: 1 1 300px; /* 최소 300px, 자동 확장 */
  background: white;
  border-radius: 8px;
  padding: 1rem;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

@media (max-width: 767.98px) {
  .card {
    flex: 1 1 100%; /* 모바일에서는 전체 너비 */
  }
}

CSS Grid 활용

.grid-container {
  display: grid;
  gap: 1rem;
  padding: 1rem;
}

/* 모바일: 1열 */
@media (max-width: 767.98px) {
  .grid-container {
    grid-template-columns: 1fr;
  }
}

/* 태블릿: 2열 */
@media (min-width: 768px) and (max-width: 991.98px) {
  .grid-container {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* 데스크톱: 3열 */
@media (min-width: 992px) {
  .grid-container {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* 자동 크기 조정 */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1rem;
}

이미지 반응형 처리

<!-- 반응형 이미지 -->
<picture>
  <source media="(max-width: 768px)" srcset="mobile.jpg" />
  <source media="(max-width: 1024px)" srcset="tablet.jpg" />
  <img src="desktop.jpg" alt="Responsive image" />
</picture>

<!-- srcset을 활용한 해상도 대응 -->
<img
  src="image.jpg"
  srcset="image.jpg 1x, image@2x.jpg 2x"
  alt="High resolution image"
/>
/* CSS로 반응형 이미지 */
.responsive-image {
  width: 100%;
  height: auto;
  max-width: 100%;
}

/* 배경 이미지 반응형 */
.hero-section {
  background-image: url("hero-mobile.jpg");
  background-size: cover;
  background-position: center;
  min-height: 50vh;
}

@media (min-width: 768px) {
  .hero-section {
    background-image: url("hero-tablet.jpg");
    min-height: 60vh;
  }
}

@media (min-width: 1200px) {
  .hero-section {
    background-image: url("hero-desktop.jpg");
    min-height: 80vh;
  }
}

타이포그래피 반응형

/* 유동적 타이포그래피 */
.title {
  font-size: clamp(1.5rem, 4vw, 3rem);
  line-height: 1.2;
}

/* 브레이크포인트별 타이포그래피 */
.heading {
  font-size: 1.5rem;
}

@media (min-width: 768px) {
  .heading {
    font-size: 2rem;
  }
}

@media (min-width: 1200px) {
  .heading {
    font-size: 2.5rem;
  }
}

/* 뷰포트 단위 활용 */
.responsive-text {
  font-size: 4vw;
  max-font-size: 2rem;
  min-font-size: 1rem;
}

네비게이션 반응형

.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
}

.nav-menu {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-item {
  margin: 0 1rem;
}

.hamburger {
  display: none;
  flex-direction: column;
  cursor: pointer;
}

.hamburger span {
  width: 25px;
  height: 3px;
  background: #333;
  margin: 3px 0;
  transition: 0.3s;
}

/* 모바일 메뉴 */
@media (max-width: 767.98px) {
  .hamburger {
    display: flex;
  }

  .nav-menu {
    position: fixed;
    top: 70px;
    left: -100%;
    width: 100%;
    height: calc(100vh - 70px);
    flex-direction: column;
    background-color: white;
    text-align: center;
    transition: 0.3s;
    box-shadow: 0 10px 27px rgba(0, 0, 0, 0.05);
  }

  .nav-menu.active {
    left: 0;
  }

  .nav-item {
    margin: 1rem 0;
  }
}

성능 최적화

/* 효율적인 미디어 쿼리 작성 */
/* ❌ 비효율적 */
@media (max-width: 1199px) {
  /* 복잡한 스타일 */
}
@media (max-width: 991px) {
  /* 복잡한 스타일 */
}
@media (max-width: 767px) {
  /* 복잡한 스타일 */
}

/* ✅ 효율적 */
.component {
  /* 기본 모바일 스타일 */
}

@media (min-width: 768px) {
  .component {
    /* 태블릿+ 스타일 */
  }
}

@media (min-width: 1200px) {
  .component {
    /* 데스크톱 스타일 */
  }
}

/* CSS 변수를 활용한 반응형 */
:root {
  --container-padding: 1rem;
  --grid-columns: 1;
}

@media (min-width: 768px) {
  :root {
    --container-padding: 2rem;
    --grid-columns: 2;
  }
}

@media (min-width: 1200px) {
  :root {
    --container-padding: 3rem;
    --grid-columns: 3;
  }
}

.container {
  padding: var(--container-padding);
}

.grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), 1fr);
}

반응형 디자인 모범 사례

브레이크포인트 선택 기준:

  1. 컨텐츠 기반: 디자인이 깨지는 지점에서 브레이크포인트 설정
  2. 주요 디바이스 고려: 실제 사용자가 많이 사용하는 디바이스 크기 우선
  3. 테스트 우선: 다양한 디바이스에서 실제 테스트
  4. 유연성 유지: 고정된 크기보다는 유연한 레이아웃 추구

성능 고려사항:

  • 모바일 퍼스트 접근 방식 사용
  • 불필요한 미디어 쿼리 중복 피하기
  • CSS 변수 활용으로 코드 중복 줄이기
  • 컨테이너 쿼리 활용으로 더 정확한 반응형 구현

반응형 디자인은 단순히 브레이크포인트를 설정하는 것을 넘어, 사용자의 컨텍스트와 디바이스 특성을 이해하고 최적의 경험을 제공하는 것이 목표입니다.

면접 팁

반응형 디자인에 대해 질문받을 때는 단순히 미디어 쿼리 문법을 설명하는 것을 넘어서, 모바일 퍼스트 접근법, 유연한 그리드 시스템, 컨테이너 쿼리 등의 현대적 기법들을 구체적인 코드 예시와 함께 설명할 수 있어야 합니다. 또한 성능 최적화와 접근성 고려사항에 대해서도 언급하면 더욱 좋은 인상을 줄 수 있습니다.

Edit on GitHub

Last updated on