React 3.0 Basics CRUD 230223 - 230302

sky (polyjean)·2023년 2월 23일
0

Studies

목록 보기
7/10

생활코딩 react

react는 복잡한 코드는 숨기고 사용자 정의 태그를 만드는 것

class vs function : function으로 하는게 많아졌다

필요할때 배워서 그때그때 사용할 수 있도록

시작

시작은 react document

온라인 플레이 그라운드 stackblitz

싱글 페이지 애플리케이션 create react app

nodejs 환경 만들기

npx create-react-app my-app

react 서버 시작

npm start

기본 구조

  1. src/index.js 입구가 되는 파일, 전역 설정

    import React from "react";
    import ReactDOM from "react-dom/client";
    import "./index.css";
    import App from "./App";
    import reportWebVitals from "./reportWebVitals";
    
    const root = ReactDOM.createRoot(document.getElementById("root")); // 요 루트는 public/index.html 의 id="root"
    root.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
    
    reportWebVitals();```
    
  2. src/index.css 전체적인 스타일

  3. src/App.js 요기가 내용

    import React from "react";
    import ReactDOM from "react-dom/client";
    import "./index.css";
    import App from "./App"; // 2. App 태그는 여기서 왔고 ./App은 뒤에 .js가 생략됨
    import reportWebVitals from "./reportWebVitals";
    
    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(
      <React.StrictMode>
        <App /> // 1. 요게 내용
      </React.StrictMode>
    );
    
    reportWebVitals();
  4. src/App.css 요거가 App의 스타일

  5. public/index.html 바탕이 되는 html

빌드

npm run build

build 폴더 생성

스테틱 서버 가동 - 실 서비스 버전

nodejs 로 돌아가는 서버 serve

npx serve -s build

컴포넌트

"리액트는 사용자 태그<tag attr=value></tag>를 만드는 기술이다"

복잡한 코드의 정리 정돈 - 한 묶음을 이름을 지어주고 정리하자 - 사용자 정의 태그는 첫글자 대문자

리액트에서는 사용자정의 태그라고 안하고 컴포넌트component 라고 한다

//컴포넌트 정의 (여기에 컴포넌트 태그 안 내용이 들어간다)
function Header() {
  return (
    <header>
      <h1>
        <a href="/">React</a>
      </h1>
    </header>
  );
}

function App() {
  return (
    <div>
      <Header></Header> //컴포넌트 사용, 대문자로 시작 (여기에는 컴포넌트 태그와 속성만 들어간다)
    </div>
  );
}

props

react에서 속성을 props 라고 한다

//컴포넌트 정의
function Header(props) { // 다들 props라고 쓴다
  return (
    <header>
      <h1>
        <a href="/">{props.title}</a> //데이터 들어갈 영역 잡기
      </h1>
    </header>
  );
}

function App() {
  return (
    <div>
      <Header title="REACT"></Header> // 해당이름의 속성으로 추가
    </div>
  );
}

props list(multiple)

여러 태그를 한번에 처리할때는 for문을 돌리거나 앱을 통해 값을 전달한다
한 영역에 여러 태그는 각각 key 값을 가져야 한다

function Navi(props) {
  const list = []; //리스트를 담기 위한 배열 만들기
  for (let i = 0; i < props.topics.length; i++) { // for문을 돌려서 태그를 생성하고 props 값을 추가한다
    let t = props.topics[i];
    list.push(
      <li key={t.id}> // 한 영역에서 다중 생성된 것은 React에서 구분하기 위해 key값이 필요하다 
        <a href={"/read/" + t.id}>{t.title}</a>
      </li>
    );
  }
  return (
    <nav>
      <ol>{list}</ol> //처리된 배열을 { }로 감싸서 전달
    </nav>
  );
}

function App() {
  //데이터값 배열 선언
  const topics = [
    { id: 1, title: "html", body: "html is..." },
    { id: 2, title: "css", body: "css is..." },
    { id: 3, title: "js", body: "js is..." }
  ];
  return (
    <div>
      <Navi topics={topics}></Navi> //prop 값으로 내용 받기
    </div>
  );
}

event

onChangeMode
이벤트를 가진 컴포넌트 만들기

function Header(props) {
  return (
    <header>
      <h1>
        <a
          href="/"
          onClick={(event) => { //javascript의 onclick과는 사용방법이 조금 다르다
            event.preventDefault();
            props.onChangeMode();
          }}>
          {props.title}
        </a>
      </h1>
    </header>
  );
}

function Navi(props) {
  const list = [];
  for (let i = 0; i < props.topics.length; i++) {
    let t = props.topics[i];
    list.push(
      <li key={t.id}>
        <a
          id={t.id}
          href={"/read/" + t.id}
          onClick={(event) => {
            event.preventDefault();
            props.onChangeMode(event.target.id);
          }}>
          {t.title}
        </a>
      </li>
    );
  }
  return (
    <nav>
      <ol>{list}</ol>
    </nav>
  );
}

function App() {
  const topics = [
    { id: 1, title: "html", body: "html is..." },
    { id: 2, title: "css", body: "css is..." },
    { id: 3, title: "js", body: "js is..." },
  ];
  return (
    <div>
      <Header
        title="REACT"
        onChangeMode={() => { // 적용되는 곳
          alert("This is Header");
        }}
</Header>
      <Navi
        topics={topics}
        onChangeMode={(id) => {
          alert(id);
        }}
</Navi>
    </div>
  );
}

state

컴포넌트의 구조에서

컴포넌트-> prop을 통해 -> [ ] -> return
[ 내부에서 컴포넌트를 사용하는 state(다시실행) ] -> return

props, states 공통점 : 작동하면 리턴값을 제공

props는 (컴포넌트) function을 사용하는 외부자를 위한 것 - 자바스크립트의 동작

state 정의

  • state 설명 : state는 컴포넌트를 만드는 내부자를 위한 - 내부가 바뀌어도 App() 함수는 다시 실행되지 않기 때문에 return 값에는 변화가 없다. 그래서, state를 사용한다

  • state 이해 : 원래대로라면 한번 실행된 (컴포넌트) function의 리턴값은 바뀌지 않지만, state를 이용하면 그 내부의 값이 바뀌었을때 return 값을 다시 처리해서 보여준다

  • state 요약 : 컴포넌트 내부 피드백 처리

state 사용

맨 위에 이렇게 선언해서 useState 가져옴

import { useState } from "react";

배열을 리턴, [0]은 상태값, [1]은 함수
변수를 두개로 나눠서 기본값두는 곳에[0]을 두고 바뀌는 곳에[1]에 두면 [1]이 변했을때 [0]이 변한다

풀어쓰면 이렇게 쓸 수 있는데,

const _mode = useState(defaultValue); // 기본값 변수 할당
const mode = _mode[0]; // [0]번은 기본값
const setMode = _mode[1]; // [1]번은 그 값을 바꿀수 있게 하는 함수

줄여쓰면 이렇게 쓸 수 있다

const [mode, setMode] = useState(defaultValue);

create

App() 에서 새로운 항목 생성을 위한 모양 생성

//return : App()의 화면 출력 영역 
<a href="/create" onClick={event=>{
  event.preventDefault();
  setMode("create"); //클릭하면 mode 값을 create로 변경
}}>Create</a>

mode가 create 일 때,

//App()의 변수 선언 부분
const [topics, setTopics] = useState([ //기존값들을 state로 변경, 유동적인 리스트가 되니까
  { id: 1, title: "html", body: "html is..." },
  { id: 2, title: "css", body: "css is..." },
  { id: 3, title: "js", body: "js is..." },
]);
const [nextId, setNextId] = useState(topics.length + 1); //신규 생성되는 id값의 초기값은 마지막 원소의 다음 번호 (여기서는 4가 된다), setNextId는 언제 사용??

let content = null; // 조건에 따라서 내용이 나오도록 영역 변수 선언
//만약에 mode가 create로 바뀌었다면
else if (mode === "create") {
  content = (
    <Create
      onCreate={(_title, _body) => { //onCreate로 생성
        const newTopic = { id: nextId, title: _title, body: _body }; //다음 신규 아이디값이 필요한데.... 그래서 nextId state 만듬
        const newTopics = [...topics]; //기존 topics를 복제해서 newTopics에...
        newTopics.push(newTopic); // 새로 복제한 newTopics에 새로운 항목 데이터 newTopic을 넣는다
        setTopics(newTopics); // 복제하고 새 데이터를 넣은 newTopics를 원래 topics에 추가setTopics한다

        setMode("read"); // (추가 작업) create mode를 read mode로 state 변경하고
        setId(nextId); // id를 다음 id(nextId)로 변경
        setNextId(nextId + 1); // (다음 글 준비) nextId에 +1 해서 다음 create 대비
      }}
</Create>
  );
}
...

return (

...
  <Navi></Navi>

...

  {content} // 조건에 따라서 내용이 나오는 영역 배정

state를 변경해서 다양한 경우에 세련되게 대응할 수 있다

참고로 ... 객체 밸류 데이터를 복제는 이렇게 한다고 한다 - 데이터 객체 변경을 위해

newValue = {...value} //범객체(객체) 오리지널을 안건드리고 객체밸류를 복제, 변경
newValue 변경
setValue(newValue)

newValue = [...value] //범객체(배열) 오리지널을 안건드리고 배열값을 복제, 변경
setValue(newValue)

update

조건, 필요요소

  • update = create + read (기능 면에서)
  • update 기능은 보기에서만 보이고 초기화면에서는 안보이게 - mode가 read 일때만
  • props(외부-이벤트 입력 등-으로 받은 값)을 state로 바꿔서 내부 변경이 가능하게 한다

구현

// Create() 를 바탕으로 
function Update(props) {
  // prop으로 받은 값을 컴포넌트 안의 state로 변경해서 변수 state를 바꿀 수 있게 해야 한다
  const [title, setTitle] = useState(props.title);
  const [body, setBody] = useState(props.body);
  return (
    <article>
      <h2>Update</h2>
      <form
        onSubmit={(event) => {
          event.preventDefault();
          const title = event.target.title.value;
          const body = event.target.body.value;
          props.onUpdate(title, body);
        }}>
        <div>
          <input
            type="text"
            name="title"
            placeholder="title"
            value={title}
            onChange={(event) => { //자바스크립트의 change와 다르게 값을 입력할때마다 바꾼다
              setTitle(event.target.value); // state를 새로 set해서 value를 바꿔준다
            }} />
        </div>
        <div>
          <textarea
            name="body"
            placeholder="body"
            value={body}
            onChange={(event) => { //자바스크립트의 change와 다르게 값을 입력할때마다 바꾼다
              setBody(event.target.value); // state를 새로 set해서 value를 바꿔준다
            }} ></textarea>
        </div>
        <div>
          <input type="submit" value="Update" />
        </div>
      </form>
    </article>
  );
}
//App() 에서 보이는 모양은...
function App() {
  const [mode, setMode] = useState("welcome"); // App 전체에 영향을 주는 mode state 설정
  const [id, setId] = useState(null); // App 전체에 영향을 주는 id state 설정
  const [topics, setTopics] = useState([ // App 전체에 영향을 주는 컨텐츠topics state 설정
    { id: 1, title: "html", body: "html is..." },
    { id: 2, title: "css", body: "css is..." },
    { id: 3, title: "js", body: "js is..." },
  ]);
  let contextControl = null; // 필요할 때만 나오도록 따로 변수로 선언

  ...

  // update 링크는 welcome (첫화면에서는 x), read(상세보기)에서만 보이게 
  else if (mode === "read") {
    contextControl = (
      <li>
        <a
          href={"/update/" + id}
          onClick={(event) => {
            event.preventDefault();
            setMode("update");
          }}>
          Update
        </a>
      </li>
    );
  }
  // update 상세 화면
  else if (mode === "update") {
    let title,
      body = null; // 내용을 받기 위해 변수(메모리 공간 확보) 준비
    for (let i = 0; i < topics.length; i++) {
      if (topics[i].id === id) { // 현재 id와  일치하는 것을 찾아 데이터를 title, body 변수에 넣기
        title = topics[i].title;
        body = topics[i].body;
      }
    }
    content = (
      <Update
        title={title}
        body={body}
        onUpdate={(title, body) => {
          const newTopics = [...topics]; // ... 수식으로 객체(배열) 내용을 새로운 변수에 복제
          const updatedTopic = { id: id, title: title, body: body }; //mode가 read 일때만 나오므로 id는 그대로 사용해도 됨
          for (let i = 0; i < newTopics.length; i++) {
            if (newTopics[i].id === id) {
              newTopics[i] = updatedTopic;
              break;
            }
          }
          setTopics(newTopics); // state로 내용 업데이트
          setMode("read"); // state로 read 상태로 변경 (섬세한? 처리)
        }}>
      </Update>
    );
  }

  ...

  return (

  ...

    <Navi></Navi>

  ...

    {content} 

  ...

    <a href="/create" ... > ... </a>

  ...

    {contextControl} // 필요할 때만 나오도록 조건문에 넣기

delete

react에서 여러개의 태그를 묶을때 빈태그 사용, 노출되지 않고 감싸기만 함 - 리액트에서는 하나의 변수에 담는 컴포넌트는 하나의 태그로 묶어야 하기 때문에 사용

  <> // 빈태그 (여는 태그)
  	...
    <li></li>
    <li></li>
    ...
  </> // 빈태그 (닫는 태그)

delete는 read 모드에 추가

function App() {
...
  else if (mode === "read") {
    ...
    contextControl = (
      <> // react 빈태그로 감싸기
        <li>
          <a
            href={"/update/" + id}
            onClick={(event) => {
              event.preventDefault();
              setMode("update");
            }}>
            Update
          </a>
        </li>
        <li>
          <input
            type="button"
            value="Delete"
            onClick={(event) => {
              const newTopics = []; // 새로운 객체를 만들고
              for (let i=0; i < topics.length; i++) { // 데이터 전체를 살펴서
                if (topics[i].id !== id) { // 현재 read의 id를 제외하고 - 삭제 처리
                  newTopics.push(topics[i]); // 새로운 객체에 추가
                }
              }
              setTopics(newTopics); // 새로운 객체를 데이터에 넣고
              setMode("welcome"); // 첫화면welcome 으로 mode 전환
            }}
        </li>
      </>
    );
  }
profile
front end developer

0개의 댓글