Inflearn brand logo image

인프런 커뮤니티 질문&답변

안연수님의 프로필 이미지
안연수

작성한 질문수

한 번에 끝내는 자바스크립트: 바닐라 자바스크립트로 SPA 개발까지

강의를 마치면서

최종프로젝트 상세페이지 출력 오류 문의드립니다!

해결된 질문

작성

·

64

·

수정됨

0

세션 9. 최종 프로젝트

  1. cityDetail 개발-2 에서
    https://trip-wiki-api.vercel.app/ 강사님이 알려주셨던 링크에는 상세페이지 이미지와 받는 정보가 없습니다! 어떻게 상세페이지를 출력할 수 있나요? Japan을 검색창에서 검색하고 상세페이지를 눌렀지만 아무런 정보가 출력되지 않고 있습니다!

     

    화면 캡처 2025-06-16 123714.png.webp화면 캡처 2025-06-16 123741.png.webp화면 캡처 2025-06-16 123913.png.webp
    import Header from "./components/Header.js";
    import RegionList from "./components/RegionList.js";
    import CityList from "./components/CityList.js";
    import CityDetail from "./components/CityDetail.js";
    
    import { request, requestCityDetail } from "./components/api.js";
    
    export default function App($app) {
      const getSortBy = () => {
        if (window.location.search) {
          return window.location.search.split("sort=")[1].split("&")[0];
        }
        return "total";
      };
      const getSearchWord = () => {
        if (window.location.search && window.location.search.includes("search=")) {
          return window.location.search.split("search=")[1];
        }
        return "";
      };
    
      this.state = {
        startIdx: 0,
        sortBy: getSortBy(),
        region: window.location.pathname.replace("/", ""),
        searchWord: getSearchWord(),
        cities: "",
        currentPage: window.location.pathname,
      };
    
      const renderHeader = () => {
        new Header({
          $app,
          initialState: {
            currentPage: this.state.currentPage,
            sortBy: this.state.sortBy,
            searchWord: this.state.searchWord,
          },
          handleSortChange: async (sortBy) => {
            const pageUrl = `/${this.state.region}?sort=${sortBy}`;
            history.pushState(
              null,
              null,
              this.state.searchWord
                ? pageUrl + `&search=${this.state.searchWord}`
                : pageUrl
            );
            const cities = await request(
              0,
              this.state.region,
              sortBy,
              this.state.searchWord
            );
            this.setState({
              ...this.state,
              startIdx: 0,
              sortBy: sortBy,
              cities: cities,
            });
          },
          handleSearch: async (searchWord) => {
            history.pushState(
              null,
              null,
              `/${this.state.region}?sort=${this.state.sortBy}&search=${searchWord}`
            );
            const cities = await request(
              0,
              this.state.region,
              this.state.sortBy,
              searchWord
            );
            this.setState({
              ...this.state,
              startIdx: 0,
              cities: cities,
              searchWord: searchWord,
            });
          },
        });
      };
    
      const renderRegionList = () => {
        new RegionList({
          $app,
          initialState: this.state.region,
          handleRegion: async (region) => {
            history.pushState(null, null, `/${region}?sort=total`);
            const cities = await request(0, region, "total");
            this.setState({
              ...this.state,
              startIdx: 0,
              sortBy: "total",
              region: region,
              cities: cities,
              searchWord: "",
              currentPage: `/${region}`,
            });
          },
        });
      };
    
      const renderCityList = () => {
        new CityList({
          $app,
          initialState: this.state.cities,
          handleItemClick: async (id) => {
            history.pushState(null, null, `/city/${id}`);
            this.setState({
              ...this.state,
              currentPage: `/city/${id}`,
            });
          },
          handleLoadMore: async () => {
            const newStartIdx = this.state.startIdx + 40;
            const newCities = await request(
              newStartIdx,
              this.state.region,
              this.state.sortBy
            );
            this.setState({
              ...this.state,
              startIdx: newStartIdx,
              cities: {
                ...this.state.cities,
                cities: [...this.state.cities.cities, ...newCities.cities],
                isEnd: newCities.isEnd,
              },
            });
          },
        });
      };
      const renderCityDetail = async (cityId) => {
        try {
          const cityDetailData = await requestCityDetail(cityId);
          new CityDetail({ $app, initialState: cityDetailData });
        } catch (err) {
          console.log(err);
        }
      };
    
      const render = async () => {
        const path = this.state.currentPage;
        $app.innerHTML = "";
        // 상세 페이지로 이동
        if (path.startsWith("/city/")) {
          const cityId = path.split("/city/")[1];
          renderHeader();
          renderCityDetail(cityId);
        } else {
          renderHeader();
          renderRegionList();
          renderCityList();
        }
      };
    
      this.setState = (newState) => {
        this.state = newState;
        render();
      };
    
      const init = async () => {
        const path = this.state.currentPage;
        // 메인 페이지
        if (!path.startsWith("/city")) {
          const cities = await request(
            this.state.startIdx,
            this.state.region,
            this.state.sortBy,
            this.state.searchWord
          );
          this.setState({
            ...this.state,
            cities: cities,
          });
        } // 상세페이지
        else {
          render();
        }
      };
    
      window.addEventListener("popstate", async () => {
        const urlPath = window.location.pathname;
    
        const prevRegion = urlPath.replace("/", "");
        const prevPage = urlPath;
        const prevSortBy = getSortBy();
        const prevSearchWord = getSearchWord();
        const prevStartIdx = 0;
        const prevCities = await request(
          prevStartIdx,
          prevRegion,
          prevSortBy,
          prevSearchWord
        );
    
        this.setState({
          ...this.state,
          startIdx: prevStartIdx,
          sortBy: prevSortBy,
          region: prevRegion,
          currentPage: prevPage,
          searchWord: prevSearchWord,
          cities: prevCities,
        });
      });
    
      init();
    }
    
    //API.js 코드입니다!
    export default function CityDetail({ $app, initialState }) {
      this.state = initialState;
      this.$target = document.createElement("div");
      this.$target.className = "city-detail";
    
      $app.appendChild(this.$target);
    
      const getScoreColor = (score) => {
        // let scoreNumber = parseInt(score);
        if (score >= 4) return "green";
        if (score >= 3) return "yellow";
        return "red";
      };
    
      this.template = () => {
        let cityData = this.state.CityDetail;
        let temp = ``;
        if (cityData) {
          temp = `<div class="image-banner">
            <img src="${cityData.image}"/>
            <div class="city-name">
              <div class="city">${cityData.city}</div>
              <div class="country">${cityData.region} / ${cityData.country}</div>
            </div>
          </div>
          <div class="progress-container">
            <div class="info-item">
              <div class="label">⭐ Total Score</div>
              <div class="progress-bar" score-color="${getScoreColor(
                cityData.total
              )}" style="--score:${cityData.total * 20}%"></div>
            </div>
              <div class="info-item">
              <div class="label">💵 Cost</div>
              <div class="progress-bar" score-color="${getScoreColor(
                cityData.info.cost
              )}" style="--score:${cityData.info.cost * 20}%"></div>
            </div>
              <div class="info-item">
              <div class="label">😆 Fun</div>
              <div class="progress-bar" score-color="${getScoreColor(
                cityData.info.fun
              )}" style="--score:${cityData.info.fun * 20}%"></div>
            </div>
              <div class="info-item">
              <div class="label">🚓 Safety</div>
              <div class="progress-bar" score-color="${getScoreColor(
                cityData.info.safety
              )}" style="--score:${cityData.info.safety * 20}%"></div>
            </div>
              <div class="info-item">
              <div class="label">🌐 Internet</div>
              <div class="progress-bar" score-color="${getScoreColor(
                cityData.info.internet
              )}" style="--score:${cityData.info.internet * 20}%"></div>
            </div>
            <div class="info-item">
              <div class="label">💨 Air Condition</div>
              <div class="progress-bar" score-color="${getScoreColor(
                cityData.info.air
              )}" style="--score: ${cityData.info.air * 20}%"></div>
            </div>
            <div class="info-item">
              <div class="label">🍜 Food</div>
              <div class="progress-bar" score-color="${getScoreColor(
                cityData.info.food
              )}" style="--score: ${cityData.info.food * 20}%"></div>
            </div>
          </div>
          `;
        }
        return temp;
      };
    
      this.render = () => {
        this.$target.innerHTML = this.template();
      };
    
      /*
      this.setState = (newState) => {
        this.state = newState;
        this.render();
      };
      */
    
      this.render();
    }
    //cityDetail.js 상세페이지 코드입니다!

답변 2

0

효빈 Hyobin님의 프로필 이미지
효빈 Hyobin
지식공유자

안녕하세요 안연수님 🙂

https://trip-wiki-api.vercel.app/ 강사님이 알려주셨던 링크에는 상세페이지 이미지와 받는 정보가 없습니다!

라고 해주셨는데요, [9.최종프로젝트-CityDetail 개발-1] 강의에서 상세페이지 정보를 어떻게 받는지 소개하고 있으니, 해당 강의를 다시 수강해보시는 것을 추천드립니다.

강의를 다시 수강해보신 다음, api.js에 알맞은 api 호출 코드를 작성해주시면 잘 동작할 것 같습니다! 그럼에도 오류가 발생한다면, 다시 질문 작성해주세요. 감사합니다!

0

안녕하세요, 인프런 AI 인턴입니다. 안연수님. 질문 주셔서 감사합니다.

현재 작성하신 코드에서 “상세 페이지 출력 오류” 문제를 겪고 계신 것으로 보이네요. 이는 여러 가지 원인으로 인해 발생할 수 있습니다. 아래에 몇 가지 확인할 수 있는 사항과 해결 방법을 제공하겠습니다.

  1. API의 응답 데이터 확인:

    • requestCityDetail 함수 호출 시 반환되는 데이터가 올바른지, 콘솔에서 확인해보세요. 이를 통해 서버에서 데이터를 제대로 받아오고 있는지 확인할 수 있습니다.
    • 콘솔에서 console.log(cityDetailData)를 추가하여 얻어오는 데이터가 무엇인지 반드시 확인하세요.
  2. API 요청 URL:

    • requestCityDetail 함수 내의 API 요청 URL이 정확한지 확인해 주세요. 현재 사용 중인 API가 유효한지를 체크해야 합니다. API가 잘못 설정되면 데이터가 올바르게 로딩되지 않습니다.
  3. 데이터 구조 및 바인딩:

    • CityDetail 컴포넌트 내 this.state.CityDetail이 올바른 데이터 구조를 가지고 있는지, 데이터가 존재하는지 확인해보세요.
    • API로부터 받아오는 데이터의 구조가 CityDetail 컴포넌트가 기대하는 것과 같은지 확인하고 코드에서 올바르게 바인딩되었는지 확인해주시기 바랍니다.
  4. 연결 문제:

    • renderCityDetail 함수에서 새로고침 후에도 정상적으로 데이터를 렌더링할 수 있도록 이벤트 핸들러와 관련된 코드가 잘 연결되어 있는지 확인하세요.
  5. 에러 핸들링:

    • 코드를 디버깅하는 과정에서 try-catch 구문을 사용하여 발생 가능한 에러를 잡고 적절한 에러 메시지를 추가하여 디버깅하세요.

이를 해결하는 과정에서 API 요청 부분과 데이터 가공 로직을 특히 주의 깊게 살펴봐야 합니다. 아래는 전반적인 안내 링크입니다:

문제 해결에 도움이 되기를 바라며, 이러한 점검 후에도 문제가 해결되지 않으면 코드 및 세부 사항을 공유해 주세요. 추가적인 도움을 드릴 수 있도록 하겠습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드립니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

안연수님의 프로필 이미지
안연수
질문자

// https://trip-wiki-api.vercel.app

const API_URL = "https://trip-wiki-api.vercel.app/";

export const request = async (startIdx, region, sortBy, searchWord) => {
  try {
    let url = `${API_URL}`;
    if (region && region !== "All") {
      url += `${region}?start=${startIdx}`;
    } else {
      url += `?start=${startIdx}`;
    }
    if (sortBy) {
      url += `&sort=${sortBy}`;
    }
    if (searchWord) {
      url += `&search=${searchWord}`;
    }
    console.log(url);
    // API 호출
    const response = await fetch(url);
    if (response) {
      let data = await response.json();
      console.log(data);
      return data;
    }
  } catch (err) {
    console.log(err);
  }
};

// DETAIL API
export const requestCityDetail = async (cityId) => {
  try {
    const response = await fetch(`${API_URL}city/${cityId}`);
    if (response) {
      let data = await response.json();
      console.log(data);
      return data;
    }
  } catch (err) {
    console.log(err);
  }
};
// api.js 코드입니다

api.js 코드에 대한 Detail API 부분 코드 에러 문제에 대해서 문의드립니다! 혹시 잘못된 코드가 있나요? 어떠한 이유로 Detail API 코드 부분이 인식되지 않는 지 문의드립니다!

 

 

안연수님의 프로필 이미지
안연수

작성한 질문수

질문하기