유튜브 클론코딩(댓글편)

TaeHyun Lee·2022년 9월 29일
0

유튜브 클론코딩

목록 보기
6/7

댓글부분 만들기


만든 부분 - 2022 / 09 / 20


나도 이제 GIF 파일을 올리는 방법을 알았다 ㅎㅎ 그건 그렇고 오늘은 나의 댓글 input 부분에 애니메이션을 추가하였다. 정말정말 간단해 보이지만 실제로 작업하는 데 30분 이상 걸렸다...


NextJS 코드 (VideoPart.js의 한 부분)

<div className={styles.Comment_input_box}>
    {
        isFocus === false ? 
        <div className={styles.Comment_line}></div>
        : <div className={styles.Comment_line_inAni}></div>
    }
     <input 
         onFocus={() => {
            setIsFocus(true) 
            setFirstFocus(true)
         }} 
         onBlur={() => setIsFocus(false)} 
         placeholder='댓글 추가...'
     />
     <div>
         {
             firstFocus ?
             <>
                 <div id={styles.Comment_btn1} className={styles.Comment_buttons}>취소</div>
                 <div id={styles.Comment_btn2} className={styles.Comment_buttons}>댓글</div>
             </> : 
             <div className={styles.Comment_buttons}></div>
         }
     </div>
</div>

CSS 코드 (VideoPart.module.css의 한 부분)

.Comment_input_box {
    width: 100%;
}
.Comment_input_box div {
    float: right;
    display: flex;
    flex-direction: row;
}
.Comment_input_box input {
    width: 100%;
    height: 100%;
    background-color: transparent;
    border: none;
    color: #666;
    vertical-align: middle;
    box-sizing: border-box;
}

.Comment_input_box input:focus {
    outline: none;
}

@keyframes inputAnimation {
    from {
        box-sizing: inherit;
        border-bottom: 1px solid #000;
        transform: scale3d(0, 1, 0);
    }
    to {
        box-sizing: inherit;
        border-bottom: 2px solid #000;
        transform: scale3d(1, 1, 1);
    }
}
.Comment_line {
    position: absolute;
    top: 116.5%;
    height: 1px;
    width: 62%;
    background-color: lightgrey;
}
.Comment_line_inAni {
    position: absolute;
    top: 116.5%;
    height: 2px;
    width: 62%;
    background-color: black;
    animation-name: inputAnimation;
    animation-duration: 0.3s;
}
.Comment_buttons {
    width: 72px;
    height: 37px;
    line-height: 37px;
    margin: 7px;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 3px;
    color: #909090;
    font-size: 13px;
}
#Comment_btn1 {
    background-color: rgb(246, 246, 246);
    color: #787878;
}
#Comment_btn2 {
    background-color: rgb(225, 225, 225);
}

keyframes로 애니메이션을 만들어서 적용시켜주는 방법을 사용하였다.


오늘 만든 부분 - 2022 / 09 / 21


오늘은 어제 했던 부분에 이어서 댓글 작성 부분에 글을 입력하면 버튼의 색이 바뀌는 것과 취소 버튼을 눌렸을 때 버튼들이 사라지는 것을 만들었다. 그리고 댓글부분도 만들었다. 저 댓글들은 임의로 데이터를 만들어서 map함수로 컴포넌트를 리턴하는 방식으로 만들었다. 그리고 VideoPart.js부분에 모든 코드가 있는 것이 더러워 보여서 CommentPart.js로 컴포넌트를 나눠주기도 하였다.


NextJS 코드 (CommentPart.js)

import React, { useState } from 'react'
import styles from "../styles/CommentPart.module.css"
import { MdSort } from "react-icons/md"
import Comment from './Comment';



const CommentPart = () => {
    const [firstFocus, setFirstFocus] = useState(false);
    const [isFocus, setIsFocus] = useState(false);
    const [isInput, setIsInput] = useState(false);

    const commentsSample = [
        {
            imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu_D4JwoqK3mF9YcK1b6_sQ8lvdQ-luwDuqt3w=s88-c-k-c0x00ffffff-no-rj",
            userName: "taehyun",
            comment: "좋아요!!",
            good: 5,
            date: "2022/05/05"
        },
        {
            imgSrc: "https://lh3.googleusercontent.com/ogw/AOh-ky1uAXwkV8yiFs8KaB07AxluQj2nYO0d-RgdXzqr=s64-c-mo",
            userName: "태현",
            comment: "정말 좋아요!!",
            good: 10,
            date: "2022/05/10"
        },
        {
            imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu9kCKCBTyW3NUg0qiakoo_NPijLpMRjHyWyeUhO1-FAXAjbtINI76YzJSfQZ5JZCGnqUxU=s88-c-k-c0x00ffffff-no-rj",
            userName: "구크르",
            comment: "정말 재밌어요!!",
            good: 2,
            date: "2022/05/25"
        },
    ]

  return (
    <div>
        <div className={styles.Comment_box}>
            <div className={styles.Comment_sort_box}>
                <span className={styles.Comment_number}>댓글 1</span>
                <div>
                    <MdSort size={30}/>
                    <span className={styles.Comment_sort}>정렬 기준</span>
                </div>
            </div>
            <div className={styles.Comment_input_part}>
                <img src='https://yt3.ggpht.com/ytc/AMLnZu9Jzp859A5IesAX3WqVFY0ocYhG3_oFkYuLlNlH1KPJhA=s88-c-k-c0x00ffffff-no-rj-mo'/>
                <div className={styles.Comment_input_box}>
                    {
                        isFocus === false ? 
                        <div className={styles.Comment_line}></div>
                        : <div className={styles.Comment_line_inAni}></div>
                    }
                    <input 
                        onFocus={() => {
                            setIsFocus(true) 
                            setFirstFocus(true)
                        }}
                        onChange={(e) => {
                            if(e.target.value)setIsInput(true)
                            else setIsInput(false)
                        }}
                        onBlur={() => setIsFocus(false)} 
                        placeholder='댓글 추가...'
                    />
                    <div>
                        {
                            firstFocus ?
                            <>
                                <div 
                                    onClick={() => setFirstFocus(false)}
                                    id={styles.Comment_btn1} 
                                    className={styles.Comment_buttons}
                                >
                                    취소
                                </div>
                                { 
                                    !isInput ? 
                                    <div id={styles.Comment_btn2} className={styles.Comment_buttons}>댓글</div>
                                    : <div id={styles.Comment_btn2_active} className={styles.Comment_buttons}>댓글</div>
                                }
                            </> : 
                            <div className={styles.Comment_buttons}></div>
                        }
                    </div>
                 </div>
            </div>
            <div className='Comments_list'>
                {
                    commentsSample.map((comment, idx) => {
                        return <Comment comment={comment} key={idx} />
                    })
                }
            </div>
        </div>
    </div>
  )
}

export default CommentPart

NextJS 코드 (Comment.js)

import React from 'react'
import { FiThumbsDown, FiThumbsUp } from 'react-icons/fi'
import styles from "../styles/Comment.module.css"


const Comment = (props) => {
  return (
    <div className={styles.Comment_box}>
        <img src={props.comment.imgSrc} />
        <div>
            <div className={styles.Comment_info}>
                <p>{props.comment.userName}</p>
                <span>{props.comment.date}</span>
            </div>
            <p>{props.comment.comment}</p>
            <div className={styles.Comment_good_bad}>
                <FiThumbsUp size={15} />
                <span>{props.comment.good}</span>
                <FiThumbsDown size={15} />
            </div>
        </div>
    </div>
  )
}

export default Comment

CSS 코드 (CommentPart.module.css)

.Comment_box {
    width: 100%;
}
.Comment_sort_box {
    width: 200px;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
}
.Comment_sort_box div {
    display: flex;
    flex-direction: row;
    align-items: center;  
    justify-content: space-between;  
}
.Comment_sort {
    color: black !important;
    font-size: 14px !important;
}
.Comment_number {
    font-size: 16px !important;
    color: black !important;
}
.Comment_input_part {
    width: 100%;
    display: flex;
    flex-direction: row;
    align-items: center;
    margin-top: 20px;
    /* background-color: aquamarine; */
}
.Comment_input_part img {
    width: 40px;
    height: 40px;
    border-radius: 100px;
    margin-bottom: 20px;
    margin-right: 10px;
}
.Comment_input_box {
    width: 100%;
    /* background-color: #787878; */
}
.Comment_input_box div {
    float: right;
    display: flex;
    flex-direction: row;
}
.Comment_input_box input {
    width: 100%;
    height: 100%;
    background-color: transparent;
    border: none;
    color: #666;
    vertical-align: middle;
    box-sizing: border-box;
}

.Comment_input_box input:focus {
    outline: none;
}

@keyframes inputAnimation {
    from {
        box-sizing: inherit;
        border-bottom: 1px solid #000;
        transform: scale3d(0, 1, 0);
    }
    to {
        box-sizing: inherit;
        border-bottom: 2px solid #000;
        transform: scale3d(1, 1, 1);
    }
}
.Comment_line {
    position: absolute;
    top: 118.5%;
    height: 1px;
    width: 62%;
    background-color: lightgrey;
    transition: 0.3s;
}
.Comment_line_inAni {
    position: absolute;
    top: 118.5%;
    height: 2px;
    width: 62%;
    background-color: black;
    animation-name: inputAnimation;
    animation-duration: 0.3s;
}
.Comment_buttons {
    width: 72px;
    height: 37px;
    line-height: 37px;
    margin: 7px;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 3px;
    color: #909090;
    font-size: 13px;
}
#Comment_btn1 {
    background-color: rgb(246, 246, 246);
    color: #787878;
}
#Comment_btn2 {
    background-color: rgb(225, 225, 225);
}
#Comment_btn2_active {
    background-color: rgb(19, 51, 255);
    color: white;
    cursor: pointer;
}
.Comments_list {
    width: 100%;
    height: auto;
    background-color: #787878;
}

CSS 코드 (Comment.module.css)

.Comment_box {
   width: 100%;
   height: auto;
   /* background-color: azure; */
   margin: 30px 0;
   display: flex;
   flex-direction: row;
   align-items: center;
   justify-content: flex-start;
}
.Comment_box:nth-child(1) {
   margin-top: 10px;
}
.Comment_box img {
   width: 40px;
   height: 40px;
   border-radius: 50%;
   margin-right: 20px;
}
.Comment_box > div > p {
   font-size: 13px;
}
.Comment_info {
   display: flex;
   flex-direction: row;
   align-items: center;
}
.Comment_info span {
   font-size: 10px;
   color: grey;
}
.Comment_info p {
   font-size: 13px;
   margin-right: 5px;
}
.Comment_good_bad {
   display: flex;
   align-items: center;
}
.Comment_good_bad span {
   margin: 0 5px;
}

오늘 만든 부분 - 2022 / 09 / 22


오늘은 이것을 만들었다. 댓글을 추가할 수 있게 하였고, 좋아요를 누를 수도 있게 만들었다. 좋아요와 싫어요 사이, '취소' 버튼과 '댓글' 버튼 사이의 디테일적인 기능이 많다. 내가 아무렇지도 않게 사용하던 기능이지만 이렇게 만들어보니 작은 기능도 코드는 길 수 있다는 것을 알게되었다.


NextJS 코드 (CommentPart.js)

import React, { useState } from 'react'
import styles from "../styles/CommentPart.module.css"
import { MdSort } from "react-icons/md"
import Comment from './Comment';



const CommentPart = () => {
    const [firstFocus, setFirstFocus] = useState(false);
    const [isFocus, setIsFocus] = useState(false);
    const [isInput, setIsInput] = useState(false);
    const [inputComment, setInputComment] = useState("");

    const Me = {
        imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu9Jzp859A5IesAX3WqVFY0ocYhG3_oFkYuLlNlH1KPJhA=s88-c-k-c0x00ffffff-no-rj-mo",
        userName: "이태현",
        comment: "",
        good: 0,
        date: "2022/09/21"
    }

    const [commentsSample, setCommentsSample] = useState([
        {
            imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu_D4JwoqK3mF9YcK1b6_sQ8lvdQ-luwDuqt3w=s88-c-k-c0x00ffffff-no-rj",
            userName: "TaeHi",
            comment: "좋아요!!",
            good: 5,
            date: "2022/05/05",
        },
        {
            imgSrc: "https://lh3.googleusercontent.com/ogw/AOh-ky1uAXwkV8yiFs8KaB07AxluQj2nYO0d-RgdXzqr=s64-c-mo",
            userName: "DoHi",
            comment: "정말 좋아요!!",
            good: 10,
            date: "2022/05/10"
        },
        {
            imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu9kCKCBTyW3NUg0qiakoo_NPijLpMRjHyWyeUhO1-FAXAjbtINI76YzJSfQZ5JZCGnqUxU=s88-c-k-c0x00ffffff-no-rj",
            userName: "구크르",
            comment: "정말 재밌어요!!",
            good: 2,
            date: "2022/05/25"
        },
    ])
  return (
    <div>
        <div className={styles.Comment_box}>
            <div className={styles.Comment_sort_box}>
                <span className={styles.Comment_number}>댓글 {commentsSample.length}</span>
                <div>
                    <MdSort size={30}/>
                    <span className={styles.Comment_sort}>정렬 기준</span>
                </div>
            </div>
            <div className={styles.Comment_input_part}>
                <img src='https://yt3.ggpht.com/ytc/AMLnZu9Jzp859A5IesAX3WqVFY0ocYhG3_oFkYuLlNlH1KPJhA=s88-c-k-c0x00ffffff-no-rj-mo'/>
                <div className={styles.Comment_input_box}>
                    {
                        isFocus === false ? 
                        <div className={styles.Comment_line}></div>
                        : <div className={styles.Comment_line_inAni}></div>
                    }
                    <input 
                        onFocus={() => {
                            setIsFocus(true) 
                            setFirstFocus(true)
                        }}
                        onChange={(e) => {
                            if(e.target.value)setIsInput(true)
                            else setIsInput(false)
                            setInputComment(e.target.value)
                        }}
                        onBlur={() => setIsFocus(false)} 
                        placeholder='댓글 추가...'
                        value={inputComment}
                    />
                    <div>
                        {
                            firstFocus ?
                            <>
                                <div 
                                    onClick={() => {
                                        setFirstFocus(false)
                                        setInputComment("")
                                    }}
                                    id={styles.Comment_btn1} 
                                    className={styles.Comment_buttons}
                                >
                                    취소
                                </div>
                                { 
                                    !isInput ? 
                                    <div id={styles.Comment_btn2} className={styles.Comment_buttons}>댓글</div>
                                    : <div 
                                        id={styles.Comment_btn2_active} 
                                        className={styles.Comment_buttons}
                                        onClick={() => {
                                            Me.comment = inputComment
                                            setCommentsSample([Me, ...commentsSample])
                                            setInputComment("")
                                        }}
                                    >
                                        댓글
                                    </div>
                                }
                            </> : 
                            <div className={styles.Comment_buttons}></div>
                        }
                    </div>
                 </div>
            </div>
            <div className='Comments_list'>
                {
                    commentsSample.map((comment, idx) => {
                        return <Comment comment={comment} key={idx} />
                    })
                }
            </div>
        </div>
    </div>
  )
}

export default CommentPart

NextJS 코드 (Comment.js)

import React, { useState } from 'react'
import { IoMdThumbsDown, IoMdThumbsUp } from 'react-icons/io'
import { BsThreeDotsVertical } from 'react-icons/bs'
import styles from "../styles/Comment.module.css"


const Comment = (props) => {

  // const [list, setList] = useState(props.commentsSample)

  const [isGood, setIsGood] = useState(false)
  const [isBad, setIsBad] = useState(false)
  const [menuOn, setMenuOn] = useState(false);
  return (
    <div onMouseOver={() => setMenuOn(true)} onMouseLeave={() => setMenuOn(false)} className={styles.Comment_box}>
      {menuOn && <BsThreeDotsVertical className={styles.Comment_menu} />} 
        <img src={props.comment.imgSrc} />
        <div>
            <div className={styles.Comment_info}>
                <p>{props.comment.userName}</p>
                <span>{props.comment.date}</span>
            </div>
            <p>{props.comment.comment}</p>
            <div className={styles.Comment_good_bad}>
                <IoMdThumbsUp 
                  onClick={() => {
                    setIsGood(!isGood)
                    setIsBad(false)
                    if(!isGood)props.comment.good++
                    else props.comment.good--
                  }} 
                  color={isGood ? "rgb(0, 102, 255)" : "black"}
                  size={15} 
                />
                <span style={{width: "10px", textAlign: "center"}}>{props.comment.good}</span>
                <IoMdThumbsDown 
                  onClick={() => {
                    setIsBad(!isBad)
                    if(isGood) {
                      props.comment.good--
                      setIsGood(false)
                    }
                  }}
                  color={isBad ? "rgb(0, 102, 255)" : "black"}
                  size={15} 
                />
            </div>
        </div>
    </div>
  )
}

export default Comment

CSS 코드 (CommentPart.module.css)

.Comment_box {
    width: 100%;
}
.Comment_sort_box {
    width: 200px;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
}
.Comment_sort_box div {
    display: flex;
    flex-direction: row;
    align-items: center;  
    justify-content: space-between;  
}
.Comment_sort {
    color: black !important;
    font-size: 14px !important;
}
.Comment_number {
    font-size: 16px !important;
    color: black !important;
}
.Comment_input_part {
    width: 100%;
    display: flex;
    flex-direction: row;
    align-items: center;
    margin-top: 20px;
    /* background-color: aquamarine; */
}
.Comment_input_part img {
    width: 40px;
    height: 40px;
    border-radius: 100px;
    margin-bottom: 20px;
    margin-right: 10px;
}
.Comment_input_box {
    width: 100%;
    /* background-color: #787878; */
}
.Comment_input_box div {
    float: right;
    display: flex;
    flex-direction: row;
}
.Comment_input_box input {
    width: 100%;
    height: 100%;
    background-color: transparent;
    border: none;
    color: #666;
    vertical-align: middle;
    box-sizing: border-box;
}

.Comment_input_box input:focus {
    outline: none;
}

@keyframes inputAnimation {
    from {
        box-sizing: inherit;
        border-bottom: 1px solid #000;
        transform: scale3d(0, 1, 0);
    }
    to {
        box-sizing: inherit;
        border-bottom: 2px solid #000;
        transform: scale3d(1, 1, 1);
    }
}
.Comment_line {
    position: absolute;
    top: 138%;
    height: 1px;
    width: 63%;
    background-color: lightgrey;
    transition: 0.3s;
}
.Comment_line_inAni {
    position: absolute;
    top: 138%;
    height: 2px;
    width: 63%;
    background-color: black;
    animation-name: inputAnimation;
    animation-duration: 0.3s;
}
.Comment_buttons {
    width: 72px;
    height: 37px;
    line-height: 37px;
    margin: 7px;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 3px;
    color: #909090;
    font-size: 13px;
}
#Comment_btn1 {
    background-color: rgb(246, 246, 246);
    color: #787878;
}
#Comment_btn2 {
    background-color: rgb(225, 225, 225);
}
#Comment_btn2_active {
    background-color: rgb(19, 51, 255);
    color: white;
    cursor: pointer;
}
.Comments_list {
    width: 100%;
    height: auto;
    background-color: #787878;
}

CSS 코드 (Comment.module.css)

.Comment_box {
    width: 100%;
    height: auto;
    /* background-color: azure; */
    margin-bottom: 10px;
    margin-top: 0;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
}
.Comment_menu {
    position: absolute;;
    right: 480px;
}
.Comment_box:nth-child(1) {
    margin-top: 10px;
}
.Comment_box img {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    margin-right: 20px;
}
.Comment_box > div > p {
    font-size: 13px;
    margin-top: 5px;
}
.Comment_info {
    display: flex;
    flex-direction: row;
    align-items: center;
    margin-top: 25px;
}
.Comment_info span {
    font-size: 10px;
    color: grey;
}
.Comment_info p {
    font-size: 13px;
    margin-right: 5px;
}
.Comment_good_bad {
    margin-top: 10px;
    display: flex;
    align-items: center;
}
.Comment_good_bad span {
    margin: 0 10px;
    color: black;
}

profile
서커스형 개발자

0개의 댓글