← Back to NOTES 🌰 ← 프론트가 서버까지 확장될 때 생기는 비용 (RSC 이슈를 보며)
← 3. 내가 더 예민(😉)해지는 구간: attack surface와 trust boundary
아래 예시들은 이번 RSC 이슈의 직접 원인을 설명하기 위한 코드가 아니다. 다만 “프론트 개발 과정에서 서버 처리 구간이 열릴 때” 실무에서 자주 등장하는 패턴을 모아 둔 것이다. 즉, attack surface / trust boundary가 늘어나는 지점을 PR에서 감지하기 위한 예시다.
// app/api/report/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const body = await req.json(); // trust boundary 넘어온 입력
return NextResponse.json({ ok: true });
}
이 파일 하나로 /api/report 라는 엔드포인트가 생긴다. 즉, attack surface가 늘어난다.
개선 예시 (간단 버전, 같은 핸들러/파일 안에서)
// content-type 확인 + 파싱 예외 처리 + 최소 검증
const ct = req.headers.get("content-type") ?? "";
if (!ct.includes("application/json")) {
return NextResponse.json({ ok: false }, { status: 400 });
}
let body: unknown;
try {
body = await req.json();
} catch {
return NextResponse.json({ ok: false }, { status: 400 });
}
if (typeof body !== "object" || body === null) {
return NextResponse.json({ ok: false }, { status: 400 });
}
// app/actions.ts
"use server";
export async function updateProfile(formData: FormData) {
const name = String(formData.get("name") ?? "");
// DB 업데이트/내부 API 호출 등 서버 권한 로직이 들어오기 쉬워진다
}
// client component
"use client";
import { updateProfile } from "@/app/actions";
export function ProfileForm() {
return (
<form action={updateProfile}>
<input name="name" />
<button type="submit">Save</button>
</form>
);
}
이 형태는 편하다. 대신 “서버 입력 → 실행” 경로가 추가되면서, 취약점/악용의 가능성이 더 많이 열린다.
개선 예시 (간단 버전, 같은 핸들러/파일 안에서)
// 인증/권한 체크 + 입력 정규화
import { cookies } from "next/headers";
const token = cookies().get("token")?.value;
if (!token) throw new Error("Unauthorized");
const name = String(formData.get("name") ?? "").trim();
if (name.length < 1 || name.length > 50) throw new Error("Invalid name");
Node runtime 기준 예시