컴포넌트 Props

#React

Props 개념

리액트에서는 부모 컴포넌트가 자식 컴포넌트에 데이터를 전달할 수 있습니다. 이때 사용하는 것이 바로 props(properties)입니다. props는 컴포넌트를 마치 HTML 태그처럼 사용해 값을 속성 형태로 전달합니다.

부모는 자식에게 props를 이용하여 데이터를 전달할 수 있다. props는 HTML의 속성처럼 컴포넌트의 태그 안에 입력되며, 여러개의 속성을 전달해도 항상 props 하나의 객체로 전달 된다.

Props 전달 기본

기본 문자/표현식

문자는 따옴표로, 숫자나 표현식은 중괄호에 넣어 부모 컴포넌트에서 다음과 같이 전달한다.

// 부모
export default function App() {
    return(
        <Header name="엄노니" age={30} />
    );
}

// 자식
export default function Header(props: { name: string; age: number }) {
    return(
        <>
            <h1>name: { props.name }</h1>
            <h1>age: { props.age }</h1>
        </>
    );
}

자식 컴포넌트 측에서는 매개변수로 props를 받아 사용할 수 있다. props가 객체로 전달되므로 props.name과 같은 형태로 객체의 값을 출력하여 사용하면 된다.

객체

부모 측에서 전달하려는 속성 값이 여러개일 경우 속성을 따로따로 정의하는 것이 번거로울 수 있는데, 이럴 때는 하나의 객체로 값을 묶어 전달할 수 있다.

//부모
export default function App() {
    const userProps = { name: "엄노니", age: 30 };
    return(
        <Header userProps={ userProps } />
    );
}

//자식
export default function Header(props: { userProps: { name: string; age: number; }; }) {
    return(
        <>
            <h1>name: { props.userProps.name }</h1>
            <h1>age: { props.userProps.age }</h1>
        </>
    );
}

함수

props에는 값 뿐만 아니라 이벤트 핸들러 함수도 전달이 가능하다. 매개변수와 리턴값이 없는 함수의 경우 매개변수 형태는 () => void로 받는다.

//부모
export default function App() {
    const userProps = { name: "엄노니", age: 30 };
    const clickHandler = () => {
         console.log('clicked!');
    }
    return(
        <Header userProps={ userProps } clickHandler={ clickHandler }/>
    );
}

//자식
export default function Header(props: { userProps: { name: string; age: number; }; clickHandler: () => void }) {
    return(
        <>
            <h1>name: { props.userProps.name }</h1>
            <h1>age: { props.userProps.age }</h1>
            <button onClick={ props.clickHandler }>클릭</button>
        </>
    );
}

Props 전달 응용

구조 분해 할당

자식 컴포넌트의 매개변수가 복잡하므로 구조 분해 할당을 이용해 정리하면 좋다.

먼저 매개변수의 props{ userProps, clickHandler }로 구조 분해 할당한다.

// 자식
export default function Header({ userProps, clickHandler }: { userProps: { name: string; age: number; }; clickHandler: () => void }) {
    return(
        <>
            <h1>name: { userProps.name }</h1>
            <h1>age: { userProps.age }</h1>
            <button onClick={ clickHandler }>클릭</button>
        </>
    );
}

userProps는 객체이므로, 한 번 더 { name, age }로 구조 분해 할당한다. 이제 name, age, clickHandler 형태로 JSX 내부에서 사용할 수 있게 되었다.

// 자식
export default function Header({ userProps: { name, age }, clickHandler }: { userProps: { name: string; age: number; }; clickHandler: () => void; }) {
    return(
        <>
            <h1>name: { name }</h1>
            <h1>age: { age }</h1>
            <button onClick={ clickHandler }>클릭</button>
        </>
    );
}

구조 분해 할당부 분리

props는 꼭 매개변수 위치에서 구조 분해 할당 하지 않아도 되고, 함수 내부에 분리가 가능하다. 구조 분해 할당한 부분을 다시 props로 대체한 뒤, return문 위쪽으로 분리해준다.

// 자식
export default function Header(props: { userProps: { name: string; age: number; }; clickHandler: () => void; }) {
    const { userProps: { name, age }, clickHandler } = props;
    return(
        <>
            <h1>name: { name }</h1>
            <h1>age: { age }</h1>
            <button onClick={ clickHandler }>클릭</button>
        </>
    );
}

interface 사용

좀 더 매개변수 선언을 줄이기 위해 ts의 interface를 사용할 수 있다. interface가 있는 파일은 src/types에 위치시키는 것이 관례이며, interface는 따로 import 없이 모든 파일에서 접근이 가능하다(설정에 따라 다를 수 있음).

// UserProps.d.ts
interface UserProps {
    userProps: { name: string; age: number; }; 
    clickHandler: () => void; 
}

// 자식
export default function Header(props: UserProps) {
    const { userProps: { name, age }, clickHandler } = props;
    return(
        <>
            <h1>name: { name }</h1>
            <h1>age: { age }</h1>
            <button onClick={ clickHandler }>클릭</button>
        </>
    );
}

전개연산자 사용

부모 측에서 전개 연산자를 쓰면 하나로 묶었던 객체를 분해해서 전달할 수 있다. <Header userProps={ userProps } ~로 전달했던 부분을 <Header { ...userProps }~로 변경한다. userProps= 사라지는 것 확인! interface와 자식 측 구조 분해 할당 코드도 전개 연산자 사용에 맞추어 변경해준다.

//부모
export default function App() {
    const userProps = { name: "엄노니", age: 30 };
    const clickHandler = () => {
         console.log('clicked!');
    }
    return(
        <Header { ...userProps } clickHandler={ clickHandler }/>
    );
}

//UserProps.d.ts
interface UserProps {
    name: string; 
    age: number;
    clickHandler: () => void; 
}

//자식
export default function Header(props: UserProps) {
    const { name, age, clickHandler } = props;
    return(
        <>
            <h1>name: { name }</h1>
            <h1>age: { age }</h1>
            <button onClick={ clickHandler }>클릭</button>
        </>
    );
}

children

컴포넌트는 <Header /> 형태로 바로 닫히는 태그를 사용할 수도 있지만, <Header></Header> 형태로 열고 닫는 태그 형태로도 사용할 수 있다. 부모 측에서 컴포넌트 태그 내부에 다른 요소를 추가하는 경우, props의 children으로 값이 전달된다. 형태는 React.ReactNode이다. 자식 측에서는 props.children을 사용해서 전달받은 콘텐츠를 출력할 수 있다. 그리고 내용이 여러 요소로 구성되어있어도 children 하나로만 전달이 되고, children은 2개 이상 존재할 수 없다.

//부모
export default function App() {
    return(
        <Header>
             <p>hello world!</p>
             <p>bye world!</p>
        </Header>
    );
}

//자식
export default function Header({ children }: { children: React.ReactNode }) {
    return(
        <>
             { children }
        </>
    );
}

children을 사용해도 다른 props 값을 사용할 수 있다.