반응형

 

개발하는데 개인적으로 필요한 내용하고 제가 이해한 방식만 적었기 때문에 필요 사항은 공식 문서 참조

 

https://react.dev/blog

 

React Blog – React

The library for web and native user interfaces

react.dev

 

 

📝컴파일러 개선

useMemo, useCallaback를 사용한 최적화 작업을 안 할 수 있도록 컴파일러가 자동 처리하게끔 설정하지만 정말 필요한 상황에선 써야하기 때문에 useMemo와 useCallback은 19버전 메뉴얼에도 제공함

 

 

📝useTransition

기존 방식

// Before Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    const error = await updateName(name);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    } 
    redirect("/path");
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

 

useTransition 사용

// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/path");
    })
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );

함수내에서 isPending으로 인한 Pending 작업 축소화

 

 

📝form 개선

// useFormStatus (form 정보 가져와서 읽기)
import { useFormStatus } from "react-dom";
import action from './actions';

function Submit() {
  const status = useFormStatus();
  return <button disabled={status.pending}>Submit</button>
}

export default function App() {
  return (
    <form action={action}>
      <Submit />
    </form>
  );
}

form을 잘 활용할 수 있게 다양한 편의성 업데이트가 있지만 개인적인 의견으로  복잡한 상황에는 useForm을 쓰는 게 더 나아보인다.

 

📝use을 이용해 fetch 보일러 플레이트 개선

// 서버 컴포넌트 (page.tsx)
import { getPosts } from './lib/api';

export default async function Page() {
  const posts = await getPosts(); 
  return <PostList posts={posts} />;
}

// 클라이언트 컴포넌트 (useState + useEffect 필요)
"use client";

function PostList({ posts: initial }) {
  const [posts, setPosts] = useState(initial);

  useEffect(() => {
    async function fetchData() {
      const data = await fetch('/api/posts').then(res => res.json());
      setPosts(data);
    }
    fetchData();
  }, []);

  return (
    <ul>
      {posts.map(p => <li key={p.id}>{p.title}</li>)}
    </ul>
  );
}

// 서버 컴포넌트 (page.tsx)
import { use } from 'react';

function fetchPosts() {
  return fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json());
}

export default function Page() {
  const posts = use(fetchPosts()); // ✅ 한 줄로 await처리

  return (
    <ul>
      {posts.map((p: any) => (
        <li key={p.id}>{p.title}</li>
      ))}
    </ul>
  );
}

상위에 선언한 Context를 가져오거나 Fetch Promise를 넣어서 코드를 간결하게 작성 가능

 

📝React 정적 랜더링

import { prerender } from 'react-dom/static';

async function handler(request) {
  const {prelude} = await prerender(<App />, {
    bootstrapScripts: ['/main.js']
  });
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  });
}

사전 렌더링 기능 지원

 

📝서버 액션

서버

"use server"

import {PrismaClient} from "@prisma/client";
import {NextResponse} from "next/server";

/** 서버 컴포넌트에서 이용하는 서버액션 방식 (Back End) **/
export async function getUsers() {
    const prisma = new PrismaClient()
    const test = await prisma.test_table.findFirst();
    console.log(test)

    // return NextResponse.json(test);

    return test;
}

/** useEffect 이용한 Data Fetch (Front End) **/
export async function GET(request: Request) {
    const data = await getUsers();
    console.log(data);
    return NextResponse.json(data);
}

 

클라이언트

"use client"

import {getUsers} from "@/app/api/main/route";
import {useEffect, useState} from "react";

export default function Page () {

    const [user, setUser] = useState<any>(null);

    /** 서버 액션 방식 **/
    // useEffect(() => {
    //     async function load() {
    //         const data = await getUsers();
    //         setUser(data);
    //     }
    //     load();
    // }, []);


    /** Fetch 방식 **/
    useEffect(() => {
        async function load() {
            const res = await fetch('/api/main');
            const data = await res.json();
            setUser(data);
        }
        load();
    }, []);
    

    return <div>
        {user?.column_name}
        <button onClick={getUsers}>btn</button>
    </div>
}

"use server”가 붙어있는 파일이다. 해당 파일에는 백엔드 관련 로직이 들어가고 클라이언트 컴포넌트에서 fetch를 통한 api 호출하는 것이 아닌 해당 함수를 직접 호출시켜서 사용한다.

 

📝forwardRef → ref로 변경

// 이전 코드 forwardRef
import { forwardRef } from 'react';

const MyInput = forwardRef<HTMLInputElement, { placeholder: string }>(
  function MyInput({ placeholder }, ref) {
    return <input ref={ref} placeholder={placeholder} />;
  }
);

const inputRef = useRef<HTMLInputElement>(null);
<MyInput placeholder="이름" ref={inputRef} />;


// 현재 코드 re
function MyInput({ placeholder, ref }: { placeholder: string; ref?: React.Ref<HTMLInputElement> }) {
  return <input ref={ref} placeholder={placeholder} />;
}

const inputRef = useRef<HTMLInputElement>(null);
<MyInput placeholder="이름" ref={inputRef} />;

상위 컴포넌트 참조 필요시 forwardRef에서 ref로 변경

 

ref 사용하는 곳

  • DOM 직접 제어 (포커스 설정, 스크롤 이동, 텍스트 선택 등)
  • 외부 라이브러리 사용 (D3, Chart.js처럼 직접 DOM에 접근해야 하는 경우)

 

📝ref 클린업 제공

<input
  ref={(ref) => {
    // ref created

    // NEW: return a cleanup function to reset
    // the ref when element is removed from DOM.
    return () => {
      // ref cleanup
    };
  }}
/>

컴포넌트가 언마운트 되면 ref 콜백에서 clean up 함수를 호출합니다.

 

📝React Helmet

function BlogPost({post}) {
  return (
    <article>
      <h1>{post.title}</h1>
      <title>{post.title}</title>
      <meta name="author" content="Josh" />
      <link rel="author" href="https://twitter.com/joshcstory/" />
      <meta name="keywords" content={post.keywords} />
      <p>
        Eee equals em-see-squared...
      </p>
    </article>
  );
}

React Helmlet에서 사용하던 기능 제공

반응형