TC - 22번일지 (최종정리)

Debug-Life ·2023년 4월 1일
0

1. 환경설정 요약

  1. Node.js 설치
  2. Create-React-App 혹은 vite 프로젝트 설치

    2-1. 그런데 난 vite 를 이용할 것이기 때문에
    만약 create-react-app이 아니라 vite를 이용하고 싶다면 node 창에서
    npm create vite를 입력한 후 폴더 이름을 생성해주고 React를 선택해주면 프로젝트가 생성된다.
    생성된 폴더를 vscode에서 열어주고 터미널에서 npm install로 패키지를 실행한다.
    npm run dev을 터미널에 입력해주면 아래처럼 성공한다.

    2-2. Create-React-App 프로젝트 설치
    vscode 터미널에서 npx-create-react-app [프로젝트이름]

  3. 터미널에서 npm start를 입력해주면 브라우저가 열리고 리액트 아이콘이 돌아가는 기본 데모웹이 실행된다.



2. 동작 방식 요약

소스코드 파일을 보면,
1. 하나의 html,css 파일
2. App.jsx
3. main.jsx
4. pakage.json / lock
5. git ignore
6. vite config



2-1. 클 틀

  • 리액트는 하나의 html 파일로 동작함.
  • 처음에 가장 처음 실행되는 파일 : main.jsx파일.
  • import 되는 파일들 : react, react-dom 같은 라이브러리. package.json에 설치가 되어있음.
  • package.json은 Node.js가 이용함.

즉, Node.js 애플리케이션 자체를 만드는 프로젝트가 아닌 프로젝트에서 패키지 관리를 위해 이용한다. 다시 말해서 package.json에 어떤 외부파일에 의존할건지 결정된다. 리액트 라이브러리는 react react-dom 두개가 전부.

그리고 import를 하고나서 ReactDOM으로 시작함. 해석해보자면 root라는 id를 갖고 있는 html 파일의 요소들을 render하라는 이야기. 무엇을? 아래의 코드들을.


2-2. 작은틀

  • App 컴포넌트 하나만 렌더링 됨.
  • 리액트는 결국 이 컴포넌트의 모음집.
  • jsx, 동적 표현.
    -사용자가 만든 커스텀 컴포넌트들은 jsx 코드를 반환하고, 동적 표현식은 중괄호로 표현
  • main.jsx 가 렌더링 하고, 상위 컴포넌트 하나만 반환해야함.
  • props : 값전달을 위해 사용. 안에 객체, 함수 전부 전달 가능함.
  • css 규칙 1 : .module.css classes 라는 이름으로 import
  • css 규칙 2 : jsx 코드에선 css에 접근하기 위해 className 사용



< 구현 가능한 기능들 요약 >

3. state (상태관리)

  • 유저가 어플이랑 상호작용하게 도와주는 리액트 도구들.
  • 핵심은 상태 업데이트 함수가 실행될 때 (상태 업데이트 함수를 호출하면) 메모리에 새 상태 값을 저장할 뿐 아니라 리액트가 그 함수를 다시 호출한다.

useState , useEffect 등등


3-1. 상태 올리기

✍ 좌측이 수정전 우측이 수정 후 PostList.jsx

역할 :

  • bodyChangeHandler 함수를 onBodyChange 담아서 객체로써 NewPost 컴포넌트에 전달

목적 :

  • 상태가 변경되는 컴포넌트에서 상태를 업데이트할 수 있고 동시에 이벤트가 발생하는 곳에 리스너를 둘 수 있음.
  • props 기능.

3-2. 이전 상태를 기반으로 업데이트

TC 프로젝트 5번 일지 포스팅 참고. 길어서 링크로 남김.




4. 오버레이 스타일

  • 아래에 표시될 컴포넌트로 래핑
  • "children" 프로퍼티를 이용 이 스타일을 이용
    children은 감싸고 있는 모든 콘텐츠.



5. 백엔드와 HTTP 통신


간단한 백엔드 api와 통신해서 데이터를 보내고, 혹은 데이터를 가져오는 작업.

const express = require('express');
const bodyParser = require('body-parser');

const { getStoredPosts, storePosts } = require('./data/posts');

const app = express();

app.use(bodyParser.json());

app.use((req, res, next) => {
  // Attach CORS headers
  // Required when using a detached backend (that runs on a different domain)
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

app.get('/posts', async (req, res) => {
  const storedPosts = await getStoredPosts();
  // await new Promise((resolve, reject) => setTimeout(() => resolve(), 1500));
  res.json({ posts: storedPosts });
});

app.get('/posts/:id', async (req, res) => {
  const storedPosts = await getStoredPosts();
  const post = storedPosts.find((post) => post.id === req.params.id);
  res.json({ post });
});

app.post('/posts', async (req, res) => {
  const existingPosts = await getStoredPosts();
  const postData = req.body;
  const newPost = {
    ...postData,
    id: Math.random().toString(),
  };
  const updatedPosts = [newPost, ...existingPosts];
  await storePosts(updatedPosts);
  res.status(201).json({ message: 'Stored new post.', post: newPost });
});

app.listen(8080);

5-1. 데이터 보내기 - fetch()


5-2. 데이터 가져오기 (비동기 처리) - useEffect hooks,

useEffect 라는 훅을 이용해 데이터를 비동기적으로 가져올때 이용함.
이유 : 서버 상황에따라 데이터를 가져오는데 시간이 많이 걸리는 경우가 많음.

단점 : 남용하면 코드 유지보수 불가능에 가까워짐. 꼭 필요한, 개발자들이 정해놓은 경우에만 사용.



6. 라우팅

  • 여러 경로를 정의해 여러 페이지를 로드하고 해당 경로에서 다른 컴포넌트를 로드하기 위해 사용. 대부분의 앱에는 라우팅이 필요함.
  • 해당 url 마다 공유할 수 있는 수단

6-1. 예시

✍main.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider, createBrowserRouter } from "react-router-dom";

import App from "./App";
import NewPost from "./components/NewPost";
import "./index.css";

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
  },
  {
    path: "/create-post",
    element: <NewPost />,
  },
]);

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

6-2. 레이아웃 라우트

✍main.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider, createBrowserRouter } from "react-router-dom";

import App from "./App";
import NewPost from "./components/NewPost";
import RootLayout from "./routes/RootLayout";
import "./index.css";

const router = createBrowserRouter([
  {
    path: "/",
    element: <RootLayout />,
    chlidren: [
      {
        path: "/",
        element: <App />,
      },
      {
        path: "/create-post",
        element: <NewPost />,
      },
    ],
  },
]);

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

6-3. loader() , action()


6-4. 동적 라우트



7. 예외처리

✍ PostList.jsx (frontend project)

  useEffect(() => {
    async function fetchPosts() {
      setIsFetching(true);
      const response = await fetch("http://localhost:8080/posts");
      const resData = await response.json();
      if (!response.ok) {
        <div style={{ textAlign: "center", color: "white" }}>
          <p>응답이 비정상적입니다...</p>
        </div>;
      }
      setPosts(resData.posts);
      setIsFetching(false);
    }

    fetchPosts();
  }, []);



profile
인생도 디버깅이 될까요? 그럼요 제가 하고 있는걸요

0개의 댓글