State
리액트에서는 변수 말고 state를 만들어서 데이터를 저장한다.
맨 윗줄에 import React, {FC, useState} from 'react'; 추가
원하는 곳에 useState('보관할 자료')를 사용하면 state에 자료를 잠깐 저장할 수 있다.
let [post, setPost] = useState('미움받을용기');
배열 비구조화를 통해 값을 변수에 저장할 수 있다. 이때 변수 post에는 데이터, setPost에는 변경함수가 들어간다.
State 를 사용하는 이유
일반변수와 달리 State 변수의 데이터가 변경이 되면 자동으로 재 렌더링이 된다. 즉, 재렌더링 때문에 사용하는 것이다. 물론 개발모드에서는 일반 변수도 재 렌더링이 되지만 배포한 후에는 바뀌지 않는다. 자동으로 재렌더링이 되면 개발 편의성이 증가하고 페이지 새로고침 없이도 화면을 바꿀 수 있다. 단, 바뀌지 않는 데이터들은 굳이 state로 만들 필요는 없다.
state 여러개
let [post, setPost] = useState(['미움받을용기', '더마인드', '도둑맞은 집중력']);
<div className='list'>
<h3>{post[0]}</h3>
<p>발행날짜</p>
</div>
값을 배열로 넣고 싶을 때는 userState 안에 배열 형태로 값들을 넣으면 된다.
에러 메시지 안뜨게 하기
코드 상단에 다음과 같은 내용을 넣으면 warning 경고가 뜨지 않게 된다.
/*eslint-disable*/
좋아요 onclick
jsx에서는 onClick 을 사용해서 버튼을 클릭했을 때 동작을 지정할 수 있다.
<div className='list'>
<h3>{post[0]}<span onClick={Temp}>❤️</span>{like}</h3>
<p>발행날짜</p>
</div>
const Temp = () : void =>{
like = like + 1;
console.log('onclick test');
}
onClick을 하면 Temp 라는 함수가 실행된다.
state 변경하기
Temp 함수에는 like를 1씩 증가시키는 코드가 들어 있다. 하지만 저렇게 사용하면 like 값이 바뀌어도 재 렌더링이 안된다. 따라서 우리는 setLike 라는 함수를 사용해야 한다.
let [like, setLike] = useState(0);
//구조분해할당
const Temp = () : void =>{
setLike(++like);
console.log('onclick test');
}
원래 계획한대로 동작하는 것을 알 수 있다.
state array 변경
state 배열을 변경하려면 어떻게 해야할 까?
<button onClick={()=>{
let cppost = [post];
cppost[0] = '들이받을용기';
setPost(cppost);
}}>변경</button>
먼저 이런 방식을 생각해 볼 수 있을 것이다. 배열의 값들을 복사해서 새 변수에 넣고 원하는 요소만 바꾸어 준후 setPost 함수로 다시 설정해준다. 하지만 이 코드는 제대로 동작하지 않는다. 이유를 알려면 state 변경함수의 동작원리를 알아야 한다.
state 변경함수의 동작원리
기존 state와 신규 state 비교검사를해서 바뀐값이 없으면 재 렌더링을 하지 않고, 바뀐 값이 있으면 재 렌더링을 한다. 그렇지만 우리는 분명 첫번째 인덱스의 값을 바꾸어 주었다. 재 렌더링이 안되는 이유는 무엇인가?
그 이유는 메모리 구조에서 찾을 수 있다. post와 cppost는 각각 배열의 메모리 주소를 저장하고 있다. cppost 변수에 post를 대입하게 되면 두 변수는 결국 같은 위치의 주소값(Heap 영역의)을 갖게 된다. 실질적으로 cppost와 post는 같은 값이라는 의미이다. 그래서 필요한 것이 spread 문법이다. spread 문법을 사용하면 새로운 heap 메모리에 카피를 하고 cppost는 새로운 주소값을 할당받게 된다. 이제 post와 cppost는 값이 다르기 때문에 state는 변경이 되었다 판단되고 재 렌더링이 일어난다.
let a = [1,2];
let b = [...a];
console.log(a === b); // deep copy, 깊은 복사
//false
서로 다른 메모리를 갖는 사본을 만드는 것을 깊은 복사라고 한다.
깊은 복사를 적용해서 코드를 고치면 다음과 같다.
<button onClick={()=>{
let cppost = [...post];
cppost[0] = '들이받을용기';
setPost(cppost);
}}>변경</button>
컴포넌트
- html 문법 자체가 길어질수록 매우 복잡하다.
- 리액트에서는 이러한 html 문법을 한 단위로 묶어주는 기능이 있는데 바로 컴포넌트이다.
- 컴포넌트는 함수를 사용하는 목적과 방법이랑 거의 흡사하다.
- 함수를 만들고 호출하여 사용하는 것처럼 컴포넌트도 만들고 호출해서 사용한다.
- 차이점은 컴포넌트는 html 내용을 묶어 놓은 것이다.
- 컴포넌트는 호출을 태그의 형식으로 한다.
사용예시
function Detail(){
return(
<div className='detail'>
<h3>제목</h3>
<h4>내용</h4>
<p>날짜</p>
</div>
)
}
실제 html에서 호출은 다음과 같다.
<Detail></Detail>
타입스크립트에 맞는 화살표 함수로도 사용할 수 있다.
const Detail : React.FC = () => {
return(
<div className='detail'>
<h3>제목</h3>
<h4>내용</h4>
<p>날짜</p>
</div>
)
}
한가지 주의할 점은 컴포넌트에서 return 문 안에서도 하나의 태그 안에서 모두 끝나야한다.
컴포넌트의 단점
- state 변수를 공유가하기가 불편해진다.
- 함수 간에도 데이터를 기본적으로 공유할 수 없고, 매개변수를 전달하거나 전역변수를 사용하여 공유하는 방법 등이 있다.
- 따라서 꼭 필요한 경우에만 컴포넌트로 분리한다.
타이머 기능과 콜백지옥
const Timer : React.FC = () => {
const [seconds, setSeconds] = useState<number>(0);
return(
<div className='timer'>
<h1>타이머 : {seconds}초</h1>
<button onClick={
function(){
setInterval(()=>{
setSeconds(seconds);
}, 1000);
}
}>시작</button>
</div>
)
}
이렇게만 하면 잘 동작할 것 같지만 계속 0초 1초를 반복하게 된다.
const Timer : React.FC = () => {
const [seconds, setSeconds] = useState<number>(0);
return(
<div className='timer'>
<h1>타이머 : {seconds}초</h1>
<button onClick={
function(){
setInterval(()=>{
setSeconds(seconds =>seconds + 1);
}, 1000);
}
}>시작</button>
</div>
)
}
이렇게 setSeconds의 콜백함수에 매번 인자를 새로 전달해서 해야 원하는 의도대로 동작한다.
게시글 구조 변경
css를 추가로 적용하고 div 태그를 이용해 더 감싼다.
<div className="App">
<div className='title-nav'>
<h1>{title}</h1>
</div>
<div className='container'>
<div className='board'>
<div className='post'>
<h3>{post[0].title}<span onClick={handleLikeClick}>❤️</span>{like}</h3>
<p>{post[0].date}</p>
</div>
<div className='post'>
<h3>{post[1].title}</h3>
<p>{post[1].date}</p>
</div>
<div className='post'>
<h3>{post[2].title}</h3>
<p>{post[2].date}</p>
</div>
</div>
</div>
.container{
max-width: 800px;
margin : 0 auto;
padding : 20px;
}
.board{
background-color: aliceblue;
border-radius: 8px;
box-shadow : 0 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.post{
border-bottom: 1px solid #282c34;
padding: 15px;
text-align: left;
}
.post:last-child{
border-bottom: none;
}
.post h3{
color : #333;
}
.post p{
color: #666;
}
상세보기 showhide
동적인UI를 만들어야한다. 동적인 UI를 만드는 순서가 있다.
- 화면에 보여줄 내용을 미리 디자인 해둔다.
- 그 UI의 상태를 state에 저장한다.
- 그 UI가 어떻게 보일지 처리해주는 루틴을 구현한다.
1번
const Detail : React.FC = () => {
return(
<div className='detail'>
<h3>제목</h3>
<h4>내용</h4>
<p>날짜</p>
</div>
)
};
이미 구현한 부분이므로 생략한다.
2번
const[detail, setDetail] = useState<boolean>(false);
state 변수를 불리언 타입으로 선언한다.
삼항연산자
리액트의 html 영역안에서는 if else 문을 사용할 수 없다.
대신 삼항연산자는 사용이 가능하다. 또한 삼항연산자를 사용하려면 중괄호 안에서 사용한다.
조건식 ? 조건식이 참일경우 실행되는 코드 : 조건식 거짓일 경우 실행되는 코드
{
10 > 5 ? <p>참</p> : <p>거짓</p>
}
3번
삼항연산자를 이용해 상세보기 루틴을 구현한다.
{
detail ? <Detail></Detail> : null
}
null의 경우 아무것도 하지 않는다는 뜻이다.
<div className='post'>
<h3 onClick={()=>setDetail(!detail)}>{post[0].title}
<span onClick={handleLikeClick}>❤️</span>{like}
</h3>
<p>{post[0].date}</p>
</div>
이제 제목을 누르면 토글방식으로 상세페이지가 보였다 안보였다 한다.
후기
state 변수의 사용법과 컴포넌트에 대해서 학습하였다. tsx에서 조건문으로 삼항연산자를 사용하는 것에 대해서도 배웠다.
키워드: 프로그래머스 데브코스, 국비지원교육, 코딩부트캠프
'프로그래머스 풀스택 데브코스 > 데브코스 TIL' 카테고리의 다른 글
웹 풀사이클 데브코스 TIL 53일차 (0) | 2024.02.05 |
---|---|
웹 풀사이클 데브코스 TIL 52일차 (0) | 2024.02.03 |
웹 풀사이클 데브코스 TIL 50일차 (0) | 2024.01.31 |
웹 풀사이클 데브코스 TIL 49일차 (0) | 2024.01.30 |
웹 풀사이클 데브코스 TIL 48일차 (0) | 2024.01.29 |