๐Ÿ“…2024. 03. 28 74์ผ์ฐจ


React TodoList

๋ชฉ๋ก ์‚ญ์ œ

  • TodoListItem: ํ•  ์ผ ๋ชฉ๋ก์—์„œ ๊ฐœ๋ณ„ ํ•ญ๋ชฉ์„ ํ‘œ์‹œํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ. todo ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์™€์„œ ํ•ด๋‹น ํ•ญ๋ชฉ์˜ ์ œ๋ชฉ๊ณผ ID๋ฅผ ํ‘œ์‹œํ•˜๊ณ , ์‚ญ์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ๋•Œ removeTodo ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ•ด๋‹น ํ•ญ๋ชฉ์„ ์‚ญ์ œ.

  • TodoList: ํ•  ์ผ ๋ชฉ๋ก์„ ํ‘œ์‹œํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ. todos ๋ฐฐ์—ด์„ ๋ฐ›์•„์™€์„œ ๋ฐฐ์—ด์˜ ๊ธธ์ด์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋‚ด์šฉ์„ ํ‘œ์‹œ. ํ•  ์ผ์ด ์—†์„ ๊ฒฝ์šฐ "ํ•  ์ผ ์—†์Œ" ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๊ณ , ํ•  ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๊ฐ ํ•  ์ผ ํ•ญ๋ชฉ์„ TodoListItem์œผ๋กœ ๋ Œ๋”๋ง. TodoListItem props์— removeTodo ์ถ”๊ฐ€

  • App: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฉ”์ธ ์ปดํฌ๋„ŒํŠธ. NewTodoForm ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ํ•  ์ผ์„ ์ถ”๊ฐ€ํ•˜๊ณ , ๊ทธ ํ•˜๋‹จ์— TodoList ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ†ตํ•ด ํ•  ์ผ ๋ชฉ๋ก์„ ํ‘œ์‹œ. ๋˜ํ•œ, ํ•  ์ผ ํ•ญ๋ชฉ์„ ์‚ญ์ œํ•˜๊ธฐ ์œ„ํ•œ removeTodo ํ•จ์ˆ˜๋ฅผ TodoList ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ.

//์ƒ๋žต
const TodoListItem = ({ todo, removeTodo: _removeTodo }) => {
	const removeTodo = () => {
		_removeTodo(todo.id);
	};

	return (
		<li className="flex items-center gap-x-3 mb-3">
			<span className="badge badge-primary badge-outline">{todo.id}</span>
			<span>{todo.title}</span>
			<button className="btn btn-primary" onClick={removeTodo}>
				์‚ญ์ œ
			</button>
		</li>
	);
};

const TodoList = ({ todos, removeTodo }) => {
	return (
		<>
			{todos.length == 0 ? (
				<h4>ํ•  ์ผ ์—†์Œ</h4>
			) : (
				<>
					<h4>ํ•  ์ผ ๋ชฉ๋ก</h4>
					<ul>
						{todos.map((todo) => (
							<TodoListItem key={todo.id} todo={todo} removeTodo={removeTodo} />
						))}
					</ul>
				</>
			)}
		</>
	);
};

const App = () => {
	//์ƒ๋žต
	const removeTodo = (id) => {
		const newTodos = todos.filter(todo => todo.id != id);
		setTodos(newTodos);
	};

	return (
		<>
			<NewTodoForm addTodo={addTodo} />
			<hr />
			<TodoList todos={todos} removeTodo={removeTodo} />
		</>
	);
};

//์ƒ๋žต

๋ฆฌ์•กํŠธ(React), ์•„์ดํ…œ ์‚ญ์ œ ๊ตฌํ˜„


๋ชฉ๋ก ์ˆ˜์ •

  • TodoListItem editMode ์ƒํƒœ์™€ newTodoTitle ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜์ • ๋ชจ๋“œ์ธ์ง€ ํ™•์ธํ•˜๊ณ  ์ˆ˜์ •ํ•  ๋‚ด์šฉ์„ ๊ด€๋ฆฌ.
    enableEditMode: ์ˆ˜์ • ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜.
    cancelEdit: ์ˆ˜์ •์„ ์ทจ์†Œํ•˜๊ณ  ์ด์ „ ๋‚ด์šฉ์œผ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ํ•จ์ˆ˜.
    commitEdit: ์ˆ˜์ •๋œ ๋‚ด์šฉ์„ ๋ฐ˜์˜ํ•˜๊ณ  ์ˆ˜์ • ๋ชจ๋“œ๋ฅผ ์ข…๋ฃŒํ•˜๋Š” ํ•จ์ˆ˜.

  • ์‚ผํ•ญ์‹ ์‚ฌ์šฉ readMode๊ฐ€ true๋ฉด ์ˆ˜์ • ์‚ญ์ œ(์ผ๋ฐ˜๋ชจ๋“œ)๋กœ false๋ฉด ์ˆ˜์ •์™„๋ฃŒ, ์ˆ˜์ •์ทจ์†Œ(์ˆ˜์ •๋ชจ๋“œ)๋กœ ์ „ํ™˜

  • App ์ปดํฌ๋„ŒํŠธ์— modifyTodo ํ•จ์ˆ˜ ์„ ์–ธ todos ๋ฐฐ์—ด์—์„œ ํŠน์ • id๋ฅผ ๊ฐ€์ง„ ํ•ญ๋ชฉ๋งŒ ๊ทธ ์ œ๋ชฉ์„ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ์ˆ˜์ •ํ•˜๊ณ , ๋‚˜๋จธ์ง€ ํ•ญ๋ชฉ๋“ค์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€. React์—์„œ ์ด๋ ‡๊ฒŒ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋Š” ์ˆ˜์ •๋œ todos ๋ฐฐ์—ด์„ ๋ฐ˜์˜ํ•˜์—ฌ ๋ฆฌ๋ Œ๋”๋ง.

//์ƒ๋žต
const TodoListItem = ({
	//์ƒ๋žต
	modifyTodo: _modifyTodo
}) => {
	const [editMode, setEditMode] = useState(false);
	const [newTodoTitle, setNewTodoTitle] = useState(todo.title);
	const readMode = !editMode;

	const enableEditMode = () => {
		setEditMode(true);
	};

	//์ƒ๋žต

	const cancleEdit = () => {
		setEditMode(false);
		setNewTodoTitle(todo.title);
	};
	const commitEdit = () => {
		if (newTodoTitle.trim().length == 0) return;

		_modifyTodo(todo.id, newTodoTitle.trim());

		setEditMode(false);
	};

	return (
		<li className="flex items-center gap-x-3 mb-3">
			<span className="badge badge-accent badge-outline">{todo.id}</span>
			{readMode ? (
				<>
					<span>{todo.title}</span>
					<button className="btn btn-outline btn-accent" onClick={enableEditMode}>
						์ˆ˜์ •
					</button>
					<button className="btn btn-accent" onClick={removeTodo}>
						์‚ญ์ œ
					</button>
				</>
			) : (
				<>
					<input
						className="input input-bordered"
						type="text"
						placeholder="ํ•  ์ผ ์จ"
						value={newTodoTitle}
						onChange={(e) => setNewTodoTitle(e.target.value)}
					/>
					<button className="btn btn-accent" onClick={commitEdit}>
						์ˆ˜์ •์™„๋ฃŒ
					</button>
					<button className="btn btn-accent" onClick={cancleEdit}>
						์ˆ˜์ •์ทจ์†Œ
					</button>
				</>
			)}
		</li>
	);
};

const TodoList = ({ todos, removeTodo, modifyTodo }) => {
	return (
		<>
			{todos.length == 0 ? (
				<h4>ํ•  ์ผ ์—†์Œ</h4>
			) : (
				<>
					<h4>ํ•  ์ผ ๋ชฉ๋ก</h4>
					<ul>
						{todos.map((todo) => (
							<TodoListItem
								key={todo.id}
								todo={todo}
								removeTodo={removeTodo}
								modifyTodo={modifyTodo}
							/>
						))}
					</ul>
				</>
			)}
		</>
	);
};

const App = () => {
	//์ƒ๋žต
	const modifyTodo = (id, title) => {
		const newTodos = todos.map((todo) =>
			todo.id != id ? todo : { ...todo, title }
		);
		setTodos(newTodos);
	};

	return (
		<>
			<NewTodoForm addTodo={addTodo} />
			<hr />
			<TodoList todos={todos} removeTodo={removeTodo} modifyTodo={modifyTodo} />
		</>
	);
};
//์ƒ๋žต

๋ฆฌ์•กํŠธ(React), ์•„์ดํ…œ ์ˆ˜์ •์‹œ input ๊ฐ€๋Šฅํ•˜๋„๋ก, ์ˆ˜์ • ์™„๋ฃŒ


๋ฆฌ์•กํŠธ ์ปค์Šคํ…€ ํ›…

const useTodoStatus  = () => {
	const [todos, setTodos] = useState([]);
	const [lastTodoId, setLastTodoId] = useState(0);

	const addTodo = (title) => {
		const id = lastTodoId + 1;

		const newTodo = {
			id,
			title
		};
		setTodos([...todos, newTodo]);
		setLastTodoId(id);
	};

	const removeTodo = (id) => {
		const newTodos = todos.filter((todo) => todo.id != id);
		setTodos(newTodos);
	};

	const modifyTodo = (id, title) => {
		const newTodos = todos.map((todo) =>
			todo.id != id ? todo : { ...todo, title }
		);
		setTodos(newTodos);
	};

	return{
		todos,
		addTodo,
		removeTodo,
		modifyTodo
	};
}
  • useTodoStatus์— App return์œ„์— ์žˆ๋Š” ๋‚ด์šฉ ์ปดํฌ๋„ŒํŠธํ™”
const todoStatus = useTodoStatus();
  • App๋ถ€๋ถ„์— ์กฐ๋ฆฝ

๋ฆฌ์•กํŠธ(React), ๊ด€๋ จ๋œ state๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ๊ธฐ, ๋ฆฌ์•กํŠธ ์ปค์Šคํ…€ ํ›…


immer ์ ์šฉ

//์ƒ๋žต
// ๊ธฐ์กด ๋ฐฉ์‹
		// setTodos([...todos, newTodo]);

		// immer ์ ์šฉ
		setTodos(
			produce(todos, (draft) => {
				draft.push(newTodo);
			})
		);

		setLastTodoId(id);
  • ์ถ”๊ฐ€
// ๊ธฐ์กด ๋ฐฉ์‹
		// const newTodos = todos.filter((todo) => todo.id != id);

		// immer ์ ์šฉ
		const newTodos = produce(todos, (draft) => {
			const index = draft.findIndex((todo) => todo.id == id);
			draft.splice(index, 1);
		});
		
		setTodos(newTodos);
  • ์‚ญ์ œ
	// ๊ธฐ์กด ๋ฐฉ์‹
		// const newTodos = todos.map((todo) =>
		// 	todo.id != id ? todo : { ...todo, title }
		// );

		// immer ์ ์šฉ
		const newTodos = produce(todos, (draft) => {
			const index = draft.findIndex((todo) => todo.id == id);
			draft[index].title = title;
		});

		setTodos(newTodos);
  • ์ˆ˜์ •

๋ฆฌ์•กํŠธ(React), immer ์ ์šฉ


useRef ์ ์šฉ

  • formInputNoRef: ์ด ์ฐธ์กฐ๋Š” ์ž…๋ ฅ ์š”์†Œ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ.

  • input ์•ˆ์— ref={formInputNoRef} ์ ์šฉ

  • formInputNoRef.current.value = '123'; formInputNoRef.current.focus();
    ์ˆซ์ž ์ž…๋ ฅํ•˜์ง€ ์•Š์•˜์„ ๋•Œ input์ฐฝ ํฌ์ปค์‹ฑ, input์ฐฝ ๋ฒจ๋ฅ˜ ๊ฐ’ '123'์œผ๋กœ ์„ค์ • current ์ด์šฉ

import React, { useState, useRef } from "https://cdn.skypack.dev/react@18";

const App = () => {
	const formInputNoRef = useRef(null);
	const [number,setNumber] = useState('');
	
	const notice = () => {
		if(!number) {
			alert('์ˆซ์ž ์ž…๋ ฅํ•ด');
			formInputNoRef.current.value = '123';
			formInputNoRef.current.focus();
			return;
		}
		
		alert(`๋„ค๊ฐ€ ์ž…๋ ฅํ•œ ์ˆซ์ž๋Š” ${number}์•ผ.`);
		setNumber('');
	}
	
	return (
		<>
			<form onSubmit={(e) => {
					e.preventDefault();
					notice();
				}}>
				<input
					ref={formInputNoRef}
					className="input input-bordered"
					type="text"
					placeholder="์ˆซ์ž ์ž…๋ ฅ"
					value={number}
					onChange={(e) => setNumber(e.target.value)}
				/>
				&nbsp;
				<button className="btn btn-primary">์ถ”๊ฐ€</button>
			</form>
		</>
	);
};

๋ฆฌ์•กํŠธ(React), useRef๋ฅผ ์‚ฌ์šฉํ•ด์„œ dom ์กฐ์ž‘ํ•˜๊ธฐ


useEffect ์ ์šฉ

  • useEffect๋ฅผ ์“ฐ๋ฉด ํƒ€์ด๋ฐ์ด ๋Šฆ์–ด์ง„๋‹ค. ๋‚˜์ค‘์— ์‹คํ–‰ ํ™œ์šฉ
import React, { useState, useEffect } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";

let AppCallCount = 0;

const App = () => {
	console.log("์‹œ์ž‘");
	
	useEffect(() => {
		AppCallCount++;
		console.log(`1App์ด ${AppCallCount}๋ฒˆ ์‹คํ–‰`);
	}, []); // [] : ์˜์กด์„ฑ ๋ฐฐ์—ด
	// 1. ๋นˆ ๋ฐฐ์—ด์ด ์—†์œผ๋ฉด useEffect ์•ˆ์— ์žˆ๋Š” ๋กœ์ง์€ ๋ฆฌ๋žœ๋”๋ง ๋œ๋‹ค.
	// 2. ๋นˆ ๋ฐฐ์—ด์ด ์žˆ์œผ๋ฉด useEffect ์•ˆ์— ์žˆ๋Š” ๋กœ์ง์€ ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰๋œ๋‹ค.
	
	useEffect(() => {
		AppCallCount++;
		console.log(`2App์ด ${AppCallCount}๋ฒˆ ์‹คํ–‰`);
	});

	console.log("๋");
	const [no, setNo] = useState(0);

	return (
		<>
			<button onClick={() => setNo(no + 1)}>์ฆ๊ฐ€ : {no}</button>
		</>
	);
};

useEffect, useRef, useState ์กฐํ•ฉ var.

  • useEffect ์จ์„œ ๋žœ๋”๋ง ๋  ๋•Œ ์ตœ์†Œ ํ•œ ๋ฒˆ ๋งŒ ๋ฐฐ์—ด ์•ˆ ๋„ฃ์œผ๋ฉด ๋žœ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ๋œ๋‹ค.
  • useRef ์‚ฌ์šฉํ•ด์„œ ์ฆ๊ฐ€ ๋ฒ„ํŠผ ๋ˆ„๋ฅธ ํ›„ ๋‚˜์ด input์œผ๋กœ ํฌ์ปค์‹ฑ
import React, {
	useState,
	useRef,
	useEffect
} from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";

let AppCallCount = 0;

const App = () => {
	AppCallCount++;
	console.log(`1App์ด ${AppCallCount}๋ฒˆ ์‹คํ–‰`);

	const inputNameRef = useRef(null);
	const inputAgeRef = useRef(null);
	const [no, setNo] = useState(0);

	// setTimeout(() => inputNameRef.current.focus());
	
	useEffect(()=>{
		inputNameRef.current.focus();
	},[]);
	
	return (
		<>
			<input ref={inputNameRef} type="text" placeholder="์ด๋ฆ„" />
			<hr />
			<input ref={inputAgeRef} type="number" placeholder="๋‚˜์ด" />
			<hr />
			<button
				onClick={() => {
					setNo(no + 1);
					inputAgeRef.current.focus();
				}}
			>
				์ฆ๊ฐ€ : {no}
			</button>
		</>
	);
	
};

์˜ค๋Š˜์˜ ๊ฐœ๋…

immer์ด๋ž€?

  • react์—์„œ ์‰ฝ๊ฒŒ ๋ถˆ๋ณ€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ์ž‘์„ฑ์„ ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

immer๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ produce ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐœ์ˆ˜์— ๋”ฐ๋ผ ํฌ๊ฒŒ 2๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

2๊ฐœ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” 1๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ์ˆ˜์ •ํ•  ์ƒํƒœ, 2๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ์ด ์ƒํƒœ๋ฅผ ์–ด๋–ป๊ฒŒ ์—…๋ฐ์ดํŠธํ•  ๊ฑด์ง€๋ฅผ ์ •์˜ํ•˜๋Š” recipe ํ•จ์ˆ˜์ด๋‹ค. ๋ณ€๊ฒฝ๋œ ์ƒˆ๋กœ์šด ์ƒํƒœ๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

import produce from 'immer';

const nextState = produce(baseState, (draft) => {
  draft[1].done = true;
  draft.push({ title: 'Tweet about it' });
});

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ์ƒํƒœ๋ฅผ ์–ด๋–ป๊ฒŒ ์—…๋ฐ์ดํŠธํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” recipe ํ•จ์ˆ˜๋งŒ์„ ๋„˜๊ธฐ๋ฉฐ ์ด๋•Œ๋Š” ํ•ด๋‹น ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

import produce from 'immer';

const toggleTodo = produce((draft, id) => {
  const todo = draft.find((todo) => todo.id === id);
  todo.done = !todo.done;
});

const baseState = [ ... ];

const nextState = toggleTodo(baseState, 'Immer');

์ฐธ๊ณ 

immer์˜ ์›๋ฆฌ

  • immer๋Š” ์–ด๋–ค ์›๋ฆฌ๋ฅผ ์ด์šฉํ–ˆ๊ธธ๋ž˜ ๋ถˆ๋ณ€์„ฑ์„ ์œ ์ง€ํ•ด ์ฃผ๋Š” ๊ฒƒ์ผ๊นŒ?

immer์˜ ํ•ต์‹ฌ ์›๋ฆฌ๋Š” Copy-on-write(์ดํ•˜ ๊ธฐ๋ก ์ค‘ ๋ณต์‚ฌ)์™€ Proxy(์ดํ•˜ ํ”„๋ก์‹œ)์— ์žˆ๋‹ค. ๊ธฐ๋ก ์ค‘ ๋ณต์‚ฌ๋ž€ ์ž์›์„ ๊ณต์œ ํ•˜๋‹ค๊ฐ€๋„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ž์›์˜ ๋ณต์‚ฌ๋ณธ์„ ์“ฐ๊ฒŒ ํ•˜๋Š” ๊ฐœ๋…์ด๋‹ค. immer๋Š” ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ ์›๋ณธ ๊ฐ์ฒด์ธ ์ƒํƒœ ๊ฐ์ฒด ๋Œ€์‹  ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋Œ€์‹  ์กฐ์ž‘(๋ณ€๊ฒฝ) ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

immer ์‚ฌ์šฉ๋ฒ•

console.clear();

import React, { useState } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";
import immer, { produce } from "https://cdn.skypack.dev/immer";

const App = () => {
	const [todos, setTodos] = useState([
		{ id: 1, title: "์ œ๋ชฉ1" },
		{ id: 2, title: "์ œ๋ชฉ2" }
	]);

	const addTodo = () => {
		// const newTodos = [...todos, {id:3, title:"์ œ๋ชฉ3"}];
		const newTodos = produce(todos, (draft) => {
			draft.push({ id: 3, title: "์ œ๋ชฉ3" });
		});

		setTodos(newTodos);
	};

	const modifyTodo = () => {
		// const newTodos = todos.map((todo) => todo.id != 1 ? todo : {...todo,title:"zxcv"});
		
		// ์‹ค๋ฌด์ฝ”๋“œ
		setTodos(produce(todos, (draft) => {
			draft[1].title = "zxcv";
		}));
	};

	const removeTodo = () => {
		// const newTodos = todos.filter((todo,index) => index != 1);
		const newTodos = produce(todos, (draft) => {
			draft.splice(1, 1);
		});
		setTodos(newTodos);
	};

	return (
		<>
			todos : {JSON.stringify(todos)}
			<hr />
			<button onClick={addTodo}>์ถ”๊ฐ€</button>
			&nbsp;
			<button onClick={modifyTodo}>์ˆ˜์ •</button>
			&nbsp;
			<button onClick={removeTodo}>์‚ญ์ œ</button>
			&nbsp;
		</>
	);
};

ReactDOM.render(<App />, document.getElementById("root"));

splice๋ž€?

  • splice() ๋ฉ”์„œ๋“œ๋Š” ๋ฐฐ์—ด์˜ ๊ธฐ์กด ์š”์†Œ๋ฅผ ์‚ญ์ œ ๋˜๋Š” ๊ต์ฒดํ•˜๊ฑฐ๋‚˜ ์ƒˆ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฐฐ์—ด์˜ ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•œ๋‹ค.

  • ํ•˜๋‚˜๋„ ์ œ๊ฑฐํ•˜์ง€ ์•Š๊ณ , 2๋ฒˆ ์ธ๋ฑ์Šค์— "drum" ์ถ”๊ฐ€

var myFish = ["angel", "clown", "mandarin", "sturgeon"];
var removed = myFish.splice(2, 0, "drum");

// myFish is ["angel", "clown", "drum", "mandarin", "sturgeon"]
// removed is [], no elements removed
  • ํ•˜๋‚˜๋„ ์ œ๊ฑฐํ•˜์ง€ ์•Š๊ณ , 2๋ฒˆ ์ธ๋ฑ์Šค์— "drum"๊ณผ "guitar" ์ถ”๊ฐ€
var myFish = ["angel", "clown", "mandarin", "sturgeon"];
var removed = myFish.splice(2, 0, "drum", "guitar");

// myFish is ["angel", "clown", "drum", "guitar", "mandarin", "sturgeon"]
// removed is [], no elements removed
  • 3๋ฒˆ ์ธ๋ฑ์Šค์—์„œ ํ•œ ๊ฐœ ์š”์†Œ ์ œ๊ฑฐ
var myFish = ["angel", "clown", "drum", "mandarin", "sturgeon"];
var removed = myFish.splice(3, 1);

// removed is ["mandarin"]
// myFish is ["angel", "clown", "drum", "sturgeon"]
  • 2๋ฒˆ ์ธ๋ฑ์Šค์—์„œ ํ•œ ๊ฐœ ์š”์†Œ ์ œ๊ฑฐํ•˜๊ณ  "trumpet" ์ถ”๊ฐ€
var myFish = ["angel", "clown", "drum", "sturgeon"];
var removed = myFish.splice(2, 1, "trumpet");

// myFish is ["angel", "clown", "trumpet", "sturgeon"]
// removed is ["drum"]
  • ์ œ๊ฑฐํ•˜๋Š” ๋™์‹œ์— ์ƒˆ ๋ฐฐ์—ด
var myFish = ["angel", "clown", "drum", "sturgeon"];
var removed = myFish.splice(2);

// myFish is ["drum", "sturgeon"]

findIndex?

  • findIndex() ํ•จ์ˆ˜๋Š” ๋ฐฐ์—ด์—์„œ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์š”์†Œ๋ฅผ ์ฐพ์•„ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋‹ค.
    ๋ฐฐ์—ด์˜ ๊ฐ ์š”์†Œ์— ๋Œ€ํ•ด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ์กฐ๊ฑด์˜ ์š”์†Œ๋ฅผ ์ฐพ๋Š”๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ๋ฐฐ์—ด์—์„œ ํŠน์ • ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ ์ฐพ๋Š” ๋ฐ ์œ ์šฉํ•˜๋‹ค.

  • ์˜ˆ์‹œ (๋ฐฐ์—ด์—์„œ findIndex() ํ•จ์ˆ˜๋กœ "green"์˜ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ)

// ๋ฐฐ์—ด์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
const colors = ["red", "green", "blue"];

// ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ž‘์„ฑํ•œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜
function findGreen(color) {
    return color === "green"; // ๋ฐฐ์—ด ์š”์†Œ๊ฐ€ "green"๊ณผ ๊ฐ™์€์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
};

// ๋ฐฐ์—ด์—์„œ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ฐพ์•„ ์ธ๋ฑ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
const green = colors.findIndex(findGreen);

// ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
console.log(green); // ์ถœ๋ ฅ: 1

useRef?

  • useRef๋Š” ๋ฆฌ์•กํŠธ ํ›…์˜ ํ•œ ์ข…๋ฅ˜๋กœ, Ref๋Š” reference(์ฐธ์กฐ)์˜ ์ค„์ž„๋ง์ด๋‹ค.
    useRef๋ฅผ ์ด์šฉํ•˜๋ฉด ํŠน์ •ํ•œ DOM์š”์†Œ์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋ฉด, ๋ถˆํ•„์š”ํ•œ ์žฌ๋ Œ๋”๋ง์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•

import { useRef } from 'react';

function MyComponent() {
  const intervalRef = useRef(0);
  const inputRef = useRef(null);
  // ...

๊ณต์‹๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด ์œ„์ฒ˜๋Ÿผ ๊ฐ€์žฅ ์ตœ์ƒ๋‹จ์— useRef๋ฅผ ์„ ์–ธํ•ด์ค„ ๊ฒƒ์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๋‹ค.

useEffect

  • ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋žœ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ํŠน์ • ์ž‘์—…์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” Hook์ด๋‹ค.

useEffect๋Š” component๊ฐ€ mount๋์„ ๋•Œ, component๊ฐ€ update ๋์„ ๋•Œ, ํŠน์ • ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
์ฆ‰, ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์†Œ๋“œ๋ฅผ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

  • useEffect ์‚ฌ์šฉ๋ฒ•

๊ธฐ๋ณธ ํ˜•ํƒœ : useEffect(functionm deps)

  • function : ์ˆ˜ํ–‰ํ•˜๊ณ ์ž ํ•˜๋Š” ์ž‘์—…
  • deps : ๋ฐฐ์—ด ํ˜•ํƒœ์ด๋ฉฐ, ๋ฐฐ์—ด ์•ˆ์—๋Š” ๊ฒ€์‚ฌํ•˜๊ณ ์ž ํ•˜๋Š” ํŠน์ • ๊ฐ’ or ๋นˆ ๋ฐฐ์—ด
import React, {useEffect} from 'react'; 

useEffect ํ•จ์ˆ˜ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

useEffect(() => {
  console.log('๋งˆ์šดํŠธ ๋  ๋•Œ๋งŒ ์‹คํ–‰๋œ๋‹ค.')
}, []);

- ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์— ๊ฐ€์žฅ ์ฒ˜์Œ ๋žœ๋”๋ง ๋  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋Š” deps ์œ„์น˜์— ๋นˆ ๋ฐฐ์—ด์„ ๋„ฃ๋Š”๋‹ค.


useEffect(() => {
  console.log('๋žœ๋”๋ง ๋  ๋•Œ ๋งˆ๋‹ค ์‹คํ–‰๋œ๋‹ค.')
});

- ๋งŒ์•ฝ ๋ฐฐ์—ด์„ ์ƒ๋žตํ•œ๋‹ค๋ฉด ๋ฆฌ๋žœ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋œ๋‹ค.

useEffect(() => {
  console.log(name)
  console.log('์—…๋ฐ์ดํŠธ ๋  ๋•Œ ์‹คํ–‰๋œ๋‹ค.')
}, [name]);

- ํŠน์ •๊ฐ’์ด ์—…๋ฐ์ดํŠธ ๋  ๋•Œ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋Š” deps ์œ„์น˜์˜ ๋ฐฐ์—ด ์•ˆ์— ๊ฒ€์‚ฌํ•˜๊ณ  ์‹ถ์€ ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.
- ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋งŒ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋งˆ์šดํŠธ ๋  ๋•Œ๋„ ์‹คํ–‰๋œ๋‹ค. ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋งŒ ์‹คํ–‰๋˜๊ฒŒ ํ•˜๊ณ  ์‹ถ์œผ๋ฉด

//์ƒ๋žต
const mounted = useRef(false);

useEffect(() => {
  if(!mounted.current){
    mounted.current = true;
  }else{
  //ajex
  }
},[๋ฐ”๋€Œ๋Š” ๊ฐ’]);
//์ƒ๋žต

- ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ ๋  ๋•Œ๋Š” if ๋ฌธ์—์„œ ์•„๋ฌด๊ฒƒ๋„ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  mounted ๊ฐ’๋งŒ ๋ฐ”๊ฟ”์ฃผ๊ณ ,
else์—์„œ ๋ฐฐ์—ด ์•ˆ์— ์žˆ๋Š” ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด, ajex ์„œ๋ฒ„ ํ†ต์‹ ์ด๋ผ๋˜์ง€ ์›ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.


useEffect(() => {
  console.log('effect')
  console.log(name)
  return () => {
    console.log('cleanup');
    console.log(name)
  };
}, []);

- cleanup ํ•จ์ˆ˜ ๋ฐ˜ํ™˜ (return ๋’ค์— ๋‚˜์˜ค๋Š” ํ•จ์ˆ˜์ด๋ฉฐ, useEffect์— ๋Œ€ํ•œ ๋’ท์ •๋ฆฌ ํ•จ์ˆ˜)
- ์–ธ๋งˆ์šดํŠธ ๋  ๋•Œ๋งŒ cleanup ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ :๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋นˆ ๋ฐฐ์—ด์„ ๋„ฃ๋Š”๋‹ค.
- ํŠน์ •๊ฐ’์ด ์—…๋ฐ์ดํŠธ ๋˜๊ธฐ ์ง์ „์— cleanup ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ :deps ๋ฐฐ์—ด ์•ˆ์— ๊ฒ€์‚ฌํ•˜๊ณ  ์‹ถ์€ ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.

deps์— ํŠน์ • ๊ฐ’ ๋„ฃ๊ธฐ

  • deps์— ํŠน์ • ๊ฐ’์„ ๋„ฃ๊ฒŒ ๋œ๋‹ค๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋งˆ์šดํŠธ ๋  ๋•Œ, ์ง€์ •ํ•œ ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ, ์–ธ๋งˆ์šดํŠธ ๋  ๋•Œ, ๊ฐ’์ด ๋ฐ”๋€Œ๊ธฐ ์ง์ „์— ๋ชจ๋‘ ํ˜ธ์ถœ์ด ๋œ๋‹ค.

  • useEffect ์•ˆ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ƒํƒœ๋‚˜, props๊ฐ€ ์žˆ๋‹ค๋ฉด, useEffect์˜ deps์— ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด ๊ทœ์น™์ด๋‹ค.

  • ๋งŒ์•ฝ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ’์„ ๋„ฃ์–ด์ฃผ์ง€ ์•Š๋Š”๋‹ค๋ฉด, useEffect์•ˆ์˜ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์ตœ์‹  ์ƒํƒœ, props๋ฅผ ๊ฐ€๋ฆฌํ‚ค์ง€ ์•Š๋Š”๋‹ค.

  • deps ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ƒ๋žตํ•œ๋‹ค๋ฉด, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋žœ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค useEffect ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€