Mi Lugarcito

React & Mysql 트위터 만들기 - backend 편 본문

React & Next.js

React & Mysql 트위터 만들기 - backend 편

selene park 2021. 4. 21. 23:24

 

front : 3000/ back : 3065

npm init // package.json -> npm install or npm i -> download
npm i express // server
npm i sequelize sequelize-cli mysql2 // server
npx sequelize init //server
npx sequelize db:create // server
node app //server
npm i -D nodemon@2 //server
npm i bcrypt // password, server
npm i cors // server, middleware 설치하기
npm i passport passport-local //이메일, 아이디 로그인
npm i express-session // session 설치
npm i cookie-parser // cookie
npm i dotenv //server



// server, 요청과 응답을 기록하는 라이브러리, front->back 요청 보낼때 어떤 요청들을 보냈는지 콘솔창에 알려준다.
npm i morgan 
npm i multer//이미지 때문
REST API

app.get //back으로 데이터 못넘김
app.post
app.put // 전체수정
app.delete// back으로 데이터 못넘김
app.patch //부분수정 (예 : 닉네임만 수정)
app.options // 찔러보기...
app.head //헤더 또는 바디만 가져오기





//주소 부분에서 동적으로 계속 바뀌는 부분 :postId ? 파라미터라고 부른다, 그리고 주소에 들어가는 모든것들은 숫자처럼 보여도 전부 문자열이다.
router.post('/:postId/commnet', async (req,res, next)=>{//POST /post/comment
//아래와 같이!!
PostId : parseInt(req.params.postId),



요청/응답 : 헤더(상태, 용량, 시간, 쿠키) 와 바디 (데이터)로 구성됨
200 : 성공
300 : 리다이렉트
400 : 클라이언트 에러
500 : 서버 에러, 예 : next(error)

swagger.io/

 

API Documentation & Design Tools for Teams | Swagger

 

swagger.io

www.postman.com/

 

Postman | The Collaboration Platform for API Development

Postman makes API development easy. Our platform offers the tools to simplify each step of the API building process and streamlines collaboration so you can create better APIs faster.

www.postman.com

config.json

{
  "development": {
    "username": "root",
    "password": "1234",
    "database": "react-nodebird",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "react-nodebird",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "react-nodebird",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}

index.js

const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];//config.json
const db={};

//sequelize 가 node & mysql 두개를 연결시켜준다
const sequelize = new Sequelize(config.database, config.username, config.password, config);

db.User = require('./user')(sequelize, Sequelize);
db.Post = require('./post')(sequelize, Sequelize);
db.Comment = require('./comment')(sequelize, Sequelize);
db.Hashtag = require('./hashtag')(sequelize, Sequelize);
db.Image = require('./image')(sequelize, Sequelize);



Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

 

user

//middleware라서 위->아래 순차적으로 실행된다. 위치 조심!
const express =require('express');//import
const {User, Post} = require('../models');
const bycrypt = require('bcrypt');//비번 암호
const router = express.Router();
const passport = require('passport');

//login 전략
//return done(null, false, {reason : '존재하지 않음'})
//middleware 확장하는 방법
router.post('/login', (req,res,next)=> {
    //2. saga에서 받은 데이터를 가지고 전략을 실행함
    passport.authenticate('local', (err, user, info)=>{
        //5. local 전략 완료 후 여기서 로긴 실행하기 
        if(err){
            //서버에러
            console.log(err);
            return next(err);
        }
        if(info){
            //client error
            return res.status(403).send(info.reason);
        }
    
        //req.login 시 passport로 로그인 한다. -> 세션 로그인 저장해줌
        //6. 여기서 passport login 한다 -> passport 실행은 index.js에서 
        return req.login(user, async (loginerr) => {
            if(loginerr){
                console.log(loginerr);
                return next(loginErr);
            }

            const fullUserWithoutPassword = await User.findOne({
                where : { id : user.id },
                attributes : {
                    exclude : ['password']
                },
                include : [
                    {
                         model : Post, // hasMany라서 Post -> 복수형이 되어 Posts로 된다.
                    },
                    {
                        model : User,
                        as : 'Followings'
                    },
                    {
                        model : User,
                        as : 'Followers'
                    },
            ],
            })
            return res.status(200).json(fullUserWithoutPassword);
        })

    })(req,res,next);
});


router.post('/logout', (req,res)=>{
    req.logout();
    req.session.destroy();//session 에 저장된 쿠키, 아이디를 없애기
    res.send('ok');//logout 
})

router.post('/', async (req,res,next)=>{//POST : /user/
    //비동기는 대부분 try-catch 로 감싸주기
    //(비)동기 함수인지는 공식 문서 찾아봐야함 
    //front saga에서 data.email, data.nickname
    try {

        const exUser = await User.findOne({
            //조건문 where
            where:{
                email : req.body.email,
            }
        });

        //요청/응답 : 헤더(상태, 용량, 시간, 쿠키) 와 바디 (데이터)로 구성됨
        if(exUser){
            return res.status(403).send('이미 사용중')//return 중요!
        }


        const hashedPassword = await bycrypt.hash(req.body.password, 12);
        await User.create({//실행 --1
            email : req.body.email ,
            nickname : req.body.nickname ,
            password: hashedPassword,
        })

        //res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3060');//혹은 주소 대신'*'
        res.status(201).send('ok');//실행 --2

    } catch (error) {
        console.log(error);
        next(error);//error 한방에 처리해줌, 500번대 서버 에러 
    }
});

module.exports = router;

//로그인시 내부적으로 res.setHeader('Cookie', 'cxlhy')이런식으로 쿠키 서버로 보내주고 알아서 세션과 연결해준다. 
            //8. 쿠키, 유저 아이디 서버에서 들고있고 프론트에 보내준다
            //user 사용자 정보를 프론트로 넘기기-> 프론트에서 action.data로 받고 -> reducer
            //1. saga 에서 데이터 보내면 -> 
            //2. server routes user.js로 전송 son(user); 로 서버가 받음-> 
            //3. saga에서 action.data로 받고 -> 
            //4. reducer 에서는 me로 받음

            //fullUserWithoutPassword에는 내 게시글숫자, 팔로잉 정보들때문에 따로 더 추가함, 비번 전달 제외하고(attributes : exclude)!

post

module.exports = (sequelize, DataTypes)=>{
    const Post = sequelize.define('Post', {
       content:{
          type: DataTypes.TEXT,
          allowNull:false,
       },
    
    }, 
    
    {
        charset : 'utf8mb4',//이모지
        collate : 'utf8mb4_general_ci'//한글저장
    
    });

    Post.associate = (db)=>{
        db.Post.belongsTo(db.User);//속해있다
        db.Post.belongsToMany(db.Hashtag,  {through:'PostHashtag'});//다 : 다
        db.Post.hasMany(db.Comment);
        db.Post.hasMany(db.Image);
        //나중에 as에 따라서 post.getLikers 처럼 게시글 좋아요 누른 사람을 가져오게 된다. 별칭같은것@@@
        db.Post.belongsToMany(db.User,  {through : 'Like' , as : 'Likers'});//다 : 다, 좋아요, through -> 중간테이블 이름 바꾸기

        //게시글 간 끼리twitt(원본 : 복사글, 1:다)
        db.Post.belongsTo(db.Post, {as : 'Retweet'}); // PostId -> RetweetId 로 바뀜


    };

    return Post;
}

comment

module.exports = (sequelize, DataTypes)=>{
    const Comment = sequelize.define('Comment', {
       content:{
            type:DataTypes.TEXT,
            allowNull:false,
       },
    
    }, 
    //belongsTo 역할
    //UserId:{}
    //PostId:{}
    {
        charset : 'utf8mb4',//이모지
        collate : 'utf8mb4_general_ci'//한글저장
    
    });

    Comment.associate = (db)=>{
        db.Comment.belongsTo(db.User);//속해있다
        db.Comment.belongsTo(db.Post);//속해있다
    };

    return Comment;
}

image

module.exports = (sequelize, DataTypes)=>{
    const Image = sequelize.define('Image', {
       src:{
            type:DataTypes.STRING(200),
            allowNull:false,
       },
    
    }, 
    
    {
        charset : 'utf8',//이모지
        collate : 'utf8_general_ci'//한글저장
    
    });

    Image.associate = (db)=>{
        db.Image.belongsTo(db.Post);
    };

    return Image;
}

hashtag

module.exports = (sequelize, DataTypes)=>{
    const Hashtag = sequelize.define('Hashtag', {
       name:{
            type:DataTypes.STRING(20),
            allowNull:false,
       },
    
    }, 
    
    {
        charset : 'utf8mb4',//이모지
        collate : 'utf8mb4_general_ci'//한글저장
    
    });

    Hashtag.associate = (db)=>{// 다 : 다
        db.Hashtag.belongsToMany(db.Post, {through:'PostHashtag'});//다 : 다
    };

    return Hashtag;
}

app.js

const express =require('express');
const postRouter = require('./routes/post');
const db = require('./models');
const app = express();

db.sequelize.sync()
    .then(()=>{
        console.log('db연결성공!!!')
    })
    .catch((error)=>
    console.log(error));


app.use('/post',postRouter);

app.listen(3065, ()=>{
    console.log('실행중....')
});

 

 

 

여기까지 하고 node.app 실행시키면

www.jetbrains.com/datagrip/

 

DataGrip: The Cross-Platform IDE for Databases & SQL by JetBrains

A powerful IDE from JetBrains for SQL on macOS, Windows, and Linux.

www.jetbrains.com

 

CORS 문제 

: 브라우저 -> 백엔드서버, 다른 서버로 보낼때 생긴다. (브라우저가 차단해버림) 브라우저 -> 백엔드

: 서버->서버로 보낼땐 문제 없음, 브라우저 -> 프론트 ->백엔드 이렇게는 문제 없음 (proxy 방식)

: 차단은 브라우저가, 허용은 서버에서 허용해달라고 브라우저에게 요청하는 방법도 있음 

res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3060');//혹은 주소 대신'*'

 

cors middleware

app.use(cors({
    origin : '*',//* 는 모두 허용, origin : true로 설정하면 * 대신 보낸 곳의 주소가 자동으로 들어가 편라함
    credentials : false,//나중에 true 로 설정하기 
}));//middleware

 

 

COOKIE & SESSION

백엔드 서버가 유저 정보를 브라우저한테 통째로 보내주는데 비밀번호 같은거는 랜덤한 문자 = 랜덤한 토큰으로 보내준다. (이게 바로 쿠키)

서버쪽은 이 쿠키랑 연결되어 있다. 

이게 바로 브라우저 쿠키와 서버쪽 세션이다. (브라우저->서버로 쿠키도 같이 보내줄때 서버는 쿠키를 읽어서, 분해해서 어떤 사용자인지 알 수 있다.)

 

서버측에서는 메모리를 아끼기 위해서 쿠키랑 유저 아이디 정보만 서버에 저장, 나중에 디비에서 데이터 복구함

 

 

 

원리 : 브라우저와 백엔드 서버의 도메인이 다르면 cors 문제가 발생 & 쿠키도 전달이 안된다. -> 백엔드 서버는 어리둥절...

cors 이슈 해결방법 : proxy 설정 / header 로 cors 모듈 활용

 

app.use(cors({
    origin : '*',//* 는 모두 허용, origin : true로 설정하면 * 대신 보낸 곳의 주소가 자동으로 들어가 편라함
    credentials : false,//나중에 true 로 설정하기 
}));//middleware

문제 : 쿠키가 전달이 안되면 백엔드 서버가 요청 보낸사람이 누군지 알 수 있는데, 로그인을 했다고 하더라도 그다음 요청이 백엔드 서버는 누가 보냈는지 몰라!

 

쿠키를 다른 도메인간 전달 해결 방법 :

 

 ---- server

app.use(cors({
    origin : '*',//* 는 모두 허용, origin : true로 설정하면 * 대신 보낸 곳의 주소가 자동으로 들어가 편라함
    credentials : true,//나중에 true 로 설정하기 (false  하면 서버로 쿠키가 전달 안됨)
}));//middleware

---- front

//공통적용
axios.defaults.baseURL = 'http://localhost:3065';
axios.defaults.withCredentials=true;//백 & 서버 쿠키 공유 해결방법 

 

 

 

여기까지 설정하고 로그인 시도했는데 로그인이 에러가 뜬다.

 

여기에서의 * 는 브라우저에 아무나 접근이 가능하도록 하겠다는 의미인데, 이제는 무조건 주소를 적어라는 이야기. 

즉, 프론트-백엔드에서 쿠키 = 민감한 정보를 오고가게 하는걸 좀 더 엄격하게 관리하겠다는 이야기 

 

 

origin 에서 프론트 서버 포트 적어주기

app.use(cors({
    //front server!!
    origin : 'http://localhost:3000',//* 는 모두 허용, origin : true로 설정하면 * 대신 보낸 곳의 주소가 자동으로 들어가 편라함
    credentials : true,//나중에 true 로 설정하기 (false  하면 서버로 쿠키가 전달 안됨)
}));//middleware

 

 

 

 

 

 

ps) SEQUALIZE 부터 받는 DATA는 자바스크립트 객체로 받는다. -> 이걸 toJson을 사용해서 바꿔줘야 한다. 

 

 

user router

//middleware라서 위->아래 순차적으로 실행된다. 위치 조심!
const express =require('express');//import
const {User, Post} = require('../models');
const bycrypt = require('bcrypt');//비번 암호
const router = express.Router();
const passport = require('passport');

//login 전략
//return done(null, false, {reason : '존재하지 않음'})
//middleware 확장하는 방법
router.post('/login', (req,res,next)=> {
    //2. saga에서 받은 데이터를 가지고 전략을 실행함
    passport.authenticate('local', (err, user, info)=>{
        //5. local 전략 완료 후 여기서 로긴 실행하기 
        if(err){
            //서버에러
            console.log(err);
            return next(err);
        }
        if(info){
            //client error
            return res.status(403).send(info.reason);
        }
    
        //req.login 시 passport로 로그인 한다. -> 세션 로그인 저장해줌
        //6. 여기서 passport login 한다 -> passport 실행은 index.js에서 
        return req.login(user, async (loginerr) => {
            if(loginerr){
                console.log(loginerr);
                return next(loginErr);
            }

            const fullUserWithoutPassword = await User.findOne({
                where : { id : user.id },
                attributes : {
                    exclude : ['password']
                },
                include : [
                    {
                         model : Post, // hasMany라서 Post -> 복수형이 되어 Posts로 된다.
                    },
                    {
                        model : User,
                        as : 'Followings'
                    },
                    {
                        model : User,
                        as : 'Followers'
                    },
            ],
            })
            return res.status(200).json(fullUserWithoutPassword);
        })

    })(req,res,next);
});


router.post('/user/logout', (req,res)=>{
    req.logout();
    req.session.destroy();//session 에 저장된 쿠키, 아이디를 없애기
    res.send('ok');//logout 
})

router.post('/', async (req,res,next)=>{//POST : /user/
    //비동기는 대부분 try-catch 로 감싸주기
    //(비)동기 함수인지는 공식 문서 찾아봐야함 
    //front saga에서 data.email, data.nickname
    try {

        const exUser = await User.findOne({
            //조건문 where
            where:{
                email : req.body.email,
            }
        });

        //요청/응답 : 헤더(상태, 용량, 시간, 쿠키) 와 바디 (데이터)로 구성됨
        if(exUser){
            return res.status(403).send('이미 사용중')//return 중요!
        }


        const hashedPassword = await bycrypt.hash(req.body.password, 12);
        await User.create({//실행 --1
            email : req.body.email ,
            nickname : req.body.nickname ,
            password: hashedPassword,
        })

        //res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3060');//혹은 주소 대신'*'
        res.status(201).send('ok');//실행 --2

    } catch (error) {
        console.log(error);
        next(error);//error 한방에 처리해줌, 500번대 서버 에러 
    }
});

module.exports = router;

//로그인시 내부적으로 res.setHeader('Cookie', 'cxlhy')이런식으로 쿠키 서버로 보내주고 알아서 세션과 연결해준다. 
            //8. 쿠키, 유저 아이디 서버에서 들고있고 프론트에 보내준다
            //user 사용자 정보를 프론트로 넘기기-> 프론트에서 action.data로 받고 -> reducer
            //1. saga 에서 데이터 보내면 -> 
            //2. server routes user.js로 전송 son(user); 로 서버가 받음-> 
            //3. saga에서 action.data로 받고 -> 
            //4. reducer 에서는 me로 받음

            //fullUserWithoutPassword에는 내 게시글숫자, 팔로잉 정보들때문에 따로 더 추가함, 비번 전달 제외하고(attributes : exclude)!

local.js

//local login 전략
const passport = require('passport');
const {Strategy : LocalStrategy } =require('passport-local');
const {User} = require('../models');
const bcrypt =require('bcrypt');


//index.js 에서 local()에서 실행됨
module.exports =()=>{
 passport.use(new LocalStrategy({
     //프론트.data -> 서버.req.body 에 대한 설정
     //3. 전략 세우기
     usernameField: 'email',
     passwordField: 'password',
 }, async (email, password, done)=>{
    
    //비동기 요청시 항상 감싸주기
    try {
        //함수 전략
        const user = await User.findOne({
            where:{email}
        });

        if(!user){
            //결과 판단 : done (서버에러, 성공, 클라이언트 에러)
        return done(null, false, {reason : '존재하지 않음'})
        }

        const result = await bcrypt.compare(password, user.password);

        if(result){
            return done(null, user);//4. done 은 callback 함수 -> routes.user.js에서 로그인으로 넘겨주기
        }

        return done(null, false, {reason : '비밀번호가 틀렸다.'})

    } catch (error) {
        console.log(error);
        return done(error);
    }
 }));
}

index.js

const passport = require('passport');
const local=require('./local');
const {User} = require('../models');



//app.js 에서 실행된다. 
//passport login 실행공간
module.exports=()=>{
    //passport login 실행공간
    //7. 쿠키랑 유저 아이디 정보만 서버에 저장
    passport.serializeUser((user, done)=>{
        done(null, user.id)
    });

    //9. 로그인 성공후 아이디로부터 사용자 정보를 디비를 통해서 복구한다.
    //로그인 한 이후부터 router 실행되기 전에 매번 실행된다. 
    passport.deserializeUser(async (id, done)=>{
       try {
            const user =  await User.findOne({where : {id}});
            done(null, user) // 이 정보를 req.user 안에 내 정보 넣어준다.
       } catch (error) {
           console.error(error);
           done(error);
       }
    });

    local();
}

 

app.js

const express = require('express');
const cors = require('cors');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const passport = require('passport');
const dotenv = require('dotenv');

const postRouter = require('./routes/post');
const userRouter = require('./routes/user');
const db = require('./models');
const passportConfig = require('./passport');

dotenv.config();
const app = express();
db.sequelize.sync()
  .then(() => {
    console.log('db 연결 성공');
  })
  .catch(console.error);
passportConfig();



app.use(cors({
    origin : '*',//* 는 모두 허용, origin : true로 설정하면 * 대신 보낸 곳의 주소가 자동으로 들어가 편라함
    credentials : false,//나중에 true 로 설정하기 
}));//middleware

app.use(express.json());//위에 적기, front에서 json  형식으로 데이터 넘기면-> req.body에 제이슨 넣어줌
app.use(express.urlencoded({ extended : true }));//위에 적기, 폼 : urlencoded -> req.body에 넣어줌

//login 관련!
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session());
app.use(passport.initialize());

app.use(passport.session({
    saveUninitialized : false,
    resave : false,
    secret : process.env.COOKIE_SECRET,//secret 이 해킹당하면 데이터가 노출된다.
}));
    

app.use('/user',userRouter);
app.use('/post',postRouter);





app.listen(3065, ()=>{
    console.log('실행중....')
});

.env

COOKIE_SECRET=nodebirdsecret
DB_PASSWORD=1234

config.js

const dotenv = require('dotenv');

dotenv.config();


module.exports={
  "development": {
    "username": "root",
    "password": process.env.DB_PASSWORD,
    "database": "react-nodebird",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": process.env.DB_PASSWORD,
    "database": "react-nodebird",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": process.env.DB_PASSWORD,
    "database": "react-nodebird",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}

 

 

 

위 : 쿠키 // 아래 : 내 데이터

www.passportjs.org/packages/passport-kakao/

 

passport-kakao

kakao oauth2 login module

www.passportjs.org

 

middlewares.js

//login 검사용
exports.isLoggedIn = (req,res,next)=>{
    if(req.isAuthenticated()){
        next();//다음 미들웨어로 간다. 
    }else{
        res.status(401).send('로그인 필요함')
    }
}

exports.isNotLoggedIn = (req,res,next)=>{
    if(!req.isAuthenticated()){
        next();//다음 미들웨어로 간다. 즉, (req,res,next)으로 간다
    }else{
        res.status(401).send('로그인 필요함')
    }
}

 

 

디비에 등록한 컬럼들을 모두 반영해서 프론트로 보내주지 않으면 발생하는 에러

 

 

아래와 같이 추가정보 더 적어서 보내주기

// PostForm -> reducer-> saga -> server -> saga(result.data 안에 들어있음)->reducer
router.post('/', isLoggedIn,  async (req,res, next)=>{//POST /post
    try {
        const post = await Post.create({
            content : req.body.content,
            UserId : req.user.id,//누가 썼는지? 로그인 해서 알 수 있다. from passport deserializeUser
        })

        //디비 테이블열에 맞춰서 부족한 부분 채워넣기
        //전체 정보 완성해서 프런트에 돌려주기 
        const fullPost = await Post.findOne({
            where : {id : post.id},
            include : [{
                model : Image,
            },{
                model : Comment,
            },{
                mode : User,
            }]
        })

        res.status(201).json(fullPost)//to front sagas

    } catch (error) {
        console.log(error);
        next(error);
    }
});

혹은 아래처럼

//login 전략
//return done(null, false, {reason : '존재하지 않음'})
//middleware 확장하는 방법
router.post('/login',isNotLoggedIn, (req,res,next)=> {
    //2. saga에서 받은 데이터를 가지고 전략을 실행함
    passport.authenticate('local', (err, user, info)=>{
        //5. local 전략 완료 후 여기서 로긴 실행하기 
        if(err){
            //서버에러
            console.log(err);
            return next(err);
        }
        if(info){
            //client error
            return res.status(403).send(info.reason);
        }
    
        //req.login 시 passport로 로그인 한다. -> 세션 로그인 저장해줌
        //6. 여기서 passport login 한다 -> passport 실행은 index.js에서 
        return req.login(user, async (loginerr) => {
            if(loginerr){
                console.log(loginerr);
                return next(loginErr);
            }

            const fullUserWithoutPassword = await User.findOne({
                where : { id : user.id },
                attributes : {
                    exclude : ['password']
                },
                include : [
                    {
                         model : Post, // hasMany라서 Post -> 복수형이 되어 Posts로 된다.
                    },
                    {
                        model : User,
                        as : 'Followings'
                    },
                    {
                        model : User,
                        as : 'Followers'
                    },
            ],
            })
            return res.status(200).json(fullUserWithoutPassword);
        })

    })(req,res,next);
});

 

 

 

쿠키확인 :  connect.sid (connect & express session이 만든 쿠키!)

이 쿠키만 있으면 서버쪽에서 로긴이 된건지 안된건지 알 수 있다. 

그런데 새로고침할때 이 만들어진 쿠키가 서버쪽으로 전달이 안되어서 로그인이 풀린것 처럼 보인거지 실제론 브라우저는 쿠키로 가지고 있기 때문에 언제든지 이 쿠키를 서버쪽으로 전달만 해주면 다시 로그인 상태로 돌아올 수 있다. 

 

//사용자 불러오기 
router.get('/', async (req,res,next)=>{//GET /user
    try {
            if(req.user){//즉, 로그인 된 상태에서 새로고침 될때만 불러오기 
                const fullUserWithoutPassword = await User.findOne({
                    where : { id : req.user.id },
                    attributes : {
                        exclude : ['password']
                    },
                    include : [
                        {
                             model : Post, // hasMany라서 Post -> 복수형이 되어 Posts로 된다.
                             attributes : ['id'],//개수만 불러올거라서..
                        },
                        {
                            model : User,
                            as : 'Followings',
                            attributes : ['id'],//개수만 불러올거라서..

                        },
                        {
                            model : User,
                            as : 'Followers',
                            attributes : ['id'],//개수만 불러올거라서..
                        },
                ],
                })
                res.status(200).json(fullUserWithoutPassword);
            }else{
                //logut 된 상태
                res.status(200).json(null);
            }
    
    } catch (error) {
        console.error(error);
        next(error);
    }
})

 

서버로부터 필요한 정보만 불러오기

include : [
                    {
                         model : Post, // hasMany라서 Post -> 복수형이 되어 Posts로 된다.
                         attributes : ['id'],//개수만 불러올거라서..
                         
                    },
                    {
                        model : User,
                        as : 'Followings',
                        attributes : ['id'],//개수만 불러올거라서..
                    },
                    {
                        model : User,
                        as : 'Followers',
                        attributes : ['id'],//개수만 불러올거라서..
                    },
            ],

 

//uploads 파일 경로를 프론트와 공유하기 위해서 설정
app.use('/', express.static(path.join(__dirname, 'uploads')))// '/' 의미 : localhost:3065/

 

 

해쉬태그

'첫 번째 게시글 #해시태그 #익스프레스'.match(/#[^\s#]+/g);

 

 <div>
            {postData.split(/(#[^\s#]+)/g).map((v, i)=>{
                //반복문 + #일치 
                //반복문이 바뀌지 않을때 index 키로 사용해도 된다. 
                if(v.match(/#[^\s#]+/g)){//#모양인 애들만 링크
                    return <Link href={`/hashtag/${v.slice(1)}`} key={i}><a>{v}</a></Link>
                }
                return v;//모든 글자 출력
            })}
        </div>

 

 

 

 if(hashtags){
            //tag.slice(1) : hashtag 때는것 + 소문자 저장
            //findOrCreate :  없을때만 등록되고 있을때는 가져오는걸로 바뀜 -> 나중에 태그 검색서비스 때문
            const result = await Promise.all(hashtags.map((tag)=>Hashtag.findOrCreate({ 
               where : {name : tag.slice(1).toLowerCase()}
            })));//findOrCreate result 결과 모양 : [[노드,true],[리액트, true]] -> 생성된건지 찾은건지..
            await post.addHashtags(result.map((v)=>v[0]));


        }