
http는 웹 서버를 만들 때 코드가 보기 좋지 않고, 확장성도 떨어짐
-> 프레임워크로 해결
ex) Express, Koa(코아), Hapi(하피)
직접 생성 or npm init 명령어 사용
app.js : 서버 구동의 핵심
express 모듈 내에서 http 서버 사용 중 -> 내부적으로 http 서버 사용
서버 실행
1. node app 명령어 사용
2. localhost:3000 접속
get, post, 주소 각각 작성해서 if문이 없더라도 app에 메서드(get, post)를 붙여주는 방식으로 구분
app.set('port', process.env.PORT || 3000);
node 대신 nodemon으로 서버를 실행할 수 있다.
=> 코드를 수정할 때마다 수동으로 on/off 하지 않아도 됨(html파일 수정은 제외)
nodemon app

모든 app 요청에 다 실행하고 싶은 코드 있음 -> 다 쓰면 중복
app.use((req, res, next) => {
console.log('모든 요청에 실행하고 싶어요');
next();
});
app.use : 미들웨어
인수에 next를 추가한 다음 next()를 실행 -> 다음 요청으로 넘어갈 수 있음
라우터를 많이 생성해줘야 하는 경우 name을 입력받아서 내용에 반영할 수도 있다.
app.get('/category/:name', (req, res) => {
res.send('hello ${req.params.name}')
});

=> 때문에 와일드카드 코드는 다른 코드보다 밑에 작성해주어야 함
'*'도 비슷
때문에 이 코드는 다른 get요청 코드보다 밑에 작성되어야 함

use안에 '/about'을 작성해주면 된다
=> next()에서 /about을 찾아감
use안에 미들웨어를 여러 개 동시에 넣어줄 수도 있다.
에러 미들웨어는 매개변수에 err, req, res, next 모두 포함되어 있어야 함
코드 작성 순서 : app.set -> 공통 미들웨어(app.use) -> 라우터(app.get, app.post) -> 에러 미들웨어
없는 라우터에 요청 전송 -> 404 대신 에러 처리 미들웨어가 실행됨
404 커스터마이징 - 라우터랑 에러 미들웨어 코드 사이에 위치
(맞는 라우터 찾지 못함 -> 에러)
app.use 사용해서

app.use로 미들웨어를 사용한 경우도 불가

또한 writeHead를 사용한 경우에도 에러 처리가 된다(이미 send한 상태이기 때문)

Tip
json 여러개, json밑에 있는 console.log
=> 모두 실행됨(json은 응답을 보내기만 할 뿐 return되지는 않음)
![]() | ![]() |
|---|
Tip
res.json은 writeHead와 end의 기능을 같이 갖고 있다.
Tip
res.render도 res.send/res.sendFile처럼 응답을 보내는 것
catch 내의 error에 인수 들어감 -> next를 통해 error 전달
한참 밑에 있는 에러 처리 미들웨어로 error 넘어감
활용하는 경우 -> if문 사용

morgan, cookieParser 설치
npm i morgan cookie-parser express-session
bodyParser는 요새 잘 안씀
: 콘솔창에 요청과 응답에 대한 내용을 기록하는 라우터
app.use(morgan('dev'));
: 쿠키를 파싱하는 기능
app.use(cookieParser());
res.cookies // { mycookie : 'test' }
res.cookie('name', encodeURIComponent(name), {
expires : new Date(),
httpOnly: true,
path: '/',
})
res.clearCookie('name', encodeURIComponent(name), {
httpOnly: true,
path: '/',
})
서명(암호) 사용한 쿠키 : signedCookies 사용
app.use(cookieParser('password'));
req.signedCookies
: 요청의 본문에 있는 데이터를 해석해서 req, body 객체로 만들어주는 미들웨어
최근 body-parser 는 express 안에 내장됨
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// urlencoded : form 파싱
// extended : true면 qs, false면 querystring -> qs가 더 좋음
app.get('/', (req, res, next) => {
req.body
res.sendFile(path.join(__dirname, 'index.html'));
});
express에 들어있지 않은 bodyparser : raw(), text()
(잘 안씀)
form에서 이미지, 영상 파일 등을 처리 못함 -> multer 모듈 사용 x
: 정적인 파일들이 제공하는 라우터 역할
app.use('요청 경로', express.static('실제 경로'));
app.use('/', express.static(path.join(__dirname, 'public'))); //public 폴더 지정
미들웨어도 순서 중요
morgan 미들웨어
-> static 미들웨어 => 파일search(파일 찾으면 여기서 끝)
-> (파일 못찾으면) 그 밑의 쿠키 파싱, 바디 파싱 등이 실행
static이 한참 밑에 있으면 쓰이지도 않을 cookie, body를 파싱하느라 비효율적으로 동작
_but 때로는 로그인이 필요할 시 등의 경우에 쿠키, 세션 미들웨어를 정적 파일보다 위에 놓을 때가 있음(원하는 대로 조정)
const session = require('express-session');
...
app.use(session())
...
app.get('/', (req, res, next) => {
req.session.id = 'hello';
res.sendFile( ... );
app.use로 session을 만들어 준 뒤 req.session.id로 id를 지정해줌
=> 해당 사용자의 id만 'hello'로 지정됨
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
name: 'session-cookie',
}));
let hello;
app.use((req, res, next) => {
hello = '계좌비번'
});
app.get('/', (req, res, next) => {
console.log(hello);
}
app.use((req, res, next) => {
req.session.data = '계좌비번'
});
app.get('/', (req, res, next) => {
req.session.data // 계좌비번
}
app.use((req, res, next) => {
req.data = '계좌비번'
});
app.get('/', (req, res, next) => {
req.data // 계좌비번
}
app.use('/', (req, res, next) => {
if(req.session.id) { // 로그인함
express.static(__dirname, 'public')(req, res, next)
} else { // 로그인 안함 -> next()로 넘어감
next();
}
});
이 방식은 로그인 한 사람들에게만 static이 실행
form 안에 "multipart/form-data"가 있는 경우
-> 이미지나 영상 포함'
=> 요청 본문 해석 위해 multer 패키지 사용
const upload = multer({
storage: multer.diskStorage({
destination(req, file, done) {
done(null, 'uploads/');
},
filename(req, file, done) {
const ext = path.extname(file.originalname);
done(null, path.basename(file.originalname, ext) + Date.now() + ext);
},
}),
limits: { fileSize: 5 * 1024 * 1024 },
});
app.get('/upload', (req, res) => {
res.sendFile(path.join(__dirname, 'multipart.html'));
});
app.post('/upload', upload.single('image'), (req, res) => {
console.log(req.file);
res.send('ok');
});
여러 파일을 업로드하는 경우 HTML 파일에서 multiple을 쓰면 된다.

app.post에는 single아 아닌 array 사용
app.post('/upload', upload.array('image'), (req, res) => {
console.log(req.files);
res.send('ok');
});
multiple이 아니라 파일 이름을 다르게 해서 여러 개 전송하는 경우
[HTML]

[app.js]
app.post에는 fields 사용
app.post('/upload', upload.fields([{ name : 'image1'}, { name : 'image2'}]), (req, res) => {
console.log(req.files.image1);
console.log(req.files.image2);
res.send('ok');
});
마지막으로 image 없이 none을 사용하는 경우(이미지 없이 form일때)
app.post('/upload', upload.none(), (req, res) => {
console.log(req.body);
res.send('ok');
});
: 비밀 키 등의 환경 변수를 관리할 수 있음(미들웨어는 아님)
쿠키 키를 안전하게 관리할 때 -> env 사용
process.env.COOKIE_SECRET
이러고 .env 폴더를 생성해 줘야 한다.
그리고 app에 최대한 위에 dotenv를 불러와준다.
만약 require한 패키지 중 .env를 사용해야 하는 패키지가 있으면 dotenv를 require보다 아래에 작성해 주어야 한다.
.env도 해킹당할 위기가 있기 때문에 비밀 키도 여러 개를 만들어 직급별로 권한을 다르게 부여하는 방식으로 지킬 수 있다.
const router = express.Router();
=> app.js 코드가 길어지는 것 방지
app.js에 다음과 같이 작성
//...
dotenv.config();
const indexRouter = require('./routes');
const userRouter = require('./routes/user');
const app = express();
app.set('port', process.env.PORT || 3000);
app.use(morgan('dev'));
// ...
app.use('/', indexRouter);
app.use('/user', userRouter);
// ...
동적으로 변하는 부분을 받을 수 있음(일반 라우터보다 뒤에 위치)
router.get('/user/:id', function(req, res) {
console.log(req.params, req.query)
})'
이 코드에 주소 요청은 다음과 같은 식으로 할 수 있다.
/users/123?limit=5&skip=10
=> id = 123
모든 라우터에서 걸리는 것 없을 때 404 에러가 발생함
app.use((req, res, next) => {
res.status(404).send('Not Found')'
});
모든 라우터 뒤에 만들어야 함
주소는 같지만 메서드가 다르면 router.route로 묶을 수 있다.
router.get('/abc', ... );
router.post('/abc', ... );
router.route('abc')
.get(...)
.post(...)


end부터 sendFile은 한 번씩만 써야함 -> 그렇다고 return 역할을 하는것도 아님
메서드 체이닝 지원
res .status(201) .cookie('test', 'test') .redirect('/admin');
템플릿 엔진
=> HTML의 정적인 단점을 개선
- 반복문, 조건문, 변수 등을 사용할 수 있음
- 동적인 페이지 작성 가능
- PHP, JSP와 유사
보통은 app.set과 app.use 사이에 코드 작성


router.get('/', (req, res, next) => {
res.render('index', { title: 'Express' }) // 여기서 title 변수는 Express가 됨
});
router.get('/', (req, res, next) => {
res.locals.title = 'Express'; // 여기서 title 변수는 Express가 됨
res.render('index');
});
이 경우에는 res.locals객체에 다른 미들웨어에서도 접근 가능함

퍼그 파일 안에서 변수 선언 가능하다.
'-'뒤에 자바스크립트 사용

자동 이스케이프(HTML로 렌더링되는 것을 막는 것)

for in 이나 each in으로 반복문 돌릴 수 있음

값과 인덱스 가져올 수 있음


퍼그 파일에 다른 퍼그 파일

레이아웃을 정할 수 있음(include와도 같이 사용)

변수는 중괄호 2개를 사용해서 {{title}} 방식으로 넣을 수 있다.

에러 발생 시 템플릿 엔진과 상관없이 템플릿 엔진 변수 설정하고 error 템플릿을 렌더링
app.use((req, res, next) => {
const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
next(error);
}
이 코드에서 404에러 발생 시 에러 처리 미들웨어에 err로 전달된다.
app.use((err, 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');
});