Search
Duplicate

[Nextjs]Serverless Function으로 Backend 통합하기

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
Next.js
Scrap
태그
9 more properties

너무 많이 분리된 서비스들

기존 서비스의 구성은 아래와 같았다. 구매 페이지인 고객용Client와 사장님용 Client로 Frontend Client가 나눠져 있었고, Notion API를 호출해주는 Backend 서버가 존재했다. 모든 개발을 혼자 담당하고 있는 상황에서 분산된 서비스들을 관리하면서 시간이 많이 낭비되었다.
특히 Express.js에 익숙하지 않아 end point가 늘어날수록 코드가 너무 보기 어려워졌다. 확장성을 고려한다면 개선이 필요한 시점이였다.

Next.js의 Route Handlers 활용하기

현재 서비스 특성상 Serverless Function만으로도 충분히 구현 가능하다고 판단했다. Next.js의 Route Handlers를 활용하면 손쉽게 구현할 수 있었다. Vercel을 통해 배포하고 있는 상황에서는 end point만 만들어준다면 다른 작업이 필요없는 상황이였다.
추가로 직접 백엔드 서버를 빌드하고 배포할때보다 빠른 배포, 테스트가 가능했고 환경변수 추가 및 삭제에서도 큰 이점을 가지고 있었다.

Express.js로 구현된 기능을 Next.js로 이전

가볍게 공식문서를 통해 공부한 후 바로 구현해보기로 하였다. 아래 코드처럼 복잡하게 구현된 Express.js 코드를 Next.js로 이전할 시간이다.
< Express.js 로 구현된 복잡하게 나뉜 route>
< Next.js로 폴더단위로 깔끔하게 나뉜 route>
우선 app/api 밑에 밀요한 route들을 폴더로 만들고 각 폴더에 route.ts 파일을 생성해 주었다. 자세한 설명은 아래 문서를 참고하자
각 파일에 필요한 기능들을 구현 하면 된다. 예를들어 auth와 관련된 기능들은 auth/route.ts에 모아두었다.
async function signin(id: string, pw: string) { const DATABASE_ID = process.env.USER_DATABASE_ID; const notion = new Client({ auth: process.env.NOTION_KEY }); if (!DATABASE_ID) { ... } return notion.databases.query({ database_id: DATABASE_ID, filter: { and: [ { property: 'id', rich_text: { equals: id, }, }, { property: 'pw', rich_text: { equals: pw, }, }, ], }, }); } /** * signin api * @param request * @returns 'success' or 'fail' */ export async function POST(request: NextRequest) { const { id, pw } = await request.json(); try { const result = await signin(id, pw); if (result.results.length === 0) { return NextResponse.json('fail', { status: 404 }); } else { return NextResponse.json('success', { status: 200 }); } } catch (error) { ... } }
TypeScript
복사
다른 Framework를 학습할 필요 없이 손쉽게 기능을 구현할 수 있었다. Request, Response에 대한 타입이 잘 정의되어 있어서 크게 어려움 없이 개발할 수 있었다.

Vercel Serverless Function

모든 기능을 구현하고 배포하면 Vercel Deployment Summary에서 각 end point들이 정상적으로 등록된 것을 확인할 수 있다. Postman 등을 활용해서 테스트해보면 정상적으로 동작하는 것을 확인할 수 있다.

CORS 문제 해결하기

Postman으로 테스트할 땐 확인하지 못했지만, 다른 웹 서비스에서 요청을 보낼 때 CORS에러가 발생하는 것을 확인할 수 있었다. webhook에 대한 요청은 브라우저에서 보내는 요청이 아니라 괜찮았지만, 사장님용 Client에서 보내는 요청이 문제였다.

1. 공식문서에서 제공한 방법 (실패)

Vercel을 통해 배포한 경우, Serverless Function에 대한 해결책이 공식문서에도 정리되어 있었다. 아래 코드를 next.config.js 파일에 추가하는 방법이였다.
module.exports = { async headers() { return [ { // matching all API routes source: "/api/:path*", headers: [ { key: "Access-Control-Allow-Credentials", value: "true" }, { key: "Access-Control-Allow-Origin", value: "*" }, { key: "Access-Control-Allow-Methods", value: "GET,OPTIONS,PATCH,DELETE,POST,PUT" }, { key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }, ] } ] } };
TypeScript
복사
바로 테스트를 해보았지만, 아래와 같은 문제가 발생했다. 기존에 보던 CORS 에러 메세지와는 조금 다른 부분이 있었다. It does not have HTTP ok status라는 에러메세지를 확인할 수 있었다.
아래 사진처럼 Origin이 동일하게 되어있더라도 응답이 막히는 것을 확인할 수 있었다.
아무래도 preflight 요청에 대해 204 응답이 오지 않는 것이 문제인 것 같았다. OPTIONS end point에 대해 강제로 204 응답을 보내주니 관련 에러가 없어졌지만, 모든 OPTIONS 요청에 대해 강제로 ok 응답을 보내다 보니 조금 위험하다는 생각을 해서 다른 해결책을 찾아보았다.

2. Next.js Discussions에서 제공한 방법(성공!)

레퍼런스가 별로 없어서 해결책을 찾기 어려웠지만, next.js 깃허브의 discussions에서 해결책을 찾을 수 있었다. 아래 코드처럼 cors library를 활용해 조금 더 안전하게 구현할 수 있었다.

최종 결과

위 방법처럼 OPTIONS에 대한 end point를 구현하지 않아도 되는 방법을 찾고 싶었는데 아직 좋은 해결책을 발견하지 못했다. 추후에 관련해서 다른 문서를 찾게 된다면 다시 도전해보려고 한다.
최종적으로 아래 형태로 구현이 완료되었고, 구현 결과 다양한 이점을 얻을 수 있었다. 혼자 개발하는 업무 특성상 서비스를 통합해서 관리할 수 있다는 것이 큰 장점이였다.
개발 시간 단축
배포하는 서비스(express.js server) 1개 단축

Reference