-
[2020.12.28] React 내 Render Stream 처리 기법(with kefir.js)개발 블로깅/기타 개념 2020. 12. 28. 21:41
Render Stream이란, 기능을 동작시키는데 필요한 로직들을 Stream으로 처리하여 단순하게 렌더링 기능을 구현할 수 있는 방식을 말합니다.
아래 예시 코드를 보시면, 각 로직 처리 부분을 단순히 FromStream 컴포넌트의 Stream props로만 내려주기만 하면, FromStream 컴포넌트에서 어떠한 로직이든 상관없이 로직을 수행하고 따로 상태 변경 로직 없이도 리렌더링을 처리하기 때문에, 코드 로직이나 상태가 매우 Less되어 프로젝트 관리나 메모리 성능면으로도 엄청난 이점을 주게 됩니다.
Render Stream기법은 반응형 프로그래밍(Reactive Programming) 기반으로 구현한 방식입니다.
해당 글은 작성자 Dmitriy Kharchenko 님의 'how to Render Streams with React'의 아티클을 번역하였습니다.
외국 아티클 번역을 많이 해보지 않아, 아직 발 번역 수준입니다..
하지만 내용 이해에는 문제가 없을 듯 하여, 귀엽게 봐주시면 감사드리겠습니다!
당신은 리액트에서 streams을 사용하기 위해 자바스크립트 닌자가 될 필요가 없습니다.(자바스크립트 닌자가 뭐지...)
이 아티클에서 이와 관련된 몇가지 간단하게 만든 컴포넌트로 보여주는 예시가 있습니다.스트림은 굉장합니다. 클라이언트 사이드에서 일어나는 모든 것들은 스트림으로 줄일 수 있습니다. 예를 들어, DOM 업데이트나 redux 상태 변화, 또는 스크롤 이벤트 등에 관한 스트림 처리가 있을 것입니다.
숙련된 스트림 스킬을 구현하는 것은 현대 Client-Side 처리에 있어 주요한 요소 중 하나입니다.
운이 좋게도, 리액트를 이용해서 스트리밍 렌더링을 어떻게 사용하는지 엄청 간단한 방식이 있습니다. 그리고 스트림을 렌더링 한다는 것은 스트림 데이터를 특정 시각적 데이터로 변환한다는 것을 의미합니다. 페이지 내 엘리먼트의 움직임이나 날짜/시간 같은 값이 될 수 있습니다.
Tip: 팀 구성요소 단위로 더 나은 앱을 만드려면 Bit을 사용하십시오. 이것은 저장소 전반에 컴포넌트를 쉽게 재사용할 수 있고, 앱 전반으로 UI 기준을 유지할 수 있고, 다른 프로젝트 끼리의 여러 컴포넌트들을 새롭게 합쳐서 빠르게 작업할 수 있습니다. 여기서 시도해보세요.
몇 가지 예시
예시를 한번 봐봅시다. 예제를 확인할 시간이 부족하다면, 해당 아티클에서 실제 렌더 스트림을 설명하는 부분으로 스크롤을 이동하십시오. 그러나 이 예시들은 아주 간단하다는 것을 제가 보장하겠습니다!
스트림을 위해서, kefir 이라는 굉장하고 추천할만한 라이브러리를 사용할 것입니다. 꼭 이 라이브러리를 이용하지 않아도 괜찮습니다. 다른 라이브러리를 이용하여 해당 예시를 구현해도 좋습니다.
Date/Time
그럼 수시로 시간이 업데이트 하도록 date/time 값을 렌더링 해봅시다. 여러분들이 직접 이것을 시도해봤었다면, 간단하게 보이는 컴포넌트가 실제로 부피가 얼마나 큰지 기억할 것입니다. 그러나 이와 달리 아래 스트림 예시는 매우 간단합니다.
import React from 'react'; import Kefir from 'kefir'; import FromStream from './FromStream'; const dateStream = Kefir.interval(1000).map(() => new Date().toString()); export default () => ( <FromStream stream={dateStream}> {(dateString) => dateString} </FromStream> );
Scroll
Scroll Bar로 렌더링하는 컴포넌트 예시는 조금 더 복잡할 수 있습니다.
import React from 'react'; import Kefir from 'kefir'; import FromStream from './FromStream'; const scrolledPercentStream = Kefir.fromEvents(document, 'scroll').map((e) => { const scrollY = window.document.pageYOffset; const scrollHeight = e.target.body.scrollHeight; return scrollY / (scrollHeight - windiw.innerHeight) * 100; }); export default () => ( <FromStream stream={scrolledPercentStream}> {(percent) => ( <div className="bar" style={{ top: `${percent}%` }}></div> )} </FromStream> );
스크롤 퍼센티지마다 스트림 매핑하는 간단한 스크롤 스트림 예시가 여기 있습니다. 스크롤 바 커서 스타일을 top 값 세팅에 이용합니다.
Animations
만약 react-transition-group 와 같은 라이브러리를 이용했다면, 아래 코드는 아마도 당신에게 친숙할 것입니다. 제가 좋아하는 점은 애니메이션을 위해 추가로 종속성을 로드할 필요가 없다는 것입니다. 아래 코드를 보시면, 제가 말한 내용들이 이해가 될 것입니다.
import React, { PureComponent } from 'react'; import FromStream from './FromStream'; const ENTERING = 'ENTERING'; const ENTERED = 'ENTERED'; const EXITING = 'EXITING'; const EXITED = 'EXITED'; const inStates = [ENTERING, ENTERED]; const outStates = [EXITING, EXITED]; const createAnimationStream = (events, timeout) => { return Kefir.stream((emitter) => { emitter.emit(events.shift()); const interval = setTimeout(() => { const nextEvent = events.shift(); if (!nextEvent) { emitter.end(); } else { emitter.emit(nextEvent); } }, timeout); return () => { return clearInterval(interval); }; }).toProperty(); }; export default class Animate extends PureComponent { static defaultProps = { in: null, out: null, timeout: 100, } render() { if (this.props.in === null && this.props.out === null) { return this.props.children(ENTERED); } const events = this.props.in ? inStates : outStates; const animateStream = createAnimationStream(events, this.props.timeout); return ( <FromStream stream={animateStream}> {this.props.children} </FromStream> ); } }
이 코드는 이전 예시보다 약간 더 길어 보이지만, react-transition group 라이브러리로 인해 여전히 짧은 편입니다. 물론, 그만큼 기능은 적지만 몇 가지 코드를 추가하여 확장할 수 있는 요소가 많습니다.
FromStream component
그러면, 이번엔 FromStream 컴포넌트를 한번 확인해봅시다.
import { PureComponent } from 'react'; export default class FromStream extends PureComponent { constructor(props) { super(props); this.state = { value: false }; } componentDidMount() { this.initStream(); } componentDidUpdate(prevProps) { if (prevProps.stream !== this.props.stream) { this.initStream(); } } componentWillUnmount() { if (this.unSubscribe) { this.unSubscribe(); } } initStream() { if (this.unSubscribe) { this.unSubscribe(); this.unSubscribe = null; } if (this.props.stream) { const onValue = (value) => { this.setState(() => ({ value })); }; this.props.stream.onValue(onValue); this.unSubscribe = () => this.props.stream.offValue(onValue); } } render() { return this.props.children(this.state && this.state.value); } }
여전히 코드가 50 라인 이하여서 매우 간단하고 좋네요! (마법 아닙니다.) 간단하지만 React 앱에서 스트림 기능을 동작시키는 데이는 매우 강력합니다.
위의 예시들은 때때로 스트림이 매우 유용하고 단순한 React 컴포넌트로 이벤트 데이터를 렌더링하거나 애니메이션과 같은 복잡한 경우에 매우 유용 할 수 있음을 보여줍니다.
믈론, 예시 코드에서는 설명을 위해 명확하고 단순화되어 있는 코드이므로, 실제로 사용할 때는 아마 더 복잡한 코드가 될 수도 있습니다.
많지는 않지만, 실제 프로덕션에서 스트림을 활용하는 사례를 몇 가지 발견할 수 있었습니다. Holloway에서 우리가 스트림을 사용하는 방법에 대해 확인할 수 있었습니다. 리더 앱 내에서는 우리가 사용한 위 컴포넌트 예시와 거의 같은 수준으로 사용되었다는 것을 발견할 수 있었습니다.
만약 당신이 이 아티클의 예시가 설명이나 디테일한 부분이 부족하다고 생각한다면, 혹은 stream과 React를 사용하는 것이 우려가 되거나 다른 생각을 가지고 있다면 저에게 연락을 주십시오. 저는 피드백을 받는 것을 선호합니다.
Original Article
Learn More
반응형'개발 블로깅 > 기타 개념' 카테고리의 다른 글
Preload의 개념, 그리고 올바른 사용법 (1) 2021.09.05 자바스크립트 검색엔진 최적화(SEO) 가이드 총 정리 (0) 2021.07.20 [2020.10.16] Google Chrome V8 엔진을 파헤쳐보자 (2) 2020.10.17 [2020.09.16] (운영체제) 사용자 수준 스레드와 커널 수준 스레드의 차이 (1) 2020.09.16 [2020.08.04] 브라우저 동작원리 (1) 2020.08.04