December 31, 2023
2023 feconf를 보고, 챕터별 간단하게 요약본, 그리고 관련 링크를 남기고자 한다. 모든 영상들은 유튜브에 잘 게시되어있으니, 솔직히..! 직접 보는게 제일 좋을것 같다..!
어떤 url은 next기반, 어떤 url 은 vue 기반 어떻게 서빙해주었을까
ALB === AWS application loadbalancer
Page url 정보가 담긴 http 헤더는 네트워크 7계층 중, 처음의 응용계층임
네트워크 계층(layer) 별 사용하는 loadbalancer들이 다른것 같음. 각 계층별 갖고있는 정보를 기준으로 서버들에게 분산시켜줄 수 있는것같음
http 헤더에 들어있는 url 정보를 확인하고, L7 로드밸런서에서 next 서버로 연결하는 방식
간혹 배포 후 next 페이지에서 404가 반환되었었는데, 각각 ALB, next, vue가 따로 빌드되다보니 next 빌드순서를 ALB가 기다려주지 않아 next 빌드가 완료되지 않았음에도 ALB에서 next 페이지로 연결시켜버림. 이를 위해, next 빌드의 순서를 보장시켜줬다 함
Vue 레거시, next 모두 개발이 한동안 필요하여 일부 사용자 환경만 컨버팅된 next 로 연결시키는 카나리 배포전략을 하지는 않았다는것 같음.
아 이게 라이브러리중에 카나리 버전이있는게 요것 종류였구나..
도메인 분리도 고민하였지만, 서브도메인 하나만 추가되어도 seo봇은 이전의 도메인은 더이상 사용하지 않는다고 판단하기 때문에 부정적인 영향을 고려하여 하지 않았음
하지만, 페이지별 컨버팅을 하다보니 기존 seo 최적화를 위한 첫페이지 이후는 csr로, 페이지전환등 사용자에게 바른속도로 보여줄 수 있었지만, url 마다 다른 프레임워크를 사용할 경우 해당 url 접근 시 그 기술과 관련된 스트립트를 새로 받아와야 했기에 장점을 잃게되었음.
이 이유로, url 기반 컨버팅 된 페이지들의 적용이 무기한 연기되었었음
마지막의 빠른 페이지 전환이 가능한 특징을 유지할 수 있음
페이지단위가 아니기 때문에, 각각 기능별로 더 작은 부품으로 컨버팅, 개발 후 적용이 가능하였음
단, iframe 내부, 외부 통신방식 설계가 필요하였음 또한, iframe에 대해 seo 관련된 정보가 부족했음
이것도 마지막의 seo에 대한 불확실로 보류
Product 팀과 협의 하여 정책을 변경하기로 함. 사용자가 가장 많이 경험하고있는 퍼널을 확인하고, 해당 퍼널의 최 하단부터 마이그레이션 진행. 마이그레이션 된 페이지의 lcp 시간은 2.5초 이내여야 함.
vue기반의 lcp (가장 큰 컨텐츠 렌더링 시간)이 길었기에, next > vue는 오래걸리지만 vue > next 로의 페이지전환은 그렇게 오래걸리지 않았음.
퍼널ui에서 가장 마지막 단계를 먼저 컨버팅하기
결국, lcp도 많이 단축하고, cls(화면 내 레이어들이 하나하나 생성되면서 ui 상 아래로 밀리는 시간)도 거의 줄일 수 있었다 함
디자인시스템 또한 각각 추구하는 가치에 따라 개발방식이 달라짐
유연함을 강조하였다면, 더 많은것을 코드로 작성해줘야 하지만 자유롭고 chakra —> 범용성 디자인
간단하고 일관성을 강조했다면 적은코드로만도 가능하지만 유연하지 않음 spectrum —> 제품언어: 디자인 의사결정의 집합임.
단순 위의 범용성 디자인의 ui 라이브러리는 회사 내부의 의사결정이 필요없거나, 유사하거나일 뿐
대부분의 회사에서는 회사 자체의 브랜드 이미지가 있고, 이러한 의사결정이 반영된 디자인을 함. 덜 자유롭지만, 일관성을 강조
회사의 브랜드이미지는 동일하기 때문에, 내부의 모든 제품을 하나의 범용 디자인 시스템을 고려하였지만, 사실 불가능하더라
이러한 의사결정은 시간이 지남에 다라 충분히 변경될 수 있고, 빠르게 모든 제품에 반영하는것이 좋음
이러한 의사결정을 기계가 읽을 수 있는 방법으로 인코딩하는것은 어떨까
디자인 토큰은 디자인 의사결정을 인코딩하는것.
carrot-500: #ff6f0f
우리는 제품에 이 색상을 사용할꺼고, 그것의 이름은carrot-500
이다
이 뜻은, 색상선택과같은 디자인이 자유롭지 못할 수 있다는 것. 디자인 값은 무한하지만, 우리는 정해둔 유한한 디자인 토큰으로만 소통함.
피그마를 절대적인 규칙으로 사용하고, 피그마에서 생성된 디자인토큰을 컴포넌트 배포 시 web hook액션을 추가하여 각 플랫폼의 코드를 생성함.
스타일시트나, 컬러셋 변수 파일 등등
의사결정이 포함된 제품언어로 디자인시스템을 개발하여 90%를 해결할 수 있더라도, 외의 10%의 유연한 개발이 필요한 경우로 인해 그 이상의 시간이 소비될 수 있음.
Zag-js를 보았는데, 하나의 코어로직에 이 컴포넌트에서 사용중인 태그들에 대한 속성들을 모두 관리하고(기본적으로 들어있어야 할 것들은 모두 들어있음) 추가적인 상태 disabled, selecetd 이런것들은 우리가 전달할 수 있도록 되어있음 이 컴포넌트에서 작동되는 포인터 다운 등의 이벤트들의 기본 비즈니스로직도 들어있음
비동기로직을 처리하기위해 포함되었던 useEffect, 혹은 useQuery없이 use만으로 해결
Promise 객체를 반환하며, 내부에서 suspense를 트리거하는것 또한 동일
반환문, 조건문, 반복문 내부에서 사용할 수 있기에 일반 hook과 차별점을 가짐
컴포넌트 내부에 use 를 바로 사용하여 패칭할 경우, 기존과 동일하게 리렌더로 인해 무한한 요청 전달.
이는, 패칭해오는 함수 앞에 react에서 제공하는 cache 구문을 통해 해결 가능. 아직은 실험모드이고 서버컴포넌트에서만 사용 가능
일종의 해당 패칭에 대한 응답값 캐싱을 하는것 뿐 메모와 유사함
단, map이나 foreach와 같이 callback으로 전달되는 경우, 이 경우에는 안됨 호출부가 hook이 아니라면 에러를 반환함
에러 호출부는 리액트 컴파일러 에러임.
현재 리액트는 컴파일러 최적화라는 목표로 생성된 값들에 대해 useCallback, useMemo가 없어도 메모가 되도록 개발을 하고 있음
이는 사실, 함수와 같은 참조형 데이터가 useEffect의 의존성으로 들어가게 될 때, 무한히 실행되는것을 막기 위해 강제적으로 useCallback과 같은 메모를 모두 해주어야 함으로서 그 과정없이 가능하도록 useEffectEvent였나 얘들이 소개되었었는데, 이 프로젝트는 무산되고 아얘 개발자들이 이 고민이 필요없도록 컴파일 최적화라는 프로젝트로 전환됨
use를 쓰다보니 생긴 문제들관 관련된 내용이였음
영상과 별개로 React19에서 useFormState와 같은 form과 관련된 hook들도 생긴다고 하니 같이 봐도 좋을것 같음
진득하게 다시보자
대략적으로 에디터에서 발생한 이벤트, 캔버스에 변경을 mvc 디자인패턴을 기준으로 개발한것을 설명하는듯
힙메모리는 지역변수등의 지역값이 비교적 고정적으로 저장되는 스택과 다르게 메모리가 동적으로 할당되거나, 빠질 수 있음. 동적으로 할당될 수 있기 때문에 메모리 누수가 발생할 경우가 있음
Node 환경 memory leack 디버깅
우리가 뭐부터 봐야 좋을까..?
대표적인 메모리 누수를 일으키는 요인
종종 발생하는 서비스의 OOM (out of memory) -> 필요하지(사용되지) 않은데 메모리를 계속 차지하고있는 현상. 불필요하게 차지하고있는 것들 때문에, 효율적으로 쓸 수 없음
성능도 나쁘고, 애플리케이션도 자꾸 죽음
힙 메모리를 늘려주거나, 누수의 범인을 찾자.
내가 띄운 노드서버라면, 메모리 누수 모니터링 툴을 달아놓을 수 있다는것 같음.
Grafna, datadog, aws cloudwatch …
메모리누수가 발생하는 코드는 모니터링 도구에서 어떻게 보일까
메모리누수가 있는 그래프의 경우 힙메모리 용량이 커지다보니 사용량 그래프가 쭉 상승하다가, 한번 뚝 떨어지고 다시오르다가 뚝 떨어지고를 반복함
v8엔진은 힙메모리 관리를 위해 사용한다면 마크, 그렇지 않으면 치운다는 의미로 Mark and Sweep 알고리즘을 씀
원시자료형이 아닌 참조자료형의 경우 때에 따라서 힙메모리에 할당된 값을 참조하고있을것임
gc는 이 자료들이 사용중인지 아닌지 체크하여 위 알고리즘을 돌리는데, 숨겨진 어딘가에서 사용되고 있다면, 살아남은 참조자료형들은 마크되어있어서 계속하여 남아있음
v8의 gc는 각각의 메모리에 대해 단계를 갖고 처리를 하는데
만약 마지막까지 살아남은 메모리가 많아지면 old space이 꽉 차게 될 것임 > 이 때, 힙 메모리 초과로 서버 사망
결국 그냥 언젠가 되면 원점이기 때문에 힙메모리를 늘려주는건 근본해결이 아님.
근데 구글링을 하면 max-old-space-size를 늘리라는 말이 많은데, 그냥 old space의 공간을 늘려줄 뿐이라 근본해결이 아님
Node —inspect index.js 해당 cli를 통해 체크 가능 개발자도구의 녹색 아이콘이 있음
개발자 도구의 memory 탭에서 프로파일링 녹화버튼을 통해 특정 구간의 힙메모리의 용량 변화를 볼 수 있음
쓰레기통은 수동 GC돌리기
보통 Allocation instrumentation on timeline이라는 특정 시간을 기준으로 힙메모리의 용량변화 그래프를 확인하고, 어떤 객체가 얼마나 많은 용량을 사용하고있는지 확인하여 찾는다 함
근데 사실 그래프가 누가 범인인지는 안알려준다고 함
찾을 때, Shallow Size 크기에 비해서 Retained Size가 큰 경우를 의심해보기
나 자체는 용량이 크지 않은데, 참조하고있는 오브젝트의 용량이 엄청 큰 상황임
Ts 5.2 부터 let, const, var 처럼 using 키워드를 사용해 변수를 생성하였다면, using으로 선언한 값의 인스턴스 내의 Symbol.dispose를 통해 cleanup을 해줄 수 있음. 이 때, 메모리를 초기화시켜주는 코드를 넣어주면 어떨까.
React 할 때 timer를 useEffect return 내부에서 cancel 시켜주는것처럼.
using으로 선언한 값이 포함된 스코프 종료 시, 위의 clean up이 호출됨
React native는 metro와 짝꿍이라고 함
일종의 webpack과 같은 번들러인가봄
위와 같은 문제(?) 아쉬움(?) 있는데, esbuild 번들러로 교체할 수는 없을까?
React native든 뭐.. 어떠한 환경이든 여러개의 모듈로 쪼개진 파일이 아닌 하나의 파일만 볼 수 있어서 그것을 합쳐주는 역할을 함
우리가 작성하는 ./app 과 같은 경우 이게 어떤 외부 라이브러리의 모듈인지, 어떠한 확장자인지 정확히 찾아주는 역할을 함 번들러가 이를 수행해주기 때문에, 우리가 크게 건드리지는 않음.
tsv5의 moduleResolution Bundler가 이 기능에 역할을 온전히 의지하는것 비슷한 느낌..?
metro에서는 resolveRequest를 통해 위의 경로를 해석하는데 설정을 줄 수 있음. 물론, esbuild도 가능 함. 이 Resolution 기능을 통해, 애매하게 축약되어있던 경로들이 정확히 어떤 위치를 보고있는지 알 수 있게됨
번들러는 브라우저들이 이해할 수 있는 스크립트 버전, 타입스크립트라면 스크립트로 변환을 해주기도 함. React-native는 javascript가 아닌 flow라는 언어를 사용하는데, 이를 metro가 javascript로 변환을 해줌.
즉. 자바스크립트가 아닌것을 자바스크립트로 변환시켜주고자 함
생각해보면 metro, esbuid 같은 기능인데, 바꿀 수 있지 않을까?
resolveExtensions 필드에, iOS와 같은 react-native 내부의 확장자도 추론할 수 있도록 후보에 넣음
비교적 최신의 크롬문법인 hidden=“until-found” 라는 속성을 태그요소에 전달하였는데, react-dom은 이 속성을 올바르게 dom으로 변환시켜주지 못함. 비교적 최신의 문법이기 때문
대문자로 작성하여 React를 속여보자
모두가 한번에 이전되지 않음.
시간이 지날수록 PW팀에서 확인 완료되는 가이드페이지가 증가되면서 빌드속도가 점점 느려짐
Next의 isr을 사용하여 변경이 있는 부분만 새롭게 빌드를 하도록 처리하긴 했지만, 근본적으로 해결되지는 않았음.
확인해보니, lnb에 모든 가이드들에 대한 진입점들이 작성되어있는데, 가이드들의 순서가 변경되거나, 삭제되거나 할 때 모든 가이드페이지의 lnb또한 순서, 제거등의 변경 이유로 그 페이지 전체가 빌드대상이였음.
하나의 콘텐츠가 변경되는것은 그냥 그 페이지만 빌드되면 되었지만, lnb가 변경되는것은 모든 페이지가 쓰기 때문에 모든 페이지가 재 빌드가 됨. 4개국어*1000개 이상의 가이드페이지가 매번 빌드되는것
새로운 컨텐츠가 빌드되면 해당 캐시가 생성되고 조회하기 위해서는 한번은 접속이 되어야 그 페이지의 캐시가 생길텐데 모든 페이지들이 캐싱된 페이지를 잘 조회할까?
sitemap이 잘 구성된 경우, 모든 페이지에 한번 접근하는애가 있음. 웹 크롤러는 sitemap에 정의된 페이지들에 1회 접근할 것임. 이 때 자동 캐시가 생성되고, 이후는 캐시히트가 발생하지 않을까 로 접근하면 괜찮아보임
즉, 사용자가 하나의 flex 앱을 사용하는 경험을 못줌
배포단위는 더 작게 페이지 이하의 단위로 각각 따로 빌드하는것은 동일하게, 단 하나의 앱으로 느껴지게끔 런타임때에는 배포 단위들을 통합 이를 위해 module Federation 개념을 도입하고자 했지만, 현재의 next 프로젝트에서는 불가능하여 next를 떼어네기로 함
module Federation?? 함 알아보자..
moduleFederation으로 해당 앱이 런타임에 로드할 다른 앱을 설정해줄 수도, 해당 앱이 런타임 대에 다른 앱에게 불릴 수 있는(?) 코드도 지정 가능하다는것 같음
다른 앱을 불러오는 하나의 앱을 host, 불려지는 앱을 remote라고 함. 모든 앱들은 host와 remote 두가지 모두 될 수 있음. 이를 전방향적인 특성이라 함.
런타임에 앱이 합성되다보니, 런타임에만 확인할 수 있는 에러가 발생할 수 있었음
이런 구조의 변경은 오랜시간이 걸림. 어떻게 기능개발과, 구조변경을 함께 진행했을 까
모노레포 내부에서 프레임워크에 영향받지 않는 순수한 react 컴포넌트로 개발을 지속하며, 두가지 환경을 모두 배포하였음
내부에서 프레임워크와 관련된 기술 기반으로 wrapping된 기술을 import 하여 쓰는 경우에는 위처럼 분리하여 쓰기가 조금 어려움
빌드 시, import 해오는 경로를 해당 프레임워크에 맞는 경로로 변경함 (React -> Next)
점진적으로 환경을 배포해가며 전환함
dev —> qa —> 사내prod …
당근에서는 웹뷰에서 페이지간 전환을 보다 앱 스럽게 하기 위해 carrotFrame이라는 라이브러리를 사용하고 있었다 함.
History api 의 스택을 직접 참고하고싶었지만, 쌓인 스택에서 특정 부분을 찾아 조회하는것은 어렵기 때문에, react 내부에 history stack과 동일한 구조의 스택을 쌓고 관리하는 방식으로 구현을 했었음.
그러다보니, react-router-dom에 강하게 의존하고 있는 문제와, react 상태와 history stack의 동기화문제가 항상 있었다 함.
새롭게 개발을 시작하여 다른 transition 전환효과, navigation을 핵심이라고 생각했지만 그런것들이 아닌 목표는 애니메이션이 존재하는 stack이였기 때문에, 해당 라이브러리에서는 history api, react-router-dom 이라는것을 배제하게 되었음
첫 아이디어는 이벤트 기반으로 설계를 하는 것
화면의 진입, 새 페이지 추가, 이전으로 돌아가기 이벤트가 있을것 같음 init, push, pop 위의 이벤트들을 통해 상태를 갱신하고, 사용하는곳에서 이해할 수 있는 상태로 변경
react 라면, useSyncExternalstore같은?
라이브러리를 사용하는 사용자가 쉽게 확장할 수 있도록 해보자
~~를 했을 때, ~~ 를 수행한다. —> callback이 생각난다.
이벤트가 추가되기 전, 이벤트로 상태가 변경되기 전에 실행될 함수를 전달받을 수 있도록 해주자
onBeforePush, onBeforePop과 같은
근데 만약 이렇게 많은 옵션을 받을 수 있다면, 학습비용이 높아질 수 있음.
확장성은 유지하면서 유저가 쉽게 느낄 수 있도록 개발할 수 없을까??
위 두가지를 기대한다 함.
이 내용은 깃 레포를 함 보며 jsdoc으로 정리해보고있다..