들어가기 전에
return() 안에는 병렬로 태그 2 개 이상 기입 금지.
하나의 태그로 시작해서 하나의 태그로 끝내야 한다.
리액트에서 자료를 잠깐 저장할 때에는 변수를 사용하고 html에 {데이터바인딩}도 할 수 있다.
하지만 중요한 데이터를 저장할 때에는 변수 대신 state를 사용한다.
State란?
- 변수 대신 쓰는 데이터 저장 공간.
- useState()를 이용해 만들어야 한다.
- 문자, 숫자, array, object, boolean 전부 저장 가능하다.
- 렌더링이나 데이터 흐름에 사용되는 값만 state에 포함시켜야 한다.
(state가 변경될 경우 component가 재렌더링 되기 때문에 렌더링,데이터 흐름에 사용되는 값을 포함하면 불필요한 렌더링이 발생해 성능을 저하시킬 수 있음)
State 만드는 법
1. 상단에 import {useState} 첨부
2. useState(데이터)
이렇게 하면 state에 데이터를 잠깐 저장할 수 있다.
나중에 저장한 데이터를 사용하고 싶다면,
3. let [작명, 작명]
아래와 같이 useState() 왼쪽에 자료를 뽑는 문법을 작성해 주면 된다.
a : state에 보관했던 데이터가 나옴.
b: state 변경을 도와주는 함수. 함수를 아직 사용할 일이 없다면 생략 가능하다.
a 자리에 state 이름을 자유롭게 작성해서 사용하면 된다.
(참고) 자바스크립트 destructuring 문법
다음과 같은 문법은 자바스크립트의 destructuring이다.
변수에 다음과 같은 배열을 저장했다.
그런데 저 두 변수를 따로 빼서 사용하고 싶다면 아래와 같이 사용할 수도 있다.
더 간단한 문법으로도 사용할 수 있는데,
이러면 a = 1, b = 2라는 변수가 생성된다.
등호 여러번 쓸 필요 없이 왼쪽과 오른쪽 형식을 똑같이 맞추면 자동으로 변수가 생성된다.
이것을 destructuring 문법이라고 한다.
결국 useState()를 쓰면 그 자리에 [데이터1, 데이터2] 이렇게 생긴 array가 남는다.
데이터1 자리에는 '8월 개봉 영화 추천' 같은 데이터가 들어있고,
데이터2 자리에는 state 변경을 도와주는 함수가 들어있다.
State를 사용하는 이유는?
- state는 변동 사항이 생기면 state를 사용하는 html도 자동으로 렌더링을 해 준다.
(변수는 변동 사항이 생겨도 자동 렌더링이 되지 않음)
즉, 자주 변경될 것 같은 데이터들을 state에 저장했다가 html에 {데이터바인딩} 한다.
- 웹이 앱처럼 부드럽게 동작하게 만들 수 있다.
- UI 기능 개발도 편리해진다.
useState() 함수 사용법
state는 state 변경 함수를 사용해서 변경해야 한다. (그렇지 않으면 html 재렌더링이 되지 않음)
state를 만들 때 작명 2개까지 할 수 있는데 두번째 작명(아래 그림에서 setPost/set함수명)이 state 변경함수이다.
사용법은 아래와 같이 쓰면 된다.
state변경함수(새로운 state 값)
state 변경함수는 ( ) 안에 넣은 걸로 state를 변경해 준다.
state 변경함수 이름은 보통 setState변수명 이렇게 작명하여 대부분 사용한다.
전체적인 사용법은 아래와 같다.
버튼을 누르면 좋아요 개수가 1씩 증가하는 기능을 만들어 보려고 한다.
버튼을 누를 때마다 좋아요 개수가 증가해야 하므로 아래와 같이 좋아요 개수를 state로 만든다.
useState()로 좋아요 개수를 초기값 '0'을 저장하고 작명을 해 줬다.
JSX에서 onClick 사용법
1. Click은 대문자로 시작해야 함.
2. { } 중괄호 사용함.
3. { } 중괄호 안에는 함수 이름을 넣어야 함.
함수 만드는 게 귀찮으면 이렇게 바로 만들어도 된다.
다시 돌아와서
이제 좋아요 버튼을 누르면 like라는 state를 +1 해 줘야 한다.
state는 state변경함수를 사용해서 state를 변경해야 한다.
이렇게 작성해 주면 된다.
state 변경함수() 는 () 안에 넣은 걸로 state를 변경해 주는 함수이기 때문이다.
(setLike(1)이라고 사용하면 like라는 state가 1로 변경,
setLike(10)이라고 사용하면 like라는 state가 10으로 변경됨)
array/object state를 변경해 보자.
post state에 글 제목들을 문자형 배열로 담고 state 변경 함수를 setPost로 지정해 주었다.
각 post[0]부터 post[2]까지 화면에 뿌려지도록 데이터 바운딩 하였다.
리스트 변경 버튼을 클릭하면 첫 번째 글의 제목이 변경되도록 하려고 한다.
위와 같이 코드를 작성해도 된다.
하지만 글이 3개가 아니라 100개라면? 그 이상이라면?
onClick 안이 코드가 매우 길어지고 확장성이 부족한 코드가 된다.
그렇다면 기존 state의 첫 글만 바꿔서 state변경 함수에 넣는 식으로 개발해 보자.
array, object 자료를 다룰 경우 원본 데이터를 직접 조작하도록 하는 것보다 기존 데이터는 보존하도록 코드를 작성하는 것이 좋은 관습이다. 그래서 let copy 변수에 기존 post를 담아 복수하였다.
이제 리스트 변경 버튼을 클릭하면 post state는 ['공포 영화 추천', '국내 여행지 추천','여름철 건강 관리 방법']이 될 것이다.
실행해 보면,
글이 바뀌지 않는 것을 확인할 수 있다.
.
.
.
왜 그런 걸까?
state 변경함수 동작 원리를 이해해야 한다.
state 변경함수는 state의 값이 변하면 그 새로운 값으로 렌더링 되도록 하는데, 그 전에 기존 state와 신규 state를 먼저 검사한다.
이렇게 기존 state === 신규 state를 해 보고 같으면 state 변경을 해 주지 않는다.
위 코드에서도 setPost(copy)를 해도 copy라는 변수가 기존 state와 같아서 변경해 주지 않은 것이다.
왜? copy[0] = '공포 영화 추천'을 선언했는데?
기존 state post[0]은 '8월 개봉 영화 추천', 신규 state copy[0]은 '공포 영화 추천이 들어있지만
post === copy 를 비교해 보면 같다고 나온다.
위와 같이 코드를 변경하고 실행해 보자.
if 조건문에 post와 copy가 같은 값이면 콘솔창에 true가 출력, 아니라면 false를 출력할 것이다.
콘솔창에는 true가 출력되었다.
왜일까?
array/object 동작 원리
자바스크립트는 array나 object의 경우 자료를 하나 만들면 램(Ram)이라는 가상 공간에 자료를 저장하게 된다.
그리고 변수를 생성할 경우 그 값을 할당하는 게 아니라 변수는 그 값이 램의 어디에 있는지 가리키는 화살표
역할을 하게 된다. (즉, 주소만 가리키는 것)
array나 object를 이렇게 복사해도 값을 새로운 램에 저장하는 게 아니라 결국엔 같은 화살표를 가르키고 있기 때문에 let copy = post는 post에 있던 자료를 copy에 복사한다는 뜻이다.
그런데 post와 copy는 자료를 각각 별개로 저장하는 것이 아니라 post와 copy는 똑같은 값을 공유한다.
진짜다.
post를 변경하면 copy도 자동으로 변경된다.
왜냐고?
변수는 화살표만 저장된다.
위 코드는 화살표만 복사한 것이다. 그래서 post, copy는 똑같은 화사료를 가지게 되고 같은 자료를 가리키게 된다.
이제 이해가 좀 된다.
그래서 같은 화살표를 가지고 있는 변수끼리는 등호로 비교해도 똑같다고 나오는 것이다.
위에서 post[0]='공포 영화 추천'으로 변경해 주어도 램 안에 있는 데이터 '값'만 변했을뿐 화살표 자체가 변하는 것은 아니라서 past와 copy를 등호로 비교했을 때 true 출력되는 것이다.
이를 참조값(reference data type)이라고 한다.
그렇다면 어떻게 해야 setPost() 함수가 state가 변경되었다고 인식하게 할 수 있을까?
Spread Operator라는 전개 구문 문법을 사용하면 된다.
post state에 ...을 붙인 것이 전개 구문 문법이다.
array, object 자료형 왼쪽에 붙일 수 있으며 뜻은 괄호를 벗겨 달라는 것이다.
그래서 ...가 뭐냐면
(1) 괄호 벗기기용 연산자
...[1,2,3] 이렇게 사용하면 그 자리에 1,2,3이 남는다.
(2) array나 object를 자료형을 복사할 때 많이 사용
(3)
data1에 전개 구문 문법(...data1)을 사용하면 data1을 감싸고 있던 배열의 괄호가 사라지기 때문에 (1,2,3이 남음) 1,2,3(...data1)을 다시 배열 괄호 []로 묶어 주고 data2 변수에 할당하면 복사가 완료된다.
아까 조건문을 돌려보면 콘솔창에 false가 출력되는 것을 확인할 수 있다.
이제 setPost() 함수를 동작 시켜 보자.
위와 같이 변경된 state를 setPost가 감지하여 재렌더링해 주는 것을 확인할 수 있다.
이렇게 리액트에서 array/object인 state를 수정하고 싶다면 독립적으로 카피본을 만들어서 수정해야 한다.
* 이 포스팅은 코딩애플 강의를 토대로 작성하였습니다.