Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[염정훈] sprint12 #327

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@tanstack/react-query": "^5.56.2",
"axios": "^1.7.3",
"next": "14.2.5",
"react": "^18",
Expand Down
20 changes: 3 additions & 17 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,8 @@ export const getArticlesComment = async ({
}
};

export const postLogin = async () => {
try {
const response = await axiosInstance.post(`/auth/signIn`, {
email: "[email protected]",
password: "92089208",
});

const token = response.data.accessToken;
localStorage.setItem("token", token);
return token;
} catch (err) {
console.error("로그인 토큰 가져오기 오류");
}
};

export const postArticlesImage = async (file: File) => {
const token = localStorage.getItem("token");
const token = localStorage.getItem("userInfo");

if (!token) {
console.error("Error Messages: 토큰이 없습니다.");
Expand All @@ -84,7 +69,8 @@ export const postArticlesImage = async (file: File) => {
};

export const postArticles = async ({ image, content, title }: ArticlesAdd) => {
const token = localStorage.getItem("token");
const token = localStorage.getItem("userInfo");


if (!token) {
console.error("Error Messages: 토큰이 없습니다.");
Expand Down
39 changes: 39 additions & 0 deletions src/api/authApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { axiosInstance } from "./axiosInstance";

export type LoginType = {
email: string,
password: string,
}

export type SignUpType = LoginType & {
nickname: string,
passwordConfirmation: string,
}

export const postLogin = async ({email, password}: LoginType) => {
try {
const response = await axiosInstance.post('/auth/signIn', {
email,
password
})

return response.data;
} catch (err) {
console.error("로그인 오류" + err)
}
}

export const postSignUp = async ({email, nickname, password, passwordConfirmation }: SignUpType) => {
try {
const response = await axiosInstance.post('/auth/signUp', {
email,
nickname,
password,
passwordConfirmation
})

return response;
} catch (err) {
console.error("회원가입 오류" + err)
}
}
14 changes: 10 additions & 4 deletions src/api/axiosInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ export const axiosInstance = axios.create({

axiosInstance.interceptors.request.use(
(config) => {
const token = localStorage.getItem("token");
const token = localStorage.getItem("userInfo");

if (token && !config.url?.includes("/auth/signIn")) {
config.headers.Authorization = `Bearer ${token}`;
}
if(token) {
const userInfo = JSON.parse(token);
const userInfoAccessToken = userInfo.accessToken;

if (userInfoAccessToken && !config.url?.includes("/auth/signIn")) {
config.headers.Authorization = `Bearer ${userInfoAccessToken}`;
}

}

return config;
},
Expand Down
9 changes: 5 additions & 4 deletions src/components/boards/AllBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@ import Link from "next/link";

export type AllBoardProps = {
articles: ArticlesList[];
setArticleQuery: React.Dispatch<React.SetStateAction<ArticlesQuery>>;
setSortOrder: React.Dispatch<React.SetStateAction<string>>;
setSearchKeyword: React.Dispatch<React.SetStateAction<string>>;
};

export default function AllBoard({ articles, setArticleQuery }: AllBoardProps) {
export default function AllBoard({ articles, setSortOrder, setSearchKeyword }: AllBoardProps) {
return (
<AllBoardWrap>
<AllBoardTitle>
<h2>게시글</h2>
<LinkButton href="/addboard">글쓰기</LinkButton>
</AllBoardTitle>
<AllBoardSearch>
<BoardSearch setArticleQuery={setArticleQuery} />
<BoardOrder setArticleQuery={setArticleQuery} />
<BoardSearch setSearchKeyword={setSearchKeyword} />
<BoardOrder setSortOrder={setSortOrder} />
</AllBoardSearch>
<AllBoardList>
{articles.map((article) => {
Expand Down
9 changes: 3 additions & 6 deletions src/components/boards/BoardOrder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import styled from "styled-components";
import { useState } from "react";
import { AllBoardProps } from "../../components/boards/AllBoard";

type SearchOrderProp = Pick<AllBoardProps, "setArticleQuery">;
type SearchOrderProp = Pick<AllBoardProps, "setSortOrder">;

export default function BoardOrder({ setArticleQuery }: SearchOrderProp) {
export default function BoardOrder({ setSortOrder }: SearchOrderProp) {
const ORDER_NEW = "최신순";
const ORDER_LIKE = "좋아요순";
const [dropText, setDropText] = useState<string>(ORDER_NEW);
Expand All @@ -13,10 +13,7 @@ export default function BoardOrder({ setArticleQuery }: SearchOrderProp) {
const handleClick = (order: string) => {
setDropText(order);
setDropDisplay(false);
setArticleQuery((prev) => ({
...prev,
orderBy: order === ORDER_NEW ? "recent" : "like",
}));
setSortOrder(order === ORDER_NEW ? "recent" : "like");
};

return (
Expand Down
16 changes: 9 additions & 7 deletions src/components/boards/BoardSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import SearchImage from "../../../public/images/i-search.png";
import { AllBoardProps } from "../../components/boards/AllBoard";
import { useState } from "react";

type SearchOrderProp = Pick<AllBoardProps, "setArticleQuery">;
type SearchOrderProp = Pick<AllBoardProps, "setSearchKeyword">;

export default function BoardSearch({ setArticleQuery }: SearchOrderProp) {
export default function BoardSearch({ setSearchKeyword }: SearchOrderProp) {
const [searchInput, setSearchInput] = useState<string>("");

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
setSearchKeyword(searchInput);
}
};

return (
<SearchWrap>
<input
Expand All @@ -16,11 +22,7 @@ export default function BoardSearch({ setArticleQuery }: SearchOrderProp) {
onChange={(e) => {
setSearchInput(e.target.value);
}}
onKeyDown={(e) => {
e.key === "Enter"
? setArticleQuery((prev) => ({ ...prev, keyword: searchInput }))
: "";
}}
onKeyDown={handleKeyDown}
placeholder="검색할 상품을 입력해주세요"
/>
</SearchWrap>
Expand Down
25 changes: 21 additions & 4 deletions src/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,26 @@ import { LinkButton } from "@/styles/ButtonStyle";
import { useRouter } from "next/router";
import Logo from "../../../public/images/logo.png";
import ProfileImage from "../../../public/images/i-profile.png";
import { useEffect, useState } from 'react';

export default function Header() {
const router = useRouter();
const { pathname } = router;
const startPathName = pathname.startsWith("/boards");
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
const [isLogout, setIsLogout] = useState<boolean>(false);
Comment on lines +14 to +15
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isLoggedInisLoggedOut은 서로 상반되는 상태라 하나의 상태로 관리해도 될 것 같습니다.
따로 상태를 선언하신 이유가 있을까요? isLoggedIn이 false이면 로그아웃인걸로 해도 될 것 같아요.


useEffect(() => {
const userInfo = localStorage.getItem('userInfo');
setIsLoggedIn(!!userInfo);
setIsLogout(true);
}, [isLogout])

const handleLogout = () => {
localStorage.removeItem('userInfo');
setIsLogout(false);
router.push('/login');
}

const MENU_COLOR = {
on: "#3692ff",
Expand All @@ -28,7 +43,7 @@ export default function Header() {
alt="판다마켓"
/>
</HeaderLogo>
<HeaderNav>
{isLoggedIn && <HeaderNav>
<ul>
<li>
<Link
Expand All @@ -44,14 +59,16 @@ export default function Header() {
<Link href="">중고마켓</Link>
</li>
</ul>
</HeaderNav>
</HeaderNav>}
</HeaderLeft>
{pathname === "/boards" ? (
{isLoggedIn ? (
<MyPageButton href="">
<img src={ProfileImage.src} alt="프로필 이미지" />

<button type="button" onClick={handleLogout}>로그아웃</button>
</MyPageButton>
) : (
<LinkButton href="">로그인</LinkButton>
<LinkButton href="/login">로그인</LinkButton>
)}
</HeaderWrap>
</HeaderTag>
Expand Down
7 changes: 6 additions & 1 deletion src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import "@/styles/global.css";

import type { AppProps } from "next/app";
import Head from "next/head";
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

query client를 useState의 state에 선언해두면 유저 마다 캐싱을 확실히 할 수 있다고 합니다.
아래의 내용을 읽어보시길 바랍니다 !

https://jong6598.tistory.com/40


export default function App({ Component, pageProps }: AppProps) {
return (
Expand All @@ -25,7 +28,9 @@ export default function App({ Component, pageProps }: AppProps) {
</Head>
<Header />
<GlobalStyle />
<Component {...pageProps} />
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
</QueryClientProvider>
<Footer />
</>
);
Expand Down
32 changes: 14 additions & 18 deletions src/pages/addboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import InputFileImage from "@/components/ui/InputFileImage";
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import axios from "axios";
import { postArticles, postLogin } from "@/api/api";
import { postArticles } from "@/api/api";
import { useMutation } from '@tanstack/react-query';
import { ArticlesAdd } from '@/types/articleType';

type StyledButtonProps = {
active?: boolean;
Expand All @@ -31,27 +33,21 @@ export default function AddBoardPage() {
const [image, setImage] = useState<string>("");
const router = useRouter();

const mutation = useMutation<void, Error, ArticlesAdd>({
mutationFn: postArticles,
onSuccess: () => {
router.push("/boards");
},
onError: (error) => {
console.error("게시글 등록 오류:", error);
},
});

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

postArticles({ image, content, title });

router.push("/boards");
mutation.mutate({ image, content, title });
};

useEffect(() => {
const handleLoad = async () => {
try {
const data = await postLogin();
return data;
} catch (err) {
console.error("에러임" + err);
}
};

handleLoad();
}, []);

return (
<div className="mx-auto mt-[24px] mb-[130px] max-w-[1200px] px-[20px] w-full">
<form onSubmit={handleSubmit}>
Expand Down
Loading
Loading