TIL 52일차 - 클론코딩 마지막 수정

박찬웅·2023년 3월 29일
0

항해99

목록 보기
57/105

23년 3월 29일

배운 것

오늘 최종적으로 마지막 수정을 하는 작업을 가졌다.

시도 한 것

오늘은 전반적으로 프로젝트에서 내가 직접 새로운 코드를 구현한거는 거의 없었고, 우리 중에서 팀원 한 분이 새로운 것들을 한번 적용을 했다고 해서, 그 부분에 대해서 복습하는 시간을 가졌다.
남은 것들은 프론트앤드분들이 구현을 하면 되는 부분이라서 몇가지 수정을 하였다.

먼저 내가 적었던 댓글을 3계층으로 분리 했었는데 repository에서 에러핸들링과 데이터베이스 관리를 모두 했었다. 그래서 서비스에서 하는 역할이 거의 전무 했었는데 다음과 같이 수정을 하였다.

const CommentRepository = require("../repositories/comment.repository.js")
const PostRepository = require("../repositories/post.repository.js")
const CustomError = require("../middlewares/errorhandler.js")


class CommentService {
    constructor() {
        this.PostRepository = new PostRepository();
        this.CommentRepository = new CommentRepository();
    }

    createComment = async ({ postId, userId, nickname, comment }) => {
        // 게시글 존재 확인
        const post = await this.PostRepository.CheckPost({ postId })
        //댓글 작성
        await this.CommentRepository.createComment({ postId, userId, nickname, comment })
    }

    getComment = async ({ postId }) => {
        return await this.CommentRepository.getComment({ postId })
    }

    deleteComment = async ({ postId, commentId, userId }) => {
        // 게시글 존재 확인
        await this.PostRepository.CheckPost({ postId })
        //댓글 존재 확인
        const existComment = await this.CommentRepository.checkComment({commentId})
        //댓글 삭제
        await this.CommentRepository.deleteComment({ commentId, userId,existComment })
    }


}

module.exports = CommentService;

댓글 서비스 부분에서 원래는 그냥 await만 적고 바로 레파지토리로 갔었지만 여기서 게시글 존재 확인과 댓글 존재 확인 하는 await 문을 적고 새로운 메소드 역할을 만들어서 전달하는 부분을 추가하였다. 게시글 존재 확인 부분은 다른 팀원의 게시글 레포지토리 파일에서 에러 핸들링을 할 수 있게 구현을 해 주셨다.

const { Comments, Users } = require("../models");
const CustomError = require("../middlewares/errorhandler.js")

class CommentRepository {
    constructor() { }

    createComment = async ({ postId, userId, nickname, comment }) => {
        // 댓글 미입력 시
        if (!comment) {
            throw new CustomError("댓글을 입력해주세요", 412)
        }
        // 댓글 생성
        await Comments.create({
            postId: postId,
            userId: userId,
            nickname: nickname,
            comment: comment,
        });
    }

    getComment = async ({ postId }) => {
        return await Comments.findAll({
            attributes: [
                "commentId",
                "User.nickname",
                "User.profileImg",
                "comment",
                "createdAt",
                "updatedAt",
            ],
            include: [
                {
                    model: Users,
                    attributes: [],
                }
            ],
            where: [{ postId: postId }],
            order: [['createdAt', 'DESC']], // 작성 날짜 기준으로 내림차순
            raw: true, // JSON 형태로 반환된 데이터를 처리
        })
    }
    getLimitComment = async ({ postId }) => {
        return await Comments.findAll({
            attributes: [
                "commentId",
                "postId",
                "User.nickname",
                "User.profileImg",
                "comment",
                "createdAt",
                "updatedAt",
            ],
            include: [
                {
                    model: Users,
                    attributes: [],
                }
            ],
            where: [{ postId: postId }],
            order: [['createdAt', 'DESC']], // 작성 날짜 기준으로 내림차순
            limit : 3,
            raw: true, // JSON 형태로 반환된 데이터를 처리
        })
    }
    checkComment = async ({ commentId }) => {
        // 댓글을 조회합니다.
        const existComment = await Comments.findOne({ where: { commentId: commentId } });
        if (!existComment) {
            throw new CustomError("댓글이 존재하지 않습니다", 404)
        }
        return existComment
    }

    deleteComment = async ({ commentId, userId, existComment }) => {
        // 로그인한 회원의 유저 아이디와 댓글 작성한 회원 아이디가 다른 경우
        if (existComment.userId !== userId) {
            throw new CustomError("댓글 삭제의 권한이 존재하지 않습니다", 403)
        }
        // 댓글의 권한을 확인하고, 댓글을 삭제합니다.
        await Comments.destroy(
            { where: { commentId: commentId } }
        );
    }
}

module.exports = CommentRepository;

댓글 레포지토리에서 먼저 checkComment 메소드는 댓글을 참조해서 commentId 없으면 에러 핸들링을 따로 분리를 시켜놓았다. 그리고 원래는 없었는데 getLimitComment 메소드가 새로 추가되었는데 이 부분은 댓글쪽에서는 크게 볼건 없었지만 게시글 전체 조회 할 때 댓글도 최대 3개까지만 보여 줄 수 있게 구현을 하는 작업이 있어서 같이 들어 있었다. 보면 limit : 3, 이라는 것이 새로 있는데 이것은 한 게시글마다 최대 3개까지만 보여 줄 수 있는 기능을 말한다.
그리고 deleteComment 메소드에서 existComment 파라미터를 가져왔는데 바로 위에 checkComment 메소드에서 existComment 참조하여서 댓글 작성한 유저아이디와 현재 접속한 유저아이디가 다를 경우 삭제 못하게 수정을 하였다.

이 외에는 나머지는 기존에 있던 부분이라서 크게 수정된건 없었다.

그리고 또 한가지 적용 한게 winston 라이브러리를 적용하였다. 이것은 무엇이냐면 보통 개발자들이 터미널로 뭔가 요청을하고 작성을 하면 성공하는 것도 있고, 에러가 나는 것도 볼 수 있다. 근데 이런 것들을 logger에다가 기록을 남겨 줄 수 있는 기능이 있는데 바로 winston 이라는 패키지이다. npm i winston 패키지를 설치하면 사용 할 수 있다.
app.js에다 다음과 같은 코드를 추가 하면 된다.

const logger = require("./config/logger.js");
...
//에러핸들링 미들웨어
app.use((err, req, res, next) => {
    res.status(err.status || 500);
    err.message = err.message || '예상치 못한 에러가 발생하였습니다.'
  
    logger.error(err.stack)
    res.json({errormessage : err.message});
})

그런 다음에 config.js 폴더에다가 logger.js 파일을 만들고 다음의 코드들을 추가하면 된다.

const { createLogger, transports, format } = require("winston");
const { combine, colorize, timestamp, printf} = format;
const env = process.env

const printLogformat = combine(
    timestamp({
        format: "YYYY-MM-DD HH:mm:dd"
    }),
    printf(({ timestamp, level, message }) => { 
        return `${timestamp} [${level}] ${message}`
    }) 
)
const ConsoleprintLogformat = combine(
    colorize(),
    printf(({ level, message }) => {
        return `[${level}]${message} `
    })
)
const logger = createLogger({
    transports: [
        new transports.File({
            filename: "access.log",
            dirname: "./logs",
            level: "info",
            format: printLogformat
        }),
    ]
});

//배포환경에서는 콘솔에 찍지 않고 파일에만 저장하기
if (env.NODE_ENV !== "production") {
    logger.add(new transports.Console({
        level: "info",
        format: ConsoleprintLogformat
    }))
}

module.exports = logger;

대부분 winston을 사용하면 약간의 차이는 있지만 기본적으로 사용하는 양식이다.
이렇게 하면 오류가 났을 때 vscode에서도 logger 파일이 만들어져서 어떤 로그들이 있었고, 어떤 에러들이 발생했는지 알 수 있다. 마지막으로 아래 배포 환경일때는 콘솔에 찍지 않고 파일에만 저장하게 구현을 하였다고 팀원분이 설명을 해 주셨다.

마지막으로 multer 사용하는 방법을 알려 주셨다. 해당 부분은 route 파일에다 사용하면 된다고 하셨다.

const multer = require('multer')
var path = require('path');
...
//multer
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/postImg/')  //uploads라는 폴더에 저장할꺼다
    },
    filename: function (req, file, cb) {
        const ext = path.extname(file.originalname)
        cb(null, path.basename(file.originalname, ext) + Date.now() + ext);
    }
})
const fileFilter = (req, file, cb) => {
    const typeArray = file.mimetype.split('/');
    const fileType = typeArray[1];

    if (fileType == 'jpg' || fileType == 'png' || fileType == 'jpeg') {
        req.fileValidationError = null;
        cb(null, true);
    } else {
        req.fileValidationError = "jpg,jpeg,png파일만 업로드 가능합니다.";
        cb(null, false)
    }
}
const upload = multer({
    storage: storage,
    fileFilter: fileFilter
})

해당 코드는 multer 패키지를 설치하고 난 후 path는 저장할 파일 경로를 지정을 하면 된다. 이렇게 하면 로컬 파일에다가 저장 할 수 있게 구현하는 양식이라고 한다. 우리 팀 같은 경우에는 jpg, png, jepg만 업로드 되게 설정을 해 줬는데 그 외에 파일 타입을 추가 하고 싶으면 해당 타입을 더 넣으면 된다. 우리팀은 사진 1장만 올릴 수 있게 구현을 했는데 이때에는 다음과 같이 수정하면 된다.

router.post("/", authMiddleware, upload.single('img'), postcontroller.CreatePost)

upload.single()는 사진 한장만 올리는 역할을 한다.

이렇게 팀원 한 분이 사용한 방법을 알려주셨고 이 외에 시간에는 내가 적은 코드들을 다시 한번 더 복습을 하였다. 오늘은 뭔가 전반적으로 공부 많이 한 것 같지는 않은감이 있었지만 이렇게 하루가 지나갔다.

해결

오늘은 구현보다는 팀원분들이 적용한 것들을 알려주셨다. 내가 처음에 구현했던 댓글 api 3계층을 보완을 해 주셨고 winston, multer 패키지 사용하는 방법을 알게 되었다.

알게 된 점

우리 팀원 중 한분이 아주 새로운 것들을 적용해서 알려 주셔서 뭔가 우리한테 새로운 것들을 가르쳐 주셨다. 물론 내가 한 것이 아니라 팀원이 한 것이기에 직접 구현한 것은 아니지만, 새로운 기능을 적용 할 수 있게 구현을 해 주셨다. 물론 아직 완벽히 이해된 건 아니지만 나중에 실전 프로젝트때 활용하면 좋을 것 같다는 느낌을 받았고 추후에 실전때 비슷한걸 적용하면 될 것 같다는 느낌을 받았다.

앞으로 할 일

드디어 내일이 클론코딩 프로젝트의 마지막 날로 인스타그램을 최종배포를 마무리 하고 발표하는 시간을 가지게 된다. 우리 node 백앤드 분들은 필요한 것들은 모두 구현을 했기 때문에, 이제 남은건 프론트앤드분들이 완성을 하는걸 기다려서 무사히 구현 마무리 하고 배포 하기를 바란다.

profile
향해 13기 node.js 백앤드

0개의 댓글