Node.js)04.22( ExpressServer ( 기초, 라우터, 미들웨어, 추가모듈, 쿠키, 세션( express-session ), multer ) )
Programming/JS

Node.js)04.22( ExpressServer ( 기초, 라우터, 미들웨어, 추가모듈, 쿠키, 세션( express-session ), multer ) )

반응형

 

 

목차

     

     

     


     

    2022.04.22 - [Programming/BACKEND] - Node.JS)04.21(HttpServer, Axios )

     

    1. ExpressServer

    Express서버의 다른서버와의 특징

    • http 모듈 웹서버의 확장판으로 코드 가독성이 좋고 확장성이 뛰어나다.
    • 프레임이 잡혀있어 파일관리 및 운영이 용이하다.
    • 비슷한 서버로서 Koa, Hapi 드이 있지만 Express서버를 가장 많이 사용한다.
    • 코드관리 및 편의성에서 많은 장점을 제공한다.>
     

    nodemon의 이점

    1. 서버구동 및 운용에 발생한 모든 과정을 로깅으로 보여준다.
    2. 에러 수정이 용이하다.
    3. 일정 시간이 지나거나 주요파일이 저장되면 서버가 다시 재구동되므로, 수동으로 서버 재시작의 불편함이 없다.

     

    package.json

    • Express 서버의 초기 설정 값들을 넣고 조절하는 파일
    • 직접 작성하여 파일을 생성해도 되고, npm init 명령어를 실행하여 생성하는 방법이 있다.

    다음과 같이 폴더구성을 한다.

     

    1.1. 사용될 명령어

    1.1.1. npm init :

    express프로젝트를 구성하는 명령어 (초기화) (필요한 디렉토리들이 생성된다)

    다음과 같은 package.json이 생성된다

     

    1.1.2. npm i express : 

    현재 폴더에 디펜던시를 설치한다.

    package-lock.json : 각 프로그램들의 버전들이 저장됨 (노드 모듈이 삭제되더라도 npm i 명령어로 모듈을 다시 받을 수 있다)

    1.1.3. npm i -D nodemon : 

    Nodemon은 프로젝트 폴더의 파일들을 모니터링 하고 있다가 파일이 수정되면 서버를 자동으로 restart 시켜주는 패키지이다

     

    1.1.4. npm start:

    express서버를 실행시키는 명령어

     


     

    1.2. express서버 구동에 핵심이 되는 파일 app.js 중요 메서드

    • app.set('port', 포트)로 서버가 실행될 포트 지정
    • app.get('키워드', 익명함수)로 GET요청이 올 때 어떤 동작을 할지 지정
    • app/get('포트', 익명함수)로 몇 번 포트에서 서버를 실행할지 지정

     


     

    1.3. express 서버 구동 순서

    1. npm init
    2. npm i express
    3. npm i -D nodemon : 개발환경용이므로 필수 사항은 아니다.
    4. app.js 또는 index.js 또는 main에 지정한 파일 (서버의 시작파일)을 제작
    5. package.json의 scripts에 "start" : "nodemon app"을 추가
    6. npm app 또는 npm run start(npm start)로 서버를 시작한다.

     


     

    1.4. express 서버 실행하기

    const express = require("express");
    
    const app = express();  //서버 객체를 변수에 저장
    
    app.set('port', process.env.PORT || 3000);
    // 서버내에서 port라는 변수를 만들어서 현재 환경의 포트 
    // 또는 포트가 지정되지 않았다면 3000을 저장한다.
    
    app.get('/', (req, res)=>{
        res.send('<h1>Hello, ExpressServer</h1>');
    });
    
    app.get("/login", (req, res)=>{
        // 로그인 관련 페이지 동작
    });
    
    app.listen(app.get('port'), ()=>{ console.log(app.get('port'), '번 포트에서 대기중입니다.');})
    // app.listen(3000, ()=>{ console.log(3000, '번 포트에서 대기중입니다.');})

     


     

    2. Express서버 - html파일 send하기

    • path모듈을 활용하여
      '__dirname''html파일명'의 조합으로 경로가 생성되고 클라이언트에 파일 자체를 전달한다.
    const express = require('express');
    const path = require('path');
    
    const app = express();
    
    app.set('port', process.env.PORT || 3000);
    
    app.get('/', (req, res)=>{
        // __dirname의 내용과 index.html파일명이 조합된 종합경로가 만들어지고 
        // 해당 파일의 내용으로 클라이언트에 응답할 예정.
        // 02_Internal_Module의 path 파일내용을 참고
    
        // 파일을 열어서 내용을 꺼내어 클라이언트에게 보내주는것이 아닌, 파일 자체를 보낸다.
        // 사용되는 메서드 res.sendFile();
        console.log('__dirname의 경로 : ' + path.join(__dirname));  // D:\Java\00_JS\04_Express\npm02
        res.sendFile( path.join(__dirname, '/index.html') );        // 'D:\Java\00_JS\04_Express\npm02' + /index.html
    });
    
    app.get('/users', (req, res)=>{});
    // 이와 같은 함수를 라우터라고 부른다.
    // 하나의 라우터에는 method와 url이 같이 표시되어 해당 내용으로 응답을 보내준다.
    
    app.listen(app.get('port'), ()=>{ console.log( app.get('port'),'번 포트에서 대기중입니다.'); }) ;
    <!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>index.html</title>
    </head>
    <body>
        <h1>익스프레스</h1>
        <h2>함께 배워보자고~😮</h2>
    </body>
    </html>

     


    3. Express서버 - 라우터/미들웨어

    • 라우터 :
      app.get() 또는 app.post 등... 리퀘스트로 키워드를 받아 해당 요청에 응답을 보내주는 메서드들을 '라우터'라고 부른다.
    • 미들웨어 :
      첫 번째 매개변수 전달요소별로 리퀘스트 키워드를 요청받아 익명함수를 실행해서 응답한다.
      그 메서드 안에 들어가는 익명함수들 ( )=>{ }을 '미들웨어'라고 부른다

     

    • app.get('(url)', ... );  -> '라우터'
    • req, res) => {    res.sendFile(path.join(__dirname, '/index.html'));   }  -> '미들웨어'

     

    3.1. 모든 라우터가 실행되기 전 실행되는 라우터

    // 미들웨어만을 위한 멤버함수(라우터)가 존재한다.
    // 1. 모든 라우터들이 실행되기 전 실행되는 라우터 : 보통 다른 라우터들의 위쪽에 기술되어져서 모든 라우터들이 실행되기 전 실행의 대상으로 인식왼다.
    
    app.use( (req, res, next)=>{
        console.log('모든 요청에 실행하고 싶어요');
        next();
        // 모든 라우터에 next가 있지만 사용하지 않아서 생략된 상태.
        // 필요하면 꺼내어 사용할 수 있다.
        // next()가 없으면 현재 라우터에서 요청에 대한 응답이 종료되니, 미들웨어를 위한 라우터는 반드시 next()를 사용한다.
    });
    
    // ----------
    
    app.get('/', (req, res)=>{
        res.sendFile(path.join(__dirname, '/index.html'));
    });
    
    app.get('/abc', (req, res)=>{
        res.sendFile(path.join(__dirname, '/index1.html'));
    });

    어떠한 페이지로 이동하든 '모든 요청에 실행'된다.

     


     

    3.2. 특정 리퀘스트요청 시 같이 실행할 미들웨어

    // 2. 특정 리퀘스트에서만 실행할 미들웨어
    app.use('/about', (req, res, next)=>{
        console.log('about 요청에만 실행되는 미들웨어입니다.');
        next();
    })
    //get과 post등 모든 method에서 리퀘스트 키워드만 같으면 실행된다.
    // 실행 후 next()로 인해 제어권이 아래로 이동하여, 해당 get이나 post등이 추가 실행된다.
    
    // --------------
    
    app.get('/about', (req, res)=>{
        res.send('<h2>Hello About</h2>');
    });

     

    3.3. 미들웨어의 특성 

    • 하나는 미들웨어에서 'res.send()' 또는 'res.sendFile()'등을 두 번이상 쓸 수 없다. 'res.json()'도 예외는 아니다.
    • http서버에서 사용하던 res.writeHeader() + res.end()가 합쳐져서 res.send()가 된 것이므로 send()는 두 번이상 쓰는 것은 의도치 않은 에러를 발생한다.
    • res.json()또한 res.writeHeader(200, {'Content-Type' : application/json'});와 res.end(FJSON.stringify({hello:'hong'}));
      위 둘이 합쳐져서 res.json({hello:'hong'});로 사용되는 것이므로 , 두 번이상 사용되지 않는다.

     

     


     

    4. Express서버 - 에러처리

    4.1. 에러 발생

    app.use((req, res, next)=>{
        throw new Error("서버 - 에러를 발생시켜주마~~");
        
        ...
        
     	next();
    });
    • '에러를 처리하는 라우터가 존재하지 않아' 에러내역이 브라우저에 모두 표시되어 서버구조가 노출되어버린다. (500에러)
    • 에러내역은 서버의 콘솔에만 나오고 브라우저에는 에러처리에 의한 내용만 나오도록 "에러처리 라우터"를 마지막에 추가해준다.

    서버 구조가 모두 노출되어 버린다.


    4.2. 에러처리 라우터

    • 위 라우터 또는 미들웨어에서 '에러가 발생했을 때 실행되는 미들웨어'.
    • 에러처리 라우터에 있는 미들웨어는 반드시 매개변수가 err, req, res, next 네 개가 쓰여져야 에러처리로 인식된다.
      넷 중에 하나라도 없으면 에러처리 라우터로 인식되지 못한다.
    //4.2. 에러처리 라우터 --------------------
    // 위 라우터 또는 미들웨어에서 에러가 발생했을 때 실행되는 미들웨어.
    // 에러처리 라우터에 있는 미들웨어는 반드시 매개변수가 err, req, res, next 네개가 쓰여져야 에러처리로 인식된다.
    // 넷 중에 하나만 빠져도 에러처리 라우터로 인식되지 못한다.
    
    app.use((err, req, res, next)=>{
        console.error(err);
        res.status(200).send('에러내용을 브라우저에 알려주지 않을래');
    });

    브라우저 대신 콘솔에 에러가 표시.

     


    5. 404 에러처리

    // 6. 404에러 처리
    app.use((req,res,next)=>{
        res.send('비정상적 접근~ 에러입니다~');
        // 400, 500번대 에러가 노출될 경우 보안에 취약하기 때문에 브라우저에 직접적으로 표시되지 않는편이 좋다.
        // res.status(404).send('404 에러임!');	
    });

     

     


    6. 리퀘스트 키워드의 '와일드카드 문자'

    • 해당 라우터가 존재하지 않을 경우 받아주는 와일드카드 문자 키워드가 존재한다.
    • 이것은 404 에러를 방지하여 조금 더 ux친화적인 방법으로 사용될 수 있다.
    • 가능한 다양한 라우터의 아래쪽에 위치시켜 명확한 라우터가 우선 실행되도록 한다.
    // 7. 리퀘스트 키워드의 와일드카드 문자.
    
    // 부츠 카테고리 라우터
    app.get('/category/Boots', (req, res)=>{
        res.send('<h2>Hello Boots</h2>');
    });
    
    // 힐 카테고리 라우터
    app.get('/category/Heel', (req, res)=>{
        res.send('<h2>Hello Heel</h2>');
    });
    
    // 와일드카드 라우터
    // 와일드카드 키워드를 사용한 라우터는 범위가 넓으므로 가능한 아래쪽에 위치시켜서, 명확한 구분은 먼저 실행되게 하고,
    // 해당 라우터가 없을 때 실행되게 하는것이 효과적이다.
    app.get('/category/:name', (req, res)=>{
        res.send(`<h2>Hello wild Card Char ${req.params.name}</h2>`);
    })

    존재하는 라우터가 실행되었을 때

     

    slipers 라우터는 존재하지 않아 와일드카드 라우터가 실행되었다.

     


     

    7. 추가 모듈 설치

    7.1. 추가적으로 설치할 모듈을 require한다.

    // 추가 모듈 설치 ------------------
    // 각각의 요청과 응답에 대한 필요정보를 보기 위한모듈
    const morgan = require('morgan');
    
    //쿠키사용을 보다 간결하게 사용하기 위한 모듈
    const cookieParser = require('cookie-parser');
    
    // 세션사용을 보다 간결하게 사용하기 위한 모듈
    const session = require('express-session');
    
    // 요청의 본문을 해석, 구분하는 모듈
    const bodyParser = require('body-parser');

     

    7.2. cmd에 npm i 모듈명1 모듈명2 ... 방식으로 디펜던시?를 받아온다.

     

    7.3. 추가한 모듈과 관련된 미들웨어 설정을 진행한다.

    // 공통 미들웨어 설정 ------------
    
    app.use(morgan('dev'));
    // 실행결과 : GET / 200 5.316 ms - 165
    // method방식, 응답결과코드, 요청과실행에 걸린 시간 등등
    // app.use(morgan('combined')); 더 자세한 내용을 볼 수도 있다.
    
    app.use(cookieParser());
    app.use(express.json());    //바디파서 json : json 사용을 위한 모듈
    app.use(express.urlencoded({extended:true}));   //바디파서 폼데이터 모듈
    //app.use(body-Parser.json());
    //app.use(body-Parser.urlencoded({extend : false}));
    
    //세션 활용을 위한 미들웨어
    app.use(session({
        resave : false,
        saveUninitialized : false,
        secret : 'keonhee',
    }));    
    
    // ---------------------------------------------------

     


     

    8 . Express 서버와 쿠키

    8.1. Express서버에서 쿠키 저장 / 삭제

    app.get('/', (req, res)=>{
    
        // ** express 서버에서 쿠키를 읽어오거나 저장하는 방식 **
        
        // 1. 저장된 쿠키를 불러와서 활용할 변수 ' req.cookies '
        console.log(req.cookies);
        
        // 2. 새로운 쿠키 저장
        const name = 'HongGillDong';
        res.cookies('name', encodeURIComponent(name),{
            expires:new Date(),
            httpOnly : true,
            path:'/'
        });
        
        // 3. 쿠키 삭제
        res.clearCookie('name', encodeURIComponent(name),{
            httpOnly:true,
            path:'/'
        });
    });

     


     

    8.2. Express서버의 쿠키 생성/활용 ( 로그인 )

    app.get('/', (req, res)=>{
        // id라는 이름의 쿠키가 있으면 ' 000님 반갑습니다 ' 를 send
        if(req.cookies.id){
            res.send(`${req.cookies.id}님 안녕하세요` + '<br><a href="/logout">로그아웃</a>');
        }else{  // id 쿠키가 없으면 index.html을  send
            res.sendFile(path.join(__dirname, '/index.html'));
        }
    });
    
    
    app.post('/login',(req,res)=>{
        // post이기 때문에 localhost:3000/login 까지만 보여진다.
    
        // ** http 서버에서는 다음과 같이 전달 파라미터를 분해한다 **
        // const { query } = url.parse(req.url);
        // const { name } = qs.parse(query);
    
        // ** express 서버에서 전달 파라미터를 활용하는 과정 ** 
        //console.log(req.body.name);
        const name = req.body.name;
    
        const expires = new Date();
        expires.setMinutes(expires.getMinutes() + 1);
        
        // 파라미터 내용을 쿠키로 추가
        res.cookie( 'id', name, {
            expires : expires,
            httpOnly : true,
            path:'/'
        }); 
    
        res.redirect('/'); // redirect('/'); 특정 리퀘스트로 이동한다.
    });
    <!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>index.html</title>
    </head>
    <body>
        <h1>익스프레스 : 라우터</h1>
        <h2>배워보자고</h2>
        <form action="/login" method="post">
            <input id="name" name="name" placeholder="이름을 입력하세요"/>
            <button id="login">로그인</button>
        </form>
    </body>
    </html>

    id 쿠키가 정상적으로 생성되었다.

     


     

     

    8.3. Express서버의 쿠키 삭제 ( 로그아웃 )

    // id 쿠키를 지우고, '/'로 리다이렉트 하는 라우터를 만든다.
    app.get('/logout',(req, res)=>{
    
        res.clearCookie('id', req.cookies.name, {
            httpOnly:true,
            path:'/'
        });
        
        res.redirect('/');
    });

    로그아웃을 하고 사라진 쿠키

     


     

    8.4. 예제1

    const express = require('express');
    const path = require('path');
    const cookieParser = require('cookie-parser');
    const morgan = require('morgan');
    
    const app = express();
    
    // 추가모듈 설정
    app.use(morgan('dev'));
    app.use(cookieParser());
    app.use(express.json());
    app.use(express.urlencoded({extended:true}));
    
    // 포트설정
    app.set('port', process.env.PORT || 3000);
    
    // 라우팅
    // 루트 라우터
    app.get('/', (req, res)=>{
    
        if(req.cookies.id){
            res.send(`${req.cookies.id} 님 반갑습니다.` + '<a href="/logout">로그아웃</a>');
        } else {
            res.sendFile(path.join(__dirname, '/index.html'));
        }
    });
    
    // 로그인 라우터
    app.post('/login',(req,res)=>{
        const id = req.body.id;
        const pw = req.body.pw;
    
        const expires = new Date();
        expires.setMinutes(expires.getMinutes() + 1);
    
        if( id=='scott' && pw == 'tiger'){
            res.cookie('id', id, {
                expires : expires,
                httpOnly : true,
                path : '/'
            });
            return res.json( {msg : 'ok'} );  //json 데이터를 갖고 호출위치로 돌아간다.
    
        }else if(id!='scott'){
            return res.json({msg:'없는 아이디입니다.'});
    
        }else if(pw!='tiger'){
            return res.json({msg:'비밀번호 오류입니다.'});
    
        }else {
            return res.json({msg:'알수없는 오류'});
        }
    });
    
    // 로그아웃 라우터
    app.get('/logout', (req,res)=>{
        res.clearCookie('id', req.cookies.id,{
            httpOnly : true,
            path : '/'
        });
        res.redirect('/');
    })
    
    
    app.listen(app.get('port'), ()=>{
        console.log(app.get('port'), '번 포트에서 대기 중');
    });
    <!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>index.html</title>
        <!-- axios -->
        <script src="https://unpkg.com/axios@0.25.0/dist/axios.min.js"></script>
    </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 type="text/javascript">
            document.getElementById('form').addEventListener('submit', async (e)=>{
    
                e.preventDefault(); // 이벤트 중단(submit)
                
                // 이벤트가 발생한 요소 값 저장 (target속성)
                const id = e.target.id.value;
                const pw = e.target.pw.value;
    
                if(id == ''){
                    alert('아이디를 입력하세요');
                    return;
                }else if(pw == ''){
                    alert("비밀번호를 입력하세요");
                    return;
                }
    
                try{
                    // axios로 '/login' 라우터에서 가져온 값을 res에 담기
                    const res = await axios.post('/login', {id, pw});
    
                    // 로그인이 잘 되었다면?
                    if(res.data.msg == 'ok') location.href='/';
                    document.getElementById('message').innerHTML = res.data.msg;
    
                }catch(err){
                    console.error(err);
                }
                
                e.target.id.value='';
                e.target.pw.value='';
            });
        </script>
    </body>
    </html>

     


     

    9. Express와 session

    • 'express-session' 모듈을 사용한다.

     

    9.1. 세션의 저장/삭제

    ...
    
    // 추가모듈 require
    const session = require('express-session');
    
    // express-session 모듈 설정
    app.use(session({
        resave:false,
        saveUninitialized:false,
        secret:"keon",  // 세션값의 암호화 코드
    }));
    
    ...
    
    // 세션의 저장
    req.session.id = 'hello';
    req.session.data = 'asdfadf';
    
    // 다른 미들웨어에서 req.session.data 라는 이름으로 사용가능 (영구적인 저장)

     


     

    9.2. 예제1

    const express = require('express');
    
    // 추가모듈 require
    const path = require('path');
    const morgan = require('morgan');
    const cookieParser = require('cookie-parser');
    const session = require('express-session');
    
    const app = express();
    app.set('port', process.env.PORT || 3000);
    
    // 추가모듈 설정
    app.use(morgan('dev'));
    app.use(cookieParser());
    app.use(express.json());
    app.use(express.urlencoded({extended:true}));
    
    // express-session 모듈 설정
    app.use(session({
        resave:false,
        saveUninitialized:false,
        secret:"keon",  // 세션값의 암호화 코드
    }));
    
    // 세션의 저장
    // req.session.id = 'hello';
    // req.session.data = 'asdfadf';
    // 다른 미들웨어에서 req.session.data 라는 이름으로 사용가능 (영구적인 저장)
    
    
    // 라우팅
    app.get('/', (req, res)=>{
        if(req.session.userid){
            res.send(`${req.session.userid}님 반갑습니다.` + '<a href="/logout">로그아웃</a>');
        }else{
            res.sendFile(path.join(__dirname,'/index.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.json({msg:'ok'});
        }else if(id!='scott'){
            return res.json({msg:'없는 아이디'});
        }else if(pw!='tiger'){
            return res.json({msg:'비번 오류'});
        }else {
            return res.json({msg:'알수없느 ㄴ오류'});
        }
    });
    
    app.get('/logout', (req,res)=>{
        // 세션 삭제
        req.session.destroy(function(){
            req.session;
        });
        res.redirect('/');
    });
    
    
    app.listen(app.get('port'), ()=>{
        console.log(app.get('port'),'번 포트에서 대기 중');
    });
    <!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>index.html</title>
        <!-- axios -->
        <script src="https://unpkg.com/axios@0.25.0/dist/axios.min.js"></script>
    </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 type="text/javascript">
            document.getElementById('form').addEventListener('submit', async (e)=>{
    
                e.preventDefault(); // 이벤트 중단(submit)
                
                // 이벤트가 발생한 요소 값 저장 (target속성)
                const id = e.target.id.value;
                const pw = e.target.pw.value;
    
                if(id == ''){
                    alert('아이디를 입력하세요');
                    return;
                }else if(pw == ''){
                    alert("비밀번호를 입력하세요");
                    return;
                }
    
                try{
                    // axios로 '/login' 라우터에서 가져온 값을 res에 담기
                    const res = await axios.post('/login', {id, pw});
    
                    // 로그인이 잘 되었다면?
                    if(res.data.msg == 'ok') location.href='/';
                    document.getElementById('message').innerHTML = res.data.msg;
    
                }catch(err){
                    console.error(err);
                }
                
                e.target.id.value='';
                e.target.pw.value='';
            });
        </script>
    </body>
    </html>

     


     

    10. multer 모듈 ( 파일 업로드 )

    'multer' 모듈을 사용하여 파일 업로드를 구현한다.

     

    10.1. 업로드된 파일 저장 디렉토리 생성

    • 업로드를 하려면 업로드된 파일이 저장될 폴더를 지정해야한다.
    • 지난 프로젝트처럼 폴더를 직접 만들지는 않고, 'fs모듈을 활용'하여 이용하려는 폴더가 있으면 그 폴더를 사용하며,없으면 새로 생성하는 기능을 사용한다. ( readdirSync( ), mkdirSync( ) 활용 )
    • 파일 폴더와 같은 외부의 리소스를 다루는 작업은 명령 오류와 상관없이 디스크 상태에 따라 오류가 발생할 수 있으며, 스스로 예외처리를 해준다.
    • 특히 지금은 'readdirSync( )'가 실행될 때 해당 폴더가 없다면 에러가 발생하므로 그에 대한 처리로 예외처리를 이용한다.
    try{
        fs.readdirSync('uploads');
    }catch(err){
        console.error('uploads폴더가 없어 새로 생성합니다.');
        fs.mkdirSync('uploads');
    }

     


     

    10.2. multer객체 생성 / 설정

    • 현재 프로젝트에서 사용할 multer 객체를 생성한다. 객체이름은 upload
    • multer함수에 전달인수로 객체 하나를 전달하는데 그 객체에는 storage와 limits라는 속성이 포함된다.
    // 현재 프로젝트에서 사용할 multer 객체를 생성한다. 객체이름은 upload
    // multer함수에 전달인수로 객체 하나를 전달하는데 그 객체에는 storage와 limits라는 속성이 포함된다.
    
    const upload = multer({
        // storage : multer.diskStorage()로 저장위치(destination), 파일명(filename) 옵션을 설정.
        storage:multer.diskStorage( { destination(){} , filename(){} } ), 
        limits : {},	// 파일 용량제한
    });

     


     

    10.3. 예제

    const express = require('express');
    
    // 추가모듈 require 
    const path=require('path');
    const fs = require('fs');
    const multer = require('multer');
    
    const app = express();
    app.set('port', process.env.PORT || 3000);
    
    
    app.use(express.json());
    app.use(express.urlencoded({extended:false}));
    
    
    // static( 정적 )폴더 지정
    app.use('/', express.static(path.join(__dirname, 'uploads')));
    
    
    // 업로드 폴더 생성
    try{
        fs.readdirSync('uploads');
    }catch(err){
        console.error('uploads폴더가 없어 새로 생성합니다.');
        fs.mkdirSync('uploads');
    }
    
    
    // 현재 프로젝트에서 사용할 multer 객체를 생성한다. 객체이름은 upload
    const upload = multer({
        storage:multer.diskStorage(
            {
                destination(req, file, done){
                    done(null, 'uploads/'); //폴더 설정
                    // 첫번째 인수 null은 현재파일(file)의 경로와 이름 그대로 사용.
                    // (변경 및 추가 없음)
                },
                filename(req, file, done){
                    const ext = path.extname(file.originalname);    //확장자 추출
                    //확장자를 뺀 파일이름 + 오늘 날짜(밀리초) + 추출된 확장자로 저장 파일명 변경
                    done(null, path.basename(file.originalname,ext)+Date.now()+ext);
                    // abc.jpg -> 'abc' + 12345214 + '.jpg' -> abc12345214.jpg
                    // 업로드 파일명이 같은 경우 cos처럼 처리할 객체가 없고, 위와같은 방법으로 파일명의 충돌을 방지한다(오늘날짜시간의 밀리초 값)
                },
            }
        ),
        limits : {
            fileSize: 5* 1024 * 1024
        },
    });
    
    
    // 라우팅
    app.get('/', (req, res)=>{
        res.sendFile( path.join(__dirname, 'multer.html'));
    
    });
    
    app.post('/upload', upload.single('image'), (req,res)=>{
        console.log( req.file );
        console.log( req.body.title );
        return res.json({
            title:req.body.title,
            filename : req.file.filename,
        })
    });
    
    
    app.listen(app.get('port'), ()=>{
        console.log(app.get('port'), '번 포트에서 대기 중');
    });
    <!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>multer.html</title>
    </head>
    <body>
        <form id="form" method="post" enctype="multipart/form-data">
            <input type="file" name="image"/>
            <input type="text" name="title"/>
            <button type="submit">업로드</button>
            <div id="message"></div>
            <div id="filename"></div>
            <div id="img"></div>
        </form>
    
        <!-- axios -->
        <script src = "https://unpkg.com/axios/dist/axios.min.js"></script>
    
        <script type="text/javascript">
            document.getElementById('form').addEventListener('submit', async (e)=>{
                e.preventDefault(); // 이벤트 중지( submit )
    
                const formData = new FormData();    // 새로운 FormData 객체 생성
    
                // 생성된 FormData 객체에 업로드 될 image과 title을 담는다.
                formData.append('image', e.target.image.files[0]); 
                formData.append('title', e.target.title.value);
    
                try{
                    const res = await axios.post('/upload', formData);  // 완성된 formData객체를 /upload request로 전송
    
                    // 현재 페이지 요소에 전달받은 데이터로 표시한다.
                    document.getElementById("message").innerHTML = "제목: " + res.data.title;
                    document.getElementById("filename").innerHTML = "업로드된 파일의 저장 이름 : " + res.data.filename;
                    document.getElementById("filename").innerHTML = '<img src="' + res.data.filename + '" width=200>';
                    
                }catch(err){
                    console.error(err);
                }
                e.target.image.value='';
                e.target.title.value='';
            });
        </script>
    
    </body>
    </html>

     

    반응형