Next.JS
환경에서 데이터를 가져오고, 처리하는 과정 중
oracledb
모듈을 직접 import
해서 api 단을 구성하고 있다.
그렇다면 이렇게 API 단과 통신하는 방식에 대해 두가지를 고려해볼 수 있다.
REST API
기반/api/data?action=???
을 통해 URL 문자열을 조합해 API를 호출하고 JSON
타입의 데이터를 받는다.tRPC
라우터를 통한 명시적 프로시저 정의zod
를 통한 입력값 검증 및 react-query
에 의존하는 데이터 fetching현재 개발 방식은 REST API
구조에 맞춰져있다.
아무래도 친숙한 방식이기도 하고, 단순 데이터 조회 쿼리(SELECT
)를 통해 가져온 데이터를
route
단을 통해 전달해주면 되기 때문.
현재 작성중인 코드 중 대표적인 예시는 다음과 같다.
// src/app/api/data/route.ts
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const action = searchParams.get('action');
switch (action) {
case 'GET_NOTIFICATION':
return await getNotification();
case 'GET_MODULE':
return await getModules();
case 'GET_SENDINFO':
return await getSendInfo();
default:
return NextResponse.json({ error: 'Invalid GET action' }, { status: 400 });
}
}
route.ts
는 get
Action에 대해 전달할 함수 로직을 담는다.// src/app/api/data/handler/sendInfoHandler.ts
import { executeQuery } from '@/lib/oracle';
import { NextRequest, NextResponse } from 'next/server';
import { getConnection } from '@/lib/oracle';
import oracledb from 'oracledb';
export async function getSendInfo() {
let connection;
try {
connection = await getConnection();
const result = await executeQuery(`
select
...
from ...
where 1 = 1
`);
return NextResponse.json(result);
} catch(error){
return NextResponse.json({ error: 'Failed to fetch notifications' }, { status: 500 });
} finally {
if (connection) {
try {
await connection.close();
} catch (err) {
console.error('Oracle DB 연결 종료 오류:', err);
}
}
}
}
oracledb
로 부터 데이터를 가져온다.// app/check/page.tsx
'use client';
import { useState, useEffect } from 'react';
...
const fetchSendInfo = async (): Promise<SendInfo[]> => {
const res = await axios.get('/api/data?action=GET_SENDINFO');
return res.data;
};
const CheckPage = () => {
const { data, isLoading, error, refetch } = useQuery<SendInfo[]>({
queryKey: ['sendInfo'],
queryFn: fetchSendInfo,
});
...
return (
<Container>
<Header>
...
<SearchButtonContainer>
<Button variant="outlined" color="primary" onClick={() => refetch()}>
조회
</Button>
</SearchButtonContainer>
</Header>
...
</Container>
);
};
export default CheckPage;
client
단에서는 axios.get
을 통해 요구 데이터를 요청하고 데이터를 fetching 해 받아온다.해당 방식은 POST
Method에서바인드 변수를 수동으로 관리하고,
별도의 타입 유효성검사 함수를 작성해야 한다는 번거로움이 있지만
아무래도 익숙한 프로토콜이며 개발방식이라는 점에서 러닝커브가 낮다는 장점이 있다.
tRPC
란 end-to-end
API 라이브러리 중 하나로,
Client ↔ Server 간 통신에서 런타임 타입 건증 없이 타입 안정성을 보장하는 프로토콜이다.
핵심 특징으론
1. 서버에서 정의한 API 스키마가 Client에 전파됨
2. Compile time에 type error를 검출함
이 있다.
서버 단에선 프로시저 기반 접근을 통해
export const appRouter = router({
getUser: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
// Oracle DB 조회
const user = await db.execute(`
SELECT ...
FROM ..
WHERE ...
`, { userId: input.id });
return user;
})
});
다음과 같이 접근할 수 있고
클라이언트 단에선
const UserProfile = () => {
const { data: user, isLoading } = trpc.getUser.useQuery({ id: '123' });
const createUser = trpc.createUser.useMutation();
if (isLoading) return <div>Loading...</div>;
return (
<UserCard>
<UserName>{user?.USER_NAME}</UserName>
<Email>{user?.EMAIL}</Email>
</UserCard>
);
};
react-query
훅 기반으로 API 함수를 호출해 데이터를 fetching하고, post할 수 있다.
당연히, react-query
가 제공하는 캐싱 및 최적화를 하나의 장점으로 가져갈 수 있다.
비교 항목 | tRPC | REST API |
---|---|---|
성능 | • JSON-RPC 기반 최적화된 페이로드 • React Query 내장 캐싱 • 추가 라이브러리 필요 (~50KB) • WebSocket 지원, Subscription 내장 | • HTTP 헤더 오버헤드 존재 • HTTP 캐싱, CDN, 브라우저 캐싱 지원 • 표준 fetch API 사용 • 별도 WebSocket 구현 필요 |
러닝 커브 | • TypeScript 필수 (고급 지식 필요) • 새로운 RPC 패러다임 • 제한적 학습 자료 • 신규 개발자 적응 시간 필요 | • TypeScript 선택사항 • 표준화된 HTTP methods 개념 • 풍부한 자료와 예제 • 대부분 개발자가 익숙함 |
유지보수성 | • 컴파일 타임 타입 체크 • 자동 타입 오류 감지 • 강제적 타입 일관성 • IDE 지원 안전한 리팩토링 • 타입 오류로 사전 방지 | • 런타임 검증 필요 • 수동 테스트 및 확인 • 개발자 규칙 의존 • 런타임 오류 발생 가능 • 수동 리팩토링 확인 |
개발 기간 | • 복잡한 초기 설정 • 스키마 정의로 빠른 API 개발 • 자동 타입 생성 • 타입 검증으로 테스트 간소화 • 코드 자체가 문서 역할 | • 간단한 초기 설정 • 개별 엔드포인트 구현 • 수동 타입 정의 • 전체 API 테스트 필요 • 별도 API 문서 작성 |
확장성 | • Monolithic 적합 • TypeScript 환경에 제한 • 제한적 써드파티 지원 • 중소규모 팀에 적합 • 복잡한 비즈니스 로직에 유리 | • MSA 분리에 유리 • 모든 플랫폼 지원 • 표준 HTTP로 광범위 지원 • 대규모 팀 협업에 유리 • 단순한 CRUD에 적합 |
종합 평가 | TypeScript 프로젝트, 복잡한 비즈니스 로직, 빠른 개발, 타입 안전성 중요한 경우 | 다양한 클라이언트 지원, 마이크로서비스, 대규모 팀, 표준 준수 필요한 경우 |
현재 작성중인 REST API
방식에서 tRPC
로 리팩토링을 진행해 볼 예정이다.
성능 상 큰 차이가 있진 않지만 애초에 react-query
기반으로 Client단을 작성해놔서 리팩토링 비용이 작기 때문이다.
특히 Next.js 만을 가져가는 Monolithic
구조이기 때문에 더더욱 그렇다.
개발하면서 서비스 구조와 아키텍처 패턴을 더 중요시 여기게 되는거 같다.