paint-brush
NextJS, TypeScript, Tailwind 및 OneEntry CMS를 사용하여 전자상거래 상점을 구축하는 방법 🛒👨‍💻by@madzadev
1,519
1,519

NextJS, TypeScript, Tailwind 및 OneEntry CMS를 사용하여 전자상거래 상점을 구축하는 방법 🛒👨‍💻

Madza2024/01/24
Read on Terminal Reader
Read this story w/o Javascript

실용적인 전자상거래 프로젝트를 구축하여 NextJS, TypeScript, Tailwind CSS 및 OneEntry CMS가 어떻게 함께 작동하고 이들이 콘텐츠 관리를 단순화하는 데 어떻게 사용될 수 있는지 살펴보겠습니다.
featured image - NextJS, TypeScript, Tailwind 및 OneEntry CMS를 사용하여 전자상거래 상점을 구축하는 방법 🛒👨‍💻
Madza HackerNoon profile picture
0-item

소개

OneEntry CMS 의 지원을 받아 작성된 파트너십 글입니다.


전자상거래 애플리케이션을 구축하는 것은 종종 어려운 작업입니다. 사용할 수 있는 대안이 너무 많기 때문에 프로젝트 요구 사항, 확장성 요구 사항 및 장기적인 지속 가능성에 맞는 기술 스택을 선택하는 것은 쉽지 않습니다.


또 다른 중요한 점은 전자상거래 프로젝트가 많은 데이터와 CRUD 작업을 처리한다는 것입니다. 견고하고 확장 가능하며 안전한 백엔드 시스템을 만드는 데는 대부분의 숙련된 개발자에게도 많은 시간이 걸릴 수 있습니다.


저는 NextJS, TypeScript, Tailwind CSS 및 OneEntry CMS를 기반으로 하는 기술 스택을 선택했습니다. 우리는 실용적인 전자상거래 프로젝트를 직접 구축하여 이것이 어떻게 함께 작동하고 콘텐츠 관리를 단순화하는 데 어떻게 사용될 수 있는지 알아볼 것입니다.


이 프로젝트의 코드는 GitHub 저장소 에서 사용할 수 있습니다.

기술 스택의 선택

NextJS는 빠르고 효율적인 웹 애플리케이션을 구축하기 위한 React 프레임워크로, 클라이언트 및 서버 렌더링, 데이터 가져오기, 경로 처리기, 미들웨어, 내장 최적화 등과 같은 기능이 함께 제공됩니다.


TypeScript는 JavaScript에 정적 타이핑을 추가하여 전자상거래와 같은 확장 가능한 프로젝트의 오류를 더 쉽게 포착하고 수정할 수 있습니다. 또한 자동 완성 및 리팩토링 지원과 같은 기능을 통해 생산성을 향상시킵니다.


Tailwind CSS는 웹 앱의 스타일링 부분을 가속화하여 개발자가 외부 CSS 파일 간에 전환하고 각각에 대한 클래스 이름을 제시할 필요 없이 마크업 내 요소의 스타일을 지정할 수 있도록 합니다.


OneEntry CMS는 사용하기 쉬운 인터페이스, 쉽게 확장 가능한 백엔드, 빠른 API 및 명확한 문서를 갖춘 헤드리스 콘텐츠 관리 시스템으로 웹 사이트 콘텐츠 생성 및 관리 경험의 생산성을 높여줍니다.

콘텐츠와 디자인

랜딩 페이지에는 제목 제목이 표시되고 상점의 기능이 나열되며 히어로 이미지가 포함됩니다.


전자상거래 상점 홈페이지


첫 번째 상점 섹션은 의류 전용입니다.


전자상거래 상점 제품 페이지


두 번째 상점 섹션에는 장비가 포함됩니다.


전자상거래 매장 제품 페이지 #2


각 항목에는 세부정보가 포함된 개별 미리보기 페이지가 있습니다.


전자상거래 상점 제품 미리보기 페이지


장바구니에 이미 있는 항목은 제거할 수 있는 옵션이 있습니다.


전자상거래 상점 제품 미리보기 페이지 #2


장바구니에 선택한 모든 항목이 나열되고 총계가 계산됩니다.


전자상거래 매장 장바구니 페이지

OneEntry 프로젝트 만들기

먼저, 사용자는 새 계정에 가입해야 합니다. 그렇게 하려면 OneEntry 홈페이지로 이동하여 이메일 계정을 통해 가입하세요 .


OneEntry 가입 페이지


그런 다음 로그인하면 OneEntry 대시보드로 이동됩니다.


새 프로젝트를 만드는 것부터 시작하세요.


OneEntry 대시보드 페이지


한 달 동안 학습 플랜을 사용할 수 있는 무료 코드를 받게 됩니다. 프로젝트 생성 과정에서 활성화할 수 있는 기회가 제공됩니다.


OneEntry 프로젝트 페이지


프로젝트를 생성하는 데 몇 분 정도 걸립니다. 준비가 완료되면 프로젝트 상태가 "작업 중"으로 변경되고 상태 표시기가 녹색이 됩니다.


OneEntry 대시보드 페이지

페이지 만들기

프로젝트가 생성되면 CMS 포털에 액세스하여 앱 데이터를 생성하고 저장할 수 있는 로그인 세부 정보가 포함된 이메일을 받게 됩니다.


OneEntry CMS 로그인 페이지


로그인 후 첫 번째 페이지를 만들 수 있습니다.


콘텐츠 관리로 이동하여 새 페이지 만들기를 클릭하고 페이지 유형, 페이지 제목, 페이지 ULR 및 메뉴 항목 이름 등 필요한 모든 데이터를 입력합니다.


OneEntry CMS - 페이지 편집


모든 데이터는 입력 시 자동으로 저장됩니다.


홈, 의류, 장비, 장바구니에 대한 4개의 서로 다른 페이지를 만듭니다. 일단 생성되면 페이지는 아래 스크린샷과 같아야 합니다.


OneEntry CMS 페이지 목록

속성 생성

다음으로 저장할 데이터 구조를 만들어야 합니다. OneEntry CMS에서는 데이터에 대한 속성을 생성하여 이를 달성합니다.


설정으로 이동하여 가로 메뉴에서 속성을 선택하세요. 이름, 마커 및 유형을 제공하는 홈 페이지에 대한 속성 세트를 만듭니다.


OneEntry CMS 페이지 유형


생성되면 아래 스크린샷과 같이 표시됩니다.


OneEntry CMS 페이지 속성


마찬가지로 의류와 장비에 대해 두 개의 별도 속성 세트를 만들어 보겠습니다. 일단 생성되면 결과는 아래 스크린샷과 같아야 합니다.


OneEntry CMS 속성 #2


이제 각 세트에 대한 특정 속성을 정의해 보겠습니다.


앞서 홈 섹션 와이어프레임에 포함했던 콘텐츠를 기반으로 제목, 설명, 이미지를 표시하려고 합니다.


홈에 대한 기어 항목을 클릭하고 아래 목록에 표시된 대로 다음 속성 이름, 마커 및 속성 유형을 만듭니다.


OneEntry CMS 페이지 설정


이제 돌아가서 의류에 대한 기어 아이콘을 클릭하세요.


이 페이지의 속성은 제품 제목, 부제, 설명, 이미지 및 가격을 표시하려고 하므로 약간 다릅니다.


속성 구조는 다음과 같습니다.


OneEntry CMS 페이지 속성


다음으로 동일한 구조를 사용하는 Gear 페이지에 대해 동일한 작업을 수행합니다.


OneEntry CMS 페이지 속성 #2

콘텐츠 추가

프로젝트의 이 단계에서 우리는 이미 콘텐츠 구조를 정의했으며 콘텐츠 자체 제작을 시작할 준비가 되었습니다.


이전에 사이트의 모든 페이지를 생성한 콘텐츠 관리 섹션으로 이동합니다.


OneEntry CMS 페이지 목록


홈의 편집 버튼을 클릭합니다. 그런 다음 가로 메뉴에서 속성 탭을 클릭합니다.


OneEntry CMS 선택 속성


속성 세트에 대해 홈을 선택하십시오. 그러면 이전에 홈 페이지 설정에서 생성한 모든 속성이 로드됩니다.


이제 홈 페이지에 표시할 샘플 데이터를 입력하세요.


OneEntry CMS 콘텐츠 제작


이제 의류 및 장비 페이지에 몇 가지 콘텐츠를 추가해 보겠습니다.


페이지 유형을 카탈로그로 선택했으므로 왼쪽 메뉴에서 카탈로그를 선택하면 두 페이지가 모두 표시됩니다.


OneEntry CMS 카탈로그


이제 의류에 대한 추가 아이콘을 클릭하고 몇 가지 항목을 추가합니다.


먼저 추가하려는 제품의 헤더를 추가하세요.


OneEntry CMS 카탈로그 헤더


이제 속성 탭으로 전환하고 속성 세트로 의류를 선택한 후 필수 데이터를 입력하세요.


OneEntry CMS 제품 추가


카탈로그 메뉴로 돌아가 의류와 장비에 대한 몇 가지 추가 항목을 확인하세요. 데모 앱의 경우 아래 스크린샷에 표시된 대로 4개 항목을 추가했습니다.


OneEntry CMS 제품 목록

API 액세스 토큰 생성

OneEntry CMS에서 생성된 모든 데이터는 보호되므로 해당 데이터에 액세스하려면 개인 토큰을 생성해야 합니다.


그렇게 하려면 설정으로 이동하여 앱 토큰을 선택하세요. 앱 이름과 만료 날짜를 입력하고 만들기를 클릭합니다. 그러면 고유한 API 키가 생성됩니다.


OneEntry CMS - 앱 토큰 생성


작업 목록에서 보기 아이콘을 클릭하면 해당 키를 확인할 수 있습니다. 튜토리얼의 다음 섹션에서 필요하므로 클립보드에 복사하세요.


OneEntry CMS - 앱 토큰 복사

NextJS 프로젝트 설정

튜토리얼의 이 섹션에서는 코드 작업을 시작하고 OneEntry CMS와 작동하도록 NextJS 프로젝트를 구성합니다.


터미널을 열고 npx create-next-app@latest 명령을 실행합니다.


CLI가 설정 마법사를 시작합니다. 프로젝트 이름을 입력하고 아래와 같이 기본값을 모두 선택합니다.


터미널의 NextJS 설정 마법사


설정이 완료될 때까지 잠시 기다리면 NextJS 앱이 생성되면 알림을 받게 됩니다.


그런 다음 cd winter-sports 명령을 사용하여 디렉터리를 새로 생성된 폴더로 변경한 다음 npm run dev 실행하여 개발자 서버를 시작합니다.


액세스하려면 터미널에 제공된 링크를 클릭하거나 웹 브라우저를 열고 수동으로 http://localhost:3000 으로 이동하세요.


NextJS 개발자 서버 랜딩 페이지가 표시되어야 합니다.


NextJS 개발자 서버 미리보기


이제 우리 앱에 필요한 환경적 가치를 만들어 보겠습니다. 코드 편집기로 다시 전환하고 프로젝트 루트에 .env 파일을 만듭니다.


앞서 클립보드에 복사한 API 키를 다음과 같이 붙여넣습니다.


 API_KEY=your-api-code-from-oneentry


OneEntry CMS에서 데이터를 가져오기 위해 API 호출을 수행하면 process.env.API_KEY 통해 키에 액세스할 수 있습니다.


또한 외부 도메인의 미디어를 포함할 수 있도록 NextJS를 구성해야 합니다. OneEntry CMS에서 이미지에 액세스하려면 이 정보가 필요합니다.


프로젝트 루트에서 next.config.js 파일을 열고 다음과 같이 편집합니다.


 const nextConfig = { images: { remotePatterns: [ { hostname: "ecommerce.oneentry.cloud", }, ], }, }; module.exports = nextConfig;


마지막으로 모든 스타일을 처음부터 작성하므로 앱의 Tailwind 기본 스타일을 재설정해야 합니다.


src 폴더에 있는 app 디렉터리에서 globals.css 파일을 열고 파일 콘텐츠를 다음과 같이 변경합니다.


 @tailwind base; @tailwind components; @tailwind utilities;

유형 생성

TypeScript로 작업할 것이므로 애플리케이션에서 사용할 데이터 유형을 정의해야 합니다.


페이지와 구성 요소 내에서 이 작업을 수행할 수 있지만 코드를 더 깔끔하게 유지하고 반복을 방지하려면 app 디렉터리 내에 새 폴더 interfaces 만듭니다. 새로 생성된 폴더 내에 data.tsx 파일을 생성하고 다음 코드를 포함합니다.


 export interface Product { id: string; category: string; title: string; subtitle: string; description: string; image: string; price: number; } export interface ProductAPI { id: string; attributeValues: { en_US: { producttitle: { value: { htmlValue: string }[]; }; productsubtitle: { value: { htmlValue: string }[]; }; productdescription: { value: { htmlValue: string }[]; }; productimage: { value: { downloadLink: string }[]; }; productprice: { value: number; }; }; }; } export interface Page { pageUrl: string; title: string; description: string; image: string; localizeInfos: { en_US: { title: string; }; }; } export interface PageAPI { attributeValues: { en_US: { herotitle: { value: { htmlValue: string }[]; }; herodescription: { value: { htmlValue: string }[]; }; heroimage: { value: { downloadLink: string }[]; }; }; }; } export interface URLProps { params: { category: string; productId: string; }; } export interface TextProps { className: string; text: string; }


제품 및 페이지 데이터에는 모두 프런트엔드 렌더링 데이터 구조에 대한 유형과 fetch 메소드를 통한 API의 응답이 있습니다.


또한 URL 매개변수의 데이터에 대한 데이터 유형과 CMS의 텍스트 입력 필드에서 수신된 데이터에 대한 텍스트 렌더러를 정의했습니다.

API 가져오기 함수 생성

이제 페이지 및 제품에 대한 데이터를 가져오기 위해 OneEntry CMS와 통신하는 데 사용할 몇 가지 기능을 만들어 보겠습니다.


다시 말하지만, 각 파일에서 이 작업을 수행할 수 있지만 코드를 더 깔끔하게 유지하기 위해 fetchData.tsx 파일이 포함된 app 디렉터리 내에 새 폴더 services 만들어 보겠습니다.


 export async function getPages() { const response = await fetch( "https://ecommerce.oneentry.cloud/api/content/pages", { method: "GET", headers: { "x-app-token": `${process.env.API_KEY}`, }, } ); return await response.json(); } export async function getProducts(category: string) { const response = await fetch( `https://ecommerce.oneentry.cloud/api/content/products/page/url/${category}?limit=4&offset=0&langCode=en_US&sortOrder=DESC&sortKey=id`, { method: "GET", headers: { "x-app-token": `${process.env.API_KEY}`, }, } ); return await response.json(); } export async function getProduct(id: string) { const response = await fetch( `https://ecommerce.oneentry.cloud/api/content/products/${id}`, { method: "GET", headers: { "x-app-token": `${process.env.API_KEY}`, }, } ); return await response.json(); }


getPages 함수는 OneEntry CMS에서 생성한 모든 페이지에 대한 데이터를 가져옵니다.


getProducts 함수는 category 매개변수를 기반으로 특정 제품 컬렉션에 대한 데이터를 가져옵니다. 함수를 제품 페이지로 가져올 때 매개변수를 전달합니다.


getProduct 함수는 우리가 연 제품의 id 기반으로 데이터를 가져옵니다. 특정 제품의 미리보기 페이지로 함수를 가져올 때 매개변수를 전달합니다.


OneEntry CMS에 대한 액세스를 인증하기 위해 이전에 .env 파일에 정의한 API 키에 액세스하기 위해 process.env.API_KEY 사용했습니다.

도우미 함수 만들기

또한 여전히 services 폴더에 있는 동안 작은 유틸리티 기능을 포함하는 helpers.tsx 라는 또 다른 새 파일을 그 안에 만들어 보겠습니다.


 export function calculateTotal(items: { price: number }[]) { return items.reduce((total, item) => total + Number(item.price), 0); } export function boughtStatus(items: { id: string }[], id: string) { return items.some((item) => item.id === id); } export function cartIndex(items: { id: string }[], id: string) { return items.findIndex((item) => item.id === id); }


calculateTotal 함수는 장바구니에 추가된 제품의 가격을 합산하고 총 가치를 반환합니다.


boughtStatus 미리보기 경로의 개별 항목이 이미 장바구니에 추가되었는지 감지합니다.


cartIndex 는 장바구니에 추가된 제품에 대한 배열의 항목 위치를 감지합니다.

구성요소 생성

app 디렉터리로 다시 이동하여 그 안에 새 폴더 components 만듭니다.


새로 생성된 폴더를 열고 그 안에 7개의 개별 파일( Header.tsx , Footer.tsx , Text.tsx , Card.tsx , Preview.tsx , Order.tsx , AddToCart.tsx )을 포함합니다.


헤더 구성요소

Header.tsx 파일을 열고 다음 코드를 포함합니다.


 import Link from "next/link"; import { Page } from "../interfaces/data"; export default function Header({ pages }: { pages: Page[] }) { return ( <div className="flex justify-between items-center mb-10 p-6"> <Link href="/"> <h1 className="text-xl">🏂 Alpine Sports</h1> </Link> <div className="flex space-x-4 list-none"> {pages.map((page, index: number) => ( <Link key={index} href={page.pageUrl === "home" ? "/" : `/${page.pageUrl}`} > {page.localizeInfos.en_US.title} </Link> ))} </div> </div> ); }

헤더의 경우 회사 이름을 표시하고 구성 요소를 페이지로 가져온 후 API에서 얻을 수 있는 탐색 링크를 반복했습니다.


우리는 2열 레이아웃을 만들고 두 요소를 화면의 반대쪽에 수평으로 배치하여 일반적인 탐색 모양을 구현했습니다.


바닥글 구성요소

Footer.tsx 파일을 열고 다음 코드를 포함합니다.


 export default function Footer() { return ( <div className="text-center mt-auto p-6"> <h1>Alpine Sports, Inc.</h1> <p>All rights reserved, {new Date().getFullYear()}</p> </div> ); }


바닥글에는 회사의 샘플 이름과 해당 연도의 콘텐츠 권한을 포함했습니다. 콘텐츠를 중앙에 배치하고 패딩을 추가했습니다.


텍스트 구성요소

Text.tsx 파일을 열고 다음 코드를 포함합니다.


 import { TextProps } from "../interfaces/data"; export default function Text({ className, text }: TextProps) { return ( <div className={className} dangerouslySetInnerHTML={{ __html: text }} /> ); }


텍스트 구성 요소는 OneEntry CMS에서 받은 텍스트 데이터를 렌더링하고 HTML 태그 없이 애플리케이션에 올바르게 표시합니다.


카드 구성요소

Card.tsx 파일을 열고 다음 코드를 포함합니다.


 import Link from "next/link"; import Text from "../components/Text"; import { Product } from "../interfaces/data"; export default function Card({ product }: { product: Product }) { return ( <Link href={`/${product.category}/${product.id}`}> <div className="group relative"> <div className="group-hover:opacity-75 h-80"> <img src={product.image} alt="Product card image" className="h-full w-full object-cover object-center" /> </div> <div className="mt-4 flex justify-between"> <div> <h3 className="text-sm text-gray-700"> <Text className="" text={product.title} /> </h3> <Text className="mt-1 text-sm text-gray-500" text={product.subtitle} /> </div> <p className="text-sm font-medium text-gray-900">${product.price}</p> </div> </div> </Link> ); }


카드 구성요소에는 각 상품의 이미지, 제목, 부제, 가격을 표시했습니다. 페이지로 가져온 후에는 모든 항목을 통해 매핑할 것입니다.


이미지는 카드 상단에 표시되고, 구성 요소 오른쪽 하단에는 제목과 설명, 가격이 표시됩니다.


미리보기 구성요소

Preview.tsx 파일을 열고 다음 코드를 포함합니다.


 "use-client"; import Image from "next/image"; import Text from "./Text"; import { Product } from "../interfaces/data"; export default function Preview({ children, productItem, }: { children: React.ReactNode; productItem: Product; }) { return ( <div className="flex mx-auto max-w-screen-xl"> <div className="flex-1 flex justify-start items-center"> <Image src={productItem.image} alt="Product preview image" width="450" height="900" /> </div> <div className="flex-1"> <Text className="text-5xl pb-8" text={productItem.title} /> <Text className="text-4xl pb-8 text-gray-700" text={`$${productItem.price}`} /> <Text className="pb-8 text-gray-500 text-justify" text={productItem.description} /> {children} </div> </div> ); }


미리보기 구성요소는 사용자가 제품을 클릭하면 각 제품에 대한 추가 정보를 표시하는 데 사용됩니다.


제품 이미지, 제목, 가격, 설명 등이 표시됩니다. 레이아웃은 2개의 열로 나누어지며 왼쪽 열에는 이미지가 표시되고 오른쪽 열에는 나머지 콘텐츠가 표시됩니다.


주문 구성요소

Order.tsx 파일을 열고 다음 코드를 포함합니다.


 "use client"; import { useState, useEffect } from "react"; import Link from "next/link"; import Image from "next/image"; import Text from "./Text"; import { calculateTotal } from "../services/helpers"; import { Product } from "../interfaces/data"; export default function Order() { const [cartItems, setCartItems] = useState<Product[]>([]); useEffect(() => { const storedCartItems = localStorage.getItem("cartItems"); const cartItems = storedCartItems ? JSON.parse(storedCartItems) : []; setCartItems(cartItems); }, []); return ( <div> {cartItems.map((item, index) => ( <div key={index} className="flex items-center border-b border-gray-300 py-2" > <div className="w-20 h-20 mr-12"> <Image src={item.image} alt={item.title} width={80} height={80} /> </div> <div> <Link href={`/${item.category}/${item.id}`} className="text-lg font-semibold" > <Text className="" text={item.title} /> </Link> <Text className="text-gray-600" text={item.subtitle} /> <p className="text-gray-800">Price: ${item.price}</p> </div> </div> ))} <div className="mt-4 text-end"> <h2 className="text-xl font-semibold mb-8"> Total Amount: ${calculateTotal(cartItems)} </h2> <button className="bg-blue-500 hover:bg-blue-700 py-2 px-8 rounded"> Proceed to checkout </button> </div> </div> ); }


주문 구성 요소에는 사용자가 장바구니에 추가한 모든 항목이 나열됩니다. 각 항목에 대해 이미지, 제목, 부제, 가격이 표시됩니다.


구성 요소가 렌더링되면 앱은 현재 장바구니에 있는 모든 항목에 액세스하고 이를 cardItems 상태 변수로 설정한 다음 map 메서드를 통해 화면에 렌더링합니다.


렌더링된 항목의 총량은 helpers.tsx 파일에서 가져온 calculateTotal 함수를 통해 계산됩니다.


AddToCart 구성 요소

AddToCart.tsx 파일을 열고 다음 코드를 포함합니다.


 "use client"; import React, { useState, useEffect } from "react"; import { boughtStatus, cartIndex } from "../services/helpers"; import { Product } from "../interfaces/data"; export default function AddToCart({ category, id, title, subtitle, image, price, }: Product) { const storedCartItems = JSON.parse(localStorage.getItem("cartItems") || "[]"); const isPurchased = boughtStatus(storedCartItems, id); const indexInCart = cartIndex(storedCartItems, id); const [btnState, setBtnState] = useState(false); useEffect(() => { isPurchased && setBtnState(true); }, []); const handleButtonClick = () => { const updatedCartItems = [...storedCartItems]; if (!btnState && !isPurchased) { updatedCartItems.push({ category, id, title, subtitle, image, price }); } else if (isPurchased) { updatedCartItems.splice(indexInCart, 1); } localStorage.setItem("cartItems", JSON.stringify(updatedCartItems)); setBtnState(!btnState); }; return ( <button className={`${ !btnState ? "bg-blue-500 hover:bg-blue-600" : "bg-yellow-300 hover:bg-yellow-400" } py-2 px-8 rounded`} onClick={handleButtonClick} > {!btnState ? "Add to Cart" : "Remove from Cart"} </button> ); }


addToCart 구성 요소는 개별 제품 미리 보기 페이지에 표시되며 사용자가 제품을 장바구니에 추가할 수 있습니다.


렌더링 시 isPurchased 기능은 이전에 제품이 이미 장바구니에 추가되었는지 여부를 감지합니다. 렌더링된 버튼이 아닌 경우 "장바구니에 추가"가 표시되고 그렇지 않으면 "장바구니에서 제거"가 표시됩니다.


handleButtonClick 함수의 클릭 기능은 위의 상태에 따라 항목 배열에서 제품을 추가하거나 제거합니다.

페이지 만들기

마지막으로 자습서의 이전 섹션에서 만든 구성 요소를 가져오고 애플리케이션에 대한 페이지 논리를 만들어 보겠습니다.


홈페이지

app 디렉터리에서 page.tsx 열고 다음과 같이 내용을 편집합니다.


 import Image from "next/image"; import Header from "./components/Header"; import Text from "./components/Text"; import Footer from "./components/Footer"; import { getPages } from "./services/fetchData"; import { PageAPI } from "./interfaces/data"; export default async function Home() { const pages = await getPages(); const getValues = (el: PageAPI) => { const { herotitle, herodescription, heroimage } = el.attributeValues.en_US; return { title: herotitle.value[0].htmlValue, description: herodescription.value[0].htmlValue, image: heroimage.value[0].downloadLink, }; }; const pageContent = getValues(pages[0]); return ( <div className="flex flex-col min-h-screen"> <Header pages={pages} /> <div className="flex flex-row mx-auto max-w-screen-xl"> <div className="flex-1"> <Text className="text-6xl pb-10 text-gray-900" text={pageContent.title} /> <Text className="text-xl pb-8 text-gray-500 text-justify" text={pageContent.description} /> </div> <div className="flex-1 flex justify-end items-center"> <Image src={pageContent.image} alt="Photo by Karsten Winegeart on Unsplash" width={450} height={900} /> </div> </div> <Footer /> </div> ); }


홈 페이지에서는 먼저 getPages 함수를 호출하여 헤더에 대한 데이터를 가져옵니다.


그런 다음 getValues 함수를 사용하여 Hero 페이지 데이터를 가져온 다음 더 쉽게 처리할 수 있도록 이를 pageContent 객체로 변환합니다.


그런 다음 가져온 머리글 및 바닥글 구성 요소를 렌더링하고 Hero 제목, 설명 및 이미지에 필요한 값을 전달합니다.


제품 페이지

app 디렉터리와 그 내부에 page.tsx 파일이라는 새 폴더 [category] 를 만듭니다.


특정 파일 이름을 사용하는 것은 NextJS가 경로를 처리하고 URL 매개변수에 액세스하는 데 사용하는 것이기 때문에 중요합니다.


page.tsx 에 다음 코드를 포함합니다.


 import Header from "../components/Header"; import Footer from "../components/Footer"; import Card from "../components/Card"; import { getPages, getProducts } from "../services/fetchData"; import { ProductAPI, URLProps } from "../interfaces/data"; export default async function Product({ params }: URLProps) { const { category } = params; const pages = await getPages(); const products = await getProducts(category); const getValues = (products: ProductAPI[]) => { return products.map((el) => { const { producttitle, productsubtitle, productdescription, productimage, productprice, } = el.attributeValues.en_US; return { id: el.id, category: category, title: producttitle.value[0].htmlValue, subtitle: productsubtitle.value[0].htmlValue, description: productdescription.value[0].htmlValue, image: productimage.value[0].downloadLink, price: productprice.value, }; }); }; const productItems = getValues(products.items); return ( <div className="flex flex-col min-h-screen"> <Header pages={pages} /> <div className="mx-auto max-w-screen-xl px-8"> <h2 className="text-4xl text-gray-900 mb-12"> Browse our {category} collection: </h2> <div className="grid gap-x-6 gap-y-10 grid-cols-4 mt-6"> {productItems.map((product) => { return <Card key={product.id} product={product} />; })} </div> </div> <Footer /> </div> ); }


제품 페이지의 경우 먼저 URL에서 category 매개변수를 가져옵니다. 이를 getProducts 함수에 추가로 전달하여 사이트의 어느 페이지를 방문했는지에 따라 가져와야 하는 제품 카테고리를 설명합니다.


데이터가 수신되면 더 쉬운 처리를 위해 페이지에 필요한 모든 속성으로 구성된 productItems 개체 배열을 만듭니다.


그런 다음 map 메소드를 통해 반복하고 component 폴더에서 가져온 Card 컴포넌트에 소품을 전달하여 화면에 렌더링합니다.


미리보기 페이지

[category] 폴더 안에 [productId] 라는 또 다른 폴더를 만듭니다.


새로 생성된 폴더를 열고 그 안에 다음 코드를 사용하여 page.tsx 파일을 생성합니다.


 import Header from "../../components/Header"; import Preview from "../../components/Preview"; import AddToCart from "../../components/AddToCart"; import Footer from "../../components/Footer"; import { getPages, getProduct } from "../../services/fetchData"; import { ProductAPI, URLProps } from "../../interfaces/data"; export default async function Product({ params }: URLProps) { const { category, productId } = params; const pages = await getPages(); const product = await getProduct(productId); const getValues = (el: ProductAPI) => { const { producttitle, productsubtitle, productdescription, productimage, productprice, } = el.attributeValues.en_US; return { id: el.id, category: category, title: producttitle.value[0].htmlValue, subtitle: productsubtitle.value[0].htmlValue, description: productdescription.value[0].htmlValue, image: productimage.value[0].downloadLink, price: productprice.value, }; }; const productItem = getValues(product); return ( <div className="flex flex-col min-h-screen"> <Header pages={pages} /> <div className="flex mx-auto max-w-screen-xl"> <div className="flex-1 flex justify-start items-center"> <Preview productItem={productItem}> <AddToCart id={productId} category={category} title={productItem.title} subtitle={productItem.subtitle} description={productItem.description} image={productItem.image} price={productItem.price} /> </Preview> </div> </div> <Footer /> </div> ); }


이 페이지를 통해 사용자는 제품 페이지에서 카드를 클릭하면 개별 제품에 대한 자세한 내용을 볼 수 있습니다.


먼저 URL에서 productId 매개변수를 가져와서 getProduct 함수에 추가로 전달하여 미리보기 페이지에 표시되는 제품에 따라 가져와야 하는 제품을 지정합니다.


데이터가 수신되면 Preview 구성 요소에 props로 전달되는 데 필요한 모든 속성으로 구성된 productItem 객체를 생성합니다.


또한 category 매개변수도 가져옵니다. 이를 장바구니에 추가 구성요소에 전달해야 장바구니 페이지의 항목에 대한 유효한 링크를 생성할 수 있기 때문입니다.


장바구니 페이지

마지막으로 app 디렉토리에 새 폴더 cart 만듭니다.


그것을 열고 다음 코드를 사용하여 그 안에 새 파일 page.tsx 만듭니다.


 import Header from "../components/Header"; import Order from "../components/Order"; import Footer from "../components/Footer"; import { getPages } from "../services/fetchData"; export default async function Cart() { const pages = await getPages(); return ( <div className="flex flex-col min-h-screen"> <Header pages={pages} /> <div className="container mx-auto max-w-screen-xl px-8"> <h2 className="text-4xl text-gray-900 mb-12">Shopping cart summary:</h2> <Order /> </div> <Footer /> </div> ); }


먼저 필요한 데이터를 가져온 다음 이를 헤더에 props로 전달했습니다.


그런 다음 탐색 기능이 있는 Header 구성 요소, 사용자가 카트에 추가한 모든 항목을 나열하는 Order 구성 요소, 회사 이름 및 저작권 정보가 포함된 Footer 구성 요소를 렌더링했습니다.

테스트

축하합니다. 작업 프로젝트를 만들었습니다!


먼저 개발자 서버가 아직 실행 중인지 확인하세요. 그렇지 않은 경우 npm run dev 명령을 실행하여 다시 시작하고 localhost:3000 에 액세스하여 확인하세요.


이제 프로젝트는 다음과 같아야 합니다.


최종 프로젝트 연습


보시다시피 홈 섹션 콘텐츠는 데이터 필드에 지정한 홈 속성 세트에서 성공적으로 가져왔습니다.


또한 OneEntry CMS 카탈로그의 모든 항목은 모든 정보가 올바르게 렌더링된 의류 및 장비 섹션에서 가져왔습니다.


NextJS 경로 처리 및 제품 매개변수 덕분에 사용자는 전용 페이지에서 각 제품을 개별적으로 미리 볼 수도 있습니다.


또한 모든 기능과 이벤트가 예상대로 작동하며 사용자는 장바구니에 항목을 추가하거나 제거할 수 있으며 총계가 계산됩니다.

결론

이 튜토리얼에서는 OneEntry CMS 덕분에 사용자가 웹사이트 페이지와 콘텐츠를 생성, 업데이트, 삭제하고 사용하기 쉬운 카탈로그 인터페이스로 제품을 쉽게 관리할 수 있는 전자상거래 프로젝트를 만들었습니다.


코드는 GitHub 에서 사용할 수 있으므로 자유롭게 복제하고 필요에 따라 더 많은 기능을 추가하세요. 더 많은 메뉴 섹션을 추가하거나, 개별 구성 요소를 확장하거나, 더 많은 구성 요소를 추가하여 새로운 기능을 구현할 수도 있습니다.


이 내용이 도움이 되기를 바라며 OneEntry CMS를 백엔드 솔루션으로 사용하는 방법, 애플리케이션의 프런트 엔드와 페어링하는 방법, NextJS, Typescript 및 Tailwind의 최고의 기능을 사용하는 방법에 대한 통찰력을 얻을 수 있기를 바랍니다. .


뉴스레터를 구독하면 제가 발견한 최고의 리소스, 도구, 생산성 팁, 경력 성장 팁을 받아보실 수 있습니다!


또한 Twitter , LinkedIn , GitHub 에서 저와 소통해 보세요!


여기에도 게시됨