From 0afe5b19f40f0e5e9391031027cc8b43215f47c3 Mon Sep 17 00:00:00 2001 From: jinddings Date: Wed, 13 Nov 2024 19:01:22 +0900 Subject: [PATCH 01/21] =?UTF-8?q?=E2=9C=A8=20feat=20:=20redis=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=B4=20=EC=B5=9C=EA=B7=BC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EB=8B=A8=EC=96=B4=20=EC=A0=80=EC=9E=A5(#57):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/package-lock.json | 79 +++++++++++++++++++ BE/package.json | 1 + BE/src/app.module.ts | 2 + BE/src/auth/strategy/jwt.strategy.ts | 7 +- BE/src/common/redis/redis.module.ts | 22 ++++++ BE/src/common/redis/redis.provider.ts | 14 ++++ BE/src/common/redis/redis.ts | 45 +++++++++++ .../list/interface/search-params.interface.ts | 1 + BE/src/stock/list/stock-list.controller.ts | 9 ++- BE/src/stock/list/stock-list.module.ts | 6 +- BE/src/stock/list/stock-list.service.ts | 10 ++- 11 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 BE/src/common/redis/redis.module.ts create mode 100644 BE/src/common/redis/redis.provider.ts create mode 100644 BE/src/common/redis/redis.ts diff --git a/BE/package-lock.json b/BE/package-lock.json index f9b6300f..b443ce76 100644 --- a/BE/package-lock.json +++ b/BE/package-lock.json @@ -32,6 +32,7 @@ "dotenv": "^16.4.5", "express": "^4.21.1", "fastify-swagger": "^5.1.1", + "ioredis": "^5.4.1", "mysql2": "^3.11.3", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -822,6 +823,12 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "license": "MIT" + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "license": "ISC", @@ -3630,6 +3637,15 @@ "node": ">=0.8" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "devOptional": true, @@ -6407,6 +6423,30 @@ "node": ">= 0.4" } }, + "node_modules/ioredis": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "license": "MIT", @@ -7813,11 +7853,23 @@ "version": "4.17.21", "license": "MIT" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -9084,6 +9136,27 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.2.2", "license": "Apache-2.0" @@ -9911,6 +9984,12 @@ "node": ">=8" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.1", "license": "MIT", diff --git a/BE/package.json b/BE/package.json index 86c3538b..9bff210b 100644 --- a/BE/package.json +++ b/BE/package.json @@ -43,6 +43,7 @@ "dotenv": "^16.4.5", "express": "^4.21.1", "fastify-swagger": "^5.1.1", + "ioredis": "^5.4.1", "mysql2": "^3.11.3", "passport": "^0.7.0", "passport-jwt": "^4.0.1", diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index ca5abc62..27220f4b 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -14,6 +14,7 @@ import { StockDetailModule } from './stock/detail/stock-detail.module'; import { typeOrmConfig } from './configs/typeorm.config'; import { StockListModule } from './stock/list/stock-list.module'; import { StockTradeHistoryModule } from './stock/trade/history/stock-trade-history.module'; +import { RedisModule } from './common/redis/redis.module'; @Module({ imports: [ @@ -29,6 +30,7 @@ import { StockTradeHistoryModule } from './stock/trade/history/stock-trade-histo StockOrderModule, StockListModule, StockTradeHistoryModule, + RedisModule, ], controllers: [AppController], providers: [AppService], diff --git a/BE/src/auth/strategy/jwt.strategy.ts b/BE/src/auth/strategy/jwt.strategy.ts index e6e85d05..7f8f5d2a 100644 --- a/BE/src/auth/strategy/jwt.strategy.ts +++ b/BE/src/auth/strategy/jwt.strategy.ts @@ -23,6 +23,11 @@ export class JwtStrategy extends PassportStrategy(Strategy) { const user: User = await this.userRepository.findOne({ where: { email } }); if (!user) throw new UnauthorizedException(); - return user; + return { + userId: user.id, + email: user.email, + tutorial: user.tutorial, + kakaoId: user.kakaoId, + }; } } diff --git a/BE/src/common/redis/redis.module.ts b/BE/src/common/redis/redis.module.ts new file mode 100644 index 00000000..d8aa958c --- /dev/null +++ b/BE/src/common/redis/redis.module.ts @@ -0,0 +1,22 @@ +// src/common/redis/redis.module.ts +import { Global, Module } from '@nestjs/common'; +import Redis from 'ioredis'; +import { RedisUtil } from './redis'; + +@Global() +@Module({ + providers: [ + { + provide: 'REDIS_CLIENT', + useFactory: () => { + return new Redis({ + host: process.env.REDIS_HOST, + port: Number(process.env.REDIS_PORT), + }); + }, + }, + RedisUtil, + ], + exports: [RedisUtil, 'REDIS_CLIENT'], +}) +export class RedisModule {} diff --git a/BE/src/common/redis/redis.provider.ts b/BE/src/common/redis/redis.provider.ts new file mode 100644 index 00000000..28e1471d --- /dev/null +++ b/BE/src/common/redis/redis.provider.ts @@ -0,0 +1,14 @@ +import { Provider } from '@nestjs/common'; +import Redis from 'ioredis'; +import dotenv from 'dotenv'; + +dotenv.config(); +export const RedisProvider: Provider = { + provide: 'REDIS_CLIENT', + useFactory: () => { + return new Redis({ + host: process.env.REDIS_HOST, + port: Number(process.env.REDIS_PORT), + }); + }, +}; diff --git a/BE/src/common/redis/redis.ts b/BE/src/common/redis/redis.ts new file mode 100644 index 00000000..e2bee153 --- /dev/null +++ b/BE/src/common/redis/redis.ts @@ -0,0 +1,45 @@ +import { Injectable, Inject } from '@nestjs/common'; +import Redis from 'ioredis'; + +@Injectable() +export class RedisUtil { + constructor( + @Inject('REDIS_CLIENT') + private readonly redis: Redis, + ) {} + + async get(key: string): Promise { + return this.redis.get(key); + } + + async set(key: string, value: string, expires?: number): Promise<'OK'> { + if (expires) { + return this.redis.set(key, value, 'EX', expires); + } + return this.redis.set(key, value); + } + + async del(key: string): Promise { + return this.redis.del(key); + } + + async zadd(key: string, score: number, member: string): Promise { + return this.redis.zadd(key, score, member); + } + + async zrange(key: string, start: number, stop: number): Promise { + return this.redis.zrange(key, start, stop); + } + + async zrevrange(key: string, start: number, stop: number): Promise { + return this.redis.zrevrange(key, start, stop); + } + + async zrem(key: string, member: string): Promise { + return this.redis.zrem(key, member); + } + + async expire(key: string, seconds: number): Promise { + return this.redis.expire(key, seconds); + } +} diff --git a/BE/src/stock/list/interface/search-params.interface.ts b/BE/src/stock/list/interface/search-params.interface.ts index 88080d0a..879f55b9 100644 --- a/BE/src/stock/list/interface/search-params.interface.ts +++ b/BE/src/stock/list/interface/search-params.interface.ts @@ -2,4 +2,5 @@ export interface SearchParams { name?: string; market?: string; code?: string; + userId: number; } diff --git a/BE/src/stock/list/stock-list.controller.ts b/BE/src/stock/list/stock-list.controller.ts index 427f9ff8..d7272784 100644 --- a/BE/src/stock/list/stock-list.controller.ts +++ b/BE/src/stock/list/stock-list.controller.ts @@ -1,5 +1,7 @@ -import { Controller, Get, Query } from '@nestjs/common'; +import { Controller, Get, Query, Req, UseGuards } from '@nestjs/common'; import { ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { Request } from 'express'; +import { JwtAuthGuard } from 'src/auth/jwt-auth-guard'; import { StockListService } from './stock-list.service'; import { StockListResponseDto } from './dto/stock-list-response.dto'; @@ -32,12 +34,15 @@ export class StockListController { @ApiQuery({ name: 'market', required: false }) @ApiQuery({ name: 'code', required: false }) @Get('/search') + @UseGuards(JwtAuthGuard) async searchWithQuery( + @Req() req: Request, @Query('name') name?: string, @Query('market') market?: string, @Query('code') code?: string, ): Promise { - return this.stockListService.search({ name, market, code }); + const userId = parseInt(req.user.userId, 10); + return this.stockListService.search({ name, market, code, userId }); } @ApiOperation({ diff --git a/BE/src/stock/list/stock-list.module.ts b/BE/src/stock/list/stock-list.module.ts index 9087c692..9c2c79e0 100644 --- a/BE/src/stock/list/stock-list.module.ts +++ b/BE/src/stock/list/stock-list.module.ts @@ -1,14 +1,16 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { RedisUtil } from 'src/common/redis/redis'; +import { RedisModule } from 'src/common/redis/redis.module'; import { StockListRepository } from './stock-list.repostiory'; import { StockListService } from './stock-list.service'; import { StockListController } from './stock-list.controller'; import { Stocks } from './stock-list.entity'; @Module({ - imports: [TypeOrmModule.forFeature([Stocks])], + imports: [TypeOrmModule.forFeature([Stocks]), RedisModule], controllers: [StockListController], - providers: [StockListRepository, StockListService], + providers: [StockListRepository, StockListService, RedisUtil], exports: [], }) export class StockListModule {} diff --git a/BE/src/stock/list/stock-list.service.ts b/BE/src/stock/list/stock-list.service.ts index 1c46c5bb..5868d9a8 100644 --- a/BE/src/stock/list/stock-list.service.ts +++ b/BE/src/stock/list/stock-list.service.ts @@ -1,4 +1,5 @@ import { Injectable, NotFoundException } from '@nestjs/common'; +import { RedisUtil } from 'src/common/redis/redis'; import { StockListRepository } from './stock-list.repostiory'; import { Stocks } from './stock-list.entity'; import { StockListResponseDto } from './dto/stock-list-response.dto'; @@ -6,7 +7,10 @@ import { SearchParams } from './interface/search-params.interface'; @Injectable() export class StockListService { - constructor(private readonly stockListRepository: StockListRepository) {} + constructor( + private readonly stockListRepository: StockListRepository, + private readonly redisUtil: RedisUtil, + ) {} private toResponseDto(stock: Stocks): StockListResponseDto { return new StockListResponseDto(stock.code, stock.name, stock.market); @@ -27,6 +31,10 @@ export class StockListService { } async search(params: SearchParams): Promise { + const key = `search:${params.userId}`; + const score = Date.now(); + + await this.redisUtil.zadd(key, score, JSON.stringify(params)); const stocks = await this.stockListRepository.search(params); return stocks.map((stock) => this.toResponseDto(stock)); } From a6170383e2d1a691fc87f3a1144fe7f133496bbb Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 17:25:05 +0900 Subject: [PATCH 02/21] =?UTF-8?q?=E2=9C=A8=20feat:=20API=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=EB=A5=BC=20=EC=9C=84=ED=95=B4=20chart=20=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ....dto.ts => stock-detail-chart-data.dto.ts} | 2 +- .../dto/stock-detail-chart-response.dto.ts | 5 + .../detail/dto/stock-detail-response.dto.ts | 14 --- .../stock/detail/stock-detail.controller.ts | 6 +- BE/src/stock/detail/stock-detail.service.ts | 98 +++++++------------ 5 files changed, 42 insertions(+), 83 deletions(-) rename BE/src/stock/detail/dto/{stock-detail-output2.dto.ts => stock-detail-chart-data.dto.ts} (96%) create mode 100644 BE/src/stock/detail/dto/stock-detail-chart-response.dto.ts delete mode 100644 BE/src/stock/detail/dto/stock-detail-response.dto.ts diff --git a/BE/src/stock/detail/dto/stock-detail-output2.dto.ts b/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts similarity index 96% rename from BE/src/stock/detail/dto/stock-detail-output2.dto.ts rename to BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts index c6df900f..fd108090 100644 --- a/BE/src/stock/detail/dto/stock-detail-output2.dto.ts +++ b/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export class InquirePriceOutput2Dto { +export class InquirePriceChartDataDto { @ApiProperty({ description: '주식 영업 일자' }) stck_bsop_date: string; diff --git a/BE/src/stock/detail/dto/stock-detail-chart-response.dto.ts b/BE/src/stock/detail/dto/stock-detail-chart-response.dto.ts new file mode 100644 index 00000000..3c580851 --- /dev/null +++ b/BE/src/stock/detail/dto/stock-detail-chart-response.dto.ts @@ -0,0 +1,5 @@ +import { InquirePriceChartDataDto } from './stock-detail-chart-data.dto'; + +export class InquirePriceChartResponseDto { + output: InquirePriceChartDataDto[]; +} diff --git a/BE/src/stock/detail/dto/stock-detail-response.dto.ts b/BE/src/stock/detail/dto/stock-detail-response.dto.ts deleted file mode 100644 index a86f9e04..00000000 --- a/BE/src/stock/detail/dto/stock-detail-response.dto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { InquirePriceOutput1Dto } from './stock-detail-output1.dto'; -import { InquirePriceOutput2Dto } from './stock-detail-output2.dto'; - -/** - * 국내주식기간별시세(일/주/월/년) API 응답값 정제 후 FE에 보낼 DTO - */ -export class InquirePriceResponseDto { - @ApiProperty({ type: InquirePriceOutput1Dto, description: '상승률 순위' }) - output1: InquirePriceOutput1Dto; - - @ApiProperty({ type: [InquirePriceOutput2Dto], description: '하락률 순위' }) - output2: InquirePriceOutput2Dto[]; -} diff --git a/BE/src/stock/detail/stock-detail.controller.ts b/BE/src/stock/detail/stock-detail.controller.ts index 5c4cfa85..64509746 100644 --- a/BE/src/stock/detail/stock-detail.controller.ts +++ b/BE/src/stock/detail/stock-detail.controller.ts @@ -2,7 +2,7 @@ import { Body, Controller, Param, Post } from '@nestjs/common'; import { ApiBody, ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger'; import { StockDetailService } from './stock-detail.service'; import { StockDetailRequestDto } from './dto/stock-detail-request.dto'; -import { InquirePriceResponseDto } from './dto/stock-detail-response.dto'; +import { InquirePriceChartResponseDto } from './dto/stock-detail-chart-response.dto'; @Controller('/api/stocks') export class StockDetailController { @@ -28,14 +28,14 @@ export class StockDetailController { @ApiResponse({ status: 201, description: '단일 주식 종목 기본값 조회 성공', - type: InquirePriceResponseDto, + type: InquirePriceChartResponseDto, }) getStockDetail( @Param('stockCode') stockCode: string, @Body() body: StockDetailRequestDto, ) { const { fid_input_date_1, fid_input_date_2, fid_period_div_code } = body; - return this.stockDetailService.getInquirePrice( + return this.stockDetailService.getInquirePriceChart( stockCode, fid_input_date_1, fid_input_date_2, diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index c0629406..a454beec 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -4,8 +4,7 @@ import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.s import { getHeader } from '../../util/get-header'; import { getFullURL } from '../../util/get-full-URL'; import { InquirePriceApiResponse } from './interface/stock-detail.interface'; -import { StockDetailQueryParameterDto } from './dto/stock-detail-query-parameter.dto'; -import { InquirePriceResponseDto } from './dto/stock-detail-response.dto'; +import { InquirePriceChartResponseDto } from './dto/stock-detail-chart-response.dto'; @Injectable() export class StockDetailService { @@ -23,23 +22,29 @@ export class StockDetailService { * * @author uuuo3o */ - async getInquirePrice( + async getInquirePriceChart( stockCode: string, date1: string, date2: string, periodDivCode: string, ) { try { - const queryParams = new StockDetailQueryParameterDto(); - queryParams.fid_cond_mrkt_div_code = 'J'; - queryParams.fid_input_iscd = stockCode; - queryParams.fid_input_date_1 = date1; - queryParams.fid_input_date_2 = date2; - queryParams.fid_period_div_code = periodDivCode; + const queryParams = { + fid_cond_mrkt_div_code: 'J', + fid_input_iscd: stockCode, + fid_input_date_1: date1, + fid_input_date_2: date2, + fid_period_div_code: periodDivCode, + fid_org_adj_prc: '0', + }; - const response = await this.requestApi(queryParams); + const response = await this.requestApi( + 'FHKST03010100', + '/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice', + queryParams, + ); - return this.formatStockData(response); + return this.formatStockInquirePriceData(response); } catch (error) { this.logger.error('API Error Details:', { status: error.response?.status, @@ -53,22 +58,25 @@ export class StockDetailService { } /** - * @private 한국투자 Open API - [국내주식] 기본시세 - 국내주식기간별시세(일/주/월/년) 호출 함수 - * @param {StockDetailQueryParameterDto} queryParams - API 요청 시 필요한 쿼리 파라미터 DTO - * @returns - 국내주식기간별시세(일/주/월/년) 데이터 + * @private 한국투자 Open API - API 호출용 공통 함수 + * @param {string} trId - API 호출에 사용할 tr_id + * @param {string} apiURL - API 호출에 사용할 URL + * @param {Record} params - API 요청 시 필요한 쿼리 파라미터 DTO + * @returns - API 호출에 대한 응답 데이터 * * @author uuuo3o */ - private async requestApi(queryParams: StockDetailQueryParameterDto) { + private async requestApi( + trId: string, + apiURL: string, + params: Record, + ): Promise { try { const accessToken = await this.koreaInvetmentService.getAccessToken(); - const headers = getHeader(accessToken, 'FHKST03010100'); - const url = getFullURL( - '/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice', - ); - const params = this.getInquirePriceParams(queryParams); + const headers = getHeader(accessToken, trId); + const url = getFullURL(apiURL); - const response = await axios.get(url, { + const response = await axios.get(url, { headers, params, }); @@ -93,52 +101,12 @@ export class StockDetailService { * * @author uuuo3o */ - private formatStockData(response: InquirePriceApiResponse) { - const stockData = new InquirePriceResponseDto(); - const { output1, output2 } = response; - - const { - hts_kor_isnm, - stck_shrn_iscd, - stck_prpr, - prdy_vrss, - prdy_vrss_sign, - prdy_ctrt, - hts_avls, - per, - } = output1; - - stockData.output1 = { - hts_kor_isnm, - stck_shrn_iscd, - stck_prpr, - prdy_vrss, - prdy_vrss_sign, - prdy_ctrt, - hts_avls, - per, - }; + private formatStockInquirePriceData(response: InquirePriceApiResponse) { + const stockData = new InquirePriceChartResponseDto(); + const { output2 } = response; - stockData.output2 = output2; + stockData.output = output2; return stockData; } - - /** - * @private 국내주식기간별시세(일/주/월/년) 요청을 위한 쿼리 파라미터 객체 생성 함수 - * @param {StockDetailQueryParameterDto} params - API 요청에 필요한 쿼리 파라미터 DTO - * @returns - API 요청에 필요한 쿼리 파라미터 객체 - * - * @author uuuo3o - */ - private getInquirePriceParams(params: StockDetailQueryParameterDto) { - return { - fid_cond_mrkt_div_code: params.fid_cond_mrkt_div_code, - fid_input_iscd: params.fid_input_iscd, - fid_input_date_1: params.fid_input_date_1, - fid_input_date_2: params.fid_input_date_2, - fid_period_div_code: params.fid_period_div_code, - fid_org_adj_prc: 0, - }; - } } From e23424b4e51d676a5b95f0509fb0367e98b0054d Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 17:34:13 +0900 Subject: [PATCH 03/21] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=94=84=EB=A1=A0?= =?UTF-8?q?=ED=8A=B8=EC=97=90=EC=84=9C=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EA=B0=92=EB=A7=8C=20=EB=B0=98=ED=99=98=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20DTO=20=EC=88=98=EC=A0=95#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/dto/stock-detail-chart-data.dto.ts | 21 ---------------- BE/src/stock/detail/stock-detail.service.ts | 24 +++++++++++++++---- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts b/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts index fd108090..4574d3a7 100644 --- a/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts +++ b/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts @@ -18,25 +18,4 @@ export class InquirePriceChartDataDto { @ApiProperty({ description: '누적 거래량' }) acml_vol: string; - - @ApiProperty({ description: '누적 거래 대금' }) - acml_tr_pbmn: string; - - @ApiProperty({ description: '락 구분 코드' }) - flng_cls_code: string; - - @ApiProperty({ description: '분할 비율' }) - prtt_rate: string; - - @ApiProperty({ description: '분할변경여부' }) - mod_yn: string; - - @ApiProperty({ description: '전일 대비 부호' }) - prdy_vrss_sign: string; - - @ApiProperty({ description: '전일 대비' }) - prdy_vrss: string; - - @ApiProperty({ description: '재평가사유코드' }) - revl_issu_reas: string; } diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index a454beec..b47a6606 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -4,7 +4,7 @@ import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.s import { getHeader } from '../../util/get-header'; import { getFullURL } from '../../util/get-full-URL'; import { InquirePriceApiResponse } from './interface/stock-detail.interface'; -import { InquirePriceChartResponseDto } from './dto/stock-detail-chart-response.dto'; +import { InquirePriceChartDataDto } from './dto/stock-detail-chart-data.dto'; @Injectable() export class StockDetailService { @@ -102,11 +102,27 @@ export class StockDetailService { * @author uuuo3o */ private formatStockInquirePriceData(response: InquirePriceApiResponse) { - const stockData = new InquirePriceChartResponseDto(); const { output2 } = response; - stockData.output = output2; + return output2.map((info) => { + const stockData = new InquirePriceChartDataDto(); + const { + stck_bsop_date, + stck_clpr, + stck_oprc, + stck_hgpr, + stck_lwpr, + acml_vol, + } = info; - return stockData; + stockData.stck_bsop_date = stck_bsop_date; + stockData.stck_clpr = stck_clpr; + stockData.stck_oprc = stck_oprc; + stockData.stck_hgpr = stck_hgpr; + stockData.stck_lwpr = stck_lwpr; + stockData.acml_vol = acml_vol; + + return stockData; + }); } } From 0203e65fc95bcda5467421dccf4f220b3088cf61 Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 17:36:22 +0900 Subject: [PATCH 04/21] =?UTF-8?q?=F0=9F=8E=A8=20style:=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/detail/stock-detail.service.ts | 64 ++++++++++----------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index b47a6606..ef463dc7 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -57,6 +57,38 @@ export class StockDetailService { } } + /** + * @private API에서 받은 국내주식기간별시세(일/주/월/년) 데이터를 필요한 정보로 정제하는 함수 + * @param {InquirePriceApiResponse} response - API 응답에서 받은 원시 데이터 + * @returns - 필요한 정보만 추출한 데이터 배열 + * + * @author uuuo3o + */ + private formatStockInquirePriceData(response: InquirePriceApiResponse) { + const { output2 } = response; + + return output2.map((info) => { + const stockData = new InquirePriceChartDataDto(); + const { + stck_bsop_date, + stck_clpr, + stck_oprc, + stck_hgpr, + stck_lwpr, + acml_vol, + } = info; + + stockData.stck_bsop_date = stck_bsop_date; + stockData.stck_clpr = stck_clpr; + stockData.stck_oprc = stck_oprc; + stockData.stck_hgpr = stck_hgpr; + stockData.stck_lwpr = stck_lwpr; + stockData.acml_vol = acml_vol; + + return stockData; + }); + } + /** * @private 한국투자 Open API - API 호출용 공통 함수 * @param {string} trId - API 호출에 사용할 tr_id @@ -93,36 +125,4 @@ export class StockDetailService { throw error; } } - - /** - * @private API에서 받은 국내주식기간별시세(일/주/월/년) 데이터를 필요한 정보로 정제하는 함수 - * @param {InquirePriceApiResponse} response - API 응답에서 받은 원시 데이터 - * @returns - 필요한 정보만 추출한 데이터 배열 - * - * @author uuuo3o - */ - private formatStockInquirePriceData(response: InquirePriceApiResponse) { - const { output2 } = response; - - return output2.map((info) => { - const stockData = new InquirePriceChartDataDto(); - const { - stck_bsop_date, - stck_clpr, - stck_oprc, - stck_hgpr, - stck_lwpr, - acml_vol, - } = info; - - stockData.stck_bsop_date = stck_bsop_date; - stockData.stck_clpr = stck_clpr; - stockData.stck_oprc = stck_oprc; - stockData.stck_hgpr = stck_hgpr; - stockData.stck_lwpr = stck_lwpr; - stockData.acml_vol = acml_vol; - - return stockData; - }); - } } From 00677bac48f023e5f6faebfc85c1b9469338a0fd Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 17:36:33 +0900 Subject: [PATCH 05/21] =?UTF-8?q?=F0=9F=94=A5=20remove:=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/stock-detail-query-parameter.dto.ts | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 BE/src/stock/detail/dto/stock-detail-query-parameter.dto.ts diff --git a/BE/src/stock/detail/dto/stock-detail-query-parameter.dto.ts b/BE/src/stock/detail/dto/stock-detail-query-parameter.dto.ts deleted file mode 100644 index feb9ca0f..00000000 --- a/BE/src/stock/detail/dto/stock-detail-query-parameter.dto.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 주식 현재가 시세 API를 사용할 때 쿼리 파라미터로 사용할 요청값 DTO - */ -export class StockDetailQueryParameterDto { - /** - * 조건 시장 분류 코드 - * 'J' 주식 - */ - fid_cond_mrkt_div_code: string; - - /** - * 주식 종목 코드 - * (ex) 005930 - */ - fid_input_iscd: string; - - /** - * 조회 시작일자 - * (ex) 20220501 - */ - fid_input_date_1: string; - - /** - * 조회 종료일자 - * (ex) 20220530 - */ - fid_input_date_2: string; - - /** - * 기간 분류 코드 - * D:일봉, W:주봉, M:월봉, Y:년봉 - */ - fid_period_div_code: string; -} From 383a166626cfd0ff1b73c92282d644cb58e78cdf Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 17:41:07 +0900 Subject: [PATCH 06/21] =?UTF-8?q?=F0=9F=93=9D=20docs:=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20swagger=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EC=88=98=EC=A0=95#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...equest.dto.ts => stock-detail-chart-request.dto.ts} | 2 +- BE/src/stock/detail/stock-detail.controller.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename BE/src/stock/detail/dto/{stock-detail-request.dto.ts => stock-detail-chart-request.dto.ts} (92%) diff --git a/BE/src/stock/detail/dto/stock-detail-request.dto.ts b/BE/src/stock/detail/dto/stock-detail-chart-request.dto.ts similarity index 92% rename from BE/src/stock/detail/dto/stock-detail-request.dto.ts rename to BE/src/stock/detail/dto/stock-detail-chart-request.dto.ts index 7bd1b1d8..4a21bbc3 100644 --- a/BE/src/stock/detail/dto/stock-detail-request.dto.ts +++ b/BE/src/stock/detail/dto/stock-detail-chart-request.dto.ts @@ -3,7 +3,7 @@ import { ApiProperty } from '@nestjs/swagger'; /** * 국내주식기간별시세(일/주/월/년) API를 이용할 때 필요한 요청 데이터를 담고 있는 DTO */ -export class StockDetailRequestDto { +export class StockDetailChartRequestDto { @ApiProperty({ description: '조회 시작일자 (ex) 20220501' }) fid_input_date_1: string; diff --git a/BE/src/stock/detail/stock-detail.controller.ts b/BE/src/stock/detail/stock-detail.controller.ts index 64509746..86149af2 100644 --- a/BE/src/stock/detail/stock-detail.controller.ts +++ b/BE/src/stock/detail/stock-detail.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Param, Post } from '@nestjs/common'; import { ApiBody, ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger'; import { StockDetailService } from './stock-detail.service'; -import { StockDetailRequestDto } from './dto/stock-detail-request.dto'; +import { StockDetailChartRequestDto } from './dto/stock-detail-chart-request.dto'; import { InquirePriceChartResponseDto } from './dto/stock-detail-chart-response.dto'; @Controller('/api/stocks') @@ -9,7 +9,7 @@ export class StockDetailController { constructor(private readonly stockDetailService: StockDetailService) {} @Post(':stockCode') - @ApiOperation({ summary: '단일 주식 종목 detail 페이지 상단부 조회 API' }) + @ApiOperation({ summary: '국내주식기간별시세(일/주/월/년) 조회 API' }) @ApiParam({ name: 'stockCode', required: true, @@ -23,16 +23,16 @@ export class StockDetailController { 'fid_input_date_1: 조회 시작일자 (ex) 20240505\n\n' + 'fid_input_date_2: 조회 종료일자 (ex) 20241111\n\n' + 'fid_period_div_code: 기간 분류 코드 (ex) D(일봉), W(주봉), M(월봉), Y(년봉)', - type: StockDetailRequestDto, + type: StockDetailChartRequestDto, }) @ApiResponse({ status: 201, - description: '단일 주식 종목 기본값 조회 성공', + description: '국내주식기간별시세(일/주/월/년) 조회 성공', type: InquirePriceChartResponseDto, }) getStockDetail( @Param('stockCode') stockCode: string, - @Body() body: StockDetailRequestDto, + @Body() body: StockDetailChartRequestDto, ) { const { fid_input_date_1, fid_input_date_2, fid_period_div_code } = body; return this.stockDetailService.getInquirePriceChart( From 463c4cd3f7c1833bc87ae6140bf20d3652a30bd8 Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 17:46:25 +0900 Subject: [PATCH 07/21] =?UTF-8?q?=E2=9E=95=20add:=20=EC=B0=A8=ED=8A=B8=20?= =?UTF-8?q?=EA=B7=B8=EB=A6=AC=EA=B8=B0=EC=97=90=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B0=92=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts | 3 +++ ...ck-detail.interface.ts => stock-detail-chart.interface.ts} | 0 BE/src/stock/detail/stock-detail.service.ts | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) rename BE/src/stock/detail/interface/{stock-detail.interface.ts => stock-detail-chart.interface.ts} (100%) diff --git a/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts b/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts index 4574d3a7..de68279f 100644 --- a/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts +++ b/BE/src/stock/detail/dto/stock-detail-chart-data.dto.ts @@ -18,4 +18,7 @@ export class InquirePriceChartDataDto { @ApiProperty({ description: '누적 거래량' }) acml_vol: string; + + @ApiProperty({ description: '전일 대비 부호' }) + prdy_vrss_sign: string; } diff --git a/BE/src/stock/detail/interface/stock-detail.interface.ts b/BE/src/stock/detail/interface/stock-detail-chart.interface.ts similarity index 100% rename from BE/src/stock/detail/interface/stock-detail.interface.ts rename to BE/src/stock/detail/interface/stock-detail-chart.interface.ts diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index ef463dc7..e66b0736 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.service'; import { getHeader } from '../../util/get-header'; import { getFullURL } from '../../util/get-full-URL'; -import { InquirePriceApiResponse } from './interface/stock-detail.interface'; +import { InquirePriceApiResponse } from './interface/stock-detail-chart.interface'; import { InquirePriceChartDataDto } from './dto/stock-detail-chart-data.dto'; @Injectable() @@ -76,6 +76,7 @@ export class StockDetailService { stck_hgpr, stck_lwpr, acml_vol, + prdy_vrss_sign, } = info; stockData.stck_bsop_date = stck_bsop_date; @@ -84,6 +85,7 @@ export class StockDetailService { stockData.stck_hgpr = stck_hgpr; stockData.stck_lwpr = stck_lwpr; stockData.acml_vol = acml_vol; + stockData.prdy_vrss_sign = prdy_vrss_sign; return stockData; }); From 764de7b4a95a8b8c8b365a26c9d2f9ceda6fda51 Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 17:59:50 +0900 Subject: [PATCH 08/21] =?UTF-8?q?=F0=9F=9A=9A=20rename:=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EB=90=98=EB=8A=94=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/detail/interface/stock-detail-chart.interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BE/src/stock/detail/interface/stock-detail-chart.interface.ts b/BE/src/stock/detail/interface/stock-detail-chart.interface.ts index 1169eb90..e49746e9 100644 --- a/BE/src/stock/detail/interface/stock-detail-chart.interface.ts +++ b/BE/src/stock/detail/interface/stock-detail-chart.interface.ts @@ -47,7 +47,7 @@ export interface InquirePriceOutput2Data { revl_issu_reas: string; } -export interface InquirePriceApiResponse { +export interface InquirePriceChartApiResponse { output1: InquirePriceOutput1Data; output2: InquirePriceOutput2Data[]; rt_cd: string; From 98b8b5793dc760e059ca68b37d94b6480f3a5460 Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 18:01:38 +0900 Subject: [PATCH 09/21] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EC=8B=9D=20?= =?UTF-8?q?=ED=98=84=EC=9E=AC=EA=B0=80=20API=EC=97=90=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20interface,=20dto=20=EA=B5=AC=ED=98=84#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...t1.dto.ts => stock-detail-response.dto.ts} | 2 +- .../interface/stock-detail.interface.ts | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) rename BE/src/stock/detail/dto/{stock-detail-output1.dto.ts => stock-detail-response.dto.ts} (94%) create mode 100644 BE/src/stock/detail/interface/stock-detail.interface.ts diff --git a/BE/src/stock/detail/dto/stock-detail-output1.dto.ts b/BE/src/stock/detail/dto/stock-detail-response.dto.ts similarity index 94% rename from BE/src/stock/detail/dto/stock-detail-output1.dto.ts rename to BE/src/stock/detail/dto/stock-detail-response.dto.ts index aa911bd1..96e4ba52 100644 --- a/BE/src/stock/detail/dto/stock-detail-output1.dto.ts +++ b/BE/src/stock/detail/dto/stock-detail-response.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export class InquirePriceOutput1Dto { +export class InquirePriceResponseDto { @ApiProperty({ description: 'HTS 한글 종목명' }) hts_kor_isnm: string; diff --git a/BE/src/stock/detail/interface/stock-detail.interface.ts b/BE/src/stock/detail/interface/stock-detail.interface.ts new file mode 100644 index 00000000..1cef7848 --- /dev/null +++ b/BE/src/stock/detail/interface/stock-detail.interface.ts @@ -0,0 +1,84 @@ +export interface InquirePriceOutputData { + iscd_stat_cls_code: string; + marg_rate: string; + rprs_mrkt_kor_name: string; + new_hgpr_lwpr_cls_code: string; + btsp_kor_isnm: string; + temp_stop_yn: string; + oprc_rang_cont_yn: string; + clpr_rang_cont_yn: string; + crdt_able_yn: string; + grmn_rate_cls_code: string; + elw_pblc_yn: string; + stck_prpr: string; + prdy_vrss: string; + prdy_vrss_sign: string; + prdy_ctrt: string; + acml_tr_pbmn: string; + acml_vol: string; + prdy_vrss_vol_rate: string; + stck_oprc: string; + stck_hgpr: string; + stck_lwpr: string; + stck_mxpr: string; + stck_llam: string; + stck_sdpr: string; + wghn_avrg_stck_prc: string; + hts_frgn_ehrt: string; + frgn_ntby_qty: string; + pgtr_ntby_qty: string; + dmrs_val: string; + dmsp_val: string; + cpfn: string; + rstc_wdth_prc: string; + stck_fcam: string; + stck_sspr: string; + aspr_unit: string; + hts_deal_qty_unit_val: string; + lstn_stcn: string; + hts_avls: string; + per: string; + pbr: string; + stac_month: string; + vol_tnrt: string; + eps: string; + bps: string; + d250_hgpr: string; + d250_hgpr_date: string; + d250_hgpr_vrss_prpr_rate: string; + d250_lwpr: string; + d250_lwpr_date: string; + d250_lwpr_vrss_prpr_rate: string; + stck_dryy_hgpr: string; + dryy_hgpr_vrss_prpr_rate: string; + dryy_hgpr_date: string; + stck_dryy_lwpr: string; + dryy_lwpr_vrss_prpr_rate: string; + dryy_lwpr_date: string; + w52_hgpr: string; + w52_hgpr_vrss_prpr_ctrt: string; + w52_hgpr_date: string; + w52_lwpr: string; + w52_lwpr_vrss_prpr_ctrt: string; + w52_lwpr_date: string; + whol_loan_rmnd_rate: string; + ssts_yn: string; + stck_shrn_iscd: string; + fcam_cnnm: string; + cpfn_cnnm: string; + apprch_rate: string; + frgn_hldn_qty: string; + vi_cls_code: string; + ovtm_vi_cls_code: string; + last_ssts_cntg_qty: string; + invt_caful_yn: string; + mrkt_warn_cls_code: string; + short_over_yn: string; + sltr_yn: string; +} +export interface InquirePriceApiResponse { + output: InquirePriceOutputData; + rt_cd: string; + msg_cd: string; + msg1: string; +} From 4fe008b7d9e0ffa08e4665c02567e8a2953edca0 Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 18:01:53 +0900 Subject: [PATCH 10/21] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EC=8B=9D=20?= =?UTF-8?q?=ED=98=84=EC=9E=AC=EA=B0=80=20API=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stock/detail/stock-detail.controller.ts | 23 ++++++- BE/src/stock/detail/stock-detail.service.ts | 63 ++++++++++++++++++- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/BE/src/stock/detail/stock-detail.controller.ts b/BE/src/stock/detail/stock-detail.controller.ts index 86149af2..533b67ca 100644 --- a/BE/src/stock/detail/stock-detail.controller.ts +++ b/BE/src/stock/detail/stock-detail.controller.ts @@ -1,6 +1,7 @@ -import { Body, Controller, Param, Post } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; import { ApiBody, ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger'; import { StockDetailService } from './stock-detail.service'; +import { InquirePriceResponseDto } from './dto/stock-detail-response.dto'; import { StockDetailChartRequestDto } from './dto/stock-detail-chart-request.dto'; import { InquirePriceChartResponseDto } from './dto/stock-detail-chart-response.dto'; @@ -8,6 +9,24 @@ import { InquirePriceChartResponseDto } from './dto/stock-detail-chart-response. export class StockDetailController { constructor(private readonly stockDetailService: StockDetailService) {} + @Get(':stockCode') + @ApiOperation({ summary: '단일 주식 종목 detail 페이지 상단부 조회 API' }) + @ApiParam({ + name: 'stockCode', + required: true, + description: + '종목 코드\n' + + '(ex) 005930 삼성전자 / 005380 현대차 / 001500 현대차증권', + }) + @ApiResponse({ + status: 200, + description: '단일 주식 종목 기본값 조회 성공', + type: InquirePriceResponseDto, + }) + getStockDetail(@Param('stockCode') stockCode: string) { + return this.stockDetailService.getInquirePrice(stockCode); + } + @Post(':stockCode') @ApiOperation({ summary: '국내주식기간별시세(일/주/월/년) 조회 API' }) @ApiParam({ @@ -30,7 +49,7 @@ export class StockDetailController { description: '국내주식기간별시세(일/주/월/년) 조회 성공', type: InquirePriceChartResponseDto, }) - getStockDetail( + getStockDetailChart( @Param('stockCode') stockCode: string, @Body() body: StockDetailChartRequestDto, ) { diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index e66b0736..8031c4d6 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -3,8 +3,13 @@ import { Injectable, Logger } from '@nestjs/common'; import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.service'; import { getHeader } from '../../util/get-header'; import { getFullURL } from '../../util/get-full-URL'; -import { InquirePriceApiResponse } from './interface/stock-detail-chart.interface'; +import { InquirePriceChartApiResponse } from './interface/stock-detail-chart.interface'; import { InquirePriceChartDataDto } from './dto/stock-detail-chart-data.dto'; +import { + InquirePriceApiResponse, + InquirePriceOutputData, +} from './interface/stock-detail.interface'; +import { InquirePriceResponseDto } from './dto/stock-detail-response.dto'; @Injectable() export class StockDetailService { @@ -12,6 +17,58 @@ export class StockDetailService { constructor(private readonly koreaInvetmentService: KoreaInvestmentService) {} + /** + * 주식현재가 시세 데이터를 반환하는 함수 + * @param {string} stockCode - 종목코드 + * @returns - 주식현재가 시세 데이터 객체 반환 + * + * @author uuuo3o + */ + async getInquirePrice(stockCode: string) { + try { + const queryParams = { + fid_cond_mrkt_div_code: 'J', + fid_input_iscd: stockCode, + }; + + const response = await this.requestApi( + 'FHKST01010100', + '/uapi/domestic-stock/v1/quotations/inquire-price', + queryParams, + ); + + return this.formatStockData(response.output); + } catch (error) { + this.logger.error('API Error Details:', { + status: error.response?.status, + statusText: error.response?.statusText, + data: error.response?.data, + headers: error.response?.config?.headers, // 실제 요청 헤더 + message: error.message, + }); + throw error; + } + } + + /** + * @private API에서 받은 주식현재가 시세 데이터를 필요한 정보로 정제하는 함수 + * @param {InquirePriceOutputData} stock - API 응답에서 받은 원시 데이터 + * @returns - 필요한 정보만 추출한 데이터 배열 + * + * @author uuuo3o + */ + private formatStockData(stock: InquirePriceOutputData) { + const stockData = new InquirePriceResponseDto(); + stockData.stck_shrn_iscd = stock.stck_shrn_iscd; + stockData.stck_prpr = stock.stck_prpr; + stockData.prdy_vrss = stock.prdy_vrss; + stockData.prdy_vrss_sign = stock.prdy_vrss_sign; + stockData.prdy_ctrt = stock.prdy_ctrt; + stockData.hts_avls = stock.hts_avls; + stockData.per = stock.per; + return stockData; + } + /** * 특정 주식의 기간별시세 데이터를 반환하는 함수 * @param {string} stockCode - 종목코드 @@ -38,7 +95,7 @@ export class StockDetailService { fid_org_adj_prc: '0', }; - const response = await this.requestApi( + const response = await this.requestApi( 'FHKST03010100', '/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice', queryParams, @@ -64,7 +121,7 @@ export class StockDetailService { * * @author uuuo3o */ - private formatStockInquirePriceData(response: InquirePriceApiResponse) { + private formatStockInquirePriceData(response: InquirePriceChartApiResponse) { const { output2 } = response; return output2.map((info) => { From 7dd9da8594338e0b727387eca4db1d28533a613f Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 19:46:15 +0900 Subject: [PATCH 11/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20requestA?= =?UTF-8?q?pi=20=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C#55?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...stock-trade-history-query-parameter.dto.ts | 16 ---- .../history/stock-trade-history.service.ts | 85 +++++++++---------- 2 files changed, 40 insertions(+), 61 deletions(-) delete mode 100644 BE/src/stock/trade/history/dto/stock-trade-history-query-parameter.dto.ts diff --git a/BE/src/stock/trade/history/dto/stock-trade-history-query-parameter.dto.ts b/BE/src/stock/trade/history/dto/stock-trade-history-query-parameter.dto.ts deleted file mode 100644 index 6d9ccfa6..00000000 --- a/BE/src/stock/trade/history/dto/stock-trade-history-query-parameter.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 주식현재가 체결 API를 사용할 때 쿼리 파라미터로 사용할 요청값 DTO - */ -export class StockTradeHistoryQueryParameterDto { - /** - * 조건 시장 분류 코드 - * 'J' 주식 - */ - fid_cond_mrkt_div_code: string; - - /** - * 주식 종목 코드 - * (ex) 005930 - */ - fid_input_iscd: string; -} diff --git a/BE/src/stock/trade/history/stock-trade-history.service.ts b/BE/src/stock/trade/history/stock-trade-history.service.ts index dc9b24e4..7617846e 100644 --- a/BE/src/stock/trade/history/stock-trade-history.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history.service.ts @@ -3,7 +3,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { KoreaInvestmentService } from '../../../koreaInvestment/korea-investment.service'; import { getHeader } from '../../../util/get-header'; import { getFullURL } from '../../../util/get-full-URL'; -import { StockTradeHistoryQueryParameterDto } from './dto/stock-trade-history-query-parameter.dto'; import { InquireCCNLApiResponse } from './interface/Inquire-ccnl.interface'; import { StockTradeHistoryOutputDto } from './dto/stock-trade-history-output.dto'; import { StockTradeHistoryDataDto } from './dto/stock-trade-history-data.dto'; @@ -23,11 +22,16 @@ export class StockTradeHistoryService { */ async getStockTradeHistory(stockCode: string) { try { - const queryParams = new StockTradeHistoryQueryParameterDto(); - queryParams.fid_cond_mrkt_div_code = 'J'; - queryParams.fid_input_iscd = stockCode; + const queryParams = { + fid_cond_mrkt_div_code: 'J', + fid_input_iscd: stockCode, + }; - const response = await this.requestApi(queryParams); + const response = await this.requestApi( + 'FHKST01010300', + '/uapi/domestic-stock/v1/quotations/inquire-ccnl', + queryParams, + ); return this.formatTradeHistoryData(response.output); } catch (error) { @@ -42,38 +46,6 @@ export class StockTradeHistoryService { } } - /** - * @private 한국투자 Open API - [국내주식] 기본시세 - 주식현재가 체결 호출 함수 - * @param {StockTradeHistoryQueryParameterDto} queryParams - API 요청 시 필요한 쿼리 파라미터 DTO - * @returns - 주식현재가 체결 데이터 - * - * @author uuuo3o - */ - private async requestApi(queryParams: StockTradeHistoryQueryParameterDto) { - try { - const accessToken = await this.koreaInvetmentService.getAccessToken(); - const headers = getHeader(accessToken, 'FHKST01010300'); - const url = getFullURL('/uapi/domestic-stock/v1/quotations/inquire-ccnl'); - const params = this.getTradeHistoryParams(queryParams); - - const response = await axios.get(url, { - headers, - params, - }); - - return response.data; - } catch (error) { - this.logger.error('API Error Details:', { - status: error.response?.status, - statusText: error.response?.statusText, - data: error.response?.data, - headers: error.response?.config?.headers, - message: error.message, - }); - throw error; - } - } - /** * @private API에서 받은 주식현재가 체결 데이터를 필요한 정보로 정제하는 함수 * @param {StockTradeHistoryOutputDto} infos - API 응답에서 받은 원시 데이터 @@ -95,16 +67,39 @@ export class StockTradeHistoryService { } /** - * @private 주식현재가 체결 요청을 위한 쿼리 파라미터 객체 생성 함수 - * @param {StockTradeHistoryQueryParameterDto} params - API 요청에 필요한 쿼리 파라미터 DTO - * @returns - API 요청에 필요한 쿼리 파라미터 객체 + * @private 한국투자 Open API - API 호출용 공통 함수 + * @param {string} trId - API 호출에 사용할 tr_id + * @param {string} apiURL - API 호출에 사용할 URL + * @param {Record} params - API 요청 시 필요한 쿼리 파라미터 DTO + * @returns - API 호출에 대한 응답 데이터 * * @author uuuo3o */ - private getTradeHistoryParams(params: StockTradeHistoryQueryParameterDto) { - return { - fid_cond_mrkt_div_code: params.fid_cond_mrkt_div_code, - fid_input_iscd: params.fid_input_iscd, - }; + private async requestApi( + trId: string, + apiURL: string, + params: Record, + ): Promise { + try { + const accessToken = await this.koreaInvetmentService.getAccessToken(); + const headers = getHeader(accessToken, trId); + const url = getFullURL(apiURL); + + const response = await axios.get(url, { + headers, + params, + }); + + return response.data; + } catch (error) { + this.logger.error('API Error Details:', { + status: error.response?.status, + statusText: error.response?.statusText, + data: error.response?.data, + headers: error.response?.config?.headers, + message: error.message, + }); + throw error; + } } } From 6df873c831d0a691212bc2943cd5b917b52fe8a8 Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 19:53:16 +0900 Subject: [PATCH 12/21] =?UTF-8?q?=F0=9F=9A=9A=20rename:=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20=EB=AA=85=ED=99=95=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD#55?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/stock-trade-history-response.dto.ts | 10 ---------- ....ts => today-stock-trade-history-data.dto.ts} | 2 +- ...s => today-stock-trade-history-output.dto.ts} | 2 +- .../today-stock-trade-history-response.dto.ts | 13 +++++++++++++ .../history/stock-trade-history.controller.ts | 8 ++++---- .../trade/history/stock-trade-history.service.ts | 16 +++++++++------- 6 files changed, 28 insertions(+), 23 deletions(-) delete mode 100644 BE/src/stock/trade/history/dto/stock-trade-history-response.dto.ts rename BE/src/stock/trade/history/dto/{stock-trade-history-data.dto.ts => today-stock-trade-history-data.dto.ts} (90%) rename BE/src/stock/trade/history/dto/{stock-trade-history-output.dto.ts => today-stock-trade-history-output.dto.ts} (92%) create mode 100644 BE/src/stock/trade/history/dto/today-stock-trade-history-response.dto.ts diff --git a/BE/src/stock/trade/history/dto/stock-trade-history-response.dto.ts b/BE/src/stock/trade/history/dto/stock-trade-history-response.dto.ts deleted file mode 100644 index 265c1d5e..00000000 --- a/BE/src/stock/trade/history/dto/stock-trade-history-response.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { StockTradeHistoryOutputDto } from './stock-trade-history-output.dto'; - -/** - * 주식현재가 체결 API 응답값 정제 후 FE에 보낼 DTO - */ -export class StockTradeHistoryResponseDto { - @ApiProperty({ type: StockTradeHistoryOutputDto, description: '상승률 순위' }) - output: StockTradeHistoryOutputDto[]; -} diff --git a/BE/src/stock/trade/history/dto/stock-trade-history-data.dto.ts b/BE/src/stock/trade/history/dto/today-stock-trade-history-data.dto.ts similarity index 90% rename from BE/src/stock/trade/history/dto/stock-trade-history-data.dto.ts rename to BE/src/stock/trade/history/dto/today-stock-trade-history-data.dto.ts index 3ead1198..7842a29b 100644 --- a/BE/src/stock/trade/history/dto/stock-trade-history-data.dto.ts +++ b/BE/src/stock/trade/history/dto/today-stock-trade-history-data.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export class StockTradeHistoryDataDto { +export class TodayStockTradeHistoryDataDto { @ApiProperty({ description: '주식 체결 시간' }) stck_cntg_hour: string; diff --git a/BE/src/stock/trade/history/dto/stock-trade-history-output.dto.ts b/BE/src/stock/trade/history/dto/today-stock-trade-history-output.dto.ts similarity index 92% rename from BE/src/stock/trade/history/dto/stock-trade-history-output.dto.ts rename to BE/src/stock/trade/history/dto/today-stock-trade-history-output.dto.ts index 05415c64..ab41d377 100644 --- a/BE/src/stock/trade/history/dto/stock-trade-history-output.dto.ts +++ b/BE/src/stock/trade/history/dto/today-stock-trade-history-output.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export class StockTradeHistoryOutputDto { +export class TodayStockTradeHistoryOutputDto { @ApiProperty({ description: '주식 체결 시간' }) stck_cntg_hour: string; diff --git a/BE/src/stock/trade/history/dto/today-stock-trade-history-response.dto.ts b/BE/src/stock/trade/history/dto/today-stock-trade-history-response.dto.ts new file mode 100644 index 00000000..e758dfba --- /dev/null +++ b/BE/src/stock/trade/history/dto/today-stock-trade-history-response.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { TodayStockTradeHistoryOutputDto } from './today-stock-trade-history-output.dto'; + +/** + * 주식현재가 체결 API 응답값 정제 후 FE에 보낼 DTO + */ +export class TodayStockTradeHistoryResponseDto { + @ApiProperty({ + type: TodayStockTradeHistoryOutputDto, + description: '상승률 순위', + }) + output: TodayStockTradeHistoryOutputDto[]; +} diff --git a/BE/src/stock/trade/history/stock-trade-history.controller.ts b/BE/src/stock/trade/history/stock-trade-history.controller.ts index 11efa7fa..8a2cf430 100644 --- a/BE/src/stock/trade/history/stock-trade-history.controller.ts +++ b/BE/src/stock/trade/history/stock-trade-history.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Param } from '@nestjs/common'; import { ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger'; import { StockTradeHistoryService } from './stock-trade-history.service'; -import { StockTradeHistoryResponseDto } from './dto/stock-trade-history-response.dto'; +import { TodayStockTradeHistoryResponseDto } from './dto/today-stock-trade-history-response.dto'; @Controller('/api/stocks') export class StockTradeHistoryController { @@ -9,7 +9,7 @@ export class StockTradeHistoryController { private readonly stockTradeHistoryService: StockTradeHistoryService, ) {} - @Get(':stockCode/trade-history') + @Get(':stockCode/today-trade-history') @ApiOperation({ summary: '단일 주식 종목에 대한 주식현재가 체결 API' }) @ApiParam({ name: 'stockCode', @@ -21,9 +21,9 @@ export class StockTradeHistoryController { @ApiResponse({ status: 200, description: '단일 주식 종목에 대한 주식현재가 체결값 조회 성공', - type: StockTradeHistoryResponseDto, + type: TodayStockTradeHistoryResponseDto, }) getStockDetail(@Param('stockCode') stockCode: string) { - return this.stockTradeHistoryService.getStockTradeHistory(stockCode); + return this.stockTradeHistoryService.getTodayStockTradeHistory(stockCode); } } diff --git a/BE/src/stock/trade/history/stock-trade-history.service.ts b/BE/src/stock/trade/history/stock-trade-history.service.ts index 7617846e..c5e0dacf 100644 --- a/BE/src/stock/trade/history/stock-trade-history.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history.service.ts @@ -4,8 +4,8 @@ import { KoreaInvestmentService } from '../../../koreaInvestment/korea-investmen import { getHeader } from '../../../util/get-header'; import { getFullURL } from '../../../util/get-full-URL'; import { InquireCCNLApiResponse } from './interface/Inquire-ccnl.interface'; -import { StockTradeHistoryOutputDto } from './dto/stock-trade-history-output.dto'; -import { StockTradeHistoryDataDto } from './dto/stock-trade-history-data.dto'; +import { TodayStockTradeHistoryOutputDto } from './dto/today-stock-trade-history-output.dto'; +import { TodayStockTradeHistoryDataDto } from './dto/today-stock-trade-history-data.dto'; @Injectable() export class StockTradeHistoryService { @@ -20,7 +20,7 @@ export class StockTradeHistoryService { * * @author uuuo3o */ - async getStockTradeHistory(stockCode: string) { + async getTodayStockTradeHistory(stockCode: string) { try { const queryParams = { fid_cond_mrkt_div_code: 'J', @@ -33,7 +33,7 @@ export class StockTradeHistoryService { queryParams, ); - return this.formatTradeHistoryData(response.output); + return this.formatTodayStockTradeHistoryData(response.output); } catch (error) { this.logger.error('API Error Details:', { status: error.response?.status, @@ -48,14 +48,16 @@ export class StockTradeHistoryService { /** * @private API에서 받은 주식현재가 체결 데이터를 필요한 정보로 정제하는 함수 - * @param {StockTradeHistoryOutputDto} infos - API 응답에서 받은 원시 데이터 + * @param {TodayStockTradeHistoryOutputDto} infos - API 응답에서 받은 원시 데이터 * @returns - 필요한 정보만 추출한 데이터 배열 * * @author uuuo3o */ - private formatTradeHistoryData(infos: StockTradeHistoryOutputDto[]) { + private formatTodayStockTradeHistoryData( + infos: TodayStockTradeHistoryOutputDto[], + ) { return infos.map((info) => { - const infoData = new StockTradeHistoryDataDto(); + const infoData = new TodayStockTradeHistoryDataDto(); infoData.stck_cntg_hour = info.stck_cntg_hour; infoData.stck_prpr = info.stck_prpr; infoData.prdy_vrss_sign = info.prdy_vrss_sign; From 875f2a7230513545441a8244500703fa21523c75 Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 20:16:18 +0900 Subject: [PATCH 13/21] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EC=8B=9D?= =?UTF-8?q?=ED=98=84=EC=9E=AC=EA=B0=80=20=EC=9D=BC=EC=9E=90=EB=B3=84=20API?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=ED=95=A0=20interface,=20dto=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84#55?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/daily-stock-trade-history-data.dto.ts | 27 +++++++++++ .../daily-stock-trade-history-ouput.dto.ts | 45 +++++++++++++++++++ .../daily-stock-trade-history-response.dto.ts | 8 ++++ .../inquire-daily-price.interface.ts | 23 ++++++++++ 4 files changed, 103 insertions(+) create mode 100644 BE/src/stock/trade/history/dto/daily-stock-trade-history-data.dto.ts create mode 100644 BE/src/stock/trade/history/dto/daily-stock-trade-history-ouput.dto.ts create mode 100644 BE/src/stock/trade/history/dto/daily-stock-trade-history-response.dto.ts create mode 100644 BE/src/stock/trade/history/interface/inquire-daily-price.interface.ts diff --git a/BE/src/stock/trade/history/dto/daily-stock-trade-history-data.dto.ts b/BE/src/stock/trade/history/dto/daily-stock-trade-history-data.dto.ts new file mode 100644 index 00000000..5cd38693 --- /dev/null +++ b/BE/src/stock/trade/history/dto/daily-stock-trade-history-data.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class DailyStockTradeHistoryDataDto { + @ApiProperty({ description: '주식 영업 일자' }) + stck_bsop_date: string; + + @ApiProperty({ description: '주식 시가' }) + stck_oprc: string; + + @ApiProperty({ description: '주식 최고가' }) + stck_hgpr: string; + + @ApiProperty({ description: '주식 최저가' }) + stck_lwpr: string; + + @ApiProperty({ description: '주식 종가' }) + stck_clpr: string; + + @ApiProperty({ description: '누적 거래량' }) + acml_vol: string; + + @ApiProperty({ description: '전일 대비 부호' }) + prdy_vrss_sign: string; + + @ApiProperty({ description: '전일 대비율' }) + prdy_ctrt: string; +} diff --git a/BE/src/stock/trade/history/dto/daily-stock-trade-history-ouput.dto.ts b/BE/src/stock/trade/history/dto/daily-stock-trade-history-ouput.dto.ts new file mode 100644 index 00000000..99427dd6 --- /dev/null +++ b/BE/src/stock/trade/history/dto/daily-stock-trade-history-ouput.dto.ts @@ -0,0 +1,45 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class DailyStockTradeHistoryOutputDto { + @ApiProperty({ description: '주식 영업 일자' }) + stck_bsop_date: string; + + @ApiProperty({ description: '주식 시가' }) + stck_oprc: string; + + @ApiProperty({ description: '주식 최고가' }) + stck_hgpr: string; + + @ApiProperty({ description: '주식 최저가' }) + stck_lwpr: string; + + @ApiProperty({ description: '주식 종가' }) + stck_clpr: string; + + @ApiProperty({ description: '누적 거래량' }) + acml_vol: string; + + @ApiProperty({ description: '전일 대비 거래량 비율' }) + prdy_vrss_vol_rate: string; + + @ApiProperty({ description: '전일 대비' }) + prdy_vrss: string; + + @ApiProperty({ description: '전일 대비 부호' }) + prdy_vrss_sign: string; + + @ApiProperty({ description: '전일 대비율' }) + prdy_ctrt: string; + + @ApiProperty({ description: 'HTS 외국인 소진율' }) + hts_frgn_ehrt: string; + + @ApiProperty({ description: '외국인 순매수 수량' }) + frgn_ntby_qty: string; + + @ApiProperty({ description: '락 구분 코드' }) + flng_cls_code: string; + + @ApiProperty({ description: '누적 분할 비율' }) + acml_prtt_rate: string; +} diff --git a/BE/src/stock/trade/history/dto/daily-stock-trade-history-response.dto.ts b/BE/src/stock/trade/history/dto/daily-stock-trade-history-response.dto.ts new file mode 100644 index 00000000..dd9bc0e1 --- /dev/null +++ b/BE/src/stock/trade/history/dto/daily-stock-trade-history-response.dto.ts @@ -0,0 +1,8 @@ +import { DailyStockTradeHistoryOutputDto } from './daily-stock-trade-history-ouput.dto'; + +/** + * 주식현재가 일자별 API 응답값 정제 후 FE에 보낼 DTO + */ +export class DailyStockTradeHistoryResponseDto { + output: DailyStockTradeHistoryOutputDto[]; +} diff --git a/BE/src/stock/trade/history/interface/inquire-daily-price.interface.ts b/BE/src/stock/trade/history/interface/inquire-daily-price.interface.ts new file mode 100644 index 00000000..2546ade6 --- /dev/null +++ b/BE/src/stock/trade/history/interface/inquire-daily-price.interface.ts @@ -0,0 +1,23 @@ +export interface InquireDailyPriceOutputData { + stck_bsop_date: string; + stck_oprc: string; + stck_hgpr: string; + stck_lwpr: string; + stck_clpr: string; + acml_vol: string; + prdy_vrss_vol_rate: string; + prdy_vrss: string; + prdy_vrss_sign: string; + prdy_ctrt: string; + hts_frgn_ehrt: string; + frgn_ntby_qty: string; + flng_cls_code: string; + acml_prtt_rate: string; +} + +export interface InquireDailyPriceApiResponse { + output: InquireDailyPriceOutputData[]; + rt_cd: string; + msg_cd: string; + msg1: string; +} From e56921ab51c6dbe06a0a319f3ae03d030e25d383 Mon Sep 17 00:00:00 2001 From: JIN Date: Wed, 13 Nov 2024 20:16:30 +0900 Subject: [PATCH 14/21] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EC=8B=9D?= =?UTF-8?q?=ED=98=84=EC=9E=AC=EA=B0=80=20=EC=9D=BC=EC=9E=90=EB=B3=84=20API?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?#55?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../history/stock-trade-history.controller.ts | 21 ++++++- .../history/stock-trade-history.service.ts | 63 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/BE/src/stock/trade/history/stock-trade-history.controller.ts b/BE/src/stock/trade/history/stock-trade-history.controller.ts index 8a2cf430..e9de0f81 100644 --- a/BE/src/stock/trade/history/stock-trade-history.controller.ts +++ b/BE/src/stock/trade/history/stock-trade-history.controller.ts @@ -2,6 +2,7 @@ import { Controller, Get, Param } from '@nestjs/common'; import { ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger'; import { StockTradeHistoryService } from './stock-trade-history.service'; import { TodayStockTradeHistoryResponseDto } from './dto/today-stock-trade-history-response.dto'; +import { DailyStockTradeHistoryDataDto } from './dto/daily-stock-trade-history-data.dto'; @Controller('/api/stocks') export class StockTradeHistoryController { @@ -23,7 +24,25 @@ export class StockTradeHistoryController { description: '단일 주식 종목에 대한 주식현재가 체결값 조회 성공', type: TodayStockTradeHistoryResponseDto, }) - getStockDetail(@Param('stockCode') stockCode: string) { + getTodayStockTradeHistory(@Param('stockCode') stockCode: string) { return this.stockTradeHistoryService.getTodayStockTradeHistory(stockCode); } + + @Get(':stockCode/daily-trade-history') + @ApiOperation({ summary: '단일 주식 종목에 대한 일자별 주식현재가 API' }) + @ApiParam({ + name: 'stockCode', + required: true, + description: + '종목 코드\n\n' + + '(ex) 005930 삼성전자 / 005380 현대차 / 001500 현대차증권', + }) + @ApiResponse({ + status: 200, + description: '단일 주식 종목에 대한 일자별 주식현재가 조회 성공', + type: DailyStockTradeHistoryDataDto, + }) + getDailyStockTradeHistory(@Param('stockCode') stockCode: string) { + return this.stockTradeHistoryService.getDailyStockTradeHistory(stockCode); + } } diff --git a/BE/src/stock/trade/history/stock-trade-history.service.ts b/BE/src/stock/trade/history/stock-trade-history.service.ts index c5e0dacf..50204aff 100644 --- a/BE/src/stock/trade/history/stock-trade-history.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history.service.ts @@ -6,6 +6,9 @@ import { getFullURL } from '../../../util/get-full-URL'; import { InquireCCNLApiResponse } from './interface/Inquire-ccnl.interface'; import { TodayStockTradeHistoryOutputDto } from './dto/today-stock-trade-history-output.dto'; import { TodayStockTradeHistoryDataDto } from './dto/today-stock-trade-history-data.dto'; +import { InquireDailyPriceApiResponse } from './interface/inquire-daily-price.interface'; +import { DailyStockTradeHistoryOutputDto } from './dto/daily-stock-trade-history-ouput.dto'; +import { DailyStockTradeHistoryDataDto } from './dto/daily-stock-trade-history-data.dto'; @Injectable() export class StockTradeHistoryService { @@ -68,6 +71,66 @@ export class StockTradeHistoryService { }); } + /** + * 특정 주식의 일자별 체결 데이터를 반환하는 함수 + * @param {string} stockCode - 종목코드 + * @returns - 특정 주식의 현재가 체결 데이터 객체 반환 + * + * @author uuuo3o + */ + async getDailyStockTradeHistory(stockCode: string) { + try { + const queryParams = { + fid_cond_mrkt_div_code: 'J', + fid_input_iscd: stockCode, + fid_period_div_code: 'D', + fid_org_adj_prc: '0', + }; + + const response = await this.requestApi( + 'FHKST01010400', + '/uapi/domestic-stock/v1/quotations/inquire-daily-price', + queryParams, + ); + + return this.formatDailyStockTradeHistoryData(response.output); + } catch (error) { + this.logger.error('API Error Details:', { + status: error.response?.status, + statusText: error.response?.statusText, + data: error.response?.data, + headers: error.response?.config?.headers, // 실제 요청 헤더 + message: error.message, + }); + throw error; + } + } + + /** + * @private API에서 받은 주식현재가 일자별 데이터를 필요한 정보로 정제하는 함수 + * @param {DailyStockTradeHistoryOutputDto} datas - API 응답에서 받은 원시 데이터 + * @returns - 필요한 정보만 추출한 데이터 배열 + * + * @author uuuo3o + */ + private formatDailyStockTradeHistoryData( + datas: DailyStockTradeHistoryOutputDto[], + ) { + return datas.map((data) => { + const historyData = new DailyStockTradeHistoryDataDto(); + historyData.stck_bsop_date = data.stck_bsop_date; + historyData.stck_oprc = data.stck_oprc; + historyData.stck_hgpr = data.stck_hgpr; + historyData.stck_lwpr = data.stck_lwpr; + historyData.stck_clpr = data.stck_clpr; + historyData.acml_vol = data.acml_vol; + historyData.prdy_vrss_sign = data.prdy_vrss_sign; + historyData.prdy_ctrt = data.prdy_ctrt; + + return historyData; + }); + } + /** * @private 한국투자 Open API - API 호출용 공통 함수 * @param {string} trId - API 호출에 사용할 tr_id From 7a61c8da7779e0f888bf03b5a8bc3810ce8bc899 Mon Sep 17 00:00:00 2001 From: jinddings Date: Thu, 14 Nov 2024 11:25:28 +0900 Subject: [PATCH 15/21] =?UTF-8?q?=F0=9F=9A=91=20!HOTFIX=20:=20=EB=8F=84?= =?UTF-8?q?=EC=BB=A4=20=EC=BB=A8=ED=85=8C=EC=9D=B4=EB=84=88=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-production.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index e2cdb2a2..04bfd32b 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -113,7 +113,8 @@ jobs: --name ${{ matrix.app.container }} \ -p ${{ matrix.app.port }}:${{ matrix.app.port }} \ --env-file .env \ - ${{ env.DOCKER_IMAGE }}-${{ matrix.app.name }}:${{ env.DOCKER_TAG }} + ${{ env.DOCKER_IMAGE }}-${{ matrix.app.name }}:${{ env.DOCKER_TAG }} \ + -v /etc/localtime:/etc/localtime:ro -e TZ=Asia/Seoul - name: Remove Github Action Ip to Security group run: | From 850e53b3fea2f427e1ed3d8dd34ae544ac6a2d5f Mon Sep 17 00:00:00 2001 From: jinddings Date: Thu, 14 Nov 2024 11:38:36 +0900 Subject: [PATCH 16/21] =?UTF-8?q?=F0=9F=9A=91=20!HOTFIX=20:=20docker=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=84=A4=EC=A0=95=20=EB=AA=85=EB=A0=B9?= =?UTF-8?q?=EC=96=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-production.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 04bfd32b..e0cbe310 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -113,8 +113,9 @@ jobs: --name ${{ matrix.app.container }} \ -p ${{ matrix.app.port }}:${{ matrix.app.port }} \ --env-file .env \ + -v /etc/localtime:/etc/localtime:ro \ + -e TZ=Asia/Seoul \ ${{ env.DOCKER_IMAGE }}-${{ matrix.app.name }}:${{ env.DOCKER_TAG }} \ - -v /etc/localtime:/etc/localtime:ro -e TZ=Asia/Seoul - name: Remove Github Action Ip to Security group run: | From 5eb4fc5ee07a8c10f9f4a3a4a9c41d65ecdbbc1c Mon Sep 17 00:00:00 2001 From: jinddings Date: Thu, 14 Nov 2024 11:43:26 +0900 Subject: [PATCH 17/21] =?UTF-8?q?=F0=9F=9A=91=20!HOTFIX=20:=20docker=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=84=A4=EC=A0=95=20=EB=AA=85=EB=A0=B9?= =?UTF-8?q?=EC=96=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-production.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index e0cbe310..c2cde28f 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -115,7 +115,7 @@ jobs: --env-file .env \ -v /etc/localtime:/etc/localtime:ro \ -e TZ=Asia/Seoul \ - ${{ env.DOCKER_IMAGE }}-${{ matrix.app.name }}:${{ env.DOCKER_TAG }} \ + ${{ env.DOCKER_IMAGE }}-${{ matrix.app.name }}:${{ env.DOCKER_TAG }} - name: Remove Github Action Ip to Security group run: | From e028917bacf3552f921fb81b3cbbc4fc8f1ef841 Mon Sep 17 00:00:00 2001 From: jinddings Date: Thu, 14 Nov 2024 11:51:50 +0900 Subject: [PATCH 18/21] =?UTF-8?q?=F0=9F=94=A7=20fix=20:=20auth=20api=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EB=B3=80=EA=B2=BD(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/auth/auth.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BE/src/auth/auth.controller.ts b/BE/src/auth/auth.controller.ts index 98c3d7ad..8fcdf300 100644 --- a/BE/src/auth/auth.controller.ts +++ b/BE/src/auth/auth.controller.ts @@ -16,7 +16,7 @@ import { ConfigService } from '@nestjs/config'; import { AuthService } from './auth.service'; import { AuthCredentialsDto } from './dto/auth-credentials.dto'; -@Controller('auth') +@Controller('/api/auth') export class AuthController { constructor( private authService: AuthService, From d218da80755ca23a211318b5bee38a9dbba71c1b Mon Sep 17 00:00:00 2001 From: jinddings Date: Thu, 14 Nov 2024 13:23:49 +0900 Subject: [PATCH 19/21] =?UTF-8?q?=F0=9F=9A=9A=20rename=20:=20redisUtil=20-?= =?UTF-8?q?>=20redisDomainService=EB=A1=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=20=EB=B0=8F=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../redis/{redis.ts => redis.domain-service.ts} | 14 +++++++++++++- BE/src/common/redis/redis.module.ts | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) rename BE/src/common/redis/{redis.ts => redis.domain-service.ts} (80%) diff --git a/BE/src/common/redis/redis.ts b/BE/src/common/redis/redis.domain-service.ts similarity index 80% rename from BE/src/common/redis/redis.ts rename to BE/src/common/redis/redis.domain-service.ts index e2bee153..ce636d6e 100644 --- a/BE/src/common/redis/redis.ts +++ b/BE/src/common/redis/redis.domain-service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import Redis from 'ioredis'; @Injectable() -export class RedisUtil { +export class RedisDomainService { constructor( @Inject('REDIS_CLIENT') private readonly redis: Redis, @@ -27,10 +27,22 @@ export class RedisUtil { return this.redis.zadd(key, score, member); } + async zcard(key: string): Promise { + return this.redis.zcard(key); + } + async zrange(key: string, start: number, stop: number): Promise { return this.redis.zrange(key, start, stop); } + async zremrangebyrank( + key: string, + start: number, + stop: number, + ): Promise { + return this.redis.zremrangebyrank(key, start, stop); + } + async zrevrange(key: string, start: number, stop: number): Promise { return this.redis.zrevrange(key, start, stop); } diff --git a/BE/src/common/redis/redis.module.ts b/BE/src/common/redis/redis.module.ts index d8aa958c..a0cf55bc 100644 --- a/BE/src/common/redis/redis.module.ts +++ b/BE/src/common/redis/redis.module.ts @@ -1,7 +1,7 @@ // src/common/redis/redis.module.ts import { Global, Module } from '@nestjs/common'; import Redis from 'ioredis'; -import { RedisUtil } from './redis'; +import { RedisDomainService } from './redis.domain-service'; @Global() @Module({ @@ -15,8 +15,8 @@ import { RedisUtil } from './redis'; }); }, }, - RedisUtil, + RedisDomainService, ], - exports: [RedisUtil, 'REDIS_CLIENT'], + exports: [RedisDomainService, 'REDIS_CLIENT'], }) export class RedisModule {} From 3bb03650d2b07a6f011585ac3970fa7ca0e6214f Mon Sep 17 00:00:00 2001 From: jinddings Date: Thu, 14 Nov 2024 13:24:18 +0900 Subject: [PATCH 20/21] =?UTF-8?q?=E2=9C=A8=20feat=20:=20=EC=B5=9C=EA=B7=BC?= =?UTF-8?q?=20=EA=B2=80=EC=83=89=EC=96=B4=2010=20=EA=B0=9C=EB=A7=8C=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/list/stock-list.module.ts | 4 ++-- BE/src/stock/list/stock-list.service.ts | 27 +++++++++++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/BE/src/stock/list/stock-list.module.ts b/BE/src/stock/list/stock-list.module.ts index 9c2c79e0..73243bb9 100644 --- a/BE/src/stock/list/stock-list.module.ts +++ b/BE/src/stock/list/stock-list.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { RedisUtil } from 'src/common/redis/redis'; +import { RedisDomainService } from 'src/common/redis/redis.domain-service'; import { RedisModule } from 'src/common/redis/redis.module'; import { StockListRepository } from './stock-list.repostiory'; import { StockListService } from './stock-list.service'; @@ -10,7 +10,7 @@ import { Stocks } from './stock-list.entity'; @Module({ imports: [TypeOrmModule.forFeature([Stocks]), RedisModule], controllers: [StockListController], - providers: [StockListRepository, StockListService, RedisUtil], + providers: [StockListRepository, StockListService, RedisDomainService], exports: [], }) export class StockListModule {} diff --git a/BE/src/stock/list/stock-list.service.ts b/BE/src/stock/list/stock-list.service.ts index 5868d9a8..f254576c 100644 --- a/BE/src/stock/list/stock-list.service.ts +++ b/BE/src/stock/list/stock-list.service.ts @@ -1,5 +1,5 @@ import { Injectable, NotFoundException } from '@nestjs/common'; -import { RedisUtil } from 'src/common/redis/redis'; +import { RedisDomainService } from 'src/common/redis/redis.domain-service'; import { StockListRepository } from './stock-list.repostiory'; import { Stocks } from './stock-list.entity'; import { StockListResponseDto } from './dto/stock-list-response.dto'; @@ -7,9 +7,11 @@ import { SearchParams } from './interface/search-params.interface'; @Injectable() export class StockListService { + private readonly SearchHistoryLimit = 10; + constructor( private readonly stockListRepository: StockListRepository, - private readonly redisUtil: RedisUtil, + private readonly RedisDomainService: RedisDomainService, ) {} private toResponseDto(stock: Stocks): StockListResponseDto { @@ -31,11 +33,24 @@ export class StockListService { } async search(params: SearchParams): Promise { - const key = `search:${params.userId}`; - const score = Date.now(); - - await this.redisUtil.zadd(key, score, JSON.stringify(params)); + await this.addSearchTermToRedis(params); const stocks = await this.stockListRepository.search(params); return stocks.map((stock) => this.toResponseDto(stock)); } + + async addSearchTermToRedis(params: SearchParams) { + const key = `search:${params.userId}`; + const timeStamp = Date.now(); + + const { name, market, code } = params; + + const searchTerm = name || market || code; + + await this.RedisDomainService.zadd(key, timeStamp, searchTerm); + + const searchHistoryCount = await this.RedisDomainService.zcard(key); + if (searchHistoryCount > this.SearchHistoryLimit) { + await this.RedisDomainService.zremrangebyrank(key, 0, 0); + } + } } From b19180cb1c8b4e200b3616fadffe70fa727ab6a0 Mon Sep 17 00:00:00 2001 From: jinddings Date: Thu, 14 Nov 2024 13:49:04 +0900 Subject: [PATCH 21/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20:=20lint?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/list/stock-list.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BE/src/stock/list/stock-list.service.ts b/BE/src/stock/list/stock-list.service.ts index f254576c..9d61f15a 100644 --- a/BE/src/stock/list/stock-list.service.ts +++ b/BE/src/stock/list/stock-list.service.ts @@ -11,7 +11,7 @@ export class StockListService { constructor( private readonly stockListRepository: StockListRepository, - private readonly RedisDomainService: RedisDomainService, + private readonly redisDomainService: RedisDomainService, ) {} private toResponseDto(stock: Stocks): StockListResponseDto { @@ -46,11 +46,11 @@ export class StockListService { const searchTerm = name || market || code; - await this.RedisDomainService.zadd(key, timeStamp, searchTerm); + await this.redisDomainService.zadd(key, timeStamp, searchTerm); - const searchHistoryCount = await this.RedisDomainService.zcard(key); + const searchHistoryCount = await this.redisDomainService.zcard(key); if (searchHistoryCount > this.SearchHistoryLimit) { - await this.RedisDomainService.zremrangebyrank(key, 0, 0); + await this.redisDomainService.zremrangebyrank(key, 0, 0); } } }