싸피에서 프론트엔드 역할로 프로젝트를 진행하며 처음 react를 접했을 때는 inline 방식으로 style를 적용했다.
Real Cart 프로젝트 : inline style 방식
NFasT 프로젝트 : styled-component 방식
그 때에는 style 적용방식에 대해 크게 신경을 쓰지 않았기에 html 코드 내에서 일일이 적어줘야했는데 나중으로 갈 수록 반복되는 컴포넌트의 style을 수정할 때 한번이 아닌 제각각 모두 일일이 수정을 해야하는 불편함을 겪었다. 이 때 style 적용방식에 대해 찾아보았고 style의 재사용성과 유지보수성을 높이는 styled-component에 대해 알 수 있었고 바로 다음 프로젝트 NFasT에서 적용해보았다.
찾아보니 inline style을 사용할 때에는 리렌더링시에 인라인 스타일이 적용된 컴포넌트/일반태그가 다르다고 판단해서 돔을 새로 그리게 되어 불필요한 리로딩이 발생한다고 하는데 styled-component를 사용할 때의 성능향상을 test해보고 싶었다.
inline style의 성능저하
객체의 불변성:
인라인 스타일을 사용할 때마다 매번 새로운 스타일 객체를 생성해야 한다.
React에서는 객체의 불변성을 유지하기 위해 새로운 객체가 생성되면 해당 객체를 새로고침하고, 이로 인해 컴포넌트가 다시 렌더링된다.
렌더링 비교:
React는 컴포넌트의 렌더링 시에 현재와 이전의 가상 DOM 트리를 비교하여 변경된 부분만을 실제 DOM에 반영한다.
인라인 스타일에서는 객체의 불변성이 유지되지 않으면 항상 변경된 것으로 간주되어 전체 컴포넌트가 리렌더링될 가능성이 높아진다.
성능 최적화의 어려움:
인라인 스타일은 컴포넌트가 렌더링될 때마다 동적으로 생성되기 때문에 성능 최적화가 어려울 수 있다.
반면에 styled-components를 사용하면 스타일이 정의된 StyledDiv 컴포넌트가 상수로 선언되어 렌더링 시에 새로 생성되지 않아 성능이 향상될 수 있다.
두 스타일 적용방식의 성능을 비교하기 위해 렌더링 시간을 비교하는 코드를 짰다.
react saga
이 때 미들웨어로 react saga 를 사용해봤다.
react saga는 액션을 모니터링하고 있다 특정 액션이 발생하면 이에 따른 내가 원하는 동작을 진행한다. 렌더링이 발생할 때 내가 원하는 렌더링 시간을 화면에 띄우도록 하는데에 좋은 라이브러리라고 생각한다.
// src/index.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import createSagaMiddleware from "redux-saga";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
// import rootReducer from "./modules/index";
import rootReducer from "./reducers";
import rootSaga from "./sagas";
const sagaMiddleware = createSagaMiddleware();
const root = ReactDOM.createRoot(document.getElementById("root"));
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(sagaMiddleware),
});
sagaMiddleware.run(rootSaga);
root.render(
<Provider store={store}>
<App />
</Provider>
);
export default store
//RenderDataSaga.js
import { takeEvery, call } from 'redux-saga/effects';
import { ADD_RENDER_DATA } from '../redux/actions/RenderDataActions';
function* handleAddRenderData(action) {
// yield call(console.log, action.payload);
}
export default function* renderDataSaga() {
yield takeEvery(ADD_RENDER_DATA, handleAddRenderData);
}
Profiler
특정 컴포넌트의 리렌더링의 시간 측정은 React의 Profiler를 통해 할 수 있었다.
import React, { useState, Profiler } from 'react';
import { useDispatch } from 'react-redux';
import { addRenderData } from '../redux/actions/RenderDataActions';
import InlineStyleComponent from './InlineStyleComponent';
import StyledComponent from './StyledComponent';
import StatsCollector from "./StatsCollector";
const RerenderingTest = () => {
const [count, setCount] = useState(0);//count라는 상태와 이를 증가시키는 버튼이 있는 함수(setCount)가 선언되었습니다.
const dispatch = useDispatch();//useDispatch를 사용하여 Redux에서 액션을 디스패치할 수 있는 dispatch 함수를 가져왔습니다.
const onRenderCallback = (//onRenderCallback 함수는 React의 Profiler 컴포넌트에서 전달되는 콜백 함수입니다.
id, phase, actualDuration
) => {
console.log(`${id} - ${phase}: ${actualDuration.toFixed(2)}ms`);
dispatch(addRenderData({ id, phase, actualDuration }));
};
//이 함수는 컴포넌트의 렌더링이 시작될 때(phase: "mount")와 업데이트될 때(phase: "update") 호출됩니다.
//해당 컴포넌트의 id, 렌더링 단계(phase), 그리고 실제로 걸린 시간(actualDuration)을 콘솔에 출력하고,
//Redux를 사용하여 해당 정보를 상태로 관리합니다.
// InlineStyleComponent와 StyledComponent가 Profiler로 감싸져 있습니다.
//이렇게 함으로써 각 컴포넌트의 렌더링이 발생할 때 onRenderCallback이 호출되어 성능 데이터를 수집합니다.
return (
<div>
<Profiler id="InlineStyleComponent" onRender={onRenderCallback}>
<InlineStyleComponent count={count} />
</Profiler>
<Profiler id="StyledComponent" onRender={onRenderCallback}>
<StyledComponent count={count} />
</Profiler>
<button onClick={() => setCount(count + 1)}>Increase Count</button>
<StatsCollector />
</div>
);
};
export default RerenderingTest;
하지만 결과는
왜!!!!!!!! styledcomponent의 렌더링 타임이 더 길다.. 그리고 계속 styled-components로 리로딩이되고있는 상태 ㅠㅠ
이게 뭐냐구~~~~~~~ ㅠㅠㅠㅠㅠ
좀 휴식좀하고 2차 시도를 해봐야겠다.
'Front-end > React' 카테고리의 다른 글
Scope,호이스팅,this 이해하기 [Javascript] (1) | 2023.12.15 |
---|---|
async와 await에 대해 이해하기 [Javascript][NFasT 프로젝트 회고] (2) | 2023.12.13 |
[React][NFasT 프로젝트] mainpage 처음부터 다시 만들어보기 1 (0) | 2023.12.11 |
리액트 폴더 구조 이해하고 정리하기 [React][NFast 프로젝트] (1) | 2023.12.05 |
[React] React에 대해 알아보자! (SPA,Virtual DOM,CSR) (0) | 2023.08.23 |