읽어보았던
추억의 CSS-in-JS
- (번역) 우리가 CSS-in-JS와 헤어지는 이유
styled-components
를 좋았지만 현재는 이런저런 이유로 사용하지 않게 되었다- 그럼에도 흥미롭다고 생각해서 돌아보기
- 런타임 CSS-in-JS는 코드 안에 작성된 스타일을 런타임에 해석하여 적용한다
colocation이라는 장점을 가장 먼저 접했다
- 컴포넌트 안에 스타일이 함께 위치한다는 것이 코드를 관리하는데 용이해진다
dynamic props 사용할 수 있다
-
const Comp = styled(View)` backgroundColor: ${(props) => props.color}; height: ${(props) => props.size}; ` ... <Comp color="red" size={15} />
- 특정 상태에 따라 스타일을 다르게 적용하기 위해서 각각의 스타일을 따로 구성하거나 인라인으로 넣어줘야하는데
- 이런식으로 props를 전달하여 바로 사용할 수 있던 것에서 가장 큰 매력을 느꼈다
-
- 반면 런타임이기 때문에 컴포넌트 레이아웃 직전에 스타일을 계산하고 반영해야하는
성능 이슈
가 존재한다- 이를 개선한 것으로 제로런타임(컴파일타임) css-in-js 가 있당 (ex. Linaria)
- 런타임에 스타일을 계산하는게 아니라 빌드 타임에 미리 스타일을 만들어 두는 방법
- 하지만 빌드타임에는 dynamic props에 대한 처리는 불가능하다
- 결국 인라인으로 파라미터 처리를 하게 되는데, 개인적으로 인라인은 ❌ 하고 있다
- 그 외 번들 크기나 디버깅이 어려운 것은 개인적으로 단점으로 느껴지지 않았다
- 장점이 더 좋으니깐...!
- 번들 크기 줄이는 방향으로 무언가 선택할 수 있다면 그것이 그 방향의 장점이 되는 것 아닐까
- 결국 가장 큰 문제점은 렌더타임의 성능 이슈이다
- 모든 컴포넌트가 css-in-js 라면 실질적인 UI 변경사항이 없더라도 컴포넌트가 리렌더 될 때 스타일도 다시 계산해야한다
- 렌더마다 스타일 직렬화를 위한 비용이 추가되는 것이다
- 이러한 css-in-js를 사용하면서 얻고자 했던 장점만을 가져가는 형태로 스타일을 작성할 수 없을까?
- 하는 고민을 하게 된다 (계속 읽다보니 글에서도 언급하고 있었다 🥳)
- 런타임, 컴파일타임 모두 해결할 수 없었던 오직 장점만 갖고 있는 시스템
colocation + dynamic props
이것 아닐까- colocation을 위해 우선 하나의 컴포넌트 파일에서 해결하고자 한다
- dynamic props를 처리하기 위해서는 컴포넌트 안에 존재해야한다
- 추가로 위에서 언급한 내용 중 가장 큰 장점으로 느꼈졌던
colocation
에 대한 글도 같이 읽어본다 - 좋았던 기억은 뒤로한채 단점이 더 크다고 느껴지기 때문에 사용하지 않고 있다 ㅠㅠ
- 다이나믹 스타일을 상태로 관리하고 있다
- colocation + dynamic props를 처리하는 방법으로
- 컴포넌트 안에서 useMemo로 하나의 상태를 스타일로 주고 있다
- 리렌더 상황에서 스타일을 재계산 하는 이슈를 그나마 해결하면서 번들 사이즈가 줄었다는 위안
- 지금 갑자기 의문을 갖게 되는데
const dynamicStyles = useMemo(() => ({ a: {...}, b: {...}, }), [...]); ... <Comp style={dynamicStyles.a} /> <Comp style={dynamicStyles.b} />
- 이거 메모이제이션 제대로 되고 있는거 맞나..????
- 확인하러 가야지
- (+추가)
- 확인결과 메모이제이션된 컴포넌트는 리렌더 되지 않았다
- 리렌더는 되었지만 DOM이 업데이트 되지 않았다
- 실질적으로 우리가 얻고자 하는 것이 이것 아닌가
- 디펜던시만 제대로 정리된다면 메모이제이션 효과를 보는게 맞다
forwardRef + memo의 props 타입 추론 어떻게 하지
- forwardRef를 쓰면 이상하게 props 타입이 추론이 안되어서 불편했다
- 내가 무언가 잘못 사용한건 아닐까
- 마치 경험기를 작성한 느낌이였다
- 맨 처음 사용하고 있었던 방법은 forwardRef 후 memo 하는 것이다
const Component = forwardRef((props: Props, ref: RefObject<View>) => { return <View ref={ref} {...props}></View>; }); const MemoizedComponent = memo(Component);
- 이렇게 하면 MemoizedComponent를 사용했을때 Props 추론이 정확하게 된다
- 하지만 MemoizedComponent를 얻기 위해 너무 불필요한 단계를 거쳐야한다고 생각했다 (귀찮았다)
memo(forwardRef(...))
이렇게 쓰고 싶다!const Component = memo(forwardRef<View, Props>((props, ref) => { return <View ref={ref} {...props}></View>; }));
- 그래서 이렇게 정리했더니 Props 인터페이스에서
PropType is defined but prop is never used
이 발생하였다 - 쓰고 있지만 쓰고 있는 것을 알지 못하는게 타입 추론이 잘 되고 있지 않은 것 같다
- 그래서 이렇게 정리했더니 Props 인터페이스에서
- 대체 왜 추론이 안될까 하면서 제네릭을 통한 HoC로 빼보았다
export const withMemoRef = <Props, Ref>( Component: (props: Props, ref?: ForwardedRef<Ref>) => JSX.Element, ) => memo(forwardRef<Ref, Props>(Component)); const Component = withMemoRef<Props, View>((props, ref) => { return <View ref={ref} {...props}></View>; })
- 이제 아무런 이슈 없이 정상적으로 타입이 잡히고 있었다
- 중간 과정에서 위 글에 나온 것처럼 타입을 assert하거나 props 이름을 바꾸거나 시도해봤는데 모두 아름답지 않았다... ㅠㅠ
- 글에서 제안하는 마지막 방법인 declare 하는 것은 생각 못해봤는데 이게 가장 깔끔해보인다
- 근데... memo로만 감싸면 ㅠㅠ
PropType is defined but prop is never used
이 재발한다
RN 안드 빌드 실패
-
Execution failed for task ':app:mergeDebugNativeLibs'. > A failure occurred while executing com.android.build.gradle.internal.tasks.MergeNativeLibsTask$MergeNativeLibsTaskWorkAction > 2 files found with path 'lib/arm64-v8a/libc++_shared.so' from inputs: - /Users/jiggag/Projects/react-native-starter/node_modules/react-native/ReactAndroid/build/intermediates/library_jni/debug/jni/arm64-v8a/libc++_shared.so - /Users/jiggag/.gradle/caches/transforms-3/87b4b130d2c85494c51740eb879d8459/transformed/jetified-react-native-0.70.0-debug/jni/arm64-v8a/libc++_shared.so If you are using jniLibs and CMake IMPORTED targets, see https://developer.android.com/r/tools/jniLibs-vs-imported-targets
- 미리 빌드된 네이티브 라이브러리 포함
- Gradle에서 외부 네이티브 빌드에 사용되지 않은 미리 빌드된 네이티브 라이브러리를 패키징하도록 하려면 모듈의 src/main/jniLibs/ABI 디렉터리에 이 라이브러리를 추가합니다.
- 4.0 버전 이전의 Android Gradle 플러그인은 jniLibs 디렉터리에 CMake IMPORTED 타겟을 포함해야 앱에 포함됩니다. 이전 버전의 플러그인에서 이전하는 경우 다음과 같은 오류가 발생할 수 있습니다.
- Android Gradle 플러그인 4.0을 사용한다면 IMPORTED CMake 타겟에서 사용하는 모든 라이브러리를 jniLibs 디렉터리 밖으로 이동하여 이러한 오류를 방지하세요.
- 현재 프로젝트에서 사용하고 있는 AGP는 v7인데 v4에서 발생하는 이슈가 나타났다?? ⁉️
- 이슈로 가보았지만 검색되는 것은 없었고 FabricExample이 있어서 이를 참고하여 build.gradle을 다시 세팅해보았다
- 무언가 수상한 차이점을 발견하였는데
- Fabric을 설정하면 react-native 모듈 대신 ReactAndroid를 바로 바라보도록 설정이 되어있었다
- Fabric 적용하면서 c++ 영향이 여기에서 시작된걸까?
- 혹시나 기대를 해보았지마.. 여전히 실패하였다
- 다시 처음으로 돌아왔다
- 같은 이슈를 겪은 이 블로그처럼 이슈 내용 그대로 중복 발생하는 패키지를 정리하도록 설정을 하면 될 것 같다
- 그렇게 해도 안되던데....
- 맨 처음 이슈를 마주하고 시도했던 패키지 설정을 변경해보았을때 계속 이슈 발생하였다
- AGP + jniLibs 관련해서
react-native-gradle-plugin
에 검색해보았다- 그러다 찾게된 packagingOptions
- 이게 지금 적용이 안되고 있는건가??
- 빌드하면 발생하는
lib*.so
를 하나씩 추가해보았다 - 오류 메세지의 so가 달라지고 있었다
- 하나씩 다음 단계로 넘어가고 있는듯 하다!!
- 하나씩 추가해서는 끝이 나지 않을 것 같았다
- 희망을 갖고 RNGP의 옵션을 전부 그대로 추가해보았다
- 그랬더니 되지 않는다?!
- 아.....
**/*.so
통째로 넣어볼까? - 이게 되네....
- 빌드하면 발생하는
- 이렇게 넣으면 안될 것 같은데
- 이 옵션이 수정된 RNGP 버전이 아직
RN@0.70.4
에는 반영되지 않았나보다 🤔 - RN@0.71.0에 반영되려나!!!
- v0.71-rc를 또 먼저 들어가볼까 고민하다가도... 지난번 빌드 오류를 릴리즈 버전까지 해결하지 못했던 것을 떠올리면 기다려본다
- 이 옵션이 수정된 RNGP 버전이 아직
- (+추가)
RN@0.71.0-rc.0
의build.gradle
에 이런 변경사항을 보았다-
// The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-native")
- 이건 분명 위의 이슈가 해결됨을 암시하는 코멘트
-
- 악마의 속삭임
- 시작해보았지만 hermes 부터 안되는걸 🙈
- hermes를 꺼야 안드 빌드가 된다
- 이건 어떤 메리트가..?!?!
- 버전을 올리면서 hermes는 포기 했지만
- 위에서 임시로 대응했던
**/*.so
설정을 제거할 수 있다는점? - 인줄 알았으나
react-native-safe-area-context
와 충돌 🚨 - gradle이 항상 발목을
- iOS 빌드는 왜 실패하는 걸까요
- fabric과 함께 c++이 들어왔다가 이번에 버전 올리면서 사라졌는데 (RN이 알아서 처리하기로 했나보다)
react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.cpp:222:34: no member named 'toString' in 'facebook::jsi::BigInt'
- 왜 터지는걸까요 🥳
- 🚨 pod update 하는데 옵션을 안붙이고 써버렸네
- 옵션이 있어야 Cpp 처리를 하는데 옵션이 없으니 fabric을 사용하지 않는 버전으로 업데이트를 받아버림