-
Notifications
You must be signed in to change notification settings - Fork 46
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
[우재현] Sprint10 #317
The head ref may contain hidden characters: "Next-\uC6B0\uC7AC\uD604-sprint10"
[우재현] Sprint10 #317
Conversation
be6caa0
to
9e37071
Compare
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.
수고하셨습니다~ 👍
const response = await axios({ | ||
method: "post", | ||
url: "/articles", | ||
data: { | ||
content, | ||
title, | ||
image, | ||
}, | ||
}); |
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.
axios 사용할 때 현재 처럼 설정 값을 객체 통해 지정하고 있는데, 아래 처럼 메서드를 직접 사용하는 방식이 보다 간결하고 명확해서 변경하면 좋겠습니다. 😸
기능적 차이는 없어요~
await axios.post('/articles', params);
const response = await axios.get(`/articles/${id}`); | ||
return response.data; |
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.
아래처럼 응답 타입에 제너릭을 추가할 수 있습니다.
const { data } = await axios.get<Article>(`/articles/${id}`);
return data;
}: SignUpParams): Promise<AuthResponse> { | ||
const response = await axios({ | ||
method: "post", | ||
url: "/auth/signUp", |
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.
이러한 URL은 contants 디렉토리 내 api.ts 파일을 하나 만들어서 상수로 관리하면 좋겠어요.
url: AUTH_SIGN_UP_URL,
// 기존 인터셉터 제거 | ||
if (interceptorId !== undefined) { | ||
instance.interceptors.request.eject(interceptorId); | ||
} | ||
|
||
// 새로운 인터셉터 추가 | ||
interceptorId = instance.interceptors.request.use((config) => { | ||
config.headers["Authorization"] = value; | ||
return config; | ||
}); |
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.
오호 interceptors 아이디어 좋습니다. 👍
interface GetCommentListByArticleId { | ||
articleId: number; | ||
limit: number; | ||
cursor?: number; | ||
} | ||
|
||
async function getCommentListByArticleId({ | ||
articleId, | ||
limit, | ||
cursor, | ||
}: GetCommentListByArticleId) { | ||
const response = await axios.get(`/articles/${articleId}/comments`, { | ||
params: { | ||
limit, | ||
cursor, | ||
}, | ||
}); | ||
return response.data; | ||
} |
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.
여기서 GetCommentListByArticleId 값이 지역 타입이기도 하고, 타입이라 예상되지만 이처럼 PascalCase 로 네이밍이 되게되면 혼란이 야기될 수 있어서 타입 혹은 인터페이스 뒤에 props 붙이는 방법도 있습니다. 즉 GetCommentListByArticleIdProps 이처럼요.
useEffect(() => { | ||
document.addEventListener("click", handler); | ||
|
||
return () => { | ||
document.removeEventListener("click", handler); | ||
}; | ||
}, [handler]); |
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.
remove 처리 까지 굳!
import { setInstanceHeaders } from "@/api/axios"; | ||
|
||
export default function setLogOut() { | ||
sessionStorage.removeItem("user"); | ||
sessionStorage.removeItem("accessToken"); | ||
sessionStorage.removeItem("refreshToken"); | ||
sessionStorage.removeItem("birth"); | ||
setInstanceHeaders(); | ||
} |
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.
물론 해당 코드가 임시코드지만, 이런 반복되는 코드를 작성할 땐 아래처럼 생각해볼 수 있어요!
const STORAGE_KEYS = ['user', 'accessToken', 'refreshToken', 'birth'] as const;
export default function setLogOut() {
STORAGE_KEYS.forEach(key => sessionStorage.removeItem(key));
setInstanceHeaders();
}
export async function getServerSideProps(context: any) { | ||
const { id } = context.params; | ||
|
||
const article = await getArticle({ id }); | ||
const initComments = await getCommentListByArticleId({ | ||
articleId: id, | ||
limit: 10, | ||
}); | ||
|
||
return { | ||
props: { | ||
article, | ||
initComments, | ||
}, | ||
}; | ||
} |
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.
일반적으로 getServerSideProps 부분은 아래에 위치하고 ArticleDetail 컴포넌트가 상위로 올라오는 구조로 작성합니다!
export default function ArticleDetail({
article,
initComments,
}: {
article: Article;
initComments: CommentList;
}) {
...
}
export async function getServerSideProps(context: any) {
const { id } = context.params;
const article = await getArticle({ id });
const initComments = await getCommentListByArticleId({
articleId: id,
limit: 10,
});
return {
props: {
article,
initComments,
},
};
}
|
||
interface CommentFormProps { | ||
value: string; | ||
onChange: (value: string) => void; | ||
onSubmit: () => void; | ||
} | ||
|
||
function CommentForm({ value, onChange, onSubmit }: CommentFormProps) { | ||
const [valid, setValid] = useState(false); | ||
|
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.
이런 타입들이 지역 타입이더라도 중간에 작성되는 것 보단, 상위로 올리는게 가독성이 좋습니다.
return ( | ||
<> | ||
<header className={styles.header}> | ||
<div className={styles.wrap}> | ||
<h2 className={styles.title}>{article.title}</h2> | ||
<Dropdown /> | ||
</div> | ||
<div className={styles.wrap}> | ||
<div className={styles.info}> | ||
<div className={styles.profile}> | ||
<Image fill src="/images/profile.svg" alt="프로필" /> | ||
</div> | ||
<span className={styles.writer}>{article.writer.nickname}</span> | ||
<span className={styles.createdAt}> | ||
{formatDate(article.createdAt)} | ||
</span> | ||
</div> | ||
<div className={styles.likeCount}> | ||
<div className={styles.heart}> | ||
<Image fill src="/images/ic_heart.svg" alt="좋아요" /> | ||
</div> | ||
<span>{article.likeCount}</span> | ||
</div> | ||
</div> | ||
</header> | ||
<main className={styles.content}>{article.content}</main> | ||
<CommentForm | ||
value={commentValue} | ||
onChange={handleChangeComment} | ||
onSubmit={handleSubmitComment} | ||
/> | ||
<CommentListWrap comments={comments} /> | ||
<Link href="/board" className={styles.buttonBack}> | ||
<span>목록으로 돌아가기</span> | ||
<div className={styles.iconBack}> | ||
<Image fill src="/images/ic_back.svg" alt="프로필" /> | ||
</div> | ||
</Link> | ||
</> |
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.
꼭 재사용이 안되더라도, 현재 파일 기준으로는 가독성과 유지보수를 위해 컴포넌트를 분리하는 방법이 더 좋겠어요.
추가적으로 예시를 들자면, DropDown 컴포넌트에서만 사용하는 useOutsideClick hook도 분리된 컴포넌트에서 호출하는게 더 좋겠어요.
const { comments, handleComment } = useComment(article.id, initComments);
return (
<>
<Components />
<Components />
<Components />
<Components />
</>
)
요구사항
기본
게시글 등록 페이지
/addboard
입니다.게시글 상세 페이지
/board/{id}
입니다.심화
게시글 등록 페이지
주요 변경사항
스크린샷
멘토에게