본문 바로가기



리액트 12: 리액트에서 동적으로 글 삭제하기



리액트를 사용해 웹 애플리케이션을 개발할 때, 데이터의 CRUD(생성, 읽기, 업데이트, 삭제) 기능은 매우 중요합니다. 이번 포스트에서는 상태값과 이벤트를 활용해 리액트에서 '삭제' 기능을 구현하는 방법을 단계별로 설명하겠습니다. 특히 상태변수 mode의 값을 통해 삭제 버튼을 조건부로 표시하고, 클릭 이벤트를 처리하여 토픽 데이터를 삭제하는 과정을 자세히 다뤄보겠습니다.

 

리액트는 상태 관리를 통해 UI를 동적으로 업데이트할 수 있는 강력한 기능을 제공합니다. 이번 예제에서는 상태변수 mode를 사용해 삭제 버튼을 조건부로 표시하고, 클릭 이벤트를 통해 데이터를 삭제하는 방법을 다룹니다. 각 단계를 따라가며 소스 코드를 이해해 보세요.

Step 1. 상태값 READ

먼저, 상태변수 modeREAD일 때만 삭제 버튼이 보이도록 설정합니다. 아래 코드를 확인하세요.

    content = <Article title="Welcome" body="Hello, WEB"></Article>
  }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 id={id} title={title} body={body}></Article>
    contextControl = <>
      <a class="action-link" href={"/update/"+id} onClick={event=>{
        event.preventDefault();
        setMode('UPDATE');
      }}>수정</a>  

      <a class="action-link" href="#" type="button" value="Delete" onClick={event=>{
        event.preventDefault(); // 기본 동작 막기
        const newTopics = []
        for(let i = 0; i < topics.length; i++){
          if(topics[i].id !== id){
            newTopics.push(topics[i])
          }
        }
        setTopics(newTopics);
        setMode("WELCOME")
      }}>삭제</a>   
    </>

 

위 코드에서 modeREAD일 때만 삭제 버튼이 보이도록 조건부 렌더링을 구현했습니다. modeREAD가 아니면 삭제 버튼이 보이지 않습니다.

Step 2. 삭제버튼 클릭

삭제 버튼을 클릭했을 때 기본 동작을 막고, onClick 이벤트 안에서 스크립트로만 처리하기 위해 event.preventDefault();를 사용합니다.

 

Setp 3. 토픽변수를 이용한 삭제처리

토픽변수 topics를 for문으로 돌면서 id 상태변수값과 일치하지 않는 항목만 newTopics에 담아줍니다. 즉, 삭제할 내용을 제외하고 나머지 항목만 모두 newTopics에 담아주는 것입니다.

Step 4: 최종 코드 정리

다음은 App.js전체 코드를 정리한 예제입니다. 이를 통해 글 삭제 기능을 완전히 구현할 수 있습니다.

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

function Header(props){
  console.log(props);
  console.log(props.title);
  return(
    <header>
    <h1>
      <a href="/" onClick={(e)=>{
        e.preventDefault();//기본 이벤트의 동작을 막아줌
        props.onChangeMode();
      }}>
        {props.title}
      </a>
    </h1>
    </header>
  )
}

function Article(porps){
  return <article>
    <h2>[ {porps.title} ]</h2>
    <p>{porps.id}</p>
    <p>{porps.title}</p>
    <p>{porps.body}</p>
  </article>
}

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

function Create(props){
  return <article>
    <h2>Create</h2>
    <form onSubmit={e=>{
      e.preventDefault();
      const title = e.target.title.value;
      const body = e.target.body.value;
      props.onCreate(title, body);
    }}>
      <p><input type='text' name='title' placeholder='title'/></p>
      <p><textarea name='body' placeholder='body'/></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>Update</h2>
    <form onSubmit={e=>{
      e.preventDefault();
      const title = e.target.title.value;
      const body = e.target.body.value;
      props.onUpdate(title, body);
    }}>
      <p><input type='text' name='title' placeholder='title' value={title} onChange={e=>{
        setTitle(e.target.value);
      }}/></p>
      <p><textarea name='body' placeholder='body' value={body} onChange={e=>{
        setBody(e.target.value);
      }}/></p>    
      <p><input type='submit' value='수정하기'/></p>
    </form>
  </article>
}

function App() {
  const [mode, setMode] = useState('WELCOME');
  const [id, setId] = useState(null);
  const [topics, setTopics] = useState([
    {id: 0, "title":"블랙핑크", body:"블랙핑크 good!"},
    {id: 1, "title":"아이브", body:"아이브 good!"},
    {id: 2, "title":"르세라핌", body:"르세라핌 good!"},
    {id: 3, "title":"뉴진스", body:"뉴진스 good!"},
    {id: 4, "title":"트와이스", body:"트와이스 good!"},
    {id: 5, "title":"레드벨벳", body:"레드벨벳 good!"},
    {id: 6, "title":"에스파", body:"에스파 good!"}
  ]);

  let content = null;
  let contextControl = null;

  if(mode === "WELCOME"){
    content = <Article title="Welcome" body="Hello, WEB"></Article>
  }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 id={id} title={title} body={body}></Article>
    contextControl = <>
      <a class="action-link" href={"/update/"+id} onClick={event=>{
        event.preventDefault();
        setMode('UPDATE');
      }}>수정</a>  

      <a class="action-link" href="#" type="button" value="Delete" onClick={event=>{
        event.preventDefault(); // 기본 동작 막기
        const newTopics = []
        for(let i = 0; i < topics.length; i++){
          if(topics[i].id !== id){
            newTopics.push(topics[i])
          }
        }
        setTopics(newTopics);
        setMode("WELCOME")
      }}>삭제</a>   
    </>
  }else if(mode === "CREATE"){
    content = <Create onCreate={(title, body)=>{
      const newId = topics.length;
      const item = {id:newId, title:title, body:body};
      const list = [...topics];
      list.push(item);
      setTopics(list);
      setId(newId);//상세정보출력을 위해서 새로추가되는 글의 id가 무엇인지 알려줍니다.
      setMode("READ");//목록이 추가되고 추가된 내용의 상세페이지를 보여주기 위해서 READ모드로 변경
    }}></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 (
    <div>
        <Header title="WEB" onChangeMode={()=>{
          setMode("WELCOME");
        }}></Header>
        <Nav topics={topics} onChangeMode={(id)=>{
          setMode("READ");
          setId(id);
        }}></Nav>
        {content}
        <br/>
        <a class="action-link" href="/create" onClick={event=>{
          event.preventDefault();
          setMode('CREATE');
        }}>추가</a>
        {contextControl} {/* 수정버튼 영역 */}
    </div>
  );
}

export default App;

삭제기능 테스트

 

이번 포스트에서는 리액트에서 삭제 기능을 구현하는 방법을 단계별로 설명했습니다. 상태값과 이벤트를 활용하여 조건부 렌더링과 데이터 삭제 처리를 이해하고, 실제로 적용해 보시기 바랍니다. 이 과정을 통해 여러분의 리액트 개발 실력이 한층 더 향상되길 바랍니다.

[ 리액트 입문강좌 시리즈 ]

01. React 설치와 초기화면 출력
02. 리액트 배포본 생성을 위한 npm run build
03. 리액트의 꽃 사용자정의 태그
04. 컴포넌트의 섬세한 활용을 위한 props속성 사용
05. 리스트데이터의 활용 topics변수 사용
06.사용자 정의 이벤트 구현
07.리스트 형태의 사용자 정의 이벤트 구현
08. React useState훅을 이용한 상태에 따른 UI표현
09. 리액트 토픽변수를 useState 훅을 이용한 글입력
10. 리액트에서 동적으로 글 생성하기
11. 리액트에서 동적으로 글 수정하기
12. 리액트에서 동적으로 글 삭제하기 삭제버튼