๋ชฉ์ฐจ
๋ฆฌ์กํธ๋ฅผ ๋ณ๋ก ์ข์ํ์ง ์๋ 1์ธ์ด๋ค.
๊ทธ๋ฐ๋ฐ ์ด์ฉ ์ ์์ด ๋ฆฌ์กํธ ํ์ ์ ํด์ผ ํด์ ํผ๋ธ๋ฆฌ์ ๊ฒธ ํ๋ฐํธ ์๋๋ก ์ฐธ์ฌ ์ค์ด๋ค.
jquery๋ผ๋ฉด ๋จ๋ฐ์ ๋๋ผ ๋ฌธ์ ์ ๊ณ ๋ฏผํ์ง ์์๋ ๋ ๋ฌธ์ ๋ค์ด ๋ฆฌ์กํธ์๋ ๋์ณ๋๋ค.
์ ์ฐ๋์ง๋ ์ฌ์ ํ ๋ชจ๋ฅด๊ฒ ์ง๋ง ๊น๋ผ๋๊น ๊น๋ค.
์ผ๋จ SPA ์ฌ์ดํธ์์ ๋ฆฌ์กํธ๊ฐ ํธํ ๊ฒ์ ๋์ถฉ ์ง์์ ๋๋ค.
๊ทธ๋ฐ๋ฐ SPA์ฌ์ดํธ๋ผ๊ณ ๋ ํด๋, ํ ์ฌ์ดํธ์ ์๋ก ๋ค๋ฅธ ๋ ์ด์์์ด ์ฌ๋ฟ ์ฌ์ฉํด์ผ ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
๋จ์ ์ผ๋ก GNB, LNB๊ฐ ์๋ Introํ์ด์ง, Loginํ์ด์ง ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ํ๋ฉด๊ณผ ๋ค๋ฅธ ๊ด๋ฆฌ์ ํ๋ฉด์ ๋ ์ด์์์ ์๋ก ๋ค๋ฅผ ์ ์๋ค. ์๋ ๋ค๋ฅผ ์ ์๋ ์ ๋๊ฐ ์๋๋ผ ๋์ฒด๋ก ๋ค๋ฅด๋ค.
๊ทธ๋ด ๋ ๋ฆฌ์กํธ ์ฌ๋ฌ ๊ฐ์ง ๋ ์ด์์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๊ฝค ๋ค์ํ์ง๋ง ์ง๊ด์ ์ด๊ณ ์ฌ์ด ๋ฐฉ๋ฒ์ ์๊ฐํ๊ฒ ๋ค.
๋ฆฌ์กํธ ๋ฉํฐ ๋ ์ด์์ react multi layout
์ฌ์ค ๊ตฌ๊ธ๋ง ํด์ ๋์ค๋ ์๋ง์ react multi layout ์ค๋ช ๋ค์ ์ง๋ค๋ผ๋ฆฌ๋ง ์๋ ์๊ธฐ๋ฅผ ํ๋ ๊ฑด์ง, ์๊ธฐ๋ค๋ ๋ชจ๋ฅด๋ ๊ฑด์ง ๋๋ฌด์ง ์์๋ค์ ์ ์๊ฑฐ๋, ๋ ์๋ก์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ๋ง ์ค๋ช ํ๊ณ ์๋ค.
์ฌ๊ธฐ์ ์๊ฐํ multi layout์ ๊ธฐ๋ณธ์ ์ผ๋ก react์์ ์ฌ์ฉํ๋ route ๊ธฐ๋ณธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ react-router ๋ง์ผ๋ก ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ์ด๋ค.
์ ๋ชฉ์ connected-react-router๋ผ๊ณ ์จ ๋์ ๊ฒ์ ๋ํด์๋ ์ซ ํ์๊ฐ ์๋ค.
์งํ ์ค์ธ ํ๋ก์ ํธ์์ connected-react-router๋ฅผ ์ฌ์ฉํ๊ณ ์์ด์ ์์๋ฅผ ๊ทธ๋ ๊ฒ ๋ค์์ ๋ฟ์ด๋ค.
connected-react-router๋ redux ๋๋ฌธ์ ์ฐ๋ ๊ฒ์ด๋, ์ด์ฉ๋ฉด ๋ค๋ฅธ ํ๋ก์ ํธ๋ค์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฐ๋ ๊ฒ์ผ ์ ์๋ค.
์ ์ฒด ์์ค๋ฅผ ๊ณต๊ฐํ๊ธฐ์๋ ํ์ฌ ํ๋ก์ ํธ์ ๋ณด์์์ฝ ๋๋ฌธ์ ๋ถ๊ฐํ๊ณ , ๋ฉํฐ ๋ ์ด์ด์ ํต์ฌ๋ง ์ธ๊ธํ๊ฒ ๋ค.
import { Route, Switch } from "react-router"; // react-router v4/v5
import { ConnectedRouter } from "connected-react-router";
// Layout
import CampaignLayout from "./layouts/CampaignLayout";
import DemoGuideLayout from "./layouts/DemoGuideLayout";
// Sub Children
import CampaignPolicyLayout from "./domain/CampaignPolicy/CampaignPolicyLayout";
import CampaignPolicyFormContainer from "./domain/CampaignPolicy/CampaignPolicyFormContainer";
import CampaignPolicyInfoContainer from "./domain/CampaignPolicy/CampaignPolicyInfoContainer";
import DemoFormList from "./domain/DemoGuide/DemoFormListLayout";
import DemoGuideFormContainer from "./domain/DemoGuide/CampaignPolicyFormContainer";
import DemoGuideInfoContainer from "./domain/DemoGuide/CampaignPolicyInfoContainer";
// Login without Layout
import { default as MemberLogin } from "./domain/Member";
function App({ history, context }) {
return (
<ConnectedRouter history={history} context={context}>
<Switch>
<Route exact path="/MemberLogin" component={MemberLogin} />
{/* ์๋ก ๋ค๋ฅธ ๋ ์ด์์์ ํผ์ดํ Route */}
<Route path="/campaign-policy/:path?" exact>
{/* ๋ ์ด์์ ํธ์ถ */}
<CampaignLayout>
{/* ๊ฐ์ ๋ ์ด์์์ ๊ณต์ ํ๋ ์๋ธ๋ฉ๋ด๋ค์ ํ๋์ Switch์์ Route๋จ */}
<Switch>
<Route
exact
path="/campaign-policy"
component={CampaignPolicyLayout}
/>
<Route
exact
path="/campaign-policy/create"
component={CampaignPolicyFormContainer}
/>
<Route
exact
path="/campaign-policy/:id"
component={CampaignPolicyInfoContainer}
/>
</Switch>
</CampaignLayout>
</Route>
<Route path="/demo-guide/:path?" exact>
{/* ๋ ์ด์์ ํธ์ถ */}
<DemoGuideLayout>
{/* ๊ฐ์ ๋ ์ด์์์ ๊ณต์ ํ๋ ์๋ธ๋ฉ๋ด๋ค์ ํ๋์ Switch์์ Route๋จ */}
<Switch>
<Route exact path="/demo-guide/" component={DemoFormList} />
<Route
exact
path="/demo-guide/create"
component={DemoGuideFormContainer}
/>
<Route
exact
path="/demo-guide/:id"
component={DemoGuideInfoContainer}
/>
</Switch>
</DemoGuideLayout>
</Route>
<ToastNotiComponent />
</Switch>
</ConnectedRouter>
);
}
export default App;
๊ฝค ๊ธธ์ด ๋ณด์ด์ง๋ง, ์ฌ์ค 3๊ฐ์ง์ Route๊ฐ ์ฌ์ฉ๋์๋ค.
์ฒซ ๋ฒ์งธ Switch์์ 3๊ฐ์ง ๋ ์ด์์์ด ๋ถ๊ธฐ๋๋ค.
/MemberLogin์์ ๋ถ๊ธฐ๋ ๋ ์ด์์์๋ ํน๋ณํ ๋ ์ด์์์ด ์๋ค.
ํด๋น ํ์ด์ง ์์ฒด๊ฐ ๋ ์ด์์ ํ์์ ๊ฐ๊ณ ์๋ค.
/campaign-policy/ ๊ฒฝ๋ก์์ ์ฌ์ฉ๋๋ ๋ ์ด์์์ <CampaignLayout></CampaignLayout> ์์์ Switch ๋์ด ๋ฉ๋ด๋ณ๋ก ๋ถ๊ธฐ๋๋ค.
๋ง์ฝ /campaign-policy/ ์ ๋ ์ด์์์ ๊ณต์ ํ๋ /campaign-policy2/์๋ธ๋ฉ๋ด๊ฐ ์๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ ์ฉํ ์ ์๋ค.
import { Route, Switch } from "react-router"; // react-router v4/v5
import { ConnectedRouter } from "connected-react-router";
// Layout
import CampaignLayout from "./layouts/CampaignLayout";
import DemoGuideLayout from "./layouts/DemoGuideLayout";
// Sub Children
import CampaignPolicyLayout from "./domain/CampaignPolicy/CampaignPolicyLayout";
import CampaignPolicyFormContainer from "./domain/CampaignPolicy/CampaignPolicyFormContainer";
import CampaignPolicyInfoContainer from "./domain/CampaignPolicy/CampaignPolicyInfoContainer";
import DemoFormList from "./domain/DemoGuide/DemoFormListLayout";
import DemoGuideFormContainer from "./domain/DemoGuide/CampaignPolicyFormContainer";
import DemoGuideInfoContainer from "./domain/DemoGuide/CampaignPolicyInfoContainer";
// Login without Layout
import { default as MemberLogin } from "./domain/Member";
function App({ history, context }) {
return (
<ConnectedRouter history={history} context={context}>
<Switch>
<Route exact path="/MemberLogin" component={MemberLogin} />
{/* ์๋ก ๋ค๋ฅธ ๋ ์ด์์์ ํผ์ดํ Route */}
<Route path="/campaign-policy/:path?" exact>
{/* ๋ ์ด์์ ํธ์ถ */}
<CampaignLayout>
{/* ๊ฐ์ ๋ ์ด์์์ ๊ณต์ ํ๋ ์๋ธ๋ฉ๋ด๋ค์ ํ๋์ Switch์์ Route๋จ */}
<Switch>
<Route
exact
path="/campaign-policy"
component={CampaignPolicyLayout}
/>
<Route
exact
path="/campaign-policy/create"
component={CampaignPolicyFormContainer}
/>
<Route
exact
path="/campaign-policy/:id"
component={CampaignPolicyInfoContainer}
/>
</Switch>
</CampaignLayout>
</Route>
<Route path="/campaign-policy2/:path?" exact>
{/* ๋ ์ด์์ ํธ์ถ */}
<CampaignLayout>
{/* ๊ฐ์ ๋ ์ด์์์ ๊ณต์ ํ๋ ์๋ธ๋ฉ๋ด๋ค์ ํ๋์ Switch์์ Route๋จ */}
<Switch>
<Route
exact
path="/campaign-policy2"
component={CampaignPolicyLayout}
/>
<Route
exact
path="/campaign-policy2/create"
component={CampaignPolicyFormContainer}
/>
<Route
exact
path="/campaign-policy2/:id"
component={CampaignPolicyInfoContainer}
/>
</Switch>
</CampaignLayout>
</Route>
<Route path="/demo-guide/:path?" exact>
{/* ๋ ์ด์์ ํธ์ถ */}
<DemoGuideLayout>
{/* ๊ฐ์ ๋ ์ด์์์ ๊ณต์ ํ๋ ์๋ธ๋ฉ๋ด๋ค์ ํ๋์ Switch์์ Route๋จ */}
<Switch>
<Route exact path="/demo-guide/" component={DemoFormList} />
<Route
exact
path="/demo-guide/create"
component={DemoGuideFormContainer}
/>
<Route
exact
path="/demo-guide/:id"
component={DemoGuideInfoContainer}
/>
</Switch>
</DemoGuideLayout>
</Route>
<ToastNotiComponent />
</Switch>
</ConnectedRouter>
);
}
export default App;
๊ทธ๋ฆฌ๊ณ demo-guide ๊ฒฝ๋ก์์๋ <DemoGuideLayout></DemoGuideLayout> ๋ ์ด์์์ ์ฌ์ฉํ๋ค.
๋ ์ด์์ ์ธก์์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ด์ฉ ์ฝํ ์ธ ๋ฅผ ์์น์ํจ๋ค.
import React, { useState, useEffect, Component } from "react";
import { useSelector } from "react-redux";
import classNames from "classnames/bind";
// left menu
import SideMenu from "../components/SideMenu/SideMenu";
// header
import Header from "../components/Header/Header";
import styles from "../App.css";
const cx = classNames.bind(styles);
// ์ปดํฌ๋ํธ ์ ์
const CampaignLayout = ({children}) => {
const leftSize = useSelector((state) => state.menuStore.leftSize);
const oLeftSize = useSelector((state) => state.menuStore.oLeftSize);
let sideMenu = null;
// let sideMenu = null;
function getWindowDimensions() {
const { innerWidth: width, innerHeight: height } = window;
return {
width,
height,
};
}
const [windowDimensions, setWindowDimensions] = useState(
getWindowDimensions()
);
useEffect(() => {
function handleResize() {
setWindowDimensions(getWindowDimensions());
}
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
// render
return (
<>
<div className={cx("root")}>
<SideMenu />
<div
className={cx("container0")}
rel={windowDimensions.height}
style={{
transform: `translate3d(${
leftSize === oLeftSize ? "0" : `-${oLeftSize}px`
}, 0, 0)`,
width: `calc(100% - ${leftSize}px)`,
left: `${oLeftSize}px`,
height: `100vh`,
}}
>
<Header />
<div className={cx("contents")}>{children}</div>
</div>
</div>
</>
);
};
export default CampaignLayout;
๋ณต์กํด ๋ณด์ด๊ฒ ์ง๋ง ์ค์ํ ํต์ฌ์ ๋ค์๊ณผ ๊ฐ๋ค.
const CampaignLayout = ({children}) => {
// render
return (
<>
{children}
</>
);
};
export default CampaignLayout;
๋๊ธ