상세 컨텐츠

본문 제목

[Node.js] EJS로 동적 웹 페이지 만들기: Express와 함께하는 실습

메타버스 백엔드 웹반

by thisnorm 2025. 1. 13. 22:58

본문

 

1. View Template Engine이란?

대표사진 삭제

사진 설명을 입력하세요.

웹 페이지를 구성하는 주된 마크업 언어(Markup Language)는 HTML이며, 이는 정적인 언어. 따라서, Javascript의 반복문을 사용하여 간단하게 처리할 수 있는 동적 연산을 HTML로 표현하려면 직접 코드를 작성해야 하는 불편함이 있다.

  • 템플릿 엔진(Template Engine)은 위에서 언급한 불편한 상황을 해소시키기 위한 도구
  • 템플릿 프로세서를 이용하여 웹 페이지를 동적으로 구현할 수 있는 시스템
  • Node.js의 대표적인 템플릿 엔진으로는 Pug(Jade), EJS, Handlebars 등이 있음
  • 템플릿 엔진은 웹 템플릿들과 웹 컨텐츠 정보를 처리하기 우해 설계된 소프트웨어
  • 템플릿 엔진은 view code(html)와 data logic code(db connection)을 분리해주는 기능을 한다.
  • 필요성
    • 대부분의 템플릿 엔진은 HTML에 비해 간단한 문법을 사용함으로써 코드량을 줄일 수 ㅣㅇㅆ음
    • 데이터만 바뀌는 경우가 굉장히 많으므로 재사용성을 높일 수 있음
    • 유지 보수에 용이
 

2. Express와 뷰 템플릿 엔진

Express에서의 뷰 템플릿 엔진 시작 공식문서에서는 기본적으로 Pug라는 뷰엔진을 사용하는 실습 코드를 제공하고 있다. 하지만 Pug라는 뷰 엔진에 대해서 처음 들어보았기 때문에 정보가 필요하다.

Java를 배우면서도 JSP, Thymeleaf, Mustache 등 여러 뷰 템플릿 엔진이 있었고 특히 문법들에 차이가 있을 수 있어서 뷰 템플릿 엔진도 그 부분을 먼저 살펴볼 필요가 있었다.

 

3. Pug VS EJS VS Handlebars

  • Pug는 기존의 Jade라는 이름에서 개명된 이름이며, 꾸준히 인기를 얻고 있는 Node.js 기반의 템플릿 엔진
  • EJS는 HTML 친화적인 템플릿으로, Pug의 고유 문법에 익숙하지 않은 개발자들이 주로 사용하는 템플릿 엔진
  • Handlebars는 Mustache를 기반으로 구현한 템플릿, 동적 컨텐츠의 경우 EJS 와 비슷하게 이중 중괄호 플레이스 홀더를 사용

위 두 설명을 통해서는 어떤것을 사용해야 할 지 모르겠지만 실제 코드로 보면 선택에 도움이 된다.

 

Pug

// index.html
doctype html
html
	include header.pug
	include body.pug
 
<!-- header.pug -->
head
	title=title
 
<!-- body.pug -->
body
	p Hello World
 

Pug의 장점

  • Pug는 독자적인 문법을 가지고 있음
  • Pug는 EJS보다 깔끔한 구문을 가지고 직관적인 느낌을 주는 장점
  • <>와 같은 기호들을 제거하여 가독성을 극대화 시킨 부분이 특징

 

Pug의 단점

  • 새로운 문법에 익숙해지는데 시간은 조금 걸리는 단점(러닝커브) → 치명적인 부분으로 작용
  • 새로운 문법이라는 것은 호환성의 불리함이 있다는것(기존 HTML 소스를 모두 재가공해야함)
 

EJS

<!-- index.ejs -->
<!DOCTYPE html>
<html>
  <%- include('header.ejs') %>
  <%- include('body.ejs') %>
</html>
 
<!-- header.ejs -->
<head>
	<title><%= title %></title>
</head>
 
<!-- body.ejs -->
<body>
  <p>Hello World</p>
</body>
 

EJS의 장점

  • Javascript 로직을 그대로 사용 할 수 있음
  • 렌더링 전 후의 코드 차이가 없음
  • 에러를 확인하고 처리하는데 수월
  • HTML 과 Javascript를 알고 있다는 전제 하에, 러닝 커브가 상당히 낮다.

 

EJS의 단점

  • block 을 사용하기 위해서는 별도의 third-party-library 를 추가하여 사용해야 함
  • <% %> <%= %> 와 같은 문법이 HTML에 들어가 있어 코드를 읽기가 쉽지 않음
  • javascript를 그대로 사용할 수 있는 장점
 

Handlebars

<!-- index.hbs -->
<!DOCTYPE html>
<html>
  {{> header}}
  {{> body}}
</html>
 
<!-- header.hbs -->
<head>
  <title>{{title}}</title>
</head>
 
<!-- body.hbs -->
<body>
  <p>Hello World</p>
</body>
 

 

Handlebars의 장점

  • Logic 과 Markup 을 깔끔하게 분리 할 수 있음. {{ }} 안에 있는 것이 로직
  • 컴파일 단계에서 코드가 해석이 되므로, 클라이언트에서 따로 코드를 해석하는 단계가 필요없다. 그러므로 로딩이 빠름
  • HTML 오픈 소스를 복사/붙여넣기 가 가능하다. (pug는 불가능, pug 형식에 맞게 다시 작성해야 함)

 

Handlebars의 단점

  • auto-complete, syntax highlighting 등 과 같은 기능을 에디터에서 제공해 주지 않을 수 있음
 

무엇을 사용해야 하는가?

물론 팀과 프로젝트의 룰에 따라가는 것이 가장 우선순위이다.

하지만 선택의 기로에 있는 학습자 입장에서는 실제 사용량을 확인하는 것이 하나의 지표라고 생각된다. 이것이 트렌드의 흐름을 파악 할 수 있기 때문이다. 그리고 생각보다 러닝커브로 소모되는 시간도 무시 할 수 없다.

pug는 진입 난이도 등 사용량이 현저히 낮아보인다. 오히려 ejs, handlebars 생태계 점유율이 높은 것을 볼 수 있다. 수많은 소스를 그나마 원본을 최대한 활용 할 수 있는 재사용성을 높은 두 엔진의 활용도가 높은 것으로 보여진다.

 

4. EJS 사용하기

실습에서는 EJS를 사용해보고자 한다.

 

  • 우선 추가적으로 Git사용 연습을 계속하기 위해서 Frontend, Backend, master로 3개의 브랜치를 사용해보자.
  • 그리고 현재는 Frontend와 관련된 부분이므로 Frontend 브랜치에 체크아웃하여 진행

NPM을 통한 EJS 패키지 설치

npm install ejs
 
 

EJS 객체 생성 및 Express에 세팅

EJS 뷰 템플릿 엔진은 Express 위에서 실행되야 하기 때문에 Express에 세팅해줘야 한다.

아래처럼 EJS 패키지를 ejs변수로 선언, 할당하고 뷰 엔진으로 설정한다.

루트 경로에 /views 폴더를 뷰페이지 관련 폴더로 설정한다.

이제 해당 폴더를 기준으로 아래 라우트함수 내 res.render()함수를 통해서 반환되는 문자열+.ejs 파일의 소스를 뷰 템플릿 엔진을 거쳐 반환하게 된다.

const express = require('express')
const ejs = require('ejs')
const app = express()
const port = 3000

app.set('view engine', 'ejs');
app.set('views', './views')

//Root URL('/') 경로에 대한 Get 요청
app.get('/', function (req, res) {
  res.render('index');
})

//특정 URL 경로에 대한 Get 요청 - 1
app.get('/boards', function (req, res) {
  res.render('boards');
})

//특정 URL 경로에 대한 Get 요청 - 2
app.get('/users', function (req, res) {
  res.render('users');
})
 
 

index.ejs 뷰 페이지 작성

res.render('index'); 를 통해서 index.ejs라는 파일이 반환될 것이다.

따라서 /views/index.ejs 와 같은 경로로 생성되어야 한다. boards.ejs, users.ejs 도 기본 페이지를 작성해둔다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Homepage</title>
</head>
<body>
    
    <h1>홈페이지에 오신것을 환영합니다.</h1>

</body>
</html>
 
 

맵핑된 URL마다 상태 확인

5. HTML 표준 레이아웃

여러 홈페이지들의 외관을 살펴보면 비슷한 레이아웃을 보여준다. 이런 모습들이 웹 표준을 따르기 위함이라고 볼 수 있다.

이를 통해 사용자들에게 익숙한 경험을 제공하면서 접근성을 높일 수 있는 방법론에서 시작하기도 한다. 개발자가 UX/UI 관점의 개발을 고려해야 하는 이유이다. 또한 재활용성, 유지보수성은 표준화, 규칙 들에서 관리되기 쉽다.

 

 

 

웹 표준

브라우저 종류 및 버전에 따른 기능 차이에 대하여 호환이 가능하도록 제시된 표준으로, 다른 기종 혹은 플랫폼에 따라 달리 구현되는 기술을 동일하게 구현함과 동시에 어느 한쪽에 최적화되어 치우치지 않도록 공통요소를 사용하여 웹 페이지를 제작하는 기법을 의미한다.

물론 프로젝트마다 회사마다 그 스타일의 차이가 있지만 공통적으로 웹 표준을 지키려고 노력하고 있는것으로 보여진다. 이를 통해 소스를 살펴보더라도 개발자 입장에서도 해당 부분들의 코드 파악이 용이하다.

 
 
 
 

웹 표준의 장점

  • 수정 및 운영관리 용이

콘텐츠의 올바른 구조화와 CSS로 시각표현을 통일하여 HTML, CSS를 따로 분리하여 제작하면 CSS 수정 만으로도 손쉽게 디자인의 수정이 가능하고 페이지 제작 시 부담 및 시간, 비용의 감소 효과가 있으며 관리가 용이하다

.

  • 접근성 향상

웹 표준을 이용하여 만든 문서는 다양한 브라우징 환경에서 접근이 가능하고 일반인뿐만 아니라 장애인, 노인 등의 다양한 사용자가 어려움 없이 접근 가능한 페이지를 만들 수 있다.

 

  • 검색엔진 최적화(SEO)

meta 요소를 이용하여 정확한 문서 정보 제공할 수 있고 의미에 맞는 마크업은 검색 시 효율성을 높일 수 있다.

 

  • File Size 축소, 서버 저장 공간 절약

구조 (HTML)과 표현 (CSS)를 분리해서 개발하면 해당 페이지를 불러오게 될 경우, 브라우저의 캐시에 CSS가 저장되고 메모리에 상주하게 되어 동일한 CSS는 다시 불러오지 않기 때문에 HTML 코드가 가벼워지게 된다.

 

  • 효율적인 마크업

CSS와 HTML 문서를 분리하여 제작할 경우, 불필요한 마크업이 최소화 되어 페이지 로딩 속도를 향상 시킨다.

 

  • 호환성 가능

기존 IE브라우저에서만 작동이 가능했던 요소들이 웹 표준을 준수함으로써 다양한 브라우저(크롬, 파이어폭스, 사파리, 오페라 등)에서도 작동

 

 

 

웹 표준 구조 예시 코드

<!-- HTML5 기본 문법구조 -->
<!DOCTYPE html>
<html lang="ko">

<head>
     <meta charset="UTF-8 /">
     <title>HTML5 기본 문법구조</title>
</head>

<body>
     <header>
         <hgroup>
             <h1>머리말 제목</h1>
         </hgroup>
     </header>

     <nav>
         <ul>
             <li>메뉴1</li>
             <li>메뉴2</li>
						 ...
         </ul>
     </nav>

     <section>
         <article>
             <header>
                 <h1>섹션 제목</h1>
             </header>
             <section>
                <h2>섹션 소제목</h2>
             </section>
          </article>
       </section>

       <aside>
          콘텐츠를 제외한 배너
	     </aside>

       <footer>
           <small>Copyright ⓒ.. , All Rights Reserved.</small>
       </footer>
   </body>
</html>
 

시맨틱 태그

Semantic은 '의미의', '의미론적인'이라는 뜻을 가진 형용사이다. 따라서 시맨틱 태그란 의미가 있는 태그를 말한다. HTML5에서 처음 등장하는 tag들이다.

기존 대다수의 프로젝트는 div에 class 값을 붙여서 스타일링하는 식으로 작업해왔다. 하지만 div나 span과 같이 의미가 없는 태그는 태그 이름만 보고는 어떤 내용인지 전혀 유추할 수가 없는 반면, form, table, article 등 의미가 있는 태그는 내용을 명확하게 정의하는데 발전하게 되었다.

물론 아직도 div에 class, id를 지정해서 하는 사례가 많지만 시맨틱 태그를 이해하고 리팩토링하는 과정을 통해 보다 명확한 코드를 나타내는 것도 발전해나가는 긍정적인 과정이라 생각한다.

시맨틱 태그 사용의 장점

  • 검색엔진최적화 (SEO : Search engine optimization)
  • 검색엔진이 알맞은 검색결과를 내기 위해 웹사이트를 크롤링할 때 웹사이트의 내부에 담긴 정보를 토대로 사이트를 분석한다. 그럴 때, 의미있는 태그를 사용하면 좀 더 정확한 구조로 분석할 수 있도록 도울 수 있다.
  • 웹 접근성
  • 일반적인 브라우저에서는 차이가 없지만 스크린리더(시각장애인을 위한 웹 서핑 프로그램)과 같은 환경에서는 웹 접근성과 사용성을 향상시켜준다.
  • 유지보수
  • 여러 사람과 함께 작업을 할 때, 굳이 클래스를 지정하지 않아도 쉽게 어느 부분이 헤더 영역이고, 본문 영역인지 쉽게 알 수 있다. 그래서 유지보수를 하기도 쉬워진다.

 


5 공통 레이아웃 구조화의 목적

레이아웃을 모듈화시키고 공통 부분으로 참조하는 것은 생각보다 개발단계의 협업, 효율을 위함과 동시에 사용자 경험에서도 신뢰도 높은 서비스를 구축하는데 중요하다.

개발자 입장에서는 우선 가독성 높은 코드, 유지보수적인 측면을 생각해서도 초기 기획단계에서부터 이 부분을 고려하고 개발을 착수해야 추후 복잡해지고 두번일하는 상황이 발생하지 않을것 같다.

프로젝트 규모가 커질수록 여러 페이지들을 생성하는데, 그 페이지마다 소스가 중복되면 유지보수성이 떨어지고(10개의 페이지면 10번 동일한 부분을 수정해야 함) 특히 개발자의 실수가 발생할 수 있다.(10개중 1개 페이지를 수정못한것을 망각)

또한 업무의 분리가 어렵다. 협업을 위해서는 업무의 분담을 해야하는데, 공통 부분을 만들면 한명이 해결 할 수 있는 부분을 2명이서 5개, 5개로 나누는 등 비효율적인 작업이 진행 될 수 있다.

공통 레이아웃 및 partials을 사용하여 코드를 재사용하고 페이지를 구조화할 수 있다. 이 작업을 하는 이유는 공통 레이아웃을 통해 중복 코드를 제거하고 특히 백엔드 개발자 입장에서는 비교적 익숙하지 않은 프론트엔드 소스에 집중력이 분산되는데 원하는 페이지에 필요한 데이터, 로직에 집중하기 위한 목적이 크다.

 

EJS 문법 <%-include>

우선 EJS 뷰 엔진이 제공하는 기본적인 참조 방법이다. 레이아웃 구조화 전 index.ejs 코드는 다음과 같다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Homepage</title>
</head>
<body>
    <h1> 홈페이지에 오신 것을 환영합니다. </h1>
    <header style="border-bottom: 1px solid lightgray; margin-bottom: 10px; padding-bottom: 10px;">
        <nav>
            <a href="/">홈</a>
            <a href="/profile">프로필</a>
            <a href="/visit">찾아오시는길</a>
            <a href="/blog">블로그</a>
            <a href="/users">회원</a>
            <a href="/contact">문의하기</a>
        </nav>
    </header>

    <h2>메인페이지 입니다.</h2>

		<footer>
		    <hr>
		    copyright Fred all right reserved.
		</footer>
</body>
</html>
 

이후 각 페이지를 생성 할 때마다 body 태그 내부는 변경이 필요하지만 공통되는 html, head, body, footer 등 태그는 중복되어 생성 될 것이다. 따라서 이 부분들을 별도 파일로 분리하고 참조시키는 방

법이다. 프로젝트 내 계층 구조를 구분하기 위해서 views/layouts 폴더를 생성 한 후

 

header.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Homepage</title>
</head>
<body>
    <h1> 홈페이지에 오신 것을 환영합니다. </h1>
    
    <header style="border-bottom: 1px solid lightgray; margin-bottom: 10px; padding-bottom: 10px;">
        <nav>
            <a href="/">홈</a>
            <a href="/profile">프로필</a>
            <a href="/visit">찾아오시는길</a>
            <a href="/blog">블로그</a>
            <a href="/users">회원</a>
            <a href="/contact">문의하기</a>
        </nav>
    </header>
 

footer.ejs

		<footer>
		    <hr>
		    copyright Fred all right reserved.
		</footer>
</body>
</html>
 

index.ejs

<%-include('layouts/header.ejs')%>

    <h2>메인페이지 입니다.</h2>

<%-include('layouts/footer.ejs')%>
 

위 처럼 header, footer 파일을 공통 레이아웃으로 분리시켜 별도로 관리하고 각 페이지를 생성 할 때 마다 위, 아래로 참조시키면서 본문의 내용만 작성되도록 분리했다.

이후로 추가적인 페이지를 생성하더라도 동일하게 위아래 참조만 시키면 되기 연속성을 제공 할 수 있고 참조된 파일들의 수정 사항에도 공통적으로 적용 시킬 수 있어 효율적인 작업이 가능하다.

<%- %> 부분이 밑줄이 쳐지게 되면 EJS Language Support 확장프로그램을 설치하면 해결된다.

 

이미지 첨부하기

app.use(express.static(__dirname+'/public'))
 

통하여 이미지의 절대경로를 설정해준다.

<%-include('layouts/header.ejs')%>

    <h2>메인페이지 입니다.</h2>
    <img src="/5.png">

<%-include('layouts/footer.ejs')%>
 

로 코드를 설정하고 서버를 다시 키면 UI에 추가된다.

 

관련글 더보기