[ 원숭이의 React ] 버튼에 기능개발을 해보자 & 리액트 state변경하는 법
글제목 옆에 따봉 갯수와 따봉 버튼을 만들어봅시다
따봉을 누르면 따봉 갯수가 1씩 증가하는 기능을 만들어볼겁니다.
하지만 기능은 좀 나중에 생각하고 일단 좋아요 버튼과 좋아요 갯수를 HTML로 표현해봅시다.
여러분의 App() 이라는 함수 안에 글제목 있죠?
이걸 하단과 같이 수정합니다.
<h3> { 글제목[0] } <span>?</span> 0 </h3>
그럼 멋진 따봉 UI가 완성됩니다.
미리보기 띄워보면 보이시죠?
여기서 이제 따봉 갯수에 좀 주목을 해보도록 합시다.
지금은 0이라고 하드코딩 해놨는데, 이러면 안될 것 같습니다.
이걸 state 이런걸로 만들어놓아야 갯수가 변경이 될 때 스무스하게 HTML도 재렌더링이 일어나겠죠?
그러니 어서 빨리 따봉갯수 데이터를 state로 만든 후 HTML에 꽂아넣어보세요.
(따봉갯수 데이터의 초기값은 0으로 설정합시다)
이번엔 버튼의 기능을 만들어봅시다
버튼을 누르면 따봉이라는 state를 1 증가시키면 기능완성아니겠습니까.
빨리 만들어봅시다.
그럼 새로운걸 하나 배워보도록 합시다.
특정 HTML 요소를 클릭했을 때 자바스크립트를 실행하고 싶으면 어떻게 코드짜죠?
<div onclick="실행할 자바스크립트">
이렇게 짭니다.
근데 리액트 JSX에서는 약간 문법이 살짝 다릅니다.
<div onClick={실행할 함수}>
이렇게 짭니다.
1. Click이 대문자인거
2. {} 중괄호 사용하는거
3. 그냥 코드가 아니라 함수를 적는거
이것만 기억해주십시오.
onClick안에 함수를 적는걸 예를 들자면
onClick={} 안에는 어디서 만든 함수명을 적거나 아니면 함수 하나를 바로 만들어서 집어넣어주시면 됩니다.
<div onClick={ showModal }> (showModal은 어디 다른데 만들어둔 함수 이름)
<div onClick={ function(){ 실행할 코드 } }>
<div onClick={ () => { 실행할 코드 } }>
▲ 셋다 가능합니다.
(옛날 그 addEventListener() 여기에 콜백함수 집어넣는 것 처럼 하시면 됩니다.)
참고로 () => {} 이 코드는 function (){} 이것의 ES6 신버전 문법입니다.
자바스크립트 신문법에선 function 대신 => 이라는 키워드를 이용할 수 있습니다. (완전 똑같은 문법은 아니고 비슷합니다)
<span>?</span> 을 눌렀을 때 따봉이라는 state를 1 증가하려면 어떻게 해야할까요?
대충 이런게 아닐까요?
function App(){
let [ 따봉, 따봉변경 ] = useState(0);
return (
<HTML 많은 곳>
<h3> { 글제목[0] } <span onClick={ ()=>{ 따봉 + 1 } } >?</span> { 따봉 }</h3>
</HTML 많은 곳>
)
}
따봉이라는게 일반 변수였다면 이렇게 하시면 끝입니다.
하지만 이렇게 하면 아무 반응없죠? 재렌더링도 일어나지 않고요.
왜냐면 state는 변수와는 다르게 값을 변경하실 때 지정된 변경함수를 쓰셔야합니다.
이게 초보자가 처음 겪는 리액트의 어려운 점입니다.
실은 따봉이라는 값을 변경하는 함수는 useState를 이용할 때 같이 만들었습니다.
let [ 따봉, 따봉변경 ] = useState(0);
따봉변경이라고 만들어놓은 변수가 바로 따봉이라는 state를 변경하기 위한 함수입니다.
사용법은 따봉변경( 대체할 데이터 ) 입니다.
따봉변경 함수의 소괄호() 내에 있는 데이터로 완전히 대체해준다는게 포인트입니다.
따봉변경(1) 이라고 사용하시면 따봉이라는 state가 1로 변경됩니다.
따봉변경(100) 이라고 사용하시면 따봉이라는 state가 100으로 변경됩니다.
알겠죠? 따봉변경( 따봉 = 따봉 + 1 ) 이런거 안됩니다. 깔끔하게 변경할 값만 넣으시면 됩니다.
다시) <span>?</span> 을 눌렀을 때 따봉이라는 state를 1 증가하려면 어떻게 해야할까요?
지금까지의 정보를 바탕으로 빨리 혼자 짜보십시오.
function App(){
let [ 따봉, 따봉변경 ] = useState(0);
return (
<HTML 많은 곳>
<h3> { 글제목[0] } <span onClick={ ()=>{ 따봉변경(따봉 + 1) } } >?</span> { 따봉 }</h3>
</HTML 많은 곳>
)
}
따봉이라는 기존 state에 1을 더한 값을 따봉변경 함수에 집어넣었습니다.
그럼 버튼을 누를 때 마다 그 값으로 대체됩니다.
미리보기 화면에서 눌러보면 1씩 증가하죠? 성공!
아무튼 오늘의 결론은
1. 클릭시 뭔가 실행하고 싶으면 onClick={함수} 이렇게 이벤트 핸들러를 달아서 사용합니다.
2. state를 변경하시려면 state 변경함수를 꼭 이용하십시오.
일단 버튼을 만들고 버튼에 기능을 추가하려면
function App(){
let [글제목, 글제목변경] = useState( ['남자코트 추천', '강남 우동맛집', '파이썬 독학'] );
return (
<HTML 많은 곳>
<button onClick={ 어쩌구~ }> 수정버튼 </button>
<h3> { 글제목[0] } <span>?</span> { 따봉 }</h3>
</HTML 많은 곳>
)
}
기존에 있던 코드에 <button>을 아무데나 하나 추가했고,
onClick 속성을 열어서 뭔가 적어주면 기능이 완성되겠죠?
근데 onClick안에 뭘 적을지가 문제입니다.
일단 onClick 안에는 함수만 들어갈 수 있댔죠? 함수를 만들어 집어넣어봅시다.
function App(){
let [글제목, 글제목변경] = useState( ['남자코트 추천', '강남 우동맛집', '파이썬 독학'] );
function 제목바꾸기() {
어쩌구~
}
return (
<HTML 많은 곳>
<button onClick={ 제목바꾸기 }> 수정버튼 </button>
<h3> { 글제목[0] } <span>?</span> { 따봉 }</h3>
</HTML 많은 곳>
)
}
함수는 저렇게 function App() 아무데나 만드실 수 있으며
만든 함수를 onClick안에 집어넣었습니다.
미리 정의된 함수를 넣으실 때는 소괄호를 넣지 않는게 포인트입니다.
그럼 즐겁게 제목바꾸기 함수 기능개발을 해보도록 합시다.
제목바꾸기 함수는 state를 변경해야하는데..
function App(){
let [글제목, 글제목변경] = useState( ['남자코트 추천', '강남 우동맛집', '파이썬 독학'] );
function 제목바꾸기() {
글제목[0] = '여자코트 추천'
}
return (
<HTML 많은 곳>
<button onClick={ 제목바꾸기 }> 수정버튼 </button>
<h3> { 글제목[0] } <span>?</span> { 따봉 }</h3>
</HTML 많은 곳>
)
}
제목바꾸기() {} 함수 안을 잘 보십시오.
글제목이라는 state의 첫째 데이터를 변경하고싶으니까 저렇게 쓰면 될까요?
안됩니다. state는 = 등호기호로 직접 조작할 수 없다니까염.
만들어놓은 state 변경함수를 꼭 사용하셔야합니다.
글제목 state만들 때 같이 만든 글제목변경() 이라는 함수를 사용하시면 됩니다.
function 제목바꾸기() {
글제목변경( ['여자코트 추천', 강남 우동맛집', '파이썬 독학'] )
}
그래서 state 변경함수인 글제목변경()을 이용해서
제목바꾸기() 함수를 이렇게 디자인했습니다.
Q. 글제목변경() 함수에 array를 왜 저렇게 넣죠?
A. state변경함수들은 state를 아예 대체할 수만 있다고 저번에 배웠습니다.
그래서 기존 state인 ['남자코트 추천', 강남 우동맛집', '파이썬 독학'] 을 바꿔치기할 새로운 array를 넣어주어야합니다.
이렇게 똑같이 따라해주시면 여러분도 버튼을 눌렀을 때 정상적으로 글제목 state가 변경됩니다.
성공!
너무 주먹구구식이라고요?
약간 프로그래머 스타일로 다시만들어보면
array 내용이 3개밖에 없어서 위처럼 하드코딩해도 상관없겠지만
array가 조금이라도 길어지면 하드코딩하기 어렵겠죠?
그러니 여러분이 좋아하는 프로그래밍 스킬을 잔뜩 넣어보겠습니다.
▼ 제목바꾸기 함수를 이렇게 프로처럼 바뀌봅시다.
function 제목바꾸기() {
var newArray = 글제목;
newArray[0] = '여자코트 추천';
글제목변경( newArray );
}
지금 뭘한거냐면..
0. 글제목이라는 state는 직접 수정할 수 없습니다. 그래서.. 복사본을 만들기로 합니다.
1. 글제목이라는 state의 복사본을 만들어 newArray라는 변수에 저장합니다.
2. newArray의 0번째 데이터를 '여자코트 추천'으로 변경합니다.
3. 그리고 그걸 글제목변경() 함수 안에 넣어서 글제목 state를 변경합니다.
이론적으로 완벽하죠?
근데 동작하지 않습니다.
왜냐면 코드는 잘 짠건 맞는데, state를 복사하실 때 문제가 하나 있었기 때문입니다.
원래 자바스크립트 내에서 array나 object 자료형은 = 등호로 복사하시면 각각 별개의 자료형이 생성되는게 아니라
값을 공유합니다. (충격)
예를 들면
var data1 = [1,2,3];
var data2 = data1;
이런 식으로 사용하면 data1에 있던 자료를 data2에 복사한다는 뜻이죠?
근데 data1과 data2는 각각 [1,2,3]을 별개로 저장하는게 아니라
data1과 data2는 똑같은 값을 공유합니다.
data1을 변경하면 data2도 자동으로 변경되고 그렇습니다. (충격)
(자세한건 javascript reference data type이라고 검색해봅시다)
그래서 결론은 state도 등호 = 를 이용해서 복사하면 문제가 일어나기 때문에
완전히 개별 복사본을 만들어주는 카피를 하셔야합니다.
shallow/deep copy라고 부르는데 가장 쉬운 방법은
var 새로운어레이 = [...원본어레이]
이렇게 사용하는 것입니다.
그래서 이렇게 하시면 안되고
function 제목바꾸기() {
var newArray = 글제목;
newArray[0] = '여자코트 추천';
글제목변경( newArray );
}
이렇게 하시면 성공입니다.
function 제목바꾸기() {
var newArray = [...글제목];
newArray[0] = '여자코트 추천';
글제목변경( newArray );
}
성공적으로 글제목 state의 사본을 생성했으며,
그 사본을 입맛에 맞게 조작한 다음에
state 변경함수에 넣어주시면 state가 성공적으로 수정됩니다.
아무튼 오늘의 결론입니다.
리액트에서 state를 수정하고 싶으면 보통 이런 패턴으로 코드를 짭니다.
1. 수정하고 싶은 state의 deep/shallow 카피본을 하나 생성합니다.
2. 카피본을 입맛에 맞게 수정합니다.
3. 카피본을 state변경함수()에 집어넣습니다.