LifeSports Application(ReactNative & Nest.js) - 11. wayfinding-service

#1 wayfinding-service

wayfinding-service는 공공데이터를 http 로컬 서버로 만들기 위한 서비스입니다.

  • 데이터는 github wayfinding-service data폴더에 있습니다.

http 로컬 서버에 모든 데이터를 띄우고 이후에 만들 map-service에서 이 데이터를 받아오도록 하겠습니다.

nest new wayfinding-service
nest generate module wayfinding
nest generate service wayfinding

wayfinding-service의 포트번호를 7900번으로 설정하겠습니다.

  • ./src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.enableCors();
  
  await app.listen(7900);
}
bootstrap();

mongodb compass를 이용하여 데이터를 넣어주도록 하겠습니다. mongodb compass에서는 import를 통해 json, execl 데이터를 한번에 넣어줄 수 있습니다.


app.controller에서 데이터를 띄우기 위한 엔드포인트를 만들도록 하겠습니다.

  • ./src/app.controller.ts
import { Controller, Get, HttpStatus, Param, Query } from '@nestjs/common';
import { Builder } from 'builder-pattern';
import { statusConstants } from './constants/status.constant';
import { ResponseMaps } from './vo/response.maps';
import { WayfindingService } from './wayfinding/wayfinding.service';

@Controller("wayfinding-service")
export class AppController {
    constructor(private readonly wayfindingService: WayfindingService) {}

    @Get('/')
    public async getAll(): Promise<any> {
        try {
            const result: any = await this.wayfindingService.getAll();
            
            if(result.status === statusConstants.ERROR) {
                return await Object.assign({
                    status: HttpStatus.INTERNAL_SERVER_ERROR,
                    payload: null,
                    message: "Error message: " + result.message
                });
            }

            const responseMaps: Array<ResponseMaps> = [];

            for(const el of result.payload) {
                responseMaps.push(el);
            }

            return await Object.assign({
                status: HttpStatus.OK,
                payload: responseMaps,
                message: "Get list of map"
            });
        } catch(err) {
            return await Object.assign({
                status: HttpStatus.BAD_REQUEST,
                payload: null,
                message: "Error message: " + err
            });
        }
    }

    @Get(':_id')
    public async getOne(@Param('_id') _id: string): Promise<any> {
        try {
            const result: any = await this.wayfindingService.getOne(_id);

            if(result.status === statusConstants.ERROR)  {
                return await Object.assign({
                    status: statusConstants.ERROR,
                    payload: null,
                    message: "Server error!"
                });
            }

            console.log(result);

            return await Object.assign({
                status: HttpStatus.OK,
                payload: Builder(ResponseMaps)._id(result.payload._id)
                                              .ycode(result.payload.ycode)
                                              .type_nm(result.payload.type_nm)
                                              .gu_nm(result.payload.gu_nm)
                                              .parking_lot(result.payload.parking_lot)
                                              .bigo(result.payload.bigo)
                                              .xcode(result.payload.xcode)
                                              .tel(result.payload.tel)
                                              .addr(result.payload.addr)
                                              .in_out(result.payload.in_out)
                                              .home_page(result.payload.home_page)
                                              .edu_yn(result.payload.edu_yn)
                                              .nm(result.payload.nm)
                                              .build(),
                message: "Get data by _id"
            });
        } catch(err) {
            return await Object.assign({
                status: statusConstants.ERROR,
                payload: null,
                message: "Server error!"
            });
        }
    }

    @Get('/list')
    public async getListByTypeNm(@Query('type_nm') type_nm: string): Promise<any> {
        try {
            console.log(type_nm);

            const result: any = await this.wayfindingService.getListByTypeNm(type_nm);
            
            if(result.status === statusConstants.ERROR) {
                return await Object.assign({
                    status: HttpStatus.INTERNAL_SERVER_ERROR,
                    payload: null,
                    message: "Error message: " + result.message
                });
            }

            const responseMaps: Array<ResponseMaps> = [];

            for(const el of result.payload) {
                responseMaps.push(el);
            }

            return await Object.assign({
                status: HttpStatus.OK,
                payload: responseMaps,
                message: "Get list of map"
            });
        } catch(err) {
            return await Object.assign({
                status: HttpStatus.BAD_REQUEST,
                payload: null,
                message: "Error message: " + err
            });
        }
    }
    
    @Get('/list')
    public async getListByGuNm(@Query('gu_nm') gu_nm: string) : Promise<any> {
        try {
            const result: any = await this.wayfindingService.getListByGuNm(gu_nm);
            
            if(result.status === statusConstants.ERROR) {
                return await Object.assign({
                    status: HttpStatus.INTERNAL_SERVER_ERROR,
                    payload: null,
                    message: "Error message: " + result.message
                });
            }

            const responseMaps: Array<ResponseMaps> = [];

            for(const el of result.payload) {
                responseMaps.push(el);
            }

            return await Object.assign({
                status: HttpStatus.OK,
                payload: responseMaps,
                message: "Get list of map"
            });
        } catch(err) {
            return await Object.assign({
                status: HttpStatus.BAD_REQUEST,
                payload: null,
                message: "Error message: " + err
            });
        }
    }

    @Get('/list')
    public async getListGuNmAndTypeNm(
        @Query('gu_nm') gu_nm: string,
        @Query('type_nm') type_nm: string
    ): Promise<any> {
        try {
            const result: any = await this.wayfindingService.getListGuNmAndTypeNm(gu_nm, type_nm);
            
            if(result.status === statusConstants.ERROR) {
                return await Object.assign({
                    status: HttpStatus.INTERNAL_SERVER_ERROR,
                    payload: null,
                    message: "Error message: " + result.message
                });
            }

            const responseMaps: Array<ResponseMaps> = [];

            for(const el of result.payload) {
                responseMaps.push(el);
            }

            return await Object.assign({
                status: HttpStatus.OK,
                payload: responseMaps,
                message: "Get list of map"
            });
        } catch(err) {
            return await Object.assign({
                status: HttpStatus.BAD_REQUEST,
                payload: null,
                message: "Error message: " + err
            });
        }
    }
}

컨트롤러의 메서드는 총 5가지입니다. 우선 rest endpoint로 설정을 하고, 후에 map-service를 구현하면서 map-service와 wayfinding-service 간 메시지 브로커 기반으로 통신할 수 있게 수정하도록 하겠습니다.

1) getAll: 전체 맵 리스트를 가져오는 메서드입니다.
2) getOne: 마커 클릭 시 해당 맵의 상세 데이터를 불러오는 메서드입니다.
3) getListByTypeNm: type_nm(운동장소)를 기반으로 맵 리스트를 가져오는 메서드입니다.
4) getListByGuNm: gu_nm(구)를 기반으로 맵 리스트를 가져오는 메서드입니다.
5) getListGuNmAndType: gu_nm, type_nm을 기반으로 맵 리스트를 가져오는 메서드입니다.

그러면 dto, vo, schema를 작성하도록 하겠습니다.

#2 mongoose설치

npm install --save @nestjs/mongoose mongoose
  • ./src/app.modules.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { WayfindingModule } from './wayfinding/wayfinding.module';

@Module({
  imports: [
    MongooseModule.forRoot("mongodb://localhost:27017/WAYFINDINGSERVICE?readPreference=primary&appname=MongoDB%20Compass&directConnection=true&ssl=false"),
    WayfindingModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
  • ./src/wayfinding/wayfinding.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { Maps, MapsSchema } from 'src/schema/maps.schema';
import { WayfindingService } from './wayfinding.service';

@Module({
  imports: [MongooseModule.forFeature([{
    name: Maps.name,
    schema: MapsSchema
  }])],
  providers: [WayfindingService],
  exports: [WayfindingService],
})
export class WayfindingModule {}

앞서 compass를 이용하여 맵 데이터를 mongodb에 저장하는 작업을 진행하였습니다. 그러면 이 데이터를 가져오기 위해 mongoose를 연동하여 wayfinding-service에서 schema를 작성하도록 하겠습니다.

npm install --save class-transformer builder-pattern
  • ./src/schema/maps.schema.ts
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Transform } from "class-transformer";

export type MapsDocument = Maps & Document;

@Schema()
export class Maps {
    @Transform(({ value }) => value.toString())
    _id: String;
    
    @Prop({ required: true })
    ycode: Number;

    @Prop({ required: true })
    type_nm: string;

    @Prop({ required: true })
    gu_nm: string;

    @Prop({ required: true })
    parking_lot: string;

    @Prop({ required: true })
    bigo: string;

    @Prop({ required: true })
    xcode: Number;

    @Prop({ required: true })
    tel: string;
    
    @Prop({ required: true })
    addr: string;

    @Prop({ required: true })
    in_out: string;

    @Prop({ required: true })
    home_page: string;

    @Prop({ required: true })
    edu_yn: string;

    @Prop({ required: true })
    nm: string;
}

export const MapsSchema = SchemaFactory.createForClass(Maps);

MapsSchema의 문서 구조는 mongodb에 저장된 문서 데이터 타입을 그대로 이용하였습니다.

vo, dto클래스를 작성하도록 하겠습니다.

  • ./src/vo/response.vo.ts
export class ResponseMaps {
    _id: string;
    
    ycode: Number;

    type_nm: string;

    gu_nm: string;

    parking_lot: string;

    bigo: string;

    xcode: Number;

    tel: string;
    
    addr: string;

    in_out: string;

    home_page: string;

    edu_yn: string;

    nm: string;
}
  • ./src/dto/maps.dto.ts
export class MapsDto {
    _id: string;
    
    ycode: Number;

    type_nm: string;

    gu_nm: string;

    parking_lot: string;

    bigo: string;

    xcode: Number;

    tel: string;
    
    addr: string;

    in_out: string;

    home_page: string;

    edu_yn: string;

    nm: string;
}

작성한 코드들을 이용하여 컨트롤러에 클래스를 import를 하고나면 service클래스만 오류가 나타날텐데 service클래스도 마저 작성하고 테스트를 진행해보도록 하겠습니다.

  • ./src/wayfinding/wayfinding.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { statusConstants } from 'src/constants/status.constant';
import { MapsDto } from 'src/dto/maps.dto';
import { Maps, MapsDocument } from 'src/schema/maps.schema';

@Injectable()
export class WayfindingService {
    constructor(@InjectModel(Maps.name) private mapsModel: Model<MapsDocument>) {}

    public async getAll(): Promise<any> {
        try {
            const maps = await this.mapsModel.find();

            if(!maps) {
                return await Object.assign({
                    status: statusConstants.ERROR,
                    payload: null,
                    message: "Database error!"
                });
            }

            const result: Array<MapsDto> = [];

            for(const element of maps) {
                result.push(element);
            }

            return await Object.assign({
                status: statusConstants.SUCCESS,
                payload: result,
                message: "Successfully get list of map in database"
            });
        } catch(err) {
            return await Object.assign({
                status: statusConstants.ERROR,
                payload: null,
                message: "Database error!"
            });
        }
    }

    public async getOne(_id: string): Promise<any> {
        try {
            const map = await this.mapsModel.findOne({ _id: _id });

            if(!map) {
                return await Object.assign({
                    status: statusConstants.ERROR,
                    payload: null,
                    message: "Database error!"
                });
            }

            return await Object.assign({
                status: statusConstants.SUCCESS,
                payload: map,
                message: "Successfully get one data in database"
            });
        } catch(err) {
            return await Object.assign({
                status: statusConstants.ERROR,
                payload: null,
                message: "Database error!"
            });
        }
    }

    public async getListByTypeNm(type_nm: string): Promise<any> {
        try {
            const maps = await this.mapsModel.find({ type_nm: type_nm });

            if(!maps) {
                return await Object.assign({
                    status: statusConstants.ERROR,
                    payload: null,
                    message: "Database error!"
                });
            }

            const result: Array<MapsDto> = [];

            for(const element of maps) {
                result.push(element);
            }

            return await Object.assign({
                status: statusConstants.SUCCESS,
                payload: result,
                message: "Successfully get list of map in database"
            });
        } catch(err) {
            return await Object.assign({
                status: statusConstants.ERROR,
                payload: null,
                message: "Database error!"
            });
        }
    }

    public async getListByGuNm(gu_nm: string): Promise<any> {
        try {
            const maps = await this.mapsModel.find({ gu_nm: gu_nm });

            if(!maps) {
                return await Object.assign({
                    status: statusConstants.ERROR,
                    payload: null,
                    message: "Database error!"
                });
            }

            const result: Array<MapsDto> = [];

            for(const element of maps) {
                result.push(element);
            }

            return await Object.assign({
                status: statusConstants.SUCCESS,
                payload: result,
                message: "Successfully get list of map in database"
            });
        } catch(err) {
            return await Object.assign({
                status: statusConstants.ERROR,
                payload: null,
                message: "Database error!"
            });
        }
    }

    public async getListGuNmAndTypeNm(gu_nm: string, type_nm: string): Promise<any> {
        try {
            const maps = await this.mapsModel.find({ 
                gu_nm: gu_nm,
                type_nm: type_nm
            });

            if(!maps) {
                return await Object.assign({
                    status: statusConstants.ERROR,
                    payload: null,
                    message: "Database error!"
                });
            }

            const result: Array<MapsDto> = [];

            for(const element of maps) {
                result.push(element);
            }

            return await Object.assign({
                status: statusConstants.SUCCESS,
                payload: result,
                message: "Successfully get list of map in database"
            });
        } catch(err) {
            return await Object.assign({
                status: statusConstants.ERROR,
                payload: null,
                message: "Database error!"
            });
        }
    }
}

service클래스는 굉장히 단순합니다. find메서드를 이용하여 전달받은 인자를 조건으로 사용하여 그에 맞는 리스트를 가져오도록 합니다. 테스트를 진행하도록 하겠습니다.

#3 테스트

1) GET /wayfinding-service/

2) GET /wayfinding-service/map/:_id

3) GET /wayfinding-service/maps/list-type-nm/:type_nm

4) GET /wayfinding-service/maps/list-gu-nm/:gu_nm

5) GET /wayfinding-service/maps/list-gu-type?query-string

테스트 결과 데이터를 잘 받아오는 모습을 볼 수 있습니다.

다음 포스트에서는 map-service를 만들고 wayfinding-service와 통신을 진행해보도록 하겠습니다.

좋은 웹페이지 즐겨찾기