다다의 개발일지 6v6

[Next.js] 서버 vs 클라이언트 컴포넌트에서 - fetch, axios로 API 호출 차이점 본문

Frontend/Next.js

[Next.js] 서버 vs 클라이언트 컴포넌트에서 - fetch, axios로 API 호출 차이점

dev6v6 2025. 3. 15. 16:40

클라이언트에서 fetch 요청 vs 서버에서 fetch 요청

Next.js에서는 fetch()를 클라이언트와 서버에서 모두 사용할 수 있음. 하지만 실행되는 환경이 다르기 때문에 성능과 보안 측면에서 차이가 있음.

 

📌 핵심 차이점 요약

  클라이언트에서 fetch() 요청 서버에서 fetch() 요청
실행 위치 브라우저 (클라이언트) Next.js 서버 (Node.js)
요청 횟수 사용자가 페이지 방문할 때마다 요청 서버에서 한 번 요청 후 결과 반환
보안 API 키가 브라우저에서 노출될 수 있음 API 키를 숨길 수 있음
성능 네트워크 요청이 많아질 수 있음 서버에서 미리 데이터 가져와서 빠름
SEO CSR 방식이므로 SEO 불리함 SSR로 SEO 최적화 가능

 

1. 클라이언트에서 fetch 요청하는 방식

 

📌 클라이언트에서 실행되는 fetch 요청은 브라우저에서 직접 API에 요청을 보냄.
즉, 용자 인터랙션이 있을 때 요청이 발생함. 

  • useEffect 또는 이벤트 핸들러에서 실행됨
  • 페이지가 렌더링된 후 데이터가 로드됨 → 로딩 화면을 따로 처리해야 함
  • API 키가 브라우저에 노출될 수 있으므로 보안상 주의 필요

 

 클라이언트에서 fetch 요청 예제

"use client"; // 클라이언트 컴포넌트

import { useState, useEffect } from "react";

export default function ClientFetchExample() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then((res) => res.json())
      .then((data) => setData(data));
  }, []);

  return (
    <div>
      <h2>클라이언트에서 API 요청</h2>
      {data ? (
        <ul>
          {data.map((user: any) => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      ) : (
        <p>Loading...</p>
      )}
    </div>
  );
}
 

 클라이언트 fetch 요청의 특징

  • useEffect를 사용하여 페이지가 로드된 후 API 요청 실행
    -> 브라우저에서 실행되기 때문에 useEffect, onClick 이벤트 같은 곳에서 호출됨
  • 브라우저에서 직접 요청 → 사용자가 방문할 때마다 API 호출
  • API 응답이 올 때까지 로딩 화면이 표시될 수 있음
  • API 요청이 많아질 경우, 클라이언트 성능 저하 가능
  • SEO가 불리함 (페이지가 클라이언트에서 렌더링되므로 검색 엔진이 내용을 크롤링하기 어려움 : 검색 엔진은 api 응답을 기다리지 않으므로)
  • 보안 문제가 있음 (API 키가 공개될 가능성이 있음)

 

2. 서버에서 fetch 요청하는 방식

📌 서버에서 실행되는 fetch 요청은 Next.js의 API 라우트 또는 서버 컴포넌트에서 실행됨.
즉, 클라이언트가 요청하기 전에 서버에서 데이터를 미리 가져와 렌더링할 수 있음.

 

 서버에서 fetch 요청 예제 (서버 컴포넌트 사용)

// app/components/ServerFetchExample.tsx (서버 컴포넌트)
async function getUsers() {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  const users = await res.json();
  return users;
}

export default async function ServerFetchExample() {
  const users = await getUsers();

  return (
    <div>
      <h2>서버에서 API 요청</h2>
      <ul>
        {users.map((user: any) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

 

 서버 fetch 요청의 특징

  • fetch()를 서버에서 실행 → 클라이언트는 HTML만 전달받음 -> 클라이언트 부담을 줄임.
  • API 응답을 서버에서 미리 받아오기 때문에 초기 로딩 시간이 짧음
  • SEO 최적화 가능 (페이지 로딩 전에 데이터가 준비됨, 검색 엔진이 완성된 HTML을 읽을 수 있음)
  • API 키를 숨길 수 있음 (브라우저에서 직접 요청하지 않으므로)
  • 데이터를 캐싱할 수 있음 (fetch의 next: { revalidate: 60 } 옵션 사용 가능)
  • Next.js가 자동으로 처리
  • _rsc 쿼리 파라미터가 붙음 → ?_rsc=XXXX
  • 브라우저에서 직접 호출한 게 아니라, React Server Components의 렌더링 과정에서 호출됨
  • 예시:
    GET /user/create?_rsc=1q6r0
    

 

+  API 라우트 (Next.js 서버 API)에서 axios 사용하기 ( fetch도 같음.)

더보기

📌 API 키 보호 및 보안이 중요한 경우, Next.js API 라우트를 이용하여 axios 요청을 서버에서 실행하는 것이 좋음.
👉 이 방법을 사용하면 클라이언트에서 직접 API 요청하지 않고, Next.js 서버를 거쳐 API를 호출할 수 있음.

 

 API 라우트에서 axios 요청 예제

// app/api/users/route.ts (Next.js 서버 API)
import { NextResponse } from "next/server";
import axios from "axios";

export async function GET() {
  try {
    const res = await axios.get("https://jsonplaceholder.typicode.com/users");
    return NextResponse.json(res.data);
  } catch (error) {
    return NextResponse.json({ message: "API 요청 실패" }, { status: 500 });
  }
}

👉 이제 클라이언트에서는 이 API를 호출하면 됨!

 

 클라이언트에서 Next.js API 호출하기

"use client";

import { useState, useEffect } from "react";
import axios from "axios";

export default function ClientAxiosWithAPI() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    axios.get("/api/users") // Next.js 서버 API 호출
      .then((res) => setUsers(res.data))
      .catch((error) => console.error("API 요청 실패:", error));
  }, []);

  return (
    <div>
      <h2>Next.js API 라우트 사용</h2>
      <ul>
        {users.map((user: any) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

 

 API 라우트를 사용하면 좋은 경우

 API 키 보호 가능 (클라이언트에서 직접 외부 API 호출 안 함)
✔ 클라이언트 요청을 Next.js 서버가 처리하여 보안 강화 가능
✔ 클라이언트에서는 내부 API(/api/users)를 호출하므로 더 간편함
✔ API 응답을 캐싱하여 성능 최적화 가능

 

 

  서버 컴포넌트 axios 클라이언트 컴포넌트 axios API 라우트 (Next.js 서버 AP)
실행 위치 Next.js 서버 브라우저 (클라이언트) Next.js 서버 (백엔드 API)
데이터 요청 시점 서버에서 미리 요청 클라이언트에서 페이지 로드 후 요청 API 호출 시 요청
API 키 보호 ✅ 보호됨 ❌ 노출 위험 있음 ✅ 보호됨
SEO ✅ 가능 (HTML로 응답) ❌ 불가능 (CSR 방식) ❌ 불가능 (CSR 방식)
초기 로딩 속도 ✅ 빠름 (미리 데이터 준비) ❌ 느림 (로딩 필요) ❌ 느림 (CSR)
상태 관리 ❌ 불가능 (useState X) ✅ 가능 (useState O) ❌ 불가능

 

  • 서버 컴포넌트에서 axios를 사용하면 SEO 최적화 + 보안 + 빠른 렌더링 가능
  • 클라이언트 컴포넌트에서는 상태 관리(useState) 및 사용자 이벤트 처리 가능
  • API 키 보호가 필요하면 API 라우트(/api)를 사용하여 보안 강화 가능

 

3. 클라이언트 vs 서버에서 API 요청 방식 비교 (fetch 외 다른 방법 포함)

  실행 위치 주요 특징 SEO 보안
클라이언트 fetch 요청 브라우저 사용자가 방문할 때마다 요청, useEffect 필요 ❌ 불리 ❌ API 키 노출 가능
서버 fetch 요청 Next.js 서버 미리 데이터를 받아 클라이언트로 전달 ✅ 유리 ✅ API 키 보호 가능
getServerSideProps (SSR) Next.js 서버 요청 시마다 API 호출 후 HTML 생성 ✅ 유리 ✅ API 키 보호 가능
getStaticProps (SSG) Next.js 서버 빌드 시 한 번 API 호출 후 정적 HTML 생성 ✅ 매우 유리 ✅ API 키 보호 가능
API 라우트 (/api 경로) Next.js 서버 API 프록시 역할, 백엔드처럼 사용 가능 - ✅ API 키 보호 가능

 

4. API 요청 방식별 사용하면 좋은 상황

 

 클라이언트에서 fetch() 요청이 적합한 경우

  • 사용자 상호작용 이후에 데이터를 가져올 때 (예: 버튼 클릭 시 API 호출)
  • 실시간 데이터가 필요할 때 (예: 실시간 채팅, 주식 데이터 등)
  • SEO가 필요 없는 경우 (예: 로그인 후 보여주는 대시보드)

 서버에서 fetch() 요청이 적합한 경우

  • 초기 페이지 로딩 속도를 최적화하고 싶을 때
  • SEO가 중요한 페이지 (블로그, 상품 상세 페이지 등)
  • API 요청을 클라이언트에서 직접 수행하지 않고 보호하고 싶을 때

 API 라우트 (Next.js /api)가 적합한 경우

  • 백엔드 서버 역할을 하고 싶을 때
  • API 키를 보호하고 싶을 때
  • 클라이언트와 백엔드 간 데이터 변환이 필요할 때

 

5. 서버 fetch를 더 최적화하는 방법

Next.js는 서버 컴포넌트의 fetch 요청을 자동으로 캐싱한다.
👉 fetch()를 next: { revalidate } 옵션과 함께 사용하면 캐싱을 조절 가능!

 

 fetch 캐싱 예제

async function getUsers() {
  const res = await fetch("https://jsonplaceholder.typicode.com/users", {
    next: { revalidate: 60 }, // 60초 동안 캐시 유지
  });
  return res.json();
}
  • 이렇게 하면 Next.js가 60초 동안 동일한 데이터를 캐싱하여 불필요한 API 호출을 줄일 수 있음
  • 캐시를 사용하지 않고 항상 새로운 데이터를 가져오려면 { cache: "no-store" } 옵션을 사용

6. 결론

 클라이언트 fetch() 요청: 사용자가 직접 브라우저에서 API 요청, 실시간 데이터가 필요할 때 적합하지만 SEO 불리하고 API 키 보안 문제 있음
 서버 fetch() 요청: Next.js 서버에서 API를 미리 요청하여 최적화, SEO 최적화 가능, API 키 보호 가능
 API 라우트 (/api 경로 사용): Next.js를 백엔드처럼 사용 가능, API 키 보호 가능

💡 Next.js에서는 서버에서 fetch() 요청하는 것이 일반적으로 더 좋음!
하지만 사용자 상호작용이 필요한 경우에는 클라이언트에서 fetch() 요청을 사용할 수도 있음.