반응형 웹

2025. 8. 14. 17:10·프론트엔드

개인 프로젝트를 진행하던 와중

API 생성, DB 연결, 디자이너 친구가 짜준 프롬프트에 맞춰 페이지 디자인까지 완료를 했다.

나는 막연히 "px같은 절대 단위가 아닌 상대 단위로 코드를 짜면 되지 않을까?" 라는 생각으로 프론트를 구성했다.

결과는 당연히 참패.

각 구성요소들의 비율을 무시해버리니 당연히 그럴 수 밖에..

그래서 반응형 웹에 대한 정리를 하기로 결심했다.

반응형 웹이란?

  • 기기나 브라우저의 크기에 맞게 구성이나 크기를 변경해가며 반응하는 웹문서 또는 이를 위해 사용하는 기법
  • 반응형 웹사이트에서 가장 핵심이 되는 키워드는 '가변성'
  • 기기에 따라 다른 화면 크기에 대응하기 위해, 웹 문서에 viewport 속성에 대한 마크업을 추가해야함
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

</body>
</html>

viewport 설정

너비를 기기의 너비 기준으로 초기화하겠다. (width=device-width)

initaial-scale = 1.0 : 원상태 그대로(1.0)


💡 단위

- 상대 단위 em, rem

흔히 사용하는 px은 절대 길이 단위다.

어떤 상황에서도 동일한 값을 유지하고 가변성이 없다.

  • em: 부모 요소의 글꼴 크기
  • rem: 루트 요소의 글꼴 크기
<div style="font-size: 20px;">
<p> 자식 요소의 1em은 20px <p>
<p> 자식 요소의 2em은 40px <p>
</div>

루트요소란?

요소이다.

루트 요소 글자 크기 기본값은 16px이다.

따라서 1rem은 16px, 2rem은 32px이다.

em으로 내, 외부 여백 크기를 정할 때는 자기 자신의 글자 크기를 기준으로 한다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>em과 rem</title>
  <style>
    .outer{
      font-size: 32px;
    }
    .inner{
      font-size:1em;
      display: inline-block;
      width:200px;
      height:200px;
      background-color: tomato;
      padding: 1em;
      margin: 1em;
    }
  </style>
</head>
<body>
  <div class="outer">
    <div class="inner">강아지</div>
</body>
</html>
  • .outer는 .inner의 부모요소
  • 따라서 .outer의 폰트 사이즈가 32px일 경우 .inner의 폰트 사이즈를 1em으로 지정하면 1em == 32px이다.
  • 하지만 부모 자식 요소와 상관없이 rem을 사용하면태그 즉, 루트 요소에 따라 기본 값이 16px이다.
<style>
    .outer{
      font-size: 24px;
    }
    .inner{
      font-size:1em;
      display: inline-block;
      width:200px;
      height:200px;
      background-color: tomato;
      padding: 1em;
      margin: 1em;
    }
  </style>
</head>
<body>
  <div class="outer">
    <div class="inner">강아지</div>
</body>
</html>
  • 부모요소인 .outer의 폰트사이즈가 24px이므로
  • 자식요소의 1em값은 전부 24px이 된다.
<style>
    .outer{
      font-size: 24px;
    }
    .inner{
      font-size:1rem;
      display: inline-block;
      width:200px;
      height:200px;
      background-color: tomato;
      padding: 1em;
      margin: 1em;
    }
  </style>
</head>
<body>
  <div class="outer">
    <div class="inner">강아지</div>
</body>
</html>
  • 부모 요소의 폰트사이즈는 24px, 자식 요소의 폰트사이즈는 1rem(==16px)
  • 패딩과 마진은 각각 1em
  • 그러면 em이니까 부모요소의 폰트사이즈를 따라 24px이 되어야 맞겠지만
  • 패딩과 마진은 내 자신의 폰트 사이즈에 영향을 받는다.
  • 내 자신인 .inner의 폰트 사이즈는 1rem 즉, 16px
  • 따라서 패딩과 마진 값 모두 16px이다.

정리

  • 절대 길이 단위인 px은 어떤 상황에서도 동일한 값을 유지 → 가변성X
  • em과 rem은 박스에서 텍스트 크기를 조정할 때 사용하는 상대 단위
  • em은 부모 요소의 글꼴 크기를, rem은 루트 요소의 글꼴 크기를 의미
  • em으로 여백 크기(padding과 margin)를 정할 때는 자기 자신의 글자 크기를 기준으로 함

💡 단위

- 뷰포트 단위 vw vh vmin vmax

em과 rem: 반응하진 않음

주변 상황에 따라 그 크기를 달리할 수 있는 가변성을 지니고 있지만

but, 브라우저나 기기 화면에 크기에 따라 크기가 달라지는 단위는 아니다.

font-size: 1vw; /* 뷰포트 너비의 100분의 1 */
font-size: 1vh; /* 뷰포트 높이 100분의 1 */

/* 뷰포트 높이와 너비 중 작은 쪽의 100분의 1 */
font-size: 1vmin; 

/* 뷰포트 높이와 너비중 큰 쪽의 100분의 1 */
font-zise: 1vmax;
  • *_vw 앞 숫자는 화면 너비의 백분율
    *_1vh: 뷰포트 너비의 1/100
    50vh: 뷰포트 너비의 1/2
  • vh 앞 숫자는 화면 높이의 백분율
  • _vmin 앞 숫자는 뷰포트 너비와 높이중에 더 작은 것에 기준
    *
    (\_ 너비가 높이보다 작으면 너비 기준으로 백분율 적용)
  • _vmax 앞 숫자는 뷰포트 너비와 높이중에 더 큰 것에 기준
    *
    (\_ 너비가 높이보다 크면 너비 기준으로 백분율 적용)

💡 가변 그리드

비율에 기반한 크기 조절

em을 이용해 상대적인 크기의 폰트를 적용할 때처럼, 레이아웃에도 비율에 기반한 개념을 적용할 수 있다.

부모 요소의 크기에 비례해 자식 요소의 크기가 변하는 방식은 가변 레이아웃을 만들 때 흔히 사용되는 방식이다.

이럴 때 사용 되는 방식은 바로 %

% 단위는 백분율 값을 나타내고 보통 부모 요소와의 상대적 크기를 지정할 때 사용한다.

너비와 높이, 여백 뿐 아니라 글자 크기에도 사용할 수 있다.

/* 부모 요소의 글자 크기가 100% */
font-size: 50%;

/* 부모 요소의 높이가 100% */
font-size: 50%;

/* 부모 요소의 너비가 100% */
width: 50%;
margin: 10%;
padding: 10%;

고정 레이아웃

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>가변 레이아웃</title>
  <style>
    .container{
      width:960px;
      margin:0 auto;
      text-align: center;
    }
    .header{
      width:960px;
      height:100px;
      background-color:tomato;
    }
    .main{
      float:left;
      width:640px;
      height:300px;
      background-color:orange;
    }
    .aside{
      float:right;
      width:320px;
      height:300px;
      background-color:purple;
    }
    .footer{
      clear:both;
      width:960px;
      height:100px;
      background-color:violet;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class ="header">HEADER</div>
    <div class ="main">MAIN</div>
    <div class ="aside">ASIDE</div>
    <div class ="footer">FOOTER</div>
</body>
</html>

 

HEADER
MAIN
ASIDE
FOOTER

 

가변 레이아웃

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>가변 레이아웃</title>
  <style>
    *{box-sizing: border-box;}
    .container{
      width:90%;
      margin:0 auto;
      text-align: center;
    }
    .header{
      width:100%;
      height:100px;
      background-color:tomato;
    }
    .main{
      float:left;
      width:67%;
      height:300px;
      padding:10%;
      background-color:orange;
    }
    .aside{
      float:right;
      width:33%;
      height:300px;
      padding:10%;
      background-color:purple;
    }
    .footer{
      clear:both;
      width:100%;
      height:100px;
      background-color:violet;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class ="header">HEADER</div>
    <div class ="main">MAIN</div>
    <div class ="aside">ASIDE</div>
    <div class ="footer">FOOTER</div>
</body>
</html>

 

HEADER
MAIN
ASIDE
FOOTER

 

  • 부모 요소인 .container 의 width 값을 전체의 90%로 잡음
  • 자식 요소들의 width %값은 .container의 기준 값(전체의 90%)의 백분율로 적용된다.
  • padding값도 마찬가지로 부모 요소의 width값 기준이다.
  • 이렇게 백분율로 가변 그리드를 설정하면 width값이 변해도 화면에 가려지지 않는 반응형 그리드가 된다.

💡 calc()

괄호 안 계산식의 결과를 속성값으로 지정하는 css 함수이다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>calc 함수</title>
  <style>
    .message{
      display:flex;
      justify-content: space-around;
      align-items: center;
      height: 100px;
      padding: 0.5em;
      border: 1px solid #000;
      font-size: 1.5em;
    }
    .message_text{
      width: calc(100% -100px);
      height:100%;
      border:none;
      resize:none;
    }

    .message_text:focus{outline : none;}

    .message_send{
      width: 100px;
      height: 100%;
      border-radius: 8px;
      background-color: orange;
    }
  </style>
</head>
<body>
  <div class="message">
    <textarea class="message_text"></textarea>
    <button class="message_send">전송</button>
  </div>

</body>
</html>
  • 버튼 width를 100px로 고정
  • 여백 없이 나머지 공간을 다 message_text로 채우고 싶음
    → calc(100% - 100px) 하면 됨 ⇒ 전체에서 100px 뺀 만큼을 차지

💡 미디어쿼리

미디어 타입을 인식하고, 콘텐츠를 읽어들이는 기기나 브라우저의 물리적 속성을 감지할 수 있는 유용한 기능이다.

모든 미디어 쿼리는 두 가지 구성요소를 가지고 있다.

  • 미디어 타입
  • 조건에 대한 물음(쿼리)

미디어 쿼리 예시

@media screen and (max-width: 768px) {
  /* 
  화면(screen)의
  너비가 768px 이하일 경우에 
  추가 적용할 것이다
  */
 }
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>미디어 쿼리</title>
  <style>
    img{
      width:200px;
      height:200px;
    }

    @media screen and (min-width: 800px){
      img{
        width:400px;
        height:400px;
      }
    @media screen and (min-width: 1200px){
      img{
        width:800px;
        height:800px;
      }
    }
  </style>
</head>
<body>
  <img src="https://i.namu.wiki/i/s4NuODr1MXsDlvf75y46xdgip_prwu9qU_gxUJdvBQGOhkf6D0vGaMQQh4ukCc_fvni5YKNzZyLEsOcAyUlAXg.svg">

</body>
</html>
  1. 첫 번째 미디어 쿼리 (min-width:800px) 800px 이상일 경우 적용
  2. 두 번째 미디어 쿼리(min-width:1200px) 1200px 이상일 경우 적용

💡 미디어 쿼리 적용의 다른 형태들

link 태그에 미디어 쿼리 추가

<link rel="stylesheet" href="style.css" 
media="screen and (max-width:768px)"

@import 구문을 이용한 추가

@import url("style.css") screen and (max-width:768px);

💡 가변이미지

이미지에 상대 단위를 적용할 때 주의사항

  1. 브라우저가 대개 이미지의 비율을 유지하려함
  2. 이미지가 무작정 커지는 것을 경계해야 함

max-width

CSS의 max-width 속성은 너비의 최대값을 지정하여 요소의 너비가 그 이상 커지지 않게 한다.

img{max-width: 200px;}
  <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>가변이미지</title>
  <style>
    div{
      width: 50%
    }
    img{
      max-width: 100%;
    }
  </style>
</head>
<body>
  <div>
    <img src="https://i.namu.wiki/i/s4NuODr1MXsDlvf75y46xdgip_prwu9qU_gxUJdvBQGOhkf6D0vGaMQQh4ukCc_fvni5YKNzZyLEsOcAyUlAXg.svg">
  </div>
</body>
</html>

picture

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>가변이미지</title>
  <style>
    div{
      width: 50%
    }
    img{
      max-width: 100%;
    }
  </style>
</head>
<body>
  <picture>
    <source srcset="https://www.kuaa.or.kr/include/images_user/ci_01.png" media ="(min-width:800px)">
    <img src="https://i.namu.wiki/i/s4NuODr1MXsDlvf75y46xdgip_prwu9qU_gxUJdvBQGOhkf6D0vGaMQQh4ukCc_fvni5YKNzZyLEsOcAyUlAXg.svg">
  </picture>
</body>
</html>
  • width가 800px 미만 일때는 img를 보여주고, 800px 이상이 될 때는 source 값을 보여준다.
  • 이처럼 추가로 보여주는 이미지는 source태그에 넣어주면 된다.
  • srcset에 이미지 주소, media 부분에 조건을 기입

💡 가변 동영상

동영상 가변처리

mp4와 같은 일반 동영상 파일 뿐 아니라

유튜브등에서 공유한 동영상 콘텐츠를 사용하는 경우도 있기 때문에 여러가지를 고려해서 처리해야한다.

동영상 소스를 가지고 있을 때

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>가변동영상</title>
    <style>
        video{
            width:100%;
        }
    </style>
</head>
<body>
    <video src="./videos/my-cat.mp4" controls></video>
</body>
</html>

*동영상 서비스로부터 동영상을 공유 받았을 때
*

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>가변동영상</title>
    <style>
        .player{
            padding-top:56.25%;
            position: relative;
        }
        iframe{
            position: absolute;
            top: 0; left:0;
            width:100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <!-- <video src="./videos/my-cat.mp4" controls></video> -->
     <div class="player">
<iframe width="560" height="315" src="https://www.youtube.com/embed/FbJcUhslINM?si=GoY0YMi_WJQ0S_x1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
     </div>

</body>
</html>

실제로 아래의 영상은 위의 코드로 띄운 영상이다.
화면의 크기를 줄였다 늘렸다하면 아래 영상도 변하는 화면의 크게에 맞게 줄어들고 늘어난다.

 

  • div태그로 영상을 묶어놓는다.
  • 여백을 주고 그 여백안에 영상을 고정시켜서 영역의 크기로부터 너비나 높이를 직접적으로 영향받지 않게한다.
  • 여백의 크기 == iframe 소스의 종횡비 : 0.5625
  • padding-top 값을 56.25% 로 설정

위치고정을 위해선 position을 해줘야함

  • 부모의 position을 relative로
  • 자식인 iframe의 position을 absolute
    ⇒ player(부모)를 기준으로 iframe(자식)이 절대값을 지정할 수 있게됨

position

  • relative는 내 위치를 기준으로 약간 움직이고,
  • absolute는 부모의 위치를 기준으로 정확히 박제한다.

⭐️ 결론

padding-top을 이용해 여백의 값을 종횡비로 맞춘 결과

일반적인 비율 값으로는 맞출 수 없던 영상의 비율을 조정할 수 있게 되었다.


💡 모듈화 디자인

Make Component


반응형 웹 개발의 대세는 ‘페이지 만들기’ → ‘컴포넌트 만들기’

웹 개발에 있어 컴포넌트(component)란 독립적이고 재사용이 가능한 모듈을 뜻한다.

컴포넌트 기반으로 웹을 제작하면 마치 블록 장난감을 조립하듯 각각의 컴포넌트 간 조합을 이용해 화면을 구성할 수 있다.

모듈화된 디자인은 작업 효율에 많은 도움이 됨


여러 개의 페이지는 각각의 다양한 레이아웃을 가지지만, 페이지를 구성하는 컴포넌트의 형태는 비슷한 경우가 많기 때문에 컴포넌트에 초점을 맞추어 웹을 만들면 일이 줄어들게 된다.

모듈화가 주는 이점


  • 반응형 컴포넌트를 만들고, 그것들을 조립해 하나의 페이지를 만들면 페이지는 자연스럽게 반응형 페이지가 된다.
  • 스타일 재사용이 용이함 필요한 경우 추가 스타일을 적용하면 된다.
  • 페이지의 일관성을 유지하기가 용이하다.

여기까지가 정리

이 개념들을 다시 내 프로젝트에 적용해서 수정하는 작업을 시작해야겠다.

방학동안 놀러다니기 바쁘고 한게 없는데, 남은 약 보름의 기간동안 수정을 완료하는 것이 목적이다.

 

 

'프론트엔드' 카테고리의 다른 글

내 손으로 만든 Toss Frontend Fundamentals 코치  (2) 2026.05.25
XHR부터 Ky까지 HTTP 클라이언트 라이브러리 파헤치기  (2) 2026.04.24
[Web Storage] localStorage 파헤치기  (1) 2026.04.14
[Web API] Intersection Observer API  (0) 2026.04.11
Network 탭으로 읽는 HTTP  (0) 2026.03.30
'프론트엔드' 카테고리의 다른 글
  • XHR부터 Ky까지 HTTP 클라이언트 라이브러리 파헤치기
  • [Web Storage] localStorage 파헤치기
  • [Web API] Intersection Observer API
  • Network 탭으로 읽는 HTTP
yun_cic
yun_cic
  • yun_cic
    체대생의 개발 기록
    yun_cic
  • 전체
    오늘
    어제
    • 분류 전체보기 (43) N
      • 백엔드 (1)
      • 프로젝트 (5)
      • etc (6)
      • 대외활동 (1)
      • 강의자료 (5)
      • 프론트엔드 (13)
        • Language (4)
        • Library (2)
      • 우테코 (12) N
        • 회고 (6) N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
    • 포트폴리오 페이지
  • 공지사항

  • 인기 글

  • 태그

    fastapi
    우테코 8기
    Python
    채널톡
    todo
    해커톤
    외주
    백엔드
    Selenium
    크몽
    Crawling
    KUCC
    bs4
    fe
    MySQL
    크롤링
    메모
    개발자 #코딩 #체대생
    우아한테크코스 8기
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
yun_cic
반응형 웹
상단으로

티스토리툴바