게임들에 접속하기 전 메인 페이지를 만들어보자. 카카오오븐이라는 목업 툴을 통해 대략적으로 구상해보았다.
해당 프로젝트는 총 8개 게임으로 이루어져 있고, 각각 지정된 경로에 따라 다른 view를 보여주어야 한다. 따라서, React Router를 사용하여 url에 따른 페이지 렌더링을 구현해보자.
✅ GameMatcher.tsx
const GameMatcher = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/gugudan" element={<GuGuDan />} />
<Route path="/word-relay" element={<WordRelay />} />
<Route path="/number-baseball" element={<NumberBaseball />} />
<Route path="/response-check" element={<ResponseCheck />} />
<Route path="/rock-scissors-paper" element={<RSP />} />
<Route path="/lotto-generator" element={<LottoGenerator />} />
<Route path="/mine-search" element={<MineSearch />} />
<Route path="/tic-tac-toe" element={<TicTacToe />} />
<Route path="/" element={<GameStart />} />
</Route>
</Routes>
);
};
✅ Header.tsx
const Header: React.FunctionComponent = () => {
const gameSet = [
['gugudan', '구구단'],
['word-relay', '끝말잇기'],
['number-baseball', '숫자야구'],
['response-check', '반응속도체크'],
['rock-scissors-paper', '가위바위보'],
['lotto-generator', '로또뽑기'],
['mine-search', '지뢰찾기'],
['tic-tac-toe', '틱텍토'],
];
return (
<div>
<div className="grid h-24 content-end justify-center gap-2 ">
<div className=" m-3 grid h-14 content-center rounded p-5 text-3xl font-black text-white transition delay-100 duration-300 ease-in-out text-shadow-h2 hover:-translate-y-1 ">
<Link to="/">PJH's Game World 🎮</Link>
</div>
</div>
<div className="mx-auto grid w-[95%] grid-cols-2 justify-center gap-3 sm:grid-cols-2 md:w-1/2 md:grid-cols-4 ">
{gameSet.map((url, i) => {
return (
<div
key={`${i + 1}번째 게임: ${url}`}
className="col-span-1 grid items-center justify-center rounded-xl bg-[#99a1e7] font-bold text-blue-900 duration-100 hover:-translate-y-1 hover:scale-100 hover:bg-indigo-500 hover:text-white"
>
<Link to={`/${url[0]}`}>
<button className="mx-3 min-w-max px-3 py-2">{url[1]} </button>
</Link>
</div>
);
})}
</div>
</div>
);
};
나는 각각의 게임 페이지 이동시에도 header, footer를 그대로 유지시키고 싶었다. 물론, header, footer를 재사용할 수 있게 컴포넌트로 제작해놓고 각각의 컴포넌트에서 사용하는 것도 방법이지만, 이번에는 Outlet을 사용해 보았다.
💡 Outlet
: Outlet은 상위의 컴포넌트를 레이아웃화 할 수 있어서<Outlet />
사용시{children}
을 사용하는 것과 같은 효과를 볼 수 있다. 따라서 중첩라우팅이 가능하다.
✅ GameMatcher.tsx
const GameMatcher = () => {
return (
<Routes>
<Route element={<Layout />}>
...
</Route>
</Routes>
);
};
✅ Layout.tsx
<Outlet/>
에는 다른 게임 컴포넌트들이 담겨있어 이제 페이지 전환시 header
, footer
는 고정된 레이아웃으로 설정된다.const Layout = () => {
return (
<B.Background>
<div id="stars"></div>
<div id="stars2"></div>
<div id="stars3"></div>
<div className="min-h-screen bg-[#292828]">
<header>
<Header />
</header>
<div className="mx-auto mt-3 h-auto min-h-[509px] w-[95%] rounded-xl border-4 border-[#003470] bg-[#d8e4f6] p-3 font-sans md:w-1/2 ">
<Outlet />
</div>
<footer>
<Footer />
</footer>
</div>
</B.Background>
);
};
메인 페이지의 배경화면은 우주를 표현하고 싶어서 오픈소스를 가져왔다.
그리고 styled components 사용하여 상위 컴포넌트인 Layout컴포넌트를 둘러싸주었다.
✅ Background.tsx
import styled from 'styled-components';
export const Background = styled.div`
#stars {
width: 1px;
height: 1px;
background: transparent;
...
}
✅ Layout.tsx
const Layout = () => {
return (
<B.Background>
<Header />
...
<Outlet />
...
<Footer />
</B.Background>
);
};
오픈소스: 우주 배경을 표현하는 반짝 애니메이션 이펙트