애플리케이션 프로젝트를 만들 때, 여러가지 컴포넌트들을 사용해 하나의 화면으로 만들게 된다. 컴포넌트(Component)란 프로그래밍에 있어 재사용이 가능한 각각의 독립된 모듈을 뜻한다. 컴포넌트 기반 프로그래밍을 하면 마치 레고 블록처럼 이미 만들어진 컴포넌트들을 조합하여 화면을 구성할 수 있다.
컴포넌트는 클래스형 컴포넌트와 함수형 컴포넌트로 선언할 수 있다.
클래스형 컴포넌트
가장 처음 리액트 프로젝트를 생성했을 때 나오는 기본적인 App 컴포넌트는 함수형 컴포넌트이다.
import React from "react";
import "./App.css";
import { Fragment } from "react";
function App() {
const name: string = "Users";
return (
<>{name === "User" ? <h1> {name} hello ! </h1> : <h1> {name} bye </h1>}</>
);
}
export default App;
이것을 클래스형으로 변환시켜보자.
import React from "react";
import "./App.css";
import { Component } from "react";
class App extends Component {
render() {
const name: string = "Users";
return (
<>{name === "User" ? <h1> {name} hello ! </h1> : <h1> {name} bye </h1>}</>
);
}
}
export default App;
클래스형으로 바꾸어도 동일한 동작을 수행할 수 있다. 클래스형으로 컴포넌트를 만들 경우, state 기능 및 라이프 사이클 기능을 사용할 수 있다는 것과 임의 메서드를 정의할 수 있다는 것이 있다.
또한, 클래스형 컴포넌트에는 render 함수가 꼭 필요하고, 그 안에서 보여줄 JSX를 반환해야 한다.
함수형 컴포넌트
그렇다고 함수 컴포넌트를 쓰지 않는것은 아니다. 함수 컴포넌트는 다음과 같은 장점이 있다.
- 클래스형보다 간편하게 선언이 가능하다.
- 메모리 자원 또한 클래스형 보다 덜 사용한다.
하지만 앞서 말했듯이 state 사용 불가와 라이프사이클 API 사용이 불가하다는 점이 있다. Hooks라는 기능을 통해 이 부분이 해결되었지만, 클래스형과 완전히 동일하게 사용할 수는 없다.
그래도 리액트 공식 레퍼런스는 함수 컴포넌트와 Hooks의 사용을 권장하고 있으므로, 향후 업데이트 되면서 조금씩 더 보완될 것을 기대해도 좋을 것이다.
컴포넌트 생성
새로운 컴포넌트를 만들 때에는 새로운 파일을 만들어서 코드를 저장해준 뒤, export를 통해 다른 파일에서 import해서 불러올 수 있도록 한다.
// src/MyComponent.tsx
const MyComponent = () => {
return <div>Hello Component !</div>;
};
export default MyComponent;
// src/App.tsx
import React from "react";
import "./App.css";
import { Component } from "react";
import MyComponent from "./MyComponent";
class App extends Component {
render() {
return <MyComponent />;
}
}
export default App;
위와 같은 형식으로 사용하면 컴포넌트에서 설정한 정보들이 나오게 된다.
props
props는 properties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용하는 요소이다. props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있다.
JSX 내부 props 렌더링
MyComponent 컴포넌트를 수정해서 해당 컴포넌트에서 name이라는 props를 렌더링하도록 설정해보자.
// src/App.tsx
import React from "react";
import "./App.css";
import { Component } from "react";
import MyComponent from "./MyComponent";
class App extends Component {
render() {
return <MyComponent name="User" />;
}
}
export default App;
// src/MyComponent.tsx
interface MyComponentProps {
name: string;
}
const MyComponent = ({ name }: MyComponentProps) => {
return <div> {name}, Component !</div>;
};
MyComponent.defaultProps = {
name: "User",
};
export default MyComponent;
자바스크립트와 달리 타입스크립트에서는 props에 어떤 타입이 들어오는지 먼저 정의를 해 주어야한다. type 혹은 interface로 예상되는 props의 프로퍼티 형식을 지정해주자.
또한, defaultProps를 통해 App.tsx 내의 MyComponent에서 name을 입력해주지 않았을 경우에 대한 기본 값을 지정해줄 수 있다.
children
컴포넌트 태그 사이의 값을 보여주는 props도 존재한다.
// src/MyComponent.tsx
import { ReactNode } from "react";
interface MyComponentProps {
name: string;
children: ReactNode;
}
const MyComponent = ({ name, children }: MyComponentProps) => {
return (
<div>
<h1>{name}, Component !</h1>
<h2>{children}</h2>
</div>
);
};
MyComponent.defaultProps = {
name: "User",
};
export default MyComponent;
MyComponent에 children이라는 것으로 ReactNode를 설정해주었다. ReactNode는 외부에서 주입받을 컴포넌트의 타입을 정의할 때 많이 사용하며, 대부분의 자바스크립트 데이터 타입을 아우르는 범용적인 타입이다. 이 타입을 통해 태그 안에 있는 내용들을 컴포넌트에서 받을 수 있게 된다.
state
리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다. props는 부모 컴포넌트가 설정하는 값으로, 자식 컴포넌트에서는 해당 값을 읽기 전용으로만 사용할 수 있다. 즉, props를 바꿔주기 위해서는 부모 컴포넌트에서 바꿔주어야 한다.
리액트에는 두 종류의 state가 존재한다. 하나는 클래스형 컴포넌트가 가진 state이고, 다른 하나는 함수 컴포넌트에서 useState라는 함수를 통해 사용하는 state이다.
클래스형 컴포넌트의 state
새로운 컴포넌트를 만들어보자.
// src/Counter.tsx
import { Component } from "react";
interface State {
num?: number;
}
class Counter extends Component {
state: State = {
num: 0,
};
render() {
const { num } = this.state;
return (
<div>
{typeof num === "number" ? (
<>
<h1>{num}</h1>
<button
onClick={() => {
this.setState({ num: num + 1 });
}}
>
+1
</button>
<button
onClick={() => {
this.setState({ num: num - 1 });
}}
>
-1
</button>
</>
) : null}
</div>
);
}
}
export default Counter;
// src/App.tsx
import React from "react";
import "./App.css";
import { Component } from "react";
import Counter from "./Counter";
class App extends Component {
render() {
return <Counter />;
}
}
export default App;
이런식으로 카운트 해주는 컴포넌트를 만들어줄 수 있다. 인터페이스에서 num에 ?가 붙는 이유는 props로 어떤 값이 들어올 지 모르기 때문에, number 혹은 undefined라고 지정해주어 에러 발생에 대비할 수 있게 된다.
render 함수에서 현재 state를 조회할 때는 this.state를 조회하면 된다. 또한, state를 변경하기 위해 this.setState 함수를 사용했고, 이 함수가 state값을 변경할 수 있게 해 준다.
위 코드를 렌더링하게되면 버튼을 누를때마다 숫자가 1 증가하거나 감소하는 화면이 나올 것이다.
state 객체 안에는 여러 값이 올 수 있다. 내가 변경하고 싶은 값은 여러 개의 값 중 하나일 수 있으므로, setState로 지정해서 변경해주면 된다.
함수 컴포넌트와 useState
이전의 리액트에서는 함수 컴포넌트에서 state를 사용할 수 없었다. 하지만 이제는 useState를 통해 함수 컴포넌트 내에서도 state를 사용가능하고, 리액트에서도 이렇게 사용하도록 권장하고 있다.
- useState 사용하기
// src/Say.tsx
import { useState } from "react";
function Say() {
const [message, setMessage] = useState("");
const onClickEnter = () => setMessage("안녕하세요!");
const onClickLeave = () => setMessage("안녕히 가세요!");
return (
<div>
<button onClick={onClickEnter}>입장</button>
<button onClick={onClickLeave}>퇴장</button>
<h1>{message}</h1>
</div>
);
}
export default Say;
// src/App.tsx
import React from "react";
import "./App.css";
import { Component } from "react";
import Say from "./Say";
class App extends Component {
render() {
return <Say />;
}
}
export default App;
위 코드를 입력하고 렌더링해보자. 입장과 퇴장을 눌렀을 때, 문구가 바뀌는 것을 확인할 수 있다.
useState에서는 반드시 객체가 아니어도 상관이 없다. 값의 형태는 자유이며, 숫자일 수도, 문자열일수도, 객체일 수도, 배열일 수도 있다.
- 한 컴포넌트에서 여러 번 useState 사용하기
useState는 한 컴포넌트에서 여러 번 사용 가능하다.
import { useState } from "react";
function Say() {
const [message, setMessage] = useState("");
const onClickEnter = () => setMessage("안녕하세요!");
const onClickLeave = () => setMessage("안녕히 가세요!");
const [color, setColor] = useState("black");
return (
<div>
<button onClick={onClickEnter}>입장</button>
<button onClick={onClickLeave}>퇴장</button>
<h1 style={{ color }}>{message}</h1>
<button style={{ color: "red" }} onClick={() => setColor("red")}>
빨간색
</button>
<button style={{ color: "green" }} onClick={() => setColor("green")}>
초록색
</button>
<button style={{ color: "blue" }} onClick={() => setColor("blue")}>
파란색
</button>
</div>
);
}
export default Say;
이런식으로 코드를 추가해주면, 글씨의 색상이 바뀌는 버튼이 추가되고, 두개의 상태를 제어할 수 있게 된다.
'WEB > React' 카테고리의 다른 글
리액트 컴포넌트 반복 사용하기 [react/component/typescript] (0) | 2023.03.12 |
---|---|
리액트 DOM 네이밍 이해하기 (0) | 2023.03.12 |
리액트 이벤트 시스템 이해하기 [react/event/typescript] (0) | 2023.03.11 |
JSX 이해하기 (0) | 2023.03.11 |
리액트 이해하기 (0) | 2023.03.11 |