- 배포 링크 : 포켓몬 도감 링크
포켓몬 1008개체에 대한 정보, 능력치, 설명을 적어놓은 포켓몬 사전 사이트 입니다.
사이트는 로그인 페이지, 메인 페이지, 포켓몬 상세 페이지로 이뤄져 있습니다.
로그인 페이지 : FireBase를 이용해 Google 로그인을 할 수 있습니다.
메인 페이지 : 원하는 포켓몬을 검색할 수 있습니다.
: 프로필을 눌러 로그아웃을 할 수 있습니다.
포켓몬 상세 페이지 : 포켓몬의 정보, 능력치, 설명을 확인할 수 있습니다.
: 포켓몬 이미지를 클릭하면 포켓몬 속성에 관한 데미지 관계가 나옵니다.
| Login 페이지 | |
|---|---|
![]() |
|
| Main 페이지 | Pokemon Detail 페이지 |
![]() |
![]() |
| Vite | TailwindCSS | Javascript | Netlify |
|---|---|---|---|
| StyledComponent | PostCSS | Typescript | Firebase |
페이지 전환을 도와주는 라이브러리입니다.
import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom"
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<MainPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/pokemon/:id" element={<DetailPage />} />
</Route>
</Routes>
</BrowserRouter>연속적인 이벤트가 발생했을 때 마지막 이벤트만 적용되게끔 해주는 Hook입니다.
//훅 생성
export const useDebounce = (value, delay) => {
const [debounceValue, setDebounceValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebounceValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debounceValue;
};
export default useDebounce;
//사용
import useDebounce from "./hooks/useDebounce";
const debouncedSearchTerm = useDebounce(searchTerm, 500);렌더링에 필요하지 않은 값을 참조할 수 있게 해주는 Hook입니다.
import { useRef } from "react";
//DOM요소를 선택하기 위해 사용된 useref
const DamageModal = ({ setIsModalOpen, damages }: DamageModalProps) => {
const ref = useRef<HTMLDivElement>(null);
useOnclickOutside(ref, () => setIsModalOpen(false));
return()
}백엔드 부분(유저 인증, 데이터 통신 등)을 지원해주는 서비스입니다.
//firebase로부터 인증에 관련된 기능들을 불러옴
import {
User,
getAuth,
signInWithPopup,
GoogleAuthProvider,
onAuthStateChanged,
signOut,
} from "firebase/auth";
import app from "../firebase";
const auth = getAuth(app);
const provider = new GoogleAuthProvider();
// --구글 로그인 정보 가져오기--
const handleAuth = () => {
signInWithPopup(auth, provider)
.then((result) => {
console.log(result);
setUserData(result.user);
storage.set("userData", result.user);
//localStorage.setItem("userData", JSON.stringify(result.user));
//유저 정보를 로컬에 저장하고 싶을시 사용
})
.catch((error) => {
console.error(error);
});
};
//--로그아웃--
const handleLogOut = () => {
signOut(auth)
.then(() => {
// localStorage.removeItem('userData');
//로컬 유저 정보를 삭제 시 사용
storage.remove("userData");
setUserData(null);
})
.catch((error) => {
alert(error.message);
});
};불러온 이미지들을 바로 보여주는게 아니라, 화면에 보일 필요가 있을때만 로딩을 할 수 있도록 하는 테크닉입니다.
추가로 ...Loading 이나 이미지를 사용하여 api에서 포켓몬 이미지 등이 아직 불러와 지지 않았음을 표기해 주었습니다.
다음과 같은 방법으로 image lazy loading를 구현할 수 있습니다.
- JavaScript 이벤트를 이용해 구현.
- Intersection Observer API를 이용해서 구현.
- 브라우저 Native Lazy Loading (loading 속성)을 이용해서 구현.
/* Native Lazy Loading 방법으로 image lazy loading을 구현했습니다.
img 태그에 loading 속성을 추가합니다.*/
const LazyImage = ({ url, alt }: LazyImageProps) => {
// isLoading이 true면 로드 중. ( loading 이미지 표기 )
const [isLoading, setisLoading] = useState<boolean>(true);
return (
<>
{
isLoading && (
<div>
...loading
</div>
)
}
<img loading='lazy'
onLoad={() => setisLoading(false)} />
</>
)
}메인 페이지에서 포켓몬을 검색할 때, 검색중인 포켓몬과 관련된 이름들을 드롭다운으로 표기해 줍니다.
/* STEP1. 자동완성 될 포켓몬의 이름을 모두 갖고 있어야 하므로, 렌더링과 별개로 모든 포켓몬 데이터를 가져와 useState 변수에 저장합니다.
검색과 관련된 api가 있다면 그 api를 가져오면 됩니다. */
// 모든 포켓몬 데이터
const [allPokemons, setAllPokemons] = useState<PokemonNameAndUrl[]>([]);
// 렌더링 되는 포켓몬 데이터
const [displayedPokemons, setDisplayedPokemons] = useState<PokemonNameAndUrl[]>([]);
/* STEP2 상황에 맞게 AutoComplete 기능을 완성합니다. */
const AutoComplete = ({ allPokemons, setDisplayedPokemons }: AutoCompleteProps) => {
// 검색 부분
const [searchTerm, setSearchTerm] = useState("");
// 조건1. 검색한 단어를 모두 소문자로 바꿔주고 filter를 통해 allPokemon에서 검색단어가 포함된 Pokemon들을 불러오기.
const filterNames = (input: string) => {
const value = input.toLowerCase();
return value ? allPokemons.filter((e) => e.name.includes(value)) : [];
};
// 조건2. 검색버튼을 누르면 검색기능 가동, 검색 부분 초기화.
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); // 새로고침 방지
let text = searchTerm.trim();
setDisplayedPokemons(filterNames(text));
setSearchTerm("");
};
// 조건3. 검색한 것과 정확히 일치하는 포켓몬이 없다면 autocomplete 없애기.
const checkEqualName = (input: string) => {
const filteredArray = filterNames(input);
return filteredArray[0]?.name === input ? [] : filteredArray;
};
return (
...
)
}포켓몬 상세 페이지에서 내가 클릭한 포켓몬 속성의 데미지 관계를 Modal로 불러옵니다.
// 모달 오픈 여부 useState 생성.
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
// 모달 open 조건 생성.
<img ...
onClick={() => setIsModalOpen(true)}
/>
// 모달 close 조건 생성.
// X 버튼을 눌렀을 때
<span
onClick={() => setIsModalOpen(false)}
className=""
>
X
</span>
// 모달 외 배경을 눌렀을 때 (useRef)
const ref = useRef<HTMLDivElement>(null);
<div ref={ref}> </div> // 모달 바깥쪽 div에 ref 속성 부여.JavaScript Object에서 Key의 Type을 자동으로 생성해주는 사이트 입니다.
JSON 데이터를 기반으로 Type을 지정하기 때문에 사이트에 JSON 파일로 제공해야 합니다.
function formatPokemonData(params: PokemonDetail) {
console.log("Quick Type에 넣을 JSON 파일 만들기", JSON.stringfy(params))
const { id, types, name } = params;
const PokeData: PokeData = {
id,
name,
type: types[0].type.name,
};
return PokeData;
}
// 결과는 PokeDex-App/src/types/PokemonDetail.ts 파일.정의된 타입을 사용할 수 있도록 도움을 주는 기능입니다.
// uuid에 대한 타입을 받기 위해 사용
npm install --save-dev @types/uuid

