[Udemy] React 기본 - 일기장 만들기(1)

React 기본 (Project)

Udemy - 한입크기로 잘라 먹는 리액트


📌 일기장 만들기 (1)

src/App.js

import "./App.css";
import DiaryEditor from "./DiaryEditor";

const App = () => {
  return (
    <div className="App">
      <DiaryEditor />
    </div>
  );
};
export default App;

src/DiaryEditor.js

☑️ DiaryEditor : 일기장 컴포넌트

  • 클래스의 네임을 컴포넌트의 네임과 일치 (직관적인 스타일 코드 작성)
  • 작성자(input), 일기 본문(textarea), 감정점수(select), 저장하기(button)

☑️ useState : 요소를 리액트가 직접 핸들링할 수 있도록 만들어야 함

☑️ 상태변화 함수 : input과 textarea가 받는 value prop으로 전달

  • useState("작성자"); : 초기값 설정

  • onChange
    값이 실시간으로 바뀌도록 이벤트 설정(콜백함수 등록하여 event 객체인 e를 매개변수로 전달받게 됨)
    사용자가 입력을 하면 어떤 사건이 발생했다고 인식하는데 onChange는 값이 바뀌었을 때 수행하는 이벤트
    = input에 값이 바뀌었을 때 onChange의 Prop에 전달한 콜백함수를 수행한다라고 생각하면 됨

  • e.target.value
    console을 통해 전달받은 이벤트 객체를 열어보면 target을 키를 열면 지금 바뀐 value 확인 가능
    이벤트 객체의 target의 value를 이용하면 입력하고 있는 값을 콜백함수에 불러서 사용 가능
    input이 변화해야 하는 값

  • setAuthor(e.target.value), setContent(e.target.value)
    작성자 input이 변화해야 하는 값이 변화할 때마다 그 값으로 업데이트 시키면 state와 함께 input을 이용할 수 있게 됨
    콜백함수가 수행될 때 매개변수인 이벤트 객체 e에서 지금 현재 입력받은(지금 현재 변화한) 값을 새로운 상태의 값으로 변화시키면서 author, content state가 바뀌어서 value가 바뀌고 실제로 화면에 렌더링된다.

const [author, setAuthor] = useState("");
const [content, setContent] = useState("");

<input name="author" value={author}
	onChange={ (e) => {setAuthor(e.target.value);}
} />
<textarea name="content" value={content}
	onChange={ (e) => {setContent(e.target.value);}
} /> 

☑️ 동일한 state (value, onChange의 콜백함수, 자료형(공백문자열)) 라면 하나의 state로 묶을 수 있다.

  • state가 author와 content를 같이 갖고 있는 배열
  • 새로운 객체를 만들어서 전달해줘야 함 (상태를 변화시킬 때는 setState 상태변화 함수에 새로 변경시킬 값을 인자로 전달해줬었기 때문) = 객체의 값을 바꾸려면 새로운 객체를 만들어서 전달해줘야 함
  • input에서 author는 e.target.value로 바뀌어야 하지만 content는 변하면 안됨(유지해야하므로) 원래있던 state로 업데이트를 해준다. (textarea에서는 content가 바뀌고 author가 유지)
const [state, setState] = useState({
	author: "",
    content: "",
});

<input name="author" value={state.author}
	onChange={ (e) => {
    	setState({
        	author: e.target.value,
            content: state.content,
        });
    }
} />
<textarea name="content" value={state.content}
	onChange={ (e) => {
    	setState({
        	content: e.target.value,
            author: state.author,
        });
   	}
} /> 

☑️ spread 연산자 활용

  • 객체로 state를 합쳤는데 만약 여러개로 값이 많을 경우
  • state가 가지고 있는 properties을 펼쳐줌
  • setState로 전달되서 새로운 객체를 만들 때 author: e.target.value에 새로 값이 들어왔을 건데, 다음 줄에 spread 연산자가 오게 되면 author의 값이 또 들어오게 됨 (원래의 값으로 덮어씌워지게 되면 결론적으로 아무 것도 업데이트가 안되게 됨) => 원래있던 state를 먼저 펼쳐주고 나서 변경하고자하는 state의 property를 마지막에 적어준다
const [state, setState] = useState({
	author: "",
    content: "",
});

<input name="author" value={state.author}
	onChange={ (e) => {
    	setState({
        	...state,
        	author: e.target.value,
        });
    }
} />
<textarea name="content" value={state.content}
	onChange={ (e) => {
    	setState({
        	...state,
        	content: e.target.value,
        });
   	}
} /> 

☑️ 중복코드 제거 - eventHandler 합치기

  • input과 textarea에 들어갈 onChange의 eventHandler
  • name과 value : 바뀐 값으로 자동 업데이트
  const handleChangeState = (e) => {
    setState({
      ...state,
      [e.target.name]: e.target.value,
    });
  };

<input name="author" value={state.author} onChange={handleChangeState} />
<textarea name="content" value={state.content} onChange={handleChangeState} />

☑️ 감정점수 1~5점으로 표시 (select, option)

  • 문자열이 아니므로 기본값을 1로 넣어줌
  • name, value, onChange 똑같이 넣어줌
  • emotion property는 select가 다른 걸 고르게 되면 onChange 이벤트가 발생 => handleChangeState 실행 => e.targer.name의 emotion의 값이 변하고 => 옵션이 바뀐다
const [state, setState] = useState({
	author: "",
    content: "",
    emotion: 1,
});

<select name="emotion" value={state.emotion} onChange={handleChangeState}>
  <option value={1}>1</option>
  <option value={2}>2</option>
  <option value={3}>3</option>
  <option value={4}>4</option>
  <option value={5}>5</option>
</select>

☑️ 저장하기 (button)

  • 버튼 누르면 alert창
  const handleSubmit = () => {
    console.log(state);
    alert("저장 성공!");
  };
  
  <button onClick={handleSubmit}>일기 저장하기</button>

☑️ CSS styling

.App {margin:0 auto;}
.DiaryEditor { border:1px solid #e5e5e5; text-align:center; padding: 20px; }

.DiaryEditor input, .DiaryEditor textarea { margin:0 0 20px 0; padding:10px; width:500px; }
.DiaryEditor textarea { width:500px; height:150px; }
.DiaryEditor select { margin:0 0 20px 0; padding:10px; width:500px; }
.DiaryEditor button { cursor:pointer; padding:10px; width:500px; }

📝 Result

import { useState } from "react";

const DiaryEditor = () => {
  const [state, setState] = useState({
    author: "",
    content: "",
    emotion: 1
  });

  const handleChangeState = (e) => {
    setState({
      ...state,
      [e.target.name]: e.target.value
    });
  };

  const handleSubmit = () => {
    console.log(state);
    alert("저장 성공!");
  };

  return (
    <div className="DiaryEditor">
      <h2>오늘의 일기</h2>
      <div>
        <input
          value={state.author}
          onChange={handleChangeState}
          name="author"
          placeholder="작성자"
          type="text"
        />
      </div>
      <div>
        <textarea
          value={state.content}
          onChange={handleChangeState}
          name="content"
          placeholder="일기"
          type="text"
        />
      </div>
      <div>
        <span>오늘의 감정점수 : </span>
        <select
          name="emotion"
          value={state.emotion}
          onChange={handleChangeState}
        >
          <option value={1}>1</option>
          <option value={2}>2</option>
          <option value={3}>3</option>
          <option value={4}>4</option>
          <option value={5}>5</option>
        </select>
      </div>
      <div>
        <button onClick={handleSubmit}>일기 저장하기</button>
      </div>
    </div>
  );
};
export default DiaryEditor;

좋은 웹페이지 즐겨찾기