상품 관리 - Part6 상품 상세 이미지 슬라이드 구현

Project Diary/React + Firebase (Snack ShoppingMall)

 Firebase에 여러 개의 파일을 저장하는 방법 기록


이 작업의 주요 포인트는 Firebase에 여러 개의 파일을 저장하고, 저장된 파일들을 받아와서 슬라이드로 보여주는 것입니다. 따라서 이 부분에 중점을 두고 구현 과정을 기록하겠습니다.

 

 

파일 업로드

1개의 파일(대표 사진)과 여러개의 파일(상세 사진)을 Firebase Storage에 업로드하는 방식의 차이점에 대해서 설명하겠습니다.

> 1개의 파일 업로드 (대표 사진 업로드)

대표 사진은 단일 파일이기 때문에, 파일을 선택하고 Storage에 업로드한 후, 해당 파일의 URL을 받아오는 과정이 간단합니다.

// 대표 사진 변경 처리
const handleFileChange = (e) => {
  const file = e.target.files[0];
  setProduct((prevProduct) => ({ ...prevProduct, photo: file }));
  setPhotoValue(e.target.value);
};

// 대표 사진 업로드
if (product.photo) {
  const fileRef = storageRef.child(product.photo.name);
  await fileRef.put(product.photo);
  addProduct.photo = await fileRef.getDownloadURL();
}
  1. 파일 선택 후 photo 상태에 파일 객체 저장.
  2. Firebase Storage에 파일을 업로드.
  3. 업로드된 파일의 URL을 받아서 addProduct.photo에 저장.

> 여러개의 파일 (상세 사진 업로드)

여러 개의 파일을 업로드할 때는 배열로 관리하고, 각 파일을 순차적으로 업로드하여 URL을 받아옵니다.

// 상세 사진 변경 처리 (여러 개 파일)
const handleDetailFileChange = (e) => {
  const files = e.target.files;
  setDetailPhotos(Array.from(files));
};

// 상세 사진 업로드
if (detailPhotos.length > 0) {
  const detailPhotoURLs = [];
  await Promise.all(
    detailPhotos.map(async (file, index) => {
      const fileName = `detailPhoto${index + 1}_${Date.now()}_${file.name}`;
      const detailFileRef = storageRef.child(fileName);
      await detailFileRef.put(file);
      detailPhotoURLs.push(await detailFileRef.getDownloadURL());
    })
  );
  addProduct.detailPhotos = detailPhotoURLs;
}
  1. 여러 파일을 선택 후 detailPhotos 상태에 파일 객체 배열 저장.
  2. 배열의 각 파일을 순차적으로 Firebase Storage에 업로드.
  3. 업로드된 각 파일의 URL을 배열에 저장하고, addProduct.detailPhotos에 저장.

 

데이터패치, 슬라이더 동기화

업로드한 파일을 패치할 때도 대표 사진과 상세 사진을 각각 처리합니다.

import React, { useState, useEffect, useRef } from "react";
import styled from "styled-components";
import { useLocation } from "react-router-dom";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { IoIosArrowDropleftCircle, IoIosArrowDroprightCircle } from "react-icons/io";

// 스타일 생략

const ProductDetailSection = () => {
  const location = useLocation();
  const { item } = location.state; // 상품 데이터 받아오기

  const [nav, setNav] = useState(null);
  const [subNav, setSubNav] = useState(null);
  let sliderRef = useRef(null);
  let subSliderRef = useRef(null);

  useEffect(() => {
    setNav(sliderRef.current);
    setSubNav(subSliderRef.current);
  }, []);

  return (
    <ProductDetailSectionBlock>
      <h2 style={{ color: "#333", marginBottom: "20px", fontSize: "30px" }}>
        {item.name}
      </h2>
      <div className="content">
        <div className="sliderContainer">
          {/* 메인 슬라이더 */}
          <Slider
            className="slide"
            asNavFor={subNav}
            ref={sliderRef}
            arrows={false}
            fade={true}
          >
            {item.detailPhotos.map((photo, index) => (
              <div key={index}>
                <img src={photo} alt={`detail ${index}`} />
              </div>
            ))}
          </Slider>
          {/* 서브 슬라이더 */}
          <Slider
            className="subSlide"
            asNavFor={nav}
            ref={subSliderRef}
            slidesToShow={3}
            slidesToScroll={1}
            dots={false}
            centerMode={true}
            focusOnSelect={true}
            infinite={true}
            draggable={false}
            prevArrow={<IoIosArrowDropleftCircle />}
            nextArrow={<IoIosArrowDroprightCircle />}
          >
            {item.detailPhotos.map((photo, index) => (
              <div key={index}>
                <img src={photo} alt={`thumbnail ${index}`} />
              </div>
            ))}
          </Slider>
        </div>
        <div className="info">
          <p>가격: {parseInt(item.price).toLocaleString()}&yen;</p>
          <p>{item.description}</p>
        </div>
      </div>
    </ProductDetailSectionBlock>
  );
};

export default ProductDetailSection;
  • 데이터 패치 : 상품 상세 페이지에서 대표 사진과 상세 사진의 URL을 이용하여 이미지를 표시, Slider 컴포넌트를 이용하여 메인 슬라이더와 썸네일 슬라이더 구현.
  • 슬라이더 동기화 : useRef와 useEffect를 이용하여 메인 슬라이더와 썸네일 슬라이더를 동기화.