이 포스팅은 김민준님의 '리액트를 다루는 기술'을 요약한 글입니다.
컴포넌트를 선언하는 방식은 함수형 컴포넌트와 클래스형 컴포넌트가 있습니다.
클래스형 컴포넌트와 함수형 컴포넌트의 역할은 같으나 둘의 차이점은 클래스형 컴포넌트의 경우 state 기능 및 라이프사이클 기능 사용 가능한 것과 임의 메서드 정의할 수 있다는 것입니다.
먼저 함수형 컴포넌트로 작성하고, 나중에 클래스형 컴포넌트로도 작성해 보겠습니다.
//MyComponent.js
import React from 'react';
const MyComponent = () => {
return <div>새로운 컴포넌트<div>;
};
export default MyComponent;
이 코드에서는 함수를 작성할 때 function 키워드를 사용하는 대신에 () ⇒ {}를 사용하여 함수를 만들어 주었습니다. 이는 ES6에 도입된 화살표 함수 문법입니다.
화살표 함수
// MyComponent.js
const MyComponent = () => {...};
//export를 통해 모듈을 내보낸다
export default MyComponent;
// App.js
// import를 통해 MyComponent.js에서 MyComponent 컴포넌트를 불러온다
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent />;
}
export default App;
props는 properties로 컴포넌트 속성 설정시 사용하는 요소입니다.
props 값은 부모 컴포넌트 (ex> 샘플에선 App) 에서 설정할 수 있습니다.
// MyComponent.js
import React from 'react';
const MyComponent = **props** => {
return <div>안녕하세요, 제 이름은 **{props.name}**입니다.</div>;
};
export default MyComponent;
// App.js
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent name="React" />;
}
export default App;
안녕하세요, 제 이름은 React입니다.
// MyComponent.js
import React from 'react';
const MyComponent = **props** => {
return <div>안녕하세요, 제 이름은 **{props.name}**입니다.</div>;
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
// App.js
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent />;
}
export default App;
안녕하세요, 제 이름은 기본 이름입니다.
// MyComponent.js
import React from 'react';
const MyComponent = props => {
return (
<div>
안녕하세요, 제 이름은 {props.name}입니다.<br />
children 값은 {props.children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
// App.js
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent> 리액트 </MyComponent>;
}
export default App;

객체에서 값을 추출하는 문법을 비구조화 할당(destructuring assignment)이라고 부릅니다. 이 문법은 구조 분해 문법이라고도 불립니다.
// MyComponent.js
import React from 'react';
const MyComponent = props => {
const { name, children } = props;
return (
<div>
안녕하세요, 제 이름은 {name}입니다.<br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
함수 파라미터 부분에서도 사용할 수 있습니다. 만약 함수의 파라미터가 객체라면 그 값을 바로 비구조화해서 사용하는 것입니다.
// MyComponent.js
import React from 'react';
const MyComponent = ({ name, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다.<br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
컴포넌트의 필수 props를 지정하거나 props의 타입을 지정할 때는 propTypes를 사용합니다. 컴포넌트의 propTypes를 지정하는 방법은 defaultProp을 설정하는 것과 비슷합니다. 우선 propTypes를 사용하려면 코드 상단에 import 구문을 사용하여 불러와야 합니다.
// MyComponent.js
import React from 'react';
import PropTypes from 'prop-types';
const MyComponent = ({ name, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다.<br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
**MyComponent.propTypes = {
name: PropTypes.string** // name 값은 무조건 문자열(string) 형태로 전달해야한다.
**}**
export default MyComponent;
// App.js
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent name={3}> 리액트 </MyComponent>;
}
export default App;

값이 나타나기는 했지만, 콘솔에 경고 메시지를 출력하여 개발자에게 propTypes가 잘못되었다는 것을 알려줍니다.
propTypes를 지정하지 않았을 때 경고 메시지를 띄우려면 propTypes를 지정할 때 뒤에 isRequired를 붙여 주면됩니다.
// MyComponent.js
import React from 'react';
import PropTypes from 'prop-types';
const MyComponent = ({ name, favoriteNumber, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다.<br />
children 값은 {children}
입니다.
<br />
제가 좋아하는 숫자는 {favoriteNumber}입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
MyComponent.propTypes = {
name: PropTypes.string,
**favoriteNumber: PropTypes.number.isRequired**
}
export default MyComponent;
// App.js
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent name='React' favoriteNumber={4}> 리액트 </MyComponent>;
}
export default App;

클래스형 컴포넌트에서 props를 사용할 때는 render 함수에서 this.props를 조회하면 됩니다.
defaultProps와 propTypes는 똑같은 방식으로 설정할 수 있습니다.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class MyComponent extends Component{
render(){
const {name, favoriteNumber, children} = this.props; // 비구조화 할당
return (
<div>
안녕하세요, 제 이름은 {name}입니다.<br />
children 값은 {children}
입니다.
<br />
제가 좋아하는 숫자는 {favoriteNumber}입니다.
</div>
);
}
};
MyComponent.defaultProps = {
name: '기본 이름'
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber:PropTypes.number.isRequired
}
export default MyComponent;
class 내부에서 지정하는 방법도 있습니다.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class MyComponent extends Component{
static defaultProps = {
name: '기본 이름'
};
static propTypes = {
name: PropTypes.string,
favoriteNumber:PropTypes.number.isRequired
};
render(){
const {name, favoriteNumber, children} = this.props; // 비구조화 할당
return (
<div>
안녕하세요, 제 이름은 {name}입니다.<br />
children 값은 {children}
입니다.
<br />
제가 좋아하는 숫자는 {favoriteNumber}입니다.
</div>
);
}
};
export default MyComponent;
리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미합니다. props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이며, 컴포넌트 자신은 해당 props를 읽기 전용으로만 사용할 수 있습니다. props를 바꾸려면 부모 컴포넌트에서 바꿔야 합니다.
리액트에는 두 가지 종류의 state가 있습니다. 하나는 클래스형 컴포넌트가 지니고 있는 state이고, 하나는 함수형 컴포넌트에서 useState라는 함수를 통해 사용하는 state입니다.
// Counter.js
import React, { Component } from 'react';
class Counter extends Component {
constructor(props){ // 컴포넌트 생성자 메서드
super(props); // constructor를 작성시 반드시 super(props) 호출 -
// 호출 시 현재 클래스형 컴포넌트가 상속받고 있는 Component 클래스가 지닌 생성자 함수 호출
this.state = { //state 초깃값 설정하기 - 객체 형식
number:0,
fixedNumber:0
};
};
render(){
const {number , fixedNumber} = this.state // state를 조회할 때는 this.state로 조회합니다.
return (
<div>
<h1>{number}</h1>
<h2>바뀌지 않는 값: {fixedNumber}</h2>
<button
onClick = {() =>{ // onClick을 props로 넣어줌 = 버튼 클릭시 호출 함수 설정 = 이벤트 설정 (이벤트로 설정할 함수를 넣어 줄 때는 화살표 함수 문법을 사용해야함)
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
this.setState({ number: number + 1 }); // 바뀌는 값만 인자 전달
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
// App.js
import Counter from './Counter';
const App = () => {
return <Counter />;
}
export default App;
앞에서 state의 초깃값을 지정하기 위해 constructor 메서드를 선언해 주었는데, 또 다른 방식으로도 state의 초깃값을 지정해 줄 수 있습니다.
import React, { Component } from 'react';
class Counter extends Component {
**state = {
number: 0,
fixedNumber:0
}**
render(){
const {number , fixedNumber} = this.state // state를 조회할 때는 this.state로 조회합니다.
return (...);
}
}
export default Counter;
this.setState를 사용하여 state 값을 업데이트할 때는 상태가 비동기적으로 업데이트됩니다.
// Counter.js - button onClick 부분
onClick = {() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
this.setState({ number: number + 1 });
this.setState({ **number: this.state.number + 1** });
}}
코드를 위와 같이 작성하면 this.setState를 두 번 사용하는 것임에도 불구하고 버튼을 클릭할 때 숫자가 1씩 더해집니다. this.setState를 사용한다고 해서 state 값이 바로 바뀌지는 않기 때문입니다.
이에 대한 해결책은 this.setState를 사용할 때 객체 대신에 함수를 인자로 넣어 주는 것입니다.
this.setState의 인자로 함수를 넣어 줄 때는 코드를 다음과 같은 형식으로 작성합니다.
this.setState((prevState, props) => {
return {
// 업데이트하고 싶은 내용
}
})
여기서 prevState는 기존 상태이고, props는 현재 지니고 있는 props를 가리킵니다. 만약 업데이트하는 과정에서 props가 필요하지 않다면 생략해도 됩니다.
// Counter.js - button
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
onClick={() => {
this.setState(prevState => {
return {
number: prevState.number +1
};
});
// 위 코드와 아래 코드는 완전히 똑같은 기능을 합니다.
// 아래 코드는 함수에서 바로 객체를 반환한다는 의미입니다.
this.setState(prevState => ({
number: prevState.number + 1
}));
}}
>
+1
</button>
이렇게 작성하면 숫자가 2씩 증가합니다.
setState를 사용하여 값을 업데이트하고 난 다음에 특정 작업을 하고 싶을 때는 setState의 두 번째 파라미터로 콜백(callback) 함수를 등록하여 작업을 처리할 수 있습니다.
// Counter.js - button
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
onClick={() => {
this.setState(
{
number: number + 1,
},
() => {
console.log("방금 setState가 호출되었습니다.");
console.log(this.state);
}
);
}}
>
+1
</button>
리액트 16.8 이전엔 함수형 컴포넌트에서 state를 사용할 수 없었습니다. 16.8 이후 useState라는 함수를 사용하여 함수형 컴포넌트에서도 state를 사용할 수 있게 되었습니다.
다만, 사용법이 조금 다릅니다 → 이 과정에서 Hooks 사용
배열 비구조 할당: 배열 안에 들어 있는 값 쉽게 추출하도록 해주는 문법
const array = [1, 2];
const one = array[0];
const two = array[1];
// array 안에 있는 값을 one과 two에 담아주는 코드입니다.
// 위 코드는 배열 비구조화 할당을 사용하면 다음과 같이 표현할 수 있습니다.
const array = [1, 2];
const [one, two] = array;
클래스형 컴포넌트에서의 state 초깃값은 객체 형태를 넣어 주어야 하지만, useState에서는 값의 형태가 자유입니다.
// Say.js
import React, { **useState** } from 'react';
const Say = () => {
// useState 함수의 인자에는 상태의 초깃값을 넣어 줍니다.
const [message, setMessage] = useState(''); // 배열의 첫번째 원소: 현재 상태, 두번째 원소: 상태 바꿔주는 함수 = 세터(Setter) 함수
const onClickEnter = () => setMessage('안녕하세요!');
const onClickLeave = () => setMessage('안녕히 가세요.');
return(
<div>
<button onClick = {onClickEnter}>입장</button>
<button onClick = {onClickLeave}>퇴장</button>
<h1>{message}</h1>
</div>
);
};
export default Say;
// App.js
import Say from './Say';
const App = () => {
return <Say />;
}
export default App;
// Say.js
import React, { **useState** } from 'react';
const Say = () => {
const [message, setMessage] = useState('');
const onClickEnter = () => setMessage('안녕하세요!');
const onClickLeave = () => setMessage('안녕히 가세요.');
**const [color, setColor] = useState('black');**
return(
<div>
<button onClick = {onClickEnter}>입장</button>
<button onClick = {onClickLeave}>퇴장</button>
<h1 **style={{ color }}**>{message}</h1>
<button style={{ color: 'red' }} onClick={() => setColor('red')}>
빨간색
</button>
<button style={{ color: 'green' }} onClick={() => setColor('green')}>
초록색
</button>
<button style={{ color: 'blue' }} onClick={() => setColor('blue')}>
파란색
</button>
</div>
);
};
export default Say;
// 사본을 만들어서 업데이트하는 예시
// 객체 다루기
const object = {a: 1, b: 2, c: 3};
const nextObject = {...object, b: 2}; // 사본을 만들어서 b 값만 덮어 쓰기
// 배열 다루기
const array = [
{ id: 1, value: true },
{ id: 2, value: true },
{ id: 3, value: false}
];
let nextArray = array.concat({ id: 4 }); // 새 항목 추가
nextArray.filter(item => item.id !== 2); // id가 2인 항목 제거
nextArray.map(item => (item.id === 1 ? { ...item, value: false } : item)); // id가 1인 항목의 value를 false로 설정
{/*
객체에 대한 사본을 만들 때는 spread 연산자라 불리는 ...을 사용하여 처리하고,
배열에 대한 사본을 만들 때는 배열의 내장 함수들을 활용합니다.
*}