함수형 프로그래밍이라는 고서(古書)

162025년 08월 31일4

"어떻게 하면, 함수가 상태를 가질 수 있을까?"

세바스티안이 던진 질문은 댄의 뇌리에서 떠나지 않았다. 그것은 마치 풀리지 않는 화두처럼, 그의 모든 생각을 지배했다. 주말 내내, 그는 자신의 서재에 틀어박혀 해답의 실마리를 찾아 헤맸다.

그의 손이 향한 곳은 최신 기술 블로그나 논문이 아니었다. 오히려 수십 년의 세월이 쌓인, 컴퓨터 과학의 가장 근본적인 원칙들을 다루는 낡은 서적들이었다. 그의 탐구는 ‘함수형 프로그래밍(Functional Programming)’이라는 오래된 지혜의 샘으로 거슬러 올라갔다.

함수형 프로그래밍의 세계에서 가장 이상적인 존재는 ‘순수 함수(Pure Function)’였다.
똑같은 입력을 주면 언제나 똑같은 출력을 반환하는 함수. 바깥세상에 어떤 영향도 주지 않고, 오직 자신에게 주어진 인자만으로 결과를 만들어내는, 예측 가능하고 고결한 존재.

리액트의 초기 아이디어 역시 이 순수 함수에서 출발했다.
UI = f(state)
이 공식에서 f가 바로 순수 함수를 의미했다. 주어진 state에 따라 언제나 똑같은 UI를 그려내는 것.

하지만 클래스 컴포넌트는 이 순수함을 지키지 못했다.
componentDidMount에서 네트워크 요청을 보내는 행위는 명백히 바깥세상에 영향을 주는 ‘부수 효과(Side Effect)’였다. this.state라는 내부 상태를 가지는 것 자체가, 함수가 더 이상 입력에만 의존하지 않음을 의미했다.

댄은 생각했다.
‘문제는 상태와 부수 효과 그 자체가 아니야. 그것들을 다루는 방식이 문제다.’

웹 애플리케이션은 필연적으로 상태를 가져야 하고, 서버와 통신하는 부수 효과를 처리해야만 한다. 함수형 프로그래밍은 이것들을 없애려 한 것이 아니었다. 오히려 그것들을 어떻게 하면 ‘순수함’을 최대한 해치지 않는 방식으로 격리하고 관리할 수 있을까를 고민해 온 학문이었다.

그는 함수형 언어들이 부수 효과를 다루는 여러 기법들을 다시 훑어보기 시작했다. 모나드(Monad), 펑터(Functor)… 이름만 들어도 머리가 아파오는 추상적인 개념들이었지만, 그 핵심 철학은 명확했다.

‘부수 효과를 가진 위험한 코드를, 순수한 값처럼 다룰 수 있게 포장하자.’

네트워크 요청이라는 예측 불가능한 행위를, 그 요청 자체를 나타내는 ‘값’으로 만들고, 그 값을 안전한 컨테이너 안에 담아 순수한 세상과 격리하는 것.

댄의 머릿속에서 아이디어가 번쩍였다.
“만약… 만약 리액트 컴포넌트를 그냥 평범한 함수로 만든다면?”

지금까지 리액트에는 두 종류의 컴포넌트가 있었다. 상태와 생명주기를 가진 ‘클래스 컴포넌트’와, 오직 props를 받아 UI만 그리는 ‘함수형 컴포넌트’. 후자는 ‘멍청한(dumb)’ 컴포넌트라고 불리기도 했다. 상태를 가질 수 없었기 때문이다.

“이 ‘멍청한’ 함수형 컴포넌트가, 리액트의 도움을 받아 상태나 부수 효과 같은 ‘능력’을 빌려 쓸 수 있다면 어떨까?”

클래스처럼 자신이 모든 것을 소유하는 방식이 아니었다.
필요할 때마다, 리액트라는 중앙 시스템에 “나 지금 상태 값이 하나 필요해” 혹은 “렌더링이 끝난 후에 이 코드 좀 실행해 줘”라고 ‘요청’하는 방식.

그렇게만 된다면, 컴포넌트 자체는 순수한 함수에 가까운 형태를 유지할 수 있었다. 상태 관리와 부수 효과 처리의 책임은 리액트의 코어 로직으로 넘어가고, 컴포넌트는 오직 UI를 그리는 데만 집중하게 된다.

이것은 세바스티안이 제시했던 방향과 정확히 일치했다.
컴포넌트라는 무거운 갑옷에서 로직을 분리해내는 것.

댄은 심장이 빠르게 뛰는 것을 느꼈다.
아직은 막연한 아이디어였지만, 안갯속에서 한 줄기 빛을 발견한 기분이었다. 그는 낡은 책을 덮고, 키보드 위에 손을 올렸다.

그는 이 아이디어를 증명할, 아주 작은 프로토타입을 만들어보기로 결심했다. 그가 모니터에 처음으로 타이핑한 문장은, 훗날 리액트의 역사를 바꿀 질문의 시작이었다.

// What if a function could have state?