Llm활용해서 귀찮은 작업 단순화하기

RaonRaon
3 min read

서론

저는 COFFEE DB라는 서비스를 운영하고 있습니다. 이 서비스는 스페셜티 커피 원두 정보를 모아 필터링하여 사용자에게 제공하는 서비스입니다.

서비스 운영 초기에는 외부 웹사이트에서 커피 정보를 하나하나 수작업으로 복사하여 데이터베이스에 입력했습니다. 이 방법을 선택한 이유는 초기에는 데이터의 양이 적었고, 빠르게 서비스를 시작하려는 목적이 있었기 때문입니다.
이 과정은 매우 번거롭고 휴먼 에러가 발생하기 쉬웠기 때문에, 작업을 효율화할 수 있는 다른 방법을 찾아보기로 했습니다.

웹 크롤링

가장 먼저 떠올린 방법은 웹 크롤링이었습니다.
Puppeteer나 Selenium 같은 크롤링 도구를 활용하여 이미 게시된 커피 쇼핑몰에서 정보를 추출하는 방식입니다. 이 방법은 크롤링 코드를 작성하는 데 시간이 오래 걸리긴 하지만, 일단 코드를 작성해두면 URL만 제공해도 전체 정보를 자동으로 수집할 수 있다는 장점이 있습니다.

하지만 단점으로, 여러 쇼핑몰에 대해 각각의 크롤링 코드를 작성해야 했습니다. 또한, 클릭을 통해서만 정보가 노출되는 동적 페이지의 경우 크롤링이 어려웠습니다. 이러한 단점들로 인해 웹 크롤링 방법은 포기하기로 했습니다.

Chat GPT

다음으로 시도한 방법은 ChatGPT를 활용하는 것이었습니다.
웹 크롤링의 복잡함을 줄이고, 다양한 페이지에서 효율적으로 데이터를 수집할 수 있는 대안으로 생각했기 때문입니다.
앞서 언급한 웹 크롤링의 단점인 크롤링 코드 작성과 다양한 페이지 대응 문제를 해결하기 위해, 프롬프트 엔지니어링을 활용하여 HTML 파일을 파싱하고 필요한 데이터를 추출하도록 시도해 보았습니다. 그러나 이 과정에서 오히려 프롬프트 엔지니어링이 크롤링 코드보다 더 많은 리소스를 소모하게 되었습니다.

또한 동적 페이지에 대응하는 데 있어서는 프롬프트 엔지니어링이 오히려 더 복잡하고 비효율적이었습니다. 결과적으로 이 방법도 적합하지 않다는 결론을 내리게 되었습니다.

이미지 분석

마지막으로 선택한 방법은 멀티모달 AI를 활용한 이미지 분석이었습니다. 이전의 방법들은 HTML과 같은 순수 텍스트를 기반으로 데이터를 추출하려 했기 때문에 어려움이 있었습니다.
그래서 웹페이지를 탐색하여 필요한 정보가 담긴 영역을 캡처한 후, 이미지를 전달하여 데이터를 추출하는 방식을 시도했습니다.

먼저 ChatGPT의 웹 인터페이스를 사용해 POC를 진행했는데, 특별한 프롬프트 없이도 이미지와 커피 정보를 추출해 달라는 간단한 요청으로 필요한 정보를 얻을 수 있음을 확인했습니다.

저의 서비스에서는 커피의 재배지, 재배 농장, 재배 고도, 품종, 가공 방법, 한글 이름, 영어 이름, 향미 정보 등 다양한 정보가 필요합니다. few-shot 프롬프팅을 통해 필요한 정보만 얻거나 다양한 프롬프트 기법을 사용할 수도 있지만, 이번에는 구조화된 객체(structured object)를 사용해 데이터를 추출하는 접근 방식을 택해 보았습니다.

구조화된 응답

LLM에 질의와 응답을 받기 위해 vercel의 AI SDK를 활용 했습니다. 해당 SDK를 활용해서 OpenAI의 다양한 모델에 질의할 수있고 API를 통해 답변을 받을 수 있습니다.

먼저 위에서 언급한 필요한 데이터들을 정의해보도록 하겠습니다.

export const coffeeSchema = z.object({
  name_kr: z.string().nullable(),
  name_en: z.string().nullable(),
  processing: z.string().nullable(),
  origin: z.string().nullable(),
  farm: z.string().nullable(),
  notes: z.array(z.string()).nullable(),
  variety: z.string().nullable(),
  altitude: z.string().nullable(),
  nations: z.string().nullable(),
});

스키마 정의 라이브러리인 zod를 활용하여 스키마를 작성 했습니다. 이제 Next.js 의 라우트를 추가해봅시다.

import { openai } from "@ai-sdk/openai";
import { convertToCoreMessages, generateObject } from "ai";

export async function POST(req: Request) {
  const { messages } = await req.json();

  const objectResult = await generateObject({
    model: openai("gpt-4o-mini"),
    schema: coffeeSchema,
    messages: convertToCoreMessages(messages),
  });

  return objectResult.toJsonResponse();
}

request로 OpenAI API로의 message를 받고 요청하고자 하는 모델과 스키마를 넘겨준 뒤 ai sdk의 generateObject 메서드를 실행시키기만 하면 끝입니다.

이제 준비한 이미지와 함께 요청 후 결과를 확인 해보겠습니다

결과를 확인 했을때, 정의한 스키마들에 대해 값들이 구조화되어 오는것을 볼 수 있습니다.

이를 통해 만든 자동화 화면은 다음과 같습니다. 먼저 input 창을 통해 이미지를 입력 받고 해당 이미지를 통해 추출한 커피 정보들을 form 에 자동으로 채워넣은뒤 DB에 밀어넣도록 했습니다.

결론

저는 커피정보를 가져오기 위해 크롤링, 프롬프트 엔지니어링 등 다양한 방법을 시도했지만 결국 이미지분석이 가장 효율적이라는것을 알게 되었습니다.
이를 통해 평군 하나의 페이지를 확인하는데 약 30분정도가 걸리던것에서 5분정도면 하나의 페이지에서 정보를 가져올 수 있게 되었습니다.

생각보다 다양한곳에서 그리고 사소한 부분이라도 불편한 부분이 있다면 AI를 활용하여 작업을 효율화 해보는게 어떨까요?

0
Subscribe to my newsletter

Read articles from Raon directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Raon
Raon