개발자공부 (2021.11~현재)/Node.js

nodejs로 mongoDB를 연결하여 member 기능(회원가입, 로그인, 정보수정 및 탈퇴) 구현하기

purplecloud 2022. 4. 26. 20:06

nodejs 설치하면 npm으로 dependencies를 관리할 수 있는데 하기 사용하게 되는 모듈을 터미널에서 npm i ~~ 로 설치해준다.

1. express - router 연결(controller 역할)

2. bodyparser - post방식으로 데이터 전송할때 필요한 모듈

3. mongoClient - mongodb 연결할 모듈

 

1-1. 모듈 객체 선언, 미들웨어 등록

const express= require("express")
const bodyParser= require("body-parser");
const mongoClient= require("mongodb").MongoClient;

const app=express();
const router=express.Router();
const port= 3000;

// 미들웨어 등록
app.use(bodyParser.urlencoded({extended:false}))
app.use("/", router)

// 서버연결
app.listen(port, ()=>{
    console.log(`${port}로 서버 동작중 ...`);
    connectDB();
})

각각 모듈을 변수에 저장하고, express에 bodyparser와 router를 등록한다.

app.listen 은 서버를 동작하는 역할을 하는데, 이때 mongoDB를 연결해주는 함수를 작동시킨다.

 

1-2. mongoDB 연결

let database;   // 몽고디비 연결 객체 전역변수
// 몽고디비 연결 함수
function connectDB(){
    const databaseURL="mongodb://127.0.0.1:27017";
    mongoClient.connect(databaseURL, {useUnifiedTopology: true}, (err, success)=> {
        if(err) console.log(err);
        else{
            // db 명을 넣어서 변수 활용
            database= success.db('frontend')
            console.log("mongodb 연결 성공")
        }
    })
}

mongoDB연결은 위에서 선언한 mongoClient 모듈 객체의 connect 메소드로 연결한다.

에러일 경우 err 객체가 생성되고, 성공하면 sucess 객체가 생성되는데

success 객체의 db메소드로 미리 생성해둔 frontend 데이터베이스를 선택해준다.

 

*** mongoDB 기본용어

✔ Nosql(Not only sql) 로 RDBMS의 한계를 극복하기 위해 만들어진 새로운 형태의 데이터베이스

✔ 관계형 데이터베이스의 테이블 개념이 없으며 스키마가 없음

✔ document -> collection -> database 형태가 됨

document이란?:  record(row)와 유사한 개념이며 데이터 구조가 한개 이상의 key, value 쌍으로 이루어져 있음. 동적인 스키마를 가지고 있음, 즉 같은 collection 안에 있는 document끼리 다른 스키마를 가지고 있을 수 있음.

collection이란?: document의 그룹으로 collection 내부에 document들이 위치함, 테이블과 유사한 개념이지만 RDBMS와 달리 스키마를 따로 가지고 있지 않음.

 

 

2-1. Regist

// 회원가입
// http://127.0.0.1:3000/member/regist (post)
router.route('/member/regist').post((req, res) =>{
    console.log('member/regist 호출')
    const userid=req.body.userid;
    const userpw=req.body.userpw;
    const name=req.body.name;
    const gender=req.body.gender;

    console.log(`userid: ${userid}, userpw: ${userpw}, name: ${name}, gender: ${gender}`)
    //DB가 연결되면,
    if(database){
        joinMember(database, userid, userpw, name, gender, (err, result)=>{
            if(err){
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원가입 실패</h2>');
                res.write('<p>가입 중 오류가 발생했습니다.</p>');
                res.end();
            }else{
                if(result.insertedCount > 0){
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원가입 성공</h2>');
                res.write('<p>가입이 완료되었습니다.</p>');
                res.end();
                }else{
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원가입 실패</h2>');
                res.write('<h2>가입에 실패했습니다.</h2>');
                res.end();
                }
            }
        })
    }else{
        res.writeHead('200', {'content-type':'text/html;charset=utf8'})
        res.write('<h2>데이터베이스 연결 실패</h2>');
        res.write('<p>몽고db에 연결되지 않았습니다!</p>');
        res.end();
    }

})

const joinMember= function(database, userid, userpw, name, gender, callback){
    // database안에 있는 collection을 선택해서 변수로 사용
    const members= database.collection('member');
    // insertMany: 객체를 저장해줄때 씀, 배열형태로 객체를 여러개 전달가능
    members.insertMany([{userid: userid, userpw: userpw, name: name, gender:gender}], (err, result) =>{
        if(err) {
            console.log(err)
            callback(err, null)
            return;
        }else{

            // 만약 result가 생긴다면 insertedCount라는 프로퍼티가 생김
            if(result.insertedCount > 0 ){
                console.log(`사용자 document ${result.insertedCount}명이 추가되었습니다.`)
            }
            else{
                console.log(`사용자 document가 추가되지 않았습니다.`)
            }
            callback(null, result)
        }
    })
}

router는 controller의 역할로 rest방식으로 각각 기능과 연결해준다.

joinMember라는 logic부분과 controller 역할을 하는 regist부분을 나눠서 구성했는데,

joinMember는 regist에서 전달받은 멤버정보를 insertMany 메소드로 저장해주고, (상단에서 선언한 database에서 컬렉션을 지정하여 변수로 사용한다) 저장 시 에러가 발생하면 콜백으로 err객체, null 을 전달하고, 

성공시 null, result객체를 전달한다.

페이지를 따로 구성하지 않았으므로 모든 기능은 postman으로 실행하여 확인할 수 있다.

 

2-2. Login

// 로그인
// http://127.0.0.1:3000/member/login (post)
router.route('/member/login').post((req, res)=>{
    console.log('/member/login 호출')
    const userid= req.body.userid;
    const userpw=req.body.userpw;
    console.log(`userid: ${userid}, userpw: ${userpw}`)
    if(database){
        loginMember(database, userid, userpw, (err, result) =>{
            if(err){
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>로그인 실패</h2>');
                res.write('<p>로그인 중 오류가 발생했습니다.</p>');
                res.end();
            }else{
                if(result){
                    const result_userid = result[0].userid;
                    const result_userpw = result[0].userpw;
                    const result_name = result[0].name;
                    const result_gender = result[0].gender;

                    res.writeHead('200', {'content-type':'text/html; charset=utf8'})
                    res.write('<h2>로그인 성공</h2>')
                    res.write(`<p>아이디: ${result_userid}</p>`)
                    res.write(`<p>비밀번호: ${result_userpw}</p>`)
                    res.write(`<p>이름: ${result_name}</p>`)
                    res.write(`<p>성별: ${result_gender}</p>`)
                    res.end();
                }else{
                    // callback 에서 null, null을 리턴할경우
                    res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                    res.write('<h2>아이디 비밀번호를 확인하세요</h2>');
                    res.end();
                }
            }

        })
    }else{
        res.writeHead('200', {'content-type':'text/html;charset=utf8'})
        res.write('<h2>데이터베이스 연결 실패</h2>');
        res.write('<p>몽고db에 연결되지 않았습니다!</p>');
        res.end();
    }
})

const loginMember = function(database, userid, userpw, callback){
    const members= database.collection('member');
    // select와 같은 역할 find(), 객체로 찾음
    // 찾을때 여러개가 나올수 있음  -> 받을때 toArray, 배열로 받게 되어있음
    members.find({userid:userid, userpw:userpw}).toArray((err, result) =>{
        if(err){
            console.log(err)
            callback(err,null)
            return;
        }else{
            if(result.length> 0){
                console.log('사용자가 있습니다.')
                callback(null, result);
            }else{
                console.log('일치하는 사용자가 없습니다.');
                callback(null, null)
            }
        }
    })
}

로그인도 비슷한 구조로 구성되어있으나 logic 부분에서 찾은 document가 여러개일 때를 생각하여

toArray 배열로 받아준다는 점이 차이가 있다.

 

2-3. Edit

router.route('/member/edit').put((req, res) =>{
    console.log('member/edit 호출')
    const userid=req.body.userid;
    const userpw=req.body.userpw;
    const name=req.body.name;
    const gender=req.body.gender;

    console.log(`userid: ${userid}, userpw: ${userpw}, name: ${name}, gender: ${gender}`)
    //DB가 연결되면,
    if(database){
        editMember(database, userid, userpw, name, gender, (err, result)=>{
            if(err){
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원정보 수정 실패</h2>');
                res.write('<p>수정 중 오류가 발생했습니다.</p>');
                res.end();
            }else{
                if(result.modifiedCount > 0){
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원정보 수정 성공</h2>');
                res.write('<p>수정이 완료되었습니다.</p>');
                res.end();
                }else{
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원정보 수정 실패</h2>');
                res.write('<h2>수정에 실패했습니다.</h2>');
                res.end();
                }
            }
        })
    }else{
        res.writeHead('200', {'content-type':'text/html;charset=utf8'})
        res.write('<h2>데이터베이스 연결 실패</h2>');
        res.write('<p>몽고db에 연결되지 않았습니다!</p>');
        res.end();
    }
})

const editMember = function(database, userid, userpw, name, gender, callback){
    const members = database.collection('member');
    members.updateOne({userid:userid}, {$set:{userid:userid, userpw:userpw, name:name, gender:gender}}, (err, result) => {
        if(err){
            console.log(err);
            callback(err, null);
            return;
        }else{
            if(result.modifiedCount > 0){
                console.log(`사용자 document ${result.modifiedCount}명 수정되었습니다`);
            }else{
                console.log(`수정된 document가 없습니다`);
            }
            callback(null, result);
        }
    });
}

updateOne 이라고하는 메소드는 userid로 해당 document를 찾아준 후 $set이라고 하는 mongoDB문법을 사용하여 바뀔 document를 넣어준다.

 

2-4. Delete

// 회원삭제
// http://127.0.0.1:3000/member/delete (delete)
router.route('/member/delete').delete((req, res) =>{
    const userid= req.body.userid;
    console.log(`userid: ${userid}`);
    if(database){
        deleteMember(database, userid, (err, result)=>{
            if(err){
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원정보 삭제 실패</h2>');
                res.write('<p>삭제 중 오류가 발생했습니다.</p>');
                res.end();
            }else{
                if(result.deletedCount > 0){
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원정보 삭제 성공</h2>');
                res.write('<p>탈퇴가 완료되었습니다.</p>');
                res.end();
                }else{
                res.writeHead('200', {'content-type':'text/html;charset=utf8'})
                res.write('<h2>회원정보 삭제 실패</h2>');
                res.write('<h2>삭제에 실패했습니다.</h2>');
                res.end();
                }
            }
        })
    }else{
        res.writeHead('200', {'content-type':'text/html;charset=utf8'})
        res.write('<h2>데이터베이스 연결 실패</h2>');
        res.write('<p>몽고db에 연결되지 않았습니다!</p>');
        res.end();
    }
})


const deleteMember= function(database, userid, callback){
    const members= database.collection('member');
    members.deleteOne({userid: userid}, (err, result)=>{
        if(err){
            console.log(err);
            callback(err, null);
            return
        }else{
            if(result.deletedCount> 0){
                console.log(`사용자 document ${result.deletedCount}명이 삭제되었습니다.`)
            }else{
                console.log(`삭제된 document가 없습니다.`)
            }
        }
        callback(null, result)
    })
}

delete 는 해당 객체만 넣어서 삭제해주면 된다.

 

 

logic 구현 부분에서 주목해야할 점은 mongoDB는 document을 하나의 객체로 본다는 점이다.

mongoDB의 document는 하나의 객체로 저장되어 이 documents 모여있는 collection은 여러 객체들이 모여있는 json형태와 동일하다.

mongoDB compass로 보면 이런 느낌이 오지 않지만 cmd창에서 보면 여러개의 객체로 모여있는것을 확인할 수 있다.

cmd로 보는 member collcection