본문 바로가기
Programming/Javascript

Vue todolist 만들기 - 7. Express로 Backend 구성하기

by Wilkyway 2023. 5. 5.
반응형

이번에는 Express.js를 이용해서 Backend를 구성해보도록 하겠습니다. Backend 서버는 Express지만,
그 외에도 추가로 MongoDB에 적당한 클러스터 생성이 필요합니다. 그리고 Kakao 로그인 연동을 위해 Kakao Developers
사이트에서 앱 생성이 필요합니다. 이부분은 많은 지문을 필요로 하는 부분이라 여기서 다루지는 않겠습니다.
 
1. 환경파일(.env) 생성
Backend 프로젝트 폴더에 .env 파일을 생성해서 비밀 환경정보 등을 저장합니다.

PORT=3000
NODE_ENV='development'
MONGO_URI='mongodb+srv://몽고디비ID:몽고디비PW@cluster0.juisc.mongodb.net/test'
KAKAO_ID=00sdfsdf6432sdfa792sdfssdfsdf28이거내가고친거임
KAKAO_REDIRECT_URI=http://localhost:3000/auth/kakao/callback
SECRET=mysecretkeytest

 
2. 패키지 설치
필요한 패키지들을 설치합니다.  설치할 땐 "npm i 패키지이름", 개발환경 의존성은 "npm i -D 패키지이름"

"dependencies": {
    "body-parser": "^1.20.2",
    "cookie-parser": "^1.4.6",
    "cors": "^2.8.5",
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "express-session": "^1.17.3",
    "mongoose": "^7.0.4",
    "passport": "^0.6.0",
    "passport-local": "^1.0.0",
    "passport-local-mongoose": "^8.0.0",
    "pm2": "^5.3.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.22"
  }

 
3. index.js
cors가 조금 문제스럽습니다. 아직도 정확히 작동하는 법은 몰라서 이것저것 남겨놓습니다.
라우팅은 포스팅 관련된 postRoute와 사용자관리 관련된 userRoute 두가지로 구성했습니다. 

import express from 'express';
import session from 'express-session';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import dotenv from 'dotenv';
import mongoose from 'mongoose';
import postRoute from './routes/postRoute.js';
import userRoute from './routes/userRoute.js';
import cors from 'cors';
import path from 'path';

dotenv.config();

const app = express();
const __dirname = path.resolve();
const { PORT, MONGO_URI, SECRET } = process.env;

app.set('port', PORT || 3000);

mongoose.connect(MONGO_URI)
    .then(() => {
        console.log('Connected to MongoDB');
    })
    .catch(e => {
        console.error(e);
    });

app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser(SECRET));
app.use(session({
    resave: false,
    saveUninitialized: false,
    secret: SECRET,
    cookie: {
        httpOnly: true,
        secure: false
    }
}));
// const options = {
//     origin: "http://localhost:4000", // 접근 권한을 부여하는 도메인
//     credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
//     optionsSuccessStatus: 200, // 응답 상태 200으로 설정
//   };
  
// app.use(cors(options));
app.use(cors());

app.all('/*', function(req, res, next){
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
})
app.use('/posts',postRoute);
app.use('/auth',userRoute);
var router = express.Router();
app.get('/', function(req,res,next){
    res.sendFile(path.join(__dirname, './public','index.html'))
})

app.listen(app.get('port'), ()=> {
  console.log(`${app.get('port')}번 포트에서 서버 실행 중..`);
});

 
4. models/Post.js
포스트 모델을 구성합니다. 

import mongoose from "mongoose";

let PostSchema = new mongoose.Schema({
  title: String,
  body: String,
  publishedDate: Date,  
  creator: {
      _id:{
          type: mongoose.Schema.Types.ObjectId,
          ref: "User"
      },
      username: String,   
      password:String,
      email: String   
  },  
});

let Post = mongoose.model("Post", PostSchema);
export default Post;

 
5. models/User.js
사용자 모델을 생성합니다.

import mongoose from 'mongoose';
import passportLocalMongoose from 'passport-local-mongoose'


let UserSchema = new mongoose.Schema({
    username: String,
    password:String,
    email: String
});

UserSchema.plugin(passportLocalMongoose);
const User = mongoose.model("User", UserSchema);
export default User;

 
6. routes/postRoute.js

import express from 'express';
import Post from '../models/Post.js';
import User from '../models/User.js';
import bodyParser from 'body-parser'

const router = express.Router();
router.use(express.json());

// 전체 리스트 읽기
router.get('/', (req, res) => {
  Post.find()
    .then((post) =>{
      res.send({post});
    })  
});

// 신규 포스트 생성
router.post('/', async (req, res) => {  
  const { title, body } = req.body;  
  // const creator = req.body.email;
  console.log(req.body);
  const post = new Post({
    title, 
    body, 
    creator: req.body.creator,
  });
  try{
    await post.save();
    // console.log(post);    
    res.body = post;
    res.send(post);
  } catch (e) {
    console.error(e);
  }

});

// 특정 포스트 조회
router.get('/:id', async (req, res) => {
  const { id } = req.params;
  console.log(id);
  try {
    const post = await Post.findById(id).exec();
    if(!post){
      res.sendStatus(404);
      return;
    }
    res.body = post;
    res.send(post);
  } catch (e) {
    console.error(e);
  }
});

// 특정 포스트 업데이트
router.patch('/:id', async (req, res) => {
  const { id } = req.params;
  // const { title, body } = req.body;
  try{
    const post = await Post.findByIdAndUpdate(id, req.body, {new:true,}).exec();
    if(!post){
      console.sendStatus(404);
      return;
    }
    res.body = post;
    res.send(post);
  } catch(e){
    console.error(e);
  }

});

// 특정 포스트 삭제
router.delete('/:id', async (req, res) => {
  const { id } = req.params;
  // console.log(req.params);
  try{
    await Post.findByIdAndRemove(id).exec();
    res.sendStatus(204);
  } catch(e){
    console.error(e);
  }
});

export default router;

 
7. routes/userRoute.js
유저 인증은 passport-local 라이브러리를 이용한 방식으로 진행합니다. Front에서 Kakao로 인증하고, 사용자 정보를 Backend로 넘겨주면 처리합니다. 이때 URL은 '/register' 만 사용하는데, 등록된 사용자면 login을 시도하고 등록 안된 사용자면 신규로 user테이블에 생성합니다. 즉 무조건 가입/로그인 되는 방식입니다.

import express from 'express';
import User from '../models/User.js';
import passport from 'passport';
import Localstrategy from 'passport-local';

const router = express.Router();

passport.use(new Localstrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

router.use(passport.initialize());
router.use(passport.session());

const isLoggedIn = (req, res, next) => {
    if (req.isAuthenticated()) {
      return next();
    }
    res.redirect('/login');
  };

router.post('/register', async (req, res, next) => {
  const { username, email, password } = req.body;

  try{
    const existingUser = await User.findOne({ email });
    // console.log(existingUser);  //DB에서 존재하는 emial인지 확인
    if (existingUser) {
        // User already exists, redirect to login page or show error message        
        passport.authenticate('local')(req, res, () => {
            // Authentication successful
            // console.log(req.user);
            console.log("Login success!");
            return res.send(req.user);
        });
    } else {

        const newUser = new User({ username, email });
        await User.register(newUser,password); // 패스워드만 따로 보내야 해싱처리 등이 적용됨 

        passport.authenticate("local")(req, res, () => {
            // console.log(req.user);
            console.log("Success! You are registered and logged in!");
            return res.send(req.user);
        });
    }
  } catch (err) {
    return next(err);
  }  
});

router.get('/protected', isLoggedIn, (req, res) => {
    res.send('Protected content');  
});

router.get("/logout", (req, res) => {
    req.logout();
    res.redirect("back");
});



export default router;

 
passport-kakao 라이브러리도 있는데, backend에서 구현하면 계속 cors문제를 해결 못했습니다. 그래서 front에서 인증하고, 사용자 정보를 backend로 넘기는 방식으로 구현함..ㅠㅠ

반응형

댓글