• 반응형 웹 뚝딱 만들기 (2) - vw, vh, vmin, vmax, em, rem 속성

    2020. 8. 23.

    by. 나나 (nykim)

     

    프롤로그

    지난 글에는 반응형을 위해 필요한 뷰포트 메타태그와 미디어 쿼리에 대해 다뤘었는데,
    이번에는 CSS 속성을 통해 좀 더 편하고 쉽게 반응형을 만드는 방법을 알아보려고 합니다 🤟
    히어뤼고!

     

    이 시리즈의 이전 글: 반응형 웹 뚝딱 만들기 (1)

     

     

     


     

     

     

    1. vw / vh

    CSS 작업을 할 때 주로 사용하는 단위는? 픽셀(px)이요!
    하지만 고정 단위인 px 대신 유동 단위인 %를 사용하면 좀 더 유연한 코드를 만들 수 있어요.
    예를 들어 width:50%로 지정해두면 항상 부모의 절반 사이즈를 가지니까 이것 역시 좋은 반응형 CSS죠!

    %말고도 유연한 값을 가지는 단위가 몇 개 있는데요,
    우선은 vw, vh라는 친구들을 만나봅니다 🙋‍♀️

     

     

     

     

    v, 뷰포트를 기준으로 하다

     

    이들의 풀네임은 Viewport Width / Viewport Height 입니다.
    이름에서 유추할 수 있듯이 뷰포트를 기준으로 한 단위입니다.

    뷰포트는 '보여지는 영역'이라고 했으니, 결국 보여지는 영역에서 얼마만큼 차지할 것인지를 지정하는 단위라 할 수 있겠네요.

     

     

     

    '1vw = 뷰포트 너비의 1%'로 계산됩니다.
    그렇다면 500px 너비인 뷰포트에서 1vw의 값은? 5px이요!

    10vw로 지정한다면 50px로 계산될 거고요. 이때 사용자가 브라우저를 잡고 쭉 늘려서 윈도우 너비가 850px이 됐다고 해볼게요.

    그럼 브라우저가 알아서 1vw의 값을 다시 계산해 줍니다.
    따로 미디어쿼리를 써주지 않았음에도 불구하고 1vw = 8.5px / 10vw = 85px으로 값이 커지게 됩니다.

    즉, 저 속성 하나만 써도 브라우저 크기에 반응해 알아서 바뀌게 할 수 있다는 거죠!

     

     

     

     

    vh단위도 똑같이 계산해요! '1vh = 뷰포트 높이의 1%'입니다.

    따라서 800px 높이의 뷰포트에서 1vh = 8px이 됩니다.

     

     

     

     

    vw와 vh 활용법

    이들 단위는 뷰포트 영역 전체를 차지하게 하거나, 그 일부분만 차지하게 하는 데 유용하게 쓸 수 있습니다.

    당장 100vw, 100vh만 써도 너비와 높이를 꽉꽉 채울 수 있어요!

    사용자가 브라우저 창 크기를 바꾸거나 모바일 화면을 회전시켜도 상관이 없죠.

     

    또, 이들을 calc()와 결합하면 좋은 시너지를 낼 수 있습니다.
    헤더영역 높이가 50px이고 컨텐츠 영역 높이를 '헤더를 제외한 나머지 높이 전체'로 만들고 싶다면, height: calc(100vh - 50px);을 지정해주면 됩니다.

     

    음, height: 100%로 지정해도 100vh처럼 전체 높이를 차지할 수 있지 않나요? 라는 생각이 문득 드네요.
    맞습니다. 하지만 %는 부모 요소를 기준으로 계산한다는 점에서 차이가 있습니다.

     

    예를 들어, 뷰포트 높이를 꽉 채우는 섹션이 있는 페이지를 만든다고 해봅시다.
    이때, height: 100%를 지정하려면 부모에게도 height: 100%을 지정해야 합니다. 그래야 상속받아서 쓸 수 있어요.

    반면, height: 100vh를 지정하면 부모의 높이에 무관하게 전체 영역을 차지합니다. vh는 부모가 아닌, 뷰포트를 기준으로 한 단위니까요!

     

    아래 코드를 통해 확인해 보세요. %는 부모에게도 높이값을 줬지만, vh는 section에게만 높이값을 줘서 뷰포트를 꽉 채운 모습입니다.

     

     

    % 단위일 때

    See the Pen Full Size Section with % by nanalike (@nykim_) on CodePen.

     

     

     

    vh 단위일 때

    See the Pen Full Size Section with vh by nanalike (@nykim_) on CodePen.

     

     

     

    vw와 vh는 너비나 높이값에만 쓸 수 있는 건 아닙니다. 폰트 사이즈나 여백 등에 다양한 속성에 쓸 수 있어요.

    즉, font-size: 10vw;와 같이 지정하면 뷰포트가 넓어지면 폰트 크기도 그에 맞춰 커지게 되겠죠.
    이런 식으로 모바일 반응형에 유용하게 쓸 수 있는 단위입니다.

     

    vw와 vh는 IE9부터 지원하는 속성입니다. (Can I Use : vw)

     

     

     


     

     

     

    2. vmin / vmax

    이 친구들은 이름이 vw, vh랑 비슷한데 hoxy...?

     

    넵, vminvmax도 viewport를 기준으로 하는 단위입니다.

    하지만 뭔가 다른 역할을 하니 이름이 다르겠죠?! 이들은 vw와 vh 중 더 작거나 큰 것을 찾아 적용합니다.

     

     

     

     

    vmin과 vmax, 더 작거나 더 크거나

    좀 더 정확하게 설명하면 이렇습니다.

     

     

     

    vmin은 vw와 vh 중 더 작은 것을 적용하고, vmax는 vw와 vh 중 더 큰 것을 적용합니다.

     

     

     

    예를 들어 1200px*600px의 뷰포트가 있습니다. 아까 배운대로 계산하면 1vw = 12px / 1vh = 6px이 됩니다.

    따라서 1vmin = 1vh = 6px으로 계산됩니다.

     

    한편 vmax는 더 큰 걸 적용할 테니, 1vmax = 1vw = 12px이 됩니다.

     

     

     


    이때 뷰포트 크기가 500px*900px로 바뀌었다고 해볼게요.

    이렇게 되면 1vw = 5px / 1vh = 9px이 됩니다.

     

    그럼 vmin과 vmax도 이에 영향을 받아 1vmin = 1vw = 5px / 1vmax = 1vh = 9px가 됩니다!

     

     

     

     

    vmin과 vmax 활용법

    이러면 vw나 vh랑 뭐가 다른가 싶지만,
    이들을 잘 사용하면 '언제든 화면에 보이는 요소'와 '언제든 화면을 꽉 채우는 요소'를 쉽게 만들 수 있어요.

     

     

     

    예를 들어 80vw * 80vw로 지정한 요소가 있습니다. 현재 뷰포트는 1000px * 1000px 이에요.
    '80vw = 뷰포트 너비의 80%'으로 계산되므로 이 요소의 실제 크기는 800px * 800px입니다.

     

     

     

    하지만 vw는 너비에만 반응할 뿐, 높이엔 반응하지 않습니다.

    그러니 브라우저 높이가 500px로 줄어든다고 해도, vw는 반응하지 않아 요소의 크기는 변하지 않습니다.
    결국 800px 높이인 요소는 500px인 브라우저 높이에서 잘려 보이게 됩니다.

     

     

     

    이번엔 가로세로 값이 80vmin인 요소입니다.

    1000px * 1000px 뷰포트일 때, 80vw와 마찬가지로 800px * 800px의 크기값을 가지겠죠?

     

     

     

    이 상태에서 아까처럼 브라우저 높이만 줄여본다면?!

    vmin은 둘 중 더 작은 값을 택하기 때문에, 작아진 높이값에 반응해서 잘리지 않고 화면에 표시됩니다.

    즉, 너비/높이값 중 무엇이 바뀌던 상관없이 화면에 잘 나타낼 수 있습니다.

     

     

    아래는 예제코드입니다.
    vmin은 브라우저 크기를 휙휙 바꾸더라도 항상 뷰포트 내에 들어오는 걸 확인할 수 있어요.

     

     

    See the Pen "vw" vs. "vmin" by nanalike (@nykim_) on CodePen.

     

     

    생소한 단위지만 vmin은 IE9부터 사용할 수 있어요! (Can I Use : vmin)

    단, vmax는 IE에서 지원되지 않으며 Edge부터 사용 가능합니다. (Can I Use : vmax)

     

     

     


     

     

     

    3. em / rem

    em, 폰트를 기준으로 한 단위

    em 역시 동적인 단위에요! 재미있게도 이 단위는 폰트 크기의 영향을 받습니다.
    '1em = 부모의 폰트 크기'라는 별난 계산법을 가지고 있어요.

     

     

     

    대부분의 브라우저에서 폰트 크기의 디폴트 값은 16px입니다.
    따라서 별도로 스타일링을 해주지 않았다면 1em = 16px이 됩니다. 2em이라면 32px이겠죠.

     

    만약 부모의 폰트 크기가 20px이라면요?
    1em = 20px이 되고, 5em = 100px으로 커지게 됩니다.

    즉, 부모의 폰트 크기를 키우면 자식 요소의 크기도 덩달아 커지게끔 할 수 있다는 뜻이죠!

     

     

    음... 하지만 디자인 시안은 px이 기준이니까, 실제로 작업할 때는 px를 기준으로 em으로 바꾸는 작업을 거쳐야겠네요.

    만약 시안에 있는 마진값이 20px이라면 이건 몇 em이 될까요?
    기본 폰트 크기가 16px이라고 했을 때, (20/16 = 1.25em) 이라는 계산이 나오게 됩니다.

     

    이러한 계산을 돕는 온라인 사이트(pxtoem.com)도 있고,

    SCSS를 쓴다면 이 계산을 함수로 만들어서 필요할 때마다 사용할 수도 있습니다.

     

    $browser-font-size: 16; 
    
    @function em($pixels, $context: $browser-font-size) {
      @return #{$pixels/$context}em;
    }
    
    .title {
      font-size: em(32); // 32/16 = 2em;
    }
    

     

     

     

     

    rem, 최상위 폰트를 기준으로 한 단위

    em의 친구로는 rem이란 속성도 있습니다. 다른 점이 있다면 rem은 'relative to the root element'라는 점입니다.

    HTML 문서에서 최상위 요소(root element)는 바로 <html> 요소를 가리킵니다.

     

     

     

    따라서 '1rem = html 요소에 지정된 폰트 크기'로 계산됩니다.
    더욱더 별난 계산법이 있었네요... 🤷‍♀️

     

    예를 들어, 이러한 구조로 된 문서가 있습니다.

     

    <html>
      <body>
        <div class="parent"></div>
        <div class="child"></div>
      </body>
    </html>

     

    이때 각 요소에 지정된 폰트 크기는 이렇습니다.

     

    html { font-size: 20px; }
    .parent { font-size: 16px; }
    .child { font-size: 1rem; }

     

    그럼 브라우저가 계산한 .child의 폰트 크기는 어떻게 될까요?
    답은 20px입니다 ;)

     

     

     

     

    em과 rem 활용법

    어... 대충 이런 신기하고 별난 단위가 있다는 건 알았는데, 그래서 어떻게 써먹을 수 있다는 걸까요?
    간단합니다. 바로 '부모의 폰트값에 영향을 받는다'는 점을 이용하는 거죠!

     

    예를 들면 이런 식으로 값을 줄 수 있어요.

     

    .title { font-size: 30px; }
    .title__image { width: 10em; }
    .title__text { font-size: 1.5em; }
    .title__text-small { font-size: 0.8em; }

     

    문제: 위 CSS 중에서 .title의 폰트값을 50px로 바꾸면 무슨 일이 생길까요?
    정답: 아래에 있는 모든 자식들이 영향을 받아서 쑥쑥 커집니다!

     

     

     

    아래는 예제 코드입니다.

    슬라이더 바를 움직여 .title의 폰트 크기를 바꾸면, 자식 요소들도 덩달아 들썩들썩 하는 걸 볼 수 있습니다.

     

     

    See the Pen How to Use 'em' Units in CSS by nanalike (@nykim_) on CodePen.

     

     

     

     

    이걸 응용한다면 미디어쿼리로 .title의 폰트 사이즈만 바꿔주면 별다른 공수 없이 반응형을 손쉽게 만들 수 있어요.

     

    .title { font-size: 40px; }
    .title__image { width: 10em; } //40px
    .title__text { font-size: 0.4em; } //16px
    
    
    @media all and (max-width: 750px) {
      .title { font-size: 20px ; } // 이제 .title 아래에 있는 모든 em 단위가 영향을 받습니다.
    }

     

    하지만 em은 계산하기 번거롭기 때문에, 저의 경우에는 em보다도 rem을 주로 사용하고 있어요.

    루트 요소의 폰트 크기를 62.5%로 지정하고 rem을 사용하면 훠어얼씬 편해집니다.

     

    html { font-size: 62.5%; }
    .image { width: 12rem; } //120px
    .item { font-size: 1.6rem; } //16px
    .copyright { margin-top: 5rem; } //50px
    
    @media all and (max-width: 750px) {
      html { font-size: 50%; } // 이제 문서 내 모든 rem 단위가 영향을 받습니다.
    }

     

    위 코드를 보면 1rem = 10px이 되어 작성하기가 훨씬 쉽고,

    미디어 쿼리로 html 요소의 폰트 사이즈값만 바꾸면 모든 rem 단위에 영향을 줄 수 있다는 걸 알 수 있습니다.

    em에 비해 훨씬 편하죠!

     

     

    그게 어떻게 가능하냐면요...

     

    html { font-size: 62.5%; }

     

    요로코롬 생긴 구문 덕분입니다!

    이거 보고 뭔 소린가 하신 분? (바로 저요 💁‍♀️)

     

     

    이는 폰트 계산을 더 편하게 하기 위함입니다.

    브라우저가 지정해주는 기본 폰트 사이즈는 100% = 16px이죠.
    이걸 계산하기 쉽게 62.5% = 10px로 바꿔버리는 거에요.

     

    여기서 다시 복습! '1rem = html 요소에 지정된 폰트 크기'로 계산한다고 했었죠.
    따라서 위 구문이 있다면 '1rem = 10px'이 됩니다.

    그래서 128px = 12.8rem, 4px = 0.4rem 처럼 쓸 수 있습니다. (편-안)

     

     

     

    예전 포스팅에서 쓱 만들었던 사이트 역시 rem을 통해 손쉽게 반응형을 만든 케이스입니다.

    모바일 퍼스트이기 때문에, 스크린 크기가 커지면 html 의 폰트 크기가 커지도록 설정했습니다.

    https://nana-like.github.io/vue-mytodo/

     

     

     

     

    예전에 이 rem 단위를 사용한 덕분에 "흠.. 시안은 없지만 모바일말고 태블릿 대응도 해주세요"라는 요구에서 빠르게 탈출할 수 있었습니다 😓휴

     

    em은 IE3부터 지원하며(어르신...), rem은 IE9부터 지원합니다.

    단, rem은 IE10 이하에서 단축 속성으로 font를 선언한 경우와 가상 엘리먼트에서 사용할 때는 지원되지 않는다고 합니다. (Can I Use: rem)

     

     

     

    근데 왜 %죠?

    급뜬금 퀴즈 타임!

     

    html { font-size: 10px; }로 지정해도 결과는 똑같은데 굳이 %로 지정한 이유는?

     

    브라우저의 기본 폰트 사이즈는 16px이라고 계속 말했지만, 그건 어디까지나 사용자가 건들지 않았을 경우입니다.

    작은 글씨가 불편한 사용자는 웹 브라우저 설정에서 '폰트 기본 사이즈: 크게'와 같이 지정할 수 있어요.

     

     

     

     

    이런 식으로 Large 사이즈를 지정하면 기본 폰트 사이즈가 16px이 아닌 20px이 됩니다.

     

    그런데 우리가 "안됩니다 html은 무조건 10px이여야 해요"이라고 하면 당황스럽겠죠! 웹 접근성 취지에서도 어긋나고요.

    하지만 %로 지정해두었으니, 사용자가 폰트 기본 사이즈를 Large로 바꾸든 Small로 바꾸든 그에 대응할 수 있습니다.

     

    rem 단위를 썼을 때 좋은 점은 이러한 경우에도 잘 대응해서 누구에게나 잘 보이는 웹 페이지를 만들 수 있다는 거죠! 👏👏

     

     

     


     

     

     

    에필로그

    원래 이렇게 길게 쓰려던 길이 아니었는....데........ 어쩌다보니 이것저것 다루게 된 짬뽕 🥘 글이 됐네요.

     

    그래도 이 글을 쓰면서 많은 공부를 할 수 있었습니다.

    미디어쿼리나 CSS속성들을 단순히 아는 것을 넘어, 좀 더 명확히 이해할 수 있었던 기회였어요.

     

    (까먹고 찾아 볼 미래의 나를 위해서라도) 어떻게 하면 더 쉽게 설명할 수 있을까 고민하는 과정 속에서
    어떤 표현과 흐름이 좋은 글을 만들어낼 수 있을지 생각해볼 수 있었습니다.

    글을 읽으신 분들께 쪼오끔이라도 도움이 되셨다면 좋겠네요!

     

     

     

     

    아, 혹시나

    이 글을 에필로그 끝까지 읽고 계셨다면

    수줍게 저의 하트를 드립니다 😘

    댓글 4

    • 프로필사진
      2020.08.24 14:58

      근데 em이.. px to em 같은 사이트에서 보면 정확한 비율이 아니더라구요. 1: 1.25같은 비율이 아니고 중간에 달라지는 부분이 있어요.

      • 프로필사진
        나나 (nykim) 2020.08.26 00:05 신고

        안녕하세요!
        아무래도 em이 상대적인 폰트다보니 계산하다보면 긴~~ 소수점이 나올 수밖에 없겠더라구요ㅎㅎ
        px to em은 소수점 셋째자리 반올림한 값을 보여주는 듯한데 뭔가 다른 게 있을까요?

        사실 저도 em을 잘 안 써서 좀 어색한 친구(?)인데요,
        내용 중 바르지 못한 부분이 있거나 알고 계신 좋은 내용이 있으시다면 나중에라도 스리슬쩍 말씀해 주세요 😊

      • 프로필사진
        메시프 2020.09.07 15:47

        SCSS(SASS) 등을 활용하시면 함수를 직접 만들어쓰시면 훨씬 편하게 구하실 수 있어요!
        상대 값이다보니 어떻게 넣냐에 따라 소수점이 길게 나올 수 밖에 없는데요.

        저는 SCSS에서 소수점 몇째자리에서 반올림을 지원하지 않아서
        floor 하기 전 미리 100을 곱했다가 100 으로 다시 나누어 주는 방법으로

        소수점 셋째자리까지만 나오게 해서 사용하는 중입니다 ㅎㅎ

    • 프로필사진
      도라에몽 2020.10.12 01:46

      설명이 너무너무 친절하네요! 특히 html에 font-size: 62.5%로 설정하는 팁...매번 px > rem으로 계산하느라 귀찮았는데 너무 꿀팁입니다...메모메모...중간중간 예시코드들도 있어 정말 잘 읽었습니다. 감사합니다 (_ _)