Seize the day

POST : Backend study

Nodejs with NestJS + Mongodb in docker연결 테스트..

Mongodb는 docker로 구성하고, Node는 Mac에 설치하여 접근하다.
지난번에 스터디하면서 만들었던 docker-compose.yml를 그대로 사용한다. 

# Use root/example as user/password credentials
version: '3.1'

services:

  mongo:
    image: mongo:6.0.2
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
    volumes:
      - ~/mongo-storage:/data/db
    ports:
      - 27017:27017
    networks:
      - backend

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 27018:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: example
      ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/
    depends_on:
      - mongo
    networks:
      - backend

networks:
  backend:
    driver: bridge
    external: true
    internal: true

docker-compose up -d
로 mongo 서버 실행

testdb를 사용할 예정이다. 접근하기위해서는 testdb에 접근할 user/pwd를 생성해야 한다. mongosh가 db접속을 위한 클라이언트 실행 이름이다. 

dajkim76@Kims-Mac-mini ~/docker/backend-mongo % docker ps
CONTAINER ID   IMAGE           COMMAND                  CREATED       STATUS          PORTS                      NAMES
d891f0402d03   mongo-express   "tini -- /docker-ent…"   2 weeks ago   Up 53 minutes   0.0.0.0:27018->8081/tcp    backend-mongo-mongo-express-1
745448f94859   mongo:6.0.2     "docker-entrypoint.s…"   2 weeks ago   Up 53 minutes   0.0.0.0:27017->27017/tcp   backend-mongo-mongo-1
dajkim76@Kims-Mac-mini ~/docker/backend-mongo % docker exec -i -t backend-mongo-mongo-1 bash
root@745448f94859:/# mongosh -u root -p example
Current Mongosh Log ID:	63688a63565e9961937cc45a
Connecting to:		mongodb://<credentials>@127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.6.0
Using MongoDB:		6.0.2
Using Mongosh:		1.6.0

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

test> db.createUser( { user: "testuser", pwd: "testpwd", roles: [ { role: "readWrite", db: "testdb" }, ] } )
{ ok: 1 }

 

노드에서 연결 테스트..

const mongoose = require('mongoose');

mongoose.Promise = global.Promise;
const MONGO_HOST = "localhost";
const MONGO_URI = `mongodb://testuser:testpwd@${MONGO_HOST}:27017/testdb`;

function onMongoConnected() {
  console.log('Successfully connected to mongodb')
  
  const Cat = mongoose.model('Cat', { name: String });
  const kitty = new Cat({ name: 'Zildjian' });
  kitty.save().then(() => console.log('meow'));
}

console.log('Connecting mongo server')
mongoose.connect(MONGO_URI)
  .then(onMongoConnected)
  .catch(e => console.error(e));

Node js에서 접근시 인증 에러가 난다.  MongoServerError: Authentication failed.

 

테스트 삼아서.. 
test> db.createUser( { user: "testuser_1", pwd: "testpwd_1", roles: [ { role: "readWrite", db: "testdb" }, ] } )
{ ok: 1 }

서버에 adbmin db에저장된 document를 보면 

roles에 db는 testdb로 저장되어 있지만 위에 db는 "test"로 저장되어 있는데 이게 문제 인듯하다.  위에 db는 createUser시에 파라미터로 넣을 수 없다. 넣으면 에러난다.  따라서 mongo-express에서 document를  "testdb"로 수정하니 인증 오류가 해결되었다. 왜 이런 문제가 생기는지는 잘 모르겠다.  db이름에 끝에 db가 들어가서 문제가 되는듯..

2022/11/14일 추가 
testdb를 선택하지 않아 생기는 문제였다. use testdb 하고 나서 createUser하면 정상적으로 유저가 등록된다...

test> use testdb
switched to db testdb
testdb> db.createUser( { user: "testuser_2", pwd: "testpwd_2", roles: [ { role: "readWrite", db: "testdb" }, ] } )
{ ok: 1 }
testdb>

 

 

Nest JS로 테스트 서버 만들기

Nest jS는 가이드는 https://docs.google.com/presentation/d/1HKl1_lRTfe2RktueA3rWZ50XUUH1DZO6Nre8Ioeb1Os/edit?usp=sharing 이 문서를 참고..

 

Nest JS

Beginning NestJS Make REST API Server

docs.google.com

 

서버  시작시 db 연결하기
main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
const mongoose = require('mongoose');

mongoose.Promise = global.Promise;

const MONGO_HOST = "localhost";
const MONGO_URI = `mongodb://testuser:testpwd@${MONGO_HOST}:27017/testdb`;

mongoose.connect(MONGO_URI)
.then(() => console.log('Successfully connected to mongodb'))
.catch(error => console.error(error))

console.log('Starting nest server port: 3000')
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

 

db 모델을 하나 정의
db_models.ts

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    name: {
        type :String,       
    },

 },{collection:'user_list',
    versionKey: false
 });

 export const User = mongoose.model('User', userSchema)

 

localhost:3000/hello/홍길동
을 입력하면 user_list 콜렉션에 저장되도독
app.controller.ts

import { Controller, Get, Param } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/hello/:name')
  getHello(@Param('name') name: string): string {
    return this.appService.getHello(name);
  }
}

app.service.ts

import { Injectable } from '@nestjs/common';
import { User } from './db_models';

@Injectable()
export class AppService {
  getHello(name: string): string {

    const user = new User({ name: name });
    user.save().then(() => console.log(name + ' saved'));
    
    return 'Hello! ' + name;
  }
}

 

auto increment 자체 구현

서버 db는 여러 유저의 데이타를 가지고 있는데, 유저마다 db를 생성할 수 없기 때문에, collection마다 이 documnet가 누구의 db인지를 구분할는 필드를 넣었다.  유저를 구분하는 용도로 did를 사용한다. 
cid는 collection별로 고정된 값을 가진다. 하나의 콜렉션에서 유저별로 서로 다른 고유하게 증가하는 sequence를 만들기 위한 테스트 작업이다. 

db_models.ts 일부 추가

 const tsListSchema = new mongoose.Schema({
    did: { type: Number, require: true}, // db id
    cid: { type: Number, require: true}, // collection id
    ts: { type: Number, default: 0 }
},{ collection:'ts_list',
    versionKey: false
});

const TsList = mongoose.model('TsList', tsListSchema);

// ref: https://mongoosejs.com/docs/tutorials/findoneandupdate.html
export async function getNextTs(cid: number, did: number) : Promise<Number | undefined> {
    const doc = await TsList.findOneAndUpdate({cid: cid, did: did}, {$inc: { ts: 1} }, {new: true, upsert: true}).exec()
    return doc.ts
}

cid와 did가 일치한 ({cid: .. , did: ...})
document를 찾아서 ts를 1 증가시키고 ($inc ...)
없으면 새로 만든다.(new: true) 
ts가 증가된 document를 리턴한다. (upsert: true)

app.controller.ts

@Get('/hello/:name')
  async getHello(@Param('name') name: string): Promise<string> {
    return this.appService.getHello(name);
  }

async를 지원하도록 수정한다. 

app.service.ts

import { Injectable } from '@nestjs/common';
import { User , getNextTs} from './db_models';

@Injectable()
export class AppService {
  async getHello(name: string): Promise<string> {

    const nextTs = await getNextTs(1, 1)
    console.log("nextTs:" + nextTs)
    const user = new User({ name: name, did: 1, ts: nextTs });
    user.save().then(() => console.log(name + ' saved'));
    
    return 'Hello! ' + name;
  }
}

 

_id 대신에 Number 타입의 id를 쓰기.  auto increment와 연계해서 만들 수는 있으나 _id  입력시 필드는 unique해야 한다 

const userSchema = new mongoose.Schema({
    // _id: ObjectId('6368d64ea66f9be19eee81c2'), 대신에 Number를 쓰겠다.
    _id: {
        type: Number
    },
    name: {
        type :String,       
    },
    did: {
        type: Number,
    },
    ts: {
        type: Number,
    }
 },{_id: false, //_id: ObjectId('6368d64ea66f9be19eee81c2'), 형태는 쓰지 않겠다.
    collection:'user_list',
    versionKey: false
 });

 

_id는 그대로 두고, id를 새로 만든다. id는 중복가능하지만 did와 id의 조합은 unique하다. 이것을 createIndex 문으로 만들 수 있다.  mongosh에서 
db.id_list.createIndex( {cid: 1, did: 1}, { unique: true } )
db.user_list.createIndex({id: 1, did: 1}, { unique: true } )

const userSchema = new mongoose.Schema({
    id: {
        type: Number
    },
    name: {
        type :String,       
    },
    did: {
        type: Number,
    },
    ts: {
        type: Number,
    }
 },{
    collection:'user_list',
    versionKey: false
 });


const nextIdSchema = new mongoose.Schema({
    did: { type: Number, require: true}, // db id
    cid: { type: Number, require: true}, // collection id
    id: { type: Number, default: 0 }
},{ collection:'id_list',
    versionKey: false
});

const NextIdCollection = mongoose.model('NextId', nextIdSchema);

export async function getNextId(cid: number, did: number) : Promise<Number | undefined> {
    const doc = await NextIdCollection.findOneAndUpdate({cid: cid, did: did}, {$inc: { id: 1} }, {new: true, upsert: true}).exec()
    return doc.id
}

 

app.service.ts 수정

import { Injectable } from '@nestjs/common';
import { User , getNextTs, getNextId} from './db_models';

@Injectable()
export class AppService {
  async getHello(name: string): Promise<string> {

    const nextId = await getNextId(1, 1)
    const nextTs = await getNextTs(1, 1)
    console.log("nextTs:" + nextTs)
    const user = new User({ id: nextId, name: name, did: 1, ts: nextTs });
    user.save().then(() => console.log(name + ' saved'));
    
    return 'Hello! ' + name;
  }
}

id도 1씩 증가하고 ts도 1씩 증가한다. 사실 같은 원리다. 

top

posted at

2022. 11. 7. 13:36


CONTENTS

Seize the day
BLOG main image
김대정의 앱 개발 노트와 사는 이야기
RSS 2.0Tattertools
공지
아카이브
최근 글 최근 댓글
카테고리 태그 구름사이트 링크