반응형
# 익스프레스 웹 서버 만들기
1. express 서버 사용해보기
- http 모듈로 웹 서버를 만들 때 코드가 보기 좋지 않고, 확장성도 떨어짐( 프레임워크로 해결. )
- 대표적인 것이 Express(익스프레스) / Koa(코아) / Hapi(하피)
- 코드 관리도 용이하고, 편의성이 많이 높아졌다.
## package.json 생성
- 직접 만들거나(직접 입력 후 npm i 해줘야 package-lock.json, node_modules 생성됨), npm init 명령어 사용하여 생성.
- nodemon : 소스 코드 변경 시 알아서 서버를 재시작해준다.
$ npm i express
$ npm i -D nodemon
const express = require('express');
const app = express();
app.set('port', process.env.PORT || 3000);
app.post('/', (req, res) => {
res.send('hello express');
});
app.get('/about', (req, res) => {
res.send('hello express');
});
app.listen(app.get('port'), () => {
console.log('익스프레스 서버 실행');
});
- package.json에서 scripts에 "start" : "nodemon app"이 등록되어있다면, npm start 입력하여 실행 가능. (그럼 자동으로 nodemon app 실행 됨)
2. express로 html 서빙하기
- sendFile 이용 html 서빙가능.
const express = require('express');
const path = require('path');
const app = express();
app.set('port', process.env.PORT || 3000);
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
app.post('/', (req, res) => {
res.send('hello express');
});
app.get('/about', (req, res) => {
res.send('hello express');
});
app.listen(app.get('port'), () => {
console.log('익스프레스 서버 실행');
});
3. 미들웨어 사용
- 메서드와 주소가 있는 형태를 라우터라고 부름.
- 익스프레스는 기본적으로 위 > 아래로 실행하지만, 미들웨어는 next를 해줘야 다음걸로 넘어간다.
const express = require('express');
const path = require('path');
const app = express();
app.set('port', process.env.PORT || 3000);
app.use((req, res, next) => {
console.log('모든 요청에 실행하고 싶어.');
next();
});
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
app.post('/', (req, res) => {
res.send('hello express');
});
app.get('/about', (req, res) => {
res.send('hello express');
});
app.listen(app.get('port'), () => {
console.log('익스프레스 서버 실행');
});
const express = require('express');
const path = require('path');
const app = express();
app.set('port', process.env.PORT || 3000);
app.use((req, res, next) => {
console.log('모든 요청에 실행하고 싶어.');
next();
});
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
app.post('/', (req, res) => {
res.send('hello express');
});
app.get('/category/Javascript', (req, res) => {
res.send('hello Javascript');
});
app.get('/category/:name', (req, res) => {
res.send(`hello ${req.params.name}`);
});
app.get('/about', (req, res) => {
res.send('hello express');
});
app.get('*', (req, res) => {
res.send('와일드카드 * 는 모든 GET은 여길 실행하겠다는 것.');
});
app.listen(app.get('port'), () => {
console.log('익스프레스 서버 실행');
});
4. 미들웨어 특성 이해
- req, res, next를 매개변수로 가지는 함수
app.use((req, res, next) => {
console.log('모든 요청에 다 실행됩니다.');
next();
});
- 익스프레스 미들웨어들도 다음과 같이 축약 가능.
순서가 중요함
static 미들웨어에서 파일을 찾으면 next를 호출 안 하므로 json, urlencoded, cookieParser는 실행되지 않음.
app.use(
morgan('dev'),
express.static('/', path.join(__dirname, 'public')),
express.json(),
express.urlencoded({ extended : false }),
cookieParser(process.env.COOKIE_SECRET),
);
- 익스프레스는 미들웨어로 구성됨
요청과 응답의 중간에 위치하여 미들웨어
app.use(미들웨어)로 장착
위에서 아래로 순서대로 실행됨.
미들웨어는 req, res, next가 매개변수인 함수.
req : 요청 / res : 응답 조작 가능
next()로 다음 미들웨어로 넘어감
- 함수, 미들웨어
app.get('/about', (req, res) => {
res.send('hello express');
});
에서..
1. 함수는
app.get(); 부분
2. 미들웨어는
함수 안에있는
(req, res) => {
res.send('hello express');
} 부분
- 아래와 같이 미들웨어를 여러개 동시에 넣어줄수 있음. (연달아 사용 가능)
app.use((req, res, next) => {
console.log('모든 요청에 실행하고 싶어.');
next();
}, (req, res, next) => {
console.log('모든 요청에 실행하고 싶어.');
next();
}, (req, res, next) => {
console.log('모든 요청에 실행하고 싶어.');
next();
});
- 에러 처리 미들웨어 : 에러가 발생하면 에러 처리 미들웨어로.
err, req, res, next까지 매개변수가 4개
첫 번째 err에는 에러에 관한 정보가 담김
res.status 메서드로 HTTP 상태 코드를 지정 가능하다. (기본값은 200)
에러 처리 미들웨어를 연결하지 않아도 익스프레스가 에러를 알아서 처리해주긴 함.
특별한 경우가 아니면 아래에 위치하도록 한다.
- 에러 처리는 매우 중요. 에러 처리 로직의 경우 가장 마지막에 작성
// 에러 미들웨어의 경우 err, req, res, next 모두 들어있어야 함(반드시!!).
app.use((err, req, res, next) => {
console.error(err);
})
// 아래와 같이.. a와 b는 다름
1. a
app.use((err, req, res, next) => {
console.error(err);
})
2. b
app.use((err, req, res) => {
console.error(err);
})
a 와 b는 다른함수로 취급 됨. next 생략 하지 말 것.
- 라우터 뒤에 아래와 같이 처리 가능 (401, 403, 404 등 관련 코드를 다르게 해줄 수도 있음)
app.get((req, res, next) => {
res.status(200).send('200이지렁!!');
});
app.get((req, res, next) => {
res.status(404).send('404이지렁!!');
});
// 이렇게 401이지만 다른 문구 등이 표시되도록 할 수 있음. (해커들에게 정보 노출을 숨길수 있음)
app.get((req, res, next) => {
res.status(401).send('404이지렁!!');
});
- 하나의 라우터에서 아래와 같이 여러번 하면 에러 발생 가능.
app.get('/', (req, res, next) => {
res.sendFile(path.join(__dirname, 'index.html'));
res.send('hi');
res.json({ hello: 'zeroro' });
});
=> 에러문구
Cannot set headers after they are sent to the client
- 미들웨어의 특성 : req, res, next를 매개변수로 가지는 함수.
app.use((req, res, next) => {
console.log('모든 요청에 다 실행 됨');
next();
});
- 익스프레스 미들웨어들도 다음과 같이 축약 가능하다.
순서가 중요함.
static 미들웨어에서 파일을 찾으면 next를 호출 안 하므로 json, urlencoded, cookieParser는 실행되지 않는다.
app.use(
morgan('dev'),
express.static('/', path.join(__dirname, 'public')),
express.json(),
express.urlencoded({ extended: false }),
cookieParser(process.env.COOKIE_SECRET),
);
- 자주 쓰는 미들웨어
morgan, cookie-parser, express-session 설치.
app.use로 장착한다.
내부에서 알아서 next를 호출해서 다음 미들웨어로 넘어간다.
5. next 활용법
- next를 호출해야 다음 코드로 넘어간다.
next를 주석 처리하면 응답이 전송되지 않는다.
다음 미들웨어(라우터 미들웨어)로 넘어가지 않기 때문.
next에 인수로 값을 넣으면 에러 핸들러로 넘어간다. ('route'인 경우 라우터로 넘어간다.)
- res.json은 응답을 보낼 뿐이지 함수 자체를 종료하는게 아님. 그래서 아래와 같이 작성 시 console.log 실행됨.
app.get('/', (req, res, next) => {
res.json({ hello: 'zeroro' });
console.log('실행 안되겠지?');
});
- 보통 에러 처리는 next 이용. next에 에러를 넣어서 에러 처리 미들웨어로 넘긴다.
app.use((req, res, next) => {
console.log('1 요청을 실행하고 싶어요.');
next();
}, (req, res, next) => {
try {
console.log(asdsada);
} catch (error) {
next(error);
}
});
app.use((err, req, res, next) => {
console.error(err);
res.status(200).send('에러 났지렁!');
});
- next('route')의 경우 다음 라우터로 넘어감.
app.get('/', (req, res, next) => {
res.sendFile(path.join(__dirname, 'index.html'));
next('route'); // 다음 라우터로 넘어감.
}, (req, res) => {
console.log('실행되나?'); // 실행 x
});
app.get('/', (res, req, next) => {
console.log('실행됨'); // 실행됨.
});
// 아래와 같이 if문에 따라 어떤 미들웨어 실행할지 가능. (next 분기처리)
app.get('/', (req, res, next) => {
res.sendFile(path.join(__dirname, 'index.html'));
if (true) {
next('route'); // if문이 true면 다음 라우터(실행됨)로 넘어감.
} else {
next(); // if문이 false면 (실행되나?)로 넘어감.
}
}, (req, res) => {
console.log('실행되나?');
});
app.get('/', (res, req, next) => {
console.log('실행됨');
});
### 미들웨어 간 데이터 전달하기
- req나 res 객체 안에 값을 넣어 데이터 전달 가능
- app.set과의 차이점 : app.set은 서버 내내 유지, req, res는 요청 하나 동안만 유지
- req.body나 req.cookies 같은 미들웨어의 데이터와 겹치지 않게 조심해야 함.
app.use((req, res, next) => {
req.data = '데이터 넣기';
next();
}, (req, res, next) => {
console.log(req.data); // 데이터 받기
next();
});
# 미들웨어 간 데이터 전달 예_1
app.use((req, res, next) => {
req.session.data = 'zerocho 비번';
});
app.get('/', (req, res, next) => {
req.session.data // 'zerocho 비번'
res.sendFile(path.join(__dirname, 'index.html'));
});
=> 계속 유지되고 싶은 데이터일때 사용.
단점 : 다음 요청때도 데이터가 남아있게 됨.
# 미들웨어 간 데이터 전달 예_2
app.use((req, res, next) => {
req.data = 'zerocho 비번';
});
app.get('/', (req, res, next) => {
req.data // 'zerocho 비번'
res.sendFile(path.join(__dirname, 'index.html'));
});
=> 요청 한번만 할 경우 사용.
### 미들웨어 확장하기
- 미들웨어 안에 미들웨어를 넣는 방법
방법1)
app.use(morgan('dev'));
방법2)
app.use((req, res, next) => {
morgan('dev')(req, res, next);
});
- 아래처럼 다양하게 활용 가능
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production') {
morgan('combined')(req, res, next);
} else {
morgan('dev')(req, res, next);
}
});
app.use('/', express.static(path.join(__dirname, 'public'))); 를 예로..
# 미들웨어 확장법 이용, 로그인 한 대상에게만 express.static 실행되도록 하려면
app.use('/', (req, res, next) => {
if (req.session.id) { // 로그인 시.
express.static(path.join(__dirname, 'public')))(req, res, next);
} else {
next();
}
});
6. morgan, bodyParser, cookieParser
### morgan
- 서버로 들어온 요청과 응답을 기록해주는 미들웨어이다.
- 로그의 자세한 정도 선택 가능(dev, tiny, short, common, combined)
app.use(morgan('dev'));
- morgan 예 (dev, combined)
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const app = express();
app.set('port', process.env.PORT || 3000);
// 개발 시 사용.
app.use(morgan('dev'));
// 실무에서 사용(배포 시?)
// app.use(morgan('combined'));
app.use(cookieParser());
### cookie-parser
- 요청 헤더의 쿠키를 해석해주는 미들웨어
- parseCookies 함수와 기능이 비슷.
- req.cookies 안에 쿠키들이 들어있다.
# app.js
app.use(cookieParser(비밀키));
비밀키로 쿠키 뒤에 서명을 붙여 내 서버가 만든 쿠키임을 검증할 수 있다.
- 실제 쿠키 옵션들을 넣을 수 있다. (expires, domain, httpOnly, maxAge, path, secure, sameSite 등, 지울 때는 clearCookie로 (expires와 maxAge를 제외한 옵션들이 일치해야 함))
res.cookie('name', 'zerocho', {
expires : new Date(Date.now() + 900000),
httpOnly : true,
secure : true,
});
res.ClearCookie('name', 'zerocho', { httpOnly : true, secure : true });
- cookieParser 예
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const app = express();
app.set('port', process.env.PORT || 3000);
// 개발 시 사용.
app.use(morgan('dev'));
// 실무에서 사용
// app.use(morgan('combined'));
app.use(cookieParser());
// cookieParser 이용 예.
app.get('/', (req, res) => {
req.cookies // { mycookie: 'test' 등으로 }알아서 파싱됨
//'Set-Cookie' : `name=${encodeURIComponent(name)}; Expires=${expires.toGMYString()}; HttpOnly; Path=/`,
res.cookie('name', encodeURIComponent(name), {
expires: new Date(),
httpOnly: true,
path: '/',
})
// 쿠키 삭제 시.
res.clearCookie('name', encodeURIComponent(name), {
httpOnly: true,
path: '/',
})
res.sendFile(path.join(__dirname, 'index.html'));
});
- cookieParser 엄호화? 서명된 쿠키 사용 예 (req.signedCookies 이용)
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const app = express();
app.set('port', process.env.PORT || 3000);
// 개발 시 사용.
app.use(morgan('dev'));
// 실무에서 사용
// app.use(morgan('combined'));
app.use(cookieParser(abcdefg));
app.get('/', (req, res) => {
req.cookies // { mycookie: 'test' 등으로 }알아서 파싱됨
req.signedCookies // 서명된 쿠키.
//'Set-Cookie' : `name=${encodeURIComponent(name)}; Expires=${expires.toGMYString()}; HttpOnly; Path=/`,
res.cookie('name', encodeURIComponent(name), {
expires: new Date(),
httpOnly: true,
path: '/',
})
// 쿠키 삭제 시.
res.clearCookie('name', encodeURIComponent(name), {
httpOnly: true,
path: '/',
})
res.sendFile(path.join(__dirname, 'index.html'));
});
### body-parser
- 요청의 본문을 해석해주는 미들웨어
- 폼 데이터나 AJAX 요청의 데이터 처리
- json 미들웨어는 요청 본문이 json인 경우 해석, urlencoded 미들웨어는 폼 요청 해석
- put이나 patch, post 요청 시에 req.body에 프런트에서 온 데이터를 넣어준다.
app.use(express.json());
app.use(express.urlencoded({ extended: false });
- 버퍼 데이터나 text 데이터일 때는 body-parser를 직접 설치해야 함.
콘솔
$ npm i body-parser
const bodyParser = require('body-parser');
app.use(bodyParser.raw());
app.use(bodyParser.text());
- bodyParser 예
app.use(express.json());
app.use(express.urlencoded({ extended : true }));
// 클라이언트에서 form submit 할때_form 파싱 (extended는 쿼리스트링 처리관련. true일 경우 qs / false면 쿼리스트링 모듈 사용)
위 2개를 넣어주면 아래와 같이 사용 가능.
req.body.name
- Multipart 데이터(이미지, 동영상 등)인 경우는 다른 미들웨어를 사용해야 함.
7. static 미들웨어
- 정적인 파일들을 제공하는 미들웨어
- 인수로 정적 파일의 경로를 제공
- 파일이 있을 때 fs.readFile로 직접 읽을 필요 없음.
- 요청하는 파일이 없으면 알아서 next를 호출해 다음 미들웨어로 넘어간다.
- 파일을 발견했다면 다음 미들웨어는 실행되지 않는다.
// app.use('요청 경로', express.static('실제 경로'));
app.use('/', express.static(path.join(__dirname, 'public')));
- 컨텐츠 요청 주소와 실제 컨텐츠의 경로를 다르게 만들 수 있음. 이로 인해 서버의 구조를 파악하기 어려워져서 보안에 도움이 된다.
예_1)
요청경로 : localhost:3000/zerocho.html
실제경로 : learn-express/public-3030/zerocho.html
예_2)
요청경로 : localhost:3000/hello.css
실제경로 : learn-express/public-3030/hello.css
=>
예1, 예2 처럼 사용하면 서버 구조를 예측할 수 없어서 좋음.
- static의 경우 해당하는 파일을 찾은 경우 next를 진행하지 않음. 그래서 미들웨어 간의 순서가 중요함. (위에서 실행될 경우 밑에 미들웨어들이 실행되지 않을 수 있음)
8. express-session 미들웨어
- 세션 관리용 미들웨어
app.use(session({
resave: false,
saveUninitialized: false,
secret:'zeropassword',
cookie: {
httpOnly: true, // 자바스크립트로 공격당하지 않기 위해 항상 설정.
secure: false,
},
name: 'connect.sid', // name의 기본값 : connect.sid(변경가능, 서명되어있어서 읽을수 없는 문자열로 바뀜)
}));
세션 쿠키에 대한 설정(secret: 쿠키 암호화, cookie: 세션 쿠키 옵션)
세션 쿠키는 앞에 s%3A가 붙은 후 암호화되어 프런트에 전송됨.
resave : 요청이 왔을 때 세션에 수정사항이 생기지 않아도 다시 저장할지 여부
saveUninitialized : 세션에 저장할 내역이 없더라도 세션을 저장할지
req.session.save로 수동 저장도 가능하지만 할 일 거의 없음.
- express-session은 요청마다 개인의 저장 공간을 만들어주는 것과 같음.
const session = require('express-session');
const app = express();
app.use(session());
app.get('/', (req, res, next) => {
req.session.id = 'hello'; // 이렇게 할 경우 요청을 보낸 사람의 id만 hello가 됨
res.sendFile(path.join(__dirname, 'index.html'));
})
9. 멀티파트 데이터 형식 (multer 사용하기)
- form 태그의 enctype이 multipart/form-data인 경우
- body-parser로는 요청 본문을 해석할 수 없음
- multer 패키지 필요.
# 콘솔
$ npm i multer
- multipart.html
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
### multer 미들웨어들
- single과 none, array, fields 미들웨어 존재.
- single은 하나의 파일을 업로드 할 때, none은 파일은 업로드하지 않을 때
- req.file 안에 업로드 정보 저장
# upload.single
app.post('/upload', upload.single('image'), (req, res) => {
console.log(req.file, req.body);
res.send('ok');
});
# upload.none
app.post('/upload', upload.none(), (req, res) => {
console.log(req.body);
res.send('ok');
});
- array와 fields는 여러 개의 파일을 업로드 할 때 사용.
- array는 하나의 요청 body 이름 아래 여러 파일이 있는 경우.
- fields는 여러 개의 요청 body 이름 아래 파일이 하나씩 있는 경우.
- 두 경우 모두 업로드된 이미지 정보가 req.files 아래에 존재.
app.post('/upload', upload.array('many'), (req, res) => {
console.log(req.files, req.body);
res.send('ok');
});
10. dotenv 사용
- 아래와 같이 입력하여 dotenv 설치
# 콘솔
$ npm i dotenv
- 별도의 .env 파일을 생성하여 db 패스워드 등 비밀키 저장.
# .env 파일 예시
COOKIE_SECRET=zerochocookiesecret
DB_PASSWORD=nodejsbook
- .env 파일의 경우 드라이브 or 클라우드에 절대 올리면 안됨. 그리고 권한별로 알 수 있는 비밀키를 다르게 해줘야 함. (보안 위험)
11. Router 객체로 라우터 분리하기.
### express.Router
- app.js가 길어지는 것을 막을 수 있다.
- userRouter의 get은 /user 와 / 가 합쳐져서 GET /user/ 가 됨.
# routes/index.js
const express = require('express');
const router = express.Router();
// GET / 라우터
router.get('/', (req, res) => {
res.send('Hello Express');
});
module.exports = router;
# routes/user.js
const express = require('express');
const router = express.Router();
// GET /user 라우터
router.get('/', (req, res) => {
res.send('Hello user');
});
module.exports = router;
# app.js
...
const path = require('path');
dotenv.config();
const indexRouter = require('./routes');
const userRouter = require('./routes/user');
...
app.use('/', indexRouter);
app.use('/user', userRouter);
app.use((req, res, next) => {
res.status(404).send('Not Found');
});
...
### 라우트 매개변수
- :id를 넣으면 req.params.id로 받을 수 있음
- 동적으로 변하는 부분을 라우트 매개변수로 만듦
req.get('/user/:id', function(req, res) {
console.log(req.params, req.query);
});
- 일반 라우터보다 뒤에 위치해야 함
req.get('/user/:id', function(req, res) {
console.log('얘만 실행됨');
});
req.get('/user/like', function(req, res) {
console.log('전혀 실행되지 않음');
});
- /users/123?limit=5&skip=10 주소 요청일 경우 아래와 같이 콘솔 출력
# 콘솔
{ id: '123' } { limit: '5', skip: '10' }
### 404 미들웨어
- 요청과 일치하는 라우터가 없는 경우를 대비해 404 라우터 만들기.
req.get((req, res, next) => {
res.status(404).send('Not Found');
});
- 이게 없으면 단순히 Cannot GET 주소 라는 문자열이 표시됨.
### 라우터 그룹화
- 주소는 같지만 메서드가 다른 코드가 있을 때
req.get('/abc', (req, res) => {
res.send('GET /abc');
});
req.post('/abc', (req, res) => {
res.send('POST /abc');
});
- router.route로 묶음
router.route('/abc')
.get((req, res) => {
res.send('GET /abc');
})
.post((req, res) => {
res.send('POST /abc');
});
### req
- res.app : req 객체를 통해 app 객체에 접근할 수 있다. req.app.get('port')와 같은 식으로 사용 가능.
- req.body : body-parser 미들웨어가 만드는 요청의 본문을 해석한 객체.
- req.cookies : cookie-parser 미들웨어가 만드는 요청의 쿠키를 해석한 객체.
- req.ip : 요청의 ip 주소가 담겨 있다.
- req.params : 라우트 매개변수에 대한 정보가 담긴 객체.
- req.query : 쿼리스트링에 대한 정보가 담긴 객체.
- req.signedCookies : 서명된 쿠키들은 req.cookies 대신 여기에 담긴다.
- req.get(헤더 이름) : 헤더의 값을 가져오고 싶을 때 사용하는 메서드.
### res
- res.app : req.app 처럼 res 객체를 통해 app 객체에 접근할 수 있다.
- res.cookie(키, 값, 옵션) : 쿠키를 설정하는 메서드.
- res.clearCookie(키, 값, 옵션) : 쿠키를 제거하는 메서드.
- res.end() : 데이터 없이 응답을 보냄.
- res.json(JSON) : JSON 형식의 응답을 보냄
- res.redirect(주소) : 리다이렉트할 주소와 함께 응답을 보냄
- res.render(뷰, 데이터) : 템플릿 엔진을 렌더링해서 응답할 때 사용하는 메서드.
- res.send(데이터) : 데이터와 함께 응답을 보냄. 데이터는 문자열 or HTML or 버퍼 or 배열일 수 있음
- res.sendFile(경로) : 경로에 위치한 파일을 응답함
- res.set(헤더, 값) : 응답의 헤더를 설정함
- res.status(코드) : 응답 시의 HTTP 상태 코드를 지정함
### 기타
- 메서드 체이닝 지원
res
.status(201)
.cookie('test', 'test')
.redirect('/admin')
- 응답은 한 번만 보내야 한다.
응답을 여러 번 보내는 경우 아래와 같은 에러 발생 가능.
Can't set headers after they are sent.
12. 템플릿 엔진
- 템플릿 엔진은 HTML의 정적인 단점을 개선.
- 반복문, 조건문, 변수 등을 사용할 수 있음.
- 동적인 페이지 작성 가능
- PHP, JSP와 유사함.
### Pug (구 Jade)
- 문법이 Ruby와 비슷해 코드 양이 많이 줄어듬
- HTML과 많이 달라 호불호가 갈림.
- 익스프레스에 app.set으로 퍼그 연결
...
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(morgan('dev'));
...
### Pug_변수
- res.render에서 두 번째 인수 객체에 Pug 변수를 넣음
router.get('/', function(req, res, next) {
res.render('index', { title : 'Express' });
});
- res.locals 객체에 넣는 것도 가능(미들웨어간 공유됨)
router.get('/', function(req, res, next) {
res.locals.title = 'Express';
res.render('index');
});
- = 이나 #{ } 으로 변수 렌더링 가능 (= 뒤에는 자바스크립트 문법 사용 가능)
- 퍼그 파일 안에서 변수 선언 가능. (- 뒤에 자바스크립트 사용)
- 변수 값을 이스케이프 하지 않을 수 있음 (자동 이스케이프)
- for in 또는 each in으로 반복문 사용 가능 (값과 인덱스 가져올 수 있음)
- if else if else문, case when문 사용 가능
### Pug_include
- 퍼그 파일에 다른 퍼그 파일을 넣을 수 있음
- 헤더, 푸터, 내비게이션 등의 공통 부분을 따로 관리할 수 있어 편리함.
- include로 파일 경로 지정
### Pug_extends와 block
- 레이아웃을 정할 수 있음.
- 공통되는 레이아웃을 따로 관리할 수 있어 좋음. include와도 같이 사용.
13. 넌적스 템플릿 엔진
- Pug의 문법에 적응되지 않는다면 넌적스를 사용하면 좋다.
- 아래와 같이 콘솔에 입력하여 넌적스 설치.
# 콘솔
$ npm i nunjucks
- 확장자는 html 또는 njk(view engine을 njk로)
...
const path = require('path');
const nunjucks = require('nunjucks');
dotenv.config();
const indexRouter = require('./routes');
const userRouter = require('./routes/user');
const app = express();
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'html');
nunjucks.configure('views', {
express: app,
watch: true,
});
app.use(morgan('dev'));
...
### 넌적스_변수
- {{변수}}
<h1>{{title}}</h1>
<p>Welcome to {{title}}</p>
<button class="{{title}}" type="submit">전송</button>
<input placeholder="{{title}} 연습"/>
- 내부 변수 선언 가능 {%set 자바스크립트 구문}
# 변수 예_1
# 넌적스
{% set node = 'Node.js' %}
{% set js = 'Javascript' %}
<p>{{node}}와 {{js}}</p>
# html
<p>Node.js와 Javascript</P>
# 변수 예_2
# 넌적스
<p>{{'<strong>이스케이프</strong>'}}</P>
<p>{{'<strong>이스케이프 하지 않음</strong>' | safe }}</p>
# html
<p><strong%gt;이스케이스</strong%gt;</p>
<p><strong>이스케이프 하지 않음</strong></p>
### 넌적스_반복문
{% %} 안에 for in 작성 (익덱스는 loop 키워드)
# 반복문 예_1
# 넌적스
<ul>
{% set fruits = ['사과', '배', '오렌지', '바나나', '복숭아'] %}
{% for item in fruits %}
<li>{{item}}</li>
{% endfor %}
</ul>
# html
<ul>
<li>사과</li>
<li>배</li>
<li>오렌지</li>
<li>바나나</li>
<li>복숭아</li>
</ul>
# 반복문 예_2
# 넌적스
<ul>
{% set fruits = ['사과', '배', '오렌지', '바나나', '복숭아'] %}
{% for item in fruits %}
<li>{{loop.index}}번째 {{item}}</li>
{% endfor %}
</ul>
# html
<ul>
<li>1번째 사과</li>
<li>2번째 배</li>
<li>3번째 오렌지</li>
<li>4번째 바나나</li>
<li>5번째 복숭아</li>
</ul>
### 넌적스_조건문
- {% if %} 안에 조건문 작성
# 조건문 예_1
# 넌적스
{% if isLoggedIn %}
<div>로그인 되었습니다.</div>
{% else %}
<div>로그인이 필요합니다.</div>
{% endif %}
# html
<!-- isLoggedIn이 true 일 때 -->
<div>로그인 되었습니다.</div>
<!-- isLoggedIn이 false 일 때 -->
<div>로그인이 필요합니다.</div>
# 조건문 예_2
# 넌적스
{% if fruit === 'apple' %}
<p>사과 입니다.</p>
{% elif fruit === 'banana' %}
<p>바나나 입니다.</p>
{% elif fruit === 'orange' %}
<p>오렌지 입니다.</p>
{% else %}
<p>사과도 바나나도 오렌지도 아닙니다.</P>
{% endif %}
# html
<!-- fruite이 apple일 때 -->
<p>사과 입니다.</p>
<!-- fruite이 banana일 때 -->
<p>바나나 입니다.</p>
<!-- fruite이 orange일 때 -->
<p>오렌지 입니다.</p>
<!-- 기본값 -->
<p>사과도 바나나도 오렌지도 아닙니다.</P>
### 넌적스_include
- 파일이 다른 파일을 불러올 수 있음
- include에 파일 경로 넣어줄 수 있음
# 넌적스
## header.html
<header>
<a href="/">Home</a>
<a href="/about">About</a>
</header>
## footer.html
<footer>
<div>푸터 입니다.</div>
</footer>
## main.html
{% include "header.html" %}
<main>
<h1>메인 파일</h1>
<p>다른 파일을 include할 수 있습니다.</p>
</main>
[% include "footer.html" %}
# html
<header>
<a href="/">Home</a>
<a href="/about">About</a>
</header>
<main>
<h1>메인 파일</h1>
<p>다른 파일을 include할 수 있습니다.</p>
</main>
<footer>
<div>푸터 입니다.</div>
</footer>
### 넌적스_레이아웃
- 레이아웃을 정할 수 있음. 공통되는 레이아웃을 따로 관리할 수 있어서 좋다. (include와 같이 사용)
# 넌적스
## layout.html
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<link rel="stylesheet" href="/style.css">
{% block style %}
{% endblock %}
</head>
<body>
<header>헤더입니다.</header>
{% block content %}
{% endblock %}
<footer>푸터입니다.</footer>
{% block script %}
{% endblock %}
</body>
</html>
## body.html
{% extends 'layout.html' %}
{% block content %}
<main>
<p>내용입니다.</p>
</main>
{% endblock %}
{% block script %}
<script src="/main.js"></script>
{% endblock %}
### 에러 처리 미들웨어
- 에러 발생 시 템플릿 엔진과 상관없이 템플릿 엔진 변수를 설정하고 error 템플릿을 렌더링함
- res.locals.변수명으로도 템플릿 엔진 변수 생성 가능
- process.env.NODE_ENV는 개발환경인지, 배포환경인지 구분해주는 속성.
...
app.use((req, res, next) => {
const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
next(error);
});
app.use((req, res, next) => {
res.locals.message = err.message;
res.locals.error = process.env.NODE_ENV !== 'production' ? err : {};
res.status(err.status || 500);
res.render('error');
});
...
반응형
'인프런 강의 학습 > Node.js 교과서' 카테고리의 다른 글
Node.js학습_SNS서비스 만들기(익스프레스 이용) (0) | 2021.10.17 |
---|---|
Node.js학습_몽고디비 (0) | 2021.10.15 |
Node.js 학습_npm(패키지 매니저) (0) | 2021.10.05 |
Node.js 학습_http 모듈로 서버 만들기 (0) | 2021.10.04 |
Node.js 학습_스레드풀과 커스텀 이벤트 / 에러 처리 (0) | 2021.10.04 |