📖 구조 노트 목차 Chapter 15 읽는 중

15. 모양을 옮겨도 남는 것

구조 노트 Chapter 15 2026. 07. 16.

건축 사무소에서 도면을 그린 뒤, 그 도면을 바탕으로 축소 모형을 만든다. 도면은 1대 100 축척이고 모형은 1대 20 축척이니 크기는 다섯 배나 차이가 난다. 종이에 그린 선이 플라스틱과 나무로 바뀌고, 치수도 모두 달라진다. 그런데도 거실에서 주방으로 갈 수 있었다면 모형에서도 그렇게 갈 수 있고, 서로 이어져 있지 않은 두 방은 모형에서도 여전히 이어져 있지 않다. 크기와 재료는 바뀌었지만, 방들 사이의 연결 관계는 그대로 옮겨진 것이다.

13장과 14장에서 살펴본 것은 두 구조가 같은지 비교하는 일이었다. 지금은 조금 다르다. 도면과 모형은 애초에 같은 대상이 아니다. 중요한 것은 둘이 같은가가 아니라, 하나를 다른 하나로 옮기는 과정에서 무엇이 그대로 남는가다.

프로그래밍에서도 이런 옮김은 흔하다. 사용자 아이디로 사용자를 조회하는 함수를 생각해 보자. 사용자가 반드시 존재하는 것은 아니므로, 이 함수는 User 대신 Optional<User>를 돌려준다. 값이 있으면 사용자를 담고, 없으면 빈 상자를 돌려주는 타입이다.

이제 사용자의 이름만 얻고 싶다고 하자. Userstring으로 바꾸는 함수는 이미 있다. 그렇다고 상자를 열어 값을 꺼내고, 이름을 구한 뒤 다시 상자에 담는 일을 매번 직접 할 필요는 없다. Optionalmap이라는 연산을 제공한다. optionalUser.map(user => user.name)이라고 쓰면, 상자 안에 값이 있을 때만 함수를 적용하고 결과를 다시 상자에 담는다. 비어 있었다면 아무 일도 하지 않는다. 원래 User에서 string으로 가던 함수 하나가, 이제는 Optional<User>에서 Optional<string>으로 가는 함수로 자연스럽게 옮겨진 것이다.

여기서 중요한 것은 단순히 함수를 하나 감쌌다는 사실이 아니다. 사용자에서 이름을 얻고, 다시 이름에서 첫 글자를 얻는 두 함수를 이어 붙인 뒤 map을 적용한 결과는, 두 함수를 각각 map으로 옮긴 뒤 차례대로 적용한 결과와 정확히 같다. 또한 아무 일도 하지 않는 함수는 map을 거친 뒤에도 여전히 아무 일도 하지 않는 함수로 남는다. 함수를 이어 붙이는 방식과 아무것도 하지 않는 함수라는 기본 규칙이 새로운 세계에서도 그대로 보존되는 것이다.

수학에서는 이런 옮김을 **펑터(functor)**라고 부른다. 펑터는 하나의 대상을 다른 대상으로 옮길 뿐 아니라, 그 대상 사이의 화살표도 함께 옮긴다. 그리고 그 과정에서 화살표를 합성하는 방법과 항등화살표를 반드시 보존한다. Optional은 어떤 타입이든 Optional<T>로 옮기고, 타입 사이의 함수도 map을 통해 새로운 함수로 옮긴다. Array.map 역시 같은 이유로 펑터다.

이렇게 보면 축소 모형을 만드는 건축가와 Optional.map을 사용하는 프로그래머가 하는 일은 크게 다르지 않다. 둘 다 하나의 세계를 다른 세계로 번역한다. 크기나 재료, 타입 같은 겉모습은 바뀌지만, 무엇이 무엇과 연결되어 있는가라는 구조만큼은 끝까지 지켜 낸다. 14장에서 구조가 같다는 기준을 살펴봤다면, 이번 장에서는 구조를 다른 세계로 옮길 때도 반드시 지켜야 하는 규칙이 있다는 사실을 확인한 셈이다.

하지만 구조를 옮기는 방법은 하나뿐일까. 같은 함수를 Optional로 옮길 수도 있고 Array로 옮길 수도 있다. 이렇게 서로 다른 두 옮김이 있을 때, 그 둘 사이에도 지켜야 할 관계가 있을까.