노마드코더

[ReactJS로 영화 웹 서비스 만들기] 3. Props & PropTypes

졸려질려 2023. 4. 20. 21:07
반응형
 

ReactJS로 영화 웹 서비스 만들기 – 노마드 코더 Nomad Coders

왕초보를 위한 React

 본 글은 노마드코더의 "ReactJS로 영화 웹 서비스 만들기" 강의를 수강하고, 개인적으로 정리한 글입니다. 더 자세하고 쉬운 설명은 강의를 참고하세요~!

 

[ReactJS로 영화 웹 서비스 만들기] 2. State

본 글은 노마드코더의 "ReactJS로 영화 웹 서비스 만들기" 강의를 수강하고, 개인적으로 정리한 글입니다. 더 자세하고 쉬운 설명은 강의를 참고하세요~! ReactJS로 영화 웹 서비스 만들기 – 노마드

choboit.tistory.com

 지난 글에서는 State 에 대한 강의를 듣고 실습하며 블로그에 정리했습니다. 이번에 정리 해볼 니코쌤의 강의는 ReactJS - Props 에 대한 강의입니다.


1. Props

 Props 란, 부모 컴포넌트(App)가 자식 컴포넌트(Minutes2Hours)에게 데이터를 보내는 방식을 뜻한다. 이전 글에서 구현했던 것은 Props 가 마땅히 필요한 구조가 아니었기 때문에, Props 의 필요성을 느끼기 위해 코드를 초기화 시켜준다.

const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);

const App = () => {
    return <div>
    </div>
};

root.render(<App />);

 우선, 하나의 애플리케이션을 만든다고 생각을 해보자. 간단하게 2개의 버튼을 함수형 컴포넌트로 만들어서 추가한다.

...
    function SaveBtn() {
        return <button>Save Changes</button>
    }

    function ConfirmBtn() {
        return <button>Confirm</button>
    }

    const App = () => {
        return <div>
            <SaveBtn />
            <ConfirmBtn />
        </div>
    };
...

2개 버튼을 간단히 추가한 모습

 그런데, 버튼의 디자인이 너무 기본적이기 때문에 이쁘지 않다. 디자인을 업그레이드 하기 위해서 Style 을 적용해준다.

...
    function SaveBtn() {
        return <button style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10
        }}>Save Changes</button>
    }

    function ConfirmBtn() {
        return <button style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10
        }}>Confirm</button>
    }

    const App = () => {
        return <div>
            <SaveBtn />
            <ConfirmBtn />
        </div>
    };
...

Style 을 적용한 버튼을 2개 추가한 모습

 확실히 디자인이 적용되니 볼만해진 것 같다. 그런데, 이런 버튼이 2개뿐만이 아니라 여러 개가 필요할 것이다. 어떤 어플리케이션이든 버튼은 정말 많이 필요하기 때문이다. 그러면, SaveBtn 이나 ConfirmBtn 같이 개당 9줄 정도 되는 것을 계속 붙여넣어야할 것이다. 한줄로 줄여서 복붙하면 될 것 같지만, 추후에 Style 이 수정된다면 그 많은 버튼들을 일일이 수정 해줘야할 것이다. 이러한 불편함을 개선하려면, Style 이 이미 적용된 버튼에 문구만 원하는 것으로 넣을 수 있다면 좋지 않을까?

 이러한 생각에서 Props 가 필요하다. 디자인과 같이 기본적으로 적용할 사항들은 구축을 해놓고, 그때그때 원하는 값을 넣어서 쉽게 재사용할 수 있게 해주는 것이 Props 이다. SaveBtn 과 ConfirmBtn 을 지우고, Style 코드만 그대로 붙여넣어, 공용으로 사용할 Btn 을 만든다.

...
    function Btn() {
        return <button style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10
        }}>Save Changes</button>
    }

    const App = () => {
        return <div>
            <Btn />
            <Btn />
        </div>
    };
...

 지금은 Style 과 내부 값이 하나인 상태이기 때문에, 똑같은 모습의 버튼이 2개 생성되었다. 이제 Btn 컴포넌트를 사용할 때, 우리가 원하는 값을 명시하고, 그 값을 Btn 함수에서 받아들여서 내부 값으로 사용해준다면 그것이 Props 를 사용하는 것이다. Props 는 Properties 의 약어이다. 한마디로, Props 는 속성을 뜻한다. 즉, 컴포넌트의 속성을 개발자가 정의하고, 그 속성값을 전달 받는 방식이 Props 인 것이다.

 우선 코드를 아래와 같이 수정하고, props 인자에 어떤 값들이 어떻게 전달되는지 살펴보자.

...
    function Btn(props) {
        console.log(props);
        
        return <button style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10
        }}>Save Changes</button>
    }

    const App = () => {
        return <div>
            <Btn text="Save Changes" />
            <Btn text="Confirm" />
        </div>
    };
...

 props 인자는 Object 타입으로 전달이 되며, 그 안에 컴포넌트를 사용할 때 넣었던 속성명과 속성값이 전달되는 것을 볼 수 있다. 이제 Object 에서 text 값을 추출하여, <button> 의 내부값으로 적용 시켜준다.

...
    function Btn(props) {
        console.log(props);

        return <button style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10
        }}>{props.text}</button>
    }

    const App = () => {
        return <div>
            <Btn text="Save Changes" />
            <Btn text="Confirm" />
        </div>
    };
...

 <Btn /> 의 속성명은 원하는 이름으로 넣어도 된다. 예를 들어, 속성명을 "banana" 라고 하고, 해당 속성값을 넣어주면 다음과 같이 전달된다.

...
    const App = () => {
        return <div>
            <Btn text="Save Changes" banana="100" />
            <Btn text="Confirm" banana="120"/>
        </div>
    };
...

 그리고, <button> 의 내부 값 외에도 Style 에 필요한 값을 전달해줄 수도 있다.

...
    function Btn(props) {
        console.log(props);

        return <button style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
            fontSize: props.fontSize
        }}>{props.text}</button>
    }

    const App = () => {
        return <div>
            <Btn text="Save Changes" fontSize={16} />
            <Btn text="Confirm" fontSize={20} />
        </div>
    };
...

 여기에 더해서 props 에 값을 계속 추가하면 그만큼 "props.~~" 으로 시작하는 코드를 계속 추가해야할 것이다. 이런 불편함을 개선하기 위해서, props 를 받으면서 바로 속성값 이름을 그대로 받을 수 있다.

...
    function Btn({text, fontSize}) {
        return <button style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
            fontSize: fontSize
        }}>{text}</button>
    }

    const App = () => {
        return <div>
            <Btn text="Save Changes" fontSize={16} />
            <Btn text="Confirm" fontSize={20} />
        </div>
    };
...

 간단하게 props 를 사용 해보았다. Btn 컴포넌트로 전달되는 인자는 Object 타입의 props 가 유일하다. 따라서, App 에서 <Btn /> 을 사용할 때마다 추가하는 속성값은 모두 하나의 Object 로 압축되어 Btn() 으로 전달되는 것이다.

 Number, String 과 같이 값 외에도 함수를 전달할 수 있다. 함수를 전달한다는 것은 콜백(리스너) 함수를 만들 수 있다는 것이다. <button> 에서 onClick 속성이 있는 것처럼, 자체적으로 만든 Component 안에 리스너 속성을 추가할 수 있다.

...
    const App = () => {
        const [value, setValue] = React.useState("Save Changes");
        const changeValue = () => setValue("Revert Changes");

        return <div>
            <Btn text={value} onChangeValue={changeValue} />
            <Btn text="Confirm" />
        </div>
    };
...

 State 를 사용하여, 클릭했을 때 <Btn> 의 문구가 바뀌게 할 것이다. 간단하게 한 개의 <Btn> 에만 props 를 추가하고, 리스너 함수를 넣어줄 속성의 이름을 "onChangeValue" 로 지어준다. 버튼을 클릭하고, 그에 대한 리스너를 직접적으로 수행하는 곳은 <button> 이다. <Btn> 은 컴포넌트일 뿐, 위 코드와 같이 속성을 추가한다고 하여 클릭 리스너가 수행되지 않는다. <Btn> 을 클릭했을 때, changeValue 함수가 실행되도록 하려면, <Btn> 의 코드도 수정해야할 것이다.

...
    function Btn({ text, onChangeValue }) {
        return <button style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
            fontSize: 16
        }}
            onClick={onChangeValue}
        >{text}</button>
    }
...

 함수를 전달한다고 하여 다르게 생각할 필요가 없다. Number, String 값을 전달했던 것처럼 함수도 전달해주면 된다. 직전 코드에서 onChageValue 라는 이름으로 속성명을 명명했으므로, <Btn> 함수에서도 동일한 이름으로 props 를 전달 받을 수 있게 인자값을 추가한다. 그리고 <button> 의 onClick 속성에 연결시켜주면 끝이다.


2. Prop Types

 이제 Component 를 사용할 때, Number, String 뿐만 아니라 Function 도 파라미터처럼 전달할 수 있다는 것을 알게 됐다. 다양하게 인자값을 전달할 수 있다는 것이 장점인 것 같지만, 단점이 될 수도 있다. 특히, 위 코드에서만 봐도 전달하는 인자값의 타입이 어떤 것인지 명시되어 있지 않다. 지금처럼 단순히 2~3개까지만 사용할 것이거나, 주석을 통해 설명을 추가해주면 괜찮겠지만, 타입이 많아지고 Component 도 많아진다면 타입 실수로 인해 오류가 발생할 가능성이 크다. 따라서, Component 를 생성함과 동시에 전달 받을 수 있는 Prop 의 타입도 따로 명시가 되어있다면 좋을 것이다.

 ReactJS 에서 생성하는 Component 의 Prop Type 을 명시할 수 있게 해주는 것이 "Prop-Types" 라이브러리이다. 구 ReactJS 홈페이지에 들어가면 PropTypes 패키지 사이트 링크를 확인할 수 있다.

구 ReactJS 홈페이지 모습

 다만, 위 홈페이지의 내용에 따르면 PropTypes 는 최신 React 에서 사용하지 않고 있다고 명시한다. 그 이유는 "TypeScript" 에 이미 타입 체킹 기능이 있기 때문이다. 최근에 ReactJS 가 TypeScript 를 통해 사용되고 있기 때문에, Type 을 명시해야하는 TypeScript 의 특성상 PropTypes 를 따로 사용할 이유가 없어졌다. 하지만, 지금 강의는 JavaScript 를 통해서 ReactJS 를 사용하는 것이기 때문에, 아래에 노란 박스에 있는 prop-types 링크를 통해 들어가 unpkg 링크를 복사한다.

NPM 홈페이지 prop-types 일부

 unpkg 링크에서 현 버전인 15.8.1 로 적용하여 <script> 로 추가해준다.

<script src="https://unpkg.com/prop-types@15.8.1/prop-types.js"></script>

 이제 prop-types 가 제대로 작동하는지 보기 위해 Btn 컴포넌트에 text, fontSize 속성을 추가한 후, 값을 다르게 넣어본다

...
    const App = () => {
        return <div>
            <Btn text="Save Changes" fontSize={16} />
            <Btn text="Confirm" fontSize={"asd"}/>
        </div>
    };
...

 위 코드에서는 간단하게 String 을 받아야하는 text 와 Number 를 받아야하는 fontSize 를 prop 으로 추가했다. 이제 prop-types 를 사용하여 text 와 fontSize 의 Type 을 명시한다.

...
    Btn.propTypes = {
        text: PropTypes.string,
        fontSize: PropTypes.number
    }
...​

 Prop 의 타입을 정하고자 하는 Component 의 이름 뒤에 ".propTypes" 를 붙여서 선언을 한다. 그리고 해당 선언 안에 Prop 의 이름과 타입을 명시하면 된다. 타입을 명시할 때는 PropTypes 를 통해 명시한다. 이제 잘 동작하는지 브라우저를 통해 살펴본다.

 fontSize 에 number 가 아닌 string 을 넣은 것에 대한 에러가 출력된 것을 확인할 수 있다. string 과 number 외에도 array, boolean, function 등등 다양한 타입 명시가 가능하다. 자세한 사항은 PropType 공식 문서(NPM 공식 페이지 or ReactJS 페이지)를 통해 확인할 수 있다.

 다양한 타입 외에도 유용하게 사용할 수 있는 기능이 하나 있다. 그것은 Prop 을 필수 Prop 으로 명시할 수 있는 것이다. 이를 통해 Prop 을 실수로 빠뜨렸을 때, 오류가 출력될 수 있도록 설정할 수 있다. 우선, App 코드를 간단하게 수정한다.

...
    const App = () => {
        return <div>
            <Btn text="Save Changes" fontSize={16} />
            <Btn text="Confirm" />
        </div>
    };
...

 "Save Changes" 버튼은 fontSize Prop 을 포함하고 있고, "Confirm" 버튼은 가지고 있지 않다. 이 상태에서 PropTypes 를 통해 "text" 와 "fontSize" 를 required(필수) 로 설정하고 결과를 확인해본다.

...
    Btn.propTypes = {
        text: PropTypes.string.isRequired,
        fontSize: PropTypes.number.isRequired
    }
...

 "isRequired" 를 뒤에 붙여주면 된다. 이제 페이지를 리로딩하여 콘솔창을 살펴본다.

 필수라고 명시했던 "fontSize" 가 없는 것에 대한 오류가 출력된 것을 확인할 수 있다.

반응형