-
Notifications
You must be signed in to change notification settings - Fork 20
Next 유선향 sprint10 #86
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
Next 유선향 sprint10 #86
The head ref may contain hidden characters: "Next-\uC720\uC120\uD5A5-sprint10"
Conversation
|
스프리트 미션 하시느라 수고 많으셨어요. |
| // | ||
| interface Props { | ||
| onClick: onClick; | ||
| onClick?: onClick; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(선택) 타입은 보통 파스칼 케이스로 사용하는게 일반적이예요 !
| onClick?: onClick; | |
| onClick?: OnClick; |
global.d.ts에서 위와 같이 명칭을 바꾸는건 어떨까요?
| // | ||
| interface Props { | ||
| onClick: onClick; | ||
| onClick?: onClick; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
또한, 해당 타입은 필요 없을 수도 있겠네요 !
| onClick?: onClick; |
(이어서)
| export default function Button({ | ||
| onClick, | ||
| children, | ||
| className, | ||
| disabled, | ||
| ...props | ||
| }: Props) { | ||
| return ( | ||
| <> | ||
| <button | ||
| onClick={onClick} | ||
| className="flex bg-blue-500 justify-center items-center w-full h-full rounded-[8px] border-none font-Pretendard text-H5Bold text-gray-100 bg-light-blue cursor-pointer" | ||
| className={clsx( | ||
| "flex bg-blue-500 justify-center items-center w-full h-full rounded-[8px] border-none font-Pretendard text-H5Bold text-gray-100 bg-light-blue cursor-pointer", | ||
| disabled ? "bg-gray-400" : "", | ||
| className | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(이어서) onClick을 지우고 다음과 같이 작성해볼 수 있겠어요
| export default function Button({ | |
| onClick, | |
| children, | |
| className, | |
| disabled, | |
| ...props | |
| }: Props) { | |
| return ( | |
| <> | |
| <button | |
| onClick={onClick} | |
| className="flex bg-blue-500 justify-center items-center w-full h-full rounded-[8px] border-none font-Pretendard text-H5Bold text-gray-100 bg-light-blue cursor-pointer" | |
| className={clsx( | |
| "flex bg-blue-500 justify-center items-center w-full h-full rounded-[8px] border-none font-Pretendard text-H5Bold text-gray-100 bg-light-blue cursor-pointer", | |
| disabled ? "bg-gray-400" : "", | |
| className | |
| )} | |
| export default function Button({ | |
| children, | |
| className, | |
| disabled, | |
| ...props | |
| }: Props) { | |
| return ( | |
| <> | |
| <button | |
| className={clsx( | |
| "flex bg-blue-500 justify-center items-center w-full h-full rounded-[8px] border-none font-Pretendard text-H5Bold text-gray-100 bg-light-blue cursor-pointer", | |
| disabled ? "bg-gray-400" : "", | |
| className | |
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
추가로 disabled도 지워볼 수 있을 것 같네요 !:
export default function Button({
children,
className,
...props
}: Props) {
return (
<>
<button
disabled={disabled}
className={clsx(
"flex bg-blue-500 justify-center items-center w-full h-full rounded-[8px] border-none font-Pretendard text-H5Bold text-gray-100 bg-light-blue cursor-pointer disabled:bg-gray-400", className
)}disabled를 버튼의 속성으로 넣게 되면 덩달아 접근성도 좋아지겠어요 !
또한, disabled를 상태 선택자를 통해서 스타일도 입힐 수 있구요 😊😊
| <input | ||
| className="relative w-full h-full bg-gray-100 font-Pretendard text-H6Regular border-none rounded-xl text-gray-800 px-4 py-6 | ||
| <div className="flex flex-col gap-6 mobile:gap-[10px] "> | ||
| <p className="text-[18px] font-Pretendard text-H4Bold">{label}</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<p> 태그는 단락을 나타냅니다 !
HTML <p> 요소는 하나의 문단을 나타냅니다. 시각적인 매체에서, 문단은 보통 인접 블록과의 여백과 첫 줄의 들여쓰기로 구분하지만, HTML에서 문단은 이미지나 입력 폼 등 서로 관련있는 콘텐츠 무엇이나 될 수 있습니다.
예를 들어 다음과 같은 값이 <p>태그에 적절할 수 있어요 😊:
Geckos are a group of usually small, usually nocturnal lizards. They are found on every continent except Antarctica.
✨ 대체 제안: <label> 태그로 변경해보세요!
| <p className="text-[18px] font-Pretendard text-H4Bold">{label}</p> | |
| <label htmlFor="fileUpload" className="text-[18px] font-Pretendard text-H4Bold"> | |
| {label} | |
| </label> |
혹은 이 텍스트가 진짜 단순한 스타일용 텍스트라면 도 괜찮아요. 하지만 입력 폼의 설명이나 타이틀 역할을 하고 있다면 <label>을 쓰는 걸 추천드려요!
| useEffect(() => { | ||
| const hasAccessToken = Boolean(localStorage.getItem("accessToken")); | ||
| setIsLogin(hasAccessToken); | ||
| }, []); | ||
|
|
||
| const handleClickLogin = async () => { | ||
| await signIn(); | ||
| setIsLogin(true); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isLogin이라는 상태는 nav 뿐 아니라 여러 곳에서 사용될 수 있겠네요 !
이럴 때, Context API와 훅을 하나 만들어두는건 어떨까요? 추가로 isLoggin 보다 userData 상태를 가지고 있어도 되겠어요. 😊
const AuthContext = createContext(null);
export const AuthProvider = ({ children }) => {
const [userData, setUserData] = useState(null);
useEffect(() => {
const token = localStorage.getItem("accessToken");
if (token) {
const userData = await getMy(); // 예시 입니다 !
setUserData({ name: userData.name, email: userData.email }); // 예시 입니다 !
}
}, []);
const signIn = async () => {
const token = await login(); // 기존 코드에 `signIn` 함수와 같음
localStorage.setItem("accessToken", token);
const userData = await getMy(); // 예시 입니다 !
setUserData({ name: userData.name, email: userData.email }); // 예시 입니다 !
};
// 추가로 signOut 함수도 필요하겠어요 !
return (
<AuthContext.Provider value={{ userData, signIn }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);| ? useFormatUpDate(value.createdAt) | ||
| : useFormatDate(value.createdAt); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두 함수는 리액트의 훅이라고 보기는 어려운 것 같아요.
유틸 함수로 사용하시는건 어떠세요?:
| ? useFormatUpDate(value.createdAt) | |
| : useFormatDate(value.createdAt); | |
| ? formatUpDate(value.createdAt) | |
| : formatDate(value.createdAt); |
| instance.interceptors.request.use( | ||
| (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => { | ||
| if (typeof window !== "undefined") { | ||
| const accessToken = localStorage.getItem("accessToken"); | ||
| if (!accessToken) return config; | ||
| config.headers.set("Authorization", `Bearer ${accessToken}`); | ||
| } | ||
| return config; | ||
| } | ||
| ); | ||
|
|
||
| instance.interceptors.response.use((response: AxiosResponse) => { | ||
| const accessToken = response.data.accessToken; | ||
| if (typeof window !== "undefined" && accessToken) { | ||
| localStorage.setItem("accessToken", accessToken); | ||
| localStorage.setItem("refreshToken", response.data.refreshToken); | ||
| } | ||
| return response; | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
크으 ~ 인터셉터를 적절히 사용하셨군요 👍👍👍
|
선향님 ~! 너무 수고 많으셨습니다. |
| export const getServerSideProps: GetServerSideProps = async (ctx) => { | ||
| const cookies = parseCookies(ctx); | ||
| const accessToken = cookies.accessToken; | ||
| const refreshToken = cookies.refreshToken; | ||
| if (!accessToken) { | ||
| return { | ||
| redirect: { | ||
| destination: "/", | ||
| }, | ||
| props: {}, | ||
| }; | ||
| } | ||
| try { | ||
| await instance.get(`/user/me`); | ||
| } catch (err: any) { | ||
| if (err.response?.status === 401) { | ||
| try { | ||
| const refreshRes = await instance.post( | ||
| `/auth/refresh-token`, | ||
| {}, | ||
| { headers: { Cookie: `refreshToken=${refreshToken}` } } | ||
| ); | ||
| const newAccessToken = refreshRes.data.accessToken; | ||
| setCookie(ctx, "accessToken", newAccessToken, { | ||
| path: "/", | ||
| httpOnly: true, | ||
| sameSite: "lax", | ||
| }); | ||
| } catch { | ||
| return { | ||
| redirect: { destination: "/" }, | ||
| props: {}, | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| return { props: {} }; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
서버사이드에서 인가처리도 훌륭히 하셨네요 ! 👍
NextJs에서 서버 자원을 사용하는 방법도 훌륭히 익히신 것 같아요 😆😆
요구사항
기본
심화
주요 변경사항
스크린샷
멘토에게