Skip to content

Feature(#77): 마이 페이지 API 연동 관련 기본 로직 작업 #263

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

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
281fdd2
refactor: 철자 수정
Jw705 Dec 12, 2023
44e5e1e
feat: 마이 페이지 API 연동 관련 기본 로직 작업 #77
LellowMellow Dec 12, 2023
b912de5
fix: 발표자 입장 시, 초기 화이트보드 데이터 받고 반영할 수 있도록 수정
platinouss Dec 12, 2023
eeba8f2
fix: redis에 string 타입으로 저장하도록 수정
platinouss Dec 12, 2023
32c67dd
feat: 참여자, 발표자 강의 제목 정상적으로 표시하도록 개선
Jw705 Dec 12, 2023
02dbb5d
Merge pull request #264 from platinouss/fix/231212-whiteboard-init
tmddus2 Dec 12, 2023
b61c349
Merge commit '02dbb5d0736518cf2844ff74122c3700ea8192d4' into feature/…
Jw705 Dec 12, 2023
d8fab4b
feat: 발표자가 강의를 나갔다가 들어오는 경우 이전 화이트보드 데이터를 가져온다 #215
Jw705 Dec 12, 2023
0ce28ac
fix: 새로 방을 개설했을 때만, 강의 시작시간과 화이트보드 초기 데이터 반영
platinouss Dec 12, 2023
fcec906
Merge pull request #265 from platinouss/fix/231212-re-connect-init
tmddus2 Dec 12, 2023
33c9766
Merge commit 'fcec906edc8199a10b3588f26007556dd0f7d00c' into feature/…
Jw705 Dec 12, 2023
de30d45
feat: 강의자 페이지 로그인 한 사용자만 가능하게 변경
Jw705 Dec 12, 2023
650e07f
Merge pull request #266 from Jw705/feature/231212-update-socket
Jw705 Dec 12, 2023
ff2c70b
fix: 참여자 페이지 게스트 토큰 임시 추가
Jw705 Dec 12, 2023
69c1488
Merge pull request #267 from Jw705/feature/231212-auth-lecture
Byeonjin Dec 12, 2023
37fcbbf
feat: 발표자 페이지 새로고침 시 해결되지 않은 질문 리스트 백업 기능 구현 #268
Byeonjin Dec 12, 2023
509a152
feat: 강의자가 새로고침 후 재접속 해도 음성이 잘 들리도록 개선 #215
Jw705 Dec 12, 2023
2ccec62
chore: 사용하지 않는 FE 디버깅 코드 제거
Jw705 Dec 12, 2023
e3a6860
Merge pull request #270 from Jw705/feature/231212-reconnect-audio
platinouss Dec 12, 2023
b3b2c0a
feat: 마이 페이지 API 연동 관련 기본 로직 작업 #77
LellowMellow Dec 12, 2023
a7c2fc6
fix: 메모지 영어, 한글 폰트가 다른 문제 해결, 폰트 크기 제어시 메모지 배경 크기 조절 #272
Byeonjin Dec 12, 2023
7326dd3
refactor: import 순서 그룹핑 #77
LellowMellow Dec 12, 2023
39be6d2
fix: 메모지와 다른 요소를 같이 선택할 경우 메모 편집 패널 표시 오류 #274
Byeonjin Dec 12, 2023
81fed72
feat: 참여자가 socket id 전송하도록 개선
Jw705 Dec 12, 2023
2bd061a
Merge pull request #275 from Jw705/feature/231212-socket-id
Jw705 Dec 12, 2023
dbe6d37
fix: 메모지 입력 시 백스페이스 문제 해결 #276
Byeonjin Dec 12, 2023
b2b4ac9
refact: 사용하지 않는 모듈 제거
Byeonjin Dec 12, 2023
e4427f6
Merge pull request #277 from Byeonjin/fix/231212-momo-bug-fix
Byeonjin Dec 12, 2023
5046423
refactor: 결과값 구조분해할당 적용 #77
LellowMellow Dec 12, 2023
ee5c8f3
Merge branch 'feature/231211-add-api-to-mypage' of https://github.com…
LellowMellow Dec 12, 2023
f58361e
feat: 마이 페이지 API 연동 관련 기본 로직 작업 #77
LellowMellow Dec 12, 2023
f4c006a
refactor: import 순서 그룹핑 #77
LellowMellow Dec 12, 2023
7f03e2a
refactor: 결과값 구조분해할당 적용 #77
LellowMellow Dec 12, 2023
937e3b1
feat: 마이 페이지 API 연동 관련 기본 로직 작업 #77
LellowMellow Dec 12, 2023
f5cbb08
fix: 비밀번호 regexp 수정 #77
LellowMellow Dec 12, 2023
02cbb2f
Merge branch 'feature/231211-add-api-to-mypage' of https://github.com…
LellowMellow Dec 12, 2023
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
7 changes: 3 additions & 4 deletions frontend/src/hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ const useAuth = () => {
Authorization: accessToken
}
})
.then((response) => {
localStorage.setItem("username", response.data.username);
localStorage.setItem("email", response.data.email);
.then((result) => {
localStorage.setItem("username", result.data.username);
Copy link

Choose a reason for hiding this comment

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

많이 반복되는건 아니지만, result.data 를 반복적으로 접근하는건 객체탐색비용만 들어가서.. 해체할당문법으로 미리 변수로 담으면 더 좋죠.
data 라는 이름은 의미가 없어서 별로입니다.

localStorage.setItem("email", result.data.email);
})
.catch((error) => {
console.log(error.response?.status);
if (error.response?.status === 401) {
Copy link

Choose a reason for hiding this comment

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

401 아닌 다른 상태코드에서는 어떻게 처리되나요?

showToast({ message: "로그인이 만료되었어요.", type: "alert" });
navigate("/userauth");
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/MyPage/MyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const MyPage = () => {
return (
<>
<Header type="main" />
<MyPageSection profileImage="" />
<MyPageSection />
</>
);
};
Expand Down
212 changes: 90 additions & 122 deletions frontend/src/pages/MyPage/components/MyPageSection.tsx
Original file line number Diff line number Diff line change
@@ -1,188 +1,156 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import axios from "axios";
import Button from "@/components/Button/Button";
import ProfileBig from "@/assets/imgs/profileBig.png";
import SubLogoOriginal from "@/assets/imgs/subLogoOriginal.png";
import EnterIcon from "@/assets/svgs/enter.svg?react";
import { useToast } from "@/components/Toast/useToast";
import ReplayLectureCard from "./ReplayLectureCard";
import useAuth from "@/hooks/useAuth";
import { useNavigate } from "react-router-dom";
Copy link

Choose a reason for hiding this comment

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

import 되는 순서를 라이브러리끼리, 컴포넌트끼리 .. 이런식으로 묶어두면 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

구현만 진행하느라 정말 기초적인 부분인데 신경쓰지 못했던 것 같습니다. 감사합니다 :)


interface MyPageSectionProps {
profileImage: string;
}
const NICKNAME_REGEXP = /^[a-zA-Z0-9가-힣]{3,15}$/;

const NICKNAME_REGEXP = /^(?![0-9-_.]+$)[가-힣A-Za-z0-9-_.]{1,10}$/;

const DUMMY_DATA = [
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.11",
title: "프로그래밍 기초",
description: "컴퓨터 프로그래밍의 기본 원리를 배우는 입문 강좌. 언어 선택부터 기본 구문까지."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.12",
title: "요리의 기초",
description: "기본적인 요리 기술과 요리법을 배우는 강좌. 초보자를 위한 쉬운 레시피와 요리 팁 제공."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.13",
title: "디지털 마케팅",
description: "디지털 마케팅 전략과 소셜 미디어 활용 방법을 배우는 실용적인 강좌."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.14",
title: "수학의 이해",
description: "기본적인 수학 개념과 문제 해결 기술을 배우는 강좌. 수학에 대한 두려움을 극복할 수 있도록 도움."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.15",
title: "역사 탐험",
description: "세계 역사의 주요 사건과 인물을 탐구하는 강좌. 역사를 통해 현재를 이해하는 데 도움."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.16",
title: "창의적 글쓰기",
description: "창의적인 글쓰기 기술과 아이디어 발상법을 배우는 강좌. 글쓰기를 통한 자기 표현 방법 탐구."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.17",
title: "영화 분석",
description: "영화의 기술적 요소와 예술적 가치를 분석하는 강좌. 영화를 깊이 있게 이해하는 방법 제공."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.18",
title: "피트니스 가이드",
description: "건강하고 효율적인 운동 방법을 배우는 강좌. 개인별 맞춤 운동 계획 설정 방법 제공."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.19",
title: "사진술 입문",
description: "기초 사진 기술과 구도, 조명 활용법을 배우는 강좌. 사진을 통해 예술적 감각 향상."
},
{
profileImage: "",
duration: "12:34",
user: "볼록이",
date: "2023.11.20",
title: "환경 보호",
description: "환경 보호와 지속 가능한 생활 방식에 대해 배우는 강좌. 일상에서 실천할 수 있는 환경 보호 활동 소개."
}
];
type Lecture = {
date: string;
duration: string;
user: string;
title: string;
description: string;
};

const MyPageSection = ({ profileImage }: MyPageSectionProps) => {
const MyPageSection = () => {
const showToast = useToast();
const [nickname, setNickname] = useState("볼록이");
const [isNicknameEdit, setIsNicknameEdit] = useState(false);
const navigate = useNavigate();
const { checkAuth } = useAuth();
const [username, setUsername] = useState(localStorage.getItem("username") || "");
const [isUsernameEdit, setIsUsernameEdit] = useState(false);
const [isValid, setIsValid] = useState(true);
const [lectureList, setLectureList] = useState<Lecture[]>([]);

useEffect(() => {
checkAuth();
axios
Copy link

Choose a reason for hiding this comment

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

이런 질문에 답변을 해본다면..?

fetch API로 안되는건 어떤 것이에요?
그렇다면 그것을 fetch 로 구현해야 한다면?

.get(`${import.meta.env.VITE_API_SERVER_URL}/lecture/list`, {
headers: { Authorization: localStorage.getItem("token") }
})
.then((result) => {
Copy link

Choose a reason for hiding this comment

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

특별한 경우가 아니면 await 표현법이 더나을거에요.

setLectureList(result.data);
Copy link

Choose a reason for hiding this comment

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

result 라는 이름, data 라는 속성 모두..구체적이지 않은 이름이네요.

})
.catch((error) => {
if (error.response.status === 401) {
showToast({ message: "로그인이 만료되었어요.", type: "alert" });
Copy link

Choose a reason for hiding this comment

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

401은 꼭 만료되는 것만을 의미할까요?

navigate("/userauth");
} else showToast({ message: "정보를 불러오는데 오류가 발생했어요.", type: "alert" });
Copy link

Choose a reason for hiding this comment

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

alert ? 은 참고로 그렇게 좋은 ux는 아닙니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

확인했습니다! 다만, 해당 코드의 경우, 브라우저의 alert 창을 띄우는 방식이 아닌 따로 개발한 Toast를 띄우는 형태인데 이 또한 좋지 않은 방향일까요?

Copy link

Choose a reason for hiding this comment

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

에러메시지를 소스코드에 직접 하드코딩하진 않고, 별도로 묶어서 관리하긴하죠.
여기 비슷한 메시지가 코드에 흩어져있으면 관리가 어려워요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

개선 사항으로 추가하겠습니다! 감사합니다 :)

});
}, []);

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newNickname = event.target.value;
setNickname(newNickname);
setIsValid(NICKNAME_REGEXP.test(newNickname));
const newUsername = event.target.value;
setUsername(newUsername);
setIsValid(NICKNAME_REGEXP.test(newUsername));
};

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") handleEditButtonClicked();
};

const handleEditButtonClicked = () => {
if (isNicknameEdit) {
if (isUsernameEdit) {
if (isValid) {
axios
.post(
`${import.meta.env.VITE_API_SERVER_URL}/profile`,
{ username },
{
headers: { Authorization: localStorage.getItem("token") }
}
)
.then((result) => {
localStorage.setItem("username", result.data.username);
localStorage.setItem("email", result.data.email);
setUsername(result.data.username);
})
.catch((error) => {
if (error.response.status === 401) {
showToast({ message: "로그인이 만료되었어요.", type: "alert" });
navigate("/userauth");
} else showToast({ message: "정보를 불러오는데 오류가 발생했어요.", type: "alert" });
});
showToast({ message: "닉네임 변경을 완료했습니다.", type: "success" });
setIsNicknameEdit(false);
setIsUsernameEdit(false);
} else {
showToast({ message: "올바르지 않은 닉네임입니다.", type: "alert" });
}
} else {
showToast({ message: "닉네임을 변경합니다.", type: "default" });
setIsNicknameEdit(true);
setIsUsernameEdit(true);
}
};

return (
<div className="flex flex-col items-center my-32 sm:mt-36">
<section className="flex relative w-11/12 max-w-3xl p-6 flex-col items-center gap-6 rounded-2xl border-default shadow-xl">
<img
src={profileImage ? profileImage : ProfileBig}
alt="프로필 이미지"
className="absolute -top-20 w-40 h-40 sm:w-56 sm:h-56 sm:-top-28"
/>
<img src={ProfileBig} alt="프로필 이미지" className="absolute -top-20 w-40 h-40 sm:w-56 sm:h-56 sm:-top-28" />

<input
type="text"
value={nickname}
value={username}
onChange={handleChange}
onKeyDown={handleKeyDown}
size={nickname.length + 1 || 3}
size={username.length + 1 || 3}
placeholder="닉네임"
maxLength={10}
disabled={!isNicknameEdit}
disabled={!isUsernameEdit}
className="mt-20 sm:mt-28 semibold-32 text-center border-b-2 border-grayscale-gray focus:border-grayscale-black outline-none transition duration-200 rounded-none disabled:border-none disabled:text-grayscale-black disabled:bg-grayscale-white"
/>

<div className="flex flex-col gap-1 justify-center items-center">
{isNicknameEdit ? (
{isUsernameEdit ? (
<>
{" "}
<p className="semibold-18 text-grayscale-darkgray">사용할 닉네임을 입력해 주세요.</p>
<p className={`medium-12 ${isValid ? "text-boarlog-100" : "text-alert-100"}`}>
한글, 영문, 숫자, -, _, ., 총 10자 이내
한글, 영문, 숫자 총 10자 이내
</p>
</>
) : (
<p className="semibold-16 text-grayscale-darkgray">[email protected]</p>
<p className="semibold-16 text-grayscale-darkgray">{localStorage.getItem("email")}</p>
)}
</div>

<div className="w-full max-w-sm">
<Button
type="full"
buttonStyle={isNicknameEdit && isValid ? "blue" : "black"}
buttonStyle={isUsernameEdit && isValid ? "blue" : "black"}
onClick={handleEditButtonClicked}
>
<EnterIcon className="fill-grayscale-white" />
{isNicknameEdit ? "닉네임 변경 완료하기" : "닉네임 변경하기"}
{isUsernameEdit ? "닉네임 변경 완료하기" : "닉네임 변경하기"}
</Button>
</div>

<h3 className="mt-12 semibold-32">강의 다시보기</h3>
<div className="flex flex-col w-full gap-6">
{DUMMY_DATA.map((value, index) => (
<ReplayLectureCard
key={index}
profileImage={value.profileImage}
date={value.date}
duration={value.duration}
user={value.user}
title={value.title}
description={value.description}
/>
))}
<div className="flex flex-col w-full gap-6 items-center">
{lectureList.length ? (
lectureList.map((value, index) => (
<ReplayLectureCard
key={index}
date={value.date}
duration={value.duration}
user={value.user}
title={value.title}
description={value.description}
onClick={() => navigate("/")}
/>
))
) : (
<>
<img className="max-w-sm w-full" src={SubLogoOriginal} />
<p className="semibold-16 text-grayscale-darkgray -mb-4">아직 수강한 강의가 존재하지 않아요.</p>
<p className="semibold-16 text-grayscale-darkgray mb-6">새로운 강의를 수강하러 가볼까요?</p>
</>
)}
</div>
</section>
</div>
Expand Down
11 changes: 7 additions & 4 deletions frontend/src/pages/MyPage/components/ReplayLectureCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ import CalendarIcon from "@/assets/svgs/calendar.svg?react";
import PlayIcon from "@/assets/svgs/play.svg?react";

interface ReplayLectureCardProps {
profileImage: string;
date: string;
duration: string;
user: string;
title: string;
description: string;
onClick: () => void;
}

const ReplayLectureCard = ({ profileImage, date, duration, user, title, description }: ReplayLectureCardProps) => {
const ReplayLectureCard = ({ date, duration, user, title, description, onClick }: ReplayLectureCardProps) => {
return (
<div className="flex flex-col w-full p-6 gap-4 justify-between bg-grayscale-white border-default rounded-xl hover:shadow-xl duration-500 cursor-pointer break-keep">
<div
className="flex flex-col w-full p-6 gap-4 justify-between bg-grayscale-white border-default rounded-xl hover:shadow-xl duration-500 cursor-pointer break-keep"
onClick={onClick}
>
<div className="flex flex-row gap-3 w-full items-center">
<img src={profileImage ? profileImage : ProfileMedium} alt="강의 진행자 프로필" className="w-12 h-12" />
<img src={ProfileMedium} alt="강의 진행자 프로필" className="w-12 h-12" />
<div className="flex flex-col w-full gap-1 justify-center items-left semibold-20">
{title}
<div className="flex flex-row gap-1 items-center medium-16 text-grayscale-darkgray">
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/pages/UserAuth/components/UserAuthSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ const UserAuthSection = ({ isSignIn, setIsSignIn }: UserAuthSectionProps) => {
type="text"
className="rounded-xl border-black w-full flex-grow medium-12 p-3 focus:outline-none focus:ring-1 focus:ring-boarlog-100 focus:border-transparent"
placeholder="이메일을 입력해주세요"
maxLength={50}
maxLength={30}
Copy link
Collaborator

Choose a reason for hiding this comment

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

EMAIL_MAX_LENGTH 처럼 상수로 따로 관리해줘도 좋을 것 같아요!

value={email}
onChange={handleEmailChange}
/>
Expand All @@ -151,7 +151,7 @@ const UserAuthSection = ({ isSignIn, setIsSignIn }: UserAuthSectionProps) => {
type="text"
className="rounded-xl border-black w-full flex-grow medium-12 p-3 focus:outline-none focus:ring-1 focus:ring-boarlog-100 focus:border-transparent"
placeholder="닉네임을 입력해주세요"
maxLength={15}
maxLength={10}
Copy link
Collaborator

Choose a reason for hiding this comment

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

이것도 NICKNAME_MAX_LENGTH로 바꿔 볼 수 있겠네요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이후 상수로 관리하는 과정에서 포함해서 개선해보겠습니다!

value={username}
onChange={handleUsernameChange}
/>
Expand All @@ -168,7 +168,7 @@ const UserAuthSection = ({ isSignIn, setIsSignIn }: UserAuthSectionProps) => {
type="password"
className="rounded-xl border-black w-full flex-grow medium-12 p-3 focus:outline-none focus:ring-1 focus:ring-boarlog-100 focus:border-transparent"
placeholder="비밀번호을 입력해주세요"
maxLength={50}
maxLength={30}
value={password}
onChange={handlePasswordChange}
/>
Expand Down