WordPress용 AI 대체 텍스트 (Alt Text) 생성기를 만든 과정과 배운 점
요약
WordPress 사이트의 접근성과 SEO를 개선하기 위해 AI 비전 서비스를 활용한 대체 텍스트 생성 플러그인을 개발한 과정과 기술 스택을 소개합니다. 개발 과정에서 겪은 데이터 분석의 오류와 이벤트 설계의 중요성을 다룹니다.
핵심 포인트
- AI 비전 서비스를 활용해 이미지의 대체 텍스트를 자동 생성하고 WordPress에 저장
- PHP, Node.js, Supabase, Stripe 등 다양한 기술 스택을 활용한 아키텍처 구축
- 텔레메트리 분석 시 작업(Job) 단위와 항목(Item) 단위의 명확한 이벤트 구분 필요성
접근성 (Accessibility)은 모두가 중요하다고 동의하는 분야 중 하나이지만, 개발 우선순위에서는 밀려나는 경우가 많습니다. 대체 텍스트 (Alt text)가 좋은 예입니다.
모든 이미지에 설명적인 대체 텍스트를 추가하면 접근성을 향상시키고, 스크린 리더 (screen-reader) 사용자가 페이지를 이해하는 데 도움을 주며, SEO (검색 엔진 최적화)를 지원할 수 있습니다. 하지만 수백 또는 수천 개의 이미지가 있는 사이트의 경우, 이를 수동으로 작성하는 것은 반복적이며 소홀해지기 쉽습니다.
이러한 문제로 인해 저는 WordPress용 AI 대체 텍스트 생성기를 만들게 되었습니다.
문제점
WordPress는 이미지를 업로드하기 쉽게 만들어 주지만, 대규모 미디어 라이브러리 전반에 걸쳐 정확한 대체 텍스트를 유지하는 것은 별개의 문제입니다. 사이트 소유자는 보통 다음 세 가지 상황 중 하나에 직면합니다:
- 이미지에 대체 텍스트가 없음
- 기존 대체 텍스트가 모호하거나 중복됨
- 수동으로 검토하기에는 이미지가 너무 많음
저는 사이트 소유자의 통제권을 유지하면서도 작업량을 줄일 수 있는 도구를 원했습니다.
플러그인의 기능
- 개별 이미지에 대한 대체 텍스트 생성
- 누락된 대체 텍스트가 있는 여러 이미지를 일괄 처리 (bulk)
- 생성된 텍스트를 WordPress에 직접 저장
- 사용량 및 생성 제한 추적
- 사용자가 결과물을 신뢰하기 전에 검토할 수 있도록 허용
목표는 인간의 판단을 제거하는 것이 아니라, 초안 작성을 획기적으로 빠르게 만드는 것입니다.
아키텍처 (Architecture)
이 프로젝트는 단순한 WordPress 플러그인 그 이상으로 성장했습니다. 현재 스택은 다음과 같습니다:
- WordPress / PHP — 플러그인 인터페이스 및 미디어 라이브러리 통합
- JavaScript — 대화형 관리자 워크플로
- Node.js — 백엔드 API
- Render — 백엔드 호스팅
- Supabase — 계정, 권한 및 사용 데이터
- Stripe — 구독 및 결제
- PostHog — 제품 분석
- AI 비전 서비스 (An AI vision service) — 이미지 분석 및 대체 텍스트 생성
WordPress 플러그인은 인증된 생성 요청을 백엔드로 보냅니다. 백엔드는 사용자를 검증하고, 사용 가능한 할당량 (quota)을 확인하며, 이미지를 처리하고, 사용 결과를 저장한 뒤, 생성된 설명을 반환합니다.
결제(billing)의 경우, Stripe 웹훅(webhooks)이 구독 상태를 확인하는 권위 있는 소스(authoritative source)입니다. 프론트엔드(frontend)는 사용자가 체크아웃(checkout) 페이지에서 돌아왔다는 이유만으로 결제가 성공했다고 가정해서는 안 됩니다.
도전 과제 1: 작업(Job) 단위 vs 이미지(Image) 단위 분석
가장 유용한 교훈 중 하나는 텔레메트리(telemetry)에서 얻었습니다. 어느 시점에 분석 데이터가 다음과 같이 나타났습니다:
- 생성 시작(generation starts) 4건
- 생성 완료(generation completions) 13건
이는 완료 이벤트가 중복된 것처럼 보였습니다. 실제 문제는 두 지표가 서로 다른 것을 측정하고 있었다는 점이었습니다. 생성 '시작(start)'은 하나의 일괄 작업(bulk job)을 나타낼 수 있는 반면, '완료(completion)' 이벤트는 해당 작업 내의 각 이미지를 나타냈습니다. 이 둘을 직접 비교하는 것은 쇼핑 횟수와 구매한 품목 수를 비교하는 것과 같았습니다.
저는 모호한 생명주기(lifecycle)를 명시적인 이벤트(explicit events)로 교체했습니다:
generation_job_started(생성 작업 시작)generation_item_completed(생성 항목 완료)generation_job_completed(생성 작업 완료)generation_job_failed(생성 작업 실패)
이제 각 작업은 generation_run_id를 가지며, 각 이미지는 고유한 generation_item_id를 가질 수 있습니다. 이를 통해 다음과 같은 유용한 질문에 답할 수 있게 되었습니다:
- 얼마나 많은 작업이 시작되었는가?
- 얼마나 많은 이미지가 처리되었는가?
- 얼마나 많은 작업이 완전히 성공했는가?
- 일괄 작업 내에서 얼마나 많은 항목이 실패했는가?
- 각 작업에 시간이 얼마나 걸렸는가?
명확한 이벤트 의미론(event semantics)을 구축하는 것은 이벤트를 수집하는 것만큼이나 중요하다는 사실을 깨달았습니다.
도전 과제 2: 중복 텔레메트리 방지
분석(analytics) 코드는 예상보다 더 자주 실행될 수 있습니다. 이벤트는 다음과 같은 이유로 중복될 수 있습니다:
- 반복적인 버튼 클릭
- 컴포넌트 재렌더링 (re-renders)
- 재시도 (retries)
- 다중 이벤트 리스너 (multiple event listeners)
- 프론트엔드와 백엔드에서 동일한 이벤트 발생
- 반복적인 웹훅(webhook) 전달
이벤트를 재시도에 안전하게(retry-safe) 만들기 위해, 저는 안정적인 PostHog $insert_id 값을 추가하고 어떤 시스템이 각 이벤트를 소유하는지 정의했습니다:
- 프론트엔드(frontend)는 사용자의 의도(예: 업그레이드 버튼 클릭)를 기록합니다.
- 백엔드(backend)는 성공적인 Stripe Checkout Session 생성을 기록합니다.
- Stripe 웹훅(webhooks)은 체크아웃 완료 및 구독 활성화를 결정합니다.
- 백엔드(backend)는 대체 텍스트(alt text)가 영구 저장된 후에만 이를 기록합니다.
이는 앱의 서로 다른 부분에 의해 동일한 비즈니스 액션이 두세 번 중복 집계되는 것을 방지합니다.
도전 과제 3: 여러 시스템에 걸친 액션 추적하기
단일 체크아웃(checkout) 과정은 다음과 같은 경로를 거칠 수 있습니다:
WordPress → Backend API → Render logs → Stripe Checkout → Stripe webhook → Supabase → PostHog
공통 식별자(shared identifier)가 없다면, 이 여정을 디버깅하는 것은 매우 고통스러운 일입니다. 그래서 저는 다음과 같은 식별자들을 도입했습니다:
correlation_idcheckout_attempt_idgeneration_run_idsignup_attempt_idsession_idsite_install_id
이를 통해 플러그인에서 시작된 하나의 액션이 백엔드를 거쳐 외부 서비스까지 이어지는 과정을 추적할 수 있습니다. 이는 특히 체크아웃이 중단되거나 로그인이 실패했을 때 매우 유용합니다. 타임스탬프(timestamp)를 기준으로 서로 연결되지 않은 시스템들을 일일이 검색하는 대신, 동일한 상관관계 값(correlation value)이 전체 여정에서 나타나게 됩니다.
도전 과제 4: 안전한 인증 텔레메트리 (telemetry)
일반적인 login_failed 이벤트는 별로 유용하지 않습니다. 원인이 잘못된 자격 증명(credentials)인지, 비활성화된 계정인지, 속도 제한(rate limiting)인지, 네트워크 타임아웃(network timeout)인지, 사용 불가능한 API인지, 토큰 생성 실패인지, 아니면 세션 저장 실패인지 알려주지 않기 때문입니다.
저는 공개적인 에러 메시지는 일반적인 수준으로 유지하면서, 통제된 내부 에러 분류 체계(taxonomy)를 추가했습니다. 상세한 공개 메시지는 특정 계정의 존재 여부를 드러낼 수 있기 때문에 이러한 구분은 매우 중요합니다. 이제 시스템은 다음과 같은 안전한 내부 코드들을 기록합니다:
invalid_credentialsaccount_disabledrate_limitednetwork_timeoutapi_unavailabletoken_creation_failedsession_creation_failedunknown_auth_error
텔레메트리(telemetry)가 전송되기 전에 비밀번호, 토큰, API 키, 그리고 가공되지 않은 서버 응답(raw server responses)은 모두 제거됩니다.
도전 과제 5: 기본 설정에 의한 프라이버시 (Privacy by default)
제품 분석(product analytics)은 놀라울 정도로 빠르게 프라이버시 문제가 될 수 있습니다. 현재 텔레메트리 계층에서는 다음과 같은 필드들을 제거합니다:
- 이메일 주소 (email addresses)
- 라이선스 키 (licence keys)
- 액세스 토큰 (access tokens)
- API 키 (API keys)
- 대체 텍스트 내용 (alt text content)
- 이미지 파일명 (image filenames)
- 이미지 URL (image URLs)
- 프롬프트 (prompts)
- 가공되지 않은 API 응답 (raw API responses)
PostHog 세션 레코딩 (session recording)은 명시적으로 활성화하지 않는 한 비활성화되어 있습니다. 분석 (analytics) 데이터는 사용자가 작업 중이던 콘텐츠를 복제하지 않으면서도 어떤 일이 일어났는지를 설명할 수 있어야 합니다.
내가 다르게 했을 점
만약 처음부터 다시 시작한다면, 대시보드를 구축하기 전에 분석 계약 (analytics contract)을 정의하고, 각 이벤트를 다음과 같이 문서화했을 것입니다:
- 정확한 트리거 (trigger)
- 프론트엔드 (frontend) 소유인지 백엔드 (backend) 소유인지 여부
- 사용자 (user), 세션 (session), 작업 (job), 항목 (item) 또는 트랜잭션 (transaction)당 한 번씩 발생하는지 여부
- 필수 속성 (properties)
- 중복 제거 전략 (deduplication strategy)
- 스키마 버전 (schema version)
깔끔한 텔레메트리 (telemetry)를 사후에 적용하는 것도 가능하지만, 처음부터 제대로 설계하는 것보다 훨씬 더 많은 작업이 필요합니다. 또한 첫날부터 상관관계 ID (correlation IDs)를 추가했을 것입니다. 이는 구현 비용이 저렴하면서도 앱이 여러 서비스로 확장될 때 엄청난 가치를 발휘합니다.
배운 점
가장 큰 교훈은 AI 생성 기능을 구축하는 것이 제품의 일부분일 뿐이라는 점입니다. 프로덕션 환경에 적합한 (production-ready) 플러그인은 다음과 같은 요소들도 필요합니다:
- 신뢰할 수 있는 인증 (authentication)
- 구독 및 할당량 관리 (subscription and quota management)
- 멱등성 웹훅 (idempotent webhooks)
- 개인정보 보호가 안전한 분석 (privacy-safe analytics)
- 명확한 이벤트 의미론 (event semantics)
- 오류 분류 (error classification)
- 엔드 투 엔드 관측성 (end-to-end observability)
- 기존 텔레메트리를 위한 마이그레이션 계획 (migration plan)
AI 부분이 헤드라인 기능(headline feature)일 수는 있지만, 신뢰성(reliability)이야말로 그것을 사용 가능한 제품으로 만드는 핵심입니다.
다음 단계
- 업데이트된 플러그인 텔레메트리 출시
- 통합된 백엔드 변경 사항 배포
- 실제 프로덕션 이벤트 검증
- 완전히 상관관계가 연결된 테스트 결제 실행
- 레거시 이벤트를 제거하기 전 기존 분석과 새로운 분석 비교
또한 생성된 대체 텍스트 (alt text)가 WordPress 내에서 검토되고 적용되는 방식을 지속적으로 개선하고 있습니다.
다른 WordPress, 접근성 (accessibility), 또는 SaaS 개발자분들의 의견을 듣고 싶습니다. 플러그인을 프로덕션 환경에 적합하게 만드는 과정에서 가장 어려웠던 점은 무엇인가요?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기