그저 내가 되었고

항해99) 3주차:: 주특기 입문; 개인과제 전체 코드(JS) 본문

개발/항해99 9기

항해99) 3주차:: 주특기 입문; 개인과제 전체 코드(JS)

hyuunii 2022. 10. 7. 10:09

Directory Structure

.

├── app.js

├── routes

│     ├── index.js

│     ├── comments.js

│     └── posts.js

└── schemas   

        ├── index.js   

        ├── comment.js   

        └── post.js


app.js

//express 프레임웤 구동. express는 노드로 서버를 빠르고 간편하게 만들 수 있게 도와주는 웹 프레임웤!!
const express = require('express');  // express 프레임웤을 express란 변수에 담고
const app = express(); //app이란 변수에!!!! 그 express 프레임웤을 할당!!! 그러면 일반적 서버 사용 위한 app 객체 생성됨~!
//서버 구동은 전부 객체를 참조한다.

const connect = require("./schemas/index");  //connect라는 변수로 schemas의 모듈 갖고 올 것(저기에 원래 저렇게 /index라고 적어줘서 index만 갖고오려고 하는 것1!!! 근데 걍 schemas까지만 적어줘도.. 폴더 이름까지만 적어주면 자동적으로 갖고오니까 안의 index 알아서 찾아서 보여줌. connect 이름은 그냥 알아보기 쉽게 하는거고.. 뭐 네가 aaa하든 bbb하든 밑에 aaa();만 잘 맞춰주면 알아서 잘 돌아감ㅋ
connect();  //위에서 require( )모라는 함수를 connect에 할당해줬고, connect변수는 함수를 품고 있으니까 저렇게만 적어줘도 함수 실행 가능~!
// const abc =  (a, b) => { return a + b}
// console.log(abc(1, 2));
//함수...???????

app.use(express.json());  //왜 굳이 데이터가 제이슨 형식이어야 하는지?...ㅜㅜ req~res 중간에 middleware가 들어감. 모듈이 미들웨어가 될 수 있다. req.body를 파싱하려 사용함. 그래서 주석해도 get은 잘 돌아감. app.use는 미들웨어를 거쳐가도록!!!!!! 하는 app객체의 use함수.

const postsRouter = require("./routes/posts");  //라우츠 파일에서 내보낸 라우터를 할당함
const commentsRouter = require("./routes/comments");
app.use("/api", [postsRouter, commentsRouter]);  //반환받은 각각의 라우터를 익스프레스에다가 적용시키기. 익스프레스라는건 프레임워크? 지금 만들고있는 서버에 적용시킨다는 뜻인건가? ㅇㅇ 도구에다가 잘 적용해주는겨ㅎ... 그 도구가 알아서 서버가 잘 만들어주고있좌나,,,, 그릉까
//app.use라는 문법 통해서 적용시킬건데(API를 등록시킬건데), app.use는 app이라는 익스프레스 객체에다가 전역으로 쓸거란 뜻??? app.use는 미들웨어를 거쳐가도록!!!!!! 하는 app 객체의 use 함수.

//기본 url 접속시 respond. 이것도 라우터를 통해 기본url에 대한 라우팅을 한 것이라고 볼 수 있음. 지금까지는 app.get과 같이 http method로 만들었다고 한다면, 나머지는 router로!!
app.get('/', (req, res) => {  ////기본 url(/)에 req와 res를 받아서 실행하는 api를 만드는 것
    res.send('반가워용✨😎!');
});

//포트 열어줌
const port = 3002;
app.listen(port, () => {
    console.log(port, '포트로 서버가 열렸어요!');
});

 

 

routes/posts.js

const express = require('express');  //express 프레임웤 설치 후 express라는 변수에 담고
const router = express.Router();  //express안의 라우터 함수를 실행해서 그 결괏값을 라우터라는 변수에 할당. 실제로 api를 만들거나 라우터를 생성할 때는 이렇게 router라는 변수를 통해 사용한당!
const Posts = require("../schemas/post.js");  //post.js라는 스키마 파일을 Posts로 갖고온다~!
//두 가지 역할을 하는 것? 데이터 모델링 & 이미 db에 저장된것 다 불러오는?

//1. 전체 게시글 목록 조회 API
router.get("/posts", async (req, res) => {  //위에서 반환 받은 라우터라는 변수로 라우터 만들기. 기본 url 말고 추가적인 경로로 실행하는 API &&& req(API에 받은 데이터)랑 res 객체 받아서 실행할게~!
    //function 앞에 async를 붙이면 해당 함수는 항상 프라미스를 반환. 자바스크립트는 await 키워드를 만나면 프라미스가 처리될 때까지 기다림. 결과는 그 이후 반환됨.
    const item = await Posts.find().sort({updatedAt: -1})  //await에서 데이터가 올때까지 기다리게 함. find하고 sort하는 데이터가.. await나 async가 안걸려있으면? 먼저 나온 데이터는 먼저 쭉쭉 가버림. 여럿이 밥먹고 한명씩 나가는거 아니고 한명씩 다 가게,,, 데이터가 점점 더 많아진다는 가정 하에 필요한 것. 사용자가 많아지게되면 서버 부하가 되고 data양도 늘어서 볼륨이 커지니까 처리시간 길어지고ㅇㅇ
    //동기는 요청을 보낸 후 해당하는 응답을 받아야만 다음으로 넘어갈 수 있는 실행방식이고
    // 반대로 비동기는 요청을 보낸 후에 응답과 관계없이 다음 동작을 실행할 수 있는 방식입니다.
    //JS는 원래 동기!!! 그래서 async, await 사용해서 비동기처럼 작동하게끔 해 주는 것.
    const mapItem = item.map((item) => {
        return {
            postId : item._id,
            writer : item.writerName,
            title : item.title,
            content : item.contents,
            createdAt : item.createdAt
        }
    })
    res.json({item: mapItem}); //mapItem으로 받은 get메소드 리턴값을 반환할 res 객체
});

//params 안의 goodsId는 오브젝트라서 객체 구조 분해 할당을 해서 간단하게 처리하면 됨! 위의 주석 처리한 const goodsId랑 얘랑은 똑같지만, 이번엔 객체 구조 분해 할당을 사용하는 것~! 이렇게 URL 파라미터에 들어온 데이터를 {goodsId}라는 변수에 할당했드아

//2. 게시글 상세 조회 API
router.get("/posts/:_postId", async (req, res) => {
    const {_postId} = req.params;   //위의.. url 뒷편의 :(콜론) 뒷부분의 것 받아옴(이름 똑같이 적어줘야됨!!(_postID 그대로!!) 특정 url 뒤의 모든 데이터는 매개변수로 보며, 그 매개변수를 단지 받아올 뿐)
    //아니 이 앞뒤 {중괄호}의 의미가 뭐임⁉⁉⁉⁉⁉ 없으면 왜 결과가 비어서 나옴?????
    //중괄호를 씌워줘서 { _postId: '633d58cc0f1fd4cfa7037402' }이렇게 나옴. 안씌우고 그냥 콘솔에 찍으면 633d58cc0f1fd4cfa7037402 번호만 띡 나옴.
    //첨에 구조 분해 할당을 안하면 변수.tilte, 변수.contents이런식으로 불러와야되니까... 받아오면서 사전에 분해해서 깔아두는 것. 안그러면 일일이 객체 안에서 ㅊㄹ력해와야됨.
    console.log(_postId)
   // console.log(1+true)
   // console.log({_postId})
   // console.log(req.params)
    const item = await Posts.find();  //데이터 조회는 find 명령어 씀.
    //console.log(item)
    const filteredPosts = item.filter((x) => {  //이 filter의 문법?
     //   console.log( x["_id"].toString())
        return x["_id"].toString() === _postId;  //[_id]이거는 key값 이용해서 value 뽑는다는것은 알겠음. 그러면... 이게, 큰 객체 안에->배열 안에->객체 하나(그 안에 postId부터 다섯개 요소?)가 뽑힌다는건.. 생성할때 그렇게 생성해줘서 아는것....? 콘솔에 찍어보면 아는것... 바로 위에서 item 찍어봤쟈나융
    });  //filter가 배열의 속성 하나하나 가져와서 비교하니까..
    const mapPosts = filteredPosts.map((item) => {
         //console.log(item["_id"])//이거랑 밑줄의 결과가 같은데... 그냥 []든 .든 접근 방법은 상관X? ._id이게 람다식???? ㅇㅇ
         //console.log(item._id)//
        return {  //get은 찾는 것 까지 역할이고... 결과를 보여주는건 return이 하는것?(함수니까 return을 해줘야 결과가 빠짐)(return 빼고 돌리면 null값 나옴)
            postId : item["_id"],
            writer : item.writerName,
            title : item.title,
            content : item.contents,
            createdAt : item.createdAt
        }  //여기서 return 안에 한 객체 안에 이렇게 쭉 적어줘서 이 형태 그대로 return되는것? 그러면 따로 return하려면???(해보니까 오류남.. 방법이 없나효?) 질문💛💛💛💛💛
    })
    // const _postId = req.params._postId;
    // const [detail] = item.filter((item) => _id === _postId);
    res.json({mapPosts});
})


//3. 게시글 생성 API
router.post("/posts", async (req, res) => {
    const {title, writerName, password, contents} = req.body;
     //console.log(req.body)  //객체구조분해할당을 해서 value값만 가져오는것
    const createdPosts = await Posts.create([{title, writerName, password, contents}]);
    console.log(createdPosts)
    //여기 윗줄에.. [{....}]이렇게 되는것도.. 배열 안에 객체가 있다고 틀 잡아주려고? 아니 단순히 괄호 하나 집어넣는다고 자료 구조가 바뀜????????????????
      //console.log({title, writerName, password, contents})
    //console.log([{title, writerName, password, contents}])
    const mapItem = createdPosts.map((item) => {
        return {
            postId : item._id,
            writer : item.writerName,
            title : item.title,
            content : item.contents,
            createdAt : item.createdAt
        }
    })
    res.json({posts: mapItem});
})



//4. 게시글 수정 API
router.put("/posts/:_postId", async (req, res) => {
    const {_postId} = req.params;
    const {password, contents, title} = req.body;
    const existsPost = await Posts.find({_id:_postId});   //이렇게 : 콜론 하면 앞뒤로 같다는것..? 이런 문법이 있었나?ㅠㅠㅠㅠ 이것도 앞뒤로 중괄호 없으면 오류나는데. .대체 중괄호 역할 무엇..?   긴가민가 ??? :_postID를 파라미터로 받았으면 findOne에서 하나가 나와야할텐데 왜 다른게 나옴??
    //console.log(existsPosts)

                //console.log(existsPost[0].password)
                //console.log(existsPost[0]["password"]) //얘는 undefined고 밑에 []는 찾아짐..왜......?
    if (existsPost.length) {
        if (password === existsPost[0].password) {
            //console.log(_postId)
            await Posts.updateOne({_id: _postId}, {$set: {contents, title}});
            //console.log(_postId)
        } else {
            return res.status(400).json({success: false, errorMessage: "패스워드를 다시 입력해주세요"});
        }
    } else {
        return res.status(400).json({success: false, errorMessage: "찾으시는 게시물이 없어요"});
    }

    res.json({success: true});
})


//5. 게시글 삭제 API
router.delete("/posts/:_postId", async (req, res) => {
    const {_postId} = req.params;
    const existsPosts = await Posts.find({_id: _postId});
    const {password} = req.body;
    // console.log(existsPosts.length)
    // console.log(existsPosts)
    // console.log(existsPosts[0].password)
    // console.log(password)

    if (existsPosts.length) {
        if (password === existsPosts[0].password) {
            await Posts.deleteOne({_id: _postId});
        } else {
            return res.status(400).json({success: false, errorMessage: "패스워드를 다시 입력해주세요"});
        }
    } else {
        return res.status(400).json({success: false, errorMessage: "찾으시는 게시물이 없어요"});
    }

    res.json({result: "success"});
})


module.exports = router;

 

 

routes/comments.js

const express = require('express');

const Comments = require("../schemas/comment.js");

const router = express.Router();


//1. 댓글 목록 조회
router.get("/comments/:_postId", async (req, res) => {
    const {_postId} = req.params;  //게시물 ID 하나하나
    console.log(_postId)
    const existsPosts = await Comments.find({_postId: _postId}).sort({updatedAt: -1});
    //[  //find라서 배열을 뱉을것
    // {
    //     "writerName" : "ㄷaaa",
    //     "password" : "0000",
    //     "contents" : "hhhh"
    // }
    // ]   //20번째줄의 item은 배열 자체가 아니라 안의 요소 하나하나
    const mapComments = existsPosts.map((x) => {
        return {
            writer: x.writerName,
            contents: x.contents
        }
        // {
        //     xN : "ㄷaaa", xC :"hhhh"
        // }
    })
    //console.log(existsPosts)
    res.json({mapComments});
});


//2. 댓글 작성
router.post("/comments/:_postId", async (req, res) => {
    const {_postId} = req.params;
    const {writerName, password, contents} = req.body;
    console.log(contents)
    if (contents === "") {
        //res.send ("끗")
        return res.status(400).json({success: false, errorMessage: "댓글 내용을 입력해주세요"});
    } else {
        const createdComments = await Comments.create({_postId, writerName, password, contents});
        res.json({comments: createdComments});
    }
})


//3. 댓글 수정
router.put("/comments/:commentId", async (req, res) => {
    const {commentId} = req.params;
    const {password, contents} = req.body;
    const existsComments = await Comments.findOne({_id: commentId});
    // console.log(req.params)
    // console.log(req.body)
    // console.log(existsComments)
    // console.log(password)
    // console.log(existsComments.password)
    if (password === existsComments.password) {
        if (contents === "") {
            res.status(400).json({success: false, errorMessage: "댓글 내용을 입력해주세요"});
        } else {
            await Comments.updateOne({_id: commentId}, {$set: {contents}});
            res.send({result: "수정완료"})
        }
    } else {
        return res.status(400).json({success: false, errorMessage: "틀린 패스워드"});
    }
})


//4. 댓글 삭제
router.delete("/comments/:commentId", async (req, res) => {
    const {commentId} = req.params;
    const {password} = req.body;
    const existsComments = await Comments.findOne({_id: commentId});
    // console.log(req.params)
    // console.log(req.body)
    // console.log(existsComments)
    // console.log(password)
    // console.log(existsComments.password)
    if (password === existsComments.password) {
        await Comments.deleteOne({_id: commentId});
    } else {
        return res.status(400).json({success: false, errorMessage: "틀린 패스워드"});
    }
    res.json({result: "success"});
})

module.exports = router;

 

 

routes/index.js

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', { title: 'Express' });
});

module.exports = router;

 

 

schemas/comments.js

const mongoose = require("mongoose");

const commentsSchema = new mongoose.Schema({
        _postId: {
            type: String
        },
        writerName: {
            type: String,
            required: true,
        },
        password: {
            type: String,
            required: true,
        },
        contents: {
            type: String,
            required: true,
        },
    },
    {
        timestamps: true
    });

module.exports = mongoose.model("Comments", commentsSchema);

 

 

schemas/index.js

const mongoose = require("mongoose");  //mongoose 라이브러리 가져와서 connect(몽구스 라이브러리는 이미 우리가 설치했다!!!! 글고 그 node_modules 열어보면 몽구스 파일 있고, 걔 열어보면 몽구스를 익스포츠 해 주고 있음. 익스포츠를 어디선가 해주기때문에!!! require가 가능한 것)

const connect = () => {
    mongoose
        .connect("mongodb://localhost:27017/sparta_prac")  //여기에 연결하고 sparta_prac이란 db에 연결한다. 아무것도 안적어주면, 알아서 몽고db가 test라고 만들어서 넣어줌.
        .catch(err => console.log(err));  //connect가 실패(몽고db에 연결 실패)하면 에러처리 진행(콘솔로그로 발생한 에러 보여주세요~!)
};

mongoose.connection.on("error", err => console.error("몽고디비 연결 에러", err));  //몽구스 커넥션 실패하면 콘솔 에러로 보여줌

module.exports = connect;  //현재 모듈에서 connect를 내보내줘서 밖에서 mongodb랑 연결하고 사용할 수 있게 해 줌. 이걸.. app.js에서 받아서 쓸 것 임!~!

 

 

schemas/posts.js

//포스트 모델 작성. 데이터 관리하기 위해 모듈을 작성.
const mongoose = require("mongoose");  //몽구스 라이브러리를 mongoose란 변수에 담고

const postsSchema = new mongoose.Schema({  //몽구스 스키마를 새롭게 정의할게.
        title: {
            type: String,
            required: true,
        },
        writerName: {
            type: String,
            required: true,
        },
        password: {
            type: String,
            required: true,
        },
        contents: {
            type: String,
            required: true,
        },
    },
    {
        timestamps: true  //얘는.. 몽구스 라이브러리의 객체임. 직접 입력받을 객체?는 아니고 option으로 몽구스에 저장될때 옵션으로 몽구스가 알아서 그냥 따라 붙여 줄 애임.
    });

module.exports = mongoose.model("Posts", postsSchema);  //몽구스 스키마를 내보낼게. 저 name으로 app.js에서 PostsRouter이 되는 것? 아니라면 name은 왜 필요??? 아앗 그게 ㅎㅎㅎㅎ 몽구스에 들어갈 때,,, db 이름이 예를들어 sparta_prac이면 그 안에!!!! 짜잘하게 posts, coments 이렇게 각각의 데이터 탭!!!! 그거 말하는것ㅎ 글고 저기 그냥 Post로 적어줘도 자동으로 posts로 바꿔서 적힌당ㅎㅎㅎㅎ
//이게 모듈이라는건... 마지막에 그냥 모듈.익스포츠 이것땜에 모듈 역할을 할 수 있는 것? ㅇㅇ마즘쓰. 글고 모듈이라는건.. 지금 이 해당하는 파일 안에만 '격리'된 애들인데 ex로 내보내줘서 re로 받으면 드디어 접근해서 쓸 수 있게 해줌.