Skip to content

Commit

Permalink
First Boilerplate (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
r4k0nb4k0n authored Jan 20, 2021
1 parent 3f11828 commit 7b35a4a
Show file tree
Hide file tree
Showing 21 changed files with 4,955 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"presets": [
"@babel/preset-env"
]
}
15 changes: 15 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"env": {
"es6": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 8,
"sourceType": "module"
},
"extends": ["plugin:prettier/recommended"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error"
}
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
db
15 changes: 15 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": true,
"files.exclude": {},
"files.insertFinalNewline": true,
"javascript.format.enable": false,
"search.exclude": {
".git": true,
".build": true,
"**/.DS_Store": true,
"**/.vscode": true,
"**/coverage": true,
"**/node_modules": true
}
}
11 changes: 11 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM node:12.20.1-alpine3.10

WORKDIR "/app"

COPY ./package.json ./

RUN yarn

COPY . .

CMD ["yarn", "dev"]
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
# DSC_web_backend
# DSC_web_backend

Express.js + MongoDB w/ Docker.

## 구조

- 📂 `DSC_web_backend/` (프로젝트 폴더)
- 📂 `.vscode/` (VS Code Workspace 설정 폴더)
- 📄 `settings.json` (VS Code Workspace 설정 파일)
- 📁 `node_modules/` (node_module이 담긴 폴더)
- 📁 `db/` (Docker로 실행된 MongoDB이 생성한 파일들이 담긴 폴더)
- 📂 `config/` (서버 설정이 담긴 폴더)
- 📄 `index.js` (서버 설정 appConfig)
- 📄 `development.js` (development 모드 설정 devConfig)
- 📄 `production.js` (production 모드 설정 prodConfig)
- 📂 `module/` (Resource들을 모은 폴더)
- 📂 `todo/` (Resource 예시 중 하나)
- 📄 `todo.controller.js` (MongoDB에서 해당 Resource를 처리할 Method들을 정의)
- 📄 `todo.model.js` (Resource 모델링)
- 📄 `todo.routes.js` (Resource에 대한 routes 정의)
- 📂 `routes/`
- 📂 `api/`
- 📄 `index.js` (Resource에 대한 routes를 쓰는 apiRoutes를 정의)
- 📄 `index.js` (app.js에서 쓰는 mainRoutes를 정의)
- 📂 `utils/`
- 📄 `asyncWrapper.js`
- 📄 `LICENSE`
- 📄 `.gitignore`
- 📄 `.babelrc` (ES6를 사용할 수 있도록 Babel 사용)
- 📄 `.eslintrc.json` (eslint 설정 파일)
- 📄 `app.js`
- 📄 `Dockerfile.dev` (Express 서버 Dockerfile)
- 📄 `docker-compose.yaml` (Express 서버와 MongoDB 서버를 도커 컨테이너로 실행하는 스크립트)
- 📄 `package.json`
- 📄 `state.of.the.art.js`

## 시작하기 전에

- 로컬에서 개발할 때 무리가 없도록 아래와 같은 설정을 했습니다.
- node module로 eslint, prettier를 미리 설정해두었기 때문에, VS Code에서 ESLint와 Prettier Extension을 설치하면 저장할 때마다 자동으로 컨벤션에 맞춰 수정됩니다.
- 개발 환경의 통일성과 편의성을 위해 Docker를 도입했습니다. VS Code에서 Docker Extension을 설치하면 편합니다. development 모드로 Express 앱과 MongoDB가 컨테이너로 같이 실행됩니다. Nodemon으로 실행하기 때문에 이 상태에서 코드를 수정하면서 실시간으로 변화를 볼 수 있습니다.
- API Documentation까지 했으면 좋았겠지만 시간이 없어서 아직 안했습니다 ㅎㅎ
- Swagger, Apidocs 등의 API Documentation은 프론트엔드 팀과 협업할 때 매우 큰 도움이 됩니다. 프론트엔드 팀도 연동을 위해 해당 프로젝트를 실행할 필요가 있으니, 협업을 시작하기 전에 구현하는 것이 좋습니다.
- 필요한 Resource에 대해 DB 모델링과 API를 구현하고 추가할 때, `module/todo` 를 참고하시면 코드를 거의 바꿀 필요 없이 처리하실 수 있을 거에요!

## How to control in development mode

```shell
# 컨테이너들 실행
docker-compose up -d
# 컨테이너들 종료
docker-compose down
# 컨테이너들 재시작
docker-compose restart
```

## How to control in production mode

```shell
# express 앱 빌드 후 시작
yarn start
```
37 changes: 37 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// A node.js web framework.
import express from "express";
// A middleware module that handles HTTP POST request in Express.js v4 and above.
import bodyParser from "body-parser";
// A middleware module that logs.
import morgan from "morgan";
// The router object that handles all API.
import mainRouter from "./routes";
// A function that connects to MongoDB.
import connectMongo from "./config/mongoConnect";
// State of the art.
import stateOfTheArt from "./state.of.the.art";

// First connect to MongoDB, and then...
connectMongo().then(() => {
const app = express();
// Handle HTTP POST Body with json and url-encoded-form.
app.use(bodyParser.json());
app.use(
bodyParser.urlencoded({
extended: true,
})
);
// Log everything at dev level.
app.use(morgan("dev"));
// Use main router.
app.use("/", mainRouter);
const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {
console.log(stateOfTheArt, "font-family:monospace");
setTimeout(() => {
console.log(`Raccoon Backend is running on ${process.env.NODE_ENV} mode`);
console.log(`Raccoon Backend is listening on http://localhost:${PORT}`);
}, 2500);
});
});
8 changes: 8 additions & 0 deletions config/env/development.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const devConfig = {
jwtKey: "",
jwtExpiration: 360000,
dbConnectionString: "mongodb://mongodb:27017",
mongoDebug: true,
};

export default devConfig;
4 changes: 4 additions & 0 deletions config/env/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const env = process.env.NODE_ENV || "development";
const appConfig = require(`./${env}`).default;

export default appConfig;
8 changes: 8 additions & 0 deletions config/env/production.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const prodConfig = {
jwtKey: "",
jwtExpiration: 360000,
dbConnectionString: "<mongodb address>",
mongoDebug: false,
};

export default prodConfig;
44 changes: 44 additions & 0 deletions config/mongoConnect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import mongoose from "mongoose";
import debug from "debug";
import appConfig from "./env";

const log = debug("app");

mongoose.Promise = Promise;

mongoose.connection.on("connected", () => {
log("MongoDB Connection Established");
});

mongoose.connection.on("reconnected", () => {
log("MongoDB Connection Reestablished");
});

mongoose.connection.on("disconnected", () => {
log("MongoDB Connection Disconnected");
});

mongoose.connection.on("close", () => {
log("MongoDB Connection Closed");
});

mongoose.connection.on("error", (error) => {
log("MongoDB ERROR: " + error);
process.exit(1);
});

mongoose.set("debug", appConfig.mongoDebug);
const connectMongo = async () => {
let connectionuri = appConfig.dbConnectionString;
await mongoose.connect(connectionuri, {
//autoReconnect: true,
//reconnectTries: 1000000,
//reconnectInterval: 3000,
useNewUrlParser: true,
useFindAndModify: false,
useCreateIndex: true,
useUnifiedTopology: true,
});
};

export default connectMongo;
23 changes: 23 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: "3"
services:
server:
build:
dockerfile: Dockerfile.dev
context: ./
volumes:
- ./:/app
- /app/node_modules
links:
- mongodb
ports:
- "8080:8080"
depends_on:
- mongodb
mongodb:
container_name: mongodb
image: mongo
restart: always
volumes:
- ./db:/data/db
ports:
- "27017:27017"
81 changes: 81 additions & 0 deletions module/todo/todo.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { todoModel } from "./todo.model";
import { StatusCodes, ReasonPhrases } from "http-status-codes";

const todoController = {};

todoController.create = async (req, res) => {
try {
console.log(req.body);
const todo = await todoModel.create({
content: req.body.content,
});
return res.json(todo);
} catch (error) {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json({ error: error.toString() });
}
};

todoController.findAll = async (req, res) => {
try {
const todos = await todoModel.find();
return res.json(todos);
} catch (error) {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json({ error: error.toString() });
}
};

todoController.findOne = async (req, res) => {
try {
const todo = await todoModel.findById(req.params.id);
if (!todo) {
return res
.status(StatusCodes.BAD_REQUEST)
.json({ message: "Todo not found" });
}
return res.json(todo);
} catch (error) {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json({ error: error.toString() });
}
};

todoController.update = async (req, res) => {
try {
let todo = await todoModel.findById(req.params.id);
if (!todo) {
return res
.status(StatusCodes.BAD_REQUEST)
.json({ message: "Todo not found" });
}
Object.assign(todo, req.body);
await todo.save();
return res.json(todo);
} catch (error) {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json({ error: error.toString() });
}
};

todoController.delete = async (req, res) => {
try {
const todo = await todoModel.findByIdAndRemove(req.params.id);
if (!todo) {
return res
.status(StatusCodes.BAD_REQUEST)
.json({ message: "Todo not found" });
}
return res.json({ message: "TOdo deleted successfully" });
} catch (error) {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json({ error: error.toString() });
}
};

export default todoController;
18 changes: 18 additions & 0 deletions module/todo/todo.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import mongoose from "mongoose";

const Schema = mongoose.Schema;

const todoSchema = new Schema({
content: {
type: String,
required: true,
},
completed: {
type: Boolean,
required: false,
default: false,
},
});

const todoModel = mongoose.model("todo", todoSchema);
export { todoModel };
13 changes: 13 additions & 0 deletions module/todo/todo.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import express from "express";
import todoController from "./todo.controller";
import { asyncWrapper } from "../../utils/asyncWrapper";

const todoRoutes = express.Router();

todoRoutes.post("/", asyncWrapper(todoController.create));
todoRoutes.get("/", asyncWrapper(todoController.findAll));
todoRoutes.get("/:id", asyncWrapper(todoController.findOne));
todoRoutes.patch("/:id", asyncWrapper(todoController.update));
todoRoutes.delete("/:id", asyncWrapper(todoController.delete));

export { todoRoutes };
Loading

0 comments on commit 7b35a4a

Please sign in to comment.