[React/Node] 마켓컬리 클론코딩 3 - 메인페이지2
일단 지난번에 고려했던 이미지를 다른 방식으로 정리하는데에는 실패했다.
import 없이 src만 가지고 객체를 만드려했으나, 이미 전에 경험한대로 로컬에 저장되어있는 이미지는 import없이는 깨져서 나오기 때문에 의미가 없었다.
일단은 기존대로 하되, 다른 방법이 있나 찾아보긴 해야겠다.
- 메인 페이지 - 메인 배너 부분, 아이템 슬라이드
1. 메인 배너
메인 배너를 만들기 위해 Banner.js 파일을 components 폴더에 추가하였다.
배너에 사용될 이미지를 images 폴더 아래에 BannerImg.js 파일에 추가하고, css 폴더 아래에 Banner.css 파일을 생성하였다.
<Banner.js>
import React from "react";
import BannerImg from "../images/BannerImg";
import "../css/Banner.css";
import { Swiper, SwiperSlide } from 'swiper/react';
import SwipeCore,{Navigation,Pagination,Autoplay} from 'swiper';
import 'swiper/css';
import 'swiper/css/autoplay';
import 'swiper/scss/navigation';
import 'swiper/scss/pagination';
SwipeCore.use([Navigation,Pagination,Autoplay]);
const Banner = () => {
return(
<nav id="banner">
<div className="slide">
<Swiper
spaceBetween={200}
slidesPerView={1}
navigation //navigation 커스텀 필요(좌우 간격)
pagination={{ clickable: true }}
autoplay={{ delay: 3000, disableOnInteraction: false }}
loop={true}
>
{BannerImg.map((slide) => (
<SwiperSlide><div className="slides" key={slide.id}><img src={slide.src} /></div></SwiperSlide>
))}
</Swiper>
</div>
</nav>
)
}
export default Banner;
<Banner.css>
nav {
padding-top: 40px;
width: 100%;
}
nav .slide{
width: 100%;
display: flex;
flex-wrap: nowrap;
overflow: hidden;
z-index: 1;
}
nav .slide .slides {
display: flex;
align-items: center;
justify-content: center;
transition: left 0.15s;
left: 0px;
flex-shrink: 0;
}
nav .swiper-button-next, nav .swiper-button-prev {
color: white;
width: 60px;
height: 60px;
border-radius: 100%;
background-color: rgb(172, 172, 172);
opacity: 0;
transition-property: all;
transition-duration: 0.5s;
}
nav .slide:hover {
.swiper-button-next, .swiper-button-prev {
opacity: 0.8;
}
}
nav .swiper-button-prev {
left: 5%;
}
nav .swiper-button-next {
right: 5%;
}
<BannerImg.js>
const BannerImg = [
{
id: 1,
src: "https://product-image.kurly.com/cdn-cgi/image/width=1900,height=370,quality=85/banner/main/pc/img/21c8cf5b-45ba-4983-846a-7500ce30404d.jpg"
},
{
id: 2,
src: "https://product-image.kurly.com/cdn-cgi/image/width=1900,height=370,quality=85/banner/main/pc/img/b67fc789-82e7-4699-bb56-abcbc5aac3ac.jpg"
},
{
id: 3,
src: "https://product-image.kurly.com/cdn-cgi/image/width=1900,height=370,quality=85/banner/main/pc/img/0ee90d6c-a2a8-4939-b69f-7b62cb5d71bd.jpg"
},
{
id: 4,
src: "https://product-image.kurly.com/cdn-cgi/image/width=1900,height=370,quality=85/banner/main/pc/img/94d94cd6-e92f-40eb-ac0c-aad816147cdd.jpg"
},
{
id: 5,
src: "https://product-image.kurly.com/cdn-cgi/image/width=1900,height=370,quality=85/banner/main/pc/img/1dca3148-0289-47e7-80a5-b2381bf0ffb3.jpg"
}
];
export default BannerImg;
JavaScript로 슬라이드를 만들어 추가하려고 했으나, <script> 태그를 이용해 추가하는 것이 먹히지 않았다.
방법이 없지는 않은 것 같은데, 일단은 react의 swiper 라이브러리를 활용해 제작하였다.
진행 중 swiper에 관한 에러가 발생하였었는데, swiper는 className 값이 붙을 수 없는 듯하여 div를 추가해 붙여주었다.
배너의 navigation(다음페이지 화살표)가 마우스를 올리면 나타나도록 효과를 주기 위해 초기에 display: none;을 사용하였었고, 원본처럼 점차 사라지고 나타나는 기능을 추가하고자 transition-duration을 이용하였는데 적용이 되지 않는 상황이 발생하였다.
문제는 display에 있었는데, transition은 랜더링 과정에서 0의 값을 1로 점차 변화시키는 과정을 표현해줄 수 있다. 그런데 display: none은 랜더 과정에서 노드를 포함시키지 않게 해버리기 때문에, 마치 0이 아닌 아예 없는 값으로 인식이 된다.
그렇기에 display처럼 시각적으로 안보이게 할 수 있는 대체재를 찾아야했고, 이에는 visibility와 opacity가 있는데 이번에는 보일 때도 투명도를 조절해주는 것이 좋기 때문에 opacity를 사용하기로 하였다.
배너의 추가는 끝났지만 문제가 발생했다. 메뉴 중에 카테고리 탭에 마우스를 올리면 세부 메뉴가 드롭다운 되도록 만들어 놨기 때문에, 호버되었을 때 배너가 아래로 밀리는 현상이 발생했다.
때문에, position: absolute를 통해 다른 요소들과 독립되도록 하고, 최상위에 보이도록 해야하므로 z-index를 사용하였다.
2. 아이템 슬라이드
아이템 슬라이드를 틀처럼 재사용하고자 하여 components 폴더에 MainPage.js 파일을 생성하고 ItemSlide.js 파일을 생성해 MainPage에서 변수를 통해 값을 제공하여 그때마다 다른 정보를 포함한 슬라이드를 제공하고자 하였다.
<MainPage.js>
import React from "react";
import ItemSlide from "./ItemSlide";
import "../css/MainPage.css"
import img from "../images/imgidx";
const MainPage = () => {
let props = 'WeekItem';
return(
<React.Fragment>
<div className="main_page">
<h2>이주의 혜택<img src={img.week} height="35"/> {">"}</h2>
<h4 className="desc">지금 만날 수 있는 혜택 상품만 모아</h4>
<ItemSlide props={props}></ItemSlide>
</div>
</React.Fragment>
)
}
export default MainPage;
<ItemSlide.js>
import React from "react";
import { Swiper, SwiperSlide } from 'swiper/react';
import WeekItem from "../images/WeekItem";
import img from "../images/imgidx";
import "../css/ItemSlide.css";
import 'swiper/css';
const ItemSlide = () => {
return (
<React.Fragment>
<div className="slide_wrap">
<button className="swiper-prev swiper_bt">{"<"}</button>
<Swiper
spaceBetween={20}
slidesPerView={4}
navigation={{
prevEl: ".swiper-prev",
nextEl: ".swiper-next"
}}
>
{WeekItem.map((slide) => (
<SwiperSlide>
<div className="slide_item" key={slide.id}><img src={slide.src} width="110%" /></div>
<button className="slide_additem"><img src={img.addCart}/> 담기</button>
<div className="slide_item_info" key={slide.id}>
<p className="item_title">{slide.title}</p>
<p className="item_price">{slide.price}{"원"}</p>
<p className="item_discount">{slide.discount}{"%"}</p>
<p className="item_disc_price">{slide.disc_price}{"원"}</p>
</div>
</SwiperSlide>
))}
</Swiper>
<button className="swiper-next swiper_bt">{">"}</button>
</div>
</React.Fragment>
)
}
export default ItemSlide;
ItemSlide.js로 모든 카테고리의 아이템 슬라이드를 제공하려고 했지만, map()함수를 사용할 때 props.map()같은 형태가 사용이 되지 않았다. 일단은 고정 값으로 해두었고 다른 방식을 찾아보아야 할 것 같다.
다른 부분은 배너 만들때와 비슷해서 문제가 없었지만, 하단의 상품 정보를 표출할때 가격이 문제가 되었다. 할인가를 표현해주기 위해선 ','를 제외하고 계산해야하기 때문에 나중에 상품 등록시에 이 부분을 신경써주어야 할 것 같다.
<ItemSlide.css>
.slide_wrap {
position: relative;
}
.swiper_bt {
font-size: 20px;
font-weight: 600;
color: black;
border-radius: 100%;
background-color: white;
border: 0.5px solid rgb(241, 241, 241);
width: 40px;
height: 40px;
display: inline-block;
}
.swiper-prev {
position: absolute;
z-index: 2;
left: -20px;
top: 33%;
}
.swiper-next {
position: absolute;
z-index: 2;
right: -20px;
top: 33%;
}
.slide_item img {
width: 100%;
}
.slide_additem img {
padding-right: 3px;
}
.slide_additem {
width: 100%;
height: 40px;
font-size: 15px;
background-color: white;
border: 0.5px solid rgb(193, 193, 193);
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
}
.slide_item_info {
.item_title { font-weight: 500; padding: 10px 0; text-align: left;}
.item_price {
color: gray; font-size: 14px;
text-decoration: line-through;
text-align: left;
}
.item_discount {
text-align: left; color: orangered;
float: left;
}
.item_disc_price {
float: left;
padding-left: 10px;
font-weight: 550;
}
}
https://memo-code.tistory.com/37
마켓컬리 클론코딩 4 - 상세 페이지
제품 상세 페이지(상품 설명 이미지 부분 제외) 기존의 메인 페이지와는 별개의 페이지이기 때문에 파일을 수정하였다. import './App.css'; import { BrowserRouter, Route, Routes } from "react-router-dom"; import Heade
memo-code.tistory.com