React에서 많이 쓰이는 상태관리 라이브러리는 redux, mobx도 있지만, 나는 실제 프로젝트에서는 recoil만 사용해봤다. 대충 지금까지는 atom과 selector로 상태를 선언하고, recoil에서 제공하는 훅들을 사용하여 단순히 전역변수처럼만 썼다.
지금까지 내가 느꼈던 recoil의 장점은
1. 컴포넌트 계층의 방향성에 구애받지 않고 state를 공유하고 동기화할 수 있다는 점
2. 지저분하게 props에 state와 setFunction을 넘기지 않아도 된다는 점
3. selector를 이용해서, atom을 관찰하다가 변화에 따라 state를 처리한다는 점
4. 기존에 사용하던 훅들과 이질감 없이, recoil 훅을 통해 코드 가독성이 높아졌다는 점
정도 인 것 같다.
단순히 이 장점들이 좋아서 사용해왔던 라이브러리이고, 그 내부가 어떻게 동작하는지는 개념적으로 얕게만 숙지하고 있었다.
그런데 궁금해졌다.
selector는 어떻게 atom의 변화를 감지할까?
서로 다른 컴포넌트에서 recoil state를 사용할 때, 누가 먼저 updated state를 받아올까?
어떻게 state들의 변화가 각 컴포넌트에 뿌려지는 것일까?
등등
recoil의 내부 원리가 궁금해졌다.
가장 좋은 방법은 소스코드를 까보는 것이다. 그래서 Recoil Drill 프로젝트를 시작했다. 꾸준히 계속해서 코드를 보다보면, 분명 그 원리가 보이고 코드 개선까지도 할 수 있을 것이라 믿는다.
github link : devKangMinHyeok/Recoil
fork하는 것은 누구나 할 수 있다. recoil repo 들어가서, fork 버튼 누르면 끝이다.
그럼 로컬에 clone 해보자.
git clone https://github.com/devKangMinHyeok/Recoil.git
그리고 yarn도 facebook이 만들었기 때문에, npm으로 설치하면 의존성 문제가 발생한다.
그러니 root 에서 yarn 명령어를 실행하여 package를 설치하자.
yarn이 설치되어있지 않다면, 구글링을 통해 yarn을 설치하면 된다.
그럼 문제 없이 패키지가 잘 설치된다.
일단 먼저 recoil 프로젝트는 facebook에서 하는 프로젝트이다. 그런데 아직도 실험단계라 메이저 버전이 0이다.
먼저 그럼 facebook에서 만든 오픈소스들이 무엇들이 있는지 대충 살펴보자.
meta github : meta

meta experimental github : metaexperimental

Meta Open Source : opensource.fb.com

내가 기존에 meta가 만들었다는 것을 알고 있던 것들은 React, React-Native, Recoil 정도인 것 같다. 그런데, 사실 꽤나 유명한 오픈소스들이 더 존재하는데, 프론트 관련한 것들만 추려보면 다음과 같다.
이 친구들은 한 번쯤 써보거나, 들어봤던 친구들인데, 얘네들도 facebook이 만들었다는 것은 몰랐다. 물론 수많은 오픈소스들이 더 존재한다. 과연 facebook이 이렇게 굵직한 프로젝트를 운영하는 조직인데, 망할 수 있을까 싶다.
내가 facebook의 다른 오픈 소스 이야기를 왜 했냐면, git clone으로 프로젝트를 까보면 이상한 코드를 보게 된다.
수많은 파일 중에 비교적 짧은 코드 하나를 가지고 왔다.
Recoil_treeCacheLRU.js
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall recoil
*/
'use strict';
import type {TreeCacheImplementation} from './Recoil_TreeCacheImplementationType';
const {LRUCache} = require('./Recoil_LRUCache');
const {TreeCache} = require('./Recoil_TreeCache');
function treeCacheLRU<T>({
name,
maxSize,
mapNodeValue = (v: mixed) => v,
}: {
name?: string,
maxSize: number,
mapNodeValue?: mixed => mixed,
}): TreeCacheImplementation<T> {
const lruCache = new LRUCache({maxSize});
const cache: TreeCache<T> = new TreeCache({
name,
mapNodeValue,
onHit: node => {
lruCache.set(node, true);
},
onSet: node => {
const lruNode = lruCache.tail();
lruCache.set(node, true);
if (lruNode && cache.size() > maxSize) {
// $FlowFixMe[incompatible-call]
cache.delete(lruNode.key);
}
},
});
return cache;
}
module.exports = treeCacheLRU;
자, 뭐가 이상한가? 분명히, js 파일인데, ts처럼 작성되어 있다. 그리고 VS code에서는 자체적으로 오류를 표출하면서 파일이 빨간줄로 도배가 된다.
사실 여기서 굉장히 당황했는데,flow가 그 이유였다.
Flow는 facebook에서 개발한 js 정적 타입 검사기라고 한다. 즉, js에서 ts의 장점을 가져다 쓸 수 있다는 말이다.
typescript와 간단히 비교해보자면,
정도 된다.
그냥 ts 쓰면 좋겠지만, facebook도 자기들이 만든 flow를 주력으로 사용해서 그런 것 같다.
이전에 yarn 명령어를 통해 의존성 모듈을 전부 설치했다면, 여기서부터 크게 할 것은 없고, vscode 세팅만 조금 바꿔주면 된다.
자세한 사용법: flow-for-vscode
일단 extension에서 Flow Language Support를 설치한다.

그리고 전역에서 flow 설정을 바꿔줄 수도 있지만, 나는 Recoil 외에는 flow를 사용하지 않아서 그러고 싶지 않았다.
그래서 일단 vscode 워크스페이스 세팅을 조금 바꿔준다.
루트 경로에 .vscode 폴더를 만들고, 거기에 다음과 같은 파일을 넣어주면, 문제 없이 작동한다.
setting.json
{
"flow.pathToFlow": "${workspaceRoot}/node_modules/.bin/flow",
"javascript.validate.enable": false
}
첫줄은 해당 워크스페이스의 node_moudles에 설치된 flow를 참조하도록 하는 것이고,
둘째줄은 좀 논란의 여지가 있는 부분 같은데, 일단 아래 github issue를 참고해보자.
What does javascript.validate.enable": false mean?
#27

해당 링크의 위 내용을 보면, "javascript.validate.enable": false 옵션은 표준 옵션이 아니라고 한다. 그리고 중요한 것은 VS Code의 코드 검사 기능을 막는다는 것인데, 아래 내용을 보자.

실제로, 이 부분을 지적한 게시글이 좋아요 40개를 받았지만, 답변은 '알아서 잘하고, 어쩔 수 없다' 식이다.
결국, Flow의 한계는 VS Code를 최고의 방식으로 사용할 수 없다는 점이다.
저 옵션을 가지고, 간단한 예시로 실험을 해보자.
자 먼저 "javascript.validate.enable": false 옵션을 없애보자.

문제가 되는 것은 flow annotation을 VS Code에서는 TS 문법으로 받아들이기 때문에, 아래와 같은 문제가 VS code 인터페이스에 표시된다.

그리고 다음 문제는 VS Code extension으로 내가 쓰고 있는 ESLint가 "javascript.validate.enable": false 옵션을 제거한 현재는 작동을 하는데,

이 "javascript.validate.enable": false 옵션을 추가하면 ESLint가 작동을 하지 않는다.

즉, "javascript.validate.enable": false 옵션을 사용하면, TS checker를 끌 수 있지만, 다른 extension들이 먹통이 되고, 사용하지 않으면 거슬리는 빨간줄을 계속 봐야한다.
물론 ESLint를 실행시켜서 확인할 수 있겠지만, extenstion으로 사용하지 못하는 점은 매우 아쉽다.
대체 recoil 개발자들은 어떤 환경에서 작업하고 있는 것일까?
쨌든, 일단 이 옵션을 넣는 것이 최선이라고 판단했고, 나는 코드를 읽는 입장이기 때문에, 이렇게 옵션 세팅을 했다.
그런데, 사용하다보니 이런저런 문제가 많았다. 쓰던 extension이 있다가 없으니까 굉장히 불편했다.
그래서 다른 방안으로 built-in extension에서 TypeScript and JavaScript Language Features 를 disable 처리해줬다.


이런 방법도 있다는 정도만 알아두면 좋을 듯 하다. 두 방법 다 그리 만족하지는 못했다.
VS Code는 자체적으로 syntax highlighting을 지원하는데, 문제는 아래와 같다.

set 함수를 보면 syntax highlighting이 잘 작동하지 않는다.
이는 language 모드를 js에서 ts로 바꿔주는 편법을 사용할 수 있는데, 오른쪽 아래 빨간 네모를 친 버튼을 눌러 ts로 바꿔준다.

그런데 이렇게 해결해도 되는지는 모르겠으나, 일단 코드를 빨리 읽어보고 싶기 때문에 이정도로 해결하고 넘어가려 한다.
이제 개발환경을 가져와, 코드를 살펴볼 수 있는 환경을 만들었으니, 앞으로는 코드를 차근차근 읽어보도록 하겠다.