프론트엔드 스터디 11주차: React 기초 2 —.
useEffect로 사이드 이펙트를 다루고, 이벤트 처리와 폼 상태 관리까지 — React로 실제 동작하는 앱을 만들기 위한 핵심 기술을 배웁니다.
<strong>입문 강의 (처음이라면):</strong> 얄코 JS 입문 강의
<strong>강의 스킵 가능:</strong> 아래 스킵 진단 문제 10개를 모두 맞히면 강의
시청 스킵 가능합니다.
<strong>심화 자료 (딥다이브 대비):</strong>
---
---
const numbers = [1, 2, 3, 4, 5];
// 각 요소를 2배로
const doubled = numbers.map((n) => n * 2);
// [2, 4, 6, 8, 10]
// 객체 배열에서 특정 값만 추출
const users = [
{ id: 1, name: "철수" },
{ id: 2, name: "영희" },
];
const names = users.map((user) => user.name);
// ["철수", "영희"]// React에서 리스트 렌더링 — 다음 주부터 이 패턴을 매일 씁니다
function UserList({ users }) {
return (
<ul>
{users.map((user) => (
<UserCard key={user.id} {...user} />
))}
</ul>
);
}const products = [
{ name: "사과", price: 1000 },
{ name: "한우", price: 50000 },
{ name: "배", price: 2000 },
];
// 1만원 이하 상품만
const affordable = products.filter((p) => p.price <= 10000);
// [{ name: "사과", ... }, { name: "배", ... }]const cart = [
{ name: "사과", price: 1000, qty: 2 },
{ name: "배", price: 2000, qty: 1 },
];
// 총 금액 계산
const total = cart.reduce((acc, item) => acc + item.price * item.qty, 0);
// 1000*2 + 2000*1 = 4000
// reduce로 객체 만들기 (그룹핑)
const byName = cart.reduce((acc, item) => {
acc[item.name] = item.price;
return acc;
}, {});
// { 사과: 1000, 배: 2000 }const scores = [85, 42, 93, 67, 55, 78, 91];
// 70점 이상인 점수만 골라 평균 구하기
const passing = scores.filter((s) => s >= 70); // [85, 93, 67, 78, 91]
const avg = passing.reduce((sum, s) => sum + s, 0) / passing.length;
// 82.8const arr = [1, 2, 3, 4, 5];
arr.find((n) => n > 3); // 4 — 조건에 맞는 첫 번째 요소
arr.findIndex((n) => n > 3); // 3 — 조건에 맞는 첫 번째 인덱스
arr.some((n) => n > 4); // true — 하나라도 조건 충족
arr.every((n) => n > 0); // true — 모두 조건 충족
arr.includes(3); // true — 포함 여부---
const user = { name: "철수", age: 25, role: "admin" };
// 원하는 프로퍼티만 변수로 추출
const { name, age } = user;
// name="철수", age=25
// 다른 이름으로 받기
const { name: userName } = user;
// userName="철수"
// 기본값 지정 — 프로퍼티가 없으면 기본값 사용
const { name = "익명" } = {};
// name="익명"
// 중첩 객체 구조 분해
const response = { data: { user: { name: "철수" } } };
const {
data: {
user: { name: apiUserName },
},
} = response;
// apiUserName="철수"const [a, b, c] = [1, 2, 3];
// a=1, b=2, c=3
// 일부만 추출 (건너뛰기)
const [first, , third] = [10, 20, 30];
// first=10, third=30
// 기본값
const [x = 0, y = 0] = [5];
// x=5, y=0// React useState — 배열 구조 분해의 핵심 활용
const [count, setCount] = useState(0);
// count = 현재 상태값, setCount = 상태를 변경하는 함수// React 컴포넌트 — 파라미터에서 바로 구조 분해
function Card({ title, description, onClick }) {
return (
<div onClick={onClick}>
<h2>{title}</h2>
<p>{description}</p>
</div>
);
}
// 사용할 때
<Card title="인사" description="안녕하세요" onClick={handleClick} />;---
// 배열 복사 & 합치기
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// 배열에 새 요소 추가 (원본 변경 없이)
const newArr = [...arr1, 7]; // [1, 2, 3, 7]// React 상태 업데이트 — 스프레드로 새 객체 만들기
const [user, setUser] = useState({ name: "철수", age: 25 });
// age만 변경한 새 객체 생성
setUser((prev) => ({ ...prev, age: prev.age + 1 }));
// { name: "철수", age: 26 } — 새 객체, 원본은 그대로
// 배열 상태에 항목 추가
const [items, setItems] = useState(["사과", "배"]);
setItems((prev) => [...prev, "포도"]);
// ["사과", "배", "포도"]// 함수의 나머지 인자 모으기
function sum(...nums) {
return nums.reduce((acc, n) => acc + n, 0);
}
sum(1, 2, 3, 4); // 10
// 구조 분해에서 나머지 모으기
const { id, ...rest } = { id: 1, name: "철수", age: 25 };
// id=1, rest={ name: "철수", age: 25 }
const [head, ...tail] = [1, 2, 3, 4, 5];
// head=1, tail=[2, 3, 4, 5]// className만 꺼내고, 나머지 props는 button에 전달
function Button({ className, ...rest }) {
return <button className={`btn ${className}`} {...rest} />;
}---
const user = null;
// 기존 방식 — 매번 null 체크를 해야 함
const city = user && user.address && user.address.city;
// 옵셔널 체이닝 — 간결하게
const city2 = user?.address?.city; // undefined (에러 없음)
// 메서드 호출에도 사용 가능
user?.greet?.(); // user가 null이면 undefined, 에러 없음
// 배열 요소 접근에도 사용 가능
const first = arr?.[0]; // arr이 null이면 undefined// ?? — null/undefined일 때만 기본값
const name = user?.name ?? "익명";
// || vs ?? 차이 — 이 차이를 반드시 이해하세요
const count1 = 0 || "기본값"; // "기본값" — 0은 falsy이므로
const count2 = 0 ?? "기본값"; // 0 — null/undefined가 아니므로
const text1 = "" || "기본값"; // "기본값" — 빈 문자열은 falsy
const text2 = "" ?? "기본값"; // "" — null/undefined가 아니므로---
// math.js — 유틸 함수는 named export
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// 다른 파일에서 import — 중괄호 필수
import { add, subtract } from "./math.js";
// 이름 변경도 가능
import { add as sum } from "./math.js";// Button.jsx — React 컴포넌트는 보통 default export
export default function Button({ label }) {
return <button>{label}</button>;
}
// 다른 파일에서 import — 중괄호 없이, 원하는 이름으로
import Button from "./Button.jsx";
import MyButton from "./Button.jsx"; // 이름 자유롭게 지정 가능// components/UserCard.jsx — 컴포넌트 (default export)
import { formatDate } from "../utils/date";
import styles from "./UserCard.module.css";
export default function UserCard({ name, createdAt }) {
return (
<div className={styles.card}>
<h3>{name}</h3>
<span>{formatDate(createdAt)}</span>
</div>
);
}// utils/date.js — 유틸 함수 (named export)
export function formatDate(date) {
return new Date(date).toLocaleDateString("ko-KR");
}
export function isToday(date) {
return new Date(date).toDateString() === new Date().toDateString();
}---
try {
// 에러가 발생할 수 있는 코드
const result = JSON.parse("잘못된 JSON");
} catch (error) {
// 에러 발생 시 실행되는 코드
console.error("파싱 실패:", error.message);
} finally {
// 에러 여부와 관계없이 항상 실행 (선택사항)
console.log("처리 완료");
}async function fetchUser(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP 에러: ${response.status}`);
}
const user = await response.json();
return user;
} catch (error) {
console.error("사용자 조회 실패:", error.message);
return null;
}
}// React useEffect에서의 데이터 fetching — 미리 눈에 익혀두세요
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function loadUser() {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
loadUser();
}, [userId]);
if (loading) return <p>로딩 중...</p>;
if (error) return <p>에러: {error}</p>;
return <h1>{user?.name}</h1>;
}---
`const numbers = [1, 2, 3, 4, 5];
const result = numbers
.filter((n) => n % 2 !== 0)
.map((n) => n \* n);
console.log(result);`---
Post Q&A
프론트엔드 스터디 9주차: React 입문 전 필수 JS 문법 — map, 구조 분해, 스프레드 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!
지금 당장 읽지 않아도 됩니다. React를 배우면서 에러 처리가 필요해지거나 면접 대비가 필요할 때 참고하세요.