• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    미해결

Cannot read property 'Likers' of null

21.10.06 19:53 작성 조회수 137

0

고생이 많으십니다.

위 에러가 났는데 찾기가 너무 어려워서 올렸습니다.

찾는다고 찾았는데 두개의 이메일로 각각 게시글을 10개씩 썼는데도 막상 해당 post 주소로 들어가게 되면

 

이렇게 존재하지 않는 게시글(404)이라고 표시되고 있습니다.

그리고 콘솔창에 계속해서 Internal Server Error 500 에러 렌더링이 반복되고 있습니다.

 

코드는

 

routes/ post.js
 
const express = require('express')
const multer = require('multer')
const path = require('path')
const fs = require('fs')

const { PostImageCommentUserHashtag } = require('../models')
const { isLoggedIn } = require('./middlewares')

const router = express.Router()

try {
    fs.accessSync('uploads')
catch (error) {
    console.log('uploads 폴더가 없으므로 생성합니다.')
    fs.mkdirSync('uploads')
}

const upload = multer({
    storage: multer.diskStorage({
        destination(reqfiledone) {
            done(null'uploads')
        },
        filename(reqfiledone) { // 제로초.png
            const ext = path.extname(file.originalname// 확장자 추출(.png)
            const basename = path.basename(file.originalnameext// 제로초
            done(nullbasename + '_' + new Date().getTime() + ext// 제로초15134314.png
        }
    }),
    limits: { fileSize20 * 1024 * 1024 } // 20mb
})

router.post('/'isLoggedIn, upload.none(), async (reqres=> { // POST /post
    try {
        const hashtags = req.body.content.match(/#[^\s#]+/g)
        const post = await Post.create({
            content: req.body.content,
            UserId: req.user.id
        })
        if (hashtags) {
            const result = await Promise.all(hashtags.map((tag=> Hashtag.findOrCreate({ where: { name: tag.slice(1).toLowerCase() } }))) // [[노드, true], [리액트, true]]
            await post.addHashtags(result.map((v=> v[0]))
        }
        if (req.body.image) {
            if (Array.isArray(req.body.image)) { // 이미지를 여러개 올리면 image: [제로초.png, 부기초.png]
                const images = await Promise.all(req.body.image.map((image=> Image.create({ srcimage })))
                await post.addImages(images)
            } else { // 이미지를 하나만 올리면 image: 제로초.png
                const image = await Image.create({ src: req.body.image })
                await post.addImages(image)
            }
        }
        const fullPost = await Post.findOne({
            where: { id: post.id },
            include: [{
                modelImage
            }, {
                modelComment,
                include: [{
                    modelUser// 댓글  작성자
                    attributes: ['id''nickname']
                }]
            }, {
                modelUser// 게시글 작성자
                attributes: ['id''nickname']
            },{
                modelUser// 좋아요 누른사람
                as'Likers',
                attributes: ['id']
            }]
        })
        res.status(201).json(fullPost)
    } catch (error) {
        console.error(error)
        next(error)
    } 
})

router.post('/images'isLoggedIn, upload.array('image'), (reqresnext=> { // POST /post/images
    console.log(req.files)
    res.json(req.files.map((v=> v.filename))
})

router.post('/:postId/comment'isLoggedInasync (reqresnext=> { // POST /post/1/comment
    try {
        const post = await Post.findOne({
            where: { id: req.params.postId }
        })
        if (!post) {
            return res.status(403).send('존재하지 않는 게시글입니다.')
        }
        const comment = await Comment.create({
            content: req.body.content,
            PostIdparseInt(req.params.postId10),
            UserId: req.user.id
        })
        const fullComment = await Comment.findOne({
            where: { id: comment.id },
            include: [{
                modelUser,
                attributes: ['id''nickname']
            }]
        })
        res.status(201).json(fullComment)
    } catch (error) {
        console.error(error)
        next(error)
    } 
})

router.get('/:postId'async (reqresnext=> { // GET /post/1
    try {
        const post = await Post.findOne({
            where: { id: req.params.postId }
        })
        if (!post) {
            return res.status(404).send('존재하지 않는 게시글입니다.')
        }
        const fullPost = await Post.findOne({
            where: { id: post.id },
            include: [{
                modelPost,
                as'Retweet',
                include: [{
                    modelUser,
                    attributes: ['id''nickname']
                }]
            }, {
                modelUser,
                attributes: ['id''nickname']
            }, {
                modelImage
            }, {
                modelComment,
                include: [{
                    modelUser,
                    attributes: ['id''nickname']
                }]
            }, {
                modelUser,
                as'Likers',
                attributes: ['id']
            }, {
                modelPost,
                as'Retweet',
                include: [{
                    modelUser,
                    attributes: ['id''nickname']
                }, {
                    modelImage
                }]
            }]
        })
        res.status(200).json(fullPost)
    } catch (error) {
        console.error(error)
        next(error)
    } 
})

router.post('/:postId/retweet'isLoggedInasync (reqresnext=> { // POST /post/1/retweet
    try {
        const post = await Post.findOne({
            where: { id: req.params.postId },
            include: [{
                modelPost,
                as'Retweet'
            }]
        })
        if (!post) {
            return res.status(403).send('존재하지 않는 게시글입니다.')
        }
        if (req.user.id === post.UserId || (post.Retweet && post.Retweet.UserId === req.user.id)) {
            return res.status(403).send('자신의 글은 리트윗할 수 없습니다.')
        }
        const retweetTargetId = post.RetweetId || post.id
        const exPost = await Post.findOne({
            where: {
                UserId: req.user.id,
                RetweetIdretweetTargetId
            }
        })
        if (exPost) {
            return res.status(403).send('이미 리트윗했습니다.')
        }
        const retweet = await Post.create({
            UserId: req.user.id,
            RetweetIdretweetTargetId,
            content'retweet'
        })
        const retweetWithPrevPost = await Post.findOne({
            where: { id: retweet.id },
            include: [{
                modelPost,
                as'Retweet',
                include: [{
                    modelUser,
                    attributes: ['id''nickname']
                }]
            }, {
                modelUser,
                attributes: ['id''nickname']
            }, {
                modelImage
            }, {
                modelComment,
                include: [{
                    modelUser,
                    attributes: ['id''nickname']
                }]
            }, {
                modelUser,
                as'Likers',
                attributes: ['id']
            }, {
                modelPost,
                as'Retweet',
                include: [{
                    modelUser,
                    attributes: ['id''nickname']
                }, {
                    modelImage
                }]
            }]
        })
        res.status(201).json(retweetWithPrevPost)
    } catch (error) {
        console.error(error)
        next(error)
    } 
})

router.patch('/:postId/like'isLoggedInasync (reqresnext=> { // PATCH /post/1/like
    try {
        const post = await Post.findOne({ where: { id: req.params.postId }})
        if (!post) {
            return res.status(403).send('게시글이 존재하지 않습니다.')
        }
        await post.addLikers(req.user.id)
        res.json({ PostId: post.idUserId: req.user.id })
    } catch (error) {
        console.error(error)
        next(error)
    }
    
})

router.delete('/:postId/like'isLoggedInasync (reqresnext=> { // DELETE /post/1/like
    try {
        const post = await Post.findOne({ where: { id: req.params.postId }})
        if (!post) {
            return res.status(403).send('게시글이 존재하지 않습니다.')
        }
        await post.removeLikers(req.user.id)
        res.json({ PostId: post.idUserId: req.user.id })
    } catch (error) {
        console.error(error)
        next(error)
    }
})

router.delete('/:postId'isLoggedInasync (reqresnext=> { // DELETE /post/10
    try {
        await Post.destroy({
            where: { 
                id: req.params.postId,
                UserId: req.user.id
            }
        })
        res.json({ PostIdparseInt(req.params.postId10) })
    } catch (error) {
        console.error(error)
        next(error)
    }
})

module.exports = router
 
 
 
 
 
 
 
 
 
 
 
 
 
reducers/ post.js
 
import produce from 'immer'

export const initialState = {
    mainPosts: [],
    singlePostnull,
    imagePaths: [],
    hasMorePoststrue,
    likePostLoadingfalse,
    likePostDonefalse,
    likePostErrornull,
    unlikePostLoadingfalse,
    unlikePostDonefalse,
    unlikePostErrornull,
    loadPostLoadingfalse,
    loadPostDonefalse,
    loadPostErrornull,
    loadPostsLoadingfalse,
    loadPostsDonefalse,
    loadPostsErrornull,
    addPostLoadingfalse,
    addPostDonefalse,
    addPostErrornull,
    removePostLoadingfalse,
    removePostDonefalse,
    removePostErrornull,
    addCommentLoadingfalse,
    addCommentDonefalse,
    addCommentErrornull,
    uploadImagesLoadingfalse,
    uploadImagesDonefalse,
    uploadImagesErrornull,
    retweetLoadingfalse,
    retweetDonefalse,
    retweetErrornull
}

export const UPLOAD_IMAGES_REQUEST = 'UPLOAD_IMAGES_REQUEST'
export const UPLOAD_IMAGES_SUCCESS = 'UPLOAD_IMAGES_SUCCESS'
export const UPLOAD_IMAGES_FAILURE = 'UPLOAD_IMAGES_FAILURE'

export const LIKE_POST_REQUEST = 'LIKE_POST_REQUEST'
export const LIKE_POST_SUCCESS = 'LIKE_POST_SUCCESS'
export const LIKE_POST_FAILURE = 'LIKE_POST_FAILURE'

export const UNLIKE_POST_REQUEST = 'UNLIKE_POST_REQUEST'
export const UNLIKE_POST_SUCCESS = 'UNLIKE_POST_SUCCESS'
export const UNLIKE_POST_FAILURE = 'UNLIKE_POST_FAILURE'

export const LOAD_POSTS_REQUEST = 'LOAD_POSTS_REQUEST'
export const LOAD_POSTS_SUCCESS = 'LOAD_POSTS_SUCCESS'
export const LOAD_POSTS_FAILURE = 'LOAD_POSTS_FAILURE'

export const LOAD_POST_REQUEST = 'LOAD_POST_REQUEST'
export const LOAD_POST_SUCCESS = 'LOAD_POST_SUCCESS'
export const LOAD_POST_FAILURE = 'LOAD_POST_FAILURE'

export const ADD_POST_REQUEST = 'ADD_POST_REQUEST'
export const ADD_POST_SUCCESS = 'ADD_POST_SUCCESS'
export const ADD_POST_FAILURE = 'ADD_POST_FAILURE'

export const REMOVE_POST_REQUEST = 'REMOVE_POST_REQUEST'
export const REMOVE_POST_SUCCESS = 'REMOVE_POST_SUCCESS'
export const REMOVE_POST_FAILURE = 'REMOVE_POST_FAILURE'

export const ADD_COMMENT_REQUEST = 'ADD_COMMENT_REQUEST'
export const ADD_COMMENT_SUCCESS = 'ADD_COMMENT_SUCCESS'
export const ADD_COMMENT_FAILURE = 'ADD_COMMENT_FAILURE'

export const RETWEET_REQUEST = 'RETWEET_REQUEST'
export const RETWEET_SUCCESS = 'RETWEET_SUCCESS'
export const RETWEET_FAILURE = 'RETWEET_FAILURE'

export const REMOVE_IMAGE = 'REMOVE_IMAGE'

export const addPost = (data=> ({
    typeADD_POST_REQUEST,
    data
})

export const addComment = (data=> ({
    typeADD_COMMENT_REQUEST,
    data
})

const reducer = (state = initialStateaction=> {
    return produce(state, (draft=> {
        switch (action.type) {
            case REMOVE_IMAGE:
                draft.imagePaths = draft.imagePaths.filter((vi=> i !== action.data)
                break
            case RETWEET_REQUEST:
                draft.retweetLoading = true
                draft.retweetDone = false
                draft.retweetError = null
                break
            case RETWEET_SUCCESS: {
                draft.retweetLoading = false
                draft.retweetDone = true
                draft.mainPosts.unshift(action.data)
                break
            }
            case RETWEET_FAILURE:
                draft.retweetLoading = false
                draft.retweetError = action.error
                break
            case UPLOAD_IMAGES_REQUEST:
                draft.uploadImagesLoading = true
                draft.uploadImagesDone = false
                draft.uploadImagesError = null
                break
            case UPLOAD_IMAGES_SUCCESS: {
                draft.imagePaths = action.data
                draft.uploadImagesLoading = false
                draft.uploadImagesDone = true
                break
            }
            case UPLOAD_IMAGES_FAILURE:
                draft.uploadImagesLoading = false
                draft.uploadImagesError = action.error
                break
            case LIKE_POST_REQUEST:
                draft.likePostLoading = true
                draft.likePostDone = false
                draft.likePostError = null
                break
            case LIKE_POST_SUCCESS: {
                const post = draft.mainPosts.find((v=> v.id === action.data.PostId)
                post.Likers.push({ id: action.data.UserId })
                draft.likePostLoading = false
                draft.likePostDone = true
                break
            }
            case LIKE_POST_FAILURE:
                draft.likePostLoading = false
                draft.likePostError = action.error
                break
            case UNLIKE_POST_REQUEST:
                draft.unlikePostLoading = true
                draft.unlikePostDone = false
                draft.unlikePostError = null
                break
            case UNLIKE_POST_SUCCESS: {
                const post = draft.mainPosts.find((v=> v.id === action.data.PostId)
                post.Likers = post.Likers.filter((v=> v.id !== action.data.UserId)
                draft.likePostLoading = false
                draft.likePostDone = true
                break
            }
            case UNLIKE_POST_FAILURE:
                draft.unlikePostLoading = false
                draft.unlikePostError = action.error
                break
            case LOAD_POST_REQUEST:
                draft.loadPostLoading = true
                draft.loadPostDone = false
                draft.loadPostError = null
                break
            case LOAD_POST_SUCCESS:
                draft.singlePost = action.data
                draft.loadPostLoading = false
                draft.loadPostDone = true
                break
            case LOAD_POST_FAILURE:
                draft.loadPostLoading = false
                draft.loadPostError = action.error
                break
            case LOAD_POSTS_REQUEST:
                draft.loadPostsLoading = true
                draft.loadPostsDone = false
                draft.loadPostsError = null
                break
            case LOAD_POSTS_SUCCESS:
                draft.mainPosts = draft.mainPosts.concat(action.data)
                draft.loadPostsLoading = false
                draft.loadPostsDone = true
                draft.hasMorePosts = action.data.length === 10
                break
            case LOAD_POSTS_FAILURE:
                draft.loadPostsLoading = false
                draft.loadPostsError = action.error
                break
            case ADD_POST_REQUEST:
                draft.addPostLoading = true
                draft.addPostDone = false
                draft.addPostError = null
                break
            case ADD_POST_SUCCESS:
                draft.mainPosts.unshift(action.data)
                draft.addPostLoading = false
                draft.addPostDone = true
                draft.imagePaths = []
                break
            case ADD_POST_FAILURE:
                draft.addPostLoading = false
                draft.addPostError = action.error
                break
            case REMOVE_POST_REQUEST:
                draft.removePostLoading = true
                draft.removePostDone = false
                draft.removePostError = null
                break
            case REMOVE_POST_SUCCESS:
                draft.removePostLoading = false
                draft.removePostDone = true
                draft.mainPosts = draft.mainPosts.filter((v=> v.id !== action.data.PostId)
                break
            case REMOVE_POST_FAILURE:
                draft.removePostLoading = false
                draft.removePostError = action.error
                break
            case ADD_COMMENT_REQUEST:
                draft.addCommentLoading = true
                draft.addCommentDone = false
                draft.addCommentError = null
                break
            case ADD_COMMENT_SUCCESS: {
                const post = draft.mainPosts.find((v=> v.id === action.data.PostId)
                post.Comments.unshift(action.data)
                draft.addCommentLoading = false
                draft.addCommentDone = true
                break
            }
            case ADD_COMMENT_FAILURE:
                draft.addCommentLoading = false
                draft.addCommentError = action.error
                break
            default:
                break
        }
    })
}

export default reducer
 
 
 
 
 
 
 
 
 
 
 
 
 
sagas/ post.js
 
import axios from 'axios'
import { alldelayputtakeLatestforkthrottlecall } from "redux-saga/effects";
import shortId from 'shortid';
import { 
    ADD_POST_FAILUREADD_POST_SUCCESSADD_COMMENT_SUCCESS
    ADD_COMMENT_FAILUREADD_POST_REQUESTADD_COMMENT_REQUEST
    REMOVE_POST_FAILUREREMOVE_POST_REQUESTREMOVE_POST_SUCCESS
    LOAD_POSTS_REQUESTLOAD_POSTS_SUCCESSLOAD_POSTS_FAILURE
    LIKE_POST_REQUESTUNLIKE_POST_REQUESTLIKE_POST_SUCCESS
    LIKE_POST_FAILUREUNLIKE_POST_SUCCESSUNLIKE_POST_FAILURE
    UPLOAD_IMAGES_REQUESTUPLOAD_IMAGES_SUCCESSUPLOAD_IMAGES_FAILURE
    RETWEET_REQUESTRETWEET_SUCCESSRETWEET_FAILURE
    LOAD_POST_REQUESTLOAD_POST_SUCCESSLOAD_POST_FAILURE 
from '../reducers/post'
import { ADD_POST_TO_MEREMOVE_POST_OF_ME } from '../reducers/user';

function addPostAPI(data) {
    return axios.post('/post'data)
}

function* addPost(action) {
    try {
        const result = yield call(addPostAPI, action.data)
        const id = shortId.generate()
        yield put({
            typeADD_POST_SUCCESS,
            data: result.data
        })
        yield put({
            typeADD_POST_TO_ME,
            data: result.data.id
        })
    } catch (err) {
        yield put({
            typeADD_POST_FAILURE,
            error: err.response.data
        })
    }
}

function retweetAPI(data) {
    return axios.post(`/post/${data}/retweet`data)
}

function* retweet(action) {
    try {
        const result = yield call(retweetAPI, action.data)
        yield put({
            typeRETWEET_SUCCESS,
            data: result.data
        })
    } catch (err) {
        yield put({
            typeRETWEET_FAILURE,
            error: err.response.data
        })
    }
}

function uploadImagesAPI(data) {
    return axios.post(`/post/images`data)
}

function* uploadImages(action) {
    try {
        const result = yield call(uploadImagesAPI, action.data)
        yield put({
            typeUPLOAD_IMAGES_SUCCESS,
            data: result.data
        })
    } catch (err) {
        yield put({
            typeUPLOAD_IMAGES_FAILURE,
            error: err.response.data
        })
    }
}

function likePostAPI(data) {
    return axios.patch(`/post/${data}/like`)
}

function* likePost(action) {
    try {
        const result = yield call(likePostAPI, action.data)
        yield put({
            typeLIKE_POST_SUCCESS,
            data: result.data
        })
    } catch (err) {
        yield put({
            typeLIKE_POST_FAILURE,
            error: err.response.data
        })
    }
}

function unlikePostAPI(data) {
    return axios.delete(`/post/${data}/like`)
}

function* unlikePost(action) {
    try {
        const result = yield call(unlikePostAPI, action.data)
        yield put({
            typeUNLIKE_POST_SUCCESS,
            data: result.data
        })
    } catch (err) {
        yield put({
            typeUNLIKE_POST_FAILURE,
            error: err.response.data
        })
    }
}

function loadPostsAPI(lastId) {
    return axios.get(`/posts?lastId=${lastId || 0}`)
}

function* loadPosts(action) {
    try {
        const result = yield call(loadPostsAPI, action.lastId)
        yield put({
            typeLOAD_POSTS_SUCCESS,
            data: result.data
        })
    } catch (err) {
        yield put({
            typeLOAD_POSTS_FAILURE,
            error: err.response.data
        })
    }
}

function loadPostAPI(data) {
    return axios.get(`/post/${data}`)
}

function* loadPost(action) {
    try {
        const result = yield call(loadPostAPI, action.data)
        yield put({
            typeLOAD_POST_SUCCESS,
            data: result.data
        })
    } catch (err) {
        yield put({
            typeLOAD_POST_FAILURE,
            error: err.response.data
        })
    }
}

function removePostAPI(data) {
    return axios.delete(`/post/${data}`)
}

function* removePost(action) {
    try {
        const result = yield call(removePostAPI, action.data)
        yield put({
            typeREMOVE_POST_SUCCESS,
            data: result.data
        })
        yield put({
            typeREMOVE_POST_OF_ME,
            data: action.data
        })
    } catch (err) {
        yield put({
            typeREMOVE_POST_FAILURE,
            error: err.response.data
        })
    }
}

function addCommentAPI(data) {
    return axios.post(`/post/${data.postId}/comment`data// POST /post/1/comment
}

function* addComment(action) {
    try {
        const result = yield call(addCommentAPI, action.data)
        yield put({
            typeADD_COMMENT_SUCCESS,
            data: result.data
        })
    } catch (err) {
        console.error(error)
        yield put({
            typeADD_COMMENT_FAILURE,
            error: err.response.data
        })
    }
}

function* watchRetweet() {
    yield takeLatest(RETWEET_REQUESTretweet)
}

function* watchUploadImages() {
    yield takeLatest(UPLOAD_IMAGES_REQUESTuploadImages)
}

function* watchLikePost() {
    yield takeLatest(LIKE_POST_REQUESTlikePost)
}

function* watchUnlikePost() {
    yield takeLatest(UNLIKE_POST_REQUESTunlikePost)
}

function* watchAddPost() {
    yield takeLatest(ADD_POST_REQUESTaddPost)
}

function* watchLoadPosts() {
    yield throttle(5000LOAD_POSTS_REQUESTloadPosts)
}

function* watchLoadPost() {
    yield takeLatest(LOAD_POST_REQUESTloadPost)
}

function* watchRemovePost() {
    yield takeLatest(REMOVE_POST_REQUESTremovePost)
}

function* watchAddComment() {
    yield takeLatest(ADD_COMMENT_REQUESTaddComment)
}

export default function* postSaga() {
    yield all([
        fork(watchRetweet),
        fork(watchUploadImages),
        fork(watchLikePost),
        fork(watchUnlikePost),
        fork(watchAddPost),
        fork(watchLoadPosts),
        fork(watchLoadPost),
        fork(watchRemovePost),
        fork(watchAddComment)
    ])
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pages/post/ [id].js
 
import axios from "axios"
import { useRouter } from "next/router"
import { useSelector } from "react-redux"
import { END } from 'redux-saga'
import AppLayout from "../../components/AppLayout"
import PostCard from "../../components/PostCard"
import { LOAD_POST_REQUEST } from "../../reducers/post"
import { LOAD_MY_INFO_REQUEST } from "../../reducers/user"
import wrapper from "../../store/configureStore"

const Post = () => {
    const router = useRouter()
    const { id } = router.query
    const { singlePost } = useSelector((state=> state.post)

    return (
        <AppLayout>
            <PostCard post={singlePost} />
        </AppLayout>
        
    )
}

export const getServerSideProps = wrapper.getServerSideProps(async (context=> {
    const cookie = context.req ? context.req.headers.cookie : '';
    console.log(context)
    axios.defaults.headers.Cookie = '';
    if (context.req && cookie) {
        axios.defaults.headers.Cookie = cookie
    }
    context.store.dispatch({
        typeLOAD_MY_INFO_REQUEST
    })
    context.store.dispatch({
        typeLOAD_POST_REQUEST,
        data: context.params.id
    })
    context.store.dispatch(END)
    await context.store.sagaTask.toPromise()
})

export default Post
 
입니다
 
 
 

답변 1

답변을 작성해보세요.

0

1번 포스트가 실제로 db에 존재하나요?

거휘님의 프로필

거휘

질문자

2021.10.06

아 말씀해주신 내용 토대로 확인하니 완전히 제가 착각하고 있었습니다. 해결은 했습니다만

그동안 강의들을 보면서 에러가 발생할때마다 경우에따라 게시글 db를 전체 삭제한경우가 있었는데

저는 그동안 전체 삭제후 id가 다시 1부터 시작하는줄 알았습니다.

그런데 워크벤치를 통해 확인해보니 db에서 삭제한 게시글들의 id가 고유하게 남아있는지 그 뒤로 계속해서 추가된걸 모르고 있었습니다. 결국 새로 생성한 게시글 id로 post주소에 입력하니 정상적으로 동작되는것으로 확인됐습니다. 소중한 시간내주셔서 너무 감사합니다.

 

혹시 게시글을 전체삭제한후 id를 1부터 다시 생성되게 할 방법은 없을까요?

auto_increment를 1로 초기화해야하는데 sql문을 사용해야 합니다. 그런데 id에 너무 신경쓰실필요는 없습니다. id가 연속적이면 예측이 돼서 문제가 되는 경우도 있습니다.

거휘님의 프로필

거휘

질문자

2021.10.06

놓치고 있던부분을 바로 콕 찝어주셔서 감사합니다. 계속 강의 이어가겠습니다!