지금까지 이 블로그에서는 RGB, XYZ, Lab, YCbCr, HSV 다섯 가지 색공간을 차례로 다루었습니다. 글마다 수식과 Python 실습으로 각 색공간을 살펴보았지만, 막상 "이 주황색은 Lab으로 얼마일까", "YCbCr (81, 90, 240)은 무슨 색일까" 같은 궁금증이 생길 때마다 코드를 꺼내 실행하는 것은 번거로운 일입니다.
그래서 값 하나를 바로 바꿔 볼 수 있는 색공간 변환기를 만들었습니다. 입력과 출력 색공간을 고르고 숫자를 넣으면 변환된 값이 나오고, 그 아래에 방금 쓰인 공식과 단계별 계산값이 함께 표시됩니다. 결과만 알려 주는 계산기가 아니라, 그 값이 어떤 식을 거쳐 나왔는지 따라가 볼 수 있게 하는 것이 목표였습니다.
이 글에서는 변환기의 사용법과 계산 기준을 설명하고, 변환기로 직접 확인해 볼 만한 실험 몇 가지를 소개합니다. 색공간 자체에 대한 설명은 이전 글에서 자세히 다루었으므로 여기서는 필요한 만큼만 요약하겠습니다.
1. 색공간 변환기
아래가 색공간 변환기입니다. 값을 바꾸면 결과와 공식이 바로 갱신됩니다.
화면은 위에서부터 색공간 선택, 예시 색, 입력과 출력, 변환 공식의 순서로 구성되어 있습니다. 입력 패널에 값을 넣으면 가운데 변환기 박스를 거쳐 출력 패널에 결과가 나타납니다. 넓은 화면에서는 입력이 왼쪽, 출력이 오른쪽에 놓이고, 휴대폰처럼 좁은 화면에서는 위에서 아래로 쌓입니다. 변환기 박스에는 "RGB → Lab"처럼 지금의 변환 방향이 적혀 있고, 각 패널의 머리글에는 그 값이 나타내는 색이 작은 견본으로 표시됩니다. 계산은 모두 브라우저 안에서 이루어집니다.
2. 사용 방법
2.1 기본 순서
- 입력 색공간과 출력 색공간을 고릅니다. RGB, XYZ, Lab, YCbCr, HSV 가운데 하나씩 선택합니다.
- 입력 패널에 값을 넣습니다. 성분마다 "R (0~255)"처럼 입력할 수 있는 범위가 함께 표시됩니다.
- 출력 패널에서 결과를 읽습니다. 출력 성분에도 값의 범위가 표시됩니다.
- 아래의 변환 공식을 확인합니다. 변환이 여러 단계를 거치면 공식도 단계별로 나뉘어 나오고, 각 단계의 끝에는 그 단계의 계산값이 표시됩니다.
2.2 색을 넣는 세 가지 방법
숫자를 직접 입력하는 것 말고도 두 가지 방법이 더 있습니다.
- 예시 색: 빨강, 노랑, 초록, 시안, 파랑, 마젠타, 흰색, 회색, 검정, 주황 열 가지 색 가운데 하나를 눌러 바로 넣습니다.
- 다른 색…: 팝업 창의 판에서 색상과 채도를, 막대에서 밝기를 골라 색을 정합니다. RGB 값이나 16진수 코드를 직접 넣을 수도 있으며, 확인을 누르면 입력에 반영됩니다.
어느 방법을 쓰든 고른 색은 지금 선택된 입력 색공간의 좌표로 바뀌어 들어갑니다. 예를 들어 입력 색공간이 HSV일 때 초록을 고르면 (120, 1, 1)이 입력됩니다.
2.3 알아 두면 편한 동작
- 입력 색공간을 바꾸면 값이 초기화되지 않고, 지금 입력된 색이 새 색공간의 좌표로 옮겨집니다.
- "입력 ↔ 출력 바꾸기"를 누르면 두 색공간이 서로 바뀌고, 방금 나온 출력값이 새 입력값이 됩니다. 변환한 값을 다시 되돌려 보는 왕복 실험에 편리합니다.
- 범위를 벗어난 값을 넣으면 범위 안의 값으로 맞추어 계산하고, 그 사실을 알려 줍니다. HSV의 H는 각도이므로 370을 넣으면 10으로 계산합니다.
3. 다섯 색공간 요약
변환기가 다루는 다섯 색공간과, 변환기에서 쓰는 값의 범위는 다음과 같습니다.
| 색공간 | 성분과 범위 | 한 줄 요약 |
|---|---|---|
| RGB | R, G, B: 0 ~ 255 | 디스플레이와 이미지 파일이 쓰는 기본 좌표입니다. 변환기에서는 8비트 sRGB로 다룹니다. |
| XYZ | X: 0 ~ 0.9505, Y: 0 ~ 1, Z: 0 ~ 1.089 | 장치와 무관한 기준 좌표이며, Y가 휘도에 해당합니다. |
| Lab | $L^*$: 0 ~ 100, $a^*$와 $b^*$: -128 ~ 127 | 좌표 사이의 거리가 사람이 느끼는 색 차이에 가깝도록 만든 좌표입니다. |
| YCbCr | Y: 16 ~ 235, Cb와 Cr: 16 ~ 240 | 밝기와 색차를 분리한 좌표로, 영상 압축과 방송에 쓰입니다. |
| HSV | H: 0 ~ 360°, S와 V: 0 ~ 1 | 색상, 채도, 명도로 색을 고르는 좌표입니다. |
XYZ의 범위가 0.9505와 1.089로 끝나는 것은 흰색 RGB (255, 255, 255)의 XYZ가 (0.9505, 1, 1.089)이기 때문입니다. 각 색공간의 배경과 수식은 색공간 4종 비교, XYZ 색공간, Lab 색공간, YUV와 메모리 구조, HSV 색공간의 다섯 가지 함정 글에서 자세히 다루었습니다.
4. 변환은 어떤 경로를 거치는가
다섯 색공간이 모두 서로 직접 연결되어 있는 것은 아닙니다. Lab은 XYZ에서 정의되고, YCbCr과 HSV는 감마가 적용된 RGB에서 정의됩니다. 그래서 변환기는 아래의 연결을 따라 한 단계씩 계산합니다.

XYZ 글에서 XYZ를 허브 공항에 비유했는데, 이 그림에서는 XYZ와 RGB가 그런 허브 역할을 합니다. RGB에서 Lab으로 가려면 XYZ를 한 번 거쳐야 하고, Lab에서 HSV로 가려면 XYZ와 RGB를 차례로 거쳐야 합니다. 변환기 박스 안에 작게 표시되는 "XYZ 경유" 같은 문구가 이 경로입니다. 변환에 필요한 단계 수를 표로 정리하면 다음과 같으며, 이 숫자는 화면 아래의 변환 공식에 표시되는 단계의 개수와 같습니다.
| 입력 ↓ / 출력 → | RGB | XYZ | Lab | YCbCr | HSV |
|---|---|---|---|---|---|
| RGB | - | 1 | 2 | 1 | 1 |
| XYZ | 1 | - | 1 | 2 | 2 |
| Lab | 2 | 1 | - | 3 | 3 |
| YCbCr | 1 | 2 | 3 | - | 2 |
| HSV | 1 | 2 | 3 | 2 | - |
각 연결에서 하는 일은 다음과 같습니다.
- RGB와 XYZ 사이: sRGB의 감마를 해제해 선형 값으로 바꾼 뒤 3×3 행렬을 곱합니다. 반대 방향은 역행렬을 곱하고 감마를 다시 적용합니다.
- XYZ와 Lab 사이: 기준 백색으로 나눈 값에 세제곱근 형태의 비선형 함수를 적용합니다.
- RGB와 YCbCr 사이: R, G, B의 가중합으로 밝기를 만들고, 밝기와의 차이로 두 색차를 만듭니다.
- RGB와 HSV 사이: 세 채널의 최댓값과 최솟값으로 색상, 채도, 명도를 구합니다.
각 단계의 수식은 변환기가 그대로 보여 주므로 여기에 다시 적지는 않겠습니다. 다만 YCbCr은 이전 글과 기준이 다르므로 짚어 두겠습니다.
4.1 YCbCr은 BT.601의 원래 범위를 따릅니다
색공간 4종 비교 글에서 소개한 YCbCr 식은 Y, Cb, Cr이 모두 0~255를 쓰는 풀레인지 형태였습니다. 이 변환기는 BT.601 표준이 정한 원래의 범위, 곧 Y는 16~235, Cb와 Cr은 16~240을 쓰는 리미티드 레인지를 따릅니다. YUV 글에서 표준과 범위(limited/full)에 따라 스케일과 오프셋이 적용된다고 했던 바로 그 차이입니다. 위아래에 남겨 둔 구간은 신호가 처리 과정에서 넘치는 경우에 대비한 여유입니다.
$R, G, B$를 255로 나눈 값을 $R', G', B'$라고 하면 계산식은 다음과 같습니다.
$$Y' = 0.299 R' + 0.587 G' + 0.114 B'$$ $$Y = 16 + 219 Y'$$ $$C_b = 128 + 224 \cdot \frac{B' - Y'}{1.772}$$ $$C_r = 128 + 224 \cdot \frac{R' - Y'}{1.402}$$
0~255의 $R, G, B$로 바로 쓰면 흔히 보는 다음 형태가 됩니다.
$$Y \approx 16 + 0.257 R + 0.504 G + 0.098 B$$ $$C_b \approx 128 - 0.148 R - 0.291 G + 0.439 B$$ $$C_r \approx 128 + 0.439 R - 0.368 G - 0.071 B$$
이 기준에서 흰색은 (235, 128, 128), 검정은 (16, 128, 128), 빨강은 (81, 90, 240)이 됩니다. 도입부에서 물었던 YCbCr (81, 90, 240)의 정체는 빨강입니다.
5. 변환기로 해 보는 실험
5.1 같은 색, 다섯 가지 좌표
예시 색의 주황 (200, 120, 60)을 입력에 두고 출력 색공간만 바꿔 보면, 같은 색이 좌표계에 따라 얼마나 다른 숫자로 표현되는지 볼 수 있습니다.
| 색공간 | 값 |
|---|---|
| RGB | (200, 120, 60) |
| XYZ | (0.31351, 0.26039, 0.07649) |
| Lab | (58.07, 26.18, 45.20) |
| YCbCr | (134, 90, 167) |
| HSV | (25.71, 0.7000, 0.7843) |
HSV의 값은 색상각이 약 26도인 주황 계열이고 채도가 높으며 조금 어두운 색이라고 바로 읽힙니다. Lab의 $a^\ast = 26.18$, $b^\ast = 45.20$은 이 색이 빨강과 노랑 쪽으로 치우쳐 있음을 말해 줍니다. 반면 XYZ의 세 숫자만 보고 색을 떠올리기는 어렵습니다. 같은 색이라도 좌표계마다 드러내는 정보가 다릅니다.
5.2 V가 같아도 밝기는 다릅니다
예시 색에서 순색 여섯 가지를 하나씩 누르고 출력 색공간을 HSV, Lab, YCbCr로 바꿔 보면 다음 값을 얻습니다.
| 색 | HSV의 V | Lab의 $L^*$ | YCbCr의 Y |
|---|---|---|---|
| 빨강 | 1.0000 | 53.23 | 81 |
| 노랑 | 1.0000 | 97.14 | 210 |
| 초록 | 1.0000 | 87.74 | 145 |
| 시안 | 1.0000 | 91.12 | 170 |
| 파랑 | 1.0000 | 32.30 | 41 |
| 마젠타 | 1.0000 | 60.32 | 106 |
V는 여섯 색 모두 1이지만 $L^\ast$는 파랑 32.30에서 노랑 97.14까지 벌어지고, YCbCr의 Y도 41에서 210까지 벌어집니다. HSV 글에서 설명한 "V는 밝기가 아닙니다"라는 내용을 숫자로 다시 확인할 수 있습니다.
5.3 회색 128은 절반 밝기가 아닙니다
RGB (128, 128, 128)을 XYZ로 바꾸면 Y = 0.21586이 나옵니다. 코드값은 255의 절반인데 휘도는 흰색의 약 21.6%에 그칩니다. sRGB 값은 빛의 세기에 비례하는 값이 아니라 감마가 적용된 값이기 때문이며, 변환 공식에 나오는 감마 해제 식이 이 관계를 보여 줍니다. 자세한 내용은 감마 보정 글에서 다루었습니다.
같은 회색을 Lab으로 바꾸면 $L^\ast = 53.59$로, 사람이 느끼는 밝기로는 중간에 가깝습니다. 반대로 Lab (50, 0, 0)을 RGB로 바꾸면 (119, 119, 119)가 나옵니다.
5.4 무채색 근처에서 H는 흔들립니다
RGB (128, 128, 128)을 HSV로 바꾸면 H = 0, S = 0이 나오고, 무채색에서는 H가 정의되지 않는다는 안내가 함께 표시됩니다. 여기서 B만 1 올려 (128, 128, 129)를 넣으면 H = 240이 되고, R만 1 올려 (129, 128, 128)을 넣으면 H = 0이 됩니다. 두 경우 모두 S는 0.0078에 불과합니다. 눈으로는 구별할 수 없는 회색인데도 H는 0과 240을 오갑니다. HSV 글에서 다룬 무채색 근처의 불안정성이 이것이며, H를 쓰는 알고리즘이 채도가 낮은 픽셀을 걸러 내야 하는 이유입니다.
5.5 sRGB로 표현할 수 없는 색
입력을 Lab, 출력을 RGB로 두고 (60, 100, 0)을 넣어 보겠습니다. 결과는 (255, 0, 149)이고, sRGB 색역을 벗어나는 색이라는 안내가 표시됩니다. 변환 공식 아래에 표시되는 계산값을 보면 이유를 알 수 있습니다. 클리핑하기 전의 값이 R = 290.019, G = -117.741, B = 148.754로, R은 255를 넘고 G는 음수입니다.
Lab의 좌표 범위는 sRGB보다 넓은 색을 담을 수 있으므로, 범위 안의 값이라도 sRGB로는 표현할 수 없는 색일 수 있습니다. 변환기는 이런 경우 RGB 단계에서 0~255로 클리핑한 값을 보여 줍니다.
5.6 8비트 YCbCr을 거치면 값이 조금 달라집니다
RGB (200, 120, 60)을 YCbCr로 바꾸면 계산값은 (133.728, 89.789, 167.423)이고, 8비트로 반올림한 출력은 (134, 90, 167)입니다. 여기서 "입력 ↔ 출력 바꾸기"를 눌러 다시 RGB로 되돌리면 (200, 121, 61)이 나옵니다. G와 B가 1씩 달라졌습니다. 빨강 (255, 0, 0)도 (81, 90, 240)을 거치면 (254, 0, 0)으로 돌아옵니다.
이런 오차가 얼마나 흔한지 8비트 색 전체에 대해 세어 보았습니다. 아래 코드는 변환기와 같은 식으로 모든 색을 YCbCr로 바꿨다가 되돌린 뒤, 가장 크게 어긋난 채널의 오차를 집계합니다.
import numpy as np
def to_ycbcr(rgb): # BT.601: Y 16~235, Cb와 Cr 16~240
r, g, b = (rgb / 255.0).T
y = 0.299 * r + 0.587 * g + 0.114 * b
return np.stack([16 + 219 * y,
128 + 224 * (b - y) / 1.772,
128 + 224 * (r - y) / 1.402], axis=1)
def to_rgb(ycc):
y = (ycc[:, 0] - 16) / 219
pb = (ycc[:, 1] - 128) / 224
pr = (ycc[:, 2] - 128) / 224
r = y + 1.402 * pr
b = y + 1.772 * pb
g = (y - 0.299 * r - 0.114 * b) / 0.587
return 255 * np.stack([r, g, b], axis=1)
def round8(x, lo=0, hi=255): # 범위로 자른 뒤 8비트 정수로 반올림
return np.floor(np.clip(x, lo, hi) + 0.5)
v = np.arange(256)
count = np.zeros(3, dtype=np.int64) # 가장 크게 어긋난 채널의 오차가 0, 1, 2인 색의 개수
for r in v: # 메모리를 아끼기 위해 R 값별로 나누어 계산
g, b = np.meshgrid(v, v, indexing='ij')
rgb = np.stack([np.full(g.size, r), g.ravel(), b.ravel()], axis=1).astype(float)
ycc = round8(to_ycbcr(rgb), [16, 16, 16], [235, 240, 240])
back = round8(to_rgb(ycc))
err = np.abs(back - rgb).max(axis=1).astype(int)
count += np.bincount(err, minlength=3)
total = count.sum()
for e, n in enumerate(count):
print(f'오차 {e}: {n:>10,d}색 ({n / total * 100:5.2f}%)')
오차 0: 2,660,528색 (15.86%)
오차 1: 14,058,295색 (83.79%)
오차 2: 58,393색 ( 0.35%)
16,777,216색 가운데 원래 값으로 정확히 돌아오는 색은 15.86%뿐입니다. 83.79%는 어느 한 채널 이상이 1만큼 달라지고, 0.35%는 2만큼 달라집니다.
YCbCr의 축은 RGB의 축과 방향이 달라서, RGB에서 정수였던 값이 YCbCr에서는 정수가 아니게 되고, 이것을 반올림하는 순간 원래 자리를 잃습니다. 단계 수를 줄이지 않는 풀레인지(0~255)에서도 이 오차는 사라지지 않습니다. 리미티드 레인지는 Y가 220단계, Cb와 Cr이 225단계만 쓰므로 정확히 돌아오는 색이 더 줄어들고, 2만큼 어긋나는 색도 생깁니다. 오차 자체는 눈에 띄지 않는 크기이지만, 8비트 YCbCr로 한 번 옮기고 나면 대부분의 색에서 원래의 RGB 값을 정확히 되찾을 수 없다는 점은 기억해 둘 만합니다.
6. 계산 기준과 주의할 점
6.1 계산 기준
| 항목 | 기준 |
|---|---|
| RGB | 8비트 sRGB입니다. 감마 해제와 적용에는 sRGB 표준의 구간별 식을 씁니다. |
| RGB와 XYZ 사이의 행렬 | 이전 글에 적은 소수 넷째 자리 계수(0.4124, 0.3576, 0.1805, ...)를 쓰고, 반대 방향은 그 역행렬을 씁니다. |
| 기준 백색 | D65이며 XYZ로 (0.9505, 1.0000, 1.0890)입니다. 흰색 RGB (255, 255, 255)의 XYZ와 같습니다. |
| YCbCr | BT.601 리미티드 레인지이며 8비트 정수로 표시합니다. |
| HSV | H는 도(°) 단위이고, S와 V는 0 ~ 1 범위입니다. |
| 표시 자릿수 | XYZ는 소수 5자리, Lab은 2자리, H는 2자리, S와 V는 4자리이고 RGB와 YCbCr은 정수입니다. |
XYZ를 소수 5자리까지 표시하는 것은 4자리까지만 쓰면 다시 RGB로 되돌릴 때 1씩 어긋나는 색이 생기기 때문입니다.
6.2 라이브러리 결과와 비교할 때
변환기의 값을 Python 라이브러리의 결과와 비교하면 기준 차이 때문에 숫자가 조금 다를 수 있습니다.
- XYZ와 Lab: scikit-image는 소수 여섯째 자리 계수(0.412453, …)와 백색점 (0.95047, 1, 1.08883)을 쓰므로, Lab 값이 변환기와 최대 0.02 정도 다르게 나옵니다. 예를 들어 빨강의 $L^\ast$는 변환기에서 53.23, scikit-image에서 53.24이고, 시안은 각각 91.12와 91.11입니다. HSV 글의 표에 실린 $L^\ast$는 scikit-image로 계산한 값입니다.
- YCbCr: scikit-image의 rgb2ycbcr는 변환기와 같은 리미티드 레인지를 쓰므로, 반올림하면 같은 값이 나옵니다. OpenCV의 COLOR_RGB2YCrCb는 풀레인지이고 채널 순서가 Y, Cr, Cb여서, 빨강이 (76, 255, 85)로 나옵니다.
- HSV: OpenCV의 uint8 변환은 H를 0~179로 반환합니다. 변환기의 H는 도(°) 단위입니다.
6.3 반올림과 클리핑
RGB와 YCbCr 출력은 8비트 정수로 반올림한 값입니다. 반올림하기 전의 값은 변환 공식의 각 단계 끝에 표시되는 계산값에서 볼 수 있습니다. 변환 도중 RGB가 0~255를 벗어나면 0 또는 255로 클리핑한 뒤 다음 단계로 넘깁니다. 벗어난 크기가 2 이하이면 반올림에서 생긴 오차로 보고 따로 알리지 않으며, 그보다 크게 벗어날 때만 색역을 벗어났다고 알립니다.
변환 공식에서 $R'$는 0~1로 정규화한 값, $R_{lin}$은 감마를 해제한 선형 값을 뜻합니다. YCbCr의 Y는 감마가 적용된 R, G, B로 계산하는 루마(luma)이고 XYZ의 Y는 선형 값으로 계산하는 휘도여서, 이름은 같지만 서로 다른 값입니다.
6.4 안내 메시지
| 메시지 | 뜻 |
|---|---|
| 입력 범위를 벗어난 값은 범위 안의 값으로 계산했습니다 | 입력값이 표시된 범위 밖이어서 가장 가까운 값으로 바꾸어 계산했습니다. H는 360도 주기로 감습니다. |
| sRGB 색역을 벗어나는 색입니다 | 변환 도중 RGB가 0~255를 크게 벗어나 클리핑했습니다. |
| 출력값이 표시 범위를 벗어났습니다 | XYZ와 Lab 사이의 변환에서 결과가 표시된 범위 밖으로 나왔습니다. 입력한 좌표가 실제 색에 해당하지 않을 수 있습니다. |
| 무채색(C = 0)에서는 H가 정의되지 않습니다 | R = G = B여서 색상각을 정할 수 없으므로 관례에 따라 0으로 표시했습니다. |
마치며
색공간 변환은 식만 놓고 보면 복잡해 보이지만, 결국 XYZ와 RGB라는 두 허브를 사이에 둔 몇 개의 단계를 차례로 밟는 일입니다. 변환기에서 입력과 출력을 바꿔 가며 변환 공식의 단계가 하나에서 셋으로 늘어나는 것을 보면 이 구조가 자연스럽게 눈에 들어옵니다.
숫자를 직접 넣어 보면 이전 글에서 다룬 내용도 다시 확인할 수 있습니다. V가 같아도 밝기는 다르고, 회색 128은 절반 밝기가 아니며, 무채색 근처에서 H는 흔들리고, 8비트 YCbCr을 거치면 값이 조금 달라집니다. 색공간을 공부할 때나 직접 구현한 변환 코드를 검산할 때 이 변환기가 손쉬운 기준점이 되기를 바랍니다.
📖 참고문헌
- 멀티미디어 지식공작소 (2026).
HSV 색공간의 다섯 가지 함정: RGB, XYZ, Lab, YCbCr과 비교.
https://multimedia.tistory.com/entry/hsv-color-space-strengths-and-pitfalls - 멀티미디어 지식공작소 (2026).
RGB만 알면 충분할까? 색공간 4종 비교 (RGB / XYZ / Lab / YCbCr).
https://multimedia.tistory.com/entry/color-space-comparison-rgb-xyz-lab-ycbcr - 멀티미디어 지식공작소 (2026).
XYZ 색공간: 모든 색공간의 뿌리, CIE 1931 XYZ.
https://multimedia.tistory.com/entry/cie-1931-xyz-color-space - 멀티미디어 지식공작소 (2026).
Lab 색공간: RGB가 말해주지 못하는 '진짜' 색 차이.
https://multimedia.tistory.com/entry/python-lab-color-space-perceptual-uniformity - 멀티미디어 지식공작소 (2026).
왜 영상은 RGB가 아니라 YUV를 쓸까? NV12/NV21/I420 메모리 구조 한눈에.
https://multimedia.tistory.com/entry/%EC%99%9C-%EC%98%81%EC%83%81%EC%9D%80-RGB%EA%B0%80-%EC%95%84%EB%8B%88%EB%9D%BC-YUV%EB%A5%BC-%EC%93%B8%EA%B9%8C-NV12NV21I420-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0-%ED%95%9C%EB%88%88%EC%97%90 - IEC (1999).
Multimedia Systems and Equipment — Colour Measurement and Management — Part 2-1: Default RGB Colour Space — sRGB.
IEC 61966-2-1:1999. - ITU-R (2011).
Studio Encoding Parameters of Digital Television for Standard 4:3 and Wide-Screen 16:9 Aspect Ratios.
Recommendation ITU-R BT.601-7. https://www.itu.int/rec/R-REC-BT.601
🏷️ 코드·그림 출처
본문의 변환기 위젯과 코드는 생성형 AI의 도움을 받아 초안을 작성한 뒤 필자가 검토·수정하고 직접 실행하여 결과를 검증한 것입니다. 4절의 변환 경로 그림은 필자가 생성형 AI의 도움을 받아 제작하였습니다.