와챠의 우당탕탕 코딩 일기장

[React]#2-3 Inflearn: 만들면서 배우는 리액트: 기초/데이터 유지/리스트/폼 본문

코딩 일기장/React

[React]#2-3 Inflearn: 만들면서 배우는 리액트: 기초/데이터 유지/리스트/폼

minWachya 2023. 1. 20. 16:10
반응형

목차

  1. 리스트(배열)
  2. 기능 추가: 하트 버튼 누르면 아래 리스트에 사진 추가
  3. 폼 다루기
    1. 입력 문자 그대로 출력
    2. 소문자 입력 시 대문자로 변환
  4. 폼 검증(form validation)
    1. 한글 있는지 검사
    2. 에러 메시지 출력
    3. 빈 값 방지
  5. 데이터 유지
  6. 데이터 유지: 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') || []
      );
반응형
Comments