-
Notifications
You must be signed in to change notification settings - Fork 26
[황휘태] sprint9 #130
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
[황휘태] sprint9 #130
The head ref may contain hidden characters: "Next-\uD669\uD718\uD0DC-sprint9"
Conversation
|
스프리트 미션 하시느라 수고 많으셨어요. |
위처럼 할 일 완료/취소 과정에서 바로 적용되지 않고 새로운 데이터를 불러올 때 까지 바로 적용이 안됩니다. 이 문제를 최적화 할 수 있는 방법이 궁금합니다!크으 ~ 정말 좋은 질문입니다. 보통 UI는 서버 응답을 받은 후에 상태를 갱신합니다. 자 한 번 생각해봅시다. 완료/취소를 했을 때 통신이 실패할 확률이 얼마나 될까요?한 100번 정도 해보시면 한 번이라도 실패를 할까요? 인스타그램이나 페이스북의 좋아요 같은 기능도 마찬가지일거예요. 따라서 방법은 "됐다고 칩시다 !" 입니다. 자세한건 코드리뷰를 통해서 어떻게 바꿀 수 있는지 확인해볼게요 ! |
| export default async function addTaskAction( | ||
| prevData: ActionState, | ||
| formData: FormData | ||
| ): Promise<ActionState> { | ||
| const name = formData.get("name")?.toString(); | ||
|
|
||
| if (!name) return { status: false, error: "할 일을 입력해주세요" }; | ||
|
|
||
| try { | ||
| const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/items`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ name }), | ||
| }); | ||
|
|
||
| if (!response.ok) return { status: false, error: response.statusText }; | ||
|
|
||
| revalidateTag("todo"); | ||
|
|
||
| return { status: true, error: "" }; | ||
| } catch (error) { | ||
| return { status: true, error: `할 일 등록에 실패했습니다. ${error}` }; | ||
| } | ||
| } |
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.
와우. 서버액션을 구현하셨네요?
뭐야 무서워요.
앱 라우터를 사용하셨군요? 반갑네요 🙌🏿
|
|
||
| if (!response.ok) return { status: false, error: response.statusText }; | ||
|
|
||
| revalidateTag("todo"); |
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.
크으. revalidateTag까지 쓰시다니.
훌륭합니다. 이벤트를 기반으로 캐싱을 하고 있군요 ! 👍👍 놀라운데요 ?
| const [state, formAction, isPending] = useActionState(addTaskAction, { | ||
| status: true, | ||
| error: "", | ||
| }); |
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.
useActionState를 사용하셨군요 !
대단합니다 휘태님. React 도큐먼트를 꼼꼼히 읽어보신 것 같아요.
적절한 상황에 적절한 React 내장 훅을 잘 사용하셨네요 크으.. 👍👍
| ? `${styles.add_btn} ${styles.writing_task}` | ||
| : styles.add_btn | ||
| } | ||
| disabled={isPending ? true : 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.
삼항연산자가 필요 없을 수 있겠군요 !
| disabled={isPending ? true : false} | |
| disabled={!!isPending} |
| /** | ||
| * 로딩 스피너 컴포넌트 | ||
| * @param {string} size - 스피너의 크기 (e.g., '50px') | ||
| * @param {string} color - 스피너의 색상 (e.g., '#007bff') | ||
| */ |
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.
jsdoc까지.. 꼼꼼합니다 🥺🥺🥺
| const onClickSetTask = () => { | ||
| setTask({ name, memo: "", imageUrl: "", isCompleted: !isCompleted }); | ||
| }; |
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.
보통 외부에서 전달받는 콜백을 지을 때 on~을 사용합니다.
지금은 내부 핸들링 함수이므로 다음과 같이 작성해볼 수 있겠네요 !
| const onClickSetTask = () => { | |
| setTask({ name, memo: "", imageUrl: "", isCompleted: !isCompleted }); | |
| }; | |
| const handleClickSetTask = () => { | |
| setTask({ name, memo: "", imageUrl: "", isCompleted: !isCompleted }); | |
| }; |
| try { | ||
| const response = await fetch( | ||
| `${process.env.NEXT_PUBLIC_API_URL}/items/${taskId}`, | ||
| { | ||
| method: "PATCH", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify(data), | ||
| } | ||
| ); | ||
|
|
||
| if (!response.ok) { | ||
| alert(`완료하지 못했습니다`); | ||
| setError(true); | ||
| return; | ||
| } | ||
|
|
||
| revalidateTodo(); | ||
| } catch (error) { | ||
| alert("완료하지 못했습니다"); | ||
| setError(true); | ||
| } finally { | ||
| setIsLoading(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.
(질문에 대한 답변) 아앗.. 현재 구조에서는 낙관적 업데이트는 어렵겠군요.
음. 코드를 수정하여 제안드리려 하는데 구조적인 문제로 보여요.
지금 구조는 NextJS 서버에서 캐싱 된 값을 revalidate시켜서 페이지를 갱신시키고 있군요.
만약 낙관적 업데이트로 바꾸고 싶으시다면,
체크리스트는 상태로써 관리(CSR)해야 할거예요.
그리고 서버 사이드에서는 Codeit 백엔드로부터 받은 데이터를 캐싱하시는건 유지하시고,
클라이언트 화면에서는 UI를 먼저 업데이트 -> 서버에 반영 -> 실패 시 롤백 구조를 가져가야 합니다.
지금 구조 너무 좋으니까, 서버 컴포넌트에서 초기 데이터를 클라이언트 컴포넌트로 내려주는 방식으로 리팩토링을 하면 성능이 훨씬 좋아질거로 기대가 되네요 👍👍
|
훌륭합니다 휘태님 ! 다만, todo와 같이 유저의 인터랙션에 따라 국부적인 리렌더링이 많은 경우 CSR도 유리할 수 있습니다 😉 미션 수행하시느라 수고 많으셨습니다 휘태님 ! |

요구사항
기본
심화
주요 변경사항
스크린샷
멘토에게
이 문제를 최적화 할 수 있는 방법이 궁금합니다!