componentDidMount(): 컴포넌트가 생성되고 첫 렌더링이 완료된 후 호출됩니다
componentDidUpdate(): 컴포넌트가 업데이트된 후 호출됩니다
componentWillUnmount(): 컴포넌트가 제거되기 직전에 호출됩니다
생명주기 메서드에 대한 이해를 돕기 위해
먼저 리액트로 간단한 카운터 기능을 만들어보겠습니다
<div id="root"></div>
<script type="text/babel">
// Counter 컴포넌트를 만들어서 상태 관리하기
class Counter extends React.Component {
constructor(props) {
super(props);
// 컴포넌트가 생성될 때(생성자함수) 상태를 가지고 시작
this.state = {
number: 0,
}
}
// 첫 렌더링이 완료된 후 10이라는 상태를 가지게 됩니다
componentDidMount() {
// setState가 render 함수를 재호출합니다
this.setState({ number: 0 })
}
render() {
return (
<div>
<h2>{this.state.number}</h2>
<button>+</button>
<button>-</button>
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Counter />
</div>
)
}
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<App />)
</script>
사용자가 아무런 조작을 하지 않았음에도 위 코드는 화면 렌더링이 2번 실행된 것입니다
별 반 차이가 없어보이지만 생성자 함수가 state 데이터를 가지고 시작하는 것보다
초기 렌더링을 끝낸 뒤 생성주기 메서드를 사용해서 데이터를 불러오는 쪽을 더 추천합니다
사용자에게 먼저 html 틀을 보여주고, 데이터는 따로 받아오는 편이 더 나은 사용성을 제공하기 때문입니다

componentDidMount() {
// setState가 render 함수를 재호출합니다
setTimeout(() => {
this.setState({ number: 10 })
}, 3000)
}
render() {
return (
<div>
<h2>{this.state.number === 0 ? "로딩중" : this.state.number}</h2>
<button>+</button>
<button>-</button>
</div>
)
}
}
↑ 앞으로는 이러한 형태의 코드를 자주 보게 될 것...
아래는 완성코드입니다
<body>
<div id="root"></div>
<script type="text/babel">
// Counter 컴포넌트를 만들어서 상태 관리하기
class Counter extends React.Component {
constructor(props) {
super(props);
// 컴포넌트가 생성될 때(생성자함수) 상태를 가지고 시작
this.state = {
number: 0,
isLoading: true,
}
this.increment = this.increment.bind(this)
}
// 첫 렌더링이 완료된 후 10이라는 상태를 가지게 됩니다
componentDidMount() {
// setState가 render 함수를 재호출합니다
setTimeout(() => {
this.setState({ number: 0, isLoading: false })
}, 1000)
}
// 상태가 바뀌면 호출될 메서드
componentDidUpdate() {
console.log(this.state.number)
}
increment() {
this.setState({
number: this.state.number + 1,
})
}
decrement = () => {
this.setState({
number: this.state.number - 1,
})
}
render() {
if (this.state.isLoading) return <div>로딩중</div>
return (
<div>
<h2>{this.state.isLoading? "로딩중" : this.state.number}</h2>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Counter />
</div>
)
}
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<App />)
</script>
</body>
렌더 메서드 실행 → ComponentDidMount로 데이터를 불러오면 렌더 메서드 재실행 →
클릭 이벤트 실행 → ComponentDidUpdate로 상태 변경 → 렌더 메서드 재실행
*이후 생명주기 메서드 안에는 axios와 같은 비동기 요청 코드가 담길 예정입니다
리액트는 기본적으로 싱글 페이지 어플리케이션의 구현을 위해 제작된 라이브러리입니다
이를 위해 하나의 html 파일에서, 각 컴포넌트의 상태(데이터)가 바뀐 부분만을 찾아 화면을 다시 그리는 방식으로 구조가 짜여있습니다
그런데 <form> 태그의 submit과 같은 기능은 데이터를 전송할 때 새로고침 현상을 발생시키고
또 자바스크립트의 코드는 페이지의 새로고침이 발생하면 모든 이벤트의 실행 결과가 초기화된다는 문제가 있습니다
아래 예제 코드를 통해 해당 이슈에 대해 파악해보겠습니다
<body>
<div id="root"></div>
<script type="text/babel">
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
value: "",
word: "",
}
}
// onChange는 엘리먼트의 상태가 바뀔 때마다 발동된다는 것을 이용합니다
handleChange = (e) => {
this.setState({ value: e.target.value })
}
handleSubmit = (e) => {
// SPA 구현을 위해 form 태그의 새로고침을 막아두고 시작해야 합니다, 자주 사용하게 될 패턴
e.preventDefault()
this.setState({
word: this.state.value,
value: "",
})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" onChange={this.handleChange} value={this.state.value} />
<button type="submit">전송</button>
<div>{this.state.word}</div>
</form>
)
}
}
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
board: [
{ id: 1, subject: 'web7722' },
{ id: 1, subject: 'web7733' },
{ id: 1, subject: 'web7744' },
{ id: 1, subject: 'web7755' },
]
}
}
getList(board) {
return board.map((val, key)=><li key={key}>{val.subject}</li>)
}
render() {
return <ul>{this.getList(this.state.board)}</ul>
}
}
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Counter />
<Form />
<List />
</div>
)
}
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<App />)
</script>
</body>


map() 매서드만큼은 사용법을 제대로 파악하고 있어야 합니다<form> 태그의 input value를 상태로 관리하고,
위 화면에서 Props : web7722는 어떤 과정을 거쳐서 전달되었을까요?
<body>
<div id="root"></div>
<script type="text/babel">
class C extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>
Props: {this.props.text}
</div>
}
}
class B extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>
<C text={this.props.id} />
</div>
}
}
class A extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>
<B id={this.props.name} />
</div>
}
}
class Props extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>
<A name="web7722" />
</div>
}
}
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Counter />
<Form />
<List />
<Props />
</div>
)
}
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<App />)
</script>
</body>

<A name="web7722" /> → <B id={this.props.name} />

<B id={this.props.name} /> → <C text={this.props.id} />

<C text={this.props.id} /> → <div>Props: {this.props.text}</div>
컴포넌트명과 변수명을 바꿔가며 자주자주 연습합시다
*컴포넌트의 innerHTML은 children이라는 속성명으로 고정되어 전달됩니다
Component는 엘리먼트의 모음이며 하나의 상태만을 가질 수 있습니다. 상태에 따라 엘리먼트가 바뀔 수 있습니다
Props는 데이터(값)의 전달이며, 방향은 자식 컴포넌트를 향해 한방향으로만 이어집니다
State(상태)는 엘리먼트에 표현할 데이터를 모아주는 변수를 뜻합니다
State 끌어올리기를 사용하려면 부모 컴포넌트와 자식 컴포넌트를 구분할 수 있어야 합니다,
그리고 함수가 값이라는 사실도 알아야 합니다
(부모 컴포넌트에서 본인의 상태를 바꾸는 함수를 미리 구현 → 자식 컴포넌트는 전달받은 함수를 실행)
생명주기는 상태 변화에 따라 실행할 추상 메서드를 뜻합니다
(this.setState()를 사용할 경우에 componentDidUpdate()와 render()가 실행되는 상황)
이벤트는 이벤트를 등록하는 방법에 대해서만큼은 정확히 인지해야합니다,
this 바인딩에 대해 공부하는 것도 자바스크립트 작동원리 이해에 큰 도움이 됩니다
조건부 렌더링 ~ JSX 문법({})과 삼항연산자를 통한 컴포넌트 호출
List 렌더링 ~ 같은 엘리먼트를 여러번 사용할 때, 배열[]에 담아서 사용해도 출력된다는 것을 알아둡시다
<ul>
{[<li>hello</li>,<li>world</li>]}
</ul>
map() 메서드를 사용해서 배열 내용을 변경할 수 있으면 OK입니다
const arr = [{a: 10},{b: 20}]
// map 함수로 그리기
[<li>a: 10</li>, <li>b: 10</li>]