리액트 기본 배우기 -컴포넌트에 대하여-

1. 컴포넌트 기반의 구조

 

리액트에서는 모든 페이지가 컴포넌트로 구성되어 있고, 하나의 컴포넌트는 또 다른 여러 개의 컴포넌트의 조합으로 구성될 수 있다.

 

이러한 컴포넌트들을 마치 레고 블록을 조립하듯이 끼워 맞춰서 새로운 컴포넌트를 만들 수 있다

 

 

에어비앤비 웹사이트 화면을 캡쳐하고 컴포넌트를 간단히 표시한 것이다.

 

A, B로 표시된 부분이 리액트 컴포넌트이다.

 

이러한 컴포넌트를 여러 번 반복적으로 사용해서 하나의 페이지를 구성하고 있다.

 

이처럼 리액트는 작은 컴포넌트들이 모여서 하나의 컴포넌트를 구성하고, 이러한 컴포넌트들이 모여서 전체 페이지를 구성한다.

 

이렇게 하나의 컴포넌트를 반복적으로 사용함으로써 전체 코드의 양이 줄어 자연스레 개발 시간과 유지 보수 비용도 줄일 수 있다.

 

 

2. 리액트 컴포넌트와 자바스크립트 함수

 

리액트 컴포넌트는 개념적으로 자바스크립트 함수와 비슷하다

 

함수가 입력을 받아 출력을 내뱉는 것처럼, 리액트 컴포넌트도 입력을 받아서 정해진 출력을 내뱉는다.

 

리액트 컴포넌트를 그냥 하나의 함수라고 생각하면 좀 더 쉽게 개념을 이해할 수 있다.

 

 

 

하지만 리액트 컴포넌트의 입력과 출력은 일반적인 자바스크립트 함수와는 조금 다르다.

 

 

리액트 컴포넌트에서의 입력은 props라는 것이고, 출력은 리액트 엘리먼트이다.

 

리액트 컴포넌트가 해주는 역할은 어떠한 속성들을 입력으로 받아서 그에 맞는 리액트 엘리먼트를 생성하여 리턴해주는 것

 

앞에서 리액트 엘리먼트는 리액트 앱을 구성하는 가장 작은 빌딩 블록들이라고 배움

 

그리고 자바스크립트 객체 형태로 존재해서 화면에 보이는 것을 기술

 

리액트 컴포넌트는 만들고자 하는 대로 props를 넣으면 해당 속성에 맞춰 화면에 나타날 엘리먼트를 만들어주는 것

 

마치 붕어빵을 굽는 과정과 비슷하다

 

 

 

붕어빵 기계에는 붕어 모양의 틀이 여러개 있고, 거기에 반죽을 부어서 붕어빵을 만든다.

 

이는 리액트 컴포넌트로부터 엘리먼트가 만들어지는 과정과 비슷하다

 

여기에서 리액트 컴포넌트는 붕어빵 틀을 의미하고 각 붕어빵들은 리액트 엘리먼트를 의미한다고 볼 수 있다.

 

이것은 마치 객체 지향 프로그래밍에서 나오는 클래스와 인스턴스 개념과 비슷하다.

 

클래스라는 붕어빵 틀에서 인스턴스라는 실제 붕어빵이 만들어져 나온다.

 

 

3. Props 개념

 

리액트 컴포넌트의 입력으로 들어간다.

 

props는 prop이 여러개인 것을 의미하며, prop은 property라는 영단어를 줄여서 쓴 것이다.

 

리액트에서는 속성이라는 뜻으로, 리액트 컴포넌트의 속성을 말한다.

 

 

리액트 컴포넌트가 붕어빵 틀인데, 여기서 props는 붕어빵에 들어가는 재료가 된다.

 

같은 붕어빵이라도 어떤 재료를 넣느냐에 따라 맛이 다른것처럼

 

 

위 그림에서는 총 3개의 각기 다른 붕어빵이 등장한다.

 

붕어빵 틀에 팥을 넣어 팥 붕어빵, 슈크림을 넣어 슈크림 붕어빵, 고구마를 넣어 고구마 붕어빵

 

여기에서 붕어빵에 들어가는 재료가 props

 

같은 붕어빵 틀에 구워져서 나오므로 모양은 같지만 속을 뜯어보면 안에 들어있는 재료도 다르며, 색깔도 다르다.

 

props는 같은 리액트 컴포넌트에서 눈에 보이는 글자나 색깔 등의 속성을 바꾸고 싶을 때 사용하는 컴포넌트 속 재료이다.

 

 

 

동일한 둥근 사각형 모양에, 각기 다른 배경이미지, 색깔, 글자를 넣어 서로 다른 형태의 엘리먼트가 나온다.

 

이처럼 컴포넌트의 모습과 속성을 결정하는 것이 props이다.

 

props는 컴포넌트에 전달할 다양한 정보를 담는 자바스크립트 객체이다.

 

컴포넌트에 어떤 데이터를 전달하고, 전달된 데이터에 따라 다른 모습의 엘리먼트를 화면에 렌더링하고 싶다면, 해당 데이터를 props에 넣어 전달하는 것이다.

 

 

4. props의 특징

 

 

props의 중요한 특징 중 하나는 읽기 전용이다.

 

읽을 수만 있다는 것은 값을 변경할 수 없다는 말이다.

 

props의 값은 리액트 컴포넌트가 엘리먼트를 생성하기 위해 사용하는 값이다.

 

그러므로 엘리먼트를 생성하는 도중에 갑자기 바뀌어버리면 제대로 된 엘리먼트를 생성할 수가 없다.

 

그러면 다른 props 값으로 엘리먼트를 생성하려면?

 

새로운 값을 컴포넌트에 전달해서 새로 엘리먼트를 생성하는 것이다.

 

이 과정에서 엘리먼트가 다시 렌더링 되는 것이다.

 

---------------------------------------------------------------------------------------------------

 

//pure함수

//input을 변경하지 않으며, 같은 input에 대해서는 항상 같은 output을 리턴

function sum(a,b){
    return a+b
}

 

위와 같은 sum()이라는 함수는 a,b를 받아 둘의 합을 리턴하는 함수이다.

 

이 과정에서 a,b 값은 변경하지 않고 있다.

 

그래서 a,b라는 파라미터 집합의 값이 같다면 항상 같은 값을 리턴한다.

 

이러한 함수를 pure 함수라고 부른다.

 

입력값을 변경하지 않고, 같은 입력값에 대해서는 항상 같은 값을 출력한다.

 

//impure

//input을 변경
function withdraw(account, amount){
    account.total -= amount;
}

 

account와 amount라는 파라미터를 입력 받아 account의 total이라는 값에서 amount를 빼는 함수이다.

 

이 함수는 입력으로 받은 account의 값을 변경했다. 이런 경우 impure 함수라고 부른다.

 

리액트 공식문서에서 컴포넌트는..

 

"All React components must act like pure functions with respect to their props"

 

모든 리액트 컴포넌트는 그들의 props에 관해서는 pure 함수 같은 역할을 해야한다.

 

즉, 모든 리액트 컴포넌트는 props를 직접 바꿀 수 없고, 같은 props에 대해서는 항상 같은 결과를 보여줘야한다.

 

 

5. props의 사용

 

props는 컴포넌트에 전달할 다양한 정보를 담고 있는 자바스크립트 객체이다.

 

5-1) 먼저 JSX를 사용하는 경우에 아래와 같이 키-값 쌍의 형태로 컴포넌트에 props를 넣을 수 있다

 

function App(props) {
    return (
        <Profile
        name = "대혁"
        introduction = "안녕하세요, 대혁입니다."
        viewCount = {1500}
        />
    )
}

 

이 코드에  App 컴포넌트가 나오고 그 안에서 Profile 컴포넌트를 사용하고 있다.

 

여기에서 Profile 컴포넌트에 name, introduction, viewCount라는 세가지 속성을 넣어주었습니다.

 

이렇게 하면 이 속성의 값이 모두 Profile 컴포넌트에 props로 전달되어 props는 아래와 같은 형태의 자바스크립트 객체가 된다.

 

{
    name: "대혁",
    introduction: "안녕하세요, 대혁입니다.",
    viewCount: 1500
}

 

여기서 중요한 점은 name = "대혁"으로 그냥 문자열을 넣었지만, viewCount = {1500}으로 정수가 들어갈때는 중괄호를 사용했다는 점이다.

 

JSX에서 중괄호를 사용하면 무조건 자바스크립트 코드가 들어간다고 배웠는데,

 

props에 넣을때도 문자열 이외에 정수, 변수, 그리고 다른 컴포넌트 등이 들어갈 경우에는 중괄호를 사용해서 감싸주어야한다.

 

물론 문자열을 중괄호로 감싸주어도 상관없다

 

function App(props) {
    return (
        <Layout
        width = {2560}
        height = {1440}
        header = {
            <Header title = "대혁의 블로그입니다."/>
        }
        footer={
            <Footer />
        }
        />
    )
}

 

 

이렇게 하면 Layout 컴포넌트의 props로는 정숫값을 가진 width, height와 리액트 엘리먼트인 Header, Footer가 들어오게 된다.

 

이처럼 JSX를 사용하는 경우에는 간단하게 컴포넌트에 props를 넣을 수 있다

 

 

5-2) 그렇다면 JSX를 사용하지 않는 경우에는?

 

 

여기서 두번째 파라미터가 props이다.

 

이곳에 자바스크립트 객체를 넣으면 그게 곧 해당 컴포넌트의 props가 된다.

 

React.createElement(
    Profile,
    {
        name:'대혁',
        introduction: "안녕하세요 대혁입니다.",
        viewCount: 1500
    },
    null
)

 

type에는 컴포넌트의 이름인 Profile이 들어가고, props로 자바스크립트 객체가 들어간다.

 

마지막으로 하위 컴포넌트가 없어서 children에는 null이 들어갔다.

 

거의 무조건 JSX로 작성하는게 좋다

 

 

6.컴포넌트의 종류

 

리액트 컴포넌트는 함수 컴포넌트, 클래스 컴포넌트로 나뉜다.

 

 

최근에는 리액트 개발에 함수 컴포넌트, 이를 개선한 훅을 주로 사용한다고 함

 

 

6-1) 함수 컴포넌트

 

"모든 리액트 컴포넌트는 pure 함수 같은 역할"

 

리액트의 컴포넌트를 일종의 함수라고 생각한다는 뜻

 

function Welcome(props){
    return <h1>안녕, {props.name}</h1>
}

 

이 코드는 Welcome라는 이름을 가진 함수가 나온다.

 

이 경우 하나의 props 객체를 받아 인사말이 담긴 리액트 앨리먼트를 리턴하므로, 리액트 컴포넌트라고 할 수 있다.

 

그리고 이렇게 함수처럼 생겨서 함수 컴포넌트라고 한다.

 

코드가 간단하기 때문에 간단한 코드를 장점으로 가진다.

 

 

6-2) 클래스 컴포넌트

 

자바스크립트 ES6에서 클래스라는 것을 사용해 만들어진 형태의 컴포넌트

 

함수 컴포넌트에 비해 몇 가지 추가적인 기능을 가지고 있다

 

class Welcome extends React.Component{
    render() {
        return <h1>안녕, {this.props.name}</h1>
    }
}

 

이 코드는 위에서 살펴본 함수 컴포넌트 Welcome과 동일한 역할을 하는 컴포넌트를 클래스 형태로 만든 것이다.

 

함수 컴포넌트와의 차이점은, 리액트의 모든 클래스 컴포넌트는 React.Component를 상속받아 만든다는 것이다.

 

상속이라는 것은 객체 지향 프로그래밍에서 나오는 개념

 

한 클래스의 변수들과 함수들을 상속받아 새로운 자식 클래스를 만드는 방법

 

여기서는 React.Component라는 클래스를 상속받아 Welcome라는 클래스를 만들었고,

 

React.Component를 상속받았기 때문에 결과적으로 리액트 컴포넌트가 되는 것이다.

 

 

7. 이름짓기

 

컴포넌트의 이름을 지을때는 항상 컴포넌트의 이름은 대문자로 시작해야한다는 점이다.

 

리액트는 소문자로 시작하는 컴포넌트를 DOM 태그로 인식하기 때문이다.

 

예를 들어 <div>나 <span>과 같이 사용하는 것은 내장 컴포넌트라는 것을 뜻하며,

 

'div'나 'span'과 같은 문자열 형태로 React.createElement()에 전달된다.

 

하지만 <Foo />와 같이 대문자로 시작하는 경우에는 React.createElement(Foo)의 형태로 컴파일되며,

 

자바스크립트 파일 내에서 사용자가 정의했거나 import 한 컴포넌트를 가리킨다.

 

그러므로 컴포넌트 이름은 항상 대문자로 이름을 시작해야한다.

 

//HTML div 태그로 인식

const element = <div />

//Welcome이라는 리액트 컴포넌트로 인식
const element = <Welcome name = '대혁' />;

 

첫번째 코드는 DOM 태그를 사용해서 엘리먼트를 만든 것이다.

 

기본적으로 DOM 태그들은 div, h1, span 등처럼 모두 소문자로 시작한다.

 

두번째 코드는 사용자가 정의한 Welcome라는 컴포넌트를 사용한 엘리먼트이다.

 

만약 여기에 컴포넌트 이름이 소문자로 시작해서 welcome가 되었다면, 리액트는 내부적으로 DOM태그로 인식하게 된다.

 

그래서 항상 컴포넌트의 이름은 대문자로 시작해야 된다는 사실을 반드시 기억해야한다.

 

정말로 소문자를 쓰고싶다면, 대문자로 시작하는 변수에 컴포넌트를 할당하고 할당된 변수를 쓰라네..

 

 

8. 컴포넌트 렌더링하기

 

컴포넌트는 붕어빵 틀을 하는 역할로, 실제 화면에 보이는 것은 이런 붕어빵 틀에 의해 만들어진 붕어빵, 리액트 엘리먼트이다.

 

그러므로 렌더링을 위해서는 가장 먼저 컴포넌트로부터 엘리먼트를 만들어야한다.

 

//DOM 태그를 사용한 element

const element = <div />

//사용자가 정의한 컴포넌트를 사용한 element

const element = <Welcome name="대혁" />;

 

위 두 줄의 코드는 모두 리액트 엘리먼트를 만든다.

 

function Welcome(props){
    return <h1>안녕, {props.name}</h1>
}

const element = <Welcome name="대혁" />
ReactDOM.render(
    element,
    document.getElementById('root')
)

 

먼저 Welcome이라는 함수 컴포넌트를 선언

 

name = "대혁"이라는 props를 넘겨서 element를 생성해 const element에 저장

 

이렇게 만들어진 element는 실제 Root DOM node에 할당되어 브라우저에 보여짐

 

 

9. 컴포넌트 합성

 

컴포넌트 합성은 여러개의 컴포넌트를 합쳐서 하나의 컴포넌트를 만드는 것

 

리액트에서는 컴포넌트 안에 또 다른 컴포넌트를 사용할 수 있으므로,

 

복잡한 화면을 여러 개의 컴포넌트로 나눠서 구현가능하다.

 

function Welcome(props) {
    return <h1>Hello, {props.name}</h1>
}

function App(props) {
    return (
        <div>
            <Welcome name="Mike" />
            <Welcome name="Steve" />
            <Welcome name="Jane" />
        </div>
    )
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
)

 

위의 코드는 props의 값을 다르게 해서 Welcome 컴포넌트를 여러번 사용하는 코드이다.

 

App이라는 컴포넌트는 Welcome 컴포넌트 3개를 포함하고 있는 컴포넌트가 된다.

 

이렇게 여러개의 컴포넌트를 합쳐서 또 다른 컴포넌트를 만드는 것을 컴포넌트 합성이라고 부른다.

 

 

App 컴포넌트 안에 3개의 Welcome 컴포넌트가 있고, 각각의 Welcome 컴포넌트는 각기 다른 props를 가지고 있다.

 

이런식으로 App컴포넌트를 root로 해서 하위 컴포넌트들이 존재하는 형태가 리액트로만 구성된 앱의 기본적인 구조이다.

 

기존에 있던 웹페이지에 리액트를 연동한다면 Root Node가 하나가 아닐 수 있어서, 이런 구조가 되지 않을 수 있다.

 

 

10. 컴포넌트 추출

 

컴포넌트 합성과 반대로 복잡한 컴포넌트를 쪼개서 여러개의 컴포넌트로 나눌 수도 있다.

 

이런 과정을 컴포넌트 추출이라고 부른다.

 

큰 컴포넌트에서 일부를 추출해서 새로운 컴포넌트를 만든다는 뜻이다.

 

컴포넌트 추출을 잘 활용하면 컴포넌트의 재사용성이 올라가게 된다.

 

컴포넌트가 작아질수록 해당 컴포넌트의 기능과 목적이 명확해지고, props도 단순해져서 다른 곳에서 사용할 수 있는 확률이 높아지기 때문이다.

 

재사용성이 올라가면서 개발 속도도 향상된다.

 

function Comment(props) {
    return (
        <div className="comment">
            <div className="user-info">
                <img className='avatar'
                src={props.author.avatarUrl}
                alt={props.author.name}
                />
                <div className='user-info-name'>
                    {props.author.name}
                </div>
            </div>

            <div className="comment-text">
                {props.text}
            </div>

            <div className="comment-date">
                {formatDate(props.date)}
            </div>
        </div>
    )
}

 

 

만약 여기에 Comment라는 컴포넌트가 하나 있다.

 

이 컴포넌트는 댓글을 표시하기 위한 컴포넌트로 내부에 작성자의 프로필 이미지와 이름, 댓글내용 작성일을 포함하고 있다.

 

이제 이 컴포넌트에서 하나씩 컴포넌트를 추출해 보면

 

먼저 Avatar 추출을 할수 있다.

 

Comment 컴포넌트에서는 <img> 태그를 사용해 사용자의 프로필 이미지를 표시하고 있다.

 

function Avatar(props){
    return (
        <img className="avatar"
        src = {props.user.avatarUrl}
        alt = {props.user.name}
        />
    )
}

 

추출된 Avatar 컴포넌트는 위와 같은 모습이 될 것이다.

 

props에 기존에 사용하던 author 대신 조금 더 보편적인 의미를 갖고 있는 user를 사용하였다.

 

보편적인 단어를 사용한다는 것은 재사용성 측면을 고려하는 것이다.

 

다른 곳에서 이 컴포넌트를 사용할 때도 props에 들어갈 속성들이 의미상 큰 차이 없이 사용할 수 있게 하기 위함이다.

 

이렇게 추출된 Avatar 컴포넌트를 Comment 컴포넌트에 넣으면...

 

function Comment(props) {
    return (
        <div className="comment">
            <div className="user-info">
                <Avatar user={props.author}/>
                <div className="user-info-name">
                    {props.author.name}
                </div>
            </div>

            <div className="comment-text">
                {props.text}
            </div>

            <div className="comment-date">
                {formatDate(props.date)}
            </div>
        </div>
    )
}

 

조금 더 간결해지긴 했네...

 

여기에 사용자 정보를 담는 부분을 추출해보자.

 

function UserInfo(props) {
    return (
        <div className="user-info">
            <Avatar user={props.user}/>
            <div className="user-info-name">
                {props.user.name}
            </div>
        </div>
    )
}

 

위의 코드는 사용자 정보를 담고 있는 부분을 UserInfo라는 컴포넌트로 추출한 것이다.

 

아까 처음에 추출했던 Avatar 컴포넌트도 여기에 함께 추출된 것을 볼 수 있다.

 

그리고 props에 author 대신에 좀 더 보편적인 의미를 가지는 user를 사용하였다.

 

여기에 추출된 UserInfo 컴포넌트를 Comment 컴포넌트에 반영해보자.

 

function Comment(props) {
    return (
        <div className="comment">
            <UserInfo user={props.author} />

            <div className="comment-text">
                {props.text}
            </div>

            <div className="comment-date">
                {formatDate(props.date)}
            </div>
        </div>
    )
}

 

UserInfo 컴포넌트를 반영하면.. 위와 같이 되며 코드가 처음에 비해 훨씬 단순해진다

 

 

Comment 컴포넌트가 UserInfo 컴포넌트를 포함하고 있고, UserInfo 컴포넌트가 Avatar 컴포넌트를 포함하고 있는 구조이다.

 

Comment-text, Comment-date 부분도 추출해서 사용할 수는 있다.

 

어디까지 추출하는 것이 좋은지에 대해서는 정해진 기준은 없다.

 

하지만 기능 단위로 구분하는 것이 좋고, 나중에 곧바로 재사용이 가능한 형태로 추출하는 것이 좋다.

 

재사용이 가능한 컴포넌트를 많이 갖고 있어야 개발 속도가 빨라져..?.....

 

 

11. 실습으로 따라하기

 

$npx create-react-app my-app으로 리액트 앱 생성

 

src 폴더에 chapter_05 생성하고 Comment.jsx 생성하기

 

import React from "react";

function Comment(props) {
    return (
        <div>
            <h1>제가 만든 첫 컴포넌트입니다.</h1>
        </div>
    )
}

export default Comment;

 

동일한 폴더에 CommentList.jsx라는 이름의 파일을 새로 만들고 작성

 

import React from "react";
import Comment from './Comment';

function CommentList(props) {
    return (
        <div>
            <Comment />
        </div>
    )
}

export default CommentList;

 

만든 컴포넌트를 index.js에서 다음과 같이 수정하여 렌더링 시도

 

import CommentList from './chapter_05/CommentList'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <CommentList />
  </React.StrictMode>
);

 

 

npm start 실행

 

 

조금 더 댓글처럼 보이도록 Comment 컴포넌트에 간단한 css 스타일을 작성

 

const styles = {
    wrapper: {
        margin: 8,
        padding: 8,
        display: 'flex',
        flexDirection: 'row',
        border: '1px solid grey',
        borderRadius:16,
    },
    imageContainer: {},
    image:{
        width:50,
        height:50,
        borderRadius:25,
    },
    contentContainer: {
        marginLeft: 8,
        display:'flex',
        flexDirection:'column',
        justifyContent:'center',
    },
    nameText: {
        color: 'black',
        fontsize:16,
        fontWeight: 'bold',
    },
    commentText: {
        color:'black',
        fontsize:16
    }
}

 

Comment 컴포넌트도 다음과 같이 변경

 

function Comment(props) {
    return (
        <div style={styles.wrapper}>
            <div style={styles.imageContainer}>
                <img
                src = "https://upload.wikimedia.org/wikipedia/commons/8/89/Portrait_Placeholder.png?20170328184010"
                style={styles.image}
                />
            </div>

            <div style={styles.contentContainer}>
                <span style={styles.nameText}>윤대혁</span>
                <span style={styles.commentText}>
                    제가 만든 첫 컴포넌트입니다.
                </span>
            </div>
        </div>
    )
}

 

다음과 같이 props로 변경하면...

 

function Comment(props) {
    return (
        <div style={styles.wrapper}>
            <div style={styles.imageContainer}>
                <img
                src = "https://upload.wikimedia.org/wikipedia/commons/8/89/Portrait_Placeholder.png?20170328184010"
                style={styles.image}
                />
            </div>

            <div style={styles.contentContainer}>
                <span style={styles.nameText}>{props.name}</span>
                <span style={styles.commentText}>
                    {props.comment}
                </span>
            </div>
        </div>
    )
}

export default Comment;

 

commentlist에서 전달하는 것이 없어서, 아무것도 표시되지 않는다.

 

 

 

다음과 같이 commentlist에서 comment부분에 name, comment를 전달하면...

 

import React from "react";
import Comment from './Comment';

function CommentList(props) {
    return (
        <div>
            <Comment name='윤대혁' comment="안녕하세요" />
        </div>
    )
}

export default CommentList;

 

 

 

commentlist에 comment 컴포넌트를 하나 더 추가해주면 

 

import React from "react";
import Comment from './Comment';

function CommentList(props) {
    return (
        <div>
            <Comment name='윤대혁' comment="안녕하세요" />
            <Comment name='태연' comment='바보' />
        </div>
    )
}

export default CommentList;

 

 

 

 

다음과 같이 배열에 데이터를 저장해서, 동적으로 할당할 수도 있다

 

const comments = [
    {
        name: "윤대혁",
        comment: "안녕하세요 대혁이에요"
    },
    {
        name: "태연",
        comment: "안녕하세요, 태연이에요"
    },
    {
        name: "수지",
        comment: "안녕하세요, 수지에요"
    }
]

function CommentList(props) {
    return (
        <div>
            {comments.map((comment) => {
                return (
                    <Comment name={comment.name} comment={comment.comment} />
                )
            })}
        </div>
    )
}

 

 

comments라는 배열에 댓글 데이터를 담은 객체들을 넣어준다.

 

자바스크립트 배열의 map()함수를 사용해서 각 댓글 객체에 대해 Comment 컴포넌트를 리턴하도록 처리

 

그리고 comments.map()~~~부분은 중괄호 안에 넣어줘야한다. 자바스크립트 코드니까

 

TAGS.

Comments