32일차 - 성능최적화, 메모이제이션, 반응형

류연찬·2023년 5월 10일
0

Codecamp FE07

목록 보기
32/39

성능최적화

// memoization 폴더 - container 파일

const containerPage = () => {
	console.log("컨테이너가 렌더링 됩니다.")

	let countLet = 0
	const [countState,setCountState] = useState(0)

	const onClickCountLet = ()=>{
		console.log(countLet+1)
		countLet += 1
	}

	const onClickCountState = ()=>{
		console.log(countState+1)
		setCountState(countState+1)
	}
    
	return(
		<div> 
			<div>================<div> 
			<h1>이것은 컨테이너 입니다.</h1>

			<div> 카운트(let): {countLet} </div>
			<button onClick={onClickCountLet}> 카운트(let) +1 올리기! </button>

			<div> 카운트(state): {countLet} </div>
			<button onClick={onClickCountState}> 카운트(state) +1 올리기! </button>

			<div>================<div>

		<MemoizationPresenterPage/>
		</div>
	)
}

export default containerPage

이렇게 만들어 놓은 후 let과 state의 버튼을 눌러 보도록 하겠습니다.

콘솔을 보시면, let은 버튼을 누르면 콘솔의 값은 올라가지만 리렌더는 일어나지 않아 “컨테이너가 렌더링 됩니다.”라는 콘솔이 찍히지 않고 있으며, 화면은 여전히 0 입니다.

하지만 state는 버튼누름과 동시에 리렌더링되며 우리가 올려두었던 countLet이 0으로 초기화 됩니다.

이는 useState를 제외한 모든 값이 다시 그려지고 있다는 것을 알 수 있습니다.

💡 우리가 폴더명을 입력해서 엔터를 치면, next.js에서는 가장먼저 해당 폴더 안의 index.js(tsx) 파일을 찾게 됩니다. 하지만 만일 우리가 주소창에 폴더명을 적고 이후에 파일명을 적게 되면 next.js는 해당 파일을 찾게 됩니다.

프리젠터 페이지를 만든 후 앞서 만들어 둔 container 페이지에 import 해주세요.

// memoization 폴더 _ presenter 파일
const MemoizationPresenterPage = () => {
	console.log("프리젠터가 렌더링 됩니다.")
  
	return (
		<div>  
			<div>================<div>
			<h1>이것은 프리젠터 입니다.</h1>
			<div>================<div>
		</div>
	)
}

export default MemoizationPresenterPage

이렇게 import 한 후 새로고침을하시면 콘솔에 “컨테이너가 렌더링 됩니다.”“프리젠터가 렌더링 됩니다.” 가 찍혀 있는것 을 볼 수 있습니다.

그런데 여기서 문제가 있습니다.

나는 부모의 state를 바꿨는데 자식도 다시 렌더링되고 있습니다. 이부분이 굉장히 비효율 적인 부분입니다.

우리가 이런 부분을 최적화 하기위해 눈으로 직접 확인하며 개발할 수 있는 도구를 설치하도록 하겠습니다.

React Developer Tools 설치

크롬 웹스토어에 React Developer Tools를 설치해주세요

설치 하시게 되면, 개발자 도구에 proflier에 생성됩니다.

그리고 설정을 하나 바꿔주셔야 합니다.

해당 설정은 렌더링 될 대상일 때 영역을 표시해주는 것 입니다.

다시 돌아와 stste카운트 버튼을 눌러보시면 container 부분과 presenter 부분이 동시에 렌더링 대상임을 볼 수 있습니다.

반면에 let카운트 버튼을 눌러보시면 아무일도 일어나지 않습니다.

그 말은 해당 버튼을 눌러도 렌더링이 일어나지 않는다는 말 입니다.

메모이제이션(Memoization)

memo

우리는 불필요한 렌더링을 줄이기 위해 memo라는 것을 이용해 볼 것 입니다.

memo라는 기능은 react에서 제공하고 있는 기능입니다. 따라서 react에서 import 해서 사용해주시면 됩니다.

// memoization 폴더 - presenter 파일

import { memo } from "react"

const MemoizationPresenterPage = () => {
	console.log("프리젠터가 렌더링 됩니다.")

	return(
		<div>  
			<div>================<div>
			<h1>이것은 프리젠터 입니다.</h1>
			<div>================<div>
		</div>
	)
}

export default memo(MemoizationPresenterPage)

이렇게 memo 를 사용해주시고 state카운트를 클릭해보시면 프리젠터는 렌더링이 되지않아 콘솔도 찍히지 않을 뿐더러 리액트 툴에도 보이지 않습니다.

useCallback(), useMemo()

자식컴포넌트는 memo를 사용해 불필요한 리렌더가 더이상 일어나지 안도록 막아주었지만, 부모 컴포넌트는 지속적으로 렌더링이 일어나는 상태입니다.

하지만 부모컴포넌트에서도 부분적으로 렌더링이 일어나지 않아도 되는 부분이 있습니다.

예를들어 stateCout를 변경했을때 letCout의 값이 지속적으로 다시 만들어지고 있는 상황입니다. (초기화라고하지만 0이라는 값이 계속 다시만들어 지는 것 입니다)

따라서 이런 불필요한 값들이 지속적으로 다시 만들어지지 않도록 유지시켜주는 hooks가 바로 useMemo(), useCallback()입니다.

useMemo(), useCallback()의 사용방법은 아래에서 바로 살펴보도록 하겠습니다.

useMemo() 사용방법

// memoization 폴더 - presenter 파일

import { useMemo } from 'react'

const containerPage = () => {
	console.log("컨테이너가 렌더링 됩니다.")

	let countLet = 0
	const [countState,setCountState] = useState(0)

	const memo = useMemo(()=>{
	// return 값을 기억합니다.
	return Math.random()
	},[])
	console.log(`${memo}는 더이상 안 만들어`)

	const onClickCountLet = ()=>{
		console.log(countLet+1)
		countLet += 1
	}

	const onClickCountState = ()=>{
		console.log(countState+1)
		setCountState(countState+1)
	}
	return(
		<div> 
			<div>================<div> 
			<h1>이것은 컨테이너 입니다.</h1>

			<div> 카운트(let): {countLet} </div>
			<button onClick={onClickCountLet}> 카운트(let) +1 올리기! </button>

			<div> 카운트(state): {countLet} </div>
			<button onClick={onClickCountState}> 카운트(state) +1 올리기! </button>

			<div>================<div>

		<MemoizationPresenterPage/>
		</div>
	)
}

export default containerPage

일반적으로 useMemo의 사용은 굉장히 복잡한 계산이외에는 그렇게 흔하지는 않습니다.

useCallback() 사용방법

// memoization 폴더 - presenter 파일

import { useCallback } from 'react'

const containerPage = () => {
	console.log("컨테이너가 렌더링 됩니다.")

	let countLet = 0
	const [countState,setCountState] = useState(0)

	// useCallback을 사용하게 되면 함수를 다시 만들지 않습니다.
	const onClickCountLet = useCallback(()=>{
		console.log(countLet+1)
		countLet += 1
		},[])

	// 눈으로 직접보기 위해 state 함수도 useCallback으로 감싸주겠습니다.
	const onClickCountState = useCallback(()=>{
		console.log(countState+1)
		setCountState(countState+1)
		},[])

	return(
		<div> 
			<div>================<div> 
			<h1>이것은 컨테이너 입니다.</h1>

			<div> 카운트(let): {countLet} </div>
			<button onClick={onClickCountLet}> 카운트(let) +1 올리기! </button>

			<div> 카운트(state): {countLet} </div>
			<button onClick={onClickCountState}> 카운트(state) +1 올리기! </button>

			<div>================<div>

		<MemoizationPresenterPage/>
		</div>
	)
}

export default containerPage

useCallback으로 함수를 감싸주게 되면 해당 함수를 다시 불러오지 않고 이전에 불러왔던 함수를 실행시키게 되는데, 우리는 이를 눈으로 확인하기 위해 state를 담은 함수에도 useCallBack을 감싸주었습니다.

그리고 카운트 버튼을 클릭하니 결과가 어땠나요?

카운트가 고정되어 있지 않았나요? 이것이 바로 useCallback의 결과 입니다.

즉, 이전에 불러왔던 값을 유지시는 것 이죠.

이렇게 함수는 다시불러오지 않지만 값은 올려주고 싶을 때 사용할 수 있는 방법이 하나 있습니다.

우리가 이전에 배웠던 prev를 이용하는 것 입니다.

setCountState((prev)=>prev+1) 이렇게 setCoutState함수를 작성해주시면 onClickCountState 함수를 새로 그리지 않지만, state는 올려줄 수 있습니다.

작은 서비스에서라면 한번 더 렌더링 되는게 문제가 될 건 없지만 서비스가 점점 커지고 기능도 많아진다면, 이런 작은 부분 하나하나가 속도를 늦출 수 있습니다.

따라서 성능 최적화 부분은 당장 사용하지 않더라도 잘 공부해두시면 훗날 굉장히 유용하게 사용될 수 있습니다.

💡 tip! 알아두면 유용한 개발자 도구
1. Apollo Client Devtools
설치후 app.tsx에서 client 설정 부분에 connectToDevTools : true 로 설정해주셔야 합니다.
2.wappalyzer
특정 사이트에 들어가시면 해당 사이트가 사용한 스택을 분석해줍니다.


반응형 웹(미디어 쿼리)

사이트는 크게 반응형 사이트와 적응형 사이트 두가지로 나뉘게 됩니다.

적응형 사이트의 대표적인 예로는 네이버가 있습니다.

크기를 줄였을 때 사이즈가 같이 줄어들지 않고 덮이는 것을 적응형사이트라고 합니다.

반면에 배달에 민족같이 크기에 따라 이미지도 줄어드는것을 반응형 사이트라고 합니다.(우리 코드캠프 사이트 또한 반응형 사이트입니다.)

요즘 사이트의 트렌드는 사이트의 크기에 따라 이미지의 크기가 변하는 반응형 사이트 입니다.

현대 웹 트렌드는 반응형이 맞지만 모든 부분을 반응형으로 만들어야 한다는 생각은 잘못된 생각입니다.

반응형이 들어가야 할 곳과 들어가지 않아도 괜찮은 부분을 구분하고 들어가야 할 부분에만 디자인을 넣어서 만들어주시면 됩니다.

따라서 웹을 기획하는 단계에서 반응형 기획과 디자인 또한 같이 들어가야 합니다.

반응형 웹 만들어보기

반응형 웹을 만들기 위해서는 화면구간을 나눠주셔야 합니다.

가장 쉽게 나누는 것으로는 모바일 크기일 때, 태블릿크기일 때, pc 크기일 때 이렇게 나눠주시는 것 입니다.

이렇게 화면 크기를 나누는 것을 breackPoint라고 하며, 화면을 전환해주는 @media 라는 css속성을 이용해 화면에 따라 스타일을 다르게 주시면 됩니다.

우리는 부트스트랩에서 제공하는 표준 breakPoint를 사용해보도록 하겠습니다.

부트스트랩에서는 아래와 같이 breakPoint를 걸어주고 있습니다.

그럼 이제 위의 표준을 가지고 직접 만들어 보도로 하겠습니다.

import styled from '@emotion/styled'

const Wrapper =styled.div`
	width : 1000px;
	height : 1000px;
	background-color : red;

	@media(min-width:767px) and (max-width:991px) {
		width : 500px;
		height : 500px;
		color : green;
	} 

	@media(max-width:767px) {
		width : 300px;
		height : 300px;
		color : yellow;
	} 
`

const MediaQuery = () => {
	return(
		<Wrapper>상자</Wrapper>
	)
}
export default MediaQuery

이렇게 만들어 주시면 아래의 사진처럼 pc 버전에서는 빨간색 박스가, 태블릿에서는 초록색 박스가, 모바일에서는 노란색 박스가 나타나게 됩니다.

PC 크기

태블릿 크기

모바일 크기

💡 breakPoint는 향후에 변동 될 가능성을 배제할 수 없어 웬만하면 파일을 따로 빼서 사용합니다

export const breakPoints = {
	tablet : "(min-width:767px) and (max-width:991px)",
	mobile : "(max-width: 767px)"
}

이렇게 파일을 분리해주시면 여기서만 바꿔주시면 모든 컴포넌트에서 적용되기때문에 유지보수에 굉장히 편리합니다.

위와 같이 breackPoint를 따로 빼서 사용하면, 사용시에 import 해서 사용해주시면 됩니다.

그리고 미디어 쿼리를 작성하실때는 @media ${breackPoint.mobile}{ ...css코드... } 이렇게 적어주시면 됩니다.

💡 반응형 웹을 만드실때는 가로사이즈를 퍼센트로 주셔야 합니다.
크기에 따라서 늘었다 줄었다 할 수 있도록 가로 사이즈를 퍼센트로 주시고, 세로사이즈를 고정해주시는 방법이 일반적 입니다.

💡 em과 rem
rem : body사이즈의 영향을 받음 (body가 작아지면 폰트가 모두 일괄적으로 작아짐)
em : 부모사이즈의 영향을 받음(부모가 작아지면 폰가가 일괄적으로 작아짐)

0개의 댓글