---
title: "쿼터니언을 몰라도 쓰는 법"
description: "쿼터니언은 \"축 하나와 그 축으로 돈 각도\" 를 네 숫자에 담은 회전입니다. 네 숫자를 직접 건드리지 않고, 함수로 만들고, 곱해서 합치고, q * v 로 벡터를 돌립니다. 곱하는 순서가 월드 기준과 자기 기준을 가릅니다."
author: "유니얼"
pubDate: 2026-11-05
category: "game-dev/math"
tags: ["Unity", "기초"]
canonical: https://blog.unialgames.com/posts/math-quaternion/
---
Inspector 의 Rotation 에는 (0, 90, 0) 처럼 각도 셋이 보이는데, 코드에서 `transform.rotation.y` 를 읽으면 0.71 같은 숫자가 나옵니다.
`transform.rotation` 은 각도 셋이 아니라 **쿼터니언**이라는 네 숫자 (x, y, z, w) 이고, 회전 하나를 **"어느 축으로, 몇 도"** 로 나타냅니다.
네 숫자의 뜻을 몰라도 됩니다. **만들고, 곱하고, 벡터에 곱하는** 세 가지만 알면 포탑 조준, 부채꼴 사격, 자기 기준 회전이 대부분 풀립니다.

> **쿼터니언은 `Quaternion.Euler` · `AngleAxis` · `LookRotation` 같은 함수로 만듭니다. 네 숫자를 직접 넣지 않습니다.
> `q * v` 는 벡터 v 를 q 만큼 돌린 벡터입니다.
> `a * b` 는 두 회전을 합친 것이고, 순서를 바꾸면 결과가 다릅니다. `회전 * q` 는 자기 축 기준, `q * 회전` 은 월드 축 기준입니다.**

## 쿼터니언은 축과 각도다

어떤 자세(물체가 어느 쪽을 보고 어느 쪽이 위인지)든, 처음 자세에서 **축 하나를 잡고 그 축으로 한 번 돌리면** 만들 수 있습니다. 여기서 축은 팽이의 심처럼 돌 때 움직이지 않는 막대입니다.
의자에 앉아 오른쪽으로 90° 돌아앉은 자세는 "위아래 축(y)으로 90°" 한 번이고, 비스듬히 기울어진 자세도 비스듬한 축 하나로 한 번 돌린 것입니다.

쿼터니언은 그 축과 각도를 네 숫자에 담습니다. 이름도 넷(quater)에서 왔습니다. 이때 각도를 그대로 넣지 않고 **각도의 절반(반각)** 을 씁니다. 90° 면 45° 입니다.

- x, y, z 자리: 축 방향에 sin(반각)을 곱한 값
- w 자리: cos(반각)

sin 과 cos 는 길이 1 인 막대를 그 각도로 세웠을 때의 높이와 바닥 길이입니다. 45° 면 둘 다 0.71 입니다.
그래서 y 축 (0, 1, 0) 으로 90° 돈 회전은 x 0, y 1 × 0.71, z 0, w 0.71 로 (0, 0.71, 0, 0.71) 이 됩니다.

| 회전 | 축 | 각도 | (x, y, z, w) |
| --- | --- | --- | --- |
| 안 돌림 | 아무거나 | 0° | (0, 0, 0, **1**) |
| 좌우로 90° | y (0, 1, 0) | 90° | (0, **0.71**, 0, 0.71) |
| 좌우로 180° | y (0, 1, 0) | 180° | (0, **1**, 0, 0) |
| 앞으로 90° 숙이기 | x (1, 0, 0) | 90° | (**0.71**, 0, 0, 0.71) |

`rotation.y` 가 0.71 이었던 이유가 이 표에 있습니다. y 축으로 90° 돈 회전의 y 자리는 sin 45° = 0.71 입니다.
**각도가 아니라 반각의 sin 을 축에 곱한 값**이라서, 이 숫자를 각도처럼 읽거나 고치면 안 됩니다.
왜 절반인지는 네 숫자끼리 곱하는 규칙이 그렇게 맞춰져 있어서인데, 쓰는 사람이 이 계산을 할 일은 없습니다. 이 절에서 가져갈 것은 **이 숫자는 각도가 아니다** 하나입니다.

Inspector 의 Rotation 칸처럼 회전을 "z 로 몇 도, x 로 몇 도, y 로 몇 도" 세 숫자로 적는 방법을 **오일러 각**이라 합니다. 오일러 각은 세 축으로 차례로 돌기 때문에, 가운데 x 를 90° 숙이면 첫 번째 z 회전과 마지막 y 회전이 같은 쪽으로 돌아 회전 방향 하나를 잃습니다. 이것이 짐벌 락입니다.
쿼터니언은 축 하나로 한 번에 도는 회전 하나라서, 서로 겹쳐질 다른 축이 없습니다. 두 회전을 이어도 곱한 결과가 다시 축 하나 각도 하나인 회전 하나가 됩니다.

| | 오일러 각 | 쿼터니언 |
| --- | --- | --- |
| 숫자 | 셋. 도 단위라 사람이 읽기 쉽습니다 | 넷. 사람이 읽고 고치기 어렵습니다 |
| 짐벌 락 | x 가 ±90 일 때 생깁니다 | 없습니다 |
| 같은 자세를 쓰는 숫자 | 끝없이 많습니다 | 둘뿐입니다 (q 와 −q, 아래 「하지 말 것」) |
| 두 자세 사이 잇기 | 숫자로 섞으면 먼 길로 돌 수 있습니다 | `Slerp` 가 짧은 길로 잇습니다 |
| 한 바퀴 넘게 감기 | 720° 처럼 적을 수 있습니다 | 자세만 담아서 두 바퀴와 0 바퀴가 같습니다 |

그래서 유니티는 회전을 쿼터니언으로 저장하고, Inspector 에서만 오일러 각으로 보여 줍니다.

네 숫자는 늘 x² + y² + z² + w² = 1 을 지킵니다. `new Quaternion(0, 90, 0, 0)` 처럼 각도를 넣으면 이 약속이 깨진 엉뚱한 값이 되고, y 로 90° 도는 회전도 아닙니다.

## 만들 때는 함수를 쓴다

| 함수 | 만드는 회전 | 예 |
| --- | --- | --- |
| `Quaternion.identity` | 안 돌림 | 처음 자세 |
| `Quaternion.Euler(x, y, z)` | 오일러 각(도) | `Quaternion.Euler(0, 90, 0)` |
| `Quaternion.AngleAxis(각도, 축)` | 축으로 각도만큼 | `Quaternion.AngleAxis(30, Vector3.up)` |
| `Quaternion.LookRotation(앞, 위)` | 앞이 그 방향을 보게 | 적을 바라보는 포탑 |
| `Quaternion.FromToRotation(a, b)` | a 방향을 b 방향으로 돌리는 회전 | 발을 경사면 법선(면에 수직으로 선 방향)에 맞추기 |

포탑이 적을 바라보게 하는 코드입니다.

```csharp
Vector3 toEnemy = enemy.position - turret.position;   // 도착 − 출발
toEnemy.y = 0f;                                        // 위아래로는 숙이지 않는다

// 앞이 toEnemy 를 보고, 위가 월드 위를 보는 회전
turret.rotation = Quaternion.LookRotation(toEnemy, Vector3.up);
```

`LookRotation` 은 방향의 길이를 따지지 않으니 정규화하지 않아도 됩니다. 다만 방향이 (0, 0, 0) 이면 바라볼 곳이 없어 회전을 만들 수 없으니, 적과 겹쳤을 때는 부르지 않습니다.

## q * v 는 벡터를 돌린다

쿼터니언에 벡터를 곱하면 **그 벡터를 그만큼 돌린 벡터**가 나옵니다. 순서는 늘 `회전 * 벡터` 이고, `벡터 * 회전` 은 컴파일되지 않습니다.

| 코드 | 결과 |
| --- | --- |
| `Quaternion.Euler(0, 90, 0) * Vector3.forward` | (1, 0, 0). 앞을 오른쪽으로 90° |
| `Quaternion.Euler(0, 180, 0) * Vector3.forward` | (0, 0, −1). 뒤 |
| `transform.rotation * Vector3.forward` | `transform.forward` 와 같음 |

`transform.forward` 는 원래 이 곱으로 만든 값입니다. 오브젝트의 회전으로 월드 앞 (0, 0, 1) 을 돌린 것이 오브젝트의 앞입니다.

총알을 부채꼴로 퍼뜨리는 것도 이 곱 한 번입니다.

```csharp
// 정면을 기준으로 -30, -15, 0, 15, 30 도
for (int i = -2; i <= 2; i++)
{
    Quaternion spread = Quaternion.AngleAxis(i * 15f, Vector3.up);
    Vector3 dir = spread * transform.forward;

    Fire(dir);
}
```

## 합치기는 곱이고, 순서가 중요하다

두 회전을 이어서 하고 싶으면 쿼터니언끼리 곱합니다. `a * b` 를 벡터에 곱하면 **b 를 먼저, a 를 나중에** 한 것과 같습니다.
그리고 회전은 **순서를 바꾸면 결과가 다릅니다.** 아래는 y 로 90° 와 x 로 90° 를 순서만 바꾼 두 경우입니다. 둘 다 **월드 축**, 곧 장면 전체에 고정된 x(오른쪽) · y(위) · z(앞)를 기준으로 돕니다.
파란 화살표가 오브젝트의 앞, 청록 화살표가 오브젝트의 위입니다.

<x-vec>
<script type="application/json">
{
  "label": "y 90° 와 x 90° 를 순서만 바꿔서",
  "space": "3d",
  "grid": 1,
  "view": [-2, -1.7, 2.2, 1.9],
  "dwell": 2.2,
  "cases": [
    {
      "name": "y 먼저, x 나중",
      "steps": [
        { "caption": "처음 자세. 앞은 z, 위는 y.", "code": "Quaternion.identity", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [0, 0, 1], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0, 1, 0], "rigid": true, "color": "accent2", "label": "위" } ] },
        { "caption": "월드 y 로 45°.", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [0.71, 0, 0.71], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0, 1, 0], "rigid": true, "color": "accent2", "label": "위" } ] },
        { "caption": "월드 y 로 90°. 앞이 오른쪽(x)을 봅니다.", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [1, 0, 0], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0, 1, 0], "rigid": true, "color": "accent2", "label": "위" } ] },
        { "caption": "이제 월드 x 로 45°. 앞은 x 축 위에 있어서 그대로이고, 위만 앞쪽으로 기웁니다.", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [1, 0, 0], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0, 0.71, 0.71], "rigid": true, "color": "accent2", "label": "위" } ] },
        { "caption": "월드 x 로 90°. 앞은 오른쪽, 위는 z 쪽으로 누웠습니다. 옆으로 누워 오른쪽을 보는 자세입니다.", "code": "Quaternion.Euler(90, 0, 0) * Quaternion.Euler(0, 90, 0)", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [1, 0, 0], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0, 0, 1], "rigid": true, "color": "accent2", "label": "위" } ] }
      ]
    },
    {
      "name": "x 먼저, y 나중",
      "steps": [
        { "caption": "같은 처음 자세.", "code": "Quaternion.identity", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [0, 0, 1], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0, 1, 0], "rigid": true, "color": "accent2", "label": "위" } ] },
        { "caption": "월드 x 로 45°. 앞이 아래로 숙여집니다.", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [0, -0.71, 0.71], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0, 0.71, 0.71], "rigid": true, "color": "accent2", "label": "위" } ] },
        { "caption": "월드 x 로 90°. 앞이 바로 아래를 봅니다.", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [0, -1, 0], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0, 0, 1], "rigid": true, "color": "accent2", "label": "위" } ] },
        { "caption": "이제 월드 y 로 45°. 앞은 y 축 위에 있어서 그대로이고, 위만 바닥에서 돕니다.", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [0, -1, 0], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [0.71, 0, 0.71], "rigid": true, "color": "accent2", "label": "위" } ] },
        { "caption": "월드 y 로 90°. 앞은 아래, 위는 오른쪽. 같은 두 회전인데 순서만 바꿔서 완전히 다른 자세가 됐습니다.", "code": "Quaternion.Euler(0, 90, 0) * Quaternion.Euler(90, 0, 0)", "items": [
          { "id": "f", "from": [0, 0, 0], "to": [0, -1, 0], "rigid": true, "label": "앞" },
          { "id": "u", "from": [0, 0, 0], "to": [1, 0, 0], "rigid": true, "color": "accent2", "label": "위" } ] }
      ]
    }
  ]
}
</script>
</x-vec>

코드를 보면 **먼저 한 회전이 오른쪽**에 있습니다. `Qx * Qy` 가 "y 먼저, x 나중" 입니다.
`(Qx * Qy) * v` 는 `Qx * (Qy * v)` 와 같기 때문입니다. 괄호 안의 `Qy` 가 먼저 v 를 돌리고, 그 결과를 `Qx` 가 다시 돌립니다. 벡터와 가까운 쪽부터 적용되는 셈입니다.

이 순서가 실제로 쓰이는 곳은 **지금 회전에 조금 더 돌릴 때**입니다.
이때 월드 축과 짝이 되는 말이 **자기 축**입니다. 물체에 붙어 함께 도는 축, 곧 물체의 오른쪽 · 위 · 앞입니다. 오른쪽으로 돌아선 오브젝트의 앞은 월드 x 쪽을 가리킵니다.

| 코드 | 어느 축 기준으로 더 도나 |
| --- | --- |
| `transform.rotation = transform.rotation * q` | **자기 축.** 내 오른쪽 축으로 숙이기, 내 위 축으로 돌기 |
| `transform.rotation = q * transform.rotation` | **월드 축.** 내가 어떻게 기울었든 월드 위 축으로 돌기 |

왜 오른쪽에 붙이면 자기 축이 되는지는 바로 위의 규칙으로 보입니다.
`transform.rotation * q` 는 **q 가 먼저** 돕니다. q 는 아직 처음 자세, 곧 오브젝트의 축이 월드 축과 똑같을 때 돕니다.
그다음 지금 회전이 오브젝트를 현재 자세로 옮기면서 q 가 돌던 축도 함께 데려갑니다. 그래서 결과적으로 **오브젝트 자기 축으로 돈 것**이 됩니다.

| 단계 | y 로 90° 돌아 오른쪽을 보는 오브젝트에 `rotation * Euler(30, 0, 0)` |
| --- | --- |
| 1. q 먼저 | 처음 자세에서 월드 x 로 30° 숙임 |
| 2. 지금 회전 | 숙인 채로 y 로 90° 돌아 오른쪽을 봄 |
| 결과 | 오브젝트의 오른쪽 축(월드 −z)으로 30° 숙인 자세 |

반대로 `q * transform.rotation` 은 지금 자세가 다 된 **뒤에** q 를 하므로 월드 축 그대로 돕니다.
비행기가 자기 날개 축으로 기수를 들게 하려면 앞의 것, 기울어진 오브젝트를 세상 위쪽을 기준으로 빙 돌리려면 뒤의 것입니다.

유니티 문서는 `a * b` 를 "a 를 먼저 하고, a 가 만든 자기 축을 기준으로 b 를 한다" 고 설명합니다. 같은 결과를 자기 축 쪽에서 읽은 말이라, 위의 표와 어긋나지 않습니다.
`transform.Rotate(x, y, z)` 는 기본이 자기 축이라 앞의 것과 같고, `Space.World` 를 넘기면 뒤의 것과 같습니다.

## 두 회전 사이를 잇는다

포탑이 적을 향해 **천천히** 돌게 하려면 지금 회전에서 목표 회전으로 조금씩 가야 합니다.
여기서 쓰는 말이 **t 비율**입니다. a 에서 b 까지 가는 길 중 얼마나 왔는지를 0 ~ 1 로 적은 것입니다. 0 이면 a, 1 이면 b, 0.5 면 한가운데입니다.
숫자 0 에서 10 까지면 t 0.5 는 5, t 0.25 는 2.5 입니다. 이렇게 섞는 함수가 `Mathf.Lerp(a, b, t)` 입니다.

회전을 각도 숫자로 섞으면 문제가 생깁니다. y 각도 350° 에서 10° 로 돌고 싶을 때, 숫자로는 340 이나 차이 나지만 실제로는 360 을 넘어가는 쪽으로 20° 만 돌면 됩니다.
아래 그림은 위에서 내려다본 모습입니다.

<x-vec>
<script type="application/json">
{
  "label": "350° 에서 10° 로",
  "view": [-1.6, -1.6, 1.6, 1.6],
  "dwell": 1.8,
  "cases": [
    {
      "name": "각도 숫자로 Mathf.Lerp",
      "steps": [
        { "caption": "t = 0. 350°, 앞에서 왼쪽으로 조금.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [-0.17, 0.98], "rigid": true, "label": "350°", "labelAt": [-0.45, 1.3] } ] },
        { "caption": "t = 0.25. 265°. 숫자 차이 340 의 4분의 1 인 85 만큼 줄어서 왼쪽으로 크게 돌았습니다.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [-1, -0.09], "rigid": true, "color": "warn", "label": "265°", "labelAt": [-1.3, 0.25] } ] },
        { "caption": "t = 0.5. 180°. 뒤를 봅니다.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [0, -1], "rigid": true, "color": "warn", "label": "180°", "labelAt": [0.4, -1.3] } ] },
        { "caption": "t = 0.75. 95°. 오른쪽.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [1, -0.09], "rigid": true, "color": "warn", "label": "95°", "labelAt": [1.3, 0.25] } ] },
        { "caption": "t = 1. 10°. 20° 면 될 것을 340° 돌아서 왔습니다.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [0.17, 0.98], "rigid": true, "label": "10°", "labelAt": [0.45, 1.3] } ] }
      ]
    },
    {
      "name": "회전으로 Quaternion.Slerp",
      "steps": [
        { "caption": "t = 0. 350°.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [-0.17, 0.98], "rigid": true, "label": "350°", "labelAt": [-0.45, 1.3] } ] },
        { "caption": "t = 0.25. 355°. 짧은 쪽, 곧 360 을 넘어가는 쪽으로 갑니다.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [-0.09, 1], "rigid": true, "color": "accent2", "label": "355°", "labelAt": [-0.45, 1.3] } ] },
        { "caption": "t = 0.5. 0°, 정면.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [0, 1], "rigid": true, "color": "accent2", "label": "0°", "labelAt": [0.35, 1.3] } ] },
        { "caption": "t = 0.75. 5°.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [0.09, 1], "rigid": true, "color": "accent2", "label": "5°", "labelAt": [0.4, 1.3] } ] },
        { "caption": "t = 1. 10°. 20° 만 돌았습니다.", "items": [ { "id": "c", "type": "circle", "at": [0, 0], "r": 1, "color": "muted" }, { "id": "v", "from": [0, 0], "to": [0.17, 0.98], "rigid": true, "label": "10°", "labelAt": [0.45, 1.3] } ] }
      ]
    }
  ]
}
</script>
</x-vec>

쿼터니언 함수는 숫자를 섞지 않고 **회전 전체를 한 축으로 도는 짧은 길**로 잇기 때문에 이런 일이 없습니다.

| 함수 | 하는 일 | 쓰는 곳 |
| --- | --- | --- |
| `Quaternion.Slerp(a, b, t)` | a 와 b 사이를 t 비율(0 ~ 1)만큼. 가장 짧은 길로 | 시작과 끝, 걸리는 시간이 정해진 연출 |
| `Quaternion.RotateTowards(a, b, 최대 각도)` | a 에서 b 쪽으로 최대 각도만큼만 | 일정한 속도로 따라 돌기 |
| `Quaternion.Angle(a, b)` | 두 회전 사이의 각도(도) | 다 돌았는지 확인 |

```csharp
Quaternion target = Quaternion.LookRotation(toEnemy, Vector3.up);

// 1초에 최대 90도씩 목표 쪽으로
float maxStep = 90f * Time.deltaTime;
turret.rotation = Quaternion.RotateTowards(turret.rotation, target, maxStep);

bool aimed = Quaternion.Angle(turret.rotation, target) < 1f;
```

`Time.deltaTime` 은 화면에 그림 한 장(프레임)을 그리는 데 걸린 초입니다. 90 에 곱하면 "1초에 90도" 가 됩니다.
`Slerp(현재, 목표, 0.1f)` 를 매 프레임 부르는 코드는 매 프레임 남은 각도의 10% 씩 가서, 1초에 그림을 많이 그리는 기계(fps 가 높은 기계)일수록 빨리 돕니다. 일정한 빠르기가 필요하면 `RotateTowards` 를 씁니다.

## 하지 말 것

- **x, y, z, w 를 직접 넣거나 고치지 않습니다.** 각도가 아니라 반각의 sin 과 cos 라서, 고치면 의도와 다른 회전이 되거나 길이 1 약속이 깨집니다
- **`eulerAngles` 를 읽어서 고쳐 다시 넣지 않습니다.** 같은 자세를 쓰는 오일러 각 조합이 여럿이라 읽을 때마다 다른 조합이 나올 수 있습니다. 각도를 직접 들고 있다가 `Quaternion.Euler` 로 새로 만듭니다
- **회전이 같은지 비교할 때는 `Quaternion.Angle(a, b) < 작은 값`** 으로 봅니다. 쿼터니언은 q 와 −q 가 같은 회전이라 네 숫자가 달라도 같은 자세일 수 있습니다.
  y 로 90° 는 (0, 0.71, 0, 0.71) 인데, 360° 를 더 돈 450° 는 반각이 225° 라 (0, −0.71, 0, −0.71) 입니다. 자세는 같고 부호만 모두 뒤집혔습니다
- **`벡터 * 회전` 은 없습니다.** 늘 `회전 * 벡터` 입니다

## 정리

- 쿼터니언은 축 하나와 그 축으로 돈 각도를 (x, y, z, w) 에 담은 회전입니다. 숫자는 반각의 sin 과 cos 라 각도로 읽지 않습니다
- 만들 때는 `Euler`, `AngleAxis`, `LookRotation`, `FromToRotation` 을 씁니다
- `q * v` 는 벡터를 돌립니다. `transform.forward` 가 `rotation * Vector3.forward` 입니다
- `a * b` 는 b 먼저, a 나중입니다. 순서를 바꾸면 다른 자세가 됩니다
- `회전 * q` 는 자기 축 기준, `q * 회전` 은 월드 축 기준으로 더 돌립니다
- 두 회전 사이는 `Slerp`(비율로)와 `RotateTowards`(일정한 각속도로)로 짧은 길을 따라 잇습니다