[React] 6일차

김민지·2026년 4월 28일

kosa-Spring

목록 보기
11/13

React 강의 필기 및 복습 노트

useRef

  • useRef() Hook을 이용해 DOM을 참조
  • 리턴이 실행되면서 DOM이 생성된다
  • React나 Vue에서는 가능하면 <form> 사용 자제 → 서버 제출 시 깨질 수 있음

레이아웃

루트 레이아웃 & 중첩 레이아웃

  • 중첩 레이아웃: app/폴더/layout.js
  • 폴더가 layout.js 파일을 가질 경우 중첩 레이아웃이 됨
  • 하위 페이지 컴포넌트들은 중첩 레이아웃에 렌더링됨

layout.js 작성 방법

  • 함수형 컴포넌트로 작성
  • props를 통해 자식 컴포넌트 전달

Ch03. Props & State

Props

Props란?

  • 컴포넌트 태그의 속성들로 구성된 객체
  • 부모 컴포넌트에서 생성하며 자식 컴포넌트로 전달
  • 자식 컴포넌트는 읽기 전용
  • 문자열 외의 값은 {}를 사용
  • 숫자를 "19"로 쓰면 문자열 타입으로 전달됨. 문자열도 {}로 감쌀 수 있음

예시 코드

개념: 부모 컴포넌트가 자식 컴포넌트에게 name, version 속성을 props로 전달하고,

자식 컴포넌트는 구조 분해 할당으로 받아서 렌더링하는 기본 props 전달 패턴

부모 컴포넌트 — props를 넘겨주는 쪽

import ChildComponent from "./ChildComponent";

function Exam01Props() {
  return (
    <div className="card">
      <div className="card-header">Exam01Props</div>
      <div className="card-body">
        {/* name은 문자열, version은 숫자({}로 감쌈), 태그 사이 내용은 children으로 전달됨 */}
        <ChildComponent name="React" version={19}>
          <div>자식 내용입니다.</div>
        </ChildComponent>
      </div>
    </div>
  );
}

export default Exam01Props;

props 객체 구조 — 자식 컴포넌트가 실제로 받는 객체 형태

{
  name: "React",       // 문자열
  version: 19,         // 숫자 ({}로 전달했기 때문)
  children: "<div>자식 내용입니다.</div>"  // 태그 사이 내용은 자동으로 children에 담김
}

자식 컴포넌트 — props를 받아서 사용하는 쪽

// 매개변수에서 구조 분해 할당으로 props를 바로 꺼내 씀
// version=18 → 부모에서 version을 안 넘겨주면 기본값 18로 사용
function ChildComponent({ name, version = 18, children }) {
  return (
    <div className="card">
      <div className="card-header">ChildComponent</div>
      <div className="card-body">
        <div>프레임워크: {name}</div>
        <div>버전: {version}</div>
        <div>버전 타입: {typeof version}</div>  {/* 숫자로 전달됐는지 확인 */}
        {children}  {/* 부모 태그 사이에 작성한 내용이 여기 렌더링됨 */}
      </div>
    </div>
  );
}

export default ChildComponent;

State (컴포넌트 상태)

State란?

  • 컴포넌트가 사용자와 상호작용하면서 내부에서 기억해야 할 값
  • 상태가 변경되면 해당 컴포넌트가 리렌더링됨 (해당 DOM 부분만 재생성)
  • 상태를 만든다 = useState를 사용한다
  • 상태를 가지는 컴포넌트는 무조건 클라이언트 컴포넌트"use client" 필요

왜 일반 변수가 아닌 State를 써야 할까?

  • 일반 변수는 처음 실행될 때만 값이 초기화되고, 이후 리렌더링 시 값이 유지되지 않음
  • 상태를 써야 UI가 새로고침될 때 변경 사항이 반영됨

State 특징

  • 상태 타입: 숫자, 문자열, 객체, 배열 모두 가능
  • 반드시 상태 변경 함수를 통해 변경 (직접 수정 금지)
    • 상태 변경 함수는 상태를 변경하고 React에게 변경을 알리는 역할
  • 객체/배열 상태는 완전히 새로운 객체/배열로 제공 (원본 보존)
    • React는 참조 비교를 통해 상태 변경을 감지하기 때문
  • Setter는 비동기적으로 업데이트
    • Setter 호출 후 바로 변경된 상태값을 읽을 수 없음
    • 상태 변경 후 자동 콜백되는 함수를 별도로 추가할 수 있음 (useEffect 활용)
  • 연이어 여러 번 Setter를 호출할 경우 마지막 Setter만 적용
    • 호출 직후 변경된 상태값을 읽을 수 없기 때문에 여러 번 실행하는 것은 의미가 없음
  • 연이어 여러 번의 Setter를 적용하려면 Setter의 매개값으로 함수를 전달
    • 전달된 함수는 순차적으로 변경된 상태값을 매개값으로 받고 실행됨

객체 타입 State 예시

개념: 상태가 객체일 때 스프레드 연산자(...)로 기존 값을 복사한 뒤,

변경할 속성만 덮어씌워 완전히 새로운 객체로 setState하는 패턴

"use client";

import { useState } from "react";

function Exam03ObjectTypeState() {
  // 상태를 객체로 정의 - number와 color를 하나의 상태로 묶어서 관리
  const [state, setState] = useState({
    number: 0,
    color: "black",
  });

  const addNumber = () => {
    // ...state로 기존 객체 복사 후, number만 +1한 새 객체 생성
    // color는 건드리지 않고 그대로 유지됨
    setState({ ...state, number: state.number + 1 });
  };

  const changeColor = () => {
    // ...state로 기존 객체 복사 후, color만 랜덤값으로 덮어씀
    // number는 건드리지 않고 그대로 유지됨
    setState({ ...state, color: getRandomColor() });
  };

  // 랜덤 hex 색상 생성 함수
  // Math.random() → 0~1 사이 소수
  // parseInt("ffffff", 16) → 16진수 ffffff를 10진수로 변환 (= 16777215)
  // 곱한 후 floor로 정수화 → toString(16)으로 다시 16진수 문자열로 변환
  const getRandomColor = () => {
    return "#" + Math.floor(Math.random() * parseInt("ffffff", 16)).toString(16);
  };

  return (
    <div className="card mt-2">
      <div className="card-header">Exam03ObjectTypeState</div>
      <div className="card-body">
        <button onClick={addNumber} className="btn btn-info btn-sm me-2">숫자 증가</button>
        <button onClick={changeColor} className="btn btn-info btn-sm me-2">색깔 변경</button>
        <hr />
        {/* state.color를 인라인 스타일로 적용, state.number를 텍스트로 출력 */}
        <h3 style={{ color: state.color }}>{state.number}</h3>
      </div>
    </div>
  );
}

export default Exam03ObjectTypeState;

이벤트 처리 & 동적 키 업데이트

개념: 여러 input을 하나의 핸들러 함수로 통합 관리하는 패턴.

event.target을 이용해 어떤 input에서 이벤트가 발생했는지 판별한다.

event.target이란?

이벤트가 실제로 발생한 DOM 요소를 가리킨다.

<input> 태그에 onChange가 발생하면 event.target은 그 <input> 요소 자체다.

속성의미예시
event.target이벤트가 발생한 DOM 요소<input name="age" value="25">
event.target.name그 요소의 name 속성값"age"
event.target.value그 요소의 현재 입력값"25"
// input 태그에 name 속성을 붙여두면
// 어떤 input에서 이벤트가 발생했는지 event.target.name으로 구별 가능
const handleChange = (event) => {
  console.log(`${event.target.name}: ${event.target.value}`);
  // 예: "age: 25", "username: hello" 처럼 출력됨
};

동적 키로 객체 상태 업데이트

// [event.target.name] → 대괄호 안에 변수를 넣으면 그 변수의 값이 키로 사용됨
// event.target.name이 "age"이면 → { ...user, age: "25" } 와 동일
setUser({ ...user, [event.target.name]: event.target.value });

// 비교: 대괄호 없이 쓰면 "event.target.name" 이라는 문자열 그대로 키가 됨 (잘못된 사용)
// setUser({ ...user, event.target.name: event.target.value });  ❌

실제 사용 예시 — input이 여러 개여도 핸들러 함수 하나로 처리

// name 속성이 상태 객체의 키와 일치해야 함
<input name="username" onChange={handleChange} />
<input name="age"      onChange={handleChange} />
<input name="email"    onChange={handleChange} />

앞으로 form 입력 데이터는 상태로 관리하기!


반복 업데이트: addNumber1 vs addNumber2

개념: Setter가 비동기로 동작하기 때문에 같은 상태를 연속으로 여러 번 바꾸려면

값 대신 함수를 전달해야 한다.

function Exam05RepeatUpdate() {
  const [number, setNumber] = useState(0);

  // ❌ 값 전달 방식 - 두 번 호출해도 +1만 됨
  // setNumber가 비동기라 두 번째 호출 시점에도 number는 아직 이전 값
  // → setNumber(0 + 1), setNumber(0 + 1) 로 동일하게 실행됨
  const addNumber1 = () => {
    setNumber(number + 1);
    setNumber(number + 1);
  };

  // ✅ 함수 전달 방식 - 두 번 호출하면 +2 됨
  // React가 함수들을 큐에 쌓아 순서대로 실행
  // prevNumber는 직전 업데이트가 반영된 최신값을 보장
  // → setNumber(0→1), setNumber(1→2) 순차 실행
  const addNumber2 = () => {
    setNumber(prevNumber => prevNumber + 1);
    setNumber(prevNumber => prevNumber + 1);
  };
}
방식설명
addNumber1number를 그 시점의 값으로 읽어 업데이트 → 두 번 호출해도 +1만 됨
addNumber2함수를 전달 → 차곡차곡 쌓여 순차적으로 업데이트 → +2 됨

관련 Hook 정리

Hook설명
useState()상태 생성 및 초기화
useEffect()상태 변경 후 콜백 함수 등록
useRef()DOM 직접 참조

Hook: use로 시작하며 컴포넌트의 상태, 생명주기 등을 함수형 컴포넌트에서 사용할 수 있게 해주는 특별한 함수

profile
안녕하세요

0개의 댓글