[๐Ÿ ํฌ์ผ“๋ชฌ ๋„๊ฐ ๋งŒ๋“ค๊ธฐ][๊ฐœ์ธ] ๋Œ€์‹œ๋ณด๋“œ ๋ Œ๋”๋ง ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๊ณ ๋ฏผ

์ฃผ์–ด์ง„ ํฌ์ผ“๋ชฌ ์ค‘์—์„œ ์›ํ•˜๋Š” ํฌ์ผ“๋ชฌ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค.

 

ํฌ์ผ“๋ชฌ์€ ์ด 6๊ฐœ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ณ  ํฌ์ผ“๋ชฌ 3๊ฐœ๋ฅผ ์„ ํƒํ–ˆ์œผ๋ฉด 3๊ฐœ์˜ ํฌ์ผ“๋ชฌ์ด ๋ณด์ด๊ณ  ์„ ํƒํ•˜์ง€ ์•Š์€ 3๊ฐœ๋Š” ๊ธฐ๋ณธ ์ด๋ฏธ์ง€๊ฐ€ ๋ณด์—ฌ์•ผ ํ•œ๋‹ค.

 

์„ ํƒํ•œ 3๊ฐœ์™€ ์„ ํƒํ•˜์ง€ ์•Š์€ ๊ธฐ๋ณธ ์ด๋ฏธ์ง€๊ฐ€ ํ•œ ๋ฒˆ์— ๋ Œ๋”๋ง์ด ์ด๋ฃจ์–ด์ ธ์•ผ ํ•˜๋Š”๋ฐ, ์ด ๋ Œ๋”๋ง ๋ฐฉ์‹์„ ์–ด๋–ป๊ฒŒ ํ•ด์ฃผ์–ด์•ผ ํ• ์ง€ ๊ณ ๋ฏผํ•œ ๊ฒƒ์„ ์ •๋ฆฌํ•ด ๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

 

์ฒ˜์Œ ์ ‘๊ทผ ๋ฐฉ์‹๊ณผ ์ด์Šˆ

์ฒ˜์Œ ์ƒ๊ฐํ•œ ๋ฐฉ์‹์€ useState ์ดˆ๊ธฐํ™”๋ฅผ ํฌ๊ธฐ๊ฐ€ 6์ธ ๋ฐฐ์—ด๋กœ ๋งŒ๋“ค์–ด๋ฒ„๋ฆฌ๊ธฐ

 

์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ๊ฐ์˜ ์š”์†Œ๋ฅผ null ๋กœ ์ดˆ๊ธฐํ™”ํ•œ ํฌ๊ธฐ๊ฐ€ 6์ธ ๋ฐฐ์—ด์„ useState ๋กœ ์„ค์ •ํ•˜์˜€๋‹ค. (๋‹น์‹œ์—” ์ด๊ฒŒ ์ตœ์„ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•จ)

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ํ•ด๋ฒ„๋ฆฌ๋‹ˆ๊นŒ ์‚ฌ์†Œํ•œ ๋ฌธ์ œ์ ๋“ค์ด ๋ฐœ์ƒํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. ๐Ÿ˜‡

 const [selectedPokemon, setSelectedPokemon] = useState(Array(6).fill(null));

 

1. add ๋ฉ”์„œ๋“œ์˜ ๋ณต์žก์„ฑ

์•„๋ž˜ ์ฝ”๋“œ๋Š” seletedPokemon ์— ๋ฐ์ดํ„ฐ ํ•œ ๊ฐœ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

const addPokemon = (newPokemon) => {
  setSelectedPokemon((prev) => {
  
    const emptyIndex = prev.findIndex((pokemon) => pokemon === null);

    if (emptyIndex !== -1) {
      const updatePokemon = [...prev];
      updatePokemon[emptyIndex] = newPokemon;
      return updatePokemon;
    }
    
    return prev;
  });
};

 

๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ ํ•œ ๊ฐœ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋กœ์ง๋งŒ ์žˆ์–ด๋„ ๋˜๋Š”๋ฐ, ๋ฐฐ์—ด์—์„œ null ์ธ ์š”์†Œ๋ฅผ ์ฐพ๊ณ ... ๋งŒ์•ฝ null ์ธ ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์•˜์œผ๋ฉด.. ๊ทธ ์ธ๋ฑ์Šค์— newPokemon ์„ ๋„ฃ์–ด์ฃผ๊ณ  ์žˆ๋‹ค. ๋ญ”๊ฐ€ ๋ณต์žกํ•˜๋‹ค.

๋ฌผ๋ก  ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๋กœ์ง์€ ์ถฉ๋ถ„ํžˆ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ํ˜„์žฌ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์€ ์–ด๋Š ์กฐ๊ฑด ์—†์ด ๊ทธ๋ƒฅ '์ถ”๊ฐ€' ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ state ์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ธฐ๋Šฅ์ผ ๋ฟ์ด๋‹ค. 

 

2. ์ œ๊ฑฐํ•  ๋•Œ null ์˜ค๋ฅ˜ 

์•„๋ž˜ ์ฝ”๋“œ๋Š” ์‚ญ์ œ ํ•จ์ˆ˜์ด๋‹ค.

  const removePokemon = (pokemon) => {
    const updatePokemon = selectedPokemon.filter((p) => {
      return p.id !== pokemon.id;
    });
    setSelectedPokemon(updatePokemon);
  };

 

์‚ญ์ œํ•˜๋ ค๋Š” ๋ฐ์ดํ„ฐ์˜ id ์™€ ๋‹ค๋ฅธ ๊ฒƒ๋“ค์„ ํ•„ํ„ฐํ•˜๊ณ  ํ•„ํ„ฐ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ set ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ์ด ๊ธฐ๋Šฅ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

 

์•„๋ž˜๋Š” ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์ด๋‹ค.

Dex.jsx:42 Uncaught TypeError: Cannot read properties of null (reading 'id')
    at Dex.jsx:42:16
    at Array.filter (<anonymous>)
    at removePokemon (Dex.jsx:41:43)
    at onClick (PokemonCard.jsx:22:13)
    at HTMLUnknownElement.callCallback2 (chunk-M324AGAM.js?v=a49fcf27:3672:22)
    at Object.invokeGuardedCallbackDev (chunk-M324AGAM.js?v=a49fcf27:3697:24)
    at invokeGuardedCallback (chunk-M324AGAM.js?v=a49fcf27:3731:39)
    at invokeGuardedCallbackAndCatchFirstError (chunk-M324AGAM.js?v=a49fcf27:3734:33)
    at executeDispatch (chunk-M324AGAM.js?v=a49fcf27:7012:11)
    at processDispatchQueueItemsInOrder (chunk-M324AGAM.js?v=a49fcf27:7032:15)

 

ํฌ์ผ“๋ชฌ์„ ํ•œ ๊ฐœ๋ผ๋„ ์„ ํƒํ•˜์ง€ ์•Š์€ ์ƒํƒœ์ด๋ฉด selectedPokemon ๋ฐฐ์—ด์„ ์ˆœํšŒํ•  ๋•Œ null ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋ฌผ๋ก  null ์ผ ๊ฒฝ์šฐ ์–ด์ฐŒ์ €์ฐŒํ•ด์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜์ง€ ์•Š๋„๋ก ํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ ๋ญ”๊ฐ€ ๋ณต์žกํ•˜๊ณ  ๋ง˜์— ์•ˆ ๋“ ๋‹ค. 

 

์ˆ˜์ •ํ•œ ๋ฐฉ์‹

... ๊ณ ๋ฏผ๋์— ๋ฐฉ๋ฒ•์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ˆ˜์ •ํ•œ ๋ฐฉ์‹์€ PokemonList ์™€ PokemonCard ์—์„œ ๋ถ„๊ธฐ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. 

 

์šฐ์„  ๋ฌธ์ œ์˜ ์‹œ์ž‘์ ์ธ useState ์ดˆ๊ธฐํ™” ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค. 

const [selectedPokemon, setSelectedPokemon] = useState([]);

 

๋ฆฌ์ŠคํŠธ ์ถœ๋ ฅ์€ ์„ ํƒํ•œ ํฌ์ผ“๋ชฌ ๋”ฐ๋กœ ์„ ํƒํ•˜์ง€ ์•Š์€ ํฌ์ผ“๋ชฌ ๋”ฐ๋กœ ๋ Œ๋”๋ง ๋˜๋„๋ก ํ–ˆ๊ณ  ์„ ํƒํ•˜์ง€ ์•Š์€ ํฌ์ผ“๋ชฌ์€ ๊ธธ์ด๋ฅผ 6์œผ๋กœ ์ •ํ•ด๋‘๊ณ  ์„ ํƒํ•œ ํฌ์ผ“๋ชฌ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋บ€ ๊ฐœ์ˆ˜๋ฅผ ๋ Œ๋”๋งํ•˜๋„๋ก ํ–ˆ๋‹ค.

return (
  <StyledSection>
    <h1>๋‚˜๋งŒ์˜ ํฌ์ผ“๋ชฌ</h1>

    <StyledDiv>
      {selectedPokemon.map((pokemon) => {
        return (
          <PokemonCard
            key={pokemon.id}
            pokemon={pokemon}
            removePokemon={removePokemon}
            isSelected={true}
          />
        );
      })}

      {Array.from({ length: 6 - selectedPokemon.length }).map((_, index) => (
        <PokemonCard key={index} />
      ))}
    </StyledDiv>
  </StyledSection>
);

 

ํฌ์ผ“๋ชฌ ์นด๋“œ๋Š” ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ–ˆ๋‹ค. props ๋กœ ์ „๋‹ฌ๋ฐ›์€ pokemon ์ด ์žˆ์œผ๋ฉด ์„ ํƒํ•œ ํฌ์ผ“๋ชฌ ์นด๋“œ๊ฐ€ ๋ Œ๋”๋ง๋˜๊ณ , pokemon ์ด ์ „๋‹ฌ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ์„ ํƒํ•˜์ง€ ์•Š์€ ์นด๋“œ๊ฐ€ ๋ Œ๋”๋ง ๋˜๋„๋ก ํ–ˆ๋‹ค. 

return pokemon ? (
  <StyledDiv
    onClick={() => {
      navigate(DETAIL_URL + "/" + pokemon.id);
    }}
  >
    <img src={pokemon.img_url} />
    <span>{pokemon.korean_name}</span>
    <span>No.{pokemon.id}</span>

    {isSelected ? (
      <button
        onClick={(e) => {
          e.stopPropagation();
          removePokemon(pokemon);
        }}
      >
        {"์‚ญ์ œ"}
      </button>
    ) : (
      <button
        onClick={(e) => {
          e.stopPropagation();
          addPokemon(pokemon);
        }}
      >
        {"์ถ”๊ฐ€"}
      </button>
    )}
  </StyledDiv>
) : (
  "์—†์Œ"
);

 

๋ณ€๊ฒฝํ•œ ๋ฐฉ์‹์ด ์ตœ์„ ์€ ์•„๋‹์ง€๋ผ๋„.. ์ด์ „๋ณด๋‹ค ๊น”๋”ํ•ด์ง„ ๋Š๋‚Œ์ด๋‹ค.