[임채민] Week20#1085
Hidden character warning
Conversation
Folder 페이지에서, FolderHeader 파일에 '링크 추가 input' 생성
commit a5e66d7 Author: Chaemin-153 <0520032@gmail.com> Date: Wed Apr 3 14:41:05 2024 +0900 :recycle: Refactor: Card 컴포넌트에서 image, create_at 에러 수정
- 라우팅 그룹 추가 - 디자인 오류 수정 - reset.scss 파일 추가
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
수고 하셨습니다 ! 위클리 미션 하시느라 정말 수고 많으셨어요. |
파트3 프로젝트 이후 전체적으로는 앱라우터로 변경했고, 15,16주차 요구사항 구현하고자 했습니다굳굳 좋습니다 ! 😊 아직 미완된 부분이 많고, 20주차 요구사항은 적용하지 못했습니다 ㅜ넵 괜찮습니다 ㅎㅎ 변경된 사항 위주로 꼼꼼히 살펴볼게요 ! |
| domains: [ | ||
| 'img1.daumcdn.net', | ||
| 'yt3.googleusercontent.com', | ||
| 'velog.velcdn.com', | ||
| 'avatars.githubusercontent.com', | ||
| 'codeit-images.codeit.com', | ||
| 'codeit-frontend.codeit.com', | ||
| 'reactjs.org', | ||
| 'assets.vercel.com', | ||
| 'storybook.js.org', | ||
| 'testing-library.com', | ||
| 'static.cdninstagram.com', | ||
| 's.pstatic.net', | ||
| 'tanstack.com', | ||
| ], |
There was a problem hiding this comment.
아마, 앱라우터 정책 상 외부 이미지를 화이트리스트로 받는다는 점에서 이렇게 설정하신 것 같군요.
요구사항은 사용자가 이미지 URL을 넣도록 되어 있어서 '모든 URL'이 될 수 있다는 점과 NextJS 보안 정책에 의해 막히고 있는 듯 합니다.
현재 채민님께서 대부분의 도메인들을 허용해주고 있으나 근본적인 해결책은 아닌 것을 자각하고 계실거라 생각해요.
이럴 경우 모든 도메인을 허용 **해주는 방법이 있으나 이는 NextJS 외부 이미지를 통한 보안을 해제하는 것과 다름 없습니다.
There was a problem hiding this comment.
그럼 어떻게 할까?
제가 제안드리는 솔루션은 특정 컴포넌트에서만 모든 외부 이미지를 허용해주는게 어떨지 제안드립니다:
function AllowedExternalImage({
src,
width,
height,
alt,
}: Pick<ImageProps, "src" | "width" | "height" | "alt">) {
return (
<Image
loader={({ src }) => src}
src={src}
alt={alt}
width={width}
height={height}
unoptimized={true}
/>
);
}
export default function Card({ title, image, id }: PostSchema) {
return (
<Link href={`/posts/${id}`} scroll={false}>
<div className="flex flex-col justify-center items-center p-4 border border-gray-300 rounded-lg cursor-pointer hover:shadow-lg transition-shadow">
{/* <Image
src={image}
alt={title}
width={300}
height={300}
className="rounded-lg"
/> */}
<AllowedExternalImage src={image} width={300} height={300} alt={title} />
<h2 className="text-2xl font-bold">{title}</h2>
</div>
</Link>
);
}위처럼 설정해두면 Card 컴포넌트에서만 모든 외부 도메인이 허용될 수 있습니다. 😊
| @@ -0,0 +1,19 @@ | |||
| 'use client'; | |||
There was a problem hiding this comment.
해당 컴포넌트는 서버 컴포넌트로 작성해도 문제될게 없어보입니다 😊
| <Head> | ||
| <title>Linkbrary - Folder</title> | ||
| </Head> |
There was a problem hiding this comment.
앱 라우터에서는 다음과 같이 title을 작성할 수 있습니다:
import type { Metadata } from 'next'
// either Static metadata
export const metadata: Metadata = {
title: '...',
}
// or Dynamic metadata
export async function generateMetadata({ params }) {
return {
title: '...',
}
}| useEffect(() => { | ||
| const accessToken = localStorage.getItem('accessToken'); | ||
|
|
||
| if (!accessToken) { | ||
| router.push('/signin'); | ||
| } | ||
| }, []); |
There was a problem hiding this comment.
해당 값을 AuthContext로 따로 만드는건 어떨까요?
컨텍스트와 컨텍스트 훅을 사용해볼 수 있을 것 같습니다.
덤으로 루트 layout을 서버 컴포넌트로 지정할 수 있겠네요. 😊
| @@ -0,0 +1,7 @@ | |||
| 'use client'; | |||
There was a problem hiding this comment.
해당 컴포넌트는 서버 컴포넌트로 만들어도 됩니다:
| 'use client'; |
| <Head> | ||
| <title>Linkbrary - Signin</title> | ||
| </Head> |
There was a problem hiding this comment.
해당 코드 또한 Metadata로 작성될 수 있습니다:
import type { Metadata } from 'next'
// either Static metadata
export const metadata: Metadata = {
title: '...',
}
// or Dynamic metadata
export async function generateMetadata({ params }) {
return {
title: '...',
}
}| <button | ||
| className={styles.submitBtn} | ||
| type="submit" | ||
| disabled={!isBtnActive} |
There was a problem hiding this comment.
disabled는 상태 관리로 관리할 필요가 없습니다 😊:
| disabled={!isBtnActive} | |
| disabled={!(email.trim() !== '' && | |
| password.trim() !== '' && | |
| password.trim().length >= 8 && | |
| password === confirmPassword && | |
| //... 그 외 validations... | |
| )} |
위 코드처럼 표현할 수 있어요. 다만 조건부 렌더링이 복잡해지겠죠?
다음과 같이 해볼 수도 있어요:
const isValidSignupForm = (email.trim() !== '' &&
password.trim() !== '' &&
password.trim().length >= 8 &&
password === confirmPassword &&
//... 그 외 validations...
);
// ...
// 렌더링 부분에서 ..
<button
className={styles.submitBtn}
type="submit"
disabled={!isBtnActive}There was a problem hiding this comment.
그리고 useMemo를 활용하여 최적화를 시킬 수도 있습니다 !
import { useMemo } from 'react';
function TodoList({ todos, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
// ...
}| if (!editFolderModalIsOpen) { | ||
| setEditFolderModalIsOpen(true); | ||
| } else { | ||
| setEditFolderModalIsOpen(false); | ||
| } |
There was a problem hiding this comment.
다음과 Not gate만으로 간단하게 토글 함수를 만들 수 있습니다 😊:
| if (!editFolderModalIsOpen) { | |
| setEditFolderModalIsOpen(true); | |
| } else { | |
| setEditFolderModalIsOpen(false); | |
| } | |
| setEditFolderModalIsOpen(!editFolderModalIsOpen); |
| <Image | ||
| className={styles.optionBtnImg} | ||
| src={deleteImg} | ||
| alt="deleteImg" |
There was a problem hiding this comment.
alt는 한국어로 편하게. 그리고 그림을 설명하듯 작성해주시면 됩니다 !
alt는 스크린 리더 사용자에 대한 보조 텍스트가 될 수 있으므로 "어떠한 이미지 인지"를 작성해주는 것이 좋아요 !
alt의 목적
- 인터넷 연결이 끊겼을 때 대체되는 이미지
- 스크린 리더 사용자를 위한 대체 텍스트
- 이미지를 볼 수 없는 환경에서 이미지를 대체하기 위한 텍스트
등 목적을 알게 된다면 alt를 어떻게 사용하시면 될지 알 수 있을 것 같아요.
다음은 하버드 에듀케이션에서 제안하는 alt 규칙입니다:
tl;dr
- Write Good Alt Text
- Add alt text all non-decorative images.
- Keep it short and descriptive, like a tweet.
- Don’t include “image of” or “photo of”.
- Leave alt text blank if the image is purely decorative
- It's not necessary to add text in the Title field.
| useEffect(() => { | ||
| const fetchProfileInfo = async () => { | ||
| try { | ||
| const response = await fetch( | ||
| 'https://bootcamp-api.codeit.kr/api/users/1' | ||
| ); | ||
| if (!response.ok) { | ||
| throw new Error('response 전달 실패'); | ||
| } | ||
| const data = await response.json(); | ||
| setProfileData(data.data[0]); | ||
| } catch (error) { | ||
| console.error('에러 발생:', error); | ||
| alert(error); | ||
| } | ||
| }; | ||
| fetchProfileInfo(); | ||
| }, []); |
There was a problem hiding this comment.
해당 값도 커스텀 훅으로 분리해볼 법 하군요 🤔
또한 모든 base url 값들을 환경 변수로 변경해보는게 어떨가 싶습니다 !
|
수고하셨습니다 채민님 !! 항상 멘토링 미팅 활발하게 활동해주셔서 진심으로 감사드립니다. 코드리뷰 중 궁금한 점 있으시면 사전 질의를 통해 편하게 물어봐주세요 ! |
|
또한, 충돌이 나서 머지를 못합니다 ㅠㅠ 충돌 해결 해주시면 감사드리겠습니다 ! |
주요 변경사항
스크린샷
멘토에게