본문 바로가기

WINK-(Web & App)/React.js 스터디

[2024 여름방학 React.js 스터디] 백채린 #3주차

반응형

1장. 리액트 입문

 

1. 리액트는 어쩌다 만들어졌을까?

 

HTML/JS로 만들어진 카운터 예시

카운터 예시

// index.html
<h2 id="number">0</h2>
<div>
  <button id="increase">+1</button>
  <button id="decrease">-1</button>
</div>
// index.js
const number = document.getElementById('number');
const increase = document.getElementById('increase');
const decrease = document.getElementById('decrease');

increase.onclick = () => {
  const current = parseInt(number.innerText, 10);
  number.innerText = current + 1;
};

decrease.onclick = () => {
  const current = parseInt(number.innerText, 10);
  number.innerText = current - 1;
};

 

→ 위의 경우, id를 사용하여 각 DOM을 선택한 뒤, 원하는 이벤트가 발생하면 DOM의 특정 속성을 바꿔주어야 함

단점: 인터랙션이 자주 발생하고, 이에 따라 동적으로 UI를 표현해야 한다면 관리하기 힘들어진다.

 

 

그래서, Ember, Backbone, AngularJS 등의 프레임워크가 만들어짐

작동 방식: 자바스크립트의 특정 값이 바뀌면 특정 DOM의 속성이 바뀌도록 연결을 해주어서, 업데이트 하는 작업을 간소화해주는 방식

 

! 리액트는 다른 발상 !

: 어떠한 상태가 바뀌었을 때, 아예 다 날려버리고 처음부터 모든걸 새로 만들어서 보여주면 어떨까? 라는 아이디어에서 개발이 시작,

그러나 이런 방식은 속도가 굉장히 느림, 그래서 리액트에서는 Virtual DOM을 사용해서 이를 가능하게 함

* virtual DOM

: 가상의 DOM, 메모리에 가상으로 존재하는 DOM, JavaScript 객체이기 때문에 작동 성능이 실제로 브라우저에서 DOM을 보여주는 것보다 속도가 훨씬 빠름

 

 

리액트의 작동 방식

: 상태 업데이트 → 업데이트가 필요한 곳의 UI를 Virtual DOM을 통해 렌더링 → 알고리즘을 통해 실제 브라우저에 보여지고 있는 DOM과 비교 → 차이가 있는 곳 감지 → 이를 실제 DOM에 패치

 

 

 

2. 작업환경 준비

 

다음 항목들을 설치

 

- Node.js: Webpack, Babel 같은 도구들이 자바스크립트 런타임인 Node.js를 기반으로 만들어져 있기 때문에 이를 사용하기 위해 설치

* Webpack, Babel

: 리액트 프로젝트를 만들게 되면서, 컴포넌트를 여러가지 파일로 분리해서 저장할 것이고, 또 이 컴포넌트는 일반 자바스크립트가 아닌 JSX 라는 문법으로 작성하게 된다. 여러가지 파일을 한개로 결합하기 위해서 Webpack 이라는 도구를 사용하고, JSX 를 비롯한 새로운 자바스크립트 문법들을 사용하기 위해서 Babel 이라는 도구를 사용한다.

 

Windows 의 경우엔, Node.js 공식 홈페이지 에서 좌측에 나타나는 LTS 버전을 설치

macOS / Linux 의 경우엔, nvm 이라는 도구를 사용하여 Node.js 를 설치하시는 것을 권장

 

$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
$ nvm install --lts

 

- Yarn: 조금 개선된 버전의 npm 이라고 생각하면 된다. 더 나은 속도, 더 나은 캐싱 시스템을 사용하기 위해 사용한다.

* npm: Node.js를 설치하게 될 때 같이 딸려오는 패키지 매니저 도구, 프로젝트에서 사용되는 라이브러리를 설치하고 해당 라이브러리들의 버전 관리를 하게 될 때 사용한다.

 

- 코드 에디터: VSCode, Atom, WebStorm, Sublime 등 여러 가지가 있으므로 좋아하는 에디터를 사용하면 된다.

 

- Git bash: 윈도우의 경우, Git for Windows를 설치해서 Git Bash 사용하고 윈도우가 아닌 경우 설치하지 않아도 된다.

 

새 프로젝트 만들어보기

 

새로운 리액트 프로젝트를 만들어 보자!

 

// 터미널에서 다음 명령어 실행 (윈도우 사용자는 Git Bash 를 사용)
$ npx create-react-app begin-react

 

→ begin react 라는 디렉터리가 생기고 그 안에 리액트 프로젝트가 생성된다.

 

$ cd begin-react
$ yarn start
// yarn 이 없다면 npm start

 

→ 브라우저에  http://localhost:3000/ 이 열리고, 돌아가는 리액트 아이콘이 보일 것이다.

자동으로 페이지가 열리지 않는다면 브라우저에 주소를 직접 입력하여 들어간다.

 

 

 

VS Code 에서 터미널 띄우기

 

VS Code 로 해당 디렉터리를 열은 뒤, 상단 메뉴의 View > Terminal 을 연다. (한글 메뉴의 경우 보기 > 터미널)

 

윈도우 사용자의 경우엔, 위 작업을 하기 전에 VS Code 에서 cmd 대신 Git Bash 를 사용하기 위하여

VS Code 에서 Ctrl + , 를 눌러 설정에 들어간 후,

terminal 을 검색 후 Terminal > External > Windows Exec 부분에 Git Bash 의 경로인 C:\Program Files\Git\bin\bash.exe 를

넣어준다.

 

Git Bash 를 열었을 때 기본 경로

 

Git Bash 에서의 ~/ 경로가 어디인지 모른다면 pwd 명령어를 입력해보자.

 

 

 

3. 나의 첫번째 리액트 컴포넌트

 

src 디렉터리 Hello.js 라는 파일을 다음과 같이 작성해보자!

 

// Hello.js
import React from 'react'; // 리액트 컴포넌트를 만들 때 이 코드로 리액트를 불러와주어야 함

const Hello = () => <div>안녕하세요</div>;

export default Hello; // Hello 라는 컴포넌트를 내보내겠다는 의미, 이렇게 해주면 다른 컴포넌트에서 불러와서 사용할 수 있다.

 

→ 리액트 컴포넌트에서는 XML 형식의 값을 반환해줄 수 있는데 이를 JSX 라고 부른다.

 

이 컴포넌트를 App.js에서 불러와서 사용

 

// App.js
import React from 'react';
import Hello from './Hello'; 

const App = () => (
  <div>
    <Hello />
  </div>
);

export default App;

 

import logo from './logo.svg';
import './App.css';

 

SVG 파일을 불러오고 CSS 적용하는 코드이지만 현재 불필요하므로 생략

 

컴포넌트는 일종의 UI 조각이며 쉽게 재사용할 수 있다.

 

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root')); 
// id 가 root 인 DOM 을 선택하고 있음

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

 

→ ReactDOM.render의 역할: 브라우저에 있는 실제 DOM 내부에 리액트 컴포넌트를 렌더링하겠다는 것을 의미한다.

 

public/index.html 을 열어보면 내부에 

 

<div id="root"></div>

 

을 찾아볼 수 있다. 결국, 리액트 컴포넌트가 렌더링 될 때에는, 렌더링된 결과물이 위 div 내부에 렌더링되는 것 이다.

 

 

 

4. JSX의 기본 규칙 알아보기

 

JSX: 리액트에서 생김새를 정의할 때, 사용하는 문법이다. 얼핏보면 HTML 같이 생겼지만 실제로는 JavaScript 이다.

 

return <div>안녕하세요</div>;

 

리액트 컴포넌트 파일에서 XML 형태로 코드를 작성하면 babel 이 JSX 를 JavaScript 로 변환을 해준다.

* Babel

: 자바스크립트의 문법을 확장해주는 도구이다.

아직 지원되지 않는 최신 문법이나, 편의상 사용하거나 실험적인 자바스크립트 문법들을 정식 자바스크립트 형태로 변환해줌으로서 구형 브라우저같은 환경에서도 제대로 실행 할 수 있게 해주는 역할을 한다.

 

JSX의 기본 규칙

꼭 닫혀야 하는 태그

 

- 태그는 꼭 닫혀있어야 한다. 그렇지 않으면 오류가 발생한다.

- 태그를 열었으면 꼭, <div></div> 이렇게 닫아주어야 한다.

- HTML 에서는 input 또는 br 태그를 사용 할 때 닫지 않고 사용하기도 하지만 리액트에서는 그렇게 하면 안된다.

- 태그와 태그 사이에 내용이 들어가지 않을 때에는, Self Closing 태그 라는 것을 사용해야 한다

* Self Closing 태그: 열리고, 바로 닫히는 태그를 의미한다.

// App.js
import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <div>
      <Hello />
      <Hello />
      <Hello />
      <input />
      <br />
    </div>
  );
}

export default App;

// Hello 컴포넌트를 사용 할 때에도 Self Closing 태그를 사용함

 

 

꼭 감싸져야하는 태그

 

두개 이상의 태그는 무조건 하나의 태그로 감싸져있어야 한다.

 

import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <Hello />
    <div>안녕히계세요.</div>
  );
}

 

→ 오류 발생

 

// 수정 코드
export default App;
import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <div>
      <Hello />
      <div>안녕히계세요</div>
    </div>
  );
}

export default App;

 

하지만, 이렇게 단순히 감싸기 위하여 불필요한 div 로 감싸는게 별로 좋지 않은 상황도 있다.

예를 들어서 스타일 관련 설정을 하다가 복잡해지게 되는 상황도 올 수 있고, table 관련 태그를 작성 할 때에도 내용을 div 같은걸로 감싸기엔 애매하다. 그럴 땐, 리액트의 Fragment 라는 것을 사용하면 된다.

 

import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <>
      <Hello />
      <div>안녕히계세요</div>
    </>
  );
}

export default App;

 

태그를 작성 할 때 이름 없이 작성을 하게 되면 Fragment 가 만들어지는데, Fragment 는 브라우저 상에서 따로 별도의 엘리먼트로 나타나지 않는다.

 

JSX 안에 자바스크립트 값 사용하기

 

JSX 내부에 자바스크립트 변수를 보여줘야 할 때에는 {} 으로 감싸서 보여준다.

 

import React from 'react';
import Hello from './Hello';

const App = () => {
  const name = 'react';
  return (
    <>
      <Hello />
      <div>{name}</div>
    </>
  );
}

export default App;

 

style 과 className

 

- JSX 에서 태그에 style 과 CSS class 를 설정하는 방법은 HTML 에서 설정하는 방법과 다르다.

- 인라인 스타일은 객체 형태로 작성을 해야 하며, background-color 처럼 - 로 구분되어 있는 이름들은 backgroundColor 처럼 camelCase 형태로 네이밍 해주어야 한다.

- CSS class 를 설정 할 때에는 class= 가 아닌 className= 으로 설정을 해주어야 한다.

 

// App.css
.gray-box {
  background: gray;
  width: 64px;
  height: 64px;
}

 

// App.js
import React from 'react';
import Hello from './Hello';
import './App.css';


const App = () => {
  const name = 'react';
  const style = {
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: 24, // 기본 단위 px
    padding: '1rem' // 다른 단위 사용 시 문자열로 설정
  }

  return (
    <>
      <Hello />
      <div style={style}>{name}</div>
      <div className="gray-box"></div>
    </>
  );
}

export default App;

 

 

주석

 

- JSX 내부의 주석은 {/* 이런 형태로 */} 작성한다.

- 추가적으로, 열리는 태그 내부에서는 // 이런 형태로도 주석 작성이 가능하다.

 

import React from 'react';
import Hello from './Hello';
import './App.css';


const App = () => {
  const name = 'react';
  const style = {
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: 24, // 기본 단위 px
    padding: '1rem' // 다른 단위 사용 시 문자열로 설정
  }

  return (
    <>
      {/* 주석은 화면에 보이지 않습니다 */}
      /* 중괄호로 감싸지 않으면 화면에 보입니다 */
      <Hello 
        // 열리는 태그 내부에서는 이렇게 주석을 작성 할 수 있습니다.
      />
      <div style={style}>{name}</div>
      <div className="gray-box"></div>
    </>
  );
}

export default App;

 

 

 

 

5. props 를 통해 컴포넌트에게 값 전달하기

 

props 는 properties 의 줄임말로 우리가 어떠한 값을 컴포넌트에게 전달해줘야 할 때, props 를 사용한다.

 

props 의 기본 사용법

 

예를 들어서, App 컴포넌트에서 Hello 컴포넌트를 사용 할 때 name 이라는 값을 전달해주고 싶다고 가정해보자!

 

// App.js
import React from 'react';
import Hello from './Hello';

const App = () => <Hello name="react" />;

export default App;

 

// Hello.js
import React from 'react';

const Hello = (props) => <div>안녕하세요 {props.name}</div>;

export default Hello;

 

컴포넌트에게 전달되는 props 는 파라미터를 통하여 조회 할 수 있다.

props 는 객체 형태로 전달되며, 만약 name값을 조회하고 싶다면 props.name 을 조회하면 된다.

 

여러개의 props, 비구조화 할당

 

Hello 컴포넌트에 또 다른 props 를 전달해보자!

 

// App.js
import React from 'react';
import Hello from './Hello';

const App = () => <Hello name="react" color="red"/>; {/* color 라는 값 설정 */}

export default App;

 

다음으로는 Hello 컴포넌트에서 color 값을 조회해서 폰트의 색상으로 설정을 해보자!

 

// Hello.js
import React from 'react';

const Hello = (props) => <div style={{ color: props.color }}>안녕하세요 {props.name}</div>;

export default Hello;

 

props 내부의 값을 조회 할 때마다 props. 를 입력하고 있는데,

함수의 파라미터에서 비구조화 할당 (혹은 구조 분해) 문법을 사용하면 조금 더 코드를 간결하게 작성 할 수 있다.

 

// Hello.js
import React from 'react';

const Hello = ({ color, name }) => <div style={{ color }}>안녕하세요 {name}</div>;

export default Hello;

 

 

defaultProps 로 기본값 설정

 

컴포넌트에 props 를 지정하지 않았을 때 기본적으로 사용 할 값을 설정하고 싶다면 컴포넌트에 defaultProps 라는 값을 설정하면 된다.

 

// Hello.js
import React from 'react';

const Hello = ({ color, name }) => <div style={{ color }}>안녕하세요 {name}</div>;
}

Hello.defaultProps = {
  name: '이름없음'
}

export default Hello;

 

App 에서 name 이 없는 Hello 컴포넌트를 렌더링해보자!

 

// App.js
import React from 'react';
import Hello from './Hello';

const App () => {
  return (
    <>
      <Hello name="react" color="red"/>
      <Hello color="pink"/>
    </>
  );
}

export default App;

렌더링 결과

 

props.children

 

컴포넌트 태그 사이에 넣은 값을 조회하고 싶을 땐, props.children 을 조회하면 된다.

 

// Wrapper.js
import React from 'react';

const Wrapper () => {
  const style = {
    border: '2px solid black',
    padding: '16px',
  };
  return (
    <div style={style}>

    </div>
  )
}

export default Wrapper;

 

이 컴포넌트를 App 에서 사용해보자!

 

// App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';

const App = () => {
  return (
    <Wrapper>
      <Hello name="react" color="red"/>
      <Hello color="pink"/>
    </Wrapper>
  );
}

export default App;

 

→ 브라우저를 확인하면 다음과 같이 Hello 컴포넌트들은 보여지지 않을 것이다.

 

내부의 내용이 보여지게 하기 위해서는 Wrapper 에서 props.children 을 렌더링해주어야 한다.

// Wrapper.js
import React from 'react';

const Wrapper = ({ children }) => {
  const style = {
    border: '2px solid black',
    padding: '16px',
  };
  return (
    <div style={style}>
      {children}
    </div>
  )
}

export default Wrapper;

 

 

 

 

6. 조건부 렌더링

 

조건부 렌더링: 특정 조건에 따라 다른 결과물을 렌더링 하는 것을 의미한다.

 

// App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';


const App = () => {
  return (
    <Wrapper>
      <Hello name="react" color="red" isSpecial={true}/> 
      {/* isSpecial 이라는 props 를 설정 */}
      {/* true 는 자바스크립트 값이기 때문에 중괄호로 감싸줌 */}
      <Hello color="pink" />
    </Wrapper>
  )
}

export default App;

 

Hello 컴포넌트에서는 isSpecial 이 true 이냐 false 이냐에 따라서 컴포넌트의 좌측에 * 표시를 보여줘보자!

이를 처리하는 가장 기본적인 방법은, 삼항연산자를 사용하는 것이다.

 

// Hello.js
import React from 'react';

const Hello = ({ color, name, isSpecial }) => {
  return (
    <div style={{ color }}>
      { isSpecial ? <b>*</b> : null }
      {/* isSpecial 값이 true 라면 <b>*</b> 를, 그렇지 않다면 null 을 보여주도록 함 */}
      안녕하세요 {name}
    </div>
  );
}

Hello.defaultProps = {
  name: '이름없음'
}

export default Hello;

 

* JSX 에서 null, false, undefined 를 렌더링하게 된다면 아무것도 나타나지 않게 된다.

삼항연산자를 사용한 조건부 렌더링은 주로 특정 조건에 따라 보여줘야 하는 내용이 다를 때 사용한다.

지금은 내용이 달라지는게 아니라, 단순히 특정 조건이 true 이면 보여주고, 그렇지 않다면 숨겨주고 있는데,

이러한 상황에서는 && 연산자를 사용해서 처리하는 것이 더 간편하다.

 

// Hello.js
import React from 'react';

const Hello = ({ color, name, isSpecial }) => {
  return (
    <div style={{ color }}>
      {isSpecial && <b>*</b>}
      {/* isSpecial 이 false 일땐 false 이고, isSpecial이  true 일 땐 <b>*</b> 가 됨 *}
      안녕하세요 {name}
    </div>
  );
}

Hello.defaultProps = {
  name: '이름없음'
}

export default Hello;

 

 

props 값 설정을 생략하면 ={true}

 

컴포넌트의 props 값을 설정하게 될 때 만약 props 이름만 작성하고 값 설정을 생략한다면, 이를 true 로 설정한 것으로 간주한다.

 

// App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';

const App = () => {
  return (
    <Wrapper>
      <Hello name="react" color="red" isSpecial />
      <Hello color="pink"/>
    </Wrapper>
  );
}

export default App;

 

→ isSpecial 이름만 넣어주면 isSpecial={true} 와 동일한 의미

 

 

 

7. useState 를 통해 컴포넌트에서 바뀌는 값 관리하기

 

컴포넌트에서 보여줘야 하는 내용이 사용자 인터랙션에 따라 바뀌어야 할 때 어떻게 구현할 수 있는지에 대하여 알아보자!

 

리액트 16.8 에서 Hooks 라는 기능이 도입되면서 함수형 컴포넌트에서도 상태를 관리할 수 있게 되었다.

이번에는 useState 라는 함수를 사용해보게 되는데, 이게 바로 리액트의 Hooks 중 하나이다.

버튼을 누르면 숫자가 바뀌는 Counter 컴포넌트를 만들어보자!

 

// Counter.js
import React from 'react';

const Counter = () => {
  return (
    <div>
      <h1>0</h1>
      <button>+1</button>
      <button>-1</button>
    </div>
  );
}

export default Counter;

 

// App.js
import React from 'react';
import Counter from './Counter';

const App = () => <Counter />;

export default App;

UI

 

이벤트 설정

 

Counter 에서 버튼이 클릭되는 이벤트가 발생 했을 때, 특정 함수가 호출되도록 설정을 해보자!

 

// Counter.js
import React from 'react';

const Counter = () => {
  const onIncrease = () => {
    console.log('+1')
  }
  const onDecrease = () => {
    console.log('-1');
  }
  return (
    <div>
      <h1>0</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

 

함수를 만들고, button 의 onClick 으로 각 함수를 연결해준다.

리액트에서 엘리먼트에 이벤트를 설정해줄때에는 on이벤트이름={실행하고싶은함수} 형태로 설정해주어야 한다.

 

! 주의 ! 함수형태를 넣어주어야 하지, 함수를 다음과 같이 실행하면 안됨

 

onClick={onIncrease()}
// 이 경우 렌더링되는 시점에서 함수가 호출됨

 

 

동적인 값 끼얹기 , useState

 

컴포넌트에서 동적인 값을 상태(state)라고 부른다. 리액트의 useState 라는 함수를 사용하면 컴포넌트에서 상태를 관리할 수 있다.

 

// Counter.js
import React, { useState } from 'react'; // 리액트 패키지에서 useState 라는 함수를 불러와줌

const Counter = () => {
  const [number, setNumber] = useState(0);
  // useState 를 사용 할 때에는 상태의 기본값을 파라미터로 넣어서 호출해줌 
  // 함수 호출 결과 배열이 반환됨, 첫번째 원소는 현재 상태이고 두번째 원소는 Setter 함수임

  const onIncrease = () => {
    setNumber(number + 1);
  }

  const onDecrease = () => {
    setNumber(number - 1);
  }
  // Setter 함수는 파라미터로 전달 받은 값을 최신 상태로 설정해줌

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

 

버튼을 누를 때마다 숫자가 바뀌게 됨

 

함수형 업데이트

 

지금은 Setter 함수를 사용 할 때, 업데이트 하고 싶은 새로운 값을 파라미터로 넣어주고 있는데,

그 대신에 기존 값을 어떻게 업데이트 할 지에 대한 함수를 등록하는 방식으로도 값을 업데이트 할 수 있다.

 

// Counter.js
import React, { useState } from 'react';

const Counter = () => {
  const [number, setNumber] = useState(0);

  const onIncrease = () => {
    setNumber(prevNumber => prevNumber + 1);
  }

  const onDecrease = () => {
    setNumber(prevNumber => prevNumber - 1);
  }

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

 

→ 함수형 업데이트는 주로 나중에 컴포넌트를 최적화를 하게 될 때 사용하게 된다. (왜 최적화랑 관련이 있는지는 나중에 알아보자...)

 

 

 

8. input 상태 관리하기

 

이번에는 리액트에서 사용자가 입력 할 수 있는 input 태그의 상태를 관리하는 방법을 알아보자!

 

// InputSample.js
import React from 'react';

const InputSample = () => {
  return (
    <div>
      <input />
      <button>초기화</button>
      <div>
        <b>값: </b>
      </div>
    </div>
  );
}

export default InputSample;

 

// App.js
import React from 'react';
import InputSample from './InputSample';

const App = () => <InputSample />;

export default App;

렌더링 결과

 

input 에 입력하는 값이 하단에 나타나게 하고, 초기화 버튼을 누르면 input 이 값이 비워지도록 구현을 해보자!

 

// InputSample.js
import React, { useState } from 'react';

const InputSample = () => {
  const [text, setText] = useState('');

  const onChange = (e) => {
    setText(e.target.value);
  };
  // e.target 은 이벤트가 발생한 DOM 인 input DOM 을 가르키게 됨
  // e.target.value 를 조회하면 현재 input 에 입력한 값이 무엇인지 알 수 있음

  const onReset = () => {
    setText('');
  };

  return (
    <div>
      <input onChange={onChange} value={text}  />
      <button onClick={onReset}>초기화</button>
      <div>
        <b>값: {text}</b>
      </div>
    </div>
  );
}

export default InputSample;

 

* input 의 상태를 관리할 때에는 input 태그의 value 값도 설정해주는 것이 중요하다. 그렇게 해야, 상태가 바뀌었을때 input 의 내용도 업데이트 된다.

 

 

 

9. 여러개의 input 상태 관리하기

 

이번에는 input 이 여러개일때는 어떻게 관리해야 하는지 알아보자!

 

// InputSample.js
import React, { useState } from 'react';

const InputSample = () => {
  const onChange = (e) => {
  };

  const onReset = () => {
  };


  return (
    <div>
      <input placeholder="이름" />
      <input placeholder="닉네임" />
      {/* input 이 비어져있을 때 인풋에 대한 설명을 보여주는 placeholder 값 설정 */}
      <button onClick={onReset}>초기화</button>
      <div>
        <b>값: </b>
        이름 (닉네임)
      </div>
    </div>
  );
}

export default InputSample;

 

input 의 개수가 여러개일 때, useState 를 여러번 사용하고 onChange 도 여러개 만들어서 구현할 수 있지만 좋은 방법은 아니다. 

더 좋은 방법은, input 에 name 을 설정하고 이벤트가 발생했을 때 이 값을 참조하는 것

그리고, useState 에서는 문자열이 아니라 객체 형태의 상태를 관리해주어야 합니다.

 

// InputSample.js
import React, { useState } from 'react';

const InputSample = () => {
  const [inputs, setInputs] = useState({
    name: '',
    nickname: ''
  });

  const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출

  const onChange = (e) => {
    const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
    setInputs({
      ...inputs, // 기존의 input 객체를 복사한 뒤
      [name]: value // name 키를 가진 값을 value 로 설정
    });
  };

  const onReset = () => {
    setInputs({
      name: '',
      nickname: '',
    })
  };


  return (
    <div>
      <input name="name" placeholder="이름" onChange={onChange} value={name} />
      <input name="nickname" placeholder="닉네임" onChange={onChange} value={nickname}/>
      <button onClick={onReset}>초기화</button>
      <div>
        <b>값: </b>
        {name} ({nickname})
      </div>
    </div>
  );
}

export default InputSample;

 

리액트 상태에서 객체를 수정해야 할 때에는,

 

inputs[name] = value;
// 위와 같이 기존 상태를 직접 수정하게 되면, 값을 바꿔도 리렌더링이 되지 않는다.

 

이런식으로 직접 수정하면 안된다. 그 대신에, 새로운 객체를 만들어서 새로운 객체에 변화를 주고, 이를 상태로 사용해주어야 한다.

 

setInputs({
  ...inputs,
  [name]: value
});

 

* spread 문법 (... 문법): 객체의 내용을 모두 "펼쳐서" 기존 객체를 복사해준다.

이러한 작업을, "불변성을 지킨다" 라고 부른다. 불변성을 지켜주어야만 리액트 컴포넌트에서 상태가 업데이트가 됐음을 감지 할 수 있고 이에 따라 필요한 리렌더링이 진행된다.

 

 

반응형