const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express();
app.use(express.static('public'));
app.use(express.urlencoded({ extended: false }));
app.get('/', function (req, res) {
res.render('index');
});
app.get('/', function (req, res) {
const htmlFilePath = path.join(__dirname, 'views', 'index.html');
res.sendFile(htmlFilePath);
});
app.get('/recommend', function (req, res) {
const htmlFilePath = path.join(__dirname, 'views', 'recommend');
res.sendFile(htmlFilePath);
app.get('/confirm', function (req, res) {
const htmlFilePath = path.join(__dirname, 'views', 'confirm.html');
res.sendFile(htmlFilePath);
});
app.get('/about', function (req, res) {
const htmlFilePath = path.join(__dirname, 'views', 'about.html');
res.sendFile(htmlFilePath);
});
app.listen(3000);
이후 index.html, recommend.html, confirm.html, about.html파일의 a태그의 링크도 파일경로가 아닌 /confirm
/about
처럼 주소형식으로 바꿔준다.
참고링크 - ejs 사용설명서
ejs란 Javascript가 내장된 html파일이다.
아마 jsp와 유사한 느낌으로 이해하면 될 것 같다.
1) 터미널에서 npm install ejs
을 입력하여 ejs를 설치.
2) html파일의 확장자를 모두 .ejs로 변경
3) js파일 코드 수정
- app.js -
const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.static('public'));
app.use(express.urlencoded({ extended: false }));
app.get('/', function (req, res) {
res.render('index');
});
app.get('/restaurants', function (req, res) {
res.render('restaurants');
});
app.get('/recommend', function (req, res) {
res.render('recommend');
});
app.post('/recommend', function (req, res) {
const restaurant = req.body;
const filePath = path.join(__dirname, 'data', 'restaurants.json');
const fileData = fs.readFileSync(filePath);
const storedRestaurants = JSON.parse(fileData);
storedRestaurants.push(restaurant);
fs.writeFileSync(filePath, JSON.stringify(storedRestaurants));
res.redirect('/confirm');
});
app.get('/confirm', function (req, res) {
res.render('confirm');
});
app.get('/about', function (req, res) {
res.render('about');
});
app.listen(3000);
여기서 app.post('/recommend', function (req, res)
부분은 post로 데이터를 받아와 json에 저장하는 역할을 하는 부분이므로 이 부분은 코드를 변경하지 않음.
json파일에서 읽어온 레스토랑 목록을 restaurants.ejs에서 보여주는 예제.
- restaurants.ejs -
<h1>Recommended restaurants</h1>
<p>Find your next favorite restaurants with help of our other users!</p>
<p>We found <%= numberOfRestaurants %> restaurants.</p>
<ul id="restaurants-list">
<% for (const restaurant of restaurants) { %>
<li class="restaurant-item">
<article>
<h2><%= restaurant.name %></h2>
<div class="restaurant-meta">
<p><%= restaurant.cuisine %></p>
<p><%= restaurant.address %></p>
</div>
<p><%= restaurant.description %></p>
<div class="restaurant-actions">
<a href="<%= restaurant.website %>">View Website</a>
</div>
</article>
</li>
<% } %>
</ul>
json에 저장된 레스토랑 데이터를 받아와 반복문을 이용해 li마다 할당하여 보여줌.
현재 json파일의 데이터는 아래와 같음.
- restaurants.json -
[
{
"name": "테스트",
"address": "테스트거리 5, 테스트시",
"cuisine": "이탈리안",
"website": "http://mytest.com",
"description": "맛나요"
},
{
"name": "테스트2",
"address": "테스트2거리 5, 테스트시2",
"cuisine": "멕시칸",
"website": "http://mytest2.com",
"description": "가나다라"
}
]
app.js파일에서 json파일을 읽어온 후 ejs파일에 작성해둔 restaurants
변수에 데이터를 전달하도록 코드 변경.
- app.js -
app.get('/restaurants', function (req, res) {
const filePath = path.join(__dirname, 'data', 'restaurants.json');
const fileData = fs.readFileSync(filePath);
const storedRestaurants = JSON.parse(fileData);
res.render('restaurants', { numberOfRestaurants: storedRestaurants.length, restaurants: storedRestaurants });
});
storedRestaurants.length
변수는 json파일 내 데이터의 갯수를 읽어와 저장하는 변수, restaurants
변수는 전체데이터를 객체로 가져와 저장하는 변수.
제대로 적용되었을 경우 위와 같은 화면이 나옴.
각 레스토랑마다 고유한 id를 부여해 구분하고 관리할 수 있도록 하기.
외부 패키지 다운로드 => 터미널에 npm install uuid
입력하여 다운.
자바스크립트는 객체에 아직 존재하지 않는 속성에 엑세스하면 자동으로 생성해줌.
- app.js -
const uuid = require('uuid');
~생략~
app.post('/recommend', function (req, res) {
const restaurant = req.body;
restaurant.id = uuid.v4();
const filePath = path.join(__dirname, 'data', 'restaurants.json');
const fileData = fs.readFileSync(filePath);
const storedRestaurants = JSON.parse(fileData);
storedRestaurants.push(restaurant);
fs.writeFileSync(filePath, JSON.stringify(storedRestaurants));
res.redirect('/confirm');
});
restaurant.id = uuid.v4();
에서 restaurant
객체에 아직 id
라는 속성은 없는 상태이며 uuid.v4()
는 외부 패키지에서 제공받은 uuid
객체를 통해 무작위로 생성되지만 고유성을 보장하는 id를 제공해주는 기능을 함.
레스토랑 상세페이지에서 id속성으로 상세페이지를 찾아가도록 a태그 주소수정.
- restaurant-item.ejs -
<li class="restaurant-item">
<article>
<h2><%= restaurant.name %></h2>
<div class="restaurant-meta">
<p><%= restaurant.cuisine %></p>
<p><%= restaurant.address %></p>
</div>
<p><%= restaurant.description %></p>
<div class="restaurant-actions">
<a href="/restaurants/<%= restaurant.id %>">View Restaurant</a>
</div>
</article>
</li>
- restaurant-detail.ejs -
<!DOCTYPE html>
<html lang="en">
<head>
<%- include('includes/head') %>
<link rel="stylesheet" href="/styles/restaurants.css" />
</head>
<body>
<%- include('includes/header') %> <%- include('includes/sidebar') %>
<main>
<h1><%= restaurant.name %></h1>
<p><%= restaurant.cuisine %> | <%= restaurant.address %></p>
<p><%= restaurant.description %></p>
<p><a href="<%= restaurant.website %>">View Website</a></p>
</main>
</body>
</html>
레스토랑 리스트 중 하나를 클릭하면 해당 레스토랑의 id를 주소로 상세페이지를 로드함.
url에러 등 원하는 경로의 페이지가 없을 경우 시스템에러페이지가 아닌 커스텀페이지를 보여주도록 변경하는 작업.
경우 1. 상세조회 할 페이지의 경로를 잘못입력한 경우 (404 상태코드)
레스토랑 상세조회 url인 http://로컬ip:3000/restaurants/r1 입력 중 레스토랑의 id를 잘못 입력한 경우.
404페이지를 만들어 대응할 수 있다.
경우 2. 서버가 요청을 이행하지 못해 에러가 나는 경우 (500 상태코드)
url경로를 틀려 에러가 나는 경우, json파일을 읽어오지 못해 에러가 나는 경우 등.
이 경우에는 경우 1과 달리 사용자가 url의 어디에서 에러를 낼지 알 수 없는 등 다양한 상황에 대한 개별적 대응이 불가능하므로 모든 요청을 받는 함수가 필요하다.
- 404.ejs -
<body>
<%- include('includes/header') %> <%- include('includes/sidebar') %>
<main>
<h1>Page not found!</h1>
<p>요청하신 페이지를 찾지 못했습니다.</p>
</main>
</body>
- app.js -
app.get('/restaurants/:id', function (req, res) {
const restaurantId = req.params.id;
const filePath = path.join(__dirname, 'data', 'restaurants.json');
const fileData = fs.readFileSync(filePath);
const storedRestaurants = JSON.parse(fileData);
for (const restaurant of storedRestaurants) {
if (restaurant.id === restaurantId) {
return res.render('restaurant-detail', { restaurant: restaurant });
}
}
res.status(404).render('404');
});
for~if문단에서 적절한 데이터를 찾아 return되지 않고 블록을 넘어갈 경우 res.render('404');
로 받아 404.ejs페이지로 넘겨준다.
http://로컬ip:3000/restaurants/r100 경로(id가 r100인 레스토랑은 없는 상태)로 접속할 시 시스템 에러페이지 대신 404.ejs페이지를 보여준다.
app.js에서 모든 url을 받을 수 있는 함수를 만들어 대응한다.
- app.js -
app.use(function (error, req, res, next) {
res.status(500).render('500');
});
http://로컬ip:3000/resta 경로(잘못된 요청)로 접속하거나 레스토랑의 데이터를 찾는 json파일의 이름을 변경한 경우(json파일요청에러) 시스템 에러페이지 대신 위와 같이 개발자가 커스텀한 페이지를 사용자에게 보여주게 된다.
res.status(500).render('500');
에서 .status(500)
는 사용자에게 보여주는 화면은 커스텀페이지로 유지하되 개발자모드로 페이지를 볼때 에러코드를 확인할 수 있도록 하기 위해 추가한코드.
.status(500)
가 없을 경우 개발자모드로 커스텀에러화면을 볼때 정상적인 요청코드(303 등)로 보임.