

TECH.KAKAO.GG
Next.js와 Spring Boot 연동 시 API 호출 방식
프론트엔드에서 데이터를 어떻게 가져올 것인지에 대한 논의이므로, 프론트엔드 카테고리에 가장 잘 어울린다고 판단했습니다.
방식 1. Next.js의 api
폴더를 사용하는 방법 (중간 API 서버를 두는 방식)
설명
-
Next.js에서 제공하는
pages/api
또는app/api
라우트를 만들어서, 여기에서 Java Spring Boot API를 호출하도록 중간 레이어를 두는 방식입니다. -
사용자가 직접 Spring Boot에 접근하지 않고, Next.js 서버를 통해 접근합니다.
장점
-
보안 및 민감 정보 보호
-
API 키나 보안 토큰, 비즈니스 로직을 클라이언트 측에 직접 노출하지 않고 Next.js 서버에서 관리할 수 있습니다.
-
클라이언트는 Next.js API만 호출하고, 내부 Spring Boot 서버 주소나 API 정보가 숨겨져 있어서 보안성이 높아집니다.
-
-
데이터 가공 및 서버 로직 추가 용이
-
Spring Boot 서버가 제공한 데이터를 Next.js에서 한 번 더 가공하여 최적화된 형태로 클라이언트에 전달 가능합니다.
-
인증, 로깅, 캐싱, 데이터 필터링, 썸네일 생성 등 다양한 로직을 Next.js 서버 레이어에서 추가로 관리할 수 있습니다.
-
-
마이크로서비스 아키텍처 지원 및 유연성
-
장기적으로 다양한 API 서버(Spring Boot, Node.js, Python 등)를 연동할 때 Next.js API 서버를 게이트웨이처럼 활용하여 각 서비스와의 연동을 통합 관리할 수 있습니다.
-
⚠️ 단점
-
추가적인 서버 계층 발생 (서버를 하나 더 만드는 개념)
-
이미 Spring Boot 서버가 별도로 있는데, 추가로 Next.js의 Node.js 기반 서버가 생성되므로 서버 자원이 더 소비됩니다.
-
요청 처리에 두 번의 API 호출(Spring Boot ↔ Next.js API)이 이루어지므로 약간의 지연(latency) 및 성능 저하가 발생할 가능성이 있습니다.
-
-
복잡도 상승 및 유지보수 부담 증가
-
Next.js 서버가 API 호출을 프록싱하면서 중복된 코드나 비슷한 로직이 양쪽(Spring Boot, Next.js API)에 모두 존재할 수 있어 유지보수가 복잡해질 수 있습니다.
-
-
개발 및 배포 복잡도 증가
-
프론트엔드 개발자가 Next.js API를 별도 개발하고 배포하는 과정이 추가되어, 배포 자동화, CI/CD, 모니터링 등 관리적인 측면의 복잡성이 늘어납니다.
-
방식 2. lib/action
에서 직접 호출하는 방식 (직접 호출)
설명
-
클라이언트가 Java Spring Boot API를 직접 호출하는 방식입니다.
-
Next.js 서버는 별도로 개입하지 않고, 클라이언트 코드에서 axios나 fetch를 이용하여 직접적으로 Spring Boot 서버의 API를 호출합니다.
장점 (추천 이유 포함)
-
간단한 구조 및 직관적인 유지보수
-
추가 API 서버를 별도로 만들지 않으므로 간결한 아키텍처를 유지할 수 있습니다.
-
호출 흐름이 명확하고 직관적이기 때문에 디버깅 및 문제 해결이 용이합니다.
-
-
성능 향상
-
불필요한 중간 레이어가 없어, 클라이언트가 서버를 직접 호출해 처리 시간이 단축되고 응답 속도도 빠릅니다.
-
요청-응답이 Spring Boot 서버와 클라이언트 간 직접 이루어져 API 호출 성능 및 네트워크 오버헤드를 최소화할 수 있습니다.
-
-
낮은 운영비 및 리소스 사용량 절감
-
추가로 Node.js 기반 서버가 필요 없으므로 서버 리소스, 운영비, 인프라 비용 등이 절약됩니다.
-
CI/CD 파이프라인도 복잡하지 않아서 배포와 관리가 편리합니다.
-
⚠️ 단점
-
보안상 민감 정보 클라이언트 노출 위험
-
클라이언트에서 API 키 등 민감한 정보가 노출될 가능성이 존재합니다. 환경 변수 관리에 주의해야 합니다.
-
반드시 공개해도 되는 정보만 클라이언트에서 관리하고, 민감 정보는 별도 인증 시스템(JWT 등)을 통해 관리해야 합니다.
-
-
클라이언트 측 코드가 복잡해질 가능성
-
인증, 데이터 변형, 에러 핸들링과 같은 로직이 많아지면 프론트엔드 코드가 복잡해질 수 있으며 유지보수가 다소 어렵습니다.
-
별도의 라이브러리나 헬퍼 함수로 잘 관리해야 합니다.
-
-
서버 변경 시 클라이언트 변경이 필수적
-
API 스펙 변경이 일어나면 클라이언트 코드를 직접 수정하고 재배포해야 하므로, 변화에 대한 유연성이 낮습니다.
-
클라이언트 단에서 직접 fetch를 URL 경로로 보내는 방식도 방식2에 포함됩니다.
// 예시: 클라이언트 컴포넌트 내에서 fetch 사용
'use client';
import { useEffect, useState } from 'react';
export default function MyClientComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
// 여기에 스프링 부트 API의 실제 URL을 입력합니다.
const response = await fetch('http://your-springboot-api.com/api/data');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
}, []);
return (
<div>
{data ? <p>Data: {JSON.stringify(data)}</p> : <p>Loading...</p>}
</div>
);
}
대부분의 경우에는 방식 2 (lib/action) 를 권장합니다.
이유는 다음과 같습니다.
-
실무에서는 성능, 관리, 유지보수 용이성을 고려하여 가능한 간단하고 명확한 구조를 선호합니다.
-
이미 Spring Boot라는 강력한 백엔드 서버가 존재하는 상황에서 굳이 Next.js 서버를 하나 더 만들면 인프라 비용, 관리 비용이 증가하고, 성능 측면에서도 손해가 발생할 수 있습니다.
-
보안 문제는 JWT 토큰 기반 인증, OAuth, 별도 보안 프로세스를 통해 해결 가능하기 때문입니다.
단, 추가적인 보안 이슈나 데이터 가공 로직이 복잡하고 민감한 경우에는 중간 레이어(Next.js api 폴더)를 만들어 처리하는 방식을 선택할 수도 있습니다.