NextJS Templates
Next.js, Vercel 및 Back4app을 사용한 청구서 앱 튜토리얼
26 분
이 튜토리얼에서는 invoicing app 을 구축할 것입니다 next js 를 프론트엔드로, back4app 을 백엔드로, 그리고 clerk 를 인증을 위한 추가 서비스로, stripe 를 결제를 위한 서비스로, resend 를 이메일 알림을 위한 서비스로 사용할 것입니다 이 가이드는 인증 설정, 인보이스 관리, 결제 처리 및 vercel 에 배포하는 과정을 안내할 것입니다 1\ 필수 조건 시작하기 전에 다음 사항을 확인하세요 back4app 계정 ( 여기에서 가입 https //www back4app com/ ) 인증을 위한 clerk 계정 ( clerk io https //clerk dev/ ) 결제 처리를 위한 stripe 계정 ( stripe com https //stripe com/ ) 이메일 알림을 위한 resend 계정 ( resend io https //resend io/ ) node js 및 npm 설치 ( node js 설치 https //nodejs org/ ) 2\ 백엔드 설정 (back4app) 2 1 back4app에서 새 프로젝트 만들기 back4app에 로그인하고 새 프로젝트를 만드세요 데이터 브라우저 data browser 로 이동하여 다음 클래스를 만드세요 2 2 데이터 모델 (클래스) 2 2 1 사용자 (clerk에 의해 자동으로 처리됨) 객체 id 사용자 이름 이메일 2 2 2 조직 다음과 같은 클래스를 생성합니다 조직 다음 열을 추가합니다 이름 (문자열) 조직의 이름 소유자 id (pointer< user>) 사용자 클래스(clerk에 의해 자동으로 생성됨)를 가리킵니다 구성원 (array\<pointer< user>>) 이 조직에 속한 사용자 배열 2 2 3 고객 다음과 같은 클래스를 생성합니다 고객 다음 열을 추가합니다 이름 (문자열) 고객의 이름 이메일 (문자열) 고객의 이메일 조직 id (pointer) 이 고객이 속한 조직 2 2 4 청구서 클래스를 생성합니다 청구서 다음 열을 추가합니다 invoicenumber (문자열) 고유 청구서 식별자 customerid (포인터) 고객에 연결됩니다 organizationid (포인터) 조직에 연결됩니다 amount (숫자) 청구서의 총 금액 status (문자열) 옵션에는 '초안', '전송', '지급', '연체'가 포함됩니다 duedate (날짜) 청구서 마감일 items (배열) 청구서의 항목 목록 2 2 5 지불 클래스를 생성합니다 지불 다음 열을 추가합니다 invoiceid (포인터) 관련 청구서에 연결됩니다 amount (숫자) 지불 금액 stripesessionid (문자열) stripe 세션의 id status (문자열) 지불 상태 (예 '대기 중', '성공', '실패') 3\ 프론트엔드 설정 (next js) 3 1 next js 프로젝트 생성 typescript, tailwind css 및 shadcn/ui 구성 요소로 next js 프로젝트를 설정하는 것으로 시작하세요 npx create next app\@latest invoicing app typescript cd invoicing app npm install tailwindcss postcss autoprefixer npx tailwindcss init 3 2 tailwind css 설정 tailwind를 구성하려면 tailwind config js 를 업데이트하고 globals css 에 스타일을 추가하세요 다음의 tailwind config js module exports = { content \[ ' /pages/ / {js,ts,jsx,tsx}', ' /components/ / {js,ts,jsx,tsx}', ], theme { extend {}, }, plugins \[], }; 다음의 styles/globals css @tailwind base; @tailwind components; @tailwind utilities; 3 3 parse sdk 및 추가 서비스 설치 parse, clerk, stripe 및 resend에 필요한 종속성을 설치하세요 npm install parse @clerk/clerk next stripe resend 3 4 next js에서 parse sdk 초기화 다음의 pages/ app tsx , parse sdk를 back4app 자격 증명으로 초기화하세요 import { clerkprovider } from '@clerk/clerk react'; import parse from 'parse'; parse initialize('your app id', 'your javascript key'); parse serverurl = 'https //parseapi back4app com'; function myapp({ component, pageprops }) { return ( \<clerkprovider> \<component { pageprops} /> \</clerkprovider> ); } export default myapp; 다음의 'your app id' 및 'your javascript key' 를 back4app 자격 증명으로 교체하세요 4\ 사용자 인증 구현 4 1 인증을 위한 clerk 통합 인증을 위해 clerk 를 설정합니다 clerk의 대시보드로 가서 애플리케이션을 생성하고 api 키를 가져옵니다 env local 파일을 clerk 키로 업데이트합니다 next public clerk frontend api=your clerk frontend api clerk api key=your clerk api key 이제 pages/sign in tsx 및 pages/sign up tsx 를 생성하여 clerk 구성 요소를 사용한 로그인 및 회원가입 기능을 구현합니다 다음은 pages/sign in tsx import { signin } from '@clerk/clerk react'; export default function signinpage() { return \<signin />; } 다음은 pages/sign up tsx import { signup } from '@clerk/clerk react'; export default function signuppage() { return \<signup />; } 5\ 조직 및 고객 관리 5 1 조직 만들기 사용자는 자신이 소유한 조직을 만들 수 있습니다 새 조직 생성을 위한 페이지 pages/organizations/new\ tsx 를 만드세요 import { usestate } from 'react'; import parse from 'parse'; export default function neworganization() { const \[name, setname] = usestate(''); const handlecreateorganization = async () => { const organization = parse object extend('organization'); const organization = new organization(); organization set('name', name); organization set('ownerid', parse user current()); try { await organization save(); alert('organization created successfully!'); } catch (error) { console error('error creating organization ', error); } }; return ( \<div> \<h1>create new organization\</h1> \<input type="text" value={name} onchange={(e) => setname(e target value)} placeholder="organization name" /> \<button onclick={handlecreateorganization}>create\</button> \</div> ); } 5 2 고객 관리 새 고객 추가를 위한 고객 관리 페이지 pages/customers/new\ tsx 를 만드세요 import { usestate } from 'react'; import parse from 'parse'; export default function newcustomer() { const \[name, setname] = usestate(''); const \[email, setemail] = usestate(''); const handlecreatecustomer = async () => { const customer = parse object extend('customer'); const customer = new customer(); customer set('name', name); customer set('email', email); customer set('organizationid', parse user current() get('organizationid')); try { await customer save(); alert('customer created successfully!'); } catch (error) { console error('error creating customer ', error); } }; return ( \<div> \<h1>create new customer\</h1> \<input type="text" value={name} onchange={(e) => setname(e target value)} placeholder="customer name" /> \<input type="email" value={email} onchange={(e) => setemail(e target value)} placeholder="customer email" /> \<button onclick={handlecreatecustomer}>create\</button> \</div> ); } 6\ 인보이스 관리 6 1 인보이스 생성 새 인보이스 생성을 위한 인보이스 생성 페이지 pages/invoices/new\ tsx 를 만드세요 import { usestate } from 'react'; import parse from 'parse'; export default function newinvoice() { const \[invoicenumber, setinvoicenumber] = usestate(''); const \[amount, setamount] = usestate(0); const handlecreateinvoice = async () => { const invoice = parse object extend('invoice'); const invoice = new invoice(); invoice set('invoicenumber', invoicenumber); invoice set('amount', amount); invoice set('organizationid', parse user current() get('organizationid')); try { await invoice save(); alert('invoice created successfully!'); } catch (error) { console error('error creating invoice ', error); } }; return ( \<div> \<h1>create new invoice\</h1> \<input type="text" value={invoicenumber} onchange={(e) => setinvoicenumber(e target value)} placeholder="invoice number" /> \<input type="number" value={amount} onchange={(e) => setamount(parsefloat(e target value))} placeholder="amount" /> \<button onclick={handlecreateinvoice}>create\</button> \</div> ); } 6 2 청구서 보기 페이지 생성 pages/invoices/index tsx 모든 청구서를 정렬 및 필터링 옵션과 함께 보기 위해 import { useeffect, usestate } from 'react'; import parse from 'parse'; export default function invoiceslist() { const \[invoices, setinvoices] = usestate(\[]); useeffect(() => { const fetchinvoices = async () => { const invoice = parse object extend('invoice'); const query = new parse query(invoice); const results = await query find(); setinvoices(results); }; fetchinvoices(); }, \[]); return ( \<div> \<h1>invoices\</h1> {invoices map(invoice => ( \<div key={invoice id}> \<h2>invoice #{invoice get('invoicenumber')}\</h2> \<p>amount ${invoice get('amount')}\</p> \<p>status {invoice get('status')}\</p> \</div> ))} \</div> ); } 7\ 결제 처리 (stripe 통합) 7 1 스트라이프 체크아웃 설정 결제를 처리하려면 stripe를 통합하세요 pages/invoices/\[id] tsx , stripe checkout을 사용하여 결제 링크 생성을 구현하세요 import { userouter } from 'next/router'; import { useeffect, usestate } from 'react'; import stripe from 'stripe'; export default function invoicedetail() { const router = userouter(); const { id } = router query; const \[invoice, setinvoice] = usestate(null); useeffect(() => { if (!id) return; const fetchinvoice = async () => { const invoice = parse object extend('invoice'); const query = new parse query(invoice); query equalto('objectid', id); const result = await query first(); setinvoice(result); }; fetchinvoice(); }, \[id]); const handlepayinvoice = async () => { const stripe = new stripe(process env stripe secret key); const session = await stripe checkout sessions create({ payment method types \['card'], line items \[ { price data { currency 'usd', product data { name `invoice #${invoice get('invoicenumber')}` }, unit amount invoice get('amount') 100, }, quantity 1, }, ], mode 'payment', success url window\ location origin + '/success', cancel url window\ location origin + '/cancel', }); window\ location href = session url; }; if (!invoice) return \<div>loading \</div>; return ( \<div> \<h1>invoice #{invoice get('invoicenumber')}\</h1> \<p>amount ${invoice get('amount')}\</p> \<button onclick={handlepayinvoice}>pay now\</button> \</div> ); } 8\ 이메일 알림 (재전송 통합) 8 1 송장 이메일 전송 다음과 같이 설정하십시오 resend 를 사용하여 고객에게 송장 이메일을 전송합니다 import { resend } from 'resend'; export default async function sendinvoiceemail(invoiceid) { const resend = new resend(process env resend api key); const invoice = await fetchinvoicedetails(invoiceid); // function to fetch invoice details const customer = await fetchcustomerdetails(invoice customerid); await resend sendemail({ from 'no reply\@invoicingapp com', to customer email, subject `invoice #${invoice invoicenumber}`, html `\<h1>invoice #${invoice invoicenumber}\</h1>\<p>amount ${invoice amount}\</p>`, }); } 9\ vercel에 배포 vercel에 앱을 배포하려면 vercel cli를 설치하고 배포하십시오 npm install g vercel vercel 배포 전에 vercel 대시보드에서 clerk, stripe, resend 및 back4app의 환경 변수를 구성하십시오 10\ 결론 이 튜토리얼에서는 송장 앱 을 next js로 구축하고, 백엔드에 back4app 을 통합하며, 인증에 clerk 을 사용하고, 결제에 stripe 을 사용하며, 이메일 알림에 resend 을 사용했습니다 사용자 및 조직 관리, 송장 생성, 결제 처리 및 배포를 다루었습니다