반응형
1. 리액트는 어쩌다가 만들어졌을까?
- 처리해야 할 이벤트, 관리해야 할 상태값, DOM 까지 다양해지게 된다면 이에 따라 업데이트 하는 규칙도 복잡해지기 때문에 업데이트 작업의 간소화가 필요하다. 리액트는 어떠한 상태가 바뀌었을 때 그 상태에 따라 Dom 을 업데이트 할 지 규칙을 정하는 것이 아니라 아예 다 날려버리고 처음부터 모든 걸 새로 만들어서 보여준다면 어떨까? 하는 아이디어가 발상이었다. 하지만 정말 동적인 UI 를 보여주기 위해서 모든 걸 다 날려버리고 새로 만들게 된다면 속도가 굉장히 느릴 것이다. 그래서 리액트에서는 Virtual DOM 을 사용하여 이를 가능하게 했다.
- Virtual DOM 은 가상의 DOM 이다. 그냥 메모리에 가상으로 존재하는 DOM 으로서 JavaScript 객체이다.
- 리액트에서 virtual dom이 사용되는 과정
- 리액트는 상태가 업데이트 되면 업데이트가 필요한 곳의 UI 를 Virtual DOM 을 통해 렌더링한다.
- 비교 알고리즘을 통하여 실제 브라우저에 보여지고 있는 DOM 과 비교한 후 차이가 있는 곳을 감지하여 이를 실제 DOM 에 패치 시켜준다.
2. 작업환경 준비
- Node.js
- Webpack 과 Babel 과 같은 도구들이 자바스크립트 런타임인 Node.js 를 기반으로 만들어져 있다.
- Webpack, Babel?
- 리액트 프로젝트를 만들게 되면서, 컴포넌트를 여러 파일로 분리 저장한다. 이 컴포넌트는 일반 자바 스크립트가 아닌 JSX 라는 문법으로 작성한다.
- 여러 파일을 한 개로 결합하기 위해 Webpack 을 사용한다.
- JSX 를 비롯한 새로운 자바 스크립트 문법들을 사용하기 위해 Babel 을 사용한다.
- Yarn
- 개선된 버전의 npm
- npm 은 Node.js 를 설치할 때의 패키지 매니저 도구
- 프로젝트에서 사용되는 라이브러리를 설치하고 해당 라이브러리를 버전 관리를 할 때 사용
- 만약 npm 이 이미 익숙하고 yarn 을 설치하기 귀찮다면 생략 가능
- 코드 에디터
- ex. VSCode, Atom, WebStorm
- Git bash
- 윈도우의 경우 Git for Windows 를 설치하여 터미널에 무엇을 입력하라는 내용이 있으면 함께 설치되는 Git bash 를 사용한다.
🚨 yarn 설치 중 error 발생
- yarn start 를 했을 때 error Command “start” not found. 라는 메세지가 나온다. yarn 을 다시 설치했지만 해결되지 않아 yarn 경로를 옮겼으나 되지 않았다. 버전 확인을 통해 설치 여부도 확인했으나 모두 무사히 설치되었던 상황이였다. 재설치로도 해결하지 못했다.
해결 방법
- 설치한 react-app 을 제거하고 설치하여 해결했다. react-app 이 설치되다가 말아서 scripts 가 완벽히 구성되지 않아 “start” command 를 찾을 수 없었던 것 같다.
- npm uninstall -g create-react-app
- npm install -g creage-react-app
- npx create-react-app wink
3. 나의 첫번째 리액트 컴포넌트
- 리액트 컴포넌트를 만들 때 리액트 불러오기
import React from 'react';
- 리액트 컴포넌트는 함수형태, 클래스 형태로 작성할 수 있다. (이 글의 단계에선 함수로 작성하는 방법만 알아보았다. )
- 리액트 컴포넌트는 XML 형식 값을 반환 가능하다. → JSX
- Hello 라는 컴포넌트를 내보내겠다는 의미
export default Hello;
- 컴포넌트는 일종의 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(, document.getElementById('root'));
// 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 내부에 리액트 컴포넌트를 렌더링하겠다는 것을 의미
- id 가 root 인 DOM 을 선택하고 있는데 이 DOM 은 public/index.html 내부에 있다.
결국 리액트 컴포넌트가 렌더링된 결과물은 div 내부에 렌더링되는 것이다.
<div id="root"></div>
4. JSX 의 기본 규칙 알아보기
- JSX 는 리액트에서 생김새를 정의할 때 사용하는 문법이다.
- HTML 같이 생겼으나 JavaScript 이다.
- 리액트 컴포넌트 파일에서 XML 형태로 코드를 작성하면 babel 이 JSX 를 JavaScript 로 변환해준다.
- Babel 은 자바스크립트의 문법을 확장해주는 도구이다. 편의상 사용하거나 실험적인 자바스크립트 문법들을 정식 자바 스크립트 형태로 변환해줌으로서 구형 브라우저 같은 환경에서도 제대로 실행할 수 있게 해주는 역할을 한다.
JSX 가 JavaScript 로 제대로 변환되기 위한 규칙
- 태그는 꼭 닫혀 있어야 한다.
- 태그를 열었으면 꼭 <div></div> 이렇게 닫아 주어야 한다.
- HTML 에서는 input 또는 br태그를 사용할 때 닫지 않고 사용하기도 하지만 리액트에서는 그러면 안 된다.
- 태그와 태그 사이에 내용이 들어가지 않을 때에는 Self Closing 태그를 사용한다. 이는 열리고 바로 닫히는 태그를 의미한다.
- 두 가지 이상의 태그는 무조건 하나의 태그로 감싸져야 한다.
- 하지만 단순히 감싸기만을 위하여 불필요한 div 로 감싸는 게 좋지 않은 상황도 있다. 예를 들면 스타일 관련 설정을 하다가 복잡해지게 되는 상황이 올 수도 있고 table 관련 태그를 작성할 때도 내용을 div 같은 것으로 감싸기 애매하다. 이럴 때 리액트의 Fragment 를 사용한다.
- 태그를 작성할 때 이름 없이 작성을 하게 되면 Fragment 가 만들어진다. 이는 브라우저 상에서 따로 별도의 엘리먼트로 나타나지 않는다.
<> <Hello /> <div>안녕하세요</div> </>
- <div> <Hello /> <div>안녕하세요</div> </div>
- JSX 내부에 자바스크립트 변수를 보여줘야 할 때에는 {} 으로 감싸서 보여준다.
- Style 과 className
- JSX 에서 태그에 style 과 CSS class 를 설정하는 방법은 HTML 에서 설정하는 방법과 다르다.
- 우선 인라인 스타일은 객체 형태로 작성해야 한다.
- background-color 처럼 - 로 구분되어 있는 이름들은 backgroundColor처럼 camelCase 형태로 네이밍 해야 한다.
- CSS class 를 설정할 때에는 class= 가 아닌 className= 으로 설정을 해야한다.
- JSX 내부의 주석은 {/* 주석입니다 */} 와 같이 작성한다.
- 열리는 태그 내부에서는 // 주석입니다 처럼 작성 가능하다.
5. props 를 통해 컴포넌트에게 값 전달하기
- props 는 properties 의 줄임말이다. 우리가 어떠한 값을 컴포넌트에게 전달해줘야 할 때, props 를 사용한다.
props 의 기본 사용법
- 예를 들어 App 컴포넌트에서 Hello 컴포넌트를 사용할 때 name 이라는 값을 전달해야 한다면
function App() {
return (
<Hello name="react" />
);
}
- 또 예를 들어 Hello 컴포넌트에서 name 값을 사용하고 싶다면
function Hello(props) {
return <div>안녕하세요 {props.name}</div>
}
- 컴포넌트에게 전달되는 props 는 파라미터를 통해 조회할 수 있다. props 는 객체 형태로 전달되며 name 값을 조회하고 싶다면 props.name 을 조회하면 된다.
여러 개의 props, 비구조화 할당
- 예시로 Hello 컴포넌트에 또 다른 props 를 전달해본다.
function App() {
return (
<Hello name="react" color="red"/>
);
}
- 그 다음 Hello 컴포넌트에서 color 값을 조회하여 폰트의 색상으로 설정한다
function Hello(props) {
return <div style={{ color: props.color }}>안녕하세요 {props.name}</div>
}
- props 내부의 값을 조회할 때마다 props. 을 입력하고 있다. 함수의 파라미터에서 비구조화 할당(혹은 구조 분해) 문법을 사용하면 조금 더 간결한 코드를 얻을 수 있다.
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
defaultProps 로 기본값 설정
- 컴포넌트에 props 를 지정하지 않았을 때 기본적으로 사용할 값을 정하고 싶다면 컴포넌트에 defaultProps 라는 값을 설정하면 된다.
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
Hello.defaultProps = {
name: '이름없음'
}
- App 에서 name 이 없는 Hello 컴포넌트를 렌더링하면 “이름없음” 이 name 으로 뜬다.
props.children
- 컴포넌트 태그 사이에 넣은 값을 조회하고 싶을 땐 props.children 을 조회하면 된다.
- 예를 들면 Wrapper 컴포넌트를 App 에서 사용할 때 Wrapper 태그 내부에 Hello 컴포넌트를 넣으면 브라우저에서 확인이 불가하다.
- 이럴 때 내부의 내용이 보여지게 하기 위해서는 Wrapper 에서 props.children 을 렌더링해주어야 한다.
Wrapper.js return ( <div style={style}> {children} </div> ) }
6. 조건부 렌더링
- 조건부 렌더링이란 특정 조건에 따라 다른 결과물을 렌더링 하는 것이다.
- 예를 들어 App 컴포넌트에서 Hello 컴포넌트를 사용할 때 isSpecial 이라는 props 를 설정한다.
function App() {
return (
<Wrapper>
<Hello name="react" color="red" isSpecial={true}/>
<Hello color="pink" />
</Wrapper>
)
}
그리고 Hello 컴포넌트에서는 isSpecial 이 true 이냐 false 이냐에 따라 컴포넌트의 좌측에 * 표시를 보여줄 것이다. 이를 처리하는 가장 기본적인 방법은 삼항 연산자를 사용하는 것이다.
function Hello({ color, name, isSpecial }) {
return (
<div style={{ color }}>
{ isSpecial ? <b>*</b> : null }
안녕하세요 {name}
</div>
);
}
Hello.defaultProps = {
name: '이름없음'
}
isSpecial 값이 true 라면 <b>*</b> 를, 그렇지 않으면 null 을 보여주도록 한다.
* JSX 에서 null, false, undefined 를 렌더링하게 되면 아무 것도 나타나지 않는다.
삼항연산자를 사용한 조건부 렌더링은 주로 특정 조건에 따라 보여줘야 하는 내용이 다를 때 사용한다. 위의 경우 내용이 달라지는 것이 아니라 단순히 특정 조건이 true 이면 보여주고 그렇지 않다면 숨겨주고 있다. 이럴 때 && 연산자를 사용하는 더 간편한 방법도 있다.
function Hello({ color, name, isSpecial }) {
return (
<div style={{ color }}>
{isSpecial && <b>*</b>}
안녕하세요 {name}
</div>
);
}
Hello.defaultProps = {
name: '이름없음'
}
isSpecial && <b>*</b> 의 결과는 isSpecial 이 false 일 땐 false 이고 isSpecial 이 true 라면 <b>*</b> 가 된다.
props 값 설정을 생략하면 ={true}
- 컴포넌트의 props 값을 설정하게 될 때 만약 props 이름만 작성하고 값 설정을 생략한다면 이를 true 로 설정한 것으로 간주한다.
7. useState 를 통해 컴포넌트에서 바뀌는 값 관리하기
- 컴포넌트에서 보여줘야 하는 내용이 사용자 인터랙션에 따라 바뀌어야 할 때 어떻게 구현할 수 있을까?
- 리액트 16.8 에서 Hooks 라는 기능이 도입되면서 함수형 컴포넌트에서도 상태를 관리할 수 있게 되었다. useState 가 바로 리액트의 Hooks 중 하나이다.
예제) 버튼을 누르면 숫자가 바뀌는 Counter 컴포넌트 만들기
Counter.js
function Counter() {
return (
<div>
<h1>0</h1>
<button>+1</button>
<button>-1</button>
</div>
);
}
- 이벤트 설정
- Counter 에서 버튼이 클릭되는 이벤트가 발생 했을 때 특정 함수가 호출되도록 Counter 컴포넌트를 수정한다.
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> ); }
- onIncrease 와 onDecrease 는 화살표 함수를 사용하여 구현한다.
- 함수를 만들고, button 의 onClick 으로 각 함수를 연결해준다. 리액트에서 엘리먼트에 이벤트를 설정해줄 때에는 on이벤트이름 = {실행하고 싶은 함수} 형태로 설정해준다.
동적인 값 끼얹기, useState
- 컴포넌트에서 동적인 값을 상태(state) 라고 부른다. 리액트에는 useState 라는 함수가 있다. 이를 사용하면 컴포넌트에서 상태를 관리할 수 있다.
- import 하여 리액트 패키지에서 useState 라는 함수를 불러와 Counter 코드를 아래와 같이 수정한다.
- useState 를 사용할 때에는 상태의 기본값을 파라미터로 넣어서 호출한다. 이 함수를 호출하면 배열이 반환된다. 여기서 첫번째 원소는 현재 상태, 두 번째 원소는 Setter 함수이다.
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;
함수형 업데이트
- 지금은 Setter 함수를 사용할 때 업데이트 하고 싶은 새로운 값을 파라미터로 넣어주고 있다. 대신에 기존 값을 어떻게 업데이트 할 지에 대한 함수를 등록하는 방식으로도 값을 업데이트 할 수 있다
const onIncrease = () => {
setNumber(prevNumber => prevNumber + 1);
}
const onDecrease = () => {
setNumber(prevNumber => prevNumber - 1);
}
- onIncrease 와 onDecrease 에서 setNumber 를 사용할 때 그 다음 상태를 파라미터로 넣어준 것이 아니라 값을 업데이트 하는 함수를 파라미터로 넣어주었다.\
- 함수형 업데이트는 주로 나중에 컴포넌트를 최적화 할 때 사용하게 된다.
8. input 태그 상태 관리하기
InputSample.js
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;
이 컴포넌트를 App 에서 렌더링한다.
- input 에 입력하는 값이 하단에 나타나게 하고 초기화 버튼을 누르면 input 이 값이 비워지도록 구현할 것이다.
- useState를 사용한다. input 의 onChange 라는 이벤트를 사용한다. 이벤트에 등록하는 함수에서는 이벤트 객체 e 를 파라미터로 받아와서 사용할 수 있다. 이 객체의 e.target 은 이벤트가 발생한 DOM 인 input DOM 을 가르키게 된다. 이 DOM 의 value 값, 즉 e.target.value 를 조회하면 현재 input 에 입력한 값이 무엇인지 알 수 있다 한다.
- 이 값을 useState 를 통해서 관리해주면 된다.
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 값도 설정해주는 것이 중요하다. 그렇게 해야 상태가 바뀌었을 때 input 의 내용도 업데이트 된다.
9. 여러 개의 input 상태 관리하기
- 이 장에서는 input 이 비워져 있을 때 인풋에 대한 설명을 보여주는 placeholder 값도 설정해본다.
- input 의 개수가 여러 개가 되었을 때 단순시 useState 를 여러 번 사용하고 onChange 도 여러 개 만들어서 구현할 수 있다. 하지만 이 방법은 가장 좋은 방법은 아니다. 더 좋은 방법은 input 에 name 을 설정하고 이벤트가 발생했을 때 이 값을 참조하는 것이다. 그리고. useState 에서는 문자열이 아니라 객체 형태의 상태를 관리해주어야 한다.
InputSample.js
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 스터디' 카테고리의 다른 글
[2023 React.js 스터디] 이지원 #3주차 - 2장. 리액트 컴포넌트 스타일링하기(1~4) / 4장. API 연동하기 (1) (0) | 2023.02.26 |
---|---|
[2023 React.js 스터디] 유승우 #4주차 - React Router v6 (0) | 2023.02.26 |
[2023 React.js 스터디] 동승환 #2주차 - 1장. 리액트 입문(10-16) (1) | 2023.02.16 |
[2023 React.js 스터디] 임혜진 #2주차 - 즐거운 수강신청 사이트 만들기~ (1) | 2023.02.16 |
[2023 React.js 스터디] 사민주 #1주차 - 1장 1~9절 <과제> (0) | 2023.02.09 |