[React] React 기본 총정리!

업데이트:



이번 포스팅은 React를 처음 공부할때 필요한 모든 지식을 총 집합해서 정리해보려고 한다. React란 무엇이고, 왜 사용하는지 부터 기초문법까지 다뤄보도록 하겠다. 세세한 문법까지 모두 다룰수는 없겠지만 꼭 기억해야하는 문법을 통해 간단한 토이프로젝트 까지 진행해볼 수 있을 정도의 개념을 정리해보자..! 작성이후에도 추가적으로 필요한 내용은 추가하면서 이 포스팅만 보면 React 개발을 어느정도 할 수 있을만큼의 도움이 되는 글을 작성하는것을 목표로 하겠다..!






✅ React란?

리액트는 사용자 인터페이스 (UI)를 구축하기 위한 선언적이고 효율적이며 유연한 자바스크립트 라이브러리이다. Augular, Vue와 달리 자바스크립트 문법을 그대로 사용하기 때문에 자바스크립트를 알고있다면 학습하는데 어려움이 많이 없다.

리액트를 사용하지 않아도, HTML/CSS 그리고 JavaScript를 사용해서 웹페이지를 충분히 만들 수 있지만 우리는 리액트를 사용한다. 도대체 왜 리액트를 사용하는 걸까 ?




📌 React를 사용하는 이유

  1. 데이터 바인딩이 쉽게 가능하다!

데이터 바인딩이란 화면상에 보여지는 데이터(View)와 브라우저 메모리에 있는 데이터(Model)을 일치시키는 것을 말한다. 좀 쉽게 설명하면 데이터를 HTML 요소에 꽂아넣어주는거라고 볼 수 있다.

일반적으로 JS를 사용하면 document.getElementById("변수명").innerHTML = "수정" 이런식으로 바인딩해서 HTML내용을 바꿔주곤 한다. 근데 이런 방식은 코드가 길기때문에 보기에도 불편하고 수정도 불편하다.

그런데 리액트에서는 이런 데이터 바인딩을 매우 쉽게 할 수 있다. 변수에 데이터를 담아주고 { } 안에 넣어 사용하면 끝이다!

function App(){
  var data = "안녕하세요";
  return (
	<>
	  <p> { data } </p>	// 안녕하세요
	</>
  )
}




  1. 컴포넌트 단위 개발

리액트는 컴포넌트 단위로 개발을 할 수 있다. 기존의 웹 프레임워크는 MVC 방식으로 분리해서 관리했기 때문에 각 요소의 의존성이 높아 재사용이 어렵다는 단점이 있었는데, 컴포넌트는 MVC의 뷰를 독립적으로 구성해서 재사용이 가능하게 하고 이를 통해 새로운 컴포넌트를 쉽게 만들 수 있게 한다.

리액트는 사용자 정의 태그를 만드는 기술이다 라고 표현할 수 있을 정도로 컴포넌트의 사용이 리액트를 사용하는 가장 큰 이유라고 볼 수 있다. 여러 태그들을 하나의 컴포넌트로 만들어 독립된 부품으로 만들 수 있고, 더 적은 복잡도로 소프트웨어를 만들수 있다.


  1. JSX를 지원한다!

JSX는 JavaScript + XML 로 구성된 자바스크립트 확장 구문인데, JS에서 HTML을 작성하듯 코드를 작성할 수 있는 큰 장점이 있다.

const hello = () => { 
  return ( 
	<div>
		<h1>안녕하세요.</h1>
	</div>
  ); 
};

위의 코드처럼 작성하는것이 JSX이고 약간의 규칙이 존재한다.

  • 컴포넌트에 여러 요소가 있을때 반드시 부모 요소 하나가 감싸는 형태여야 한다.
  • 자바스크립트의 값을 JSX안에서 랜더링 할 수 있다. { 변수명 } 의 형태로 사용가능하다.
  • if문을 사용할 수 없고 조건부 연산자를 사용한다.


function  App()  {
	const  name  =  '리액트';  
		return  (  
			<div>  { 
					name  ===  '리액트'?  
					(<h1>리액트</h1>) : (<h2>리액트가 아님</h2>)
					}  
			</div>
	);  
}
  • 기존 CSS 스타일 지정할때 background-color와 같이 - 문자가 포함된 이름을 -를 없애고 카멜 표기법으로 작성해야 한다! ex ) background-color -> backgroundColor
  • 기존 클래스명을 선언할때 class="title"처럼 선언하던것을 className="title" 의 형식으로 선언해야 한다.
  • 태그 사용시 반드시 닫아주는 태그를 사용해야만 한다!
  • JSX에 style속성을 넣으려면 무조건 style= { 스타일 } 처럼 중괄호 사용해야한다. 그리고 스타일은 무조건 object 자료형으로 만든 스타일을 사용해야 한다. ` <div style = { { color : ‘blue’ } } > 개발 블로그 </div>`






✅ React 개발 환경 설정

📌 사전 개발 환경

  • node.js 설치
  • IDE - VS Code ( 쓰고싶은거 쓰면됨 )




📌 프로젝트 생성 및 실행

프로젝트를 진행 할 폴더를 생성하고 VS Code 폴더열기를 통해 폴더에 접근한뒤 해당 폴더 위치로 터미널을 켜줍니다.

이후 아래 명령어를 터미널에 입력해줍니다.

npx create-react-app [디렉토리명]
cd [디렉토리명]
npm start

==========================================================================
// 예시
npx create-react-app basic-app	// basic-app이라는 폴더에 리액트 환경 설치
cd basic-app			// 설치후에 터미널 경로를 basic-app폴더로 이동
npm start				// 리액트 실행!

이렇게 하면 프로젝트가 실행된다! 결과창은 보통 자동으로 웹브라우저로 나타나고 localhost:3000으로 접속하면 볼 수 있다.






✅ React 프로젝트 구조

터미널 명령어 create-react-app을 통해 생성한 리액트 프로젝트의 구조는 아래와 같다.

  basic-app					
	├─ node_modules
	├─ package-lock.json
	├─ package.json
	├─ public
	│ 	├─ favicon.ico
	│ 	├─ index.html
	│ 	├─ logo192.png
	│ 	├─ logo512.png
	│ 	├─ manifest.json
	│ 	└─ robots.txt
	├─ README.md
	└─ src
		├─ App.css
		├─ App.js
		├─ App.test.js
		├─ index.css
		├─ index.js
		├─ logo.svg
		├─ reportWebVitals.js
		└─ setupTests.js

  • node_modules : 현재 프로젝트에 포함된 라이브러리들이 설치되어 있는 폴더
  • package.json : 프로젝트 이름, 버전 등에 대한 정보와 라이브러리 목록이 포함되어 있다.
  • public : 정적 파일이 모여 있는 폴더로 변할 일이 없는 이미지를 주로 넣는다
  • src : 리액트 내부에서 작성하는 거의 모든 파일들이 들어있는 폴더이다.
    • App.js : 메인페이지라고 보면된다.
    • App.css : App.js에 대한 css파일이다.
    • index.js : App.js를 실행해주는 JS파일이다.


이정도로만 알고있으면 문제없이 리액트를 다룰 수 있을거라고 생각한다. 각 폴더, 파일마다 더 세세하게 들어갈 수 있지만, 프론트엔드 개발자를 희망하는 사람이 아니라면 이정도만 알고 가도 리액트 개발하는데 큰 문제는 없어 보인다.






✅ 컴포넌트란 ?

컴포넌트의 사용은 리액트의 본질이라고 표현 할 수 있을 정도로 중요하다. 리액트는 화면에서 UI 요소를 구분할 때 ‘컴포넌트’라는 단위를 사용하는데, 쉽게 말하면 리액트에서 앱을 이루는 가장 작은 조각이라고 할 수 있고, 레고 블록으로 집을 쌓게 된 경우 하나의 블록이 ‘컴포넌트’라고 할 수 있다. 컴포넌트는 새로운 컴포넌트를 생성할 수 도 있고, MVC 의 뷰를 독립적으로 구성하여 재사용할 수도 있다.



🚩 [예제 1] 일반적인 컴포넌트 사용법

컴포넌트를 모아두기위한 폴더 component를 생성하고 컴포넌트 JS를 생성한다.

파일경로 : src/component/Component1.js

export default function Component1(){
	return (
		<div> 컴포넌트 1 출력 </div>
	)
}


이제 컴포넌트로 만들어준 Component1.jsApp.js에서 사용해보자!

파일경로 : src/App.js

import './App.css';
import Component1 from './component/Component1';	// 컴포넌트를 import해준다

function App() {
  return (
    <div className="App">
        <Component1/> 			// 컴포넌트 1 출력
    </div>
  );
}
export default App;

컴포넌트를 다른 JS파일에서 사용하기 위해선 해당 컴포넌트를 import 해줘야만 한다. import 컴포넌트명 from 컴포넌트위치; 형태로 import 할 수 있다. 이때 작성한 컴포넌트명을 변수명처럼 사용하면 된다. 위의 코드에서는 컴포넌트명Component1 로 지정해줬으므로 <Component1/> 이라고 작성하면 해당 컴포넌트를 사용할 수 있다.



보통 컴포넌트 폴더를 만들고 그안에 컴포넌트 JS파일을 만들어서 컴포넌트별로 관리해 import해서 사용하지만, 하나의 JS파일에서 코드를 정리하기 위해 컴포넌트를 사용할 수도 있다.

function Header() {
  return (
	<header>
		<h1> 리액트 재밌다 </h1>
	</header>
  }
}


function App(){
  return (
	<Header/>
	<Header></Header>		// Header를 컴포넌트로 사용함
	<Header></Header>
  )
}


위의 코드는 하나의 파일내에서 컴포넌트를 사용한 예제이다.




🚩 [예제 2] 컴포넌트 안에 컴포넌트 사용하기

컴포넌트 내에서도 다른 컴포넌트를 불러와 사용할 수 있다. 코드로 보자.

파일경로 : src/component/SubComponent.js

export default function SubComponent(){
    return (
        <div> Welcome! This is SubComponent! </div>
    )
}


파일경로 : src/component/Component1.js

import SubComponent from "./SubComponent"

export default function Component1(){
    return (
        <div> 
            <h1>Hello</h1> 			// Hello
             <SubComponent/> 		// Welcome! This is SubComponent!
        </div>
    )
}




🚩 컴포넌트 사용시 주의사항

  • 컴포넌트 이름은 반드시 대문자로 시작해야하고 CamelCase를 사용하는게 좋다.
  • 컴포넌트에서 JSX 리턴시에는 반드시 하나의 태그로 전체를 감싸서 리턴해야 한다. 태그로 감싸지 않거나, 두개이상의 태그를 사용할 수 없다!
    • ex ) return ( <div> 리턴값 </div> )
    • ex ) return ( <> 리턴값 </>
    • ex ) return ( <div> </div> <div> </div> ) 이런 경우는 안됨!
  • 리액트에서 html태그는 반드시 소문자로!









✅ CSS 적용

리액트에서 CSS를 적용하는 방법은 크게 3가지이다. 첫번째는 인라인 스타일, 두번째는 외부 CSS파일 import, 마지막으로는 CSS 모듈 사용 이다. 각각의 경우를 예제를 통해 알아보도록 하겠다.



📌 인라인 스타일 CSS

일반적으로 인라인 스타일 CSS 적용은 잘 사용하지 않고 권장하지 않는다. 코드 가독성이 떨어지고 관리하기도 불편하기 때문이다. 그럼에도 언급하는 이유는 JSX에서 CSS는 반드시 객체형태로 작성해야 하는 차이점이 있기 때문이다. 예제를 통해 알아보자.

import Message from "./Message"

export default function Hello(){
    return (
        <div> 
            <h1 style = {
                 {  color : '#f00',                  //객체이기 때문에 ; 아니고 , 
                    borderRight : '2px solid #000',  // - 쓰지 말고 카멜케이스
                    marginButtom : "30px",
                    opacity : 0.5                    // 숫자는 그대로
                 }
            } 
			<h1> Hello</h1> 
            <Message/>			// Welcome! 카카오 클라우드 컴퓨팅
            <Message/>			// Welcome! 카카오 클라우드 컴퓨팅
            <Message/>			// Welcome! 카카오 클라우드 컴퓨팅
        </div>
    )
}

CSS스타일을 보면 중괄호가 두개인것을 볼 수 있다. 첫번째 중괄호는 데이터 바인딩을 위한 중괄호이고, 두번째 중괄호는 객체형식선언을 위한 중괄호이다. 그리고 각 스타일 속성은 객체형식이므로 =이 아닌 :을 사용해 선언한다.

다음과 같이 적용된다.




📌 외부 CSS

기본 리액트 프로젝트의 CSS파일은 두가지가 있다. 첫번째로 index.css는 전체 프로젝트에 영향을 미치는 스타일을 작성할 수 있고 , 두번째 App.css 에는 App.js 컴포넌트에 한정된 스타일이 작성되어 있다. 그러나 App.js 컴포넌트에만 적용되는건 또 아니기도 하다. 때문에 이후 설명할 CSS 모듈이 필요하다.



🚩 [예제 1]

경로 : src/App.css

.csstest { color : red ; }


경로 : src/App.js

import './App.css';
import Hello  from './component/Hello';

function App() {
  return (
    <div className="App">
       <p className="csstest"> AppCssTest </p>	// CSS적용
       <Hello /> 
    </div>
  );
}
export default App;





🚩 [예제 2] 외부 CSS import

경로 : src/component/Hello.js

import Message from "./Message"
import '../App.css'; 

export default function Hello(){
    return (
        <div>
            <h1 className="csstest">Hello</h1>
            <Message/>
            <Message/>
            <Message/>
        </div>
    )
}

앞서 작성한 App.css를 import해서 사용하는 방법이다.




🚩 [예제 3] CSS 모듈 컴포넌트 별로 CSS 스타일을 별도로 관리하고 싶을 때 사용한다. 보통 컴포넌트이름.module.css로 사용한다.

경로 : src/component/Hello.module.css


import Message from "./Message"
import styles from "./Hello.module.css";
 

export default function Hello(){
    return (
        <div>  
            <h1 className= {styles.csstest}> Hello</h1>
            <Message/>
            <Message/>
            <Message/>
        </div>
    )
}







✅ Props ( 속성 )

리액트에서 컴포넌트들을 만들면 컴포넌트는 다양한 HTML태그로 구성되어 있을것이다. 그런데 HTML 태그는 src, width, height 등과 같은 속성들을 가지고 있고 이에 따라 HTML태그의 상태가 다르게 적용된다. 그래서 리액트에서도 이러한 HTML 속성값들을 부여할 수 있는 기술이 존재하는데 바로 PROP 이다.

아주 쉬운 예제를 통해 props를 적용하는 방법을 직접 보면서 설명해보도록 하겠다. 먼저 기본 틀을 만들어놓고 설명하도록 하겠다.

import  './App.css';

function  Header() {
	return (
		<h1> REACT</h1>
		)
	}


function  Nav() {
	return (
		<nav>
			<ol>
				<li><a  href="/read/2">css</a></li>
				<li><a  href="/read/2">css</a></li>
				<li><a  href="/read/3">js</a></li>
			</ol>
		</nav>
	)
}

 
function  Article() {
	return (
		<article>
			<h2> Welcome </h2>
			Hello, WEB
		</article>
	)
}


function  App() {
	return (
		<div>
			<Header></Header>
			<Nav></Nav>
			<Article></Article>
		</div>
	);
}

export  default  App;





🚩 [예제 1] 컴포넌트에 props 전달하기

function  Header(props) {	// 함수에 매개변수로 props를 넣어준다
	return (
		<header>
			<h1>  <a  href="/">{props.title}</a></h1>	// 중괄호에 속성을 넣어준다
		</header>
	)
}

function  App() {
	return (
		<div>
			<Header title="REACT"></Header>	// props로 보내줄 속성을 설정한다
			<Nav></Nav>
			<Article></Article>
		</div>
	);
}

<Header> 함수에 파라미터를 지정하고 이름은 props 라고 지정한다. 파라미터 이름은 어떤걸 설정해도 문제가 없지만, 대부분의 코드에서 props라고 지정하기때문에 우리도 props 라고 설정하도록 하자.

그리고 이 props를 가져오려면 return문 안에 중괄호에 넣어준 형태로 가져올 수 있다. <h1> <a href="/">{props.title}</a></h1> 위의 코드에서는 App.js에서 Header의 title을 지정해줬으므로 {props.title}의 형태로 가져왔다.

props를 설정하는 순서가 좀 헷갈릴거 같아서 정리해보면,

  1. 컴포넌트에 매개변수 props를 추가해준다. ` function Header(props) `
  2. 속성값을 사용하려면 return문에 {props.속성} 의 형태로 넣어준다. <h1> <a href="/">{props.title}</a></h1>
  3. App.js에 해당 태그에 속성을 부여한다. <Header title="REACT"></Header>

이렇게 하면 처음과 똑같은 결과를 볼 수 있다.




🚩 [예제 2] 하나의 props로 여러개의 태그 만들기

props를 이용하면 하나의 props만으로 내용이 다른 여러개의 태그를 만들 수 있다.

function  Article(props) {	// 함수에 매개변수로 props를 넣어준다
  return (
	<article>
		<h2> {props.title} </h2>	// 중괄호에 속성을 넣어준다
		{props.body}
	</article>
  )
}

function  App() {
  return (
	<div>
		<Header title="REACT"></Header>	// props로 보내줄 속성을 설정한다
		<Nav></Nav>
		<Article title="Welcome" body="Hello,Web"></Article>	// article 태그
		<Article title="Hi" body="Hello,REACT"></Article>	// article 태그2
	</div>
  );
}


결과를 보면 Article태그를 두번 작성한것만으로 하나의 props를 통해 출력된것을 볼 수 있다. 이렇게 props를 활용해서 코드의 재사용을 높일 수 있다!




🚩 [예제 3] props에 객체 담기

props에는 객체형태로 변수를 담아서 전달할 수도 있다.

function  Nav(props) {
  const  listProps = [];

  for(let  i=0;i<props.topics.length;i++){
	let  t = props.topics[i];
	listProps.push(<li  key={t.id}>  {t.title}</li>) // li는 key값 설정 필요
  }
  
  return (
    <nav>
	  <ol>
	    {listProps}
	  </ol>
    </nav>
  )
}

function  App() {
  return (
  
	const  topics = [
	  {id:1, title:"html", body:"html is ..."},
	  {id:2, title:"css", body:"css is ..."},
	  {id:3, title:"javascript", body:"javascript is ..."}
	]
	
	<div>
		<Header title="REACT"></Header>	// props로 보내줄 속성을 설정한다
		<Nav topics={topics}></Nav>
		<Article title="Welcome" body="Hello,Web"></Article>	// article 태그
		<Article title="Hi" body="Hello,REACT"></Article>	// article 태그2
	</div>
  );
}

코드가 좀 복잡한데 하나하나 순서대로 설명을 해보면

  1. 기존 Nav는 리스트 형태로 구성되어 있다.
  2. 각각의 list에 대한 title, body를 topics라는 배열에 담는다. 이때 id를 반드시 설정해줘야만 한다! ( key에 대한 설명은 너무 길기때문에 그냥 알아만 두자 )
  3. Nav 컴포넌트에 props로 넘어온 list의 속성을 담아줄 배열을 선언한다.
  4. for문을 사용해 listProps 배열에 list속성을 담아준다.
  5. return문에 listProps를 출력한다.

이런 형태로 props를 활용해 객체를 전달해줄 수도 있다!






✅ 이벤트 처리

리액트의 이벤트 처리는 HTML 이벤트 처리와 거의 같다. 이벤트 역시 props를 활용해 전달해주는 원리이다. 기본적인 사용방법은 다음과 같다.

  • onClick = { 함수 }
  • onClick = () => { 실행할 내용 } }


🚩 [예제 1] 이벤트 핸들러 등록

export default function Message(){

    function onClickHandle(){
        console.log("clicked");
        alert("clicked");
    }

    return (
        <div> Welcome! 카카오클라우드컴퓨팅 
            <button onClick={onClickHandle}>Click</button>
        </div>
    )
}



어렵지 않다. 이벤트를 함수로 선언하고 데이터바인딩해주면 된다.




🚩 [예제 2] 이벤트 핸들러 등록, event 객체 활용

export default function Message(){
    function onClickHandle(){
        console.log("clicked");
        alert("clicked");
    }
    function onChangeName(event){			// text를 입력하면 console에 출력
        console.log(event.target.value); 	// 
    }
    return (
        <div> 
             Welcome! 
             <input type="text" onChange={onChangeName} /> 
             카카오클라우드컴퓨팅 
            <button onClick={onClickHandle}>Click</button>
        </div>
    )
}



input태그에 텍스트가 입력될 때마다 console.log를 찍어주는 이벤트이다. event객체를 활용했다.




🚩 [예제 3] 익명 함수로 이벤트 핸들러 등록

export default function Message(){
    function onClickHandle(){
        console.log("clicked");
        alert("clicked");
    }
    function onChangeName(event){
        console.log(event.target.value); 
    }
    return (
        <div> 
             Welcome! 
             <input type="text" onChange={onChangeName} /> 
             <input type="text" onChange={(event) => {
                    console.log(event.target.value);        
             } } />
             카카오클라우드컴퓨팅 
            <button onClick={onClickHandle}>Click</button>
        </div>
    )
}


이벤트를 함수로 따로 작성하지 않고 익명함수 형태로 바로 지정해준 방식이다.






✅ State

리액트에서는 State역시 정말 중요한 요소이다. props와 거의 유사하지만 약간의 차이가 있는데 이를 공식문서에는 어떻게 정의했는지 알아보자.

props와 state는 일반 자바스크립트 객체다. 두 객체 모두 렌더링 결과물에 영향을 주는 정보를 갖고 있는데 한 가지 중요한 방식에서 차이가 있다. props는 (함수 매개변수처럼) 컴포넌트에 전달되는 반면 state는 (함수 내 선언된 변수처럼) 컴포넌트 안에서 관리한다. React에서 this.props와 this.state는 모두 렌더링된 값을 나타낸다. 다시 말해 현재 화면에 보이는 걸 말한다


또 다른 문서에 따르면 이러하다.

state는 컴포넌트의 현재 상황에 대한 정보를 나타내기 위해 리액트에서 쓰는 일반 자바스크립트 객체다. 함수에 선언된 모든 변수와 마찬가지로 컴포넌트에서 관리된다. 차이점은 일반 변수는 함수가 종료될 때 사라지지만 state 변수는 리액트에 의해 보존된다는 것이다. 컴포넌트의 역할은 원시 데이터를 HTML로 바꾸는 것이기 때문에 이 원시 데이터는 props, state 객체로 구성돼 있다고 생각할 수 있다. props와 state가 render()의 입력 데이터라고 말할 수도 있다. 따라서 state는 컴포넌트의 동작을 제어하는 일부 속성을 나타낸다.

위의 정의들을 정리해보면, state는 UI에 나타나는 요소들을 비동기방식으로 변환시켜주기 위해 사용하고, 컴포넌트의 동작을 제어하는 일부 속성이다.

이제 state를 사용하는 기본예제를 통해 사용법을 알아보자.




🚩 [예제 1] 이벤트 핸들러 등록, event 객체 활용

import { useState } from "react";

export default function Message(){ 
    const [no, setNo] = useState(3) ;   //배열 형태 [ state속성명,  setter ] 

    function changeNum(){
        setNo(no+1) ;    // setter 호출 하면 자동으로 no값 부분 화면 갱신 
    }
    return (
        <div> 
             Welcome!  
             <input type="text"  />  
             카카오 클라우드 컴퓨팅   
             <span id="no">{no}</span>기 
             <button onClick={changeNum}> +1 Num </button> 
        </div>
    )
}


  1. 먼저 state를 사용하기 위해서는 import {usestate} from 'react' 선언해줘야 한다.
  2. useState(데이터) -> 이함수를 실행하고 나면 [a,b] 이런 배열이 하나 생기는데, a에는 데이터가 들어가고, b에는 state를 수정하기 위한 함수가 들어간다.
  3. let [ a, b ] = useState('데이터') 이런식으로 보통 사용한다.
  4. b에 들어간 함수는 a를 변경시기키 위한 함수인데 b('데이터2') 이렇게 사용하면 a의 데이터가 데이터2로 변경된다.
  5. 위의 코드는 버튼을 누르면 기존 ‘3기’ 라는 텍스트에서 +1씩 상태를 변화시키는 코드이다.


state에 데이터를 저장해서 쓰는 이유는 웹이 App처럼 동작하게 만들고 싶어서 쓰는것이기도 하다. state가 변경되면 HTML이 자동으로 재렌더링이 되는데 그냥 일반 변수에 저장된건 새로고침을 해줘야만 재렌더링이 된다. 그러나 state에 저장된 내용은 비동기로 동작하기 때문에 곧바로 재렌더링되어 새로고침 없이 변할 수 있다. 즉 자주바뀌는, 중요한 데이터는 변수말고 state로 저장해서 사용해야 한다!




📌 State 데이터 수정시 주의할 점! 굉장히 중요!!!!!

state는 자주 바뀌는 데이터를 저장해서 UI상에 비동기적 변화를 주기위해 사용할 수 있는데, setState를 사용하면 state의 값을 바꿀수 있다고 했다. 그럼 일반적으로 setState를 사용해서 state의 값을 바꿔보도록 하자.

import { useState } from "react" ; 

export default function Hello() {
   const [user, setUser] = useState({id:1,name:"kim"}) ;
   function test(){
      user.name="lee"; 
      setUser(user);//user로 하면 화면 갱신 안됨.
      // user 객체의 주소값이 동일하여 변화가 없는 것으로 인식 
   }
   return (
      <button onClick={test}>{user.name} </button>
   )
}

useState를 사용해 위 코드처럼 값을 변경하려고 하면 값이 갱신이 안될 것이다. 지금 state에 저장된 데이터는 객체 형태를 띄는데 자바스크립트에서 객체와 배열의 특징을 알면 왜 값이 갱신이 안되는지 잘 알수 있다.

JS에서는 객체나 배열을 어떤 변수에 저장하면 변수에는 그 값 자체가 아니라 객체나 배열의 주소값이 저장된다.

let arr = [1,2,3]

위와 같은 배열이 선언됐을때 arr에는 [1,2,3]이 저장되는것이 아니라 [1,2,3]이 참조하고 있는 주소값이 저장된다. 한마디로 [1,2,3]의 위치가 저장된 것이다.

이를 배경지식으로 가진채 useState 변경함수의 특징까지 고려해야한다. useState 함수는 객체나 배열이 가지고 있는 주소값이 일치하다면 값을 굳이 변경해주지 않는 매커니즘을 가지고 있다.

그렇다면 위의 코드에서 기존에 user.name="kim" 이라고 저장돼있는 상태에서 user.name="lee" 라고 코드를 작성했을때, 실제로 user.name의 데이터는 "kim“에서 "lee"로 변경되지만, user.name의 주소값은 변경되지 않고 그대로일 것이다. 그렇다면 setUser(user) 입장에서 user의 데이터는 변경됐을지라도 주소값은 여전히 똑같기 때문에 값을 변경해주지 않게된다.

때문에 객체나 배열의 데이터값을 변경하기 위해서는 아예 새로운 변수를 만들고, 주소값까지 달리한다음, 기존 객체나 배열을 복사한뒤 useState의 변경함수를 사용해야 한다.

import { useState } from "react" ; 

export default function Hello() {
   const [user, setUser] = useState({id:1,name:"kim"}) ;
   function test(){
      // 아래와 같이 수정하면 화면이 갱신됩니다. 
      let NewUser = {...user, name:"lee"};  //새로운 객체 생성
      setUser(NewUser); 

   }
   return (
      <button onClick={test}>{user.name} </button>
   )
}

...은 Destructing을 의미하는데 객체나 배열을 쪼개서 개별적인 변수에 할당한다는 의미이다. 이를 통해 기존 객체나 배열이 쪼개져서 각각의 데이터가 하나의 변수를 가지게 되고 이 과정을 거친 후 다시 새로운 변수(NewUser) 를 선언하고 담아줌으로써 주소값이 다른 새로운 객체를 만들수 있다. 이런 과정을 Deep Copy를 한다고 표현한다.

let [글제목,글제목변경] =  useState(['남자 코트 추천','여자 코트 추천', '남녀공용 코트 추천']); // [state 데이터, state 데이터 변경 함수]

function  제목바꾸기(){
	var newArray = 글제목;			// 이건 복사가 아니라 값 공유
	var newArray = [...글제목];		// 이게 deep copy
									// ...뜻이 모든 중,대괄호를 없애달라
									// 그러고 [] 를 다시 만들어달라
									// 즉 copy하겠다.
	newArray[0] =  '여자 코트 추천';
	newArray[1] =  '남자 코트 추천';
	글제목변경(newArray);
	}






여기 까지하면 리액트의 기본에 대해서는 거의 다 다룬것 같다. React가 무엇인지, React를 왜쓰는지, 컴포넌트, CSS, Props, State, 이벤트까지 다뤄봤고 이후 포스팅에서는 React Router DOM에 대해서 알아보도록 하겠다.

댓글남기기