코딩/React

타입스크립트 React props 전달하는 방법 및 IntrinsicAttributes 오류 해결

드리프트 2022. 2. 20. 12:29
728x170

안녕하세요?

 

오늘은 타입스크립트로 리액트 코드를 짤 때 하위 컴포넌트(child componet)에 Props를 전달하는 제대로 된 방법을 알아보겠습니다.

 

그냥 자바스크립트로 작성하면 크게 문제가 안되지만 타입스크립트로 작성하면 되게 까다롭습니다.

 

먼저, 상황을 설정해 보겠습니다.

 

supabase로 posts 테이블에서 데이터를 불러와서 title로 리스트를 작성하는 페이지를 작성해 보겠습니다.

 

먼저 코드를 보겠습니다.

 

import { useState, useEffect } from "react";
import { supabase } from "../lib/supabase";

const ListPosts = (post) => {
  return (
    <li>
      <div>{post.title}</div>
      <div>{post.content}</div>
    </li>
  );
};

const PostsPage = () => {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetchPosts();
  }, []);

  const fetchPosts = async () => {
    let { data: posts, error } = await supabase
      .from("posts")
      .select("id, title, content")
      .order("id");
    if (error) console.log(error);
    else setPosts(posts);
  };

  return (
    <div className="py-32 text-center">
      <h2>Posts List</h2>
      <ul>
        {posts &&
          posts.map((post) => (
            <ListPosts key={post.id} post={post}></ListPosts>
          ))}
      </ul>
    </div>
  );
};

export default PostsPage;

 

먼저 코드를 간단하게 설명해 보면 PostsPage에서 useEffect 훅으로 fetchPosts 함수로 supabase에서 데이터를 가져옵니다.

 

그리고 그 데이터를 posts라는 State(스테이트)에 저장합니다.

 

그리고 posts라는 배열을 map 함수로 리스트 업 하는 코드인데요.

 

posts.map 에서 하위 컴포넌트로 post 데이터를 패싱 하는 코드입니다.

 

<ListPosts key={post.id} post={post}></ListPosts> 코드에서 우리는 post라는 이름으로 하위 컴포넌트에 post를 넘기고 있습니다.

 

그리고 ListPosts 함수는 (post)라고 해서 post 받고 그다음 post.title, post.content 데이터를 화면에 출력하게 됩니다.

 

실행 결과를 볼까요?

 

Posts List 밑에 데이터가 출력이 안 되고 있습니다. 뭔가 이상합니다.

 

PostsPage 컴포넌트에서 부른 하위 컴포넌트 ListsPosts에 post 데이터가 전달이 안되고 있습니다.

 

왜일까요?

 

타입스크립트로 이유를 알아보기 전에 자바스크립트에서는 어떻게 작동되는지 볼까요?

 

먼저 ListProps 코드를 다음과 같이 바꾸고 확장자도. jsx로 바꾸고 실행해 보겠습니다.

 

const ListPosts = (props) => {
  console.log(props);
  return (
    <li>
      <div>{props.post.title}</div>
      <div>{props.post.content}</div>
    </li>
  );
};

정상적으로 실행되고 있네요.

 

리액트는 하위 컴포넌트로 props객체를 보내는데 그 객체에 상세 props가 들어 있습니다.

 

즉, ListPosts에 props라고 명시했으니까 이 props 객체에 post가 들어 있는 경우죠.

 

그래서 맨 처음 코드가 작동이 안 되었던 겁니다.

 

그럼, 다시 타입스크립트로 돌아가 보겠습니다.

 

파일 확장자를. tsx로 바꾸고 ListPosts 함수도 적당하게 바꿔 보겠습니다.

 

ListPosts props를 위해 아래와 같이 PostsProps도 만들어 보겠습니다.

 

export interface PostsProps {
  id: number;
  title: string;
  content: string;
}

그리고 PostsProps를 이용해서 ListPosts 컴포넌트도 바꿔 보겠습니다.

 

const ListPosts = (post: PostsProps) => {
  return (
    <li>
      <div>{post.title}</div>
      <div>{post.content}</div>
    </li>
  );
};

 

뭔가 제대로 진행되고 있는 느낌입니다.

 

테스트 결과를 볼까요?

 

리스트가 나타나지 않고 있습니다.

 

그리고 아래와 같이  PostsPage 컴포넌트에서도 에러가 나왔습니다.

 

IntrinsicAttributes 에러인데요.

 

왜 이런 에러가 나왔을까요?

 

 

원인은  ListPosts = (post: PostsProps) 여기에 있습니다.

 

리액트는 props 객체를 그대로 보내고 그 객체 밑에 테이터를 저장하는 형태입니다.

 

그래서 우리의 post는 사실 props.post.title 형식으로 사용돼야 합니다.

 

어떻게 고쳐야 할까요?

 

const ListPosts = ({ post }) => {

 

위와 같이 인수를 { post }로 넣어 줘야 합니다.

 

이 방식은 넘겨오는 props 객체를 디스트럭쳐링해서 post 항목만 빼온다는 얘기입니다.

 

그럼 타입스크립트를 쓴다면 { post }의 타입을 지정해 줘야 하는데요.

 

const ListPosts = ({ post: PostsProps }) => {

 

위와 같이 쓸까요? 아닙니다.

 

객체 디스트럭쳐링에서는 위와 같이 쓰면 안 됩니다.

 

바로 아래와 같이 쓰셔야 합니다.

 

const ListPosts = ({ post }: { post: PostsProps }) => {

 

위와 같이 쓰면 아까 발생했던 타입스크립트의 IntrinsicAttributes  에러도 없어졌습니다.

 

 

React Children 알아보기

 

지금까지 리액트에서 타입스크립트로 하위 컴포넌트에 데이터를 넘기는 걸 알아보았는데요.

 

좀 더 나아가서 React Children에 대해 알아보겠습니다.

 

Children 이란 바로 다음과 같이 컴포넌트를 쓸 때 발생합니다.

 

<ListPosts key={post.id} post={post}></ListPosts>

만약 위의 코드를 다음과 같이 쓰게 되면 children 이 없는 거죠

 

<ListPosts key={post.id} post={post} />

 

즉, 리액트에서 children 이란 바로 <> 태그와 </> 태그 사이에 있는 데이터가 children 이 됩니다.

 

이 children은 또 다른 DOM이 올 수 있고 그냥 string이 올 수 도 있습니다.

 

그럼 아래와 같이 React Children을 일부로 만들었습니다.

 

<ListPosts key={post.id} post={post}>
	Hello
</ListPosts>

이럴 경우 타입스크립트로는 어떻게 props 타입을 전달해 줘야 할까요?

 

간단합니다.

 

props 객체 밑에 post 객체와 children  객체 두 개가 전달된 경우입니다.

 

그래서 객체 디스트럭쳐링 방식으로 아래와 같이 쓰면 됩니다.

 

const ListPosts = ({
  post,
  children,
}: {
  post: PostsProps;
  children: React.ReactNode;
}) => {
  return (
    <li>
      <div>{children}</div>
      <div>{post.title}</div>
      <div>{post.content}</div>
    </li>
  );
};

즉, post, children 두 개가 있는데 그 타입은 각각 PostsProps이고 React.ReactNode라고 지정했습니다.

 

실행결과를 볼까요?

 

아까 children으로 넣었던 Hello 가 잘 표시되고 있네요.

 

그래서 children 이 필요 없을 경우는 아래와 같이 쓰는 게 좀 더 좋습니다.

 

<ListPosts key={post.id} post={post} />

 

이상으로 타입스크립트로 하위 컴포넌트에 props 전달하는 방법에 대해 알아봤습니다.

 

 

그리드형