본문 바로가기



리액트 11: 리액트에서 동적으로 글 수정하기



안녕하세요. 각성한 데브키라입니다. 리액트(React)는 현대 웹 개발에서 가장 인기 있는 자바스크립트 라이브러리 중 하나로, 동적인 사용자 인터페이스를 구성하는 데 매우 효과적입니다. 그 중에서도, 리액트를 활용한 글 수정 기능 구현은 실제 웹 애플리케이션에서 자주 마주치는 시나리오입니다. 이 과정을 통해, 개발자는 상태 관리와 컴포넌트 간의 데이터 전달 방식에 대해 심도 있게 이해할 수 있습니다. 본 문서에서는 리액트를 활용하여 글 수정 기능을 구현하는 세부 단계를 소개합니다. 이를 통해, 리액트의 핵심 개념을 체계적으로 이해하고, 보다 복잡한 애플리케이션을 개발할 준비를 할 수 있습니다.

Step 1. 글수정버튼 클릭 - App.js

먼저, 글 수정 버튼을 클릭하면 `mode` 상태가 `UPDATE`로 변경되도록 설정합니다. 이는 `App.js` 파일에서 구현됩니다. 링크를 클릭했을 때, `setMode` 함수를 호출하여 `UPDATE` 상태로 변경합니다.

  let contextControl = null;
  
contextControl = <a class="action-link" href={"/update/"+id} onClick={event=>{
  event.preventDefault();
  setMode('UPDATE');
}}>수정</a>  

{contextControl} {/* 수정버튼 영역 */}

Step 2. 별도로 분리된 Update컴포넌트

다음으로, 글을 수정할 때 사용되는 Update 컴포넌트를 작성합니다. 이 컴포넌트는 사용자가 입력했던 글을 보여주고 사용자가 새롭게 입력 수정한 제목과 본문을 받아서 처리하는 폼을 포함합니다. 폼이 제출되면 props로 전달된 onUpdate 함수를 호출하여 글을 생성합니다.

 

매우 중요: props는 리액트 컴포넌트에 전달된 속성들을 담고 있는 객체입니다. 부모 컴포넌트로부터 전달받은 props의 값은 읽기 전용이며, 컴포넌트 내부에서 직접적으로 수정할 수 없습니다. 따라서, props.title이나 props.body와 같은 값이 input이나 textarea의 값을 관리하는 데 사용될 때, 사용자의 입력에 따라 이 값들이 자동으로 갱신되지 않습니다.

이는 리액트의 데이터 흐름 원칙 때문입니다. 리액트는 데이터가 상위 컴포넌트에서 하위 컴포넌트로 단방향으로 흐른다는 원칙을 가지고 있습니다. 따라서, 컴포넌트 내부에서 사용자의 입력을 반영하여 props 값을 변경하고자 할 때, 직접 props를 변경하는 것이 아니라, 상태 관리를 통해 해결해야 합니다.

예를 들어, input 요소에서 사용자의 입력을 반영하고자 할 때, useState 훅을 사용하여 상태 변수를 선언하고, 이 상태 변수를 input 요소의 value 속성에 연결합니다. 그리고 onChange 이벤트 핸들러에서 해당 상태 변수를 업데이트하는 방식으로 구현합니다. 이렇게 하면 사용자의 입력이 해당 상태 변수에 반영되고, 리액트가 이를 감지하여 컴포넌트를 적절히 리렌더링합니다.

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>
}

Step 3. 클생성 이벤트를 받아서 처리

  1. UPDATE 상태로 변경된 후, App 컴포넌트가 다시 렌더링됩니다.
  2. mode가 UPDATE일 때, Update 컴포넌트를 화면에 표시합니다.
  3. 사용자가 글을 수정하고 전송 버튼을 클릭하면 onUpdate 이벤트가 발생합니다.
  4. onUpdate 함수에서 기존에 입력했던 글 중에서 선택된 글을 수정하고, topics 상태에 내용을 갱신합니다.
  5. 글이 수정된 후, mode를 READ로 변경하여 수정된 글의 상세 페이지를 표시합니다.
  }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>
  }

 

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>  
  }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. 리액트에서 동적으로 글 삭제하기 삭제버튼