CRA (23/03/06)

nazzzo·2023년 3월 6일
0

1. CRA (Create React App)




Create React App,
약칭 CRA는 리액트 웹 애플리케이션 개발을 위한 웹팩과 바벨 설정 등을 제공합니다
간단히 말해서 번거로운 리액트 초기 세팅 작업을 대신해주는 고마운 툴입니다


npm install @babel/core babel-loader css-loader webpack webpack-cli...

↓↓↓

npx create-react-app [프로젝트명]

  • npx 명령어는 기본적으로 패키지를 실행시키는 명령어이지만, 해당 패키지가 설치되어 있지 않다면 설치 후 실행시킵니다
  • 프로젝트명은 폴더명이라고 봐도 무방합니다
  • 설치가 끝나면 기본적으로 git이 활성화됩니다
  • 만약 설치중 문제가 생긴다면 npm cache clean --force & sudo npm install -g npm
  • npm run start : 리액트 서버를 실행합니다 (개발모드)
  • npm run build : 번들링만 진행합니다
  • npm run test : 테스트 코드를 실행합니다
  • npm run eject: 사용 후 되돌아갈 수 없습니다 (불가피한 경우가 아니면 사용 X)

나머지 필요한 패키지 설치

npm install react-router-dom styled-components



+) 포트번호 변경

CRA를 통해 생성한 프로젝트는 기본 실행 포트가 3000번으로 설정되어 있습니다
이를 바꾸고 싶다면 프로젝트 폴더에 .env 파일을 생성한 뒤 포트넘버를 기입하면 됩니다


[.env]

PORT=3005



2. CSR (Client Side Rendering)


디렉토리 구조

/src
|-- components
|-- layouts
|-- pages
|-- hooks
  • components : 세부 컴포넌트에 대한 내용을 만들어서 저장합니다
    ex) button, input, checkbox...
  • hooks : 커스텀 훅을 저장합니다
  • layouts : header, footer, sidebar, quickMenu...
  • pages : 메인 페이지, 게시판, 회사소개 등

*디렉토리 구조를 짜는데 있어서 정답은 없습니다. 각자의 취향대로



header 만들기


[./src/layouts/header.jsx]

export const Header = () => {
  return (
    <>
      <h1>Logo</h1>
      <div id="nav">
        <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">About</a></li>
            <li><a href="#">Login</a></li>
            <li><a href="#">Contact</a></li>
        </ul>
      </div>
    </>
  );
};

[app.jsx]

import { Header } from "./layouts/header"

const App = () => {
  return (
    <>
      <Header></Header>
    </>
  );
}

export default App;

+) 스타일드 컴포넌트 생성 및 적용

[./src/components/header/headerWrapper.styled.jsx]

import styled from 'styled-components'

export const HeaderWrapper = styled.div`
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 20px;
    background-color: #333;
    color: #fff;
`

스타일드 컴포넌트를 적용하려면 header.jsx를 수정해야 합니다

export const Header = () => {
  return (
    <HeaderWrapper>
      <h1>Logo</h1>
      <div id="nav">
        <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">About</a></li>
            <li><a href="#">Login</a></li>
            <li><a href="#">Contact</a></li>
        </ul>
      </div>
    </HeaderWrapper>
  );
};

스타일드 컴포넌트용 디렉토리에 jsx 파일이 많아지면 import 구문이 난잡해질 수 있습니다
이럴 경우를 방지하기 위해 jsx 파일이 담길 디렉토리 안에 index.jsx 파일을 만들어서 관리하는 것을 추천합니다

[./src/components/header/index.jsx]

export * from './logo.styled'
export * from './headerWrapper.styled'
export * from './nav.styled'

[header.jsx]

// import { HeaderWrapper } from "../components/header/headerWrapper.styled";
// import { Logo } from "../components/header/logo.styled";
import { Logo, Nav , HeaderWrapper } from '../components/header'

export const Header = () => {
  return (
    <HeaderWrapper>
      <Logo>Logo</Logo>
      <Nav>
        <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">About</a></li>
            <li><a href="#">Login</a></li>
            <li><a href="#">Contact</a></li>
        </ul>
      </Nav>
    </HeaderWrapper>
  );
};



2-1. React Router

리액트(싱글 페이지 어플리케이션)에서 링크 이동과 같은 효과를 내려면 어떻게 해야 할까요
당연히 실제로 링크이동(새로고침)이 되어서는 안됩니다

위에서 설치한 react-router-dom 라이브러리를 사용하면 라우터와 같은 효과를 낼 수 있습니다


먼저 header.jsx 파일을 아래와 같이 수정합니다

[header.jsx]

import { Logo, Nav , HeaderWrapper } from '../components/header'
import { NavLink } from "react-router-dom"


export const Header = () => {
  return (
    <HeaderWrapper>
      <Logo>Logo</Logo>
      <Nav>
        <ul>
            <li><NavLink to="/">Home</NavLink></li>
            <li><NavLink to="/about">About</NavLink></li>
            <li><NavLink to="/login">Login</NavLink></li>
            <li><NavLink to="/contact">Contact</NavLink></li>
        </ul>
      </Nav>
    </HeaderWrapper>
  );
};

그리고 <App /> <Header /> 사이에서 라우터 역할을 해 줄 <BrowserRouter> 컴포넌트가 필요합니다
<Routes> 컴포넌트에서는 path값에 따라 그에 해당하는 <Route> 컴포넌트가 발동합니다
자바스크립트 switch문과 유사합니다

[app.jsx]

import { Header } from "./layouts/header"
import { Home, About, Contact, Login } from "./pages"
import { BrowserRouter, Routes, Route } from "react-router-dom"


const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        {/* Header에 관한 라우터 */}
        <Route path="*" element={<Header></Header>}></Route>
        {/* <Route path="*" element={<>헤더2</>}></Route> */}
      </Routes>
      <Routes>
        {/* Content에 관한 라우터 */}
        <Route path="/" element={<Home></Home>}></Route>
        <Route path="/About" element={<About></About>}></Route>
        <Route path="/Login" element={<Login></Login>}></Route>
        <Route path="/Contact" element={<Contact></Contact>}></Route>
        <Route path="*" element={<>error</>}></Route>
      </Routes>
    </BrowserRouter>
  );
}

export default App;

이제 헤더 메뉴를 클릭하면 URL 이동효과와 함께 각 path에 해당하는 컴포넌트만 렌더링됩니다

그리고 조건에 맞는 path값이 없으면 <Route path="*" element={<>페이지를 찾을 수 없습니다</>}></Route> 코드가 발동하도록 할 수도 있습니다

*ReactDOMreact-router-dom은 21년 12월을 기준으로 문법과 내용이 많이 달라졌습니다
검색 시 주의...



2-2. 로그인 기능 구현하기


로그인 기능을 구현하려면 그 상태관리는 최상위 컴포넌트인 <App />에서부터 시작할 필요가 있습니다


[app.jsx]

const App = () => {
  const [user, setUser] = useState("")


  return (
    <BrowserRouter>
      <Routes>
        {/* Header에 관한 라우터 */}
        <Route path="*" element={<Header user={user}></Header>}></Route>
      </Routes>
      ...

그리고 프롭스로 하위 컴포넌트에 유저에 관한 상태를 전달합니다

[header.jsx]

export const Header = ({ user }) => {
  return (
    <HeaderWrapper>
      <Logo>Logo</Logo>
      <Nav>
        <ul>
          <li>
            <NavLink to="/">Home</NavLink>
          </li>
          <li>
            <NavLink to="/about">About</NavLink>
          </li>
          {user === "" ? (
            <li>
              <NavLink to="/login">Login</NavLink>
            </li>
          ) : (
            <li>
              <NavLink to="/logout">Logout</NavLink>
            </li>
          )}
          <li>
            <NavLink to="/contact">Contact</NavLink>
          </li>
        </ul>
      </Nav>
    </HeaderWrapper>
  );
};

[login.jsx]

import { useInput } from "../hooks/useInput";

export const Login = () => {
  const userid = useInput(""); // value, onChange
  const userpw = useInput("");

  const handleSubmit = (e) => {
    e.preventDefault();

    if (userid.value === "web7722" && userpw.value === "1234") {
      alert("로그인 성공");
    } else {
      alert("로그인 실패");
    }
  };

  return (
    <>
      <form onSubmit={handleSubmit}>
        <input type="text" {...userid} id="userid" name="userid" />
        <input type="password" {...userpw} id="userpw" name="userpw" />
        <button type="submit">Login</button>
      </form>
    </>
  );
};

[useInput.jsx]

import { useState } from "react";

export const useInput = (initialState) => {
  const [value, setValue] = useState(initialState);
  const onChange = (e) => {
    const { value } = e.target;
    setValue(value);
  };

  return {
    value,
    onChange,
  };
};

이제 로그인이 성공이 되었을 때, 헤더의 상태를 바꿔야 합니다 (로그인 → 로그아웃)
헤더 컴포넌트에 있는 데이터('web7722', '1234')를 어떻게 <App /> 컴포넌트로 끌고 올 수 있을까요?

<App /> 컴포넌트에서 함수의 매개변수를 통해 전달받는 것이 일반적입니다


[app.jsx]

const App = () => {
  const [user, setUser] = useState("")

  const login = (user) => {
    setUser(user)
  }


  return (
    <BrowserRouter>
      <Routes>
        {/* Header에 관한 라우터 */}
        <Route path="*" element={<Header user={user}></Header>}></Route>
      </Routes>

      <Routes>
        {/* Content에 관한 라우터 */}
        <Route path="/" element={<Home></Home>}></Route>
        <Route path="/about" element={<About></About>}></Route>
        <Route path="/login" element={<Login login={login}></Login>}></Route>
        <Route path="/contact" element={<Contact></Contact>}></Route>
        <Route path="*" element={<>error</>}></Route>
      </Routes>
    </BrowserRouter>
  );
}

[login.jsx]

import { useInput } from "../hooks/useInput";
import { useNavigate } from "react-router-dom";

export const Login = ({ login }) => {
  const navigate = useNavigate();
  const userid = useInput(""); // value, onChange
  const userpw = useInput("");

  const handleSubmit = (e) => {
    e.preventDefault();

    if (userid.value === "web7722" && userpw.value === "1234") {
      login(userid.value);
      alert("로그인 성공");
      navigate("/"); // 로그인 성공과 함께 home으로 이동
    } else {
      alert("로그인 실패");
    }
  };

  return (
    <>
      <form onSubmit={handleSubmit}>
        <input type="text" {...userid} id="userid" name="userid" />
        <input type="password" {...userpw} id="userpw" name="userpw" />
        <button type="submit">Login</button>
      </form>
    </>
  );
};

문제는 새로고침이 되면 로그인 상태가 초기화된다는 것인데요...
이에 대한 처리 방법은 다음 포스트에서 알아보도록 하겠습니다



+) 로그아웃 기능 구현

[app.jsx]

  const logout = () => {
    setUser("")
  }
  
    return (
    <BrowserRouter>
      <Routes>
        {/* Header에 관한 라우터 */}
        <Route path="*" element={<Header user={user} logout={logout} ></Header>}></Route>
      </Routes>

[header.jsx]

export const Header = ({ user, logout }) => {
  const handleClick = () => {
    logout()
  };

0개의 댓글