React.js + Next.js/이것저것

GPT 요약 에이전트 구성

진호우 2026. 2. 17. 01:31

Inngest로 백그라운드 잡 붙이고, GPT 요약 에이전트까지 한 번에 가져가기

이 글에서는 Next.js + tRPC 프로젝트에서 버튼 한 번으로 백그라운드 잡 실행부터 GPT 요약까지 이어지게 만든 구성을 정리한다.


Inngest란?

Inngest는 이벤트가 발생하면 등록된 함수를 백그라운드에서 실행해 주는 서비스이다.

Inngest를 쓰는 경우

  • 요청은 빨리 끝내고, 이메일·알림·무거운 연산은 나중에 처리하고 싶을 때
  • tRPC mutation 한 번 호출 → 이벤트 발행 → 백그라운드 함수 실행까지 한 파이프라인으로 묶고 싶을 때

Inngest Agent Kit이란?

Inngest Agent Kit은 Inngest 함수 안에서 LLM 에이전트(요약, 분류, 추출 등)를 실행할 수 있게 해 주는 라이브러리이다. 이번 구성에서는 백그라운드 함수에서 Agent Kit으로 GPT 2단어 요약까지 연결했다.


Inngest 클라이언트

앱에서 이벤트를 보내고, 그 이벤트를 받아 실행할 함수와 연결하는 진입점이 클라이언트이다.

// src/inngest/client.ts
import { Inngest } from 'inngest';

export const inngest = new Inngest({ id: 'vibe-development' });

클라이언트 정리

  • inngest.send({ name, data })로 이벤트를 발행한다.
  • id는 앱·환경 구분용이다. 개발·스테이징·프로덕션마다 다르게 둘 수 있다.
  • 이벤트를 받아 실행할 함수는 아래에서 정의한다.

이벤트가 오면 실행할 함수 정의하기

Inngest에서는 "이 이벤트 이름으로 뭔가 오면 이 함수를 실행해라"라고만 정해 두면 된다. 처음에는 동작 확인용으로 1초 sleep 후 인사 메시지를 반환하는 단순한 함수로 시작했다.

createFunction 인자

  • 메타: 함수 id 등. 대시보드·로그 구분용.
  • 트리거: 어떤 이벤트일 때 실행할지. 예: event: 'test/hello.world'
  • 핸들러: 실제 로직. event.datainngest.send 시 넣은 payload가 들어 있다.

event: 'test/hello.world'로 등록해 두면 이 이름으로 발행된 이벤트가 들어올 때마다 이 함수가 실행된다. step.sleep은 "1초 기다린다"는 단계라서, 실패 후 재시도 시 이 단계부터 다시 돌아간다. 나중에 같은 이벤트는 그대로 두고 함수 본문만 GPT 요약 에이전트로 바꿀 예정이다.

// src/inngest/functions.ts — 처음 버전
export const helloWorld = inngest.createFunction(
  { id: 'hello-world' },
  { event: 'test/hello.world' },
  async ({ event, step }) => {
    await step.sleep('wait-a-moment', '1s');
    return { message: `Hello ${event.data.email}!` };
  }
);

Next.js 쪽에서는 Inngest가 우리 앱을 호출할 진입점만 열어 주면 된다.

// src/app/api/inngest/route.ts
import { serve } from 'inngest/next';
import { inngest } from '@/inngest/client';
import { helloWorld } from '@/inngest/functions';

export const { GET, POST, PUT } = serve({
  client: inngest,
  functions: [helloWorld],
});
  • 로컬에서는 Inngest Dev Server를 띄우면 /api/inngest로 자동 연동된다.

tRPC mutation에서 이벤트만 보내고 바로 응답하기

API는 "이벤트 잘 넘겼다"만 하고, 실제 무거운 작업은 전부 Inngest에게 맡긴다.

// src/trpc/routers/_app.ts — invoke 부분
invoke: baseProcedure
  .input(z.object({ value: z.string() }))
  .mutation(async ({ input }) => {
    await inngest.send({
      name: 'test/hello.world',
      data: {
        value: input.value,
      },
    });
    return { ok: 'success' };
  }),

invoke의 역할

  • 사용자가 보낸 한 줄 텍스트 value를 그대로 이벤트 payload에 실어 보낸다.
  • inngest.send가 끝나면 곧바로 { ok: 'success' }만 반환한다. 1초를 기다리거나 GPT 호출이 끝날 때까지 기다리지 않는다.
  • 이벤트 이름과 data.value만 맞춰 두었기 때문에, helloWorld를 "2단어 요약"으로 바꿔도 이 mutation 코드는 그대로 둬도 된다.

화면에서 입력 한 줄 + 버튼 한 번으로 호출하기

사용자가 입력한 문자열을 그대로 invoke mutation에 넘기는 페이지이다.

// src/app/page.tsx
'use client';

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useTRPC } from '@/trpc/client';
import { useMutation } from '@tanstack/react-query';
import { useState } from 'react';

export default function Page() {
  const [value, setValue] = useState('');

  const trpc = useTRPC();
  const invoke = useMutation(trpc.invoke.mutationOptions({}));

  return (
    <div className="p-4 max-w-7xl mx-auto">
      <Input value={value} onChange={(e) => setValue(e.target.value)} />
      <Button onClick={() => invoke.mutate({ value })}>
        Invoke Background Job
      </Button>
    </div>
  );
}

동작

  • Input에 긴 문장을 넣고 "Invoke Background Job"을 누르면 tRPC invoke가 호출된다.
  • 서버에서 inngest.send('test/hello.world', { value })가 실행된 뒤 백그라운드에서 helloWorld가 돌아간다.
  • mutation 성공·실패는 React Query와 (원하면) Toaster로 처리하면 된다.
  • 이제 "helloWorld가 실제로 뭘 하느냐"만 바꾸면 된다.

백그라운드 함수를 GPT 2단어 요약 에이전트로 바꾸기

@inngest/agent-kit을 쓰면 Inngest 함수 안에서 LLM 에이전트를 그대로 실행할 수 있다. 기존의 1초 sleep과 인사 메시지는 빼고, GPT-4o로 "2단어 요약"만 하도록 바꿨다.

// src/inngest/functions.ts — 최종
import { inngest } from './client';
import { openai, createAgent } from '@inngest/agent-kit';

export const helloWorld = inngest.createFunction(
  { id: 'hello-world' },
  { event: 'test/hello.world' },
  async ({ event }) => {
    const summarizer = createAgent({
      name: 'summarizer',
      system: 'You are an expert summarizer. You summarize in 2 words.',
      model: openai({ model: 'gpt-4o' }),
    });

    const { output } = await summarizer.run(
      `Summarize the following text: ${event.data.value}`
    );

    console.log(output);
    return { output };
  }
);

Agent Kit 사용 시

  • createAgent에 이름·시스템 프롬프트·모델(gpt-4o)만 넣어 준다.
  • summarizer.run에는 사용자가 입력한 event.data.value를 "Summarize the following text: ..."에 넣어서 넘긴다.
  • 반환되는 output이 2단어 요약 결과이다. 로그로 찍어 두면 Inngest 대시보드나 서버 로그에서 확인하기 좋다.
  • 이벤트 이름 test/hello.world와 payload 구조(value)는 앞에서 만든 tRPC·페이지와 그대로 맞춰져 있어서, tRPC나 페이지 코드는 손대지 않고 "백그라운드에서 GPT 요약"까지 연결된다.

전체 흐름

입력 필드에 문장을 넣고 버튼을 누른다 → tRPC invoke 호출 → 서버는 inngest.send('test/hello.world', { value })만 한 뒤 바로 응답 → Inngest가 이벤트를 받아 helloWorld 실행 → Agent Kit이 value를 2단어로 요약해 output으로 반환.

정리

  • 같은 이벤트·함수 하나로 "백그라운드 잡"부터 "GPT 요약 에이전트"까지 한 번에 가져갈 수 있다.
  • 이메일 발송, 알림, 다른 LLM 태스크도 같은 패턴으로 이벤트만 추가하고 함수만 새로 정의하면 된다.
  • OpenAI API 키 등 환경 변수는 Inngest·Agent Kit 문서대로 설정하면 된다.