배경 및 목표
카카오 쇼핑 이벤트 페이지를 통해 쿠폰 발급 기능을 구현하고, 사용자의 이메일을 기반으로 userId를 가져와 쿠폰 발급 요청을 처리할 수 있도록 한다.
소스코드 URL:
kakaoshop-FE

kakaoshop-FE
구조 및 구현
kakaoshop-FE
├── src/
│ ├── assets/images/products/
│ ├── styles/eventpage.css
│ ├── components/
│ │ ├── Popup.jsx
│ │ └── Popup.css
│ ├── pages/
│ │ └── EventPage.jsx
│ ├── .env
│ └── index.js
└── package.json
JavaScript
복사
파일명 | 내용 |
.env | API URL 설정 및 환경변수 관리 |
EventPage.jsx | 이벤트 페이지 구현 및 쿠폰 발급 요청 로직 처리 |
Popup.jsx | 팝업 컴포넌트 구현 및 이벤트 페이지 연결 |
Popup.css | 팝업 스타일링 및 레이아웃 설정 |
eventpage.css | 이벤트 페이지 레이아웃 및 스타일 적용 |
소스코드
.env
API 서버 URL 및 환경변수를 설정한다.
# env.local
REACT_APP_SHOP_API_URL = http://localhost:8080
REACT_APP_COUPON_API_URL = http://localhost:8081
Java
복사
•
EventPage.jsx
이벤트 페이지로 쿠폰 리스트를 표시하고, userId와 함께 쿠폰 발급 API를 호출한다.
import React, { useState, useEffect } from "react"; // useEffect 추가
import "../styles/eventpage.css";
import firstImage from "../assets/images/products/firstimage.webp";
import saleProduct1 from "../assets/images/products/saleproducts1.webp";
import saleProduct2 from "../assets/images/products/saleproducts2.webp";
import saleProduct4 from "../assets/images/products/saleproducts4.webp";
import saleProduct5 from "../assets/images/products/saleproducts5.webp";
import saleProduct6 from "../assets/images/products/saleproducts6.webp";
import saleProduct7 from "../assets/images/products/saleproducts7.webp";
import saleProduct8 from "../assets/images/products/saleproducts8.webp";
import saleProduct9 from "../assets/images/products/saleproducts9.webp";
import saleProduct10 from "../assets/images/products/saleproducts10.webp";
import saleProduct11 from "../assets/images/products/saleproducts11.webp";
import saleProduct12 from "../assets/images/products/saleproducts12.webp";
import couponImage from "../assets/images/products/negowang.webp";
const EventPage = () => {
const [userId, setUserId] = useState(null); //userId 상태 추가
const REACT_APP_SHOP_API_URL = process.env.REACT_APP_SHOP_API_URL; // kakaoshop-API
const REACT_APP_COUPON_API_URL = process.env.REACT_APP_COUPON_API_URL; // coupon-API
const saleImages = [
[saleProduct1, 70],
[saleProduct2, 51],
[saleProduct4, 64],
[saleProduct5, 53],
[saleProduct6, 68],
[saleProduct7, 62],
[saleProduct8, 58],
[saleProduct9, 50],
[saleProduct10, 40],
[saleProduct11, 62],
[saleProduct12, 62],
];
const coupons = [
{ id: 1, amount: 5000, minPurchase: 50000, expiry: "2024.09.20" },
{ id: 2, amount: 3000, minPurchase: 30000, expiry: "2024.09.20" },
{ id: 3, amount: 2000, minPurchase: 20000, expiry: "2024.09.20" },
{ id: 4, amount: 1000, minPurchase: 10000, expiry: "2024.09.20" },
];
// 쿠키에서 특정 값을 가져오는 함수
function getCookieValue(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
// 이메일로 userId 가져오기
async function getUserIdByEmail(email) {
try {
const response = await fetch(`${REACT_APP_SHOP_API_URL}/find-id`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
});
if (!response.ok) {
throw new Error("사용자 ID 조회 실패");
}
const data = await response.json();
return data.userId; // API가 반환하는 userId 값
} catch (error) {
console.error("Error fetching userId:", error);
return null;
}
}
useEffect(() => {
const email = getCookieValue("email");
if (email) {
getUserIdByEmail(decodeURIComponent(email)).then((id) => {
setUserId(id);
});
}
}, []);
// 쿠폰버튼 클릭 시 실행
async function handleButtonClick(couponId) {
const token = getCookieValue("token");
if (!token || !userId) {
alert("로그인 후 이용 가능합니다");
return;
}
console.log("User ID:", userId, "type: ", typeof(userId)); // userId 로그 출력, 형태 확인
try {
// 쿠폰 발급 요청
const response = await fetch(`${REACT_APP_COUPON_API_URL}/v2/issue-async`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userId, // 변환된 사용자 ID
couponId, // 클릭한 쿠폰 ID
}),
});
console.log(response); // 응답 로그 출력
if (!response.ok) {
throw new Error("네트워크 응답이 올바르지 않습니다.");
}
const data = await response.json(); // 응답 데이터를 JSON으로 파싱
console.log("Response data:", data); // 응답 데이터 로그
// 성공 여부에 따라 처리
if (data.isSuccess) {
alert(`네고왕 X 카카오쇼핑 ${coupons.find(coupon => coupon.id === couponId).amount}원 쿠폰이 발급되었습니다.`);
} else {
alert(`쿠폰 발급 실패: ${data.comment}`);
}
} catch (error) {
alert(`쿠폰 발급에 실패했습니다: ${error.message}`);
}
}
return (
<div className="layout">
<div className="imagewrapper">
<img src={firstImage} alt="Event" />
</div>
<div className="main-text">네고왕 x 카카오 상품 선착순 할인쿠폰</div>
<div className="containers-layout">
<div className="containers">
{coupons.map((coupon) => (
<div
key={coupon.id}
onClick={() => handleButtonClick(coupon.id)}
className="items"
>
<div className="items-main">
<div className="coupon-image">
<img src={couponImage} alt="네고왕" />
</div>
<div className="sale-text">
<div>{coupon.amount}원 쿠폰</div>
<div>({coupon.minPurchase}원 이상 구매시)</div>
<div>{coupon.expiry}까지</div>
</div>
</div>
<div>선착순 쿠폰이 조기 마감될 수 있으니 서둘러 주세요.</div>
</div>
))}
</div>
</div>
<div className="youtube">
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/93mnQmY5FiM?si=SViEW0AB2aYnoOhx"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy="strict-origin-when-cross-origin"
allowFullScreen
></iframe>
</div>
{saleImages.map((image, index) => (
<div key={index} className="imagewrapper">
<img src={image[0]} alt={`Sale product ${index + 1}`} />
<div className="blue"></div>
</div>
))}
</div>
);
};
export default EventPage;
Java
복사
기능 | 설명 |
useEffect | 쿠키에 저장된 이메일을 userId로 변환 |
getUserIdByEmail | 이메일을 기반으로 서버에서 userId를 가져오는 함수 |
handleButtonClick | 쿠폰 클릭 시 API를 호출하여 쿠폰 발급 요청 처리 |
쿠폰 리스트 렌더링 | 쿠폰 데이터 배열을 map() 함수를 사용해 화면에 표시 |
•
Popup.jsx
팝업 컴포넌트를 통해 이벤트 페이지로 유도한다.
import React from "react";
import "./Popup.css";
const Popup = ({ isOpen, onClose }) => {
if (!isOpen) return null;
const staticServerUri = process.env.REACT_APP_PATH || "";
const onClickEventPage = (e) => {
e.preventDefault();
window.location.href = `${staticServerUri}/event`;
};
// 오버레이 클릭 시 팝업 닫기
const handleOverlayClick = (e) => {
if (e.target === e.currentTarget) {
onClose();
}
};
return (
<div className="modal-overlay" onClick={handleOverlayClick}>
<div className="modal-container" onClick={(e) => e.stopPropagation()}>
<iframe
width="100%"
height="320px"
src="https://www.youtube.com/embed/93mnQmY5FiM?si=SViEW0AB2aYnoOhx"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy="strict-origin-when-cross-origin"
allowFullScreen></iframe>
<div className="kakao-button-wrapper">
<div className="kakao-button" onClick={onClickEventPage}>
네고왕 X 카카오 이벤트 바로가기
</div>
</div>
<div className="today-button" onClick={onClose}>
오늘 하루 보지 않기
</div>
<div className="modal-close-button" onClick={onClose}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512">
<path d="M376.6 84.5c11.3-13.6 9.5-33.8-4.1-45.1s-33.8-9.5-45.1 4.1L192 206 56.6 43.5C45.3 29.9 25.1 28.1 11.5 39.4S-3.9 70.9 7.4 84.5L150.3 256 7.4 427.5c-11.3 13.6-9.5 33.8 4.1 45.1s33.8 9.5 45.1-4.1L192 306 327.4 468.5c11.3 13.6 31.5 15.4 45.1 4.1s15.4-31.5 4.1-45.1L233.7 256 376.6 84.5z" />
</svg>
</div>
</div>
</div>
);
};
export default Popup;
Java
복사
Popup.css
eventpage.css
실행
npm install
npm run build
serve -s build
Java
복사
결론
쿠폰 발급 이벤트 페이지와 팝업을 통해 카카오 쇼핑과 프론트엔드가 연동되었다.
•
이벤트 페이지: 사용자 인증 후 쿠폰 API 호출
•
팝업 페이지: 이벤트 페이지로 이동을 유도
메인페이지 내 팝업페이지
이벤트페이지
Q&A
Q.이메일 기반으로 userId를 가져오는 이유는?
Related Posts
Search