TIL) Vanilla JS에서 React로 리팩토링

정우시·2022년 8월 17일
1

2. 코드스테이츠

목록 보기
34/52

Section 1에서 구현한 나만의 아고라 스테이츠를 React로 다시 개발하였습니다.

my-agora-states-in-react


  • Section 1에서 구현한 나만의 아고라 스테이츠를 React로 다시 개발합니다.
    • create-react-app으로 프로젝트 생성

    • 기존에 만든 나만의 아고라 스테이츠를 React로 옮기기

      • 디스커션 나열 기능
    • 로컬 환경에서 실행한 나만의 아고라 스테이츠 서버에서 discussions 데이터를 조회합니다.

      • 더 이상 data.js 파일을 사용하지 않고, discussions 데이터를 받아옵니다.
      • Fetch API를 이용합니다.

1. 필요없는 파일 지우기

  • create-react-app를 다운 받은 후 필요없는 파일은 지운다.
- logo.svg
- reportWebVitals.js
- setupTests.js
- App.test.js

1-1. 파일에서 필요없는 코드는 지워서 깔끔하게 만든다.

App.js

import './App.css';
function App() {
  return (
    <div>
      
    </div>
  );
}

export default App;

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

2. 와이어프레임

  • 해당 이미지를 컴포넌트 단위로 나눠보기

  • 맨 위에는 Form.js로 만들어본다.
  • 질문들이 나오는 리스트는 Discussions.js
  • 질문 하나하나는 Discussion.js

→ 질문들을 map을 이용하여 Discussion.js에 뿌려준다.

→ 페이지가 단순하기 때문에 굳이 SPA 없이 (따라서 라우터도 필요없다.) 컴포넌트 파일을 만들어서 바로 만든다.


3. 컴포넌트 별로 하나씩 자바스크립트 파일을 만든다.

  • Form.js
  • Discussion.js
  • Discussions.js

4. Form.js

  • 전에 html로 만든 form 코드를 복사한다.

  • 가져온 후에 jsx로 변환한다.

  • 크롬에서 Copy element를 통해 form__container를 다 가져온다.

  • 복사한 form 코드를 App.js에서 사용하기 위해 export를 해주고 그 안에 return을 해준다.

  • 에러나 나온다면 화살표가 해당하는 곳에서 확인할 수 있다. JSX 요소 'input'에 닫는 태그가 없는 것으로 알 수 있다.

  • class를 className으로 바꿔줘야 한다. 같은 class이기에 cmd + D 또는 cmd + shift + L을 눌러주면 한번에 선택이 가능하다.

  • JSX는 for가 아니라 htmlFor를 적어야 한다. 그래서 <label for="name">Enter your name: </label>이렇게 된 것을 <label htmlFor="name">Enter your name: </label> 이렇게 적는다.


5. App.js

  • App.js에서 form을 받아오면 된다.

  • import를 해주고 div안에 form을 써준다.

  • Discussions도 연결해준다.

  • usestate를 통해 데이터가 올 때마다 바꿔준다.

    • 빈 배열로 하고 discussions, setDiscussions로 해준다.
    • usestate를 import를 해준다.
  • 이제 useEffect를 통해 데이터를 받아온다.

    • 리액트는 제어가 안되는 것을 싫어한다.
    • fetch는 리액트 입장에서 side effect이다.
    • 이것을 제어하기 위해 useEffect를 사용한다.
    • json으로 해독한 데이터를 상태 갱신 함수로 설정한 setDiscussions를 통해 갱신을 해줘야 한다.
    • 그리고 마지막에 빈 배열([])을 표시해줘야 데이터를 한번만 받아오게 된다. 안그러면 데이터를 계속 받아오게 된다. (dependency array)
    • 이제 discussions에 데이터가 저장이 되게 된다. 그리고 discussions에 받아온 데이터를 Discussions에 넘겨줘야 한다. 그러면 discussions가 props가 된다.
import './App.css';
import { useEffect, useState } from 'react';
import { Discussions } from './components/Discussions';
import {Form} from "./components/Form"

function App() {
  const [discussions, setDiscussions] = useState([]);

  useEffect(() => {
    fetch('http://localhost:4000/discussions')
    .then(res => res.json())
    .then(data => {
      setDiscussions(data);
    })
  }, []);

  return (
    <div>
      <Form />
      <Discussions discussions={discussions}></Discussions>
    </div>
  );
}

export default App;

6. Discussions.js

  • form.js와 같이 복사를 하는데 이번에는 <section class="discussion__wrapper"></section>에서 복사를 한다.

  • 에러를 확인해보니 이미지에 닫는 태그가 없어서 닫는 태그를 달아준다.

  • 필요없는 li는 없애주고 ul로 닫아준다.

  • App.js와 연결해주기 위해 export를 해준다. 그리고 return도 같이 해준다.

  • class를 claaName으로 해준다.

  • map을 사용해서 질문질문 하나를 컴포넌트로 만들어준다.

export const Discussions = () => {
    return(
<section class="discussion__wrapper">
        <ul class="discussions__container">
          <li class="discussion__container">
            <div class="discussion__avatar--wrapper">
              <img class="discussion__avatar--image" src="https://avatars.githubusercontent.com/u/12145019?s=64&amp;u=5c97f25ee02d87898457e23c0e61b884241838e3&amp;v=4" alt="avatar of kimploo" />
            </div>
            <div class="discussion__content">
              <h2 class="discussion__title">
                <a href="https://github.com/codestates-seb/agora-states-fe/discussions/6">[notice] 좋은 질문하는 법</a>
              </h2>
              <div class="discussion__information">
                kimploo / 2022-04-22T14:08:33Z
              </div>
            </div>
            <div class="discussion__answered"><p></p></div>
          </li>
        </ul>
      </section>
    )}
  • 현재 <li> 태그로 묶인 곳이 반복되고 있기 때문에 map을 이용해서 데이터로 뿌려줄 것이다. 따라서 <li> 태그는 Discussion.js로 옮겨서 따로 컴포넌트를 만들어주고 Discussions.js에서는 <li>를 없애준다. 없애면 바로 아래와 같은 코드가 된다.

  • 현재 Discussions가 빈 껍데기이기에 App.js에서 작업을 해줘야 한다.

  • App.js에서 작업이 완료가 되었으면 이제 map으로 처리를 해주면 된다.

  • ul 태그 안에 중괄호를 먼저 쓰고 {discussions.map}을 해서 discussion의 리턴 <Discussion></Discussion>을 해주면 된다.

  • 그리고 Discussion에 discussion을 내려줘야 한다.

  • 그리고 map에서 key를 써줘야 한다.

  • 그리고 Discussion의 import를 잊지 않는다.

    • {discussions.map(discussion= > {return <Discussion discussion={discussion} key={discussion.id}></Discussion>})}
import { Discussion } from './Discussion';

export const Discussions = (discussions) => {
    return(
<section className="discussion__wrapper">
        <ul className="discussions__container">
          {discussions.map(discussion => {
            return <Discussion discussion={discussion} key={discussion.id}></Discussion>
          })}
        </ul>
      </section>
    );
}

7. index.css

  • 개발자 도구를 통해 css 파일을 다 가져온다.

8. index.html

  • index.html 기본값이 div로 되어 있는데 이거를 css에는 main으로 되어있기에 main으로 바꿔준다.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <main id="root"></main>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

9. Discussion.js

  • Discussion.js에서는 <li> 태그를 따로 컴포넌트로 만든다.

  • 먼저 export 해준다.

  • 데이터를 받아온다.

  • Discussion은 데이터가 올 때마다 데이터가 렌더링이 되면서 바뀌기 때문에 App.js에서 usestate를 사용해야 한다.

  • App.js에서 처리한 데이터를 Discussion.js로 받아온다.

export const Discussion = () => {
<li class="discussion__container">
            <div class="discussion__avatar--wrapper">
              <img class="discussion__avatar--image" src="https://avatars.githubusercontent.com/u/12145019?s=64&amp;u=5c97f25ee02d87898457e23c0e61b884241838e3&amp;v=4" alt="avatar of kimploo" />
            </div>
            <div class="discussion__content">
              <h2 class="discussion__title">
                <a href="https://github.com/codestates-seb/agora-states-fe/discussions/6">[notice] 좋은 질문하는 법</a>
              </h2>
              <div class="discussion__information">
                kimploo / 2022-04-22T14:08:33Z
              </div>
            </div>
            <div class="discussion__answered"><p></p></div>
          </li>
}
  • 하드 코딩된 코드를 수정한다.
export const Discussion = ({discussion}) => {
    const {url, author, avatarUrl, title, createdAt, answer} = discussion;
    return (
      <li className="discussion__container">
      <div className="discussion__avatar--wrapper">
      <img className="discussion__avatar--image" src={avatarUrl} alt={`avatar of ${author}`} />
      </div>
    <div className="discussion__content">
    <h2 className="discussion__title"><a href={url}>{title}</a></h2>
    <div className="discussion__information">{`${author} / ${new Date(createdAt).toLocaleString()}`}</div>
    </div>
    <div className="discussion__answered">
      <p className="discussion__isAnswered">{answer ? "✅" : "❎" }</p>
    </div>
  </li>
    )}

⭐️ 안되면 서버 작동하고 있는지 확인한다.

profile
프론트엔드 공부하고 있는 정우시입니다.

0개의 댓글