이 단계별 가이드에서는 Upstash QStash, Upstash Redis, Next.js Server Actions 및 Vercel을 사용하여 트윗 스케줄러를 구축한 방법에 대해 설명합니다. Twitter 게시물을 예약하면 일관된 존재감을 유지하고 최적의 시간에 청중과 소통하며 콘텐츠 전략을 효율적으로 관리하는 데 도움이 됩니다.
전제조건
다음이 필요합니다:
- Node.js 18 이상
- Upstash 계정
- 트위터 계정
- Vercel 계정
기술 스택
이 가이드에서는 다음 기술이 사용됩니다:
단계
이 가이드를 완료하고 자신만의 트윗 스케줄러를 배포하려면 다음 단계를 따라야 합니다:
- Upstash Redis 설정
- Upstash QStash 설정
- Twitter 개발자 애플리케이션 설정
- 새 Next.js 애플리케이션 만들기
- Twitter OAuth 2.0으로 사용자 인증 구현
- 트윗 예약을 위한 사용자 인터페이스 구축
- Upstash QStash를 사용하여 트윗 예약
- Vercel에 배포
Upstash Redis 설정
Upstash 계정을 생성하고 로그인하면 Redis 탭으로 이동하여 데이터베이스를 생성하게 됩니다.


데이터베이스를 생성한 후 REST API 섹션을 찾을 때까지 아래로 스크롤하고 .env를 선택합니다. 버튼. 콘텐츠를 복사하여 안전한 곳에 저장하세요.

Upstash QStash 설정
주어진 간격으로 예약 끝점에 대한 POST 요청을 예약하려면 QStash를 사용합니다. QStash 탭으로 이동하여 Request Builder 탭까지 아래로 스크롤하세요.

QStash URL, TOKEN, 현재 서명 키 및 다음 서명 키를 복사하여 안전한 곳에 저장하세요.

Twitter 개발자 애플리케이션 설정
Twitter OAuth 2.0으로 인증을 설정하기 위해 Twitter 개발자 포털에서 애플리케이션을 생성합니다. Twitter 애플리케이션을 설정하려면 다음을 수행하세요:
- Twitter의 개발자 포털> 프로젝트를 엽니다.
- 프로젝트를 생성합니다.
- 애플리케이션 설정의 설정 탭으로 이동하여 다음을 수행하세요.
Read and write and Direct message선택 앱 권한에서 .Web App, Automated App or Bot선택 앱 유형 .http://localhost:3000/api/auth/callback/twitter을 입력하세요. 콜백 URI/재전송 URL .
Keys and tokens로 이동 애플리케이션 설정 탭에서 아래로 스크롤하여 다음을 수행합니다.Client ID을 복사하세요.TWITTER_CLIENT_ID로 안전한 곳에 보관하세요. .Client Secret를 복사하세요.TWITTER_CLIENT_SECRET으로 안전한 곳에 보관하세요. .
이것이 OAuth 2.0용 Twitter 개발자 애플리케이션을 성공적으로 설정하는 데 필요한 전부입니다.
새 Next.js 애플리케이션 만들기
새로운 Next.js 프로젝트를 만들어 시작해 보겠습니다. 터미널을 열고 다음 명령을 실행하세요:
npx create-next-app@latest schedule-qstash-queue-upstash 메시지가 나타나면 다음을 선택하세요:
YesTypeScript를 사용하라는 메시지가 표시될 때.NoESLint를 사용하라는 메시지가 표시되면YesTailwind CSS를 사용하라는 메시지가 표시되면Nosrc/를 사용하라는 메시지가 표시되면 디렉토리.Yes앱 라우터를 사용하라는 메시지가 표시될 때.No기본 가져오기 별칭(@/*)을 사용자 정의하라는 메시지가 표시되면 ).
완료되면 프로젝트 디렉터리로 이동하고 다음 명령을 실행하여 개발 모드에서 앱을 시작하세요.
cd schedule-qstash-queue-upstash
npm run dev 앱은 localhost:3000에서 실행되어야 합니다.
이제 .env을 생성하세요. 프로젝트 루트에 있는 파일입니다. 위 섹션에서 저장한 항목을 추가하게 됩니다.
다음과 같아야 합니다:
# .env
# Obtained from the steps as above
# Twitter Environment Variables
TWITTER_CLIENT_ID="..."
TWITTER_CLIENT_SECRET="..."
TWITTER_AUTH_CALLBACK_URL="http://localhost:3000/api/auth/callback/twitter"
# Upstash Environment Variables
UPSTASH_REDIS_REST_URL="https://...upstash.io"
UPSTASH_REDIS_REST_TOKEN="...="
QSTASH_URL="https://qstash.upstash.io/v2/publish/"
QSTASH_TOKEN="...="
QSTASH_CURRENT_SIGNING_KEY="sig_..."
QSTASH_NEXT_SIGNING_KEY="sig_..." shadcn/ui 구성요소 통합
사용자 인터페이스의 프로토타입을 빠르게 만들기 위해 shadcn/ui를 설정합니다. Next.js로. shadcn/ui 앱에 복사하여 붙여넣을 수 있는 아름답게 디자인된 구성 요소 모음입니다. 터미널 창에서 아래 명령을 실행하여 shadcn/ui 설정을 시작하세요. :
npx shadcn-ui@latest init
components.json을 구성하려면 몇 가지 질문을 받게 됩니다. , 다음을 선택하세요:
YesTypeScript를 사용하라는 메시지가 표시될 때.Default사용할 스타일을 선택하라는 메시지가 표시되면Slate기본 색상을 선택하라는 메시지가 표시되면app/globals.css전역 CSS 파일을 입력하라는 메시지가 표시될 때.yes색상에 CSS 변수를 사용하라는 메시지가 표시되는 경우Leave blank맞춤 순풍 접두어를 입력하라는 메시지가 표시되는 경우tailwind.config.tstailwind.config.js의 위치를 입력하라는 메시지가 표시될 때.@/components구성 요소의 별칭을 구성하라는 메시지가 표시되면@/lib/utilsutils의 별칭을 구성하라는 메시지가 표시되면YesReact Server 구성 요소 사용을 선택하라는 메시지가 표시되는 경우YesComponents.json에 구성 쓰기를 진행하라는 메시지가 표시되는 경우
이 작업이 완료되면 Next.js 애플리케이션에 React 구성 요소를 쉽게 추가할 수 있는 CLI가 설정되었습니다. 그런 다음 터미널 창에서 아래 명령을 실행하여 버튼, 입력, 텍스트 영역, 팝오버, 달력 및 토스트 요소를 가져옵니다.
npx shadcn-ui@latest add button
npx shadcn-ui@latest add input
npx shadcn-ui@latest add textarea
npx shadcn-ui@latest add toast
npx shadcn-ui@latest add popover
npx shadcn-ui@latest add calendar
이 작업이 완료되면 이제 ui가 표시됩니다. app/components 내부 디렉토리 button.tsx이 포함된 디렉토리 , input.tsx , calendar.tsx , input.tsx , popover.tsx , textarea.tsx , toast.tsx , toaster.tsx 및 use-toast.ts .
다음으로 app/layout.tsx를 엽니다. 파일을 작성하고 다음을 추가하세요:
+ // File: app/layout.tsx
import './globals.css'
+ import { cn } from '@/lib/utils'
import type { Metadata } from 'next'
+ import { Inter } from 'next/font/google'
+ import { Toaster } from '@/components/ui/toaster'
+ const fontSans = Inter({
+ subsets: ['latin'],
+ variable: '--font-sans',
+ })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body
+ className={cn(fontSans.variable, 'min-w-screen flex min-h-screen flex-col items-center justify-center bg-background font-sans antialiased')}
>
{children}
+ <Toaster />
</body>
</html>
)
}
위의 코드 변경에서 Toaster를 가져왔습니다. 구성요소(shadcn/ui에 의해 생성됨) ), 전체 Next.js 애플리케이션에 존재하는지 확인하세요. useToast를 통해 코드 어디에서나 토스트 알림을 표시할 수 있습니다. 훅.
예약된 트윗을 저장할 Upstash 대기열 만들기
이 섹션에서는 Upstash 대기열을 사용하여 예약된 트윗 정보를 저장하는 방법을 알아봅니다. Next.js 서버 작업을 사용하여 API 경로 대신 함수로 호출되는 서버 측 코드를 생성하는 방법을 배우게 됩니다.
먼저 터미널 창에서 다음을 실행하여 Upstash SDK를 설치하세요:
npm install @upstash/qstash @upstash/queue @upstash/redis@1.28.0 위 명령은 다음 패키지를 설치합니다:
@upstash/qstash:HTTP 요청을 통해 Upstash QStash 인스턴스와 상호작용하는 SDK.@upstash/queue:Upstash Redis가 지원하는 스트림 기반 메시지 대기열을 관리하기 위한 SDK입니다.@upstash/redis:Upstash REST API를 기반으로 구축된 Redis와 HTTP 요청을 통해 상호작용하는 SDK입니다.
Upstash Redis 및 Upstash 대기열 클라이언트 초기화
위에 설치된 라이브러리를 사용하여 Upstash 대기열과 상호작용하려면 lib/upstash.ts 파일을 생성하세요. 다음 코드를 사용하세요:
// File: lib/upstash.ts
import { Redis } from '@upstash/redis'
import { Queue } from '@upstash/queue'
export const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL as string,
token: process.env.UPSTASH_REDIS_REST_TOKEN as string,
})
export const queue = new Queue({
redis,
queueName: 'tweets',
concurrencyLimit: 5,
}) 위의 코드는 다음을 수행합니다:
Redis을 가져옵니다. 및Queue패키지에서 내보낸 클래스입니다.redis를 내보냅니다. 요청 인증 토큰을 사용하여 Upstash Redis URL을 가리키는 인스턴스입니다.queue을 내보냅니다. 위에서 생성된 Upstash Redis를 가리키는 인스턴스입니다. 대기열 이름을tweets로 설정합니다. 그리고concurrencyLimit최대 5개까지 동시에 메시지를 처리할 수 있습니다.
대기열 인스턴스를 사용하여 트윗 텍스트와 트윗 날짜를 받아 향후 처리를 위해 트윗 개체를 대기열에 푸시하는 Next.js 서버 작업을 생성합니다. app/schedule.server.tsx 파일 만들기 다음 코드를 사용하세요:
// File: app/schedule.server.tsx
'use server'
import { queue } from '@/lib/upstash'
import type { FormProps } from './form'
export async function schedule(_: any, formData: FormData): Promise<FormProps> {
try {
const tweet_text = formData.get('tweet_text') as string
const tweet_date = formData.get('tweet_date') as string
const now = new Date().getTime()
const delay = new Date(tweet_date).getTime() - now
await queue.sendMessage({ tweet_text, tweet_date }, delay)
return { ok: true, tweet_date }
} catch (e) {
console.log(e)
return { ok: false }
}
} 위의 코드는 다음을 수행합니다:
queue를 가져옵니다. 인스턴스schedule라는 서버 작업을 내보냅니다. , 예정된 트윗의 텍스트와 날짜가 포함된 양식 제출을 허용합니다.- 그 때부터 트윗이 예정된 시간까지의 시간을 계산합니다.
- 위에서 계산된 시간으로 지연이 설정된 날짜와 텍스트가 포함된 트윗 개체를 삽입합니다.
이를 통해 트윗이 예정된 경우에만 대기열에 푸시되도록 보장했습니다. 이렇게 하면 대기열 상태를 유지하는 프로세스가 쉬워지고 만 특정 날짜에 예정된 트윗은 대기열에서 확인할 수 있습니다.
OAuth 2.0 PKCE 흐름을 사용하여 사용자를 대신하여 트윗할 수 있는 권한을 획득하는 단계로 넘어가겠습니다.
Twitter OAuth 2.0으로 사용자 인증 구현
이 섹션에서는 Twitter OAuth 클라이언트를 구성하고 인증 작업을 위해 Twitter SDK에서 제공하는 도우미 기능을 사용하여 애플리케이션에서 Twitter OAuth 2.0 인증 흐름을 설정하는 방법을 알아봅니다. 인증 흐름에는 권한 부여 및 콜백 엔드포인트 생성이 포함되며, 이를 통해 사용자는 액세스 권한을 부여하고 토큰을 받을 수 있으며, 액세스 토큰은 Upstash Redis에 저장됩니다. 또한 Upstash Redis에 유효한 액세스 토큰이 있음을 나타내는 엔드포인트를 통해 인증 상태를 생성하게 됩니다.
먼저 터미널 창에서 아래 명령을 실행하여 Twitter OAuth 2.0 인증 구현에 필요한 라이브러리를 설치하세요.
npm install twitter-api-sdk 위 명령은 다음 패키지를 설치합니다:
twitter-api-sdk:Twitter API용 TypeScript SDK입니다.
트위터 인증 클라이언트 생성
Twitter API의 복잡성을 탐구하지 않고 인증 URL을 생성하려면 Twitter SDK의 인증 클라이언트를 사용합니다. twitter.ts 파일 만들기 lib 내부 다음 코드가 포함된 디렉토리:
// File: lib/twitter.ts
import { auth } from 'twitter-api-sdk'
export const authClient = new auth.OAuth2User({
client_id: process.env.TWITTER_CLIENT_ID as string,
callback: process.env.TWITTER_AUTH_CALLBACK_URL as string,
client_secret: process.env.TWITTER_CLIENT_SECRET as string,
scopes: ['tweet.write', 'tweet.read', 'offline.access', 'users.read'],
}) 위의 코드는 Twitter SDK에서 인증 도우미를 가져오는 것으로 시작됩니다. 그런 다음 인증 URL을 생성하고 인증된 사용자에게 액세스 토큰을 요청하는 도우미 기능을 제공하여 Twitter API 구문 학습 시간을 절약할 인증 클라이언트를 내보냅니다.
Twitter 인증 URL 준비
OAuth 2.0 흐름의 첫 번째 단계는 사용자가 인증 URL로 리디렉션되는 것입니다. 인증 URL은 클라이언트 애플리케이션에 권한을 부여하여 인증 프로세스를 시작하기 위해 사용자가 리디렉션되는 OAuth 2.0 서버에서 제공하는 엔드포인트입니다. 일반적으로 사용자에게는 여러 선택 항목(예:Continue with Twitter)이 표시됩니다. , Continue with Google 등)을 로그인/업 화면에서 확인한 후 플랫폼(여기서는 Twitter)에서 호스팅하는 인증 화면으로 이동합니다.
app/api/auth/twitter/route.ts 파일 만들기 다음 코드를 사용하세요:
// File: app/api/auth/twitter/route.ts
export const dynamic = 'force-dynamic'
import { NextResponse } from 'next/server'
import { authClient } from '@/lib/twitter'
export async function GET() {
// Obtain an authorization URL from Twitter
const authUrl = authClient.generateAuthURL({
state: 'state',
code_challenge: 'challenge',
code_challenge_method: 'plain',
})
// Return with a 303 as a redirect to the authorization URL
return NextResponse.redirect(authUrl, 303)
} 위의 코드는 다음을 수행합니다:
NextResponse를 가져옵니다. Web Response API를 확장하는 도우미 함수입니다.authClient가져오기 이전에 생성되었습니다.GET를 내보냅니다./api/auth/twitter에서 들어오는 GET 요청에 응답하는 HTTP 핸들러 .generateAuthURL를 사용하여 인증 URL을 생성합니다. Twitter SDK의 도우미 기능입니다.redirect을 사용하여 생성된 승인 URL로 리디렉션합니다. NextResponse의 메소드입니다.
사용자가 Twitter 애플리케이션에 액세스 권한을 부여할 때 요청에 응답하는 엔드포인트를 생성해 보겠습니다.
승인 콜백 URL 준비
OAuth 2.0 흐름의 두 번째 단계는 인증 플랫폼의 콜백에 응답하는 것입니다. Twitter에서 들어오는 인증 콜백 요청을 처리하려면 액세스 권한을 부여한 후 사용자가 리디렉션되는 엔드포인트를 애플리케이션에 구성해야 합니다. 이 콜백 URL은 인증 코드를 수신하는 데 필수적이며 애플리케이션이 Upstash Redis에서 얻은 액세스 토큰을 저장할 수 있도록 합니다. 해당 토큰을 사용하면 애플리케이션의 API를 통해 트윗을 자동화할 수 있습니다.
app/api/auth/callback/twitter/route.ts 파일 만들기 다음 코드를 사용하세요:
// File: app/api/auth/callback/twitter/route.ts
export const dynamic = 'force-dynamic'
import { redis } from '@/lib/upstash'
import { NextResponse } from 'next/server'
import { authClient } from '@/lib/twitter'
export async function GET(request: Request) {
// Look for the callback URL to contain code
const code = new URL(request.url).searchParams.get('code')
// If no code query param found, return 403
if (!code) return NextResponse.json({}, { status: 403 })
// If code query param found, create another authorization URL to update internal code_verifier
authClient.generateAuthURL({
state: 'state',
code_challenge: 'challenge',
code_challenge_method: 'plain',
})
// Obtain the access_token to use it for making requests in the future
const {
token: { access_token },
} = await authClient.requestAccessToken(code)
// Save the access_token in Upstash
await redis.set('twitter_oauth_access_token', access_token)
// Return back to homepage
return NextResponse.redirect(new URL('/', request.url), 303)
} 위의 코드는 다음을 수행합니다:
redis을 가져옵니다. Upstash Redis를 사용하는 인스턴스입니다.NextResponse를 가져옵니다. Web Response API를 확장하는 도우미 함수입니다.authClient를 가져옵니다. 이전에 생성되었습니다.GET내보내기/api/auth/callback/twitter에서 들어오는 GET 요청에 응답하는 HTTP 핸들러 .code구조해제 콜백 URL의 쿼리 매개변수- SDK에서 생성된 내부 상태를 업데이트하기 위해 해킹으로 이전과 같은 인증 URL을 생성합니다.
requestAccessToken에 전화를 겁니다. Twitter SDK의 기능을 통해 API를 통해 트윗을 생성할 수 있는 액세스 및 새로 고침 토큰을 얻을 수 있습니다.access_token를 분해합니다.token에서 개체를 얻었습니다.twitter_oauth_access_token로 얻은 액세스 토큰 값을 저장합니다. Upstash Redis의 키로 사용됩니다.- 색인 URL(
/)로 리디렉션됩니다. )redirect사용 NextResponse의 메소드입니다.
이제 Twitter OAuth 2.0 흐름이 완료되었습니다.
유효한 twitter_oauth_access_token 보유 Upstash Redis 인스턴스에는 인증 흐름 상태를 나타내는 표시기가 있습니다. 사용자 인터페이스에서 동일한 내용을 전달하려면 app/api/auth/twitter/authenticated/route.ts 파일을 생성하세요. 다음 코드를 사용하세요:
// File: app/api/auth/twitter/authenticated/route.ts
export const dynamic = 'force-dynamic'
import { redis } from '@/lib/upstash'
import { NextResponse } from 'next/server'
export async function GET() {
try {
const access_token = await redis.get<string>('twitter_oauth_access_token')
if (!access_token) return NextResponse.json({ ok: false }, { status: 200 })
return NextResponse.json({ ok: true }, { status: 200 })
}
catch(e) {}
return NextResponse.json({ ok: false }, { status: 200 })
} 위의 코드는 다음을 수행합니다:
redis를 가져옵니다. Upstash Redis를 사용하는 인스턴스입니다.NextResponse을 가져옵니다. Web Response API를 확장하는 도우미 함수입니다.GET내보내기/api/auth/twitter/authenticated에서 들어오는 GET 요청에 응답하는 HTTP 핸들러 .twitter_oauth_access_token와 연관된 값을 가져옵니다. Upstash Redis의 키입니다.ok가 포함된 JSON 응답을 반환합니다. Upstash Redis에 유효한 액세스 토큰이 있는지 여부를 나타내는 부울입니다.
귀하가 생성한 API 엔드포인트를 사용하기 위한 사용자 인터페이스 생성으로 넘어가겠습니다.
트윗 예약을 위한 사용자 인터페이스 구축
이 섹션에서는 React 후크 useFormState를 사용하여 반응형 양식 상태를 생성하는 방법을 배웁니다. 및 useFormStatus , 서버 작업을 호출하여 트윗을 예약합니다.
먼저 예정된 트윗 정보가 포함된 양식을 렌더링하고 트윗 정보가 있는지 나타내는 토스트 알림을 표시하는 React 컴포넌트를 생성하겠습니다. app/form.tsx 파일 만들기 다음 코드를 사용하세요:
// File: app/form.tsx
'use client'
// UI Imports
import { cn } from '@/lib/utils'
import { format } from 'date-fns'
import { Button } from '@/components/ui/button'
import { Calendar } from '@/components/ui/calendar'
import { useToast } from '@/components/ui/use-toast'
import { Textarea } from '@/components/ui/textarea'
import { Calendar as CalendarIcon } from 'lucide-react'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
// Form Status hook
import { useFormStatus } from 'react-dom'
import { useEffect, useState } from 'react'
// Define Form Props
export interface FormProps {
ok?: boolean
tweet_date?: string
}
export default function ({ ok, tweet_date }: FormProps) {
const { toast } = useToast()
// Use React's useFormStatus hook to detect form submission state
const { pending } = useFormStatus()
useEffect(() => {
// If the form is not pending
if (!pending) {
// If the server ok-ed the query, reset the form
if (ok) {
const scheduleForm = document.getElementById('schedule_form') as HTMLFormElement
if (scheduleForm) scheduleForm.reset()
// Display that scheduling was succesful
toast({
title: 'Scheduled Tweet',
description: tweet_date,
})
} else {
// Display that scheduling failed
toast({
variant: 'destructive',
title: 'Uh oh! Something went wrong.',
description: 'There was a problem with your request.',
})
}
}
}, [pending])
// Listen to the date picker changes
const [date, setDate] = useState<Date>()
return (
<>
<span className="font-semibold">Tweet Scheduler</span>
{/* Date Picker for Scheduling Tweet on future dates */}
<input id="tweet_date" name="tweet_date" className="hidden" value={date?.toString()} />
<Popover>
<PopoverTrigger asChild>
<Button variant={'outline'} className={cn('w-[280px] justify-start text-left font-normal', !date && 'text-muted-foreground')}>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, 'PPP') : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar mode="single" selected={date} onSelect={setDate} />
</PopoverContent>
</Popover>
{/* Text Area for Entering Text of Tweet */}
<Textarea id="tweet_text" name="tweet_text" className="min-h-[300px] w-[280px]" placeholder="Tweet" />
{/* Schedule Button Tweet */}
<Button disabled={pending} className="w-[280px]">
{pending ? 'Scheduling...' : <>Schedule →</>}
</Button>
</>
)
} 위의 코드는 다음을 수행합니다:
shadcn/ui에서 생성된 구성요소를 가져옵니다. 이전에 CLI 명령을 실행했습니다.- 시각적으로 숨겨진
input을 렌더링하는 React 구성요소를 내보냅니다. 예약된 트윗 날짜를 설명하는 HTML 요소 - 구성요소는
textarea도 렌더링합니다. 사용자가 트윗 텍스트를 시각적으로 받아들이는 HTML 요소입니다. - 구성요소는
Button도 렌더링합니다. 이전 양식 제출 상태에 따라 조건부로 비활성화됩니다. - 구성요소는
useEffect도 사용합니다. 서버 작업이 성공한 경우 양식을 재설정하는 후크입니다. 어느 쪽이든 오른쪽 하단에 토스트 알림이 표시되어 요청 상태를 나타냅니다.
다음으로 사용자의 트위터 인증 상태를 보여주는 컴포넌트를 생성하겠습니다. components/twitter.tsx 파일 만들기 다음 코드를 사용하세요:
// File: components/twitter.tsx
'use client'
import { useEffect, useState } from 'react'
import { Button } from '@/components/ui/button'
export default function () {
const state: { [k: string]: { message: string; variant: 'outline' | 'secondary' | 'destructive' } } = {
pending: {
message: '...',
variant: 'outline',
},
true: {
message: '✔️ Authenticated Instance',
variant: 'secondary',
},
false: {
message: '1-Time Authentication with Twitter →',
variant: 'destructive',
},
}
const [authenticated, setAuthenticated] = useState<string | boolean>('pending')
useEffect(() => {
fetch('/api/auth/twitter/authenticated')
.then((res) => res.json())
.then((res) => {
setAuthenticated(res.ok as boolean)
})
}, [])
{
/* Authenticate with Twitter */
}
return (
<Button
className="w-[280px]"
onClick={() => {
if (!authenticated) window.location.href = '/api/auth/twitter'
}}
variant={state[authenticated.toString()].variant}
>
{state[authenticated.toString()].message}
</Button>
)
} 위의 코드는 다음을 수행합니다:
useEffect를 가져옵니다. 및useState사용자 인증 상태를 가져오기 위해 React가 후크를 사용합니다.Button을 렌더링하는 React 구성요소를 내보냅니다. 구성 요소(shadcn/ui기준) ) 다양한 상태에서 각 상태를 사용자의 Twitter 인증 상태와 연결합니다.
사용자가 인덱스 경로(예:/)를 열 때 트윗과 상호 작용하고 예약할 수 있는 양식을 렌더링합니다. ), app/page.tsx 파일을 생성하세요. 다음 코드를 사용하세요:
// File: app/page.tsx
'use client'
// Form with Pending Status
import Form from './form'
// Form with access to the server returned data
import { useFormState } from 'react-dom'
// Scheduling Next.js Action
import Twitter from '@/components/twitter'
import { schedule } from './schedule.server'
export default function () {
const [state, formAction] = useFormState(schedule, {})
return (
<div className="flex w-[300px] flex-col gap-y-3 p-5">
<Twitter />
<form id="schedule_form" action={formAction} className="flex w-[300px] flex-col gap-y-3">
<Form {...state} />
</form>
</div>
)
} 위의 코드는 다음을 수행합니다:
Form가져오기 및Twitter이전에 생성된 구성 요소입니다.useFormState을 가져옵니다.state를 사용하여 Next.js 서버 작업에서 반환된 데이터를 처리할 수 있도록 React에서 연결합니다. 변수를 사용하고 양식 제출을 통해 서버 작업을 호출할 수 있습니다.schedule을 가져옵니다. 이전에 생성된 서버 액션.<form>가 포함된 React 구성요소를 내보냅니다.schedule_form가 포함된 구성요소 id이며formAction을 호출하도록 설정되어 있습니다. 사용자가 양식을 제출할 때
Upstash QStash를 사용하여 트윗 예약
사용자를 대신하여 트윗을 예약하려면 Upstash 대기열에 저장된 트윗 데이터를 사용하는 엔드포인트를 생성한 다음 Twitter API에 게시하여 트윗을 수행합니다. app/api/schedule/route.ts 파일 만들기 다음 코드를 사용하세요:
// File: app/api/schedule/route.ts
export const dynamic = 'force-dynamic'
import { NextResponse } from 'next/server'
import { queue, redis } from '@/lib/upstash'
import { verifySignatureAppRouter } from '@upstash/qstash/dist/nextjs'
interface TweetBody {
tweet_text?: string
tweet_date?: number
}
async function tweet(access_token: string, text: string | undefined) {
if (text) {
await fetch('https://api.twitter.com/2/tweets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: ['Bearer', access_token].join(' '),
},
body: JSON.stringify({ text }),
})
}
}
async function handler() {
const access_token = await redis.get<string>('twitter_oauth_access_token')
if (!access_token) return NextResponse.json({}, { status: 403 })
const tweets = await Promise.all(Array.from({ length: 4 }, () => queue.receiveMessage<TweetBody>()))
await Promise.all(tweets.map((i) => tweet(access_token, i?.body?.tweet_text)))
return NextResponse.json({}, { status: 200 })
}
export const POST = verifySignatureAppRouter(handler) 위의 코드는 다음을 수행합니다:
redis을 가져옵니다. 및queueUpstash Redis와 Upstash Queue를 각각 사용하는 인스턴스verifySignatureAppRouter을 가져옵니다. 요청이 QStash에서만 발생하는지 확인하기 위해 요청 서명을 확인하는 Upstash의 기능입니다.tweet함수를 생성합니다. 인증된 사용자를 대신하여 트윗을 수행하기 위해 트윗 텍스트와 액세스 토큰을 허용합니다.POST를 생성합니다./api/schedule에서 들어오는 POST 요청에 응답하는 HTTP 핸들러 . Upstash 대기열에서 4개의 메시지를 가져오고tweet를 호출합니다. 사용자를 대신하여 트윗을 수행하는 기능입니다.
Upstash QStash를 사용하면 트윗 수행 프로세스를 예약할 수 있습니다. 매일 자정에 프로세스가 자동으로 시작되고 트윗이 나가도록 자동화하고 싶다고 가정해 보겠습니다. 이를 위해 이전에 생성된 엔드포인트(/api/schedule)를 사용합니다. QStash에 있습니다.

Upstash 콘솔의 QStash 탭으로 이동하여 Request Builder 탭까지 아래로 스크롤한 후 다음을 수행하세요:
Endpoint를 선택하세요. 탭.URL을 입력하세요./api/schedule에 대한 절대 URL로 끝점.Type를 선택하세요.Scheduled으로 .Every를 선택하세요.every day at midnight로 .Schedule를 클릭하세요. .
위의 단계를 통해 /api/schedule에 POST되는 작업을 만들었습니다. 매일 자정.
정말 많이 배웠습니다! 이제 모든 작업이 완료되었습니다 ✨
Vercel에 배포
이제 저장소를 Vercel에 배포할 준비가 되었습니다. 배포하려면 다음 단계를 따르세요.
- 앱 코드가 포함된 GitHub 저장소를 만드는 것부터 시작하세요.
- 그런 다음 Vercel 대시보드로 이동하여 새 프로젝트를 만듭니다. .
- 새 프로젝트를 방금 생성한 GitHub 저장소에 연결하세요.
- 설정 ,
Environment Variables을 업데이트하세요. 지역.env의 항목과 일치시키려면 파일. Deploy를 클릭하세요. .
추가 정보
더 자세한 통찰력을 얻으려면 이 게시물에 인용된 참고 자료를 살펴보세요.
- GitHub 저장소
- Twitter OAuth 2.0 흐름
- Twitter 앱 토큰 생성
- React 양식 후크
- Next.js 서버 작업
결론
이 가이드에서는 Upstash의 강력한 Redis 데이터베이스와 QStash 대기열을 활용하여 강력한 트윗 스케줄러를 구축하는 방법을 배웠습니다. Upstash의 확장성은 Vercel의 원활한 배포와 결합하여 트윗의 안정적인 저장 및 예약을 보장하여 완전히 자동화된 시스템을 만듭니다.