useState vs useRef vs let: 언제 무엇을 써야 할까?

2025. 8. 21. 14:31·Frontend Development
728x90

의의와 배경

useRef는 리렌더링 간에도 유지되는 가변 저장소를 제공한다. 값 변경이 렌더를 유발하지 않는다는 점에서 useState와 구분되며, 컴포넌트 인스턴스마다 독립적으로 유지된다는 점에서 단순 let과도 다르다. 주로 DOM 접근, 타이머/외부 핸들 저장, 최신 값 보관 등 렌더와 무관한 정보를 관리할 때 사용한다.


목차

  • 개념 정리: 렌더링 모델과 ref.current
  • 비교: useState vs useRef vs let
  • 사용 시나리오와 코드 예시
  • 주의사항(안티패턴)
  • 체크리스트
  • 결론

개념 정리

  • useRef<T>(initial)는 { current: T } 형태의 객체를 반환한다.
  • ref.current를 변경해도 컴포넌트는 리렌더링되지 않는다.
  • 같은 컴포넌트 인스턴스에서 렌더 간 동일한 ref 객체가 유지된다.
  • 의도: “렌더 출력에 직접 참여하지 않는, 그러나 렌더 사이에 유지돼야 하는 값”을 담는다.

비교: 언제 무엇을 쓰나

항목 useState useRef let(컴포넌트 내부)
렌더 트리거 값 변경 시 렌더 렌더 없음 렌더마다 초기화됨
값 유지(렌더 간) O O X
인스턴스별 독립성 O O O(내부이지만 매 렌더 초기화)
주 용도 UI에 반영돼야 하는 상태 렌더와 무관한 가변 값, DOM 핸들 순수 계산용 임시 변수

주의: 컴포넌트 “외부 모듈 스코프”에 let을 두면 모든 인스턴스가 공유하므로 권장하지 않는다.


사용 시나리오와 코드 예시

1) DOM 접근과 제어

import { useRef } from 'react';

export default function FocusInput() {
  const inputRef = useRef<HTMLInputElement | null>(null);
  return (
    <div>
      <input ref={inputRef} />
      <button onClick={() => inputRef.current?.focus()}>포커스</button>
    </div>
  );
}

2) 타이머/외부 핸들 저장(클린업 용이)

import { useEffect, useRef } from 'react';

export default function Ticker() {
  const intervalId = useRef<ReturnType<typeof setInterval> | null>(null);
  useEffect(() => {
    intervalId.current = setInterval(() => {
      console.log('tick');
    }, 1000);
    return () => {
      if (intervalId.current) clearInterval(intervalId.current);
    };
  }, []);
  return <div>running...</div>;
}

3) 렌더를 유발하지 않는 카운터/측정값

import { useRef } from 'react';

export default function ClickCounter() {
  const clicks = useRef(0);
  return (
    <button onClick={() => { clicks.current += 1; console.log(clicks.current); }}>
      클릭(화면 숫자는 안 바뀜)
    </button>
  );
}

4) 최신 값 보관으로 stale closure 방지

import { useEffect, useRef } from 'react';

export function useLatest<T>(value: T) {
  const ref = useRef(value);
  ref.current = value;
  return ref; // 항상 최신 값을 가리킴
}

export default function Worker({ onMessage }: { onMessage: (s: string) => void }) {
  const latestHandler = useLatest(onMessage);
  useEffect(() => {
    const id = setInterval(() => {
      latestHandler.current('ping'); // 최신 핸들러 호출
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return null;
}

주의사항(안티패턴)

  • ref.current로 뷰를 업데이트하려 하지 말 것. 뷰에 반영돼야 하는 값은 useState로 관리한다.
  • 렌더 중 ref.current를 변경해 렌더 결과에 의존하는 로직을 만들지 말 것. 사이드이펙트는 이펙트 훅에서 처리한다.
  • 모듈 스코프의 let으로 컴포넌트 간 상태를 공유하지 말 것(인스턴스 간 간섭 발생).
  • ref 남용으로 상태 추적이 어려워지지 않도록, “UI에 영향”이면 state, “렌더 무관”이면 ref를 기본 규칙으로 삼는다.

체크리스트

  • 이 값이 화면에 반영돼야 하는가? 그렇다면 useState를 사용한다.
  • 렌더 사이에 유지돼야 하지만 화면에 직접 반영되지 않는가? useRef를 사용한다.
  • 외부 자원 핸들(타이머/소켓/DOM)을 저장하는가? useRef로 보관하고 이펙트에서 관리한다.
  • 모듈 전역 let로 상태를 공유하고 있지 않은가? 인스턴스 격리를 보장하도록 수정한다.

결론

useRef는 “렌더와 분리된 가변 값”을 안전하게 보존하는 도구다. DOM 접근, 타이머·외부 핸들 저장, 최신 콜백 유지 등에서 특히 유용하다. 반대로 뷰에 영향을 주는 값은 useState로 관리해 예측 가능한 렌더 사이클을 유지하는 것이 바람직하다.

728x90
저작자표시 비영리 변경금지 (새창열림)

'Frontend Development' 카테고리의 다른 글

Next.js 최신 캐싱 전략 총정리  (0) 2025.09.03
면접에서 묻는 "의존성 주입 경험이 있나요?"의 의미  (1) 2025.08.26
Core Web Vitals: LCP, INP, CLS 개념과 개선 방법  (3) 2025.08.20
코드가 깔끔해지는 비밀: 프론트엔드에서 함수형 프로그래밍 활용하기  (3) 2025.08.18
프론트엔드 개발자를 위한 AI 용어 완전 정리  (3) 2025.07.29
'Frontend Development' 카테고리의 다른 글
  • Next.js 최신 캐싱 전략 총정리
  • 면접에서 묻는 "의존성 주입 경험이 있나요?"의 의미
  • Core Web Vitals: LCP, INP, CLS 개념과 개선 방법
  • 코드가 깔끔해지는 비밀: 프론트엔드에서 함수형 프로그래밍 활용하기
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (133)
      • Frontend Development (62)
      • Backend Development (25)
      • Algorithm (33)
        • 백준 (11)
        • 프로그래머스 (17)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (3)
      • Language (6)
        • JavaScript (6)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    frontend development
    tailwindcss
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
useState vs useRef vs let: 언제 무엇을 써야 할까?
상단으로

티스토리툴바