Create React App,
약칭 CRA는 리액트 웹 애플리케이션 개발을 위한 웹팩과 바벨 설정 등을 제공합니다
간단히 말해서 번거로운 리액트 초기 세팅 작업을 대신해주는 고마운 툴입니다
npm install @babel/core babel-loader css-loader webpack webpack-cli...
↓↓↓
npx create-react-app [프로젝트명]
npx
명령어는 기본적으로 패키지를 실행시키는 명령어이지만, 해당 패키지가 설치되어 있지 않다면 설치 후 실행시킵니다npm cache clean --force
& sudo npm install -g npm
나머지 필요한 패키지 설치
npm install react-router-dom styled-components
+) 포트번호 변경
CRA를 통해 생성한 프로젝트는 기본 실행 포트가 3000번으로 설정되어 있습니다
이를 바꾸고 싶다면 프로젝트 폴더에 .env
파일을 생성한 뒤 포트넘버를 기입하면 됩니다
[.env]
PORT=3005
디렉토리 구조
/src
|-- components
|-- layouts
|-- pages
|-- hooks
components
: 세부 컴포넌트에 대한 내용을 만들어서 저장합니다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>
);
};
리액트(싱글 페이지 어플리케이션)에서 링크 이동과 같은 효과를 내려면 어떻게 해야 할까요
당연히 실제로 링크이동(새로고침)이 되어서는 안됩니다
위에서 설치한 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>
코드가 발동하도록 할 수도 있습니다
*ReactDOM
과 react-router-dom
은 21년 12월을 기준으로 문법과 내용이 많이 달라졌습니다
검색 시 주의...
로그인 기능을 구현하려면 그 상태관리는 최상위 컴포넌트인 <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()
};