Node.js_Express 웹서버 만들기_2(미들웨어)

Node.js 2021. 7. 3. 13:16

2021.07.02 - [Node.js] - Node.js_Express 웹서버 만들기_1

 

Node.js_Express 웹서버 만들기_1

(본 포스팅은 길벗의 'Node.js 교과서' 책을 공부하면서 작성되었습니다_내돈내산) 1. Express 기본 셋팅 - app.js 파일 생성하기 - npm init 명령어로 package.json 파일 생성하기 - npm i express 로 express 설..

pg-titannia.tistory.com

(본 포스팅은 길벗의 'Node.js 교과서' 책을 공부하면서 작성되었습니다_내돈내산) 

 

5. morgan, bodyParser, cookieParser, static, express-session 미들웨어

- morgan
 >> 접속시 접속정보가 log에 모두 찍히도록 도와준다. 서버로 들어온 요청과 응답을 기록해준다.
 >> 로그의 자세한 정도 선택이 가능함.

npm install morgan --save

 >> 예시) GET / 200 51.267ms - 1539 (순서대로 HTTP요청, 요청주소, 상태코드, 응답속도-응답바이트)
 >> 개발환경에서는 dev, 배포환경에서는 combined를 애용함(IP, 브라우저, 정확한 시간까지 기록)

- cookieParser(cookie-parser)
 >> 요청헤더의 쿠키를 해석해주는 미들웨어
 >> parseCookies 함수와 기능이 비슷함

npm install cookie-parser --save

 >> 실제 쿠키 옵션들을 넣을 수 있음(expire, domain, httpOnly, maxAge, path, secure, sameSite 등
 >> req.cookies 안에 쿠키들이 들어있으며 문자열이 아닌 객체 타입으로 삽입이 가능하다
 >> 미들웨어에 등록할때 인자로 암호를 지정할수 있으며 req.signedCookies 내부에 서명된 쿠키값이 들어가게된다.

res.cookie('name', encodeURIComponent(name), {
  expires: new Date(),
  httpOnly: true,
  path: '/',
});

// 쿠키를 삭제할때는 clearCookie로 expires와 maxAge를 제외한 모든 옵션이 일치해야함
res.clearCookie('name', encodeURIComponent(name), {
  httpOnly: true,
  path: '/',
});

 

- bodyParser(body-parser)
 >> 요청의 본문을 해석해주는 미들웨어, 폼데이터나 AJAX 요청의 데이터 처리
 >> 이전에는 따로 설치해주어야 했지만, 현재는 express프레임워크에 내장되어있어 바로 사용이 가능하다.
 >> jason 미들웨어는 요청본문이 json인 경우 해석, urlencoded 미들웨어는 폼 요청 해석

app.use(express.json());
// extended: true면 qs. false면 queryString 모듈 사용(qs가 더강력하니까 true로 해놓는게 좋음)
app.use(express.urlencoded({ extended: true }));

 >> req.body.___ 로 body에 담겨온 데이터를 바로 읽을 수 있다.

 

- static

 >> 정적인 파일들을 제공하는 미들웨어
 >> 인수로 정적파일의 경로를 제공
 >> 파일이 있을때 fs.readFile로 직접 읽을 필요 없음
 >> 요청하는 파일이 없으면 알아서 next를 호출해 다음 미들웨어로 넘어감
 >> 파일을 발견했다면 다음 미들웨어는 실행되지 않음.
 >> 컨텐츠 요청주소와 실제 컨텐츠의 경로를 다르게 만들 수 있음(보안적인 요소 추가)

app.use('요청 경로', express.static('실제 경로'));
// 실제 복잡한 경로를 다음처럼 요약해서 접근경로를 다르게 만들 수 있음
app.use('/', express.static(path.join(__dirname, 'public')))

 ** 미들웨어의 위치가 매우 중요한데, morgan, cookieparser, bodyparser 같은경우 next( ) 메서드가 내장되어있지만, static 메서드의 경우 정적위치를 찾으면 다음미들웨어로 넘어가지 않는다.  정적파일의 경우 먼저 위에서 실행되면 아래의 다른 미들웨어가 실행될 필요가 없기 때문에 static 미들웨어의 경우는 왠만하면 다른 미들웨어보다 상단에 써준다.

 

- express-session
 >> 세션 관리용 미들웨어
 >> 세션 쿠키에 대한 설정(secret: 쿠키 암호화, cookie: 세션쿠키 옵션)

npm install express-session --save

 >> 세션쿠키는 앞에 s%3A가 붙은 후 암호화되어 프런트에 전송됨.

app.use(session({
  resave: false,
  saveUninitialized: false, 
  secret: precess.env.COOKIE_SECRET, // 쿠키를 암호화(서명 하는) cookie-parser의 서명과 같게 하면좋다
  cookie: {
    httpOnly: true,
    secure: false,
  },
  name: 'session-cookie', // 서명되어있기때문에 읽을 수 없는 문자열로 바뀌어져 있음.
}));

req.session // 요청을 보낸 사용자마다 특정한 session 공간을 만들어 줌.
req.session.name = 'ggaebong'; //  세션 등록
req.sessionID; // 세션아이디 확인
req.session.destroy(); // 세션 모두 제거

 >> resave: 요청이 왔을 때 세션에 수정사항이 생기지 않아도 다시 저장할지의 여부 
 >> saveUninitilized: 세션에 저장할 내역이 없더라도 세션을 저장할지
 >> req.session.save로 수동 저장도 가능하지만 할 일 거의 없음.

- multer

npm install multer --save


 >> 세션 관리용 미들웨어(multer 함수를 호출하면 4가지의 미들웨어가 포함되어 있다)
 >> 세션 쿠키에 대한 설정(secret: 쿠키 암호화, cookie: 세션쿠키 옵션)

const multer = require('multer');
const fs = require('fs');

// multer 미들웨어가 파일을 저장할 경로를 먼저생성
try {
  fs.readdirSync('uploads');
} catch (error) {
  console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
  fs.mkdirSync('uploads');
}

const upload = multer({
  // 파일저장소 옵션. HDD, 메모리, 클라우드 저장소 등 모두 가능
  storage: multer.diskStorage({
  // 파일 저장 위치 설정
    destination(req, file, done) {
      done(null, 'uploads/');
    },
    // 저장될 파일명 설정
    filename(req, file, done) {
      const ext = path.extname(file.originalname);
      // done > 첫번째 인자: 에러처리 내용
      // done > 두번째 인자: 실행내용(여기서는 파일명 지정)
      done(null, path.basename(file.originalname, ext) + Date.now() + ext);
    },
  }),
  // 파일 제한사항
  limits: { fileSize: 5 * 1024 * 1024 },
});

// 라우터에 multer로 만든 내용을 삽입해서 쓸수 있다.
// 특정라우터에서만 업로드가 발생하기 때문에 공통미들웨어로 지정하기 보다는 라우터에 넣는것이 효율적
// upload.single => 하나의 파일만 업로드, 괄호안의 값은 input 태그의 name 속성을 넣어줌
app.post('/upload', upload.single('image'), (req, res) => {
  // 업로드 정보는 req.file 안으로 들어감
  console.log(req.file);
  res.send('ok');
});
// upload.array => body의 하나의 속성안에 여러개의 파일이 있을때
app.post('/upload', upload.array('many'), (req, res) => {
  // 여러개가 넘어오니까 req.files 안에 배열로 들어감
  console.log(req.files);
  res.send('ok');
});
// upload.fields => body의 여러개의 속성안에 각각 이미지가 있을 때
// 각 요소마다 다르게 설정할 수 있다.
app.post('/upload', upload.fields([{name: 'image1'}, {name: 'image2'}]), (req, res) => {
  console.log(req.files.image1); // 속성명으로 정보 조회 가능
  res.send('ok');
});
// upload.none => 파일을 업로드하지 않을 때
// 이미지는 없는데 링크타입은 formData로 보낼경우에 사용
app.post('/upload', upload.none(), (req, res) => {
  req.body.title
  res.send('ok');
});

 

- dotenv

npm install dotenv

 >> .env 파일을 읽어서 process.env 파일로 만듬
 >> 환경변수에 숨겨놓은 여러가지 키, 암호 등을 불러서 쓸 수 있다.(보안적인 설계가 가능해짐)

 >> 프로젝트 폴더에 .env 파일을 생성해준다. .env 파일 내부에 '키 = 값' 형식으로 값을 넣어준다. 쎄미콜론 적지않는다

// 최대한 위쪽에서 불러오는것이 좋다.
const dotenv = require('dotenv');

dotenv.config() // process.env.로 호출해서 부를 수 있다.

>> drive나 cloud에는 .env 파일을 올리지 않는다. 

 

- 미들웨어 간 데이터 전달하기

app.use((req, res, next) => {
  req.data = '데이터 넣기';
  next();
}, (req, res, next) => {
  console.log(req.data); // 데이터 받기
  next();
});

 >> req 나 res객체 안에 값을 넣어 데이터 전달이 가능함.
 >> req, res의 경우 요청 하나동안만 유지 되기때문에 휘발성이 있어 보안적으로 용이하다.
 >> req.session에 넣으면 다음 요청때도 데이터가 남아있다.(session이 만료되거나 삭제되지 않는이상 남아있음)

 

- 미들웨어 확장하기

// 두가지가 같은 역할을 한다
app.use(morgan('dev'));
// 또는
app.use((req,res,next) => {
  morgan('dev')(req,res,next);
})

// 활용방법 - 1
app.use((req,res,next)=>{
  if(process.env.NODE_ENV === 'production') {
    morgan('combined')(req,res,next) // 뒤에 (req,res,next) 만 써주면 된다!
  }else {
    morgan('dev')(req,res,next);
  }
});

// 활용방법 - 2 (로그인 상태일 경우에만 static사용)
app.use((req,res,next)=>{
  if(req.session.id) {
    express.static(path.join(__dirname, 'public')(req,res,next) // 뒤에 (req,res,next) 만 써주면 된다!
  }else {
    next();
  }
});
admin