선형대수 이야기: 행렬은 공간을 바꾸는 동사다

선형대수 이야기: 행렬은 공간을 바꾸는 동사다

행렬은 숫자표가 아니다. 공간을 바꾸는 동사다. 선형대수의 기본은 사실 이 선언 하나에서 다 풀려 나와. 대학에서 행렬을 네모난 숫자 덩어리로 외우다 지쳤다면, 그건 네 탓이 아니야. 정작 이게 무슨 동작인지는 안 알려주고 표만 외우게 시켜서 그래.

피그마에서 사각형 하나를 잡고 회전시킨다. 크게 늘이고, 비스듬히 기울인다. CSS로 쓰면 transform: rotate(30deg) scale(1.4) skewX(20deg) 같은 한 줄이다. 이 모든 변형 뒤에는 똑같은 물건 하나가 돌아간다. 행렬(matrix)이다.

그러니까 학교에서 배운 네모난 표는 행렬의 겉모습일 뿐이야. 그 표가 진짜로 하는 일은 방금 피그마에서 본 회전이고 늘이기고 기울이기고. 이 글은 그 한 문장을 붙들고 선형대수의 기본을 통째로 꿰어볼 참이야. 공식 대신 그림만 따라오면 된다.

디자이너: “행렬, 그냥 숫자를 네모나게 늘어놓은 표 아니야?”

동료: “그건 겉모습이야. 행렬은 공간을 통째로 바꾸는 동사야.”

디자이너: “동사?”

동료: “피그마에서 회전하고 늘이는 그 변형, 그게 행렬이 하는 일이야.”

벡터는 화살표이자 좌표

자, 맨 밑바닥부터 가자. 시작은 벡터(vector)야. 벡터? 별거 아니야. 원점에서 뻗어 나간 화살표 하나다. 2차원이면 화살표 끝이 어디 있는지를 (x, y) 두 숫자로 적어. 디자인으로 옮기면 점의 위치이자, 얼마만큼 움직이라는 이동량이기도 하고. (2, 1)은 오른쪽으로 2칸, 위로 1칸 간 자리야.

좌표는 두 기준 화살표의 조합

그런데 (2, 1)이라는 좌표에는 숨은 뜻이 하나 더 있어. 이건 기준이 되는 화살표 두 개를 얼마씩 섞었는지 적은 레시피야. 오른쪽으로 한 칸 가는 단위 화살표를 î, 위로 한 칸 가는 단위 화살표를 ĵ라고 하자. 그럼 (2, 1)은 î를 두 번, ĵ를 한 번 쌓은 거지.

îĵv
모든 점은 기저 î와 ĵ를 얼마씩 쌓았는지로 적힌다. v = 2î + 1ĵ.

이 두 기준 화살표 î랑 ĵ를 기저(basis)라고 불러. 좌표란 언제나 이 기저를 얼마씩 쌓았느냐는 말이야. (2, 1)2î + 1ĵ를 다르게 적은 것뿐이고. 사소해 보이지? 근데 이 관점 하나가 바로 다음 장에서 행렬의 정체를 여는 열쇠가 돼. 여기 밑줄 딱 그어놔.

행렬은 기저가 어디로 가는지를 적은 것

이제 공간을 변형한다고 하자. 변형이란 결국 모든 점을 새 자리로 옮기는 일이야. 근데 점이 무한히 많잖아. 막막하지? 여기서 많이들 지레 포기하는데, 겁먹지 마. 결정적인 사실이 하나 있거든. 모든 점은 î랑 ĵ의 조합이고, 우리가 다루는 변형(선형 변환)은 그 조합 관계를 그대로 지켜줘. 2î + 1ĵ였던 점은 변형 뒤에도 여전히 새 î의 두 배에 새 ĵ의 한 배야.

무슨 뜻이냐. î랑 ĵ 딱 둘이 어디로 가는지만 정하면, 나머지 무한한 점의 행선지가 저절로 따라온다는 거야. 무한개를 다 옮길 필요가 없어, 딱 둘만 보면 돼. 2×2 행렬이 바로 그 새 î랑 새 ĵ의 위치를 세로줄(열)로 나란히 적어둔 거야. 첫 열이 î의 도착지, 둘째 열이 ĵ의 도착지.

예를 들어 î는 제자리에 두고 ĵ만 오른쪽으로 눕혀서 (1, 1)로 보내는 변형을 생각해보자. 이걸 기울이기(shear)라고 해. 행렬로 적으면 이래.

[ 1  1 ]   첫 열 (1,0) = 새 î,  둘째 열 (1,1) = 새 ĵ
[ 0  1 ]

이 변형을 공간 전체에 적용하면 격자가 통째로 기울어서 평행사변형이 돼. 그리고 아까 그 점 v는? 여전히 새 기저로도 2î + 1ĵ이니까, 그 기운 격자를 따라 새 자리로 얌전히 옮겨가.

îĵ'v
행렬은 î와 ĵ를 어디로 보낼지만 정한다. 격자가 통째로 기울고, v는 여전히 2î + 1ĵ로 따라간다.

봐. 행렬을 숫자표로만 보면 [[1,1],[0,1]]은 그냥 숫자 네 개야. 근데 동사로 보면 이건 î는 그대로 두고 ĵ를 오른쪽으로 눕혀라라는 하나의 명령이야. 그게 행렬의 진짜 뜻이다. 느낌 오지?

곱셈은 그 규칙을 좌표에 적용하는 것

그럼 행렬에 벡터를 곱한다는 건 뭘 하는 거냐. 겁낼 거 없어, 방금 그 이야기 그대로야. 좌표가 (x, y)인 점은 x·(새 î) + y·(새 ĵ)로 가. 곧 x만큼 첫 열을, y만큼 둘째 열을 쌓는 거지. 위 기울이기 행렬로 점 (2, 1)을 직접 옮겨보자.

[ 1  1 ] [ 2 ]   = 2 × (1,0) + 1 × (1,1)
[ 0  1 ] [ 1 ]   = (2,0) + (1,1)
                 = (3, 1)

(2, 1)(3, 1)로 갔지. 봐, 공식 외운 사람 아무도 없어. 각 좌표만큼 새 기저를 쌓아라, 이 한 문장이 행렬 곱셈의 전부다. 이게 다야.

익숙한 변형들이 다 행렬이다

이렇게 보면 디자인 툴에서 하던 변형이 전부 행렬로 읽혀. 기저를 어디로 보내느냐만 다를 뿐이야.

확대 (2배)      회전 (90도)      기울이기
[ 2  0 ]        [ 0 -1 ]         [ 1  1 ]
[ 0  2 ]        [ 1  0 ]         [ 0  1 ]
î,ĵ를 늘림       î→(0,1)          ĵ를 눕힘
                ĵ→(-1,0)

확대와 회전을 그림으로 보면 이래. 확대는 î랑 ĵ를 나란히 늘여 격자 칸을 키운다.

1칸변형 전×22칸가로세로 2배
확대 행렬 [[2,0],[0,2]]는 î와 ĵ를 각각 두 배로 늘여, 격자 칸이 가로세로 2배로 커진다.

회전은 î랑 ĵ를 같은 각만큼 돌려서 격자를 통째로 돌린다.

îĵ변형 전반시계 90°î'ĵ'변형 후
회전 행렬 [[0,-1],[1,0]]은 î를 위로, ĵ를 왼쪽으로 보내 격자를 통째로 반시계 90도 돌린다.

CSS의 transform: matrix(a, b, c, d, tx, ty)가 정확히 이 2×2 행렬(에 이동을 더한 것)이야. 앞의 네 숫자 a, b, c, d가 바로 새 î랑 새 ĵ의 좌표고. 피그마에서 변형 핸들 잡고 돌리고 늘일 때도, 툴은 뒤에서 이 숫자들을 고쳐 쓰고 있는 거야.

행렬 곱은 변형을 이어 붙이는 것

변형을 하나 하고, 그 위에 또 하나를 하면? 두 행렬을 곱하는 거야. 행렬 곱 AB는 B를 먼저 적용하고 그 결과에 A를 적용하라는 뜻이야. 변형을 순서대로 이어 붙이는 게 곱셈이다.

여기서 중요한 성질이 하나 나와. 순서를 바꾸면 결과가 달라진다. 먼저 회전하고 늘이는 거랑, 먼저 늘이고 회전하는 건 보통 다른 그림이 돼. 그래서 행렬 곱은 교환법칙이 성립하지 않아. ABBA가 달라. CSS transform에서 함수 순서만 바꿨는데 결과가 달라지는 이유가 정확히 이거야. rotate(30deg) scale(1.5, 1)이랑 scale(1.5, 1) rotate(30deg)는 다른 변형이야. 툴이 괜히 까다롭게 구는 게 아니라, 변형을 이어 붙이는 순서 자체가 원래 결과를 바꾸는 거지.

덤: 행렬식은 넓이가 몇 배인지

변형이 넓이를 얼마나 키우거나 줄이는지, 그걸 숫자 하나로 요약한 게 행렬식(determinant)이야. î랑 ĵ가 이루던 단위 정사각형이 변형 뒤에 평행사변형이 되는데, 그 평행사변형의 넓이가 곧 행렬식이다. 2배 확대는 가로세로가 다 2배니까 넓이가 4배, 그래서 행렬식이 4. 앞의 기울이기는 격자가 비스듬해져도 넓이는 그대로라 행렬식이 1이고. 값이 음수면 공간이 뒤집힌 거(거울상), 0이면 공간이 납작하게 눌려서 넓이가 사라진 거야. 넓이가 0으로 눌리면 정보가 뭉개져서 되돌릴 수가 없어. 역변환이 없다는 말이랑 같지.

그림으로 보면 이래.

넓이 1îĵ변형넓이 2î'ĵ'넓이 배율 = 행렬식 = 2
단위 정사각형(넓이 1)이 변형 뒤 평행사변형이 되고, 그 평행사변형의 넓이가 곧 행렬식이다. 여기선 넓이가 2배라 행렬식이 2.

예시 문제

자, 행렬이 î랑 ĵ를 어디로 보내는지 적은 거고, 행렬에 벡터를 곱하는 건 좌표만큼 새 기저를 쌓는 거라고 했지. 이제 직접 두 점만 옮겨보자. 공식 아니야, 방금 그 한 문장이면 돼.

문제 1. 확대 행렬 [[2,0],[0,2]]로 점 (3, 1)을 옮기면 어디로 갈까?

직접 한번 해보고 내려와. 풀이 간다.

풀이. 먼저 행렬의 두 열부터 읽어. 첫 열 (2, 0)이 새 î, 둘째 열 (0, 2)가 새 ĵ야. î가 두 배로 늘고 ĵ도 두 배로 는 거지.

이제 점 (3, 1)은 새 기저를 얼마씩 쌓으라는 거였어. 첫 좌표 3만큼 새 î를, 둘째 좌표 1만큼 새 ĵ를 쌓아.

[ 2  0 ] [ 3 ]   = 3 × (2,0) + 1 × (0,2)
[ 0  2 ] [ 1 ]   = (6,0) + (0,2)
                 = (6, 2)

(3, 1)(6, 2)로 갔어. 봐, 가로세로를 다 두 배로 늘리는 변형이니까 점도 딱 두 배로 밀려난 거지. 답이 말이 되지? 각 좌표만큼 새 기저를 쌓아라, 그 한 문장이 전부였어.

문제 2. 이번엔 회전이야. 반시계로 90도 도는 행렬 [[0,-1],[1,0]]로 점 (1, 2)를 옮기면?

직접 한번 해보고 내려와. 풀이 간다.

풀이. 열부터 읽자. 첫 열 (0, 1)이 새 î야. 원래 오른쪽을 보던 î가 위로 벌떡 선 거지. 둘째 열 (-1, 0)이 새 ĵ고. 위를 보던 ĵ가 왼쪽으로 눕었어. 딱 반시계 90도지.

(1, 2)는 새 î를 1만큼, 새 ĵ를 2만큼 쌓으라는 거야.

[ 0 -1 ] [ 1 ]   = 1 × (0,1) + 2 × (-1,0)
[ 1  0 ] [ 2 ]   = (0,1) + (-2,0)
                 = (-2, 1)

(1, 2)(-2, 1)로 갔어. 오른쪽 위에 있던 점이 왼쪽 위로 돌아갔지? 종이를 반시계로 90도 돌렸다고 생각하면 딱 그 자리야. 피그마에서 오브젝트 90도 회전 걸 때 뒤에서 도는 게 바로 이 계산이야. 숫자 네 개가 새 î랑 ĵ의 주소라는 거, 이제 눈에 보이지?

정리

자, 오늘 딱 하나만 가져가. 행렬은 숫자표가 아니라 공간을 바꾸는 동사다. 벡터가 점이라는 명사라면, 행렬은 그 점 전체에 작용하는 변형이야. 행렬의 열은 기저 î랑 ĵ가 도착한 자리이고, 행렬 곱셈은 좌표만큼 새 기저를 쌓는 일이며, 행렬끼리의 곱은 변형을 순서대로 이어 붙이는 일이다. 다음에 피그마에서 오브젝트를 기울이거나 CSS transform을 쓸 때, 뒤에서 도는 그 숫자 네 개가 사실은 î랑 ĵ의 새 주소라는 걸 떠올려봐. 그 순간 행렬은 외워야 할 표에서 눈에 보이는 동작으로 확 바뀌어. 봐, 숫자표라고 겁먹었던 게 억울할 정도지?

곱을 덧셈으로 눕히는 로그 이야기는 앞 글에서 다뤘어.

이 글, 어떠셨어요?