Skip to main content

Command Palette

Search for a command to run...

[css] 변환 행렬 - 후편

Published
2 min readView as Markdown
[css] 변환 행렬 - 후편

이번 편은 이전 편으로부터 이어집니다.

전편에서는 transform의 원리와 주요 변환 행렬에 대해 소개했습니다. 이번 편에서는 이를 사용하면서 흔히 범할 수 있는 오류에 대해 다룹니다.

좌측에서 우측으로? 우측에서 좌측으로?

transform: rotate(90deg) skewX(45deg);

어떻게 동작하실 것 같은가요? \(90^\circ\)를 뒤집었으니 사실상 회전하지 않은 거나 다름없을 테고 거기다 $x$축을 따라 \(45^\circ\)를 기울였다? 이건 앞에 rotate를 빼나 넣으나 똑같을 것 같네요. 맞나요? 아니죠. 왜 그럴까요? 이미 예상하셨겠지만, 순서가 반대라서 그렇습니다. 실제로는 기울인 다음에 회전하게 됩니다. 수식을 확인하면 확실하게 이해하실 겁니다.

$$\begin{aligned} \begin{pmatrix} x' \\ y' \\ 1 \end{pmatrix} = \begin{pmatrix} \cos90^\circ & -\sin90^\circ & 0 \\ \sin90^\circ & \cos90^\circ & 0 \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} 1 & \tan45^\circ & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \end{pmatrix} \end{aligned}$$

수식 상에서 행렬의 배치는 좌측에서 우측으로 갑니다. 하지만 변환 순서는 어떻게 될지 한번 생각해 봅시다. 좌표가 우측에 있으니 자연히 좌표와 더 가깝게 붙어있는 우측부터 계산하게 되겠죠? 우측에서 좌측으로 즉, 위의 경우엔 기울인 다음에 회전하게 되는 것입니다.

변환은 요소에 대해 적용되는 개념이 아니다.

transform: scale(2) translate(50px, 50px);

위 코드는 어떻게 동작할까요? 이제 순서에 대해서는 이해하고 계실 것입니다. $x$축과 $y$축에 대해 각각 50 픽셀을 이동하고 크기를 2배로 확대하겠네요. 이제 얼마나 이동했을까요? $x$축과 $y$축에 대해 각각 50픽셀만큼 이동했을까요? 그러나 실제로는 100 픽셀 이동한 것을 알 수 있습니다. 왜 그럴까요? 우리가 원한 것은 50픽셀만큼 이동하는 것이었는데 말입니다.

원점과 동일한 좌표를 갖는 임의의 점 $P(0, 0)$이 있다고 생각해 보겠습니다. 이에 대해 위와 동일한 translate 함수를 적용해 봅시다. 좌표는 $(50, 50)$으로 이동할 것입니다. 글쎄요. 물론 그렇긴 하지만 보다 정확히 말하자면 벡터의 변환으로 보는 것이 적절합니다. 그렇다면 이 벡터는 각 축에 대해 0이었다가 50으로 증가했다는 의미가 되겠네요. 여기에 위의 scale 함수를 적용하면 어떻게 될까요? 벡터를 2배로 잡아 늘렸으니 자연히 각 축에 대해 100을 취하게 될 것입니다.

이는 모든 변환에 대해 마찬가지입니다. 모두 벡터를 변환하는 것일 뿐이고, 요소 자체에 적용되는 개념이 아닙니다. 그렇다면 translate를 통해 정확히 원하는 만큼 이동하려면 어떻게 해야 할까요? 간단합니다. translate가 가장 마지막에 적용되도록 항상 좌측에 위치하도록 하면 됩니다.

transform: translate(tx, ty) ...;

읽어주셔서 감사합니다!

묻고 답하기

개인적인 판단에 의해 적절하다고 여겨지는 경우, 모두가 볼 수 있도록 이곳에 문답이 추가됩니다. 그렇지 않더라도 질문에 대한 답변은 별도로 이루어집니다.

More from this blog

[TypeScript] 자료 구조로 담아내기. #14 - 연결 리스트(with. 이중 연결)

이번 편은 이전 편으로부터 이어집니다. 연결 리스트의 또 다른 형태는 이중 연결 리스트입니다. 후임자의 참조만 저장하는 단일 연결 리스트와 달리 이중 연결 리스트는 선임자의 참조도 함께 저장합니다. interface DoublyLinkedListNode<T> extends DoublyLinkedList<T> { linkBefore(succ: DoublyLinkedListNode<T>): void; linkAfter(pred: D...

May 3, 20253 min read9
[TypeScript] 자료 구조로 담아내기. #14 - 연결 리스트(with. 이중 연결)

[TypeScript] 자료 구조로 담아내기. #12 - 연결 리스트(with. null 노드)

이번 편은 이전 편으로부터 이어집니다. 조금 예리하신 분들은 이전 편에서 다룬 헤드 노드만으로는 충분치 않다는 것을 느끼셨을 것입니다. 최소한 첫 노드에 예외를 부여하는 상황은 피했으나 후임자가 null임을 확인하는 구현 또한 만족스럽지는 않습니다. 카이로의 코끼리 카이로의 코끼리에 대해 알고 계십니까? 링크를 통해 확인하실 수 있습니다. 꽤 재밌으므로 한번 읽어 보시길 추천합니다. 여기서 중요한 아이디어는 끝 위치에 미리 코끼리를 둔다는 것...

Apr 19, 20252 min read12
[TypeScript] 자료 구조로 담아내기. #12 - 연결 리스트(with. null 노드)

[TypeScript] 자료 구조로 담아내기. #11 - 연결 리스트(with. 헤드 노드)

이번 편은 이전 편으로부터 이어집니다. 이전 편에서는 연결 리스트의 접근자는 첫 노드의 선임자가 적절하다는 것을 알았습니다. 얼핏 보기엔 문제없어 보이는데 어떤가요? 최상위 연결 리스트 즉, 가장 처음에 위치한 노드로 시작하는 경우를 생각해 봅시다. 가장 처음에 위치한 노드의 선임자는 무엇인가요? 맞습니다. 선임자가 존재하지 않겠지요. 그럼 어떻게 해야 할까요? 선임자를 가지지 않는 첫 노드를 갖는 연결 리스트에 대해 예외를 적용해야 할까요?...

Apr 12, 20252 min read13
[TypeScript] 자료 구조로 담아내기. #11 - 연결 리스트(with. 헤드 노드)

고라니드로의 블로그

47 posts