본문 바로가기
개발/웹 개발

Day 27: CRUD API 구현

1. 오늘의 학습 목표

학습 목표: 대부분의 웹 애플리케이션의 기본이 되는 데이터 처리 기능인 CRUD(Create, Read, Update, Delete)를 이해합니다. 각 기능에 매칭되는 HTTP 메서드(POST, GET, PUT, DELETE)를 올바르게 사용하여 RESTful한 API를 직접 구현하는 방법을 배웁니다.

핵심 요약: CRUD는 데이터를 다루는 4가지 기본 연산입니다. 이 연산들을 적절한 HTTP 메서드와 연결하여 API를 설계하는 것은 백엔드 개발의 가장 핵심적인 기술 중 하나입니다.


2. 핵심 개념 파헤치기

2.1. CRUD란 무엇인가?

CRUD는 영속성을 갖는 데이터 저장소에서 데이터를 다루는 네 가지 기본 기능을 의미하는 약어입니다.

  • Create (생성): 새로운 데이터를 만듭니다 (회원 가입, 새 글 작성)
  • Read (읽기/조회): 기존 데이터를 읽어옵니다 (게시판 목록 보기, 내 정보 보기)
  • Update (수정): 기존 데이터를 변경합니다. (프로필 수정, 글 내용 바꾸기)
  • Delete (삭제): 기존 데이터를 삭제합니다. (회원 탈퇴, 글 삭제)

 

2.2. CRUD와 REST API HTTP 메서드 매핑

REST API에서는 CRUD 연산을 각각의 목적에 맞는 HTTP 메서드에 매핑하여 사용합니다.

Create → POST: 새로운 리소스를 생성합니다. 요청 본문(request body)에 생성할 데이터를 담아 보냅니다.

Read → GET: 리소스를 조회합니다. 데이터를 가져오기만 하므로 요청 본문은 사용하지 않습니다.

Update → PUT 또는 PATCH: 리소스를 수정합니다.

  • PUT: 리소스 전체를 교체하는 의미입니다.
  • PATCH: 리소스의 일부만 수정하는 의미입니다. (일반적으로 더 많이 사용됩니다.)

Delete → DELETE: 리소스를 삭제합니다.


3. 코드로 배우기

3.1. 구현 목표

실제 데이터베이스 대신, 메모리 상의 배열을 데이터 저장소로 사용하여 사용자 데이터를 관리하는 완전한 CRUD API를 구현합니다. (Day 26의 라우터-컨트롤러 구조를 유지합니다.)

 

3.2. [1단계: 데이터 준비 및 express.json() 미들웨어]

server.js에 POST와 PUT 요청의 본문(req.body)을 파싱하기 위한 필수 미들웨어인 express.json()을 추가합니다. 컨트롤러에서는 임시 데이터베이스 역할을 할 배열을 만듭니다.

//server.js
//...
//JSON 요청 본문을 파싱하기 위한 미들웨어
app.use(express.json()); 
//...

 

//controllers/userController.js

//임시 데이터베이스 (메모리 배열)
let users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];
//... 컨트롤러 함수들 ...

 

3.3. [2단계: 컨트롤러에 CRUD 로직 구현]

userController.js에 실제 CRUD 로직을 작성합니다.

//controllers/userController.js
//... (임시 데이터베이스) ...

// READ (모든 사용자 조회)
exports.getAllUsers = (req, res) => {
  res.status(200).json({ users });
};

//CREATE (새 사용자 생성)
exports.createUser = (req, res) => {
  const newUser = {
    id: users.length > 0 ? users[users.length - 1].id + 1 : 1,
    name: req.body.name,
  };
  users.push(newUser);
  res.status(201).json(newUser);
};

//UPDATE (사용자 정보 수정)
exports.updateUser = (req, res) => {
  const { id } = req.params;
  const userIndex = users.findIndex(u => u.id === parseInt(id));
  if (userIndex > -1) {
    users[userIndex].name = req.body.name;
    res.status(200).json(users[userIndex]);
  } else {
    res.status(404).send('User not found');
  }
};

// DELETE (사용자 삭제)
exports.deleteUser = (req, res) => {
  const { id } = req.params;
  const initialLength = users.length;
  users = users.filter(u => u.id !== parseInt(id));
  if (users.length < initialLength) {
    res.status(204).send(); // 204 No Content
  } else {
    res.status(404).send('User not found');
  }
};

 

3.4. [3단계: 라우터에 CRUD 경로 연결]

userRoutes.js에 PUT과 DELETE 메서드에 대한 라우트 추가합니다

//routes/userRoutes.js
//...
router
  .route('/:id')
  .get(userController.getUserById) //(READ One - 이전 시간에 구현)
  .put(userController.updateUser)   //UPDATE
  .delete(userController.deleteUser); //DELETE
//...

4. 전체 코드

controllers/userController.js

//임시 DB
let users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' },
];
let nextId = 4;

exports.getAllUsers = (req, res) => res.status(200).json({ users });

exports.getUserById = (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  user ? res.status(200).json(user) : res.status(404).send('User not found');
};

exports.createUser = (req, res) => {
  if (!req.body.name) return res.status(400).send('Name is required');
  const newUser = { id: nextId++, name: req.body.name };
  users.push(newUser);
  res.status(201).json(newUser);
};

exports.updateUser = (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (user) {
    user.name = req.body.name || user.name;
    res.status(200).json(user);
  } else {
    res.status(404).send('User not found');
  }
};

exports.deleteUser = (req, res) => {
  const index = users.findIndex(u => u.id === parseInt(req.params.id));
  if (index > -1) {
    users.splice(index, 1);
    res.status(204).send();
  } else {
    res.status(404).send('User not found');
  }
};

 

routes/userRoutes.js

const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');

router
  .route('/')
  .get(userController.getAllUsers)
  .post(userController.createUser);

router
  .route('/:id')
  .get(userController.getUserById)
  .put(userController.updateUser)
  .delete(userController.deleteUser);

module.exports = router;

 

server.js

const express = require('express');
const app = express();
const port = 3000;
const userRouter = require('./routes/userRoutes');

  //JSON body parser 미들웨어
app.use(express.json());

app.get('/', (req, res) => res.send('CRUD API is running...'));
app.use('/api/users', userRouter);

app.listen(port, () => {
  console.log(`Server listening at http://localhost:${port}`);
});