return을 사용하지 않으면 setTodos는 아무것도 받지 못합니다. 즉, 상태를 업데이트할 수 없습니다. return을 사용하여 map() 함수가 반환한 새로운 배열을 setTodos에게 전달해야 합니다. 즉 return은 “새로운 상태를 React에게 전달하는 방법”입니다. 이를 통해 React가 상태를 올바르게 업데이트할 수 있습니다.
개념 살펴보기 React에서 상태를 업데이트할 때, setState 함수에 새로운 상태를 전달합니다. 상태는 보통 배열이나 객체와 같은 복잡한 데이터 구조입니다. 우리는 상태를 변경할 때, 변경된 새로운 데이터를 React에게 전달해줘야 합니다.
1. 현재 상태를 받아오기: setTodos 함수에 콜백 함수(함수 안의 함수)를 전달합니다. 이 콜백 함수는 현재 상태인 todos 배열을 인수로 받습니다.
todos => {
// 이 안에서 todos는 현재 상태입니다.
}
2.상태 변경:
todos.map()을 사용하여 현재 상태인 todos 배열의 각 항목을 순회합니다. 각 항목이 updatedTodo의 id와 일치하는지 확인합니다.
2-2) 낙관적 업데이트 미리 수정 전송했던 그 데이터로 화면을 갱신 --> 데이터를 받으려면 시간이 걸리니까 일단 먼저 갱신('어차피 성공하겄지' 성공할 것을 예상하고) --> 실패한 경우에는 미리 갱신한 것 다시 되돌리는 방법도 있음
- 장점: 사용자가 수정을 시도하는 순간 화면이 갱신되니까 속도가 굉장히 빠르다고 느낄 수 있음
cf. 낙관적 업데이트란 ?
낙관적 업데이트(Optimistic UI Update)는 사용자 경험을 향상시키기 위한 전략으로, 서버에서 실제 데이터가 업데이트되기 전에 UI를 먼저 업데이트하여 사용자에게 더 빠른 반응을 제공하는 방법입니다. 이 방법은 서버 요청이 성공할 것이라고 가정하고, 서버 요청이 실패할 경우에만 UI를 다시 롤백합니다.
엔터를 누르자 마자 title 곧바로 변경
cf. 낙관적 업데이트 이후 코드에서 에러가 나는 상황 처리
async function updateTodo() {
// 낙관적 업데이트: UI를 먼저 업데이트
setTodo({
...todo,
title: title // title은 수정된 title로 바로 적용
})
console.log('서버로 전송', title)
try {
// 서버에 PUT 요청
const res = await fetch(
`https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos/${todo.id}`,
{
method: 'PUT',
headers: {
'content-type': 'application/json',
apikey: '5X8Z1k7M2vU5Q',
username: 'Grepp_KDT4_ParkYoungWoong'
},
body: JSON.stringify({
title, // 수정된 title
done: todo.done
})
}
)
// 서버 응답을 받아 업데이트된 todo를 얻음
const updatedTodo: Todo = await res.json()
console.log(updatedTodo, title)
// 이미 낙관적 업데이트로 상태를 업데이트했기 때문에, 이 부분은 선택 사항
// setTodo(updatedTodo)
} catch (error) {
console.error('서버 요청 실패:', error)
// 어 ?! 문제가 생겼네 ..
// 근데 나는 낙관적으로 업데이트를 이미 했는데 ..
// 어쩌지 ?
// 서버 요청 실패 시 롤백하는 로직을 추가할 수 있음 !!
setTodo(todo)
}
}
fetch 사용과 에러처리
fetch API는 HTTP 요청이 실패해도 기본적으로 네트워크 에러(예: 인터넷 연결 문제)만 catch 블록으로 전달합니다. HTTP 상태 코드가 4xx나 5xx인 경우는 에러로 간주되지 않으며, 이 경우에는 직접 처리해야 합니다.
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
--> fetch 응답의 ok 속성을 검사하여 HTTP 상태 코드가 성공적이지 않을 경우 에러를 던집니다. 이렇게 하면 네트워크 에러 뿐만 아니라 HTTP 에러도 catch 블록에서 처리할 수 있습니다.
async function updateTodo() {
// 낙관적 업데이트: UI를 먼저 업데이트
setTodo({
...todo,
title: title // title은 수정된 title로 바로 적용
})
console.log('서버로 전송', title)
try {
const res = await fetch(
`https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos/${todo.id}`,
{
method: 'PUT',
headers: {
'content-type': 'application/json',
apikey: '5X8Z1k7M2vU5Q',
username: 'Grepp_KDT4_ParkYoungWoong'
},
body: JSON.stringify({
title, // 수정된 title
done: todo.done
})
}
)
// HTTP 응답 상태 코드가 성공적인지 확인
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
const updatedTodo: Todo = await res.json()
console.log(updatedTodo, title)
} catch (error) {
console.error('서버 요청 실패:', error)
setTodo(todo)
}
}
두 번째 방법 1) 수정할 데이터를 서버로 전송 (성공) 2) 새로운 목록 자체를 서버에서 다시 가져옴 (입력값으로 수정된 목록) - 장점: 작성하기 편리함 - 단점: 네트워크 통신을 두 번 해야 함 (성능 최적화 측면에서는 조금 아쉽) --> 트레이드 오프 관계
updatetodo() 로 목록 업데이트 하고 나서 getTodo()로 목록을 가져오기
import/export 키워드는 하나의 파일의 최상위 레벨에 있어야 사용 가능 !
App 밖으로 빼서 최상위 레벨에 위치하게 만들어줘야 함
밖으로 빼지 않는 방법은 없을까 ?
tsx 파일은 파일이자 리액트 컴포넌트 ! 리액트 컴포넌트는 import/export 말고도 데이터 전달이 가능한 상황이 있음
--> getTodos()를 TodoItem에 컴포넌트로 직접 전달
--> props 형식으로 전달 (함수도 참조형 데이터 중 하나이기 때문에 전달 가능)
<TodoItem todo={todo} getTodos={getTodos}/>
TodoItem.tsx
--> 네트워크 통신이 2번 되는 모습
데이터가 삭제 되었을 때 화면 갱신 실습
두 번째 방법 1) 수정할 데이터를 서버로 전송 (성공) 2) 새로운 목록 자체를 서버에서 다시 가져옴 (입력값으로 수정된 목록) - 장점: 작성하기 편리함 - 단점: 네트워크 통신을 두 번 해야 함 (성능 최적화 측면에서는 조금 아쉽) --> 트레이드 오프 관계
deleteTodo로 todoItem을 지운 후에 getTodo로 목록을 다시 가져와라 !
--> trade off ! 코드는 간단하지만 네트워크 통신이 두 번되고 있음 !
첫 번째 방법 1) 수정할 데이터를 서버로 전송 (성공) --> 수정한 데이터의 결과를 응답해주는데
2-1) 응답 받은 결과로 화면을 갱신 (수정된 부분만 갈아끼우는 것 !)
filter 메소드 설명 1) id가 'c'인 경우 false를 반환하니까 결국 세 번째 객체(true)는 반환하지 않음 2) id가 c랑 같지 않을 경우 반환
map과 filter
전체 코드
프로젝트 버전관리 시작하기
git
'--' --> flag
git config --global --list
--list: 현재 설정된 Git 설정 목록을 표시
force flag
웬만하면 쓰지 않기(코드를 다 덮어써버리기 때문)
어떤 결과가 나올 것인지를 내가 명백하게 알고 있을 때 사용하기
git push origin main --force
// 축약형: -f
상태관리를 위한 git 명령
git init (초기화) git status git add . (스테이징) git status git commit -m "커밋 메세지"
레파지토리 생성 시 꼭 필요한 경우 아니면 README 파일 안 만드는게 좋음 (충돌방지)
git remote add origin 'https://github.com/suhyun9892/beginner-react-todo.git' --> origin: 내가 원하는 이름으로 써도 상관없지만, 통상 origin이라고 씀 git push origin main
cf. 하나의 프로젝트에 여러 저장소를 연결하고 싶다면 ? 별칭을 다르게 ! git remote add bitbucket '다른 주소 복사한 것' git push bitbucket main