And Brain said,

React, 프론트엔드의 왕 - [2] 본문

IT/React + TypeScript

React, 프론트엔드의 왕 - [2]

The Man 2023. 4. 9. 19:47
반응형

 

목차
1. 컴포넌트(Component)와 Props
2. 상태 관리(useState, useReducer, useContext)
3. 사용자 입력 처리 (Material-UI)
4. 폼 및 유효성 검사 (react-hook-form)
5. 애니메이션 (React Transition Group, CSS-in-JS)
6. 네비게이션과 라우팅 (Next.js)
7. 비동기 처리 (React Hooks, axios)

 
 
오늘은 React의 기초 이론에 대해서 알아보는 시간을 가져보자.
 
언어는 타입스크립트로 리액트와 타입스크립트를 함께 사용하여, 타입 안정성을 제공하고 오류를 줄여보도록 하자. 
 


1. 컴포넌트(Component)와 Props

 
 
컴포넌트(Component)란 React의 가장 기본적인 구성 요소로, UI를 구성하는 개별적인 블록이다.
 
컴포넌트는 재사용 가능하며, 각 컴포넌트는 독립적인 기능을 가지게 설계되야 한다.
 
컴포넌트는 주로 함수형(Functional Component)으로 작성되지만 클래스형(Class Component)으로도 작성할 수 있다.
 
여기서는 함수형 컴포넌트를 중점적으로 다룰 것이다.

 

Props는 컴포넌트에 전달되는 데이터를 일컬으며, 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용된다. 

 


먼저, 컴포넌트가 받아야 하는 props의 타입을 정의해야 한다, 이를 위해 TypeScript의 인터페이스를 사용해보자.

interface GreetingProps {
  name: string;
  age: number;
  isStudent: boolean;
}

 
다음으로 함수형 컴포넌트를 정의하고, 인터페이스를 사용하여 props의 타입을 지정한다.

import React from 'react';

interface GreetingProps {
  name: string;
  age: number;
  isStudent: boolean;
}

const Greeting: React.FC<GreetingProps> = ({ name, age, isStudent }) => {
  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>You are {age} years old.</p>
      <p>{isStudent ? "You are a student." : "You are not a student."}</p>
    </div>
  );
};

 
여기서 Greeting 컴포넌트는 GreetingProps 인터페이스를 사용하여 props의 타입을 지정한다.
 
컴포넌트 함수의 매개변수로 구조 분해 할당(destructuring assignment)을 사용하여 props를 전달받아 각각의 변수에 할당한다.

이제 Greeting 컴포넌트를 사용하는 부모 컴포넌트를 작성해보자.
 

import React from 'react';
import Greeting from './Greeting';

const App: React.FC = () => {
  return (
    <div>
      <Greeting name="Horang" age={26} isStudent={true} />
    </div>
  );
};

export default App;

 
이렇게 작성하면, Greeting 컴포넌트는 name, age, isStudent의 값을 올바른 타입으로 전달받아 사용하게 되며, 타입이 일치하지 않으면 TypeScript에서 컴파일 시 오류를 발생시킨다.
 
 


2. 상태 관리(useState, useReducer, useContext)

 

 
React의 상태 관리는 주로 컴포넌트의 상태를 관리하는 데 사용되는 useState, useReducer, useContext 등의 Hooks를 사용한다.
 
먼저, useState는 컴포넌트의 상태를 관리하기 위한 가장 기본적인 Hook으로 useState를 사용하여 상태를 초기화하고, 상태를 업데이트하는 함수를 얻을 수 있다.
 

import React, { useState } from 'react';

interface CounterProps {
  initialCount: number;
}

const Counter: React.FC<CounterProps> = ({ initialCount }) => {
  const [count, setCount] = useState<number>(initialCount);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
      <button onClick={() => setCount(count - 1)}>Decrease</button>
    </div>
  );
};

 
 
여기서, Counter 컴포넌트는 초기 카운트 값을 받아서 내부 상태를 관리한다. 
 
useState에 제네릭을 사용하여 상태의 타입을 지정한다.


다음으로, useReducer는 복잡한 상태 로직을 관리하기 위해 사용하는 Hook으로, 상태를 업데이트하는 로직을 reducer 함수로 분리하여 관리할 수 있다.
 

import React, { useReducer } from 'react';

type CounterAction = { type: 'increment' } | { type: 'decrement' };

function counterReducer(state: number, action: CounterAction): number {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      return state;
  }
}

const Counter: React.FC = () => {
  const [count, dispatch] = useReducer(counterReducer, 0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increase</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrease</button>
    </div>
  );
};

 
이 예제에서는 counterReducer 함수를 정의하고, 이 함수를 useReducer에 전달하여 상태를 관리하고, CounterAction 타입을 사용하여 액션 객체의 가능한 타입을 지정한다.
 
 
마지막으로, useContext는 전역 상태를 관리하거나, 컴포넌트 간에 상태를 공유할 때 사용하는 Hook으로, React.createContext를 사용하여 Context를 생성하고, useContext를 사용하여 해당 Context의 값을 사용할 수 있다.
 

import React, { createContext, useContext, useState } from 'react';

interface Theme {
  color: string;
  backgroundColor: string;
}

const ThemeContext = createContext<Theme | undefined>(undefined);

const ThemeProvider: React.FC = ({ children }) => {
  const [theme, setTheme] = useState<Theme>({ color: 'black', backgroundColor: 'white' });

  return (
    <ThemeContext.Provider value={theme}>
      {children}
    </ThemeContext.Provider>
  );
};

const ThemedButton: React.FC = () => {
  const theme = useContext(ThemeContext);

  if (!theme) {
    throw new Error('ThemedButton must be used within a ThemeProvider');
  }

  const { color, backgroundColor } = theme;

  return (
    <button style={{ color, backgroundColor }}>
      Themed Button
    </button>
  );
};

const App: React.FC = () => {
  return (
    <ThemeProvider>
      <ThemedButton />
    </ThemeProvider>
  );
};

 
이 예제에서는 ThemeContext를 생성하고 ThemeProvider 컴포넌트를 사용하여 하위 컴포넌트에 전달하는데, ThemedButton 컴포넌트에서는 useContext를 사용하여 ThemeContext의 값을 받아와 스타일을 적용한다.
 
이렇게 하면 전역 상태를 사용하여 테마를 설정하고 공유할 수 있게 된다.
 
 


3. 사용자 입력 처리 (Material-UI)

 

Mui(Material-UI)는 현대적인 웹 애플리케이션을 위한 인기 있는 React UI 프레임워크로,  Mui를 사용하여 사용자 입력을 처리해보도록 하자.

 

import React, { useState } from 'react';
import {
  Button,
  TextField,
  Checkbox,
  FormControlLabel,
  Radio,
  RadioGroup,
  Switch
} from '@mui/material';

const UserInputExample = () => {
  const [inputValue, setInputValue] = useState('');
  const [isChecked, setIsChecked] = useState(false);
  const [selectedRadio, setSelectedRadio] = useState('option1');
  const [isSwitched, setIsSwitched] = useState(false);

  const handleButtonClick = () => {
    alert('Button clicked!');
  };

  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setInputValue(event.target.value);
  };

  const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setIsChecked(event.target.checked);
  };

  const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setSelectedRadio(event.target.value);
  };

  const handleSwitchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setIsSwitched(event.target.checked);
  };

  return (
    <div>
      <Button onClick={handleButtonClick}>Click me!</Button>
      <TextField value={inputValue} onChange={handleInputChange} label="Enter text" />
      <FormControlLabel
        control={<Checkbox checked={isChecked} onChange={handleCheckboxChange} />}
        label="Check me"
      />
      <RadioGroup value={selectedRadio} onChange={handleRadioChange}>
        <FormControlLabel value="option1" control={<Radio />} label="Option 1" />
        <FormControlLabel value="option2" control={<Radio />} label="Option 2" />
      </RadioGroup>
      <FormControlLabel
        control={<Switch checked={isSwitched} onChange={handleSwitchChange} />}
        label="Switch me"
      />
    </div>
  );
};

export default UserInputExample;

 

이 예제에서는 다음과 같은 작업을 수행하고 있다.


Button 클릭 시, 알림 표시, TextField에서 사용자가 입력한 텍스트를 상태로 관리, Checkbox의 선택 여부를 상태로 관리, RadioGroup 내의 Radio 중 선택된 값을 상태로 관리, Switch의 전환 여부를 상태로 관리한다.

 

이외에도 다양한 기능이 존재하니 Mui 공식 문서에서 확인해보시길 바란다.

 

 


4. 폼 및 유효성 검사 (React-hook-form)

 

 

바로 위에서 배운 Material-UI의 TextField와 Button 컴포넌트 사용하여 폼을 만들고 React Hook Form 라이브러리를 이용해 유효성 검사를 수행해보자.

 

import React from 'react';
import { useForm } from 'react-hook-form';
import { TextField, Button } from '@mui/material';

type FormData = {
  email: string;
  password: string;
};

const SignupForm = () => {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>();

  const onSubmit = (data: FormData) => {
    console.log('Submitted data:', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <TextField
        label="Email"
        {...register('email', {
          required: 'Email is required',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
            message: 'Invalid email address',
          },
        })}
        error={!!errors.email}
        helperText={errors.email?.message}
      />
      <TextField
        type="password"
        label="Password"
        {...register('password', {
          required: 'Password is required',
          minLength: {
            value: 8,
            message: 'Password must be at least 8 characters',
          },
        })}
        error={!!errors.password}
        helperText={errors.password?.message}
      />
      <Button type="submit">Sign Up</Button>
    </form>
  );
};

export default SignupForm;

 

이 예제에서는 useForm 훅을 사용하여 폼 관련 로직을 처리하고, TextField 컴포넌트에 register 함수를 사용하여 유효성 검사 규칙을 설정한다.


또한, 에러 메시지를 표시하기 위해 TextField의 error 및 helperText 속성을 사용하였고 Button 컴포넌트를 사용하여 폼 제출을 처리한다.


이렇게 Material-UI와 React Hook Form을 사용하여 회원가입 폼을 만들고, 이메일과 비밀번호 입력에 대한 유효성 검사를 간단하게 수행하였다.

 

 


5. 애니메이션 (React Transition Group, CSS-in-JS)

 

 

React에서 애니메이션을 구현하는 다양한 방법이 있지만, 여기서는 React Transition Group과 CSS-in-JS 라이브러리를 사용하여 애니메이션을 구현해보도록 하자.

 

import React, { useState } from 'react';
import { Transition } from 'react-transition-group';
import styled from '@emotion/styled';

const duration = 300;

const defaultStyle = {
  transition: `opacity ${duration}ms ease-in-out`,
  opacity: 0,
};

const transitionStyles = {
  entering: { opacity: 0 },
  entered: { opacity: 1 },
  exiting: { opacity: 0 },
  exited: { opacity: 0 },
};

const Box = styled.div`
  width: 100px;
  height: 100px;
  background-color: red;
`;

const FadeAnimationExample = () => {
  const [inProp, setInProp] = useState(false);

  return (
    <div>
      <button onClick={() => setInProp(!inProp)}>
        Toggle Fade
      </button>
      <Transition in={inProp} timeout={duration}>
        {(state) => (
          <Box style={{ ...defaultStyle, ...transitionStyles[state] }} />
        )}
      </Transition>
    </div>
  );
};

export default FadeAnimationExample;

 

먼저, in, timeout, 그리고 자식 함수를 받는 Transition 컴포넌트를 사용하여 페이드 애니메이션을 구현한다.


그리고 애니메이션의 지속 시간을 설정하고, 초기 스타일 및 전환 스타일을 정의하자.

 

state 값에 따라 전환 스타일을 적용한 뒤, Emotion을 사용하여 Box 컴포넌트를 정의한다.


버튼을 클릭하면 inProp 값을 변경하여 애니메이션을 토글하게 된다.

 

이렇게 React Transition Group과 CSS-in-JS 라이브러리인 Emotion을 사용하여 간단한 페이드 인/아웃 애니메이션을 만드는 방법을 알아보았다.

 

 


6. 네비게이션과 라우팅 (Next.js)

 

 

일전에 Next.js를 포스팅한 적이 있긴 하지만, 이곳에서는 네비게이션과 라우팅 기능만 설명할 것이다.

https://theworldaswillandidea.tistory.com/103

 

Next.js, React의 Next Level

https://theworldaswillandidea.tistory.com/66 SSR과 CSR의 기본적인 이해도가 필요합니다. Next.js는 React의 프레임워크다. 다들 알다시피 React는 라이브러리기 때문에 프로젝트 초반에 설정해줘야할 것들이 꽤

theworldaswillandidea.tistory.com

 

 

Next.js에서는 파일 기반 라우팅을 사용하는데, /pages 폴더 내에 생성된 파일 이름을 기반으로 라우팅 경로가 자동으로 구성된다.

예를 들어, pages/index.tsx 파일이 있으면, 이 파일은 루트 경로 /에 매핑된다. 

 

마찬가지로, pages/about.tsx 파일은 /about 경로에 매핑된다.

자 그러면, pages/index.tsx와 pages/about.tsx 두 개의 페이지를 생성해보자.

// pages/index.tsx

import React from 'react';
import Link from 'next/link';

const HomePage: React.FC = () => {
  return (
    <div>
      <h1>Home Page</h1>
      <Link href="/about">
        <a>About Page</a>
      </Link>
    </div>
  );
};

export default HomePage;

 

// pages/about.tsx

import React from 'react';
import Link from 'next/link';

const AboutPage: React.FC = () => {
  return (
    <div>
      <h1>About Page</h1>
      <Link href="/">
        <a>Home Page</a>
      </Link>
    </div>
  );
};

export default AboutPage;

 

Next.js에서는 Link 컴포넌트를 사용하여 페이지 간에 쉽게 이동할 수 있다.

 

Link 컴포넌트는 href 속성으로 이동할 경로를 전달받는데, a 태그를 사용하여 사용자에게 클릭 가능한 링크를 제공한다.

 

이외의 추가적인 Next.js 기능들은 공식 문서를 참고해주시길 바란다.

 

 


7. 비동기 처리 (React Hooks, axios)

 

 

 

React에서 비동기 처리를 위해 React Hooks와 axios 라이브러리를 사용할 수 있다.

간단하게 사용자 정보를 가져오는 컴포넌트를 만들어보자.

 

import React, { useState, useEffect } from 'react';
import axios from 'axios';

interface User {
  id: number;
  name: string;
  email: string;
}

const UsersList: React.FC = () => {
  const [users, setUsers] = useState<User[]>([]);

  useEffect(() => {
    const fetchUsers = async () => {
      const response = await axios.get<User[]>('https://jsonplaceholder.typicode.com/users');
      setUsers(response.data);
    };

    fetchUsers();
  }, []);

  return (
    <div>
      <h1>Users List</h1>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name} - {user.email}</li>
        ))}
      </ul>
    </div>
  );
};

export default UsersList;

 

이 예제에서는 useState를 사용하여 users 상태를 관리한다.

 

useEffect는 컴포넌트가 마운트될 때 API 호출을 수행하고, 데이터가 반환되면 상태를 업데이트하게 된다.

axios를 사용하여 웹 API를 호출하고, 응답을 처리하는데, axios.get 메서드를 사용하여 GET 요청을 수행하고, 반환된 데이터를 상태로 설정한다.

 

 


끝으로,

 

오늘은 React와 TypeScript를 사용하여 애플리케이션을 구축하는데 필요한 기본 개념과 도구들을 살펴보았다.

 

다양한 컴포넌트 작성 방법, 상태 관리, 사용자 입력 처리, 폼 및 유효성 검사, 애니메이션, 네비게이션 및 라우팅, 그리고 비동기 처리까지 간단히 살펴보았는데, 이제 여러분은 이러한 기술과 도구를 사용하여 자신만의 React 애플리케이션을 개발할 수 있는 기초 지식을 어느정도 갖추게 되었다.

 

물론, 이 글에서 다룬 내용들은 막대한 React 생태계의 일부분일 뿐이므로, 계속해서 새로운 라이브러리와 기술을 탐색하고 꼭 경험해 보셔야 한다.

 

여러분들의 프로젝트에 이러한 기본 개념들이 도움이 되길 바라며,

 

Thanks for watching, Have a nice day.

 

 

References

https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/basic_type_example/
https://github.com/piotrwitek/react-redux-typescript-guide
https://mui.com/material-ui/getting-started/overview
https://react-hook-form.com/get-started
https://nextjs.org/docs/getting-started
https://nextjs.org/docs/routing/introduction

반응형
Comments