<번역> JavaScript 에서의 상태관리

keonhee Lee·2022년 6월 29일
0

State management in JavaScript

자바스크립트 에서 의 상태관리


브라우저 기반의 자바스크립트 어플리케이션 에서의 상태관리는 종종 불필요해 보이기 때문에 어려운 일이다.

오늘의 목표는 상태관리를 불필요하게 어렵게 만드는 몇가지 일반적인 문제점들을 피하는 몇가지 큰 틀을 잡아보는 것 입니다.

The principles ( 원칙들 )

If you want more effective programmers, you will discover that they should not waste their time debugging, they should not introduce the bugs to start with.

Edsger W. Dijkstra

  • 모든 유니크한 상태 데이터변수 / 프로퍼티 로 나타내는 것
  • 어플리케이션 내 의 모든 파생 데이터 ( derived data ) 를 functions / method 로 나타내는 것
  • 데이터 안의 의존성 ( dependencies ) 을 명시적으로 정의하는 것 과 상호참조 ( circular dependencies ) 들을 방지하는 것
  • 상태 데이터가 변경되었을때 , 의손성의 순서 (dependency order ) 안의 파생 데이터를 재검토 하는것
  • 파생중 일 때에는 절대 상태변경을 하지 않는다, 상태 변경중 일 때에는 절대 파생시키지 않는다.
  • Native DOM 을 위한 templates 나 DSLs 보다는 추상화를 선호한다.
  • 가능한 스코프 안 에서 로컬 화 ( localised ) 된 상태를 선호한다.

다음과 같은 원칙들이 있는것을 알기 바란다.

이러한 원칙들은 :

  • frameworks 의 넓은 범위에 걸쳐서 효과적이다. ( 심지어 jQuery 또한 마찬가지 )
  • 오래 전 부터 존재하던 문서화 되고 연구된 확고한 원칙들을 따른다.
  • 후 에도 지속적으로 유의미( relevant ) 할 것이다.
  • 다른 많은 프로그래밍 패러다임이나 스타일에도 적용 가능하다.


State vs derived data

원(Circles) 이 있다고 가정해보자.

원 은 보통 세가지 값들 ( SVG, etc..) 을 통해 대표 되는데, x , y , 그리고 r (radius) .

이러한 세가지 값 들은 원의 상태를 나타내고, 세가지 값 중 한가지만 바뀌게 된다면 새로운 원이 생길 것이다.

다른 모든 프로퍼티 또는 동일한 원을 표현하는 방법은 아래의 세 가지 값에서 유래될 수 있습니다.

예를 들어 :

  • 원의 극 좌표
  • 원의 둘레 ( 원주 )
  • 원의 면적

주어진 상태의 파생된 값 을으변하는 것은 의미가 없습니다.

예를 들어, 2 * PI * Radius 이외의 둘레를 가지고 있는 은 더이상 정의상 으로는 더이상 이 아니게 됩니다.

설명하자면, 극좌표 원의 위치가 "상태"로 지정될 수 있고, 여기서 x/y 값이 "유래"될 수 있다는 점에 유의 해야 합니다. 마찬가지로, 원의 둘레, 반지름 및 면적은 원의 크기를 나타내기 위해 모두 교환 가능하므로 이러한 특성에 대한 "상태"와 "유래"라는 명칭은 다소 임의적( arbitrary )입니다.

또한, 컨텐츠의 article 에서 제목을 추출 하는것 처럼 원론적인 (derivations ) 것들이 “소실 ( lossy )” 될 가능성이 있습니다. 이 article 의 제목 하나만으로 article 을 재구성 하는것은 간단하게 불가능 합니다.

이러한 경우에는 전체 article 을 “상태" 로 사용하는것 과, 파생된 값 으로 만 추출의 과정을 거치는 것 은 조심스러울 수 있습니다.


Unique state

원의 위치는 이분법 적으로 구분 될 수 있는데, ( 극좌표 의 상태, 데카르트 좌표의 상태 )

복제된 상태는 보통 더 큰 복잡성을 초래합니다.

아래의 코드는 가상 원 객체의 포지션을 두가지 좌표 시스템 ( 극좌표, 데카르트 좌표 ) 로 나타내는데 :

const myCircle = {
  r: 100,
  theta: Math.PI / 2
};

function cartesian (c) {
  return [c.r * Math.cos(c.theta), c.r * Math.sin(c.theta)];
}

이 경우 극좌표 가 상태(myCircle 객체의 프로퍼티)로 지정되고 데카르트 값이 도출됩니다( 위의 연산 을 사용).

여기에는 극좌표 또는 데카르트 좌표를 도출하는 두 가지 가능한 간단한 방법 (strategies)만 있습니다.

두 가지 함수는 같은 유니크한 상태를 기반으로 하기 때문에, 테스트는 개별적으로 통과할 수는 없지만 상호 참조될 경우에는 통과 할 수 없습니다.

아래의 코드 또한 비슷한 원 객체의 포지션 데이터를 나타낸다 :

function cartesian (c) {
  return [c.x, c.y];
}

function polar (c) {
  return [c.r, c.theta];
}

이 경우에는 극좌표 와 데카르트 좌표 모두 상태 처럼 다루어지고 있지만 (원 객체의 프로퍼티 처럼 다루어 졌어야 했지었지만)

처음에는 코드가 쉬워 보일 순 있지만, 과정 속에서 복잡도 가 변화했고 , 아마도 상승 되었을 것이다.

코드를 읽는 것만 으로는 보여지지 않고, 설사 이 네가지 상태 프로퍼티들이 내부적으로 항상 지속적일 지라도,

극좌표와 데카르트좌표 가 독립적으로는 테스트를 통과 하는 것이 가능하긴 하지만, 원 객체를 지속적이지 않은 상태와 교차 참조 하게 된다면 실패 할 수도 있을것이다.

이러한 상황에서의 잠재적인 숨겨진 상태 복제에 주의해야 한다 :


Updating DOM

DOM 을 업데이트 하는것은 암시적으로 비동기 로직을 가지고 있는, 유저와의 상호작용을 수용하기 시작하면서 중요해 지게 되었다.

우리가 상태와 도출된 값 사이에서의 완벽한 초기의 셋업을 가지고 있다고 생각해보자.

유효한 초기 상태에 대해 일련의 값을 도출한 후 바닐라 자바스크립트 ( RAW JavaScript ) 를 사용하여 이러한 값을 시각적으로 DOM 으로 구축하는것은 상대적으로 쉽게 나타낼 수 있을 것이다.

이제, 상태 안의 무언가가 바뀌었고, 그 바뀐 상태에 응답해야 한다고 생각해보자.

이상적으로는 새로운 상태를 정의함으로 우리는 간단하게 바뀐 상태를 model 할수 있을것 이고, 재 도출 한 후 브라우저에게 새로운 DOM이 어떻게 보여야 할지 알려줄 수 있다.

예를들어 DOM이 REST API 이었다면, 간단하게 PUT request 를 해줌으로서 완료 했을 것입니다.

불행하게도, DOM은 상응하는 PATCH endpoint 만을 제공하는데, 이 말은 우리가 PUT request 를 simulate 하기 위해서 무엇이 우리를 patching 하는지 추적해야 해야만 합니다.

즉, 상태와 도출된 값이 업데이트 될 때마다, “이전” 값 과 “이후” 의 차이를 계산 해야 합니다.

이 “계산된 차이 ( calculated difference) ” 란 무엇을 DOM API 에 pass 해 주어야 하는지를 말합니다 ( “이후” 의 값 만을 말하는것이 아님)


Reliably patching the DOM by hand is infeasible for any large codebase.

이러한 문제를 해결하기 위해서는 자동화된 주요 task 들을 다루어주는 도구 (tools) 가 필요합니다.

  • 어떠한 파생 함수의 값이 각 상태 변화에 일치 하는지 추적해야 합니다.
  • 파생된 함수를 재 실행 하고, 리턴 밸류의 변화를 추적해야 합니다.
  • DOM API update method 에 적합한 변화된 값을 pass 해야 합니다.
  • 계산이 완료되기 전의 성급한 DOM 업데이트를 지양해야 합니다.

운좋게도 위의 일들을 적합하게 하는 많은 tool들이 이미 존재합니다. 보통 주된 차이점 이라면 tool 에

dependency information 을 feeding 하는 방식일 것입니다.

현재는 두 가지의 자주 쓰이는 스타일들이 있습니다.

  • DAGs 는 직접적으로 마지막에 DOM 엘리먼트와 같이 값에 변화를 줍니다.
  • Virtual DOM 은 DOM 을 simulate 하지만 차이를 계산하는것에 최적화 되어있습니다.

위의 것들중에 어느것이든 본인의 입맛에 (most intuituve to you ) 을 사용하면 됩니다 .

개인적으로는 DAG 접근 를 ‘magic’ 이 덜 포함되었이 때문에 선호합니다.


**Don’t template, abstract!**

템플릿 은 tools 에 대한 접근을 제어 하고 그것들의 생태계 ( native )를 불명확하게 만듭니다.

추상화는 도구 자체를 조합하고 연장함 으로 더 유용하게 만듭니다.

HTML templating 은 HTML을 string 으로 나타내는 언어에서의 서버사이드 에서는 인기가 있기도 하고 민감하기도 합니다.

Tempating 시스템은 유효하지 않은 마크업에 대한 수정을 또는 경고를 할수도 있고, XSS 보안 문제에 대한 방어를 제공할 수도 있습니다.

브라우저 안의 자바스크립트는 real DOM 에 직접적으로 접근 하기 때문에 스트링으로 나타나는 HTML ( HTML as strings) 패러다임에는 적용되지 않습니다.

template string 을 새로운 DOM 엘리먼트로 변환하는것은 숨겨졌고 ( is hidden away 금지되었다 정도로 해석될 거라고 생각 ) 감지하기 힘든 보안 문제를 야기할 수 있습니다.


Write functions to accept, compose and return native DOM elements.

Native DOM 엘리먼트 는 당신의 코드, 상위 라이브러리 와 브라우저 사이의 궁극적인 상호 운용을 제공합니다.

현존하는 자바스크립트 라이브러리들은 raw DOM 엘리먼트들을 어떠한 방식으로 만들어져 있는지 상관하지 않고happily 하게 허용하고, 아주 소수의 라이브러리들 만이 직접적으로 프로세스 되지 않은 template 코드를 허용 합니다.

Templating system을 사용해야만 한다면 JSX 와같은 1 : 1로 DOM을 map 할 수 있는 라이브러리를 사용하면 됩니다.

일반적인 예시

<div className="sidebar" />

JSX 를 사용한 template의 예시

React.createElement(
  'div',
  {className: 'sidebar'},
  null
)

많은 다른 프레임워크들은 template 이 필요하지 않은 single-element-function의 접근법을 채택하고 있고,

Templating system을 새로 다시 배우기 보다는 개인적으로는 간단한 React.div() 방식을 선호 합니다.


**Prefer local scope over global scope**

버그는 스코프 안의 모든것들에 영향을 줄수 있습니다.

거대한 global state 객체를 함수에 넘겨주는 것은 거대한 global bugs 를 초래합니다.

작은 파생된 값을 함수에 넘겨주는 것은 local 에 한정적인 (localised ) 버그를 초래합니다.

global distributed state 를 넘겨주는것은 $280M 을 잃는것과 같습니다.

함수가 허용하는 스코프의 버그는 그 스코프의 데이터 양과 같습니다.

너무 많은 arguments 들이 있거나, 객체에 메소드를 부여할때마저도 외부의 상태를 내부의 함수에서 참조하는 유혹을 피하고, function argument 를 사용하여 작업하는것을 지향합니다.

출처 : https://codeburst.io/state-management-in-javascript-15d0d98837e1

0개의 댓글