모바일 우선 반응형 웹 디자인

Aaron·2020년 7월 2일
23

반응형 웹

목록 보기
2/3
post-thumbnail

📢 모바일 우선 반응형 웹 디자인을 위해 알아야 할 개념 총 정리.

틀린 점이 있다면 배울 수 있도록 알려주시면 감사하겠습니다!

📐설계


화면 구성

header, nav, main, footer 등 모바일 화면을 구성하는 각 요소들의 배치를 고려한다.
반응형 웹 화면 구조
출처: 유튜브-드림코딩 by 엘리

브레이크 포인트

를 정한다.
브레이크포인트
❗ 위는 범용 예시이며, 절대적인 기준이 아니다.
지원하고자 하는 기기와 실제 동작을 참고하여 조정해야 한다.
출처: 유튜브-드림코딩 by 엘리

🎥 미디어 쿼리


정의

사용자 미디어의 종류와 상태에 대해 질의를 던져 기기의 종류나 화면 크기에 맞춰 개별적인 css 코드가 적용될 수 있도록 하는 CSS3의 기능.

문법

@media only|not 매체유형 and (표현식) { CSS스타일코드; }

예시

media query
출처: Nextree

브라우저 호환성

IE9부터 지원. 브라우저 호환성 굿.
media query
출처: Can I use

👀 뷰포트


정의

화면 위 가상의 표시 영역
뷰포트
출처: 번데기 개발자님 티스토리 블로그

  • 뷰포트 시초는 애플인데, 아이폰 3GS 시절 320 x 480개의 픽셀로 데스크톱 기준으로 개발된 웹 사이트를 렌더링하려니 한 화면에 담기지 못하는 문제점이 있었음.

  • 그래서 애플은 가상의 뷰포트 개념을 도입해 320px인 디스플레이를 980px의 뷰포트에 렌더링 해버림.

  • 이후 별다른 뷰포트 설정이 없으면 대부분의 모바일 브라우저의 뷰포트는 980px이 기본값이 됨.

  • 뷰포트를 제대로 알려면 픽셀을 알아야됨.

  • 픽셀은 기기의 물리적 크기랑 좀 다른 얘기임. 개수로 세는거임.

  • 예시로 아래 기기들의 해상도를 보자.
    1. LG그램 15인치: 1920x1080 (가로 1920개, 세로 1080개 픽셀)
    2. 갤럭시 S10: 1440x3040 (가로 1440개, 세로 3040개 픽셀)

  • 갤럭시는 그램에 비해 물리적인 크기가 작을 뿐, 픽셀 개수로는 전혀 안꿀림.

  • 물리적인 화면 크기는 훨씬 작은데, 데스크톱과 비슷한 개수의 픽셀로 렌더링해 버리면 작아보일 수 밖에.
    실제로는 뷰포트 설정 없으면 980px로 렌더링 되겠지만, 이것도 스마트폰의 작고 귀여운 화면에서는 큰 편

  • 한 마디로 작은 스마트폰 화면에 웹 페이지를 모두 표시하려고 하니 전체 페이지의 배율이 작게 축소되어 보이는 것.

    🤷‍♂️ 그래서 어떻게 해결하는데?

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        // mobile first markup
    </body>
</html>
  • width: 뷰포트의 가로 크기를 결정.
    device-width로 설정하면 최적화된 UI를 적용하기 위해 기기마다 독립적으로 설정된 픽셀값을 뷰포트의 width로 설정하고, 그것에 웹 페이지를 맞춤.
    갤럭시 S10: 360 x 740

  • initial-scale: 페이지가 처음 로드될 때 줌 레벨을 조정.
    기기 방향에 상관없이 브라우저가 각 기기에 독립적으로 설정된 픽셀과 CSS 픽셀 간에 1:1 관계를 설정하고, 페이지에서 전체 가로 너비(width)를 활용할 수 있게 함

  • 그 외
    - minimum-scale : viewport의 최소 배율값, 기본값은 0.25.
    - maximum-scale : viewport의 최대 배율값, 기본값은 1.6.
    - user-scalable : 사용자의 확대/축소 기능을 설정, 기본값은 yes.

📱 모바일 우선


  1. 우선 모바일을 기준으로 마크업을 한다.

  2. 이후 작성된 코드 아래에 미디어 쿼리를 사용해 각 브레이크 포인트별로 코드를 더한다.

.block__element--modifier {
	// Mobile
    @media screen and (min-width: 768px) and (max-width: 1023px) {
    	// Tablet
    }
    @media screen and (min-width: 1024px) {
    	// PC
    }
}

🔀 css 단위


px

◻ 소개

  • css의 가장 기본 단위이다.
  • 1px는 화소 1개의 크기를 의미한다.
  • 절대값이다.

◻ 사용법

사용자의 브라우저 설정에도 불구하고 늘 절대적인 값을 유지했으면 하는 경우
ex) border

◻ 주의사항

웹 접근성(Accessibility)을 위해 위와 같이 꼭 필요할 때만 사용하는 것이 좋다

%

◻ 소개

  • 언제나 다른 수치를 참조한다.
  • 참조값은 같은 요소에 적용된 다른 속성이거나, 조상 요소의 같은 속성이거나, 컨테이닝 블록오프셋 속성(left, right, top, bottom)이나 박스모델 속성(width, height, margin, padding)과 같은 서식 상황(formatting context)에서의 측정값이거나, 혹은 다른 어떤 것이다.
    참조: w3.org

◻ 사용법

  • 레이아웃 지정에 유용하다.
    블로그 예시
    // html
    <article class="blog__post">
        <div class="blog__post--image">
        </div>
        <div class="blog__post--content">
        </div>
    </article>
    
    // css
    .blog__post {
    	width: 100%;
    }
    
    .blog__post .blog__post--image {
    	width: 50%;
    }
    
    .blog__post .blog__post--content {
    	width: 50%;
    }
  • flexbox와 함께 사용하면 유용하다.
    // html
    <div class="container">
        <div class="item">1</div>
        <div class="item">22</div>
        <div class="item">three</div>
    </div>
    
    //css
    .container {
        display: flex;
        justify-content: center;
        background-color: red;
    }
    
    .container .item {
        width: 20%;
        border: 1px solid black;
        background-color: skyblue;
    }
    
    // sandbox에서 위 코드를 실험한 결과
    container에 display: flex와 justify-content: center를 선언하면
    디폴트로 item들의 width는 내부 요소의 width에 맞춰져 주축 row를 기준으로 중앙 정렬 되고,
    창의 폭을 줄이기 시작하면 여백이 먼저 감소하다가
    여백이 모두 줄어들었을 때에도 item들의 width는 내부 요소의 width 아래로 떨어지지 않는다.
    
    이 때 item에 width: 20%를 선언하면
    item들이 container의 width를 20%씩 총 60%를 갖게 되고
    나머지 40%는 양 쪽에 20%씩 나눠져서
    그 비율이 내부 요소의 크기와 관계없이 유지된다.
    즉 위와 같이 창의 폭을 줄일 때, 
    item의 width가 내부 요소의 width보다 줄어들더라도 해당 비율을 고수한다.
    이는 height도 마찬가지다.
  • 마진, 패딩에 사용하면 유용하다.

◻ 주의사항

  • % 단위가 무조건 부모 요소를 참조한다고 생각하기 쉬운데, 이는 사실이 아니다.
    특히 박스 모델 속성(width, height, padding, margin)과 오프셋 속성(left, right, top, bottom)에 %를 적용할 때 -> % 수치는 단순히 '부모 요소'가 아닌 '컨테이닝 블록'을 기준으로 계산된다.

  • 대부분의 경우 어떤 요소의 컨테이닝 블록은 가장 가까운 블록 레벨 조상의 콘텐츠 영역이나, 항상 그런 것은 아니다.

  • height, top, bottom은 컨테이닝 블록의 height를,
    width, padding, margin, left, right은 width를 사용해 백분율을 계산한다.
    참고: 컨테이닝 블록의 모든 것-MDN

  • 특정 요소의 height에 100%를 선언할 때 margin을 고려하지 못하면 컨테이닝 블록 밖으로 탈출하는 현상이 생긴다.
    트랙 이탈 사진

    // html
    <div class="slider__track">
        <div class="slider__track--slide">
        </div>
    </div>
    
    // css
    .slider__track {
        display: flex;
        justify-content: center;
        height: 150px;
        background-color: red;
    }
    
    .slider__track--slide {
        width: 20%;
        height: 100%;
        margin: 20px 0;
        background-color: skyblue;
    }

    ✔ 해결 방법

    1. slide 요소의 마진을 제거한다.
    2. slide 요소의 height에서 마진값을 뺀다.
      .slider__track--slide {
          height: calc(100% - (20px + 20px));
      }
  • 컨테이닝 블록의 중앙에 내부 요소를 배치하기 위해 position과 top, left를 %와 함께 사용할 때, 아래와 같이 내부 요소의 중심이 아닌 모서리가 중앙에 맞춰진다.
    중앙 정렬 나쁜 예시

    // html
    <header class="header">
        <div class="header__logo">
        </div>
    </header>
    
    // css
    .header {
        position: relative;
    }
    
    .header .header__logo {
        position: absolute;
        top: 50%;
        left: 50%;
    }

    ✔ 해결 방법

    .header .header__logo {
        position: absolute;
        top: 50%;
        left: 50%;
        // 아래 코드를 추가한다
        transform: translate(-50%, -50%);
    }

rem, em

◻ 소개

  • rem: root element에 지정된 font 크기.
  • em: 현재 element의 font 크기.

◻ 사용법

.wrapper1 {
	font-size: 2rem;
}
.wrapper1 .heading {
	font-size: 3em;
}
.wrapper1 .content {
   	font-size: 1em;
}

.wrapper2 {
	font-size: 3rem;
}
.wrapper2 .heading {
   	font-size: 2em;
}
.wrapper2 .content {
   	font-size: 0.5em;
}

◻ 주의사항

  • em의 남용은 레이아웃이 깨졌을 때 어디서부터 잘못됐는지 모르는 사태를 초래할 수 있다.
// 혼돈의 카오스
html {
	font-size: 100%
}
body {
	font-size: 1em;
}
.article {
	font-size: 1.5em;
}
.article .wrapper {
	font-size: 2em;
}
.article .wrapper .heading {
	font-size: 3em;
}
.article .wrapper .content {
   	font-size: 1em;
}

✔ 해결 방법

  1. 가능한 low-level에서 em을 선언한다.
  2. rem을 우선적으로 사용하고, em은 local에서 부모와 자식 요소의 상대적 크기를 지정할 때 사용한다.

vw, vh, vmin, vmax

◻ 소개

  • vw: 1vw는 뷰포트 너비의 1%
  • vh: 1vh는 뷰포트 높이의 1%
  • vmin: 1vmin은 뷰포트의 작은 방향의 1%
  • vmax: 1vmax는 뷰포트의 큰 방향의 1%
    ex) width: 1000px, heigth: 2000px인 뷰포트의 경우
    1vmin = 10px
    1vmax = 20px

◻ 사용법

  • responsive typhography 구현에 사용할 수 있다.
  • full-height layout, hero image, sticky footer 구현에 사용할 수 있다.
  • fluid aspect ratio 구현에 사용할 수 있다.
    출처: Fun with viewport units-css tricks

◻ 주의사항

  • 블록 요소의 width 단위로 vw를 사용할 시, 스크롤바를 고려하지 못할 수 있다.
  • 브라우저 호환성이 %, rem, em 등 여타 단위들보다 좋지 않다.

📎 반응형 콘텐츠


이미지

◻ img vs background-image

이미지가 사용자의 페이지에 대한 이해를 돕는다면 img를 사용하고
오로지 디자인이나 배경 이미지로 사용할 목적이라면 background-image를 사용하자.
img vs background-image__stack overflow
안티 패턴으로서의 CSS background-image 속성__velog

html의 img와 css의 background-image 각각
ppi와 사이즈를 고려한 반응형 이미지를 만들 수 있는 방법은
아래를 참고하자.
developers.google.com: Responsive images

◻ width, height

🙌 결론부터 말하자면, 상대 크기를 사용하자.

아래 html 구조를 예시로 설명한다.

<div class="container">
    <img src="lion image from unsplash" alt="lion-father and baby">
</div>
  • 기본적으로 이미지에 하나의 치수만 선언하면 늘 종횡비가 유지된다.
    사자1
.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  width: 100%;
}
  • 기존 이미지의 크기가 줄어들거나 늘어나는 것에 상관없이 컨테이닝 블록의 width에 이미지 크기를 딱 맞추고 종횡비를 유지하길 원한다면 이미지 태그의 width에 100%, height에 auto를 주면 된다.

사자2

.containing-block {
  width: 100%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  width: 100%;
  height: auto;
}
  • 아래 사진처럼 기존 이미지의 크기가 컨테이닝 블록을 벗어날 때가 있다.

사자3

.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

이럴 땐 max-width: 100%, height: auto를 주면 컨테이닝 블록의 width에 맞게 줄어들면서 종횡비를 유지한다.

사자4

.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  max-width: 100%;
  height: auto;
}

벗어난 부분만 자르고 싶다면 부모 요소에 overflow:hidden을 줄 수도 있다.

사자5

.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
  overflow: hidden;
}
  • 기존 종횡비를 유지하되 가로 세로 크기를 임의로 제한하고자 한다면 max-width와 max-height가 매우 유용하다.

사자6

.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  max-width: 300px;
  max-height: 300px;
}

위에 더해 이미지가 반응형이길 원한다면 상대적 단위값(%, vw 등)을 가지는 width를 img에 선언하여 max-width와 함께 활용할 수 있다.

.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  max-width: 300px;
  max-height: 300px;
  width: 40%;
}

참고: 100% width or height while keeping aspect ratio?__stack overflow

◻ 해상도 전환

  • 기기의 특성(화면 크기, 픽셀 밀도)에 따라 적합한 사이즈의 이미지를 제공하는 문제.

  • 같은 이미지 다른 크기.
    해상도 전환 샘플
    출처: howtogeek-how to automatically size pictures in powerpoint

  • img 요소의 srcset을 활용한다. sizes 프로퍼티는 w(width descriptor)를 사용한다면 필수이고, x(display density descriptor)를 사용한다면 필수가 아니다.
    x는 화면 픽셀 밀도를 계산해 적합한 이미지를 다운로드 하지만,
    w는 화면 픽셀 밀도와 함께 이미지의 최종 사이즈를 고려해 최적의 이미지를 다운로드 하기 때문이다.
    w 사용 시 sizes가 없으면 이미지의 최종 사이즈가 어떻게 될 지 판단할 수 없다.
    그 이유는 이미지 다운로드가 css와 javascript를 로딩하고 해석하는 과정이 끝나지 않은 상태에서 이루어지기 때문이다.
    사용자 화면의 레이아웃과 이미지의 사이즈가 아직 결정되지 않은 상태인데 최적의 이미지를 골라 다운로드 하라고 하는 것은
    아직 침대 사이즈를 어떤 걸로 살 지 결정되지도 않았는데 침대 커버를 사이즈에 맞게 사오라는 것과 같은 뜻이다.
    자세한 내용은 아래를 참고하자.
    cloudfour: Responsive Images 101, Part 4: Srcset Width Descriptors
    developers.google.com: Responsive images

// w

<img src="400.png" 
     sizes="(min-width: 600px) 25vw, (min-width: 500px) 50vw, 100vw"
     srcset="100.png 100w, 200.png 200w, 400.png 400w,
             800.png 800w, 1600.png 1600w" alt="an example image">
// x
<img src="photo.png" srcset="photo@2x.png 2x, photo@3x.png 3x, ...">
  • 따라서 크기가 정해진 이미지에 한해 x descriptor를 사용하는 것이 권장되고, 나머지 반응형에서는 w descriptor 사용이 권장된다.

  • srcset과 sizes가 지원되지 않는 브라우저에서는 src 내부의 이미지가 사용되기에 src에 1x의 이미지를 잘 적용하는 것은 중요하다.

◻ 아트 디렉션

  • 기기의 특성(화면 크기, 픽셀 밀도)에 따라 특정 부분을 자르거나 강조해서 보여주고 싶은 문제.

  • 같은 이미지 다른 부분.
    아트 디렉션 샘플
    출처: developers.google.com

  • picture 요소를 활용한다. 기본적으로 활용될 이미지를 img 태그에 선언하고, 브레이크 포인트별로 사용될 이미지들은 source에 선언한다.
    source도 img와 동일하게 srcset, sizes, w, x를 상황에 맞게 사용할 수 있다.
    자세한 내용은 아래를 참고하자.
    cloudfour: Responsive Images 101, Part 6: Picture Element
    MDN: 반응형 이미지

<picture>
  <source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x">
  <source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x">
  <img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" alt="a head carved out of wood">
</picture>

동영상

◻ video

이미지와 동일하게 width, max-width, height를 적절히 섞어 쓰면 된다.

video {
  width: 100%;
  max-height: 100%;
  height: 100%;
}

◻ iframe

컨테이닝 블록의 특성을 이용한다.
요소의 position이 absolute인 경우, position이 static이 아닌 부모의 패딩 영역(내부 여백 영역)이 컨테이닝 블록이 된다.

// html
<div class="videoWrapper">
    <iframe width="560" height="349" src="https://www.youtube.com/embed/tgbNymZ7vqY" frameborder="0" allowfullscreen></iframe>
</div>

// css
.videoWrapper {
  position: relative;
  padding-bottom: 56.25%; /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */
  height: 0;
}
.videoWrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

아니면 이렇게 사용할 수도 있다.

<div class="videoWrapper" style="--aspect-ratio: 3 / 4;">
  <iframe ...>
</div>

.videoWrapper {
  ...
  /* falls back to 16/9, but otherwise uses ratio from HTML */
  padding-bottom: calc(var(--aspect-ratio, .5625) * 100%); 
}

참고
css tricks: fluid width video
MDN: var()
MDN: css custom properties

📏 유동적 레이아웃


flexbox, grid의 모든 개념을 설명하기엔 이 글의 범위를 벗어나므로 간략한 설명과 참조로 대신한다.

flexbox

◻ 개념

1차원에서 강력한 공간 배분 및 정렬 기능을 보유한 css3의 새로운 레이아웃 기능.

◻ 특징

  • 수직, 혹은 수평 중 한 방향으로 정렬하거나 공간 배분을 하는 내비게이션과 같은 레이아웃을 만드는데 탁월하다.
  • container와 item으로 구성된다.
  • container에 display: flex를 선언함으로써 적용된다.
  • 주축(Main Axis)과 교차축(Cross Axis)으로 구성된다.

◻ 참조

MDN: flexbox의 기본 개념
heropy님 블로그: css flex 완벽 가이드
네이버 D2: flexbox로 만들 수 있는 10가지 레이아웃

grid

◻ 개념

2차원의 레이아웃 시스템으로 수평선과 수직선이 교차해서 이루어지며, 각각 가로행과 세로열을 정의한다.

◻ 특징

◻ 참조

MDN: CSS 그리드 레이아웃
MDN: Basic concepts of grid layout
heropy님 블로그: css grid 완벽 가이드
Box 정렬 가이드 by 야무

profile
Maker를 지향하는 웹 개발자입니다.

3개의 댓글

comment-user-thumbnail
2023년 8월 2일

👍👍

1개의 답글