9/26 ~ 9/30
useEvent
- RFC: useEvent ❌
- useEffect나 useCallback에서 디펜던시로 추가하고 싶지는 않지만 사용해야하는 경우 ref로 조건 처리하고는 했는데
- 이런 상태를 대체할 수 있을 것이라 보고 있었으나
- 뒤돌아서 생각해보면 디펜던시 대혼란에 빠지게 될 것 같았다
- 린트 룰을 꺼버리는게 차라리 나을지도 모르는? (이게 더 명시적이니깐)
- 근데 useEvent가 대혼란을 극복하지 못하고 사라지는중
default export
- [번역] 자바스크립트 모듈에서 default export는 끔찍합니다
default export는 어떤 이름으로든 export 할 수 있으므로 여러 개발자는 default import를 위해 각각 다른 이름과 명명 스키마를 생각할 것입니다
- 만약 다른 모듈에서 명명된 두 named export의 이름이 같은 경우 어떻게 해야하나요?"라고 생각할 수 있습니다. 다행히 이 문제는 import aliasing으로 쉽게 해결할 수 있습니다.
- 그러나 import 할 때마다 이름을 고민하는 것 보다 하나의 파일 안의 스코프에서 일회성으로 이름을 고민하는 것이 훨씬 편합니다.
- default export로 매번 import 할때마다 고민하기보다 동일한 이름의 named export가 발생한 시점에 일회성으로 고민하는 것이 더 낫다
- 그럼에도 default export 를 써야겠다면?
- index 파일을 만들어서 여기에 전부 default import 하고 이 index 에 접근하도록
{}
- (번역) 소프트웨어 변경에 대해 이야기하는 방법
우리는 {새로운 가설} 때문에 {변경}이 {미흡한 점}을 개선할 것으로 기대합니다
- 무섭게 다가오는 문장
- 리팩터링을 통해 더 나아졌기를
9/19 ~ 9/25
flex 레이아웃
- 항상 잘 사용하지 못하는 flex
- 이 글 여러번 읽었더니 드디어 이해가...
- 감성으로 이해하는데, 이제 이성으로도 살짝!
- 두번 정도 더 겪으면 될 것 같다
- 하지마 알고 있고 이해하는 것과 다르게 제대로 사용하는 것
- 제대로 사용하는 것까지 이어져야 완전히 내것이 되는데
- 가장 조심해야하는 것이 무의식적인 사용
- 지금도 잘 사용하고 있다고 생각하는 것이 있다면....
- 나혼자만의 착각
데드라인 드리블 글쓰기
- 데드라인 드리블 글쓰기
- 한달에 하나 정도 더 쓰자는 마음이였는데 실행하지 못한지 반년 훌쩍
- 나 또한 마찬가지로
한달동안
을 마지노선으로 가져간다... - 글감을 바로 메모해두며 그것이 글이 되어야하는데, 내 글감은 그대로 한마디가 되고 끝나버린다
- 짧은 생명주기
스터디를 진행하고 나면
- 책을 한권 읽는다던지 그리고 후기를 적는다면
- 이런느낌으로 정리해두는 것도 좋겠는데
- 스터디를 참여하는 것 자체가 부담이라면
- 이런 온라인에 올라와있는 후기와 함께 개인적으로 느낀 것을 정리하면서 읽어보는 것도 재밋겠다
- 단위테스트 책을 읽어보고 싶었는데, 후기에 올라온 내용처럼 보통 백엔드 위주의 내용이라 어떻게 접목할 수 있을까 하는 고민이였다
타입스크립트 프로젝트 클린 아키텍처 (의존성 분리)
- (번역)타입스크립트 프로젝트를 위한 궁극적인 클린 아키텍처 템플릿
- 예제는 core, data, di, presenter 구성인데, RN 플젝을 이렇게 구성한다면?
- presenter에 일반적인 components, container
- core에서는 presenter에서 사용할 유즈케이스
- data에 core의 유즈케이스에서 사용할 데이터 구조
- di에서 data를 core의 유즈케이스에 따라 변환하여 실제 presenter에서 사용
- 패키지 간의 종속성 설정에서 이런 의존성을 확인할 수 있다
- data는 core에 의존
- di는 data, core에 의존
- presenter는 di, core에 의존
- 이런 의존 관계로 인해 core가 변경되면 data, di, presetner 모두 다시 빌드 되어야한다
- data가 달라지면 core에서 처리하는 과정이 변경되어야하고 이에 따라 di도 달라질 수 있다
- di가 변경되면 core에서 data를 변환하는 과정이 달라져야하고
- core가 변경되면 data를 변환한 di가 달라지고 presenter에서 보여지는 것이 달라질 수 있기 때문
- 확실하게 의존성 분리가 되겠는데, 갑자기 리덕스가 생각난다 🤔
9/12 ~ 9/18
프론트엔드 아키텍쳐
- 프론트엔드 아키텍쳐 트랜드?
- redux + saga가 원탑이라고 생각하던 지난 시간
- 지금은 서버 API 캐싱을 잘 관리하는 것이 더 중요해졌다
- 이렇게 계속 변화되는 아키텍쳐 속에서 하고자 하는 의미만 알아두면 좋겠다
- 비즈니스 로직을 완전히 분리하여 View를 구성할 수 있다면 좋겠지만
- 분리하지 못한다면 비즈니스 로직을 포함한 거대한 컴포넌트가 되어버리는데
- 로직도 있고 View도 있고
- 분리하지 못한다면 비즈니스 로직을 포함한 거대한 컴포넌트가 되어버리는데
- 여기서 View만 이라도 한번 더 분리해서 UI 독립적일 수 있게 되면 좋겠지만
- 그럼 props drilling하게 되고 오히려 컴포넌트가 어디서든 재사용하는 것이 어려운
- UI 독립이기는 하지만 정말 독립적이지는 않은 이 로직에 대하여 의존적인 컴포넌트가 되었다
- 어차피 재사용 되지 못한다면 비즈니스 로직에 의존적이더라도 언제든 다른 것으로 교체가능하도록
- 재사용은 되지 않지만 교체는 수월한 최소한의 컴포넌트
선언형, 명령형 관계
- 선언형, 명령형 코드 그리고 추상화
- 명령형 => 어떻게(How)
const 등록완료된_리스트 = (list) => { const result = []; for (let i = 0; i < list.length; i++) { if (list[i].isCompleted) { result.push(list[i]) } } return result; };
- 선언형 => 무엇을(What) => 명령형에서 어떻게(How)를 감추고 무엇을(What)만 노출 => 명령형을 추상화하였다
const 등록완료된_리스트 = (list) => { return list.filter(item => item.isCompleted); };
- 선언형 내부에 명령형이 추상화되어 숨겨져있다
- 명령형 => 어떻게(How)
- 그렇다면 명령형 선언형 구분할 필요 없이 명령형을 구조적으로 정리하여 내부에 숨겨두는 것이 무조건 선언형인가?
선언형은 사이드 이펙 없이 순수해야한다
라는 내용에 따르면언제 어디서 + 어떤 상황에서 호출되어도 동일한 기댓값
을 주어야한다- 명령형 => 시간 순서를 지켜야하는 절차적으로 호출
- 선언형 => 순수하기 때문에 시간 순서 상관없이 어디서든 호출
- 모든것을 선언형으로만 작성할 수 없을 것 같다
- 선언형 내부를 따라가다보면 결국 시간 순서에 따라 작성되어있는 명령형이 감춰져있을 것
- 이것들을 추상화하여 잘 전달하고 사용하도록
- 그럼 이런 명령형 코드를 찾아보게 되는 시점이 언제 있을까?
- 추상화되어있는 RN 컴포넌트나 브릿지 사용하다가 무언가 오잉? 하는 시점에 따라가다보면
- 어디엔가 나열되어있는 명령형 코드를 찾아볼 수 있다
- 이 글을 읽고 하나 더 와닿은
회사 프론트 동료들과 추상화와 선언적인 코드의 관계에 대해서 이런저런 이야기를 나눴습니다. 머릿속에 추상적으로 있던 개념이 좀 각이 잡혀서 ㅎㅎ 한 번 글로 적어보려 합니다.
- 이야기 한 것 만으로 머릿속에서 정리하고 흘러갈 수 있는 것을 기록하면서 다시 한번 다듬어서 남겨두기
9/5 ~ 9/11
리액트 베타 문서
- 기대되는 것: Offscreen
- 마음은 알겠지만...: When Strict Mode is on, React mounts components twice (in development only!) to stress-test your Effects.
- 초기 렌더에는 결국 모두 계산해야하니깐: useMemo won’t make the first render faster. It only helps you skip unnecessary work on updates.
리액트 컴포넌트 리렌더링
- 부모, props, state가 변경되었을때 리렌더가 일어날 것이라 생각한다
React에서 렌더링에 대해 이야기할 때, 우리는 대부분 render 함수의 실행에 대해서만 이야기합니다. 하지만 렌더링이 항상 UI 업데이트를 의미하지는 않습니다.
리렌더가 일어날 것
이라 생각되는 부분은render 함수의 실행
이였다- 실제로 diffing 연산에 의해 UI 업데이트는 발생하지 않았다
- 확인해보면 컴포넌트 자체는 리렌더 되는 것처럼 보이지만 DOM은 업데이트 되지 않았다
- 그러나 render 함수가 실행되었다는 사실은 변함없기 때문에
- 리액트는 diffing 연산을 계속 사용해야만했다
- DOM은 업데이트 되지 않더라도 함수형 컴포넌트 자체는 계속 재실행 되었다
- 참고:
클래스 컴포넌트와 다르게 함수형 컴포넌트는 아무 것도 변경되지 않는 경우에도 다시 함수를 호출하기 때문에렌 리렌더링된다
- 여기서 리액트의 연산은 최적화 되어있다는 신뢰 기반으로 🤔
DOM은 업데이트 되지 않더라도 함수형 컴포넌트 자체는 계속 재실행 되었다
는 부분을 최적화해야만한다컴포넌트의 render 함수가 호출된다는 것 > 모든 하위 컴포넌트들이 리렌더링 (render 실행)
- 의도치 않은 낭비
- 메모이제이션 하는 것 외에 코드 재구성(상태를 처리하는 코드를 별도의 리렌더가 되어야하는 컴포넌트로 이동)을 통한 리렌더링 방지해야한다
- 참고: memo를 사용하지 말아야하는 이유
- 메모리에 저장 + 얕은 비교
- 그나마 얕은 비교의 위험? 연산 낭비?을 해결 하기 위해
- 원시타입으로 사용하는 것을 추구하거나 정말 필요한 프로퍼티만 포함하도록 하였는데
- 이미 리액트가 diffing 으로 최적화를 해두었는데, 그 최적화를 오히려 방해하는 연산이 들어가게 되는 것
- 참고:
컴포넌트에 많은 props가 있고 자손이 많지 않은 경우 컴포넌트를 다시 렌더링하는 것과 비교하여 props가 변경되었는지 확인하는 것이 실제로 더 느릴 수 있습니다.
- 그럼 불필요한 메모리 저장을 제거하고 최소한의 연산을 위해 복잡한 컴포넌트에서의 memo를 제거해볼까
- 결론
- render 함수의 실행 자체를 방지하려면 상태가 변경되는 지점을 해당 컴포넌트 내부에 위치해야한다
- 순수 컴포넌트로 만들어서 자식 컴포넌트가 불필요하게 리렌더 되는 것을 방지한다
- 컨텍스트는 보이지 않는 props 또는 내부 props로 위험 인자다...
RN fabric
- 아직도 안드로이드 fabric 버전에서 ReactNavigation 라이브러리가 동작하지 않는다
- 이것저것 확인해보니 디펜던시 라이브러리가 문제가 되고 있는 듯 (react-native-screens, react-native-safe-area-context 무언가)
- ndk 오류 관련 깃헙 이슈 코멘트
- 최근에 RN 0.70.0 에서도 cmake로 변경되었던데....?
- 혹시나 해보면 RN 버전부터 다시 올려본다 => 그러나 safe-area-context가 아직 대응이 되지 않았기에 여전했다...
- 같은 이슈가 올라와있는데 아직....
- 근데 전부터 FabricExample 살펴보면 항상 의문인 것은
newArchEnabled=false
?- fabric 버전으로 올린 이유가 이거 때문 아닌가..?
- ☠️ 대체 왜...
리액트 상태 관리
- [번역] 리액트 상태 관리의 새로운 흐름
- 리액트에서는 전역으로 상태를 관리하는 방법에 대한 가이드라인이 없다
- 단순히 전역으로 전달하는 것은 Context로 할 수 있지만
- 여기서 말하는 상태 관리와는 목적이 다르다
- 참고: 전역상태 관리에 대한 단상 - Context API 성능
- 전역 상태 관리에 대한 최적화 처리가 되어있는 Redux와 달리 Context는 전달하는 목적만 있었기에 최적화 작업이 포함되어있지 않다
- 그렇다면 Context를 활용해 전역에서 접근한다면 어떻게 될까?
- 사용하는 곳에서 단순한 Context 변경사항에도 리렌더를 유발하게 된다
-
Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value prop가 바뀔 때마다 다시 렌더링 됩니다 . 상위 컴포넌트에서 React.memo 또는 shouldComponentUpdate를 사용하더라도 useContext를 사용하고 있는 컴포넌트 자체에서부터 다시 렌더링됩니다. . useContext를 호출한 컴포넌트는 context 값이 변경되면 항상 리렌더링 될 것입니다.
- 문서: useContext
- 컨텍스트 잘 사용하기: 메모이제이션 최적화
- 컨텍스트 분리 또는 하위 컴포넌트 memo 또는 useMemo로 jsx 반환
-
- 하위 컴포넌트에서 최적화를 진행하더라도 결국 불필요한 리소스가 소요된다
- 각각의 라이브러리들은 서로 다른 문제에 대해 서로 다른 해결방법을 제시했는데
- 요구사항에 맞는 적절한 라이브러리를 선택할 수 있어야한다
- 전역 상태 관리 라이브러리가 해결하고자 하는 문제
- 저장된 상태를 컴포넌트 트리 어디에서든지 읽어 올 수 있는 기능
- props drilling을 피하고 어디서든 이 상태를 가져와서 사용하고자
- 이때 상태 변경에 따른 리렌더링 최적화 필요
- 저장된 상태를 수정하는 기능
- 불편성을 유지하면서 상태 업데이트 할 수 있도록
- 렌더링을 최적화하는 메커니즘을 제공
- 실제 상태 변경에 따라 리렌더 되어야하는 컴포넌트에서 셀렉터를 구독하도록
- 사용하지 않는 곳에서는 이 상태를 구독할 필요가 없다 (불필요한 리렌더를 유발할뿐)
- 메모리 사용을 최적화하는 메커니즘을 제공
- 저장된 상태를 컴포넌트 트리 어디에서든지 읽어 올 수 있는 기능
- 이러한 문제를 해결하고자 Redux가 초기 도입되었다
- 그러나 전역상태 + 로컬 UI 상태 + 원격 API 상태 ....
-
예를 들어 로컬 UI 상태의 경우 데이터와 해당 데이터를 업데이트하는 메서드 모두 드릴링하는 것은 상황이 커질수록 상대적으로 빠르게 문제가 되는 경우가 많습니다. 이 문제는 상태 끌어 올리기와 함께 컴포넌트 컴포지션 패턴을 사용하면 꽤 깔끔하게 해결할 수 있습니다.
- 현재 마주하고 있는 문제이다
- 이를 끌어올리거나 컴포지션으로 정리해야하는데...
-
- 수 많은 상태들이 분리되기 시작하였다
- 여기서 또 다른 문제는 모든 상태가 전역에 들어가버릴 수 있다 (정말 불필요했다...)
-
실제로 많은 웹 애플리케이션은 주로 프런트엔드를 원격 상태 데이터와 동기화해야 하는 CRUD(생성, 읽기, 업데이트 및 삭제) 스타일의 애플리케이션입니다. 즉, 시간을 할애할 가치가 있는 주요 문제는 원격 서버 캐시 문제들입니다. 이러한 문제에는 서버 상태를 가져오고 캐시하고 동기화하는 방법이 포함됩니다.
- 이러한 이유 (=원격 API 상태를 관리하기 위해서)로 리액트 쿼리를 사용하게 되었는데
- 그러나 전역상태 + 로컬 UI 상태 + 원격 API 상태 ....
- 아직 리덕스, 컨텍스트에서 완전히 빠져나오지 못했으며
- 잘못된 구독으로 불필요한 리렌더를 유발하는 것을 정리하지 못했다
- 여기서 다시 보니 반가운 Context는 상태 관리가 아니다
9/1 ~ 9/4
리액트 성능 최적화를 위해
- (번역) 리액트 성능 최적화, 500ms 에서 1.7ms 까지 : 그 여정과 체크리스트
- 리액트 메모를 사용해야하는 시점 === 퍼포먼스가 잘 나오지 않다고 느껴지는 경우
- 습관적으로 메모를 사용하고 있으나 실제로 최적화가 필요한 포인트를 찾아야함
- 단순히 성능을 개선했다는 것이 아니라 원인이 되는 것을 제거하도록
과도한 메모이제이션
🙈
- 큰 컴포넌트를 잘 정의된 작은 컴포넌트로 분해하고 컴포넌트가 원시 타입의 프로퍼티만을 사용하도록 한다면 리렌더링 최적화를 이끌 수 있다
- memo로 개선할 수 있지만
원시 타입의 프로퍼티
만을 위해서 현재 컴포넌트에 드릴링 되는 무거운 프로퍼티들을 제거하거나- diffing 조건을 잘 이렇게 저렇게 하는 작업이...
- 최근 신경 쓰는 부분: jsx를 const로 변환
renderCallback (useCallback<JSX.Element>)
이 아닌RenderComponent (useMemo<JSX.Element>)
또는RenderComponent (memo)
함수 호출은 항상 발생하고 jsx를 다시 빌드하기 때문에, jsx의 const 변수를 컴포넌트로 변환하는 것이 더 성능이 좋은 것으로 간주할 수 있습니다.
- 컴포넌트가 리렌더링 되는 이유를 파악하여 그 원인을 해결해야하는데
- 간단하게 memo가 효과가 있을 수 있으나
- 결국 원시 타입이 아닌 객체가 재생성되어 리렌더를 유발 시키기 때문에
- 이런 컴포넌트 트리를 찾아내야한다 🤡
계속 추가되는 비즈니스에 대응하기
- [더 나은 쿠폰 서비스에 대한 아이디어 기록)(https://johngrib.github.io/wiki/article/coupon-service-and-code-data/)
- 쿠폰 서비스를 위한 ifelse 지옥...
- 쿠폰 뿐만 아닌 그 어떤 곳에서도 마주할 수 있는 상황에서
- 이를 조합으로 해결할 수 있을까하여 참고하고자