Skip to content

Commit

Permalink
프로필 수정 기능
Browse files Browse the repository at this point in the history
  • Loading branch information
ipcgrdn committed Dec 20, 2024
1 parent b2dce81 commit 2b7c086
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 45 deletions.
10 changes: 10 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 @@ -25,6 +25,7 @@
"clsx": "^2.1.1",
"cookie": "^1.0.1",
"framer-motion": "^11.9.0",
"js-cookie": "^3.0.5",
"lucide-react": "^0.446.0",
"next": "14.2.13",
"next-themes": "^0.3.0",
Expand Down
3 changes: 3 additions & 0 deletions src/components/bar/setting-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import {
IconLogout,
IconSettings,
} from "@tabler/icons-react";
import { useRouter } from "next/navigation";

export function SettingMenu() {
const router = useRouter();
const { logout } = useUser();
const { isLoggedIn } = useAuth();
const signInModal = useSigninModal();
Expand All @@ -28,6 +30,7 @@ export function SettingMenu() {

const handleLogout = async () => {
logout();
router.refresh();
}

const toggleTheme = useCallback(() => {
Expand Down
112 changes: 67 additions & 45 deletions src/components/modal/profileEdit-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import ModalTitle from "./modal-title";
import { Textarea } from "../ui/textarea";
import { CustomModal } from "./custom-modal";
import { api } from "@/lib/axios";
import { getProfileUUID } from "@/services/profileService";

const ProfileEditModal = () => {
const [file, setFile] = useState<File | null>(null);
const [isLoading, setIsloading] = useState(false);
const profileEditModal = useProfileEditModal();
const uuid = getProfileUUID();

const onChange = (open: boolean) => {
if (!open) {
Expand All @@ -45,15 +47,11 @@ const ProfileEditModal = () => {
const formData = new FormData();
formData.append("file", file);

const response = await api.post(
`/upload/images`,
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
const response = await api.post(`/upload/images`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});

if (response.status === 201) {
return response.data;
Expand All @@ -63,17 +61,24 @@ const ProfileEditModal = () => {
};

const FormSchema = z.object({
name: z.string().min(1, "이름은 필수입니다.").max(20, "20자 이하로 입력하세요"),
description: z.string().max(500, "소개는 500자 이내로 작성하세요").optional(),
link1: z
.string()
.regex(/^https?:\/\//, "http:// 또는 https:// 형식의 URL을 입력하세요")
.optional(),
link2: z
.string()
.regex(/^https?:\/\//, "http:// 또는 https:// 형식의 URL을 입력하세요")
.optional(),
});
name: z
.string()
.min(1, "1자 이상으로 작성하세요.")
.max(20, "20자 이하로 입력하세요")
.optional(),
description: z
.string()
.max(500, "소개는 500자 이내로 작성하세요")
.optional(),
link1: z
.string()
.regex(/^https?:\/\//, "http:// 또는 https:// 형식의 URL을 입력하세요")
.optional(),
link2: z
.string()
.regex(/^https?:\/\//, "http:// 또는 https:// 형식의 URL을 입력하세요")
.optional(),
});

const {
register,
Expand All @@ -94,44 +99,57 @@ const ProfileEditModal = () => {

const onSubmit: SubmitHandler<FieldValues> = async (values) => {
try {
setIsloading(true);
const hasChanges =
Object.values(values).some(
(value) => value !== null && value !== "" && value !== undefined
) || file !== null;

const profileImage = file;
if (!profileImage) {
toast.error("프로필 사진이 필요합니다.");
if (!hasChanges) {
toast.error("변경된 내용이 없습니다.");
return;
}

const profileImageUrl = await uploadToS3(profileImage);
setIsloading(true);

const requestData = {
...values,
profileImage: profileImageUrl,
};
let requestData = { ...values };

const response = await api.patch(
`/profile`,
requestData,
{
withCredentials: true,
if (file) {
try {
const profileImageUrl = await uploadToS3(file);
requestData.profileImage = profileImageUrl;
} catch (error) {
toast.error("이미지 업로드에 실패했습니다.");
return;
}
}

const filteredRequestData = Object.fromEntries(
Object.entries(requestData).filter(
([_, value]) => value !== null && value !== "" && value !== undefined
)
);

if (response.status !== 200) {
throw new Error("프로필 수정에 실패했습니다.");
if (!uuid) {
toast.error("프로필 정보를 찾을 수 없습니다.");
return;
}

const response = await api.patch(
`/profile/${uuid}`,
filteredRequestData,
{ withCredentials: true }
);

toast.success("프로필이 수정되었습니다.");
reset();
profileEditModal.onClose();
} catch (error) {
if (axios.isAxiosError(error) && error.response?.data) {
const errorData = error.response.data;
toast.error(
errorData.detail || "프로필 수정 중 오류가 발생했습니다."
);
if (axios.isAxiosError(error)) {
const errorMessage =
error.response?.data?.detail || "프로필 수정 중 오류가 발생했습니다.";
toast.error(errorMessage);
} else {
toast.error("프로필 수정 과정에서 오류가 발생했습니다.");
toast.error("프로필 수정 오류가 발생했습니다.");
}
} finally {
setIsloading(false);
Expand Down Expand Up @@ -179,8 +197,8 @@ const ProfileEditModal = () => {
<Input
id="name"
disabled={isLoading}
{...register("name", { required: true })}
placeholder="🚨 이름 *"
{...register("name", { required: false })}
placeholder="🚨 이름"
className="w-full h-14"
/>
<p className={errors.name ? "text-red-500 text-xs" : "hidden"}>
Expand Down Expand Up @@ -218,7 +236,11 @@ const ProfileEditModal = () => {
취소
</div>
</button>
<button className="p-[3px] relative" type="submit" disabled={isLoading}>
<button
className="p-[3px] relative"
type="submit"
disabled={isLoading}
>
<div className="px-8 py-2 bg-[#FF3F8F] rounded-xl relative group transition duration-200 text-white hover:bg-opacity-75 text-sm">
확인
</div>
Expand Down
6 changes: 6 additions & 0 deletions src/services/profileService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { api } from "@/lib/axios";
import axios from "axios";
import Cookies from "js-cookie";

export interface Profile {
name: string;
Expand All @@ -10,6 +11,11 @@ export interface Profile {
isMain: true;
}

export const getProfileUUID = (): string | null => {
const profile = Cookies.get("profile");
return profile || null;
};

export const getProfile = async (): Promise<Profile | null> => {
try {
const response = await api.get<Profile>("/profile");
Expand Down

0 comments on commit 2b7c086

Please sign in to comment.