ReactTypeScriptenvStateLifecycleHookside-effectlet userName: string = "skrr";
let age: number = 28;
let isMarriage: boolean = false;
let ary: string[] = ["skrr", "skrr2"];
객체의 타입을 미리 선언해두고 변수 타입으로 사용.
?는 옵셔널 프로퍼티(있어도 되고 없어도 됨).
interface User {
email : string;
password : string;
address?: string;
}
const user: User = {
email : "tpfh801@gmail.com",
password : "12345678"
}
function showMessage(name: string) : string {
return `${name}님 환영합니다.`;
}
json
{
"comment": [
{ "writer": "임정섭", "comment": "..." },
{ "writer": "박선아", "comment": "..." }
]
}
최상위 key가 그대로 리소스 경로가 된다.
api.get('/comment') // db.json의 "comment" 배열 반환
REACT_APP_BACKEND_ENDPOINT=http://localhost:5000
서버 주소를 코드 밖으로 뺀다. 주소가 바뀌어도 이 한 줄만 수정.
process.env.REACT_APP_BACKEND_ENDPOINT // "http://localhost:5000"
REACT_APP_으로 시작해야 인식됨 (Vite는 VITE_)import axios from 'axios';
const endPoint = process.env.REACT_APP_BACKEND_ENDPOINT;
const api = axios.create({
baseURL : endPoint
});
export default api;
baseURL을 미리 심어둔 인스턴스를 만들어 export.
api.get('/comment') // → http://localhost:5000/comment
공통 헤더나 인터셉터도 여기 한 곳에서 관리 가능
같은 인터페이스(title, onClick)를 styled-components와 MUI 두 방식으로 실습했다.
// ButtonPage.jsx (부모)
<MaterialButton
title='글 작성하기'
onClick={(e) => saveHandler()}/>
부모가 표시할 값(title)과 실행할 함수(onClick)를 함께 내려준다.
// MaterialButton.jsx (자식)
<Button onClick={props.onClick}>{props.title}</Button>
npm install styled-components
import Styled from "styled-components";
const StyledButton = Styled.button`
padding : 8px 16px;
font-size : 16px;
border-radius : 8px;
cursor : pointer;
`;
const Button = (props) => {
return <StyledButton onClick={props.onClick}>{props.title}</StyledButton>;
}
Styled.태그명 + 백틱 안에 CSS를 쓰면 스타일이 입혀진 컴포넌트가 만들어진다. 클래스명을 직접 짓거나 css 파일을 따로 관리할 필요가 없다.
npm install @mui/material @emotion/react @emotion/styled
import Button from '@mui/material/Button';
const MaterialButton = (props) => {
return <Button onClick={props.onClick}>{props.title}</Button>;
}
이미 디자인된 컴포넌트를 가져다 쓴다. 직접 CSS를 작성하지 않아도 됨.
MUI 컴포넌트를 Button으로 import했기 때문에 래퍼 이름은 MaterialButton으로 구분. 한 파일에서 둘 다 쓰려면 import { Button as MuiButton }처럼 별칭이 필요하다.
입장/퇴장 버튼으로 인원수를 증감시키는 실습. components/styled/Button.jsx 사용.
State = 컴포넌트가 가지는 상태/데이터.
클래스 컴포넌트의 생명주기:
출생 인생 사망
Mounting → Updating → Unmounting
State는 직접 수정할 수 없다(하면 안 된다). 변경 함수를 통해야 React가 변경을 인지하고 재렌더링한다.
use로 시작하는 함수 == Hook.
클래스 기반으로 설계하지 않아도(function component) state와 lifecycle 관리가 가능해졌다.
// let cnt = 0; → 값이 바뀌어도 화면이 갱신되지 않음
let [cnt, setCnt] = useState(0);
일반 변수는 렌더링될 때마다 초기화되고, 값이 바뀌어도 React가 감지하지 못한다. useState로 관리해야 setCnt 호출 시 재렌더링이 일어난다.
const upCntHandler = (e) => {
setCnt( cnt => cnt + 1 );
}
useEffect(이펙트 함수, 의존성 배열);
렌더링 이후 side-effect로 sync를 맞추는 용도. 두 번째 인자의 값이 바뀔 때마다 함수가 다시 실행된다.
useEffect( () => {
console.log(`debug >>>> side effect render cnt ${cnt}`);
}, [cnt]);
| 의존성 배열 | 실행 시점 |
|---|---|
[] | 최초 1회만 (마운트 시) |
[cnt] | 최초 + cnt가 바뀔 때마다 |
| 생략 | 렌더링될 때마다 |
의존성 배열로 실행 시점을 조절해 클래스 컴포넌트의 생명주기(Mounting/Updating/Unmounting)에 해당하는 작업을 처리할 수 있다.
버튼 클릭 시마다 컴포넌트 함수 전체가 재실행됨
컴포넌트는 렌더링될 때마다 처음부터 다시 실행되는 함수다. 상태가 바뀌면 React가 CapacityPage()를 통째로 다시 호출해 새 JSX를 받아온다.
첫 클릭에 cnt 1이 찍힘
[
"comment"
{
"writer" : "임정섭",
"comment" : "강사님과 함께하는 즐거운 React..."
}
]
문법 이해를 못했다.
{
"comment": [
{
"writer": "임정섭",
"comment": "강사님과 함께하는 즐거운 React..."
}
]
}
const Comment = (data) => {
... {data.writer} ... {data.comment}
}
매개변수 이름을 data로 지었다고 해서 그 안의 data 속성이 꺼내지는 게 아니라, 그냥 props 객체 전체에 data라는 이름표를 붙인 것뿐임. 그래서 data.writer는 props.data.writer가 아니라 props.writer를 찾는 셈이고, 그런 건 없으니 undefined → 화면에 아무것도 안 보이는 문제 발생
해결
1. 그냥 props 쓰기
const Comment = (props) => {
... {props.data.writer} ... {props.data.comment}
}
2. 구조분해
const Comment = ({ data }) => {
... {data.writer} ... // 기존 코드 그대로 동작
}