01. 리액트는 어쩌다가 만들어졌을까?
- 처리해야 할 이벤트, 관리해야 할 상태값, DOM이 다양해질수록 업데이트를 하는 규칙도 복잡해짐
-> Ember, Backbone, AngularJS 등의 프레임워크: 자바스크립트의 특정 값이 바뀌면 특정 DOM의 속성이 바뀌도록 연결, 업데이트 작업 간소화
- 리액트는 어떠한 상태가 바뀌었을 때, DOM을 업데이트 하는 규칙을 정하는 것이 아니라 다 날려버리고 처음부터 모든 걸 새로 만들어서 보여주면 어떨까? 라는 아이디어에서 시작함.
=> DOM을 어떻게 업데이트 해야 할지에 대한 고민을 하지 않아도 됨! 하지만 모든걸 다 날려버리고 새로 만들게 된다면 속도가 굉장히 느려질 것 ==> Virtual DOM 사용
Virtual DOM
: 브라우저에 실제로 보여지는 DOM이 아니라 메모리에 가상으로 존재하는 DOM
: 자바스크립트 객체이기 때문에 작동 성능이 실제로 브라우저에서 DOM을 보여주는 것보다 속도가 훨씬 빠름.
- 상태가 업데이트 되면, 업데이트가 필요한 곳의 UI를 Virtual DOM을 통해서 렌더링함 -> 효율적인 비교 알고리즘을 통해 실제 브라우저에서 보여지고 있는 DOM과 비교 -> 차이가 있는 곳을 감지해서 실제 DOM에 패치시킴
02. 작업 환경 준비
1) Node.js: Webpack, Babel 같은 도구들이 Node.js를 기반으로 함.
- Webpack: 모듈 번들러(JavaScript 모듈을 브라우저에서 실행할 수 있는 단일 JavaScript 파일로 묶는데 사용되는 도구)
- Babel: 자바스크립트 컴파일러
2) Yarn: 개선된 버전의 npm(Node.js를 설치할 때 딸려오는 패키지 매니저 도구), 프로젝트에서 사용되는 라이브러리를 설치하고 해당 라이브러리들의 버전 관리를 할 때.
- 더 나은 속도, 더 나은 캐싱 시스템을 사용하기 위함
3) 코드 에디터: VSCode, Atom, WebStorm, Sublime ...
- 새 프로젝트 만들어보기!
$ npx create-react-app begin-react
-> begin-react라는 디렉토리가 생기고 그 안에 리액트 프로젝트가 생성됨
$ cd begin-react
$ yarnn start (npm start)
03. 나의 첫번째 리액트 컴포넌트
- Hello.js
- App.js
- 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'));
- ReactDOM.render: 브라우저에 있는 실제 DOM 내부에 리액트 컴포넌트를 렌더링하겠다는 의미
- id가 root인 DOM을 선택하고 있는데 이 DOM은 public/index.html 내부에 존재 = 리액트 컴포넌트가 렌더링 될 때는 렌더링된 결과물이 위 div 내부에 렌더링됨.
04. JSX
: 리액트에서 생김새를 정의할 때 사용하는 문법
(얼핏 보면 HTML 같이 생겼지만 자바스크립트다...)
- 리액트 컴포넌트 파일에서 XML 형태로 코드를 작성하면 babel이 JSX를 JavaScript로 변환해준다
(Babel: 자바스크립트의 문법을 확장해주는 도구. 아직 지원되지 않는 최신 문법이나, 실험적인 자바스크립트 문법들을 자바스크립트 형태로 변환해줌으로써 구형 브라우저 환경에서도 제대로 실행할 수 있게 됨)
- JSX가 JavaScript로 제대로 변환이 되려면 지켜야 하는 몇 가지 규칙이 있음!
1) 태그는 꼭 닫혀 있어야 한다 (태그 사이에 내용이 들어가지 않을 때는 Self Closing 태그 사용 ex: Hello() )
2) 두가지 이상의 태그는 무조건 하나의 태그로 감싸져있어야 한다 (단순히 감싸기 위하여 불필요한 div로 감싸는게 좋지 않을 때도 있음 -> 리액트의 Fragment 를 사용) // Fragment: 태그를 작성할 때 이름 없이 작성하면 만들어짐, 브라우저 상에서 따로 별도의 엘리먼트로 나타나지 않음
3) JSX 안에 자바스크립트 값 사용하기: JSX 내부에 자바스크립트 변수를 보여줘야 할 때에는 {}으로 감싸서 보여줌
4) style과 className: 인라인 스타일은 객체 형태로 작성해야 하며, -로 구분되어 있는 이름들은 camelCase 형태로 네이밍 해줘야 함
// camelCase: 단어가 합쳐진 부분마다 맨 처음 글자를 대문자로 표기하는 방법
- JSX 내부의 주석은 {/* 주석! */} 형태로 작성, 열리는 태그 내부에서는 // 주석! 형태로도 작성 가능
05. props 를 통해 컴포넌트에게 값 전달하기
props: properties의 줄임말. 어떠한 값을 컴포넌트에게 전달해줘야 할 때 사용
ex) App 컴포넌트에서 Hello 컴포넌트를 사용할 때 name 이라는 값을 전달하고 싶다!
//App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" />
);
}
export default App;
Hello 컴포넌트에서 name 값을 사용하고 싶다!
//Hello.js
import React from 'react';
function Hello(props) {
return <div>안녕하세요 {props.name}</div>
}
export default Hello;
- 컴포넌트에게 전달되는 props 는 파라미터를 통하여 조회 가능.
- props 는 객체 형태로 전달되며, name 값을 조회하고 싶다면 props.name을 조회하면 됨!
- 여러 개의 props, 비구조화 할당
1) Hello 컴포넌트에 또 다른 props 전달하기!
//App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" color="red"/>
);
}
export default App;
- color 값 설정
//Hello.js
import React from 'react';
function Hello(props) {
return <div style={{ color: props.color }}>안녕하세요 {props.name}</div>
}
export default Hello;
Hello 컴포넌트에서 color 값을 조회해서 폰트의 색상으로 설정
- props 내부 값을 조회할 때마다 props.를 입력하고 있는데, 함수의 파라미터에서 비구조화 할당 문법을 사용하면 좀 더 간결하게 작성할 수 있음.
//Hello.js
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
export default Hello;
- 컴포넌트에 props 를 지정하지 않았을 때 기본적으로 사용할 값을 설정하고 싶다면 컴포넌트에 defalutProps를 설정하면 됨
//Hello.js
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
Hello.defaultProps = {
name: '이름없음'
}
export default Hello;
- 컴포넌트 태그 사이에 넣은 값을 조회하고 싶을 때, props.children 을 조회하면 됨
- props.children 사용하는 새로운 컴포넌트 만들기
import React from 'react';
function Wrapper() {
const style = {
border: '2px solid black',
padding: '16px',
};
return (
<div style={style}>
</div>
)
}
export default Wrapper; //Wrapper.js를 src 디렉토리에 만들기
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';
function App() {
return (
<Wrapper>
<Hello name="react" color="red"/>
<Hello color="pink"/>
</Wrapper>
);
}
export default App;
//Wrapper 태그 내부에 Hello 컴포넌트 두 개를 넣었지만 브라우저에서는 보이지 않음
- 내부의 내용이 보여지게 하기 위해서는 Wrapper 에서 props.children 을 렌더링해줘야 함
import React from 'react';
function Wrapper({ children }) {
const style = {
border: '2px solid black',
padding: '16px',
};
return (
<div style={style}>
{children}
</div>
)
}
export default Wrapper;
06. 조건부 렌더링
: 특정 조건에 따라 다른 결과물을 렌더링 하는 것
ex) App 컴포넌트에서 Hello 컴포넌트를 사용할 때, isSpecial props 를 설정
//App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';
function App() {
return (
<Wrapper>
<Hello name="react" color="red" isSpecial={true}/>
<Hello color="pink" />
</Wrapper>
)
}
export default App;
- true 는 자바스크립트 값이니 중괄호로 감싸주기
- Hello 컴포넌트에서 isSpecial 이 true, false에 따라 컴포넌트의 좌측에 * 표시를 보여주기
=> 삼항연산자 사용
//Hello.js
import React from 'react';
function Hello({ color, name, isSpecial }) {
return (
<div style={{ color }}>
{ isSpecial ? <b>*</b> : null }
안녕하세요 {name}
</div>
);
}
Hello.defaultProps = {
name: '이름없음'
}
export default Hello;
- isSpecial 값이 true 라면 <b>*</b>, 그렇지 않다면 null
(JSX에서 null, false, undefined 를 렌더링하게 된다면 아무것도 나타나지 않음)
- 보통 삼항연산자는 사용한 조건부 렌더링을 주로 특정 조건에 따라 보여줘야 하는 내용이 다를 때 사용
- 단순히 특정 조건이 true 면 보여주고, 그렇지 않다면 숨기는 상황에서는 && 연산자를 사용하는 것이 더 간편
//Hello.js
import React from 'react';
function Hello({ color, name, isSpecial }) {
return (
<div style={{ color }}>
{isSpecial && <b>*</b>}
안녕하세요 {name}
</div>
);
}
Hello.defaultProps = {
name: '이름없음'
}
export default Hello;
- 컴포넌트의 props 값을 설정하게 될 때, 만약 props 이름만 작성하고 값 설정을 생략한다면 이를 true로 설정한 것으로 간주
//App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';
function App() {
return (
<Wrapper>
<Hello name="react" color="red" isSpecial />
<Hello color="pink"/>
</Wrapper>
);
}
export default App; //isSpecial 이름만 넣어주면 isSpecial={true}
07. useState 를 통해 컴포넌트에서 바뀌는 값 관리하기
- 컴포넌트에서 보여줘야 하는 내용이 사용자 인터랙션에 따라 바뀌어야 할 때 어떻게 구현할 수 있는지!!?
- 리액트 16.8 이전 버전에서는 함수형 컴포넌트에서는 상태를 관리할 수 없었는데 Hooks 기능이 도입되면서 함수형 컴포넌트에서도 관리가 가능하게 됨
- useState 함수 사용 <- 리액트의 Hooks 중 하나
ex) 버튼을 누르면 숫자가 바뀌는 Counter 컴포넌트 만들어보기!
//Counter.js
import React from 'react';
function Counter() {
return (
<div>
<h1>0</h1>
<button>+1</button>
<button>-1</button>
</div>
);
}
export default Counter;
//App에서 Counter 렌더링
import React from 'react';
import Counter from './Counter';
function App() {
return (
<Counter />
);
}
export default App;
- 이벤트 설정
Counter 에서 버튼이 클릭되는 이벤트가 발생했을 때, 특정 함수가 호출되도록 설정
//Counter.js
import React from 'react';
function 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;
리액트에서 엘리먼트에 이벤트를 설정해줄때에는 on이벤트이름={실행하고싶은함수}
(⚠️함수형태를 넣어야함. 함수를 실행하면 렌더링되는 시점에서 함수가 호출되어버림)
- 동적인 값 넣기(useState)
//Counter.js
import React, { useState } from 'react';
function Counter() {
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(number + 1);
}
const onDecrease = () => {
setNumber(number - 1);
}
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
- useState를 사용할 때에는 상태의 기본값을 파라미터로 넣어서 호출
- 이 함수를 호출하면 배열이 반환되는데, 첫 번째 원소는 현재 상태, 두 번째 원소는 Setter 함수
Setter 함수: 파라미터로 전달받은 값을 최신 상태로 설정
- 함수형 업데이트
Setter 함수 대신, 기존 값을 어떻게 업데이트 할지에 대한 함수를 등록하는 방식으로도 값 업데이트 가능
//Counter.js
import React, { useState } from 'react';
function 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;
- 함수형 업데이트는 컴포넌트를 최적화하게 될때 사용
08. input 상태 관리하기
//inputSample.js 생성
import React from 'react';
function InputSample() {
return (
<div>
<input />
<button>초기화</button>
<div>
<b>값: </b>
</div>
</div>
);
}
export default InputSample;
//App에 렌더링
import React from 'react';
import InputSample from './InputSample';
function App() {
return (
<InputSample />
);
}
export default App;
- input에 입력하는 값이 하단에 나타나게 하고, 초기화 버튼을 누르면 input의 값이 비워지도록 구현
=> useState 사용. input의 onChange 이벤트 사용
이벤트에 등록하는 함수에서는 이벤트 객체 e를 파라미터로 받아와서 사용할 수 있는데, 이 객체의 e.target은 이벤트가 발생한 DOM인 input DOM을 가리킴
이 DOM 의 value 값 (=e.target.value) 을 조회하면 현재 input에 입력한 값이 무엇인지 알 수 있음
이 값을 useState를 통해서 관리해주면 됨
import React, { useState } from 'react';
function InputSample() {
const [text, setText] = useState('');
const onChange = (e) => {
setText(e.target.value);
};
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 값도 설정해주는 것이 중요!
09. 여러 개의 input 상태 관리하기
import React, { useState } from 'react';
function InputSample() {
const onChange = (e) => {
};
const onReset = () => {
};
return (
<div>
<input placeholder="이름" />
<input placeholder="닉네임" />
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
이름 (닉네임)
</div>
</div>
);
}
export default InputSample;
- input의 개수가 여러개가 됐을 때, 단순히 useState, onChange을 여러개 만들어서 구현하기보다는 input에 name 을 설정하고 이벤트가 발생했을 때 이 값을 참조하는 방법으로! useState에서는 문자열이 아니라 객체 형태의 상태를 관리해줘야 함
import React, { useState } from 'react';
function 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 문법. 객체의 내용을 모두 펼쳐서 기존 객체를 복사
- 이러한 작업을 불변성을 지킨다고 하는데, 불변성을 지켜주어야만 리액트 컴포넌트에서 상태가 업데이트됐음을 감지할 수 있고 이에 따라 필요한 렌더링이 진행됨. 만약 직접 수정하게 되면, 값을 바꿔도 리렌더링이 되지 않음. 또 불변성을 지켜주어야만 컴포넌트 업데이트 성능 최적화를 제대로 할 수 있음!
'WINK-(Web & App) > React.js 스터디' 카테고리의 다른 글
[2024 React.js 스터디] 한승훈 #4주차 (0) | 2024.05.14 |
---|---|
[2024 React.js 스터디] 류상우 #4주차 (0) | 2024.05.14 |
[2024 React.js 스터디] 박지민 #4주차 "리액트 입문 1-9" (1) | 2024.05.13 |
[2024 React.js 스터디] 정호용 #4주차 "React.JS 맛보기" (0) | 2024.05.13 |
[2024 React.js 스터디] 박건민 #4주차 (0) | 2024.05.12 |