와챠의 우당탕탕 코딩 일기장
[React]#2-3 Inflearn: 만들면서 배우는 리액트: 기초/데이터 유지/리스트/폼 본문
목차
- 리스트(배열)
- 기능 추가: 하트 버튼 누르면 아래 리스트에 사진 추가
- 폼 다루기
- 입력 문자 그대로 출력
- 소문자 입력 시 대문자로 변환
- 폼 검증(form validation)
- 한글 있는지 검사
- 에러 메시지 출력
- 빈 값 방지
- 데이터 유지
- 데이터 유지: JSON
1. 리스트(배열)
리스트: 배열에서 map을 돌면서 리액트 UI를 반환한다.
map은 아래와 같이 사용함
<ul>
{favorites.map(image =>
<img src={image} />
)
}
</ul>
현재 이는 아래와 같은 코드로 이루어져 있는데
<ul className="favorites">
<CatItem img="https://cataas.com/cat/BxqL2EjFmtxDkAm2/says/inflearn" />
<CatItem img="https://cataas.com/cat/18MD6byVC1yKGpXp/says/JavaScript" />
</ul>
이거를 리스트를 사용해서 바꿔보자
이때 리스트에는 고유한 값인 key값이 필요한데, 지금은 그냥 url로 해줌,
function Favorites() {
const CAT1 = "https://cataas.com/cat/HSENVDU4ZMqy7KQ0/says/react";
const CAT2 = "https://cataas.com/cat/BxqL2EjFmtxDkAm2/says/inflearn";
const CAT3 = "https://cataas.com/cat/18MD6byVC1yKGpXp/says/JavaScript";
const cats = [CAT1, CAT2];
return (
<ul className="favorites">
{cats.map((cat) => (
<CatItem img={cat} key={cat} />
))}
</ul>
);
};
2. 기능 추가: 하트 버튼 누르면 아래 리스트에 사진 추가
const App = () => {
const CAT1 = "https://cataas.com/cat/HSENVDU4ZMqy7KQ0/says/react";
const CAT2 = "https://cataas.com/cat/BxqL2EjFmtxDkAm2/says/inflearn";
const CAT3 = "https://cataas.com/cat/18MD6byVC1yKGpXp/says/JavaScript";
// ...
// !!좋아요 누른 배열 상태!!
const [favorites, setFavorites] = React.useState([CAT1, CAT2]);
//...
// 하트 클릭 시
function handleHeartClick() {
// 기존 배열에 CAT3 추가
setFavorites([...favorites, CAT3]);
}
return (
<div>
<Title>{counter}번째 고양이 가라사대</Title>
<Form handelFormSubmit={handelFormSubmit} />
// 하위로 하트 클릭 리스너 전달
<MainCard img={mainCatImage} handleHeartClick={handleHeartClick} />
// 좋아요 누른 사진들 배열 전달
<Favorites favorites={favorites} />
</div>
);
};
ReactDOM.render(<App />, 여기다가그려);
하트 눌렀을 때의 사진들을 저장할 State를 만들고,
하트 클릭 시 동작할 핸들러를 만들어서 전달해준다.
// 하트 클릭 리스너 받아서
const MainCard = ({ img, handleHeartClick }) => {
return (
<div className="main-card">
<img
src={img}
alt="고양이"
width="400"
/>
// 하트 버튼에 리스너 추가
<button onClick={handleHeartClick}>🤍</button>
</div>
);
};
하트 버튼에 리스너를 달고
// 좋아요 누른 배열 보여주기
function Favorites({ favorites }) {
return (
<ul className="favorites">
{favorites.map((cat) => (
<CatItem img={cat} key={cat} />
))}
</ul>
);
};
좋아요를 누른 배열은 map을 사용해 출력한다.
3. 폼 다루기
3-1. 입력 문자 그대로 출력
console.log(e.target.value);
event > target > value값을 출력해주면 된다.
전체 코드는 아래와 같다.
form의 onChange에 핸들러를 달아, 사용자가 입력 시마다 입력 글자들을 출력할 수 있다.
function handelInputChange(e) {
console.log(e.target.value);
}
return (
<form onSubmit={handelFormSubmit}>
<input
type="text"
name="name"
placeholder="영어 대사를 입력해주세요"
onChange={handelInputChange}
/>
<button type="submit">생성</button>
</form>
);
3-2. 소문자 입력 시 대문자로 변환
const [value, setValue] = React.useState("");
input의 text Vaule를 관리할 State를 생성해주고,
setValue(e.target.value.toUpperCase());
onChange 이벤트 발생 시마다 입력한 문자를 toUpperCase()를 사용해 대문자로 바꿔준다.
이때 form의 value값을 State의 vaule값으로 초기화해준다.
const Form = ({ handelFormSubmit }) => {
// 텍스트 상태
const [value, setValue] = React.useState("");
// 텍스트 입력 시 소문자 -> 대문자로 바꿔주는 핸들러
function handelInputChange(e) {
setValue(e.target.value.toUpperCase());
}
return (
<form onSubmit={handelFormSubmit}>
<input
type="text"
name="name"
// 텍스트 값 달기
value={value}
placeholder="영어 대사를 입력해주세요"
// 핸들러 달기
onChange={handelInputChange}
/>
<button type="submit">생성</button>
</form>
);
};
4. 폼 검증(form validation)
4-1. 에러 메시지 출력
const [errorMsg, setErrorMsg] = React.useState("");
에러 발생 시의 상태를 저장할 State을 생성해준다.
에러 메시지가 뜰 공간을 만들어주고 value값으로 errorMsg릏 넣어주면
한글이 포함되었을 때 에러를 출력하게 된다.
4-2. 한글 있는지 검사
const includesHangul = (text) => /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/i.test(text);
text 전달 시 해당 text에 한글이 포함됐는지 아닌지를 반환하는 함수를 사용한다.
// 한글 입력 방지
if (includesHangul(userValue)) setErrorMsg("한글은 입력할 수 없습니다.");
else setErrorMsg("");
<form onSubmit={handelFormSubmit}>
<input
type="text"
name="name"
value={value}
placeholder="영어 대사를 입력해주세요"
onChange={handelInputChange}
/>
<button type="submit">생성</button>
<p style={{ color: 'red' }}>{errorMsg}</p>
</form>
4-3. 빈값 방지
function handelFormSubmit(e) {
e.preventDefault(); // 폼 제출 시 리프레시 방지
// 초기화
setErrorMsg("");
if (value == "") {
setErrorMsg("빈 값으로 만들 수 없습니다.");
return;
}
updateMainCat();
}
폼 제출 시 동작하는 핸들러에
빈값이 들어올 시 에러 메시지 상태를 바꾼 뒤 바로 리턴한다.
전체 코드는 다음과 같다.
const Form = ({ updateMainCat }) => {
// 한글 있는지 검사
const includesHangul = (text) => /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/i.test(text);
// 폼 사용자 입력값
const [value, setValue] = React.useState("");
// 에러 메시지 상태값
const [errorMsg, setErrorMsg] = React.useState("");
function handelInputChange(e) {
const userValue = e.target.value;
// 한글 입력 방지
if (includesHangul(userValue)) setErrorMsg("한글은 입력할 수 없습니다.");
else setErrorMsg("");
setValue(e.target.value.toUpperCase());
}
function handelFormSubmit(e) {
e.preventDefault();
// 초기화
setErrorMsg("");
if (value == "") {
setErrorMsg("빈 값으로 만들 수 없습니다.");
return;
}
updateMainCat();
}
return (
<form onSubmit={handelFormSubmit}>
<input
type="text"
name="name"
value={value}
placeholder="영어 대사를 입력해주세요"
onChange={handelInputChange}
/>
<button type="submit">생성</button>
<p style={{ color: 'red' }}>{errorMsg}</p>
</form>
);
};
5. 데이터 유지
브라우저 자체 데이터베이스에 사용자 데이터를 저장할 수 있다!!!
로컬 스토리지라는 브라우저 기본 api를 사용하여 저장할 수 있다.
이렇게 로컬 스토리지에 데이터를 저장하는 것을 로컬 스토리지에 데이터 싱크하기라고 한다.
이 데이터는 7일간 유지된다.
저장하기
localStorage.setItem(key, value);
가져오기
localStorage.getItem(key);
이를 사용해 타이틀 숫자의 상태를 로컬 디비에 저장해보겠다 ㅎㅎ
// 타이틀 숫자 상태
const [counter, setCounter] = React.useState(
Number(localStorage.getItem('counter'))
);
타이틀 숫자의 State를 로컬에서 얻어온다.
이때, 이렇게 바로 가져오면 string으로 저장되기 때문에 number로 바꿔주어야 한다.
이거 안 해주면 3 + 1 = 31 이런 사태가 벌어진다 ㄷㄷ
// 폼 제출 시 메인 사진 변경 및 타이틀 숫자 증가
function updateMainCat() {
setMainCatImage(CAT2);
const nextCounter = counter + 1
setCounter(nextCounter);
localStorage.setItem('counter', nextCounter);
}
폼 제출 시에 타이틀 숫자가 1씩 증가되어 바로 저장되도록 구현해주면 완성
<Form updateMainCat={updateMainCat} />
위 함수를 폼에 전달하여
<form onSubmit={handelFormSubmit}>
<input
type="text"
name="name"
value={value}
placeholder="영어 대사를 입력해주세요"
onChange={handelInputChange}
/>
<button type="submit">생성</button>
<p style={{ color: 'red' }}>{errorMsg}</p>
</form>
이렇게 제출 시마다 동작하도록 설정해주면 된다.ㅎㅎ
6. 데이터 유지: JSON
위에서는 localStorage로 바로 저장하고 사용하면 string으로 저장된다는 문제점이 있었는데,
이는 아래 코드를 사용해서 해결할 수 있다.
const jsonLocalStorage = {
setItem: (key, value) => {
localStorage.setItem(key, JSON.stringify(value));
},
getItem: (key) => {
return JSON.parse(localStorage.getItem(key));
},
};
이를 사용해 5번 코드를 고치면 아래아 같다.
이제 Number()를 사용하지 않아도 잘 돌아간다!
// 타이틀 숫자 상태
const [counter, setCounter] = React.useState(
jsonLocalStorage.getItem('counter')
);
// ------------
jsonLocalStorage.setItem('counter', nextCounter);
+ 가져온 배열이 널값이면 빈 배열로 초기화
// 좋아요 누른 배열 상태
const [favorites, setFavorites] = React.useState(
jsonLocalStorage.getItem('favorites') || []
);