Skip to main content
Summary

서버가 기억한다는 말의 뜻

지금까지 서버는 요청을 받으면 바로 응답했습니다. 상품 목록을 보내거나, 폼 내용을 읽거나, JSON으로 결과를 돌려주었습니다. 그런데 실제 서비스는 한 가지 일을 더 해야 합니다. 받은 내용을 잠깐이라도 기억해야 합니다.

예를 들어 예약 접수 서버를 만든다고 생각해 보겠습니다.

손님이 이름과 날짜를 보내면 서버는 이렇게 해야 합니다.

  1. 예약 내용을 받는다.

  2. 예약 목록에 추가한다.

  3. 나중에 다시 목록을 보여 준다.

이때 "예약 목록에 추가한다"가 바로 데이터 저장입니다. 처음부터 거창한 데이터베이스를 쓰지 않아도 됩니다. JavaScript 배열에 담아 볼 수 있습니다. 배열은 책상 위 메모지와 비슷합니다. 적고 읽기는 편하지만, 책상 불을 끄고 나가면 사라질 수 있습니다. 서버에서는 이 상황이 서버를 끄면 데이터가 사라진다는 뜻입니다.

이번 장에서는 먼저 배열에 데이터를 저장해 보고, 그 한계를 직접 확인합니다. 그다음 JSON 파일에 데이터를 적어 두는 방식으로 조금 더 오래 남기는 방법을 배웁니다.

데이터를 잠깐 보관하고 다시 꺼내기AI 생성 이미지

배열에 예약 목록 저장하기

이번에 만들 것은 간단한 예약 목록 API입니다. 브라우저나 API 테스트 도구가 서버에 예약 정보를 보내면, 서버가 배열에 저장합니다. 그리고 다시 목록을 요청하면 지금까지 저장된 예약을 JSON으로 보여 줍니다.

먼저 어떤 파일을 열지 정합니다.

VS Code에서 express-practice 폴더를 열고, app.js 파일을 엽니다. 이전 장의 코드가 남아 있다면 잠시 다른 곳에 복사해 두거나, 이번 실습용으로 app.js를 새로 정리해도 괜찮습니다.

처음에는 Express를 불러오고 앱을 만듭니다.

const express = require('express');
const app = express();

첫 줄은 Express 도구를 가져오는 코드입니다. 두 번째 줄은 그 도구로 서버 앱을 하나 만드는 코드입니다.

JSON 요청 본문을 읽기 위해 미들웨어도 등록합니다.

app.use(express.json());

이 줄이 있어야 클라이언트가 보낸 JSON 데이터를 req.body에서 읽을 수 있습니다. 서버가 손님이 건넨 신청서를 글자로만 보는 것이 아니라, 이름과 날짜가 적힌 칸으로 나누어 읽게 해 주는 과정이라고 생각하면 됩니다.

이제 예약을 담을 배열을 만듭니다.

const reservations = [];

이 배열은 서버가 켜져 있는 동안만 살아 있습니다. 아직 파일이나 데이터베이스에 저장하지 않았습니다.

예약 목록을 조회하는 주소를 만듭니다.

app.get('/api/reservations', (req, res) => {
  res.json(reservations);
});

GET /api/reservations 요청이 오면 현재 배열 안에 들어 있는 예약 목록을 JSON으로 돌려줍니다.

이번에는 새 예약을 받는 주소를 만듭니다.

app.post('/api/reservations', (req, res) => {
  const { name, date } = req.body;

  if (!name || !date) {
    return res.status(400).json({ message: '이름과 날짜가 필요합니다.' });
  }

  const newReservation = {
    id: reservations.length + 1,
    name,
    date
  };

  reservations.push(newReservation);

  res.status(201).json(newReservation);
});

위 코드는 몇 단계로 나누어 읽으면 덜 어렵습니다.

const { name, date } = req.body;는 요청 본문에서 name과 date를 꺼냅니다. 예약 신청서에서 이름 칸과 날짜 칸을 읽는 과정입니다.

if (!name || !date)는 둘 중 하나라도 비어 있는지 확인합니다. 비어 있다면 400 상태 코드와 함께 안내 메시지를 보냅니다. 400은 "요청 내용이 부족합니다"에 가까운 신호입니다.

newReservation은 새 예약 객체입니다. 여기서는 간단히 배열 길이에 1을 더해 id를 붙였습니다. 학습용으로는 충분하지만, 실제 서비스에서는 여러 사용자가 동시에 요청할 수 있으므로 더 안전한 방식이 필요합니다.

reservations.push(newReservation)는 배열에 새 예약을 추가합니다. 마지막으로 201 상태 코드와 함께 방금 만든 예약을 응답합니다. 201은 "새 데이터가 만들어졌습니다"라는 뜻으로 자주 쓰입니다.

마지막으로 서버를 켭니다.

app.listen(3000, () => {
  console.log('예약 서버가 http://localhost:3000 에서 실행 중입니다.');
});

터미널에서 다음 명령을 입력합니다.

node app.js

성공하면 터미널에 다음과 비슷한 문장이 보입니다.

예약 서버가 http://localhost:3000 에서 실행 중입니다.

브라우저에서 http://localhost:3000/api/reservations로 접속하면 처음에는 빈 배열이 보입니다.

[]

아직 예약을 넣지 않았기 때문입니다.

서버를 끄면 배열 데이터는 사라진다

이제 예약을 하나 추가해 보겠습니다. POST 요청은 브라우저 주소창만으로 보내기 어렵습니다. 이전 장처럼 Postman, Insomnia, VS Code REST Client 같은 도구를 사용해도 좋습니다.

요청은 이렇게 보냅니다.

  • 요청 방식: POST

  • 주소: http://localhost:3000/api/reservations

  • 본문 형식: JSON

  • 본문 내용:

{
  "name": "민지",
  "date": "2026-07-01"
}

성공하면 이런 응답이 보입니다.

{
  "id": 1,
  "name": "민지",
  "date": "2026-07-01"
}

다시 브라우저에서 http://localhost:3000/api/reservations를 열면 배열 안에 예약이 들어 있습니다.

[
  {
    "id": 1,
    "name": "민지",
    "date": "2026-07-01"
  }
]

여기까지 보면 서버가 데이터를 기억하는 것처럼 보입니다. 하지만 이 기억은 서버가 켜져 있는 동안만 유지됩니다.

터미널에서 Ctrl + C를 눌러 서버를 끕니다. 그리고 다시 실행합니다.

node app.js

다시 http://localhost:3000/api/reservations에 접속해 보세요.

[]

예약이 사라졌다면 실습은 성공입니다. 이상한 일이 아닙니다. 배열은 서버 프로그램의 메모리 안에 있었고, 서버 프로그램이 종료되면서 함께 사라진 것입니다.

메모리 저장은 연습이나 임시 처리에는 편합니다. 하지만 문의 내역, 결제 기록, 회원 정보, 예약 목록처럼 나중에 다시 꺼내야 하는 데이터에는 부족합니다.

JSON 파일에 저장해 보기

이번에는 예약 목록을 JSON 파일에 적어 보겠습니다. JSON 파일은 작은 노트와 비슷합니다. 서버가 꺼져도 파일은 컴퓨터 안에 남아 있으므로, 다시 켰을 때 읽어 올 수 있습니다.

다만 이 방식도 완성된 해결책은 아닙니다. 여러 사람이 동시에 쓰는 상황에는 약하고, 파일이 깨질 수도 있습니다. 그래도 데이터 저장의 흐름을 이해하기에는 좋은 중간 단계입니다.

먼저 프로젝트 폴더 안에 data 폴더를 만들고, 그 안에 reservations.json 파일을 만듭니다.

파일 위치는 이렇게 됩니다.

express-practice/
  app.js
  data/
    reservations.json

reservations.json 파일에는 처음 내용으로 빈 배열을 적습니다.

[]

이 파일을 만들지 않고 서버를 실행하면, 뒤에서 파일을 읽을 때 오류가 날 수 있습니다. 그러니 data 폴더와 reservations.json 파일이 실제로 만들어졌는지 한 번 확인해 주세요.

이제 app.js 위쪽에 파일을 다루는 도구를 불러옵니다.

const fs = require('fs');
const path = require('path');

fs는 파일을 읽고 쓰는 Node.js 기본 도구입니다. path는 운영체제에 맞게 파일 경로를 안전하게 이어 주는 도구입니다. Windows와 macOS는 경로 표시 방식이 조금 다를 수 있으므로, path.join()을 사용하면 실수를 줄일 수 있습니다.

파일 위치를 변수로 만들어 둡니다.

const dataFilePath = path.join(__dirname, 'data', 'reservations.json');

__dirname은 현재 app.js 파일이 있는 폴더를 뜻합니다. 여기에 data와 reservations.json을 이어 붙여서 파일의 정확한 위치를 만듭니다.

이번에는 파일에서 예약 목록을 읽는 함수를 만듭니다.

function readReservations() {
  const fileData = fs.readFileSync(dataFilePath, 'utf-8');
  return JSON.parse(fileData);
}

첫 줄은 JSON 파일의 내용을 글자로 읽습니다. 두 번째 줄은 그 글자를 JavaScript 배열로 바꿉니다. 파일 안의 []는 글자일 뿐이지만, JSON.parse()를 지나면 배열로 사용할 수 있습니다.

반대로 배열을 파일에 저장하는 함수도 만듭니다.

function saveReservations(reservations) {
  const fileData = JSON.stringify(reservations, null, 2);
  fs.writeFileSync(dataFilePath, fileData, 'utf-8');
}

JSON.stringify()는 JavaScript 배열이나 객체를 JSON 글자로 바꿉니다. null, 2는 파일을 사람이 읽기 좋게 들여쓰기해 달라는 뜻입니다. 그다음 fs.writeFileSync()가 파일에 내용을 씁니다.

여기서 사용한 readFileSync()와 writeFileSync()는 파일 작업이 끝날 때까지 다음 일을 잠시 기다리는 방식입니다. 지금처럼 흐름을 배우는 작은 예제에서는 이해하기 쉽지만, 실제 서비스에서는 더 안전하고 효율적인 방식이나 데이터베이스를 사용합니다.

이제 처음에 만들었던 배열 변수 const reservations = [];를 계속 들고 있을 필요가 없습니다. 조회 라우트에서 파일을 읽도록 바꿉니다.

app.get('/api/reservations', (req, res) => {
  const reservations = readReservations();
  res.json(reservations);
});

요청이 올 때마다 파일에서 최신 예약 목록을 읽고 응답합니다.

예약 추가 라우트도 파일을 읽고, 배열에 추가한 뒤, 다시 파일에 저장하도록 바꿉니다.

app.post('/api/reservations', (req, res) => {
  const { name, date } = req.body;

  if (!name || !date) {
    return res.status(400).json({ message: '이름과 날짜가 필요합니다.' });
  }

  const reservations = readReservations();

  const newReservation = {
    id: reservations.length + 1,
    name,
    date
  };

  reservations.push(newReservation);
  saveReservations(reservations);

  res.status(201).json(newReservation);
});

흐름은 단순합니다.

  1. 파일에서 기존 예약 목록을 읽습니다.

  2. 새 예약을 만듭니다.

  3. 배열에 새 예약을 추가합니다.

  4. 바뀐 배열을 다시 파일에 저장합니다.

  5. 새 예약을 응답으로 보냅니다.

파일 저장 방식으로 바꾼 뒤에도 맨 아래의 서버 실행 코드는 그대로 필요합니다.

app.listen(3000, () => {
  console.log('예약 서버가 http://localhost:3000 에서 실행 중입니다.');
});

서버를 다시 실행합니다.

node app.js

그리고 다시 POST /api/reservations로 예약을 하나 추가합니다.

{
  "name": "도윤",
  "date": "2026-07-03"
}

성공하면 data/reservations.json 파일 내용도 함께 바뀝니다.

[
  {
    "id": 1,
    "name": "도윤",
    "date": "2026-07-03"
  }
]

이제 서버를 Ctrl + C로 끄고 다시 켜 보세요. 그 뒤 GET /api/reservations를 요청하면 데이터가 남아 있어야 합니다. 파일에 적어 두었기 때문입니다.

파일 저장은 왜 임시 방법일까

JSON 파일 저장은 배열보다 한 걸음 나아간 방식입니다. 서버가 꺼져도 데이터가 남습니다. 하지만 실제 서비스의 중심 저장소로 쓰기에는 여러 위험이 있습니다.

첫째, 여러 요청이 동시에 들어오면 문제가 생길 수 있습니다. 두 사람이 거의 같은 순간에 예약을 넣는다고 생각해 보겠습니다. 서버가 파일을 읽고 쓰는 타이밍이 겹치면, 한 사람의 예약이 덮어써질 수 있습니다.

둘째, 데이터가 많아질수록 불편해집니다. 예약이 몇 개일 때는 파일 전체를 읽어도 괜찮습니다. 하지만 수만 개, 수십만 개가 되면 매번 파일 전체를 읽고 다시 쓰는 방식은 부담이 됩니다.

셋째, 원하는 데이터를 찾기 어렵습니다. "7월 3일 예약만 보여 줘", "이메일이 같은 예약을 찾아 줘", "최근 예약 10개만 보여 줘" 같은 작업이 많아지면 파일 하나만으로는 관리가 복잡해집니다.

그래서 실제 서비스에서는 보통 데이터베이스를 사용합니다.

데이터베이스는 엑셀 파일 하나를 여러 사람이 동시에 안전하게 쓰기 위한 전용 시스템에 가깝습니다. 누가 언제 어떤 줄을 추가하는지 관리하고, 필요한 줄만 빠르게 찾고, 갑작스러운 충돌을 줄여 줍니다.

많이 쓰이는 데이터베이스로는 MongoDB와 PostgreSQL이 있습니다.

MongoDB는 데이터를 JSON과 비슷한 문서 형태로 다루는 방식이어서 JavaScript를 배우는 사람에게 친숙하게 느껴질 수 있습니다. PostgreSQL은 표 형태의 데이터를 안정적으로 다루는 관계형 데이터베이스입니다. 둘 중 무엇이 더 좋다고 외우기보다, 처음에는 서버의 기본 흐름을 먼저 이해하는 것이 중요합니다.

지금 단계에서 가장 중요한 흐름은 이것입니다.

요청을 받는다 → 데이터를 읽는다 → 필요한 처리를 한다 → 데이터를 저장한다 → 응답한다

배열을 쓰든, JSON 파일을 쓰든, 데이터베이스를 쓰든 이 큰 흐름은 크게 달라지지 않습니다.

개인정보를 파일에 그대로 저장하면 안 되는 이유

학습용 예제에서는 이름과 날짜 정도를 저장했습니다. 하지만 실제 서비스에서는 조심해야 할 데이터가 많습니다.

예를 들어 비밀번호, 주민등록번호, 전화번호, 주소, 결제 정보, 건강 정보 같은 데이터는 매우 민감합니다. 이런 값을 JSON 파일에 그대로 저장하면 위험합니다.

파일은 생각보다 쉽게 노출될 수 있습니다. 실수로 public 폴더에 넣을 수도 있고, GitHub 같은 공개 저장소에 올릴 수도 있습니다. 서버 권한 설정이 잘못되어 외부에서 다운로드될 수도 있습니다. 백업 파일이나 로그에 남을 수도 있습니다.

특히 비밀번호는 절대로 원래 글자 그대로 저장하면 안 됩니다. 실제 서비스에서는 비밀번호를 해시라는 방식으로 바꾸어 저장합니다. 해시는 원문을 그대로 보관하지 않고, 비교 가능한 다른 값으로 바꾸는 방법입니다. 지금 당장 구현할 필요는 없지만, "비밀번호를 파일에 적어 둔다"는 방식은 피해야 한다는 점은 꼭 기억해야 합니다.

개인정보도 필요한 만큼만 받아야 합니다. 예약 날짜만 필요한 서비스라면 생년월일이나 주소까지 받을 이유가 없습니다. 서버는 데이터를 많이 모을수록 더 큰 책임을 갖게 됩니다.

이번 장에서 기억할 것

배열 저장은 가장 단순합니다. 서버가 켜져 있는 동안에는 데이터를 담고 다시 꺼낼 수 있습니다. 하지만 서버를 끄면 사라집니다.

JSON 파일 저장은 조금 더 오래 남습니다. 서버를 다시 켜도 파일에 적힌 데이터를 읽어 올 수 있습니다. 하지만 여러 요청이 동시에 들어오는 실제 서비스에서는 안전한 저장 방식이 아닙니다.

데이터베이스는 데이터를 더 안전하고 체계적으로 저장하기 위한 전용 시스템입니다. MongoDB나 PostgreSQL 같은 이름을 지금 모두 익힐 필요는 없습니다. 먼저 Express 서버가 요청을 받고, 데이터를 읽고, 저장하고, 응답하는 흐름에 익숙해지는 것이 우선입니다.

다음 단계에서는 오류가 났을 때 서버가 멈추거나 이상한 응답을 보내지 않도록 다루는 방법을 배웁니다. 파일을 읽다가 실패할 수도 있고, 잘못된 JSON이 들어올 수도 있습니다. 오류는 학습이 잘못되었다는 신호가 아니라, 서버가 현실의 상황을 만나는 과정입니다.

책 목록

비개발자를 위한 Node.js Express 서버 입문

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기