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

[Dev] 유저 api 작업 #30

Open
wants to merge 6 commits into
base: feature/api-integration-20-3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
162 changes: 124 additions & 38 deletions src/app/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,136 @@
"use client";

import { Button, Input } from "@/shared/ui";
import { Button, InputLabel } from "@/shared/ui";
import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { useCallback, useState } from "react";

import { useState } from "react";
import { InputLabelStatus } from "@/shared/ui/InputLabel/InputLabel";
import axios from "axios";
import { debounce } from "lodash";
import { toast } from "sonner";
import usePostNickname from "@/entities/user/api/usePostNickname";
import { useRouter } from "next/navigation";
import useValidateNickname from "@/entities/user/api/useValidateNickname";

type SignUpForm = {
nickname: string;
};

const SignUpPage = () => {
const [borderColor, setBorderColor] = useState("#4F118C");
const {
control,
handleSubmit,
formState: { errors },
setError,
clearErrors,
} = useForm<SignUpForm>();

const [status, setStatus] = useState<InputLabelStatus>("default");
const [message, setMessage] = useState<string>("");

const router = useRouter();

const validateNickname = useValidateNickname();
const postNickname = usePostNickname();

const validationCheckNickname = useCallback(
debounce((nickname: string) => {
validateNickname.mutate(
{ nickname },
{
onSuccess: (data) => {
const validateCheck = data.data;
if (validateCheck && validateCheck.status === 200) {
clearErrors("nickname"); // 유효성 검사 성공 시 에러 지우기
setStatus("correct");
setMessage("사용 가능한 닉네임입니다.");
}
},
onError: (error) => {
console.error(error);
if (axios.isAxiosError(error)) {
const errorMessage =
error.response?.data?.message || "서버 오류가 발생했습니다.";
setError("nickname", {
type: "manual",
message: errorMessage,
});
setStatus("error");
} else {
setError("nickname", {
type: "manual",
message: "알 수 없는 오류가 발생했습니다.",
});
}
},
},
);
}, 500),
[],
);

const handleChangeNickname = (nickname: string) => {
validationCheckNickname(nickname);
};

// TODO: 디바운싱 적용
// TODO: 닉네임 상태관리
// TODO: Validation Check
// TODO: 중복 ID 체크 API
// TODO: border 색상 관리
const updateNickname: SubmitHandler<SignUpForm> = (data) => {
postNickname.mutate(
{ nickname: data.nickname },
{
onSuccess: (data) => {
toast("닉네임이 성공적으로 업데이트됐어요");
router.push("/");
},
},
);
};

return (
<div className="flex flex-col w-full h-[calc(100vh-64px)] justify-center items-center p-4">
<div className="flex flex-col gap-[72px]">
<div className="flex w-full flex-col justify-center items-center gap-[5px]">
<div className="text-gray-900 font-bold text-[40px]">
닉네임을 적어주세요!
</div>
<div className="flex w-full flex-col justify-center items-center">
<div className="text-gray-700 text-lg">
시ː작에서 사용할 닉네임을 적어주세요.
</div>
<div className="text-gray-700 text-lg">
닉네임은 나중에 수정할 수 있어요.
</div>
</div>
</div>
<div className="flex flex-col w-full h-full gap-[72px] items-center">
<div className="w-[400px] h-[88px]">
<div className="text-custom-purple]">닉네임 입력</div>
{/* TODO: 이 방식이 맞는지 확인 필요 */}
<div className={`border-b-2 border-[${borderColor}]`}>
<Input
placeholder="띄어쓰기 없이 2자 ~ 12자까지 가능해요."
className="text-lg border-none shadow-none h-14 focus-visible:ring-0"
/>
</div>
</div>
<Button className="flex justify-center items-center w-[400px] h-14 bg-custom-purple">
<div className="text-2xl text-center">시ː작 하기</div>
</Button>
</div>
</div>
<form
onSubmit={handleSubmit(updateNickname)}
className="flex flex-col gap-[72px]"
>
<Controller
name="nickname"
control={control}
defaultValue=""
rules={{
required: "닉네임은 필수로 작성해주셔야 해요.",
minLength: {
value: 2,
message: "띄어쓰기 없이 2자 ~ 12자까지 가능해요.",
},
maxLength: {
value: 12,
message: "닉네임은 최대 12자까지 설정 가능합니다.",
},
}}
render={({ field }) => (
<InputLabel
labelContent="닉네임 입력"
placeholder="띄어쓰기 없이 2자 ~ 12자까지 가능해요."
error={!!errors.nickname}
onChange={(e) => {
field.onChange(e.target.value); // react-hook-form의 onChange 호출
handleChangeNickname(e.target.value); // 디바운스된 유효성 검사 호출
}}
onBlur={field.onBlur} // react-hook-form의 onBlur 호출
value={field.value} // field.value를 통해 입력값 전달
status={status}
message={errors.nickname?.message || message} // 메시지 처리
/>
)}
/>
<Button
className="flex justify-center items-center w-[400px] h-14 bg-custom-purple"
disabled={
(!!errors.nickname && status !== "correct") || status === "default"
} // 오류가 있을 경우 버튼 비활성화
>
<div className="text-2xl text-center">시ː작 하기</div>
</Button>
</form>
</div>
);
};
Expand Down
Loading