NodeJS Handlebars 레이아웃 분리하기: Navigation과 Bottom
웹디자인을 적용할 때,
모든 페이지마다 네비게이션과 bottom의 카피라이트 영역의 코드를 작성하는 것은,
매우 비효율적인 방법입니다.
그래서, 이러한 레이아웃 영역을 분리해서 코딩을 하게 되는데요.
오늘은 NodeJS의 뷰템플릿엔진인 Handlebars를 사용할 때,
Navigation과 Bottom영역등의 레이아웃을 분리하는 방법에 대해서 정리해 보겠습니다.
참고로 Handlebars에 대한 기본적인 사항은 아래 글을 참조해 주세요.
>> Handlebars 사용방법의 모든 것: Nodejs 뷰 템플릿 엔진
1. 파일 구성 및 nodejs 설정
1-1. 파일 구성
직접 코딩 작업을 하기 전에,
각각의 레이아웃들을 어떻게 폴더로 나누어서 저장하는지에 대해서 먼저 보겠습니다.
handlebars의 파일들은 보통 '/views'폴더에 저장하는데요.
이 폴더에 통으로 들어가 있던 파일들을 아래와 같은 구조로 나누어 담는 것 입니다.
- layouts/main.hbs: 레이아웃의 뼈대를 그려놓는 곳입니다.
- partials/navigation.hbs, bottom.hbs: 네비게이션과 bottom부분의 레이아웃을 정의해 놓습니다.
- home.hbs - 페이지의 body부분, 즉 컨첸츠 부분의 뷰를 정의해 놓는 곳 입니다.
아래와 같이 파일을 생성해 놓으면 되겠지요.
1-2. NodeJS설정
이제 저렇게 구성된 파일들을 NodeJS에도 알려줘야 합니다.
원래는 아래와 같이 심플하게 구성되어 있었는데요.
const app = express();
const { engine } = require('express-handlebars');
app.engine('hbs', engine({ extname: '.hbs', defaultLayout: false }));
아래와 같이 app.engine()부분을 채워줘야 하는데요.
크게 3가지를 변경해 주어야 합니다.
- defaultLayout: 뼈대가 되는 의 파일명
- layoutsDir: 템플릿 레이아웃들이 들어가 있는 디렉토리를 지정
- partialsDir: 공통적으로 사용되는 hbs뷰파일들의 디렉토리를 지정
정의해 주면 됩니다.
const app = express();
const { engine } = require('express-handlebars');
app.engine('hbs', engine({
defaultLayout: 'main',
extname: '.hbs',
layoutsDir: path.join(__dirname, 'views/layouts'),
partialsDir: path.join(__dirname, 'views/partials')
}));
라우팅에서는 위에서 컨텐츠를 제공하는 파일로 정한,
home.hbs를 정의해 주면 됩니다.
// 정적 파일 제공
app.use(express.static(path.join(__dirname, 'public')));
// 라우팅
app.get('/', (req, res) => {
res.render('home', { pageTitle: '홈' });
});
이제 NodeJS에게는 모든 것을 알려주었으니,
아래의 레이아웃 파일들을 작성만 해 주면 됩니다.
- main.hbs(뼈대)
- navigation.hbs, bottom.hbs(공통적으로 사용한 네비게이션과 bottom)
- home.hbs(메인 페이지 컨텐츠)
2. Main.hbs (기본 레이아웃 생성)
먼저 main.hbs 파일을 아래와 같이 생성해서,
뼈대를 세워둡니다.
여기서 아래의 2 부분은,
각각 navigation과 bottom 부분 템플릿을 삽입하는 Handlebars 부분 템플릿 구문입니다.
- {{> navigation}}
- {{> bottom}}
'{{>'로 시작하는 것이 조금 특이합니다.
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{pageTitle}}</title>
<link rel="stylesheet" href="/styles/main.css">
</head>
<body>
{{> navigation}}
<main>
{{{body}}}
</main>
{{> bottom}}
<script src="/js/main.js"></script>
</body>
</html>
3. navigation.hbs, bottom.hbs(공통적으로 사용한 네비게이션과 bottom)
navigation.hbs는 아래와 같이 간단하게 구성해 줍니다.
<nav>
<ul>
<li><a href="/">홈</a></li>
<li><a href="/about">소개</a></li>
<li><a href="/contact">연락처</a></li>
</ul>
</nav>
아래는 bottom영역이고요.
일반적으로 카피라이트와 개인정보처리방침 및 이용약관 등이 들어갑니다.
<footer>
<p>2024 내 앱. All rights reserved.</p>
<nav>
<ul>
<li><a href="/privacy">개인정보처리방침</a></li>
<li><a href="/terms">이용약관</a></li>
</ul>
</nav>
</footer>
4. home.hbs (컨텐츠)
이제 main.hbs에서 정의해 놓은,
body부분에 들어갈 컨텐츠를 정의해 놓아주면 됩니다.
아래에서는 간단하게 아래와 같이 구성하였습니다.
<h1>환영합니다!</h1>
<p>이것은 테스트 컨텐츠입니다.</p>
5. 특정 페이지에서만 Navigation 숨기기
특정 페이지에서는 Navigation을 보여주지 않아야 하는데요.
이럴 때는 Handlebars의 조건문을 활용해 주면 됩니다.
handlebars의 if문은 아래와 같이 동작합니다.
true의 경우에만 보여지지 않도록 하는 것이므로,
if 문을 사용할수도 있지만,
unless를 쓰면 코드를 더 간결하게 쓸 수 있습니다.
라우팅에서 hideNavigation을 true로 값을 넘겨줄 경우에만,
navigation을 숨겨주게 됩니다.
<!DOCTYPE html>
<head>
...
</head>
<body>
{{#unless hideNavigation}}
{{> navigation}}
{{/unless}}
<main> {{{body}}} </main>
{{> bottom}}
</body>
</html>
login페이지에서는 아래와 같이 라우팅을 해서,
navigation이 렌더링 되지 않도록 하였습니다.
async function handleLogin(req, res) {
res.render('login', { hideNavigation: true });
}
이제 모든 작업이 끝났습니다.
필요에 따라서, 각각의 페이지들을 수정해서 정의하여 사용해 주면 됩니다.