WEB/⚛️ React

[React] 4. CRUD 게시판 구현하기

무딘붓 2023. 2. 16. 16:30

생활코딩 React 2022년 개정판 강의(https://opentutorials.org/course/4900)를 듣고 개인 학습용으로 정리한 게시글입니다.


 

react로 구현한 crud 게시판 작동 모습

 

React를 이용하여 CRUD( Create, Read, Update, Delete )기능을 구현한 게시판이다.

 

예전에 Node.js로 같은 기능을 구현한 적이 있는데, React로 구현하니 더 깔끔한것 같다.

Node.js로 구현했던 CRUD 게시판은 아래 링크를 참고.

https://sirius7.tistory.com/48

 

[Node.js] 8. CRUD 게시판 구현

CRUD CRUD란 Create(생성), Read(읽기), Update(갱신), Delete(삭제) 의 약자로, 사용자 인터페이스가 갖추어야 할 기능을 말한다. Node.js로 CRUD 구현 아래 소스코드는 생활코딩 강의 WEB2 - Node.js (링크) 에서 사

sirius7.tistory.com

 

React로 구현한 crud 게시판의 소스코드는 다음과 같다.

 

<App.js>

import logo from './logo.svg';
import './App.css';
import { useState } from 'react';

function Header(props) {
  return <header>
    <h1><a href="/" onClick={(event)=>{
      event.preventDefault();
      props.onChangeMode();
    }}>{props.title}</a></h1>
  </header>
}

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

function Article(props) {
  return <article>
    <h2>{props.title}</h2>
    {props.body}
  </article>
}

function Create(props) {
  return <article>    
    <h2>글 작성</h2>
    <form onSubmit={event=>{
      event.preventDefault();
      const title = event.target.title.value;
      const body = event.target.body.value;
      props.onCreate(title,body)
    }}>
      <p>제목</p>
      <p><input type="text" name="title" placeholder="title"/></p>
      <p>내용</p>
      <p><textarea name="body" placeholder="body"></textarea></p>
      <p><input type="submit" value="작성 완료"/></p>     
    </form>
  </article>
}

function Update(props) {
  const [title, setTitle] = useState(props.title);
  const [body, setBody] = useState(props.body);

  return <article>    
    <h2>글 수정</h2> 
    <form onSubmit={event=>{
      event.preventDefault();
      const title = event.target.title.value;
      const body = event.target.body.value;
      props.onUpdate(title,body)
    }}>
      <p>제목</p>
      <p><input type="text" name="title" placeholder="title" value={title} onChange={event=>{
        setTitle(event.target.value);
      }}/></p>
      <p>내용</p>
      <p><textarea name="body" placeholder="body" value={body} onChange={event=>{
        setBody(event.target.value);
      }}></textarea></p>
      <p><input type="submit" value="수정 완료"/></p>     
    </form>
  </article>
}



function App() {

  const [mode, setMode] = useState('WELCOME');
  const [id, setId] = useState(null);
  const [nextId, setNextId] = useState(4);

  const [topics,setTopics] = useState([
    {id:1, title:'Apple', body:'사과'},
    {id:2, title:'Banana', body:'맛있다'}
  ]);

  let content = null;
  let contextControl = null;

  if(mode==="WELCOME"){
    content = <Article title="Welcome" body="Hello, web"></Article>
    contextControl = <>
      <a class="btn" href="/create" onClick={event => {
        event.preventDefault();
        setMode('CREATE');
      }}>글 작성</a>
    </>
  }
  else if (mode === 'READ') {
    let title, body = null;
    for (let i = 0; i < topics.length; i++) {
      if (topics[i].id === id) {
        title = topics[i].title;
        body = topics[i].body;
      }
    }
    content = <Article title={title} body={body}></Article>
    contextControl = <>
      <a class="btn" href="/create" onClick={event => {
        event.preventDefault();
        setMode('CREATE');
      }}>글 작성</a>
      <a class="btn" href={'/update/' + id} onClick={event => {
        event.preventDefault();
        setMode('UPDATE');
      }}>글 수정</a>
      <input class="btn" type="button" value="글 삭제" onClick={() => {
        const newTopics = []
        for (let i = 0; i < topics.length; i++) {
          if (topics[i].id !== id) {
            newTopics.push(topics[i]);
          }
        }
        setTopics(newTopics);
        setMode('WELCOME');
      }} />
    </>
  }
  else if (mode === 'CREATE') {
    content = <Create onCreate={(_title,_body)=>{
      const newTopic = {id:nextId, title:_title,body:_body};
      const newTopics = [...topics];
      newTopics.push(newTopic);
      setTopics(newTopics);
      setMode('READ');
      setId(nextId);
      setNextId(nextId+1);
    }}></Create>
  }
  else if (mode === 'UPDATE') {
    let title, body = null;
    for (let i = 0; i < topics.length; i++) {
      if (topics[i].id === id) {
        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}
      for(let i=0;i<newTopics.length;i++){
        if(newTopics[i].id===id){
          newTopics[i] = updatedTopic;
          break;
        }
      }
      setTopics(newTopics);
      setMode('READ');

    }}></Update>    
  }

  return (
    <>
      <Header title="React 게시판 구현" onChangeMode={() => {
        setMode('WELCOME');
      }}></Header>

      <div class="background">
        <div class="titlebar">글 목록</div>
        <div class="content">

          <Nav topics={topics} onChangeMode={(_id) => {
            setMode('READ');
            setId(_id);
          }}></Nav>
        </div>

        <div class="titlebar">글 내용</div>
        <div class="content">
          {content}
            <div class="btncover">
              {contextControl}
            </div>
        </div>

      </div>
    </>
  );
}

export default App;

<App.css>

body {
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: lightgray;
}
        
.background {
    background-color: white;
    height: auto;
    width: 450px;
}

.titlebar {
    background-color: #F9F9F9;
    font-weight: 400;
    border: none; border-bottom: 1px solid #CCCCCC;
    padding: 18px;
}        
.content {
    padding: 20px;
    max-height: max-content;
}

.btncover {
    display: flex;
    justify-content: center;
    padding: 10px;
}        
.btn {
    font-size: 15px;
    text-decoration: none;
    text-align: center;
    background-color: white;
    color: black;
    border: 1px solid lightgray;
    padding: 5px 10px;
    margin: 5px;
    display: inline-block;
    cursor: pointer;
    border-radius: 3px;
}

h1{
    text-align: center;
}
h1 a{
    color:black;
    text-decoration: none; 
}

생활코딩 강의에서 나온 내용에서 디자인만 조금 수정한 코드이다.

 

 

만들면서 몇가지 주의해야할 점도 배웠다.

 

state에 저장된 값이 배열이나 객체일때는 다음과 같이 수정한다.

const newTopics = [...topics]
const updatedTopic = {id:id, title:title, body:body}
for(let i=0;i<newTopics.length;i++){
	if(newTopics[i].id===id){
    	newTopics[i] = updatedTopic;
		break;
	}
}
setTopics(newTopics);

위와 같이, 저장된 값이 배열이나 객체인 경우에는

그 값을 복제한 다음, 복제한 데이터를 수정한다.

그런 다음, 복제한 데이터를 이용해서 값을 바꿔준다.

 

만약, 복제하지 않고 원래 state에 저장된 배열이나 객체를 수정한 다음

함수를 호출해서 값을 바꾸려고 하면 HTML이 자동으로 다시 랜더링되지 않는다.

 

객체를 복제하는 방법은 다음과 같다.

newValue = {...value}

배열은 다음과 같이 복제한다.

newValue = [...value]

 

 

또한, 이미 저장된 값을 변경할때의 주의사항이 있다.

props로 넘어온 데이터는 수정이 되지 않는다.


그래서 게시글을 수정하는 기능을 구현할 때에는,

props로 넘어온 데이터를 state로 변경한 다음, 아래와 같이 구현할 수 있다.

 

리액트에서는 값을 입력할때마다 onChange가 실행되기 때문에, 이를 이용한 구현이다.

function Update(props) {
  const [title, setTitle] = useState(props.title);
  const [body, setBody] = useState(props.body);

  return <article>    
    <h2>글 수정</h2> 
    <form onSubmit={event=>{
      event.preventDefault();
      const title = event.target.title.value;
      const body = event.target.body.value;
      props.onUpdate(title,body)
    }}>
      <p>제목</p>
      <p><input type="text" name="title" placeholder="title" value={title} onChange={event=>{
        setTitle(event.target.value);
      }}/></p>
      <p>내용</p>
      <p><textarea name="body" placeholder="body" value={body} onChange={event=>{
        setBody(event.target.value);
      }}></textarea></p>
      <p><input type="submit" value="수정 완료"/></p>     
    </form>
  </article>
}

'WEB > ⚛️ React' 카테고리의 다른 글

[React] 5. fetch 함수  (0) 2023.02.22
[React] 3. state  (0) 2023.02.11
[React] 2. 컴포넌트, props, event  (0) 2023.02.10
[React] 1. 시작하기 (리액트 소개, 설치, 수정, 배포)  (0) 2023.02.07