HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📝
프론트엔드 스쿨 교안(1기)
/
📝
CSS
/
📝
16. perspective (원근법)
📝

16. perspective (원근법)

perspective - CSS: Cascading Style Sheets | MDN
The perspective CSS property determines the distance between the z=0 plane and the user in order to give a 3D-positioned element some perspective. Indicates that no perspective transform is to be applied. A giving the distance from the user to the z=0 plane. It is used to apply a perspective transform to the children of the element.
perspective - CSS: Cascading Style Sheets | MDN
https://developer.mozilla.org/en-US/docs/Web/CSS/perspective
perspective - CSS: Cascading Style Sheets | MDN
 
원근법을 이용하면 좀 더 다양한 예제를 실행할 수 있습니다. 우선 예제 하나 보고 시작하도록 하겠습니다.
나와 닮은 연예인 찾기!
당신과 닮은 연예인은 누구일까요? 지금 확인해보세요!
https://weniv.github.io/looklike/%EC%9C%A0%EC%9E%AC%EC%84%9D.html
 
여기서 카드가 저 멀리서 앞으로 튀어나오는 원근감은 perspective 800px이 들어있기 때문입니다.
.cont-card { display: flex; align-items: center; justify-content: center; perspective: 800px; }
 
원근감이 있고 없고의 차이를 실습하도록 하겠습니다.
<!DOCTYPE html> <html lang="ko"> <head> <style> .container { position: relative; width: 500px; height: 500px; margin-top: 500px; margin-left: 500px; } .y { position: absolute; transform: rotate(270deg); top: -300px; left: -250px; width: 500px; } .z { position: absolute; transform: rotate(140deg); top: 78px; left: -352px; width: 400px; } .line { display: flex; gap: 0.5em; align-items: center; margin: 50px 0; } .line::before { content: ' '; flex-grow: 1; height: 3px; background-color: black; } .box-container{ /* 주석을 풀어보세요. */ /* perspective: 500px; */ position: relative; top: -260px; width: 200px; height: 200px; background-color: darkcyan; opacity: 0.3; } .box { position: absolute; top: -20px; left: 0; width: 220px; height: 220px; background-color: black; opacity: 0.7; /* Chrome 개발자 도구 열어서 수치를 증가시켜보세요. */ transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg); } </style> </head> <body> <div class="container"> <div class="line x">line x</div> <div class="line y">line y</div> <div class="line z">line z</div> <div class="box-container"> <div class="box"></div> </div> </div> </body> </html>
 
간단한 실습을 해보도록 하겠습니다.
<!DOCTYPE html> <html lang="ko"> <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>perspective</title> <style> .원본 { width: 200px; height: 200px; border: 1px solid black; margin: 100px auto; perspective: 400px; } .회전패널 { width: 200px; height: 200px; background: aqua; /* 실습 1 */ /* transform: rotate(45deg); */ /* 실습 2 perspective를 준것과 주지 않은것을 비교해보세요. */ /* transform: rotateX(45deg); */ /* 실습 3 perspective를 준것과 주지 않은것을 비교해보세요.*/ /* transform: rotateY(45deg); */ } </style> </head> <body> <div class="원본"> <div class="회전패널"></div> </div> </body> </html>
 
X축과 Y축, Z축 회전은 아래 그림을 참고해주세요.
notion image
 
어때요? 감이 오시나요? perspective는 우리가 대상을 보는 거리입니다. 라이켓이 저 사각형을 보는 거리라고 생각해주세요. 값이 적을수록 더 가까이 보게 되므로, 효과가 더 극적으로 나타나게 됩니다.
 
그런데 아래 코드를 확인해보시면 의아하실 거에요.
<!DOCTYPE html> <html lang="ko"> <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>perspective</title> <style> .원본 { display: flex; justify-content: space-between; width: 1000px; height: 200px; border: 1px solid black; margin: 100px auto; perspective: 400px; } .회전패널 { width: 200px; height: 200px; background: aqua; transform: rotateY(45deg); } </style> </head> <body> <div class="원본"> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> </div> </body> </html>
notion image
 
왜 같은 각도로 움직였는데 똑같은 박스로 나오지 않았을까요? 이는 소실점(vanishing point)을 바라보고 틀어지는 각도가 다르기 때문입니다. 소실점은 중앙이 맞습니다.
아래 코드를 실행해보시면 맨 왼쪽에 있는 카드가 보이지 않는 것을 확인하실 수 있습니다. 완전히 세로로 배치되어 보이지 않게 된 것이죠. 1px이라도 보여야 하는 것 아니냐라고 하실 수 있지만 두깨가 없으므로 보이지 않는 것이 맞습니다.(물리적인 법칙에서 생각하시면 안됩니다.)
<!DOCTYPE html> <html lang="ko"> <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>perspective</title> <style> .원본 { display: flex; width: 1000px; height: 800px; flex-wrap: wrap; border: 1px solid black; margin: 100px auto; perspective: 400px; } .회전패널 { width: 200px; height: 200px; background: aqua; transform: rotateY(45deg); } </style> </head> <body> <div class="원본"> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> </div> </body> </html>
 
여기서 소실점을 한 번 보죠.
notion image
 
여러분은 저 빨간 원이 소실점이라고 생각하실 것입니다. 그러나 소실점은 정 중앙입니다! 정 중앙에서 45도씩 양수로 움직였기 때문에 그렇게 보이는 것이죠. 이는 45도를 -45도로 그리고 마지막으로 90도로 바꿔봄으로 알 수 있습니다.
 
이 값을 조정하고 싶다면 perspective-origin을 사용합니다. 초기 값은 perspective-origin: 50% 50%; 입니다. 아래 값을 .원본 class 안에 perspective-origin 속성으로 설정해보세요.
 
perspective-origin: top; perspective-origin: bottom; perspective-origin: 50% 50%; /* 위와 같은 의미입니다. */ perspective-origin: 500px 400px; /* 요소가 15개일때 정중앙 */ perspective-origin: 10% 50%; /* One-value syntax */ perspective-origin: x-position; /* Two-value syntax */ perspective-origin: x-position y-position;
 
여러개로 실습을 해보면 이해가 잘 안될 수 있으니 한개씩 별도로 테스트해볼 수 있도록 코드를 준비해봤습니다. 여기서 각각 해당 박스를 보고 있는 라이켓을 그려보세요!
 
<!DOCTYPE html> <html lang="ko"> <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>perspective</title> <style> .원본 { display: flex; width: 200px; height: 200px; flex-wrap: wrap; border: 1px solid black; margin: 200px auto; } .원본:nth-child(1) { perspective: 400px; perspective-origin: 0px 0px; } .원본:nth-child(2) { perspective: 400px; perspective-origin: 100px 100px; } .원본:nth-child(3) { perspective: 400px; perspective-origin: 200px 200px; } .원본:nth-child(4) { perspective: 400px; perspective-origin: bottom right; } .회전패널 { display: inline-block; width: 200px; height: 200px; background: aqua; perspective: 400px; transform: rotateY(45deg); } </style> </head> <body> <div class="원본"> <div class="회전패널"></div> </div> <div class="원본"> <div class="회전패널"></div> </div> <div class="원본"> <div class="회전패널"></div> </div> <div class="원본"> <div class="회전패널"></div> </div> </body> </html>
 
자, 이제 처음 예제를 다시 가지고 와봤습니다. 여기서 perspective-origin을 바꿔가며 z값을 증가시켜보세요.
<!DOCTYPE html> <html lang="ko"> <head> <style> .container { position: relative; width: 500px; height: 500px; margin-top: 500px; margin-left: 500px; } .y { position: absolute; transform: rotate(270deg); top: -300px; left: -250px; width: 500px; } .z { position: absolute; transform: rotate(140deg); top: 78px; left: -352px; width: 400px; } .line { display: flex; gap: 0.5em; align-items: center; margin: 50px 0; } .line::before { content: ' '; flex-grow: 1; height: 3px; background-color: black; } .box-container{ /* 주석을 풀어보세요. */ /* perspective: 500px; */ position: relative; top: -260px; width: 200px; height: 200px; background-color: darkcyan; opacity: 0.3; } .box { position: absolute; top: -20px; left: 0; width: 220px; height: 220px; background-color: black; opacity: 0.7; /* Chrome 개발자 도구 열어서 수치를 증가시켜보세요. */ transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg); } </style> </head> <body> <div class="container"> <div class="line x">line x</div> <div class="line y">line y</div> <div class="line z">line z</div> <div class="box-container"> <div class="box"></div> </div> </div> </body> </html>
어려우시죠? 걱정마세요. 실무에서 이런 소실점을 연구하듯 이렇게 살펴보는 일은 없을 것입니다. 제가 가장 많은 힌트를 얻었던 사이트를 첨부합니다.
imjignesh.com
https://imjignesh.com/how-css-perspective-works/
💡
여러분도 이런 사이트를 만들어보는 것은 어떨까요? 앞에서 한 마크업에 골격잡는 사이트도 좋을 것 같습니다.
 
우리는 간단하게 카드를 뒤집도록 만들 것인데요. 좀 더 다양한 예제를 손 수 해보고 싶다면 아래 튜토리얼을 추천합니다. 코드만 살펴보는 것으로도 큰 도움이 되실 것입니다.
  • transform-style: preserve-3d는 자식요소를 3d 공간처럼 처리합니다. transform-style: flat을 주면 평평하게 보이게 하고요. flat으로 바꾼뒤 뒤집어보세요.
Intro to CSS 3D transforms
We now have all the tools to start making 3D objects. Let's get started with the basics, flipping a card. Here's the basic markup we'll need: The .scene will house the 3D space. The .card acts as the 3D object. Two separate .card__face elements are used for the faces of the card.
https://3dtransforms.desandro.com/card-flip
Intro to CSS 3D transforms
 
우리는 좀 더 쉬운 예제로 살펴보도록 하겠습니다.
<!DOCTYPE html> <html lang="ko"> <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>card</title> <style> /* index.html */ html, body { width: 100%; height: 100%; } .cont-card { display: flex; align-items: center; justify-content: center; perspective: 800px; } .item-card { /* rotateY(720deg);, rotateX(720deg);, rotateZ(720deg); 모두 실습해보세요. */ transform: scale(0.1) rotateY(720deg); width: 500px; height: 500px; background:#F2994A; transition: all 1.2s; } .item-card:hover { width: 500px; height: 500px; background-color: aqua; transform: scale(1) rotateY(0deg); } </style> </head> <body class="cont-card"> <div class="item-card"> <h1>hello world</h1> </div> </body> </html>