[황휘태] sprint9#130
Hidden character warning
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.
와우. 서버액션을 구현하셨네요?
뭐야 무서워요.
앱 라우터를 사용하셨군요? 반갑네요 🙌🏿
|
|
||
| if (!response.ok) return { status: false, error: response.statusText }; | ||
|
|
||
| revalidateTag("todo"); |
There was a problem hiding this comment.
크으. revalidateTag까지 쓰시다니.
훌륭합니다. 이벤트를 기반으로 캐싱을 하고 있군요 ! 👍👍 놀라운데요 ?
| const [state, formAction, isPending] = useActionState(addTaskAction, { | ||
| status: true, | ||
| error: "", | ||
| }); |
There was a problem hiding this comment.
useActionState를 사용하셨군요 !
대단합니다 휘태님. React 도큐먼트를 꼼꼼히 읽어보신 것 같아요.
적절한 상황에 적절한 React 내장 훅을 잘 사용하셨네요 크으.. 👍👍
| ? `${styles.add_btn} ${styles.writing_task}` | ||
| : styles.add_btn | ||
| } | ||
| disabled={isPending ? true : false} |
There was a problem hiding this comment.
삼항연산자가 필요 없을 수 있겠군요 !
| disabled={isPending ? true : false} | |
| disabled={!!isPending} |
| /** | ||
| * 로딩 스피너 컴포넌트 | ||
| * @param {string} size - 스피너의 크기 (e.g., '50px') | ||
| * @param {string} color - 스피너의 색상 (e.g., '#007bff') | ||
| */ |
| const onClickSetTask = () => { | ||
| setTask({ name, memo: "", imageUrl: "", isCompleted: !isCompleted }); | ||
| }; |
There was a problem hiding this comment.
보통 외부에서 전달받는 콜백을 지을 때 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.
(질문에 대한 답변) 아앗.. 현재 구조에서는 낙관적 업데이트는 어렵겠군요.
음. 코드를 수정하여 제안드리려 하는데 구조적인 문제로 보여요.
지금 구조는 NextJS 서버에서 캐싱 된 값을 revalidate시켜서 페이지를 갱신시키고 있군요.
만약 낙관적 업데이트로 바꾸고 싶으시다면,
체크리스트는 상태로써 관리(CSR)해야 할거예요.
그리고 서버 사이드에서는 Codeit 백엔드로부터 받은 데이터를 캐싱하시는건 유지하시고,
클라이언트 화면에서는 UI를 먼저 업데이트 -> 서버에 반영 -> 실패 시 롤백 구조를 가져가야 합니다.
지금 구조 너무 좋으니까, 서버 컴포넌트에서 초기 데이터를 클라이언트 컴포넌트로 내려주는 방식으로 리팩토링을 하면 성능이 훨씬 좋아질거로 기대가 되네요 👍👍
|
훌륭합니다 휘태님 ! 다만, todo와 같이 유저의 인터랙션에 따라 국부적인 리렌더링이 많은 경우 CSR도 유리할 수 있습니다 😉 미션 수행하시느라 수고 많으셨습니다 휘태님 ! |

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