React 이벤트 시스템은 Synthetic Event(합성 이벤트) 라는 것을 사용
-> 브라우저 간의 호환성을 위해 만들어진 일종의 래퍼
// HTML 에서의 이벤트
// 문자열, 브라우저 고유 이벤트
<button onclick="handleClick()">클릭</button>
// React 에서의 이벤트
// 함수 참조, SyntheticEvent (React가 만든 이벤트)
<button onClick={handleClick}>클릭</button>
// 버튼 클릭 이벤트
function ClickExample() {
const handleClick = () => {
alert('버튼이 클릭됨!');
};
return (
<button onClick={handleClick}>클릭하세요</button>
);
}
// 이벤트 객체 사용
// React 이벤트 핸들러가 이벤트 객체를 자동으로 넘겨줌
function InputExample() {
const handleChange = (event) => {
console.log(event.target.value); // 입력값 출력
};
return (
<input type="text" onChange={handleChange} />
);
}
컴포넌트가 생성, 사용, 소멸될 때 까지의 과정이다.
DOM이 생성되고 브라우저에 나타나는 시점이다.
컴포넌트의 생서자(Constructor)가 실행된다.
컴포넌트가 여러 번 렌더링 되는 것이다.
컴포넌트의 props가 변경되거나 setState() 함수 호출에 의해 state가 변경되거나, forceUpdate()로 강제 업데이트 함수 호출로 인해 컴포넌트가 렌더링될 때, 부모 컴포넌트가 새로 리렌더링 한다.
컴포넌트를 DOM에서 제거하는 것
상위 컴포넌트에서 현재 컴포넌트를 더 이상 화면에 표시하지 않게 될 때 언마운트 된다.
클래스형 컴포넌트에서 필수로 정의해야 하는 메서드
JSX를 반환하며, 그 JSX가 사용자 화면(UI)에 출력된다.
컴포넌트의 state나 props가 변경되면, React는 render()를 다시 호출하여 화면을 업데이트한다.
import React, { Component } from 'react';
class Hello extends Component {
// render() 안에서 return되는 JSX가 실제로 브라우저에 그려진다
// this.props, this.state 등을 통해 동적으로 내용을 바꿀 수 있다
render() {
return <h1>안녕하세요, {this.props.name}님!</h1>;
}
}
클래스의 생성자 함수이다.
React 컴포넌트에서 초기 설정을 하며 this.state를 초기화하는 데 자주 사용된다.
super(props)를 반드시 호출해야 this.props를 사용할 수 있다.
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props); // 부모 클래스의 생성자 호출 (반드시 필요)
// 상태값(state) 초기값 설정
this.state = {
name: 'React'
};
// this.handleClick = this.handleClick.bind(this); // 메서드 바인딩 (필요한 경우)
}
render() {
return <h1>안녕하세요, {this.state.name}</h1>;
}
}
React 클래스형 컴포넌트의 정적(static) 생명주기 메서드 중 하나로, props의 변경에 따라 state를 동기화하고 싶을 때 사용한다.
props가 변경되었을 때 자동 호출된다.(컴포넌트 생성 시 + 업데이트 시)
리턴 값으로 새로운 state 객체를 반환하거나 null을 반환해서 state를 그대로 둘 수 있다.
static getDerivedStateFromProps(nextProps, prevState) {
// props 또는 state를 기반으로 새로운 state를 반환하거나 null
return null; // 혹은 { someState: updatedValue }
}
// prop를 통해 state 업데이트
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.greeting !== prevState.message) {
return {
message: nextProps.greeting
};
}
return null; // state를 변경할 필요 없을 때
}
render() {
return <p>{this.state.message}</p>;
}
}
props로 받은 값에 따라 내부 상태(state)를 동적으로 업데이트해야 할 때 사용한다.
ex) 외부에서 새로운 값을 props로 넘겨줄 때 컴포넌트 내 표시 값도 갱신되어야 하는 경우
컴포넌트가 화면에 "처음" 렌더링(마운트)된 직후 실행되는 메서드
데이터 가져오기(API 요청), 이벤트 리스너 등록, 서드파티 라이브러리 초기화 등에 적합하다.
class MyComponent extends React.Component {
componentDidMount() {
// 여기서 API 호출, 타이머 설정, DOM 접근 등 가능
console.log('컴포넌트가 화면에 나타났습니다!');
}
render() {
return <div>Hello, world!</div>;
}
}
// 데이터 불러오기
// componentDidMount() 내부에서 fetch를 실행해서 데이터를 가져오고,
// 데이터가 도착하면 this.setState()로 업데이트 → 자동으로 render()가 다시 실행
class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
users: []
};
}
componentDidMount() {
// 마운트 후 API 호출
fetch('https://jsonplaceholder.typicode.com/users')
.then((res) => res.json())
.then((data) => this.setState({ users: data }));
}
render() {
return (
<ul>
{this.state.users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
}
컴포넌트가 리렌더링 되기 전에 "렌더링을 할지 말지"를 결정할 수 있게 해주는 성능 최적화용 메서드 (불필요한 렌더링을 방지-> 성능 최적화)
props나 state가 변경되었을 때, render()를 다시 호출할지 말지 결정하는 함수
shouldComponentUpdate(nextProps, nextState) {
// 기본값은 항상 true
return true; // false로 바꾸면 render() 막음
}
// 버튼을 클릭하면 count가 증가하지만, 짝수일 때만 화면이 실제로 다시 렌더링
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
shouldComponentUpdate(nextProps, nextState) {
console.log('변화 감지:', nextState.count);
return nextState.count % 2 === 0; // 짝수일 때만 리렌더링
}
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
console.log('렌더링됨');
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>+1</button>
</div>
);
}
}
DOM이 변경되기 직전에 실행되는 함수
컴포넌트가 업데이트되어 DOM에 반영되기 직전에 호출된다.
여기서 반환한 값은 componentDidUpdate로 전달된다.
-> 스크롤 위치, 포커스, DOM 위치 정보 등을 기록할 때 사용
// 형식
getSnapshotBeforeUpdate(prevProps, prevState) {
// DOM 업데이트 전에 실행됨
// 예: 스크롤 위치 저장
return snapshot;
}
// 생명주기 메소드
componentDidUpdate(prevProps, prevState, snapshot) {
// getSnapshotBeforeUpdate가 반환한 snapshot을 여기에 받음
}
// 스크롤 위치 기억하기
class ChatBox extends React.Component {
constructor(props) {
super(props);
this.messagesEndRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 새 메시지가 오기 전에 스크롤이 제일 아래였는지 확인
const wasScrolledToBottom =
this.messagesEndRef.current.scrollHeight -
this.messagesEndRef.current.scrollTop ===
this.messagesEndRef.current.clientHeight;
return wasScrolledToBottom;
}
componentDidUpdate(prevProps, prevState, wasScrolledToBottom) {
// 새 메시지 후에도 자동으로 스크롤을 유지
if (wasScrolledToBottom) {
this.messagesEndRef.current.scrollTop =
this.messagesEndRef.current.scrollHeight;
}
}
render() {
return (
<div
ref={this.messagesEndRef}
style={{ height: '300px', overflowY: 'scroll' }}
>
{this.props.messages.map((msg, index) => (
<p key={index}>{msg}</p>
))}
</div>
);
}
}
컴포넌트가 업데이트되고 나서 호출되는 메서드
props나 state가 변경되어 컴포넌트가 리렌더링된 직후 호출된다.
업데이트 이후의 처리가 필요한 경우 여기에 작성
ex) 서버에 변경사항 전송, 새 props에 따라 추가 작업, getSnapshotBeforeUpdate에서 받은 값 사용 등에 활용
// 이전 props 값, 이전 State 값, getSnapshotBeforeUpdate() 에서 반환된 값
componentDidUpdate(prevProps, prevState, snapshot) {
// 업데이트 직후 실행됨
}
// 버튼을 클릭하면 count가 바뀌고 → componentDidUpdate()가 실행
// 이전 상태와 현재 상태를 비교하여 변화 감지
class Counter extends React.Component {
state = { count: 0 };
componentDidUpdate(prevProps, prevState) {
// 무한 루프 주의
if (prevState.count !== this.state.count) {
console.log(`count가 ${prevState.count} → ${this.state.count}로 변경됨`);
}
}
render() {
return (
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
{this.state.count}
</button>
);
}
}
포넌트가 화면에서 제거되기 직전에 호출
주 용도는 정리(clean-up) 작업
ex) 이벤트 리스너 제거, 타이머(clearTimeout, clearInterval), 외부 라이브러리 리소스 해제, 웹소켓 연결 종료 등
componentWillUnmount() {
// 정리 작업
}
// 타이머 제거
class Timer extends React.Component {
state = { seconds: 0 };
componentDidMount() {
this.interval = setInterval(() => {
this.setState((prevState) => ({ seconds: prevState.seconds + 1 }));
}, 1000);
}
// 메모리 누수 방지
componentWillUnmount() {
clearInterval(this.interval); // 타이머 제거
console.log('타이머 제거됨');
}
render() {
return <div>타이머: {this.state.seconds}초</div>;
}
}
// 함수형 컴포넌트
useEffect(() => {
// mount 시 실행
console.log('컴포넌트 마운트됨');
return () => {
// unmount 시 실행 (== componentWillUnmount)
console.log('컴포넌트 언마운트됨');
};
}, []); // 빈 배열이면 mount/unmount 타이밍에만 실행
React 클래스형 컴포넌트에서 에러를 잡기 위한 생명주기 메서드
컴포넌트 트리에서 발생한 자식 컴포넌트의 JavaScript 오류를 잡고 처리
컴포넌트의 자식에서 렌더링 중, 생성자 중, 혹은 생명주기 메서드 중 오류가 발생했을 때 호출된다.
에러 경계(Error Boundary) 역할을 하며, 앱이 죽지 않게 방어해준다.
-> 오류를 화면에 표시하거나, 로그를 서버로 전송하는 데 사용
// 실제 발생한 에러 객체, 컴포넌트 스택 정보 (componentStack) 등
componentDidCatch(error, info) {
// error: 발생한 에러 객체
// info: 오류 발생 위치 등의 정보
}
// 에러 경계 컴포넌트
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
console.log('에러 발생:', error);
console.log('에러 정보:', info.componentStack);
// 서버로 에러 로깅 가능
}
render() {
if (this.state.hasError) {
return <h1>문제가 발생했습니다 😢</h1>;
}
return this.props.children;
}
}
// 사용
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
✅ 렌더링 중 발생한 오류
✅ 생명주기 메서드 중 발생한 오류
✅ 자식 컴포넌트의 constructor에서 발생한 오류
❌ 이벤트 핸들러 내부 오류 (별도로 try/catch 필요)
❌ 비동기 코드(setTimeout, fetch) 오류
사용자와의 상호작용을 처리하는 이벤트 시스템과 컴포넌트의 생명주기 흐름에 대해 공부했다. 클래스형 컴포넌트에서 사용하는 생명주기 메서드들이 많아 외우기도 어렵고, 각각의 쓰임새가 혼동되었다.