Next js로 API 만들어 사용하기(API routes)
Next js를 오랜만에 사용하려고 보니 이제 nextjs에서 바로 express로 추가해서 api 서버를 만드는 게 가능하다는 것을 알았습니다. 예전 같으면 express 서버를 따로 배포해서 사용했어야 됐지만 이제는 간단히 nextjs에 추가해서 vercel로 배포하면 되더라고요.
오늘 해볼 것은 nextjs api routes를 사용해서 sqlite 데이터 베이스에서 가저온 데이터를 response로 보내는 것을 해볼까 합니다.
Nextjs API routes는 무엇인가?
Nextjs는 API를 바로 추가 할 수 있는 기능을 API routes라고 부르고 있다. 이 기능은 pages/api 디렉토리 안에 만들어진 파일들이 rest api path가 된다
넥스트 js를 시작할 때 가장 먼저 해야 하는 부분인 nextjs를 설치하도록 하자
아래와 같이 커맨드라인에 입력해 보자
npx create-next-app@latest
# or
yarn create next-app
만든 디렉토리에서 nextjs 프로젝트를 열면 아래와 같이 구성되어 있는 것을 볼 수 있다. pages 바로 밑에 보면 api가 보이는데 여기에 API를 추가할 수 있다.
nextjs를 사용해본 사람은 알겠지만 pages 디렉토리 구조가 url이 되는 것처럼 api도 동일하게 작동한다. hello.ts는 api 버전에 helloworld로 간단하게 json을 리턴한다
이제 우리가 직접 한번 api를 추가해 보자 먼저 api 디렉터리에 pokemon.ts 파일을 추가해보자. 앞에서 설명했던 것처럼 api/pokemon이 api에 path가 되는 것이다.
먼저 필요한 type을 import 하자.
import type { NextApiRequest, NextApiResponse } from 'next'
일단 간단하게 아래처럼 api를 만들 수 있습니다. Function이름은 꼭 handler가 되야 할 필요는 없습니다. 원하는 이해하기 쉬운 이름을 사용 하면 됩니다.
export default function handler (
req: NextApiRequest,
res: NextApiResponse<Data>) {
res.status(200).json({ pokemon: {
name: '꼬부기'
} })
}
만약에 api/pokemons/id가 필요하다면 어떻게 만들어야 할까요?
일단 /pokemons과 /pokemons/id가 둘 다 필요하다면 디렉터리를 만들어서 하는 것이 좋습니다.
아래처럼 pokemons 디렉터리에 index.ts를 만들어서 api/pokemon api를 관리하고 [id] 디렉터리를 만들어서 api/pokemons/id api를 만들어 줍니다.
id를 사용하기 위해서는 query에서 가져와야 합니다. 만약 api가 api/pokemons/100 라면 name을 포켓몬 아이디 100으로 출력할 것입니다.
export default function handler (
req: NextApiRequest,
res: NextApiResponse<Data>) {
const { id } = req.query
res.status(200).json({ pokemon: {
name: '포켓몬 아이디 ' + id
} })
}
데이터 베이스 데이터 API routes를 통해서 가져오기
많은 인터넷 예시들이 그냥 하드코드된 json을 리턴 했는데 여기서는 데이터 베이스에서 데이터를 가져와서 리턴해 보겠다. sqlite를 통해서 진행하려고 한다. Mysql 같은 다른 데이터 베이스들도 커넥션만 만들어서 사용하면 별반 차이가 없다.
sqlite 설치
yarn add sqlite sqlite3
이제 db가 될 sqlite 파일을 만들어 보자
touch db.sqlite
migration을 통해서 포켓몬 테이블을 추가 해보자.
migration을 실행 할 스크립트를 만들어서 아래와 같이 입력 해주자.
-- Up
CREATE TABLE Pokemon (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
);
INSERT INTO Pokemon (name) values('Pikachu');
INSERT INTO Pokemon (name) values('Eevee');
INSERT INTO Pokemon (name) values('Snorlax');
INSERT INTO Pokemon (name) values('Ditto');
-- Down
DROP TABLE Pokemon;
위에 migration을 설명 하자면 CREATE TABLE로 Pokemon 테이블을 만들고 테이블을 세팅 해준다. 다음 INSERT INTO Pokemon으로 샘플 pokemon 데이터를 추가해준다.
DB initialize
sqlite에 migration을 돌리기 위해서는 아래와 같이 db를 open 함수로 열어주고 db.migrate을 실행 시키면 된다. migration이 성공적으로 된걸 확인 하려면 pokemons를 console.log 해보면 알 수 있다.
const sqlite3 = require('sqlite3')
const sqlite = require('sqlite')
async function initialize() {
const db = await sqlite.open({
filename: './db.sqlite',
driver: sqlite3.Database
})
await db.migrate()
// const pokemons = await db.all('SELECT * FROM pokemon')
}
initialize()
이제 다시 pokemons/id index.ts 파일로 돌아와서 데이터 베이스와 연결 해봅시다.
// sqlite 연결 하기 위한 라이브러리 import
import sqlite3 from 'sqlite3'
import { open } from 'sqlite'
import type { NextApiRequest, NextApiResponse } from 'next'
나머지는 pokemon id로 매치 하는 포켓몬 찾아서 러턴 해주기.
type Data = {
id: number,
name: string
}
export default async function getPokemonById (
req: NextApiRequest,
res: NextApiResponse<Data>) {
// pokemons/id id 부분
const { id } = req.query
const db = await open({
filename: './db.sqlite' ,
driver: sqlite3.Database
})
const pokemons = await db.all('select * from pokemon')
res.status(200).json(pokemons.find(pokemon => String(pokemon.id) === String(id)))
}
nextjs frontend에서는 어떻게 이 API들을 사용 할 수 있을까 ?
앞에서 만들어 놓은 api를 사용하는 방법은 간단하다 frontend에서 호출해주기만 하면 됩니다.
앞에 만들어 놓은 api를 다음과 같이 호출 할 수 있습니다. fetch할 때에는 반드시 absolute url을 사용 해야합니다.
const fetchPokemon = async () => {
const res = await fetch('http://localhost:3000/api/pokemons/1')
const data = await res.json()
return data
}
이제 Nextjs에 제공하는 API routes가 어떤 것인지 어떻게 사용하는지 설명해 보았습니다. 더 궁금한 점이 있거나 어려운 부분이 있다면 댓글로 남겨주세요.