객체 뿐만 아니라 모든 요소의 타입 정의가 가능하다. 유니온 타입이나, 컴포넌트의 props, state, 이벤트 등의 타입도 정의가 가능해서 React 환경 개발에서는 type을 주로 사용한다.
**유니온 타입 : 여러 타입 중 하나를 선택하는 것
type Info = { phoneNum : string | number; } //유니온 타입 예시
interface와 type은 확장 방식도 다르다.
//interface의 경우
interface Member {
name:string;
age: number
}
interface Student extends Member {
stuId:number;
major:string;
}
//type 의 경우
type Member = {
name:string;
age: number
}
Type Teacher = Member & {
tcId:number;
major:string;
}
}
Next.js는 서버사이드랜더링과 정적 페이지 생성을 제공하는 React프레임워크이다 리엑트를 기반으로 동작하기 때문에 리엑트에서 사용되는 기본적인 상태관리나 스타일 관련 라이브러리, 코드 번들러, babel 등을 동일하게 사용할 수 있어 리액트 개발자들이 쉽게 사용할 수 있다.
Next에 대해 알기 전에는(어깨너머로 알 때는) 서버사이드랜더링을 써야하는 이유에 대해 이해하지 못했다. 어차피 유저 인터렉션 많은 페이지에서는 큰 의미가 없는게 아닌가 라고 생각했기 때문이다.
그러나 실제로 사용해보면서, 단순히 서버에서 페이지를 렌더링하는 것 이상의 다양한 성능 최적화와 기본 설정이 제공된다는 사실을 알게 되었다.
서버-클라이언트 구조가 사용자 경험에 긍정적인 영향을 미치는지를 경험하면서, 왜 많은 회사에서 Next를 사용하게 되었는지 이해했다.
내가 알게된 내용에 대해 요약하고, 코드에 적용한 것을 아래에 정리해봤다.
1.서버사이드 랜더링과 정적 페이지 생성 데이터가 실시간으로 업데이트 되지 않는 경우, 매번 계속 페이지를 생성하는 것은 비효율적이다. 이럴때
ISR 을 사용하면 데이터가 갱신될 때만 페이지가 새로 생성하거나,
SSG를 통해 한 번 생성된 페이지를 정적으로 제공해서 서비스의 성능을 높일 수 있다.
또한 SSR이 기본적으로 제공되기 때문에 서버에서 미리 html을 랜더링해와 초기 로딩속도를 줄일 수 있다. -> SEO최적화에 도움됨.
** ISR과 SSG의 차이**
- ISR (incremental static regeneration):특정 주기(revalidate)로 페이지를 갱신함. 특정 주기마다 데이터가 업데이트 되는 경우 적합하다. e.g) 뉴스 등
- SSG (static site generation) : 빌드 타임에 모든 페이지를 정적으로 미리 생성한다. 페이지가 빌드될 때 한 번 생성하고, 그 이후에는 동일한 정적 파일을 계속 제공한다. 데이터를 업데이트하기위해서는 사이트 전체를 다시 빌드해야한다. 자주 변하지 않는 서비스에 적합. e.g)블로그나 기술 문서 등
2. 파일 기반 라우팅 Next.js의 파일 기반 라우팅(App Routing)은 파일 경로만 설정하면 자동으로 URL이 생성되기 때문에 기존 리액트에 비해 프로젝트의 구조가 훨씬 간결해진다. 폴더명이 url이 되는 형식.
e.g) App/News/Page.js 의 구조일 때 myappUrl/News 로 접속하면 news 폴더 안의 pages.js 내용이 랜더링 된다.
3. 성능 최적화 Next.js는 기본적으로 코드 스플리팅과 이미지 최적화를 제공하기 때문에 로딩 속도 최적화가 가능하다. 단순히 기능을 제공할 뿐만 아니라, 유저 사용성을 높이기 위해서는 next를 쓰는 것이 좋다. 물론 기본 리엑트에서도 스플리팅과 이미지 최적화 모두 가능하지만 설정이 필요하다.
백오피스 시스템을 개발하면서 복잡한 기능 구현과 데이터 처리가 큰 도전과제였다. 특히 대용량 데이터를 효율적으로 관리하고, 비즈니스 로직을 유연하게 확장하며, 일관된 방식으로 유지해야 했다.
이 과정에서 데이터 요청이 많고, 중복 요청을 유연하게 처리하기 위하여 React Query(Tanstack Query)를 사용하게 되었다. 리액트 쿼리를 통해 데이터를 빠르게 가져오고, 캐시를 통해 중복 요청을 줄이면서 서비스를 최적화할 수 있었다.
이번 글에서는 백오피스 시스템에서 복잡한 데이터 처리 기능을 효율적으로 관리하고, 성능을 최적화 했는지에 대해 공유하겠다!
코드의 일관성과 유지보수성을 높이기 위해, 쿼리 키와 API 호출 로직을 별도로 분리했다. 이를 통해 팀원들과 코드 스타일을 맞추고, 유지보수가 쉬운 코드를 작성할 수 있었다. 또한 Axios를 적용하여 HTTP 요청을 간결하게 처리하고, 응답 데이터를 자동으로 JSON으로 파싱하여 쉽게 다루도록 했다. (Axios를 사용하면 JSON으로 응답이 돌아와서 추가 처리가 필요 없다!)
** 예시를 들기 위해 작성한 코드 입니다 ~.~ 실제 업무용 코드 x
쿼리 키 작성
const employeeKey = {
employee: ['employee'] as const,
employeeWithID: (id: string) => ['employee', { id }] as const,
};
쿼리 키를 따로 파일로 분리하면 팀원들이 중복된 키를 작성하여 잘못된 데이터를 가져오거나 전송하는 문제를 방지할 수 있다. 유지보수 시에도 편리하므로 쿼리 키는 별도로 관리하는 것이 좋다.
커스텀 훅 작성
import { useQuery } from 'react-query';
import axios from 'axios';
import { employeeKey } from "./employeeQueryKey";
// API 호출 함수 (전체 또는 개별 사원 정보 패칭)
const fetchEmployees = (id?: string) =>
axios.get(`/api/employees${id ? `/${id}` : ''}`).then(res => res.data); //api 주소를 여기에 넣으면 됨
// useEmployeeInfo 커스텀 훅
const useEmployeeInfo = (employeeId?: string) => {
const { data, isError, isLoading, error } = useQuery({
queryKey: employeeId ? employeeKey.employeeWithID(employeeId) : employeeKey.employee,
queryFn: () => fetchEmployees(employeeId), // employeeId가 없으면 undefined 전달
staleTime: 1000 * 60 * 10, //캐시 지속 시간
});
return {
fetchData: data, // 패칭된 데이터
isFetchDataLoading: isLoading, // 로딩 상태
isFetchDataError: isError, // 에러 상태
fetchError: error, // 에러 정보
};
};
이제 컴포넌트에서 위에서 구현한 커스텀 훅을 통해 데이터를 가져오자
const EmployeeInfo = ({ employeeId }: { employeeId: string }) => {
// 사원 ID를 넘겨서 특정 사원의 정보를 패칭
const { fetchData, isFetchDataLoading, isFetchDataError, fetchError } = useEmployeeInfo(employeeId);
if (isFetchDataLoading) return <div>Loading employee data...</div>;
if (isFetchDataError) return <div>Error loading data: {fetchError?.message}</div>;
return (
<div>
<h1>Employee Name: {fetchData.name}</h1>
<p>Position: {fetchData.position}</p>
<p>Department: {fetchData.department}</p>
</div>
);
};
여기서 useQuery안에 들어가는 옵션 등을 props로 받아온다면 더 높은 수준의 쿼리문을 작성할 수 있을 것이다 ~,~ 그 부분에 대해서는 추후 서술하겠음!