ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OnlineShop 만들기 - 8) Product Detail Page 만들기1 (상품 이미지, 상품 설명란 만들기)
    NODE.JS 2021. 5. 15. 23:24

    상품에 대한 디테일 정보를 나타내는 페이지를 만들 것이다.

    상품 사진란과 설명을 하는 란 2가지로 나위어서 진행한다.

     

    순서
    완성된 화면

     

    views>DetailProductPage>DetailProductPage.js파일을 만든다.

    import React from 'react'
    
    function DetailProductPage() {
        return (
            <div>
                
            </div>
        )
    }
    
    export default DetailProductPage
    

     

    우선 app.js에 route를 만들어준다.

    import DetailProductPage from './views/DetailProductPage/DetailProductPage';
    
    //아무나 상세페이지 확인 가능하도록 null로 만들어준다.
    
    <Route exact path="/product/:productId" component={Auth(DetailProductPage, null)} />
    

     

    우리는 이미 랜딩페이지에서 카드를 누르면 다음의 링크로 (productId) 이동하도록 설정해놨다.

    const renderCards = Products.map((product, index) => {
    
            return <Col lg={6} md={8} xs={24}>
                <Card
                    hoverable={true}
                    //링크 누르면 다음의 주소로 이동한다.
                    cover={<a href={`/product/${product._id}`} > <ImageSlider images={product.images} /></a>}//이미지가 애니메이션 형식으로 옆으로 이동하도록 설정
                >
                    <Meta //상품 이름하고 가격이 나오도록 한다. 
                        title={product.title}
                        description={`$${product.price}`}
                    />
                </Card>
            </Col>
        })

     

    디테일 프로덕트 페이지에 다음과 같은 문장을 추가해준다.

    (axios 보내기)

    import React,{useEffect} from 'react'
    
    function DetailProductPage() {
        //링크타고 들어온 주소에 이미 productId 가 있으므로 이것을 이용한다. 
        const productId = props.match.params.productId
        useEffect(() => {
            //이렇게 하는 이유는 get을 해서 가져올때는 쿼리를 이렇게 넣는다. 
            Axios.get(`api/product/products_by_id?id = ${productId}&type = single`)
                .then(response=>{
                    
                })
        }, [])

    axios를 보냈으니 router를 생성한다.

     

    router.get("/products_by_id", (req, res) => {
        let type = req.query.type //이번에는 쿼리로 가져왔기 때문에 body가 아닌 쿼리를 사용한다.
        let productIds = req.query.id
        if(type =="array"){//타입이 배열이라서 여러개를 가져온다면 다음과 같이 처리한다(여기서 우리는 single로 가져와서 상관 없다.)
    
        }
    
        //we need to find product information that belong to product Id
        Product.find({'_id':{$in:productIds}}) //아이디에 해당하는 모든 정보 받을 수 있다 ($in)
            .populate('writer')
            .exec((err, product)=>{
                if(err) return res.status(400).send(err);
                return res.status(200).send(product)
            })
    });

     

    다음과 같이 정보를 받아오면 처리해준다.

    function DetailProductPage() {
        //링크타고 들어온 주소에 이미 productId 가 있으므로 이것을 이용한다. 
        const productId = props.match.params.productId
        const [Product, setProduct] = useState([])//Axios에서 받아온 상품에 대한 정보를 받는다.
        useEffect(() => {
            //이렇게 하는 이유는 get을 해서 가져올때는 쿼리를 이렇게 넣는다. 여기서 single은 우리가 원하는 프로덕트 디테일이 1개이기 때문에 넣은 것이다.(아이디 하나만 넣는다)여러가지라면 type =array
            Axios.get(`api/product/products_by_id?id = ${productId}&type = single`)
                .then(response=>{
                    setProduct(response.data[0])//res에 담긴 product 1개의 정보만 받아와서 세팅한다.
    
                })
        }, [])

     

    이제 디테일 페이지의 템플릿을 작성하는데, 

    우리는 이미지를 나타낼 때, react-image-gallery를 사용하므로 client에 

    npm install react-image-gallery --save를 해준다.

    https://www.npmjs.com/package/react-image-gallery

     

    react-image-gallery

    React carousel image gallery component with thumbnail and mobile support

    www.npmjs.com

     

     

        return (
            <div className="postPage" style={{ width: '100%', padding: '3rem 4rem' }}>
    
                <div style={{ display: 'flex', justifyContent: 'center' }}>
                    <h1>{product.title}</h1>
                </div>
    
                <br />
    
                <Row gutter={[16, 16]} >
                    <Col lg={12} xs={24}>
                        ProductImage
                    </Col>
                    <Col lg={12} xs={24}>
                        ProductInfo
                    </Col>
                </Row>
            </div>
        )
    }
    

     

    여기서 상품 이미지를 나타내는 파일과 세부정보를 나타내는 파일을 sections 폴더 안에 각각 만들어 줄 것이다. 

    DetailProductPage>Sections>ProductImage.js

    DetailProductPage>Sections>ProductInfo.js파일을 각각 생성해준다.

     

    다음과 같이 디테일 페이지에 import 해준다.

    그다음 상품에 대한 정보를 각각 props로 넣어준다. 

    import React,{useEffect, useState} from 'react'
    import Axios from 'axios'
    import { Row, Col } from 'antd';
    import ProductImage from './Sections/ProductImage';
    import ProductInfo from './Sections/ProductInfo';
    
    
                <Row gutter={[16, 16]} >
                    <Col lg={12} xs={24}>
                        <ProductImage detail = {Product}/>
                    </Col>
                    <Col lg={12} xs={24}>
                        <ProductInfo detail = {Product}/>
                    </Col>
                </Row>
    
    
    export default DetailProductPage
    

    현재상태

     

     

    productImag.js파일로 가서 image gallery를 import 해주고 템플릿을 만들어준다.

    index.css에는 다음과 같은 디자인을 추가해준다.

    @import "~react-image-gallery/styles/css/image-gallery.css";
    
    

     

    이미지 갤러리 사용법 참고

     

    import React,{useEffect, useState} from 'react'
    import ImageGallery from 'react-image-gallery';
    
    function ProductImage(props) {
        const [Images, setImages] = useState([])
    
        useEffect(() => {
            //이미지가 있고 1개이상 존재한다면
            if(props.detail.images && props.detail.images.length>0) {
                let images = [];
    
                //오리지날은 슬라이드하면서 보여주는 큰 사진이고, 썸네일은 아래 보이는 작은 보기들이다.
                props.detial.images && props.detail.images.map(item =>{
                    images.push({
                        original: `http://localhost:5000/${item}`,
                        thumbnail: `http://localhost:5000/${item}`
                    })
                })
                setImages(images)
            }
        }, [props.detail])//우리가 이미지를 가져우기 위해서 dom이 로드 되었을 때 우리가 props.detail을 가져오지 못하면 실행이 안되므로, props.detial이 가져와 졌을 때 이것을 실행한다는 의미이다. (props.detial이 변경되었으면 useEffect가 다시 실행될 것이다. )
        return (
            <div>
                {/* image gallery를 이용해서 이미지들을 넣을 것이다. */}
                <ImageGallery items= {Images}/>
            </div>
        )
    }
    
    export default ProductImage

     

    현재상태

     

     

    이제 product info 부분을 만들자. 

    antd를 사용할 것이다. 

     

    import React, { useEffect, useState } from 'react'
    import { Button, Descriptions } from 'antd';
    
    function ProductInfo(props) {
    
        const [Product, setProduct] = useState({})//Product 정보 가져와서 담을 것이다. 
    
        useEffect(() => {
    
            setProduct(props.detail)//상품정보 가져와서 셋팅
    
        }, [props.detail])//있을 때 실행(변경되거나 존재할때 실행)
    
        return (
            <div>
                        {/* 상품들의 정보를 가져온다.  */}
    
                <Descriptions title="Product Info"> 
                    <Descriptions.Item label="Price"> {Product.price}</Descriptions.Item>
                    <Descriptions.Item label="Sold">{Product.sold}</Descriptions.Item>
                    <Descriptions.Item label="View"> {Product.views}</Descriptions.Item>
                    <Descriptions.Item label="Description"> {Product.description}</Descriptions.Item>
                </Descriptions>
    
                <br />
                <br />
                <br />
                <div style={{ display: 'flex', justifyContent: 'center' }}>
                    <Button size="large" shape="round" type="danger"
                        onClick
                    >
                        Add to Cart
                        </Button>
                </div>
            </div>
        )
    }
    
    export default ProductInfo

     

    css 스타일링을 주기 위해서 index.css에 다음과 같이 추가한다. 

    table {
      font-family : arial, sans-serif;
      border-collapse: collapse;
      width: 100%;
    }
    
    td, th {
      border: 1px solid #dddddd;
      text-align: left;
      padding: 8px;
    
    }
    
    tr:nth-child(even){
      background-color: #dddddd;
    }

    현재 화면

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.