
지난 시간에 이어 북스토어 프로젝트를 계속 진행하겠습니다. 이번 시간에는 라우팅 설정, 데이터 모델 정의, 서버 통신, 그리고 회원가입 폼 구현까지의 과정을 정리했습니다.
화면을 이동하기 위해서는 URL을 사용합니다. URL과 화면 컴포넌트를 일치시키는 라우팅 작업을 위해 react-router-dom 패키지를 활용합니다. 타입스크립트 환경에서는 타입 정의가 필요하므로 @types/react-router-dom 도 함께 설치해야 합니다.
pnpm install react-router-dom
pnpm install -D @types/react-router-dom
createBrowserRouter 함수로 경로(path)와 컴포넌트(element)를 연결하고, RouterProvider 컴포넌트로 화면에 렌더링합니다.
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Layout from './components/layout/Layout';
import Home from './views/Home';
import Detail from './views/Detail';
const router = createBrowserRouter([
{
path: '/',
element: (
<Layout>
<Home />
</Layout>
),
},
]);
function App() {
return (
<BookStoreThemeProvider>
<ThemeSwitcher />
<Layout>
<RouterProvider router={router} />
</Layout>
</BookStoreThemeProvider>
);
}
export default App;
Layout 컴포넌트로 각 페이지를 감싸면 헤더와 푸터를 라우트 단위로 공통 적용할 수 있습니다. 경로마다 어떤 컴포넌트가 보일지 한눈에 파악되는 구조라 유지보수에도 유리합니다.
서버와 통신할 때 프론트엔드에서 어떤 형태의 데이터를 받을지 미리 정의해 두는 것이 중요합니다. 이러한 타입(인터페이스)들을 모아서 관리하는 곳이 models 폴더입니다.
파일 이름을 지을 때는 user.model.ts 처럼 확장자 앞에 역할을 명시하면 구분하기 편리합니다.
// src/models/user.model.ts
export interface User {
id: number;
email: string;
password?: string;
}
자바스크립트처럼 매번 데이터의 존재 여부를 런타임에 확인하는 대신, 미리 인터페이스를 만들어 타입을 지정해두면 개발 단계에서 오류를 미리 방지할 수 있습니다. 서버 응답 구조가 바뀌었을 때도 타입 에러로 바로 확인할 수 있어서 디버깅이 훨씬 수월해집니다.
서버와 통신하는 방법에는 여러 가지가 있지만, 이번 프로젝트에서는 axios 라이브러리를 활용합니다.
npm install axios
axios 인스턴스를 미리 만들어두면 baseURL, 인터셉터 등을 한 곳에서 관리할 수 있어 편리합니다.
// src/api/axios.ts
import axios from 'axios';
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
});
export default axiosInstance;
백엔드 서버와 연결하는 과정에서 CORS 에러가 발생할 수 있습니다. CORS는 브라우저가 다른 출처(Origin)의 리소스 요청을 제한하는 보안 정책입니다.
이 문제는 프론트엔드가 아닌 백엔드 서버 측에서 CORS 정책을 허용해 주는 방식으로 해결해야 합니다. 프론트엔드에서 임의로 우회하려 하면 안 되고, 서버가 응답 헤더에 Access-Control-Allow-Origin 을 포함해야 합니다.
API 호출 로직을 컴포넌트 안에 직접 작성하면 코드가 금방 복잡해집니다. api 폴더를 따로 만들어 서버 통신 함수를 분리해두면 재사용성과 테스트 용이성이 높아집니다.
// src/api/auth.ts
import axiosInstance from './axios';
import type { User } from '../models/user.model';
export const fetchUserInfo = async (): Promise<User> => {
const response = await axiosInstance.get('/users/me');
return response.data;
};
회원가입 같은 폼(Form) 화면을 만들 때 기본 onChange 와 제출 로직을 일일이 작성하면 코드가 매우 길어집니다. 이를 보완하기 위해 react-hook-form 라이브러리를 사용합니다.
npm install react-hook-form
useForm() 훅은 다음과 같은 유용한 기능을 제공합니다.
| 반환값 | 역할 |
|---|---|
register | 입력값을 추적하고 유효성 검사 규칙을 연결합니다 |
handleSubmit | 제출 이벤트를 관리합니다 |
formState.errors | 유효성 검사 에러 메시지를 확인합니다 |