본문으로 건너뛰기
개발 머꼬
개발 노트Next.js
hohyeon.dev20

로그를 남기느라 응답이 200ms씩 느려지던 것을 after로 걷어낸 과정

  • #Engineering Note
  • #Next.js
  • #Performance

문제 발생

페이지에서 조회 이벤트를 기록하고 있었습니다.

export default async function Page({ params }) {
  const post = await getPost(params.slug);
  await logView(post.id);       // 분석 서버 응답까지 기다린다
  return <Article post={post} />;
}

분석 서버가 느린 날에는 그만큼 사용자 화면도 늦어졌습니다. await를 떼면 이번에는 서버리스 환경에서 요청이 끝나며 로그가 유실됐습니다.

원인 분석

"응답 후에 해야 하는 일"을 표현할 방법이 없었던 것입니다. await를 붙이면 응답을 막고, 떼면 실행이 보장되지 않습니다.

Next.js는 이 용도로 after를 제공합니다. 문서 설명 그대로 — after는 응답(또는 프리렌더)이 끝난 뒤에 실행할 작업을 예약합니다. 로깅이나 분석처럼 응답을 막으면 안 되는 작업과 부수 효과에 유용합니다.

사용 가능한 위치도 명확합니다 — Server Component(generateMetadata 포함), Server Function, Route Handler, Proxy입니다.

한 가지 중요한 성질이 있습니다 — after는 Request-time API가 아니며, 호출한다고 해서 라우트가 동적으로 바뀌지 않습니다. 정적 페이지에서 쓰면 빌드 시점이나 재검증 시점에 콜백이 실행됩니다.

해결 방안

  1. 응답 뒤로 미룹니다.
import { after } from "next/server";

export default async function Page({ params }) {
  const post = await getPost(params.slug);
  after(() => logView(post.id));
  return <Article post={post} />;
}
  1. Server Component 안에서는 요청 API를 콜백 밖에서 읽습니다. 문서가 명확히 합니다 — Server Component(페이지·레이아웃·generateMetadata)에서는 after 콜백 안에서 cookiesheaders를 쓸 수 없고, 호출하면 런타임 오류가 납니다. 필요하면 미리 읽어 값으로 넘깁니다.
const userAgent = (await headers()).get("user-agent") ?? "unknown";
after(() => logView(post.id, userAgent));
  1. Route Handler와 Server Function에서는 콜백 안에서 직접 읽어도 됩니다. 문서가 이 차이를 명시합니다 — 뮤테이션 뒤 활동 로깅에 유용한 조합입니다.

  2. 실패해도 실행된다는 점을 감안합니다. 문서 그대로 — 응답이 성공적으로 끝나지 않아도, 오류가 던져지거나 notFound/redirect가 호출되어도 after는 실행됩니다. "성공했을 때만" 남겨야 하는 기록이라면 콜백 안에서 그 조건을 확인합니다.

  3. 작업 큐의 대체재로 쓰지 않습니다. 문서 설명대로 after는 라우트의 최대 실행 시간 안에서 동작합니다. 몇 분씩 걸리는 작업은 여전히 별도 워커의 일입니다.

  4. 셀프 호스팅 환경을 확인합니다. Node.js 서버와 Docker에서는 지원되지만 정적 export에서는 동작하지 않습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.