Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
04eeca5
refactor(mentor): form을 사용하는쪽에서 주입해서 기능이 변하도록 수정
cksrlcks Jan 17, 2025
b11110c
refactor(mentor): Form컴포넌트 타입 개선
cksrlcks Jan 17, 2025
cb1d137
refactor(mentor): message 타입 개선
cksrlcks Jan 17, 2025
63e3b83
refactor: 이미지 업로드 컴포넌트 리팩토링
cksrlcks Jan 17, 2025
5abc252
refactor: tags 인풋 컴포넌트 리팩토링
cksrlcks Jan 18, 2025
38fddfa
refactor: useActionState 제거 및 react hook form의 submit에서 server action 사용
cksrlcks Jan 21, 2025
a146ec4
refactor: react-query queryclient 생성 방법 변경
cksrlcks Jan 22, 2025
bccb719
refactor: 자유게시판 리스트 레이아웃 컴포넌트명 수정
cksrlcks Jan 22, 2025
7be290d
feat: 내정보 페이지 작업
cksrlcks Jan 22, 2025
7a5d3d2
feat: 헤더 프로필 드롭메뉴에 내정보페이지 링크 추가
cksrlcks Jan 22, 2025
f3a4d46
refactor: 유저정보조회를 auth service에서 분리
cksrlcks Jan 22, 2025
18d27a2
feat: 아바타 컴포넌트 개선 (추가 classname을 받도록)
cksrlcks Jan 22, 2025
227b753
feat: 유저활동 컴포넌트 작업
cksrlcks Jan 22, 2025
60deb35
fix: 서버용 axios 인터셉터 위치 수정
cksrlcks Jan 22, 2025
5ae77e0
feat: 비밀번호 수정 페이지 작업
cksrlcks Jan 23, 2025
b99bb77
feat: 프로필 변경 기능 작업
cksrlcks Jan 23, 2025
83949da
fix: 헤더 프로필 버튼 수정
cksrlcks Jan 23, 2025
1ac3032
feat: 미들웨어 추가 (비밀번호 변경, 프로필 변경 페이지)
cksrlcks Jan 23, 2025
1e7c814
refactor: axios 인터셉터 리팩토링
cksrlcks Jan 23, 2025
c09aded
refactor: 불필요한 코드 지우기
cksrlcks Jan 23, 2025
1184ac3
fix: 서버액션에서 로그인후 클라이언트 세션이 업데이트 안되는 현상 수정
cksrlcks Jan 23, 2025
3ee9121
feat: 마이페이지 로딩 추가
cksrlcks Jan 23, 2025
fe90213
feat: 마이페이지 버튼구역 컴포넌트 추가
cksrlcks Jan 23, 2025
877251e
feat: 프로필 사진을 교체 안했으면, 제출 못하도록 수정
cksrlcks Jan 24, 2025
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
32 changes: 20 additions & 12 deletions src/app/(auth)/_components/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
"use client";

import { FieldItem, Input } from "@components/Field";
import { useRouter } from "next/navigation";
import { FieldItem, Form, Input } from "@components/Field";
import { Button } from "@components/ui";
import useFormWithError from "@hooks/useFormWithError";
import { zodResolver } from "@hookform/resolvers/zod";
import { signinFormSchmea, SigninFormType } from "@schemas/auth";
import { FieldAdapter } from "@components/adaptor/rhf";
import { ServerForm } from "@/components/Field/ServerForm";
import { useActionState } from "react";
import action from "../login/action";

export default function LoginForm() {
const [formStatus, formAction, isPending] = useActionState(action, {
message: "",
});
const {
control,
formState: { isValid },
formError,
handleSubmit,
formState: { isSubmitting, isValid },
} = useFormWithError<SigninFormType>({
mode: "onBlur",
resolver: zodResolver(signinFormSchmea),
Expand All @@ -25,12 +23,22 @@ export default function LoginForm() {
password: "",
},
});
const router = useRouter();

async function onSubmit(data: SigninFormType) {
const response = await action(data);
if (response.success) {
router.replace("/items");
} else {
throw new Error(response.message);
}
}

return (
<ServerForm
action={formAction}
isLoading={isPending}
error={formStatus?.message}
<Form
isLoading={isSubmitting}
error={formError}
onSubmit={handleSubmit(onSubmit)}
Comment on lines +38 to +41
Copy link
Collaborator

Choose a reason for hiding this comment

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

오.... Form 공통화를 이렇게 해두니 사용하기 편하게 잘 하셨네요ㅎㅎ!

>
<FieldItem>
<FieldItem.Label htmlFor="email">이메일</FieldItem.Label>
Expand Down Expand Up @@ -63,6 +71,6 @@ export default function LoginForm() {
<Button type="submit" size="xl" disabled={!isValid}>
로그인
</Button>
</ServerForm>
</Form>
);
}
33 changes: 20 additions & 13 deletions src/app/(auth)/_components/SignupForm.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
"use client";

import { FieldItem, Input } from "@components/Field";
import { useRouter } from "next/navigation";
import { FieldItem, Form, Input } from "@components/Field";
import { Button } from "@components/ui";
import useFormWithError from "@hooks/useFormWithError";
import { zodResolver } from "@hookform/resolvers/zod";
import { signupFormSchema, SignupFormType } from "@schemas/auth";
import { FieldAdapter } from "@components/adaptor/rhf";
import action from "../signup/action";
import { useActionState } from "react";
import { ServerForm } from "@/components/Field/ServerForm";

export default function SignupForm() {
const [formStatus, formAction, isPending] = useActionState(action, {
message: "",
});

const {
control,
formState: { isValid },
formError,
handleSubmit,
formState: { isSubmitting, isValid },
} = useFormWithError<SignupFormType>({
mode: "onBlur",
resolver: zodResolver(signupFormSchema),
Expand All @@ -28,12 +25,22 @@ export default function SignupForm() {
passwordConfirmation: "",
},
});
const router = useRouter();

async function onSubmit(data: SignupFormType) {
const response = await action(data);
if (response.success) {
router.replace("/login");
} else {
throw new Error(response.message);
}
}

return (
<ServerForm
action={formAction}
isLoading={isPending}
error={formStatus?.message}
<Form
isLoading={isSubmitting}
error={formError}
onSubmit={handleSubmit(onSubmit)}
>
<FieldItem>
<FieldItem.Label htmlFor="email">이메일</FieldItem.Label>
Expand Down Expand Up @@ -92,6 +99,6 @@ export default function SignupForm() {
<Button type="submit" size="xl" disabled={!isValid}>
회원가입
</Button>
</ServerForm>
</Form>
);
}
33 changes: 16 additions & 17 deletions src/app/(auth)/login/action.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
"use server";

import { signIn } from "@/auth";
import { signinFormSchmea } from "@/schemas/auth";
import { signinFormSchmea, SigninFormType } from "@/schemas/auth";
import { CredentialsSignin } from "next-auth";
import { isRedirectError } from "next/dist/client/components/redirect-error";
import { redirect } from "next/navigation";
export default async function action(
prevState: { message: string },
formData: FormData
) {
const parsed = signinFormSchmea.safeParse(Object.fromEntries(formData));

export default async function action(data: SigninFormType) {
const parsed = signinFormSchmea.safeParse(data);

if (!parsed.success) {
return {
message: "제출양식에 문제가 있습니다. 확인해주세요",
success: false,
};
}

Expand All @@ -23,20 +20,22 @@ export default async function action(
password: parsed.data.password,
redirect: false,
});
redirect("/");
} catch (error) {
if (isRedirectError(error)) {
throw error;
}

return {
message: "로그인 성공",
success: true,
};
} catch (error) {
if (error instanceof CredentialsSignin) {
return {
message: `로그인 실패 : ${error.code}`,
success: false,
};
}
}

return {
message: "로그인 성공",
};
return {
message: `로그인 실패`,
success: false,
};
}
}
31 changes: 14 additions & 17 deletions src/app/(auth)/signup/action.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
"use server";

import { signupFormSchema } from "@/schemas/auth";
import { signupFormSchema, SignupFormType } from "@/schemas/auth";
import { signUp } from "@/service/auth";
import { isAxiosError } from "axios";
import { isRedirectError } from "next/dist/client/components/redirect-error";
import { redirect } from "next/navigation";

export default async function action(
prevState: { message: string },
formData: FormData
) {
const parsed = signupFormSchema.safeParse(Object.fromEntries(formData));
export default async function action(data: SignupFormType) {
const parsed = signupFormSchema.safeParse(data);

if (!parsed.success) {
return {
message: "제출양식에 문제가 있습니다. 확인해주세요",
success: false,
};
}

Expand All @@ -26,22 +22,23 @@ export default async function action(
passwordConfirmation: parsed.data.passwordConfirmation,
});

redirect("/login");
return {
message: "회원가입 성공",
success: true,
};
} catch (error) {
if (isRedirectError(error)) {
throw error;
}

if (isAxiosError(error)) {
const message =
error.response?.data.message || "알 수 없는 에러가 발생했어요.";
return {
message: `회원가입 실패 : ${message}`,
success: false,
};
}
}

return {
message: "회원가입 성공",
};
return {
message: "회원가입 실패",
success: false,
};
}
}
10 changes: 10 additions & 0 deletions src/app/(common)/(board)/_components/ArticleAddForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use client";

import ArticleForm from "./ArticleForm";
import useArticleActions from "./useArticleActions";

export default function ArticleAddForm() {
const { handleArticleAdd } = useArticleActions();

return <ArticleForm mode="add" onFormSubmit={handleArticleAdd} />;
}
37 changes: 23 additions & 14 deletions src/app/(common)/(board)/_components/ArticleForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,24 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { Article } from "@/types/article";
import { FieldAdapter } from "@components/adaptor/rhf";
import { useRouter } from "next/navigation";
import useArticleActions from "./useArticleActions";
import { ArticleFormSchema, ArticleFormType } from "@/schemas/article";

interface ArticleFormProps {
initialData?: Article;
mode?: "add" | "edit";
articleId?: number;
interface ArticleAddFormProps {
mode: "add";
onFormSubmit: (data: ArticleFormType) => Promise<Article | undefined>;
}
interface ArticleModifyFormProps {
initialData: Article;
mode: "edit";
onFormSubmit: (data: ArticleFormType) => Promise<Article | undefined>;
}

type ArticleFormProps = ArticleAddFormProps | ArticleModifyFormProps;
Comment on lines +19 to +29
Copy link
Collaborator

Choose a reason for hiding this comment

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

타입스크립트가 잘 추론해서 사용할 수 있도록 해주셨네요ㅎㅎ!


export default function ArticleForm({
initialData,
mode = "add",
articleId,
}: ArticleFormProps) {
export default function ArticleForm(props: ArticleFormProps) {
const { mode, onFormSubmit } = props;
const initialData = mode === "edit" ? props.initialData : undefined;
const router = useRouter();
const { handleArticleAdd, handleArticleModify } =
useArticleActions(articleId);
const onFormSubmit = mode === "add" ? handleArticleAdd : handleArticleModify;

const {
control,
Expand Down Expand Up @@ -103,7 +103,16 @@ export default function ArticleForm({
<FieldAdapter
name="image"
control={control}
render={(props) => <ImageUpload {...props} />}
render={(props) => (
<ImageUpload
{...props}
value={props.value}
onChange={(file) => {
props.onChange(file);
props.onBlur();
}}
/>
)}
/>
</FieldItem>
</Section.Content>
Expand Down
21 changes: 21 additions & 0 deletions src/app/(common)/(board)/_components/ArticleModifyForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client";

import { Article } from "@/types/article";
import ArticleForm from "./ArticleForm";
import useArticleActions from "./useArticleActions";

export default function ArticleModifyForm({
initialData,
}: {
initialData: Article;
}) {
const { handleArticleModify } = useArticleActions(initialData.id);

return (
<ArticleForm
mode="edit"
onFormSubmit={handleArticleModify}
initialData={initialData}
/>
);
}
4 changes: 2 additions & 2 deletions src/app/(common)/(board)/addBoard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { PageWrapper } from "@/components/Page";
import ArticleForm from "../_components/ArticleForm";
import ArticleAddForm from "../_components/ArticleAddForm";

export default function AddBoardPage() {
return (
<PageWrapper>
<ArticleForm mode="add" />
<ArticleAddForm />
</PageWrapper>
);
}
2 changes: 1 addition & 1 deletion src/app/(common)/(board)/boards/(lists)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PageWrapper } from "@/components/Page";
import { ReactNode } from "react";

export default function dss({
export default function ListLayout({
all,
best,
children,
Expand Down
8 changes: 2 additions & 6 deletions src/app/(common)/(board)/modifyBoard/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { notFound, redirect } from "next/navigation";
import { Suspense } from "react";
import { Message } from "@/components/ui";
import { getArticle } from "@/service/article";
import ArticleForm from "../../_components/ArticleForm";
import { isAxiosError } from "axios";
import { isRedirectError } from "next/dist/client/components/redirect-error";
import ArticleModifyForm from "../../_components/ArticleModifyForm";

export default async function ModifyBoardPage({
params,
Expand All @@ -32,11 +32,7 @@ export default async function ModifyBoardPage({
<Suspense
fallback={<Message>게시물정보를 가져오는 중입니다...</Message>}
>
<ArticleForm
mode="edit"
initialData={filteredDetail}
articleId={Number(id)}
/>
<ArticleModifyForm initialData={filteredDetail} />
</Suspense>
</PageWrapper>
);
Expand Down
10 changes: 10 additions & 0 deletions src/app/(common)/(market)/_components/ProductAddForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use client";

import ProductForm from "./ProductForm";
import useProductActions from "./useProductActions";

export default function ProductAddForm() {
const { handleProductAdd } = useProductActions();

return <ProductForm mode="add" onFormSubmit={handleProductAdd} />;
}
Loading
Loading