Node.js 를 사용하여 기본적인 CRUD 서버를 구축해보려고 한다
Node.js 는 JavaScript 런타임 으로써
Single Thread , Non-blocking I/O 이다
$ npm init
npm -> 라이브러리 설치를 쉽게 도와주는 도구
npm init -> package.json 파일 생성용 명령어
$ npm install express
express : Node.js 를 서버를 구현하게 해주는 라이브러리 집합체
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
/news 로 접속하면 들어가면 Hello news 화면이 나오게 된다
app.get('/news', (req, res) => {
res.send('Hello news')
})
html 보내서 좀더 가시성을 높히고 싶다면
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
hello
</body>
</html>
파일 전송시에는
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html')
})
dirmame 이라고 치면 현재 server.js 의 절대경로
dirname 뒤에 /index.html 추가시 /index.html 파일경로를 출력해준다
html 파일에서 css파일을 가져다 쓸 경우 우선 해당 css 파일이 들어있는 폴더를 서버에 등록해야한다
일반적으로 css 파일은 public 폴더 안에다 넣는다
app.use(express.static(__dirname + '/public'));
이렇게 서버가 있는 메인에 미들웨어처럼 넣어주면된다
서버 : Node.js express
DB : MongoDB
기능 : 회원가입 CRUD
app.ts : 각 기능별 API method
server.ts : mongoDB 연결 method
mongoDB atlas
이전에 작성해둔 글 참고하여 연결시킨다
# server.ts
# 서버 접속 및 MongoDB 연결코드
import mongoose from 'mongoose';
import app from './app';
const port = 3000;
mongoose.connect('mongodb://localhost/ex2').then(() => {
console.log('Connected to MongoDB');
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
});
# app.ts
import cors from 'cors'
import express from 'express';
const app = express();
app.use(cors());
app.oprion('*', cors());
app.use(express.json());
app.use(express.urlencoed({extended: false});
app.post('/login', AuthRoutes.login);
app.post('/register', UserRoutes.register);
app.patch('/user', MAuth, UserRoutes.updateUser);
app.delete('/user', UserRoutes.deleteUser);
app.get('/user/:id', UserRoutes.findUser);
export default app;
각 Api 별로 나눠서 구현해보자
MongoDB는 고정적인 스키마를 갖지 않기 때문에 mongoose를 사용해서 스키마를 생성하는데 데이터베이스 서버측에서 만드는 스키마가 아닌 우리의 웹서버가 DB에 들어있는 문서들을 객체화하여 사용 할 수 있도록 스키마를 설정해주어야 한다.
Model : 스키마를 사용해서 만드는 인스턴스로 DB에서 실제 작업을 처리할 수 있는 함수를 지니는 객체
Schema : 스키마는 컬렉션에 들어가는 문서 내부의 각 필드가 어떤 형식으로 되어 있는지 정의하는 객체
User Schema 는 기본적으로 id , password , name , regDate 를 값으로 가지도록 설정했다
# Models/User.ts
import { Schema, model } from 'mongoose';
export interface User {
id: string;
password: string;
name?: string;
regDate?: Date;
}
const userSchema = new Schema<User>(
{
id: { type: String, required: true, unique: true },
password: { type: String, required: true },
name: { type: String },
regDate: { type: Date, default: Date.now },
},
{ versionKey: false },
);
// versionkey : mongoDB 자동 업데이트 방지 일과성 유지
// Document : mongoDB 컬렉션 내 단일 레코드
// model : mongoose 에서 Schema 기반으로 생성되며 MongoDB 컬렉션과 상호 작용하는 인터페이스
const UserModel = model<User>('users', userSchema);
export default UserModel;
#routes/User.ts
static register = async (req: Request, res: Response) => {
const { id, password, name } = req.body;
if (!id) {
res.status(400).send(new ErrorResponse(-301, 'Id required'));
return;
} else {
const user: { _id: Types.ObjectId } | null = await UserModel.exists({ id });
if (user) {
res.status(400).send(new ErrorResponse(-409, 'Id duplicated'));
return;
}
}
if (!password) {
res.status(400).send(new ErrorResponse(-302, 'Password required'));
return;
}
if (!name) {
res.status(400).send(new ErrorResponse(-304, 'Name required'));
return;
}
const mongoSession = await UserModel.startSession();
mongoSession.startTransaction();
try {
const user: User | null = await new UserModel({
id,
password,
name,
}).save();
if (!user) {
await mongoSession.abortTransaction();
res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
} else {
await mongoSession.commitTransaction();
res.status(200).json({
msg: '회원가입이 완료되었습니다.',
id: user.id,
name: user.name,
});
}
} catch (err: unknown) {
await mongoSession.abortTransaction();
res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
} finally {
await mongoSession.endSession();
}
};
각 예외는 Status 상태코드를 최대한 적용하여 예외처리를 해봤다
Post man 을 사용하여 만들어둔 api 를 테스트 해보자
User 의 Schema에서 정의해둔 id, name , password 를 입력해주면 메시지 표기와 함께 정상적으로 값이 들어갔음을 확인 가능하다
계속해서 login 까지 만들어보자
class AuthRoutes {
static login = async (req: Request, res: Response) => {
const { id, password } = req.body;
if (!id) {
res.status(400).send(new ErrorResponse(-301, 'Id required'));
return;
}
if (!password) {
res.status(400).send(new ErrorResponse(-302, 'Password required'));
return;
}
try {
const user: User | null = await UserModel.findOne({ id, password });
if (!user) {
res.status(404).send(new ErrorResponse(-404, 'User not found'));
return;
}
res.status(200).json({
msg: 'User Login Success',
userId: user.id,
});
} catch (err: unknown) {
res.status(500).send(new ErrorResponse(-1, 'Internal server Error'));
}
};
}
이번에는 기존에 만들어둔 userts routes 를
fatch() 를 통해
static updateUser = async (req: Request, res: Response) => {
const { id, name } = req.body;
if (!id && !name) {
res.status(400).send(new ErrorResponse(-341, 'id and name required'));
return;
}
try {
const mongoSession = await UserModel.startSession();
mongoSession.startTransaction();
try {
const Userupdate: User | null = await UserModel.findOneAndUpdate(
{ id },
{ name },
{ runValidators: true, new: true },
);
if (!Userupdate) {
await mongoSession.abortTransaction();
res.status(404).send(new ErrorResponse(404, 'User not found'));
} else {
await mongoSession.commitTransaction();
res.status(200).json({
msg: 'name changed',
name: Userupdate.name,
});
}
} catch (error: unknown) {
await mongoSession.abortTransaction();
res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
} finally {
mongoSession.endSession();
}
} catch (error: unknown) {
res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
}
};
static deleteUser = async (req: Request, res: Response) => {
const { id } = req.body;
try {
const mongoSession = await UserModel.startSession();
mongoSession.startTransaction();
const user: User | null = await UserModel.findOne({ id });
if (!user) {
res.status(404).send(new ErrorResponse(-341, 'User not found'));
return;
}
try {
const deleteUser = await UserModel.findOneAndDelete({ id });
if (!deleteUser) {
await mongoSession.abortTransaction();
res.status(404).send(new ErrorResponse(404, 'User not found'));
}
await mongoSession.commitTransaction();
res.status(200).send({ msg: 'user deleted' });
} catch (error: unknown) {
res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
} finally {
mongoSession.endSession();
}
} catch (error: unknown) {
res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
}
};
Node.js Express와, MongoDB, typeScript 를 사용하여서 최대한 RestFul 하게 CRUD 를 연습해보았다 다음에는 JWT를 적용해봐야겠다