useEffect와fetch없이 서버에서 직접 데이터를 꺼내오는 현대적인 풀스택 로딩 전략을 분석함
키워드: Direct DB Access, RSC async/await, lib 폴더 구조, SQLite
기존 리액트(CSR)는 브라우저가 API 서버를 호출하고 기다리는 복잡한 과정을 거쳤지만, Next.js 서버 컴포넌트는 서버 내부에서 DB에 직접 접근합니다.
| 비교 항목 | 기존 리액트 (CSR) | Next.js 서버 컴포넌트 (RSC) |
|---|---|---|
| 통신 방식 | 브라우저 → API 서버 → DB | 서버 컴포넌트 → DB (직접) |
| 필요한 훅 | useState, useEffect 필수 | 필요 없음 (async/await 사용) |
| 보안 | API 엔드포인트 노출 위험 | 서버 내부 실행으로 안전함 |
lib/meals.js)데이터베이스 연결과 쿼리 로직은 별도의 lib 폴더에서 관리하여 코드의 가독성과 재사용성을 높입니다.
import sql from 'better-sqlite3';
const db = sql('meals.db');
export async function getMeals() {
// 로딩 상태 확인을 위해 의도적인 지연(Delay) 추가
await new Promise((resolve) => setTimeout(resolve, 2000));
// .all()은 모든 행을 배열로 가져옴
return db.prepare('SELECT * FROM meals').all();
}
가장 많은 입문자가 헷갈리는 부분입니다. 왜 일반 리액트 컴포넌트는 async가 안 되고, 서버 컴포넌트는 될까요?
일반적인 클라이언트 컴포넌트는 "즉시 UI(JSX)를 반환해야 하는 함수"입니다
문제점: async를 붙이면 함수가 JSX가 아닌 Promise를 반환합니다.
결과: 리액트 엔진은 화면에 그릴 HTML이 필요한데 '나중에 줄게'라는 약속(Promise)을 받으면 에러를 냅니다. 그래서 예전엔 useEffect 안에서 데이터를 따로 관리했습니다.
서버 컴포넌트는 브라우저가 아닌 서버(백엔드 환경)에서 실행됩니다.