Quickstarters
How to Build an E-Commerce Backend?
84 분
이 실습 튜토리얼에서는 백엔드 서비스로부터 시작하여 탄탄한 풀스택 전자상거래 애플리케이션을 처음부터 끝까지 만들 것입니다 그 과정에서 여러분은 안전한 사용자 인증, 권한이 있는 쇼핑객과 관리자만 접근할 수 있도록 보장 직관적인 데이터 모델, 제품 카탈로그, 고객 프로필 및 주문 이력을 지원하도록 설계됨 restful 및 실시간 api, 프론트엔드가 항상 재고, 장바구니 및 체크아웃 상태와 동기화되도록 함 자동화된 배포 파이프라인, 업데이트를 자신 있게 푸시하고 문제가 발생할 경우 롤백할 수 있도록 함 마지막에는 생산 준비가 완료된 상점뿐만 아니라, 웹 애플리케이션을 확장하고, 확장하고, 보안할 수 있는 아키텍처 지식을 갖추게 될 것입니다 완성된 전자상거래 애플리케이션을 웹 전자상거래 앱 에서 확인해 보세요 시작해 봅시다! 주요 요점 빠른 백엔드 설정 back4app의 로우코드 플랫폼을 사용하여 몇 분 안에 전체 전자상거래 데이터 모델을 시작하고 구성하세요 안전한 사용자 흐름 클라우드 코드와 세션 토큰을 사용하여 강력한 가입, 로그인 및 로그아웃을 구현하세요 api 우선 개발 자동 생성된 rest/graphql 엔드포인트와 parse sdk를 통해 데이터를 액세스하세요 클라우드 코드의 비즈니스 로직 권한을 적용하면서 제품 crud, 장바구니 관리 및 체크아웃과 같은 핵심 작업을 중앙 집중화하세요 컨테이너화된 프론트엔드 배포 일관되고 확장 가능한 호스팅을 위해 back4app 웹 배포에서 next js 상점을 패키징하고 배포하세요 전제 조건 및 환경 설정 이 튜토리얼을 따르려면 다음이 필요합니다 back4app 계정 back4app 웹사이트 https //www back4app com/signup 에서 무료로 가입할 수 있습니다 node js가 설치되어 있어야 하며, npm 또는 yarn으로 종속성을 관리할 수 있어야 합니다 back4app의 sdk를 활용하고 사용자 정의 클라우드 코드를 작성하기 위해 javascript에 대한 이해가 필요합니다 사용자 인증, 제품 카탈로그 관리 및 주문 처리와 같은 전자상거래 개념에 대한 기본 이해가 필요합니다 버전 관리를 위한 git, 변경 사항을 추적하고 필요 시 롤백하며 원활하게 협업할 수 있습니다 위의 항목을 확보하면 back4app의 기능을 최대한 활용하고 주요 전자상거래 기능을 구현하는 데 도움이 됩니다 back4app 계정 생성 및 구성 back4app에서 전자상거래 백엔드를 설정하려면 back4app https //www back4app com 를 방문하고 가입하기 이메일 주소를 입력하고, 강력한 비밀번호를 선택하며, 필요한 세부 정보를 제공하세요 이메일을 확인하면 back4app 대시보드에 접근할 수 있습니다 back4app 가입 페이지 대시보드 안에서 새 앱 만들기 또는 새 앱 구축 를 선택하고 프로젝트에 이름을 지정하세요 나중에 쉽게 찾을 수 있도록 설명적인 이름을 사용하는 것이 좋습니다 선택 사항을 확인한 후, 앱 생성 프로세스가 완료될 때까지 기다리세요 프로젝트 대시보드에는 클래스, 분석 및 구성 사항이 표시됩니다 creating a back4app app 이 단계들을 통해 전자상거래 플랫폼을 만들었습니다 다음 섹션에서는 데이터 스키마를 설계하고 제품 관리, 주문 처리 및 사용자 인증을 준비할 것입니다 전자상거래 데이터 스키마 설계 전자상거래 플랫폼을 위한 확장 가능하고 유지 관리가 용이한 백엔드를 구현하기 위해, 다음의 7개 핵심 테이블을 중심으로 데이터를 구성할 것입니다 user , buyer , seller , product , cart , order , and orderdetails 각 테이블은 시스템의 전반적인 기능을 지원하는 데 집중된 역할을 하며, 함께 정규화되고 효율적인 스키마를 형성합니다 사용자 user 테이블로 시작하세요 이 테이블은 모든 사용자 신원의 기초 역할을 합니다 플랫폼의 모든 사람—역할에 관계없이—이 테이블을 사용하여 인증합니다 기본 로그인을 처리하기 위해 username , password , 및 email 과 같은 필드를 포함하세요 검증 상태를 추적하기 위해 emailverified 을 추가하고, 서드파티 로그인이나 oauth 흐름을 지원할 계획이라면 authdata 를 추가하세요 구매자와 판매자를 구분하기 위해 usertype 필드를 정의하세요 이 필드는 접근을 제어하고, 뷰를 관리하며, 사용자 역할에 따라 기능을 분리할 수 있게 해주면서도 단일하고 일관된 로그인 시스템을 유지합니다 모든 사용자 유형을 하나의 테이블에 혼합하는 대신, buyer 와 seller 테이블을 별도로 생성하여 user 를 userid 필드를 통해 참조하세요 이렇게 하면 핵심 사용자 테이블을 부풀리지 않고 역할별 데이터를 저장할 수 있습니다 buyer 테이블은 체크아웃 및 배송 프로세스에 필수적인 paymentinfo 및 shippingaddress 와 같은 구매 관련 필드를 보유할 수 있습니다 반면에 seller 테이블은 storename , bio , 및 phonenumber ,과 같은 상인과 관련된 속성을 추적합니다 이러한 분리를 통해 구매자와 판매자를 독립적으로 쉽게 관리하고 쿼리할 수 있으며, 여전히 그들을 user 의 공유된 신원으로 연결할 수 있습니다 당신의 product 테이블은 판매할 항목이 저장되는 곳입니다 각 제품은 이름, 상세 설명 및 하나 이상의 이미지와 같은 기본 설명 정보를 포함해야 합니다 가격 및 재고 관리를 위해 가격, 불리언을 통한 가용성 상태 isactive , 및 quantityavailable 아래의 재고 수량 필드를 포함해야 합니다 중요하게도, 각 제품은 이를 나열한 판매자와 연결되어야 합니다 그 관계를 설정하기 위해 sellerid 외래 키를 추가하여 seller 테이블을 참조합니다 이 구조는 카탈로그의 모든 항목이 검증된 상인과 연결되도록 보장하며, 판매자별 필터링을 간단하게 만듭니다 구매 전 행동을 처리하기 위해 cart 테이블을 도입합니다 각 장바구니는 사용자에 속하므로 userid 필드를 포함하여 user 와 연결합니다 장바구니의 items 배열을 저장하여, 각 항목은 사용자가 구매할 의도가 있는 제품을 나타내며, 수량이나 선택한 옵션과 같은 필요한 메타데이터를 포함합니다 장바구니의 totalprice 를 추적하면 매 요청마다 값을 재계산하지 않고도 결제 요약을 빠르게 표시할 수 있습니다 이 설정은 기본 장바구니 기능을 지원하고 세션 지속성을 단순화합니다 배열 구조가 제한적이라고 느끼면—특히 더 복잡한 항목 수준 데이터를 기록해야 하는 경우—항목을 나중에 별도의 테이블로 리팩토링할 수 있습니다 구매자가 주문을 할 때, order 테이블에 거래를 기록합니다 각 주문은 buyerid , 즉 구매자가 유효하고 권한이 있는지 확인하기 위해 buyer 테이블을 참조합니다 이 링크 외에도 orderdate , 현재 orderstatus , 및 total 필드 아래의 총 금액을 저장합니다 이는 구매에 대한 명확한 요약을 제공하고 주문 추적 및 이행과 같은 워크플로를 지원합니다 각 주문에는 여러 제품이 포함될 수 있으므로, 개별 항목을 관리하기 위해 orderdetails 테이블이 필요합니다 이 테이블은 각 항목을 orderid 를 통해 부모 주문에 연결하고, productid 를 사용하여 구매한 제품에 연결합니다 구매 상태를 정확하게 기록하려면 quantity 와 unitprice 를 판매 시점에 포함하세요 이렇게 주문 세부정보를 분리하면 보고서를 작성하고, 송장을 생성하며, 반품 또는 조정을 정밀하게 처리할 수 있습니다 이 스키마는 관심사의 명확한 분리와 관계 무결성을 통해 전자상거래 시스템의 강력한 기반을 형성합니다 데이터 모델이 이제 플랫폼의 실제 워크플로우에 맞춰 조정되었으므로, 백엔드에 구현을 시작할 준비가 되었습니다 user , buyer , 및 seller 테이블을 설정하고 그로부터 확장해 나가세요 대시보드에서 클래스 설정하기 아래 단계를 따라 back4app에서 엔티티를 생성하고 연결하세요 user , buyer , seller , cart , product , order , 및 orderdetails , 클래스를 정의한 후 상속, 일대다, 다대다와 같은 관계를 나타내기 위해 포인터를 설정합니다 데이터베이스 브라우저에 접근하기 애플리케이션의 대시보드를 열고 user라는 새 클래스를 만듭니다 왼쪽 메뉴에서 database 를 클릭하세요 back4app dashboard “user” 클래스 만들기 back4app은 자동으로 user 클래스를 생성합니다 user 클래스는 다음 열을 포함합니다 사용자 이름 비밀번호 이메일 등 usertype 열을 추가하려면 새 열 추가 를 선택하세요 add new column 열의 이름, 데이터 유형, 기본값 등을 제공하십시오 add usertype column 참고 parse에서는 모든 클래스에 기본 objectid 필드가 포함되어 있으며, 이는 기본 키로 작동합니다 이는 objectid 가 관계형 모델에서 userid 로 효과적으로 작용함을 의미합니다 “구매자” 클래스 만들기 구매자 클래스를 만들려면 데이터베이스 브라우저 로 돌아가서 클래스 추가 를 선택하십시오 create new class “ 사용자 정의 ”를 선택하고 이름을 구매자 로 지정하십시오 add buyer class “ 포인터 ”라는 열을 추가하고 user 라고 명명하십시오 이는 user 클래스에 포인팅됩니다 create userid class 이는 기본 사용자의 objectid 를 참조하여 “상속”을 시뮬레이션합니다 3\ 다음 열을 추가하십시오 배송 주소 (문자열) 결제 정보 (문자열) 새로운 구매자 객체를 생성할 때, buyer user 를 해당 사용자의 objectid로 설정하여 기존 사용자 레코드에 연결합니다 “판매자” 클래스 생성 같은 과정을 반복하여 판매자 클래스를 생성합니다 user 클래스를 참조하는 user 라는 이름의 포인터 열을 추가합니다 다음 추가 열을 추가하세요 상점 이름 (문자열) 소개 (문자열) 전화번호 (숫자) “제품” 클래스 생성 선택하십시오 클래스 만들기 다시, 이름을 제품 포인터 라는 열을 추가하고 판매자 라는 열을 추가하여 판매자 클래스에 포인팅합니다 (이는 한 판매자가 여러 제품을 가질 수 있는 일대다 관계를 강제합니다) 다음 열을 추가하십시오 이름 (문자열) 설명 (문자열) 가격 (숫자) 재고수량 (숫자) 활성화 여부 (부울) 이미지 (배열) 제품 이미지를 나타내는 객체를 저장합니다 “장바구니” 클래스 만들기 클릭하십시오 클래스 만들기 , 클래스 이름을 장바구니 포인터 라는 열을 추가하고 사용자 라는 열을 추가하여 사용자 클래스에 포인팅합니다 (이는 각 장바구니를 특정 사용자와 연결합니다) 그런 다음 장바구니의 항목을 나타내기 위해 다음 열을 추가하십시오 항목 (배열) 각 장바구니 항목을 나타내는 객체를 저장합니다 각 항목은 { product 제품에 대한 포인터, quantity 숫자 }와 같은 객체일 수 있습니다 총가격 (숫자) “주문” 클래스 만들기 새로운 클래스를 생성합니다 order pointer 라는 열을 추가하고 buyer 라는 이름을 붙여 buyer 클래스(하나의 구매자가 여러 주문을 가질 수 있음을 나타냄)를 가리킵니다 다음 열을 포함하세요 orderdate (날짜 또는 문자열, 선호에 따라 다름) orderstatus (문자열) “orderdetails” 클래스 생성하기 마지막으로, orderdetails 라는 이름의 클래스를 생성합니다 두 개의 pointer 열을 추가하세요 order → order 를 참조합니다 product → product 를 참조합니다 그런 다음, 나머지 열을 추가하세요 quantity (숫자) unitprice (숫자) 이 설정은 order 와 product 간의 다대다 관계를 orderdetails 를 통해 허용합니다 각 주문은 여러 제품을 포함할 수 있고, 각 제품은 여러 주문에 나타날 수 있습니다 관계 시각화 다음은 클래스와 그 포인터를 반영한 간소화된 엔터티 관계 다이어그램(erd)입니다 dbschema2 이 클래스들이 준비되면, user 클래스를 통해 회원가입 및 로그인을 처리하고, 다양한 사용자 유형에 대한 전문화된 구매자 또는 판매자 기록을 생성하며, 명확한 외래 키 참조로 제품 또는 주문 정보를 저장할 수 있습니다 이렇게 데이터베이스를 구조화하면 모든 전자상거래 애플리케이션에 대해 깔끔하고 유지 관리가 용이한 디자인을 얻을 수 있습니다 스키마가 준비되면, 제품 목록 작성, 주문 배치 또는 관리 작업 수행을 제한하기 위해 인증 및 권한 부여를 설정할 준비가 된 것입니다 다음 단계에서는 방금 정의한 역할과 클래스를 기반으로 안전한 가입 및 로그인 흐름을 구성하는 방법을 보여줍니다 back4app으로 인증 구현하기 안전한 인증은 모든 전자상거래 플랫폼의 중심으로, 사용자 계정, 결제 세부정보 및 개인 데이터를 보호하는 데 도움을 줍니다 back4app은 고전적인 사용자 이름/비밀번호 조합부터 소셜 로그인 또는 토큰 기반 흐름에 이르기까지 여러 방법으로 이를 간소화하여 쇼핑객에게 간단하고 안전한 경험을 제공할 수 있도록 합니다 전자상거래 애플리케이션에서 인증을 설정하려면 클라우드 코드를 사용해야 합니다 클라우드 코드는 자체 서버를 호스팅하지 않고도 사용자 지정 비즈니스 로직을 작성할 수 있도록 백엔드 기능을 확장합니다 즉, 주문을 검증하고, 수수료를 계산하거나 특정 이벤트에서 알림을 트리거하는 등의 작업을 back4app의 인프라 내에서 완전히 수행할 수 있습니다 이는 민감한 거래나 엄격한 제어와 신속한 실행이 필요한 데이터 프로세스를 처리하는 이상적인 방법입니다 back4app에서 클라우드 코드를 활성화하려면 앱의 대시보드를 열고 cloud code 섹션을 찾으세요 find cloud code 클라우드 코드를 클릭하면 사용자 정의 기능을 구현할 수 있는 main js 파일이 표시됩니다 cloud code back4app에서 인증 흐름을 구현하려면 사용자를 등록하고, 로그인하고, 로그아웃하는 기능을 클라우드 코드에 배포해야 합니다 새 사용자 등록 (기본적으로 구매자) 새 사용자를 등록하려면 parse의 가입 방법을 사용하세요 username , password , 및 email ,과 같은 표준 필드를 설정하고 역할을 나타내는 사용자 정의 필드를 포함하세요 buyer 또는 seller 기본적으로 새 사용자는 변경하기 전까지 buyer 역할이 부여됩니다 시작하려면 클라우드 코드의 클라우드 디렉토리에 auth js 파일을 만들고 아래 코드 블록을 추가하세요 parse cloud define('signupuser', async (request) => { const { name, password, confirmpassword, email, shippingaddress, paymentinfo } = request params; // validate password match if (password !== confirmpassword) { throw new error('passwords do not match'); } const user = new parse user(); user set('username', name); user set('password', password); user set('email', email); user set('usertype', "buyer"); try { // sign up the user const signedupuser = await user signup(); console log('user registered ', signedupuser id); // create a buyer record linked to the new user const buyer = parse object extend('buyer'); const buyer = new buyer(); buyer set('user', signedupuser); buyer set('shippingaddress', shippingaddress); buyer set('paymentinfo', paymentinfo); const savedbuyer = await buyer save(); console log('buyer created ', savedbuyer id); return { success true, message 'user registered and buyer created', userid signedupuser id, buyerid savedbuyer id }; } catch (error) { console error('sign up failed ', error); throw new error('sign up failed ' + error message); } }); 이 클라우드 함수는 구매자의 사용자 등록을 처리합니다 입력을 검증하고, 새로운 parse 사용자를 생성하며, 역할 ( buyer )을 할당하고, 구매자 전용 데이터를 별도의 구매자 클래스에 저장합니다 사용자 로그인 사용자를 인증하려면 parse의 내장 parse user login 메서드를 호출하고 세션 토큰을 클라이언트에 반환합니다 다음 코드를 auth js 파일에 추가하여 클라우드 코드 백엔드에서 이 로직을 구현합니다 parse cloud define("loginuser", async (request) => { const { email, password } = request params; try { const user = await parse user login(email, password); // example block users with disabled flag if (user get("isdisabled")) { throw new parse error(403, "account is disabled"); } return { sessiontoken user getsessiontoken(), userid user id, }; } catch (error) { throw new parse error(101, "invalid credentials"); } }); 이 함수는 클라이언트로부터 이메일과 비밀번호를 받아 사용자를 인증하려고 시도하며, 성공할 경우 세션 토큰을 반환합니다 계정이 비활성화로 표시된 경우 접근을 차단하고 명확한 오류를 발생시킵니다 로그 아웃 백엔드의 로그 아웃 기능을 구현하려면 아래 코드 블록을 auth js 파일에 추가하세요 parse cloud define('logoutuser', async (request) => { const sessiontoken = request headers\['x parse session token']; if (!sessiontoken) { throw new error('session token required'); } const sessionquery = new parse query(' session'); sessionquery equalto('sessiontoken', sessiontoken); const session = await sessionquery first({ usemasterkey true }); if (session) { await session destroy({ usemasterkey true }); } return { success true, message 'session invalidated' }; }); 이것은 백엔드에서 세션을 안전하게 종료하여 토큰의 추가 사용을 방지합니다 이러한 클라우드 코드 기능이 설정되면, 이제 다음과 같은 안전한 인증 시스템을 갖추게 됩니다 역할 기반 로직으로 사용자를 등록합니다 세션 토큰을 사용하여 인증 및 접근 권한을 부여합니다 사용자 계정을 구매자 전용 기록에 연결합니다 깨끗한 로그인 및 로그 아웃 흐름을 지원합니다 back4app의 내장 세션 및 이메일 확인 도구를 활용합니다 전자상거래 앱을 위한 비즈니스 로직 구축 전자상거래 플랫폼에서 사용자, 제품, 장바구니 및 거래를 관리하려면 back4app에서 일련의 클라우드 코드 기능을 정의해야 합니다 이 기능을 통해 판매자, 제품 및 주문과 같은 리소스를 생성, 읽기, 업데이트 및 삭제(crud)하면서 사용자 역할에 연결된 보안 접근 제어를 유지할 수 있습니다 아래는 주요 엔티티와 작업하는 방법을 보여주는 클라우드 함수 세트입니다 판매자 , 제품 , 장바구니 , 주문 , 및 주문 세부정보 각 기능은 특정 백엔드 작업을 처리하며 사용자 검증 및 역할 검사를 통해 권한이 시행되도록 보장합니다 판매자 플랫폼에서 제품을 제공하고자 하는 사용자를 위한 판매자 프로필을 생성합니다 판매자 생성 이 기능은 현재 사용자의 역할을 판매자로 업데이트하고 판매자 기록을 상점 정보와 함께 생성합니다 이를 클라우드 코드에 구현하려면 먼저 seller js 파일을 생성하고 아래의 코드 블록을 추가합니다 parse cloud define('createseller', async (request) => { const currentuser = request user; // get the current logged in user if (!currentuser) { throw new error('user is not logged in'); } try { // check and update the user type const usertype = currentuser get('usertype'); if (usertype === 'buyer') { currentuser set('usertype', 'seller'); await currentuser save(); } // create the seller object const { businessname, bio, phone } = request params; const seller = parse object extend('seller'); const seller = new seller(); seller set('user', currentuser); seller set('storename', businessname); seller set('bio', bio); seller set('phonenumber', number(phone)); const newseller = await seller save(); console log('seller created ', newseller id); return { success true, message 'seller account created', sellerid newseller id }; } catch (error) { console error('error creating seller ', error); throw new error('error creating seller ' + error message); } }); 이것은 인증된 사용자만 판매자가 될 수 있도록 보장하며, 판매자 기록을 해당 사용자와 포인터를 통해 연결합니다 제품 판매자가 제품을 나열하고 구매자가 탐색, 쿼리 및 구매할 수 있습니다 이 기능을 통해 판매자는 제품을 생성, 검색, 업데이트 및 삭제할 수 있으며, 구매자에게 제품 접근을 제공합니다 제품 생성 이 기능은 로그인한 판매자를 위해 새 제품을 추가하고 최대 세 개의 제품 이미지를 첨부합니다 클라우드 코드에 구현하려면 product js 파일을 만들고 아래의 코드 블록을 추가하세요 parse cloud define("addproduct", async (request) => { // destructure parameters sent from the client const { name, description, price, quantityavailable, isactive, imagefiles } = request params; // optional check if user is logged in (request user is available when an active session exists) const currentuser = request user; if (!currentuser) { throw new error("unauthorized you must be logged in to add a product "); } // ensure the user is indeed a seller by checking their usertype const usertype = currentuser get("usertype"); if (usertype != "seller") { throw new error("this user is not a seller "); } const seller = parse object extend("seller"); const query = new parse query(seller); query equalto("userid", currentuser); // match the pointer field in seller const sellerobj = await query first({ usemasterkey true }); if (!sellerobj) { throw new error("no matching 'seller' object found for this user "); } if(!array isarray(imagefiles) || imagefiles length > 3) { throw new error("a maximum of 3 images are provided"); } // create the new product object const product = parse object extend("product"); const product = new product(); product set("seller", sellerobj); // pointer to the seller product set("name", name); product set("description", description); product set("price", number(price)); product set("quantityavailable", number(quantityavailable)); product set("isactive", isactive); product set("images", imagefiles); // save the product to the database try { const savedproduct = await product save(null, { usemasterkey true }); return savedproduct; } catch (error) { throw new error(`could not create product ${error message}`); } }); 이 기능은 사용자가 로그인되어 있고 판매자 역할을 가지고 있는지, 해당 seller 객체가 존재하는지, 세 개 이상의 이미지가 제출되지 않았는지 확인합니다 검증이 완료되면, 제품을 저장하고 판매자와 연결합니다 모든 제품 조회 이 기능은 시스템의 모든 제품을 검색하여 공개 상점을 구축하는 데 사용할 수 있습니다 이를 클라우드 코드에 구현하려면, 아래 코드 블록을 products js 파일에 추가하세요 parse cloud define("fetchallproducts", async (request) => { try { const product = parse object extend("product"); const query = new parse query(product); const products = await query find({ usemasterkey true }); // return products in a json friendly format return products map((product) => ({ id product id, sellerid product get("seller")? id, name product get("name"), description product get("description"), price product get("price"), quantityavailable product get("quantityavailable"), isactive product get("isactive"), createdat product createdat, updatedat product updatedat, })); } catch (error) { throw new error(`error fetching products ${error message}`); } }); 이 기능은 usemasterkey 플래그를 사용하여 제품을 가져오고, 프론트엔드 소비를 위해 형식을 지정합니다 단일 제품 조회 구매자에게 상세한 제품 정보를 표시할 때 이 기능을 사용하세요 이를 클라우드 코드에 구현하려면 아래 코드 블록을 products js 파일에 추가하세요 parse cloud define("getsingleproduct", async (request) => { const { productid } = request params; const currentuser = request user; // ensure user is logged in if (!currentuser) { throw new error("you must be logged in to retrieve product information "); } if (!productid) { throw new error("missing required parameter productid "); } // query the product class const product = parse object extend("product"); const query = new parse query(product); query equalto("objectid", productid); try { const product = await query first({ usemasterkey true }); if (!product) { throw new error("product not found "); } // return a json friendly object return { objectid product id, name product get("name"), description product get("description"), price product get("price"), quantityavailable product get("quantityavailable"), isactive product get("isactive"), // optionally return more fields like images, category, etc }; } catch (error) { throw new error(`error fetching product ${error message}`); } }); 이 기능은 하나의 항목에 대한 전체 제품 세부정보를 반환합니다 유효한 productid 가 전달되었는지 확인하고 요청자가 로그인했는지 확인합니다 판매자의 제품 조회 이 기능은 판매자가 자신의 모든 제품을 검색할 수 있도록 합니다 이를 클라우드 코드에 구현하려면 아래 코드 블록을 products js 파일에 추가하세요 parse cloud define("getsellerproducts", async (request) => { const currentuser = request user; // ensure the user is logged in if (!currentuser) { throw new error("you must be logged in to fetch seller products "); } // if your schema depends on verifying the user is truly a seller, you can check usertype const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can view their own products "); } // if you want to retrieve products for the currently logged in seller // fetch the seller record that points to currentuser const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // finally, fetch all products pointing to this seller const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("seller", sellerrecord); productquery limit(1000); // adjust or paginate as needed try { const products = await productquery find({ usemasterkey true }); // return a more friendly json structure return products map((prod) => ({ objectid prod id, name prod get("name"), description prod get("description"), price prod get("price"), quantityavailable prod get("quantityavailable"), isactive prod get("isactive"), createdat prod createdat, updatedat prod updatedat, })); } catch (error) { throw new error(`error fetching seller products ${error message}`); } }); 이 기능은 사용자가 판매자인지 확인하고 관련된 seller 레코드를 찾고, 판매자에 대한 포인터를 사용하여 product 클래스를 쿼리합니다 판매자는 이를 사용하여 자신의 모든 제품을 가져옵니다 제품 삭제 이 기능은 제품을 생성한 판매자만이 이를 삭제할 수 있도록 보장합니다 이를 클라우드 코드에 구현하려면, 아래의 코드 블록을 products js 파일에 추가하세요 parse cloud define("deleteproduct", async (request) => { const { productid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to delete a product "); } const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can delete products "); } if (!productid) { throw new error("missing required parameter productid "); } // 1 find the seller record for the current user const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // 2 fetch the product with a query that ensures the user owns it const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); productquery equalto("seller", sellerrecord); // must match the correct seller const product = await productquery first({ usemasterkey true }); if (!product) { throw new error("product not found or you do not have permission to delete it "); } // 3 destroy the product try { await product destroy({ usemasterkey true }); return { message "product deleted successfully ", productid }; } catch (error) { throw new error(`error deleting product ${error message}`); } }); 이 기능은 사용자의 소유권을 확인한 후 데이터베이스에서 제품을 제거합니다 제품 업데이트 판매자가 이 기능을 사용하여 제품의 세부정보를 수정할 수 있도록 허용합니다 이를 클라우드 코드에 구현하려면 아래 코드 블록을 products js 파일에 추가하세요 parse cloud define("updateproduct", async (request) => { const { productid, name, description, price, quantityavailable, isactive } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to update a product "); } const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can update products "); } if (!productid) { throw new error("missing required parameter productid "); } // 1 find the seller record for the current user const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // 2 fetch the product const product = parse object extend("product"); const query = new parse query(product); query equalto("objectid", productid); query equalto("seller", sellerrecord); // must match the seller to ensure ownership const product = await query first({ usemasterkey true }); if (!product) { throw new error("product not found or you do not have permission to modify it "); } // 3 update product fields if (name !== undefined) product set("name", name); if (description !== undefined) product set("description", description); if (price !== undefined) product set("price", price); if (quantityavailable !== undefined) product set("quantityavailable", quantityavailable); if (isactive !== undefined) product set("isactive", isactive); // 4 save changes try { const updatedproduct = await product save(null, { usemasterkey true }); return { objectid updatedproduct id, name updatedproduct get("name"), description updatedproduct get("description"), price updatedproduct get("price"), quantityavailable updatedproduct get("quantityavailable"), isactive updatedproduct get("isactive"), }; } catch (error) { throw new error(`error updating product ${error message}`); } }); 사용자가 판매자인지 확인하고, 제품 소유권을 확인하며, 요청에 제공된 필드만 업데이트합니다 장바구니 장바구니 cart 기능은 구매자가 구매할 항목을 관리할 수 있도록 합니다 이러한 기능에는 장바구니에 추가, 수량 업데이트, 내용 보기 및 항목 제거가 포함됩니다 장바구니에 추가하기 이 기능은 구매자의 장바구니에 제품을 추가하거나 이미 존재하는 경우 수량을 업데이트합니다 이를 구현하기 위해 cart js 파일을 만들고 아래의 코드 블록을 추가하세요 parse cloud define("addtocart", async (request) => { const { productid, quantity } = request params; // ensure there is a currently logged in user const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to modify the cart "); } if (!productid || !quantity) { throw new error("missing required parameters productid and quantity "); } // 1 fetch or create the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); let cart = await cartquery first({ usemasterkey true }); if (!cart) { cart = new cart(); cart set("user", currentuser); cart set("items", \[]); // initialize empty array cart set("totalprice", 0); // initialize price } // 2 fetch the product for price details (or any other attributes you need) const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); const product = await productquery first({ usemasterkey true }); if (!product) { throw new error("product not found "); } const productprice = product get("price") || 0; // 3 insert or update the item in the cart const cartitems = cart get("items") || \[]; // check if this product is already in the cart const existingitemindex = cartitems findindex( (item) => item product objectid === productid ); if (existingitemindex >= 0) { // if product is already in cart, update the quantity cartitems\[existingitemindex] quantity += quantity; } else { // otherwise, add a new entry cartitems push({ product { type "pointer", classname "product", objectid productid, }, quantity quantity }); } // 4 recalculate total price // e g , summation of productprice quantity for each cart item let total = 0; for (const item of cartitems) { if (item product objectid === productid) { // use productprice from the newly fetched product total += productprice item quantity; } else { // this is a simplified approach ideally, you'd also store a price attribute on each item // or re fetch each product's price // for demonstration, we'll just skip them } } cart set("items", cartitems); cart set("totalprice", total); // 5 save the cart object try { const savedcart = await cart save(null, { usemasterkey true }); return { message "cart updated successfully", cartid savedcart id, items savedcart get("items"), totalprice savedcart get("totalprice"), }; } catch (error) { throw new error(`error saving cart ${error message}`); } }); 사용자가 인증되었는지 확인하고, 제품을 검증하며, 항목 목록을 업데이트하고, 총 장바구니 가격을 재계산합니다 장바구니 쿼리하기 현재 사용자의 장바구니 내용을 제품 세부정보 및 소계와 함께 가져옵니다 이를 구현하려면 아래 코드 블록을 cart js 파일에 추가하세요 parse cloud define("getcart", async (request) => { if (!request user) throw "user must be logged in "; const user = request user; const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", user); cartquery include("items product"); // deep include const cart = await cartquery first(); if (!cart) return { items \[], totalprice 0 }; const items = cart get("items") || \[]; const parseditems = \[]; for (const item of items) { const product = item product; parseditems push({ productid product id, name product get("name"), price product get("price"), image product get("image")? url() || null, quantity item quantity, subtotal product get("price") item quantity, }); } return { cartid cart id, items parseditems, totalprice cart get("totalprice") || 0, }; }); 이 기능은 include("items product") 를 사용하여 중첩된 제품 데이터를 한 번의 쿼리로 가져옵니다 장바구니 편집하기 장바구니의 항목 수량을 업데이트하거나 새 수량이 0이면 완전히 제거합니다 이를 구현하려면 아래 코드 블록을 cart js 파일에 추가하세요 parse cloud define("updatecart", async (request) => { const { productid, newquantity } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to update the cart "); } if (!productid || newquantity === undefined) { throw new error("missing required parameters productid and newquantity "); } // fetch or create the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); let cart = await cartquery first({ usemasterkey true }); if (!cart) { // if there's no existing cart, create one cart = new cart(); cart set("user", currentuser); cart set("items", \[]); cart set("totalprice", 0); } let cartitems = cart get("items") || \[]; // find the item matching the productid const itemindex = cartitems findindex( (item) => item product id === productid ); if (itemindex === 1) { throw new error("product not found in cart please add it first "); } // if newquantity <= 0, remove the item from the cart if (newquantity <= 0) { cartitems splice(itemindex, 1); } else { // otherwise, update the quantity cartitems\[itemindex] quantity = newquantity; } // recalculate total by fetching current prices from each product let total = 0; if (cartitems length > 0) { const product = parse object extend("product"); // for each item in the cart, fetch the product to get its price for (const item of cartitems) { const productquery = new parse query(product); productquery equalto("objectid", item product objectid); const product = await productquery first({ usemasterkey true }); if (product) { const productprice = product get("price") || 0; total += productprice item quantity; } } } // update and save the cart cart set("items", cartitems); cart set("totalprice", total); try { const updatedcart = await cart save(null, { usemasterkey true }); return { message "cart updated successfully", cartid updatedcart id, items updatedcart get("items"), totalprice updatedcart get("totalprice"), }; } catch (error) { throw new error(`error updating cart ${error message}`); } }); 업데이트 중에 모든 제품 가격을 재검증하여 총 정확성을 보장합니다 장바구니에서 삭제하기 이 기능은 사용자의 장바구니에서 제품을 제거합니다 이를 구현하기 위해 아래의 코드 블록을 cart js 파일에 추가하세요 parse cloud define("removefromcart", async (request) => { const { productid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to remove items from the cart "); } if (!productid) { throw new error("missing required parameter productid "); } // fetch the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); const cart = await cartquery first({ usemasterkey true }); if (!cart) { throw new error("no existing cart found for this user "); } let cartitems = cart get("items") || \[]; // filter out the item with the specified productid const filtereditems = cartitems filter( (item) => item product id !== productid ); // if nothing changed, the product wasn't in the cart if (filtereditems length === cartitems length) { throw new error("product not found in cart "); } // recalculate the cart total let total = 0; if (filtereditems length > 0) { const product = parse object extend("product"); for (const item of filtereditems) { const productquery = new parse query(product); productquery equalto("objectid", item product objectid); const product = await productquery first({ usemasterkey true }); if (product) { const productprice = product get("price") || 0; total += productprice item quantity; } } } // update and save the cart cart set("items", filtereditems); cart set("totalprice", total); try { const updatedcart = await cart save(null, { usemasterkey true }); return { message "item removed from cart", cartid updatedcart id, items updatedcart get("items"), totalprice updatedcart get("totalprice"), }; } catch (error) { throw new error(`error removing item from cart ${error message}`); } }); 장바구니 배열에서 항목을 필터링하고 저장하기 전에 총액을 재계산합니다 주문 이 기능들은 주문 생성부터 판매자의 주문 내역 가져오기까지 주문 프로세스를 관리합니다 주문 생성 이 기능은 구매자의 장바구니를 공식 주문으로 변환합니다 이를 구현하기 위해 order js 파일을 만들고 아래의 코드 블록을 추가하세요 parse cloud define("createorder", async (request) => { const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to place an order "); } // ensure the user is a buyer (assumes buyer class with user pointer) const buyer = parse object extend("buyer"); const buyerquery = new parse query(buyer); buyerquery equalto("user", currentuser); const buyer = await buyerquery first({ usemasterkey true }); if (!buyer) { throw new error("only buyers can place orders "); } // retrieve the cart for the user const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", buyer get("user")); // assuming the user pointer is stored in buyer const cart = await cartquery first({ usemasterkey true }); if (!cart || !cart get("items") || cart get("items") length === 0) { throw new error("cart is empty"); } const items = cart get("items"); let totalamount = 0; // adjust stock and calculate total order price const orderdetails = \[]; for (const item of items) { const productid = item product id; const quantity = item quantity; const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); const product = await productquery first({ usemasterkey true }); if (!product) { throw new error(`product with id ${productid} not found`); } const availablequantity = product get("quantityavailable"); if (availablequantity < quantity) { throw new error(`not enough stock for product ${product get("name")}`); } // reduce the product quantity product set("quantityavailable", availablequantity quantity); await product save(null, { usemasterkey true }); const unitprice = product get("price"); totalamount += unitprice quantity; // prepare order detail entry orderdetails push({ product, quantity, unitprice }); } // create the new order object const order = parse object extend("order"); const order = new order(); order set("buyer", buyer); // set the buyer pointer order set("total", totalamount); // set the total amount order set("orderstatus", "pending"); // set the order status order set("orderdate", new date()); // set the order date // save the order to the database try { const savedorder = await order save(null, { usemasterkey true }); // create orderdetails for each item const orderdetails = parse object extend("orderdetails"); for (const detail of orderdetails) { const orderdetail = new orderdetails(); orderdetail set("order", savedorder); orderdetail set("product", detail product); orderdetail set("quantity", detail quantity); orderdetail set("unitprice", detail unitprice); await orderdetail save(null, { usemasterkey true }); } // optionally, clear the cart after saving the order cart set("items", \[]); await cart save(null, { usemasterkey true }); return savedorder; } catch (error) { throw new error(`could not create order ${error message}`); } }); 구매자를 검증하고, 재고 가용성을 확인하며, 제품 재고를 줄이고, 주문 및 주문 세부정보 를 저장하며, 성공적인 주문 후 장바구니를 비웁니다 판매자 주문 가져오기 이 기능은 현재 판매자가 판매하는 제품을 포함한 모든 주문을 검색합니다 이를 구현하려면 아래 코드 블록을 order js 파일에 추가하세요 parse cloud define("getsellerorders", async (request) => { const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to view your orders "); } if (currentuser get("usertype") !== "seller") { throw new error("only sellers can access this endpoint "); } try { const seller = parse object extend("seller"); const seller = await new parse query(seller) equalto("userid", currentuser) first({ usemasterkey true }); if (!seller) { throw new error("seller profile not found "); } // find products by this seller const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("seller", seller); const products = await productquery find({ usemasterkey true }); if (!products || products length === 0) { return \[]; } // get product ids const productids = products map(product => product id); // find order details containing these products const orderdetails = parse object extend("orderdetails"); const orderdetailsquery = new parse query(orderdetails); orderdetailsquery containedin("product", products); const orderdetails = await orderdetailsquery find({ usemasterkey true }); if (!orderdetails || orderdetails length === 0) { return \[]; } const orderids = \[ new set(orderdetails map(detail => detail get("order") id))]; // query orders for these ids const order = parse object extend("order"); const orderquery = new parse query(order); orderquery containedin("objectid", orderids); orderquery include("buyer"); const orders = await orderquery find({ usemasterkey true }); // return the orders return orders map(order => ({ id order id, total order get("total"), status order get("orderstatus"), date order get("orderdate"), buyer order get("buyer") })); } catch (error) { throw new error(`could not fetch orders ${error message}`); } }); 판매자가 소유한 제품을 찾고, orderdetails 를 쿼리하여 해당 제품의 주문을 가져오고, 구매자 정보를 포함한 간소화된 구조를 반환합니다 주문 상태 업데이트 이 기능은 주문의 상태를 “대기 중”과 “완료” 사이에서 전환합니다 이를 구현하기 위해 아래의 코드 블록을 order js 파일에 추가하세요 parse cloud define("updateorder", async (request) => { const { orderid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to view your orders "); } if (currentuser get("usertype") !== "seller") { throw new error("only sellers can access this endpoint "); } // validate the input parameter if (!orderid) { throw new error("missing required field orderid"); } try { // find the order with the specified orderid const order = parse object extend("order"); const query = new parse query(order); query equalto("objectid", orderid); const order = await query first({ usemasterkey true }); if (!order) { throw new error("order not found"); } // check current status // update the order status if (order get("orderstatus") === "completed") { order set("orderstatus", "pending"); } else { order set("orderstatus", "completed"); } // save the updated order await order save(null, { usemasterkey true }); return { success true, message "order status updated to completed" }; } catch (error) { throw new error(`could not update order status ${error message}`); } }); 이것은 판매자만 이 작업을 수행할 수 있도록 하고, 상태를 업데이트하기 전에 주문이 존재하는지 확인합니다 이 백엔드 기능은 귀하의 전자상거래 앱이 cloud code를 사용하여 back4app 내에서 제품 목록, 구매자 장바구니, 주문 및 판매자 재고와 같은 모든 핵심 작업을 안전하게 관리할 수 있는 기능을 제공합니다 외부에서 이러한 클라우드 기능에 접근할 수 있도록 하려면, 모든 클라우드 코드 파일을 main js 파일에 등록해야 합니다 이를 위해 아래의 코드 블록을 main js 파일에 추가하면 됩니다 //main js require(" /auth js") require(" /cart js") require(" /order js") require(" /product js") require(" /seller js") 프론트엔드 생성 비주얼적으로 전자상거래 스토어프론트를 구축하기 위해 v0 dev를 사용할 것입니다 이를 위해 v0 dev http //v0 dev 계정이 없으면 계정을 생성하세요 계정이 설정되면 프론트엔드를 만들기 시작할 수 있습니다 프론트엔드를 만들기 위해 아래의 프롬프트를 입력하세요 create an e commerce web application with authentication features and a buyer and seller feature 프롬프트는 요청된 기능을 갖춘 next js 전자상거래 앱을 구축합니다 create ecommerce app 미리보기 기능을 사용하면 애플리케이션을 탐색하여 모든 것이 예상대로 작동하는지 확인할 수 있습니다 복잡한 문제가 있거나 더 많은 기능을 추가하고 싶다면 다른 프롬프트를 전달하면 v0이 애플리케이션을 수정합니다 예를 들어, 프롬프트를 전달하세요 the seller can create, add, delete and update products 이 프롬프트는 앱을 수정하여 판매자가 crud 작업을 수행할 수 있는 페이지를 생성합니다 modify ecommerce app 마지막으로, 프롬프트를 전달하세요 do not sort products by categories and remove the categories page and input fields, remove the second and third step from the process of becoming a seller and add a bio input field to the first step, users should only sign in with email, and enhance the seller functionality to fully support product management (crud operations) 이 프롬프트는 애플리케이션에 최종 수정을 합니다 수정된 애플리케이션의 미리보기를 보려면 위 이미지의 강조된 섹션에 있는 보기 버튼을 클릭하세요 애플리케이션이 원하는 것임을 확신하면, 다음 단계는 프로젝트를 로컬 장치로 가져오는 것입니다 이를 위해 v0가 생성한 다운로드 버튼을 클릭하세요 download button 버튼을 클릭하면 링크와 함께 download zip 버튼이 있는 드롭다운 메뉴가 나타납니다 dropdown menu 다음으로, download zip 버튼을 클릭하세요 zip 다운로드가 완료되면 터미널을 열고 원하는 디렉토리에 ecommerce app이라는 새 폴더를 만드세요 mkdir ecommerce app 이제 zip 폴더의 내용을 ecommerce app 폴더로 추출하세요 터미널에서 ecommerce app 디렉토리로 이동하고 필요한 종속성을 설치하세요 이를 위해 다음 명령어를 실행하세요 cd ecommerce app npm install 종속성을 설치한 후, 터미널에서 npm run dev 명령어를 실행하여 로컬호스트 서버에서 프로젝트를 확인하세요 프론트엔드를 백엔드와 통합하기 프론트엔드를 back4app 백엔드의 cloud code 함수와 연결하려면 parse javascript sdk를 사용합니다 sdk는 사용자를 인증하고, 백엔드 기능을 호출하며, 앱의 데이터 모델과 안전하고 효율적으로 상호작용할 수 있게 해줍니다 sdk를 설정하려면, 터미널에서 프로젝트 디렉토리에서 다음 명령어를 실행하세요 npm install parse 그런 다음 lib/parse js 파일을 만들어 연결을 구성합니다 이 파일에 아래의 코드 블록을 입력하세요 import parse from "parse/dist/parse min js"; parse initialize("your app id", "your javascript key"); parse serverurl = "https //parseapi back4app com"; export default parse; your app id와 your javascript key 를 back4app 대시보드의 앱 설정 에서 찾은 자격 증명으로 교체하세요 이 기본 구성은 sdk가 특정 프로젝트에 연결하는 방법을 알 수 있도록 합니다 이제 프론트엔드를 백엔드에 연결했으므로, back4app 애플리케이션에서 정의한 클라우드 코드 함수를 호출하는 함수를 작성할 수 있습니다 클라이언트에서의 인증 이 섹션에서는 parse sdk를 사용하여 프론트엔드에서 사용자 가입, 로그인 및 로그아웃을 처리하는 방법을 보여줍니다 각 함수는 back4app 백엔드에서 구현한 클라우드 코드 함수 또는 sdk 메서드에 해당합니다 애플리케이션에서 app/auth/register 디렉토리는 사용자 가입 로직을 보유하고 있습니다 page tsx 에서 formdata 상태를 정의합니다 이 상태는 사용자를 가입시키는 데 필요한 자격 증명을 보유합니다 formdata 상태는 다음과 같아야 합니다 const \[formdata, setformdata] = usestate({ name "", email "", password "", confirmpassword "", shippingaddress "", paymentinfo "", }); 전달된 자격 증명을 사용하여 formdata , 당신은 back4app 애플리케이션에서 정의한 signupuser 클라우드 함수를 호출할 수 있습니다 lib/parse js 에서 parse를 가져오는 것을 잊지 마세요 import parse from "@/lib/parse"; const handlesubmit = async(e react formevent) => { e preventdefault(); try { const response = await parse cloud run("signupuser", { name formdata name, email formdata email, password formdata password, confirmpassword formdata confirmpassword, shippingaddress formdata shippingaddress, paymentinfo formdata paymentinfo, }); console log("signup successful ", response); } catch (error any) { console error("signup failed ", error); } } 이 함수는 회원가입 양식을 제출할 때 실행됩니다 signupuser 클라우드 함수를 호출합니다 이 함수는 사용자 자격 증명과 구매자 특정 데이터(배송 및 결제 정보와 같은)를 전달합니다 마지막으로, 작업이 성공하면 응답을 기록하고 실패하면 오류를 출력합니다 사용자 로그인 사용자를 로그인하려면, loginuser 클라우드 코드 함수를 백엔드에서 사용하세요 애플리케이션의 app/auth/login 디렉토리로 이동합니다 page tsx 파일 내에서, v0는 사용자를 로그인하는 데 필요한 사용자 자격 증명을 포함하는 상태를 생성합니다 필요한 것은 그 자격 증명을 loginuser 함수에 전달하는 것입니다 예를 들어 import parse from "@/lib/parse"; const handlesubmit = async (e react formevent) => { e preventdefault(); setisloading(true); try { const result = await parse cloud run("loginuser", { email, password }); // log in using returned session token await parse user become(result sessiontoken); console log("user logged in with session ", result sessiontoken); setisloading(false); router push("/"); // proceed to app } catch (error) { console error("login failed ", error); } }; 이 함수는 클라우드 함수 loginuser , 사용자의 로그인 자격 증명을 서버에 전송합니다 유효한 세션 토큰을 응답으로 받은 후, parse user become 을 사용하여 클라이언트를 인증하고 세션을 로그인한 사용자에게 속하도록 설정합니다 사용자 로그아웃 사용자를 안전하게 로그아웃하려면 parse user logout() 와 사용자 정의 logoutuser 클라우드 코드 함수를 모두 호출해야 합니다 이렇게 하면 세션 토큰이 클라이언트에서 지워지고 서버에서 무효화됩니다 로그아웃 버튼은 헤더 컴포넌트에서 찾을 수 있습니다 이 컴포넌트는 루트 디렉토리의 components 폴더에 있습니다 컴포넌트에서 이 함수를 정의하세요 const handlelogout = async () => { const sessiontoken = parse user current()? get("sessiontoken"); if (sessiontoken) { await parse user become(sessiontoken); // ensures proper context await parse cloud run("logoutuser"); // delete the session on server } await parse user logout(); // clear the local session }; 이 함수는 사용자를 로그아웃하고 클라이언트와 서버 모두에서 세션을 지웁니다 컴포넌트의 jsx 부분에서 onclick 이벤트를 사용하여 로그아웃 버튼에 함수를 바인딩하세요 예를 들어 \<dropdownmenuitem onclick={handlelogout}> logout \</dropdownmenuitem> 판매자 온보딩 이 섹션에서는 구매자가 계정을 업그레이드하고 판매자로 등록할 수 있는 함수를 보여줍니다 애플리케이션에서 app/seller/register 디렉토리로 이동하세요 이 디렉토리에서 page tsx 파일을 찾을 수 있으며, 여기에서 createseller 클라우드 함수를 호출하는 함수를 정의합니다 이 함수는 현재 사용자에 대한 판매자 프로필을 생성하는 역할을 합니다 page tsx 파일에서 formdata 상태를 다음과 같이 수정하세요 const \[formdata, setformdata] = usestate({ phone "", bio "", businessname "", }); v0에서 생성한 handlesubmit 함수의 로직을 아래의 로직으로 교체하세요 const handlesubmit = async (e react formevent) => { e preventdefault(); try { const result = await parse cloud run("createseller", { businessname formdata businessname, bio formdata bio, phone formdata phone, }); console log("seller created ", result sellerid); } catch (error) { console error("error creating seller ", error); } }; 양식을 제출할 때 이 함수를 호출하여 사용자에 대한 판매자 프로필을 생성합니다 제품 관리 판매자로서 제품 작업을 관리하기 위해 프론트엔드에서 이러한 함수 호출을 사용하세요 제품 생성 데이터베이스에 제품을 추가하는 클라우드 코드 함수를 호출하려면 app/seller/products/new 디렉토리를 찾으세요 이 디렉토리 내에는 page tsx 파일이 있으며, 이 파일에는 제품에 대한 정보와 이미지를 포함하는 양식이 있습니다 다음과 같이 product 상태를 수정하세요 const \[product, setproduct] = usestate({ name "", price "", description "", stock "", status "active", }); 또한 이미지를 보관할 상태를 생성하세요 const \[images, setimages] = usestate\<file\[]>(\[]); 이제 이미지를 데이터베이스에 추가하는 함수를 작성하세요 이 함수는 다음과 같아야 합니다 const handleimageupload = async (event) => { event preventdefault(); try { let name = "image jpg"; const file = new parse file(name, event target files\[0]); const photo = await file save(); setimages((prev) => \[ prev, photo]); console log("file saved ", file); alert(`image uploaded successfully`); } catch (error) { console error("error saving file ", error); } }; 파일 업로드를 담당하는 입력에 이 함수를 onchange 이벤트에 바인딩합니다 다음으로, handlesubmit 함수를 정의합니다 이 함수는 필요한 정보를 전달하면서 addproduct 클라우드 코드를 호출합니다 이와 같이 const handlesubmit = async (e react formevent) => { e preventdefault(); try { interface addproductparams { name string; description string; price number; quantityavailable number; isactive boolean; } interface addproductresponse { id string; } parse cloud run\<addproductresponse, addproductparams>("addproduct", { name product name, description product description, price product price, quantityavailable product stock, isactive product status === "active", imagefiles images, }) then((response) => { console log("product created ", response id); }); } catch (error) { console error("error creating product ", error); } }; 이 호출은 제품 데이터를 포함하여 이미지를 백엔드에 제출하여 새로운 목록을 생성합니다 모든 제품 가져오기 이 호출은 공개 제품 목록 또는 홈페이지 피드를 위한 전체 카탈로그를 검색합니다 fetchallproducts 클라우드 함수를 호출하는 함수를 만들기 위해 app/products 디렉토리로 이동합니다 다음의 page tsx 파일에 이 코드를 추가하세요 interface product { id string; name string; description string; price number; image? string; } const \[products, setproducts] = usestate\<product\[]>(\[]); async function getallproducts() { try { const response = await parse cloud run("fetchallproducts"); console log("products fetched ", response); return response; } catch (error) { console error("error fetching products ", error); throw error; } } 위의 코드 블록은 제품에 대한 인터페이스와 products 상태를 정의합니다 getallproducts 함수는 데이터베이스에서 모든 제품을 가져오기 위해 클라우드 함수 fetchallproducts 를 호출합니다 페이지 로드 시 제품을 가져오고 렌더링하기 위해 위의 getallproducts 함수를 useeffect() 훅 안에서 호출해야 합니다 이와 같이 useeffect(() => { getallproducts() then((products) => { setproducts(products); }) catch((error) => { console error("error fetching products ", error); }); }, \[]); 단일 제품 가져오기 이 호출은 특정 제품에 대한 전체 세부정보를 검색합니다 \[id] 폴더로 이동하여 app/products 디렉토리에서 이 폴더는 page tsx 파일을 포함하고 있으며, 이 파일은 단일 제품의 세부정보를 가져오는 로직을 포함합니다 page tsx 파일에 이 코드를 작성하세요 interface product { id string; name string; description string; price number; images? string\[]; quantityavailable number; } const \[product, setproduct] = usestate\<product>({ id "", name "", description "", price 0, images \[], quantityavailable 0, }); async function getsingleproduct() { try { const response = await parse cloud run("getsingleproduct", {productid params id}); console log("product fetched ", response); return response; } catch (error) { console error("error fetching products ", error); throw error; } } useeffect(() => { getsingleproduct() then((product) => { setproduct(product); }) catch((error) => { console error("error fetching products ", error); }); }, \[]); 이 코드 블록은 제품 객체에 대한 인터페이스를 정의하고 기본값으로 product 상태 변수를 설정합니다 getsingleproduct 함수는 경로 매개변수에서 productid 를 사용하여 getsingleproduct 클라우드 코드 함수를 호출합니다 이 useeffect 훅은 컴포넌트가 마운트될 때 한 번 실행됩니다 제품 업데이트 판매자가 제품을 편집할 때, 업데이트된 필드와 함께 이 함수를 호출합니다 제품을 업데이트하려면 새로운 데이터를 수집하는 입력 필드가 있는 양식이 필요하며, 이 양식은 page tsx 파일에 있습니다 이 파일은 app/seller/products/\[id]/edit 디렉토리에 위치해 있습니다 제품을 업데이트하기 전에 제품을 가져와야 하며, 이를 위해 이 코드를 page tsx 파일에 추가하세요 const \[product, setproduct] = usestate({ id "", name "", price "", description "", stock "", images \["", "", ""], status "active", }); useeffect(() => { async function fetchproduct() { try { const response = await parse cloud run("getsingleproduct", { productid params id, }); setproduct({ id response objectid, name response name || "", price string(response price ?? "0"), description response description || "", stock string(response quantityavailable ?? "0"), // fallback to placeholders if actual image data is not provided images \[ response image1 || `/placeholder svg?height=500\&width=500\&text=product`, response image2 || `/placeholder svg?height=500\&width=500\&text=productview+2`, response image3 || `/placeholder svg?height=500\&width=500\&text=productview+3`, ], status response isactive ? "active" "out of stock", }); } catch (error) { console error("error fetching product ", error); } } fetchproduct(); }, \[]); 이 코드는 back4app 백엔드에서 단일 제품의 세부 정보를 가져오고 getsingleproduct 클라우드 코드 함수를 사용하여 세부 정보를 컴포넌트 상태 product 에 저장하여 수정하기 전에 양식의 기본값으로 표시합니다 제품 세부 정보를 가져온 후, 양식을 사용하여 세부 정보를 변경할 수 있습니다 양식을 작성한 후, 양식을 제출하면 handlesubmit 함수를 호출하여 제품 업데이트 로직을 처리합니다 당신의 handlesubmit 함수는 다음과 같아야 합니다 const handlesubmit = async (e react formevent) => { e preventdefault(); try { await parse cloud run("updateproduct", { productid product id, name product name, description product description, price parsefloat(product price), quantityavailable parseint(product stock, 10), isactive product status === "active", }); } catch (error) { console error("error updating product ", error); } }; 이 함수는 당신의 updateproduct 클라우드 코드 함수를 호출하고 업데이트된 제품 데이터를 포함한 객체를 보냅니다 제품 삭제 판매자로부터 확인 후 제품을 삭제하기 위해 이 함수를 사용하세요 제품을 삭제하려면 handledelete 함수를 추가해야 하며, 이 함수는 제품 삭제에 필요한 로직을 포함합니다 당신은 handledelete 함수를 page tsx 파일의 app/seller/products/\[id]/edit 디렉토리에서 정의할 것입니다 const handledelete = async () => { const confirmdelete = confirm( "are you sure you want to delete this product? this action cannot be undone " ); if (!confirmdelete) return; try { await parse cloud run("deleteproduct", { productid product id, }); } catch (error) { console error("error deleting product ", error); } }; 이 handledelete 함수는 사용자의 의도를 먼저 확인한 후, 당신의 백엔드에 정의된 deleteproduct 클라우드 코드 함수를 호출합니다 handledelete 함수를 컴포넌트의 jsx 부분에서 삭제 버튼에 바인딩하세요 예를 들어 \<button type="button" variant="destructive" onclick={handledelete} \> \<trash2 classname="h 4 w 4 mr 2" /> delete product \</button> 판매자의 제품 가져오기 현재 로그인한 판매자가 소유한 모든 제품을 가져오기 위해 이 함수를 사용하세요 판매자의 제품을 가져오는 함수를 page tsx 파일의 app/seller/dashboard 디렉토리에서 정의할 것입니다 파일에 이 코드를 작성하세요 const \[products, setproducts] = usestate< { objectid string; name string; price number; quantityavailable number; sales number; isactive boolean; }\[] \>(\[]); useeffect(() => { async function fetchproducts() { try { const result = await parse cloud run("getsellerproducts"); setproducts(result); } catch (error) { console error("error fetching products ", error); } }; fetchproducts(); }, \[]); 이 코드 블록은 products 상태를 정의하고 useeffect 훅을 사용하여 fetchproducts 함수를 한 번 로드 시 호출합니다 이 fetchproducts 함수는 getsellerproducts 클라우드 코드 함수를 호출하고 products 상태를 호출 결과로 업데이트합니다 장바구니 관리 이 호출은 구매자가 주문을 하기 전에 장바구니를 관리할 수 있게 해줍니다 장바구니에 추가 이 호출은 선택한 제품을 사용자의 장바구니에 추가하거나 수량을 증가시킵니다 page tsx 파일을 app/products 디렉토리에서 찾아 다음 코드를 추가하세요 async function handleaddtocart(productid string, quantity number = 1) { try { const response = await parse cloud run("addtocart", { productid, quantity, }); console log("add to cart success ", response); } catch (error) { console error("failed to add to cart ", error); } } 함수 handleaddtocart 는 productid와 선택적 수량을 인수로 받아들이며, 백엔드에서 addtocart 클라우드 코드를 호출합니다 이 함수는 사용자가 제품 카드에서 "장바구니에 추가" 버튼을 클릭할 때 실행되어야 합니다 예를 들어 \<button classname="w full" size="sm" onclick={() => handleaddtocart(product id)}> \<shoppingcart classname="h 4 w 4 mr 2" /> add to cart \</button> 파일에 이 코드를 작성하세요 장바구니 보기 장바구니의 내용을 가져오고 실시간 장바구니 요약을 렌더링하는 데 사용합니다 이를 위해, page tsx 파일로 이동하여 app/cart 디렉토리 내에 아래 코드 블록을 추가하세요 interface cartitem { id string; // product's objectid name string; price number; quantity number; image string; } const \[cartitems, setcartitems] = usestate\<cartitem\[]>(\[]); useeffect(() => { async function fetchcart() { try { const response = await parse cloud run("getcart"); const parsed = response items map((item) => ({ id item productid, name item name, price item price, quantity item quantity, image item image, })); setcartitems(parsed); } catch (error) { console error("failed to fetch cart ", error); } } fetchcart(); }, \[]); 위의 코드 블록은 인터페이스 cartitem 을 사용하여 프론트엔드에서 장바구니 데이터의 형태를 정의하고, 장바구니 항목을 보유하는 cartitems 상태를 정의합니다 또한 useeffect 를 사용하여 페이지 로드 시 한 번 fetchcart 함수를 트리거합니다 이 fetchcart 함수는 클라우드 코드 함수 getcart 를 호출하여 백엔드에서 장바구니를 가져옵니다 이 함수는 map 메서드를 사용하여 백엔드 형식을 프론트엔드 형식으로 변환한 후 처리된 장바구니 데이터를 cartitems 상태에 저장합니다 장바구니 항목 업데이트 이 호출은 장바구니의 특정 제품 수량을 변경합니다 page tsx 파일은 app/cart 디렉토리에 있으며, updatequantity 함수가 있습니다 아래의 함수로 해당 함수를 교체하세요 const updatequantity = async (productid string, newquantity number) => { if (newquantity < 1) return; try { await parse cloud run("updatecart", { productid, newquantity, }); console log("updated the cart"); } catch (error) { console error("failed to update cart item ", error); } }; 이 함수는 두 개의 매개변수를 받습니다, productid 와 사용자가 설정하고자 하는 업데이트된 수량 newquantity 이 함수는 updatecart 클라우드 함수를 호출하고 productid 와 newquantity 를 매개변수로 보냅니다 이 함수를 +/ 버튼에 onclick 이벤트를 사용하여 바인딩합니다 이와 같이 \<div classname="flex items center gap 2"> \<button variant="outline" size="icon" classname="h 8 w 8" onclick={() => updatequantity(item id, item quantity 1)} \> \<minus classname="h 3 w 3" /> \<span classname="sr only">decrease quantity\</span> \</button> \<span classname="w 8 text center">{item quantity}\</span> \<button variant="outline" size="icon" classname="h 8 w 8" onclick={() => updatequantity(item id, item quantity + 1)} \> \<plus classname="h 3 w 3" /> \<span classname="sr only">increase quantity\</span> \</button> \</div> 장바구니에서 제거 장바구니에서 항목을 완전히 제거합니다 이를 위해 아래 코드 블록을 page tsx 파일에 추가하십시오 app/cart 디렉토리 내의 이 파일 const removeitem = async (productid string) => { try { await parse cloud run("removefromcart", { productid, }); setcartitems((items) => items filter((item) => item id !== productid)); } catch (error) { console error("failed to remove item ", error); } }; 이 함수, removeitem ,은 백엔드에서 클라우드 함수 removefromcart 을 호출하여 사용자의 장바구니에서 특정 제품을 제거합니다 이 함수는 또한 프론트엔드에서 변경 사항을 반영하기 위해 로컬 장바구니 상태를 업데이트합니다 이 함수를 "항목 제거" 버튼에 바인딩하십시오, 다음과 같이 \<button variant="ghost" size="icon" classname="h 8 w 8 text muted foreground" onclick={() => removeitem(item id)} \> \<trash2 classname="h 4 w 4" /> \<span classname="sr only">remove item\</span> \</button> 주문 관리 이 기능은 구매자와 판매자를 위한 주문 생성 및 추적을 처리합니다 주문 생성 이것은 사용자의 장바구니의 현재 내용에서 주문을 생성합니다 이 기능은 page tsx 파일에서 app/cart 디렉토리에 정의됩니다 주문을 생성하는 로직을 포함하는 함수는 다음과 같아야 합니다 const handleorder = async () => { try { const user = parse user current(); if (!user) { throw new error("you must be logged in to place an order "); } const result = await parse cloud run("createorder"); console log("order created successfully ", result); } catch (error any) { console error("failed to create order ", error); } }; 이 함수, handleorder ,는 back4app 백엔드를 통해 주문을 하여 체크아웃 프로세스를 처리합니다 사용자가 로그인했는지 확인한 후, createorder 클라우드 함수를 호출하여 현재 사용자의 장바구니를 공식 주문으로 변환합니다 체크아웃에서 이 함수를 호출하려면, onclick 이벤트를 사용하여 "checkout" 버튼에 함수를 바인딩하세요 예를 들어 \<button classname="w full" onclick={handleorder}>proceed to checkout\</button> 판매자 주문 보기 판매자는 이 호출을 사용하여 자신의 제품이 포함된 주문을 볼 수 있습니다 page tsx 파일을 app/seller/dashboard 디렉토리에 정의하세요 다음 코드를 page tsx 파일에 추가하세요 const \[ordersdata, setordersdata] = usestate<{ id string; total number; buyer string; date date; status string; }\[] \>(\[]); async function fetchsellerorders() { try { const orders = await parse cloud run("getsellerorders"); console log("seller orders ", orders); setordersdata(orders); } catch (err) { console error("error fetching seller orders ", err); } }; 이 코드는 판매자의 제품이 포함된 주문 목록을 검색하여 판매자 대시보드 또는 주문 관리 보기에서 표시하기 위해 로컬 상태 ordersdata 에 저장합니다 페이지 로드 시 함수가 한 번 실행되도록 useeffect 훅에서 함수를 호출해야 합니다 주문 업데이트 이것은 주문의 상태를 "대기 중"과 "완료" 사이에서 전환합니다 page tsx 파일을 app/seller/dashboard 디렉토리에 정의하세요 함수는 다음과 같아야 합니다 const handleupdate = async (id string) => { const confirmupdate = confirm( "are you sure you want to update this order?" ); if (!confirmupdate) return; try { await parse cloud run("completeorder", { orderid id, }); console log("updated order"); } catch (error) { console error("error updating order ", error); } };const handleupdate = async (id string) => { const confirmupdate = confirm( "are you sure you want to update this order?" ); if (!confirmupdate) return; try { await parse cloud run("completeorder", { orderid id, }); console log("updated order"); } catch (error) { console error("error updating order ", error); } };const handleupdate = async (id string) => { const confirmupdate = confirm( "are you sure you want to update this order?" ); if (!confirmupdate) return; try { await parse cloud run("completeorder", { orderid id, }); console log("updated order"); } catch (error) { console error("error updating order ", error); } }; 이 함수, handleupdate , 기존 주문의 상태를 업데이트합니다 이는 back4app 백엔드의 completeorder 라는 클라우드 함수를 호출하여 이루어집니다 이 함수를 컴포넌트의 jsx 부분에서 "edit" 버튼에 바인딩하세요 이와 같이 \<button variant="ghost" size="icon" onclick={() => {handleupdate(order id)}}> \<pencil classname="h 4 w 4" /> \<span classname="sr only">view\</span> \</button> back4app 컨테이너에 프론트엔드 배포하기 back4app 컨테이너는 프론트엔드 애플리케이션을 패키징하고 배포하는 간소화된 방법을 제공합니다 별도의 호스팅 서비스를 관리하는 대신, 모든 종속성을 docker 이미지 내에 저장하여 일관된 성능을 보장하고 유지 관리를 간소화할 수 있습니다 이 컨테이너 기반 접근 방식은 next js 애플리케이션에 특히 유용하며, 속도와 신뢰성을 위해 빌드를 최적화할 수 있습니다 애플리케이션을 컨테이너화하려면, dockerfile 을 생성하여 애플리케이션이 어떻게 빌드되고 제공되는지를 정의하세요 아래는 next js 애플리케이션을 위한 샘플 dockerfile입니다 # stage 1 build the next js app from node 20 alpine as builder workdir /app copy package json package lock json / run npm install copy run npm run build \# stage 2 run the next js app from node 20 alpine workdir /app copy from=builder /app / expose 3000 cmd \["npm", "start"] dockerfile을 생성한 후, dockerignore 파일을 생성하고 다음 명령어를 추가하세요 # node modules (reinstalled in docker) node modules \# next js build output next out \# logs npm debug log yarn debug log yarn error log pnpm debug log \# env files (optional — only if you handle secrets another way) env env local env development env production env test \# os / ide / editor junk ds store thumbs db vscode idea \# git git gitignore 이제 애플리케이션을 로컬에서 빌드하고 테스트할 수 있습니다 이를 위해 터미널에서 다음 명령어를 실행하세요 docker build t ecommerce app docker run p 3000 3000 ecommerce app 열기 http //localhost 3000 사이트가 제대로 작동하는지 확인하세요 모든 것이 예상대로 작동하는지 확인한 후, 코드를 github에 푸시하고 back4app을 통해 배포할 시간입니다 코드가 github 리포지토리에 커밋되고 푸시되었는지 확인하세요 이렇게 하면 back4app이 배포를 위해 프로젝트에 접근할 수 있습니다 back4app 계정에서 대시보드로 이동하여 화면 상단의 대시보드 드롭다운 메뉴를 클릭하세요 dashboard menu 드롭다운에서 웹 배포 플랫폼 을 선택하세요 web deployment platform 이것은 웹 애플리케이션 배포 페이지로 이동합니다 deploy web application page 배포 프로세스를 시작하려면 웹 앱 배포 버튼을 클릭하세요 link github repo 그런 다음 github 리포 가져오기 를 클릭하고 dockerfile이 포함된 리포지토리를 선택하세요 프로젝트에 이름을 지정하세요 (예 ecommerce back4app) 안내에 따라 앱을 빌드하고 배포하세요 배포가 완료되면 back4app은 다음과 같은 확인 페이지를 표시합니다 successful deployment 위 이미지에서 강조된 영역은 귀하의 애플리케이션이 실시간으로 운영되는 url입니다 이를 사용하여 웹 브라우저에서 직접 앱을 보고 테스트할 수 있습니다 배포가 완료되면 back4app 대시보드를 사용하여 빌드 로그를 모니터링하고, 컨테이너 상태를 추적하며, 필요 시 이전 버전으로 롤백할 수 있습니다 대시보드는 컨테이너 상태에 대한 실시간 업데이트를 표시하여 오류를 쉽게 발견하고 사용자에게 안정적인 환경을 유지하는 데 도움을 줍니다 컨테이너를 사용하면 back4app 백엔드와 원활하게 결합되는 유연하고 휴대 가능한 프론트엔드 솔루션을 얻을 수 있으며, 일관되고 번거롭지 않은 릴리스를 위한 길을 열어줍니다 이 튜토리얼에서 구축한 전자상거래 웹사이트를 방문할 수 있습니다 여기 https //ecommerceback4app mcphit37 b4a run/ 결론 축하합니다—이제 전투 테스트를 거친, 생산 준비가 완료된 전자상거래 스택의 모든 레이어를 조립했습니다 백엔드 설정 back4app 계정 프로비저닝부터 사용자, 제품 및 주문 모델링까지 보안 및 api 강력한 인증 구현 및 rest 및 graphql 엔드포인트를 통한 데이터 노출 비즈니스 로직 및 실시간 업데이트 클라우드 코드로 워크플로 자동화 및 라이브 쿼리를 통한 실시간 변경 스트리밍 프론트엔드 배포 javascript sdk 연결 및 back4app 컨테이너에서 next js 상점 배포 다음은 무엇인가요? back4app 문서 https //www back4app com/docs 및 parse platform 가이드 https //docs parseplatform org 를 통해 심층 참조를 하거나, 커뮤니티 포럼에 참여하여 유지 관리자로부터 고급 팁과 샘플 앱을 얻어보세요 이 기초는 시작에 불과합니다—웹훅 및 예약 작업 통합, 결제 게이트웨이 연결 또는 모바일 동반 앱 구축을 통해 더 확장해 보세요 계속해서 반복하고, 리팩토링하며, 코드베이스를 발전시키세요—그리고 귀하의 상점이 귀하의 야망과 함께 성장하는 것을 지켜보세요