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 |