취업 후 4달만에 쓰는 블로그라니.. 😱 그 동안 바쁘다는 핑계로 너무 소홀했던 것 같다.
매일매일은 아니더라도 꾸준히 글을 써야겠다는 다짐(작심삼일)을 하면서 오랜만에 글을 써본다.
성능 최적화에 대해 설명하기에 앞서 React에서 렌더링이 일어나는 이유에 대해 알아야 한다.
React에서 렌더링을 수행하는 시점은 다음과 같다.
- Props의 변화가 일어났을 때
- State의 변화가 일어났을 때
forceUpdate()
를 실행했을 때- 부모 컴포넌트가 렌더링되었을 때
1 ~ 3의 과정을 통해 컴포넌트가 렌더링이 될 때, 자식 컴포넌트 또한 같은 과정으로 렌더링이 진행된다.
하지만 컴포넌트에서 렌더링 결과에 전혀 영향을 미치지 않는 변화가 일어나면 불필요한 렌더링이 발생하게 되고, 이는 앱의 성능에 악영향을 끼치게 된다.
렌더링 과정에서 수행하는 로직이 많을수록, 많은 컴포넌트를 출력할 수록 손실은 더욱 커지게 된다.
이번에 설명하고자 하는 스타일링과 관련한 성능 최적화와 렌더링은 무슨 상관이 있을까?
간단한 예시를 들어보겠다.
우리가 스타일링을 할 때 어쩔수 없이 인라인 스타일로 스타일링을 할 때가 있다.
// 예시
import React from "react";
const Button = () => {
return <button style={{ backgroundColor: "#fff" }}>버튼</button>;
}
export default Button;
이런식으로 말이다.
앱이 렌더링이 되면서 코드를 읽어 내려가는데, 객체의 메모리 주소값은 항상 다르기 때문에 가상 DOM에 변화가 일어났다고 인식해서 불필요한 리렌더링이 발생하게 된다.
그렇다면 어떤식으로 스타일링을 해야 불필요한 리렌더링을 방지할 수 있을까?
모든 회사에 웹디자이너 혹은 웹퍼블리셔가 있지는 않기 때문에 직접 UI를 구성하고 개발하는 경우가 많이 있다.
그래서 나 같이 미적감각이 전무한 사람들은 잘 만들어진 UI 라이브러리를 많이 사용하게 된다.
그러나 잘 만들어진 UI 라이브러리를 사용하게 되면 디자인들이 획일화되기도 하고, 회사가 원하는 바와 다르게 나올수 있기 때문에 직접 커스텀을 해서 사용하게 된다.
보통 커스텀을 하게 된다면 인라인 스타일로 원래 있는 CSS 속성을 덮어쓰게 되는데 이렇게 되면 위에서 적은 불필요한 리렌더링이 발생하게 된다.
그렇다면 UI 라이브러리를 사용하면서 성능 최적화와 커스텀 두 마리의 토끼를 잡을 수 있을까?
물론 CSS 파일을 만들어 클래스명을 가져와 덮어씌우는 방법도 있지만 그리 힙한 방법은 아니다.
우리에게는 힙한 styled-components
가 있지 않은가? 물론 요즘은 emotion
이 핫한 것 같지만...
어쨌든 styled-components
를 활용해 성능 최적화를 할 수가 있다.
예시는 요즘 내가 사용하는 Ant-design
을 예시로 들어보겠다.
// 인라인 스타일 예시
import React from "react";
import { Button } from "antd";
const LoginForm = () => {
return <Button type="submit" style={{ marginTop: "10px" }}>로그인</Button>
}
export default LoginForm;
인라인 스타일을 사용하게 되면 코드도 깔끔하지 않을 뿐더러, 아무런 변화가 없지만 불필요한 리렌더링을 발생시킨다.
// styled-components 예시
import React from "react";
import styled from "styled-components";
import { Button } from "antd";
const LoginBtn = styled(Button)`
margin-top: 10px;
`;
const LoginForm = () => {
return <LoginBtn type="submit">로그인</LoginBtn>
}
export default LoginForm;
이렇게 UI 라이브러리의 모듈을 styled-components
를 활용하여 인라인 스타일을 사용하지 않고 쉽게 커스텀을 할 수가 있다.
React Hook 중에 useMemo
라는 것이 있다. Memo는 memoized
를 의미하는데, 이는 이전에 한 번 사용한 값을 재사용한다는 의미를 가지고 있다.
그렇다면 useMemo
를 이용해서 어떻게 불필요한 리렌더링을 방지할 수 있을까?
import React, { useMemo } from "react";
import { Button } from "antd";
const LoginForm = () => {
const btnStyle = useMemo(() => ({ marginTop: "10px" }), [])
return <Button style={btnStyle}>로그인</Button>
}
export default LoginForm;
이렇게 Hook을 이용하여 필요한 스타일 객체를 캐싱해놓고 재사용을 하게 된다면 이미 기억하고 있는 객체이기 때문에 메모리주소값이 변하지 않아 불필요한 리렌더링이 발생하지 않게 된다.
틀린 점이나 궁금한 점이 있으시면 댓글 달아주세요!
읽어주셔서 감사합니다 🙇♂️