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

[김민재] Sprint11 #693

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
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BASE_URL = https://panda-market-api.vercel.app
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 파일은 프로젝트와 무관하므로 .gitignore에 다음과 같이 작성하시면 git의 추적을 무시할 수 있습니다.

.env

Copy link
Collaborator

Choose a reason for hiding this comment

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

.gitignore에 env 파일을 추가해 주셨군요!

하지만 아직 github에서 확인되는 걸 보니 캐시를 지워줘야 할 것으로 보입니다 :)

  git rm .env --chached

gitignore에 .env 파일을 추가해도 commit history에 env파일이 남아있게 됩니다!

git에 env파일을 완전히 지우려면 추가 작업이 필요해요 😂
아래 블로그를 참고해 보시면 좋을 것 같아요!

[Git] 실수로 올린 env 삭제하기 (commit history까지 완전 삭제)

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
/.next/
/out/

/.env
Copy link
Collaborator

Choose a reason for hiding this comment

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

어라? .gitignore에 env 파일을 추가해 주셨군요!

하지만 아직 github에서 확인되는 걸 보니 캐시를 지워줘야 할 것으로 보입니다 :)

  git rm .env --chached

gitignore에 .env 파일을 추가해도 commit history에 env파일이 남아있게 됩니다!

git에 env파일을 완전히 지우려면 추가 작업이 필요해요 😂
아래 블로그를 참고해 보시면 좋을 것 같아요!

[Git] 실수로 올린 env 삭제하기 (commit history까지 완전 삭제)


# production
/build

Expand Down
3 changes: 3 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const nextConfig = {
"example.com",
],
},
env: {
BASE_URL: process.env.BASE_URL,
},
};

export default nextConfig;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"next": "14.2.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.51.5",
"react-responsive": "^10.0.0"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions panda.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default defineConfig({
blueBanner: { value: "#CFE5FF" },
textBasic: { value: "#374151" },
disabledBasic: { value: "#9CA3AF" },
errorRed: { value: "#F74747" },
},
fonts: {
ROKAF: { value: "ROKAF Sans" },
Expand Down
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

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

12 changes: 3 additions & 9 deletions src/apis/article/postArticles.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import { Token } from "@/types/api";
import axiosInstance from "../axiosInstance";
import { PostData } from "@/components/addBoardComponents/Form";
import { PostData } from "@/components/addBoardComponents/ArticleForm";

const postArticles = async (
option: PostData = {
content: "",
title: "",
image: null,
},
token?: string | null
}
) => {
const { image, ...restOption } = option;
const payload = image ? option : restOption;

try {
const { data } = await axiosInstance.post<any>("/articles", payload, {
headers: {
Authorization: `Bearer ${token}`,
},
});
return data;
await axiosInstance.post<any>("/articles", payload);
Copy link
Collaborator

Choose a reason for hiding this comment

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

우리 any를 하나하나 지워보도록 합시다 ~!

Suggested change
await axiosInstance.post<any>("/articles", payload);
await axiosInstance.post<PostArticleResponse>("/articles", payload);

혹은

Suggested change
await axiosInstance.post<any>("/articles", payload);
await axiosInstance.post<{success: boolean, id: string 등등..}>("/articles", payload);

Copy link
Collaborator

Choose a reason for hiding this comment

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

/**
 * @todo 나중에 고쳐야됨
*/

} catch (error) {
console.error(`Failed to fetch data: ${error}`);
throw error;
Expand Down
8 changes: 3 additions & 5 deletions src/apis/auth/PostRefreshToken.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Token } from "@/types/api";
import axiosInstance from "../axiosInstance";
import { Token } from "@/types/api";

const PostRefreshToken = async (
refreshToken: string | null
): Promise<string> => {
const PostRefreshToken = async (refreshToken?: string): Promise<Token> => {
try {
Copy link
Collaborator

Choose a reason for hiding this comment

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

컴포넌트가 아닌 ts혹은 js 함수는 일반적으로 카멜케이스로 작성합니다:

Suggested change
const PostRefreshToken = async (refreshToken?: string): Promise<Token> => {
const postRefreshToken = async (refreshToken?: string): Promise<Token> => {

const { data } = await axiosInstance.post<string>("/auth/refresh-token", {
const { data } = await axiosInstance.post<Token>("/auth/refresh-token", {
refreshToken: refreshToken,
});
return data;
Expand Down
56 changes: 53 additions & 3 deletions src/apis/axiosInstance.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import axios, { AxiosRequestConfig } from "axios";
// git push origin Next.js-김민재-sprint10 실수하지않을려고 push config설정 안했는데 브랜치이름 너무 치기어려워서 넣었습니다.
import axios, { AxiosRequestConfig, AxiosError } from "axios";
import {
getToken,
loadTokenFromLocalStorage,
saveTokenToLocalStorage,
} from "@/utils/localStorageToken";
import PostRefreshToken from "@/apis/auth/PostRefreshToken";

interface InternalAxiosRequestConfig extends AxiosRequestConfig {
_retry?: boolean;
}

Comment on lines +9 to +11
Copy link
Collaborator

Choose a reason for hiding this comment

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

오호 ~! 굳굳 ! 기존 타입을 커스텀해서 사용했군요?

인터셉터를 위해서 _retry를 추가하셨네요. 훌륭합니다.

const axiosConfig: AxiosRequestConfig = {
baseURL: "https://panda-market-api.vercel.app/",
baseURL: `${process.env.BASE_URL}`,
headers: {
Copy link
Collaborator

Choose a reason for hiding this comment

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

굳굳굳 ~! 환경 변수를 적용하셨군요 😊

"Content-Type": "application/json",
Accept: "application/json",
Expand All @@ -10,4 +20,44 @@ const axiosConfig: AxiosRequestConfig = {

const axiosInstance = axios.create(axiosConfig);

axiosInstance.interceptors.request.use(
async (config) => {
const accessToken = await getToken();
if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);

axiosInstance.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
const originalRequest = error.config as InternalAxiosRequestConfig;
if (!originalRequest.headers) {
return Promise.reject(error);
}
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
const token = loadTokenFromLocalStorage();
if (token?.refreshToken) {
try {
const newToken = await PostRefreshToken(token.refreshToken);
saveTokenToLocalStorage(newToken);
originalRequest.headers.Authorization = `Bearer ${newToken.accessToken}`;
return axiosInstance(originalRequest);
} catch (refreshError) {
console.error(`Failed to refresh token: ${refreshError}`);
return Promise.reject(refreshError);
}
}
}

return Promise.reject(error);
}
);

export default axiosInstance;
5 changes: 3 additions & 2 deletions src/apis/comment/getArticlesIdComment.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { ArticleId } from "@/types/articles";
import axiosInstance from "../axiosInstance";

interface GetArticlesIdCommentResponse {
list: [];
list: ArticleId[];
nextCursor: null | string;
}

const getArticlesIdComment = async (
articleId: string | string[] | undefined
articleId?: string | string[]
): Promise<GetArticlesIdCommentResponse> => {
try {
const { data } = await axiosInstance.get<GetArticlesIdCommentResponse>(
Expand Down
16 changes: 2 additions & 14 deletions src/apis/comment/postArticlesIdComment.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import axiosInstance from "../axiosInstance";

const postArticlesComment = async (
content: string,
id: string,
token?: string | null
) => {
const postArticlesComment = async (content: string, id: string) => {
try {
await axiosInstance.post<any>(
`/articles/${id}/comments`,
{ content },
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
await axiosInstance.post<any>(`/articles/${id}/comments`, { content });
} catch (error) {
console.error(`Failed to fetch data: ${error}`);
throw error;
Expand Down
5 changes: 2 additions & 3 deletions src/apis/postImage.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import axiosInstance from "@/apis/axiosInstance";

interface PostImageResponse {
url?: null | Blob | MediaSource;
image?: null | Blob | MediaSource;
}

const postImage = async (token: string | null, image?: any) => {
const postImage = async (image?: any) => {
try {
const formData = new FormData();
if (image) {
Expand All @@ -15,7 +15,6 @@ const postImage = async (token: string | null, image?: any) => {
formData,
{
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "multipart/form-data",
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ interface FormProps {
onChangeImage: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

function Form({ postData, file, onChangeInput, onChangeImage }: FormProps) {
function ArticleForm({
postData,
file,
onChangeInput,
onChangeImage,
}: FormProps) {
const [imageUrl, setImageUrl] = useState<string | StaticImageData>(imageAdd);

useEffect(() => {
Expand All @@ -35,36 +40,42 @@ function Form({ postData, file, onChangeInput, onChangeImage }: FormProps) {
return (
<div className={cx(formStyle, css({ marginTop: "24px" }))}>
<div className={labelInput}>
<label className={labelBasicStyle}>*제목</label>
<input
name="title"
type="text"
value={postData.title}
onChange={onChangeInput}
className={inputRecipe()}
placeholder="제목을 입력해주세요"
/>
<label className={labelBasicStyle}>
*제목
<input
name="title"
type="text"
value={postData.title}
onChange={onChangeInput}
className={inputRecipe()}
placeholder="제목을 입력해주세요"
/>
</label>
Comment on lines +43 to +53
Copy link
Collaborator

Choose a reason for hiding this comment

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

굳굳 ~! labelinput을 연결했군요 ! 👍

</div>
<div className={labelInput}>
<label className={labelBasicStyle}>*내용</label>
<textarea
name="content"
value={postData.content}
onChange={onChangeInput}
className={inputRecipe({ visual: "xLarge" })}
placeholder="내용을 입력해주세요"
/>
<label className={labelBasicStyle}>
*내용
<textarea
name="content"
value={postData.content}
onChange={onChangeInput}
className={inputRecipe({ visual: "xLarge" })}
placeholder="내용을 입력해주세요"
/>
</label>
</div>
<div className={labelInput}>
<label className={labelBasicStyle}>이미지</label>
<input
type="file"
name="image"
accept="image/*"
onChange={onChangeImage}
className={css({ display: "none" })}
id="image-upload"
/>
<label className={labelBasicStyle}>
이미지
<input
type="file"
name="image"
accept="image/*"
onChange={onChangeImage}
className={css({ display: "none" })}
id="image-upload"
/>
</label>
<label htmlFor="image-upload" className={css({ cursor: "pointer" })}>
<Image
src={imageUrl}
Expand All @@ -83,4 +94,4 @@ function Form({ postData, file, onChangeInput, onChangeImage }: FormProps) {
);
}

export default Form;
export default ArticleForm;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface FormTitleProps {
handleSubmit: MouseEventHandler<HTMLButtonElement>;
}

function FormTitle({ isValid, handleSubmit }: FormTitleProps) {
function ArticleFormTitle({ isValid, handleSubmit }: FormTitleProps) {
return (
<div className={hstack({ justifyContent: "space-between" })}>
<h2 className={subTitle}>게시글 쓰기</h2>
Expand All @@ -24,4 +24,4 @@ function FormTitle({ isValid, handleSubmit }: FormTitleProps) {
);
}

export default FormTitle;
export default ArticleFormTitle;
4 changes: 2 additions & 2 deletions src/components/boardsComponents/NormalPost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ function NormalPost() {
useEffect(() => {
const loadArticles = async () => {
const receive = await getArticles({
orderBy: `${orderBy}`,
keyword: `${searchValue}`,
orderBy: orderBy,
keyword: searchValue,
});
setArticles(receive.list);
};
Expand Down
8 changes: 7 additions & 1 deletion src/components/shared/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ import { useRouter } from "next/router";
import Image from "next/image";
import { buttonRecipe } from "@/css/recipe/buttonRecipe.styled";
import userPassiveIcon from "@/assets/icons/user_passive_ic.svg";
import { useEffect, useState } from "react";

function Header() {
const router = useRouter();
const [isToken, setIsToken] = useState<string | undefined>("");
Copy link
Collaborator

Choose a reason for hiding this comment

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

다음과 같이 처리하면 string | undefined가 됩니다 !

Suggested change
const [isToken, setIsToken] = useState<string | undefined>("");
const [isToken, setIsToken] = useState<string>();

Copy link
Collaborator

Choose a reason for hiding this comment

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

그리고 토큰이 빈 값인 경우는 "없음"이란 의미로 보기 어려울 것 같아요 ! 차라리 undefined로 들어가있으면 어떨까요?


useEffect(() => {
setIsToken(localStorage.getItem("accessToken")?.replace(/"/gi, ""));
});

return (
<div className={headerContainer}>
Expand All @@ -40,7 +46,7 @@ function Header() {
중고마켓
</Link>
</div>
{router.pathname !== "/" ? (
{isToken ? (
<Link href="/signin">
<Image src={userPassiveIcon} alt="userPassive" />
</Link>
Expand Down
Loading
Loading