Vue + Node + MongoDB CRUD 고찰

모기·2025년 6월 12일

사실 나는 Vue는 찍찍찍찍찍먹 사후르 밖에 못해봤다.
그래서 vue와 node 여정이 조금은 어려울지도 모른다.

아래 Vue와 Node에 대한 간단한 특징은 여기에 정리해놨다.

그러나 아래 내용은 너무 간단하기 때문에 AI 힘을 더 써서 조금 더 구체적으로 정리했다.

Prologue

Vue

특징

  • 점진적 프레임워크로, 필요에 따라 라우터, 상태관리 등 추가 가능.
    • 점진적 프레임워크란?
      • Vue는 필요한 기능만 점진적으로 도입할 수 있는 구조입니다.
      • 처음에는 단순한 <script> 태그 하나로도 시작 가능.
      • 규모가 커지면 Vue Router, Vuex (또는 Pinia), Composition API, TypeScript 등을 추가해 확장 가능.
      • React처럼 처음부터 모든 걸 세팅하지 않아도 됩니다.
  • 컴포넌트 기반 구조, 가상 DOM 사용.
    • 컴포넌트 기반 구조
      • UI를 재사용 가능한 단위로 분할할 수 있음.
      • 각 컴포넌트는 HTML, CSS, JavaScript를 .vue 단일 파일로 구성(SFC: Single File Component).
      • 예: <UserCard />, <TodoItem /> 등으로 쪼갤 수 있어 유지보수가 쉬움.
    • 가상 DOM* 사용
      • Vue는 DOM 조작의 성능을 최적화하기 위해 가상 DOM(Virtual DOM)을 사용.
      • 변경 사항을 메모리 상의 가상 트리에서 먼저 계산 → 실제 DOM에 최소한의 변화만 적용.
      • 리렌더링 성능이 뛰어나며 불필요한 업데이트 방지.
  • 양방향 데이터 바인딩*(MVVM)과 단방향 데이터 흐름* 모두 지원.
    • 양방향 데이터 바인딩(MVVM) + 단방향 흐름 지원
      • Vue는 v-model 디렉티브를 통해 양방향 데이터 바인딩을 지원 (입력 값 → 상태, 상태 → 입력 값 자동 연동).
      • 동시에, 컴포넌트 간 데이터 흐름은 부모 → 자식은 props, 자식 → 부모는 이벤트 emit 방식으로 단방향 데이터 흐름을 유지함.
      • 즉, 필요한 곳에서는 편리한 양방향 바인딩을 쓰되, 전체 구조는 깔끔한 단방향 패턴을 따름.
  • 배우기 쉽고, HTML/CSS/JS만 알면 빠르게 시작 가능.
    • 러닝커브가 낮음
      • HTML, CSS, JavaScript만 알면 기본적인 Vue 사용이 가능.
      • 템플릿 문법이 익숙한 HTML 구조이므로 초보자도 진입 장벽이 낮음.

장점

  • 러닝 커브가 낮아 초보자도 쉽게 접근 가능.
  • 빠른 렌더링 성능(가상 DOM).
  • 공식 라이브러리(라우터, 상태관리 등)가 잘 갖춰져 있음.
  • React와 Angular의 장점을 모두 흡수.

단점

  • 생태계와 커뮤니티가 React에 비해 작음.
  • 일부 영어 외 문서/자료가 부족할 수 있음.
  • 구버전(2.x)에서는 TypeScript와의 결합도가 떨어짐.
* 가상 DOM
 - 웹페이지의 실제 DOM(문서 객체 모델, Document Object Model)을 메모리 상에 가볍게 복사해둔 자바스크리브 객체형태의 복사본
 - 실제 DOM은 브라우저가 화면을 그릴 때 사용하는 구조, HTML 요소들을 계층적으로 표현한 트리 구조
 - React 같은 프레임워크에서 상태가 바뀔 때마다 새로운 가상 DOM을 만들고, 이전 DOM과 비교해서 변경된 부분만 찾아내어 반영함.
 - 실제 DOM에 반영(reconciliation)할 때 불필요한 리렌더링을 줄일 수 있음
 
* 양방향 데이터 바인딩
 - 웹 애플리케이션에서 모델과 뷰가 서로 자동으로 데이터를 동기화하는 기능
 - 입력 폼에서 사용자가 값을 입력하면 해당 값이 바로 자바스크립트 변수에 반영되거나, 반대로 자바스크립트에서 값을 변경하면 입력 폼의 값도 자동으로 바뀜
 
* 단방향 데이터 흐름
 - 모델의 데이터가 변경되면 뷰가 자동으로 업데이트되지만 뷰에서 직접적으로 모델의 값을 바꿀 순 없음.
 - 사용자가 뷰에서 값을 변경하고 싶다면, 이벤트(onChange, onClick 등)를 통해 자바스크립트 함수를 호출해서 모델의 값을 변경함.

Node

특징

  • 서버사이드 JavaScript 런타임, 비동기 이벤트 기반 구조.
    • 서버사이드 JavaScript 런타임이란?
      • Node는 브라우저 밖에서도 JavaScript를 실행할 수 있게 만든 런타임 환경입니다.
      • 파일 시스템 접근, 네트워크 통신, 데이터베이스 처리 등 백엔드 작업을 JavaScript로 처리 가능.
      • 내부적으로는 Chrome의 V8 엔진을 기반으로 동작하며, 이벤트 루프를 통해 비동기적으로 작업을 처리합니다.
  • NPM을 통한 방대한 오픈소스 생태계.
  • 싱글스레드, 논블로킹 I/O.
    • 싱글스레드와 논블로킹 I/O
      • Node는 하나의 메인 스레드에서 JavaScript 코드를 처리합니다.
      • I/O 작업은 논블로킹(Non-blocking) 방식*으로 처리하여 동시에 많은 요청을 처리할 수 있습니다.
      • 예: 파일 읽기, DB 쿼리, HTTP 요청 등은 작업을 OS에 위임하고, 완료되면 콜백 함수로 결과를 받아옵니다.

장점

  • 프론트/백엔드 모두 JavaScript로 개발 가능(단일 언어).
  • 비동기 I/O로 실시간, 대규모 트래픽 처리에 강점.
  • 쉽고 빠른 확장성, 크로스플랫폼 개발 지원.
  • 개발 속도가 빠르고, 다양한 라이브러리 활용 가능.

단점

  • CPU 집중적 작업에 부적합(싱글스레드 구조).
  • 콜백 지옥, 비동기 코드의 복잡성(최근엔 async/await로 개선).
  • API 불안정성, 경험 많은 개발자 부족 문제.
  • 보안, 코드 모듈화에 주의 필요.
 * 논블로킹 방식
  - 어떤 작업을 실행할 때, 그 결과가 나올 때까지 기다리지 않고, 다음 작업을 먼저 수행하는 방식
	1. JavaScript 코드 실행 (싱글 스레드)
	•	Node.js는 하나의 메인 스레드에서 JavaScript 코드를 실행합니다.
	•	여기서는 파일 읽기, 네트워크 요청, DB 호출 등 다양한 명령이 포함될 수 있습니다.
	•	이 메인 스레드는 요청을 처리하면서도 다음 코드로 바로 넘어갈 수 있도록 설계되어 있습니다.

	2. 비동기 작업 요청 (libuv로 위임)
	•	파일을 읽거나 네트워크 통신처럼 시간이 오래 걸리는 작업은 즉시 처리하지 않고, Node.js 내부의 C++ 기반 라이브러리인 libuv에게 위임합니다.
	•	libuv는 Node의 핵심으로, 비동기 I/O와 이벤트 루프를 관리하는 역할을 합니다.

	3. libuv가 OS의 백그라운드 스레드 풀에 작업 위임
	•	libuv는 실제 작업을 운영체제의 비동기 기능 또는 자체 스레드 풀로 넘깁니다.
	•	예: 파일 시스템 접근, DNS 조회, 데이터베이스 쿼리 등
	•	이 작업들은 메인 스레드와는 별개로 백그라운드에서 동시에 처리됩니다.

	4. 작업 완료 후 콜백 등록 (이벤트 큐로 전달)
	•	백그라운드 작업이 완료되면, Node는 미리 지정된 콜백 함수를 콜백 큐(또는 태스크 큐, 이벤트 큐)에 등록합니다.
	•	이때 JS 메인 스레드는 여전히 멈추지 않고 다른 작업을 수행 중입니다.

	5. 이벤트 루프(Event Loop)가 콜백 실행
	•	Node.js는 **이벤트 루프(Event Loop)**를 통해 콜백 큐에 등록된 작업을 하나씩 메인 스레드에서 실행시킵니다.
	•	이벤트 루프는 다음을 반복합니다:
		1.	메인 스레드 작업 완료 확인
		2.	큐에 있는 콜백이 있으면 꺼내서 실행
		3.	없다면 대기 또는 다음 작업

	6. 최종 결과를 처리하거나 출력
	•	콜백 함수 내에서 결과 데이터를 처리하거나, 클라이언트에 응답을 보내는 등의 후속 작업이 실행됩니다.
	•	이 전 과정을 통해, 메인 스레드는 어떤 요청도 막힘없이 처리 가능하게 됩니다.

Gochal

Vue

우선 vue 프로젝트를 만드는 것으로 시작해보자.
모든 환경 설정이 준비되었다고 가정하고 시작한다.

vue create crud

default ([vue 3] babel, eslint)

잘 만들어졌다.

npm run serve

대충 vue로 어떻게 만들어졌는지 살펴봤으니,
이제 만들어진 vue를 사용할 것이다.

리액트는 어떤지 모르겠지만, vue는 다운받을 수 있는 테마가 있다.

이 테마를 쓸 예정이다.
만들었던 폴더는 삭제하고 (덮어쓰기 할 경우 충돌이 일어난다.) 다운받은 폴더를 그대로 넣은 뒤에
npm install을 실행한다.

컴포넌트가 굉장히 많은데, 하나하나 다 만들려고 했으면 꽤나 고생했을 것 같다.
여기서 하나씩 지워서 board나 로그인 관련 소스 코드만 남길 예정이다.

먼저 옆에서 지울 것들 다 지워버리자.
table list, typography icons, maps를 지울 예정이다.
근데 얘네들 현재 어떤 식으로 짜여져있는지 감도 안잡힌다. 전역 검색을 활용해보자.
typography가 가장 독특하니 typography부터 전역검색을 하면

이렇게 나오는데
routes.js에 들어가보면

routes라는 배열에서
json형태로 path, component, redirect및 children을 받고 있는 것을 확인할 수 있다.
이 routes에 대한 참조는 이따 찾아보기로 하고, component란 무엇인지에 대해서 알아보도록 하자.

리액트 네이티브에서는 이런 component들이 화면의 요소 하나하나를 담당했었다.

-AI-

개인적으로 여기서 중요하다고 생각하는 것은 props, 부모/자식 관계, 라이프 사이클이라는 키워드이다.

보통 화면에 보이는 값을 return 한다고 생각하면 된다.

보이는 UI 함수라고 해야하나

어떤 느낌이냐면
먼저, Button이라는 컴포넌트와 Box라는 컴포넌트 뼈대를 만들어둔다.
이 뼈대를 가져와서 변수나 함수를 등록할 수 있다.

Count라는 int 변수와 count를 증가, 감소시키는 increase, decrease 함수를 만들었을 때
Box 컴포넌트에는 Count라는 변수가 보이도록,
Button 컴포넌트는 2개 만들어서 각각 increase와 decrease 함수를 등록시킬 수 있다.
같은 Button 컴포넌트이지만 등록하는 함수에 따라 역할이 바뀐다.
내부의 props가 count 변수나 increase, decrease 함수로 설정된 것이다.

이런 컴포넌트들은 사실 screen이라는 큰 컴포넌트의 하위 컴포넌트로 위치해있다. 이게 부모/자식 관계라고 볼 수 있다.

마지막으로 increase나 decrease를 누른다고 해서, 즉 상태를 업데이트 했다고 해서 화면의 값이 바로바로 변경되는 것은 아니다. 리렌더링을 해주어야 비로소 화면에 반영된다. react-native 같은 경우는 hook으로 등록한 변수를 변경하는 함수를 실행하면 해당 컴포넌트와 하위 컴포넌트를 리렌더링 해준다.
컴포넌트가 실행된 것을 mount했다고 표현하는 것 같은데 정확하지는 않아서 AI 검증할 예정이다.

그리고 이 상태 업데이트나 mount, unmount하는 일련의 과정을 종합적으로 life cycle이라 부른다.

설명은 여기까지하고 CRUD 구현과 관련없는 친구들은 지울 예정이다. 앞선 설명 중에 컴포넌트를 재사용할 수 있다는 말이 있으니 타고 들어가서 컴포넌트까지 삭제하진 않을 것이다. 우선은 배열에 있는 친구들부터 제거한다.

먼저 routes.js에서 지운다.

근데, 업데이트된 화면을 봐도 여전히 남아있다.
우리는 무엇을 한 것일까?

정답은 눌러보면 안다.

404

그럼 옆에 저 버튼까지 없애려면?
DashboardLayout.vue로 간다.

이름도 바꾸고 싶은대로 바꾸자.

이제 할 일은 Dashboard를 뜯어고치는 일이다.

여기에다가 게시판을 만들 예정이다. 이를 위해 Overview.vue로 가면된다.
게시판을 뜯어고치기 전에 template와 script가 어떻게 상호작용하는지 살펴보자.

아래는 각각 line chart의 template과 script이다.

chart-card라는 하위 컴포넌트 안에 chart-data, chart-option의 props에 script 안의 데이터가 전달됐다.

chart card 컴포넌트는
import를 통해서 다른 vue 소스 파일에서 불러오며
export default를 통해 해당 파일 전체를 다른 곳에서 import 할 수 있게 만든다.

Task가 게시판처럼 생겼으니 해당 컴포넌트를 제외한 다른 컴포넌트들을 모두 지우자.
상단의 stats card도 예쁘게 생겼으니 일단 남겨두려고 한다.


그럼 이런 모습이 된다.

너무 작다. 크기를 키우려고 클래스를 살펴보니

col-md-...가 보였고, 12로 바꿔줬더니 넉넉하게 작동한다.

그리고 overview.vue에서

return {
        editTooltip: 'Edit Task',
        deleteTooltip: 'Remove',
        tableData: {
          data: [
            {title: 'Sign contract for "What are conference organizers afraid of?"', checked: false},
          ]
        }
      }

table관련 컴포넌트 데이터를 이렇게 고쳤는데, 나중에 여기에는 db에서 데이터를 불러와서 채우는 형태로 변경할 예정이다.

그 밖에 마음에 들지 않는 이름들은 알아서 고치자.


준비가 됐다.

얼추 기본적인 세팅은 다 됐으니 Node로 넘어가보자

Node (with express.js)

Node만 쓰려고 했는데 왠걸, express도 쓰라고 한다.
보아하니 Node만 쓸 수는 있지만 express를 쓰면 조금 더 편하다고 한다.

현재 폴더 구조를 얘기하자면

VueNode
 |--front_end(Vue)
 |  |--...
 |
 |--back_end(Node)
    |--...

이런 느낌이다.
따라서 back_end를 cmd로 켜서
npm init -y
npx express-generator --view=pug
npm install을 하면
준비완료다.

그렇게 생긴 app.js에

const port = 3000

app.listen(prot, ()=> {})

을 추가해주고, (백엔드 포트 3000번을 의미한다.) front_end 폴더로가서 npm install axios를 하자.

그리고 createError(404) 윗 부분에 다음을 추가해주면 백엔드는 준비 완료다.

app.get('/api/tableData', (req, res) => {
  res.json({
    data: [
      {title: 'Sign contract for', checked: false},
    ]
  });
});

현재 백엔드 최종 코드는 다음과 같고,
back_end(node) - app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();
const port = 3000;
// const cors = require('cors');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// app.use(cors());

app.use('/', indexRouter);
app.use('/users', usersRouter);

app.get('/api/tableData', (req, res) => {
  res.json({
    data: [
      {title: 'Sign contract for', checked: false},
    ]
  });
});

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

app.listen(port, ()=> {console.log('Server start');});

module.exports = app;

프론트에서는 overview.vue의 스크립트 부분을 다음처럼 수정해주면 된다.
front_end(vue) - src - pages - Overview.vue

<script>
  import StatsCard from 'src/components/Cards/StatsCard.vue'
  import LTable from 'src/components/Table.vue'
  import axios from 'axios';

  export default {
    components: {
      LTable,
      StatsCard
    },
    data () {
      return {
        editTooltip: 'Edit Task',
        deleteTooltip: 'Remove',
        tableData: {
          data: []
        }
      }
    },
    mounted() {
      this.fetchTableData();
    },
    methods: {
      fetchTableData() {
        axios
        .get('http://localhost:3000/api/tableData')
        .then(response => {
          this.tableData = response.data;
        })
        .catch(error => {
          console.error('data loading error: ', error);
        })
      }
    },
  }
</script>

서버와 백의 연동이 다 됐으니, 이제 추가해줄 건 데이터 베이스다.

MongoDB

사실 간단한 CRUD 구현이라 뭘 써도 상관없긴한데, 리액트 스프링에서는 MySQL을 쓸 것 같으니
MongoDB를 써야겠다.

mongoose와 관련된 코드들을 추가하면 된다.

Implementation

이제
1. 회원가입
2. 글쓰기
3. 글 불러오기
4. 글 목록 리스트로 만들기
5. 댓글

기능을 구현하면 된다.
디자인 뽑는건 귀찮으니 대충하자.
오히려

이런게 더 느낌있을지도 모른다.

회원가입은 마지막에 구현하는 걸로 하고
글쓰기부터 구현해야겠다.

우선 버튼부터 다시 바꿀 예정이다.

      <div class="row">
        <div class="col-md-12">
          <card>
            <template slot="header">
              <h5 class="title">Board</h5>

		...
	  </div>

      <div class="row">
        <div class="col-md-1">
          <button type="submit" class="btn btn-primary btn-fill float-right" @click.prevent="updateProfile">
            Post
          </button>
        </div>
      </div>
이 버튼을 누르면 postpage로 넘어갈 것이다.

board 페이지랑 똑같은 페이지를 하나 만들고 (파일 이름은 PostPage.vue이다.) 위에 카드 디자인만 뺀다.
그리고 Post를 눌렀을 때 해당 페이지로 넘어가게 만들 것이다.

코딩할 때 GPT도 물론 많이 쓰지만 이미 만들어진 파일을 많이 참고한다.
뇌빼고 코딩하기 때문에 내가 만든 프로그램에서도 직접 만든 함수나 변수들, 문제 해결 방법들을 많이 참고한다.

이 말을 왜하냐면 post로 페이지 넘어가는 것 역시 다른 소스 파일을 참고할 예정이기 때문이다.
그래서 routes.js에

  {
    path: '/post',
    component: PostPage,
    redirect: '/post',
  },

이렇게 추가하고 버튼에 링크 걸어봤는데 아무 일도 안 일어나더라..
gpt를 쓴다.

routes.js를

const routes = [
  {
    path: '/',
    component: DashboardLayout,
    redirect: '/admin/overview'
  },
  {
    path: '/admin',
    component: DashboardLayout,
    redirect: '/admin/overview',
    children: [
      {
        path: 'overview',
        name: 'Overview',
        component: Overview
      },
      {
        path: 'user',
        name: 'User',
        component: UserProfile
      },
      {
        path: 'notifications',
        name: 'Notifications',
        component: Notifications
      },
      {
        path: 'post',
        name: 'Post',
        component: PostPage
      },
    ]
  },
  { path: '*', component: NotFound }
]

이렇게 바꾸고, (사실 이건 디버깅에 크게 상관없는 부분이긴하다. 단지 좀 더 예쁘다.)

Overvue.vue에

          <router-link to="/admin/post">
            <button type="button" class="btn btn-primary btn-fill float-right">
              Post
            </button>
          </router-link>

버튼을 이런 식으로 바꿨다.

그리고 page 디렉토리에 PostPage.vue를 추가했다.
처음에는 overvue 코드를 그대로 들고와서 조금씩 수정하다가 다음과 같은 코드가 됐다.
overvue와는 완전히 다른 코드가 되긴 했다.

<template>
    <div class="content">
      <div class="container-fluid">
        <card>
          <h4 slot="header" class="card-title">Post</h4>
          <form>
            <div class="row">
              <div class="col-md-3">
                <base-input type="text"
                          label="Title"
                          placeholder="Title"
                          v-model="user.title">
                </base-input>
              </div>
            </div>
          
            <div class="row">
              <div class="col-md-12">
                <div class="form-group">
                  <label>Content</label>
                  <textarea rows="5" class="form-control border-input"
                            placeholder="Here can be your thinking"
                            v-model="user.content">
                  </textarea>
                </div>
              </div>
            </div>
              
            <div class="row">
              <div class="col-md-1">
                <button type="submit" class="btn btn-primary btn-fill float-right" @click.prevent="updateProfile">
                  Post
                </button>
              </div>
            </div>
          </form>
        </card>
      </div>
    </div>
</template>
<script>
  import Card from 'src/components/Cards/Card.vue'
  import axios from 'axios';

  export default {
    components: {
      Card
    },
    data () {
      return {
        user: {
          title: '',
          content: ``
        }
      }
    },
    methods: {
    },
  }
</script>
<style>

</style>

이제 post를 누르면

이런 페이지로 이동한다. 이제 front에서는 post를 누르면 서버로 데이터를 전송, back에서는 데이터를 받으면 db에 저장하는 api 코드를 짤 것이다.
  1. post 누르면 서버로 데이터 전송

우선 메소드를 만들어야한다. 메소드 만드는 방법은 editprofileform.vue를 참고헀다.

두 번째로 post 메소드 형태로 서버에 보내야한다. 이건 앞서 overview.vue에 gpt가 짜준 코드를 참고하려했으나
post코드는 또 get과는 코드가 다르니 힘을 빌렸다.

먼저 vue에서 post method는

                <button type="submit" class="btn btn-primary btn-fill float-right" @click.prevent="postPosting">
                  Post
                </button>

...

postPosting() {
        axios.post('http://localhost:3000/api/posting', {
          title: this.user.title,
          content: this.user.content
        })
        .then(response => {
          console.log(response.data);
        })
        .catch(error => {
          console.error(error);
        });
      }

이런 형태로 썼고, 서버에서 post api는

app.post('/api/posting', (req, res) => {
  const { title, content } = req.body;
  console.log('get data:', title, content);
  res.json({
    data: { title, content }
  });
});

이렇게 했다.
근데 이렇게만 하면 아마 에러가 뜰 수 있다.
cors*를 추가해줘야 한다. 나도 잘 모른다. 그 녀석이 추가하라고 했다.

* CORS: '다른 출처'의 웹페이지에서 서버에 리소스를 요청할 수 있도록 서버 측에서 허용할지 말지를 결정하는 메커니즘

CORS가 필요한 이유:
브라우저는 보안상 자바스크립트가 다른 도메인에 요청을 보내는 걸 제한해요.
하지만 실제로는 프론트엔드와 백엔드가 서로 다른 포트에서 개발되는 경우가 많죠.
그래서 서버가 **“이 출처는 허용해도 괜찮아”**라고 명시적으로 허용해주어야 합니다.

CORS의 흐름
	1.	클라이언트에서 axios.post('http://localhost:3000/api') 같은 요청 발생
	2.	브라우저가 CORS 정책 확인
	3.	서버에서 응답 헤더에 Access-Control-Allow-Origin이 없으면 차단
	4.	허용된 경우, 응답이 정상적으로 전달됨

그래서
app.js. 즉, 서버에서 npm install cors 및 아래 코드를 추가했다.

const cors = require('cors');

app.use(cors());

그리고 vue.config.js에서 css의 밑에 devServer를 추가했다.

  css: {
    // Enable CSS source maps.
    sourceMap: process.env.NODE_ENV !== 'production'
  },
  devServer: {
    proxy: {
      '/api' : {
        target: 'http://loaclhost:3000',
        changeOrigin: true,
        pathRewrite: { '^/api': '' }
      }
    }
  }

그럼 이제 받은 데이터를 mongodb에 저장해보자.

먼저 back_end(Node)에 models 폴더를 추가하고 Post.js를 추가한다.

그리고 Post.js에 다음과 같은 코드를 추가한다.

const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: String,
  content: String,
  createdAt: { type: Date, default: Date.now },
});

module.exports = mongoose.model('Post', postSchema);

이번엔
app.js에 다음과 같은 코드를 추가한다.

const Post = require('./models/Post');

그리고 post api를 수정한다.

app.post('/api/posting', async (req, res) => {
  try{
    const { title, content } = req.body;
    const newPost = new Post({title: title, content: content});
    await newPost.save();

    res.json({ message: '게시물 저장 성공!', data: newPost });
  } catch(e) {
    console.log('post saving error: ', e);
    res.status(500).json({ message: '서버 에러' });
  }
});

3T studio를 이용해서 데이터가 잘 저장됐는지 확인해보자.

잘 됐으면 이제 이 데이터들을 게시판으로 불러와야 한다.
따라서 get method를 수정할 예정이다. 간단하다. gpt와 함께라면.

app.get('/api/tableData', async (req, res) => {
  try {
    const posts = await Post.find();
    res.json({
      data: posts
    });
  } catch(e) {
    res.status(500).json({ message: 'DB 조회 에러' });
  }
});

형식에 맞게 수정하는게 조끔 까다로울 뿐, 할만하다.

Epilogue

react/spring이 시간이 상당히 걸릴 것 같으니
vue, node는 여기까지만 할 예정이다.

  1. front와 back 연결 및 back과 db연결
  2. front에서 api로 보낸 데이터 back에서 처리하고 db에 전송
  3. back에서 처리된 데이터 front로 전송

사실 여기까지 구현이 됐다면
나머지 부분은 이 기능을 살짝살짝 바꿔서 구현하는 게 전부라고 생각한다.
게시글 눌러서 안에 내용 보는 것도 결국 api로 get요청 보내기일 것이고
댓글 달기/읽기도 post와 get의 향연이 아닐까?

딱 하나, 회원과 수정/삭제 권한 구현만 조금 다를 것 같다고 생각한다.
회원정보 보내서 일치하는 지 확인하는 것?이 전부일 것 같은데
다만 토큰을 구현해야하는 게 다를 것 같다.

구현해야하는 모든 것들은 react/spring에서 다룰 것이다.

profile
안녕

0개의 댓글