타입을 설정하는 interface를 모두 한 파일로 모아서 util 폴더에 typeCollection 으로 모아두었는데 멘토님께서 보시고 아래와 같이 조언해주셨다.
한 파일로 묶는 건 좋지만 다른 곳에서 사용하지 않는 type이라면 사용하는 폴더 최상단에 그냥 적는 게 관리하기 편할 것 같다. 다른 곳에서 사용되는 type이라면 묶어두는 것도 좋다. (좀 사람 by 사람의 문제인 거 같다고 말씀하시기도 했다.)
api 응답 type과 view의 props type은 분리하자. (모아두는 파일을 분리하는 것도 포함되고, 아래와 같이 propsType을 apiRes 에 가져와 쓰는 것도 포함된다. 둘을 분리하지 않는 경우라면 같이 써도 상관은 없지만 그런 경우는 정말 드물다.) (ex: propsType을 후에 절대 수정하지 않는 경우라던가)
export interface apiRes{
data: propsType [];
ex1: string;
ex2: number;
...
}
export interface propsType{
props1: ...
}
category 같은 걸 만들 때 array를 사용해서 만들었는데... 솔직히 썩 좋은 방법은 아니라는 생각이 들어서 멘토님께 여쭤봤다.
//before //login.tsx
const signin = () => {
const user = userArr.find(
(user) => user.email === userId && user.password === password
);
if (user === undefined) {
alert("찾을 수 없는 사용자입니다.");
return;
}
alert("로그인 완료!");
setIsLogin(true);
navigator("/");
localStorage.setItem(
"accessToken",
`${process.env.REACT_APP_ACCESS_TOKEN}`
);
return user;
};
//arrayCollection.tsx
export const userArr= [
{ email: "codestate1", password: "12345678" },
{ email: "codestate2", password: "87654321" },
];
//after //login.tsx
interface loginType {
[key: string]: string;
}
const signIn = () => {
const hash: loginType = {
codestate1: "12345678",
codestate2: "87654321",
};
const user = hash[userId] === password ? true : false;
if (!user) {
console.log(`userId: ${hash[userId]} // password: ${password}`);
alert("사용자를 찾을 수 없습니다.");
return;
} else {
console.log(`userId: ${hash[userId]} // password: ${password}`);
alert("로그인 완료!");
setIsLogin(true);
navigator("/");
localStorage.setItem(
"accessToken",
`${process.env.REACT_APP_ACCESS_TOKEN}`
);
}
return user;
};
//arrayCollection.tsx
... 코드 지움
전에 작성했던 코드는 사용자가 입력한 userId와 password를 useState로 저장하고, userArray와 같은 지 비교하는 코드였다.
수정한 코드는 hash의 사용자가 입력한 userId가 있다면, 사용자가 입력한 password와 해당하는 value값이 같을 때 true, 틀리면 false를 리턴한다. 사용자가 입력한 userId가 hash에 없다면 undefined를 리턴한다.
//before //apiCollection.tsx
export const getNewsDataAPI = async (
q: string,
date: string,
country: string,
page_size: number,
offset: number,
sort: string,
topic: string,
signal: any
) => {
try {
const res = await axios.get(
`https://api.newscatcherapi.com/v2/search?q=${q}&from=${date}&countries=${country}&page_size=${page_size}&page=${offset}&sort_by=${sort}&topic=${topic}`,
{
headers: {
"x-api-key": `${process.env.REACT_APP_API_KEY}`,
},
signal,
}
);
return res;
} catch (err: unknown) {
return console.error(err);
}
};
//after //apiCollection.tsx
type GetNewsDataApiParams = {
keyword: string;
date: string;
country: string;
pageSize: number;
offset: number;
sort: string;
topic: string;
signal: any;
};
export const getNewsDataAPI = async ({
keyword,
date,
country,
pageSize,
offset,
sort,
topic,
signal,
}: GetNewsDataApiParams) => {
try {
const res = await axios.get(
`https://api.newscatcherapi.com/v2/search?q=${keyword}&from=${date}&countries=${country}&page_size=${pageSize}&page=${offset}&sort_by=${sort}&topic=${topic}`,
{
headers: {
"x-api-key": `${process.env.REACT_APP_API_KEY}`,
},
signal,
}
);
return res;
} catch (err: unknown) {
return console.error(err);
}
};
//news.tsx
const getData = async () => {
try {
const res = await getNewsDataAPI({
keyword,
date,
country,
pageSize,
offset,
sort,
topic,
signal,
});
if (signal.aborted) return;
const resData = (res as AxiosResponse<any, any>).data.articles;
const resInfo = (res as AxiosResponse<any, any>).data.total_datas;
setDataInfo(resInfo);
setNewsData(resData);
setIsLoading(true);
} catch (err: unknown) {
if (signal.aborted) return;
console.error(err);
setIsLoading(true);
}
};
// before
import { useState } from "react";
interface PaginationProps {
totalPage: number;
curPage: number;
setCurPage: (curPage: number) => void;
pageCount: number;
}
export default function Pagenation({
totalPage,
curPage,
setCurPage,
pageCount,
}: PaginationProps) {
const [pageGroup, setPageGroup] = useState(Math.ceil(curPage / pageCount));
let lastPage = pageGroup * pageCount;
if (lastPage > totalPage) {
lastPage = totalPage;
}
let firstPage = lastPage - (pageCount - 1);
if (pageCount > lastPage) {
firstPage = 1;
}
const pagination = () => {
let arr = [];
for (let i = firstPage; i <= lastPage; i++) {
arr.push(
<button
key={i}
className={`pagination-but ${i === curPage ? "bg-gray-500" : ""}`}
onClick={() => setCurPage(i)}
>
{i}
</button>
);
}
return arr;
};
const minusPage = () => {
if (curPage === firstPage) {
setPageGroup(pageGroup - 1);
setCurPage((pageGroup - 1) * 10);
return;
} else if (curPage !== firstPage) {
setCurPage(curPage - 1);
return;
}
};
const plusPage = () => {
if (curPage === lastPage) {
setPageGroup(pageGroup + 1);
setCurPage(curPage + 1);
return;
}
if (curPage !== lastPage) {
setCurPage(curPage + 1);
return;
}
};
return (
<div className="flex">
<button
className="pagination-but"
onClick={minusPage}
disabled={curPage === 1}
>
<
</button>
{pagination()}
<p className="px-2">...</p>
<button
className={`pagination-but font-bold ${
totalPage === curPage ? "bg-gray-500" : ""
}`}
onClick={() => setCurPage(totalPage)}
>
{totalPage}
</button>
<button
className="pagination-but"
onClick={plusPage}
disabled={curPage === lastPage && curPage === totalPage}
>
>
</button>
</div>
);
}
//사용하는 곳 //news.tsx
const [dataInfo, setDataInfo] = useState(1);//총 몇개의 페이지가 있는지
const [offset, setOffset] = useState(1);//현재 선택하고 있는 페이지
...생략
<Pagenation
totalPage={dataInfo}
curPage={offset}
setCurPage={setOffset}
pageCount={10}
/>
//after //news.tsx
const { dataInfo, setDataInfo, offset, setOffset } = usePagenation();
//PagenationState.tsx
import { create } from "zustand";
interface PagenationType {
dataInfo: number;
setDataInfo: (dataInfo: number) => void;
offset: number;
setOffset: (offset: number) => void;
}
export const usePagenation = create<PagenationType>((set) => ({
dataInfo: 1,
setDataInfo: (input) => set(() => ({ dataInfo: input })),
offset: 1,
setOffset: (input: number) => set(() => ({ offset: input })),
}));