

UI는 사용자 인터페이스라는 뜻으로, 웹 페이지에서 사용자와 상호작용하는 요소를 말합니다.
Viewer 컴포넌트 만들기
src에 component 폴더를 만들고, 이 폴더에서 Viewer.js 파일을 생성합니다.
계속해서 Viewer.js 코드는 다음과 같이 작성합니다.
const Viewer = () => {
return (
<div>
<div>현재 카운트: </div>
<h1>0</h1>
</div>
);
};
export default Viewer;
App.js 파일도 다음과 같이 작성하여 Viewer 컴포넌트를 App의 자식으로 배치합니다.
import "./App.css";
import Viewer from "./component/Viewer";
function App() {
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<Viewer />
</section>
</div>
);
}
export default App;
이때, <section> 태그로 Viewer 컴포넌트를 감싸 영역을 분리해줍니다.
Controller 컴포넌트 만들기
component 폴더 안에 Controller.js 파일을 추가하고 다음과 같이 코드를 작성합니다.
const Controller = () => {
return (
<div>
<button>-1</button>
<button>-10</button>
<button>-100</button>
<button>+100</button>
<button>+10</button>
<button>+1</button>
</div>
);
};
export default Controller;
App.js도 다음과 같이 수정합니다.
import "./App.css";
import Controller from "./component/Controller";
import Viewer from "./component/Viewer";
function App() {
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<Viewer />
</section>
<section>
<Controller />
</section>
</div>
);
}
export default App;
컴포넌트 스타일링하기
src 폴더 App.css에서 기존 코드를 모두 삭제하고 다음과 같이 작성합니다.
body {
padding: 20px;
}
.App {
margin: 0 auto;
width: 500px;
}
.App > section { ①
padding: 20px;
background-color: rgb(245, 245, 245);
border: 1px solid rgb(240, 240, 240);
border-radius: 5px;
margin-bottom: 10px;
}
UI 구현은 모두 끝났습니다. 지금까지 작업한 결과는 아래와 같습니다.

그렇다면 State는 어떤 컴포넌트에 만들어야 할까요?
import './App.css';
import { useState } from "react";
import Viewer from './component/Viewer';
import Controller from './component/Controller';
function App() {
const[count, setCount] = useState(0);
const handleSetCount = (value) => {
setCount(count + value);
};
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<Viewer count = {count}/> //Viewer 컴포넌트에서 State변수 count의 값을 Props로 전달
</section>
<section>
<Controller handleSetCount={handleSetCount} /> // Controller 컴포넌트에 State값을 변경하는 함수 setCount 를 Props로 전달
</section>
</div>
);
}
export default App;
const Viewer = ({ count }) => {
return (
<div>
<div>현재 카운트 : </div>
<h1>{count}</h1>
</div>
);
};
export default Viewer;
const Controller = ({ handleSetCount }) => {
return (
<div>
<button onClick={() => handleSetCount(-1)}>-1</button>
<button onClick={() => handleSetCount(-10)}>-10</button>
<button onClick={() => handleSetCount(-100)}>-100</button>
<button onClick={() => handleSetCount(100)}>+100</button>
<button onClick={() => handleSetCount(10)}>+10</button>
<button onClick={() => handleSetCount(1)}>+1</button>
</div>
);
};
export default Controller;
지금까지 카운트 기능을 구현하려면 State를 App 컴포넌트에서 만들어야 한다는 점을 살펴보았습니다. 그 이유를 다시 정리하면 State 값은 Viewer 컴포넌트, set 함수는 Controller 컴포넌트에 전달해야 하기 때문입니다.
리액트는 State 값이나 set함수를 여러 컴포넌트에서 사용하는 경우, 이들을 상위 컴포넌트에서 관리합니다. 리액트에서는 이 기능을 다른 말로 ‘State 끌어올리기(State Lifting)’ 라고 합니다.
Props의 전달 방향은 언제나 부모로부터 자식에게 전달하는 방식입니다. 이러한 데이터 전달 특징을 '단방향 데이터 흐름'이라고 합니다.
반면 State를 변경하는 이벤트는 자식에서 부모를 향해 역방향으로 전달되어야 합니다.
리액트의 라이프 사이클은 크게 3단계로 구분합니다. 단계는 다음과 같습니다.
리액트 훅의 하나인 함수 useEffect를 이용해 쉽게 제어가 가능합니다.
어떤 값이 변경될 때마다 특정 코드를 실행하는 리액트 훅 입니다.
useEffect를 이용해 컴포넌트의 State값이 바뀔때마다 변경된 값을 콘솔에 출력하게 할 수 있습니다.
하나의 값 검사하기
App.js를 다음과 같이 수정합니다.
import { useState, useEffect } from "react";
(...)
function App() {
const [count, setCount] = useState(0);
const handleSetCount = (value) => {
setCount(count + value);
};
useEffect(() => {
console.log("count 업데이트: ", count);
}, [count]);
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<Viewer count={count} />
</section>
<section>
<Controller handleSetCount={handleSetCount} />
</section>
</div>
);
}
export default App;
위 코드를 통해 App컴포넌트에서 State 변수 count의 값이 바뀌면, 변경된 값을 콘솔에 출력할 수 있습니다.
두 번째 인수로 전달한 배열을 의존성 배열(Dependency Array, 줄여서 deps)이라고 하는데, useEffect는 이 배열 요소의 값이 변경되면 첫 번째 인수로 전달한 콜백함수를 실행합니다.
useEffect의 용법
useEffect(callback, [deps])
콜백 함수 의존성 배열
여러 개의 값 검사하기
의존성 배열 요소 중 하나가 변경되어도 useEffect는 콜백 함수를 실행 합니다.
(...)
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
b
const handleSetCount = (value) => {
setCount(count + value);
};
const handleChangeText = (e) => {
setText(e.target.value);
};b
useEffect(() => {
console.log("업데이트: ", text, count);
}, [count, text]);
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<input value={text} onChange={handleChangeText} />
</section>b
<section>
<Viewer count={count} />
</section>
<section>
<Controller handleSetCount={handleSetCount} />
</section>
</div>
);
}
export default App;
App.js를 다음과 같이 바꾸면 useEffect의 의존성 배열 요소로 State 변수 count와 text가 있으므로, 이 값이 바뀌면 콜백 함수가 실행됩니다.
useEffect로 라이프 사이클 제어하기
useEffect를 사용하여 라이프 사이클 중 업데이트가 발생하면 특정 코드를 실행할 수 있습니다.
(...)
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
const handleSetCount = (value) => {
setCount(count + value);
};
const handleChangeText = (e) => {
setText(e.target.value);
};
useEffect(() => {
console.log("컴포넌트 업데이트");
});
(..)
}
export default App;
App 컴포넌트가 위와 같을때, 의존성 배열에 아무것도 전달하지 않으면 useEffect는 컴포넌트를 렌더링할 때마다 콜백 함수를 실행합니다.
리렌더 될 때만 콜백함수를 실행시키는 것도 가능합니다.
import { useRef, useState, useEffect } from "react";
(...)
function App() {
(...)
const didMountRef = useRef(false);
useEffect(() => {
if (!didMountRef.current) {
didMountRef.current = true;
return;
} else {
console.log("컴포넌트 업데이트!");
}
});
(...)
}
export default App;
함수 useRef를 이용해 App.js 를 다음과 같이 수정하면, 업데이트 시점에만 콜백 함수를 실행하도록 할 수 있습니다.
useEffect의 콜백 함수에 추가한 조건문을 통해 마운트 시점에 함수 호출시 아무것도 출력하지 않고 종료하고, 업데이트 시점에 호출시 문자열을 콘솔에 출력합니다.
컴포넌트의 마운트 제어하기
(...)
function App() {
(...)
const didMountRef = useRef(false);
useEffect(() => {
if (!didMountRef.current) {
didMountRef.current = true;
return;
} else {
console.log("컴포넌트 업데이트!");
}
});
useEffect(() => {
console.log("컴포넌트 마운트");
}, []);
return (
(...)
);
}
export default App;
위 코드에서 useEffect 함수는 의존성 배열에 빈 배열을 사용해 컴포넌트의 마운트 시점에만 콜백 함수를 실행합니다.
이와 같이 컴포넌트의 마운트 시점에 실행되는 코드를 '컴포넌트의 마운트를 제어한다'라고 표현합니다.
컴포넌트 언마운트 제어하기
사이클의 마지막 단계인 언마운트는 컴포넌트가 페이지에서 제거될 때입니다.
언마운트 시점을 제어하기 위해서는 클린업(Cleanup) 기능이 필요합니다.
클린업(Cleanup)
클린업을 이해하기 위해 App 컴포넌트에서 다음과 같이 함수 useEffect를 한 번 더 호출하겠습니다.
(...)
function App() {
(...)
useEffect(() => {
setInterval(() => {
console.log("깜빡");
}, 1000);
});
(...)
}
export default App;
저장한 직후 개발자 도구의 콘솔을 통해 1초마다 문자열 깜빡이 출력되지만, App 컴포넌트를 여러 번 리렌더 한 후에는 함수 setInterval에서 정한 인터벌(1초)이 아닌 매우 빠른 속도로 ‘깜빡’ 문자열이 콘솔에 출력되는 현상을 볼 수 있습니다.
이는 2가지 이유 때문입니다.
이럴때 useEffect의 클린업 기능으로 해결이 가능합니다.
앞서 작성한 useEffect 코드를 다음과 같이 수정합니다.
(...)
function App() {
(...)
useEffect(() => {
const intervalID = setInterval(() => {
console.log("깜빡");
}, 1000);
return () => {
console.log("클린업");
clearInterval(intervalID);
};
});
(...)
}
export default App;
useEffect의 콜백함수가 반환하는 함수를 클린업 함수라고 합니다. 이 함수는 콜백 함수를 다시 호출하기 전에 실행되므로 렌더링할 때마다 새 인터벌을 생성하고 기존 인터벌은 삭제합니다.
클린업을 이용해 컴포넌트 언마운트 제어하기
이제 컴포넌트가 페이지에서 사라질 때 원하는 코드를 실행하는 ‘컴포넌트 언마운트’에 대해 살펴보겠습니다.
'컴포넌트 언마운트'란 클린업 기능을 이용하여 컴포넌트가 페이지에서 사라질 때 원하는 코드를 실행할 수 있는 기능입니다.
function Even() {
return <div>현재 카운트는 짝수입니다</div>;
}
export default Even;
Even.js 생성
(...)
import Even from "./component/Even";
function App() {
(...)
return (
<div className="App">
(...)
<section>
<Viewer count={count} />
{count % 2 === 0 && <Even />}
</section>
(...)
</div>
);
}
export default App;
코드를 위처럼 구성하면 count 값이 짝수일 때 Even 컴포넌트를 페이지에 렌더링합니다.
useEffect를 사용하여, Even 컴포넌트를 다음과 같이 수정합니다.
import { useEffect } from "react";
function Even() {
useEffect(() => {
return () => {
console.log("Even 컴포넌트 언마운트");
};
}, []);
return <div>현재 카운트는 짝수입니다</div>;
}
export default Even;
위 코드는 함수 useEffect에 의존성 배열로 빈 배열을 전달하고, 콜백 함수가 함수를 반환하면 이 함수는 컴포넌트의 언마운트 시점에 실행됩니다.
개발자 콘솔을 이용하여 State 값이 홀수가 되면, Even 컴포넌트를 언마운트하면서 콘솔에 'Even 컴포넌트 언마운트'라는 문자열을 출력하는 것을 확인할 수 있습니다.
지금까지 함수 useEffect를 이용해 값을 검사하고, 컴포넌트 라이프 사이클까지 제어할 수 있음을 확인했습니다.
useEffect는 앞으로도 자주 사용할 리액트 훅이므로 숙지하기를 바랍니다.
useEffect를 수정하고 console.log로 확인하는 과정의 번거로움을 해결하기 위해 리액트 개발자 도구를 사용할 수 있습니다.
도구 설치하기
Chrome Web Store
브라우저에 새로운 기능을 추가하고 탐색 환경을 맞춤설정합니다.
chromewebstore.google.com
Chrome Web Store에서 [React Developer Tools] 검색 후 설치합니다.
설치 후 [도구 더보기]-[확장 프로그램]을 클릭하고 React Developer Tools 스위치를 On으로 설정한 뒤 <세부 정보> 페이지에서 옵션을 다음과 같이 설정합니다.
마지막으로 확장 프로그램 아이콘을 클릭해 고정하면 크롬 우측 상단에 아이콘과 상태가 표시됩니다.
설치 확인하기
VS Code에서 카운터 앱을 npm run start 명령어로 시작하면 리액트 개발자 도구 아이콘이 나타나는 것을 확인할 수 있습니다.
리액트 개발자 도구의 기능 사용하기
개발자 도구를 통해 위 네가지 기능을 사용할 수 있습니다.
출처: 이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023).
Corner React.js
Editor: jyeon
| [React.js] UseReducer와 상태 관리 -최적화 (0) | 2025.11.28 |
|---|---|
| [React.js] 리액트를 다루는 기술-8장.hooks (0) | 2025.11.21 |
| [React.js]5장.리액트의 기본 기능 다루기 (0) | 2025.11.07 |
| [React.js]3장. Node.js ~ 4장. 리액트 시작하기 (0) | 2025.10.28 |
| [React.js]2장. 자바스크립트의 실전-구조 분해 할당 ~ 2장. 자바스크립트 실전 - 비동기 처리 (1) | 2025.10.10 |