Multi Developer SuHo

Node JS EJS(Embedded JavaScript) 템플릿 엔진에 대하여... 본문

Node.js 기록

Node JS EJS(Embedded JavaScript) 템플릿 엔진에 대하여...

Dreaming Developer Student 2025. 2. 27. 10:17
SMALL

안녕하세요~~ Node JS로 진입하면서 EJS 템플릿 엔진을 사용해보고  EJS 템플릿 엔진이 무엇인지 알아보겠습니다.

📑목차
1. EJS 템플릿 엔진
1-1. EJS 템플릿 엔진이란?
1-2. EJS 템플릿 엔진 특징
1-3. EJS 템플릿 엔진 문법
1-4. EJS 템플릿 엔진 장단점
1-5. EJS 템플릿 엔진을 대체하는 프레임워크
1-6. EJS 템플릿 엔진을 이용한 게시판 CRUD 

서론

먼저 이 포스팅을 읽기 전에 EJS 템플릿 엔진이 무엇이고, EJS 템플릿 엔진의 특징, 문법, 장단점을 알아볼 필요가 있겠습니다. 최근에는 다양한 프레임워크와 라이브러리가 등장하여 EJS 템플릿 엔진의 가동성이 낮아지고 있습니다. 간략하게 EJS 템플릿 엔진에 대한 본문으로 넘어가시죠

 

 


본론

 

1. EJS 템플릿 엔진

 

1-1. 템플릿 엔진이란?

서버에서 html 요소를 동적으로 만들어서 브라우저에게 응답을 보내서 페이지를 렌더링할 수 있도록  도와주는 도구

 

1-2. 템플릿 엔진 특징

> EJS는 html에서 자바스크립트를 제어해서 동적인 페이지를 만드는게 목적
> 서버측에서 응답해서 렌더링 해준다 -> 서버 사이드 렌더링
> 서버사이드 렌더링을 사용할 때 사용
> 동적인 HTML의 필요성을 느끼게 되고, 프론트에서 동적인 웹페이지를 처리하다보니 내용이 무거워질수록 렌더링 속도가 저하됬고, 사용자의 이탈이 생길 수 있다.
> 서버사이드 렌더링(`SSR`) 클라이언트 사이드(`CSR`)가 아닌 서버에서 검색을 하는 경우에도 페이지의 완성을 서버측에서 동적인 페이지 완성을 해서 클라이언트에게 응답을 해준다.
> 서버측 자원을 사용하고 클라이언트의 자원을 줄일 수 있다.
> nodejs가 많이 알려지면서 express같은 라이브러리의 사용이 많아졌고, express가 버전이 많이 업데이트되면서 ejs의 내용도 지원하기 시작했다.​

 

1-3. EJS 템플릿 엔진 문법

 

대표적으로 <% 코드 내용 %>  형식으로 작성한다.

 

<%=  변수  %> 이렇게 작성하면 EJS 파일에서 변수처럼 사용하여 서버측으로 응답을 보내서 처리할 수 있다.

ejs 모듈 설치

$ npm i ejs



ejs 템플릿 엔진 파일 생성

 

 

## ejs 문법
```html
<!-- 자바스크립트 문법을 html에 작성-->

<html>
    <header></header>
    <body>
        <div> <%= title %> </div>
    </body>
</html>

<!-- html 파일의 내용을 읽어서 ejs구문이 있는 부분을 해석 코드 내용을 호출한다.  -->
<!-- <%= title %>  = 이 들어간 <%= %> 변수를 html의 영역에 할당 title이란 변수를 찾아서 값을 전달. -->

 

 

1-4. EJS 템플릿 엔진 장단점

 

장점

1.  렌더링 속도가 빠르다 

2.  node.js 랑 호환성이 좋다. 

3.  express와 쉽게 연동이 가능하다.

 

단점

1. 로직이 길어질수록 코드의 가독성이 떨어진다.

2.  SSR 방식으로 접근할 시 서버 비용 부담이 많이 든다

3. 내장 기능 부족

 

1-5. EJS 템플릿 엔진을 대체하는 프레임워크 

 

1. Next.js

React 기반의 웹 풀스택 애플리케이션을 구축하기 위한 SSR 방식 프레임워크

 

 

 

2. Nest.js

백엔드 서버를 구축하기 위한  node.js 기반 프레임워크

 

 

3. Nust.js

웹 개발 환경 구축을 도와주는 Vue.js 기반의 프레임워크 

 

이렇게 EJS 템플릿 엔진을 보완하기 위해 다양한 프레임워크의 등장으로  EJS 템플릿의 사용률이 줄어들었습니다. 

 

1-5. EJS 템플릿 엔진을 이용한 게시판 CRUD 

 

index.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <% for(let i = 0; i < count; i++) { %>
        메인 페이지 <%= name %>
    <% } %>

    <% if(count === 4) { %> 
        count가 5야
    <% } %>
    
    <ul>
        <% if(board.length !== 0) { %>
            출력 
            <% for(let i = 0; i < board.length; i++) { %>
                <a href="/detail?index=<%= board[i].index -1 %>">여기도 눌러봐요요</a>
                <li style="display: flex;" onclick="detailView('<%= board[i].index %>')">
                    <div><%= board[i].index %></div>
                    <div><%= board[i].title %></div>
                    <div><%= board[i].content %></div>
                </li>
            <% } %>
        <% } %>
    </ul>

    <form action="/board" method="post">
        <label for="">제목</label>
        <input type="text" name="title">
        <label for="">내용</label>
        <textarea name="content" id=""></textarea>
        <button>글 추가</button>
    </form>
</body>
<script>
    const detailView = (index) => {
        location.href = `/detail?index=${index - 1}`
    }
</script>
</html>



detail.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>글 번호 : <%= index %></div>
    <div>제목 : <%= title %></div>
    <div>내용 : <%= content %></div>
    <button class="update_btn">글 수정</button>
    <button class="delete_btn">글 삭제</button>
</body>
<script>
     document.querySelector(".update_btn").onclick = (index) => {
        location.href = `/update?index=<%= index -1%>`; 
    };
     document.querySelector(".delete_btn").onclick = () => {
        location.href = `/delete?index=<%= index -1%>`;
     }
</script>
</html>


update.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>수정 페이지</title>
</head>
<body>
    <form action="/update?index=" method="post">
        <input type="hidden" name="index" value="<%= index %>">
        
        <label for="title">제목</label>
        <input type="text" name="title" <%= title %>>

        <label for="content">내용</label>
        <textarea id="content" name="content" <%= content %>></textarea>

        <button type="submit"  class="save_btn">저장</button>
        <button>취소</button>
    </form>
</body>
<script>
   document.querySelector(".save_btn").onclick = (index) => {
    location.href = `/?index=${index}`; 
};
</script>
</html>







server.js 

const express = require('express');

// 서버 객체 생성
// 서버 상태가 들어있는 객체
const app = express();

// 임시 데이터베이스
// 서버에 데이터가 저장된다
// 서버측에서 응답한 페이지를 보여줘야 한다.
// 서버를 끄면 사용하던 데이터, 변수들이 제거
const data = []; 

app.set("view engine", "ejs");
// app.set("views", path.join(__dirname, "page"));
// console.log(app);

// body의 파싱 내용을 추가, 미들웨어로 추가
// use 메서드는 상관없이 미들웨어 호출
// 가장 위에서 호출을 시키기 위해 위에 배치
// GET, POST 상관없이 요청 경로만 확인
// 경로를 전달하지 않으면 모든 경로

// url의 문자열 형태를 객체로 파싱하는 기능을 하는 함수를 반환
// 깊은 객체란  {name : {age : {} } }
// 문자열에서 깊은 객체도 포함해서 파싱을 할것인지? 아닌지?
// extended : false -> 기본적으로 사용 안함, 사용해야될 경우는 true로 주면 된다.

// app.use((req, res, next) => {
//     // body 라는 키를 추가하면서 next를 전달
//     req.body = "문자열을 파싱해서 객체의 내용을 추가"
//     next();
// })

app.use(express.urlencoded({extended : false}))

// 특정 미들웨어에만 추가
app.use("/mypage", (req, res, next) => {
    req.cookies
    // 로그인이 되어있으면 next 로 넘긴다
    if(req.cookies) {
        next();
    } else {
        res.redirect('/login');
    }
})

app.use("/mypage", (req, res) => {
    res.render('mypage');
   
})



// GET /HTTP/1.1
app.get('/', (req, res) => {
    // render(파일의 이름, 전달하는 데이터 객체)
    // 기본으로 설정되어 있는 경로
    // views의 key의 value인 경로에 접근해서 파일을 찾는다.
    // 'C:\\Users\\akak7\\OneDrive - 인덕대학교\\바탕 화면\\NodeFile\\20250227\\views
    // app.set("view engine", "ejs"); -> 엔진을 사용하게 되면
    // 확장자를 사용하는 엔진의 파일 확장자명을 찾는다.
    // 페이지를 완성할때 참조할 수 있는  값을 전달
    // 서버에서 페이지를 완성할때 필요한 값, 즉 페이지를 완성시킨다 서버에서.

/*
        <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        soon
        메인 페이지
    </body>
    </html>
*/

    res.render("index", { name : "Suho", count : 5, board : data});
})

app.get('/detail', (req, res) => {
    console.log(req.query.index); // 인덱스 0번 접근
    const _data = data[req.query.index];
    // {index : 1 , title : 121341, content: 123123}
    res.render("detail", _data);
})


app.get('/update', (req, res) => {
    console.log(req.query.index);
    const index = req.query.index;
    res.render("update", { index : index, title : data[index].title , content : data[index].content })
})



// 업데이트 페이지 요청 부분
// 업데이트 부분에서는 name으로 전달하여 수정
app.post('/update', (req, res) => {
    console.log("전체 req.body:", req.body);
    
    const index = req.body.index;
    console.log("index:", index);
    console.log("title:", req.body.title);
    console.log("content:", req.body.content);

    const newTitle = req.body.title;
    const newContent = req.body.content;

    console.log("data 배열 길이:", data.length);

    if (data[index]) {
        data[index].title = newTitle;
        data[index].content = newContent;
    }

    // 수정된 콘솔 확인
    console.log("수정된 데이터:", data[index]);
    
    res.redirect('/');
})
app.get('/delete', (req, res) => {
    const  index = req.query.index;
    console.log("선택된 query 인덱스: ",index);
    
    // data 배열에서 splice 메서드를 사용하여 인덱스에서 제거
    data.splice(index, 1);
    // 삭제된 데이터 확인
    console.log("삭제후 데이터",data);

    res.redirect('/');
})


// POST / HTTP/1.1
// POST body의 내용을 요청 메세지에서 사용할 수 있다.
// POST는 안전하게 값을 전달해서 값을 요청 메세지로 전달해서
// 서버로직에서 변환해서 사용
// POSTMAN : API를 테스트할때 사용, 프론트에서 구현이 되지않은 상태에서
// 백엔드 개발자가 요청을 보내서 데이터를 확인할 때 test로 사용
// redirect() === 300 번대의 상태코드를 반환
// 브라우저에서 서버로 요청을 보내고, 받은 응답은? 야 다시 여기로 재요청 보내
// 브라우저는 두번을 요청하게 된다.
// /board로 POST 요청을 보내고, 받은 응답은 redirect로 "/" 경로로 get 요청을 보내
// 브라우저는 "/" 경로로 get 요청을 보낸다.

app.post("/board", (req, res) => {
    // Create
    // body 내용을 가져와서 데이터를 추가해주고 싶어
    // body 내용을 서버측에서 받아야한다
    // req 요청 메세지를 파싱할 때 body의 내용을  추가
    console.log(req.body.title);
    console.log(req.body.content);
    // 임시 데이터 베이스에 기록
    data.push({index : data.length + 1 , title : req.body.title, content : req.body.content})
    res.redirect("/") // 재요청 보낸다.
})



app.listen(3000, () => {
    console.log("서버 작동중");
})

 

결론

EJS 템플릿을 이용하면 서버 측에서 동적으로 HTML을 생성할 수 있어  웹 페이지 구성이 가능합니다. JavaScript 코드와 함께 데이터를 효과적으로 렌더링할 수 있으며, 코드의 재사용성과 유지보수성이 향상됩니다. 따라서 EJS는 간단하면서도 변수로 사용할 수 있다는 점이 있고 CRUD 기능도 구현할 수 있습니다. 

 

LIST