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 #318

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
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"cSpell.words": ["addboard", "addfile", "sortmobile"]
"cSpell.words": ["addboard", "addfile", "Kakao", "signup", "sortmobile"]
}
16 changes: 5 additions & 11 deletions apis/addBoard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,18 @@ import axios from "@/lib/axios";

interface Params<T> {
data: T;
token: string;
}

export const postNewArticle = async ({ data, token }: Params<object>) => {
const res = await axios.post("/articles", data, {
headers: { Authorization: `Bearer ${token}` },
});
export const postNewArticle = async ({ data }: Params<object>) => {
const res = await axios.post("/articles", data);
};

export const postUploadImage = async ({ data, token }: Params<File>) => {
export const postUploadImage = async ({ data }: Params<File>) => {
const formData = new FormData();
formData.append("image", data);
try {
const res = await axios.post("/images/upload", formData, {
Copy link
Collaborator

Choose a reason for hiding this comment

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

인터셉터를 사용하니, 인증 헤더에 대한 부분을 요청시마다 반복적으로 쓰지 않아도 돼서 좋네요👍

headers: {
Authorization: `Bearer ${token}`,
},
});
const res = await axios.post("/images/upload", formData);
console.log(res);
return res.data;
} catch (err) {
console.log(err);
Expand Down
35 changes: 35 additions & 0 deletions apis/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import axios from "@/lib/axios";

interface SignInData {
email: string;
password: string;
}

interface SignUpData extends SignInData {
passwordConfirmation: string;
nickname: string;
}

interface ResponseData {
accessToken: string;
refreshToken: string;
}

export const postSignUp = async (signUpData: SignUpData) => {
try {
await axios.post("/auth/signUp", signUpData);
return true;
} catch (err) {
console.log(err);
return false;
}
};

export const postSignIn = async (signInData: SignInData) => {
try {
const res = await axios.post("/auth/signIn", signInData);
return res.data;
} catch (err) {
return false;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

p5;
현재는 단순히 post 요청 후 에러 발생시 false만 리턴하는 방식이지만,
추후에는 에러의 응답 코드나 에러 메시지 등을 통해 에러를 케이스별로 처리하여

클라이언트에서도 어떤 에러때문에 요청이 실패했는지 알 수 있도록 작성해보면 좋을 것 같아요!

지금은 이렇게만으로도 충분할 것 같긴 합니다!

};
6 changes: 1 addition & 5 deletions apis/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@ import axios from "@/lib/axios";
interface PostArticleCommentParams {
articleId: number;
data: string;
token: string;
}

export const postArticleComment = async ({
articleId,
data,
token,
}: PostArticleCommentParams) => {
try {
const body = {
content: data,
};
const res = await axios.post(`/articles/${articleId}/comments`, body, {
headers: { Authorization: `Bearer ${token}` },
});
const res = await axios.post(`/articles/${articleId}/comments`, body);
} catch (err) {
console.log(err);
}
Expand Down
18 changes: 0 additions & 18 deletions apis/signIn.ts

This file was deleted.

10 changes: 10 additions & 0 deletions apis/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import axios from "@/lib/axios";

export const getMe = async () => {
try {
const res = await axios.get("/users/me");
return res.data;
} catch {
console.log("유저정보가 없습니다");
}
};
8 changes: 4 additions & 4 deletions components/addboard/AddBoardForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ export default function AddBoardForm() {
onChangeFileInput,
isInputValid,
} = useAddBoard();
const activeColor = isInputValid ? "bg-my-blue" : "bg-gray-400";
return (
<form className="m-container mt-[70px] pt-4">
<div className="flex justify-between">
<span className="font-bold text-xl">게시글 쓰기</span>
<Button
className="btn w-[74px] h-[42px]"
<button
onClick={onSubmitForm}
activeBtn={isInputValid}
className={`flex-center text-white btn w-[74px] h-[42px]${activeColor}`}
>
등록
</Button>
</button>
</div>
<Input
name="title"
Expand Down
4 changes: 2 additions & 2 deletions components/boards/Article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export default function Article({ articles, setTarget }: Props) {
href={`/boards/${article.id}`}
className="flex h-full flex-col justify-between"
>
<div className="flex justify-between gap-10">
<div className="font-semibold text-lg">{article.content}</div>
<div className="flex justify-between gap-10 overflow-ellipsis overflow-hidden whitespace-nowrap">
<div className="font-semibold text-lg ">{article.content}</div>
{article.image && (
<Image
className="rounded-lg border border-gray-200 max-h-[72px]"
Expand Down
58 changes: 58 additions & 0 deletions components/layout/AuthContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { PropsWithChildren, ReactNode } from "react";
import Image from "next/image";

import logoImg from "@/public/images/img_logo.svg";
import logoText from "@/public/images/img_logo_text.svg";
import icKakao from "@/public/images/ic_kakao.png";
import icGoogle from "@/public/images/ic_google.png";
import Link from "next/link";
import { usePathname } from "next/navigation";
import path from "path";

const INITIAL_TEXT = {
login: {
msg: "판다마켓이 처음이신가요?",
link: "회원가입",
},
signup: {
msg: "이미 회원이신가요?",
link: "로그인",
},
} as const;

export default function AuthContainer({ children }: PropsWithChildren) {
const pathName = usePathname();
const nowPathname = pathName === "/auth/login" ? "login" : "signup";
Copy link
Collaborator

Choose a reason for hiding this comment

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

p4;
지금도 괜찮지만, currentPathname 변수명 추천드립니다!


return (
<div className="mx-auto mt-20 max-w-2xl px-4 md:mt-48 md:px-5 lg:mt-56">
<div className="flex-center">
<Link href="/" className="flex-center gap-3 md:gap-5">
<div className=" relative w-[52px] md:w-[103px] h-[52px] md:h-[103px]">
<Image fill priority src={logoImg} alt="로고 이미지" />
</div>
<div className="relative h-[45px] md:h-[90px] w-[133px] md:w-[268px] ">
<Image fill priority src={logoText} alt="로고 글씨" />
</div>
</Link>
</div>
<div className="pt-6 md:pt-14">{children}</div>
<div className="mt-6 flex w-full items-center justify-between rounded-lg bg-blue-100 px-6 py-4">
<span className="text-base">간편 로그인하기</span>
<div className="flex gap-4">
<Image alt="구글 아이콘" src={icGoogle} width={42} height={42} />
<Image alt="카카오 아이콘" src={icKakao} width={42} height={42} />
</div>
</div>
<p className="flex-center gap-1 pt-6 text-sm font-medium">
{INITIAL_TEXT[nowPathname].msg}
<Link
href={nowPathname === "login" ? "/auth/signup" : "/auth/login"}
className="text-my-blue underline"
>
{INITIAL_TEXT[nowPathname].link}
</Link>
</p>
</div>
);
}
83 changes: 83 additions & 0 deletions components/layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useContext, useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import Image from "next/image";
import Link from "next/link";

import { UserContext, UserContextInterface } from "@/context/userProvider";

import logoImg from "@/public/images/img_logo.svg";
import logoText from "@/public/images/img_logo_text.svg";
import icProfile from "@/public/images/ic_profile.svg";

export default function Header() {
const pathname = usePathname();
const [visibleHeader, setVisibleHeader] = useState(false);
const [isLogin, setIsLogin] = useState(false);
const { user } = useContext(UserContext) as UserContextInterface;
useEffect(() => {
if (!user) {
setIsLogin(false);
} else {
setIsLogin(true);
}
}, [user]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

p3;
isLogin 상태를 추가로 관리하는 건 불필요해보입니다!
user라는 상태로도 충분히 계산이 가능해 보입니다.

그럼에도 isLogin이라는 상태가 꼭 필요하다면
useEffect 대신
useMemo로 처리하는 것도 좋을 것 같습니다!

const isLogin = useMemo(() => {
  if (!user) return false;
  return true;
}, [user])


useEffect(() => {
if (pathname?.includes("auth")) {
setVisibleHeader(false);
} else {
setVisibleHeader(true);
}
}, [pathname]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

p4;
요것도 visibleHeader를 상태로 만드는 것보다는 변수 처리를 통해
jsx를 리턴하는 것도 좋을 것 같아요!

const isHeaderVisible = pathname?.includes("auth");

if (!isHeaderVisible) return null;
return (
  <nav>
    ...
  </nav>
)

return (
<>
{visibleHeader && (
<nav
className={`fixed top-0 w-full border-b bg-white drop-shadow-sm z-10`}
>
<div className="mx-auto flex h-[70px] max-w-[1120px] items-center justify-between gap-2 px-4 py-2.5 md:gap-5 lg:gap-8">
<Link href="/" className="flex gap-2.5">
<Image
src={logoImg}
alt="로고이미지"
width={40}
height={41}
className="hidden md:block"
priority
/>
<Image
src={logoText}
alt="로고글씨"
width={101}
height={41}
priority
/>
</Link>
{pathname?.includes("items") && (
Copy link
Collaborator

Choose a reason for hiding this comment

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

p3;
현재 코드베이스에서는 items가 들어간 path를 찾을 수 없는데 어떤 경우에 나타나는 건가요?

<div className="grow font-bold">
<Link href="forum" className="px-1 md:px-4">
자유게시판
</Link>
<Link href="items" className={`px-1 text-gray-600 md:px-4`}>
중고마켓
</Link>
</div>
)}
{isLogin ? (
<Image
src={icProfile}
width={32}
height={32}
alt="유저프로필이미지"
/>
) : (
<Link href="/auth/login">
<button className="btn h-[48px] w-[128px]">로그인</button>
</Link>
)}
</div>
</nav>
)}
</>
);
}
21 changes: 21 additions & 0 deletions components/ui/NewButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { MouseEvent } from "react";

type ButtonProps = {
className?: string;
children: string;
activeBtn?: boolean;
};

export default function NewButton({
children,
activeBtn,
className,
}: ButtonProps) {
const activeColor = activeBtn ? "bg-my-blue" : "bg-gray-400";

return (
<button className={` flex-center text-white ${className} ${activeColor}`}>
{children}
</button>
);
}
37 changes: 37 additions & 0 deletions components/ui/NewInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FieldErrors, FieldValues } from "react-hook-form";

interface InputProps {
label: string;
type: string;
placeholder: string;
name: string;
register: any;
validator: object;
errors: any;
}

export default function NewInput({
name,
placeholder,
type,
label,
register,
validator,
errors,
}: InputProps) {
return (
<div className="flex flex-col pb-6">
<label className="pb-4 text-lg font-bold" htmlFor={name}>
{label}
</label>
<input
className="rounded-xl border-none bg-gray-100 px-6 py-4 outline-none"
type={type}
placeholder={placeholder}
id={name}
{...register(name, validator)}
></input>
<div className="text-my-error-red">{errors[name]?.message}</div>
</div>
);
}
Loading
Loading