뷰포트부터 반응형 웹까지 개념정리

bityoungjae·2020년 3월 31일
4
post-thumbnail

뷰포트란 무엇인가

웹 페이지가 브라우저 화면상에서 실제로 표시되는 영역을 뷰포트라고 합니다.

뷰포트는 장치에 따라 다릅니다.
모바일 화면에서는 작고, 컴퓨터 스크린에서는 보다 큰 영역을 가지고 있습니다.

스마트폰과 태블릿의 전성시대 이전의 기성 웹사이트들은 오직 컴퓨터 스크린에만 최적화하여 디자인이 되었습니다.

그 시절에는 정적인 디자인과 웹 페이지내 요소들에 대한 고정된 크기 설정은 일반적인 경우였죠.

정적 디자인의 문제점

컴퓨터 스크린에 맞춰서 고정된 크기로 디자인된 웹 사이트들은, 모바일 화면에서는 지나치게 비대했습니다.

따라서 기존 데스크탑 환경에서는 불필요하던 스크롤링이 필요하게 되었습니다.

스마트폰과 태블릿을 통해 인터넷을 막 시작하게 된 초창기 웹 생태계에서는 이 때문에 웹 사이트를 스마트폰 너비에 맞게끔 강제로 축소시켜서 표시하였죠.

그러자 이제는 유저들에게 불필요한 확대와 축소를 강제하게 되었습니다.

이렇듯 서로다른 뷰포트를 가진 다양한 장치들에게 고정된 크기와 레이아웃을 제공하는 것은 상당한 불편함을 초래합니다.

반응형 웹과 적응형 웹

이러한 문제들을 해결하려면 장치마다 서로 다른 뷰포트의 크기에 맞게끔 서로 다른 크기와 레이아웃을 제공하면 될 것입니다.

하나의 웹 페이지가 뷰포트의 크기에 반응하여 크기와 레이아웃을 그때그때 변경되는 디자인을 반응형 웹 디자인이라고 하고, 애초에 특정 뷰포트의 크기에 맞게끔 설계된 웹 페이지를 만들어서 별도로 제공하는 것을 적응형 웹 디자인 이라고 합니다.

반응형 웹은 미디어 쿼리라는 것을 이용하여 뷰포트의 크기 변화에 반응하여 레이아웃과 크기에 시시각각 변화를 주는 방법을,
적응형 웹은 서버를 통해 웹 페이지를 요청하는 기기를 감지하여(HTTP GET 요청의 user-agent 헤더 정보를 이용) 특정 유형의 기기일 경우 해당 뷰포트에 최적화된 페이지로 리디렉션 시켜주는 방법을 사용합니다.

적응형 웹의 경우 우리가 스마트폰으로 포털 사이트에 접속하면 자동으로 m.naver.com 이나 m.daum.net 과 같이 모바일 전용 페이지로 리디렉션을 되는 것을 떠올리면 이해하기 쉬울 것 같습니다.

두 방식은 각각의 장단점을 가지고 있습니다.

반응형 웹의 경우 똑같은 내용물을 포함한 하나의 페이지를 가지고, 그 안에서 모든 요소들을 십분 활용하여 이리저리 움직이고 늘리고 줄이는 방식을 사용해야 합니다.
뷰포트의 크기 변화에 대해 즉각적으로 반응해야 하기 때문에, 각 뷰포트 사이즈의 다양한 경우의 수들에 대한 스타일 정의와 스크립트를 가지고 있어야 하죠.

반대로 적응형 웹의 경우 사용자의 디바이스에 최적화된 꼭 필요한 리소스(마크업, CSS, 스크립트 등등)들만을 가져오므로, 반응형 웹에 비해서 데이터를 적게 사용하고 상대적으로 빠르다는 장점이 있습니다.

웹 사이트의 경우 필연적으로 다수의 유저가 이용하게 되어있습니다.
한 번의 다운로드에 있어서 아주 작은 크기의 차이더라도, 이게 수백번 수천번이 되면 그 차이는 상당히 커지기 마련이겠죠.
때문에 하루에도 수천만번 접속 요청이 쇄도하는 포털 사이트들이 왜 적응형 웹을 선택하게 되었는지 이해해볼 수 있을 것 같습니다.

또한, 적응형 웹의 경우 기존에 만들어둔 컴퓨터 스크린용 페이지가 있다면, 해당 페이지를 다시 수정할 필요 없이.. 그러니까 기존에 공들여서 잘 만들어둔 것은 그것대로 유지하면서, 모바일용 페이지를 하나 따로 만들어 제공해주기만 하면 된다는 점이 장점이 되기도 합니다.

하지만 이 부분을 역으로 생각하면 반응형 웹은 한 벌의 마크업과 컨텐츠 묶음을 가지고 여러 뷰포트 크기를 대응하면 되지만, 적응형 웹의 경우 두 벌 이상의 마크업과 컨텐츠 묶음을 지속 관리해야 한다는 점에서 유지보수 등에 상대적으로 많은 비용이 들어갈 수 있다는 단점이 생기기도 합니다.

뷰포트의 크기 설정

<head> 요소의 자식 요소로써 <meta> 요소를 사용해 브라우저 뷰포트의 크기와 배율을 지정할 수 있습니다.

아래는 가장 일반적인 설정 두 개를 보여줍니다.

<meta
  name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.2"
/>
<meta name="viewport" content="width=device-width, user-scalable=no" />

스마트폰 화면에서 표시할 수 있는 최대 가로 픽셀이 320픽셀인데, 뷰포트 너비를 2000으로 설정했다고 칩시다.

오른쪽으로 넓은 여백이 생겨서 가로 스크롤바가 생길 것입니다.

또한, 분명 나는 100vw 와 같은 방법으로(애초에 너비 설정이 없는 박스 요소는 기본적으로 가로 영역을 100% 차지합니다) 뷰포트에 대해 100%로 가로 너비를 설정했는데, 화면이 왼쪽 일부만 표시가 됩니다.

위 두 설정이 가장 일반적인 설정인 이유는, 뷰포트의 크기를 디바이스의 너비와 맞춤으로써 불필요한 여백을 없애고 정확한 크기 설정에 대한 기준을 제공해주기 때문입니다.
즉, 내가 가로 너비를 100vw라고 설정하면 그냥 어떤 장치에서든 가로 화면을 가득 채우게 되는 것이죠.

아울러, scale 관련 속성들을 이용해 모바일 브라우저에서 확대와 축소에 대한 초기값과, 최소, 최대 배율을 지정할 수도 있습니다.

initial-scale=1 을 설정하여 기기의 픽셀과 CSS의 픽셀을 1:1로 매칭합니다.
initial-scale=2 로 설정하면 CSS 상의 픽셀이 화면상에서 2배로 표현되겠죠?
user-scalable=no 라는 것은 유저가 확대나 축소를 할 수 없다는 의미가 됩니다.

그리드뷰

많은 웹 페이지들이 그리드뷰라는 것에 기반하여 디자인 되었습니다.
그리드뷰란 여러 개의 세로 영역으로 구분된 페이지를 의미합니다.

웹 페이지내에 한 화면을 몇 개의 동일한 너비의 영역으로 나누었다고 가정하고, 요소들을 배치할 때 이용하는 것이죠.

한 번 를 떠올려보세요.

동일한 크기를 가졌을 때 12개의 열을 가진 표와 6개의 열을 가진 표가 있을 수 있겠죠?
각 셀들을 몇 개씩 병합하여 각각 요소들의 자리를 만들어 배치하는 겁니다.

나누어진 영역의 갯수, 즉 열의 갯수가 12개라면 12컬럼 그리드 24개를 가졌으면 24 컬럼 그리드라고 부르거나..
그냥 12개의 열을 가진 그리드 시스템이라고 표현하면 됩니다.

960px의 너비에 대해 12개의 열을 가진 그리드 시스템과 같이 기준이 되는 너비와 같이 이야기할 수 있습니다.

고정 그리드 뷰와 반응형 그리드 뷰 역시 그 개념이 단순합니다.
그리드 뷰 내부에서 너비를 고정된 단위로 설정하느냐 상대적 단위로 설정하느냐의 차이일 뿐이죠.

고정 그리드뷰

고정 그리드뷰는 어떻게 구현할 수 있을까요?

역시 간단합니다.

기준이 되는 그리드뷰의 전체 너비를 m픽셀이라고 설정하였다면,
12컬럼 그리드의 경우 m/12픽셀 단위로 컬럼을 나누고 만약 2개의 컬럼을 차지하는 요소를 배치하고 싶다면 m/12 * 2픽셀 만큼의 너비를 설정하면 되는 것이죠.

여백과 외곽선으로 인한 크기 설정의 불편함을 예방하기 위해 box-sizing 속성을 border-box 로 설정 후 구성하는 것이 좋습니다.

반응형 그리드뷰

고정 그리드뷰와의 차이점은 단지, 크기를 설정하는 단위로 상대 단위를 사용하는 것 뿐입니다.
그리드뷰의 너비를 100% 혹은 100vw 와 같은 가변 단위로 설정한 뒤, 그 내부 요소들의 너비를 비율로써 설정하는 것입니다.

기준이 되는 그리드뷰 전체 너비를 80vw 라고 설정했을 때,
12컬럼 그리드의 경우 (100 * 1 / 12)% 단위로 컬럼을 나누고 만약 2개의 컬럼을 차지하는 요소를 배치한다면 여기에 2만큼의 비율을 너비로 설정하는 것이죠.

가변 글꼴

반응형 웹에서는 글꼴의 크기도 상대 단위를 사용해 구성하는 것이 용이합니다.
em 과 rem 단위를 이용해 글꼴의 크기를 지정하면 기준점이 되는 폰트의 크기를 변경함으로써 하위 요소들의 글꼴 크기도 일괄적으로 변경할 수 있습니다.

em

'엠' 이라고 읽을 수 있습니다.
대문자 M 의 가로 너비를 의미합니다.

em은 font-size 와 그 크기가 일치한다고 보시면됩니다.
어떤 요소에 font-size 속성이 24px 로 설정되었다면 1em은 24px가 됩니다.

font-size 속성은 조상으로부터 설정값이 상속되는 속성이므로, 어떤 요소에 font-size가 자체적으로 설정되어 있지 않다면 가장 가까운 부모 요소의 font-size가 1em 이 됩니다.

따라서, 복잡하게 중첩된 HTML요소들이 각각의 em 단위를 사용한다면 정확한 크기를 예측하는데 어려움이 생깁니다.

rem

'렘' 또는 '루트 엠' 이라고 읽을 수 있습니다.
Root em 이라는 의미입니다.

em 단위가 자기 자신이나 가장 가까운 부모 요소의 font-size 를 의미한다면, rem은 최상단 요소(html 요소)의 font-size 값을 의미합니다.

em 과 rem 모두 루트 요소까지 별도로 설정된 font-size 가 없을 경우 브라우저의 설정값을 참조합니다.

별도의 사용자 설정이 없다면 브라우저 기본 설정값은 보통 16px로 설정되어 있습니다.

rem과 em은 어떤 상황에서 사용해야할까요?

padding, margin, width 가 font-size의 변화에 반응하여 동시에 움직여야 할 경우 em 단위를 고려할 수 있을 것입니다.

h1 {
  font-size: 1rem;
  padding: 1em;
  margin: 1em;
}

font-size 속성을 변경함으로써 padding과 margin 도 그에 반응하여 적절히 크기를 늘리거나 줄입니다.
만약 padding과 margin도 rem 속성을 사용했을 경우엔 개별로 다시 크기 지정을 해줘야겠죠?

즉, font-size 에 따라 가변하는 단위가 필요할 경우 em 을 사용하되 그 외에는 rem 단위를 사용하면 되겠습니다.

반응형 이미지

이미지 또한 뷰포트의 크기에 반응하여 이미지의 해상도와 너비가 달라져야 할 필요가 있습니다.

필요한 이유는 간단합니다.

작은 화면에서 이미지가 잘리거나 큰 화면에서 이미지가 작게 표시될 수 있기 때문에.
또, 작은 화면에서 과한 해상도의 이미지가 출력되어 데이터를 낭비하거나 큰 화면에서 낮은 해상도의 이미지가 출력되어 이미지가 깨져보일 수 있기 때문에.

SVG 이미지라면 상관없습니다.
단, 픽셀의 조합으로 이루어진 래스터 이미지라면 당연히 확대시 이미지를 구성하는 픽셀이 도드라져 보일 것입니다.

img[srcset]

img 태그에 srcset 속성과 sizes 속성을 함께 사용하면 마크업단에서 간단히 해결할 수 있다.

<img
  srcset="small.png 300w,
          medium.png 600w,
          large.png 960w"
  sizes="(max-width: 300px) 280px,
         (max-width: 800px) 700px,
         800px"
  src="normal.png"
  alt="가변 이미지" />

srcset="small.png 300w" 에서 300w 는 small.png 파일의 원본 해상도(이미지의 실제 사이즈)를 의미한다.
w 단위 대신 x 단위를 사용할 수도 있는데 여기서 x는 1.5x 와 같이 기본 이미지에 대한 상대적인 배율을 지정한 것이다.

보통은 w 단위를 사용하면 된다.

sizes 속성에는 미디어 쿼리가 사용되었다.
(max-width: 300px) 즉, 뷰포트의 너비가 300px 이하일 때 280px의 너비로 이미지를 표시하라는 의미이다.

src 속성에는 srcsetsizes 속성을 이해하지 못 하는 낡은 브라우저가 표시할 이미지를 지정한다.

picture

역시 반응형 이미지를 만들어낼 수 있는 태그이다.
마치 전날 소개해드렸던 video 태그와 같이 picture 요소안에 source 요소로 각각의 미디어를 지정한다.

구성은 역시 아주 간단하다.
각 source에 지정된 media 속성에 미디어 쿼리를 통해 어떤 상황에서 어떤 이미지를 표시할지를 명시한다.

<picture>
  <source media="(max-width: 300px)" srcset="small.png">
  <source media="(min-width: 800px)" srcset="large.png">
  <img src="normal" alt="가변 이미지">
</picture>

picture 요소는 반드시 img 요소를 하나 포함하고 있어야만한다.

img 태그로 지정한 이미지는 source 태그들에 지정된 media 속성에 만족하는 미디어가 없을 경우나 낡은 브라우저일 경우에 대체로 표시할 이미지가 된다.

img[srcset] 과 picture 요소는 어떤 경우에 사용할까?

역시 간단하다.

img 태그의 srcset은 Resolution switching 즉 해상도 전환에 대한 대응을 목적으로 사용하고, picture 태그는 Art direction 즉 뷰포트의 너비에 따라 서로 다른 연출의 이미지를 표시할 목적으로 사용한다.

미디어 쿼리

미디어 쿼리란 스크린 해상도에 따라 컨텐츠 렌더링을 변형할 수 있도록 하는 CSS3 모듈이다.
반응형 웹 디자인의 토대가 되는 기술이라고 할 수 있다.

@media screen and (display-mode: fullscreen) {
  /* ... */
}

@media all and (orientation: landscape) {
  /* ... */
}

@media screen and (min-device-width: 500px) {
  /* ... */
}

미디어 쿼리는 미디어 타입과 하나 이상의 media에 대한 내용을 포함한 '참' 혹은 '거짓'으로 결정되는 표현식으로 구성된다.

@media [only | not]? media-type [and|or] (media-feature)

screen and (display-mode: fullscreen) 는 screen(노트북, 스마트폰, 태블릿 등 일반적인 화면을 가진 디바이스) 이면서 화면 모드가 '전체화면'인 경우일 때 { ... } 를 적용한다라고 해석될 수 있다.

미디어 타입들

단지 어떤 장치이냐를 의미한다.

  • all (모든 장치에 대해)
  • braille
  • embossed
  • handheld
  • print
  • projection
  • screen
  • speech
  • tty
  • TV

다양한 경우들이 있지만 보통 현실적으로 고려해야할 미디어 유형으로는 screen 과 print 를 들 수 있을 것이다.

print를 이용해 프린터 출력시 적용될 스타일을 별도로 지정할 수 있다.

실제로 필자의 경우 블로그 포스트 등을 실제 출력물로 혹은 PDF 포맷으로 인쇄하는 경우가 많은데, 이를 고려했고 안 했고의 차이가 상당히 크게 느껴졌다.

약간의 과장을 보태자면 인쇄 버튼을 눌렀을 때 마치 워드프로세서로 작성한 문서처럼 깔끔하게 출력된 블로그 포스트를 보았을 때는 감동까지 느껴질 정도였다.

미디어 기능 표현식

  • color - 색상 지원여부 (숫자를 값으로 사용할 경우 픽셀 당 비트수) / min or max prefix 사용가능
  • monochrome - 흑백 (숫자를 값으로 사용할 경우 픽셀 당 비트수) / min or max prefix 사용가능
  • aspect-ratio - 화면비 가로/세로 비율 / min or max prefix 사용가능
  • width - 뷰포트의 너비 / min or max prefix 사용가능
  • height - 뷰포트의 높이 / min or max prefix 사용가능
  • resolution - 해상도 dpi와 dpcm을 이용해 표기한다. / min or max prefix 사용가능
  • orientation - 화면의 방향

아래의 html 코드와 css 코드들은 w3c 공식 문서에서 발췌한 코드들이다.

<link rel="stylesheet" media="print and (color)" href="http://…" />
<link rel="stylesheet" media="print and (monochrome)" href="http://…" />

첫째줄은 컬러를 지원하는 장치를, 두번째는 흑백을 지원하는 장치에 대한 미디어 쿼리이다.

@media screen and (aspect-ratio: 16/9) {}
@media screen and (aspect-ratio: 32/18) {}
@media screen and (aspect-ratio: 1280/720) {}
@media screen and (aspect-ratio: 2560/1440) {}

화면비에 따라 미디어 쿼리를 작성할 수도 있다.
첫째 줄은 16:9 디바이스, 마지막 줄은 2560 x 1440 디바이스를 의미한다.

서로 다른 경우의 수들인 것 같으나 다시끔 살펴보면 값들이 다 ratio(비율) 이다.
즉! 계산하면 모두 같은 비율을 나타내는 것이므로 다 같은 경우에 매칭한다.

@media all and (orientation:portrait) {}
@media all and (orientation:landscape) {}

orientation 은 장치의 방향을 지정한다.
portrait 는 세로 방향 landscape 는 가로 방향을 의미한다.

portrait은 초상화를 landscape 는 풍경화를 의미한다.
초상화는 세로로 길고 풍경화는 가로로 길고 일반적인 이미지를 활용해 기억해두자.

미디어 타입은 또한 head 요소 내의 link 요소에 media 속성으로 지정할 수도 있다.
따라서, 미디어 타입에 따라 특정한 스타일시트를 불러올 수도 있다는 의미이다.

또한 <style> 태그에도 media 속성을 통해 미디어 쿼리를 사용할 수 있으므로 참고하자.

<link rel="stylesheet" media="print and (color)" href="http://…" />

미디어 쿼리의 중단점

웹 페이지 디자인의 일부분이 서로 다른 양상으로 표현되도록 미디어 쿼리로 설정된 각 조건들을 미디어 쿼리의 '중단점' 이라고 한다.

@media screen and (max-width: 320px) {
  /* ... */
}
@media screen and (max-width: 500px) {
  /* ... */
}
@media screen and (max-width: 960px) {
  /* ... */
}

위 예시에서 우리는 width 조건에 대해 중단점을 세 개 설정한 것이다.

중단점들은 시중에 나와있는 스마트폰이나 태블릿 등의 device-width 에 따라 대응하는 조합이 달라질 수 있는데,

구글 검색창에 media query breakpoint 라는 키워드를 입력하면 media query breakpoints 2020 같은 것들이 자동완성 목록에 나온다.

중단점들을 어떻게 설정하면 시중 디바이스들을 골고루 커버할 수 있는지 뭐 이런 내용들의 글들이 많이 있는데, 그 중에서 하나를 발췌해보았다.

아래는 Responsive breakpoints in 2020 - WordPress responsive design tutorial 라는 글에서 발췌한 중단점 목록들이다.

/* 1366px 이상의 너비를 가진 모니터 화면 */

h1 { font-size: 36px; }

/* 가로모드 태블릿이나 오래된 데스크탑 모니터 */

@media only screen and (min-width: 1024px) and (max-width: 1365px) {
h1 { font-size: 32px; }
}

/* 세로 모드의 태블릿이나 큰 화면을 가진 스마트폰의 가로모드 */

@media only screen and (min-width: 768px) and (max-width: 1023px) {
h1 { font-size: 28px; }
}

/*  일반적인 스마트폰의 가로모드 */

@media only screen and (min-width: 421px) and (max-width: 767px) {
h1 { font-size: 24px; }
}

/*  일반적인 스마트폰의 세로모드  */

@media only screen and (max-width: 420px) {
h1 { font-size: 20px; }
}

읽어보면 좋을만한 글로 아래 글을 추천한다.

Media Queries for Standard Devices | CSS-Tricks

끝..

스터디원들이랑 공부한다고 작성하기 시작했는데 생각보다 글이 많이 장황해졌다.

하지만 역시 적자생존이라고 이번에도 적으면서 많이 배웠다.

의식에 흐름을 따라 작성한 글이라 부족한 점이 많지만 너그러이 이해해주길 바라며, 눈치채신 분들도 계시겠지만 너무 급하게 작성하다보니 글 도입부에는 존대를 중간부터는 반말투로 갑작스럽게 말투가 바뀌었다..

쓰고 나서 깨달았지만 귀찮아서 수정은 미룬다..

profile
안녕하세요

1개의 댓글

comment-user-thumbnail
2020년 5월 20일

도움이 많이 되었습니다!!

답글 달기