-
Notifications
You must be signed in to change notification settings - Fork 39
[이태경] Sprint9/Refactor #244
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/Refactor #244
The head ref may contain hidden characters: "Next-\uC774\uD0DC\uACBD-sprint9-r"
Conversation
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.
/src/features/todo/components → /src/app/_components/Empty 이동되었습니다!
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.
네, 만약 공용 폴더라면 app 바깥에 components 폴더를 만들어 유지해주시고,
특징 라우트그룹/ 페이지에서만 쓰이는 컴포넌트라면 이렇게 유지해주시면 될것같아요 :)
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.
서버 액션을 사용해봤습니다. 파일 위치가 app 폴더 내에 있는게 좀 이상한거 같긴 한데, 공식 문서에서도 app 폴더 내에 위치하고 있어서 여기에 생성을 했습니다.
| const initialDataRef = useRef<TodoItemType[]>(data); | ||
| const [optimisticState, toggleOptimisticState] = useOptimistic< | ||
| TodoItemType[], | ||
| number | ||
| >(todoAll, (currentState, id) => { | ||
| >(initialDataRef.current, (currentState, id) => { | ||
| return currentState.map((todo) => | ||
| todo.id === id ? { ...todo, isCompleted: !todo.isCompleted } : 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.
말씀하셨던대로 useOptimistic의 초기값을 data로 설정했지만 여전히 이전 데이터로 돌아가더라구요....
뭔가 page에서 내려주는 데이터가 최신화가 아닌 상태로 props로 계속 내려주고 있는 와중에 TodoListArea가 계속 렌더링 되면서 useOptimistic를 계속 초기화값으로 되돌리는게 아닌가라고 추측을 해서..(사실인지 아닌지는 잘 모르겠습니다..) 어쩔수 없이 useRef를 사용할 수 밖에 없었습니다..
여기 부분은 미션10에서 리액트쿼리 사용하면서 다시 적용해보겠습니다!
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.
ㅎㅎ 제가 이 문제를 전체 코드베이스 훑어보며 좀 파보니까, 구조적으로 이런 문제가 있더라고요
- page.tsx 컴포넌트의
<TodoAddForm/>에서 새로운 할 일을 추가할 때createTodoItemAction이 실행됨 - 이 액션에서
revalidateTag("todoList")가 호출되어 캐시가 무효화됨 - Next.js는 캐시가 무효화되면 서버 컴포넌트를 다시 렌더링하므로, page.tsx의
getTodoList()가 다시 호출되어 새로운 데이터를 가져오고 - 이 과정을 통해 새로운 data prop이 TodoListArea에 전달됨
- 새로운 data prop이 전달되면 useOptimistic의 초기값이 재설정됨
결론적으로 이 문제는 서버 컴포넌트의 리렌더링 때문에 발생하는 현상입니다.
서버 상태와 클라이언트 상태를 분리하셔야할것같아요.
예를 들면
const TodoListArea = ({ data }: Props) => {
// 클라이언트 상태로 서버 데이터 관리
const [clientData, setClientData] = useState(data);
...이런식으로 클라이언트 상태로 서버 데이터를 관리하는게 부가적으로 필요할것같고,
useOptimistic의 초기값을 clientData로 고정한다음
서버 데이터가 변경될 때 + API 응답 성공시 클라이언트 데이터를 업데이트하는 과정이 필연적일것같네요.
결과를 보면 이전에 태경님이 생각하셨던 방식과 비슷하게 풀어가는게 좋을 것 같아요.
다만, 이제 정확한 구조적 원인을 파악했으니 이 문제를 클라이언트 / 서버 상태를 분리하는 방식으로 해결해본다는 점이 이전에 드렸던 피드백과 살짝 달라지겠네요 :)
한번 참고해보시고, 더 좋은 방법이 있을지 고민해보세요!
한단계 더 나아가기위한 좋은 리팩토링 주제입니다 👍
addiescode-sj
left a comment
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.
리팩토링 수고하셨습니다~! 🤩
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.
네, 만약 공용 폴더라면 app 바깥에 components 폴더를 만들어 유지해주시고,
특징 라우트그룹/ 페이지에서만 쓰이는 컴포넌트라면 이렇게 유지해주시면 될것같아요 :)
| const initialDataRef = useRef<TodoItemType[]>(data); | ||
| const [optimisticState, toggleOptimisticState] = useOptimistic< | ||
| TodoItemType[], | ||
| number | ||
| >(todoAll, (currentState, id) => { | ||
| >(initialDataRef.current, (currentState, id) => { | ||
| return currentState.map((todo) => | ||
| todo.id === id ? { ...todo, isCompleted: !todo.isCompleted } : 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.
ㅎㅎ 제가 이 문제를 전체 코드베이스 훑어보며 좀 파보니까, 구조적으로 이런 문제가 있더라고요
- page.tsx 컴포넌트의
<TodoAddForm/>에서 새로운 할 일을 추가할 때createTodoItemAction이 실행됨 - 이 액션에서
revalidateTag("todoList")가 호출되어 캐시가 무효화됨 - Next.js는 캐시가 무효화되면 서버 컴포넌트를 다시 렌더링하므로, page.tsx의
getTodoList()가 다시 호출되어 새로운 데이터를 가져오고 - 이 과정을 통해 새로운 data prop이 TodoListArea에 전달됨
- 새로운 data prop이 전달되면 useOptimistic의 초기값이 재설정됨
결론적으로 이 문제는 서버 컴포넌트의 리렌더링 때문에 발생하는 현상입니다.
서버 상태와 클라이언트 상태를 분리하셔야할것같아요.
예를 들면
const TodoListArea = ({ data }: Props) => {
// 클라이언트 상태로 서버 데이터 관리
const [clientData, setClientData] = useState(data);
...이런식으로 클라이언트 상태로 서버 데이터를 관리하는게 부가적으로 필요할것같고,
useOptimistic의 초기값을 clientData로 고정한다음
서버 데이터가 변경될 때 + API 응답 성공시 클라이언트 데이터를 업데이트하는 과정이 필연적일것같네요.
결과를 보면 이전에 태경님이 생각하셨던 방식과 비슷하게 풀어가는게 좋을 것 같아요.
다만, 이제 정확한 구조적 원인을 파악했으니 이 문제를 클라이언트 / 서버 상태를 분리하는 방식으로 해결해본다는 점이 이전에 드렸던 피드백과 살짝 달라지겠네요 :)
한번 참고해보시고, 더 좋은 방법이 있을지 고민해보세요!
한단계 더 나아가기위한 좋은 리팩토링 주제입니다 👍
질문에 대한 답변
폴더 구조를 짜는데 완벽한 정답은 없습니다 :) 만약
|
요구사항
기본
주요 변경사항
/app/_components/Empty폴더로 위치 이동스크린샷
멘토에게
검색을 해봤을 땐 프로젝트의 규모에 따라 다르겠지만 app 폴더와 형제 폴더로 components, api, ... 이런식으로 사용한다는 포스팅을 보기도 했었고, 큰 규모로 가면 feature 폴더를 추가하기도 한다고 봤었어요. 참고했던 포스트 입니다!
그래서 강사님이 route group과 private folder를 언급 해주셨을 때 좀 혼란이 오기도 했었습니다! 말씀하셨던 뜻이 이렇게 진행을 하라고 하셨던게 맞는지도 궁금하기도 하구요 ㅎㅎ 곧 있으면 심화 프로젝트에 들어가기도 해서 혹시 넥스트로 진행을 하게 된다면 폴더 구조를 어떻게 짜는게 좋을지 혹시 참고할만한 글이 있을지 (구글링을 했을 땐 거의 첨부 드린 링크랑 내용이 거의 비슷했어요 ㅎㅎ) 궁금합니다!