노마드코더

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

졸려질려 2023. 3. 21. 21:24
반응형

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

 

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

왕초보를 위한 React

nomadcoders.co

 직전 강의 정리는 다음 링크로 들어가시면 됩니다.

 

[ReactJS로 영화 웹 서비스 만들기] 1. The basic of REACT

본 글은 노마드코더의 "ReactJ로 영화 웹 서비스 만들기" 강의를 복습 정리한 글이다. 영상 강의에서 더 자세하고 쉬운 설명을 들을 수 있으니, 본 강의에 흥미가 생긴다면 영상 강의를 수강하자.

choboit.tistory.com


1. State?

 State 는 기본적으로 데이터가 저장되는 곳을 뜻한다. 이전의 ReactJS 기초 강의에서, 맨처음에 JavaScript 와 ReactJS 만 썼을 때는 클릭 리스너를 통해 리렌더링하여 클릭 카운트가 올라가는 것을 볼 수 있었다. 그 후 JSX 에 대한 설명을 정리하면서 UI 관련된 부분만 다뤘다. 이번에는 JSX 를 활용한 React 코드에서 클릭 카운트가 올라가는 화면을 구현해본다.

 카운트 횟수를 저장할 변수가 하나 필요하고, 클릭 했을 때 카운트 횟수가 올라가는 함수, 그리고 클릭 리스너와 카운트 함수를 붙여주도록 한다.

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

let counter = 0;

function countUp() {
    counter = counter + 1;
}

const Container = () => {
    return <div>
        <h3>Total Clicks: {counter}</h3>
        <button onClick={countUp}>Click me</button>
    </div>
};
root.render(<Container />);

 이전의 정리 글에서는 Span 과 Button 컴포넌트를 만들어서 사용했다. 이번에는 카운트 횟수가 클릭 때마다 바뀌는 것이 주 목적이므로, 컴포넌트 코드를 최대한 줄였다. 본론으로 돌아와서, 위 코드를 보면 카운트 횟수를 저장하는 "counter" 변수, 카운트 횟수를 증가시켜주는 "countUp" 함수, 그리고 button 의 onClick 이 추가된 것을 볼 수 있다. 위 상태로 브라우저에서 열어보면 다음과 같다.

 클릭을 했을 때 화면에 변화는 없지만, 콘솔에서 counter 변수의 값을 쳐보면 클릭 횟수만큼 값이 출력되는 것을 볼 수 있다. 즉, 클릭 리스너를 통해 counter 값은 정상적으로 변하지만, 렌더링에 반영이 되지 않는 것이다. 이전 글에서도 언급했듯이, React Element 는 모두 Immutable 성질을 가진다. 즉, 한 번 렌더링 되면 그 안에 값을 수정할 수 없다. 따라서, 값이 변할 때 렌더링을 다시 해줘야한다.

 한 번만 호출했던 "root.render(<Container />)" 코드를 함수로 바꿔서, 본 코드의 끝과 "countUp()" 함수 안에 각각 넣어준다. 최초에 한번 렌더링을 하고, 값이 변할 때 다시 렌더링을 해주도록 한다.

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

let counter = 0;

function countUp() {
    counter = counter + 1;
    render();
}

function render() {
    root.render(<Container />);
}

const Container = () => {
    return <div>
        <h3>Total Clicks: {counter}</h3>
        <button onClick={countUp}>Click me</button>
    </div>
};

render();

 위와 같이 수정하여 다시 브라우저에서 열어보면, 값이 정상적으로 변하는 것을 확인할 수 있다. 

결과 GIF

 위에 첨부한 결과 GIF 를 보면 흥미로운 점을 발견할 수 있다. 위에 구현한 코드에서는 버튼을 클릭 할 때마다 <Container /> 전체를 다시 렌더링 하는 것처럼 되어있다. 하지만, 실제로 Inspect 를 해보면, 전체보다는 {counter} 변수 부분만 변하고 있는 것을 볼 수 있다. 이 점이 ReactJS 의 숨겨진 매력이기도 하다.

 지금으로도 충분히 좋아보이지만, 만약 한 화면에서 변경되는 데이터가 많다면, 각 데이터가 변할 때마다 render() 를 호출해야 할 것이다. 이런 불편한 점을 State 를 사용함으로써 개선할 수 있다. State 를 도입하기에 앞서, 코드를 최대한으로 다시 줄여준다.

<html lang="en">
    <body>
        <div id="root"></div>
    </body>
    <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const rootDiv = document.getElementById("root");
        const root = ReactDOM.createRoot(rootDiv);

        const Container = () => {
            return <div>
                <h3>Total Clicks: 0</h3>
                <button>Click me</button>
            </div>
        };

        root.render(<Container />);
    </script>
</html>

 이제 위 코드에서 Rect.useState(); 로 임의의 State 변수를 하나 추가해본다. 그리고 해당 변수를 콘솔창에 로그로 띄운다.

...
const Container = () => {
    const data = React.useState();
    console.log(data);

    return <div>
        <h3>Total Clicks: 0</h3>
        <button>Click me</button>
    </div>
};
...

 그러면 위와 같이 크기가 2개인 배열의 형태로 로그에 출력되는 것을 볼 수 있다. React.useState() 는 우리가 구현했었던 counter 와 countUp 함수의 집약체라 보면 된다. 생성된 State 의 첫번째 파라미터는 초기값(counter)이고, 두번째 파라미터는 값을 변경하는 함수(countUp) 이라 볼 수 있다. React.useState(0) 와 같이 초기값을 undefined 가 아닌 값으로 설정할 수 있다.

useState 코드와 결과 콘솔창

 그렇다면, 코드 안에서 초기값을 사용하려면 어떻게 접근하면 될까? 콘솔창에 출력된 것으로 보아, State 는 크기가 2인 배열의 형태이다. 즉, 배열처럼 받아서 쓸 수 있다. useState() 의 첫번째 값뿐만 아니라, 값을 변경하는 함수를 담는 두번째 파라미터도 사용할 수 있다.

...
const Container = () => {
    const data = React.useState(0);
    console.log(data);
    return <div>
        <h3>Total Clicks: {data[0]}</h3>
        <button onClick={data[1]}>Click me</button>
    </div>
};        
...

 다만 위 코드에서 버튼을 클릭하면, 에러가 발생할 것이다. 배열의 형태로 접근할 수 있다~ 정도로 확인하면 되는 코드이다. 배열의 인덱스를 사용하여 접근하는 방법도 있지만, 배열에서 한번에 원하는 변수값으로 각각 뽑아올 수 있는 방법도 존재한다.

...
const Container = () => {
    const [counter, modifier] = React.useState(0);
    console.log(counter);
    return <div>
        <h3>Total Clicks: {counter}</h3>
        <button onClick={modifier}>Click me</button>
    </div>
};
....

 "const [counter, modifier] = React.useState()" 코드 한 줄로, counter 와 modifier 를 각각 초기화할 코드 2줄을 1줄로 줄일 수 있다. 이제 modifier 부분에 대해 알아보자.

 modifier 는 counter 값을 변경하는 함수이다. "modifier(1)" 이라 호출하면, counter 의 값은 0에서 1로 변한다. 그리고 화면을 리렌더링 해준다. State 의 장점이 여기서 보이게 된다. 값을 변경하는 modifier 함수를 호출하면, 화면을 알아서 다시 렌더링 해준다. 버튼을 클릭했을 때, 숫자가 변하도록 하는 코드는 다음과 같이 구성할 수 있다.

...
const Container = () => {
    const [counter, modifier] = React.useState(0);
    const onClick = () => {
        modifier(counter + 1);
    }
    return <div>
        <h3>Total Clicks: {counter}</h3>
        <button onClick={onClick}>Click me</button>
    </div>
};
...

 혹여나, <button> 의 onClick 에 modifier() 를 바로 넣는다면, 엄청난 빨간줄들을 볼 수 있다. 다른 함수로 한 번 랩핑하여, <button> 에 연동해준다. 결과 화면은 따로 첨부하지 않는다.

 이전에 counter 와 countUp 을 사용한 코드는 다음과 같았다.

...
let counter = 0;

function countUp() {
    counter = counter + 1;
    render();
}

function render() {
    root.render(<Container />);
}

const Container = () => {
    return <div>
        <h3>Total Clicks: {counter}</h3>
        <button onClick={countUp}>Click me</button>
    </div>
};

render();
...

 하지만, 위 코드에서 State 를 적용한 결과, 다음과 같이 코드가 깔끔해진 것을 볼 수 있다.

...
const Container = () => {
    const [counter, modifier] = React.useState(0);
    const onClick = () => {
        modifier(counter + 1);
    }
    return <div>
        <h3>Total Clicks: {counter}</h3>
        <button onClick={onClick}>Click me</button>
    </div>
};

root.render(<Container />);
...

 추가적으로, modifier 라는 이름보다는 "set[VALUE_NAME]" 의 형식으로 네이밍을 한다. 예를 들어, 위에서 State 에서 사용하는 값의 이름은 counter 라고 명명한다면, modifier 의 함수명은 "setCounter" 로 네이밍을 해주는 것이다.

const [counter, setCounter] = React.useState(0);
const onClick = () => {
    setCounter(counter + 1);
}

 그리고, "setCounter(counter + 1)" 을 "setCounter((current) => current + 1)" 로 바꿀 수 있다. counter 변수가 외부에서 변하더라도 State 내부의 값은 유지할 수 있으며, 더 안전하게 setCounter() 함수를 실행할 수 있을 것이다.

...
    const Container = () => {
        const [counter, setCounter] = React.useState(0);
        const onClick = () => {
            setCounter((current) => current + 1);
        }
        return <div>
            <h3>Total Clicks: {counter}</h3>
            <button onClick={onClick}>Click me</button>
        </div>
    };
...

2. DOM Element

 위에 State 는 값을 초기화하고, 변경하여 출력하는 방법까지 정리하였다. 이번에는 사용자가 값을 입력했을 때, 그 값을 State 로 저장하여 어떻게 다룰 수 있는지에 대해 정리한다. 이번에는 Total Click 이 아닌 단위 변환기 기능으로 코딩을 해본다. 이전까지 만든 코드에서 Container 부분만 남기고 정리한다.

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

const Container = () => {
    return <div>
        <h3>Super Converter</h3>
    </div>
};

root.render(<Container />);

 그리고, Container 안에 <input /> 을 2개 생성하여 넣어준다. 처음으로 만들어볼 변환기는 분(Minute) 에서 시(Hour) 로 변환하는 기능이다. <label /> 까지 붙여서 HTML 처럼 구현을 해본다. 코드는 다음과 같다.

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

const Container = () => {
    return <div>
        <h1>Super Converter</h1>
        <label for="minutes">Minutes</label>
        <input id="minutes" placeholder="Minutes" type="number" />
        <label for="hours">Hours</label>
        <input id="hours" placeholder="Hours" type="number" />
    </div>
};

root.render(<Container />);

 위와 같이 구현하면, "Minutes" <label> 부분을 클릭하면 "Minutes" 의 <input /> 이 포커스 되고, "Hours" <label> 부분을 클릭하면 "Hours" 의 <input /> 이 포커스 된다. 그런데, 위 코드는 React 의 Development 버전을 쓰고 있기 때문에 콘솔창에서 에러가 출력된다. 기능은 정상적으로 동작하겠지만, 추후에 문제가 발생할 수 있으므로 에러의 원인을 고쳐야 한다.

react-dom.development.js:73
Warning: Invalid DOM property `for`. Did you mean `htmlFor`?
    at label
    at div
    at Container

 위 에러문의 내용은 간단하다. <label> 에 속성명으로 사용했던 "for" 가 Invalid 하다는 것이다. 왜냐하면, 지금 작성한 코드는 React 이므로 ReactJS 가 기본적으로 돌아가고 있다. 그런데, "for" 는 ReactJS 에서 반복문을 의미하는 예약어(keyword) 이기 때문에, 이를 HTML element 의 속성명으로 사용하여 에러가 나타난 것이다. "for" 외에도 HTML element 의 속성 이름으로 많이 사용하는 "class" 도 위와 같이 사용하게 되면 에러로 나타난다. 따라서, React 에서 JSX 를 사용하여 HTML 코드를 작성할 때, "for" 나 "class" 를 다른 이름으로 사용해야한다.

 에러문에서도 나왔듯이, "for" 는 "htmlFor" 로 사용 해야한다. 그리고, "class" 는 "className" 으로 사용해야한다.

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

const Container = () => {
    return <div>
        <h1>Super Converter</h1>
        <label className="class" htmlFor="minutes">Minutes</label>
        <input id="minutes" placeholder="Minutes" type="number" />
        <label className="class" htmlFor="hours">Hours</label>
        <input id="hours" placeholder="Hours" type="number" />
    </div>
};

root.render(<Container />);

 위 코드로 수정하면, 에러문이 나타나지 않는다. 그리고, Inspect 해보니 웹페이지의 코드는 HTML 에서 사용하던 모습으로 변환되어 있는 것을 볼 수 있다. 이 외에도 "for" 과 "class" 같이 React 에서 다르게 써줘야하는 이름들은 다음 링크에서 자세히 알 수 있다.

 

DOM 엘리먼트 – React

A JavaScript library for building user interfaces

ko.reactjs.org


3. Inputs and State

 다시 본론으로 돌아와서, Minutes 와 Hours 에 숫자를 입력하여 State 로 저장을 해볼 것이다. <input> 에는 내부에 값이 입력될 때마다 Callback 해주는 onChange 속성이 있다. 그리고 입력된 값을 저장하고 있는 value 속성도 있다. 이 2개의 속성을 State 와 연동하여, 입력될 때마다 State 의 값이 변하도록 할 수 있다.

 우선, useState() 로 State 를 선언한다.

const Container = () => {
    const [minutes, setMinutes] = React.useState();

    return <div>
        <h1>Super Converter</h1>
        <label className="class" htmlFor="minutes">Minutes</label>
        <input id="minutes" placeholder="Minutes" type="number" />
        <label className="class" htmlFor="hours">Hours</label>
        <input id="hours" placeholder="Hours" type="number" />
    </div>
};

 minutes 가 State 의 값이고, setMinutes 는 minutes 값을 변경하는 함수이자 변경될 때 렌더링을 다시 해주는 함수이다. minutes 의 값은 페이지 위에 보여줄 때 사용하면 될 것이고, setMinutes 는 <input> 에서 받은 값을 전달 받아서 minutes 를 변경해주도록 한다.

const Container = () => {
    const [minutes, setMinutes] = React.useState();
    const onChange = (event) => {
        console.log(event);
    };

    return <div>
        <h1>Super Converter</h1>
        <label className="class" htmlFor="minutes">Minutes</label>
        <input value={minutes} onChange={onChange} id="minutes" placeholder="Minutes" type="number" />
        <label className="class" htmlFor="hours">Hours</label>
        <input id="hours" placeholder="Hours" type="number" />
    </div>
};

  위에서 언급했듯이, <input> 에는 입력이 들어올 때마다 콜백해주는 onChange 속성을 가지고 있다. onChange 속성에 Listener 함수를 따로 선언하여 연결해준다. 그리고 setMinutes() 호출해주면, 입력 받아서 바로 minutes 의 값이 변경되는 로직이 완성된다. 하지만 그 전에, <input> 에서 입력 받은 값을 어떻게 추출 해내야하는지 알아야한다. 그래서 위 코드와 같이, onChage 속성으로 호출된 onChange 로컬 함수로 들어오는 event 의 값이 어떻게 구성되어 있는지 콘솔을 통해 확인해본다.

 <input> 에 입력을 했더니, "SyntheticBaseEvent" 라는 객체가 들어오는 것을 볼 수 있다. 해당 객체에서 "target" 의 "value" 값을 보면 입력 받은 값을 확인할 수 있다.

 SyntheticBaseEvent 객체 안에 입력된 값이 어디에 있는지 알아냈다. 이제 해당 값을 참조하여 Console 에 띄워보도록 한다.

const Container = () => {
    const [minutes, setMinutes] = React.useState();
    const onChange = (event) => {
        console.log(event.target.value);
    };

    return <div>
        <h1>Super Converter</h1>
        <label className="class" htmlFor="minutes">Minutes</label>
        <input value={minutes} onChange={onChange} id="minutes" placeholder="Minutes" type="number" />
        <label className="class" htmlFor="hours">Hours</label>
        <input id="hours" placeholder="Hours" type="number" />
    </div>
};

 "event.target.value" 로 위에서 본 "value" 의 값을 얻어낼 수 있다.

  하지만, 아직 State 의 값은 적용되지 않았다. <input> 의 value 속성으로 minutes 를 두긴 했으나, 이는 <input> 의 입력칸에 minutes 의 값을 두기 위함이다. 이제 로컬에서 선언한 onChange 함수 안에 setMinutes() 함수를 호출하여 minutes 의 값이 입력 받은 값으로 업데이트 되도록 한다. 그리고 임의로 minutes 를 출력할 공간을 만들어서 확인한다.

const Container = () => {
    const [minutes, setMinutes] = React.useState();
    const onChange = (event) => {
        setMinutes(event.target.value);
    };

    return <div>
        <h1>Super Converter</h1>
        <label className="class" htmlFor="minutes">Minutes</label>
        <input value={minutes} onChange={onChange} id="minutes" placeholder="Minutes" type="number" />

        <h3>Your Minutes is {minutes}</h3>

        <label className="class" htmlFor="hours">Hours</label>
        <input id="hours" placeholder="Hours" type="number" />
    </div>
};

 위와 같이 입력 받은 Minutes 값이 그대로 아래의 <h3> 에 출력되는 것을 볼 수 있다. 기능은 정상적으로 동작하나, 콘솔창을 보면 에러가 하나 출력되어 있을 것이다.

A component is changing an uncontrolled input to be controlled.
This is likely caused by the value changing from undefined to a defined value, which should not happen.
Decide between using a controlled or uncontrolled input element for the lifetime of the component.
More info: https://reactjs.org/link/controlled-components
    at input
    at div
    at Container

 에러문에서 눈에 띄는 문장은 "This is likely caused by the value changing from undefined to a defined value," 이 부분일 것이다. 처음 입력 했을 때, undefined 에서 defined value 로 변경된 것이 에러의 원인이라는 것이다. 다시 위에 코드를 보면 한 가지 빼놓은 것이 보인다. 바로, "React.useState()" 에서 useState() 함수의 파라미터를 넣어주지 않은 것이다. 해당 파라미터는 State 의 Default Value 를 정해주는 것이다. 즉, 초기 값을 정해주지 않았으니 State 의 기본 값이 undefined 로 되어 있는데, 입력을 함으로써 State 의 값이 number 로 변하면서 에러가 발생한 것이다. 따라서, "React.useState(0)" 로 수정을 해주면 에러는 사라진다.

...
    const [minutes, setMinutes] = React.useState(0);
...

 초기 값을 설정해주는 것은 값과 함께 State 의 타입도 결정해주는 것이기 때문에, 이제는 State 의 Default Value 를 설정 해주는 것이 좋다. 이어서 Minutes-Hours 변환기를 마저 완성 시킨다.


4. Minutes-Hours Converter Complete

 이제 Minutes-Hours Converter 에 간단한 추가 기능들을 넣어서 완성 해보도록 한다.

4-1) Minutes to Hours

 먼저, 간단하게 입력한 Minutes 값을 Hours 에 변환하여 넣어주도록 수정한다.

<h1>Super Converter</h1>
<label htmlFor="minutes">Minutes</label>
<input value={minutes} onChange={onChange} id="minutes" placeholder="Minutes" type="number" />

<h3>Your Minutes is {minutes}</h3>

<label htmlFor="hours">Hours</label>
<input value={minutes / 60} id="hours" placeholder="Hours" type="number" />

 위 코드를 보면 간단하게 수정한 것을 확인할 수 있다. Hours 의 <input> 에 value 속성 값을 {minutes / 60} 으로 넣어준 것이다.

 소수점이 안보이게 하고 싶다면, Math.round() 를 사용하면 된다.

...
    <input value={Math.round(minutes / 60)} id="hours" placeholder="Hours" type="number" />
...

4-2) Reset Button

 이번에는 입력한 Minutes 와 변환된 Hours, 두 <input> 의 value 값을 초기 상태로 돌리는 Reset 버튼을 추가한다.

...
    const onReset = () => setMinutes(0);
...
    return <div>
        <h1>Super Converter</h1>
        <div>
            <label htmlFor="minutes">Minutes</label>
            <input value={minutes} onChange={onChange} id="minutes" placeholder="Minutes" type="number" />

            <h3>Your Minutes is {minutes}</h3>

            <label htmlFor="hours">Hours</label>
            <input value={Math.round(minutes / 60)} id="hours" placeholder="Hours" type="number" />
        </div>

        <button onClick={onReset}>Reset</button>
    </div>
...

  위 코드와 같이 추가해주면, Reset 버튼을 눌렀을 때 Minutes 와 Hours <input> 이 모두 0으로 초기화 되는 것을 볼 수 있다.


4-3) Flip : Minutes2Hours & Hours2Minutes

 Flip 기능은 Minutes 에서 Hours 로 변환 되던 것을 Hours 에서 Minutes 로 변환 되도록 반전 시키는 기능이다. 여기서 중요한 것은 Minutes 에서 Hours 로 변환하는 모드일 때는 Hours 의 입력이 불가능(Disabled)하게 만들어야한다. 반대도 마찬가지이다.

 flipped 라는 State 를 새로 만들고, 해당 flipped 이 true 이면 Minutes -> Hours 모드, false 이면 Hours -> Minutes 로 되도록 구현한다. 코드는 다음과 같다.

...
    const [flipped, setFlipped] = React.useState(false);

    const onFlipped = () => setFlipped(current => !current);

    return <div>
        <h1>Super Converter</h1>
        <div>
            <label htmlFor="minutes">Minutes</label>
            <input disabled={!flipped} value={minutes} onChange={onChange} id="minutes" placeholder="Minutes" type="number" />

            <h3>Your Minutes is {minutes}</h3>

            <label htmlFor="hours">Hours</label>
            <input disabled={flipped} value={Math.round(minutes / 60)} id="hours" placeholder="Hours" type="number" />
        </div>

        <button onClick={onReset}>Reset</button>
        <button onClick={onFlipped}>Flip</button>
    </div>
...

 두 <input> 의 속성인 disabled 를 flipped 에 따라 true 및 false 가 되도록 추가한다. 그러면 "Flip" 버튼을 클릭할 때마다, Hours 나 Minute 의 <input> 이 모드에 맞게 Disabled 되는 것을 볼 수 있다. 이제 계산식도 Flip 모드에 따라 다르게 해줘야한다.

 flipped 가 true 이면, Minutes 에서만 입력을 받을 수 있고, 입력 받은 Minutes 의 값을 Hours 의 <input> 에 60으로 나누어 출력한다. 반대로, flipped 가 false 이면, Minutes 의 <input> 을 disabled 하고, Hours 에서만 입력을 받으며, Minutes 의 <input> 에 60을 곱한 값을 출력한다. 그렇다면 Hours 에서 입력 받는 값을 저장할 State 를 새로 만들어야하나 싶다. 하지만, minutes 를 amount 로 바꾸어 Minutes 와 Hours 에서 모두 공용으로 사용할 수 있게 변수명을 바꾸어준다. 지금 정리한 내용을 코드로 바꾸면 다음과 같다.

...
    const Container = () => {
        const [amount, setAmount] = React.useState(0);
        const [flipped, setFlipped] = React.useState(false);

        const onChange = (event) => {
            setAmount(event.target.value);
        };
        const onReset = () => setAmount(0);
        const onFlipped = () => { 
            onReset(); 
            setFlipped(current => !current); 
        };

        return <div>
            <h1>Super Converter</h1>
            <div>
                <label htmlFor="minutes">Minutes</label>
                <input disabled={flipped} value={flipped ? (amount * 60) : amount} onChange={onChange} id="minutes" placeholder="Minutes" type="number" />

                <label htmlFor="hours">Hours</label>
                <input disabled={!flipped} value={flipped ? amount : Math.round(amount / 60)} onChange={onChange} id="hours" placeholder="Hours" type="number" />
            </div>

            <button onClick={onReset}>Reset</button>
            <button onClick={onFlipped}>Flip</button>
        </div>
    };
...

 먼저, <input> 의 value 에서 삼항연산자를 사용하여 flipped 의 값에 따라 출력할 amount 값을 다르게 설정해주었다. 그리고 Flip 될 때, 각 <input> 의 값을 초기화 해주는 것을 추가하여 완성했다. 이제 실행해보면 다음과 같이 동작한다.

반응형