코딩/project

pj, 6일차

wqk 2024. 5. 8. 17:47


ORM(객체 관계 매핑, Object Relational Mapping)
- 객체 지향 프로그래밍 언어에서 사용되는 객체와 관계형 데이터베이스 간의 불일치를 해결하기 위한 기술
- 객체 모델과 관계형 데이터베이스의 테이블 간의 매핑을 수행하여 개발자가 SQL 쿼리 대신 객체 지향 코드를 사용할 수 있도록 함

ODM(객체 문서 매핑, Object Document Mapping)
- NoSQL 데이터베이스와 객체 지향 프로그래밍 언어 간의 매핑을 제공하는 기술
- 주로 문서 지향 데이터베이스(MongoDB)와 함께 사용

현재 작업중인 브랜치를 확인
- 목록을 확인

    git branch

- * 표시가 되어 있는 것이 현재 작업중인 브랜치 

branch 생성하기
git branch 브랜치이름

branch 전환하기 
git switch 브랜치이름
git checkout 브랜치이름

branch 생성과 동시에 전환하기 
git checkout -b 브랜치이름 

// config.js

// config.js

import dotenv from 'dotenv';

dotenv.config();

function required(key, defaultValue=undefined){
    const value = process.env[key] || defaultValue; // or: 앞의 값이 true로 판별되면 앞의 값이 대입되고 값이 false로 판별되면 뒤에 값이 대입됨
    if(value == null){
        throw new Error(`키 ${key}는 undefined!!`);
    }
    return value;
}

export const config = {
    jwt: {
        secretKey: required('JWT_SECRET'),
        expiresInSec: parseInt(required('JWT_EXPIRES_SEC', 172800))
    },
    bcrypt: {
        saltRounds: parseInt(required('BCRYPT_SALT_ROUNDS', 10))
    },
    host: {
        port: parseInt(required('HOST_PORT', 8080))
    },
    db: {
        host: required('DB_HOST'),
        user: required('DB_USER'),
        database: required('DB_DATABASE'),
        password: required('DB_PASSWORD'),
        port: required('DB_PORT')
    }
}

 

// .env

# DB
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=****
DB_DATABASE=kdt

# JWT
JWT_SECRET=abcd1234%^&*
JWT_EXPORES_SEC=172800

# BCRYPT
BCRYPT_SALT_ROUNDS=10

# SERVER
PORT=8080

 

// app.js

// app.js

import express from "express";
import morgan from "morgan";
import tweetsRouter from './router/tweets.js'
import authRouter from './router/auth.js'
import { config } from "./config.js";
// import { db } from "./db/database.js";

const app = express();

app.use(express.json());  // json로 연결
app.use(morgan("dev"));

//tweetsRouter 미들웨어 등록
app.use('/tweets', tweetsRouter);  
//authsRouter 미들웨어 등록
app.use('/auth', authRouter); 

app.use((req, res, next) => {
    res.sendStatus(404);
});

// DB 연결 테스트!
// db.getConnection().then(Connection => console.log(Connection));

app.listen(config.host.port);

 

// data/auth.js

// data/auth.js

import { db } from '../db/database.js';

// 아이디(username) 중복 검사 
export async function findByUsername(username){
    return db.execute('select * from users where username = ?', [username]).then((result)=> {
        console.log(result);
        return result[0][0];
    });
}

// id 중복 검사 
export async function findById(id){
    return db.execute('select * from users where id = ?', [id]).then((result) => {
        console.log(result);
        return result[0][0];
    }); 
}

export async function createUser(user){
    console.log(user);
    const {username, hashed, name, email, url} = user;
    return db.execute('insert into users (username, password, name, email, url) values (?, ?, ?, ?, ?)', [username, hashed, name, email, url]).then((result) => {
        console.log(result);    //result[0].insertId
        return result[0].insertId;
    });
}


// export async function login(username){
//     return users.find((users) => users.username === username);
// }


// {
//     "username":"orange",
//     "password":"12345",
//     "name":"오렌지",
//     "email":"orange@orange.com",
//     "url":"https://img.freepik.com/premium-vector/banana-cute-kawaii-style-fruit-character-vector-illustration_787461-1772.jpg"
// }

 

//controller/auth.js

import * as authRepository from '../data/auth.js';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
// controller/auth.js

import * as authRepository from '../data/auth.js';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { config } from "../config.js";

/*
문제
jwt.js를 참고하여 controller/auth.js 에 토큰을 발행하고 login()에 로그인 완료되면 클라이언트에 토큰을 출력하는 프로세스를 만들어보자 
*/

function createJwtToken(id){
    return jwt.sign({id}, config.jwt.secretKey, {expiresIn: config.jwt.expiresInSec});
}

/*
문제.
회원가입시 아이디 중복체크 하기
단. 중복이라면 409을 리턴 
*/

// 회원가입 중복검사 
export async function signup(req, res, next){
    const {username, password, name, email, url} = req.body;
    const found = await authRepository.findByUsername(username);
    if(found){
        return res.status(409).json({message:`${username}이 이미 있습니다`});
    }
    const hashed = await bcrypt.hash(password, config.bcrypt.saltRounds);
    const userId = await authRepository.createUser({username, hashed, name, email, url});
    const token = createJwtToken(userId);
    res.status(201).json({token, username});
}

/*
문제
controller/auth.js 에서 login()를 bcrypt를 적용하여 로그인 프로세스를 만들어보자
*/

// 로그인 
export async function login(req, res, next){
    const {username, password} = req.body;
    // const user = await authRepository.login(username);
    const user = await authRepository.findByUsername(username);
    console.log(user);
    if(!user){
        return res.status(401).json({message : '아이디를 찾을 수 없음'})
    }
    const isValidpassword = await bcrypt.compareSync(password, user.password);
    if(!isValidpassword){
        return res.status(401).json({message : `비밀번호가 틀렸음`});
    }
    const token = createJwtToken(user.id);
        return res.status(200).json({token, username});
}

// export async function verify(req, res, next){
//     const token = req.header['Token'];
//     if(token){
//         res.status(200).json(token);
//     }
// }

export async function me(req, res, next){
    const user = await authRepository.findById(req.userId);
    console.log(user);
    if(!user){
        return res.status(404).json({message: `일치하는 사용자가 없음`});
    }
    res.status(200).json({token: req.Token, username: user.username});
}

 

// middleware/auth.js

// middleware/auth.js

import jwt from 'jsonwebtoken';
import * as authRepository from '../data/auth.js';

const AUTH_ERROR = {message: "인증에러"};

export const isAuth = async (req, res, next) => {
    const authHeader = req.get("Authorization");
    console.log(authHeader);

    if(!(authHeader && authHeader.startsWith('Bearer'))){
        console.log('에러1');
        return res.status(401).json(AUTH_ERROR);
    }
    const token = authHeader.split(' ')[1];

    jwt.verify(
        token, 'abcd1234%^&*', async(error, decoded) => {
            if(error){
                console.log('에러2');
                return res.status(401).json(AUTH_ERROR);
            }
            const user = await authRepository.findById(decoded.id);
            if(!user){
                console.log('에러3');
                return res.status(401).json(AUTH_ERROR);
            }
            req.userId = user.id;
            next();
        }
    );
}

 

//data/tweet.js

// data/tweet.js

import { db } from '../db/database.js';

const SELECT_JOIN = 'select tw.id, tw.text, tw.createdAt, tw.userId, us.username, us.email, us.url from tweets as tw join users as us on tw.userId = us.id';

const ORDER_DESC = 'order by tw.createdAt desc';

// 모든 트윗을 리턴
export async function getAll() {
    return db.execute(`${SELECT_JOIN} ${ORDER_DESC}`).then ((result) => {
        console.log(result);
        return result;
    });
}

// 해당 아이디에 대한 트윗을 리턴
export async function getAllByUsername(username){
    return db.execute(`${SELECT_JOIN} where username = ? ${ORDER_DESC}`, [username]).then ((result) => {
        console.log(result);
        return result;
    });
}

// 글번호에 대한 트윗을 리턴
export async function getById(id){
    return db.execute(`${SELECT_JOIN} where tw.id = ? ${ORDER_DESC}`, [id]).then ((result) => {
        console.log(result);
        return result;
    });
}

// 트윗을 작성
export async function create(text, userId){
    return db.execute('insert into tweets (text, userId) values (?, ?)', [text, userId]).then ((result) => {
        console.log(result);
        return getById(result[0].insertId);
    });
}

// 트윗을 변경
export async function update(id, text){
    return db.execute('update tweets set text = ? where id =?', [text, id]).then ((result) => {
        console.log(result);
        return getById(id);
    });
}

// 트윗을 삭제
export async function remove(id){
    return db.execute('delete from tweets where id = ?', [id]);
}

 

 

 

.🤍

 

ChatGPT에게 물어본 오늘의 공부-

 

 

✔️ Morgan

Morgan은 Node.js의 HTTP 요청 로깅 미들웨어입니다. Express 애플리케이션과 함께 사용되며, 요청에 대한 로그를 기록하는 데 사용됩니다. Morgan은 미들웨어로서 Express 애플리케이션에 추가되어 각 HTTP 요청에 대한 정보를 기록하고 이를 콘솔이나 파일에 출력합니다.

Morgan을 사용하면 다음과 같은 정보를 로깅할 수 있습니다.

  1. HTTP 메서드 (GET, POST, 등)
  2. 요청 경로 (URL)
  3. 상태 코드 (HTTP 상태 코드)
  4. 응답 시간
  5. 요청 IP 주소
  6. 요청 헤더
  7. 기타 요청 및 응답 관련 정보

Morgan은 로깅 형식을 지정할 수 있어서 로그 메시지의 형식을 사용자가 조정할 수 있습니다. 또한 프로덕션 및 개발 환경에 따라 로그 레벨을 조정하여 필요한 만큼의 상세한 정보를 기록할 수 있습니다. 이를 통해 개발자는 애플리케이션의 동작을 추적하고 디버깅하는 데 도움을 받을 수 있습니다.

 

 

✔️ result[0][0] 반환하는 이유

이 코드에서 findByUsername 및 findById 함수는 데이터베이스에서 사용자를 조회하는 비동기 함수입니다. SQL 쿼리를 실행하고 결과를 반환하는 것은 Promise를 반환하는 db.execute 함수입니다. 이 함수의 결과는 SQL 쿼리 결과에 해당하는 배열입니다.

결과 배열의 첫 번째 요소 result[0]는 실제로 데이터 레코드의 배열을 포함하는 배열입니다. 즉, 여러 개의 사용자를 반환하는 쿼리를 실행하는 경우 첫 번째 배열은 사용자 목록이 됩니다. 각 사용자는 이 배열의 요소로 들어가게 됩니다.

그러므로 result[0][0]은 첫 번째 사용자를 나타냅니다. 이 함수들은 주로 단일 사용자를 조회하는 데 사용되기 때문에 첫 번째 사용자만 반환하는 것입니다. 만약 여러 명의 사용자를 조회하고자 한다면 결과 배열 전체를 반환하거나 처리할 수 있는 방식으로 변경할 수 있습니다.

 

 

✔️ hashed

hashed는 일반적으로 암호화된 비밀번호를 나타내는 변수입니다. 사용자의 비밀번호를 데이터베이스에 저장할 때 보안상의 이유로 평문으로 저장하는 것은 안전하지 않습니다. 따라서 일반적으로 비밀번호를 해시 함수를 사용하여 암호화한 다음에 이를 데이터베이스에 저장합니다. 이 때 사용되는 암호화된 비밀번호를 일반적으로 "해시된" 또는 "해싱된" 비밀번호라고 합니다.

해시 함수는 입력된 데이터(여기서는 비밀번호)를 고정된 길이의 문자열로 변환합니다. 이 변환된 문자열은 원본 데이터와 관련된 특정한 형태를 가지고 있지만, 원본 데이터를 복원하기가 매우 어렵거나 거의 불가능합니다. 따라서 데이터베이스에 저장된 해시된 비밀번호를 해독하여 원본 비밀번호를 알아내는 것은 매우 어렵습니다.

bcrypt와 같은 암호화 알고리즘을 사용하여 비밀번호를 해시하는 것이 보편적인 방법 중 하나입니다. bcrypt는 안전한 해시 알고리즘으로, 암호화된 비밀번호를 저장할 때 많이 사용됩니다.

 

 

✔️ insertId

insertId는 MySQL 데이터베이스에서 삽입된 행의 ID를 반환하는 속성입니다. 일반적으로 MySQL에서 INSERT 쿼리를 실행할 때 새로운 행을 삽입하면 해당 행의 고유 식별자(ID)가 자동으로 생성됩니다. 이때 insertId 속성은 마지막으로 삽입된 행의 ID를 반환합니다.

위의 코드에서 db.execute 메서드는 MySQL 데이터베이스에서 쿼리를 실행하는 데 사용됩니다. 이 메서드를 사용하여 새로운 사용자를 데이터베이스에 삽입할 때, insertId 속성을 사용하여 새로 삽입된 사용자의 ID를 반환합니다. 이렇게 반환된 ID는 일반적으로 새로운 사용자를 식별하는 데 사용됩니다

 

🤍