React/Next/TS 기본 개념 및 사용이유

2025. 6. 22. 18:57Web_Programming

React란?

사용자 인터페이스 구축을 위한 JavaScript 라이브러리


등장 배경

기존의 UI 구현 방식의 한계

 

명령형 방식 + jQuery

$('#button').click(function() {
  $('#modal').show();
});
  • 직접 절차적으로 명시
  • 상태 변화에 따른 DOM 조작 직접 관리
  • => 로직이 복잡하고 버그가 발생하기 쉬움
  • => 상태와 UI 간의 일관성 유지가 어려움

React의 주요 특징 및 해결책

선언형 프로그래밍

  • “무엇을 보여줄 것인가”를 선언하면, React가 UI를 알아서 그려줌
  • 상태가 변하면 UI를 자동으로 다시 렌더링
  • 복잡한 DOM 조작을 직접 하지 않아도 됨
// Vanila JS
const list = document.getElementById("list");
const item = document.createElement("li");
item.textContent = "Apple";
list.appendChild(item);

//JQuery
$('#button').click(function() {
  $('#modal').show();
});

//React
function FruitList({ fruits }) {
  return (
    <ul>
      {fruits.map(fruit => <li key={fruit}>{fruit}</li>)}
    </ul>
  );
}

 

구분 명령형 선언형 (React)
접근 방식 어떻게 할 것인가 무엇을 할 것인가
예시 DOM 직접 조작 JSX로 UI 선언
상태 변화 반영 수동 자동 (React가 처리)
유지보수성 낮음 높음

가상 DOM

  • 이전 가상 DOM과 비교하여 차이점만 실제 DOM에 반영 (최소 단위 변경)
  • JSX 트리 => 가상돔 => 이전 가상돔과 비교 => 렌더

컴포넌트 기반 구조

  • UI를 재사용 가능한 컴포넌트 단위로 분리
  • 모듈화, 유지보수, 테스트, 협업에 용이
<div id="app"></div>

<script>
  $(document).ready(function () {
    const $button = $('<button></button>')
      .text('Click me')
      .on('click', function () {
        alert('Clicked!');
      });

    $app.append($button);
  });
</script>

// Button.jsx
export default function Button({ label, onClick }) {
  return <button onClick={onClick}>{label}</button>;
}

// App.jsx
import Button from './Button';

export default function App() {
  return (
    <div>
      <h1>Welcome</h1>
      <Button label="Click me" onClick={() => alert("Clicked!")} />
    </div>
  );
}

 단방향 데이터 흐름

  • 상위 부모컴포넌트 => 하위 자식 컴포넌트로 props를 통해 전달, 반대로는 전달할 수 없다
  • 데이터 흐름이 예측 가능하고, 디버깅이 용이함

 

구현

컴포넌트

import React, { useState } from 'react';

interface Props{
 name:string;
 age:number;
}

export default function Greeting({ name, age, children }:propsWithChildren<Props>) {
  const [isHello, setIsHello] = useState(true);
  const ref = useRef(true);

  const toggleGreeting = useCallback(() => {
    setIsHello(prev => !prev);
  },[]); //마운트 시점만 생성
  
  const data = useMemo(()=>{
    return 1+1;
  },[isHellow]); //isHellow 변경 시점 마다 재연산.
  
  useEffect(() => {
    console.log("마운트");
    
    setIsHello(false);
    ref.current = false
    console.log(isHello, ref.current); //true, false
    
    return () => {
        console.log("언마운트");
    };
  }, []);
  
  return (
    <div>
      {children}// 자식
      <h1>{isHello ? `${name} Hello!` : `${name} Goodbye!``}</h1>
      <button onClick={toggleGreeting}>Toggle</button> //toggleGreeting 생성마다 리렌더링.
    </div>
  );
}


// <Greeting name={"이름"} age={12}>{"자식"}</Greeting>

속성값 (Props)과 상태값 (State)

  • Props: 부모 컴포넌트로부터 전달받은 불변 데이터
    • 하위 컴포넌트에서는 변경이 불가능합니다.
  • State: 컴포넌트 내부에서 관리하는 가변 데이터

Hook

주요 내장 Hook

커스텀 훅 구현 방법

// useCount.js
import { useState } from 'react';

export default function useCount({
  initialValue = 0,
}: {
  initialValue: number;
}) {
  const [count, setCount] = useState(initialValue);

  const increase = () => setCount((prev) => prev + 1);
  const decrease = () => setCount((prev) => prev - 1);
  const reset = () => setCount(initialValue);

  return { count, increase, decrease, reset };
}

Next.js란?

React를 기반으로 한 웹 애플리케이션 프레임워크입니다.

특히 SSR, SSG(정적 페이지), Server Components 등 기능을 제공하여, React의 단점을 보완


1. 등장 배경

React의 한계

  • CSR 중심 구조
    • 페이지 로딩 시 자바스크립트를 모두 다운로드 후 렌더링 → 초기 로딩 지연, SEO 취약.

2. 주요 특징 및 React 한계 해결책

1. Server Components

  • React Server Components (RSC) 지원
//page.tsx
import LikeButton from '@/app/ui/like-button'
import { getPost } from '@/lib/data'
 
export default async function Page({ params }: { params: { id: string } }) {
  const post = await getPost(params.id)
 
  return (
    <div>
      <main>
        <h1>{post.title}</h1>
        {/* ... */}
        <LikeButton likes={post.likes} />
      </main>
    </div>
  )
}

//link-button.tsx
'use client'
 
import { useState } from 'react'
 
export default function LikeButton({ likes }: { likes: number }) {
  // ...
}

 

2. App Router

3. 서버 기능 통합

  • API Routes, Middleware 등을 통해 백엔드 없이도 API 구축 가능.

4. SEO 친화적

  • 페이지를 미리 렌더링(SSR/SSG)하여 검색 엔진 최적화에 강력.

5. 성능 최적화 내장

  • 코드 스플리팅, 이미지 최적화(next/image), 폰트 최적화 등 자동 제공.

전역 상태 관리

React 내장 전역 상태: useContext

  • Context API를 통해 전역 상태 생성 가능
export const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// App.jsx
const { theme, setTheme } = useContext(ThemeContext);

provider 하위는 context 변경마다 모두 리렌더링되어 비효율적.


Zustand

기본 특징

  • 패키지 사이즈가 가볍고, 빠른 전역 상태 관리 라이브러리
  • 간편화된 redux

Jotai

기본 특징

  • 원자(atom) 단위로 상태를 정의하는 상태 관리.
  • 내부적으로 Recoil과 유사한 개념, 훨씬 더 간단하고 유연.
  • Context 없이도 Scoped 상태동적 상태 표현 가능.

프로젝트 내 사용 예시

  • 도메인 하위에서 여러 state 관리 ⇒ zustand
  • 단일 state 및 state 적용 범위가 적은 경우 ⇒ jotai

tanstack/react-query

1. 기존 방식의 문제점

  • 로딩/에러 처리 반복 코드 많음
  • 캐싱, 리패칭, 백그라운드 갱신, 쿼리 무효화 등 수동 처리
  • 서버 상태 공유 어려움
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
 setLoading(true);
  fetch('/api/data')
    .then(res => res.json())
    .then(setData);
    .catch((e)=>{
      setError(error);
    })
    .filnally(()=>{
       setLoading(false);
    })
}, []);

import { useQuery } from '@tanstack/react-query';

function MyComponent() {
  const [enabled, setEnabled]=useState(false);
  const { data, isLoading, error, refecth } = useQuery({
    queryKey: ['posts'],           
    queryFn: () => fetch('/api/posts').then(res => res.json()),
    enabled: enabled
    refechOnWindowFocus:true
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error occurred</p>;

  return (
    <>
    <button onClick={()=>setEnabled(true)}>fetch</button>
    <ul>
      {data.map(post => <li key={post.id}>{post.title}</li>)}
    </ul>
    </>
  );
}

2. tanstack Query의 주요 특징

기능 설명
자동 캐싱 동일 요청은 자동 캐시, 빠른 화면 전환
자동 리패칭 refetch, focus 시 갱신 등
쿼리 무효화 데이터 갱신 시 쿼리 갱신
로딩/에러/성공 상태 관리 isLoading, error, data 등

 

TypeScript

JS 한계

타입 검사 없음 (런타임 오류 발생 가능)
에디터 지원 비교적 약함
코드 명확성 추측 필요
협업 의도 파악 어려움
리팩토링 타입 정보 부족으로 위험

TypeScript를 쓰는 이유

1. 정적 타입 검사

  • 오류를 사전에 잡을 수 있음
    • 런타임이 아니라 코드 작성 시점에 오류 감지
    • 예: 잘못된 파라미터 타입, 오탈자 등
function greet(name: string) {
  console.log('Hello, ' + name);
}
greet(123); // 컴파일 에러 발생

2. 개발 편의

  • 자동 완성 (auto-completion)
  • 타입 추론, 타입 정보 제공 (hover 시)
  • 코드 리팩토링 지원 강화

3. 가독성과 유지보수성 향상

  • 함수의 입력/출력 타입을 명시함으로써, 협업 시 함수 사용법이 명확해짐
  • API 응답 구조, 객체 구조 등이 명시되어 있어 추측 없이 개발 가능
반응형