Day39_팀프로젝트_공연정보_아웃소싱 프로젝트2

정소현·7일 전
0

스파르타

목록 보기
48/50

API정보 연결 후 레이아웃 설계 및 내용 작성, TailwindCss를 사용한 디자인


🌼 상세페이지 API연결

: 상세페이지 정보는 KOPIS의 개발 PDF를 참고하여 상세데이터 API를 연결하였다. URL뒤에 개별 id를 붙여서 가져오는 방법으로 작성하였다.
XML데이터를 json으로 변환해주고 data정보는 zustand를 사용해 저장해주었다. axios를 사용하여 api를 불러오는 것은 detailApi.js에 저장시켰다.

// detailApi.js
import axios from "axios";
import { XMLParser } from "fast-xml-parser";

const fetchDetailData = async (Id) => {
  const apiKey = import.meta.env.VITE_KOPIS_KEY; // Vite의 환경 변수 사용
  const url = `http://kopis.or.kr/openApi/restful/pblprfr/${Id}?service=${apiKey}`;

  try {
    const response = await axios.get(url);

    // XML 데이터를 JSON으로 변환
    const parser = new XMLParser({
      ignoreAttributes: false, // 속성 유지
      attributeNamePrefix: "", // 속성명 앞에 접두어 없이 처리
    });
    const jsonData = parser.parse(response.data);

    console.log(jsonData); // 변환된 JSON 데이터 콘솔 출력
    return jsonData;
  } catch (error) {
    console.error("Error fetching performance details:", error);
    throw new Error("데이터를 불러오는 중 오류가 발생했습니다.");
  }
};

export default fetchDetailData;
// useKopisStore.js
import { create } from "zustand";
import fetchKopisDataById from "../api/detailApi";

const useKopisStore = create((set) => ({
  data: null,
  error: null,
  fetchData: async (id) => {
    try {
      const result = await fetchKopisDataById(id); // API 호출
      if (result.dbs.db) {
        set({ data: result.dbs.db, error: null }); // 상태 업데이트
      } else {
        set({ error: "No data found", data: null });
      }
    } catch (error) {
      set({ error: error.message, data: null }); // 상태 업데이트
    }
  },
}));

export default useKopisStore;

그리고 Detail.jsx 상세페이지에서 zustand에 있는 data를 사용해주었다.

// Detail. jsx
import React, { useEffect } from "react";
import Tabs from "./Tabs";
import useKopisStore from "../../zustand/kopisStore";

const DetailPage = () => {
  const { data, fetchData, error } = useKopisStore((state) => ({
    data: state.data,
    fetchData: state.fetchData,
    error: state.error,
  }));
  const id = "PF248932"; // 임시 ID

  useEffect(() => {
    fetchData(id);
  }, [fetchData, id]);

  if (error) {
    return <div className="text-red-500">에러 발생: {error}</div>;
  }

  if (!data) {
    return <div className="text-gray-500">Loading...</div>;
  }

  // 데이터가 유효한지 확인
  const {
    genrenm = "정보 없음",
    prfnm = "정보 없음",
    prfpdfrom = "정보 없음",
    prfpdto = "정보 없음",
    poster = "",
    area = "정보 없음",
    fcltynm = "정보 없음",
    prfstate = "정보 없음",
    pcseguidance = "정보 없음",
    prfcast = "정보 없음",
    entrpsnmH = "정보 없음",
    entrpsnmS = "정보 없음",
    prfruntime = "정보 없음",
  } = data;

  return (
    <div className="w-full min-h-screen  flex items-center justify-center p-4 ">
      <div className="w-[1200px] p-6  ">
        <div className="mb-12 border-b border-gray-800 pb-4 ">
          <h2 className="text-sm font-semibold mb-4">{genrenm}</h2>
          <h3 className="text-5xl font-extrabold mb-4">{prfnm}</h3>
          <p className="text-sm mb-4">
            {prfpdfrom} - {prfpdto}
          </p>
        </div>

        <div className={`flex ${poster ? "gap-4" : ""} mb-4 `}>
          {poster && (
            <img
              src={poster}
              alt={prfnm}
              className="w-full max-w-[430px] h-auto object-cover rounded-lg mr-8"
            />
          )}
          <div className=" w-[600px] ">
            <div className="flex items-baseline mb-4 ">
              <p className="w-20 font-bold flex-shrink-0 mr-4">출연진: </p>
              <p className="flex-grow">{prfcast}</p>
            </div>
            <div className="flex items-baseline  mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">공연 장소: </p>
              <p className="flex-grow">
                {area} {fcltynm}
              </p>
            </div>
            <div className="flex items-baseline mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">공연 상태: </p>
              <p className="flex-grow">{prfstate}</p>
            </div>
            <div className="flex items-baseline mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">티켓 가격: </p>
              <p className="flex-grow">{pcseguidance}</p>
            </div>
            <div className="flex items-baseline mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">공연시간: </p>
              <p className="flex-grow">{prfruntime}</p>
            </div>
            <div className="flex items-baseline mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">주최사: </p>
              <p className="flex-grow">
                {entrpsnmH},{entrpsnmS}
              </p>
            </div>
          </div>
        </div>

        <div className="w-full mt-16">
          <Tabs />
        </div>
      </div>
    </div>
  );
};

export default DetailPage;

🌼 TailwindCSS 사용법

TailwindCss에 아직 익숙하지 않아 헷갈리는 것들을 정리해보았다.
className="" 안에 스타일을 지정하여 사용하는 방식이다.

mt => margin-top
pt => padding-top

이런식으로 단축된 용어를 사용하고 있다.

TailwindCss 반응형 사용법

tailwind에선 일반적인 breakpoint을 기본값으로 제공한다.

sm 640px @media (min-width: 640px) { ... }
md 768px @media (min-width: 768px) { ... }
lg 1024px @media (min-width: 1024px) { ... }
xl 1280px @media (min-width: 1280px) { ... }
2xl 1536px @media (min-width: 1536px) { ... }

사용자가 직접 breakpoint 를 설정하는 것도 가능하다.

module.exports = {
theme: {
screens: {
'sm': '640px',
// => @media (min-width: 640px) { ... }
'md': '1024px',
// => @media (min-width: 1024px) { ... }
'lg': '1280px',
// => @media (min-width: 1280px) { ... }
},
}
}


💥 트러블슈팅

  1. 데이터의 변환 상태
    🔥 문제상황
    : XML데이터를 json으로 변환시키고 데이터를 불러와 작성하는 중 id를 불러와 상세페이지 정보를 나타내는 것이기 때문에 어떤 공연들은 해당 데이터가 없을 수도 있고 그로인해 에러가 발생하였다.
    ✨ 해결방법
    : api를 연결한 데이터를 zustand에서 data에 저장해왔는데 데이터를 꺼내쓰려면 {data.genrenm}이렇게 꺼내왔어야 했다.
    편리성과 데이터가 없을 때의 문제점을 해결하기 위해
 // 데이터가 유효한지 확인
  const {
    genrenm = "정보 없음",
    prfnm = "정보 없음",
    prfpdfrom = "정보 없음",
    prfpdto = "정보 없음",
    poster = "",
    area = "정보 없음",
    fcltynm = "정보 없음",
    prfstate = "정보 없음",
    pcseguidance = "정보 없음",
    prfcast = "정보 없음",
    entrpsnmH = "정보 없음",
    entrpsnmS = "정보 없음",
    prfruntime = "정보 없음",
  } = data;

로 data에 저장되어있는 값들을 구조분해할당 해와 처리해주었다.
이렇게 처리 후 데이터가 없어서 생기는 오류가 해결되었을 뿐 아니라 데이터의 정보를 꺼내쓸 때 좀 더 간편하게 쓸 수 있게 되었다.

  1. 헷갈리는 flex
    🔥 문제상황
    : tailwindCss로 작업하던 도중 mb을 쓰려고 하니 적용이 되지 않는 현상이 발생했다.
    부모컴포넌트에 flex가 있었고 이럴경우 자식 컴포넌트들 사이에서 mb가 적용이 되지 않을 수도 있다는 것을 알게되었다.
    이 문제를 해결하기 위해 flex-shrink, flex-grow를 사용하게되었다.
    ✨ 해결방법
    :
  2. flex-basis
    Flex Item의 크기를 설정한다. 이때 axis 방향으로의 크기를 설정한다.

즉 위의 상태에서 box에 flex-basis 값을 준 경우 box의 좌우 너비가 변하고, flex-direction: column인 경우 높이가 변한다.

이때, flex-shrink 속성을 0으로 설정하지 않는다면 내부 컨텐츠에 따라 유연한 크기를 갖는다.

초기값(default value)은 auto이다.

  • Flex Item의 flex-basis가 auto인 경우 width, height 속성이 우선한다.

  • Flex Item의 flex-basis가 auto가 아닌 경우, flex-basis 속성이 우선한다.

  1. flex-grow
    Flex Container에 공간이 남을 경우 Flex Item의 flex-basis 크기가 얼마나 더 할당 가능한지 나타내는 속성이다.

초기값(default value)은 0이고, 음수로의 설정은 불가능하다.

  1. flex-shrink
    flex-grow와 반대로, Flex Container에 공간이 부족해질 때 Flex Item의 axis 방향 크기가 얼마나 줄어들 수 있는지 지정하는 값이다.
<div className=" w-[600px] ">
            <div className="flex items-baseline mb-4 ">
              <p className="w-20 font-bold flex-shrink-0 mr-4">출연진: </p>
              <p className="flex-grow">{prfcast}</p>
            </div>
            <div className="flex items-baseline  mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">공연 장소: </p>
              <p className="flex-grow">
                {area} {fcltynm}
              </p>
            </div>
            <div className="flex items-baseline mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">공연 상태: </p>
              <p className="flex-grow">{prfstate}</p>
            </div>
            <div className="flex items-baseline mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">티켓 가격: </p>
              <p className="flex-grow">{pcseguidance}</p>
            </div>
            <div className="flex items-baseline mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">공연시간: </p>
              <p className="flex-grow">{prfruntime}</p>
            </div>
            <div className="flex items-baseline mb-4">
              <p className="w-20 font-bold flex-shrink-0 mr-4">주최사: </p>
              <p className="flex-grow">
                {entrpsnmH},{entrpsnmS}
              </p>
            </div>
          </div>
        </div>

0개의 댓글