[React] Components와 Props

문지은·2023년 7월 12일
0

React

목록 보기
4/24
post-thumbnail

Components and Props

Components

  • 리액트는 컴포넌트 기반(Component Based)구조
    • 작은 컴포넌트들이 모여서 하나의 컴포넌트를 구성하고, 이러한 컴포넌트들이 모여서 전체 페이지를 구성
    • 하나의 컴포넌트를 반복적으로 사용함으로써 전체 코드 양을 줄어 개발 시간과 유지 보수 비용을 줄일 수 있다.
  • 컴포넌트의 역할은 어떠한 속성을 입력으로 받아 그에 맞는 리액트 엘리먼트를 생성하여 리턴해주는 것
    • 만들고자 하는 대로 props(속성)을 넣으면 해당 속성에 맞춰 화면에 나타날 엘리먼트를 만들어 주는 하나의 함수로 이해하면 쉬움

Props

  • 리액트 컴포넌트의 속성
    • 리액트 컴포넌트에서 눈에 보이는 글자나 색깔 등의 속성을 바꾸고 싶을 때 사용하는 컴포넌트 속의 재료
    • 컴포넌트에 전달할 다양한 정보를 담고 있는 자바스크립트 객체
  • 컴포넌트에 어떤 데이터를 전달하고 전달된 데이터에 따라 다른 모습의 엘리먼트를 화면에 렌더링하고 싶을 때, 해당 데이터를 props에 넣어 전달
  • Props는 읽기 전용
    • 모든 리액트 컴포넌트는 props를 직접 바꿀 수 없고, 같은 props에 대해서는 항상 같은 결과(엘리먼트)를 리턴해야 함
    • 리액트 컴포넌트는 Pure 함수와 같은 역할을 함

Props 사용법

JSX를 사용하는 경우

  • 키와 값으로 이루어진 키-값 쌍의 형태로 컴포넌트에 props를 넣을 수 있음
  • 아래 코드에서는 App 컴포넌트 안에 Profile 컴포넌트를 사용하고 있으며, Profile 컴포넌트 안에 name, introduction, viewCount라는 세 가지 속성이 props로 전달됨
function App(props) {
	return (
    	<Profile
        	name="지은"
          	introduction="안녕하세요, 지은입니다."
          	viewCount={1500}
        />
    );
}
  • 즉, props는 아래와 같은 형태의 자바스크립트 객체가 됨
{
	name: "지은",
    introduction: "안녕하세요, 지은입니다.",
    viewCount: 1500
}
  • 이전에 JSX에서 자바스크립트 코드를 사용하려면 중괄호를 사용해야 한다고 학습하였음
    • props에 값을 넣을 때에도 문자열 이외에 정수, 변수, 그리고 다른 컴포넌트 등이 들어갈 경우에는 중괄호를 사용해서 감싸주어야 함
    • 중괄호를 사용하게 되면 아래와 같이 props의 값으로 컴포넌트를 넣을 수도 있음
funtion App(props) {
	return (
    	<Layout
          width={2500}
          height={1440}
          header={
          	<Header thtile="Jieun의 블로그 입니다.">
          }
          footer={
          	<Footer />
          }
        />
    );
}

JSX를 사용하지 않는 경우

  • createElement 두 번째 파라미터 값에 자바스크립트 객체를 넣으면 그게 곧 해당 컴포넌트의 Props
React.createElement(
	type,
    [props],
    [...children]
)
  • 위에서 작성했던 Profile 컴포넌트를 JSX를 사용하지 않고 코드를 작성하면 아래와 같다.
React.createElement(
	Profile,
  	{	
      	name: "지은",
      	introduction: "안녕하세요, 지은입니다.",
      	viewCount: 1500
    },
  	null
);

컴포넌트 만들기

컴포넌트 종류

함수 컴포넌트

  • 리액트의 컴포넌트는 일종의 함수
  • 하나의 props 객체를 받아 리액트 엘리먼트를 리턴
  • 아래 Welcome 함수는 props 객체를 받아 인사말이 담긴 리액트 엘리먼트를 리턴
function Welcom(props) {
	return <h1>안녕, {props.name}</h1>;
}

클래스 컴포넌트

  • 자바스크립트 ES6의 클래스를 사용하여 만들어진 형태의 컴포넌트
  • 클래스 컴포넌트는 React.Component를 상속받아서 만듦
  • 위에서 살펴본 함수 컴포넌트 Welcome과 동일한 역할을 하는 컴포넌트를 클래스 형태로 만들면 아래와 같다.
class Welcome extends React.Component {
	render() {
    	return <h1>안녕, {this.props.name}</h1>;
    }
}

컴포넌트 이름 짓기

  • 컴포넌트의 이름은 항상 대문자로 시작해야함
    • 리액트는 소문자로 시작하는 컴포넌트를 DOM 태그로 인식하기 때문
    • <div><span>과 같이 사용하는 것은 내장 컴포넌트라는 것을 뜻하며, div, span과 같은 문자열 형태로 React.createElement()에 전달됨
    • 하지만 <Foo />와 같이 대문자로 시작하는 경우에는 React.createElement(Foo) 형태로 컴파일되며 자바스크립트 파일 내에서 사용자가 정의했거나 임포트한 컴포넌트를 가리킴
// HTML div 태그로 인식
const element = <div />;

// Welcome이라는 리액트 컴포넌트로 인식
const element = <Welcome name="지은" />;

컴포넌트 렌더링

  • 컴포넌트로부터 엘리먼트를 만든 후 엘리먼트를 파라미터로 ReactDOM.render()를 호출하여 화면에 렌더링
  • 위에서 작성했던 Welcome 컴포넌트를 렌더링하는 코드를 작성하면 아래와 같다.
function Welcome(props) {
	return <h1>안녕, {props.name}</h1>;
}

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

컴포넌트 합성

  • 여러 개의 컴포넌트를 합쳐서 하나의 컴포넌트를 만드는 것
  • 리액트에서는 컴포넌트 안에 또 다른 컴포넌트를 사용할 수 있기 때문에, 복잡한 화면을 여러 개의 컴포넌트로 나누어서 구현할 수 있음
  • Welcome 컴포넌트를 사용해서 컴포넌트 합성을 하는 코드를 작성해보면 아래와 같다.
    • App 컴포넌트 안에 세 개의 Welcome 컴포넌트가 있고, 각각의 Welcome 컴포넌트는 각기 다른 props를 가지고 있음
    • 이렇게 App 컴포넌트를 root로 해서 하위 컴포넌트들이 존재하는 형태가 리액트로만 구성된 앱의 기본적인 구조
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도 단순해지기 때문에 다른 곳에서 사용할 수 있는 확률이 높아지기 때문!

예시 코드를 통해 컴포넌트를 추출하는 과정을 살펴보자.

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>
    );
}
  • 위 컴포넌트에서는 <img> 태그를 사용하여 사용자의 프로필 이미지를 표시하고 있음
    • 이 부분을 추출하여 Avatar라는 별도의 컴포넌트로 만들어보자.
function Avatar(props) {
	return (
    	<img className="avatar"
        	src={props.user.avatarUrl}
          	alt={props.user.name}
         />
    );
}
  • 이렇게 추출한 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>
    );
}
  • 이번에는 사용자 정보를 담고 있는 부분을 추출해보자.
funtion UserInfo(props) {
  return (
  	<div className="user-info">
            <Avatar user={props.user} />
        <div className="">
            {props.user.name}
        </div>
    </div>
  );
}
  • 추출한 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>
    );
}
  • 처음 코드와 비교하면 코드가 매우 단순해 진 것을 볼 수 있음.
  • 컴포넌트를 어느 정도 수준까지 추출하는 것이 좋은지에 대해 정해진 기준은 없지만, 기능 단위로 구분하는 것이 좋고, 나중에 곧바로 재사용이 가능한 형태로 추출하는 것이 좋음.

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

  • 컴포넌트와 props에 대해 학습한 내용을 바탕으로 댓글 컴포넌트를 만들어보자.
  • Comment라는 이름의 리액트 함수 컴포넌트를 만든다.
// src/chapter_05/Comment.jsx

import React from 'react';

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

export default Comment;
  • 여러개의 댓글 컴포넌트를 포함하고 있는 댓글 목록 컴포넌트를 만든다.
// src/chapter_05/CommentList.jsx

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

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

export default CommentList;
  • 만든 CommentList 컴포넌트를 실제로 화면에 렌더링하기 위해 index.js 파일을 수정한다.
// src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import CommentList from '.chapter_05/CommentList';

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

reportWebVitals();
  • 실행 결과
  • 실제 댓글 모양처럼 보이게 하기 위해 Comment 컴포넌트에 간단한 CSS 스타일을 작성하고 컴포넌트 코드를 변경해보자.
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: 16,
        fontWeight: "bold",
    },
    commentText: {
        color: "black",
        fontSize: 16,
    },
};

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"
                    style={styles.image}
                />
            </div>

            <div style={styles.contentContainer}>
                <span style={styles.nameText}>문지은</span>
                <span style={styles.commentText}>제가 만든 첫 컴포넌트입니다.</span>
            </div>
        </div>
    );
}

export default Comment;
  • 실행 결과
  • 작성자 이름과 댓글 내용을 동적으로 변경할 수 있게 하기 위해 props를 추가해보자.
    • 작성자 이름과 댓글 내용을 각각 props.name, props.comment 값을 사용하도록 변경한다.
// src/chapter_05/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: 16,
        fontWeight: "bold",
    },
    commentText: {
        color: "black",
        fontSize: 16,
    },
};

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"
                    style={styles.image}
                />
            </div>

            <div style={styles.contentContainer}>
                // 작성자 이름과 댓글 내용을 각각 `props.name`, `props.comment` 값을 사용하도록 변경한다.
                <span style={styles.nameText}>{props.name}</span>
                <span style={styles.commentText}>{props.comment}</span>
            </div>
        </div>
    );
}

export default Comment;
  • 변경하면 아직 name과 comment 값이 정의되지 않아 undefined 값이기 때문에 아래와 같이 아무런 내용도 나오지 않는다.
  • CommentList 컴포넌트에서 코드를 수정하여 props로 name과 comment 값을 전달해보자.
// src/chapter_05/CommentList.jsx

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

function CommentList(props) {
    return (
        <div>
            <Comment name={"문지은"} comment={"안녕하세요, 문지은입니다."}/>
        </div>
    );
}

export default CommentList;
  • 이제 댓글 내용이 정상적으로 표시됨을 확인할 수 있다.
  • Comment 컴포넌트를 하나 더 추가하고 작성자 이름과 댓글 내용을 다르게 입력하여 댓글을 하나 더 추가해보자.
// src/chapter_05/CommentList.jsx

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

function CommentList(props) {
    return (
        <div>
            <Comment name={"문지은"} comment={"안녕하세요, 문지은입니다."}/>
            <Comment name={"유재석"} comment={"리액트 재미있어요!"}/>
        </div>
    );
}

export default CommentList;
  • 댓글이 하나 더 표시됨을 확인할 수 있다.
  • 이제 댓글 데이터를 별도의 객체로 분리해서 동적으로 받아온 데이터를 표시할 수 있는 구조로 만들어보자.
    • comments 라는 배열을 만들어 댓글 데이터를 담고 있는 개체들을 넣어주고,
    • 자바스크립트 배열의 map() 함수를 써서 각 댓글 객체에 대해 Comment 컴포넌트를 리턴하도록 코드를 작성한다.
// src/chapter_05/CommentList.jsx

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;
  • comments 배열에 있는 댓글 객체 수만큼 Comment 컴포넌트가 렌더링된 것을 볼 수 있다.

실습 전체 코드

References

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글