본문 바로가기

react

[소플] ch 5. 컴포넌트와 Props

1. 컴포넌트 기반 구조 

 

- 리액트를 컴포넌트 기반이라고 부르는 것은 작은 컴포넌트들이 모여서 하나의 컴포넌트를 구성하고, 또 이러한 컴포넌트들이 모여 전체 페이지를 구성하기 때문

- 하나의 컴포넌트를 반복적으로 사용함으로써 전체 코드의 양이 줄어 개발 시간과 유지 보수 비용을 줄일 수 있음

 

 

 

 

- 리액트 컴포넌트는 어떠한 속성들을 입력받아서 그에 맞는 리액트 엘리먼트를 생성하여 리턴해줌

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

 

 

2. Props

 

- 리액트 컴포넌트의 속성

- 리액트 컴포넌트는 붕어빵 틀, Element는 붕어빵 틀에서 만들어진 붕어빵, props는 붕어빵에 들어가는 재료 

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

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

 

 

3. Props의 특징

 

- Props의 중요한 특징은 읽기 전용(Read-only)라는 것 

- All React componenets must act like pure functions with respect  to their props

- 모든 리액트 컴포넌트는 props를 직접 바꿀 수 없고, 같은 props에 대해서는 항상 같은 값을 보여줘야 함 

 

pure 함수 

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

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

 

impure 함수 

- input을 변경 

function withdraw(accout, amount) {
    account.total -= amount;
}

 

- 리액트 컴포넌트에 입력으로 들어오는 props는 자바스크립트 함수의 파라미터와 같음 

- 함수가 Pure하다는 것은 함수의 입력값인 파라미터를 바꿀 수 없다는 의미를 포함 

 

 

4. props의 사용법 

 

- 컴포넌트에 props라는 객체를 전달하는 방법

 

1) JSX를 사용하는 경우

 

 - 키-값 쌍의 형태로 컴포넌트에 props를 넣을 수 있음 

 

ex) App 컴포넌트 

function App(props) {
	return (
    	<profile
            name="소플"
            introduction="안녕하세요, 소플입니다."
            viewCount={1500}
        />
    );
}

- name, introduction, viewCount 속성의 값이 모두 Profile 컴포넌트에 props로 전달되며 props는 자바스크립트 객체가 됨

 

{		
    name="소플",
    introduction="안녕하세요, 소플입니다.",
    viewCount=1500
}

 

ex) 

App 컴포넌트 

function App(props) {
	return (
    	<Layout
            witdh={2560}
            height={1440}
            header={
            	<header title="소플의 블로그입니다."/>
            }
            footer={
            	<Footer/>
            }
        />
    );
}

- Layout 컴포넌트의 props로는 정숫값을 가진 witdh, height와 리액트 엘리먼트인 header, footer가 들어오게 됨 

 

 

2) JSX를 사용하지 않는 경우 

 

리액트의 createElement() 함수 

React.createElement (
    type
    [props], 
    [...children]
)

- 두 번째 파라미터인 props에 자바스크립트 객체를 넣으면 그게 곧 해당 컴포넌트의 props가 됨 

 

React.creaetElement (
    Profile,
    {
    	name : "소플",
        introduction : "안녕하세요, 소플입니다.",
        viewCount: 1500
    },
    null
);

- 타입은 컴포넌트의 이름인 Profile이 들어가고 props로 자바스크립트 객체라 들어감 

- children에는 하위 컴포넌트가 없기 때문에 null이 들어감 

 

 

5. 컴포넌트 만들기 

 

1) 함수 컴포넌트 

 

- 리액트의 컴포넌트는 일종의 함수 

 

ex) 

- 하나의 props 객체를 받아서 인사말이 담긴 리액트 엘리먼트를 리턴

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

 

2) 클래스 컴포넌트 

 

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

- React.Component를 상속 받아서 만듬 

 

ex)

- React.Component 클래스를 상속받아서 Welcome 이라는 클래스를 만듬

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

 

 

6. 컴포넌트 렌더링 

 

- 렌더링을 위해서는 먼저 컴포넌트로부터 엘리먼트를 만들어야 함 

- Welcom 함수 컴포넌트 선언 

- <Welcome name="인제"/>라는 값을 가진 엘리먼트를 파라미터로 해서 ReactDOM.render()를 호출

- 리액트는 Welcome 컴포넌트에 { name:"인제"}라는 props를 넣어서 호출하고 그 결과로 리액트 엘리먼트가 생성 

- 생성된 엘리먼트는 최종적으로 React DOM을 통해 실제 DOM에 효과적으로 업데이트되고 사용자가 브라우저를 통해서 볼 수 있게 됨 

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

const element = <Welcome name="인제" />;
ReactDOM.render (
    element,
    document.getElementById('root')
);

 

 

6. 컴포넌트 합성

 

컴포넌트 합성 (Composing Components)

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

- 리액트에서는 컴포넌트 안에 또 다른 컴포넌트를 사용할 수 있기 때문에, 복잡한 화면을 여러 개의 컴포넌트로 나눠서 구현할 수 있음 

 

ex)

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');
);

- App 컴포넌트는 Welcome 컴포넌트를 3개를 포함한 컴포넌트

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

 

 

7. 컴포넌트 추출 

 

컴포넌트 추출(Extracting component)

 

- 큰 컴포넌트에서 일부를 추출해서 새로운 컴포넌트를 만듬 

- 컴포넌트의 재사용성이 향상됨 

 -> 컴포넌트가 작아질수록 해당 컴포넌트의 기능과 목적이 명확해지고, props도 단순해짐 

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

 

 

Comment 컴포넌트 

- 댓글을 표시하기 위한 컴포넌트로 내부에 작성자의 프로필 이미지와 이름 그리고 댓글 내용과 작성일 포함 

function Comment(props) {
    return (
        <div className="comment">
            <div className="user-info">
                <img className="avatar"
                    src={props.author.avartaUrl}
                    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.data)};
            </div>
        </div>
    );
}

 

1) Avarter 컴포넌트 추출

 

Avarter 컴포넌트 

- 사용자의 프로필 이미지 표시

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

- Avarter 컴포넌트를 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.data)};
            </div>
        </div>
    );
}

 

2) User-Info 컴포넌트 추출

 

User-Info 컴포넌트 

- 사용자의 정보를 담고 있음

- Avatar 컴포넌트도 함께 추출 

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

 

- UserInfo 컴포넌트를 Comment 컴포넌트에 반영 

function Comment(props) {
    return (
        <div className = "comemnt">
            <UserInfo user={props.author} />
            <div className="comment-text">
                {props.text}
            </div>
            <div className="comment-data">
                {formatDate(props.date)}
            </div>
        </div>
    );
}

 

- 추출된 컴포넌트 구조 

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

 

 

8. 실습 - 댓글 컴포넌트 만들기 

 

 

Comment.jsx 댓글 컴포넌트 
CommentList.jsx 댓글 목록 컴포넌트 
Comments 댓글 데이터를 담고 있는 객체 

 

Comment.jsx

import React from "react";

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: 100,
        fontWeight: "bold",
    },
    commentText: {
        color: "black",
        fontSize: 100,
    },
};

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"
                    alt={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.jsx

 

- 댓글 데이터를 별도의 객체로 분리해서 동적으로 받아온 데이터를 표시할 수 있는 구조 

- comments라는 배열을 만들어서 댓글 데이터를 담고 있는 객체들을 넣어줌 

- map() 함수를 써서 각 댓글 객체에 대해서 Comment 컴포넌트를 리턴하도록 작성

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

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>
    );
}

export default CommentList;