Skip to content

openOneMeal/OSS_openOneMeal_server

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

μ˜€ν”ˆν•œλΌ ν”„λ‘œμ νŠΈ Server Repository

πŸ“… Update Date: 2024-05-27

1. Overview

이 λ¦¬ν¬μ§€ν† λ¦¬λŠ” μ˜€ν”ˆν•œλΌ ν”„λ‘œμ νŠΈ μ„œλ²„ κ΅¬ν˜„μ„ μœ„ν•΄ λ…Έλ ₯ν•œ κ³Όμ •κ³Ό μ½”λ“œλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

1.1. μ„œλ²„ κ΅¬ν˜„ μ€€λΉ„

  • Node.js μ„€μΉ˜ ν›„ npm ν™˜κ²½ λ³€μˆ˜ 등둝
  • MongoDB Atlas 에 Organization, Project, Cluster 생성
  • MongoDB Compass 및 mongosh μ„€μΉ˜
  • μ„œλ²„ μ½”λ“œλ₯Ό μ €μž₯ν•  폴더λ₯Ό 생성 ν›„ VS Code 터미널 μ‹€ν–‰
  • npm λͺ¨λ“ˆ μ„€μΉ˜: express, mongoose, nodemon, axios
    1. npm init -y
    2. npm i express mongoose axios
    3. npm i -D nodemon

2. MongoDB

2.1. MongoDB μ œν’ˆ 및 라이브러리 μ„€λͺ…

  • MongoDB Atlas: MongoDB μ—μ„œ μ œκ³΅ν•˜λŠ” ν΄λΌμš°λ“œ λ°μ΄ν„°λ² μ΄μŠ€ μ„œλΉ„μŠ€
  • MongoDB Compass: λ°μ΄ν„°λ² μ΄μŠ€ 관리에 GUI ν™˜κ²½μ„ μ œκ³΅ν•˜λŠ” ν”„λ‘œκ·Έλž¨
  • MongoDB Shell (mongosh): ν„°λ―Έλ„μ—μ„œ λ°μ΄ν„°λ² μ΄μŠ€ 관리 κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” ν”„λ‘œκ·Έλž¨
  • Mongoose: Node.js ν™˜κ²½μ—μ„œ μ•„μ£Ό μ‰½κ²Œ MongoDB 객체 λͺ¨λΈλ§μ„ μ§€μ›ν•˜λŠ” 라이브러리. λ³΅μž‘ν•œ 데이터 λͺ¨λΈμ΄λ‚˜ κ΄€κ³„ν˜• λͺ¨λΈμ„ 생각할 ν•„μš” 없이 JavaScript νŒŒμΌμ— μŠ€ν‚€λ§ˆ λͺ¨λΈμ„ μΆ”κ°€ν•˜κ³ , μ„œλ²„ μ½”λ“œμ—μ„œ import ν•˜μ—¬ μŠ€ν‚€λ§ˆμ— 따라 Document λ₯Ό μ½κ±°λ‚˜ μ“Έ 수 μžˆμŠ΅λ‹ˆλ‹€.

2.2. ν΄λŸ¬μŠ€ν„°λͺ…

  • Production ν™˜κ²½μ„ μœ„ν•œ ν΄λŸ¬μŠ€ν„°: ProductionCluster

    μ‹€μ œ μš΄μ˜μ€‘μΈ μ„œλ²„μ—λŠ” ProductionCluster λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

  • Development ν™˜κ²½μ„ μœ„ν•œ ν΄λŸ¬μŠ€ν„°: DevelopmentCluster

    개발 쀑인 μ„œλ²„μ—λŠ” DevelopmentCluster λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

2.3. mongoose μ½”λ“œ

  • mongoose μž„ν¬νŠΈ 및 DB μ—°κ²°

    // CommonJS 방식
    const mongoose = require('mongoose'); // (1)
    // ES Module 방식
    // package.json 에 "type": "module" λͺ…μ‹œ
    import mongoose from 'mongoose'       // (2)
    
    // mongoose λ₯Ό μ΄μš©ν•΄ DB 에 μ—°κ²°
    mongoose.connect("MongoDB Atlas의 DB μ£Όμ†Œ");
  • μ»¬λ ‰μ…˜μ˜ μŠ€ν‚€λ§ˆ μ„€μ •

    // Users.js
    import mongoose from 'mongoose';
    const { Schema, model } = mongoose;
    
    const usersSchema = new Schema({
        name: {
            type: String,
            required: true,
            maxlength: 3,
        },
        gender: {
            type: String,
            required: true,
            maxlength: 2,
        },
        email: {
            type: String,
            // required: λˆ„λ½ ν•˜λ©΄ μ•ˆλ˜λŠ” ν•„λ“œ
            required: true,
            maxlength: 50
        },
        password: {
            type: String,
            required: true,
        },
        createdAt: {
            type: Date,
            default: () => Date.now(),
            // immutable: 첫 ν• λ‹Ή 이후 μˆ˜μ • λΆˆκ°€λŠ₯ν•œ ν•„λ“œ
            immutable: true,
        },
        updatedAt: Date,
    });
    
    // save λ©”μ„œλ“œλ₯Ό μ΄μš©ν–ˆμ„ λ•Œ μžλ™μœΌλ‘œ updatedAt ν•„λ“œλ₯Ό ν˜„μž¬ λ‚ μ§œλ‘œ κ°±μ‹ ν•˜λŠ” 미듀웨어
    usersSchema.pre('save', function(next) {
        this.updatedAt = Date.now();
        next();
    });
    
    // μ™ΈλΆ€μ—μ„œ import ν–ˆμ„ λ•Œ μ‚¬μš©ν•  수 μžˆλŠ” λͺ¨λΈμ΄λ©°,
    // User 둜 접근이 이 μŠ€ν‚€λ§ˆμ— 접근이 κ°€λŠ₯함.
    const Users = model('Users', usersSchema);
    export default Users;
  • DB 와 톡신

    // Document 생성 및 μ €μž₯
    // 1번 방법: μΈμŠ€ν„΄μŠ€ 생성 ν›„ save()
    const user = new User({ email: 'test@test.com' });
    await user.save();
    // 2번 방법: μΈμŠ€ν„΄μŠ€ 생성 및 μ €μž₯을 ν•œκΊΌλ²ˆμ— μˆ˜ν–‰ν•΄μ£ΌλŠ” create()
    const user = await User.create({ email: 'test@test.com' }) //
    
    // Update (κ°±μ‹ )
    // 방법: . 으둜 ν•„λ“œλ₯Ό 직접 μ°Έμ‘°ν•΄μ„œ κ°’ μˆ˜μ • ν›„ save()
    user.email = "test@test.com"
    await user.save();
    
    // Find (쑰회)
    // 1번 방법: findOne(), findMany()
    const user = await User.findOne({ name: "λΉ„λ₯΄μΈ " })
    const user = await User.findMany({ name: ["그리말도", "μ‹œν¬"] })
    // 2번 방법: findById()
    const user = await User.findById("ID 번호").exec();
    // 3번 방법: .where().equals().select()
    // where 은 ν•„λ“œλ₯Ό 필터링, select λŠ” 보여쀄 ν•„λ“œ(projection)λ₯Ό κ²°μ •
    const user = await User.where("email").eqauls("test@test.com").select("name");
    
    // Delete (μ‚­μ œ)
    // 방법: deleteOne(), deleteMany()
    const user = await User.deleteMany({ email: ["test@test.com", "test2@test.com"] });

3. μ„œλ²„ κ΅¬ν˜„

3.1. 미듀웨어 μ„€μ •

  • JSON 객체 처리, CORS 사전 μš”μ²­ 처리λ₯Ό μœ„ν•œ 미듀웨어 μ„€μ •

    • JSON 객체 처리: μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” JSON 객체둜 ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλ²„κ°€ 톡신. 예λ₯Ό λ“€μ–΄, fetch λ˜λŠ” axios λ₯Ό μ΄μš©ν•œ μš”μ²­μ€ μ „λΆ€ JSON 객체이며, 이λ₯Ό νŒŒμ‹±ν•˜μ—¬ μžλ°”μŠ€ν¬λ¦½νŠΈ 객체둜 λ³€ν™˜ν•΄μ€„ 미듀웨어λ₯Ό μ„€μ •
    • CORS 사전 μš”μ²­ 처리: ν΄λΌμ΄μ–ΈνŠΈλŠ” μ„œλ²„λ‘œ μ‹€μ œ μš”μ²­μ„ 보내기 이전 CORS 사전 μš”μ²­μ„ 보냄. μ„œλ²„κ°€ 이 μš”μ²­μ„ ν—ˆμš©ν•΄μ•Ό ν΄λΌμ΄μ–ΈνŠΈκ°€ μ‹€μ œ μš”μ²­μ„ 보낼 수 μžˆμœΌλ―€λ‘œ, CORS 사전 μš”μ²­μ„ ν—ˆμš©ν•˜λ„λ‘ 미듀웨어λ₯Ό μ„€μ •
    // express.json() 미듀웨어λ₯Ό 톡해 JSON ν˜•μ‹μ˜ μš”μ²­ 본문이 μ•Œμ•„μ„œ νŒŒμ‹±λ¨
    app.use(express.json())
    
    import cors from 'cors';
    // ν—ˆμš©ν•  CORS 의 헀더, λ©”μ„œλ“œ, origin κΉŒμ§€ 직접 λͺ…μ‹œν•  μˆ˜λ„ μžˆμœΌλ‚˜,
    // μ΄λ ‡κ²Œ μ„€μ •ν•˜λ©΄ λͺ¨λ“  CORS 사전 μš”μ²­μ„ ν—ˆμš©ν•¨.
    app.use(cors())
    // ν΄λΌμ΄μ–ΈνŠΈκ°€ λ³΄λ‚΄λŠ” 사전 μš”μ²­μ€ OPTION λ©”μ„œλ“œμ΄λ©°,
    // μ΄λ ‡κ²Œ ν•˜λ©΄ μ„œλ²„μ— μ‘΄μž¬ν•˜λŠ” λͺ¨λ“  κ²½λ‘œμ— λŒ€ν•˜μ—¬ CORS κ°€ ν—ˆμš©λ¨
    app.options('*', cors());

3.2. 둜그인 κΈ°λŠ₯

  • 둜그인 κΈ°λŠ₯은 ν΄λΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„° 이메일과 νŒ¨μŠ€μ›Œλ“œλ₯Ό JSON 객체둜 λ°›μ•„μ„œ, 미듀웨어λ₯Ό 톡해 νŒŒμ‹±ν•˜κ³ , 객체의 이메일 ν•„λ“œμ˜ 값을 DB μ—μ„œ μ‘°νšŒν•˜μ—¬ μΌμΉ˜ν•˜λŠ” 이메일이 μžˆλŠ”μ§€ ν™•μΈν•˜κ³ , νŒ¨μŠ€μ›Œλ“œλ„ μΌμΉ˜ν•˜λŠ”μ§€ 확인함. νŒ¨μŠ€μ›Œλ“œλŠ” bcrypt.compare() ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄ μž…λ ₯ 받은 평문 νŒ¨μŠ€μ›Œλ“œλ₯Ό ν•΄μ‹œν•˜μ—¬ DB 에 μ €μž₯된 ν•΄μ‹œλœ νŒ¨μŠ€μ›Œλ“œμ™€ 비ꡐλ₯Ό μˆ˜ν–‰ν•¨.

  • ν΄λΌμ΄μ–ΈνŠΈλŠ” axios 의 POST μš”μ²­

    const response = await axios.post(
    	"URL/api/signin",
    	sendData
    );
  • μ„œλ²„λŠ” express 의 POST 응닡

    app.post('/api/signin', async (req, res) => {
        const { email, password } = req.body;
    
        try {
            const user = await User.findOne({ email });
    
            if (!user) {
                return res.status(401).json({status: 401, message: '이메일 ν˜Ήμ€ νŒ¨μŠ€μ›Œλ“œκ°€ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.'});
            }
            
            const isMatch = await bcrypt.compare(password, user.password);
    
            if (!isMatch) {
                return res.status(401).json({status: 401, message: '이메일 ν˜Ήμ€ νŒ¨μŠ€μ›Œλ“œκ°€ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.'});
            }
    
            return res.status(200).json({ status: 200, message: '둜그인 성곡' });
        } catch (err) {
            return res.status(500).json({ status: 500, message: 'Server error'});
        }
    });

3.3. νšŒμ›κ°€μž… κΈ°λŠ₯

  • ν΄λΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„° νšŒμ›κ°€μž… 폼에 μž…λ ₯된 값듀이 μ €μž₯된 JSON 객체λ₯Ό λ„˜κ²¨λ°›μ•„, 이메일이 μ€‘λ³΅λλŠ”μ§€ κ²€μ‚¬ν•˜κ³ , μ€‘λ³΅λ˜μ§€ μ•Šμ•˜μœΌλ©΄ νŒ¨μŠ€μ›Œλ“œλ₯Ό ν•΄μ‹œν™”ν•œ λ’€ DB 에 계정을 생성함

  • ν΄λΌμ΄μ–ΈνŠΈλŠ” axios 의 POST μš”μ²­

    const response = await axios.post(
    	"URL/api/signup",
    	sendData
    );
  • μ„œλ²„λŠ” express 의 POST 응닡

    app.post('/api/signup', async (req, res) => {
        const { name, gender, email, password } = req.body;
    
        try {
            
            const user = await Users.findOne({ email });
            if (user) {
                return res.status(409).json({status:409, message: 'μ€‘λ³΅λœ 이메일'});
            }
    
            // bcrypt.hash λ₯Ό 톡해 전달받은 νŒ¨μŠ€μ›Œλ“œλ₯Ό ν•΄μ‹œν™”ν•¨
            // λ‘λ²ˆμ§Έ 맀개인자인 μˆ«μžλŠ” ν•΄μ‹œ 레벨이며,
            // ν•΄μ‹œ λ ˆλ²¨μ„ 높일 수둝 처리 μ†λ„λŠ” λŠλ €μ§€μ§€λ§Œ 그만큼 λ³΅μž‘ν•œ ν•΄μ‹œν™”
            const hashedPassword = await bcrypt.hash(password, 10);
    
            await Users.create({
                name: name,
                gender: gender,
                email: email,
                password: hashedPassword,
            })
            
            res.status(201).send('계정 생성 성곡');
        } catch (error) {
            res.status(500).send('계정 생성 쀑 였λ₯˜ λ°œμƒ');
        }
    });

4. Heroku μ„œλ²„ ν˜ΈμŠ€νŒ…

4.1. μ„œλ²„ ν˜ΈμŠ€νŒ… μ€€λΉ„

  • Heroku 계정 생성
  • Heroku CLI μ„€μΉ˜
  • Heroku μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 생성
  • 예방적 μ°¨μ›μœΌλ‘œ package.json 의 dependencies λ₯Ό npm install 둜 μ „λΆ€ μ„€μΉ˜

4.2. ν™˜κ²½ λ³€μˆ˜ μ„€μ •

  • Heroku ν™˜κ²½ λ³€μˆ˜μ— MongoDB Atlas 의 Connect μ£Όμ†Œλ₯Ό λ“±λ‘ν•˜κ³ ,

    mongoose.connect(process.env.MONGODB_URI); 와 같이 ν™˜κ²½ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ DB에 μ—°κ²°

  • μ„œλ²„λ₯Ό μ—΄ λ•ŒλŠ” Heroku 의 ν™˜κ²½ λ³€μˆ˜μ— λ“±λ‘λœ 포트 번호λ₯Ό 이용

    app.listen(process.env.PORT, () β‡’ {});

4.3. μ—”νŠΈλ¦¬ 포인트 λͺ…μ‹œ

  • package.json 의 β€œscripts” μ„Ήμ…˜μ— β€œstart”: β€œnode index.js” μΆ”κ°€

  • Procfile 파일 생성

    • Heroku λŠ” Procfile 을 μ‚¬μš©ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹œμž‘ν•¨
    • 전체 ν”„λ‘œμ νŠΈμ˜ μ΅œμƒμœ„ 디렉터리에 Procfile 을 ν™•μž₯자 없이 μƒμ„±ν•˜κ³ , μ‹€ν–‰ν•  λͺ…령을 μΆ”κ°€
    // Procfile
    web: node index.js

4.4. MongoDB Atlas 의 Network Access IP μ„€μ •

  • Heroku ν˜ΈμŠ€νŒ… μ„œλΉ„μŠ€μ˜ IP 의 접근을 ν—ˆμš©ν•΄μ•Ό ν•˜λŠ”λ°, Heroku λŠ” 유료 버전을 μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄ 동적인 IP μ£Όμ†Œλ₯Ό ν• λ‹Ήν•˜κΈ° λ•Œλ¬Έμ— 전체 접근을 ν—ˆμš©ν•˜λ„λ‘ μ„€μ •

4.5. Github 에 Push ν›„ Heroku 에 Deploy

  • Heroku 에 Deploy λ₯Ό κ°„μ†Œν™”ν•˜κΈ° μœ„ν•΄, Github 리포에 μ„œλ²„ μ½”λ“œλ₯Ό μ „λΆ€ μ—…λ‘œλ“œν•˜κ³  Heroku μ—μ„œ Github 의 리포λ₯Ό μ΄μš©ν•΄ μ„œλ²„λ₯Ό ν˜ΈμŠ€νŒ…ν•˜λ„λ‘ μ„€μ •

4.6. μ„œλ²„ μ‹€ν–‰

  • heroku ps:scale web=1 μ„€μ •ν•˜μ—¬ μ„œλ²„ μ‹€ν–‰
  • heroku ps:scale web=0 μ„œλ²„ μ’…λ£Œ
  • heroku ps:restart μ„œλ²„ μž¬μ‹œμž‘
  • heroku logs --tail μ„œλ²„ 둜그 확인

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published