.env
파일을 통해 환경이나 특정 상황에 따라 변경될 환경 변수값 정의하고 자주 사용하는데, 뒤에 붙는 확장자에 따라서 개발용과 배포용으로 나눠서 사용할수가 있다.
환경변수 사용하기
Vite
에서는 약간 사용법이 다른데, cra
같은 경우 아래와 같은 명명 규칙을 지키면서 사용한다.REACT_APP_변수명=값
사용법 예시(환경변수처리)
REACT_APP_BASE_URL=http://localhost:8080.com
REACT_APP_TEST=TEST
사용법 예시(가져오기)
{process.env.REACT_APP_변수명}
환경에 따른 환경변수 적용
.env.local
, .env.development
, .env.production
, .env.test
등 개발환경이나거 배포환경에 따라 구분하여 사용할 수 있다.
🎲 .env.local
로컬 개발시 사용한다. test환경 외 모든 환경에서 로딩된다.
🎲 .env.development(Start)
개발 환경에서 사용되며, 아래 명령어로 실행시 동작한다.
$ npm run start
$ yarn start
아래 순서로 동작된다. env.development.local파일이 가장 우선순위가 높다.
.env.development.local > .env.development > .env.local > .env
🎲 .env.production(Build)
서버 배포시 사용되며, 아래 명령어로 실행시 동작한다.
$ npm run build
$ yarn build
아래 순서로 동작된다. env.production.local파일이 가장 우선순위가 높다.
.env.production.local > .env.production > .env.local > .env
🎲 .env.test(Test)
테스트 환경시 사용되며, 아래 명령어로 실행시 동작한다.
$ npm run test
$ yarn test
아래 순서로 동작된다. env.test.local파일이 가장 우선순위가 높다.
.env.test.local > .env.test > .env.local > .env
👀 실행 결과
'WEB > React' 카테고리의 다른 글
RN - styled-component 내부에 css 변수를 적용하는 방법 (0) | 2024.02.08 |
---|---|
Vite - React & TS에서 svg파일 import 사용하기 (2) | 2023.11.23 |
이제는 Vite로 개발을 해보자(CRA안녕...) (0) | 2023.07.17 |
Vite - 환경 변수 설정하고 불러오기 (0) | 2023.07.10 |
React - 무한 롤링 슬라이드(배너) 구현하기 (2) | 2023.07.07 |
먼저 알아야 할 개념
웹 프론트개발을 시작했다면 먼저 알아야할 지식과 개념들을 먼저 정리한다.
😶🌫️CRA(create-react-app)
프론트엔드를 시작해보겠다고 Get Start문서부터 많은 동영상 강의와 책을 보면 항상 cra
를 통해 React
프로젝트를 작성한다.cra
없이 React
프로젝트를 구성하려면 꽤나 많은 의존 모듈 설치와 설정이 필요하기에 너무나도 용이하게 npx create-react-app my-app
한 줄로 설치하면 프로젝트를 작성을 시작할 수 있었다.
🤔모듈 시스템
갑자기 모듈 시스템?... 그래도 알아보자
자바나 C#, C++같은 언어들은 import와 같은 구문을 통해 모듈 시스템을 구축하지만 과거 자바스크립트에는 모듈 시스템이라는 개념이 없었다.
script태그로 필요한 javascript 파일을 작성하거나 연결하고, 그러다보니 컨텍스트가 섞이거나 전역변수가 남발하고 더욱 레거시한 환경으로 넘어가면 let, const도 없으니 var로만 구성된곳에선 변수가 다른 값으로 대입되게 되고... 많은 문제가 있었다.
이런 문제를 해결하고자 처음 나온 개념이 CommonJS의 require
, module.export
문법이다.
현대의 브라우저는 ESM(ES Modules)
덕에 네이티브 모듈 시스템이 생겼다.
- ESM: import, export와 같은 ES6 표준 모듈 시스템을 말한다.
🔥번들러
이런 문제를 해결하기 위해 Bundler
라는 개념이 나오기 시작한다. 기존에는 script형태로 넣다보니 모든 것들을 다 가져오게 되면서 용량문제도 발생하고, 여러 개의 파일이 아닌 한개의 JS파일이 만들어지기 때문에 모듈의 순서와 언제 불러와야 할 지에 대한 순서 문제도 있었는데, 번들러덕에 이런 것들이 해소되기 시작한다.
번들러 개념이 잘 없는 개발자들도 종종 들어본 번들러가 있다. 바로 Webpack
으로 cra에도 도입된 번들러이기도 하다.Webpack
덕분에 위에서 발생하던 문제들은 해결되었지만, 속도의 문제가 있었다. JS파일을 하나로 만들어주기 위해 코드 수정이 이루어질때마다 새롭게 빌드를 하게되고 규모가 커질수록 파일이 많아지게되니 시간이 오래걸리면 대기시간이 길어지는 개발자들의 피로도가 엄청나게 늘어났다.
🍬ES Build의 등장
이번엔 ES Build
이다.
기존 번들러(Webpack
)의 문제를 해결하기 위해 나왔다. 대략 100배 이상 빠르다고 한다.
JS를 기반으로 작성된 번들러와 달리 Go
라는 언어로 작성되어 빠른 빌드가 가능하게 되었다.
다만 Webpack
은 HMR
외에도 code splitting
과 같은 기능을 종합적으로 제공했기에 복잡한 앱에서 ES Build
가 대체할 수는 없었다.
⚡이제는 Vite를 씁시다!
Vite
(바이트가 아니다 빗!!! 이다.) 페이지만 들어가도 빠르다라는 키워드를 자주 접할 수 있다.
현대의 브라우저는ESM(ES Modules)
가 추가되었고, 위에서 언급한 ES Build
를 활용하여 Webpack
을 대체한다.
기존의 번들러는 소스 코드가 변경되면 전체적으로 번들링 과정을 다시 거쳐야하다 보니 서비스가 거대해질수록 빌드시간이 늘어나게 된다.Vite
는 HMR
방식을 지원하는데, 여기서 포인트는 ESM
을 활용하여 수정해야 할 부분만 소스 코드만 반영할 수 있게 처리했다.
- HMR: 앱을 종료하지 않고 갱신된 파일만 교체하는 방식
dependencies
와 source code
로 구분하고 개발시 내용이 바뀌지 않는 부분들은 ES Build
로 번들링하고 수정하는 부분만 ESM
으로 소스 코드를 반영한다. ESM은 요청받은 모듈만 전달하기 때문에 훨씬 빠르게 서버에 반영이 된다.
- dependencies: 개발시 내용이 바뀌지 않을 JS코드로 기존에는 매우 비효율적인 시간으로 번들링이 이루어졌지만 Vite는 ESBuild를 통해 10~100배 가까이 빠른 속도를 제공한다.
- source code: JSX, CSS와 같이 컴파일링이 필요하고 수정이 잦은 부분들을 말한다. Vite는 브라우저가 요청하는 대로 소스 코드를 변환하고 제공한다.
⚙️Vite으로 React 프로젝트 구성하기
npm create vite@latest
빗을 생성한다.- 프로젝트 명을 작성하라고 나온다.(원하는 프로젝트 이름을 입력)
- 프로젝트 명을 입력하면 원하는 개발 프레임워크를 물어본다. 원하는 개발로 선택한다.
- 다음은 타입스크립트로 개발할 것인지 SWC를 도입할 것인지 물어본다.
- 프로젝트 위치로 이동한다.
cd 프로젝트명
- 바로 플젝 시작하면 오류가 난다!!!.
npm i
를 입력하고 기본 라이브러리 의존 설치를 진행한다.
npm run dev
입력하고 프로젝트를 열어본다! 개발 시작!!!
- SWC:
Rust
라는 언어로 제작된 빌드 툴로, JS프로젝트의 컴파일, 번들링을 제공하는 웹 컴파일러 툴이다.
'WEB > React' 카테고리의 다른 글
Vite - React & TS에서 svg파일 import 사용하기 (2) | 2023.11.23 |
---|---|
React - 리액트에서 환경변수 개발용 배포용 나눠서 세팅하기 (2) | 2023.10.16 |
Vite - 환경 변수 설정하고 불러오기 (0) | 2023.07.10 |
React - 무한 롤링 슬라이드(배너) 구현하기 (2) | 2023.07.07 |
Redux 개념에 대해 알아보기(vanilla redux작성) (0) | 2023.06.14 |
⚡Vite에서 환경 변수 설정하고 호출하기
cra에서 환경변수 사용법과 약간의 차이가 있어서 정리하고자 한다.
⚙️Vite에서 환경 변수 설정하기
먼저 .env
파일을 프로젝트 최상단에 생성합니다.
vite에서 호출하는 환경 변수는 VITE_
라는 접두사를 붙여서 작성합니다.
(문자열이라도 "", ''를 제외한 값만 입력합니다.)
.env
VITE_WHEATHER_KEY=1a2b3c4d5e6f7g8k123456789
HELLO_KEY=HELLO // 동작 안함
😶🌫️소스에서 환경변수 호출하기
사용하고자 하는 소스에서 아래의 규칙으로 호출합니다.import.meta.env.환경변수명
sample.tsx
export default function Title(){
console.log(import.meta.env.VITE_WHEATER_KEY);
console.log(import.meta.env.HELLO_KEY);
return null;
}
🪄출력결과
'WEB > React' 카테고리의 다른 글
React - 리액트에서 환경변수 개발용 배포용 나눠서 세팅하기 (2) | 2023.10.16 |
---|---|
이제는 Vite로 개발을 해보자(CRA안녕...) (0) | 2023.07.17 |
React - 무한 롤링 슬라이드(배너) 구현하기 (2) | 2023.07.07 |
Redux 개념에 대해 알아보기(vanilla redux작성) (0) | 2023.06.14 |
React - hook 파헤치기(useMemo) (0) | 2023.06.05 |
🎞️ 롤링 슬라이드
주식이나 뉴스같은 곳에서 한줄로 끊임없이 텍스트가 물처럼 한방향으로 흐르거나 요즘 트렌드의 스타일로 작성된 사이트들을 구경하다 보면 자주 접할 수 있는 스타일의 UI이다.
텍스트뿐만 아니라 관련된 이미지를 통해서도 좀 더 인터렉티브하고 동적으로 움직이다보니 시각적으로 집중이 되는 효과를 확인 할 수 있다.
대표적으로 찾은 기능으로 네이버의 vibe사이트에서 추천 플레이리스트 부분에 아티스트들의 앨범 정보가 끊임없이 흐르는 애니메이션도 볼 수 있는데, 해당 기능을 참고하여 구현하고자 한다.
바이브 url: https://vibe.naver.com/about/.
🤔 동작 방식
구현하기 전 구현에 대한 설명이 필요하다.
- 먼저 슬라이드 형태로 흘러가야할 요소들을 한줄로 세워준다.
- 똑같은 요소들을 하나 더 만들어준다.
- css 속성을 통해 한줄로 이어서 붙여준다. (부모속성에 flex와 nowrap처리를 통해 쉽게 구현이 가능하다.)
- 한줄이 된 슬라이드를 각기 다른 옵션으로 애니메이트를 처리한다.
- 첫번째 원본 영역을 10초간 이동하면 그동안 빈 공백의 10초를 기다려야 하는데, 이때 복사 영역으로 똑같이 붙여서 10초간 이동시키고 무한으로 돌린다.
- 슬라이드 영역에 마우스를 올리면
animation-play-state
의 값을paused
처리 하는 클래스를 토글 형태로 처리하면 된다..stop{ animation-play-state: paused; }
⚙️ 구현에 필요한 기술
css 레이아웃 개념과 animation
의 개념, @keyframe
사용법 익히면 별다른 라이브러리 없이 구현이 가능하다.
hook또한 useState하나만 사용하여 처리 할 예정이다.
아래 구현 예시를 확인해보자.
🪄 구현 시작
HomePage.tsx
import { useState } from "react";
import "./Homepage.scss";
const slides = [
{ color: "red", target: "#" },
{ color: "orange", target: "#" },
{ color: "yellow", target: "#" },
{ color: "green", target: "#" },
{ color: "blue", target: "#" },
{ color: "navy", target: "#" },
{ color: "purple", target: "#" },
];
export default function Homepage() {
const [animate, setAnimate] = useState(true);
const onStop = () => setAnimate(false);
const onRun = () => setAnimate(true);
return (
<div className="wrapper">
<div className="slide_container">
<ul
className="slide_wrapper"
onMouseEnter={onStop}
onMouseLeave={onRun}
>
<div
className={"slide original".concat(
animate ? "" : " stop"
)}
>
{slides.map((s, i) => (
<li
key={i}
className={i % 2 === 0 ? "big" : "small"}
>
<div
className="item"
style={{ background: s.color }}
></div>
</li>
))}
</div>
<div
className={"slide clone".concat(animate ? "" : " stop")}
>
{slides.map((s, i) => (
<li
key={i}
className={i % 2 === 0 ? "big" : "small"}
>
<div
className="item"
style={{ background: s.color }}
></div>
</li>
))}
</div>
</ul>
</div>
</div>
);
}
Homepage.scss
* {
padding: 0;
margin: 0;
}
ul,
li {
list-style: none;
}
.wrapper {
.slide_container {
overflow: hidden;
.slide_wrapper {
display: flex;
flex-wrap: nowrap;
}
.slide {
display: flex;
align-items: center;
flex-wrap: nowrap;
position: relative;
border-top: 1px solid #bbb;
border-bottom: 1px solid #bbb;
padding: 40px 0;
&::before {
content: "";
display: block;
width: 100%;
height: 1px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #bbb;
z-index: 1;
}
&.original {
animation: 10s linear infinite normal none running
infiniteAnimation1;
}
&.clone {
animation: 10s linear infinite infiniteAnimation2;
}
&.stop {
animation-play-state: paused;
}
li {
margin: 0 80px;
cursor: pointer;
z-index: 2;
transition: 0.3s;
transform: scale(1);
&:hover {
transform: scale(0.98);
&::after {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.2);
}
}
&.big {
width: 280px;
height: 280px;
}
&.small {
width: 200px;
height: 200px;
}
.item {
width: 100%;
height: 100%;
}
}
}
}
}
@keyframes infiniteAnimation1 {
0% {
transform: translateX(0%);
}
50% {
transform: translateX(-100%);
}
50.1% {
transform: translateX(100%);
}
100% {
transform: translateX(0%);
}
}
@keyframes infiniteAnimation2 {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(-200%);
}
}
✨ 구현 결과물
아주 잘된다.
바이브처럼 뒷부분에 선도 그어봤다.
괜찮은 배경이미지와 색상 대신 이미지를 활용하면 더욱 좋은 결과물을 볼 수 있을 것 같다.
(아래는 위 소스를 기반으로 작성한 리액트 샘플 소스를 받아볼 수 있는 github주소입니다.)
'WEB > React' 카테고리의 다른 글
이제는 Vite로 개발을 해보자(CRA안녕...) (0) | 2023.07.17 |
---|---|
Vite - 환경 변수 설정하고 불러오기 (0) | 2023.07.10 |
Redux 개념에 대해 알아보기(vanilla redux작성) (0) | 2023.06.14 |
React - hook 파헤치기(useMemo) (0) | 2023.06.05 |
React - typescript 버전으로 변경하면서 오류 해결 (0) | 2023.06.05 |
Semantic tag
웹 페이지 레이아웃을 잡을때 div만을 사용해서 사용할 수도 있지만, 시멘틱 태그를 사용하므로서 웹 사이트의 각 구조에 대한 내용을 명확하게 전달할 수 있다. 머리글은 header
바닥글은 footer
와 같은 태그를 사용하고 문서의 내용은 section
을 사용할 수도 있다.
div태그만으로 레이아웃을 잡을 수 있는데, 시멘틱 태그를 사용하는 이유는 뭘까?
- 일단 SEO(검색엔진 최적화)에 유리해진다.
검색 엔진이 태그의 목적에 맡도록 설계된 사이트는 효율적으로 정보를검색하여 검색 노출이 유리해진다. - 웹 접근성에 효율적이다.
웹 접근성이 필요로 하는 시각장애인인이나 스크린리더같은 환경에서 용이하다. - 유지보수성의 장점
div만 남발하는 경우 class나 내부의 설계된 내용을 확인해야 하지만, 시멘틱 태그를 확인하므로서 해당 태그의 기능이 어떤걸 뜻하는지 확인이 용이해진다.
아래에서 시멘틱 태그별 내용을 정리하고자 한다.
⭐header
웹 사이트의 상단 부분을 맡거나 머리글의 요소이다.
로고나 제목 등이 위치하며 보통은 바디부분의 최상단이지만 불가피한 경우 레이아웃 구조상 메인의 상단으로 작성되는 경우도 있다.
- 일반적으로 header에 포함된 내용이다.
- 하나 이상의 제목 요소(h1 ~ h6)
- 로고 또는 아이콘
- 배포 정보
⭐nav
네이게이션의 줄임말로 목차나 리스트와 같은 항목이 들어가는 요소이다.
다른 페이지로 이동시키는 링크의 덩어리가 위치하다보니 메뉴로 구성되는 경우가 많다.
모든 링크관련 요소가 nav
에 있어야 하는것은 아니다.
일반적으로 ul
, li
, a
태그의 조합으로 구성한다.
⭐main
웹 사이트의 주요 내용이 담기는 요소이다.
사이드바, 저작권 정보, 사이트 로고, 검색 양식과 같은 반복되는 컨텐츠를 포함시키지 않는다.
⭐section
주제, 카테고리, 영역 별로 그룹을 구분하는 용도로 사용하는 요소이다.
⭐article
문서내에서 독립적인 컨텐츠를 지정하는 요소이다.
article의 내용 자체로 독립적인 내용이어야 한다.
- 포럼 게시물
- 블로그 게시글
- 뉴스
⭐aside
좌측이나 우측의 사이드에 붙는 요소이다.
컨텐츠와 별개의 일부 컨텐츠를 작성할 때 사용된다. 주변 컨텐츠와 간접적인 내용이 들어가거나 본문과 부수적인 내용이 들어간다.
구글 광고와 같은 부수적인 내용이 대부분이 aside
에 들어간다.
⭐footer
페이지 내 바닥글을 정의하는 요소이다.
- footer에 포함되는 내용은 아래와 같다.
- 저작권 정보
- 발행 정보
- 연락처
- 사이트맵
- 상단으로 이동시키는 링크
- 관련 문서 정보
'WEB > HTML,CSS' 카테고리의 다른 글
IPhone에서 특정 폰트만 커지는 현상 방지처리 (text-size-adjust) (0) | 2023.04.05 |
---|---|
CSS - before 가상 요소가 기존 요소의 내용을 가릴때(background, image등) (0) | 2023.03.14 |
HTML - id속성에 관하여...(css적용, javascript 사용 ) (0) | 2022.05.18 |
HTML - Input태그에서 엔터 키입력시 submit 동작 현상 막기(form태그 내부 input태그 키입력) (0) | 2021.09.13 |
HTML, CSS - 헤더컬럼 고정형 table 구성하기 (2) | 2020.11.27 |
Redux에 대해 알아보기
그동안 전역 상태관리를 위해 recoil로 충분히 사용이 가능했지만, redux 또한 놓칠 수 없는 개념이기에 학습하고 정리하는 내용을 작성해본다.
먼저 관련된 키워드와 내용에 대해 알아보고, 사용법을 알아보고자 한다.
🔹Action
액션은 Reducer
와 소통하는 존재이다.
변화가 일어나면 액션이라는 것이 발생하는데, 구분자 형태로 사용된다.
액션 작성 규칙
-액션은 객체(Object)
형태로 작성해야한다.
-객체 내부에는 type
이라는 필드를 필수적으로 가지고 있어야 한다.
🔹ActionCreator
액션 객체를 만들어주는 함수이다.
해당 함수는 액션을 만들어주다보니 당연히 Object형태를 반환하고 type 필드를 가진다.
그 외에 동작할 명세를 작성한다.
const changeInput = (text:string) => ({
type: "CHANGE_INPUT",
text
});
🔹Reducer
변화를 일으키는 함수다.
현재 상태와 전달받은 액션 객체 파라미터를 받는다.
두 개의 값을 비교하여 새로운 상태를 반환한다.
구분자할 값이 많아지면 비교적 if문보단 switch문이 가독성이 좋아서 switch를 사용한다.
const initialState = {
counter: 1
}
function reducer(state=initialState, action){
switch(action.type){
case "INCREASE":
return state.counter+1
}
}
🔹Store
프로젝트에 store는 한 개를 가진다.
(여러개를 가질 수도 있지만 복잡해질 수 있음)
스토어에는 현재 어플리케이션의 상태와 리듀서가 들어간다.
그 외에 중요한 내장 함수들로 구성된다.
🔹Dispatch
리듀서에서 state
와 action
두 개의 파라미터를 받는데, 여기서 reducer
에게 action
을 보내는 행위를 dispatch
가 처리한다.
dispatch는 리듀서를 불러와서 current state와 내가 작성한 action을 처리한다.
🔹Subscribe
store에 등록된 값의 변화를 감지하다가 변화가 발생하면 콜백 함수가 동작한다.
🔹Vanilla-Redux
npm install redux
const { createStore } = require("redux");
const divToggle = document.querySelector(".toggle");
const counter = document.querySelector("h1");
const btnIncrease = document.querySelector("#increase");
const btnDecrease = document.querySelector("#decrease");
const TOGGLE_SWITCH = "TOGGLE_SWITCH";
const INCREASE = "INCREASE";
const DECREASE = "DECREASE";
/**
* 액션을 가지고 액션 생성 함수를 작성한다.
* type은 필수값으로 가지고 있어야하며 원하는 형태로 커스텀 할 수 있다.
*/
const toggleSwitch = () => ({ type: TOGGLE_SWITCH });
const increase = (difference) => ({ type: INCREASE, difference });
const decrease = () => ({ type: DECREASE });
// 초기값 설정
const initialState = {
toggle: false,
counter: 0,
};
/**
* reducer defined.
* 변화를 일으키는 함수다. state, action 두 개의 값을 받는다.
*/
function reducer(state = initialState, action) {
switch (action.type) {
case TOGGLE_SWITCH:
return {
// 불변성 유지를 위해 새로운 객체를 return
...state,
toggle: !state.toggle,
};
case INCREASE:
return {
...state,
counter: state.counter + action.difference,
};
case DECREASE:
return {
...state,
counter: state.counter - 1,
};
default:
return state;
}
}
// store작성 리듀서 함수를 넣어준다.
const store = createStore(reducer);
/**
* render함수 작성
*/
const render = () => {
const state = store.getState();
if (state.toggle) {
divToggle.classList.add("active");
} else {
divToggle.classList.remove("active");
}
counter.innerHTML = state.counter;
};
render();
store.subscribe(render);
// 구독하기
// const listener = () => {
// console.log("상태가 업데이트됨.");
// };
// const unsubscribe = store.subscribe(listener);
// unsubscribe();
divToggle.onclick = () => {
store.dispatch(toggleSwitch());
};
btnIncrease.onclick = () => {
store.dispatch(increase(1));
};
btnDecrease.onclick = () => {
store.dispatch(decrease());
};
'WEB > React' 카테고리의 다른 글
Vite - 환경 변수 설정하고 불러오기 (0) | 2023.07.10 |
---|---|
React - 무한 롤링 슬라이드(배너) 구현하기 (2) | 2023.07.07 |
React - hook 파헤치기(useMemo) (0) | 2023.06.05 |
React - typescript 버전으로 변경하면서 오류 해결 (0) | 2023.06.05 |
React - Node JS와 로그인 연동 해보기04(결과물) (0) | 2023.05.26 |
💎useMemo 파헤치기
🤔그래서 useMemo가 뭐지?
리액트에서 useMemo
를 사용하게 되면 불필요한 렌더링을 막고 앱을 최적화 시킬 수 있다.
여기서 Memo는 Memoization
을 뜻하는데, 메모이제이션이란 간단하게 프로그램적으로 반복 행위가 있는 것을 메모리에 저장하여 가져다 사용해서 실행속도를 빠르게 처리하는 기술을 뜻한다.
즉, 한번 동작자체가 부하가 심한 행위가 있는 기능이면서 페이지의 렌더링이 심한 컴포넌트에서 적용하기에 안성맞춤 기술이다.
아직 텍스트만으론 잘 이해가 되질 않는다. 더욱 자세히 알아보겠다.
⚙️useMemo의 구성
function fn(){...}
useMemo(()=> fn(), []);
useMemo
는 콜백함수와 의존성 배열 2개의 매개변수를 받는다.
첫번째는 저장(캐싱) 할 함수값을 넣는다.
두번째 의존성 배열부분에는 업데이트 되었을때 저장한 함수가 동작될 요소를 넣어준다.
즉, 배열에 저장된 요소가 업데이트 되면 그때 저장된 함수가 동작된다.
빈 배열을 넣게되면 페이지가 처음 로드되었을때 한번만 캐싱되고 이후부터 사용할 때만 캐싱된 함수를 호출해서 사용한다.
🙃그렇다면 무조건 쓰면 좋을까?
그렇다고 무조건 모든 객체나 원시타입들을 useMemo
에 저장해놓고 쓰는게 좋을까?
대답은 No이다.
메모이제이션이기 때문에 결국 useMemo
에 등록된 요소들은 결국 어딘가에 저장이 되는데, 개발을 하면서 상황에 맞지 않는 모든 것을 다 저장한다면 오히려 페이지에 부하가 발생하고 느려지는 상황을 초래할 수 도 있다.
😤그럼 언제 써야합니까?!
예시가 제일 빠른 확인이 가능할 것 같다.
import { useState } from "react";
function slow(ms: number) {
const wakeUpTime = Date.now() + ms;
while (Date.now() < wakeUpTime) {}
}
function calculator(arr: number[]) {
console.log("calculator --- start");
slow(100);
if (arr.length === 0) return 0;
return Math.floor(arr.reduce((a, b) => a + b, 0) / arr.length);
}
// useMemo 테스트 컴포넌트
export default function TestUseMemo() {
const [number, setNumber] = useState("");
const [inputNumber, setInputNumber] = useState<number[]>([]);
return (
<div>
<div>
<input
type="number"
name="input"
value={number}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNumber(e.target.value);
}}
/>
<button
onClick={() => {
setInputNumber((prev) => [...prev, +number]);
setNumber("");
}}
>
추가
</button>
<div>
<b>지금까지 등록된 값</b>
<ul>
{inputNumber.map((x) => (
<li>{x}</li>
))}
</ul>
</div>
<div>
<b>등록된 평균값:</b>
{calculator(inputNumber)}
</div>
</div>
</div>
);
}
일단 부하가 심한 기능이라는 가정을 위해 일부러 slow
라는 함수를 정의하여 calculator()
내부에서 호출하여 강제로 동기화 기능을 처리하였다.
해당 부분때문에, 약 0.1초 정도의 부하만 줬는데도 부드럽지 않고 끊기는 느낌이 있다.
해당 예제는 input에 입력한 숫자를 추가하면 평균값을 보여주는 간단한 예제인데, 벌써부터 최악인 모습으로 input에 입력할때마다 onChange
에서 변경된 state
값에 의해 매번 페이지가 재렌더링이 일어나면서 느려터진 slow
함수덕에 엄청 버벅거리는 모습을 경험할 수 있다.
실제로도 input에 "1234" 4번만 입력했는데 매번 calculator가 호출되고 있고 있다.
이럴때 바로 useMemo
를 사용하면 추가버튼을 누를때만 해당 함수가 호출되어 최적화를 할 수 있다.
최적화하기
import { useState, useMemo } from "react";
function slow(ms: number) {
const wakeUpTime = Date.now() + ms;
while (Date.now() < wakeUpTime) {}
}
function calculator(arr: number[]) {
console.log("calculator --- start");
slow(100);
if (arr.length === 0) return 0;
return Math.floor(arr.reduce((a, b) => a + b, 0) / arr.length);
}
// useMemo 테스트 컴포넌트
export default function TestUseMemo() {
const [number, setNumber] = useState("");
const [inputNumber, setInputNumber] = useState<number[]>([]);
/*
* useMemo를 통해 최적화를 한다.
* 두번째 매개변수 의존 배열에는 해당 값이 바뀔때만 호출될 수 있도록 설정한다
* 첫번째 매개변수에는 콜백함수를 넣는데 두번째에 넣은 값이 바뀔때마다 해당 콜백이 호출된다.
*/
const avg = useMemo(() => calculator(inputNumber), [inputNumber]);
return (
<div>
<div>
<input
type="number"
name="input"
value={number}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNumber(e.target.value);
}}
/>
<button
onClick={() => {
setInputNumber((prev) => [...prev, +number]);
setNumber("");
}}
>
추가
</button>
<div>
<b>지금까지 등록된 값</b>
<ul>
{inputNumber.map((x) => (
<li>{x}</li>
))}
</ul>
</div>
<div>
<b>등록된 평균값:</b>
{avg}
</div>
</div>
</div>
);
}
이제 초기 렌더링될 때 한번 호출되고 input에 입력해보면 전혀 부하가 발생하지 않는 걸 볼 수 있다.
숫자를 입력하고 추가버튼을 누르면 그때 콜백함수가 호출되면서 calculaotr가 호출된다.
⚠️유의사항
어느정도 useMemo
를 언제 사용해야할지와 사용법에 대해 알아보았는데, 조심해야할 부분이 있다.
자바스크립트에는 원시타입과 객체타입의 개념이 있다.
원시타입의 경우 특정 정수나 문자열이 있을 때, 해당 값을 주소값에 넣어두고 같은 값이라면 어떤 변수든 같은 주소값을 바라보기에 아래와 같이 비교 연산이 가능하다.
const test = "hello";
const test2 = "hello";
test === test2 // true 같은 주소값을 바라본다.
하지만 객체타입인 배열이나 객체의 종류의경우 비교연산시 문제가 발생한다.
const obj = {name: "mike"}; // 0x01 에 할당
const obj2 = {name: "mike"}; // 0x02에 할당
obj === obj2 // false 주소값이 서로 다르다.
shallow copy
, deep copy
의 개념이 필요한 부분인데 이부분에 대해서 더 자세히 알아보면 좋을 것이다.
객체타입의 오류
아래 예제를 보면 위에서 왜 객체 타입에 대한 개념을 언급했는지 알 수 있다.
import { useState, useEffect } from "react";
// useMemo 테스트 컴포넌트
export default function TestUseMemo() {
const [name, setName] = useState("");
const [regitName, setRegitName] = useState("");
const obj = { name: regitName };
useEffect(() => {
console.log("regitName에 추가될 때만 호출되어야 합니다!");
}, [obj]);
return (
<div>
<div>
<input
type="text"
name="input"
value={name}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
}}
/>
<button onClick={() => setRegitName(name)}>변경</button>
<div>
<b>입력한 이름</b>
<ul>{obj.name}</ul>
</div>
</div>
</div>
);
}
obj라는 객체를 useState
에서 받아온 regitName
값으로 정의하는 경우 해당 경우에도 매번 재 렌더링이 발생한다.
새로운 객체를 계속해서 새로 넣는다고 판단되기 때문인데, 이런 경우에도 useMemo
를 통해 처리가 가능하다.
객체 개선하기
import { useState, useEffect, useMemo } from "react";
// useMemo 테스트 컴포넌트
export default function TestUseMemo() {
const [name, setName] = useState("");
const [regitName, setRegitName] = useState("");
// const obj = { name: regitName };
const obj = useMemo(() => {
return { name: regitName };
}, [regitName]);
useEffect(() => {
console.log("regitName에 추가될 때만 호출되어야 합니다!");
}, [obj]);
return (
<div>
<div>
<input
type="text"
name="input"
value={name}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
}}
/>
<button onClick={() => setRegitName(name)}>변경</button>
<div>
<b>입력한 이름</b>
<ul>{obj.name}</ul>
</div>
</div>
</div>
);
}
위 처럼 obj객체를 useMemo
로 감싸서 처리하면 추가 버튼을 누르는 경우에만 regitName
이 동작하면서 useEffect
의 콘솔값이 그때 찍히는걸 볼 수 있다.
'WEB > React' 카테고리의 다른 글
React - 무한 롤링 슬라이드(배너) 구현하기 (2) | 2023.07.07 |
---|---|
Redux 개념에 대해 알아보기(vanilla redux작성) (0) | 2023.06.14 |
React - typescript 버전으로 변경하면서 오류 해결 (0) | 2023.06.05 |
React - Node JS와 로그인 연동 해보기04(결과물) (0) | 2023.05.26 |
React - Node JS와 로그인 연동 해보기03(뷰 추가) (0) | 2023.05.25 |
변환하면서 자주 만나는 에러 종류 정리
React에서 typescript로 작성하면서 자주 만나는 에러들을 정리해보려고 한다.
No overload matches this call.
export default function S_useMemo(){
const [number, setNumber] = useState("");
const [list, setList] = useState([]);
const onInsert = (e: React.MouseEvent<HTMLButtonElement>) => {
setList((prev) => prev.concat(parseInt(number))); //error No overload matches this call.
setNumber("");
};
return (...)
}
기존 state의 값인 list에 concat으로 붙여서 기존 배열을 늘려나가는 방식으로 동작하는 insert함수이다.
해당부분에서 state에 담긴 문자열 number를 정수형으로 변환하여 붙여넣는 방식인데, No overload matcheds this call 에러가 발생하고 있다.
기본적으로 typescript이더라도 useState초기값에 따라 값을 추론하여 타입을 지정해주는데, const [list, setList] = useState([]);
이부분에서 list배열은 어떤 타입의 배열을 받는건지 정해진 값이 없어서 발생한 오류이다 제네릭으로 정수형 배열을 받는다고 처리해주면 된다.
해결
export default function S_useMemo(){
const [number, setNumber] = useState("");
const [list, setList] = useState<number[]>([]);
const onInsert = (e: React.MouseEvent<HTMLButtonElement>) => {
setList((prev) => prev.concat(parseInt(number))); //error No overload matches this call.
setNumber("");
};
return (...)
}
'WEB > React' 카테고리의 다른 글
Redux 개념에 대해 알아보기(vanilla redux작성) (0) | 2023.06.14 |
---|---|
React - hook 파헤치기(useMemo) (0) | 2023.06.05 |
React - Node JS와 로그인 연동 해보기04(결과물) (0) | 2023.05.26 |
React - Node JS와 로그인 연동 해보기03(뷰 추가) (0) | 2023.05.25 |
React - Node JS와 로그인 연동 해보기02(서버 추가 설정) (0) | 2023.05.25 |