서버

[Node.js] API 만들기

키또카또 2026. 4. 8. 16:16

1. 초기 세팅

 

필요한 라이브러리를 깔아 준다.

pnpm i cors http-staus-code dotenv mysql2

- cors :cors 문제 해결

- http-status-code : http 코드를 상수로 사용

- dotenv : 환경변수를 process.env로 로드

- sql2 : MySQL 드라이버

 

 

환경 변수 저장을 위해 .gitignore .env 파일을 만들어 준다

#.gitignore
node_modules/

.env
.env.*


#.env
PORT = 3000 // PORT 기본 값= 3000

 

cf)

node_modules/는 실제 패키지 파일 내용들이 들어있다. 패키지 파일의 양이 많기도 하고, 패키지 매니저를 통해 팀원들과 사용하는 패키지를 맞출 수 있기 때문에 깃허브에는 올리지 않는다고 함!!

 

//index.js
import dotenv from "dotenv";
import express from "express";

dotenv.config();

const app = express();
const port = process.env.PORT;

app.use(cors()); //cors
app.use(express.static("public")); //정적 파일 접근
app.use(express.json()); //json 파싱
app.use(express.urlencoded({ extended: false })); //urlencoded 데이터를 단순 객체 문자열 형태로 데이터 파싱

app.get("/", (req, res) => { //get 요청을 여기서 받겠다.
  res.send("Hello World!");
});

app.listen(port, () => { //서버 실행
  console.log(`Example app listening on port ${port}`);
});

 

//db.config.js
import mysql from "mysql2/promise";
import dotenv from "dotenv";

dotenv.config();

export const pool = mysql.createPool({
  host: process.env.DB_HOST, 
  user: process.env.DB_USER, 
  port: process.env.DB_PORT,
  database: process.env.DB_NAME,
  password: process.env.DB_PASSWORD,
  waitForConnections: true,
  //커넥션이 모두 일하고 있을 때 새로운 작업이 들어왔다.
  //true - 큐에 넣어 기다리게 한다 , false - 에러를 반환한다.
  connectionLimit: 10, //동시에 최대 몇개의 커넥션을 가지는지
  queueLimit: 0, // 큐의 한도. 최대 몇 개까지 기다리게 할 것인가
    // 0은 무한정으로 기다리게 하겠다는 뜻이다. (한도를 두지 않음)
});

서버와 DB의 통신을 위해 커넥션 풀을 만들어 준다.

 

 

env 파일을 업데이트 해 준다.

DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD={비밀번호}
DB_NAME={데이터베이스_이름}
PORT=3000

 

 

2. API 설계

만들 API를 설계 해보자.

API를 설계할 때 필수적으로 고려해야 하는 것들은 다음과 같다.

1. 자원 중심 URL 설계

주소는 행위가 아닌 자원을 중심으로 설계하는 것이 좋다. (행위는 HTTP 메서드로 표현)

 

- 안 좋은 예

/getUserData

/deleteMyMagazine

-좋은 예

/users

/magazine/my

 

2. 알맞는 HTTP 메서드 사용

- GET : 데이터 조회

- POST : 새로운 데이터 생성

- PUT/PATCH : 데이터 수정 (전체, 일부)

- DELETE : 데이터 삭제

 

3. 일관된 요청, 응답 구조

기본 응답 구조를 이용해 틀(일관된 형태)을 유지하는 것이 좋다

//예시
{
  "success": true,            // 성공 여부 (true/false)
  "message": "요청 성공",       // 결과 메시지 (에러 발생 시 에러 원인 명시)
  "data": {                   // 실제 전달할 데이터 (실패 시 null)
    "id": 1,
    "title": "API 설계 방법"
  }
}

 

 

4. 명확한 HTTP 상태 코드 사용

  • 200 (OK): 정상적으로 처리됨 (주로 GET, PUT)
  • 201 (Created): 새로운 데이터가 정상적으로 생성됨 (주로 POST)
  • 400 (Bad Request): 클라이언트의 요청이 잘못됨 (예: 필수 파라미터 누락)
  • 401 (Unauthorized): 로그인 등 인증이 필요함
  • 403(Forbidden) : 권한 부족, IP 차단, 잘못된 URL
  • 404 (Not Found): 요청한 URL이나 데이터를 찾을 수 없음
  • 500 (Internal Server Error): 서버 내부 오류 (DB 연결 실패, 문법 에러 등)

참고) RESTful API

REST 아키텍처 스타일을 따르는 웹 API를 RESTful API라고 한다.

- REST란?

( REpresentational State Transfer ) 웹 상의 자원을 이름으로 구분하고 해당 자원의 상태를 주고 받는 모든 것을 의미한다. 

  • 자원 중심 : 모든 데이터를 고유한 URI로 식별한다.
  • HTTP 메소드 활용
  • 무상태성 : 서버는 클라이언트 상태를 저장하지 않으며, 각 요청은 독립적으로 처리된다.
  • 계층형 구조: 클라이언트는 서버와 직접 통신하는지, 중간 서버를 거치는지 알 수 없어 확장성이 높다.
  • 자기 설명성 : API 구조만 보고도 의도를 파악할 수 있어 이해가 쉽다.

REST API 설계 규칙

  • URI는 자원을 나타내는 명사를 사용한다
  • URI 경로에는 소문자를 사용한다
  • 하이픈은 URI의 가독성을 높이는데에 사용한다
  • 언더바(_)는 URI에 사용하지 않는다
  • HTTP 메소드를 활용한다
  • HTTP 응답 상태 코드를 활용한다
  • 계층 관계를 슬래시 (/)로 표현하고, URI의 끝에는 슬래시를 포함하지 않는다
  • 파일 확장자는 URI에 포함하지 않는다
  • 조회 시 쿼리를 활용한다 (ex. /users?page=2&count=10)
  • 리소스 간의 연관 관계가 있는 경우 '/리소스명/리소스ID/관계가 있는 다른 리소스명'으로 표현한다

 3. API 구현

Controller

import { StatusCodes } from "http-status-codes";
import { bodyToUser } from "../dtos/user.dto.js";
import { userSignUp } from "../services/user.service.js";

export const handleUserSignUp = async (req, res, next) => {
  const user = await userSignUp(bodyToUser(req.body));
  res.status(StatusCodes.OK).json({ result: user }); // 200
};

 

DTO

export const bodyToUser = (body) => {
  return {
    name: body.name,
    email: body.email,
    interests: body.interests || [],
    preferences: body.preferences,
  };
};

 

 

Service

import { responseFromUser } from "../user.dto.js";
import { addUser, getUser } from "../repositories/user.repository.js";

export const userSignUp = async (data) => {
  const existUserId = await addUser({
    name: data.name,
    email: data.email,
    interests: data.interests,
    preferences: data.preferences,
  });

  if (existUserId === null) {
    throw new Error("계정이 이미 존재하는 이메일입니다.");
  }

  const user = await getUser(existUserId);
  return responseFromUser({ user, preferences });
};

 

Repository

import { responseFromUser } from "../user.dto.js";
import { addUser, getUser } from "../repositories/user.repository.js";

export const userSignUp = async (data) => {
  const existUserId = await addUser({
    name: data.name,
    email: data.email,
    interests: data.interests,
    preferences: data.preferences,
  });

  if (existUserId === null) {
    throw new Error("계정이 이미 존재하는 이메일입니다.");
  }

  const user = await getUser(existUserId);
  return responseFromUser({ user, preferences });
};

 

 

 

'서버' 카테고리의 다른 글

[Node] 미들웨어  (0) 2026.04.08
[Node.js] ORM  (0) 2026.04.08
[Node.js] 스터디 4주차. Javascript  (0) 2026.04.01
[DB] 스터디 3주차 데이터베이스 기초  (0) 2026.03.25
[Node.js] 스터디 2주. Prisma 와 Middleware  (0) 2026.03.19