본문 바로가기

POSCO x CODINGOn 웹개발자 양성 부트캠프

[포스코x코딩온] 웹개발자 풀스택 과정 12주차 회고 | React : [Lifecycle, useEffect()] + React에서 axios사용법

React Lifecycle

  • React component의 생명 주기
  • Component가 생성될 때, 업데이트할 때, 제거될 때 어떻게 작동할지 정의할 수 있다.
  • 자주 사용되는 메서드
    • componentDidMount
    • componentDidUpdate
    • componentWillUnmount

 

component lifecycle

Lifecycle 용어

  • Mount (마운트): DOM이 생성되고 웹 브라우저 상에 나타남
  • Update (업데이트): props 및 state가 바뀌었을 때 업데이트함
  • Unmount (언마운트): componenet가 화면에서 사라질 때(제거될 때)

 

마운트(mount) 될 때 호출되는 메서드

  • construction
  • render
  • getDerivedStateFromProps
  • componentDidMount

 

업데이트(update) 될 때 호출되는 메서드

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • componentDidUpdate

 

언마운트(unmount) 될 때 호출되는 메서드

  • componentWillUnmount

 

메서드 사용 예제

import { Component } from 'react';

// 자식 클래스 component 정의
class MyComponent extends Component {
	
    // 본 component가 생길 때 호출되는 함수
    componentDidMount() {
    	console.log('Mount! - component가 생김')
	};
	
    // 본 component가 다시 렌더링될 때 호출되는 함수
	componentDidUpdate() {
    	console.log('Update! - component가 다시 렌더됨')
	};
    
    // 본 component가 제거될 때 호출되는 함수
    componendWillUmount() {
    	console.log('Unmount! - component가 제거됨')
	};
    
    render() {
		return(
			<>
            	<div>My Component {this.props.number}</div>
            </>
        );
	}
}

// 부모 클래스 component 정의
class LifeCycleClass extends Component {

	state = {
		number: 0,
        visible: true
    };
    
   	changeNumberState = () => {
		this.setState({ number: this.state.number + 1 });
	};
    
    changeVisibleState = () => {
		this.setState({ visible: !this.state.visible });
    };
    
    render() {
    	return(
        	<>
            	<button type='button' onClick={this.changeNumberState}>증가</button>
                <button type='button' onClick={this.changeVisibleState}>ON/OFF</button>
                {this.state.visible && <MyComponent number={this.state.number}></MyComponent>}
            </>
		);
	}

}

export default LifeCycleClass;

 

부모 component에서 number와 visible이라는 state를 생성하고 증가 버튼이 클릭되면 기존 number값에 1일 증감하다. visible값이 true일 경우에만 자식 component를 렌더링 하게 되며 ON/OFF 버튼이 클릭되면 자식 component의 렌더링 상태를 바꿔줄 수 있다.

 

  • 자식 component가 렌더링 될 경우, 바로 componentDidMount() 함수가 호출된다.
  • 자식 component는 부모 component의 number state값을 props로 받기 때문에, 증가 버튼이 클릭되면 props값이 바뀌는 관계로 자식 component가 다시 렌더링 된다. 이때, componentDidUpdate() 함수가 호출된다.
  • 부모 component에서 ON/OFF 버튼이 클릭되어 자식 component가 렌더링 되지 않는 상태로 바뀔 경우, componentWillUnmount() 함수가 호출된다.

 

해당 함수들은 클래스형 component에서만 사용이 가능하다.


 

함수형 component에서 Lifecycle은?

  • 제공되는 여러 Hook들 중, useEffect() 함수로 함수형 component의 lifecycle을 관리할 수 있다.

 

useEffect() 

  • Side Effect (부수효과)를 수행한다.
    • Side Effect: component가 렌더링 된 이후 비동기로 처리되어야 하는 부수적인 효과
    • mount / unmount / update
  • 클래스형 component의 componentDidMount 함수와 componentDidUpdate 함수가 합쳐진 형태다.

 

// component가 재렌더링 여부 상관 없이 렌더링이 될때마다 호출된다.

useEffect(() => {
	console.log('렌더링이 완료되었습니다.');
})



// useEffect함수의 두번째 인자로 빈 배열 []을 추가할 경우,
// component가 최초로 렌더링 될때만 함수가 실행된다.

useEffect(() => {
	console.log('처음 렌더링될 때만 실행됨');
}, []);



// 특정 값이 바뀔 때만 호출하고 싶을 경우, 두번째 인자에 component의 특정 state(들)을 넣어준다.

useEffect(() => {
	console.log('state가 바뀌었습니다');
}, [상태])



// component가 unmount될 때 작업을 수행하고 싶다면,
// useEffect함수의 두번째 인자 (의존성 배열)을 빈 배열로 설정해주고,
// useEffect함수의 첫번째 인자 (콜백 함수) 안에 수행하고 싶은 코드를 함수형으로 return 해준다.
// 만약 component가 렌더링되기 전(재렌더 포함)에 어떤 작업을 수행하고 싶다면 useEffect함수의 두번째 인자 없이 return 해줄 수 있다.

useEffect(() => {
    console.log('effect');
    return () => {
		console.log('component가 제거됨')
	}
}, []);

 


React에서 axios

만약 특정 component가 처음 렌더될 경우(mount) 벡엔드에서 데이터를 받아와 state에 저장하고 해당 state를 요소에 보여주고 싶을 경우, useEffect() 안에서 axios를 사용할 수 있다.

 

다만, useEffect()의 호출 함수는 promise를 반환하지 않는 관계로 해당 호출 함수 안에서 비동기 코드를 작성해야 한다.

 

 

아래와 같이 코드를 작성하면 제대로 동작하지 않는다.

// 잘못된 코드 예제

import { useState, useEffect } from 'react';
import axios from 'axios';

export default function UserList() {
	const [users, setUsers] = useState([]);
    
    useEffect(async() => {
		const result = await axios({
					method: 'GET',
					url: 'https://jsonplaceholder.typicode.com/users',
				});
        console.log(result)
    }, [])
}

 

올바른 코드 예시

import { useState, useEffect } from 'react';
import axios from 'axios';

export default function UserList() {
	const [users, setUsers] = useState([]);
	const [loading, setLoading] = useState(false);

	useEffect(() => {
		//useEffect는 비동기함수를 직접적으로 지원하지 않음.
		//비동기 함수를 사용하려면 내부에 비동기함수를 정의하고 바로 호출

		const axiosUser = async () => {
			try {
				const result = await axios({
					method: 'GET',
					url: 'https://jsonplaceholder.typicode.com/users',
				});
				setUsers(result.data);
				setLoading(true);
			} catch (error) {
				console.log(error);
			}
		};
		axiosUser();

		return () => {
			console.log('연결 해제 완료');
		};
	}, []);

	useEffect(() => {
		console.log('유저 정보 업데이트', users.length);
	}, [users]);

	return (
		<div>
			{loading ? (
				<ul>
					{users.map((user) => {
						return (
							<li key={user.id}>
								{user.name} - {user.email}
							</li>
						);
					})}
				</ul>
			) : (
				<div>Loading...</div>
			)}
		</div>
	);
}

 

느낀 점들

useEffect()를 사용함으로 예전 프로젝트에서 가끔 사용할 수밖에 없었던 즉시 실행 함수를 사용할 필요가 없어진 것 같다. 추가적으로 useEffect()의 clean up (return) 기능으로 component가 unmount 될 때의 경우도 다룰 수 있다는 점도 매우 편리한 것 같다.