[TIL] 5월 1일 기록
오늘의 할 일
- ✅ 📌 어제자 TIL - [내일 할 일] 확인
- ✅ 📌 ~20:00 팀 데일리 미션 출제
- ✅ 📌 ~다음날 06:00 팀 데일리 미션 답변
- ✅ 📌 TIL 작성
- ✅ 어제자 데일리 미션 답변
- ✅ week6 미션 셀프 코드 리뷰 & 질문 업로드 (pr 업데이트는 내일!)
오늘의 나는 무엇을 잘했을까?
- 다른 팀원들의 데일리 미션 덕에 라우팅과 제어 컴포넌트에 대해 상세히 공부할 수 있는 기회가 되었다.
- 오늘 면접 스터디 완료💪
그리 어려운 문제는 아니었지만 어쨌든 술술 답변하고, 말투 부분에 있어서 많이 신경써서 답변해서, 팀원들의 좋은 피드백도 받을 수 있었다!
오늘의 나는 무엇을 배웠을까? ⭐(중요체크)
React로 데이터 다루기
네트워크 로딩 처리하기
데이터를 불러오는 동안에 유저가 행동하지 못하게 막아둘 필요가 있음 → isLoading
state를 따로 관리
const [isLoading, setIsLoading] = useState(false)
const handleLoad = async (options) => {
let result;
try {
setIsLoading(true);
result = await getReviews(options);
} catch (error) {
console.error(error);
return;
} finally {
setIsLoading(false);
}
...
}
return (
<button disabled={isLoading} onClick={handleLoadMore}>
더 보기
</button>
);
handleLoad 함수를 호출하는 순간, isLoading
을 true
로 설정하여 button의 disabled로 설정되어 버튼을 누르지 못하게 한 다음, 데이터를 로드하는 동안 계속 유지된다.
그러다가 데이터 로드가 끝나고 finally 구문으로 isLoading
을 false
로 바꾸어 버튼을 누를 수 있게 한다.
네트워크 에러 처리하기
네트워크가 느리거나 통신이 끊겨서 데이터를 받아오는 데 에러가 발생한 경우 → 프로그램 동작이 멈추는 위험성이 있고 또 유저에게도 에러 발생을 알려줄 필요가 있음
export async function getReviews({
order = 'createdAt',
offset = 0,
limit = 6,
}) {
const query = `order=${order}&offset=${offset}&limit=${limit}`;
const response = await fetch(
`https://learn.codeit.kr/api/film-reviews?${query}`
);
if (!response.ok) { // 👈
throw new Error('리뷰를 불러오는데 실패했습니다');
}
const body = await response.json();
return body;
}
response.ok
= response가 성공적으로 받아지면 true를 리턴하고 아니라면 false를 리턴함.
✅ !response.ok
는 정상적인 주소로 fetch를 보냈는데, 응답코드가 200이 아닌 다른 코드를 받았을 때 실행된다. 즉 잘못된 주소로 fetch를 보내면 응답 자체를 받지 못하기 때문에 이 때는 “failed to fetch” 에러가 발생한다.
const [loadingError, setLoadingError] = useState(null);
const handleLoad = async (options) => {
let result;
try {
setLoadingError(null);
setIsLoading(true);
result = await getReviews(options);
} catch (error) {
setLoadingError(error); // 👈
return;
} finally {
setIsLoading(false);
}
...
};
return (
<div>
{loadingError?.message && <span>{loadingError.message}</span>}
</div>
)
catch문에서 받은 에러를 loadingError
state에 설정 → catch문에서 return하기 때문에 finally까지만 실행되고 뒤에 있는 코드는 실행되지 않고 종료한다.
⇒ 즉 fetch 부분에서 throw error를 한 뒤, 컴포넌트 측에선 loadingError
state로 네트워크 에러를 처리한다.
리액트에선 input의 값을 주로 state로 관리한다.
state 값과 input의 값을 동일하게 만드는 게 핵심! ⇒ 제어 컴포넌트
✅ input 관련 이벤트
- onInput: 사용자가 input에 입력할 때마다 발생
- onChange: 사용자 입력이 끝났을 때 발생하는 이벤트 ⇒ 리액트에서의
onChange
prop은 바닐라 js의 onInput처럼 사용자가 값을 입력할 때마다 onChange 이벤트가 발생함
import { useState } from 'react';
function ReviewForm() {
const [title, setTitle] = useState('');
const handleTitleChange = (e) => {
setTitle(e.target.value);
};
return (
<form className="ReviewForm">
<input value={title} onChange={handleTitleChange} /> // 👈
</form>
);
}
export default ReviewForm;
JSX에서 input 태그의 onChange 이벤트 핸들러 함수에서 현재 받고 있는 값을 title state에 설정하는 코드
form 제출하기 = onSubmit
- 제출 버튼에 type=”submit”으로 설정하기
- 제출 버튼을 감싸는 form 태그에
onSubmit
이벤트 핸들러 등록하기
const handleSubmit = (e) => {
// onSubmit의 기본동작은 input value를 가지고 GET 리퀘스트를 보내는 동작임.
// 만약 이 기본동작을 막고 싶다면 preventDefault 함수 호출하기
e.preventDefault();
console.log({
title,
rating,
content,
});
};
return (
<form className="ReviewForm" onSubmit={handleSubmit}> // 👈
<input value={title} onChange={handleTitleChange} />
<input type="number" value={rating} onChange={handleRatingChange} />
<textarea value={content} onChange={handleContentChange} />
<button type="submit">확인</button> // 👈
</form>
);
하나의 state로 여러 input의 입력값 받아오기
import { useState } from 'react';
function FoodForm() {
const [values, setValues] = useState({
title: '',
calorie: 0,
content: '',
});
const handleSubmit = (e) => {
e.preventDefault();
console.log(values);
};
// ⭐ 포인트!
const handleChange = (e) => {
const {name, value} = e.target; // 👈 input의 name과 value prop을 이용한다!
setValues({
// values의 title, rating, content 중에 name과 겹칠텐데, 이렇게 겹치는 프로퍼티 key는 가장 나중에 선언한 값으로 기존 값이 덮어씌워짐
...values,
[name]: value,
});
};
return (
<form onSubmit={handleSubmit}>
<input name="title" value={values.title} onChange={handleChange} /> // 👈 name, value 프로퍼티 주의깊게 보기!
<input
type="number"
name="calorie"
value={values.calorie}
onChange={handleChange}
/>
<input name="content" value={values.content} onChange={handleChange} />
<button type="submit">확인</button>
</form>
);
}
export default FoodForm;
input이 각각 여러 개 있어도 모두 name
과 value
프로퍼티를 갖는다는 공통점을 이용해 하나의 함수로 각 input의 값을 받아와서 state로 설정할 수 있다.
*이 때 state는 모든 input의 값을 받도록 객체 형태로 선언
setValues({
...values,
[name]: value,
});
하나의 input 값만 바꿔도 모든 input 값을 관리하고 있는 객체 values의 값을 바꿔야 하며, 이 때 새로 바뀌는 값을 제외한 기존의 다른 값들은 이전 values에서 갖고와서 spread 구문을 이용한다.
만약 기존 values={title: “alice”, calorie: “100”, content: “마이떵”}
이고, 새로 받은 값이 title: “john”
이면 title이 중복되므로 가장 나중에 선언된 값으로 덮어 씌워지므로, 따라서 기존 값에서 현재 새로운 값만 업데이트한 결과를 낳는다. ⇒ values={title: “john”, calorie: “100”, content: “마이떵”}
제어 컴포넌트(Controlled Component)
input의 value값을 리액트에서 지정하고 제어
ex) 사용자는 소문자를 입력하지만 대문자로 다 변환되어 입력되어짐
const handleChange = (e) => {
const nextValue = e.target.value.toUpperCase();
setValue(nextValue);
};
<input name="title" value={value} onChange={handleChange} />
⭐ 이게 가능한 이유는 input의 value
프로퍼티에 state를 내려주었고, onChange
프로퍼티에 이벤트 핸들러 함수를 내려주었기 때문
→ 더 정확히는, 리액트의 input 태그는 value prop으로 어떤 read only 데이터를 설정할 수 있다. 따라서 사용자는 실제로 값을 입력할 때 그대로 input 화면에 설정되는 것이 아니라, 리액트 내부적으로 값을 state에 반영 → 그 state를 value prop으로 설정되어 화면에 보여지는 것이다.
비제어 컴포넌트(Unconrolled Componenet)
제어 컴포넌트와 달리, value 값을 지정하지 않는 컴포넌트 = 리액트에서 사용하는 값(state)이랑 실제 input 값이랑 다름. 경우에 따라서 필요한 방법!
⇒ 제어 컴포넌트는 input의 값과 리액트의 state 값이 항상 일치하기 때문에 동작을 예측하기가 쉽고, input 값을 여러 군데에서 쉽게 바꿀 수 있다. 따라서 주로 권장되는 방법!
ex)
import { useState } from "react";
function MyInput({value, onChange}) { // 👈 부모 컴포에서 받은 value를..
const handleChange = (e) => {
const nextValue = e.target.value;
// 이벤트 핸들러 함수는 부모 컴포에서 선언하고, 실제 호출은 자식 컴포에서 진행
onChange(nextValue);
};
return <input value={value} onChange={handleChange} />; // 👈 그대로 input vale에 연결
}
function App() {
const [value, setValue] = useState('');
const handleClear = () => setValue('');
return (
<div>
<MyInput value={value} onChange={setValue} /> // 👈 setter 함수를 prop으로 전달 가능!
<button onClick={handleClear}>지우기</button>
</div>
);
}
- 부모 컴포에서 관리하는 state를 자식 제어 컴포넌트에 전달함으로써 부모에서state의 값을 바꿈으로써 쉽게 자식 제어 컴포넌트의 input value를 바꿀 수 있음
- 위 코드에서 부모 컴포에서 handleClear 함수는 해당 state의 값을
''
로 설정하는 것이고, 이는 자식 제어 컴포넌트(MyInput
)의 input value도 그대로''
로 설정됨
오늘의 나는 무엇이 궁금했나?
week6를 진행하면서 들었던 궁금점들 → 모두 week6 pr에 질문할 예정이다
- useState 초기값으로 빈 배열 or null 중에 뭘 선택하는게 좋고 그런게 있나요?
- 실제 서비스 사이트들을 보면 아이콘은 svg 태그로 넣는 것 같은데 이렇게 바꾸는게 좋을까요?
- useEffect를 하나밖에 쓰지 못하나요? 예를 들어 어떤 state냐에 따라 작업을 달리 하는 경우에 어떻게 하나요?
오늘 하루 회고
월요벙 + 온라인 = 혼절
재택 공부는 항상 장단점이 극대화되는 것 같은데, 오늘은 단점이 더 컸던 것 같다😭 일단 집중하기가 많이 힘들어서 딴짓도 많이 하게 된 것 같고, 배우는 것들이 낯설다 보니 이해할 때까지 계속 반복해서 공부해서 지치는 것도 컸던 것 같다
하지만 하루 안에 내가 최소한으로 해야할 것들을 나만의 규칙으로 만들어서 꾸준히 지키고 있는 점은 잘하고 있는 것 같다! 괜히 다음날로 미루어서 하면 오히려 더 하기 싫어지는 것도 있고, 이렇게 최소한의 책임감을 부여하며 공부하는 것이 계속 꾸준히 할 수 있는 원동력이 되고 있다!
그리고 막판엔 집중해서 공부를 많이 할 수 있었는데, 역시 초반 기초를 이해하고 진행하니 강의에서 하는 설명은 잘 이해할 수 있게 됐다. 낼부턴 다시 오프라인 공부니까 그만큼 더 열심히 해야겠다 화이팅!