2022. 9. 12. 22:07ㆍFrontend
Overview
React Suspense의 개념적 모델을 다룬 이전 포스팅의 마지막 부분에서 Dan Abramov(React Core Team)의 Suspense에 대한 Comment를 잠깐 언급했습니다. 이 Comment에 대한 구체적인 내용은 아래 인용문과 같습니다. (아래 인용문은 Suspense가 Fallback UI를 처리하기 위해서 throw 된 Promise를 사용한다는 내용을 이해하고 있다는 것을 전제로 합니다. 해당 내용에 대한 이해가 필요하다면 이에 대해 다룬 이전 포스팅을 참고해 주세요)
This builds on a React feature called “Suspense”, which is in active development for the data fetching use case. The interesting part, of course, is that the data might not yet be in the movieCache — in which case we need to do something because we can’t proceed below. Technically, in that case the read() call throws a Promise (yes, throws a Promise — let that sink in). This “suspends” the execution. React catches that Promise, and remembers to retry rendering the component tree after the thrown Promise resolves.
This isn’t an algebraic effect per se, even though this trick was inspired by them.
- Dan Abramov(overreacted.io)
이는 Suspense가 사용하는 개념적인 모델, 즉 Data의 상태('pending' | 'success' | 'error')를 확인하면서 'pending' 상태인 경우에는 원하는 데이터의 요청에 대한 promise를 throw해서 Suspense가 Fallback UI를 렌더 하도록 하고, 'success' 상태인 경우에는 Component를 렌더하도록 하는 이 모델은 그 자체로는 "Algebraic Effect(대수적 효과)"는 아니지만, 이로부터 영향을 받았다는 의미로 해석할 수 있습니다. 그렇다면 여기서의 대수적 효과라는 것이 어떤 의미이며, Suspense의 개념적 모델이 어떤 측면에서 이 대수적 효과에 영향을 받았다는 것일까요? 이번 포스팅에서는 이 부분에 대해 간략하게 살펴보도록 하겠습니다.
What is Algebraic Effects?
algebra: a generalization of arithmetic in which letters representing numbers are combined according to the rules of arithmetic - webster dictionary
"대수적 효과(Algebraic Effects)"에 대해 살펴보기 위해서는 먼저 "대수"가 무엇인지에 대해 이해하고 넘어가는 것이 좋을 것 같습니다. "대수(代數)"라는 말은 대신할 대(代) 자를 사용하는 데서 그 의미를 예측할 수 있듯이, "수를 대신한다(letters representing numbers)"는 뜻을 가지고 있습니다. 어떠한 수학적인 문제를 해결할 때, 수들의 연산으로 이를 해결하는 것이 아닌 문자와 문자들 사이의 추상적 관계(함수)들을 통해 이를 해결하는 방식을 '대수적 방식'이라고 하고, 이는 변수와 함수를 사용해서 문제를 해결하는 프로그래밍과도 관련이 있습니다.
그렇다면 대수적 효과는 무엇일까요? Matija Pretnar의 "An Introduction to Algebraic Effects and Handlers Invited tutorial paper"에 의하면 대수적 효과는 다음과 같이 정의됩니다.
Algebraic effects are an approach to computational effects based on a premise that impure behaviour arises from a set of operations such as get & set for mutable store, read & print for interactive input & output, or raise for exceptions. This naturally gives rise to handlers not only of exceptions, but of any other effect, yielding a novel concept that, amongst others, can capture stream redirection, backtracking, co-operative multi-threading, and delimited continuations. - Matija Pretnar
이를 그대로 해석하면, "대수적 효과란 exception throw등을 포함하는 연산들의 집합(set of operations)으로부터 순수하지 못한 행위(impure behavior)들이 발생할 수 있다는 전제를 바탕으로 Computational Effect에 대해 접근하는 방식이다"라는 뜻이 됩니다. 앞서 살펴본 "대수"의 정의로부터 미루어봤을 때, "exception throw등을 포함하는 연산들의 집합" 은 "수를 대신하는 변수(대수)를 사용하는 프로그램에서 목적을 달성하기 위한 연산들이 일어나고, 그 연산의 집합(set)의 구성요소(element)중 하나로 exception throw등이 있다"고 이해할 수 있을 것 같습니다. 즉 대수적 효과란 대수를 사용해서 연산을 수행할 때 순수하지 못한 행위들이 일어날 수 있고, 이를 전제로 Computational Effect에 대해 접근하는 방식이라는 것인데 "순수함수", "Computational Effects"와 같은 용어들로 인해 이를 그대로 받아들이기에는 조금 난해한 부분이 있으므로, 먼저 이 요소들을 하나씩 쪼개서 살펴보도록 하겠습니다.
순수(pure)한 행위와 순수하지 못한(impure) 행위
컴퓨터 프로그래밍에서 "순수"한 것이 무엇인지를 알기 위해서는 순수 함수의 정의를 먼저 살펴볼 필요가 있습니다. 아래의 정의에 따르면 어떤 함수가 순수하다는 것은, 동일한 입력에 대해 항상 동일한 결과를 리턴하고, Side Effect를 발생시키지 않는 것을 의미합니다. 이 정의 하에서 exception throw와 같은 연산은 Side Effect를 발생시키며, 따라서 비순수한 행위라고 할 수 있습니다.
In computer programming, a pure function is a function that has the following properties:
1. the function return values are identical for identical arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams)
2. the function application has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or input/output streams).
Computational Effect
Computational Effect라는 용어를 "컴퓨터 효과"라고 번역하는 것이 정확하지 않은 느낌이 들어서, 이 글에서는 해당 단어를 번역하지 않고 원문 그대로 사용하겠습니다. 아래는 Stackoverflow에서 Algebraic Effects에서의 Computational Effect를 잘 정의한 댓글들이 있어서 이를 인용한 것입니다.
An computational effect is any computation that includes an alteration of its environment. For example, things like total disk capacity, network connectivity are external effects, that play a role in operations like reading/writing files or accessing a database. Anything that a function produces, besides the value it computes, is a computational effect. From the perspective of that function, even another function that accesses the same memory as that function does, can be considered an effect.
That's the theoretical definition. Practically, its useful to think of an effect as any interaction between a sub expression and a central control which handles global resources in a program. Sometimes, a local expression may need to send messages to the central control while execution, along with enough information so that once the central control is done, it can resume the suspended execution. - stackoverflow
인용한 글에 의하면 Computational Effect는 "자신의 환경(environment)을 변경하는 모든 연산"을 포함하는 개념입니다. 이는 이론적인 관점에 가까우므로 조금 더 실용적인 관점에서 설명한다면, "sub expression"과 "central control" 사이에서 일어나는 interaction을 의미합니다. (굳이 central 이라는 용어에 집중할 필요는 없습니다.)
예를 들어, 어떤 로컬 함수(sub expression)에서 부모 환경(central control)으로 어떤 작업을 넘겨 처리하게 하고, 처리가 끝나면, 다시 로컬 함수의 실행이 멈췄던 곳으로 돌아가 작업을 계속한다면, 여기에서는 Computational Effect가 발생한 것입니다. (작업을 넘긴다는 것을 부모 컴포넌트로 promise를 throw하는 것으로 바꿔서 생각해보면 이것이 바로 Suspense가 Fallback UI를 처리하는 방식이라는 것을 알아차릴 수 있을 것입니다.)
이를 바탕으로 위의 대수적 효과의 정의를 다시 한번 살펴보면, 다음과 같이 이해할 수 있을 것입니다.
"대수적 효과(Algebraic Effects)란 서로 다른 환경 사이의 interaction에서 발생하는 Computational Effects에 대한 접근 방식을 의미한다. 여기에는 전제가 있는데, 이러한 컴퓨터 동작들의 집합들로부터 순수하지 못한 효과가 발생한다는 것이다."
훨씬 나아졌습니다. 그렇다면 이 대수적 효과의 접근 방식이라는 것이 무엇일까요? 앞서 인용한 논문의 바로 다음 문장에 의하면, 이는 적절한 "handler"를 사용하는 것을 의미합니다.
This naturally gives rise to handlers not only of exceptions, but of any other effect, yielding a novel concept that, amongst others, can capture stream redirection, backtracking, co-operative multi-threading, and delimited continuations
즉 Algebraic Effect는 서로 다른 환경 사이의 interaction에서는 impure 한 SideEffect가 발생할 수 있는데, 이것을 적절한 handler를 사용해 해결하는 방식을 의미하는 것이고, 이 handler는 "sub-expression"에서 발생시킨 effect를 "central-control"에서 잡아 적절하게 처리한 뒤, 다시 "sub-expression"이 멈췄던 곳으로 흐름을 되돌려 주는 것입니다.
Why does it matters? (with Suspense)
Try to think of Algebraic Effects as some sort of try / catch mechanism, where the catch handler does not just "handle the exception", but is able to provide some input to the function which threw the exception. The input from the catch handler is then used in the throwing function, which continues as if there was no exception. - stackoverflow
지금까지 꽤나 긴 호흡으로 대수적 효과(Algebraic Effect)가 무엇인지에 대해 살펴보았습니다. 그렇다면, 왜 이것이 React를 이해하는데에 중요하며, Suspense에 영향을 미치게 된 것일까요? 이를 살펴보기 위해 이미 여러 번 다른 문장으로 살펴보았던 Algebraic Effect를 조금 더 Javascript적이고 React적인 관점을 담아 소개해 보도록 하겠습니다. 참고로, 이 문장은 100% 정확한 Algebraic Effect를 나타내지는 않을 수 있습니다.(제가 임의로 이해하기 쉽게 작성한 것입니다.) Dan Abramov 자신도 React Suspense에 적용된 개념적 모델이 정확히 Algebraic Effect는 아니며, 여기서 영감을 얻었다고 했기 때문에 개념에 대한 인사이트를 얻는다면 그것으로 충분할 것입니다. (This isn’t an algebraic effect per se, even though this trick was inspired by them. - Dan)
"Algebraic Effect는 부모 컴포넌트(Suspense)와 자식 컴포넌트(UserList) 사이의 interaction에서 발생한 SideEffect(throw Promise)를 적절한 Handler(catch Promise in Suspense)를 사용해 해결하는 방식을 의미하는 것이고, 이 Handler는 sub-expression(UserList)에서 발생시킨 Effect(throw Promise)를 central-control(Suspense)에서 잡아 적절하게 처리한 뒤, 다시 sub-expression(UserList)이 멈췄던 곳으로 흐름을 되돌려 주는 것입니다."
Algebraic Effects와 Suspense와의 연관성이 보이시나요? 이렇게 Algebraic Effects는 어떤 코드 조각을 감싸는 맥락으로 책임을 분리하고, 분리된 책임에 대한 처리는 감싸는 맥락에서 처리하게 함으로써 "무엇"과 "어떻게"를 분리합니다. 이렇게 책임을 분리하는 방식은 코드를 조금 더 "선언적"으로 작성할 수 있는 관점을 제공하며, 이것은 React가 지향하는 것과 완전히 동일합니다..(더 자세한 내용은 선언형 UI 라이브러리로서의 React를 다룬 이전 포스팅을 참고해 주세요.)
Suspense는 Algebraic Effects를 사용해서 부모 컴포넌트에게 로딩 상태에 대한 처리의 "책임"을 넘겼으며, 따라서 메인 컴포넌트에서 로딩 UI 표시의 책임을 "분리"하게 됩니다. 이는 UI Component를 조금 더 "선언적"으로 작성하도록 만들어주며, 이것은 React가 지향하는 바입니다.
Conclusion
Algebraic Effects는 아직 특정한 프로그래밍 언어에 구현되어 널리 사용되고 있는 상용화된 패러다임은 아닙니다. (적어도 Javascript에서는 Algebraic Effects를 지원하지 않습니다.) 하지만 이것이 제공하는 Basic Idea는 React의 선언적인 속성과 IoC(Inversion of Control)에 많은 영향을 주었으며, Suspense도 그 영향을 받은 Component라고 이야기할 수 있습니다.
가볍게 시작했다가, 꽤나 많은 내용을 다루게 된 글인 것 같습니다. 다음 포스팅에서는 실제로 이 Suspense가 내부적으로 어떻게 구현되어 있는지를 살펴보려고 합니다.
Reference
https://overreacted.io/algebraic-effects-for-the-rest-of-us/
An Introduction to Algebraic Effects and Handlers
Programming with Algebraic Effects and Handlers
https://stackoverflow.com/questions/49626714/what-does-algebraic-effects-mean-in-fp
'Frontend' 카테고리의 다른 글
Node.js + Puppeteer Memory Leak Handling (2) | 2022.11.20 |
---|---|
Suspense SSR Architecture in React 18 (1) | 2022.09.18 |
Conceptual Model of React Suspense (0) | 2022.09.12 |
프론트엔드 테스트 전략 - (2) Integration Test (1) | 2022.09.04 |
프론트엔드 테스트 전략 - (1) Overview (0) | 2022.09.04 |