생활코딩! React 리액트 프로그래밍

egoing - React - create

lshjju 2025. 6. 23. 23:08

글쓰기 기능 만들기



유저 글쓰기 유아이 빌딩 합니다.


export default function App() {
  const [mode, setMode] = useState("WELCOME");
  const [id, setId] = useState(null);
  const topics = [
    { id: 1, title: "html", body: "html is ..." },
    { id: 2, title: "css", body: "css is ..." },
    { id: 3, title: "javascript", body: "javascript is ..." },
  ];
  let content = 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 title={title} body={body}></Article>;
  } else if (mode === "CREATE") {
    content = <Create></Create>;
  }
  return (
    <div className="App">
      <Header
        title="WEB"
        onChangeMode={() => {
          setMode("WELCOME");
        }}
      ></Header>
      <Nav
        topics={topics}
        onChangeMode={(_id) => {
          setMode("READ");
          setId(_id);
        }}
      ></Nav>
      {content}
      <a
        href="/create"
        onClick={(event) => {
          event.preventDefault();
          setMode("CREATE");
        }}
      >
        Create
      </a>
    </div>
  );
}

 

Completion


      <a href="/create" onClick={event=>{
        event.preventDefault();
        setMode('CREATE');
      }}>Create</a>

 

펑션앱 리턴 가장 아래 콘텐트 데이터 아래

 

크리에이트 유아이를 만듭니다.

에이 태그 디폴트 액션 막습니다.

스테이트 변경 코드를 세팅합니다.

      {/* 새로운 내용을 생성하는 링크입니다. */}
      <a href="/create" onClick={event=>{
        // 기본 동작(링크 클릭 시 페이지 이동)을 방지합니다.
        event.preventDefault();
        // 클릭 시 mode를 'CREATE'로 변경하여 Create 폼을 보여주게 합니다.
        setMode('CREATE');
      }}>Create</a>

else if(mode === 'CREATE') {
    content = <Create></Create>
  }

 

펑션앱 리턴 위

 

글쓰기를 탭했을 때 뷰를 바꿀 준비를 합니다.

크리에이트 태그를 변수로 만들어 준비하면 됩니다.

else if(mode === 'CREATE') {
    // mode가 'CREATE'이면 Create 폼을 보여주는 Create 컴포넌트를 할당합니다.
    content = <Create></Create>
  }


펑션크리에잇 추가 합니다.


function Create() {
  return (
    <article>
      <h2>Create</h2>
      <form>
        <p><input type="text" name="title" placeholder="title" /></p>
        <p><textarea name="body" placeholder="body"></textarea></p>
        <p><input type="submit" value="Create"></input></p>
      </form>
    </article>
  )
}

 

펑션앱 바로 위

 

유저가 사용할 글쓰기 유아이를 펑션으로 추가합니다.

// Create 컴포넌트를 정의합니다. 이 컴포넌트는 새로운 내용을 생성하는 폼을 담당합니다.
function Create() {
  return (
    // HTML의 article 태그를 사용합니다.
    <article>
      <h2>Create</h2>
      {/* 내용을 입력받을 폼입니다. */}
      <form>
        {/* 제목을 입력받는 텍스트 입력 필드입니다. */}
        <p><input type="text" name="title" placeholder="title" /></p>
        {/* 본문 내용을 입력받는 텍스트 영역 필드입니다. */}
        <p><textarea name="body" placeholder="body"></textarea></p>
        {/* 폼 제출 버튼입니다. */}
        <p><input type="submit" value="Create"></input></p>
      </form>
    </article>
  )
}

test

 

 

크리에이트 링크 탭해서 글쓰기 유아이 잘 나오는지 체크합니다.



글 작성 후 데이터를 전달 합니다.


export default function App() {
  const [mode, setMode] = useState("WELCOME");
  const [id, setId] = useState(null);
  const topics = [
    { id: 1, title: "html", body: "html is ..." },
    { id: 2, title: "css", body: "css is ..." },
    { id: 3, title: "javascript", body: "javascript is ..." },
  ];
  let content = 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 title={title} body={body}></Article>;
  } else if (mode === "CREATE") {
    content = <Create onCreate={(_title, _body) => {}}></Create>;
  }
  return (
    <div className="App">
      <Header
        title="WEB"
        onChangeMode={() => {
          setMode("WELCOME");
        }}
      ></Header>
      <Nav
        topics={topics}
        onChangeMode={(_id) => {
          setMode("READ");
          setId(_id);
        }}
      ></Nav>
      {content}
      <a
        href="/create"
        onClick={(event) => {
          event.preventDefault();
          setMode("CREATE");
        }}
      >
        Create
      </a>
    </div>
  );
}

 

Completion


 else if(mode === 'CREATE') {
    content = <Create onCreate={(_title, _body)=>{
      
    }}></Create>
  }

 

펑션앱 모드크리에이트 내부

 

크리에이트 버튼을 탭했을 때 프롭에 펑션을 전달해야 합니다.

속성인 타이틀과 파라미터로 받은 타이틀은 헷갈리지 않도록 언더스코어로 구분 합니다.

바디도 마찬가지입니다.

else if(mode === 'CREATE') {
    // 'mode'가 'CREATE'일 경우, 새로운 글을 작성하는 Create 컴포넌트를 할당합니다.
    // Create 컴포넌트가 폼 제출 시 호출할 'onCreate' 함수를 props로 전달합니다.
    content = <Create onCreate={(_title, _body)=>{

    }}></Create>
  }

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

 

Completion


function Create(props)

 

펑션크리에이트

 

데이터를 전송 받기 위해 파라미터로 프롭스 사용 합니다.


      <form onSubmit={event=>{
        event.preventDefault();
        const title = event.target.title.value;
        const body = event.target.body.value;
        props.onCreate(title, body);
      }}>

 

펑션크리에이트 폼태그

 

유저가 작성한 제목과 내용을 변수에 저장해서 메서드 콜합니다.

      {/* <form> 태그는 사용자 입력을 받는 폼을 생성합니다. */}
      {/* onSubmit 이벤트 핸들러는 폼이 제출될 때 실행될 함수를 정의합니다. */}
      <form onSubmit={event=>{
        // 폼 제출 시 페이지가 새로고침되는 기본 동작을 방지합니다.
        event.preventDefault();
        // 폼 필드의 값을 가져옵니다. event.target은 폼 자체를 가리키며,
        // name 속성(title, body)을 통해 해당 input/textarea 요소에 접근합니다.
        const title = event.target.title.value;
        const body = event.target.body.value;
        // 부모 컴포넌트(App)로부터 전달받은 onCreate 함수를 호출하며,
        // 새로 입력받은 제목과 내용을 인자로 전달하여 새로운 토픽을 생성하도록 지시합니다.
        props.onCreate(title, body);
      }}>


전달 받은 데이터 세이브 합니다.


export default function App() {
  const [mode, setMode] = useState("WELCOME");
  const [id, setId] = useState(null);
  const [nextId, setNextId] = useState(4);
  const [topics, setTopics] = useState([
    { id: 1, title: "html", body: "html is ..." },
    { id: 2, title: "css", body: "css is ..." },
    { id: 3, title: "javascript", body: "javascript is ..." },
  ]);
  let content = 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 title={title} body={body}></Article>;
  } 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);
        }}
      ></Create>
    );
  }
  return (
    <div className="App">
      <Header
        title="WEB"
        onChangeMode={() => {
          setMode("WELCOME");
        }}
      ></Header>
      <Nav
        topics={topics}
        onChangeMode={(_id) => {
          setMode("READ");
          setId(_id);
        }}
      ></Nav>
      {content}
      <a
        href="/create"
        onClick={(event) => {
          event.preventDefault();
          setMode("CREATE");
        }}
      >
        Create
      </a>
    </div>
  );
}

 

Completion


  const [topics, setTopics] = useState([
    {id:1, title:'html', body:'html is ...'},
    {id:2, title:'css', body:'css is ...'},
    {id:3, title:'javascript', body:'javascript is ...'}
  ]);

 

펑션앱 상단 토픽스 변수

 

토픽스 데이터를 스테이트로 변경 합니다.

  // 'topics' 상태는 애플리케이션에서 다룰 글(토픽)들의 목록을 배열 형태로 저장합니다.
  // useState를 통해 이 데이터가 상태로 관리되어 변경 시 화면이 다시 렌더링됩니다.
  const [topics, setTopics] = useState([
    {id:1, title:'html', body:'html is ...'},
    {id:2, title:'css', body:'css is ...'},
    {id:3, title:'javascript', body:'javascript is ...'}
  ]);

    content = <Create onCreate={(_title, _body)=>{
      const newTopic = {title:_title, body:_body}
    }}></Create>

 

펑션앱 모드크리에이트 콘텐트변수 

 

유저가 쓴 새 글을 오브젝에 저장해야 합니다.

      // 새로운 토픽 객체를 생성합니다. 
      const newTopic = {title:_title, body:_body}

  const [nextId, setNextId] = useState(4);

 

펑션앱 상단 아이디변수 아래

 

새글 저장할 준비를 하다 보니 글 아이디도 필요하겠네요.

글이 새로 생긴다면 글 아이디도 스테이트로 변경하는게 맞을것 같습니다.

원래 데이터가 3개 있으니 4부터 시작하는게 좋겠습니다.

  // 'nextId' 상태는 새로운 토픽이 생성될 때 할당될 다음 ID 값을 관리합니다.
  // 기존 토픽의 ID가 1, 2, 3이므로 초기값은 4입니다.
  const [nextId, setNextId] = useState(4);

      const newTopic = {id:nextId, title:_title, body:_body}

 

펑션앱 모드크리에이트 콘텐트변수 뉴토픽변수

 

아이디를 세팅했으니 새글 오브젝에 아이디도 추가합니다.

      // 새로운 토픽 객체를 생성합니다. nextId를 사용하여 고유한 ID를 부여합니다.
      const newTopic = {id:nextId, title:_title, body:_body}

      const newTopics = [...topics]
      newTopics.push(newTopic);
      setTopics(newTopics);

 

펑션앱 모드크리에이트 콘텐트변수 뉴토픽변수 아래

 

리액트는 원시데이터 타입이 변경되면 잘 알아봅니다.

하지만 오브젝이나 어레이 데이터가 변경되면 잘 몰라봅니다.

그래서 오브젝이나 어레이의 경우는 복제해서 그것으로 교체합니다.

그러면 잘 알아봅니다.

즉, 오브젝이나 어레이는 복제본을 들이대야 리액트가 알아 챕니다.

      // 기존 'topics' 배열을 불변성(immutability)을 유지하면서 새로운 배열을 만듭니다.
      // 전개 연산자(...topics)를 사용하여 기존 배열의 모든 요소를 복사한 뒤,
      // 새로 생성한 'newTopic'을 추가합니다.
      const newTopics = [...topics]
      newTopics.push(newTopic);
      // 'topics' 상태를 새롭게 생성된 'newTopics' 배열로 업데이트합니다.
      setTopics(newTopics);

test

타이틀과 내용을 입력하고 크리에잇 탭합니다.

잘 작동하는지 체크합니다.



새글 세입하면 알아서 새글보기로 이동하는 것이 좋겠습니다.


export default function App() {
  const [mode, setMode] = useState("WELCOME");
  const [id, setId] = useState(null);
  const [nextId, setNextId] = useState(4);
  const [topics, setTopics] = useState([
    { id: 1, title: "html", body: "html is ..." },
    { id: 2, title: "css", body: "css is ..." },
    { id: 3, title: "javascript", body: "javascript is ..." },
  ]);
  let content = 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 title={title} body={body}></Article>;
  } 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>
    );
  }
  return (
    <div className="App">
      <Header
        title="WEB"
        onChangeMode={() => {
          setMode("WELCOME");
        }}
      ></Header>
      <Nav
        topics={topics}
        onChangeMode={(_id) => {
          setMode("READ");
          setId(_id);
        }}
      ></Nav>
      {content}
      <a
        href="/create"
        onClick={(event) => {
          event.preventDefault();
          setMode("CREATE");
        }}
      >
        Create
      </a>
    </div>
  );
}

 

Completion


      setMode('READ');
      setId(nextId);
      setNextId(nextId+1);

 

펑션앱 모드크리에이트 콘텐트변수 셋토픽스메서드 아래

 

모드를 읽기로 변경하고 새글 아이디를 세팅하고 다음 아이디도 미리 세팅합니다.

      // 새로운 토픽 생성 후, 해당 토픽의 내용으로 즉시 이동하여 보여주기 위해 모드를 'READ'로 변경합니다.
      setMode('READ');
      // 방금 생성된 토픽의 ID를 현재 읽을 토픽의 ID로 설정합니다.
      setId(nextId);
      // 다음 번에 생성될 토픽의 ID를 1 증가시킵니다.
      setNextId(nextId+1);

test



Completion

import "./styles.css";
import { useState } from "react";

function Header(props) {
  console.log("props", props.title);
  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>Create</h2>
      <form
        onSubmit={(event) => {
          event.preventDefault();
          const title = event.target.title.value;
          const body = event.target.body.value;
          props.onCreate(title, body);
        }}
      >
        <p>
          <input type="text" name="title" placeholder="title" />
        </p>
        <p>
          <textarea name="body" placeholder="body"></textarea>
        </p>
        <p>
          <input type="submit" value="Create"></input>
        </p>
      </form>
    </article>
  );
}

export default function App() {
  const [mode, setMode] = useState("WELCOME");
  const [id, setId] = useState(null);
  const [nextId, setNextId] = useState(4);
  const [topics, setTopics] = useState([
    { id: 1, title: "html", body: "html is ..." },
    { id: 2, title: "css", body: "css is ..." },
    { id: 3, title: "javascript", body: "javascript is ..." },
  ]);
  let content = 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 title={title} body={body}></Article>;
  } 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>
    );
  }
  return (
    <div className="App">
      <Header
        title="WEB"
        onChangeMode={() => {
          setMode("WELCOME");
        }}
      ></Header>
      <Nav
        topics={topics}
        onChangeMode={(_id) => {
          setMode("READ");
          setId(_id);
        }}
      ></Nav>
      {content}
      <a
        href="/create"
        onClick={(event) => {
          event.preventDefault();
          setMode("CREATE");
        }}
      >
        Create
      </a>
    </div>
  );
}

Comment ver

// styles.css 파일을 가져와 애플리케이션의 스타일을 적용합니다.
import "./styles.css";
// React의 useState 훅을 가져와 함수형 컴포넌트에서 상태를 관리할 수 있도록 합니다.
import { useState } from "react";

/**
 * Header 컴포넌트: 애플리케이션의 상단 영역을 담당합니다.
 * @param {object} props - 컴포넌트에 전달되는 속성 객체입니다.
 * @param {string} props.title - 헤더에 표시될 제목입니다.
 * @param {function} props.onChangeMode - 제목 클릭 시 호출될 콜백 함수입니다.
 */
function Header(props) {
  // props.title 값을 콘솔에 출력하여 디버깅 용도로 사용합니다.
  console.log("props", props.title);
  return (
    <header>
      <h1>
        {/* 헤더 제목 링크: 클릭 시 기본 동작을 막고 onChangeMode 함수를 호출하여 페이지 모드를 변경합니다. */}
        <a
          href="/"
          onClick={(event) => {
            event.preventDefault(); // 기본 링크 동작(페이지 새로고침)을 방지합니다.
            props.onChangeMode(); // 부모 컴포넌트로부터 전달받은 모드 변경 함수를 호출합니다.
          }}
        >
          {props.title} {/* props로 전달받은 제목을 표시합니다. */}
        </a>
      </h1>
    </header>
  );
}

/**
 * Nav 컴포넌트: 내비게이션 메뉴를 렌더링합니다.
 * @param {object} props - 컴포넌트에 전달되는 속성 객체입니다.
 * @param {Array<object>} props.topics - 내비게이션 항목으로 사용될 주제(topic) 배열입니다. 각 객체는 id, title, body를 포함합니다.
 * @param {function} props.onChangeMode - 내비게이션 항목 클릭 시 호출될 콜백 함수입니다. 선택된 항목의 id를 인자로 전달합니다.
 */
function Nav(props) {
  const lis = []; // 내비게이션 리스트 아이템(<li>)을 저장할 배열입니다.
  // props로 전달받은 topics 배열을 순회하며 리스트 아이템을 생성합니다.
  for (let i = 0; i < props.topics.length; i++) {
    let t = props.topics[i]; // 현재 topic 객체를 가져옵니다.
    lis.push(
      // 각 topic에 대한 리스트 아이템을 생성합니다.
      <li key={t.id}>
        {/* 내비게이션 링크: 클릭 시 기본 동작을 막고 onChangeMode 함수를 호출합니다. */}
        <a
          id={t.id} // 링크의 id를 topic의 id로 설정하여 클릭 이벤트에서 접근할 수 있도록 합니다.
          href={"/read/" + t.id} // 링크의 URL을 설정합니다.
          onClick={(event) => {
            event.preventDefault(); // 기본 링크 동작(페이지 이동)을 방지합니다.
            // 부모 컴포넌트로부터 전달받은 모드 변경 함수를 호출하며, 클릭된 항목의 id를 숫자로 변환하여 전달합니다.
            props.onChangeMode(Number(event.target.id));
          }}
        >
          {t.title} {/* topic의 제목을 링크 텍스트로 표시합니다. */}
        </a>
      </li>
    );
  }
  return (
    <nav>
      <ol>{lis}</ol> {/* 생성된 리스트 아이템들을 정렬된 리스트(<ol>)로 렌더링합니다. */}
    </nav>
  );
}

/**
 * Article 컴포넌트: 메인 콘텐츠 영역을 담당합니다.
 * @param {object} props - 컴포넌트에 전달되는 속성 객체입니다.
 * @param {string} props.title - 아티클의 제목입니다.
 * @param {string} props.body - 아티클의 본문 내용입니다.
 */
function Article(props) {
  return (
    <article>
      <h2>{props.title}</h2> {/* props로 전달받은 제목을 표시합니다. */}
      {props.body} {/* props로 전달받은 본문 내용을 표시합니다. */}
    </article>
  );
}

/**
 * Create 컴포넌트: 새로운 게시글을 생성하기 위한 폼을 렌더링합니다.
 * @param {object} props - 컴포넌트에 전달되는 속성 객체입니다.
 * @param {function(string, string): void} props.onCreate - 폼 제출 시 호출될 콜백 함수입니다. 제목과 본문 내용을 인자로 전달합니다.
 */
function Create(props) {
  return (
    <article>
      <h2>Create</h2> {/* 'Create' 제목을 표시합니다. */}
      {/* 폼 제출 시 기본 동작을 방지하고, 입력된 제목과 본문으로 onCreate 콜백 함수를 호출합니다. */}
      <form
        onSubmit={(event) => {
          event.preventDefault(); // 폼 제출 시 페이지 새로고침을 방지합니다.
          // 폼 요소의 name 속성을 이용하여 입력된 제목과 본문 내용을 가져옵니다.
          const title = event.target.title.value;
          const body = event.target.body.value;
          props.onCreate(title, body); // 부모 컴포넌트로부터 전달받은 생성 함수를 호출합니다.
        }}
      >
        <p>
          <input type="text" name="title" placeholder="title" /> {/* 제목 입력 필드입니다. */}
        </p>
        <p>
          <textarea name="body" placeholder="body"></textarea> {/* 본문 내용 입력 필드입니다. */}
        </p>
        <p>
          <input type="submit" value="Create"></input> {/* 제출 버튼입니다. */}
        </p>
      </form>
    </article>
  );
}

/**
 * App 컴포넌트: 애플리케이션의 메인 엔트리 포인트입니다.
 * 전체 UI를 구성하고 상태를 관리합니다.
 */
export default function App() {
  // 현재 애플리케이션의 모드를 관리하는 상태 변수입니다 (예: "WELCOME", "READ", "CREATE").
  const [mode, setMode] = useState("WELCOME");
  // 현재 선택된 topic의 ID를 관리하는 상태 변수입니다.
  const [id, setId] = useState(null);
  // 새로운 topic에 할당될 다음 ID를 관리하는 상태 변수입니다.
  const [nextId, setNextId] = useState(4);
  // 내비게이션 및 콘텐츠를 구성하는 데 사용될 주제(topic) 데이터 배열입니다.
  // useState를 사용하여 상태로 관리되므로, 데이터 추가 시 리렌더링이 발생합니다.
  const [topics, setTopics] = useState([
    { id: 1, title: "html", body: "html is ..." },
    { id: 2, title: "css", body: "css is ..." },
    { id: 3, title: "javascript", body: "javascript is ..." },
  ]);

  let content = null; // 현재 모드에 따라 렌더링될 메인 콘텐츠 컴포넌트를 저장할 변수입니다.

  // 현재 mode 상태에 따라 다른 콘텐츠를 렌더링합니다.
  if (mode === "WELCOME") {
    // mode가 "WELCOME"일 경우, 환영 메시지를 담은 Article 컴포넌트를 렌더링합니다.
    content = <Article title="Welcome" body="Hello, Web"></Article>;
  } else if (mode === "READ") {
    // mode가 "READ"일 경우, 선택된 topic의 내용을 담은 Article 컴포넌트를 렌더링합니다.
    let title,
      body = null;
    // topics 배열을 순회하여 현재 선택된 id와 일치하는 topic을 찾습니다.
    for (let i = 0; i < topics.length; i++) {
      if (topics[i].id === id) {
        title = topics[i].title; // 해당 topic의 제목을 가져옵니다.
        body = topics[i].body; // 해당 topic의 본문 내용을 가져옵니다.
      }
    }
    content = <Article title={title} body={body}></Article>; // 찾은 topic 내용으로 Article 컴포넌트를 렌더링합니다.
  } else if (mode === "CREATE") {
    // mode가 "CREATE"일 경우, Create 컴포넌트를 렌더링하여 새 글 작성을 허용합니다.
    content = (
      <Create
        onCreate={(_title, _body) => {
          // Create 컴포넌트에서 전달받은 제목과 본문으로 새로운 topic 객체를 생성합니다.
          const newTopic = { id: nextId, title: _title, body: _body };
          // 기존 topics 배열을 복사하여 불변성을 유지하고 새 topic을 추가합니다.
          const newTopics = [...topics];
          newTopics.push(newTopic);
          setTopics(newTopics); // 업데이트된 topics 배열로 상태를 변경합니다.
          setMode("READ"); // 새 글 생성 후 "READ" 모드로 전환합니다.
          setId(nextId); // 새로 생성된 글의 ID를 현재 선택된 ID로 설정합니다.
          setNextId(nextId + 1); // 다음 글 작성을 위해 nextId를 1 증가시킵니다.
        }}
      ></Create>
    );
  }

  return (
    <div className="App">
      {/* Header 컴포넌트를 렌더링하고, 제목은 "WEB"으로 설정합니다. */}
      {/* onChangeMode 프롭스에 콜백 함수를 전달하여 클릭 시 mode를 "WELCOME"으로 변경하도록 합니다. */}
      <Header
        title="WEB"
        onChangeMode={() => {
          setMode("WELCOME");
        }}
      ></Header>
      {/* Nav 컴포넌트를 렌더링하고, topics 데이터를 전달합니다. */}
      {/* onChangeMode 프롭스에 콜백 함수를 전달하여 클릭 시 mode를 "READ"로, id를 선택된 topic의 ID로 변경하도록 합니다. */}
      <Nav
        topics={topics}
        onChangeMode={(_id) => {
          setMode("READ");
          setId(_id);
        }}
      ></Nav>
      {/* 현재 mode에 따라 동적으로 결정된 content 컴포넌트를 렌더링합니다. */}
      {content}
      {/* "Create" 링크: 클릭 시 기본 동작을 막고 mode를 "CREATE"로 변경하여 글쓰기 폼을 표시합니다. */}
      <a
        href="/create"
        onClick={(event) => {
          event.preventDefault(); // 기본 링크 동작(페이지 이동)을 방지합니다.
          setMode("CREATE"); // 모드를 "CREATE"로 설정하여 새 글 작성 폼을 표시합니다.
        }}
      >
        Create
      </a>
    </div>
  );
}

'생활코딩! React 리액트 프로그래밍' 카테고리의 다른 글

egoing - React - delete  (0) 2025.06.25
egoing - React - update  (0) 2025.06.24
egoing - React - state  (0) 2025.06.23
egoing - React - event  (0) 2025.06.21
egoing - React - props - array  (0) 2025.06.21