마운트 시: constructor -> render -> ref -> componentDidMount
업데이트 시: setState/props -> shouldComponentUpdate(true) -> render -> componentDidUpdate
부모가 자식을 삭제했을 시: componentWillUnmount
에러 발생 시: componentDidCatch
대충 이정도 생각하면 되는데,
처음 보는 용어들이 많이 나타나고 있다 간단하게 정리만 한번 해보자!
componentDidMount
언제 쓰이는가?: 컴포넌트가 처음으로 DOM에 마운트된 직후에 호출된다.
주요 목적: 컴포넌트가 처음 렌더링된 후에 필요한 초기화 작업을 수행한다. 주로 네트워크 요청, 타이머 설정, 외부 라이브러리 초기화 등을 수행한다.
<비동기 요청을 많이 한다. >
--> componentDidMount는 React 컴포넌트가 처음으로 DOM에 마운트된 직후에 호출되는 라이프사이클 메서드이다. 이 시점에서 컴포넌트는 이미 렌더링이 완료된 상태이므로, 추가적인 초기화 작업이나 데이터를 가져오는 작업을 수행하기에 이상적인 시점이다.
shouldComponentUpdate(true)
언제 쓰이는가?: 컴포넌트가 새 props 또는 state를 받았을 때, 리렌더링을 할지 말지 결정하는 시점에 호출된다.
주요 목적: 불필요한 리렌더링을 방지하여 성능을 최적화한다. true를 반환하면 업데이트가 진행되고, false를 반환하면 업데이트를 중지한다.
componentDidUpdate
언제 쓰이는가?: 컴포넌트의 업데이트가 완료된 후에 호출된다. 즉, 컴포넌트가 리렌더링된 후에 실행된다.
주요 목적: DOM이 갱신된 후에 추가 작업을 수행합니다. 주로 DOM 조작, 추가 데이터 페칭, 이전 props와 state와 비교하여 특정 작업을 수행할 때 사용한다.
componentWillUnmount
언제 쓰이는가?: 컴포넌트가 DOM에서 제거되기 직전에 호출된다.
주요 목적: 컴포넌트가 제거되기 전에 필요한 정리 작업(clean-up)을 수행한다. 주로 타이머 해제, 구독 취소, 네트워크 요청 취소, 인터벌 중단! 등을 처리한다.
componentDidCatch
언제 쓰이는가?: 하위 컴포넌트에서 발생한 JavaScript 에러를 잡아낼 때 호출된다. 에러 경계(Error Boundary)로 사용된다.
주요 목적: 에러를 잡아내고, 대체 UI를 표시하거나 로그를 기록하는 등의 에러 처리를 수행한다.
componentDidMount에서 비동기 요청 실행 --> componentDidCatch에서 비동기 요청 정리(중단, 취소)
짝을 이뤄서 많이 이루어진다고 한다!
- componentDidMount: 컴포넌트가 DOM에 처음 삽입된 후 1초마다 "안녕하세요"를 출력하는 타이머를 설정한다.
- componentWillUnmount: 컴포넌트가 DOM에서 제거되기 직전에 타이머를 정리하여 타이머가 계속 실행되지 않도록 한다.
이와 같이 완료되지 않는 비동기 요청은 componentWillUnmount를 통해 정리를 꼭 해주어야 한다
(해주지 않으면 메모리가 ㄱ계속 쌓여서 펑! 하고 터지게 된다!)
갑자기 중요한 부분 --> render() 부분에는 setState()가 들어가면 안된다.
render()이 로딩이 되는데, 그 안에 setState()가 있으면, setState()가 또 로딩되고 무한 반복 이 이루어지기 떄문이다.
자 이제, 가위바위보 그림에 맞춰서 1초에 한번 씩 그림이 바뀌도록 코드를 작성해보자
//이미지 좌표
const rspCoords = {
바위: '0',
가위: '-142px',
보: '-284px',
};
componentDidMount() {
this.interval = setInterval(() => {
const { imgCoord } = this.state;
if (imgCoord === rspCoords.바위) {
this.setState({
imgCoord: rspCoords.가위,
});
} else if (imgCoord === rspCoords.가위) {
this.setState({
imgCoord: rspCoords.보,
});
} else if (imgCoord === rspCoords.보) {
this.setState({
imgCoord: rspCoords.바위,
});
}
}, 1000);
}
이 가위바위보 코드도 마찬가지다. 이를 render() 부분에 넣어버리면, 무한 로딩이 걸리기 때문에
componentDidMount()에 넣어주어야 한다!
이후,
componentWillUnmount() {
clearInterval(this.interval);
}
componentWillUnmount()를 통해 interval를 clear를 해주어야 한다.
render()를 통해 컨포넌트가 딱 화면에 보이자마자 componentDidMount()를 통해 다음 동작들을 비동기 적으로 만들어 줄 수 있고, componentWillUnmount()를 통해 DOM에서 제거되기 직전에 중지 시킬 수 있다.
우선 class 밖으로 뺴둔 rspCoords , score, computerChoice()를 살펴보자
간단하게 설명하자면 , rspCoords는 주먹 가위 보 이미지 좌표를 의미하고,
score 은 각각의 점수를,
computerChoice()함수는 컴퓨터에서 선택한 이미지를 고르는 코드이다.
자세하게 들여다보면, rspCoords를 entries -> '바위' : '0' 키와 값으로 저장하고(entries 메서드를 통해 배열로 변환), 그 중에 인덱스 1번쨰 값이 imgCoord 같은 것의 인덱스 0번째를 반환한다.
결론은 imgCoord와 좌표가 같은 것을 찾은 뒤, 가위 바위 보 중 하나를 출력하는 것이다.
이제 화면에 뿌려질 render()부분을 살펴보자
바위 가위 보 각각의 버튼이 있고, 그 버튼을 누르면, 결과와 점수를 출력할 수 있도록 코드를 작성해 보았다.
여기서 onClickBtn 이벤트를 통해서 버튼을 눌렀을 때, 실행할 함수를 정의했다.
그 전에, render()가 로딩되면서 비동기로 실행해줘야할 부분이 있다.
바로 주먹 가위 보 이미지가 계속해서 로딩하는 것이다.
위에서 언급했던 부분이다.
imgCoord가 rspCoords.바위와 좌표가 같은지 확인한 후, 같다면, 다음 이미지로 가위를 보여주는 식으로
바위 -> 가위-> 보 순으로 이미지가 계속 보여주는 것이다! 이는 0.1초에 한번 씩 이미지가 변경되는 것을 의미한다.
자 이제 onClickBtn()함수를 살펴보러 가자

자, 버튼을 누르기 전에는 계속 이미지가 바위 -> 가위-> 보 순으로 로딩이 계쏙~~~ 되고 있는 상황이다.
이때, 버튼을 누르면, onClickBtn()함수가 실행되면서, 이미지 로딩이 잠깐 멈춰서, 어떤 이미지인지 확인하고, 계산하고 결과를 출력해야 한다.
그러기 위해서는 우선 clearInterval() 메서드를 통해 interval을 멈춰줘야 한다.
이후, 사용자의 점수를 계산한다. 사용자의 점수(myScore)는 score 객체에서 해당 선택(choice)에 대한 값을 가져온다.
컴퓨터의 선택에 대한 점수를 계산한다. 컴퓨터의 선택은 computerChoice 함수를 통해 현재 이미지의 좌표에 해당하는 선택으로 결정된다.
사용자와 컴퓨터의 점수 차이(diff)를 계산한다.
점수 차이에 따라 게임의 결과를 결정하고, 그에 따라 결과 메시지와 점수를 업데이트한다.
이후, 점수도 나오고, 결과도 출력되면, setTimeout() 함수를 통해서 2초 후에 이미지 변경을 시작한다.
이때 setInterval 함수를 사용하여 0.1초마다 이미지를 변경하는 changeHand 함수를 호출해서 다시 이미지 로딩을 시작한다.

요런 느낌이다 .. ㅋㅋㅋㅋ 갈수록 코드들이 어려워지고 있다.. 정신 똑디 차리자
여기서 중요한 점은
이전 코드와 비교하자면,

어떨때는 이벤트 함수를 불러올 때
onClick = {this.onClickScreen}으로 불러오는 경우도 있고,
어떨 떄에는
ocClick = {()=> this.onClickBtn('바위')} 으로 불러오는 경우가 있다
왜 다른걸까??
첫 번째 경우인 onClick={this.onClickScreen}에서는 클릭 이벤트가 발생했을 때 호출해야 할 함수에 별도의 인자가 필요하지 않으므로, 그냥 함수 레퍼런스를 전달하여 호출한다. 이 경우에는 클릭 이벤트에 대한 정보가 필요하지 않기 때문에 그냥 함수를 호출하면 되는 것이다.
두 번째 경우인 onClick={() => this.onClickBtn('바위')}에서는 버튼을 클릭했을 때 호출해야 할 함수가 클릭한 버튼의 정보 등을 필요로 하기 때문에, 이 정보를 함수 호출 시에 전달해야 한다. 따라서 화살표 함수 내에서 함수를 호출하면서 필요한 인자를 함께 전달하는 방식을 사용하는 것이다.
그렇게 해서 첫 번째 경우는 단순히 함수 레퍼런스만 전달하면 되고, 두 번째 경우는 호출될 때 추가 정보를 전달해야 해서 함수를 호출할 때 인자를 전달하도록 구성한 것이다.
이렇게 함수를 호출할 때마다
() => 를 기록하는 것은 조~~~ 금 번거롭기도 하다.
그럴 때에는 해당 함수 자체에 ()=>를 추가해주면 제거할 수 있다. 
요렇게 화살표 함수를 추가할 때에는 저 위치에 가장 마지막에 추가를 해주어야 하고, 함수에 바로 추가를 해준다면
요렇게 제거하고 사용할 수가 있다.
실무에서 자주 자용하는 방법이라고 하니까 기억해두자!
class 에서 사용했던 라이프사이클은 hooks에서는 존재하지 않는다.
--> useEffect 를 대신해서 사용해야 한다!
useEffect()를 사용하면 컴포넌트가 렌더링된 후에 비동기적으로 실행할 코드를 작성할 수 있다.
useEffect() 안에 작성된 코드는 렌더링 후에 실행되며, 여러 종류의 부수 효과를 처리할 수 있다.
이러한 부수 효과에는 데이터 가져오기, 구독 설정하기, 이벤트 리스너 등이 포함될 수 있다.
useEffect() Hook은 클래스 컴포넌트의 componentDidMount, componentDidUpdate, componentWillUnmount 라이프사이클 메서드와 유사한 역할을 수행한다!!
useEffect(() => {
// 부수 효과를 수행하는 코드
// componentDidMount, componentDidUpdate 역할
return () => {
// 정리(clean-up) 코드 --> componentWillUnmount 역할
};
}, [dependencies]);
여기서 특이한 점이 마지막에 배열을 받는다는 점이다.
dependencies는 의존성 배열로, useEffect()가 실행되는 조건을 지정한다.
dependencies가 비어있다면 useEffect()는 컴포넌트가 처음 렌더링될 때에만 실행된다.
만약 dependencies가 존재한다면, 배열 안의 값이 변경될 때마다 useEffect()가 실행된다.
useEffect(() => {
// componentDidMount, componentDidUpdate 역할(1대1 대응은 아님)
interval.current = setInterval(changeHand, 100);
return () => {
// componentWillUnmount 역할
clearInterval(interval.current);
};
}, [imgCoord]);
const changeHand = () => {
if (imgCoord === rspCoords.바위) {
setImgCoord(rspCoords.가위);
} else if (imgCoord === rspCoords.가위) {
setImgCoord(rspCoords.보);
} else if (imgCoord === rspCoords.보) {
setImgCoord(rspCoords.바위);
}
};
useEffect()를 사용한 부분을 살펴보자**
여기서 주의깊게 살펴봐야할 부분은 [imgCooard]부분이다.
imgCoord를 의존성 배열에 명시하여, imgCoord 값이 변경될 때마다 useEffect()가 재실행되고, interval.current에 저장된 인터벌이 클리어된다.
즉, 이미지가 변경될 때마다, useEffect()가 실행되고 -> clearInterval이 실행된다.
changeHand 자체가 이미지가 바위 -> 가위 -> 보 로 반복되는데, imgCoord가 반복될 때마다 useEffect()가 실행이 되기 때문에 반복된다고 생각하면 된다.
전체적으로 코드를 살펴보면
import React, { useState, useRef, useEffect } from 'react';
const rspCoords = {
바위: '0',
가위: '-142px',
보: '-284px',
};
const scores = {
가위: 1,
바위: 0,
보: -1,
};
const computerChoice = (imgCoord) => {
return Object.entries(rspCoords).find(function (v) {
return v[1] === imgCoord;
})[0];
};
const RSP = () => {
const [result, setResult] = useState('');
const [imgCoord, setImgCoord] = useState(rspCoords.바위);
const [score, setScore] = useState(0);
const interval = useRef();
useEffect(() => {
// componentDidMount, componentDidUpdate 역할(1대1 대응은 아님)
interval.current = setInterval(changeHand, 100);
return () => {
// componentWillUnmount 역할
clearInterval(interval.current);
};
}, [imgCoord]);
const changeHand = () => {
if (imgCoord === rspCoords.바위) {
setImgCoord(rspCoords.가위);
} else if (imgCoord === rspCoords.가위) {
setImgCoord(rspCoords.보);
} else if (imgCoord === rspCoords.보) {
setImgCoord(rspCoords.바위);
}
};
const onClickBtn = (choice) => () => {
if (interval.current) {
clearInterval(interval.current);
interval.current = null;
const myScore = scores[choice];
const cpuScore = scores[computerChoice(imgCoord)];
const diff = myScore - cpuScore;
if (diff === 0) {
setResult('비겼습니다!');
} else if ([-1, 2].includes(diff)) {
setResult('이겼습니다!');
setScore((prevScore) => prevScore + 1);
} else {
setResult('졌습니다!');
setScore((prevScore) => prevScore - 1);
}
setTimeout(() => {
interval.current = setInterval(changeHand, 100);
}, 1000);
}
};
return (
<>
<div
id="computer"
style={{
background: `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${imgCoord} 0`,
}}
/>
<div>
<button id="rock" className="btn" onClick={onClickBtn('바위')}>
바위
</button>
<button id="scissor" className="btn" onClick={onClickBtn('가위')}>
가위
</button>
<button id="paper" className="btn" onClick={onClickBtn('보')}>
보
</button>
</div>
<div>{result}</div>
<div>현재 {score}점</div>
</>
);
};
export default RSP;
요러한 느낌이다.
class 에서 사용하는
componentDidMount
componentWillUnmount
요 두개는 componentDidMount 안에 여러 개를 동시에 지정할 수 있다.
반면,
hooks에서 사용하는
useEffect()의 경우,
보통의 경우 하나씩 지정을 한다--> 여러 개의 useEffect()를 만들 수 있다