React

React로 커머스 사이트 만들기 (3): 홈페이지 & 상품페이지

Jinmidnight 2023. 9. 23. 16:26

 

 

GitHub - jinmidnight01/powersell_frontend

Contribute to jinmidnight01/powersell_frontend development by creating an account on GitHub.

github.com

본 내용의 코드는 전부 위 링크에 있으니 참고바랍니다

 


홈페이지 (LandingPage.js)

 

전 상품 정보 GET 

- useEffect를 통해 최초 렌더링 시에만 작동

- useState를 통해 itemList 상태를 GET한 전 상품정보로 set

- useState를 통해 전 상품정보를 GET할 경우 loading 상태 false로 변경

...
  // REST API 2-1: get all items
  useEffect(() => {
    async function rendering_item() {
      axios
        .get(`${hostURL}/api/items`)
        .then((response) => {
          setItemList(response.data);
          setLoading(false);
        })
        .catch((error) => {
          console.log("Error fetching items: ", error.response.data);
        });
    }
    rendering_item();
  }, []);
...

 

Map 함수로 상품 나열

- 삼향연산자를 통해 itemList에 원소가 있을 때만 map 함수 실행

- key를 itemId로 설정

return 
...
  { itemList.length === 0 ? (
  <div>
    서비스 정비 중...
  </div>
  ) : (
  <div>
    {itemList.map((product) => (
      <div key={product.itemId}>
        <Link
          to={`/product/${product.itemId}`}
        >
          <img
            src={itemImage(product)}
            alt={product.name}
          />
          <div>
            <p>
              {product.stockQuantity === 0 && (
                <span>품절</span>
              )}
              {product.name}
            </p>
            <p>{product.originalPrice}원</p>
            <h2>80% {product.price}원</h2>
          </div>
        </Link>
      </div>
    ))}
  </div> )
...

 

상품 이미지 구현

- 상품 이름에 따라 다른 이미지 출력

- swtich문 활용

import 삼다수 from "../../images/home/삼다수.jpg";
import 신라면 from "../../images/home/신라면.jpg";
import 컵밥 from "../../images/home/컵밥.jpg";
import 햇반 from "../../images/home/햇반.jpg";
import 구운란 from "../../images/home/구운란.jpg";
...
// item image
const itemImage = (order) => {
  switch (order.name) {
    case "제주 삼다수 2L (6개입)":
      return 삼다수;
    case "농심 신라면 (5개입)":
      return 신라면;
    case "오뚜기 컵밥 오삼불고기덮밥 310g":
      return 컵밥;
    case "햇반 백미밥 210g (3개입)":
      return 햇반;
    case "[EEE] 무항생제 맥반석 구운계란 (15구)":
      return 구운란;
    default:
      return null;
  }
};
...

 

서비스 후기 POST

 

- useState를 통해 feedback state 설정

- handleFeedbackChange 함수를 통해 실시간으로 feedback state이 변하도록 구현

- 후기 POST가 완료되면 feedbackLoading state가 false로 변경

...
  const [feedback, setFeedback] = useState("");
  const handleFeedbackChange = (event) => {
    setFeedback(event.target.value);
  };
  const [isFeedbackLoading, setFeedbackLoading] = useState(false);
...
  // REST API 3-2: POST feedback
  function submitFeedback() {
    if (feedback.trim() === "") {
      alert("피드백을 입력해주세요!");
      return;
    }
    setFeedbackLoading(true);
    const inputs = { content: feedback };
    axios
      .post(`${hostURL}/api/feedbacks`, inputs)
      .then(() => {
        alert("피드백이 전송되었습니다. 감사합니다 :-)");
        setFeedback("");
        setFeedbackLoading(false);
      })
      .catch((error) => {
        console.log(error);
      });
  }
...
return
...
  {isFeedbackLoading ? (
    <div>
      <img
        src={spinner}
        alt="로딩 중..."
        width="15%"
      />
    </div>
  ) : (
    <textarea
      onChange={handleFeedbackChange}
      value={feedback}
      type="text"
      placeholder="짧은 한 줄 소감도 큰 도움이 됩니다!"
    />
  )}
  <input
    type="button"
    value="제출하기"
    onClick={submitFeedback}
  />
...

 


상품페이지 (DetailProductPage.js)

 

특정 상품 정보 GET

- useEffect를 통해 최초 렌더링 시에만 작동

- useState를 통해 product 상태를 GET한 상품정보로 set

- useState를 통해 해당 상품정보를 GET할 경우 loading 상태 false로 변경

- 존재하지 않는 productId가 API 주소에 입력될 경우 NotFoundPage(404)로 연결

...
  // REST API 2-3: get item detail
  useEffect(() => {
    axios
      .get(`${hostURL}/api/items/${productId}`)
      .then((response) => {
        setProduct(response.data);
        setLoading(false);
      })
      .catch((error) => {
        navigate("/404");
      });
  }, [productId, navigate]);
...

 

SNS 공유 기능

- navigator.share 함수를 통해 SNS 공유

- Android 버전 같은 경우 navigator.share 함수가 작동하지 않기에 kakao 공유로 제한

...
  // templateId related to name
  switch (product.name) {
    case "제주 삼다수 2L (6개입)":
      imageUrl = "https://i.ibb.co/Fqt03tQ/image.jpg";
      break;
    case "농심 신라면 (5개입)":
      imageUrl = "https://i.ibb.co/hs8kHWs/image.jpg";
      break;
    case "오뚜기 컵밥 오삼불고기덮밥 310g":
      imageUrl = "https://i.ibb.co/wsxHrfj/image.jpg";
      break;
    case "햇반 백미밥 210g (3개입)":
      imageUrl = "https://i.ibb.co/hdqThLv/image.jpg";
      break;
    case "[EEE] 무항생제 맥반석 구운계란 (15구)":
      imageUrl = "https://i.ibb.co/gyv3MMB/image.jpg";
      break;
    default:
      imageUrl = "https://i.ibb.co/Rvw6N3H/landing.png";
  }

  // kakao share function
  const kakaoButton = () => {
    if (window.Kakao) {
      const kakao = window.Kakao;

      if (!kakao.isInitialized()) {
        kakao.init("92b357c41da16ab9f3e0fa7f98cfbc30");
      }
      kakao.Share.sendDefault({
        objectType: "feed",
        content: {
          title: product.name,
          description: `${product.price}원`,
          imageUrl: imageUrl,
          link: {
            mobileWebUrl: `https://www.cheapat9.com/product/${product.itemId}`,
            webUrl: `https://www.cheapat9.com/product/${product.itemId}`,
          },
        },
        buttons: [
          {
            title: "보러가기",
            link: {
              mobileWebUrl: `https://www.cheapat9.com/product/${product.itemId}`,
              webUrl: `https://www.cheapat9.com/product/${product.itemId}`,
            },
          },
        ],
      });
    }
  };

  // share function
  const handleShare = () => {
    if (navigator.share) {
      navigator.share({
        title: product.name,
        url: `https://www.cheapat9.com/product/${product.itemId}`,
      });
    } else {
      kakaoButton();
    }
  };
...
  return
...
    // share button img
    <img
      onClick={handleShare}
      className="share-icon"
      src={share}
      alt=""
    ></img>
...

 

구매 Modal로 props 전달

- 품절 시 구매하기 버튼 미작동

- 구매하기 버튼 클릭 시 자식 컴포넌트인 구매 Modal로 props(closeModal, product, setPosting, isClicked) 전달

return
...
  // 구매하기 버튼
  <div>
    <Button
      className={isOutOfStock ? "negative_button" : "positive-button"}
      onClick={() => {
        if (!isOutOfStock) {
          setClicked(!isClicked);
        }
      }}
      text={isOutOfStock ? "품절" : "바로구매"}
    />
  </div>

  // 구매 Modal
  {isClicked && (
    <div>
      <Modal
        closeModal={() => {
          setClicked(!isClicked);
          document.body.style.overflow = "auto";
        }}
        product={product}
        setPosting={setPosting}
        isClicked={isClicked}
      ></Modal>
    </div>
  )}
...