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창에서 보면 여러개의 객체로 모여있는것을 확인할 수 있다.