Solid Start 완벽 가이드 | 초고성능 풀스택·라우팅·Server Functions·배포
이 글의 핵심
Solid Start는 SolidJS를 위한 메타 프레임워크로, Vite·Vinxi·Nitro를 묶어 SSR(서버 사이드 렌더링)·하이드레이션·서버 함수·정적 배포까지 한 프로젝트에서 다룹니다. React 기반의 Next.js가 “앱 라우터·서버 컴포넌트·캐시 시맨틱”으로 복잡도를 키운 반면, Solid Start는 세밀한 반응형(Signals) 과 컴포넌트는 한 번만 실행되는 모델을 유지한 채 풀스택 경로를 제공합니다.
이 글에서는 핵심 아키텍처, routes 디렉터리 기반 라우팅, Server Functions와 RPC 스타일 통신, query·createResource 기반 데이터 페칭, Nitro preset으로 Vercel·Netlify·Cloudflare에 올리는 방법, 마지막으로 Next.js와의 선택 기준을 정리합니다. 공식 문서 기준(2026년, Solid Start 2.x 전제)으로 설명하며, 실제 코드는 프로젝트 템플릿과 버전에 맞게 조정해야 합니다.
참고: Solid Start는 빌드 체인과 런타임 어댑터가 함께 움직이므로, 프리셋·Node 호환 플래그·직렬화 모드는 배포 환경마다 한 번씩 검증하는 것이 안전합니다.
들어가며: 왜 Solid Start인가
풀스택 프레임워크를 고를 때 흔한 기준은 다음과 같습니다. 개발자 경험(DX), 런타임 성능, 호스팅 비용과 배포 단순성, 팀의 기술 스택(React vs Solid) 입니다. Solid Start는 UI는 Solid, 서버는 Nitro가 추상화하는 구조라, “같은 레포에서 페이지·API·서버 함수”를 유지하면서도 번들과 런타임 반응형 이점을 가져가기 좋습니다.
다만 이 생태계는 Next.js 대비 자료·채용 시장·서드파티 통합 규모가 작습니다. 초고성능이 필요하고 Solid에 이미 익숙한 팀에게 특히 잘 맞고, 엔터프라이즈 표준 툴링을 우선한다면 Next.js·Remix 쪽을 비교 검토하는 편이 현실적입니다.
1. Solid Start란 무엇인가
1-1. 메타 프레임워크로서의 위치
SolidJS는 UI 라이브러리이고, Solid Start는 그 위에 라우팅·빌드·서버 실행을 얹은 메타 프레임워크입니다. 클라이언트는 Vite로 묶이고, 서버 측 배포는 Nitro(통합 빌드·어댑터)를 통해 Node, Vercel, Netlify, Cloudflare Workers 등으로 출력됩니다. 개발 시에는 Vinxi가 라우터(서버·클라이언트·서버 함수)를 나눠 다루는 그림으로 이해하면 됩니다.
1-2. UI 라우트와 API 라우트의 분리
Solid Start 문서는 라우트를 크게 둘로 나눕니다.
- UI 라우트:
routes아래 파일의 default export가 컴포넌트인 경우 — 사용자에게 HTML을 그립니다. - API 라우트: 같은 파일 규칙을 쓰지만 HTTP 메서드명(
GET,POST등)을 export하는 경우 — JSON·Response·웹훅 등을 다룹니다.
API 라우트는 동일 경로의 UI보다 우선할 수 있으며, 문서에 따르면 GET에서 응답을 반환하지 않으면 UI로 폴백할 수 있습니다. 공개 REST·웹훅·OAuth 콜백처럼 “URL이 곧 계약” 인 경우 API 라우트가 적합합니다.
1-3. 직렬화(Serialization)와 CSP
서버 함수는 서버와 클라이언트 사이에 페이로드를 직렬화해 넘깁니다. app.config.ts에서 serialization.mode를 설정할 수 있습니다.
json: 클라이언트에서JSON.parse— 엄격한 CSP(Cross-Site 보안) 에 유리하나 페이로드가 다소 커질 수 있습니다. Solid Start v2 기본값입니다.js: Seroval 기반으로 더 작고 빠를 수 있으나, 역직렬화에unsafe-eval이 필요할 수 있어 CSP가 느슨해야 할 수 있습니다. v1 호환을 위해 알아두면 좋습니다.
프로덕션에서 헤더·쿠키·리다이렉트를 다루는 서버 로직은 스트리밍이 시작되기 전에 끝내야 합니다. 스트리밍 이후에는 “헤더를 이미 보냈다”는 제약이 생깁니다. 문서에서는 이런 쿼리에 deferStream 을 켜서 스트리밍을 지연하는 패턴을 안내합니다.
2. 프로젝트 구조와 진입점
2-1. 스캐폴딩
새 프로젝트는 보통 아래와 같이 생성합니다(공식 가이드와 동일한 흐름).
npm create solid@latest my-app
cd my-app
npm install
npm run dev
템플릿에 따라 src/app.tsx, src/routes, app.config.ts 등이 배치됩니다. 핵심은 routes 디렉터리가 URL 경로의 단일 출처라는 점입니다.
2-2. FileRoutes와 루트 레이아웃
UI 라우트는 @solidjs/start/router의 FileRoutes 로 수집되며, @solidjs/router의 Router 와 함께 씁니다. 루트에는 Suspense 로 자식을 감싸는 패턴이 권장됩니다. 라우트 컴포넌트는 지연 로드되므로, Suspense 없이면 하이드레이션 관련 예기치 않은 동작이 날 수 있습니다.
import { Suspense } from "solid-js";
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
export default function App() {
return (
<Router root={(props) => <Suspense>{props.children}</Suspense>}>
<FileRoutes />
</Router>
);
}
위 구조에서 root 는 앱 전체의 최상위 레이아웃 역할을 합니다. 공통 헤더·글로벌 프로바이더·에러 경계를 여기 두면 모든 페이지에 일관되게 적용하기 좋습니다.
3. 라우팅과 파일 시스템
Solid Start의 라우팅은 파일 기반입니다. routes 아래 파일 이름과 폴더 구조가 URL에 매핑됩니다.
3-1. 기본 규칙
/routes/index.tsx→//routes/about.tsx→/about/routes/blog/article-1.tsx→/blog/article-1
폴더 안의 index 는 그 세그먼트가 비었을 때 매칭되는 인덱스 페이지입니다.
3-2. 중첩 레이아웃
routes/blog.tsx 와 routes/blog/article-1.tsx 처럼 부모 폴더와 같은 이름의 파일이 있으면, 그 파일이 하위 라우트의 레이아웃이 됩니다. 자식 영역은 props.children으로 배치합니다.
import type { RouteSectionProps } from "@solidjs/router";
export default function BlogLayout(props: RouteSectionProps) {
return (
<article>
<header>Blog</header>
{props.children}
</article>
);
}
3-3. 동적 세그먼트·옵션·Catch-all
- 동적:
/users/:id→routes/users/[id].tsx - 옵션:
routes/users/[[id]].tsx—/users와/users/123모두 매칭 - Catch-all:
routes/blog/[...post].tsx— 여러 세그먼트를 하나의 파라미터로 슬래시 구분 문자열로 받습니다.
동적 값은 useParams() 로 읽습니다. 라우터 파라미터와 서버 함수의 인자는 역할이 다르므로, “URL이 주는 식별자” 와 “서버에서만 조회하는 비밀” 을 분리하는 습관이 중요합니다.
3-4. 라우트 그룹과 인덱스 파일 이름
폴더명을 괄호로 감싸면 (marketing) 처럼 URL에 포함되지 않는 그룹을 만들 수 있어, 파일 구조만 정리할 때 유용합니다. 또한 여러 index.tsx 를 검색하기 어렵다면 (폴더이름).tsx 형태로 인덱스를 대체할 수 있습니다(문서의 “Renaming Index”).
3-5. route export로 추가 설정
파일에서 route 객체를 export하면 프리로드 등 라우터별 설정을 붙일 수 있습니다. preload 를 정의해 데이터를 미리 가져오는 패턴과 맞물립니다.
4. Server Functions와 RPC 스타일
4-1. “서버에서만 실행”되는 함수
Server Functions는 서버에서만 실행되도록 표시된 함수입니다. 클라이언트 번들에 DB 비밀키를 넣지 않고도, 타입이 보존된 채 서버 로직을 호출하는 RPC(원격 프로시저 호출)에 가까운 경험을 줍니다. 구현체는 빌드 시 별도의 서버 함수 라우터로 분리되며, 클라이언트는 HTTP를 통해 해당 엔드포인트를 호출합니다(개발자가 URL을 직접 관리하지 않는 것이 일반적입니다).
문서에서는 함수 본문에 "use server" 지시자를 두는 패턴이 등장합니다. 이는 경계를 명시해 트리 셰이킹과 실행 위치를 분리하는 데 도움이 됩니다.
4-2. query와의 결합
@solidjs/router의 query 는 서버 함수를 데이터 페칭 단위로 묶는 데 쓰입니다. 아래는 세션을 읽고 사용자를 조회하며, 미인증 시 리다이렉트를 던지는 예시입니다(실제 DB·세션 모듈은 프로젝트에 맞게 교체).
import { query, redirect } from "@solidjs/router";
import { useSession } from "vinxi/http";
// import { db } from "./db";
const getCurrentUserQuery = query(
async (_id: string) => {
"use server";
const session = await useSession({
password: process.env.SESSION_SECRET as string,
name: "session",
});
if (session.data.userId) {
// return await db.users.get({ id: session.data.userId });
return { id: session.data.userId };
}
throw redirect("/login");
},
"currentUser"
);
RPC라고 부를 만한 이유는, 클라이언트가 함수 시그니처처럼 서버 작업을 호출하지만, 실제로는 요청·응답 직렬화가 끼어 있다는 점입니다. 따라서 대용량 페이로드·빈번한 호출은 설계를 다시 검토하고, 공개 API가 필요하면 아래 API 라우트를 병행합니다.
4-3. API 라우트와의 역할 분담
| 목적 | 권장 |
|---|---|
| 페이지·쿼리에서만 쓰는 서버 데이터 | Server Functions + query |
| 외부 클라이언트·모바일 앱·웹훅 | GET/POST export API 라우트 |
| GraphQL·tRPC 게이트웨이 | API 라우트에서 핸들러 조립 |
API 라우트는 APIEvent 를 받아 request, params, 내부 fetch 등을 사용합니다. tRPC는 fetchRequestHandler로 한 경로에 붙이는 예시가 문서에 있습니다.
5. 데이터 fetching 패턴
5-1. createResource와 라우터 쿼리
SolidJS 기본 createResource 는 비동기 데이터를 리액티브 소스와 연결합니다. Solid Router의 queries 문서와 함께 보면, 로딩·에러·재시도를 컴포넌트 반응형 모델에 맞게 표현할 수 있습니다.
5-2. 서버 함수를 fetcher로
Solid Start 문서의 Data fetching 페이지는 서버 함수 + query를 핵심으로 설명합니다. 즉, “DB에 직접 닿는 fetcher”를 서버에 두고, UI는 쿼리 프리미티브로 소비합니다. REST API를 한 번 더 만들지 않아도 된다는 점이 DX상 이점입니다. 반면 공개 계약이 필요한 API는 별도 라우트로 노출하는 편이 명확합니다.
5-3. 스트리밍과 헤더
앞서 언급했듯, 세션·쿠키·리다이렉트처럼 헤더를 바꾸는 로직이 스트리밍 이후에 실행되면 오류가 납니다. 이런 쿼리는 deferStream: true 옵션(예: createAsync와 함께)으로 스트리밍을 지연시키는 방식을 문서에서 권장합니다. 인증이 필요한 레이아웃을 설계할 때 특히 주의해야 합니다.
6. 배포 전략: Vercel, Netlify, Cloudflare
Solid Start는 Nitro preset으로 배포 대상을 고릅니다. app.config.ts에서 defineConfig를 사용합니다.
import { defineConfig } from "@solidjs/start/config";
export default defineConfig({
server: {
preset: "vercel", // 또는 netlify, netlify_edge, cloudflare_module 등
},
});
6-1. Vercel
vercel: 서버리스 함수 형태에 가깝게 배포할 때 흔히 사용합니다.vercel_edge: 엣지 런타임이 필요할 때 후보가 됩니다.
Vercel은 Node의 async local storage 등을 비교적 잘 지원하는 편이라, 문서에서 Cloudflare보다 설정 부담이 적은 경우로 언급됩니다. 팀이 이미 Vercel 파이프라인을 쓰고 있다면 가장 빠른 선택지 중 하나입니다.
6-2. Netlify
netlify,netlify_edge가 문서 예시에 있습니다.
JAMstack·폼·프리뷰 워크플로가 Netlify에 맞춰져 있다면 preset만 맞추고 빌드 출력을 Netlify가 기대하는 형태로 두면 됩니다. 엣지 배포 시에는 런타임 제약(콜드 스타트, Node API 가용성)을 릴리즈 전에 확인하세요.
6-3. Cloudflare Workers / Pages
Cloudflare는 Workers 런타임 특성상 Node API와의 괴리가 있습니다. 문서에는 cloudflare_module preset과 함께 rollupConfig.external 에 node:async_hooks 등을 두는 예시가 나옵니다. 또한 wrangler.toml 에 nodejs_compat 호환 플래그를 켜야 하는 경우가 있습니다.
Solid Start는 async local storage를 사용하므로, Cloudflare에서 별도 설정이 필요할 수 있다는 공식 주의사항이 있습니다. 즉, “가장 저렴한 엣지”만 보고 골라서는 안 되고, 실제 앱이 쓰는 Node API·세션·쿠키를 스테이징에서 검증해야 합니다.
6-4. 공통 체크리스트
- 환경 변수: 빌드·런타임 모두에 필요한 키가 호스팅 대시보드에 반영되었는지
- 직렬화 모드: CSP 정책과
jsonvsjs선택 - 리전·지연: 엣지 vs 노드 리전에서 DB 왕복 시간
- 프리뷰 URL: PR마다 미리보기가 필요한지
7. Next.js vs Solid Start
두 프레임워크는 “React 생태계 vs Solid 생태계” 라는 큰 갈림에서 출발합니다. 아래는 선택 기준을 빠르게 잡기 위한 비교입니다.
| 구분 | Next.js (App Router) | Solid Start |
|---|---|---|
| UI 모델 | React — 리렌더링 기반 | Solid — Signals, 컴포넌트 함수 1회 실행 |
| 서버 데이터 | RSC, fetch 캐시, Route Segment Config 등 규모가 큰 규약 | Server Functions, query, Nitro preset 중심 |
| 생태계 | 매우 큼 — 채용·라이브러리·예제 | 작고 빠르게 진화 — 자료 상대적으로 적음 |
| 배포 | Vercel 1급 지원, 타 플랫폼도 성숙 | Nitro preset으로 다중 타깃 — Cloudflare는 추가 설정 가능 |
| 적합 팀 | React 표준을 원하는 조직 | 성능·번들·반응형 모델에 강한 확신이 있는 팀 |
Next.js를 선택해야 하는 대표적 신호는 조직 표준이 React이고, 디자인 시스템·인증·모니터링이 React용으로 이미 정착한 경우입니다. Solid Start는 동일 화면에서 불필요한 리렌더링 비용을 줄이고 싶고, Solid의 멘탈 모델에 이미 익숙한 팀에게 유리합니다.
8. 자주 묻는 질문 (FAQ)
Q. Server Function과 REST API 중 무엇을 써야 하나요?
A. 같은 풀스택 앱 내부에서만 쓰는 데이터는 Server Function + query가 간결합니다. 외부 소비자·버전 계약·웹훅이 있으면 API 라우트로 노출하세요.
Q. Cloudflare에 배포할 때만 주의할 점이 있나요?
A. 문서 기준으로 async local storage와 Node 호환 플래그가 이슈가 될 수 있습니다. cloudflare_module preset과 nodejs_compat, 필요 시 rollupConfig.external 설정을 적용하고 반드시 스테이징에서 검증하세요.
Q. 직렬화 모드 json과 js는 어떻게 고르나요?
A. 엄격한 CSP가 우선이면 json(v2 기본). 페이로드 크기·역직렬화 성능이 병목이고 CSP를 조정할 수 있으면 js를 검토합니다.
Q. API 라우트와 UI 라우트가 같은 경로를 쓰면?
A. 문서에 따르면 API 라우트가 우선할 수 있으며, GET에서 응답을 반환하지 않으면 UI로 폴백할 수 있습니다. 의도한 콘텐츠 협상(Accept 헤더 등)을 확인하세요.
9. 마무리
Solid Start는 Solid의 성능 철학을 서버·배포까지 확장한 프레임워크입니다. routes로 URL을 고정하고, Server Functions로 서버 경계를 함수형으로 유지하며, Nitro preset으로 호스팅을 갈아끼우는 흐름을 익히면 풀스택 개발 속도를 낼 수 있습니다. 다만 배포 타깃별 런타임 차이와 직렬화·스트리밍·헤더 타이밍은 프로덕션에서 반드시 한 번씩 검증해야 합니다.
이 글은 개념과 공식 문서의 권장 패턴을 중심으로 정리했습니다. 실제 프로젝트에서는 템플릿 버전·의존성 메이저에 따라 import 경로나 API 이름이 조금씩 다를 수 있으므로, 최신 Solid Docs — Solid Start와 Nitro Deploy를 함께 참고하는 것을 권장합니다.
외부 참고 (공식)