[TIL] 240701 (Next.js 개인과제 Image 태그 오류, 서버 컴포넌트 params 문제 해결)

·2024년 7월 1일

TIL

목록 보기
86/268
post-thumbnail

🥞 오늘 한 일

  • 넥스트 개인과제
    • 필수 구현 사항
      • 프로젝트 셋업
      • Layout 구성
      • /pokemons api 구성
      • PokemonList 컴포넌트 작성
      • /pokemons/[id] api 구성
      • PokemonDetail 컴포넌트 작성
  • 스탠다드반 수업
    • 타입스크립트 과제 코멘트
    • 타입스크립트 복습#2 - 타입스크립트 심화
  • 알고리즘 코드카타
    • 수업 (문제 소개)
    • week3 - ChangeListOrder 풀이

넥스트 개인과제

NextJS 를 이용해 나만의 포켓몬 도감을 만드는 과제였다. 일단 필수 구현 사항들은 거진 다 완료된 상태이지만, 아직 UI 부분을 신경쓰지 않았고 디테일하게 손 볼 부분이 많아 해당 부분을 진행해야할 것 같다.

🍽️ 트러블 슈팅

넥스트 개인과제

Image 오류

문제

과제의 필수 구현 사항으로, Next.js 내의 Image 태그를 사용해 이미지를 넣으려는데 계속 오류가 뜨는 문제가 있었다.

해결

공식 문서 및 구글링을 통해, 외부에서 가져온 이미지 경로에 대해서는 next.config.js 파일에 이미지의 경로에 대한 정보를 작성해주어야한다는 점을 알게 되었다. 때문에 next.config.js 파일을 제작하고 내가 사용하는 이미지 경로에 대한 정보를 작성해주었다.

// next.config.js

module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "raw.githubusercontent.com",
        port: "",
        pathname: "/PokeAPI/sprites/master/sprites/pokemon/**",
      },
    ],
  },
};

문제가 해결되었다.

서버 컴포넌트에서 params 가져오기

필수 조건 중 디테일 페이지에서는 무조건 서버 컴포넌트로 구현을 해야하는 조건이 있어, 여러가지 방법을 통해 파라미터를 가져오는 방법을 찾아보았다.
그러던 중 getServerSideProps같은 방법도 찾아 적용시켜보려 했으나 난이도가 높아 이게 맞는지 의구심이 들어 튜터님을 찾아뵀고, getServerSideProps가 아닌 더 쉬운 방법을 힌트로 알려주셨다. 아래는 그렇게 알게된 방법으로 작성한 코드이다.

const PokemonDetailPage = ({ params }: { params: { id: string } }) => {
  const { id } = params;
  return (
    <div>
      <PokemonDetail id={id} />
    </div>
  );
};

매개변수에 {params}만으로도 가져올 수 있다. next 14 버전부터 적용된 기능이라고 하시는데, 나는 이걸 알고나서 다시 찾아보아도 찾아지지가 않는(심지어 공식 문서에서도 못 찾겠는...) 문제가 있어서 공식 문서 보는 능력 및 구글링하는 능력을 더 길러야겠다는 생각이 들었다.

알고리즘 코드카타

리스트 순서 바꾸기

문제

'위로', '아래로' 버튼 클릭 시, 즉각적으로 리스트의 순서가 바뀔 수 있게 해주세요.
맨 위 아이템, 맨 아래 아이템은 더 이상 위로, 아래로 가지 못하게 해주셔야 합니다!

풀이

풀이는 코드 속에 작성했다.

import { useState } from "react";
import { MOCK_DATA } from "./MOCK_DATA.js";

const ChangeListOrder = () => {
  const [pokemonData, setPokemonData] = useState(MOCK_DATA);

  // TODO '위로' 버튼을 눌렀을 때, 실행되는 로직을 작성합니다. 첫 번째 아이템은 위로 이동 할 수 없음을 기억해주세요!
  const moveItemUp = (selectedIndex) => {
    // 첫 번째 아이템일 경우 return을 통해 아무 코드도 실행할 수 없게 한다.
    if (selectedIndex === 0) {
      return;
    }
    // 선택된 아이템과 바로 전 아이템을 찾아서 새로운 배열로 만들고, 해당 배열을 reverse한다.
    const filteredPokemons = pokemonData
      .filter((pokemon, index) => {
        return selectedIndex === index || selectedIndex - 1 === index;
      })
      .reverse();
    // 전체 배열을 map 하여, 직전 인덱스에는 필터링한 배열의 첫번째 요소(선택된 아이템)를, 선택된 인덱스에는 두번째 요소(직전 아이템)을 넣는다.
    // 해당 두 경우가 아닐 경우에는 그대로 return 한다.
    // 해당 배열을 pokemonData에 적용한다.
    setPokemonData(
      pokemonData.map((pokemon, index) => {
        if (index === selectedIndex - 1) {
          return filteredPokemons[0];
        } else if (index === selectedIndex) {
          return filteredPokemons[1];
        }
        return pokemon;
      })
    );
  };

  // TODO '아래' 버튼을 눌렀을 때, 실행되는 로직을 작성합니다. 마지막 아이템은 아래로 이동 할 수 없음을 기억해주세요!
  const moveItemDown = (selectedIndex) => {
    // 마지막 아이템일 경우 return을 통해 아무 코드도 실행할 수 없게 한다.
    if (selectedIndex === pokemonData.length - 1) {
      return;
    }
    // 선택된 아이템과 바로 후 아이템을 찾아서 새로운 배열로 만들고, 해당 배열을 reverse한다.
    const filteredPokemons = pokemonData
      .filter((pokemon, index) => {
        return selectedIndex === index || selectedIndex + 1 === index;
      })
      .reverse();
    // 전체 배열을 map 하여, 선택된 인덱스에는 필터링한 배열의 첫번째 요소(직후 아이템)를, 직후 인덱스에는 두번째 요소(선택된 아이템)을 넣는다.
    // 해당 두 경우가 아닐 경우에는 그대로 return 한다.
    // 해당 배열을 pokemonData에 적용한다.
    setPokemonData(
      pokemonData.map((pokemon, index) => {
        if (index === selectedIndex) {
          return filteredPokemons[0];
        } else if (index === selectedIndex + 1) {
          return filteredPokemons[1];
        }
        return pokemon;
      })
    );
  };

  return (
    <div className="container mx-auto">
      <h2 className="w-full text-center py-10">리스트 순서 바꾸기</h2>
      <div className="flex flex-col gap-2">
        {/* TODO Index 도 필요하다면, 수정해주세요 */}
        {pokemonData.map((pokemon, index) => (
          <div
            key={pokemon.id}
            className="pokemon p-4 border rounded-lg flex justify-between"
          >
            <div>
              <img
                src={pokemon.sprites.front_default}
                alt={pokemon.korean_name}
              />
              <p>{pokemon.korean_name}</p>
              <p>도감번호: {pokemon.id}</p>
            </div>
            <div className="flex gap-5 items-center">
              {/* TODO moveItemUp 함수에 매개변수로 넣어주고 싶은게 있으시면 추가 시키셔도 됩니다. */}
              <button
                className="bg-brand h-10 p-2 rounded text-[#ffffff] font-bold"
                onClick={() => moveItemUp(index)}
              >
                위로
              </button>
              {/* TODO moveItemDown 함수에 매개변수로 넣어주고 싶은게 있으시면 추가 시키셔도 됩니다. */}
              <button
                className="bg-state-warning h-10 p-2 rounded text-[#ffffff] font-bold"
                onClick={() => moveItemDown(index)}
              >
                아래로
              </button>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default ChangeListOrder;

🍴 돌아보기

주말 간 강의를 모두 수강했기 때문에 좀 더 빠르게 과제를 진행할 수 있었던 것 같다. 때문인지 탄력을 받아 필수 구현 사항은 거의 완성되었다. 역시 무리하지 않는 선에서는 미리미리 해둬서 나쁠 게 없다! 이 기세로 쭉쭉 진행해야겠다.

🍳 내일 목표

  • 넥스트 개인과제
    • UI 제작
    • 선택 구현 사항
      • tanstack query 캐시 처리
profile
웹 프론트엔드 개발자

2개의 댓글

comment-user-thumbnail
2024년 7월 1일

챡챡챡 잘 해내고 계시군여 솔님 ! 앞으로의 행보도 응원할게요 (˶• ﻌ •˶)

1개의 답글