ETC/CloneCoding
[React] 배스킨라빈스 클론코딩
chanCo
2022. 5. 16. 16:44
#1. 배스킨라빈스 클론코딩
1. 전체 레이아웃 구조 확인
2. 레이아웃 구조
3. 구현결과
4. 구현 및 상세 내용
- 데이터는 json-server를 활용해서 axios를 통해 가져온다. (localhost는 3001포트 사용)
- css는 styled-components로 처리한다.
1) src 디렉터리
- src / index.js
/**
* @filename : index.js
* @author : chanCo
* @description : index.html의 root에 랜더링할 컴포넌트 정의
*/
// 패키지 참조
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { createGlobalStyle } from "styled-components";
// 컴포넌트 참조
import App from './App';
import Meta from './Meta';
// 전역 스타일 정의
const GlobalStyle = createGlobalStyle`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Nanum Gothic', sans-serif;
list-style: none;
text-decoration: none;
outline: none;
}
`;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
<Meta />
<GlobalStyle />
<BrowserRouter>
<App />
</BrowserRouter>
</>
);
- src / App.jsx
/**
* @filename : App.jsx
* @author : chanCo
* @description : 모든 컴포넌트 참조 및 배치
*/
// 패키지 참조
import React, { memo } from 'react';
import { Routes, Route } from 'react-router-dom';
// 컴포넌트 참조
import Header from './components/Header';
import Main from './page/Main';
import Login from './page/Login';
import Footer from './components/Footer';
// TODO: 화면을 구성할 컴포넌트들을 배치한다.
function App() {
return (
<>
<Header />
<Routes>
<Route path='/' exact={true} element={<Main />} />
<Route path='/login' element={<Login />} />
</Routes>
<Footer />
</>
);
}
export default memo(App);
- src / Meta.jsx
/**
* @filename : Meta.jsx
* @author : chanCo
* @description : SEO처리 및 기본 참조 리소스 명시
*/
// 패키지 참조
import React from 'react';
import { Helmet, HelmetProvider } from 'react-helmet-async';
const Meta = (props) => {
return (
<HelmetProvider>
<Helmet>
<title>{props.title}</title>
<meta charset="utf-8" />
<meta name="description" content={props.description} />
<meta name="keywords" content={props.keywords} />
<meta name="author" content={props.author} />
<meta property="og:type" content="website" />
<meta property="og:title" content={props.title} />
<meta property="og:description" content={props.description} />
<meta property="og:url" content={props.url} />
<meta property="og:image" content={props.image} />
{/* favicon 설정 */}
<link rel="shortcut icon" href={props.image} type="image/png" />
<link ref="icon" href={props.image} type="image/png" />
{/* 웹폰트 적용 */}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Nanum+Gothic:wght@400;700;800&display=swap" rel="stylesheet" />
</Helmet>
</HelmetProvider>
);
};
// props에 대한 기본값 설정
Meta.defaultProps = {
title: '배스킨라빈스 :: 클론코딩',
description: 'React.js로 구현한 배스킨라빈스 클론코딩 연습 페이지 입니다.',
keywords: 'React, layout, demo, cloncoding',
author: 'chanCo',
url: 'http://www.baskinrobbins.co.kr',
image: 'https://www.baskinrobbins.co.kr/assets/images/common/favicon.ico',
};
export default Meta;
2) components 디렉터리
- src / components / Header.jsx
/**
* @filename : Header.jsx
* @author : chanCo
* @description : 헤더 영역 정의
*/
import React, { useCallback, useState, useEffect, memo } from "react";
import axios from "axios";
import styled from "styled-components";
import { NavLink } from 'react-router-dom';
import Nav from "./Nav";
// TODO: Header 스타일 정의
const HeaderContainer = styled.div`
& {
position: relative;
width: 100%;
border-top: 3px solid #ff7c98;
border-bottom: 1px solid #e1e1e1;
background: url(./img/bg_header.gif) center center;
.header {
position: relative;
display: flex;
width: 1200px;
margin: auto;
padding: 20px 0;
justify-content: space-between;
.headerSocial {
position: relative;
display: flex;
justify-content: center;
align-items: center;
ul {
display: flex;
li {
padding: 0 5px;
}
}
}
.help {
position: relative;
display: flex;
ul {
position: relative;
display: flex;
justify-content: center;
align-items: center;
li {
padding-left: 20px;
a {
font-size: 10px;
color: #222;
}
}
.search {
button {
background-color: transparent;
border: none;
cursor: pointer;
}
}
}
}
}
}
`;
// TODO: search 버튼 클릭시 나오는 이벤트 영역
const SearchArea = styled.div`
& {
position: absolute;
top: 0;
margin-top: 186px;
width: 100%;
max-height: 0;
z-index: 99;
transition: 0.4s ease-out;
overflow: hidden;
background-color: #fff;
.searchMenu {
position: relative;
width: 1200px;
margin: auto;
padding: 25px 0;
.searchIfon {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
h4 {
padding-top: 8px;
width: 89px;
font-weight: normal;
font-size: 13px;
}
input {
padding: 8px 0 8px 10px;
height: 32px;
background-color: #efefef;
border: none;
}
.productNameWrap {
display: flex;
width: 570px;
flex-direction: row;
select {
width: 128px;
height: 32px;
border: 1px solid #e1e1e1;
border-radius: 5px;
padding-left: 10px;
margin-right: 14px;
color: #555;
}
input {
width: 260px;
}
}
.hashTagWrap {
display: flex;
width: 630px;
flex-wrap: wrap;
justify-content: space-between;
input {
width: 540px;
color: #ff7c98;
}
.hashTag {
width: 526px;
margin: 10px 0 8px auto;
p {
color: #9c9c9c;
font-size: 13px;
}
a {
color: #ff7c98;
padding-right: 5px;
font-size: 12px;
}
}
}
.allergyWrap {
display: flex;
width: 100%;
.check {
display: flex;
width: 280px;
flex-wrap: wrap;
span {
display: flex;
width: 70px;
height: 30px;
align-items: center;
input {
margin-right: 5px;
cursor: pointer;
}
label {
font-size: 13px;
cursor: pointer;
}
}
}
}
}
.searchBtn {
padding-top: 25px;
text-align: center;
button {
border: none;
padding: 10px 60px;
border-radius: 20px;
background-color: #ff7c98;
color: #fff;
font-size: 15px;
cursor: pointer;
}
}
}
}
`;
const Header = () => {
// TODO: 화면에 표시할 상태값(headerSocial) -> ajax 연동 결과로 받아올 json
const [headerSocial, setHeaderSocial] = useState([]);
// TODO: 화면에 표시할 상태값(checkList) -> ajax 연동 결과로 받아올 json
const [checkList, setCheckList] = useState([]);
// TODO: 검색 버튼 클릭시 이미지 변경
const [button, setButton] = useState(false);
// TODO: 높이를 넣을 값
const [searchHeight, setSearchHeight] = useState({
maxHeight: 0,
opacity: 0,
});
// 버튼 토글 구현
const onButtonChange = useCallback(() => {
setButton(!button);
setSearchHeight(button === false ? { maxHeight: "100vh", opacity: 1 } : { maxHeight: 0, opacity: 0 });
}, [button]);
// headerSocial 데이터 불러오기
useEffect(() => {
(async () => {
try {
const response = await axios.get("http://localhost:3001/headerSocial");
setHeaderSocial((headerSocial) => response.data);
} catch (e) {
console.error(e);
alert("데이터를 불러오지 못했습니다.");
}
})();
}, []);
// checkList 데이터 불러오기
useEffect(() => {
(async () => {
try {
const response = await axios.get("http://localhost:3001/checkList");
setCheckList((checkList) => response.data);
} catch (e) {
console.error(e);
alert("데이터를 불러오지 못했습니다.");
}
})();
}, []);
return (
<>
<HeaderContainer>
<div className="header">
<nav className="headerSocial">
<ul>
{/* TODO: JSON으로 받아온 데이터로 구현 */}
{headerSocial.map((v, i) => {
return (
<li key={v.id}>
<a href={v.url} target="_blank" rel="noreferrer">
<img src={v.img} alt={v.alt} />
</a>
</li>
);
})}
</ul>
</nav>
<div className="logo">
<NavLink to='/'>
<img src="./img/logo_baskinrobbins.png" alt="baskin robins" />
</NavLink>
</div>
<nav className="help">
<ul>
<li>
<a href="#!">고객센터</a>
</li>
<li>
<a href="#!">CONTACT US</a>
</li>
<li className="search">
{/* TODO: 버튼 이미지 변경 */}
<button type="button" onClick={onButtonChange}>
{button === false ? <img src="./img/icon_search.png" alt="search icon" /> : <img src="./img/btn_search_close.gif" alt="close icon" />}
</button>
</li>
</ul>
</nav>
</div>
</HeaderContainer>
{/* Nav 영역, 버튼 클릭시 변하는 값을 props로 전달 */}
<Nav buttonState={button} />
{/* 검색 버튼 활성화 영역 */}
<SearchArea style={searchHeight}>
<div className="searchMenu">
<div className="searchIfon">
<div className="productNameWrap">
<h4>제품명</h4>
<select>
<option value="whole">전체</option>
<option value="icecream">아이스크림</option>
<option value="cake">아이스크림케이크</option>
<option value="beverage">음료</option>
<option value="coffee">커피</option>
<option value="dessert">디저트</option>
<option value="blockPack">block pack</option>
<option value="readyPack">ready pack</option>
</select>
<input type="text" />
</div>
<div className="hashTagWrap">
<h4>해시태그</h4>
<input type="text" />
<div className="hashTag">
<p>· 자주 찾는 해시태그</p>
<a href="#!">#피카피카피카츄</a>
<a href="#!">#피카츄초코바나나블라스트</a>
<a href="#!">#쿨쿨잠만보밀키소다블라스트</a>
<a href="#!">#고라파덕아이스크림콘</a>
<a href="#!">#푸린아이스크림콘</a>
<a href="#!">#포켓몬스터</a>
</div>
</div>
<div className="allergyWrap">
<h4>알레르기 성분</h4>
<div className="check">
{/* 데이터를 받아 map을 활용해서 작성 */}
{checkList.map((v, i) => {
return (
<span key={v.id}>
<input type="checkbox" id={v.name} />
<label htmlFor={v.name}>{v.label}</label>
</span>
);
})}
</div>
</div>
</div>
<div className="searchBtn">
<button type="submit">검색</button>
</div>
</div>
</SearchArea>
</>
);
};
export default memo(Header);
+ data.json
"headerSocial": [
{
"id": 1,
"url": "https://www.facebook.com/baskinrobbins.kr",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/icon_facebook.png",
"alt": "facebook"
},
{
"id": 2,
"url": "https://twitter.com/BaskinrobbinsKR",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/icon_twitter.png",
"alt": "twitter"
},
{
"id": 3,
"url": "http://blog.naver.com/brgirl31",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/icon_blog.png",
"alt": "blog"
},
{
"id": 4,
"url": "https://www.instagram.com/baskinrobbinskorea",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/icon_instgram.png",
"alt": "instagram"
},
{
"id": 5,
"url": "https://www.youtube.com/user/baskinrobbinskorea",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/icon_youtube.png",
"alt": "youtube"
}
],
"checkList": [
{ "id": 1, "name": "egg", "label": "계란" },
{ "id": 2, "name": "bean", "label": "대두" },
{ "id": 3, "name": "pork", "label": "돼지고기" },
{ "id": 4, "name": "peanut", "label": "땅콩" },
{ "id": 5, "name": "wheat", "label": "밀" },
{ "id": 6, "name": "peach", "label": "복숭아" },
{ "id": 7, "name": "milk", "label": "우유" },
{ "id": 8, "name": "none", "label": "없음" }
],
- src / components / Nav.jsx
/**
* @filename : Nav.jsx
* @author : chanCo
* @description : 헤더 영역의 Nav 영역 컴포넌트 작성
*/
import React, { useCallback, useState, memo } from "react";
import styled from "styled-components";
import { NavLink } from 'react-router-dom';
const NavContainer = styled.div`
& {
position: relative;
width: 100%;
background-color: #fff;
.navWrap {
position: relative;
width: 1200px;
margin: auto;
display: flex;
justify-content: space-between;
.loginWrap {
display: flex;
width: 110px;
justify-content: space-between;
a {
font-size: 11px;
font-weight: 800;
color: #747474;
padding: 15px 0;
span {
color: #ff99ae;
}
}
}
.navLinkWrap {
display: flex;
width: 980px;
justify-content: space-between;
align-items: center;
&:hover {
cursor: pointer;
}
.text {
color: #4f3b35;
font-size: 13px;
font-weight: 800;
padding: 15px 0;
span {
font-size: 12px;
}
}
}
}
}
`;
const SubMenu = styled.div`
position: absolute;
width: 100%;
overflow: hidden;
z-index: 9;
transition: 1s ease-out;
border-top: 0.5px solid #000;
border-bottom: 0.5px solid #000;
background-color: #fff;
.subMenuWrap {
background-color: #fff;
position: relative;
width: 1240px;
margin: auto;
display: flex;
.happyPointImg {
display: flex;
align-items: center;
margin-left: 15px;
}
.icecreamImg {
margin-right: 55px;
opacity: 0;
transition: 1.5s ease-in;
}
.menuList {
display: flex;
width: 800px;
justify-content: space-between;
padding-top: 40px;
.menuList1 {
margin-right: 15px;
}
.menuList2 {
margin-right: 35px;
}
li {
padding-bottom: 20px;
text-align: center;
a {
font-size: 13px;
color: #afa6a0;
&:hover {
color: #ff7c98;
}
}
}
}
}
`;
const Nav = ({ buttonState }) => {
// TODO: 서브메뉴 상태 정의
const [subMenu, setSubMenu] = useState({ maxHeight: 0 });
const [icecreamImg, setIcecreamImg] = useState({ opacity: 0 });
// 마우스 올리면 나오는 서브메뉴 정의
const onMouseOver = useCallback(() => {
// 검색 버튼의 상태가 false 일때 서브메뉴 높이 값 지정, true면 0
setSubMenu(buttonState === false ? { maxHeight: "100vh" } : { maxHeight: 0 });
setIcecreamImg({ opacity: 1 });
}, [buttonState]);
// 마우스가 빠져나가면 서브메뉴 사라짐
const onMouseOut = useCallback(() => {
setSubMenu({ maxHeight: 0 });
setIcecreamImg({ opacity: 0 });
}, []);
const menuList1 = ["아이스크림", "아이스크림케이크", "음료", "커피", " 디저트"];
const menuList2 = ["아이스크림", "음료", "커피"];
const menuList3 = ["진행중이벤트", "당첨자발표"];
const menuList4 = ["매장찾기", "고객센터", "단체주문"];
const menuList5 = ["공지사항", "보도자료", "채용정보", "점포개설문의", " CONTACT US"];
return (
<>
<NavContainer>
<div className="navWrap">
<div className="loginWrap">
<NavLink to='/login'>
<span>LOGIN</span>
</NavLink>
<NavLink to='/join'>JOIN</NavLink>
</div>
<div className="navLinkWrap" onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
<NavLink to='/flavor' className="text noLeft">
FLAVOR OF THE MONTH
</NavLink>
<NavLink to='/menu' className="text">
MENU
</NavLink>
<NavLink to='/allergy' className="text">
<span>영양 성분 및 알레르기</span>
</NavLink>
<NavLink to='/evnt' className="text">
EVENT
</NavLink>
<NavLink to='/store' className="text">
STORE
</NavLink>
<NavLink to='/about' className="text noRight">
ABOUT
</NavLink>
</div>
</div>
{/* TODO: 서브메뉴 구현 */}
<SubMenu style={subMenu} onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
<div className="subMenuWrap">
<div className="happyPointImg">
<img src="./img/img_happypoint_app.jpg" alt="QR코드 이미지" />
</div>
<div className="icecreamImg" style={icecreamImg}>
<a href="#!">
<img src="./img/img_monthly_fom_220429.png" alt="" />
</a>
</div>
<div className="menuList">
<ul className="menuList1">
{menuList1.map((v, i) => <li key={i}> <NavLink to='/'>{v}</NavLink> </li>)}
</ul>
<ul className="menuList2">
{menuList2.map((v, i) => <li key={i}> <NavLink to='/'>{v}</NavLink> </li>)}
</ul>
<ul className="menuList3">
{menuList3.map((v, i) => <li key={i}> <NavLink to='/'>{v}</NavLink> </li>)}
</ul>
<ul className="menuList4">
{menuList4.map((v, i) => <li key={i}> <NavLink to='/'>{v}</NavLink> </li>)}
</ul>
<ul className="menuList5">
{menuList5.map((v, i) => <li key={i}> <NavLink to='/'>{v}</NavLink> </li>)}
</ul>
</div>
</div>
</SubMenu>
</NavContainer>
</>
);
};
export default memo(Nav);
- src / components / Footer.jsx
/**
* @filename : Footer.jsx
* @author : chanCo
* @description : footer 영역 정의
*/
import React, { memo, useState, useEffect, useCallback } from 'react';
import styled from 'styled-components';
import axios from 'axios';
const FooterContainer = styled.div `
& {
position: relative;
width: 100%;
padding-bottom: 60px;
border-top: 2px solid #fed69a;
.policy {
width: 1200px;
margin: auto;
ul {
position: relative;
padding: 0 80px;
display: flex;
justify-content: space-between;
li {
display: flex;
align-items: center;
height: 77.5px;
a {
font-size: 14px;
color: #726454;
span {
color: #ff7c98;
font-weight: 600;
}
}
}
}
}
.brFamily {
width: 100%;
height: 75px;
background-color: #f9f8f7;
.brFamilyWrap {
position: relative;
width: 1200px;
margin: auto;
padding: 22px 0 0 222px;
display: flex;
align-items: center;
.brFamilyImg {
margin-right: 50px;
&:nth-child(4) {
margin-right: 10px;
}
&:nth-child(5) {
width: 31px;
margin-right: 10px;
img {
width: 100%;
}
}
&:nth-child(6) {
margin-right: 110px;
}
}
button {
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 11px;
border: 1px solid #afafaf;
border-radius: 5px;
background-color: #fff;
width: 150px;
height: 32px;
color: #afafaf;
font-size: 12px;
box-shadow: 2px 2px 10px rgba(0,0,0,.1);
cursor: pointer;
}
.faimlySubMenu {
position: absolute;
border: 1px solid #afafaf;
width: 150px;
background-color: #fff;
border-radius: 10px 10px 0 0;
padding: 10px 0 10px;
z-index: 1;
right: 175px;
bottom: 30px;
li {
margin-right: 0;
padding-bottom: 5px;
&:hover {
background-color: #eeeeee;
}
a {
font-size: 13px;
color: #726454;
padding-left: 15px;
}
}
}
}
}
.brInfo {
position: relative;
width: 1200px;
margin: auto;
text-align: center;
padding-top: 48px;
h1 { padding-bottom: 35px; }
.info1 {
display: flex;
justify-content: center;
padding-bottom: 10px;
}
.info2 {
display: flex;
justify-content: center;
}
li {
margin: 0 10px;
font-size: 11px;
color: #6d6661;
letter-spacing: -0.5px;
}
p {
padding-top: 20px;
color: #bebbb9;
font-size: 11px;
}
}
}
`;
const Footer = () => {
// TODO: family 영역의 이미지 상태값 정의
const [brFamily, setBrFamily] = useState([]);
useEffect(() => {
(async () => {
try {
const response = await axios.get('http://localhost:3001/brFamilyImg');
setBrFamily(brFamily => response.data);
} catch(e) {
console.error(e)
alert('데이터 요청 실패')
}
})();
}, []);
// TODO: FAMILY SITE 버튼의 상태값 정의
const [familyBtn, setFamilyBtn] = useState(false);
// 버튼 토글 구현
const onToggle = useCallback(() => {
setFamilyBtn(!familyBtn)
}, [familyBtn]);
return (
<FooterContainer>
<div className='footerWrap'>
<div className='policy'>
<ul>
<li><a href="#!">점포개설문의</a></li>
<li><a href="#!">채용문의</a></li>
<li><a href="#!">윤리신고센터</a></li>
<li><a href="#!">이용약관</a></li>
<li><a href="#!"><span>개인정보처리방침</span></a></li>
<li><a href="#!">영상정보처리기기운영관리방침</a></li>
<li><a href="#!">거래희망회사 사전등록</a></li>
</ul>
</div>
<div className='brFamily'>
<ul className='brFamilyWrap'>
{brFamily.map((v,i) => {
return (
<li key={v.id} className='brFamilyImg'>
<a href={v.url} target='_blank' rel='noreferrer'>
<img src={v.img} alt={v.alt} />
</a>
</li>
);
})}
<button type='button' onClick={onToggle}>FAMILY SITE
{familyBtn === false ?
<img src="./img/family_size_off.png" alt="size off" /> :
<img src="./img/family_size_on.png" alt="size on" />}
</button>
{familyBtn &&
<ul className='faimlySubMenu'>
<li><a href="#!">배스킨 스쿨</a></li>
<li><a href="#!">SPC그룹사이트</a></li>
<li><a href="#!">SPC MAGAZINE</a></li>
<li><a href="#!">BR코리아</a></li>
<li><a href="#!">해피포인트카드</a></li>
<li><a href="#!">파스쿠찌</a></li>
<li><a href="#!">삼립</a></li>
<li><a href="#!">파리바게트</a></li>
<li><a href="#!">던킨도너츠</a></li>
</ul>}
</ul>
</div>
<div className='brInfo'>
<h1>
<img src="./img/footer_logo.png" alt="footer logo" />
</h1>
<ul className='info1'>
<li>사업자 등록번호 : 303-81-09535</li>
<li>비알코리아(주) 대표이사 도세호</li>
<li>서울특별시 서초구 남부순환로 2620(양재동 11-149번지)</li>
</ul>
<ul className='info2'>
<li>TEL : 080-555-3131</li>
<li>개인정보관리책임자 : 김경우</li>
</ul>
<p>Copyright ⓒ 2016 BRKOREA Company. All Rights Reserved.</p>
</div>
</div>
</FooterContainer>
);
};
export default memo(Footer);
+ data.json
"brFamilyImg" : [
{
"id": 1,
"url": "http://www.happypointcard.com",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/btn_happypoint.png",
"alt": "HAPPY POINT"
},
{
"id": 2,
"url": "http://m.celectory.com",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/btn_happymarket.png",
"alt": "HAPPY MARKET"
},
{
"id": 3,
"url": "http://www.spc.co.kr/contributionAll",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/btn_spc_story.png",
"alt": "SPC그룹 사회공헌활동 SPC 행복한 이야기"
},
{
"id": 4,
"url": "https://sealinfo.verisign.com/splash?form_file=fdf/splash.fdf&dn=WWW.BASKINROBBINS.CO.KR&lang=ko",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/btn_norton.gif",
"alt": "Norton SECURED"
},
{
"id": 5,
"url": "http://www.kca.go.kr/ccm/",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/btn_ccm_2.png",
"alt": "소비자중심경영 인증제도"
},
{
"id": 6,
"url": "http://knqa.ksa.or.kr/knqa/2276/subview.do",
"img": "http://www.baskinrobbins.co.kr/assets/images/common/btn_ksa.png",
"alt": "국가품질상 - KSA 한국표준협회"
}
],
3) pages 디렉터리
- src / pages / Main.jsx
/**
* @filename : Main.jsx
* @author : chanCo
* @description : 메인 페이지에 쓰이는 모든 컴포넌트 참조
*/
import React, { memo } from 'react';
import BrMainSlide from '../mainComponents/BrMainSlide';
import BrEvent from '../mainComponents/BrEvent';
import BrMenu from '../mainComponents/BrMenu';
import BrStore from '../mainComponents/BrStore';
import BrSns from '../mainComponents/BrSns';
const Main = () => {
return (
<>
<BrMainSlide />
<BrEvent />
<BrMenu />
<BrStore />
<BrSns />
</>
);
};
export default memo(Main);
- src / pages / Login.jsx
/**
* @filename : Login.jsx
* @author : chanCo
* @description : 로그인 페이지 화면 구성
*/
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import axios from 'axios';
import Meta from '../Meta';
const LoginContainer = styled.div`
& {
position: relative;
width: 1200px;
margin: auto;
padding-bottom: 160px;
.loginPageTitle {
padding: 133px 0 35px;
text-align: center;
p {
font-size: 20px;
font-weight: 500;
padding-top: 10px;
color: #483834;
}
}
.loginBox {
padding: 46px 0 47px;
width: 916px;
margin: auto;
border-top: 2px solid #ff99bf;
border-bottom: 1px solid #ccc;
display: flex;
.subTitle {
font-size: 17px;
font-weight: 500;
color: #2f231c;
padding-bottom: 10px;
}
.loginBoxLeft {
position: relative;
width: 50%;
.loginTitle {
padding-bottom: 20px;
p {
font-size: 18px;
font-weight: 400;
color: #ff7c98;
}
}
.inputArea {
position: relative;
.input {
border: none;
background-color: #efefef;
padding: 17px 15px;
width: 284px;
color: #6e6b68;
margin-bottom: 9px;
&.inputId::-webkit-input-placeholder { background: url(./img/key.png) left center no-repeat; }
&.inputPassword::-webkit-input-placeholder { background: url(./img/unlock.png) left center no-repeat; }
&.inputId::-webkit-input-placeholder,
&.inputPassword::-webkit-input-placeholder {
background-size: contain;
margin-bottom: 9px;
text-indent: 30px;
color: #000;
opacity: .4;
}
}
button {
position: absolute;
right: 5%;
top: -5%;
width: 118px;
height: 118px;
border-radius: 50%;
border: none;
background-color: #f56f98;
color: #fff;
font-size: 17px;
font-weight: 600;
cursor: pointer;
}
}
ul {
padding-top: 17px;
display: flex;
width: 284px;
justify-content: space-between;
li {
a {
font-size: 13px;
color: #999;
border-right: 1px solid #ccc;
padding-right: 10px;
}
.last {
border: none;
padding-right: 0;
}
}
}
}
.loginBoxRight {
position: relative;
width: 50%;
border-left: 1px solid #ccc;
padding-left: 35px;
.service {
padding-bottom: 50px;
p {
font-size: 13px;
color: #999;
letter-spacing: -1px;
line-height: 1.5;
}
}
.customer {
.content {
display: flex;
font-size: 13px;
p {
color: #999;
width: 75px;
padding: 0 0 10px 2px;
}
}
}
}
}
.benefit {
padding-top: 30px;
width: 916px;
margin: auto;
h3 {
text-align: center;
padding-bottom: 32px;
font-size: 16px;
font-weight: 500;
color: #483834;
line-height: 1.5;
}
ul {
display: flex;
justify-content: space-between;
li {
display: flex;
align-items: center;
width: 265px;
&:nth-child(1) { margin-right: 40px; }
img { margin-right: 20px; }
h4 {
color: #2f231c;
font-size: 17px;
font-weight: 500;
padding-bottom: 7px;
}
p {
color: #948780;
font-size: 13px;
line-height: 1.5;
}
}
}
}
}
`;
const Login = () => {
// TODO: 로그인페이지 데이터 정보 상태 정의
const [benefit, setLoginPage] = useState([]);
// 데이터 가져오기
useEffect(() => {
(async () => {
try {
const response = await axios.get('http://localhost:3001/benefit');
setLoginPage(benefit => response.data);
} catch(e) {
console.error(e);
}
})();
}, []);
return (
<LoginContainer>
<Meta title='배스킨라빈스 :: 로그인' description='로그인 페이지 클론코딩' />
<div className='loginPageTitle'>
<img src="./img/h_login.png" alt="login" />
<p>배스킨 라빈스 홈페이지에 오신 것을 환영합니다.</p>
</div>
<div className='loginBox'>
<div className='loginBoxLeft'>
<div className='loginTitle'>
<h4 className='subTitle'>배스킨라빈스 로그인</h4>
<p>해피포인트 아이디로 간편하게 로그인하세요.</p>
</div>
<form className='inputArea'>
<input className='inputId input' type="text" placeholder='아이디를 입력하세요' required />
<button type='submit'>로그인</button>
<input className='inputPassword input' type="password" placeholder='비밀번호를 입력하세요' required />
</form>
<ul>
<li><a href="#!">아이디 찾기</a></li>
<li><a href="#!">비밀번호 재발급</a></li>
<li><a href="#!" className='last'>해피포인트 가입</a></li>
</ul>
</div>
<div className='loginBoxRight'>
<div className='service'>
<h4 className='subTitle'>SPC 통합회원 서비스</h4>
<p>
하나의 ID/Password로 SPC가 운영하는 사이트(배스킨라빈스, 던킨도너츠,<br/> 해피포인트카드, 파리바게뜨, 파리크라상, 파스쿠찌, SPC그룹,우리밀愛)를 한번에!!<br/> 간단한 동의절차만 거치면 하나의 ID/Password로 제휴사이트를<br/> 로그인 하실 수 있습니다.
</p>
</div>
<div className='customer'>
<h4 className='subTitle'>고객센터</h4>
<div className='content'>
<p>운영시간</p>
<span>월~금 09:00~17:30 토/일요일 휴무</span>
</div>
<div className='content'>
<p>Tel.</p>
<span>080-555-3131(수신자부담)</span>
</div>
</div>
</div>
</div>
<div className='benefit'>
<h3>해피포인트 회원이 아니시라면 지금 해피포인트에 가입하시고<br /> 다양한 혜택을 경험하세요.</h3>
<ul>
{benefit.map((v,i) => {
return (
<li key={v.id}>
<img src={v.img} alt={v.alt} />
<div>
<h4>{v.title}</h4>
<p>{v.desc}</p>
</div>
</li>
);
})}
</ul>
</div>
</LoginContainer>
);
};
export default Login;
+ data.json
"benefit": [
{
"id": 1,
"img": "./img/ico_benefit_1.png",
"alt": "benefit image1",
"title": "혜택 하나",
"desc": "베스킨라빈스 온라인이벤트 행사에 참여할 수 있습니다."
},
{
"id": 2,
"img": "./img/ico_benefit_2.png",
"alt": "benefit image2",
"title": "혜택 둘",
"desc": "월별 신제품을 보다 먼저 만날 수 있습니다."
},
{
"id": 3,
"img": "./img/ico_benefit_3.png",
"alt": "benefit image3",
"title": "혜택 셋",
"desc": "배스킨라빈스 이벤트 정보 메일링을 받아 볼 수 있습니다."
}
]
4) mainComponents 디렉터리
- src / mainComponents / BrMainSlide.jsx
/**
* @filename : BrMainSlide.jsx
* @author : chanCo
* @description : 메인 페이지의 이미지 슬라이드 구현
*/
import React, { useState, useEffect, memo } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { Navigation, Pagination, Autoplay } from 'swiper';
import styled from 'styled-components';
import axios from "axios";
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
SwiperCore.use([Navigation, Pagination, Autoplay]);
const PikachuBanner = styled.div`
width: 100%;
height: 150px;
background: url(./img/1714824579.jpg) center center;
background-size: cover;
border-bottom: 3px solid #fff;
`;
const SwiperContainer = styled.div`
position: relative;
width: 100%;
.swiperImg {
position: relative;
width: 100%;
height: 697px;
}
.swiper-button-prev,
.swiper-button-next {
width: 110px;
height: 110px;
border-radius: 50%;
&::after {content: "";}
&:hover { background-color: #bdb0b079; }
}
.swiper-button-prev {background: url(./img/btn_banner_prev.png) center center;}
.swiper-button-next {background: url(./img/btn_banner_next.png) center center;}
.swiper-pagination {
display: flex;
justify-content: center;
align-items: center;
.swiper-pagination-bullet {
margin: 15px;
width: 10px;
height: 10px;
background-color: #fff;
opacity: 1;
&:hover { background-color: #222; }
}
.swiper-pagination-bullet-active {
width: 12px;
height: 12px;
background-color: #222;
}
}
`;
const BrMainSlide = () => {
// TODO: 이미지 슬라이드 상태값
const [imgSlide, setImgSlide] = useState([]);
// TODO: 이미지 슬라이드 값을 가져온다.
useEffect(() => {
(async () => {
try {
const response = await axios.get('http://localhost:3001/mainImgSlide');
setImgSlide(imgSlide => response.data);
} catch(e) {
console.error(e);
alert('ajax 연동 실패');
}
})();
}, []);
return (
<>
<PikachuBanner />
<SwiperContainer>
<Swiper className='swiper'
slidesPerView={1}
navigation={true}
pagination={{ clickable: true }}
autoplay={{ delay: 3000, disableOnInteraction: false }}
loop={true}
>
{imgSlide.map((v,i) => {
return (
<SwiperSlide key={v.id}>
<a href={v.url} target='_blank' rel='noreferrer'>
<img className='swiperImg' src={v.img} alt={v.alt} />
</a>
</SwiperSlide>
);
})}
</Swiper>
</SwiperContainer>
</>
);
};
export default memo(BrMainSlide);
+ data.json
"mainImgSlide": [
{
"id": 1,
"url": "http://www.baskinrobbins.co.kr/menu/fom.php#area4",
"img": "http://www.baskinrobbins.co.kr/upload/main/1714824551.jpg",
"alt": "이미지 슬라이드1"
},
{
"id": 2,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10704",
"img": "http://www.baskinrobbins.co.kr/upload/main/1649133684.png",
"alt": "이미지 슬라이드2"
},
{
"id": 3,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10684",
"img": "http://www.baskinrobbins.co.kr/upload/main/1714816944.png",
"alt": "이미지 슬라이드3"
},
{
"id": 4,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10664",
"img": "http://www.baskinrobbins.co.kr/upload/main/1714808856.png",
"alt": "이미지 슬라이드4"
},
{
"id": 5,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10344",
"img": "http://www.baskinrobbins.co.kr/upload/main/1667440402.png",
"alt": "이미지 슬라이드5"
},
{
"id": 6,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10104",
"img": "http://www.baskinrobbins.co.kr/upload/main/1667383155.png",
"alt": "이미지 슬라이드6"
},
{
"id": 7,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=8624",
"img": "http://www.baskinrobbins.co.kr/upload/main/1643869369.png",
"alt": "이미지 슬라이드7"
},
{
"id": 8,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10744",
"img": "http://www.baskinrobbins.co.kr/upload/main/1664934495.png",
"alt": "이미지 슬라이드8"
},
{
"id": 9,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10724",
"img": "http://www.baskinrobbins.co.kr/upload/main/1649140992.png",
"alt": "이미지 슬라이드9"
},
{
"id": 10,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10564",
"img": "http://www.baskinrobbins.co.kr/upload/main/1670146295.png",
"alt": "이미지 슬라이드10"
},
{
"id": 11,
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=&seq=10544",
"img": "http://www.baskinrobbins.co.kr/upload/main/1667539209.png",
"alt": "이미지 슬라이드11"
}
],
- src / mainComponents / BrEvent.jsx
/**
* @filename : BrEvent.jsx
* @author : chanCo
* @description : BrEvent 영역의 슬라이드 구현
*/
import React, { memo, useEffect, useState } from 'react';
import styled from 'styled-components';
import axios from "axios";
import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { Pagination } from 'swiper';
import 'swiper/css';
import 'swiper/css/pagination';
SwiperCore.use(Pagination);
const BrEventContainer = styled.div`
& {
position: relative;
width: 1200px;
margin: auto;
margin-bottom: 50px;
.eventTitle {
padding: 80px 0 50px;
text-align: center;
}
.swiper-slide {
height: 520px;
.eventLink {
color: #222;
font-size: 15.5px;
.typeImg { margin: 22px 0 8px; }
.eventSubTitle { margin-bottom: 11px; }
.period {
color: #afa6a0;
font-size: 13px;
}
}
}
.swiper-pagination {
display: flex;
justify-content: center;
align-items: center;
.swiper-pagination-bullet {
margin: 15px;
width: 7px;
height: 7px;
&:hover {
background-color: #222;
opacity: 1;
}
}
.swiper-pagination-bullet-active {
width: 10px;
height: 10px;
background-color: #222;
}
}
}
`;
const BrEvent = () => {
// TODO: event 상태값 정의
const [event, setEvent] = useState([]);
useEffect(() => {
(async () => {
try {
const response = await axios.get("http://localhost:3001/brEvent");
setEvent(event => response.data);
} catch(e) {
console.error(e);
alert("데이터 연동 실패");
}
})();
}, []);
return (
<BrEventContainer>
<div className='eventTitle'>
<img src="./img/h_event.png" alt="BR EVENT" />
</div>
<Swiper
slidesPerView={4}
spaceBetween={15}
pagination={{ clickable: true }}
slidesPerGroup={4}
>
{event.map((v,i) => {
return (
<SwiperSlide key={v.id}>
<a href={v.url} target='_blank' rel='noreferrer' className='eventLink'>
<img className='swiperImg' src={v.img} alt={v.alt} />
<img className='typeImg' src={v.eventType} alt={v.alt} />
<p className='eventSubTitle'>{v.title}</p>
<span className='period'>{v.period}</span>
</a>
</SwiperSlide>
);
})}
</Swiper>
</BrEventContainer>
);
};
export default memo(BrEvent);
+ data.json
"brEvent": [
{
"id": 1,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/banner_delivery.png",
"url": "http://www.baskinrobbins.co.kr/store/catering.php",
"eventType": "./img/stit_store.gif",
"title": "해피오더 딜리버리로 간편하게 주문하세요!",
"period": "상시진행",
"alt": "이벤트 이미지1"
},
{
"id": 2,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/banner_delivery_bm.png",
"url": "http://www.baskinrobbins.co.kr/store/catering.php",
"eventType": "./img/stit_store.gif",
"title": "배달의 민족에서 빠르게 주문하세요!",
"period": "상시진행",
"alt": "이벤트 이미지2"
},
{
"id": 3,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/banner_delivery_ygy.png",
"url": "http://www.baskinrobbins.co.kr/store/catering.php",
"eventType": "./img/stit_store.gif",
"title": "배달의 민족에서 빠르게 주문하세요!",
"period": "상시진행",
"alt": "이벤트 이미지3"
},
{
"id": 4,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/banner_delivery_kakao.png",
"url": "http://www.baskinrobbins.co.kr/store/catering.php",
"eventType": "./img/stit_store.gif",
"title": "배달의 민족에서 빠르게 주문하세요!",
"period": "상시진행",
"alt": "이벤트 이미지4"
},
{
"id": 5,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/1670146074.png",
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=B&seq=10564",
"eventType": "./img/stit_online.gif",
"title": "기아 멤버스 최대 50% 제휴 혜택",
"period": "상시진행",
"alt": "이벤트 이미지5"
},
{
"id": 6,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/1667538956.png",
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=B&seq=10544",
"eventType": "./img/stit_online.gif",
"title": "2022 배스킨라빈스 특별한 제휴혜택",
"period": "상시진행",
"alt": "이벤트 이미지6"
},
{
"id": 7,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/1659436130.png",
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=A&seq=10285",
"eventType": "./img/stit_store.gif",
"title": "고소한 오트밀크와 깔끔한 콜드브루의 만남, 콜드브루 오트라떼 출시!",
"period": "상시진행",
"alt": "이벤트 이미지7"
},
{
"id": 8,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/1643869329.png",
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=A&seq=8624",
"eventType": "./img/stit_store.gif",
"title": "KT멤버십 고객이라면 누구나 파인트 30% 할인!",
"period": "상시진행",
"alt": "이벤트 이미지8"
},
{
"id": 9,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/1667382917.png",
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=A&seq=10104",
"eventType": "./img/stit_store.gif",
"title": "현대카드 M포인트 50% 사용",
"period": "상시진행",
"alt": "이벤트 이미지9"
},
{
"id": 10,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/1639297260.png",
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=A&seq=4922",
"eventType": "./img/stit_store.gif",
"title": "제휴 할인 카드 혜택 안내",
"period": "상시진행",
"alt": "이벤트 이미지10"
},
{
"id": 11,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/1570702843.png",
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=B&seq=3722",
"eventType": "./img/stit_online.gif",
"title": "2018 한글날 기념 무료 글꼴 공개! 배스킨라빈스체",
"period": "상시진행",
"alt": "이벤트 이미지11"
},
{
"id": 12,
"img": "http://www.baskinrobbins.co.kr/upload/event/image/1578277305.png",
"url": "http://www.baskinrobbins.co.kr/event/view.php?flag=A&seq=3302",
"eventType": "./img/stit_store.gif",
"title": "1회용 컵 사용 줄이기 안내",
"period": "상시진행",
"alt": "이벤트 이미지12"
},
{
"id": 13,
"img": "http://www.baskinrobbins.co.kr/upload/event/images/banner_praise_2022-1.png",
"url": "http://www.baskinrobbins.co.kr/customer/praise.php",
"eventType": "./img/stit_store.gif",
"title": "2022년 1분기 고객 BEST 칭찬점포 안내",
"alt": "이벤트 이미지13"
}
],
- src / mainComponents / BrMenu.jsx
/**
* @filename : BrMenu.jsx
* @author : chanCo
* @description : BrMenu 영역 정의
*/
import React, { memo } from 'react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
const BrMenuContainer = styled.div`
& {
position: relative;
width: 100%;
height: 1157px;
background: url(./img/bg_menu.jpg) no-repeat center center;
overflow: hidden;
.menuTitle {
padding: 78px 0 0;
text-align: center;
}
.menuList {
position: relative;
width: 981px;
margin: 50px auto 0;
text-align: center;
.icecream {
position: absolute;
top: 3%;
left: 22%;
width: 400px;
height: 300px;
opacity: 0;
}
.icecreamCake {
position: absolute;
top: 3%;
right: 6%;
width: 276px;
height: 472px;
opacity: 0;
}
.beverage {
position: absolute;
top: 40%;
left: 1%;
width: 230px;
height: 366px;
opacity: 0;
}
.coffee {
position: absolute;
top: 40%;
left: 29%;
width: 329px;
height: 292px;
opacity: 0;
}
.gift {
position: absolute;
bottom: 21%;
right: 6%;
width: 275px;
height: 183px;
opacity: 0;
}
.dessert {
position: absolute;
bottom: 4%;
left: 29%;
width: 329px;
height: 177px;
opacity: 0;
}
}
}
`;
const BrMenu = () => {
return (
<BrMenuContainer>
<div className='menuTitle'>
<img src="./img/h_menu.png" alt="BR Menu" />
</div>
<div className='menuList'>
<img src="./img/img_menu_list_220429.png" alt="메뉴 리스트" />
<Link to="/" className='icecream'><h4>ICECREAM</h4></Link>
<Link to="/" className='icecreamCake'><h4>ICECREAMCAKE</h4></Link>
<Link to="/" className='beverage'><h4>BEVERAGE</h4></Link>
<Link to="/" className='coffee'><h4>COFFEE</h4></Link>
<Link to="/" className='gift'><h4>GIFT</h4></Link>
<Link to="/" className='dessert'><h4>DESSERT</h4></Link>
</div>
</BrMenuContainer>
);
};
export default memo(BrMenu);
- src / mainComponents / BrStore.jsx
/**
* @filename : BrStore.jsx
* @author : chanCo
* @description : BrStore 영역 정의
*/
import React from 'react';
import styled from 'styled-components';
const BrStoreContainer = styled.div`
& {
position: relative;
width: 1200px;
margin: auto;
display: flex;
padding-bottom: 120px;
h3 {
padding: 95px 0 50px;
text-align: center;
}
.storeImg {
width: 100%;
}
}
`;
const BrStore = () => {
return (
<BrStoreContainer>
<div className='brStoreTitle'>
<h3>
<img src="./img/h_store.png" alt="br store" />
</h3>
<a href="#!">
<img className='storeImg' src="./img/img_store.jpg" alt="매장 찾기" />
</a>
</div>
<div className='happyOrderTilte'>
<h3>
<img src="./img/h_happyorder_delivery.png" alt="happy order" />
</h3>
<a href="#!">
<img className='storeImg' src="./img/img_happyorder_delivery.png" alt="매장 찾기" />
</a>
</div>
</BrStoreContainer>
);
};
export default BrStore;
- src / mainComponents / BrSns.jsx
/**
* @filename : BrSns.jsx
* @author : chanCo
* @description : BrSns 영역 정의
*/
import React, { memo, useEffect, useState } from 'react';
import axios from 'axios';
import styled from 'styled-components';
const BrSnsContainer = styled.div`
& {
position: relative;
width: 100%;
.brSns {
position: relative;
width: 1200px;
margin: auto;
overflow: hidden;
.brSnsTitle {
position: relative;
padding-bottom: 26px;
text-align: center;
}
.brSnsLink {
position: relative;
display: flex;
justify-content: center;
align-items: center;
padding-bottom: 35px;
li {
padding: 0 10px;
a {
&:hover { opacity: .7; }
}
}
}
.instagramTitleWrap {
margin-bottom: 30px;
.instagramTitle {
padding: 26px 0;
img {
position: absolute;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
hr { opacity: .5; }
}
}
.instagramImg {
position: relative;
margin-bottom: 190px;
width: 100%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.imgWrap {
display: inline-block;
cursor: pointer;
img {
width: 237px;
height: 238.5px;
}
}
}
}
}
`;
const BrSns = () => {
// TODO: sns 로고 이미지 상태값 정의
const [snsImg, setSnsImg] = useState([]);
// sns 로고 이미지 데이터 가져오기
useEffect(() => {
(async () => {
try {
const response = await axios.get('http://localhost:3001/brSns');
setSnsImg(snsImg => response.data);
} catch(e) {
console.error(e);
alert('데이터 연동 실패');
}
})();
}, []);
// TODO: 인스타그램 이미지 상태값 정의
const [instaImg, setInstaImg] = useState([]);
// 인스타그램 이미지 데이터 가져오기
useEffect(() => {
(async () => {
try {
const response = await axios.get('http://localhost:3001/instagramImg');
setInstaImg(instaImg => response.data);
} catch(e) {
console.error(e);
alert('데이터 연동 실패');
}
})();
}, []);
return (
<BrSnsContainer>
<div className='brSns'>
<h3 className='brSnsTitle'>
<img src="./img/h_sns.png" alt="SNS" />
</h3>
<ul className='brSnsLink'>
{snsImg.map((v,i) => {
return (
<li key={v.id}>
<a href={v.url} target='_blank' rel='noreferrer'>
<img src={v.img} alt={v.alt} />
</a>
</li>
);
})}
</ul>
<div className='instagramTitleWrap'>
<h4 className='instagramTitle'>
<img src="./img/tit_sns.png" alt="baskinrobbis instagram" />
<hr />
</h4>
</div>
<div className='instagramImg'>
{instaImg.map((v,i) => {
return (
<div key={v.id} className='imgWrap'>
<img src={v.img} alt={v.alt} />
</div>
);
})}
</div>
</div>
</BrSnsContainer>
);
};
export default memo(BrSns);
+ data.json
"brSns": [
{
"id": 1,
"url": "https://www.facebook.com/baskinrobbins.kr",
"img": "http://www.baskinrobbins.co.kr/assets/images/main/sns_facebook.png",
"alt": "SNS 이미지 1"
},
{
"id": 2,
"url": "https://twitter.com/BaskinrobbinsKR",
"img": "http://www.baskinrobbins.co.kr/assets/images/main/sns_twitter.png",
"alt": "SNS 이미지 2"
},
{
"id": 3,
"url": "http://blog.naver.com/brgirl31",
"img": "http://www.baskinrobbins.co.kr/assets/images/main/sns_blog.png",
"alt": "SNS 이미지 3"
},
{
"id": 4,
"url": "https://www.instagram.com/baskinrobbinskorea",
"img": "http://www.baskinrobbins.co.kr/assets/images/main/sns_instagram.png",
"alt": "SNS 이미지 4"
},
{
"id": 5,
"url": "https://www.youtube.com/user/baskinrobbinskorea",
"img": "http://www.baskinrobbins.co.kr/assets/images/main/sns_youtube.png",
"alt": "SNS 이미지 5"
}
],
"instagramImg": [
{
"id": 1,
"img": "https://cdn.attractt.com/contents/2021/04/26/CVmM4uEPLMQ_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 2,
"img": "https://cdn.attractt.com/contents/2021/04/26/CVosOOhBVK0_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 3,
"img": "https://cdn.attractt.com/contents/2021/04/26/CUJgLTmPoXF_image.jpg",
"alt": "@instaram"
},
{
"id": 4,
"img": "https://cdn.attractt.com/contents/2021/04/26/CT1by-Ylprm_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 5,
"img": "https://cdn.attractt.com/contents/2021/04/26/Ca8lOpOv731_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 6,
"img": "https://cdn.attractt.com/contents/2021/04/26/Ca1DpO8hr_d_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 7,
"img": "https://cdn.attractt.com/contents/2021/04/26/Ca16fydPnGS_image.jpg",
"alt": "@instaram"
},
{
"id": 8,
"img": "https://cdn.attractt.com/contents/2021/04/26/Ca4eFeNvZiu_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 9,
"img": "https://cdn.attractt.com/contents/2021/04/26/Ca4sSPFLZsy_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 10,
"img": "https://cdn.attractt.com/contents/2021/04/26/Ca4w8-EFf2__carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 11,
"img": "https://cdn.attractt.com/contents/2021/04/26/CbC7oQnrf7f_image.jpg",
"alt": "@instaram"
},
{
"id": 12,
"img": "https://cdn.attractt.com/contents/2021/08/06/232180242_436638067458433_4542285466444366688_n.jpg",
"alt": "@instaram"
},
{
"id": 13,
"img": "https://cdn.attractt.com/contents/2021/04/26/CTl319uva3y_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 14,
"img": "https://cdn.attractt.com/contents/2021/04/26/CbWrfCNrzhd_carousel_01.jpg",
"alt": "@instaram"
},
{
"id": 15,
"img": "https://cdn.attractt.com/contents/2021/04/26/CaeKcywlF8Z_carousel_01.jpg",
"alt": "@instaram"
}
],