React 와 State

JJ_Dean·2022년 12월 20일
0

React

목록 보기
3/14
post-thumbnail

React State 공부 소감

페이지 안에서 이벤트가 일어나고 데이터가 바뀐다. 그 즉시 바뀐 데이터를 반영하여 UI의 요소가 수정될 수 있는 방법, 또 Components 간의 데이터 전달이 어떻게 이루어 지는지 공부했다.

이전 바닐라 자바스크립트로 사이트의 데이터를 가져오고 UI에 적용하기 위해서는

데이터 수정 이벤트가 발생한다 ➡
서버에 데이터를 추가하거나 수정한다 ➡
필요한 데이터를 서버에서 가져온다 ➡
새로 고침 및 페이지를 이동 시키면서 가져온 데이터를 UI 요소에 적용한다 ➡
바뀐 페이지를 사용자에게 보여준다

의 프로세스를 거쳤다.

위 프로세스로 UI의 데이터가 바뀔 때마다 페이지가 새로 고침되거나 강제로 페이지를 이동 시키면 데이터의 이동 과정이 복잡해지고 특히나 주소 뒤에 데이터 이름을 추가해서 해당 페이지에 필요한 데이터를 가져오던 나로서는 여간 귀찮은 일이 아닐 수 없었다.

하지만 React에서는 State 라는 기능으로 이벤트가 일어나서 데이터가 바뀌게 되면 그 즉시 UI요소에 바뀐 데이터를 랜더링한다.
새로고침이나 페이지 이동 없이.


다음은 중요하다고 생각한 내용을 정리하였다.

깃허브(링크) 에서 전체 코드를 확인할 수 있습니다.

State 특징

이벤트 리스너, 핸들링

React가 작동하기 위해서는 React 앱의 컴포넌트 안에 있는 JSX 코드를 읽어야한다. 이때 JSX 코드 안에 있는 또 다른 컴포넌트 호출을 발견하면 해당 컴포넌트 함수를 실행하여 안에 있는 JSX 코드를 읽는다. 이렇게 모든 JSX 파일을 읽고 페이지 렌더링을 끝낸다.

이후 컴포넌트 안의 요소에 바닐라 자바스크립트 코드처럼 addEventListener를 사용해서 이벤트에 접근하려고 해도 되지 않는다. React의 특징 중 하나인 JSX 코드에서 이벤트 리스너를 만드는 방법은 일반적인 바닐라 자바스크립트의 방법과는 다르다.
다음은 React에서 드롭다운 메뉴의 옵션을 선택할 때마다 이벤트가 발생하는 모습이다. 이벤트가 발생하면 콘솔창에 해당 이벤트의 value가 출력된다.

DOM에서 일어나는 이벤트에 접근하기 위해서는 JSX 코드안에 이벤트가 일어나는 태그 안에 on+'something'처럼 on으로 시작하는 props를 추가해야 한다. 위 코드에서는 onChange 라는 prop을 사용하여 값이 변하는 이벤트에 접근할 수 있도록 했다. 그리고 이벤트가 일어나면 filterChangeHandler 라는 함수를 실행하도록 {filterChangeHandler}로 정의했다. 이때 함수명 뒤에 ()가 붙지 않는 이유는 ()가 붙으면 이벤트가 발생하기 전에 React가 JSX 코드를 읽으면서 해당 함수를 실행시켜 버리기 때문이다.

State

State는 번역하면 상태라는 뜻이다. React는 이 State 기능을 통해 데이터의 상태를 관리하고 데이터의 값이 바뀌면 실시간으로 UI에 그 데이터 값을 반영할 수 있게 한다.
다음은 State를 적용하여 input 태그에 이벤트 리스너를 추가하여 값이 변할 때마다 데이터를 수정하는 새롭게 수정하는 모습이다.


State 기능은 첫 번째 줄처럼 useState를 'react'로 부터 import 받으면 사용할 수 있다. 현재는 명시적 import로 받아왔다.

const [enteredTitle, setEnteredTitle] = useState('');

useState()로 시작하여 괄호 안에는 초기값을 지정한다.
이 구문을 자세히 보면 enteredTitle 과 setEnteredTitle을 useState에서 배열의 형태로 뽑아내어 사용하고 있다.

enteredTitle

배열의 첫 번째 값 즉 enteredTitle은 State가 관리하고 있던 기존의 데이터이다. 이벤트가 발생하기 전에는 useState('')로 초기화 했기 때문에 특별한 값 없이 ''이다.

setEnteredTitle

이처럼 set으로 시작하는 배열의 두번째 값은 함수이다. 이벤트 발생 후 함수가 호출되면 다음과 같은 프로세스를 거친다.

이벤트가 발생되어 titleChangeHandler 함수가 호출된다. ➡
titleChangeHandler 함수 내부에서 setEnteredTitle('안녕하세요')를 호출한다. ➡
이때 React는 set 함수를 바로 실행하지 않고 Handler 함수가 끝나고 실행할 수 있게 예약을 걸어둔다. ➡
Handler 함수의 실행이 끝난 후 enteredTitle의 값을 기존 값에서 '안녕하세요' 로 바꾼다. ➡
컴포넌트 함수를 다시 실행한다.

이와 같은 프로세스로 State를 이용하면 기존 '' 값이였던 enteredTitle이 '안녕하세요'라는 값으로 바뀌고 컴포넌트 함수를 재실행하기 때문에 새로운 JSX 코드가 리턴되어 UI의 요소가 실시간으로 바뀔 수 있다.

const로 정의해도 되는 이유

분명 setEnteredTitle()이 호출되면 enteredTitle이 바뀌는데 const 로 정의해도 상관없는 이유는 무엇일까. 등호 연산자(=)로 값을 재할당하지 않기 때문이다.

여러개의 데이터 State로 관리

다음은 여러개의 데이터를 하나의 컴포넌트에서 State로 관리하는 방법들이다.

위 코드는 3개의 데이터를 각각의 State로 만들어서 따로 관리하는 코드이다.

위 코드는 3개의 데이터를 객체의 형태로 useState()의 괄호안에 넣어서 기본 값으로 정의하였다. 즉 3개의 데이터를 한 개의 State로 만든 것이다.
새로운 값을 받아서 처리하기위한 setUsetInput 함수에서도 이전 값을 '...userInput'으로 모두 불러온 다음 변경하고 싶은 값을 덮어쓰는 방법으로 작성되어 있다.

이 방법은 특정 사례에서는 실패할 수 있다.

이런 방법으로 여러개의 데이터를 한꺼번에 불러와서 부분적인 데이터만 덮어쓰게 되면 오류가 날 수 있다. setUesrInput 함수는 즉시 실행이 아닌 예약이다. 이 데이터 변경 예약이 한꺼번에 3개가 중첩된다면 잘못된 기존 값으로 업데이트가 될 수 있다.

예를들어 3개의 데이터 중 하나가 좋아요 수를 표시하는 데이터라서 클릭 이벤트가 일어났을 때 기존 좋아요 수에 +1 을 해주어야 한다. 하지만 이 좋아요+1 업데이트 예약이 다른 데이터들이 업데이트 될 예약과 함께 예약되면 +1이 된 새로운 값이 반영되지 않는 오류로 이어질 수 있다. 즉 이전 State에 의존하는 State를 업데이트를 하기에는 옳바르지 않은 방법이다.

따라서 setState는 다음과 같이 사용해야 한다.

밑의 코드는 setUserInput 함수의 인수로 익명의 화살표 함수를 사용한다. 이때 화살표 함수의 인수로 받은 'prevState'는 항상 최신 상태의 State를 가져온다. 위의 코드 보다 더 안전한 방법이다.

양방향 바인딩 (two-way binding)

예를들어 정보 입력 form 내부 input을 모두 작성한 후 submit 버튼을 눌러 해당 데이터를 전송했다고 가정하자. submit 버튼을 누른 후 input 입력칸에 있던 값은 모두 전송 되었기 때문에 더 이상 필요하지 않다.
다음은 양방향 바인딩의 예시이다.

input 태그의 value 속성을 이용해 default 값으로 'enteredTitle' 즉 State에서 관리하는 값을 지정했다.
UI에 출력된 input 창에 값을 입력하고 submit 버튼을 누르면,
submit 버튼을 눌렀을 때 실행되는 이벤트 핸들러 함수(submitHandler)에서 'enteredTitle'의 초기값을 다시 ''로 만든다.
setEnteredTitle이 호출되었기 때문에 JSX 코드는 다시 읽어지게 되고 input의 value는 ''로 되어 사용자에게 보여지는 input 칸은 빈칸으로 돌아간다.

이처럼 State를 업데이트하기 위해 input에서 변경사항을 수신하고,
반대로 input에 State를 다시 보내주기도 한다.
즉 input으로 State를 변경하면 input도 변한다.
이를 양방향 바인딩이라고 한다.

State 끌어올리기

Props를 이용하면 데이터가 부모 컴포넌트에서 자식 컴포넌트로 이동할 수 있다.
반대로 자식 컴포넌트에서 생성된 데이터가 부모 컴포넌트로 이동할 수 있는 방법을 State 끌어올리기 라고 한다.
다음은 부모, 자식 컴포넌트의 예시이다.


자식 컴포넌트에서는 데이터를 객체 형태로 정의하여 만들어둔다.
이 데이터를 부모 컴포넌트로 이동시키기 위해서는 자식 컴포넌트에서 썼던 이벤트 리스너 onChange prop의 원리를 이용한다.

React는 JSX 코드를 읽어내려 가다가 onChange를 읽으면 onChange를 가진 렌더링 된 요소에 이벤트 리스너를 추가한다. 이처럼 부모 컴포넌트로부터 커스텀 이벤트 prop을 생성하면 자식 컴포넌트로 함수를 전달할 수 있다.

과정을 조금 더 자세히 설명하면

부모 컴포넌트

부모 컴포넌트에서는 자식 컴포넌트의 속성을 추가하는 props처럼 'onSaveExpenseData'라는 커스텀 prop을 만들고 그 값으로 부모 컴포넌트에 정의되어있는 'saveExpenseDataHandler' 함수를 갖는다.

자식 컴포넌트

자식 컴포넌트에서는 'props.onSaveExpenseData'로 부모의 saveExpenseDataHandler' 함수에 접근할 수 있다. 이때 인수로 자식 컴포넌트에서 만든 데이터를 가져간다.

가능한 이유

부모 컴포넌트에서는 'saveExpenseDataHandler' 함수의 포인터를 'onSaveExpenseData'에 값으로 전달한다.
따라서 자식 컴포넌트에서 'props.onSaveExpenseData'를 호출하더라도 실제 함수를 복사해서 가져온 것이 아닌 포인터를 가져온 것이기 때문에 실행은 부모 컴포넌트에서 이루어진다.

profile
공부하고 내 것으로 만들자.

0개의 댓글