728x90
목차
1. 라우터 분리
1.1. routers 폴더 생성
아래 이미지와 같이 폴더와 js파일을 구성한다.
1.1.1. routers/index.js
const express = require('express');
// const app = express();
const router = express.Router();
// app.get('/', (req, res)=>{});
router.get('/', (req, res)=>{
res.send("<h1>Hello, Express router - index - '/'</h1>");
});
router.get('/about', (req, res)=>{
res.send("<h1>Hello, Express router - index - '/about'</h1>");
});
module.exports = router;
1.1.2. routers/users.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.send("<h1>Hello, Express router - USERS '/'</h1>");
});
router.get('/search', (req, res) => {
res.send("<h1>Hello, Express router - USERS '/search'</h1>");
});
module.exports = router;
1.2. 라우터 설정
1.2.1. 라우터 설정
// 라우터 설정
const indexRouter = require('./routers'); // index.js는 자동인식된다. (상대경로 ./)
const userRouter = require('./routers/users');
1.2.2. 분리된 라우터 연결
// 라우터 설정
const indexRouter = require('./routers'); // index.js는 자동인식된다. (상대경로 ./)
const userRouter = require('./routers/users');
// 라우팅
// 현재 파일에서 사용한 '/' 와 indexRouter 에 있는 '/' 와 조합이 됩니다
// '//' 가 '/' 로 사용이 됩니다
app.use('/', indexRouter);
// indexRouter의 '/' 요청 -> http://localhost:3000/
// indexRouter의 '/about' 요청 -> http://localhost:3000/about
// 현재 파일에서 사용한 '/users' 와 userRouter 에 있는 '/' 와 조합되어
// '/users/' 가 사용됩니다.
app.use('/users', userRouter);
// userRouter '/' 요청 -> http://localhost:3000/users
// userRouter '/search' 요청 -> http://localhost:3000/users/search
1.3. 전체 코드 ( app.js )
const express = require('express');
const app = express();
app.set('port', process.env.PORT || 3000);
// 라우터 설정
const indexRouter = require('./routers');
const userRouter = require('./routers/users');
// 라우팅
app.use('/', indexRouter);
app.use('/users', userRouter);
app.listen(app.get('port'), () => {
console.log(app.get('port'), '번 포트에서 대기 중');
});
2. 쿠키와 세션의 암호화
- 쿠키와 세션은 '보안상 중요'하기 때문에 절대 소스코드에 노출하면 안 되고, 환경변수 파일에서 따로 관리하도록 한다.
2.1. dotenv 모듈
- 디폴트로 현재 디렉토리에 위치한 .env 파일로 부터 환경 변수를 읽어냅니다.
- 따라서, .env 파일을 생성하고, 그 안에 필요한 환경 변수를 키=값의 포멧으로 나열한다.
- 최상위에 dotenv모듈을 임포트(require)한 후 config() 함수를 호출한다.
onst express = require('express');
// dotenv모듈 import
const dotenv = require('dotenv');
// 비밀키 비공개를 위한 기본 환경 구성
dotenv.config();
2.1.1. .env 환경변수 파일 저장
COOKIE_SECRET=nodejsdotenv
2.2. 쿠키 / 세션 암호화 설정
// 쿠키 암호화(환경변수파일에서 키 받아옴)
app.use(cookieParser(process.env.COOKIE_SECRET)); // 추가3
...
// 세션 설정
app.use(session({
resave: false,
saveUninitialized: false,
//secret: 'nodejsdotenv', // 세션을 발급할 때 사용되는 키입니다.
secret: process.env.COOKIE_SECRET, // 세션 암호화(환경변수파일에서 키 받음)
cookie: {
httpOnly: true,
secure: false,
},
name: 'session-cookie',
}));
secret : 세션을 발급할 때 사용되는 키 설정.
2.3. 전체 코드
2.3.1. app.js
const express = require('express');
// 추가모듈
const cookieParser = require('cookie-parser');
const session = require('express-session');
const dotenv = require('dotenv'); // 추가1
const path = require('path');
// 비밀키 비공개를 위한 기본 환경 구성
dotenv.config(); // 추가2
const app = express();
app.set('port', process.env.PORT || 3000);
// 쿠키 암호화(환경변수파일에서 키 받아옴)
app.use(cookieParser(process.env.COOKIE_SECRET)); // 추가3
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// 세션 설정
app.use(session({
resave: false,
saveUninitialized: false,
//secret: 'nodejsdotenv', // 세션을 발급할 때 사용되는 키.
secret: process.env.COOKIE_SECRET, // 세션 암호화(환경변수파일에서 키 받음)
cookie: {
httpOnly: true,
secure: false,
},
name: 'session-cookie',
}));
// 라우팅
app.get('/', (req, res)=>{
if( req.session.userid ){
res.send(`${req.session.userid} 님 반갑습니다.` + '<a href="/logout">로그아웃</a>');
}else{
res.sendFile(path.join(__dirname, '/login.html'));
}
});
app.post('/login', (req, res)=>{
const id = req.body.id;
const pw = req.body.pw;
if( id=='scott' && pw=='tiger'){
req.session.userid = id;
return res.send('ok');
}else if(id!='scott'){
return res.send('없는 아이디입니다');
}else if(pw!='tiger'){
return res.send('비밀번호가 맞지 않습니다');
}else{
return res.send('알수없는 이유로 로그인 안됩니다.');
}
});
app.get('/logout', (req, res)=>{
req.session.destroy(function(){
req.session;
});
res.redirect('/');
});
app.listen(app.get('port'), () => {
console.log(app.get('port'), '번 포트에서 대기중입니다');
});
2.3.2. login.html
<!DOCTYPE html>
<html lang="en">
<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>login.html</title>
</head>
<body>
<form method="post" id="form">
<input type="text" name="id" ><br /> <input type="password" name="pw" ><br />
<input type="submit" value="로그인"><br /> <div id="message"></div>
</form>
<script src="https://unpkg.com/axios@0.25.0/dist/axios.min.js"></script>
<script type="text/javascript">
document.getElementById("form").addEventListener( 'submit', async (e)=>{
e.preventDefault();
const id = e.target.id.value;
const pw = e.target.pw.value;
if( id == '' ){
alert('이름을 입력하세요'); return;
}else if( pw == '' ){
alert("비밀번호를 입력하세요"); return;
}
try{
const res = await axios.post('/login', { id, pw });
if(res.data == 'ok') location.href='/';
document.getElementById('message').innerHTML = res.data;
}catch(err){
console.error(err);
}
e.target.id.value='';
e.target.pw.value='';
});
</script>
</body>
</html>
3. nunjucks(넌적스) 템플릿 엔진
넌적스는 템플릿 엔진 중 하나로 모질라 재단에서 만든 템플릿
3.1. nunjucks 템플릿 모듈
- 넌적스는 npm 으로 설치할 수 있습니다.
- configure 의 첫 번째 인수로 'views 의 경로'를 전달해 줍니다.
- 그리고 두 번째 인수로 '옵션'을 지정해 줍니다.
const nunjucks = require('nunjucks'); // 템플릿엔진 사용을 위한 require
...
// 넌적스 템플릿 엔진을 사용하기 위한 설정
app.set('view engine', 'html');
nunjucks.configure('views', {
express: app,
watch: true,
});
3.2. 미들웨어에서의 nunjucks
res.render 호출 시 보내는 변수를 nunjucks 가 처리한다.
- 넌적스를 이용해서 html 파일을 클라이언트에 보낼때 그 파일에 전달해줄 데이터를 위와 같이 객체형식으로 하나이상 같이 태워 보낼수가 있다.
( 스프링에서의 request.setAttribute, model.addAttribute, mav.addObject 와 비슷한 기능 ) - 파일을 클라이언트에 응답으로 보낼때는 'render()' 라는 메서드를 사용한다
app.get('/', (req, res)=>{
res.render( 'index' , {title:'Express'} );
});
app.get('/include', (req, res)=>{
res.render( 'main' , {title:'Express'} );
});
app.get('/extends', (req, res)=>{
res.render( 'extends' , {title:'Express'} );
});
3.3. nunjucks 문법
3.3.1. 변수
<!-- 내부변수 -->
{% set node = 'Node - Js' %}
{% set js = 'JavaScript' %}
<h2> {{node}} 와 {{js}} </h2>
<br><hr>
<!-- 꺽쇠괄호 태그의 적용 유무(safe) -->
<h3>{{'<strong>이스케이프</strong>'}}</h3>
<h3>{{'<strong>이스케이프 하지 않음</strong>' | safe}}</h3>
3.3.2. 반복문
<br><hr>
<!-- 반복문 - 중괄호-퍼센트 안에 for in 작성. index 는 loop.index -->
{% set fruits=['사과','배','오렌지','바나나','복숭아'] %} <!--넌적스 배열 생성-->
<ul>
{% for item in fruits %}
<li>{{item}}</li>
{% endfor %}
</ul>
<ul>
{% for item in fruits %}
<li>{{loop.index}} 번째 {{item}}</li>
{% endfor %}
</ul>
3.3.3. 조건문
<br><hr>
<!-- 조건문 -->
{% set isLogged = true %}
{% if isLogged %}
<h3>로그인 되었습니다</h3>
{% else %}
<h3>로그인이 필요합니다</h3>
{% endif %}
{% set fruit = '' %}
{% if fruit === 'apple' %}
<h3>사과입니다</h3>
{% elif fruit === 'banana' %}
<h3>바나나입니다</h3>
{% elif fruit === 'orange' %}
<h3>오렌지입니다</h3>
{% else %}
<h3>아무것도 아닙니다</h3>
{% endif %}
3.3.4. include
main.html에 header.html과 footer.html을 include한다.
header.html
<header>
<a href="/">HOME</a>
<a href="/about">About</a>
이것은 header
<br><hr>
</header>
footer.html
<footer>
<hr>
<h3>이것은 footer</h3>
</footer>
main.html
{% include "header.html" %}
<main>
<h1>메인파일</h1>
<h2>여기가 메인입니다. 헤더와 푸터가 인클루드 되었습니다</h2>
</main>
{% include "footer.html" %}
3.3.5. extends & block
- 특이한 점은 레이아웃이 되는 html이 불러와진다는 것!
- 본체가 블럭이다. ( 미들웨어에서 호출을 블럭으로 해야한다 )
extends.html
{% extends 'layout.html' %}
{% block content %}
<h1>여기는 Block Content 의 본 내용 입니다</h1>
<h2> 현위치에 쓰여진 Block Content 의 본내용이 layout.html 에 전달되어 표시되는 거 같아 보이지만<br>
실제는 layout.html 에 담긴 전체 페이지 양식이 이곳에 와서 주위에 배치된 형식입니다</h2>
{% endblock %}
layout.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<main>
<h1>layout.html 파일</h1>
<h4>여기가 layout.html의 본파일 내용입니다. <br>
아래에 추가 내용이 확장되어 최종 파일이 완성될 예정입니다 예정입니다</h4>
<h4>layout.html 파일은 특정 위치의 내용을 상활별로 달리하지만 양식은 하나로 유지해야할때 사용합니다</h4>
<h2>여기서부터 확장 내용 시작입니다.</h2>
<hr>
{% block content %}
{% endblock %}
<hr>
<h2>여기까지가 확장 내용 끝입니다.</h2>
<h4>다시 layout.html이 시작합니다</h4>
<h4>include 는 다른 파일의 내용을 이곳으로 불러와서 표시하는 형식이지만, extends 는 현재파일이 다른 애용에 제공되어 양식을 구성하는 형식입니다. 그래서 app.js 에서 render 할때도 layout.html 파일을 지목하지 않고, block content 의 내용을 이루는 파일을 지목합니다.</h4>
</main>
</body>
</html>
300x250