프론트엔드 스터디 11주차: React 기초 2 —.
useEffect로 사이드 이펙트를 다루고, 이벤트 처리와 폼 상태 관리까지 — React로 실제 동작하는 앱을 만들기 위한 핵심 기술을 배웁니다.
Section 7 (배열 메서드, 고차함수, 스프레드·디스트럭처링) 챕터 — 6:25:21부터
딥다이브 요약 GitHub
안녕하세요. 9주차입니다. 다음 주부터 React를 시작합니다. React 코드를 처음 열면 거의 모든 줄에서 이번 주에 다루는 문법들이 등장합니다. 개념을 암기하기보다는 언제, 왜 쓰는지에 집중하며 읽어보세요. 이번 주 내용을 자연스럽게 읽을 수 있다면, 다음 주 React 코드가 훨씬 편하게 느껴질 것입니다.
이번 주 대면에서는 Next.js 렌더링 진화와 웹 퍼포먼스를 다룹니다. 비대면에서 배우는 map/filter, 구조 분해, 스프레드, 모듈 문법은 React 컴포넌트를 읽기 위한 기초이면서, Next.js에서 서버 컴포넌트와 클라이언트 컴포넌트를 나누고 데이터를 화면에 연결할 때도 계속 등장합니다. 문법을 외우기보다, 이 문법들이 데이터를 UI로 바꾸는 과정에서 어떤 역할을 하는지 같이 보면 좋습니다.
아래 10문제를 막힘없이 풀 수 있다면,
이번 주 학습 자료를 생략하거나, 모르는 부분만 선택적으로 읽으셔도 좋습니다.
바로 하단의 본문 섹션들을 확인해보세요.
고차함수(Higher-Order Function)는 함수를 인자로 받거나 반환하는 함수입니다. 배열 메서드 중 React에서 가장 자주 사용하는 세 가지를 살펴봅니다.
map은 배열의 각 요소에 콜백 함수를 적용하고,
변환된 값으로 이루어진 새 배열을 반환합니다. 원본 배열은
변경되지 않습니다.
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에서는 배열 데이터를 화면에 렌더링할 때 map을 사용합니다. 아래 코드는
다음 주부터 매일 보게 될 패턴입니다.
// React에서 리스트 렌더링 — 다음 주부터 이 패턴을 매일 씁니다
function UserList({ users }) {
return (
<ul>
{users.map((user) => (
<UserCard key={user.id} {...user} />
))}
</ul>
);
}
filter는 콜백이 true를 반환하는 요소만 모아 새 배열을
반환합니다.
const products = [
{ name: "사과", price: 1000 },
{ name: "한우", price: 50000 },
{ name: "배", price: 2000 },
];
// 1만원 이하 상품만
const affordable = products.filter((p) => p.price <= 10000);
// [{ name: "사과", ... }, { name: "배", ... }]
reduce는 배열의 각 요소를 순회하며 하나의 값으로 누적합니다. 합계 계산,
그룹핑 등에 사용됩니다.
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 }
map, filter, reduce는 모두 새 배열(또는 값)을 반환하므로, 점(.)으로
이어서 조합할 수 있습니다.
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.8
const 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 — 포함 여부
구조 분해 할당(Destructuring)은 배열이나 객체에서 원하는 값을 변수로 한 번에 꺼내는 문법입니다. React에서 props, useState, API 응답 처리 등 어디서나 사용합니다.
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가 바로 배열 구조 분해의 대표적인 사용 예입니다. 다음 주에
가장 먼저 만나게 될 패턴입니다.
// React useState — 배열 구조 분해의 핵심 활용
const [count, setCount] = useState(0);
// count = 현재 상태값, setCount = 상태를 변경하는 함수
React 컴포넌트의 props를 받을 때 가장 자주 사용하는 패턴입니다.
// React 컴포넌트 — 파라미터에서 바로 구조 분해
function Card({ title, description, onClick }) {
return (
<div onClick={onClick}>
<h2>{title}</h2>
<p>{description}</p>
</div>
);
}
// 사용할 때
<Card title="인사" description="안녕하세요" onClick={handleClick} />;
스프레드(...)는 배열이나 객체를 펼치는 문법이고, Rest는
나머지를 모으는 문법입니다. 같은 ... 기호를 사용하지만
역할이 다릅니다.
// 배열 복사 & 합치기
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에서 상태를 업데이트할 때, 기존 객체를 직접 수정하지 않고 스프레드로 새 객체를 만드는 패턴을 사용합니다. 이것이 React의 불변 업데이트입니다.
// 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]
Rest 패턴은 React에서 특정 props만 꺼내고 나머지를 하위 컴포넌트에 전달할 때 유용합니다.
// className만 꺼내고, 나머지 props는 button에 전달
function Button({ className, ...rest }) {
return <button className={`btn ${className}`} {...rest} />;
}
API에서 받아온 데이터는 특정 필드가 null이거나 undefined일 수 있습니다.
이때 안전하게 접근하기 위한 두 가지 연산자를 알아봅니다.
?.는 앞의 값이 null 또는 undefined이면
에러 없이 undefined를 반환합니다.
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일 때만 오른쪽 기본값을 반환합니다.
||와의 차이가 중요합니다.
// ?? — 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가 아니므로
실무에서 0이나 빈 문자열("")이 유효한 값인 경우가 많습니다. 이때 ||를
쓰면 의도치 않게 기본값으로 대체되므로,
??를 사용하는 것이 안전합니다.
모듈은 파일 단위로 코드를 분리하고, 필요한 것만 import/export하는
시스템입니다. React 프로젝트는 전부 모듈 기반으로 작성됩니다. 하나의
컴포넌트가 하나의 파일이 되는 구조입니다.
// 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"; // 이름 자유롭게 지정 가능
React 프로젝트에서는 일반적으로 컴포넌트는 default export, 유틸 함수나 상수는 named export로 내보냅니다. 실제 React 코드를 열면 이런 구조를 볼 수 있습니다.
// 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/catch는 이런 에러를 잡아서 앱이 멈추지 않도록 처리하는 문법입니다.
try {
// 에러가 발생할 수 있는 코드
const result = JSON.parse("잘못된 JSON");
} catch (error) {
// 에러 발생 시 실행되는 코드
console.error("파싱 실패:", error.message);
} finally {
// 에러 여부와 관계없이 항상 실행 (선택사항)
console.log("처리 완료");
}
API 호출은 비동기(async)로 이루어지므로, async/await와 try/catch를 함께
사용합니다. 이 패턴은 React에서 데이터를 불러올 때 기본이 됩니다.
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 안에서 API를 호출하고, try/catch로 에러를
처리합니다. 아래 패턴은 다음 주에 React를 배우면서 직접 작성하게 됩니다.
// 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, 구조 분해, 스프레드 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!
useEffect로 사이드 이펙트를 다루고, 이벤트 처리와 폼 상태 관리까지 — React로 실제 동작하는 앱을 만들기 위한 핵심 기술을 배웁니다.
지금 당장 읽지 않아도 됩니다. React를 배우면서 에러 처리가 필요해지거나 면접 대비가 필요할 때 참고하세요.
React를 처음 시작합니다. 컴포넌트, JSX, props, useState까지 — React 앱을 만들기 위한 핵심 네 가지를 한 번에 배웁니다.
React를 배우면서 this가 헷갈리거나, 면접 대비가 필요할 때 참고하는 JS 심화 자료입니다. 이 내용을 지금 당장 완벽히 이해하지 않아도 React를 배우는 데 지장이 없습니다.
Next.js가 무엇이고 왜 필요한지 React와 비교해 이해한 뒤, CSR부터 SSR, SSG, ISR, PPR까지 렌더링 전략과 Web Vitals를 정리하며 스터디를 마무리합니다.
다음 주부터 React를 시작합니다. React 코드에서 매 줄 등장하는 map/filter, 구조 분해, 스프레드, 옵셔널 체이닝, 모듈까지 — React 코드를 막힘없이 읽기 위한 JS 문법을 한 번에 정리합니다.
비동기와 async/await를 배운 뒤, 대면에서는 API를 단순 호출 방법이 아니라 프론트엔드와 백엔드 사이의 계약으로 바라봅니다. HTTP, REST, CORS, 프록시, API 클라이언트 구조, TanStack Query와 파일 배치까지 연결합니다.
JS 비동기 처리의 흐름을 배웁니다. 클로저가 왜 중요한지, 콜백 지옥을 해결한 Promise, 그리고 현대 JS의 표준인 async/await까지 이어지는 흐름을 이해합니다.
바닐라 JS의 한계에서 React가 등장한 이유까지, 컴포넌트와 렌더링 방식을 이해하고 프로젝트 구조 감각을 만들어가는 대면입니다.
JS에서 가장 낯선 개념 중 하나인 프로토타입 기반 상속을 다룹니다. const가 왜 불변을 보장하지 않는지, 타입을 런타임에 정확히 확인하는 방법까지 함께 배웁니다.
JavaScript에서 거의 모든 것은 객체입니다. 함수가 일급 객체라는 의미, var와 let의 스코프 차이, 그리고 렉시컬 스코프까지 다룹니다.
4~6주차 공백 이후 진행하는 첫 번째 대면입니다. HTML/CSS/JS 핵심을 가볍게 다시 연결하고, 브라우저가 코드를 실제 화면으로 바꾸는 과정을 통해 렌더링 파이프라인과 성능 감각을 잡습니다.
길어진 6주차 대면의 두 번째 차시입니다. AI 시대에 개발자가 실제로 어떻게 일하는지, 그리고 프론트엔드 개발자가 디자이너·백엔드·PM 사이에서 어떤 연결과 리딩 역할을 하는지 다룹니다.
JavaScript를 처음 제대로 배우는 주차입니다. var/let/const의 차이, JS만의 독특한 타입 시스템, 그리고 호이스팅·TDZ까지 다룹니다.
배경 이미지를 제어하고, Position으로 요소를 원하는 위치에 배치하며, Flexbox로 유연한 가로·세로 레이아웃을 구성합니다. 요소를 화면 정중앙에 띄우거나, 내비게이션 바처럼 가로로 예쁘게 정렬하는 것이 이번 주 목표입니다.
3주차 박스 모델과 스타일링 복습, 과제 풀이, 4주차 Position과 Flexbox 레이아웃까지 다룬 대면 스터디 정리입니다. 면접에서 자주 나오는 마진 겹침, box-sizing, Flexbox 관련 질문도 함께 준비합니다.
폰트와 색상을 다루고, 인라인과 블록 요소의 차이를 이해하며, 마진·패딩으로 구성되는 박스 모델을 완벽하게 익힙니다. 요소들 사이의 여백을 자유자재로 제어하고 화면이 찌그러지지 않게 박스 크기를 다루는 것이 이번 주 목표입니다.
2주차 폼과 CSS 선택자 복습, 과제 풀이, 3주차 박스 모델과 스타일링까지 다룬 대면 스터디 정리입니다. 면접에서 자주 나오는 GET vs POST, CSS 우선순위, 박스 모델 관련 질문도 함께 준비합니다.
사용자 입력을 받는 폼(Form)을 만들고, CSS를 HTML에 연결하는 방법과 원하는 요소를 정확히 선택하는 CSS 선택자 규칙을 배웁니다. 로그인·회원가입 폼을 직접 만들고, CSS로 원하는 요소를 콕 집어내는 것이 이번 주 목표입니다.
1주차 HTML 핵심 복습과 과제 풀이, 2주차 폼(Form)과 CSS 선택자까지 다룬 대면 스터디 정리입니다. 면접에서 자주 나오는 시맨틱 HTML, img alt 속성 관련 질문도 함께 준비합니다.
기초 태그 강의에서 한 걸음 더 나아갑니다. 카카오톡 공유 썸네일, 모바일 데이터 절약, 스크린 리더기를 위한 숨은 정보 등 실무에서 마주하게 될 중급 HTML 개념들을 가볍게 훑어봅니다.
프론트엔드 개발자가 실무에서 하는 일, 2025-2026 기술 스택, 현실적인 연봉과 채용 트렌드를 정리했습니다. 스터디 자료 구조와 앞으로의 방향도 함께 안내합니다.
스터디 커리큘럼 방향을 정하기 위한 세 번째 사전 진단 퀴즈입니다. 자바스크립트의 스코프, 참조 타입의 메모리 할당, DOM 제어와 이벤트 흐름 등 React 실무의 근간이 되는 CS 지식을 점검합니다.
스터디 커리큘럼 방향을 정하기 위한 마지막 사전 진단 퀴즈입니다. React 컴포넌트를 다루기 위해 필수적인 배열 고차 함수(map, filter)와 비동기 통신(async/await) 능력을 점검합니다.
스터디 커리큘럼 방향을 정하기 위한 마지막 사전 진단 퀴즈입니다. 선언적 UI, 상태(State), 단방향 데이터 흐름, 부수 효과(Side Effect) 등 React 생태계 진입을 위한 필수 개념을 점검합니다.
스터디 커리큘럼 방향을 정하기 위한 첫 번째 사전 진단 퀴즈입니다. HTML 태그 구조부터 CSS 박스 모델까지, 실무와 React 환경에서 이 기초들이 왜 중요한지 점검합니다.
스터디 커리큘럼 방향을 정하기 위한 두 번째 사전 진단 퀴즈입니다. 시맨틱 마크업, 폼 이벤트 제어, CSS 우선순위, Flexbox 레이아웃 등 실전 활용 능력을 점검합니다.