웹/프론트엔드

[Next.js] Next.js에서 API Routes + TypeORM 사용하기

이민훈 2022. 7. 17. 23:57

저는 회사에서 Next.js로 프론트 개발을 해오고 있는데,

 

백엔드 개발자가 바빠 일일이 API를 부탁하기 조금 어렵거나, 요구사항이 자주 바뀌는 API의 경우

 

Next.js의 API Routes 기능을 사용해 직접 API를 만들고 있습니다.

 

Create, Update, Delete의 경우 복잡한 비즈니스 로직이나, 트랜잭션 관리가 필요하고,

 

DB에 락을 걸기 때문에, Select쿼리만을 사용하는 통계 쿼리 정도만 직접 만들어 차트로 시각화하고 있습니다.

 

pages/api 폴더 안에 member.ts라는 파일을 만들고 아래와 같은 코드를 짜게 되면,

 

//pages/api/member.ts
export default (req, res) => {
  return res.status(200).json({ name: "John Doe" });
};

 

api/member라는 API를 호출 시 {name: "John Doe}라는 객체를 반환합니다.

 

이제 DB에 쿼리를 날려 정보를 가져온 후 가공하여 보내주는 API를 만들기 위해선,

 

사용하고있는 db에 접속이 가능하게끔 해주는 라이브러리들을 깔아주면 되겠죠?

 

저는 mysql을 사용 중이기 때문에, mysql과 ORM과 native query 등 다양하게 활용할 수 있는 TypeORM을 깔아 사용 중입니다.

 

https://www.npmjs.com/package/typeorm

 

typeorm

Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, MongoDB databases.. Latest version: 0.3.7, last published: 18 days ago. Start using typeorm in your project by running `npm i typeorm`. There

www.npmjs.com

 

npm install typeorm --save

npm install reflect-metadata --save

npm install @types/node --save-dev

npm install mysql --save

 

4개의 라이브러리를 깔고, ORM을 사용하려면

 

//src/typeorm/entity/member.ts
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm";

@Entity("member")
export class MemberEntity extends BaseEntity {
  @PrimaryGeneratedColumn()
  member_id: number;

  @Column("text", { nullable: true })
  email: string;
}

 

엔티티를 먼저 만들어줘야 합니다.

 

엔티티를 만드는 법은 공식문서에도 자세히 나와 있으니 참고하시면 될 것 같습니다.

 

//src/typeorm/dataSource.ts
import { DataSource } from "typeorm";
import { MemberEntity } from "./entity/member";
import "reflect-metadata";

export const AppDataSource = new DataSource({
  type: "mysql",
  host: process.env.NEXT_PUBLIC_DB_HOST,
  port: 3306,
  username: process.env.NEXT_PUBLIC_DB_USER_NAME,
  password: process.env.NEXT_PUBLIC_DB_PASSWORD,
  database: process.env.NEXT_PUBLIC_DB,
  synchronize: false,
  logging: false,
  entities: [MemberEntity],
  subscribers: [],
  migrations: [],
});

 

그런 다음 dataSource.ts파일을 만들어주는데, entities 속성에 만들었던 엔티티들을 넣어주시면 됩니다.

 

synchronize 옵션을 키게 될 경우 실제 DB에 현재 엔티티를 반영하게 되니 조심하셔야 합니다.

 

//pages/api/member.ts
import { AppDataSource } from "typeorm/dataSource";
import { MemberEntity } from "typeorm/entity/member";

export default async (req, res) => {
  if (!AppDataSource.isInitialized) {
    await AppDataSource.initialize();
    console.log("initialized");
  }

  const memberEntity = AppDataSource.getRepository(MemberEntity);
  const data = await memberEntity.find();

  return res.status(200).json({ members: data });
};

 

이제 다시 API 부분을 볼까요?

 

db에 연결하거나, 쿼리를 날리는 경우 비동기 작업이기 때문에 API를 async 함수로 만들고

 

비동기 작업 앞에 await 구문을 주어 비동기 작업이 끝날 때 까지 대기해줍니다.

 

import axios from "axios";
import { useEffect, useState } from "react";

const readMembers = async () => {
  const data = await axios.get("/api/member");
  return data;
};

const MembersComponent = () => {
  const [data, setData] = useState();

  useEffect(() => {
    readMembers()
      .then((res) => {
        setData(res.data.data);
        console.log(res.data.data);
      })
      .catch((err) => console.log(err));
  }, []);

  return (
    <div />
  );
};

export default MembersComponent;

 

이제 만든 API를 호출하고 사용하기만 하면 됩니다.

 

만약 native query를 사용하고 싶으시다면 entity를 만들 필요 없이 아래 코드처럼 db에 바로 쿼리를 날릴 수 있습니다.

 

import { AppDataSource } from "typeorm/dataSource";

export default async (req, res) => {
  if (!AppDataSource.isInitialized) {
    await AppDataSource.initialize();
    console.log("initialized");
  }

  const data = await AppDataSource.manager.query(`select * from member`);

  return res.status(200).json({ members: data });
};

 

간단한 쿼리나, 좀 더 빠르게 데이터를 가져와 프로토타입을 만들어보고 싶은 경우

 

Next.js의 API Routes 기능을 이용해 서버리스하게 API를 구성하는 것도 좋은 방법이라 생각됩니다.