Docker로 MongoDB를 시작하여 Heroku가 디버깅할 때까지 개발
76681 단어 DockerHerokuTypeScriptExpresstech
개시하다
나는 전방에서 Docker를 별로 사용하지 않으려고 하지만 DB 서버를 준비하려면 Docker를 사용하면 프로그램을 설치할 필요가 없어 편리해진다.
그래서 몬godB가 Docker로 개발을 시작한 과정을 보도하고 싶습니다.
그리고 이번에 만든 소스는 여기에 두세요.
사용된 기술
MongoDB 서버 시작
Docker 설치
아직 Docker가 설치되지 않은 사람은 이쪽을 참고하여 설치하세요.
docker-compose로mongoDB 환경 구축
몬godb 디렉터리에 다음 코드가 적힌
docker-compose.yml
를 놓으세요.간단하게 말하면
mongo
와 mongo-express
가 시작하고 mongo
몬goDB 서버에 시작하고 mongo-express
GUI로 조작하는 서버를 시작한다.서로 연결하기 위해username과password를 결합시켜야 하기 때문에 환경 변수에서 접근할 수 있습니다.
mongodb/docker-compose.yml
version: '3.1'
services:
mongo:
image: mongo:4.4.5
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGODB_USERNAME}
MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PASSWORD}
ports:
- 27017:27017
volumes:
- ./db:/data/db
- ./configdb:/data/configdb
mongo-express:
image: mongo-express:0.54
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGODB_USERNAME}
ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGODB_PASSWORD}
환경 변수는 같은 디렉토리에 파일 이름.env
으로 구성되어 있으면 자동으로 읽을 수 있습니다.mongodb/.env
MONGODB_USERNAME=root
MONGODB_PASSWORD=password
파일을 배치한 후 mongodb
디렉터리로 이동하고 아래 명령을 누르면 시작합니다.$ docker-compose up -d
시작 후 방문localhost:8081
시 다음과 같은 화면이 나타나며 GUI를 통해 MongoDB의 내용을 확인할 수 있다.참고 자료
Mongoose에서 MongodB 조작
express 서버 설정
우선express 서버의 프레임워크를 만듭니다.API는
server/apis
디렉토리에서 관리되므로 import을 직접 사용합니다.server/server.ts
import Express from 'express';
import http from 'http';
import path from 'path';
import apiRouter from './apis/';
const app = Express();
const port = process.env.PORT || 9000;
const server = http.createServer(app);
app.use(Express.json());
app.use(Express.urlencoded({ extended: true }));
// static以下に配置したファイルは直リンクで見れるようにする
app.use(Express.static(path.resolve(__dirname, 'static')));
// APIの設定
app.use('/api', apiRouter);
server.listen(port, () => {
console.log(`Listening on port ${port}`);
});
server/apis/index.ts
는 다음과 같다.이번엔 그 정도는 아니어도 괜찮지만 앞으로 규모가 커질 것을 고려해서요.server/apis/index.ts
import Express from 'express';
// この後作る
// import todoRouter from './router/todos';
const router = Express.Router();
// サーバーの動作確認
router.get('/health', (req, res) => {
res.send('I am OK!');
});
// ルーティングの設定
// router.use('/todos', todoRouter);
export default router;
일단 방문locahost:9000/api/health
하면,이런 문자열이 바뀌었다.Mongoose를 통해 MongoDB 액세스
server/server.ts
에 다음 코드를 추가하여 MongoDB에 접근합니다.접근할 때username과password가 필요하기 때문에 dotenv
환경 변수 파일을 읽는 파일을 조합합니다.server/server.ts
import mongoose from 'mongoose';
// 本番じゃない時はローカルのDBに接続する
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config({ path: path.resolve(__dirname, '../mongodb/.env') });
const { MONGODB_USERNAME, MONGODB_PASSWORD } = process.env;
mongoose.connect('mongodb://localhost:27017', {
useNewUrlParser: true,
useUnifiedTopology: true,
user: MONGODB_USERNAME,
pass: MONGODB_PASSWORD,
dbName: 'todo-app',
})
.catch((err) => {
console.error(err);
});
}
모델 정의
그런 다음 Todo 데이터의 모델을 정의합니다.어렵게 타입 스크립트를 만들었기 때문에 정적 모형도 잘 만들고 싶었는데 생각보다 힘들었어요.마지막으로 기본적으로 자신의 유형 정의가 되었다.
ITodoFields
는 기본 데이터 형식으로timestamps 옵션createdAt
과 업데이트dAt`를 추가합니다.이것은 진짜와 가짜를 전환해야 하지만 설정이 어려워서 모두 진짜 설정의 구상에 따라 진행된다.server/model/Todo.ts
import mongoose from 'mongoose';
interface ITimestamps {
createdAt: Date;
updateAt: Date;
}
type tDocument<Fields> = Fields & ITimestamps & mongoose.Document;
type tSchema<Fields> = {
[K in keyof Fields]: mongoose.SchemaDefinitionProperty<Fields[K]>;
};
interface ITodoFields {
isDone: boolean;
text: string;
}
const todoSchema: tSchema<ITodoFields> = {
isDone: { type: Boolean, default: false },
text: String,
}
export default mongoose.model<tDocument<tSchema<ITodoFields>>>(
'Todo',
new mongoose.Schema(todoSchema, { timestamps: true })
);
참고 자료API 정의 및 DB 작업
그런 다음 이 모델을 사용하여 각 API에 적절한 작업을 적습니다.
server/apis/router/todos.ts
import Express from 'express';
import TodoModel from '../../models/Todo';
const router = Express.Router();
router.route('/')
.get((req, res) => {
TodoModel
.find()
.sort({ createdAt: -1 })
.then((todos) => {
res.json(todos);
})
.catch((err) => {
res.status(500).send(err);
});
})
.post((req, res) => {
const { text } = req.body;
const todo = new TodoModel();
todo.text = text;
todo.save((err) => {
if (err) {
res.status(500).send(err);
return;
}
res.send('ok');
});
});
router.route('/:todoId')
.delete((req, res) => {
const { todoId } = req.params;
TodoModel
.deleteOne({ _id: todoId })
.then(() => {
res.send('ok');
})
.catch((err) => {
res.status(500).send(err);
});
});
router.route('/check/:todoId')
.put((req, res) => {
const { todoId } = req.params;
const { isDone } = req.body;
TodoModel
.findOne({ _id: todoId })
.then((todo) => {
if (todo == null) {
res.status(400).send(`Todo (id: ${todoId}) not found.`);
return;
}
todo.isDone = isDone;
todo.save((err) => {
if (err) {
res.status(500).send(err);
return;
}
res.send(todo);
});
})
.catch((err) => {
res.status(500).send(err);
});
});
export default router;
API를 만든 후 curl로 전송해 보십시오.todo-app
데이터베이스를 구축했는데 todos
소장품에 데이터가 하나 있으면 성공한다.$ curl -X POST -H "Content-Type: applicati
on/json" -d '{"text":"test todo"}' localhost:9000/api/todos
TODO 리스트 화면 제작
API가 완성되면 화면이 완성됩니다.여기도 타입 스크립트로 쓸 수 있지만 이번에는 서버 위주여서 프론트에서 index를 간단하게 사용할 수 있다.에 불과하다.
맨 위에 있는 Todo를 삭제하면 애니메이션이 순조롭게 진행되지 않습니다. 신경 쓰이지만 당분간은 가능합니다.(Vue.js2학과 때는 괜찮아요.)
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>MongoDBを使ったTODOリスト</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.4/vue.global.js"></script>
<script src="https://unpkg.com/vue-types"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
<style>
* {
box-sizing: border-box;
}
.list {
position: relative;
padding: 0;
}
.item {
position: relative;
display: flex;
align-items: center;
width: 100%;
transition: all 0.5s;
padding: 10px;
margin-top: 10px;
border: solid 1px #ddd;
border-radius: 5px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
cursor: pointer;
}
.item__text {
flex: 1 1 0;
padding: 0 5px;
}
/* 要素が入るときのアニメーション */
.flip-enter-from {
opacity: 0;
transform: translate3d(0, -30px, 0);
}
.flip-enter-to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
/* 要素が消える時のアニメーション */
.flip-leave-active {
position: absolute;
}
.flip-leave-from {
opacity: 1;
transform: translate3d(0, 0, 0);
}
.flip-leave-to {
opacity: 0;
transform: translate3d(0, -30px, 0);
}
</style>
</head>
<body>
<div id="app"></div>
<script>
const { reactive, onBeforeMount } = Vue;
// APIリクエストの設定
const api = axios.create({
baseURL: '/api',
timeout: 15000,
});
/**
* 入力フォームコンポーネント
*/
const InputForm = {
template: `
<form @submit="onSubmit">
<input v-model="state.text" type="text" placeholder="TODO" />
<button type="submit" :disabled="state.text === ''">送信</button>
</form>
`,
emits: {
submit: (text) => {
return text != null;
},
},
setup(props, context) {
const state = reactive({
text: '',
});
return {
state,
/**
* 送信時
* @param {event} event - DOMのイベント
*/
onSubmit(event) {
event.preventDefault();
context.emit('submit', state.text);
state.text = '';
},
};
},
};
/**
* TODOリストコンポーネント
*/
const TodoList = {
template: `
<transition-group tag="ul" class="list" name="flip">
<template
v-for="item in $props.todoList"
:key="item._id"
>
<li
class="item"
@click="$emit('check', item._id, !item.isDone)"
>
<input type="checkbox" :checked="item.isDone" />
<span class="item__text">{{ item.text }}</span>
<button @click="onDeleteTodo($event, item._id)">削除</button>
</li>
</template>
</transition-group>
`,
emits: {
check: (todoId, isChecked) => {
return todoId != null && isChecked != null;
},
delete: (todoId) => {
return todoId != null;
},
},
props: {
todoList: VueTypes.arrayOf(VueTypes.shape({
_id: VueTypes.string.isRequired,
isDone: VueTypes.bool.isRequired,
text: VueTypes.string.isRequired
}).loose).isRequired
},
setup(props, context) {
return {
/**
* 削除ボタンをクリックした時
* @param {event} event - DOMのイベント
* @param {number} todoId - TODOのID
*/
onDeleteTodo(event, todoId) {
event.stopPropagation();
context.emit('delete', todoId);
},
};
},
};
const app = Vue.createApp({
template: `
<div>
<InputForm
@submit="onSubmit"
/>
<p>TODO LIST</p>
<TodoList
:todoList="state.todoList"
@check="onCheckTodo"
@delete="onDeleteTodo"
/>
</div>
`,
components: {
InputForm,
TodoList,
},
setup() {
const state = reactive({
todoList: [],
});
const fetchTodoList = async () => {
const response = await api.get('/todos');
state.todoList = response.data;
};
onBeforeMount(async () => {
await fetchTodoList();
});
return {
state,
/**
* 送信された時
* @param {string} text - テキスト
*/
async onSubmit(text) {
await api.post('/todos', {
text,
});
await fetchTodoList();
},
/**
* TODOのチェック
* @param {number} todoId - TODOのID
* @param {boolean} isNextDone - 次更新する終了ステータス
*/
async onCheckTodo(todoId, isNextDone) {
await api.put(`/todos/check/${todoId}`, {
isDone: isNextDone,
});
await fetchTodoList();
},
/**
* TODOの削除ボタンがクリックされた時
* @param {number} todoId - TODOのID
*/
async onDeleteTodo(todoId) {
await api.delete(`/todos/${todoId}`);
await fetchTodoList();
},
}
},
});
app.mount('#app');
</script>
</body>
</html>
Heroku에게 프러포즈
DB 서버 공식 사용 준비
마지막으로 헤로쿠를 디버깅하고 그 전에 정식 공연용 DB를 준비한다.예전에는 mLab이라는 아드레날린이 있었는데 지금은 없어졌다
MongoDB Atlas
.여기 기사를 참고해서 컬렉션을 만들어 보세요.
제작이 완료되면 연결 정보가 표시되면 여기의 URI를 복사합니다.그러나 비밀번호
<password>
가 포함되지 않기 때문에 설정된 비밀번호로 바꿔야 한다.URI 취득 후 다음 연결 몬godB 연결 방법을 전환할 수 있는지 확인합니다.
server/server.ts
// 一旦本番DBに接続する
if (process.env.NODE_ENV !== 'production') {
const MONGODB_URI = '<password>を書き換えてコピーしたURI';
mongoose.connect(MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: 'todo-app',
})
.catch((err) => {
console.error(err);
});
}
Heroku 환경 변수의 설정과 디자인
Heroku 응용 프로그램을 만들면 Settings 페이지에 환경 변수를 추가합니다.
이름은 무엇이든지 가능하지만 여기
MONGODB_URI
의 이름으로 저장합니다.이 환경 변수를 사용하여 액세스할 코드를 덮어씁니다.
server/server.ts
// 本番じゃない時はローカルのDBに接続する
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config({ path: path.resolve(__dirname, '../mongodb/.env') });
const { MONGODB_USERNAME, MONGODB_PASSWORD } = process.env;
mongoose.connect('mongodb://localhost:27017', {
useNewUrlParser: true,
useUnifiedTopology: true,
user: MONGODB_USERNAME,
pass: MONGODB_PASSWORD,
dbName: 'todo-app',
})
.catch((err) => {
console.error(err);
});
-}
+} else {
+ mongoose.connect(process.env.MONGODB_URI || '', {
+ useNewUrlParser: true,
+ useUnifiedTopology: true,
+ dbName: 'todo-app',
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+}
이후 히로쿠의 디자인이 완성됐다.끝맺다
이상은 Docker를 사용하여 MongoDB 서버 개발을 시작하고 Heroku 디버깅을 할 때까지 하는 절차입니다.예전에는 mLab이라는 공짜 Mongodb adion이 있었는데, Heroku를 사용하면 좋을 것 같았는데, 지금은 그 은혜를 받지 못한 것 같다.DB를 공짜로 쓰는 게 어려워요?만약 작은 데이터가 무료 파일이라면 Firebase는 더욱 쉽게 할 수 있고 DB를 일부러 사용한다는 뜻은😅
필요하신지 모르겠는데 누구한테 도움이 됐으면 좋겠어요.
Reference
이 문제에 관하여(Docker로 MongoDB를 시작하여 Heroku가 디버깅할 때까지 개발), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/wintyo/articles/14abaa91975e22텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)