레이아웃을 이용해 블로그 첫 화면을 만들어보자.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Blog</title>
<meta name="description" content="My first application using Node.js, Express and MongoDB">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div class="container">
<!-- 헤더 : 로고, 상단 메뉴, 로그인 -->
<header class="header">
<!-- 로고 -->
<a href="/" class="header-logo">오후의 블로그</a>
<!-- 상단 메뉴 -->
<nav class="header-nav">
<ul>
<li>
<a href="#">Home</a>
</li>
<li>
<a href="#">About</a>
</li>
</ul>
</nav>
<!-- 관리자 로그인 -->
<div class="header-button">
<a href="#">관리자 로그인</a>
</div>
</header>
<!-- 메인 : 실제 내용이 들어갈 부분 -->
<main class="main">
// 실제 바뀌는 내용이 들어가는 부분
</main>
</div>
</body>
</html>
해당 html을 활용할 것이다. 복사해서 레이아웃으로 만들자.
전에 만든 main.ejs의 내용을 지우고 붙여넣자.
정적인 파일은 public 폴더에 저장된다. 강의에서 주어진 css, img 폴더를 public 폴더에 복붙하자.
파일을 옮겼으니 app.js에서 그 정적인 파일들이 어디 있는지 경로를 알려주자.
app.use(express.static("public"));
public 폴더 안에 정적인 파일들이 있다.
레이아웃에서 home과 about의 경로를 수정하자. 내용 부분도 변경 내용이 들어갈 수 있도록 body 부분에 ejs 태그를 넣어준다. 탭의 제목 부분이 바뀔수 있게 title 부분에도 태그를 넣어주자.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= locals.title %></title> // 수정
<meta name="description" content="My first application using Node.js, Express and MongoDB">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div class="container">
<!-- 헤더 : 로고, 상단 메뉴, 로그인 -->
<header class="header">
<!-- 로고 -->
<a href="/" class="header-logo">오후의 블로그</a>
<!-- 상단 메뉴 -->
<nav class="header-nav">
<ul>
<li>
<a href="/home">Home</a> // 수정
</li>
<li>
<a href="/about">About</a> // 수정
</li>
</ul>
</nav>
<!-- 관리자 로그인 -->
<div class="header-button">
<a href="#">관리자 로그인</a>
</div>
</header>
<!-- 메인 : 실제 내용이 들어갈 부분 -->
<main class="main">
<%- body %> // 수정
</main>
</div>
</body>
</html>
이번에는 블로그에 접속했을 때 처음 보게되는 첫 번째 화면을 만들어 보겠다. 파일은 index.ejs에 저장할 것이다. / 혹은 home 경로로 접속했을 때 표시할 화면이다.
<!-- 상단 소개글, 히어로 이미지 -->
<div class="top">
<h1 class="top-heading">하루하루 스터디</h1>
<p class="top-body">매일 1시간씩 공부한 내용을 기록하고 있습니다.</p>
</div>
<img src="/img/top-hero.jpg" alt="노트에 기록하는 모습" class="hero-image" width="840" height="400">
<!-- 최근 게시물 -->
<section class="articles">
<h2 class="articles-heading">최근 게시물</h2>
<ul class="article-ul">
<li>
<a href="#">
<span>Title 1</span>
<span class="article-list-date">2023.01.01</span>
</a>
</li>
</ul>
</section>
이제 서버에서 /, home 경로로 접속했을 때 index.ejs 파일이 열려야 한다. 테스트해보자.


제목, 이미지, 게시물 목록으로 구성된 index.ejs 파일이 잘 나타난다. home도 똑같은 라우트 코드를 사용하기 때문이다.
여기서 메뉴의 about을 클릭하면

이렇게 나온다. 우리가 따로 코드를 작성하지 않았기 때문에 이렇게 나온다.
home으로 접속하든, about으로 접속하든 위의 레이아웃은 유지가 되고 아래 부분만 바뀌는 것을 볼 수 있다. 이렇게 레이아웃을 이용하면 전체적인 틀은 유지하면서 실제 필요한 부분만 내용을 바꿔 표시할 수 있다.
몽고 db의 데이터베이스에 접근하기 위해선 커넥션 스트링을 알아야 한다. 한 번 사용했던 몽고 db라면 vscode에서도 쉽게 복사 가능하다.

이렇게 우클릭 후 copy connection string으로 복사할 수 있다.
이렇게 복사한 커넥션 스트링을 외부에 드러내지 않고 저장하기 위해 env 파일을 사용할 것이다.
.env 파일을 생성하고 복사한 커넥션 스트링을 저장하자. 또한 우리는 새로운 db가 필요하다. 커넥션 스트링의 / 뒤에 원하는 db 이름을 추가해서 db를 만들자.
MONGOEB_URI = 복사한 커넥션 스트링 붙여넣기/myBlog
애플리케이션에서 몽고 db를 다루기 위해 모듈 몇 가지를 설치하자. 모듈 설치 전에는 실행 중인 서버를 종료하자.
모듈 설치 : mongoose, express-async-handler
npm i mongoose express-async-handler
이제 db에 접속하는 코드를 작성하자. config 폴더 안에 db.js 파일을 만들어 줄 것이다.
db.js 에서는 env 파일에 저장된 uri를 불러와서 그 경로로 접속하도록 할 것이다.
const mongoose = require("mongoose");
const asyncHandler = require("express-async-handler");
require("dotenv").config();
const connectDb = asyncHandler(async () => {
const connect = await mongoose.connect(process.env.MONGODB_URI);
console.log(`DB Connected: ${connect.connection.host}`);
});
module.exports = connectDb;
connectDb 라는 함수는 커넥션 스트링을 이용해 db에 접속을 하는 함수이다. 성공하면 메세지와 호스트 이름을 표현해 줄 것이다. 이렇게 작성한 connectDb는 모듈로 내보내 주면 된다.
이렇게 모듈로 내보낸 것은 app.js에서 가져와서 사용해 주면 된다.
const connectDb = require("./config/db");
그리고 db에 접속하도록 실행시켜 줘야 한다.
connectDb();
db에 제대로 접속됐는지 테스트해보자.
[nodemon] starting `node app.js`
App listening on port 3000
DB Connected: ac-ckstacf-shard-00-00.gshxubx.mongodb.net
콘솔 창에 서버를 실행했을 때 다음과 같이 나온다. db에 정상적으로 연결됐다는 것이다.
db에 자료를 저장하기 위해서는 그 자료에 스키마를 만들고 모델링을 해 줘야 한다. models 폴더에 Post.js 파일을 만들고 그 안에 스키마, 모델링을 해보자.
const mongoose = require("mongoose");
const PostSchema = new mongoose.Schema( {
title: {
type: String,
required: true
},
body: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now()
}
});
우리가 작성할 게시물에는 제목, 본문, 작성한 시간만 넣어보도록 하겠다.
스키마가 만들어졌으니 모델링을 해 주자.
module.exports = mongoose.model("Post", PostSchema);
PostSchema를 이용해서 Post 라는 모델을 만들라는 뜻이다. db에서는 posts 의 컬렉션 형태로 만들어지게 된다.
임시로 몇 가지 게시물을 추가 해 보겠다.
const Post = require("../models/Post");
우선 모델을 불러오고
Post.insertMany([
{
title: "제목 1",
body: "내용 1 - 내용입니다."
}
]); // 게시물 여러개 넣을 것(배열 형태로 들어감)
임시 데이터를 생성한다. 게시물 사이에 쉼표를 넣어 구별한다. 5개 정도 넣어보겠다.
저장 후 제대로 db에 할당 되는지 살펴보자.

두 번 저장하는 바람에 document 10개가 할당된 모습이다.
이렇게 임시 데이터를 넣고 난 후에 db에 있는지 확인되었다면 임시 데이터 부분을 삭제하거나 주석 처리하자. 그렇지 않으면 수정된 소스를 저장할 때마다 db에 임시 데이터가 들어가 버린다.
이제 할 일은 첫 번째 화면, index.ejs를 표시 할 때 그 화면에 db에 있는 게시물을 모두 가져와서 보여 줄 것이다.
<!-- 최근 게시물 -->
<section class="articles">
<h2 class="articles-heading">최근 게시물</h2>
<ul class="article-ul">
<% data.forEach(post => { %>
<li>
<a href="#">
<span><%= post.title %></span>
<span class="article-list-date"><%= post.createdAt.toDateString() %></span>
</a>
</li>
<% }) %>
</ul>
</section>
이 부분을 이렇게 수정해 줄 것이다.
이제 실행해보자.

이렇게 db에 저장한 게시물들이 나온다.
이제 제목을 클릭했을 때 실제 게시물 내용을 보여주는 컨트롤러 함수를 작성해보자.
게시물 각각의 내용을 보여주기 위해서는 파라미터 id를 활용해서 그 id 값에 해당하는 게시물을 가져오면 된다.
// 게시물 상세 보기
// GET /post/:id
router.get(
"/post/:id",
asyncHandler(async(req,res)=>{
const data = await Post.findOne({_id: req.params.id});
res.render("post", {data, layout: mainLayout});
})
)
post 경로로 id를 함께0 넘겨주면 db에서 그 값에 해당하는 자료를 찾아 data로 가져오고, 그 data를 post.ejs로 렌더링 하라는 뜻이다. 이 post.ejs 는 레이아웃이 mainLayout을 사용 할 것이다.
아직 post.ejs를 안 만들었기 때문에 빠르게 작성해보자.
<h1><%= data.title %></h1>
<article class="article">
<%= data.body %>
</article>
이렇게만 해주면 된다. 넘겨받은 data로 제목과 본문을 각각 표시를 해 주는 것이다.
이제 index.ejs를 post.ejs로 링크해주는 일만 남았다. index.ejs로 돌아가자.
<a href="/post/<%= post._id %>">
이제 한번 확인해보자.

제목을 클릭하면 이렇게 내용이 잘 나온다. 레이아웃은 여전히 잘 유지되고 있다.

우리가 앞에서 만든 일반 사용자 화면과 관리자 화면의 차이를 보여주는 그림이다.
일반 사용자 화면과 관리자 화면이 큰 차이는 없고 약간씩의 변화만 있기 때문에 일반 사용자 화면의 레이아웃을 복사해서 필요한 부분만 수정해서 쓰도록 하겠다.
views 폴더 안의 layouts 폴더에 admin.ejs 파일을 만들어주자. 그리고 main.ejs 안의 코드를 모두 복붙 후 약간의 수정을 하자.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= locals.title %></title>
<meta name="description" content="My first application using Node.js, Express and MongoDB">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div class="container">
<!-- 헤더 : 로고, 상단 메뉴, 로그인 -->
<header class="header">
<!-- 로고 -->
<a href="/" class="header-logo">Admin</a>
<!-- 상단 메뉴 -->
<nav class="header-nav">
<ul>
<li>
<a href="/home">Home</a>
</li>
<li>
<a href="/about">About</a>
</li>
</ul>
</nav>
<!-- 관리자 로그아웃 -->
<div class="header-button">
<a href="#">관리자 로그아웃</a>
</div>
</header>
<!-- 메인 : 실제 내용이 들어갈 부분 -->
<main class="main">
<%- body %>
</main>
</div>
</body>
</html>
사용자가 관리자로 로그인했을 때 관리자 화면으로 이동할 수 있는 라우트 코드도 작성해 보겠다.
routes 폴더 안에 admin.js 파일을 만들자.
const express = require("express");
const router = express.Router();
// Admin Page
// GET /admin
router.get("/admin", (req,res)=>{
res.send("Admin Page");
})
module.exports = router;
이렇게 라우트 코드 작성을 마치고 라우터를 모듈로 내보내는 것까지 했다.
이제 작성한 라우트 코드를 애플리케이션에서 사용할 수 있도록 app.js로 가자.
app.use("/", require("./routes/admin"));
이렇게 등록해 주었다. 이제 실제로 동작하는지 확인해보자.

이렇게 해당 경로를 입력하면 우리가 입력한대로 텍스트가 나온다. 라우트 코드가 app.js에 잘 연결되어 동작한다는 것을 확인했다.
이제 좀 더 세부적인 화면으로 넘어가 보겠다.
처음으로 로그인 화면이 나타나야 하고, 관리자 정보를 db에 저장해 두어야 한다.
이를 위해서 로그인 화면을 만들고, 관리자 정보 등록 폼을 만들어야 한다.
일반 사용자 화면과 헷갈리지 않도록 관리자 화면을 따로 만들 것이다. views 폴더 안에 admin 이라는 새 폴더를 만들어주자.
이 admin 폴더 안에 첫 화면을 나타낼 index.ejs 파일을 만들자. 이곳에 로그인 폼을 만들 것이다.
<h3>로그인</h3>
<form action="/admin" method="POST">
<label for="username"><b>사용자 이름</b></label>
<input type="text" name="username" id="username">
<label for="password"><b>비밀번호</b></label>
<input type="password" name="password" id="password">
<input type="submit" value="로그인" class="btn">
</form>
이 화면은 admin 요청으로 들어왔을 때 첫 화면으로 나타나야 한다.
이제 라우트 코드를 수정하자.
const express = require("express");
const router = express.Router();
const adminLayout = "../views/layouts/admin";
// Admin Page
// GET /admin
router.get("/admin", (req,res)=>{
const locals={
title: "관리자 페이지"
}
res.render("admin/index", {locals, layout: adminLayout});
})
module.exports = router;
이렇게 admin 폴더에 있는 index.ejs를 표시해 달라고 했다. 또 탭의 제목이 관리자 페이지가 되도록 locals 변수도 render()에서 같이 넘겨주자.

로그인 창이 나타난다. 그리고 현재 로그인 하기 전인데 관리자 로그아웃이라는 링크가 있다. 저 링크는 이 화면에서 필요하지 않다. 그러나 관리자 로그아웃이 들어있는 admin.ejs의 레이아웃을 뜯어고치면 나중에 로그인 했을 때 로그아웃할 수 있는 링크가 사라져 버린다.
즉, 관리자로 로그인 하기 위해서는 2가지 레이아웃이 필요하다 이야기가 된다.
admin.ejs를 다른 이름으로 저장하고(admin-nologout.ejs), 로그아웃 링크를 지우자.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= locals.title %></title>
<meta name="description" content="My first application using Node.js, Express and MongoDB">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div class="container">
<!-- 헤더 : 로고, 상단 메뉴, 로그인 -->
<header class="header">
<!-- 로고 -->
<a href="/" class="header-logo">Admin</a>
<!-- 상단 메뉴 -->
<nav class="header-nav">
<ul>
<li>
<a href="/home">Home</a>
</li>
<li>
<a href="/about">About</a>
</li>
</ul>
</nav>
</header>
<!-- 메인 : 실제 내용이 들어갈 부분 -->
<main class="main">
<%- body %>
</main>
</div>
</body>
</html>
그 후 다시 라우트 코드로 돌아와서 새로 만든 로그아웃 링크가 없는 레이아웃을 불러오자.
const adminLayout2 = "../views/layouts/admin-nologout";
...
router.get("/admin", (req,res)=>{
const locals={
title: "관리자 페이지"
}
res.render("admin/index", {locals, layout: adminLayout2});
})
이러면 index.ejs는 로그아웃 링크가 없는 레이아웃을 사용하게 된다.
