From edb326f6007dfd7b75e4158d0cf38188c8b17b58 Mon Sep 17 00:00:00 2001 From: JIN Date: Thu, 14 Nov 2024 16:49:08 +0900 Subject: [PATCH 01/36] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=8B=A4=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=B2=B4=EA=B2=B0=EA=B0=80=20=EC=9B=B9=EC=86=8C?= =?UTF-8?q?=EC=BC=93=20=EC=97=B0=EA=B2=B0=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1#56?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stock-trade-history-socket.service.ts | 53 +++++++++++++++++++ .../history/stock-trade-history.controller.ts | 3 ++ .../history/stock-trade-history.module.ts | 6 ++- BE/src/websocket/socket.gateway.ts | 4 ++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 BE/src/stock/trade/history/stock-trade-history-socket.service.ts diff --git a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts new file mode 100644 index 00000000..d97a818b --- /dev/null +++ b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts @@ -0,0 +1,53 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { SocketGateway } from '../../../websocket/socket.gateway'; +import { BaseSocketService } from '../../../websocket/base-socket.service'; + +@Injectable() +export class StockTradeHistorySocketService { + private TR_ID = 'H0STCNT0'; + private readonly logger = new Logger('StockTradeHistorySocketService'); + + constructor( + private readonly socketGateway: SocketGateway, + private readonly baseSocketService: BaseSocketService, + ) { + baseSocketService.registerSocketOpenHandler(() => { + this.logger.debug('trade-history 소켓 연결 성공'); + }); + + baseSocketService.registerSocketDataHandler( + this.TR_ID, + (dataList: string[]) => { + try { + // BaseSocketService에서 이미 split된 데이터가 옴 + const tradeData = { + stck_prpr: dataList[2], + cntg_vol: dataList[12], + prdy_ctrt: dataList[5], + stck_cntg_hour: dataList[1], + }; + + const eventName = `trade-history/${dataList[0]}`; + this.socketGateway.sendStockTradeHistoryValueToClient( + eventName, + tradeData, + ); + } catch (error) { + this.logger.error('Error processing trade data:', error); + this.logger.error('Raw data was:', dataList); + } + }, + ); + } + + subscribeByCode(stockCode: string) { + try { + this.logger.log(`Subscribing to stock: ${stockCode}`); + this.baseSocketService.registerCode(this.TR_ID, stockCode); + this.logger.log(`Successfully subscribed to stock: ${stockCode}`); + } catch (error) { + this.logger.error(`Failed to subscribe to stock ${stockCode}:`, error); + throw error; + } + } +} 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 4885aed2..f3850545 100644 --- a/BE/src/stock/trade/history/stock-trade-history.controller.ts +++ b/BE/src/stock/trade/history/stock-trade-history.controller.ts @@ -3,12 +3,14 @@ import { ApiOperation, ApiParam, ApiResponse, ApiTags } 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'; +import { StockTradeHistorySocketService } from './stock-trade-history-socket.service'; @ApiTags('주식현재가 체결 조회 API') @Controller('/api/stocks') export class StockTradeHistoryController { constructor( private readonly stockTradeHistoryService: StockTradeHistoryService, + private readonly stockTradeHistorySocketService: StockTradeHistorySocketService, ) {} @Get(':stockCode/today-trade-history') @@ -26,6 +28,7 @@ export class StockTradeHistoryController { type: TodayStockTradeHistoryResponseDto, }) getTodayStockTradeHistory(@Param('stockCode') stockCode: string) { + this.stockTradeHistorySocketService.subscribeByCode(stockCode); return this.stockTradeHistoryService.getTodayStockTradeHistory(stockCode); } diff --git a/BE/src/stock/trade/history/stock-trade-history.module.ts b/BE/src/stock/trade/history/stock-trade-history.module.ts index 965889a1..c21563f3 100644 --- a/BE/src/stock/trade/history/stock-trade-history.module.ts +++ b/BE/src/stock/trade/history/stock-trade-history.module.ts @@ -2,10 +2,12 @@ import { Module } from '@nestjs/common'; import { KoreaInvestmentModule } from '../../../koreaInvestment/korea-investment.module'; import { StockTradeHistoryController } from './stock-trade-history.controller'; import { StockTradeHistoryService } from './stock-trade-history.service'; +import { StockTradeHistorySocketService } from './stock-trade-history-socket.service'; +import { SocketModule } from '../../../websocket/socket.module'; @Module({ - imports: [KoreaInvestmentModule], + imports: [KoreaInvestmentModule, SocketModule], controllers: [StockTradeHistoryController], - providers: [StockTradeHistoryService], + providers: [StockTradeHistoryService, StockTradeHistorySocketService], }) export class StockTradeHistoryModule {} diff --git a/BE/src/websocket/socket.gateway.ts b/BE/src/websocket/socket.gateway.ts index d533c3f0..32ff997d 100644 --- a/BE/src/websocket/socket.gateway.ts +++ b/BE/src/websocket/socket.gateway.ts @@ -20,4 +20,8 @@ export class SocketGateway { this.server.emit(event, stockIndexValue); } + + sendStockTradeHistoryValueToClient(event, chartData) { + this.server.emit(event, chartData); + } } From b2664686fbe68be7b0420826980f70742245742d Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 10:14:35 +0900 Subject: [PATCH 02/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20status=20pending=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/stock-order.repository.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/BE/src/stock/order/stock-order.repository.ts b/BE/src/stock/order/stock-order.repository.ts index 6cd70c39..6db56d6f 100644 --- a/BE/src/stock/order/stock-order.repository.ts +++ b/BE/src/stock/order/stock-order.repository.ts @@ -15,6 +15,7 @@ export class StockOrderRepository extends Repository { async findAllCodeByStatus() { return this.createQueryBuilder('orders') .select('DISTINCT orders.stock_code') + .where({ status: StatusType.PENDING }) .getRawMany(); } From 57d1174277e2ad6154f44b50e306f0000f8edc8a Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 10:15:04 +0900 Subject: [PATCH 03/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20=EB=A7=A4=EB=8F=84/?= =?UTF-8?q?=EB=A7=A4=EC=88=98=20=EC=B2=B4=EA=B2=B0=20=EC=8B=9C=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=EB=AC=B8=20=EC=88=98=EC=A0=95=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/stock-order.repository.ts | 57 +++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/BE/src/stock/order/stock-order.repository.ts b/BE/src/stock/order/stock-order.repository.ts index 6db56d6f..2b419efa 100644 --- a/BE/src/stock/order/stock-order.repository.ts +++ b/BE/src/stock/order/stock-order.repository.ts @@ -34,38 +34,41 @@ export class StockOrderRepository extends Repository { .createQueryBuilder() .update(Asset) .set({ - cash_balance: () => `cash_balance - ${realPrice}`, - total_asset: () => `total_asset - ${realPrice}`, - total_profit: () => `total_profit - ${realPrice}`, + cash_balance: () => `cash_balance - :realPrice`, + total_asset: () => `total_asset - :realPrice`, + total_profit: () => `total_profit - :realPrice`, total_profit_rate: () => `total_profit / 10000000`, last_updated: new Date(), }) .where({ user_id: order.user_id }) + .setParameters({ realPrice }) .execute(); - await queryRunner.manager - .createQueryBuilder() - .insert() - .into(UserStock) - .values({ - user_id: order.user_id, - stock_code: order.stock_code, - quantity: order.amount, - avg_price: order.price, - }) - .orUpdate( - [ - `quantity = quantity + ${order.amount}`, - `avg_price = ((avg_price * quantity + ${order.price} * ${order.amount}) / (quantity + ${order.amount}))`, - ], - ['user_id', 'stock_code'], - ) - .execute(); + await queryRunner.query( + ` + INSERT INTO user_stocks (user_id, stock_code, quantity, avg_price, last_updated) + VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + quantity = quantity + ?, + avg_price = (avg_price * quantity + ? * ?) / (quantity + ?) +`, + [ + order.user_id, + order.stock_code, + order.amount, + order.price, + new Date(), + order.amount, + order.amount, + order.price, + order.amount, + ], + ); await queryRunner.commitTransaction(); } catch (err) { await queryRunner.rollbackTransaction(); - throw new InternalServerErrorException(); + throw new InternalServerErrorException(err); } finally { await queryRunner.release(); } @@ -86,22 +89,24 @@ export class StockOrderRepository extends Repository { .createQueryBuilder() .update(Asset) .set({ - cash_balance: () => `cash_balance + ${realPrice}`, - total_asset: () => `total_asset + ${realPrice}`, - total_profit: () => `total_profit + ${realPrice}`, + cash_balance: () => `cash_balance + :realPrice`, + total_asset: () => `total_asset + :realPrice`, + total_profit: () => `total_profit + :realPrice`, total_profit_rate: () => `total_profit / 10000000`, last_updated: new Date(), }) .where({ user_id: order.user_id }) + .setParameters({ realPrice }) .execute(); await queryRunner.manager .createQueryBuilder() .update(UserStock) .set({ - quantity: () => `quantity - ${order.amount}`, + quantity: () => `quantity - :newQuantity`, }) .where({ user_id: order.user_id, stock_code: order.stock_code }) + .setParameters({ newQuantity: order.amount }) .execute(); await queryRunner.commitTransaction(); From 139bc4a1d97fb8414d887fb5f8bd490944fc7745 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 10:16:25 +0900 Subject: [PATCH 04/36] =?UTF-8?q?=E2=9E=95=20add:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EB=B3=B4=EC=9C=A0=20=EC=A3=BC=EC=8B=9D=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=9C=A0=EB=8B=88=ED=81=AC=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=B6=94=EA=B0=80=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/userStock/user-stock.entity.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BE/src/userStock/user-stock.entity.ts b/BE/src/userStock/user-stock.entity.ts index 60a1b169..a02bf7f3 100644 --- a/BE/src/userStock/user-stock.entity.ts +++ b/BE/src/userStock/user-stock.entity.ts @@ -2,10 +2,12 @@ import { Column, Entity, PrimaryGeneratedColumn, + Unique, UpdateDateColumn, } from 'typeorm'; @Entity('user_stocks') +@Unique(['user_id', 'stock_code']) export class UserStock { @PrimaryGeneratedColumn() id: number; From b2fb455550fd801fddb1245690864ab46211cbde Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 10:17:12 +0900 Subject: [PATCH 05/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20user=20id=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/stock-order.controller.ts | 28 ++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/BE/src/stock/order/stock-order.controller.ts b/BE/src/stock/order/stock-order.controller.ts index 31960590..268f4786 100644 --- a/BE/src/stock/order/stock-order.controller.ts +++ b/BE/src/stock/order/stock-order.controller.ts @@ -14,10 +14,10 @@ import { ApiResponse, ApiTags, } from '@nestjs/swagger'; +import { Request } from 'express'; import { StockOrderService } from './stock-order.service'; import { StockOrderRequestDto } from './dto/stock-order-request.dto'; import { JwtAuthGuard } from '../../auth/jwt-auth-guard'; -import { RequestInterface } from './interface/request.interface'; @Controller('/api/stocks/trade') @ApiTags('주식 매수/매도 API') @@ -36,10 +36,13 @@ export class StockOrderController { description: '주식 매수 예약 등록 성공', }) async buy( - @Req() request: RequestInterface, + @Req() request: Request, @Body(ValidationPipe) stockOrderRequest: StockOrderRequestDto, ) { - await this.stockOrderService.buy(request.user.id, stockOrderRequest); + await this.stockOrderService.buy( + parseInt(request.user.userId, 10), + stockOrderRequest, + ); } @Post('/sell') @@ -54,13 +57,16 @@ export class StockOrderController { description: '주식 매도 예약 등록 성공', }) async sell( - @Req() request: RequestInterface, + @Req() request: Request, @Body(ValidationPipe) stockOrderRequest: StockOrderRequestDto, ) { - await this.stockOrderService.sell(request.user.id, stockOrderRequest); + await this.stockOrderService.sell( + parseInt(request.user.userId, 10), + stockOrderRequest, + ); } - @Delete('/:order_id') + @Delete('/:orderId') @ApiBearerAuth() @UseGuards(JwtAuthGuard) @ApiOperation({ @@ -71,10 +77,10 @@ export class StockOrderController { status: 200, description: '주식 매도/매수 취소 성공', }) - async cancel( - @Req() request: RequestInterface, - @Param('order_id') orderId: number, - ) { - await this.stockOrderService.cancel(request.user.id, orderId); + async cancel(@Req() request: Request, @Param('orderId') orderId: number) { + await this.stockOrderService.cancel( + parseInt(request.user.userId, 10), + orderId, + ); } } From e81574ebc2b985d2bef01acce6cf7e230fb24dec Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 10:17:46 +0900 Subject: [PATCH 06/36] =?UTF-8?q?=E2=9E=95=20add:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=B6=94=EA=B0=80=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/stock-order-socket.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BE/src/stock/order/stock-order-socket.service.ts b/BE/src/stock/order/stock-order-socket.service.ts index 6e2bec3b..dada0cfd 100644 --- a/BE/src/stock/order/stock-order-socket.service.ts +++ b/BE/src/stock/order/stock-order-socket.service.ts @@ -36,8 +36,8 @@ export class StockOrderSocketService { this.checkExecutableOrder( data[0], // 주식 코드 data[2], // 주식 체결가 - ).catch(() => { - throw new InternalServerErrorException(); + ).catch((err) => { + throw new InternalServerErrorException(err); }); }, ); From e0c8c27fe4c2b8f7166a164bbef332e8611b212d Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 10:23:37 +0900 Subject: [PATCH 07/36] =?UTF-8?q?=E2=9E=95=20add:=20=EB=A7=A4=EC=88=98?= =?UTF-8?q?=EC=8B=9C=20=EA=B0=80=EC=9A=A9=20=EC=9E=90=EC=82=B0=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/stock-order.service.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/BE/src/stock/order/stock-order.service.ts b/BE/src/stock/order/stock-order.service.ts index bdbfe8ce..d97e48eb 100644 --- a/BE/src/stock/order/stock-order.service.ts +++ b/BE/src/stock/order/stock-order.service.ts @@ -11,6 +11,7 @@ import { TradeType } from './enum/trade-type'; import { StatusType } from './enum/status-type'; import { StockOrderSocketService } from './stock-order-socket.service'; import { UserStockRepository } from '../../userStock/user-stock.repository'; +import { AssetRepository } from '../../asset/asset.repository'; @Injectable() export class StockOrderService { @@ -18,9 +19,18 @@ export class StockOrderService { private readonly stockOrderRepository: StockOrderRepository, private readonly stockOrderSocketService: StockOrderSocketService, private readonly userStockRepository: UserStockRepository, + private readonly assetRepository: AssetRepository, ) {} async buy(userId: number, stockOrderRequest: StockOrderRequestDto) { + const asset = await this.assetRepository.findOneBy({ user_id: userId }); + + if ( + asset && + asset.cash_balance >= stockOrderRequest.amount * stockOrderRequest.price + ) + throw new BadRequestException('가용 자산이 충분하지 않습니다.'); + const order = this.stockOrderRepository.create({ user_id: userId, stock_code: stockOrderRequest.stock_code, From 3b7999da907ed124ec939c0f31a9c26949cf7e91 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 10:27:54 +0900 Subject: [PATCH 08/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20=EB=A7=A4=EC=88=98?= =?UTF-8?q?=EC=8B=9C=20=EA=B0=80=EC=9A=A9=20=EC=9E=90=EC=82=B0=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/stock-order.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BE/src/stock/order/stock-order.service.ts b/BE/src/stock/order/stock-order.service.ts index d97e48eb..1c13617e 100644 --- a/BE/src/stock/order/stock-order.service.ts +++ b/BE/src/stock/order/stock-order.service.ts @@ -27,7 +27,7 @@ export class StockOrderService { if ( asset && - asset.cash_balance >= stockOrderRequest.amount * stockOrderRequest.price + asset.cash_balance < stockOrderRequest.amount * stockOrderRequest.price ) throw new BadRequestException('가용 자산이 충분하지 않습니다.'); From 8ae299c6b303f774161798e5a7199e3817064443 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 10:38:59 +0900 Subject: [PATCH 09/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20=EB=A7=A4=EC=88=98?= =?UTF-8?q?=EC=8B=9C=20=EB=B3=B4=EC=9C=A0=20=EC=A3=BC=EC=8B=9D=20=ED=8F=89?= =?UTF-8?q?=EA=B7=A0=EA=B0=80=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=EC=88=98=EC=A0=95=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/stock-order.repository.ts | 10 ++-------- BE/src/stock/order/stock-order.service.ts | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/BE/src/stock/order/stock-order.repository.ts b/BE/src/stock/order/stock-order.repository.ts index 2b419efa..94bfd37f 100644 --- a/BE/src/stock/order/stock-order.repository.ts +++ b/BE/src/stock/order/stock-order.repository.ts @@ -45,22 +45,16 @@ export class StockOrderRepository extends Repository { .execute(); await queryRunner.query( - ` - INSERT INTO user_stocks (user_id, stock_code, quantity, avg_price, last_updated) - VALUES (?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - quantity = quantity + ?, - avg_price = (avg_price * quantity + ? * ?) / (quantity + ?) -`, + `INSERT INTO user_stocks (user_id, stock_code, quantity, avg_price, last_updated) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE avg_price = (avg_price * quantity + ? * ?) / (quantity + ?), quantity = quantity + ?`, [ order.user_id, order.stock_code, order.amount, order.price, new Date(), + order.price, order.amount, order.amount, - order.price, order.amount, ], ); diff --git a/BE/src/stock/order/stock-order.service.ts b/BE/src/stock/order/stock-order.service.ts index 1c13617e..dd6df0cc 100644 --- a/BE/src/stock/order/stock-order.service.ts +++ b/BE/src/stock/order/stock-order.service.ts @@ -50,7 +50,7 @@ export class StockOrderService { stock_code: stockOrderRequest.stock_code, }); - if (!userStock || userStock.quantity === 0) + if (!userStock || userStock.quantity < stockOrderRequest.amount) throw new BadRequestException('주식을 매도 수만큼 가지고 있지 않습니다.'); const order = this.stockOrderRepository.create({ From e454dc2cf2308891f3d44c58c139a6a4bb4766d0 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Fri, 15 Nov 2024 11:40:42 +0900 Subject: [PATCH 10/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20=EB=B3=B4=EC=9C=A0?= =?UTF-8?q?=20=EC=A3=BC=EC=8B=9D=20=ED=8F=89=EA=B7=A0=EA=B0=80=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=88=98=EC=A0=95=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/userStock/user-stock.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BE/src/userStock/user-stock.entity.ts b/BE/src/userStock/user-stock.entity.ts index a02bf7f3..5d0803dc 100644 --- a/BE/src/userStock/user-stock.entity.ts +++ b/BE/src/userStock/user-stock.entity.ts @@ -21,7 +21,7 @@ export class UserStock { @Column({ nullable: false }) quantity: number; - @Column('decimal', { nullable: false, precision: 10, scale: 5 }) + @Column('float', { nullable: false, scale: 5 }) avg_price: number; @UpdateDateColumn() From d68955bb80167b25878ce6b73ee1f8829dd7c347 Mon Sep 17 00:00:00 2001 From: JIN Date: Fri, 15 Nov 2024 13:52:20 +0900 Subject: [PATCH 11/36] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=8B=A4=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=B2=B4=EA=B2=B0=EA=B0=80=20=EC=9B=B9=EC=86=8C?= =?UTF-8?q?=EC=BC=93=20=EC=97=B0=EA=B2=B0=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20test.html=20=EC=83=9D=EC=84=B1#56?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/main.ts | 1 + .../stock-trade-history-socket.service.ts | 34 ++-- BE/src/stock/trade/history/test.html | 181 ++++++++++++++++++ 3 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 BE/src/stock/trade/history/test.html diff --git a/BE/src/main.ts b/BE/src/main.ts index ed080e3c..97508254 100644 --- a/BE/src/main.ts +++ b/BE/src/main.ts @@ -15,6 +15,7 @@ async function bootstrap() { 'http://juga.kro.kr:5173', 'http://juga.kro.kr:3000', 'http://223.130.151.42:3000', + 'http://localhost:63342', ], methods: 'GET, HEAD, PUT, PATH, POST, DELETE', preflightContinue: false, diff --git a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts index d97a818b..c96371b7 100644 --- a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts @@ -2,10 +2,18 @@ import { Injectable, Logger } from '@nestjs/common'; import { SocketGateway } from '../../../websocket/socket.gateway'; import { BaseSocketService } from '../../../websocket/base-socket.service'; +interface TradeHistoryData { + stck_prpr: string; // 체결가(주식 현재가) + cntg_vol: string; // 체결 거래량 + prdy_ctrt: string; // 전일 대비율 + stck_cntg_hour: string; // 주식 체결 시간 +} + @Injectable() export class StockTradeHistorySocketService { private TR_ID = 'H0STCNT0'; private readonly logger = new Logger('StockTradeHistorySocketService'); + private subscribedStocks = new Set(); constructor( private readonly socketGateway: SocketGateway, @@ -13,21 +21,26 @@ export class StockTradeHistorySocketService { ) { baseSocketService.registerSocketOpenHandler(() => { this.logger.debug('trade-history 소켓 연결 성공'); + this.subscribedStocks.forEach((stockCode) => { + this.baseSocketService.registerCode(this.TR_ID, stockCode); + }); }); baseSocketService.registerSocketDataHandler( this.TR_ID, (dataList: string[]) => { try { - // BaseSocketService에서 이미 split된 데이터가 옴 - const tradeData = { + const stockCode = dataList[0]; + + const tradeData: TradeHistoryData = { stck_prpr: dataList[2], cntg_vol: dataList[12], prdy_ctrt: dataList[5], stck_cntg_hour: dataList[1], }; - const eventName = `trade-history/${dataList[0]}`; + const eventName = `trade-history/${stockCode}`; + this.logger.debug(`Emitting trade data for ${stockCode}`); this.socketGateway.sendStockTradeHistoryValueToClient( eventName, tradeData, @@ -41,13 +54,12 @@ export class StockTradeHistorySocketService { } subscribeByCode(stockCode: string) { - try { - this.logger.log(`Subscribing to stock: ${stockCode}`); - this.baseSocketService.registerCode(this.TR_ID, stockCode); - this.logger.log(`Successfully subscribed to stock: ${stockCode}`); - } catch (error) { - this.logger.error(`Failed to subscribe to stock ${stockCode}:`, error); - throw error; - } + this.baseSocketService.registerCode(this.TR_ID, stockCode); + this.subscribedStocks.add(stockCode); + } + + unsubscribeByCode(stockCode: string) { + this.baseSocketService.unregisterCode(this.TR_ID, stockCode); + this.subscribedStocks.delete(stockCode); } } diff --git a/BE/src/stock/trade/history/test.html b/BE/src/stock/trade/history/test.html new file mode 100644 index 00000000..0aecd1a1 --- /dev/null +++ b/BE/src/stock/trade/history/test.html @@ -0,0 +1,181 @@ + + + + + 주식 실시간 데이터 테스트 + + + + +
+

주식 실시간 데이터 테스트

+ +
+ +
+ + +
+
+ +
+

실시간 데이터가 여기에 표시됩니다...

+
+
+ + + + \ No newline at end of file From 43f5c1e6f9469bdc9b2687ede1a68e01b6e2577e Mon Sep 17 00:00:00 2001 From: jinddings Date: Mon, 18 Nov 2024 13:35:26 +0900 Subject: [PATCH 12/36] =?UTF-8?q?=F0=9F=94=A7=20fix=20:=20alpha=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20CD/CD=20=EC=9B=8C=ED=81=AC=ED=94=8C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deply-alpha.yml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/deply-alpha.yml b/.github/workflows/deply-alpha.yml index db8ea854..36e91731 100644 --- a/.github/workflows/deply-alpha.yml +++ b/.github/workflows/deply-alpha.yml @@ -17,10 +17,7 @@ jobs: max-parallel: 1 matrix: app: - [ - { name: 'be', dir: 'BE', port: 3000, container: 'juga-docker-be' }, - { name: 'fe', dir: 'FE', port: 5173, container: 'juga-docker-fe' }, - ] + [{ name: 'be', dir: 'BE', port: 3000, container: 'juga-docker-be' }] steps: - uses: actions/checkout@v4 @@ -35,11 +32,7 @@ jobs: - name: Create .env file run: | touch ./${{ matrix.app.dir }}/.env - if [ "${{ matrix.app.name }}" = "be" ]; then - echo "${{ secrets.ENV }}" > ./${{matrix.app.dir}}/.env - else - echo "${{ secrets.ENV_FE }}" > ./${{matrix.app.dir}}/.env - fi + echo "${{ secrets.ENV }}" > ./${{matrix.app.dir}}/.env - name: Install dependencies working-directory: ./${{matrix.app.dir}} @@ -108,12 +101,7 @@ jobs: port: 22 script: | docker system prune -af - if [ "${{ matrix.app.name }}" = "be" ]; then - echo "${{ secrets.ENV }}" > .env - else - echo "${{ secrets.ENV_FE }}" > .env - fi - + echo "${{ secrets.ENV }}" > .env docker network create juga-network || true From f8b6560a05626816e573101f1918aa564606f5f4 Mon Sep 17 00:00:00 2001 From: jinddings Date: Mon, 18 Nov 2024 13:36:03 +0900 Subject: [PATCH 13/36] =?UTF-8?q?=F0=9F=9A=9A=20rename=20:alpha=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=20rename?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/{deply-alpha.yml => deploy-alpha.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{deply-alpha.yml => deploy-alpha.yml} (100%) diff --git a/.github/workflows/deply-alpha.yml b/.github/workflows/deploy-alpha.yml similarity index 100% rename from .github/workflows/deply-alpha.yml rename to .github/workflows/deploy-alpha.yml From 0ed0871209e8b38a21ac8352da211c4976b64fcd Mon Sep 17 00:00:00 2001 From: JIN Date: Mon, 18 Nov 2024 13:56:22 +0900 Subject: [PATCH 14/36] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=8B=A4=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=B2=B4=EA=B2=B0=EA=B0=80=20=EC=9B=B9=EC=86=8C?= =?UTF-8?q?=EC=BC=93=20=EC=97=B0=EA=B2=B0=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20test.html=20=EC=83=9D=EC=84=B1#127?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stock-trade-history-socket.service.ts | 143 +++++++++++++----- 1 file changed, 104 insertions(+), 39 deletions(-) diff --git a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts index c96371b7..f7c3e92b 100644 --- a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts @@ -1,6 +1,12 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { + Injectable, + Logger, + OnModuleInit, +} from '@nestjs/common'; import { SocketGateway } from '../../../websocket/socket.gateway'; -import { BaseSocketService } from '../../../websocket/base-socket.service'; +import { WebSocket } from 'ws'; +import axios from 'axios'; +import { SocketConnectTokenInterface } from '../../../websocket/interface/socket.interface'; interface TradeHistoryData { stck_prpr: string; // 체결가(주식 현재가) @@ -10,56 +16,115 @@ interface TradeHistoryData { } @Injectable() -export class StockTradeHistorySocketService { +export class StockTradeHistorySocketService implements OnModuleInit { private TR_ID = 'H0STCNT0'; private readonly logger = new Logger('StockTradeHistorySocketService'); private subscribedStocks = new Set(); + private socket: WebSocket; + private socketConnectionKey: string; - constructor( - private readonly socketGateway: SocketGateway, - private readonly baseSocketService: BaseSocketService, - ) { - baseSocketService.registerSocketOpenHandler(() => { - this.logger.debug('trade-history 소켓 연결 성공'); - this.subscribedStocks.forEach((stockCode) => { - this.baseSocketService.registerCode(this.TR_ID, stockCode); - }); - }); - - baseSocketService.registerSocketDataHandler( - this.TR_ID, - (dataList: string[]) => { - try { - const stockCode = dataList[0]; - - const tradeData: TradeHistoryData = { - stck_prpr: dataList[2], - cntg_vol: dataList[12], - prdy_ctrt: dataList[5], - stck_cntg_hour: dataList[1], - }; - - const eventName = `trade-history/${stockCode}`; - this.logger.debug(`Emitting trade data for ${stockCode}`); - this.socketGateway.sendStockTradeHistoryValueToClient( - eventName, - tradeData, + constructor(private readonly socketGateway: SocketGateway) {} + + async onModuleInit() { + this.socketConnectionKey = await this.getSocketConnectionKey(); + this.socket = new WebSocket(process.env.KOREA_INVESTMENT_TEST_SOCKET_URL); + + this.socket.onopen = () => { + this.registerCode(this.TR_ID, '005930'); + }; + + this.socket.onmessage = (event) => { + const data = + typeof event.data === 'string' + ? event.data.split('|') + : JSON.stringify(event.data); + + if (data.length < 2) { + const json = JSON.parse(data[0]); + if (json.body) + this.logger.log( + `한국투자증권 웹소켓 연결: ${json.body.msg1}`, + json.header.tr_id, ); - } catch (error) { - this.logger.error('Error processing trade data:', error); - this.logger.error('Raw data was:', dataList); - } - }, + if (json.header.tr_id === 'PINGPONG') + this.socket.pong(JSON.stringify(json)); + return; + } + + const dataList = data[3].split('^'); + + if (Number(dataList[1]) % 500 === 0) + this.logger.log(`한국투자증권 데이터 수신 성공 (5분 단위)`, data[1]); + + console.log(dataList); + }; + + this.socket.onclose = () => { + this.logger.warn(`한국투자증권 소켓 연결 종료`); + }; + } + + registerCode(trId: string, trKey: string) { + this.socket.send( + JSON.stringify({ + header: { + approval_key: this.socketConnectionKey, + custtype: 'P', + tr_type: '1', + 'content-type': 'utf-8', + }, + body: { + input: { + tr_id: trId, + tr_key: trKey, + }, + }, + }), + ); + } + + unregisterCode(trId: string, trKey: string) { + this.socket.send( + JSON.stringify({ + header: { + approval_key: this.socketConnectionKey, + custtype: 'P', + tr_type: '2', + 'content-type': 'utf-8', + }, + body: { + input: { + tr_id: trId, + tr_key: trKey, + }, + }, + }), ); } subscribeByCode(stockCode: string) { - this.baseSocketService.registerCode(this.TR_ID, stockCode); this.subscribedStocks.add(stockCode); } unsubscribeByCode(stockCode: string) { - this.baseSocketService.unregisterCode(this.TR_ID, stockCode); this.subscribedStocks.delete(stockCode); } + + async getSocketConnectionKey() { + if (this.socketConnectionKey) { + return this.socketConnectionKey; + } + + const response = await axios.post( + 'https://openapivts.koreainvestment.com:29443/oauth2/Approval', + { + grant_type: 'client_credentials', + appkey: process.env.KOREA_INVESTMENT_TEST_APP_KEY, + secretkey: process.env.KOREA_INVESTMENT_TEST_APP_SECRET, + }, + ); + + this.socketConnectionKey = response.data.approval_key; + return this.socketConnectionKey; + } } From e15169bc6359eea221b849b1b5026c2cb718e28e Mon Sep 17 00:00:00 2001 From: JIN Date: Mon, 18 Nov 2024 13:58:30 +0900 Subject: [PATCH 15/36] =?UTF-8?q?=F0=9F=94=A5=20remove:=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20html=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C#127?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/trade/history/test.html | 181 --------------------------- 1 file changed, 181 deletions(-) delete mode 100644 BE/src/stock/trade/history/test.html diff --git a/BE/src/stock/trade/history/test.html b/BE/src/stock/trade/history/test.html deleted file mode 100644 index 0aecd1a1..00000000 --- a/BE/src/stock/trade/history/test.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - 주식 실시간 데이터 테스트 - - - - -
-

주식 실시간 데이터 테스트

- -
- -
- - -
-
- -
-

실시간 데이터가 여기에 표시됩니다...

-
-
- - - - \ No newline at end of file From 279bd4b27882968829bf86f9118957639f837332 Mon Sep 17 00:00:00 2001 From: JIN Date: Mon, 18 Nov 2024 15:53:14 +0900 Subject: [PATCH 16/36] =?UTF-8?q?=F0=9F=93=9D=20docs:=20swagger=EC=97=90?= =?UTF-8?q?=20=EC=9D=91=EB=8B=B5=20=ED=83=80=EC=9E=85=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stock/trade/history/stock-trade-history.controller.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 8332b51b..54a67b41 100644 --- a/BE/src/stock/trade/history/stock-trade-history.controller.ts +++ b/BE/src/stock/trade/history/stock-trade-history.controller.ts @@ -1,9 +1,9 @@ import { Controller, Get, Param } from '@nestjs/common'; import { ApiOperation, ApiParam, ApiResponse, ApiTags } 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'; import { StockTradeHistorySocketService } from './stock-trade-history-socket.service'; +import { TodayStockTradeHistoryDataDto } from './dto/today-stock-trade-history-data.dto'; +import { DailyStockTradeHistoryDataDto } from './dto/daily-stock-trade-history-data.dto'; @ApiTags('주식현재가 체결 조회 API') @Controller('/api/stocks/trade-history') @@ -25,10 +25,9 @@ export class StockTradeHistoryController { @ApiResponse({ status: 200, description: '단일 주식 종목에 대한 주식현재가 체결값 조회 성공', - type: TodayStockTradeHistoryResponseDto, + type: TodayStockTradeHistoryDataDto, }) getTodayStockTradeHistory(@Param('stockCode') stockCode: string) { - this.stockTradeHistorySocketService.subscribeByCode(stockCode); return this.stockTradeHistoryService.getTodayStockTradeHistory(stockCode); } From 03ac949c44a004c7e360f6c2405203b9a4ee3f33 Mon Sep 17 00:00:00 2001 From: JIN Date: Mon, 18 Nov 2024 15:54:03 +0900 Subject: [PATCH 17/36] =?UTF-8?q?=E2=9C=A8=20feat:=20=EB=AA=A8=EC=9D=98=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9A=A9=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80#127?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/util/get-full-URL.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BE/src/util/get-full-URL.ts b/BE/src/util/get-full-URL.ts index da79427f..7be7ea50 100644 --- a/BE/src/util/get-full-URL.ts +++ b/BE/src/util/get-full-URL.ts @@ -1,3 +1,7 @@ export const getFullURL = (url: string) => { return `${process.env.KOREA_INVESTMENT_BASE_URL}${url}`; }; + +export const getFullTestURL = (url: string) => { + return `${process.env.KOREA_INVESTMENT_TEST_BASE_URL}${url}`; +}; From 5212038fb29e39d9e2fcac4517d57a9edadbd1fb Mon Sep 17 00:00:00 2001 From: JIN Date: Mon, 18 Nov 2024 16:16:06 +0900 Subject: [PATCH 18/36] =?UTF-8?q?=E2=9C=A8=20feat:=20SSE=EB=A1=9C=20?= =?UTF-8?q?=EC=8B=A4=EC=8B=9C=EA=B0=84=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1#12?= =?UTF-8?q?7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade/history/interface/sse-event.ts | 6 ++ .../stock-trade-history-socket.service.ts | 73 ++++++++++--------- .../history/stock-trade-history.controller.ts | 34 ++++++++- 3 files changed, 78 insertions(+), 35 deletions(-) create mode 100644 BE/src/stock/trade/history/interface/sse-event.ts diff --git a/BE/src/stock/trade/history/interface/sse-event.ts b/BE/src/stock/trade/history/interface/sse-event.ts new file mode 100644 index 00000000..e20f58cb --- /dev/null +++ b/BE/src/stock/trade/history/interface/sse-event.ts @@ -0,0 +1,6 @@ +export interface SseEvent { + data: string; + id?: string; + type?: string; + retry?: number; +} diff --git a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts index f7c3e92b..4489d613 100644 --- a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts @@ -1,37 +1,26 @@ -import { - Injectable, - Logger, - OnModuleInit, -} from '@nestjs/common'; -import { SocketGateway } from '../../../websocket/socket.gateway'; import { WebSocket } from 'ws'; import axios from 'axios'; +import { Observable, Subject } from 'rxjs'; +import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; +import { SseEvent } from './interface/sse-event'; import { SocketConnectTokenInterface } from '../../../websocket/interface/socket.interface'; - -interface TradeHistoryData { - stck_prpr: string; // 체결가(주식 현재가) - cntg_vol: string; // 체결 거래량 - prdy_ctrt: string; // 전일 대비율 - stck_cntg_hour: string; // 주식 체결 시간 -} +import { getFullTestURL } from '../../../util/get-full-URL'; +import { TodayStockTradeHistoryDataDto } from './dto/today-stock-trade-history-data.dto'; @Injectable() export class StockTradeHistorySocketService implements OnModuleInit { - private TR_ID = 'H0STCNT0'; - private readonly logger = new Logger('StockTradeHistorySocketService'); - private subscribedStocks = new Set(); + private readonly logger = new Logger(''); private socket: WebSocket; private socketConnectionKey: string; - - constructor(private readonly socketGateway: SocketGateway) {} + private subscribedStocks = new Set(); + private TR_ID = 'H0STCNT0'; + private eventSubject = new Subject(); async onModuleInit() { this.socketConnectionKey = await this.getSocketConnectionKey(); this.socket = new WebSocket(process.env.KOREA_INVESTMENT_TEST_SOCKET_URL); - this.socket.onopen = () => { - this.registerCode(this.TR_ID, '005930'); - }; + this.socket.onopen = () => {}; this.socket.onmessage = (event) => { const data = @@ -53,10 +42,20 @@ export class StockTradeHistorySocketService implements OnModuleInit { const dataList = data[3].split('^'); - if (Number(dataList[1]) % 500 === 0) - this.logger.log(`한국투자증권 데이터 수신 성공 (5분 단위)`, data[1]); - - console.log(dataList); + const tradeData: TodayStockTradeHistoryDataDto = { + stck_cntg_hour: dataList[1], + stck_prpr: dataList[2], + prdy_vrss_sign: dataList[3], + cntg_vol: dataList[12], + prdy_ctrt: dataList[5], + }; + + this.eventSubject.next({ + data: JSON.stringify({ + stockCode: data[1], + tradeData, + }), + }); }; this.socket.onclose = () => { @@ -64,6 +63,20 @@ export class StockTradeHistorySocketService implements OnModuleInit { }; } + getTradeDataStream(): Observable { + return this.eventSubject.asObservable(); + } + + subscribeByCode(stockCode: string) { + this.registerCode(this.TR_ID, stockCode); + this.subscribedStocks.add(stockCode); + } + + unsubscribeByCode(stockCode: string) { + this.unregisterCode(this.TR_ID, stockCode); + this.subscribedStocks.delete(stockCode); + } + registerCode(trId: string, trKey: string) { this.socket.send( JSON.stringify({ @@ -102,21 +115,13 @@ export class StockTradeHistorySocketService implements OnModuleInit { ); } - subscribeByCode(stockCode: string) { - this.subscribedStocks.add(stockCode); - } - - unsubscribeByCode(stockCode: string) { - this.subscribedStocks.delete(stockCode); - } - async getSocketConnectionKey() { if (this.socketConnectionKey) { return this.socketConnectionKey; } const response = await axios.post( - 'https://openapivts.koreainvestment.com:29443/oauth2/Approval', + getFullTestURL('/oauth2/Approval'), { grant_type: 'client_credentials', appkey: process.env.KOREA_INVESTMENT_TEST_APP_KEY, 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 54a67b41..cbbe09c1 100644 --- a/BE/src/stock/trade/history/stock-trade-history.controller.ts +++ b/BE/src/stock/trade/history/stock-trade-history.controller.ts @@ -1,9 +1,11 @@ -import { Controller, Get, Param } from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { Controller, Get, Param, Sse } from '@nestjs/common'; import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; import { StockTradeHistoryService } from './stock-trade-history.service'; import { StockTradeHistorySocketService } from './stock-trade-history-socket.service'; import { TodayStockTradeHistoryDataDto } from './dto/today-stock-trade-history-data.dto'; import { DailyStockTradeHistoryDataDto } from './dto/daily-stock-trade-history-data.dto'; +import { SseEvent } from './interface/sse-event'; @ApiTags('주식현재가 체결 조회 API') @Controller('/api/stocks/trade-history') @@ -48,4 +50,34 @@ export class StockTradeHistoryController { getDailyStockTradeHistory(@Param('stockCode') stockCode: string) { return this.stockTradeHistoryService.getDailyStockTradeHistory(stockCode); } + + @Sse(':stockCode/today-sse') + @ApiOperation({ summary: '단일 주식 종목에 대한 실시간 체결 데이터 스트림' }) + @ApiParam({ + name: 'stockCode', + required: true, + description: + '종목 코드\n\n' + + '(ex) 005930 삼성전자 / 005380 현대차 / 001500 현대차증권', + }) + @ApiResponse({ + status: 200, + description: + '단일 주식 종목에 대한 주식현재가 체결값 실시간 데이터 조회 성공', + type: TodayStockTradeHistoryDataDto, + }) + streamTradeHistory(@Param('stockCode') stockCode: string) { + this.stockTradeHistorySocketService.subscribeByCode(stockCode); + + return new Observable((subscriber) => { + const subscription = this.stockTradeHistorySocketService + .getTradeDataStream() + .subscribe(subscriber); + + return () => { + this.stockTradeHistorySocketService.unsubscribeByCode(stockCode); + subscription.unsubscribe(); + }; + }); + } } From e6cf90a6727f0eddd1de18ee9494a0a4f7c1cd4f Mon Sep 17 00:00:00 2001 From: JIN Date: Mon, 18 Nov 2024 16:39:47 +0900 Subject: [PATCH 19/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20cors=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20=EC=84=A4=EC=A0=95=EC=97=90=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=86=8C=EA=B0=80=20=ED=8F=AC?= =?UTF-8?q?=ED=95=A8=EB=90=98=EC=96=B4=EC=9E=88=EB=8D=98=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EC=88=98=EC=A0=95#127?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/BE/src/main.ts b/BE/src/main.ts index 97508254..ed080e3c 100644 --- a/BE/src/main.ts +++ b/BE/src/main.ts @@ -15,7 +15,6 @@ async function bootstrap() { 'http://juga.kro.kr:5173', 'http://juga.kro.kr:3000', 'http://223.130.151.42:3000', - 'http://localhost:63342', ], methods: 'GET, HEAD, PUT, PATH, POST, DELETE', preflightContinue: false, From 9a4eaf2224012c32754e179ee13d3e86631b42ea Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Mon, 18 Nov 2024 16:54:46 +0900 Subject: [PATCH 20/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20=EC=86=8C=EC=BC=93?= =?UTF-8?q?=20=EC=97=B0=EA=B2=B0=20=EC=A2=85=EB=A3=8C=20=EC=8B=9C=201?= =?UTF-8?q?=EB=B6=84=20=EA=B0=84=EA=B2=A9=EC=9C=BC=EB=A1=9C=20=EC=9E=AC?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?#126?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/websocket/base-socket.service.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/BE/src/websocket/base-socket.service.ts b/BE/src/websocket/base-socket.service.ts index 66bc6a1d..d075fffe 100644 --- a/BE/src/websocket/base-socket.service.ts +++ b/BE/src/websocket/base-socket.service.ts @@ -63,6 +63,11 @@ export class BaseSocketService implements OnModuleInit { this.socket.onclose = () => { this.logger.warn(`한국투자증권 소켓 연결 종료`); + setTimeout(() => { + this.onModuleInit().catch((err) => { + throw new InternalServerErrorException(err); + }); + }, 60000); }; } From 0f3e141c94380f231532e7dc2f570d5890c4db84 Mon Sep 17 00:00:00 2001 From: jinddings Date: Mon, 18 Nov 2024 17:45:28 +0900 Subject: [PATCH 21/36] =?UTF-8?q?=F0=9F=94=A7=20fix=20:=20alpha,=20product?= =?UTF-8?q?ion=20ENV=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-alpha.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-alpha.yml b/.github/workflows/deploy-alpha.yml index 36e91731..ba52ca0e 100644 --- a/.github/workflows/deploy-alpha.yml +++ b/.github/workflows/deploy-alpha.yml @@ -32,7 +32,7 @@ jobs: - name: Create .env file run: | touch ./${{ matrix.app.dir }}/.env - echo "${{ secrets.ENV }}" > ./${{matrix.app.dir}}/.env + echo "${{ secrets.ENV_ALPHA }}" > ./${{matrix.app.dir}}/.env - name: Install dependencies working-directory: ./${{matrix.app.dir}} @@ -101,7 +101,7 @@ jobs: port: 22 script: | docker system prune -af - echo "${{ secrets.ENV }}" > .env + echo "${{ secrets.ENV_ALPHA }}" > .env docker network create juga-network || true From 4df492aba87d5a67e3fe16e8a4ccd4ef11334370 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Mon, 18 Nov 2024 18:19:46 +0900 Subject: [PATCH 22/36] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20requestA?= =?UTF-8?q?pi=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=EC=97=90=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../korea-investment.service.ts | 42 +++++- BE/src/stock/detail/stock-detail.service.ts | 62 ++------ BE/src/stock/index/stock-index.controller.ts | 64 ++------ BE/src/stock/index/stock-index.service.ts | 138 ++++++++---------- BE/src/stock/topfive/stock-topfive.service.ts | 61 +++----- .../history/stock-trade-history.service.ts | 62 ++------ 6 files changed, 161 insertions(+), 268 deletions(-) diff --git a/BE/src/koreaInvestment/korea-investment.service.ts b/BE/src/koreaInvestment/korea-investment.service.ts index 29dc323c..43eb43e2 100644 --- a/BE/src/koreaInvestment/korea-investment.service.ts +++ b/BE/src/koreaInvestment/korea-investment.service.ts @@ -1,12 +1,15 @@ import axios from 'axios'; -import { UnauthorizedException } from '@nestjs/common'; +import { Logger, UnauthorizedException } from '@nestjs/common'; import { getFullURL } from '../util/get-full-URL'; import { AccessTokenInterface } from './interface/korea-investment.interface'; +import { getHeader } from '../util/get-header'; export class KoreaInvestmentService { private accessToken: string; private tokenExpireTime: Date; + private readonly logger = new Logger(); + async getAccessToken() { // accessToken이 유효한 경우 if (this.accessToken && this.tokenExpireTime > new Date()) { @@ -29,4 +32,41 @@ export class KoreaInvestmentService { return this.accessToken; } + + /** + * @private 한국투자 Open API - API 호출용 공통 함수 + * @param {string} trId - API 호출에 사용할 tr_id + * @param {string} apiURL - API 호출에 사용할 URL + * @param {Record} params - API 요청 시 필요한 쿼리 파라미터 DTO + * @returns - API 호출에 대한 응답 데이터 + * + * @author uuuo3o + */ + async requestApi( + trId: string, + apiURL: string, + params: Record, + ): Promise { + try { + const accessToken = await this.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; + } + } } diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index 28589a67..8ae002ff 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -1,8 +1,5 @@ -import axios from 'axios'; 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 { InquirePriceChartApiResponse } from './interface/stock-detail-chart.interface'; import { InquirePriceChartDataDto } from './dto/stock-detail-chart-data.dto'; import { @@ -35,11 +32,12 @@ export class StockDetailService { fid_input_iscd: stockCode, }; - const response = await this.requestApi( - 'FHKST01010100', - '/uapi/domestic-stock/v1/quotations/inquire-price', - queryParams, - ); + const response = + await this.koreaInvestmentService.requestApi( + 'FHKST01010100', + '/uapi/domestic-stock/v1/quotations/inquire-price', + queryParams, + ); return await this.formatStockData(response.output); } catch (error) { @@ -106,11 +104,12 @@ export class StockDetailService { fid_org_adj_prc: '0', }; - const response = await this.requestApi( - 'FHKST03010100', - '/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice', - queryParams, - ); + const response = + await this.koreaInvestmentService.requestApi( + 'FHKST03010100', + '/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice', + queryParams, + ); return this.formatStockInquirePriceData(response).slice().reverse(); } catch (error) { @@ -158,41 +157,4 @@ export class StockDetailService { return stockData; }); } - - /** - * @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( - trId: string, - apiURL: string, - params: Record, - ): Promise { - try { - const accessToken = await this.koreaInvestmentService.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; - } - } } diff --git a/BE/src/stock/index/stock-index.controller.ts b/BE/src/stock/index/stock-index.controller.ts index 330685e2..9ac6f114 100644 --- a/BE/src/stock/index/stock-index.controller.ts +++ b/BE/src/stock/index/stock-index.controller.ts @@ -26,46 +26,22 @@ export class StockIndexController { type: StockIndexResponseDto, }) async getStockIndex() { - const accessToken = await this.koreaInvestmentService.getAccessToken(); + await this.koreaInvestmentService.getAccessToken(); const [kospiChart, kosdaqChart, kospi200Chart, ksq150Chart] = await Promise.all([ - this.stockIndexService.getDomesticStockIndexListByCode( - '0001', - accessToken, - ), // 코스피 - this.stockIndexService.getDomesticStockIndexListByCode( - '1001', - accessToken, - ), // 코스닥 - this.stockIndexService.getDomesticStockIndexListByCode( - '2001', - accessToken, - ), // 코스피200 - this.stockIndexService.getDomesticStockIndexListByCode( - '3003', - accessToken, - ), // KSQ150 + this.stockIndexService.getDomesticStockIndexListByCode('0001'), // 코스피 + this.stockIndexService.getDomesticStockIndexListByCode('1001'), // 코스닥 + this.stockIndexService.getDomesticStockIndexListByCode('2001'), // 코스피200 + this.stockIndexService.getDomesticStockIndexListByCode('3003'), // KSQ150 ]); const [kospiValue, kosdaqValue, kospi200Value, ksq150Value] = await Promise.all([ - this.stockIndexService.getDomesticStockIndexValueByCode( - '0001', - accessToken, - ), // 코스피 - this.stockIndexService.getDomesticStockIndexValueByCode( - '1001', - accessToken, - ), // 코스닥 - this.stockIndexService.getDomesticStockIndexValueByCode( - '2001', - accessToken, - ), // 코스피200 - this.stockIndexService.getDomesticStockIndexValueByCode( - '3003', - accessToken, - ), // KSQ150 + this.stockIndexService.getDomesticStockIndexValueByCode('0001'), // 코스피 + this.stockIndexService.getDomesticStockIndexValueByCode('1001'), // 코스닥 + this.stockIndexService.getDomesticStockIndexValueByCode('2001'), // 코스피200 + this.stockIndexService.getDomesticStockIndexValueByCode('3003'), // KSQ150 ]); const stockIndexResponse = new StockIndexResponseDto(); @@ -90,25 +66,13 @@ export class StockIndexController { @Cron('*/5 9-16 * * 1-5') async cronStockIndexLists() { - const accessToken = await this.koreaInvestmentService.getAccessToken(); + await this.koreaInvestmentService.getAccessToken(); const stockLists = await Promise.all([ - this.stockIndexService.getDomesticStockIndexListByCode( - '0001', - accessToken, - ), // 코스피 - this.stockIndexService.getDomesticStockIndexListByCode( - '1001', - accessToken, - ), // 코스닥 - this.stockIndexService.getDomesticStockIndexListByCode( - '2001', - accessToken, - ), // 코스피200 - this.stockIndexService.getDomesticStockIndexListByCode( - '3003', - accessToken, - ), // KSQ150 + this.stockIndexService.getDomesticStockIndexListByCode('0001'), // 코스피 + this.stockIndexService.getDomesticStockIndexListByCode('1001'), // 코스닥 + this.stockIndexService.getDomesticStockIndexListByCode('2001'), // 코스피200 + this.stockIndexService.getDomesticStockIndexListByCode('3003'), // KSQ150 ]); this.socketGateway.sendStockIndexListToClient({ diff --git a/BE/src/stock/index/stock-index.service.ts b/BE/src/stock/index/stock-index.service.ts index fa05ed4c..d3146717 100644 --- a/BE/src/stock/index/stock-index.service.ts +++ b/BE/src/stock/index/stock-index.service.ts @@ -1,95 +1,85 @@ -import { Injectable, InternalServerErrorException } from '@nestjs/common'; -import axios from 'axios'; +import { Injectable, Logger } from '@nestjs/common'; import { StockIndexListChartElementDto } from './dto/stock-index-list-chart.element.dto'; import { StockIndexValueElementDto } from './dto/stock-index-value-element.dto'; import { StockIndexChartInterface, StockIndexValueInterface, } from './interface/stock-index.interface'; -import { getFullURL } from '../../util/get-full-URL'; -import { getHeader } from '../../util/get-header'; +import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.service'; @Injectable() export class StockIndexService { - async getDomesticStockIndexListByCode(code: string, accessToken: string) { - const result = await this.requestDomesticStockIndexListApi( - code, - accessToken, - ); + private readonly logger = new Logger(); - return result.output.map((element) => { - return new StockIndexListChartElementDto( - element.bsop_hour, - element.bstp_nmix_prpr, - element.bstp_nmix_prdy_vrss, - ); - }); - } + constructor( + private readonly koreaInvestmentService: KoreaInvestmentService, + ) {} - async getDomesticStockIndexValueByCode(code: string, accessToken: string) { - const result = await this.requestDomesticStockIndexValueApi( - code, - accessToken, - ); + async getDomesticStockIndexListByCode(code: string) { + try { + const queryParams = { + fid_input_hour_1: '300', + fid_cond_mrkt_div_code: 'U', + fid_input_iscd: code, + }; - const data = result.output; - - return new StockIndexValueElementDto( - data.bstp_nmix_prpr, - data.bstp_nmix_prdy_vrss, - data.bstp_nmix_prdy_ctrt, - data.prdy_vrss_sign, - ); - } - - private async requestDomesticStockIndexListApi( - code: string, - accessToken: string, - ) { - const response = await axios - .get( - getFullURL( + const result = + await this.koreaInvestmentService.requestApi( + 'FHPUP02110200', '/uapi/domestic-stock/v1/quotations/inquire-index-timeprice', - ), - { - headers: getHeader(accessToken, 'FHPUP02110200'), - params: { - fid_input_hour_1: 300, - fid_cond_mrkt_div_code: 'U', - fid_input_iscd: code, - }, - }, - ) - .catch(() => { - throw new InternalServerErrorException( - '주가 지수 차트 정보를 조회하지 못했습니다.', + queryParams, ); - }); - return response.data; + return result.output.map((element) => { + return new StockIndexListChartElementDto( + element.bsop_hour, + element.bstp_nmix_prpr, + element.bstp_nmix_prdy_vrss, + ); + }); + } 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 async requestDomesticStockIndexValueApi( - code: string, - accessToken: string, - ) { - const response = await axios - .get( - getFullURL('/uapi/domestic-stock/v1/quotations/inquire-index-price'), - { - headers: getHeader(accessToken, 'FHPUP02100000'), - params: { - fid_cond_mrkt_div_code: 'U', - fid_input_iscd: code, - }, - }, - ) - .catch(() => { - throw new InternalServerErrorException( - '주가 지수 값 정보를 조회하지 못했습니다.', + async getDomesticStockIndexValueByCode(code: string) { + try { + const queryParams = { + fid_cond_mrkt_div_code: 'U', + fid_input_iscd: code, + }; + + const result = + await this.koreaInvestmentService.requestApi( + 'FHPUP02100000', + '/uapi/domestic-stock/v1/quotations/inquire-index-price', + queryParams, ); - }); - return response.data; + const data = result.output; + + return new StockIndexValueElementDto( + data.bstp_nmix_prpr, + data.bstp_nmix_prdy_vrss, + data.bstp_nmix_prdy_ctrt, + data.prdy_vrss_sign, + ); + } 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; + } } } diff --git a/BE/src/stock/topfive/stock-topfive.service.ts b/BE/src/stock/topfive/stock-topfive.service.ts index 82f659d5..16489241 100644 --- a/BE/src/stock/topfive/stock-topfive.service.ts +++ b/BE/src/stock/topfive/stock-topfive.service.ts @@ -1,4 +1,3 @@ -import axios from 'axios'; import { Injectable, Logger } from '@nestjs/common'; import { StockRankingQueryParameterDto } from './dto/stock-ranking-request.dto'; import { StockRankingResponseDto } from './dto/stock-ranking-response.dto'; @@ -8,8 +7,6 @@ import { StockApiOutputData, StockApiResponse, } from './interface/stock-topfive.interface'; -import { getHeader } from '../../util/get-header'; -import { getFullURL } from '../../util/get-full-URL'; import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.service'; @Injectable() @@ -20,38 +17,6 @@ export class StockTopfiveService { private readonly koreaInvestmentService: KoreaInvestmentService, ) {} - /** - * @private 한국투자 Open API - [국내주식] 순위 분석 - 국내주식 등락률 순위 호출 함수 - * @param {StockRankingQueryParameterDto} queryParams - API 요청 시 필요한 쿼리 파라미터 DTO - * @returns - 국내주식 등락률 데이터 - * - * @author uuuo3o - */ - private async requestApi(queryParams: StockRankingQueryParameterDto) { - try { - const accessToken = await this.koreaInvestmentService.getAccessToken(); - const headers = getHeader(accessToken, 'FHPST01700000'); - const url = getFullURL('/uapi/domestic-stock/v1/ranking/fluctuation'); - const params = this.getStockRankingParams(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; - } - } - /** * 국내주식 등락률 데이터 중 필요한 시장 종류 데이터를 반환하는 함수 * @param {MarketType} marketType - 시장 종류(ALL, KOSPI, KOSDAQ, KOSPI200) @@ -81,15 +46,25 @@ export class StockTopfiveService { break; } - const highResponse = await this.requestApi({ - ...queryParams, - fid_rank_sort_cls_code: '0', - }); + const highResponse = + await this.koreaInvestmentService.requestApi( + 'FHPST01700000', + '/uapi/domestic-stock/v1/ranking/fluctuation', + this.getStockRankingParams({ + ...queryParams, + fid_rank_sort_cls_code: '0', + }), + ); - const lowResponse = await this.requestApi({ - ...queryParams, - fid_rank_sort_cls_code: '1', - }); + const lowResponse = + await this.koreaInvestmentService.requestApi( + 'FHPST01700000', + '/uapi/domestic-stock/v1/ranking/fluctuation', + this.getStockRankingParams({ + ...queryParams, + fid_rank_sort_cls_code: '1', + }), + ); const response = new StockRankingResponseDto(); response.high = this.formatStockData(highResponse.output); 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 449b9451..2b53bd3d 100644 --- a/BE/src/stock/trade/history/stock-trade-history.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history.service.ts @@ -1,8 +1,5 @@ -import axios from 'axios'; 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 { 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'; @@ -32,11 +29,12 @@ export class StockTradeHistoryService { fid_input_iscd: stockCode, }; - const response = await this.requestApi( - 'FHKST01010300', - '/uapi/domestic-stock/v1/quotations/inquire-ccnl', - queryParams, - ); + const response = + await this.koreaInvestmentService.requestApi( + 'FHKST01010300', + '/uapi/domestic-stock/v1/quotations/inquire-ccnl', + queryParams, + ); return this.formatTodayStockTradeHistoryData(response.output); } catch (error) { @@ -89,11 +87,12 @@ export class StockTradeHistoryService { fid_org_adj_prc: '0', }; - const response = await this.requestApi( - 'FHKST01010400', - '/uapi/domestic-stock/v1/quotations/inquire-daily-price', - queryParams, - ); + const response = + await this.koreaInvestmentService.requestApi( + 'FHKST01010400', + '/uapi/domestic-stock/v1/quotations/inquire-daily-price', + queryParams, + ); return this.formatDailyStockTradeHistoryData(response.output); } catch (error) { @@ -132,41 +131,4 @@ export class StockTradeHistoryService { return historyData; }); } - - /** - * @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( - trId: string, - apiURL: string, - params: Record, - ): Promise { - try { - const accessToken = await this.koreaInvestmentService.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 b6ec95cf16abb6d9be30b7938ea5091b663b0b3a Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Mon, 18 Nov 2024 18:30:06 +0900 Subject: [PATCH 23/36] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B8=20=EC=84=9C=EB=B9=84=EC=8A=A4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9=EB=90=98=EB=8A=94=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=EB=A5=BC=20domain-service=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/app.module.ts | 4 ++-- .../interface/korea-investment.interface.ts | 0 .../korea-investment.domain-service.ts} | 6 +++--- .../koreaInvestment/korea-investment.module.ts | 10 ++++++++++ .../websocket/base-socket.domain-service.ts} | 10 ++++++---- .../websocket/interface/socket.interface.ts | 0 .../websocket/socket-token.domain-service.ts} | 4 ++-- .../{ => common}/websocket/socket.gateway.ts | 0 BE/src/common/websocket/socket.module.ts | 10 ++++++++++ .../koreaInvestment/korea-investment.module.ts | 10 ---------- BE/src/stock/detail/stock-detail.module.ts | 2 +- BE/src/stock/detail/stock-detail.service.ts | 8 ++++---- .../stock/index/stock-index-socket.service.ts | 18 +++++++++--------- BE/src/stock/index/stock-index.controller.ts | 10 +++++----- BE/src/stock/index/stock-index.module.ts | 4 ++-- BE/src/stock/index/stock-index.service.ts | 8 ++++---- .../stock/order/stock-order-socket.service.ts | 16 ++++++++-------- BE/src/stock/order/stock-order.module.ts | 2 +- BE/src/stock/topfive/stock-topfive.module.ts | 2 +- BE/src/stock/topfive/stock-topfive.service.ts | 8 ++++---- .../history/stock-trade-history.module.ts | 2 +- .../history/stock-trade-history.service.ts | 8 ++++---- BE/src/websocket/socket.module.ts | 10 ---------- 23 files changed, 77 insertions(+), 75 deletions(-) rename BE/src/{ => common}/koreaInvestment/interface/korea-investment.interface.ts (100%) rename BE/src/{koreaInvestment/korea-investment.service.ts => common/koreaInvestment/korea-investment.domain-service.ts} (93%) create mode 100644 BE/src/common/koreaInvestment/korea-investment.module.ts rename BE/src/{websocket/base-socket.service.ts => common/websocket/base-socket.domain-service.ts} (90%) rename BE/src/{ => common}/websocket/interface/socket.interface.ts (100%) rename BE/src/{websocket/socket-token.service.ts => common/websocket/socket-token.domain-service.ts} (86%) rename BE/src/{ => common}/websocket/socket.gateway.ts (100%) create mode 100644 BE/src/common/websocket/socket.module.ts delete mode 100644 BE/src/koreaInvestment/korea-investment.module.ts delete mode 100644 BE/src/websocket/socket.module.ts diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index 2b2a14d5..39527728 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -8,8 +8,8 @@ import { AppService } from './app.service'; import { AuthModule } from './auth/auth.module'; import { StockIndexModule } from './stock/index/stock-index.module'; import { StockTopfiveModule } from './stock/topfive/stock-topfive.module'; -import { KoreaInvestmentModule } from './koreaInvestment/korea-investment.module'; -import { SocketModule } from './websocket/socket.module'; +import { KoreaInvestmentModule } from './common/koreaInvestment/korea-investment.module'; +import { SocketModule } from './common/websocket/socket.module'; import { StockOrderModule } from './stock/order/stock-order.module'; import { StockDetailModule } from './stock/detail/stock-detail.module'; import { typeOrmConfig } from './configs/typeorm.config'; diff --git a/BE/src/koreaInvestment/interface/korea-investment.interface.ts b/BE/src/common/koreaInvestment/interface/korea-investment.interface.ts similarity index 100% rename from BE/src/koreaInvestment/interface/korea-investment.interface.ts rename to BE/src/common/koreaInvestment/interface/korea-investment.interface.ts diff --git a/BE/src/koreaInvestment/korea-investment.service.ts b/BE/src/common/koreaInvestment/korea-investment.domain-service.ts similarity index 93% rename from BE/src/koreaInvestment/korea-investment.service.ts rename to BE/src/common/koreaInvestment/korea-investment.domain-service.ts index 43eb43e2..3e96c55b 100644 --- a/BE/src/koreaInvestment/korea-investment.service.ts +++ b/BE/src/common/koreaInvestment/korea-investment.domain-service.ts @@ -1,10 +1,10 @@ import axios from 'axios'; import { Logger, UnauthorizedException } from '@nestjs/common'; -import { getFullURL } from '../util/get-full-URL'; +import { getFullURL } from '../../util/get-full-URL'; import { AccessTokenInterface } from './interface/korea-investment.interface'; -import { getHeader } from '../util/get-header'; +import { getHeader } from '../../util/get-header'; -export class KoreaInvestmentService { +export class KoreaInvestmentDomainService { private accessToken: string; private tokenExpireTime: Date; diff --git a/BE/src/common/koreaInvestment/korea-investment.module.ts b/BE/src/common/koreaInvestment/korea-investment.module.ts new file mode 100644 index 00000000..2ffd0889 --- /dev/null +++ b/BE/src/common/koreaInvestment/korea-investment.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { KoreaInvestmentDomainService } from './korea-investment.domain-service'; + +@Module({ + imports: [], + controllers: [], + providers: [KoreaInvestmentDomainService], + exports: [KoreaInvestmentDomainService], +}) +export class KoreaInvestmentModule {} diff --git a/BE/src/websocket/base-socket.service.ts b/BE/src/common/websocket/base-socket.domain-service.ts similarity index 90% rename from BE/src/websocket/base-socket.service.ts rename to BE/src/common/websocket/base-socket.domain-service.ts index 66bc6a1d..3eb70b2c 100644 --- a/BE/src/websocket/base-socket.service.ts +++ b/BE/src/common/websocket/base-socket.domain-service.ts @@ -5,10 +5,10 @@ import { Logger, OnModuleInit, } from '@nestjs/common'; -import { SocketTokenService } from './socket-token.service'; +import { SocketTokenDomainService } from './socket-token.domain-service'; @Injectable() -export class BaseSocketService implements OnModuleInit { +export class BaseSocketDomainService implements OnModuleInit { private socket: WebSocket; private socketConnectionKey: string; private socketOpenHandlers: (() => void | Promise)[] = []; @@ -18,11 +18,13 @@ export class BaseSocketService implements OnModuleInit { private readonly logger = new Logger(); - constructor(private readonly socketTokenService: SocketTokenService) {} + constructor( + private readonly socketTokenDomainService: SocketTokenDomainService, + ) {} async onModuleInit() { this.socketConnectionKey = - await this.socketTokenService.getSocketConnectionKey(); + await this.socketTokenDomainService.getSocketConnectionKey(); this.socket = new WebSocket(process.env.KOREA_INVESTMENT_SOCKET_URL); this.socket.onopen = () => { diff --git a/BE/src/websocket/interface/socket.interface.ts b/BE/src/common/websocket/interface/socket.interface.ts similarity index 100% rename from BE/src/websocket/interface/socket.interface.ts rename to BE/src/common/websocket/interface/socket.interface.ts diff --git a/BE/src/websocket/socket-token.service.ts b/BE/src/common/websocket/socket-token.domain-service.ts similarity index 86% rename from BE/src/websocket/socket-token.service.ts rename to BE/src/common/websocket/socket-token.domain-service.ts index f4ff9e3c..cce5ea03 100644 --- a/BE/src/websocket/socket-token.service.ts +++ b/BE/src/common/websocket/socket-token.domain-service.ts @@ -1,8 +1,8 @@ import axios from 'axios'; import { SocketConnectTokenInterface } from './interface/socket.interface'; -import { getFullURL } from '../util/get-full-URL'; +import { getFullURL } from '../../util/get-full-URL'; -export class SocketTokenService { +export class SocketTokenDomainService { private approvalKey: string; async getSocketConnectionKey() { diff --git a/BE/src/websocket/socket.gateway.ts b/BE/src/common/websocket/socket.gateway.ts similarity index 100% rename from BE/src/websocket/socket.gateway.ts rename to BE/src/common/websocket/socket.gateway.ts diff --git a/BE/src/common/websocket/socket.module.ts b/BE/src/common/websocket/socket.module.ts new file mode 100644 index 00000000..0cc8ceac --- /dev/null +++ b/BE/src/common/websocket/socket.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { SocketGateway } from './socket.gateway'; +import { SocketTokenDomainService } from './socket-token.domain-service'; +import { BaseSocketDomainService } from './base-socket.domain-service'; + +@Module({ + providers: [SocketTokenDomainService, SocketGateway, BaseSocketDomainService], + exports: [SocketGateway, BaseSocketDomainService], +}) +export class SocketModule {} diff --git a/BE/src/koreaInvestment/korea-investment.module.ts b/BE/src/koreaInvestment/korea-investment.module.ts deleted file mode 100644 index 8e7dd10a..00000000 --- a/BE/src/koreaInvestment/korea-investment.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { KoreaInvestmentService } from './korea-investment.service'; - -@Module({ - imports: [], - controllers: [], - providers: [KoreaInvestmentService], - exports: [KoreaInvestmentService], -}) -export class KoreaInvestmentModule {} diff --git a/BE/src/stock/detail/stock-detail.module.ts b/BE/src/stock/detail/stock-detail.module.ts index b6447a76..50f25a5e 100644 --- a/BE/src/stock/detail/stock-detail.module.ts +++ b/BE/src/stock/detail/stock-detail.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { KoreaInvestmentModule } from '../../koreaInvestment/korea-investment.module'; +import { KoreaInvestmentModule } from '../../common/koreaInvestment/korea-investment.module'; import { StockDetailController } from './stock-detail.controller'; import { StockDetailService } from './stock-detail.service'; import { StockDetailRepository } from './stock-detail.repository'; diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index 8ae002ff..389d7147 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.service'; +import { KoreaInvestmentDomainService } from '../../common/koreaInvestment/korea-investment.domain-service'; import { InquirePriceChartApiResponse } from './interface/stock-detail-chart.interface'; import { InquirePriceChartDataDto } from './dto/stock-detail-chart-data.dto'; import { @@ -14,7 +14,7 @@ export class StockDetailService { private readonly logger = new Logger(); constructor( - private readonly koreaInvestmentService: KoreaInvestmentService, + private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, private readonly stockDetailRepository: StockDetailRepository, ) {} @@ -33,7 +33,7 @@ export class StockDetailService { }; const response = - await this.koreaInvestmentService.requestApi( + await this.koreaInvestmentDomainService.requestApi( 'FHKST01010100', '/uapi/domestic-stock/v1/quotations/inquire-price', queryParams, @@ -105,7 +105,7 @@ export class StockDetailService { }; const response = - await this.koreaInvestmentService.requestApi( + await this.koreaInvestmentDomainService.requestApi( 'FHKST03010100', '/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice', queryParams, diff --git a/BE/src/stock/index/stock-index-socket.service.ts b/BE/src/stock/index/stock-index-socket.service.ts index f82ba9a2..05ecb307 100644 --- a/BE/src/stock/index/stock-index-socket.service.ts +++ b/BE/src/stock/index/stock-index-socket.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { StockIndexValueElementDto } from './dto/stock-index-value-element.dto'; -import { BaseSocketService } from '../../websocket/base-socket.service'; -import { SocketGateway } from '../../websocket/socket.gateway'; +import { BaseSocketDomainService } from '../../common/websocket/base-socket.domain-service'; +import { SocketGateway } from '../../common/websocket/socket.gateway'; @Injectable() export class StockIndexSocketService { @@ -15,16 +15,16 @@ export class StockIndexSocketService { constructor( private readonly socketGateway: SocketGateway, - private readonly baseSocketService: BaseSocketService, + private readonly baseSocketDomainService: BaseSocketDomainService, ) { - baseSocketService.registerSocketOpenHandler(() => { - this.baseSocketService.registerCode(this.TR_ID, '0001'); // 코스피 - this.baseSocketService.registerCode(this.TR_ID, '1001'); // 코스닥 - this.baseSocketService.registerCode(this.TR_ID, '2001'); // 코스피200 - this.baseSocketService.registerCode(this.TR_ID, '3003'); // KSQ150 + baseSocketDomainService.registerSocketOpenHandler(() => { + this.baseSocketDomainService.registerCode(this.TR_ID, '0001'); // 코스피 + this.baseSocketDomainService.registerCode(this.TR_ID, '1001'); // 코스닥 + this.baseSocketDomainService.registerCode(this.TR_ID, '2001'); // 코스피200 + this.baseSocketDomainService.registerCode(this.TR_ID, '3003'); // KSQ150 }); - baseSocketService.registerSocketDataHandler( + baseSocketDomainService.registerSocketDataHandler( this.TR_ID, (data: string[]) => { this.socketGateway.sendStockIndexValueToClient( diff --git a/BE/src/stock/index/stock-index.controller.ts b/BE/src/stock/index/stock-index.controller.ts index 9ac6f114..09d5bd40 100644 --- a/BE/src/stock/index/stock-index.controller.ts +++ b/BE/src/stock/index/stock-index.controller.ts @@ -3,15 +3,15 @@ import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { Cron } from '@nestjs/schedule'; import { StockIndexService } from './stock-index.service'; import { StockIndexResponseDto } from './dto/stock-index-response.dto'; -import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.service'; -import { SocketGateway } from '../../websocket/socket.gateway'; +import { KoreaInvestmentDomainService } from '../../common/koreaInvestment/korea-investment.domain-service'; +import { SocketGateway } from '../../common/websocket/socket.gateway'; @Controller('/api/stocks/index') @ApiTags('주가 지수 API') export class StockIndexController { constructor( private readonly stockIndexService: StockIndexService, - private readonly koreaInvestmentService: KoreaInvestmentService, + private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, private readonly socketGateway: SocketGateway, ) {} @@ -26,7 +26,7 @@ export class StockIndexController { type: StockIndexResponseDto, }) async getStockIndex() { - await this.koreaInvestmentService.getAccessToken(); + await this.koreaInvestmentDomainService.getAccessToken(); const [kospiChart, kosdaqChart, kospi200Chart, ksq150Chart] = await Promise.all([ @@ -66,7 +66,7 @@ export class StockIndexController { @Cron('*/5 9-16 * * 1-5') async cronStockIndexLists() { - await this.koreaInvestmentService.getAccessToken(); + await this.koreaInvestmentDomainService.getAccessToken(); const stockLists = await Promise.all([ this.stockIndexService.getDomesticStockIndexListByCode('0001'), // 코스피 diff --git a/BE/src/stock/index/stock-index.module.ts b/BE/src/stock/index/stock-index.module.ts index d763d972..dd3d21f4 100644 --- a/BE/src/stock/index/stock-index.module.ts +++ b/BE/src/stock/index/stock-index.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { StockIndexController } from './stock-index.controller'; import { StockIndexService } from './stock-index.service'; -import { KoreaInvestmentModule } from '../../koreaInvestment/korea-investment.module'; -import { SocketModule } from '../../websocket/socket.module'; +import { KoreaInvestmentModule } from '../../common/koreaInvestment/korea-investment.module'; +import { SocketModule } from '../../common/websocket/socket.module'; import { StockIndexSocketService } from './stock-index-socket.service'; @Module({ diff --git a/BE/src/stock/index/stock-index.service.ts b/BE/src/stock/index/stock-index.service.ts index d3146717..428f6e6e 100644 --- a/BE/src/stock/index/stock-index.service.ts +++ b/BE/src/stock/index/stock-index.service.ts @@ -5,14 +5,14 @@ import { StockIndexChartInterface, StockIndexValueInterface, } from './interface/stock-index.interface'; -import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.service'; +import { KoreaInvestmentDomainService } from '../../common/koreaInvestment/korea-investment.domain-service'; @Injectable() export class StockIndexService { private readonly logger = new Logger(); constructor( - private readonly koreaInvestmentService: KoreaInvestmentService, + private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, ) {} async getDomesticStockIndexListByCode(code: string) { @@ -24,7 +24,7 @@ export class StockIndexService { }; const result = - await this.koreaInvestmentService.requestApi( + await this.koreaInvestmentDomainService.requestApi( 'FHPUP02110200', '/uapi/domestic-stock/v1/quotations/inquire-index-timeprice', queryParams, @@ -57,7 +57,7 @@ export class StockIndexService { }; const result = - await this.koreaInvestmentService.requestApi( + await this.koreaInvestmentDomainService.requestApi( 'FHPUP02100000', '/uapi/domestic-stock/v1/quotations/inquire-index-price', queryParams, diff --git a/BE/src/stock/order/stock-order-socket.service.ts b/BE/src/stock/order/stock-order-socket.service.ts index dada0cfd..60c6afa1 100644 --- a/BE/src/stock/order/stock-order-socket.service.ts +++ b/BE/src/stock/order/stock-order-socket.service.ts @@ -4,8 +4,8 @@ import { Logger, } from '@nestjs/common'; import { LessThanOrEqual, MoreThanOrEqual } from 'typeorm'; -import { BaseSocketService } from '../../websocket/base-socket.service'; -import { SocketGateway } from '../../websocket/socket.gateway'; +import { BaseSocketDomainService } from '../../common/websocket/base-socket.domain-service'; +import { SocketGateway } from '../../common/websocket/socket.gateway'; import { Order } from './stock-order.entity'; import { TradeType } from './enum/trade-type'; import { StatusType } from './enum/status-type'; @@ -19,18 +19,18 @@ export class StockOrderSocketService { constructor( private readonly socketGateway: SocketGateway, - private readonly baseSocketService: BaseSocketService, + private readonly baseSocketDomainService: BaseSocketDomainService, private readonly stockOrderRepository: StockOrderRepository, ) { - baseSocketService.registerSocketOpenHandler(async () => { + baseSocketDomainService.registerSocketOpenHandler(async () => { const orders: Order[] = await this.stockOrderRepository.findAllCodeByStatus(); orders.forEach((order) => { - baseSocketService.registerCode(this.TR_ID, order.stock_code); + baseSocketDomainService.registerCode(this.TR_ID, order.stock_code); }); }); - baseSocketService.registerSocketDataHandler( + baseSocketDomainService.registerSocketDataHandler( this.TR_ID, (data: string[]) => { this.checkExecutableOrder( @@ -44,11 +44,11 @@ export class StockOrderSocketService { } subscribeByCode(trKey: string) { - this.baseSocketService.registerCode(this.TR_ID, trKey); + this.baseSocketDomainService.registerCode(this.TR_ID, trKey); } unsubscribeByCode(trKey: string) { - this.baseSocketService.unregisterCode(this.TR_ID, trKey); + this.baseSocketDomainService.unregisterCode(this.TR_ID, trKey); } private async checkExecutableOrder(stockCode: string, value) { diff --git a/BE/src/stock/order/stock-order.module.ts b/BE/src/stock/order/stock-order.module.ts index 155c0855..dd4a7142 100644 --- a/BE/src/stock/order/stock-order.module.ts +++ b/BE/src/stock/order/stock-order.module.ts @@ -4,7 +4,7 @@ import { StockOrderController } from './stock-order.controller'; import { StockOrderService } from './stock-order.service'; import { Order } from './stock-order.entity'; import { StockOrderRepository } from './stock-order.repository'; -import { SocketModule } from '../../websocket/socket.module'; +import { SocketModule } from '../../common/websocket/socket.module'; import { AssetModule } from '../../asset/asset.module'; import { StockOrderSocketService } from './stock-order-socket.service'; import { UserStockModule } from '../../userStock/user-stock.module'; diff --git a/BE/src/stock/topfive/stock-topfive.module.ts b/BE/src/stock/topfive/stock-topfive.module.ts index 5be62a4f..1b2fb051 100644 --- a/BE/src/stock/topfive/stock-topfive.module.ts +++ b/BE/src/stock/topfive/stock-topfive.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { StockTopfiveController } from './stock-topfive.controller'; import { StockTopfiveService } from './stock-topfive.service'; -import { KoreaInvestmentModule } from '../../koreaInvestment/korea-investment.module'; +import { KoreaInvestmentModule } from '../../common/koreaInvestment/korea-investment.module'; @Module({ imports: [ConfigModule, KoreaInvestmentModule], diff --git a/BE/src/stock/topfive/stock-topfive.service.ts b/BE/src/stock/topfive/stock-topfive.service.ts index 16489241..077ad93f 100644 --- a/BE/src/stock/topfive/stock-topfive.service.ts +++ b/BE/src/stock/topfive/stock-topfive.service.ts @@ -7,14 +7,14 @@ import { StockApiOutputData, StockApiResponse, } from './interface/stock-topfive.interface'; -import { KoreaInvestmentService } from '../../koreaInvestment/korea-investment.service'; +import { KoreaInvestmentDomainService } from '../../common/koreaInvestment/korea-investment.domain-service'; @Injectable() export class StockTopfiveService { private readonly logger = new Logger(); constructor( - private readonly koreaInvestmentService: KoreaInvestmentService, + private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, ) {} /** @@ -47,7 +47,7 @@ export class StockTopfiveService { } const highResponse = - await this.koreaInvestmentService.requestApi( + await this.koreaInvestmentDomainService.requestApi( 'FHPST01700000', '/uapi/domestic-stock/v1/ranking/fluctuation', this.getStockRankingParams({ @@ -57,7 +57,7 @@ export class StockTopfiveService { ); const lowResponse = - await this.koreaInvestmentService.requestApi( + await this.koreaInvestmentDomainService.requestApi( 'FHPST01700000', '/uapi/domestic-stock/v1/ranking/fluctuation', this.getStockRankingParams({ diff --git a/BE/src/stock/trade/history/stock-trade-history.module.ts b/BE/src/stock/trade/history/stock-trade-history.module.ts index 965889a1..12c6c886 100644 --- a/BE/src/stock/trade/history/stock-trade-history.module.ts +++ b/BE/src/stock/trade/history/stock-trade-history.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { KoreaInvestmentModule } from '../../../koreaInvestment/korea-investment.module'; +import { KoreaInvestmentModule } from '../../../common/koreaInvestment/korea-investment.module'; import { StockTradeHistoryController } from './stock-trade-history.controller'; import { StockTradeHistoryService } from './stock-trade-history.service'; 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 2b53bd3d..97548ff4 100644 --- a/BE/src/stock/trade/history/stock-trade-history.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { KoreaInvestmentService } from '../../../koreaInvestment/korea-investment.service'; +import { KoreaInvestmentDomainService } from '../../../common/koreaInvestment/korea-investment.domain-service'; 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'; @@ -12,7 +12,7 @@ export class StockTradeHistoryService { private readonly logger = new Logger(); constructor( - private readonly koreaInvestmentService: KoreaInvestmentService, + private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, ) {} /** @@ -30,7 +30,7 @@ export class StockTradeHistoryService { }; const response = - await this.koreaInvestmentService.requestApi( + await this.koreaInvestmentDomainService.requestApi( 'FHKST01010300', '/uapi/domestic-stock/v1/quotations/inquire-ccnl', queryParams, @@ -88,7 +88,7 @@ export class StockTradeHistoryService { }; const response = - await this.koreaInvestmentService.requestApi( + await this.koreaInvestmentDomainService.requestApi( 'FHKST01010400', '/uapi/domestic-stock/v1/quotations/inquire-daily-price', queryParams, diff --git a/BE/src/websocket/socket.module.ts b/BE/src/websocket/socket.module.ts deleted file mode 100644 index e7a2dcc7..00000000 --- a/BE/src/websocket/socket.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { SocketGateway } from './socket.gateway'; -import { SocketTokenService } from './socket-token.service'; -import { BaseSocketService } from './base-socket.service'; - -@Module({ - providers: [SocketTokenService, SocketGateway, BaseSocketService], - exports: [SocketGateway, BaseSocketService], -}) -export class SocketModule {} From 52152b927d13abf880bd7a64fa87cafba49ba83d Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Mon, 18 Nov 2024 18:39:26 +0900 Subject: [PATCH 24/36] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20stock=20?= =?UTF-8?q?index=20controller=20=EB=A1=9C=EC=A7=81=20=EC=A4=84=EC=9D=B4?= =?UTF-8?q?=EA=B3=A0=20service=EB=A1=9C=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/index/stock-index.controller.ts | 63 ++----------------- BE/src/stock/index/stock-index.service.ts | 64 +++++++++++++++++++- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/BE/src/stock/index/stock-index.controller.ts b/BE/src/stock/index/stock-index.controller.ts index 09d5bd40..0de30ccc 100644 --- a/BE/src/stock/index/stock-index.controller.ts +++ b/BE/src/stock/index/stock-index.controller.ts @@ -3,17 +3,11 @@ import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { Cron } from '@nestjs/schedule'; import { StockIndexService } from './stock-index.service'; import { StockIndexResponseDto } from './dto/stock-index-response.dto'; -import { KoreaInvestmentDomainService } from '../../common/koreaInvestment/korea-investment.domain-service'; -import { SocketGateway } from '../../common/websocket/socket.gateway'; @Controller('/api/stocks/index') @ApiTags('주가 지수 API') export class StockIndexController { - constructor( - private readonly stockIndexService: StockIndexService, - private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, - private readonly socketGateway: SocketGateway, - ) {} + constructor(private readonly stockIndexService: StockIndexService) {} @Get() @ApiOperation({ @@ -25,61 +19,12 @@ export class StockIndexController { description: '주가 지수 조회 성공', type: StockIndexResponseDto, }) - async getStockIndex() { - await this.koreaInvestmentDomainService.getAccessToken(); - - const [kospiChart, kosdaqChart, kospi200Chart, ksq150Chart] = - await Promise.all([ - this.stockIndexService.getDomesticStockIndexListByCode('0001'), // 코스피 - this.stockIndexService.getDomesticStockIndexListByCode('1001'), // 코스닥 - this.stockIndexService.getDomesticStockIndexListByCode('2001'), // 코스피200 - this.stockIndexService.getDomesticStockIndexListByCode('3003'), // KSQ150 - ]); - - const [kospiValue, kosdaqValue, kospi200Value, ksq150Value] = - await Promise.all([ - this.stockIndexService.getDomesticStockIndexValueByCode('0001'), // 코스피 - this.stockIndexService.getDomesticStockIndexValueByCode('1001'), // 코스닥 - this.stockIndexService.getDomesticStockIndexValueByCode('2001'), // 코스피200 - this.stockIndexService.getDomesticStockIndexValueByCode('3003'), // KSQ150 - ]); - - const stockIndexResponse = new StockIndexResponseDto(); - stockIndexResponse.KOSPI = { - value: kospiValue, - chart: kospiChart, - }; - stockIndexResponse.KOSDAQ = { - value: kosdaqValue, - chart: kosdaqChart, - }; - stockIndexResponse.KOSPI200 = { - value: kospi200Value, - chart: kospi200Chart, - }; - stockIndexResponse.KSQ150 = { - value: ksq150Value, - chart: ksq150Chart, - }; - return stockIndexResponse; + getStockIndex() { + return this.stockIndexService.getDomesticStockIndexList(); } @Cron('*/5 9-16 * * 1-5') async cronStockIndexLists() { - await this.koreaInvestmentDomainService.getAccessToken(); - - const stockLists = await Promise.all([ - this.stockIndexService.getDomesticStockIndexListByCode('0001'), // 코스피 - this.stockIndexService.getDomesticStockIndexListByCode('1001'), // 코스닥 - this.stockIndexService.getDomesticStockIndexListByCode('2001'), // 코스피200 - this.stockIndexService.getDomesticStockIndexListByCode('3003'), // KSQ150 - ]); - - this.socketGateway.sendStockIndexListToClient({ - KOSPI: stockLists[0], - KOSDAQ: stockLists[1], - KOSPI200: stockLists[2], - KSQ150: stockLists[3], - }); + await this.stockIndexService.cronDomesticStockIndexList(); } } diff --git a/BE/src/stock/index/stock-index.service.ts b/BE/src/stock/index/stock-index.service.ts index 428f6e6e..cdafe9e8 100644 --- a/BE/src/stock/index/stock-index.service.ts +++ b/BE/src/stock/index/stock-index.service.ts @@ -6,6 +6,8 @@ import { StockIndexValueInterface, } from './interface/stock-index.interface'; import { KoreaInvestmentDomainService } from '../../common/koreaInvestment/korea-investment.domain-service'; +import { StockIndexResponseDto } from './dto/stock-index-response.dto'; +import { SocketGateway } from '../../common/websocket/socket.gateway'; @Injectable() export class StockIndexService { @@ -13,9 +15,67 @@ export class StockIndexService { constructor( private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, + private readonly socketGateway: SocketGateway, ) {} - async getDomesticStockIndexListByCode(code: string) { + async getDomesticStockIndexList() { + await this.koreaInvestmentDomainService.getAccessToken(); + + const [kospiChart, kosdaqChart, kospi200Chart, ksq150Chart] = + await Promise.all([ + this.getDomesticStockIndexListByCode('0001'), // 코스피 + this.getDomesticStockIndexListByCode('1001'), // 코스닥 + this.getDomesticStockIndexListByCode('2001'), // 코스피200 + this.getDomesticStockIndexListByCode('3003'), // KSQ150 + ]); + + const [kospiValue, kosdaqValue, kospi200Value, ksq150Value] = + await Promise.all([ + this.getDomesticStockIndexValueByCode('0001'), // 코스피 + this.getDomesticStockIndexValueByCode('1001'), // 코스닥 + this.getDomesticStockIndexValueByCode('2001'), // 코스피200 + this.getDomesticStockIndexValueByCode('3003'), // KSQ150 + ]); + + const stockIndexResponse = new StockIndexResponseDto(); + stockIndexResponse.KOSPI = { + value: kospiValue, + chart: kospiChart, + }; + stockIndexResponse.KOSDAQ = { + value: kosdaqValue, + chart: kosdaqChart, + }; + stockIndexResponse.KOSPI200 = { + value: kospi200Value, + chart: kospi200Chart, + }; + stockIndexResponse.KSQ150 = { + value: ksq150Value, + chart: ksq150Chart, + }; + return stockIndexResponse; + } + + async cronDomesticStockIndexList() { + await this.koreaInvestmentDomainService.getAccessToken(); + + const stockLists = await Promise.all([ + this.getDomesticStockIndexListByCode('0001'), // 코스피 + this.getDomesticStockIndexListByCode('1001'), // 코스닥 + this.getDomesticStockIndexListByCode('2001'), // 코스피200 + this.getDomesticStockIndexListByCode('3003'), // KSQ150 + ]); + + this.socketGateway.sendStockIndexListToClient({ + KOSPI: stockLists[0], + KOSDAQ: stockLists[1], + KOSPI200: stockLists[2], + KSQ150: stockLists[3], + }); + } + + private async getDomesticStockIndexListByCode(code: string) { try { const queryParams = { fid_input_hour_1: '300', @@ -49,7 +109,7 @@ export class StockIndexService { } } - async getDomesticStockIndexValueByCode(code: string) { + private async getDomesticStockIndexValueByCode(code: string) { try { const queryParams = { fid_cond_mrkt_div_code: 'U', From 3e85e3db57b4c8eb169a2f5382549c48acc1e763 Mon Sep 17 00:00:00 2001 From: JIN Date: Mon, 18 Nov 2024 20:29:07 +0900 Subject: [PATCH 25/36] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EC=8B=9D=20?= =?UTF-8?q?=EC=A2=85=EB=AA=A9=EC=BD=94=EB=93=9C=EB=8F=84=20=ED=95=A8?= =?UTF-8?q?=EA=BB=98=20=EB=B0=98=ED=99=98=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/topfive/dto/stock-ranking-data.dto.ts | 3 +++ BE/src/stock/topfive/stock-topfive.service.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/BE/src/stock/topfive/dto/stock-ranking-data.dto.ts b/BE/src/stock/topfive/dto/stock-ranking-data.dto.ts index 5f1a3026..268283e7 100644 --- a/BE/src/stock/topfive/dto/stock-ranking-data.dto.ts +++ b/BE/src/stock/topfive/dto/stock-ranking-data.dto.ts @@ -4,6 +4,9 @@ import { ApiProperty } from '@nestjs/swagger'; * 등락률 API 요청 후 받은 응답값 정제용 DTO */ export class StockRankingDataDto { + @ApiProperty({ description: '주식 종목 코드' }) + stck_shrn_iscd: string; + @ApiProperty({ description: 'HTS 한글 종목명' }) hts_kor_isnm: string; diff --git a/BE/src/stock/topfive/stock-topfive.service.ts b/BE/src/stock/topfive/stock-topfive.service.ts index 82f659d5..5058752a 100644 --- a/BE/src/stock/topfive/stock-topfive.service.ts +++ b/BE/src/stock/topfive/stock-topfive.service.ts @@ -118,6 +118,7 @@ export class StockTopfiveService { private formatStockData(stocks: StockApiOutputData[]) { return stocks.slice(0, 5).map((stock) => { const stockData = new StockRankingDataDto(); + stockData.stck_shrn_iscd = stock.stck_shrn_iscd; stockData.hts_kor_isnm = stock.hts_kor_isnm; stockData.stck_prpr = stock.stck_prpr; stockData.prdy_vrss = stock.prdy_vrss; From dab1f7b0c5339a21e53ece8a987b4b495df07e57 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Tue, 19 Nov 2024 11:23:38 +0900 Subject: [PATCH 26/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20websocket=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stock/trade/history/stock-trade-history-socket.service.ts | 2 +- BE/src/stock/trade/history/stock-trade-history.module.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts index 4489d613..dab0aea2 100644 --- a/BE/src/stock/trade/history/stock-trade-history-socket.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history-socket.service.ts @@ -3,7 +3,7 @@ import axios from 'axios'; import { Observable, Subject } from 'rxjs'; import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { SseEvent } from './interface/sse-event'; -import { SocketConnectTokenInterface } from '../../../websocket/interface/socket.interface'; +import { SocketConnectTokenInterface } from '../../../common/websocket/interface/socket.interface'; import { getFullTestURL } from '../../../util/get-full-URL'; import { TodayStockTradeHistoryDataDto } from './dto/today-stock-trade-history-data.dto'; diff --git a/BE/src/stock/trade/history/stock-trade-history.module.ts b/BE/src/stock/trade/history/stock-trade-history.module.ts index 3d462105..ca5ce277 100644 --- a/BE/src/stock/trade/history/stock-trade-history.module.ts +++ b/BE/src/stock/trade/history/stock-trade-history.module.ts @@ -3,7 +3,7 @@ import { KoreaInvestmentModule } from '../../../common/koreaInvestment/korea-inv import { StockTradeHistoryController } from './stock-trade-history.controller'; import { StockTradeHistoryService } from './stock-trade-history.service'; import { StockTradeHistorySocketService } from './stock-trade-history-socket.service'; -import { SocketModule } from '../../../websocket/socket.module'; +import { SocketModule } from '../../../common/websocket/socket.module'; @Module({ imports: [KoreaInvestmentModule, SocketModule], From f7b96730a0dcbd652087c767aeec480e0c8c05a5 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Tue, 19 Nov 2024 11:35:03 +0900 Subject: [PATCH 27/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20try-catch=EB=AC=B8=20=EB=B0=8F=20logger=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../korea-investment.domain-service.ts | 31 ++---- BE/src/stock/detail/stock-detail.service.ts | 75 +++++---------- BE/src/stock/index/stock-index.service.ts | 96 +++++++------------ BE/src/stock/topfive/stock-topfive.service.ts | 91 ++++++++---------- .../history/stock-trade-history.service.ts | 74 +++++--------- 5 files changed, 136 insertions(+), 231 deletions(-) diff --git a/BE/src/common/koreaInvestment/korea-investment.domain-service.ts b/BE/src/common/koreaInvestment/korea-investment.domain-service.ts index 3e96c55b..26e890d3 100644 --- a/BE/src/common/koreaInvestment/korea-investment.domain-service.ts +++ b/BE/src/common/koreaInvestment/korea-investment.domain-service.ts @@ -1,5 +1,5 @@ import axios from 'axios'; -import { Logger, UnauthorizedException } from '@nestjs/common'; +import { UnauthorizedException } from '@nestjs/common'; import { getFullURL } from '../../util/get-full-URL'; import { AccessTokenInterface } from './interface/korea-investment.interface'; import { getHeader } from '../../util/get-header'; @@ -8,8 +8,6 @@ export class KoreaInvestmentDomainService { private accessToken: string; private tokenExpireTime: Date; - private readonly logger = new Logger(); - async getAccessToken() { // accessToken이 유효한 경우 if (this.accessToken && this.tokenExpireTime > new Date()) { @@ -47,26 +45,15 @@ export class KoreaInvestmentDomainService { apiURL: string, params: Record, ): Promise { - try { - const accessToken = await this.getAccessToken(); - const headers = getHeader(accessToken, trId); - const url = getFullURL(apiURL); + const accessToken = await this.getAccessToken(); + const headers = getHeader(accessToken, trId); + const url = getFullURL(apiURL); - const response = await axios.get(url, { - headers, - params, - }); + 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; - } + return response.data; } } diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index 389d7147..2735113e 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -26,30 +26,18 @@ export class StockDetailService { * @author uuuo3o */ async getInquirePrice(stockCode: string) { - try { - const queryParams = { - fid_cond_mrkt_div_code: 'J', - fid_input_iscd: stockCode, - }; - - const response = - await this.koreaInvestmentDomainService.requestApi( - 'FHKST01010100', - '/uapi/domestic-stock/v1/quotations/inquire-price', - queryParams, - ); + const queryParams = { + fid_cond_mrkt_div_code: 'J', + fid_input_iscd: stockCode, + }; - return await 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; - } + const response = + await this.koreaInvestmentDomainService.requestApi( + 'FHKST01010100', + '/uapi/domestic-stock/v1/quotations/inquire-price', + queryParams, + ); + return this.formatStockData(response.output); } /** @@ -94,34 +82,23 @@ export class StockDetailService { date2: string, periodDivCode: string, ) { - try { - 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 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.koreaInvestmentDomainService.requestApi( - 'FHKST03010100', - '/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice', - queryParams, - ); + const response = + await this.koreaInvestmentDomainService.requestApi( + 'FHKST03010100', + '/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice', + queryParams, + ); - return this.formatStockInquirePriceData(response).slice().reverse(); - } 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; - } + return this.formatStockInquirePriceData(response).slice().reverse(); } /** diff --git a/BE/src/stock/index/stock-index.service.ts b/BE/src/stock/index/stock-index.service.ts index cdafe9e8..f68aa9d1 100644 --- a/BE/src/stock/index/stock-index.service.ts +++ b/BE/src/stock/index/stock-index.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { StockIndexListChartElementDto } from './dto/stock-index-list-chart.element.dto'; import { StockIndexValueElementDto } from './dto/stock-index-value-element.dto'; import { @@ -11,8 +11,6 @@ import { SocketGateway } from '../../common/websocket/socket.gateway'; @Injectable() export class StockIndexService { - private readonly logger = new Logger(); - constructor( private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, private readonly socketGateway: SocketGateway, @@ -76,70 +74,48 @@ export class StockIndexService { } private async getDomesticStockIndexListByCode(code: string) { - try { - const queryParams = { - fid_input_hour_1: '300', - fid_cond_mrkt_div_code: 'U', - fid_input_iscd: code, - }; + const queryParams = { + fid_input_hour_1: '300', + fid_cond_mrkt_div_code: 'U', + fid_input_iscd: code, + }; - const result = - await this.koreaInvestmentDomainService.requestApi( - 'FHPUP02110200', - '/uapi/domestic-stock/v1/quotations/inquire-index-timeprice', - queryParams, - ); + const result = + await this.koreaInvestmentDomainService.requestApi( + 'FHPUP02110200', + '/uapi/domestic-stock/v1/quotations/inquire-index-timeprice', + queryParams, + ); - return result.output.map((element) => { - return new StockIndexListChartElementDto( - element.bsop_hour, - element.bstp_nmix_prpr, - element.bstp_nmix_prdy_vrss, - ); - }); - } 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; - } + return result.output.map((element) => { + return new StockIndexListChartElementDto( + element.bsop_hour, + element.bstp_nmix_prpr, + element.bstp_nmix_prdy_vrss, + ); + }); } private async getDomesticStockIndexValueByCode(code: string) { - try { - const queryParams = { - fid_cond_mrkt_div_code: 'U', - fid_input_iscd: code, - }; + const queryParams = { + fid_cond_mrkt_div_code: 'U', + fid_input_iscd: code, + }; - const result = - await this.koreaInvestmentDomainService.requestApi( - 'FHPUP02100000', - '/uapi/domestic-stock/v1/quotations/inquire-index-price', - queryParams, - ); + const result = + await this.koreaInvestmentDomainService.requestApi( + 'FHPUP02100000', + '/uapi/domestic-stock/v1/quotations/inquire-index-price', + queryParams, + ); - const data = result.output; + const data = result.output; - return new StockIndexValueElementDto( - data.bstp_nmix_prpr, - data.bstp_nmix_prdy_vrss, - data.bstp_nmix_prdy_ctrt, - data.prdy_vrss_sign, - ); - } 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; - } + return new StockIndexValueElementDto( + data.bstp_nmix_prpr, + data.bstp_nmix_prdy_vrss, + data.bstp_nmix_prdy_ctrt, + data.prdy_vrss_sign, + ); } } diff --git a/BE/src/stock/topfive/stock-topfive.service.ts b/BE/src/stock/topfive/stock-topfive.service.ts index 28b8e1ff..2feeef86 100644 --- a/BE/src/stock/topfive/stock-topfive.service.ts +++ b/BE/src/stock/topfive/stock-topfive.service.ts @@ -25,62 +25,51 @@ export class StockTopfiveService { * @author uuuo3o */ async getMarketRanking(marketType: MarketType) { - try { - const queryParams = new StockRankingQueryParameterDto(); - queryParams.fid_cond_mrkt_div_code = 'J'; + const queryParams = new StockRankingQueryParameterDto(); + queryParams.fid_cond_mrkt_div_code = 'J'; - switch (marketType) { - case MarketType.ALL: - queryParams.fid_input_iscd = '0000'; - break; - case MarketType.KOSPI: - queryParams.fid_input_iscd = '0001'; - break; - case MarketType.KOSDAQ: - queryParams.fid_input_iscd = '1001'; - break; - case MarketType.KOSPI200: - queryParams.fid_input_iscd = '2001'; - break; - default: - break; - } + switch (marketType) { + case MarketType.ALL: + queryParams.fid_input_iscd = '0000'; + break; + case MarketType.KOSPI: + queryParams.fid_input_iscd = '0001'; + break; + case MarketType.KOSDAQ: + queryParams.fid_input_iscd = '1001'; + break; + case MarketType.KOSPI200: + queryParams.fid_input_iscd = '2001'; + break; + default: + break; + } - const highResponse = - await this.koreaInvestmentDomainService.requestApi( - 'FHPST01700000', - '/uapi/domestic-stock/v1/ranking/fluctuation', - this.getStockRankingParams({ - ...queryParams, - fid_rank_sort_cls_code: '0', - }), - ); + const highResponse = + await this.koreaInvestmentDomainService.requestApi( + 'FHPST01700000', + '/uapi/domestic-stock/v1/ranking/fluctuation', + this.getStockRankingParams({ + ...queryParams, + fid_rank_sort_cls_code: '0', + }), + ); - const lowResponse = - await this.koreaInvestmentDomainService.requestApi( - 'FHPST01700000', - '/uapi/domestic-stock/v1/ranking/fluctuation', - this.getStockRankingParams({ - ...queryParams, - fid_rank_sort_cls_code: '1', - }), - ); + const lowResponse = + await this.koreaInvestmentDomainService.requestApi( + 'FHPST01700000', + '/uapi/domestic-stock/v1/ranking/fluctuation', + this.getStockRankingParams({ + ...queryParams, + fid_rank_sort_cls_code: '1', + }), + ); - const response = new StockRankingResponseDto(); - response.high = this.formatStockData(highResponse.output); - response.low = this.formatStockData(lowResponse.output); + const response = new StockRankingResponseDto(); + response.high = this.formatStockData(highResponse.output); + response.low = this.formatStockData(lowResponse.output); - return response; - } 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; - } + return response; } /** 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 97548ff4..564ba4ca 100644 --- a/BE/src/stock/trade/history/stock-trade-history.service.ts +++ b/BE/src/stock/trade/history/stock-trade-history.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { KoreaInvestmentDomainService } from '../../../common/koreaInvestment/korea-investment.domain-service'; import { InquireCCNLApiResponse } from './interface/Inquire-ccnl.interface'; import { TodayStockTradeHistoryOutputDto } from './dto/today-stock-trade-history-output.dto'; @@ -9,8 +9,6 @@ import { DailyStockTradeHistoryDataDto } from './dto/daily-stock-trade-history-d @Injectable() export class StockTradeHistoryService { - private readonly logger = new Logger(); - constructor( private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, ) {} @@ -23,30 +21,19 @@ export class StockTradeHistoryService { * @author uuuo3o */ async getTodayStockTradeHistory(stockCode: string) { - try { - const queryParams = { - fid_cond_mrkt_div_code: 'J', - fid_input_iscd: stockCode, - }; + const queryParams = { + fid_cond_mrkt_div_code: 'J', + fid_input_iscd: stockCode, + }; - const response = - await this.koreaInvestmentDomainService.requestApi( - 'FHKST01010300', - '/uapi/domestic-stock/v1/quotations/inquire-ccnl', - queryParams, - ); + const response = + await this.koreaInvestmentDomainService.requestApi( + 'FHKST01010300', + '/uapi/domestic-stock/v1/quotations/inquire-ccnl', + queryParams, + ); - return this.formatTodayStockTradeHistoryData(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; - } + return this.formatTodayStockTradeHistoryData(response.output); } /** @@ -79,32 +66,21 @@ export class StockTradeHistoryService { * @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 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.koreaInvestmentDomainService.requestApi( - 'FHKST01010400', - '/uapi/domestic-stock/v1/quotations/inquire-daily-price', - queryParams, - ); + const response = + await this.koreaInvestmentDomainService.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; - } + return this.formatDailyStockTradeHistoryData(response.output); } /** From c07c5bc5b23ea0cbd8b35e42aa5393a54d0d6699 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Tue, 19 Nov 2024 11:37:34 +0900 Subject: [PATCH 28/36] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20logger=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/detail/stock-detail.service.ts | 4 +--- BE/src/stock/topfive/stock-topfive.service.ts | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index 2735113e..4de8f87f 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { KoreaInvestmentDomainService } from '../../common/koreaInvestment/korea-investment.domain-service'; import { InquirePriceChartApiResponse } from './interface/stock-detail-chart.interface'; import { InquirePriceChartDataDto } from './dto/stock-detail-chart-data.dto'; @@ -11,8 +11,6 @@ import { StockDetailRepository } from './stock-detail.repository'; @Injectable() export class StockDetailService { - private readonly logger = new Logger(); - constructor( private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, private readonly stockDetailRepository: StockDetailRepository, diff --git a/BE/src/stock/topfive/stock-topfive.service.ts b/BE/src/stock/topfive/stock-topfive.service.ts index 2feeef86..15052081 100644 --- a/BE/src/stock/topfive/stock-topfive.service.ts +++ b/BE/src/stock/topfive/stock-topfive.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { StockRankingQueryParameterDto } from './dto/stock-ranking-request.dto'; import { StockRankingResponseDto } from './dto/stock-ranking-response.dto'; import { StockRankingDataDto } from './dto/stock-ranking-data.dto'; @@ -11,8 +11,6 @@ import { KoreaInvestmentDomainService } from '../../common/koreaInvestment/korea @Injectable() export class StockTopfiveService { - private readonly logger = new Logger(); - constructor( private readonly koreaInvestmentDomainService: KoreaInvestmentDomainService, ) {} From 8d60390153e3cd3cc7b838c8a8a5a4b29d33bb31 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Tue, 19 Nov 2024 13:04:21 +0900 Subject: [PATCH 29/36] =?UTF-8?q?=F0=9F=94=A7=20fix:=20=EB=A7=A4=EB=8F=84/?= =?UTF-8?q?=EB=A7=A4=EC=88=98=20=EC=8B=9C=20=EC=A2=85=EB=AA=A9=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20isNotEmpty=20=ED=99=95=EC=9D=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/dto/stock-order-request.dto.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BE/src/stock/order/dto/stock-order-request.dto.ts b/BE/src/stock/order/dto/stock-order-request.dto.ts index 407d05ad..458f54af 100644 --- a/BE/src/stock/order/dto/stock-order-request.dto.ts +++ b/BE/src/stock/order/dto/stock-order-request.dto.ts @@ -1,8 +1,9 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsInt, IsNumber, IsPositive } from 'class-validator'; +import { IsInt, IsNotEmpty, IsNumber, IsPositive } from 'class-validator'; export class StockOrderRequestDto { @ApiProperty({ description: '주식 id', example: '005930' }) + @IsNotEmpty() stock_code: string; @ApiProperty({ description: '매수/매도 희망 가격' }) From 49edc8fe8460e725006501b6ae813e86fcd68ecf Mon Sep 17 00:00:00 2001 From: JIN Date: Tue, 19 Nov 2024 13:36:44 +0900 Subject: [PATCH 30/36] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EC=8B=9D=20?= =?UTF-8?q?=EC=A3=BC=EB=AC=B8=EC=97=90=20=EC=82=AC=EC=9A=A9=ED=95=A0=20?= =?UTF-8?q?=EC=83=81=ED=95=9C=EA=B0=80,=20=ED=95=98=ED=95=9C=EA=B0=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80#144?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/detail/dto/stock-detail-response.dto.ts | 6 ++++++ BE/src/stock/detail/stock-detail.service.ts | 2 ++ 2 files changed, 8 insertions(+) diff --git a/BE/src/stock/detail/dto/stock-detail-response.dto.ts b/BE/src/stock/detail/dto/stock-detail-response.dto.ts index 96e4ba52..6c7d1aa6 100644 --- a/BE/src/stock/detail/dto/stock-detail-response.dto.ts +++ b/BE/src/stock/detail/dto/stock-detail-response.dto.ts @@ -24,4 +24,10 @@ export class InquirePriceResponseDto { @ApiProperty({ description: 'PER' }) per: string; + + @ApiProperty({ description: '주식 상한가' }) + stck_mxpr: string; + + @ApiProperty({ description: '주식 하한가' }) + stck_llam: string; } diff --git a/BE/src/stock/detail/stock-detail.service.ts b/BE/src/stock/detail/stock-detail.service.ts index 4de8f87f..b3e537ed 100644 --- a/BE/src/stock/detail/stock-detail.service.ts +++ b/BE/src/stock/detail/stock-detail.service.ts @@ -61,6 +61,8 @@ export class StockDetailService { prdy_ctrt: stock.prdy_ctrt, hts_avls: stock.hts_avls, per: stock.per, + stck_mxpr: stock.stck_mxpr, + stck_llam: stock.stck_llam, }; } From 22dd87d5450aa42b5d920e50fa8d8bb9a7f482d3 Mon Sep 17 00:00:00 2001 From: jinddings Date: Tue, 19 Nov 2024 15:21:38 +0900 Subject: [PATCH 31/36] =?UTF-8?q?=F0=9F=94=A7=20fix=20:=20cookie=EB=A5=BC?= =?UTF-8?q?=20=EC=9D=B4=EC=9A=A9=ED=95=9C=20accessToken=20validate(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/auth/auth.controller.ts | 2 +- BE/src/auth/strategy/jwt.strategy.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/BE/src/auth/auth.controller.ts b/BE/src/auth/auth.controller.ts index 8fcdf300..02c5198c 100644 --- a/BE/src/auth/auth.controller.ts +++ b/BE/src/auth/auth.controller.ts @@ -47,7 +47,7 @@ export class AuthController { @Get('/test') @UseGuards(AuthGuard('jwt')) test(@Req() req: Request) { - return req; + return 'test'; } @ApiOperation({ summary: 'Kakao 로그인 API' }) diff --git a/BE/src/auth/strategy/jwt.strategy.ts b/BE/src/auth/strategy/jwt.strategy.ts index 7f8f5d2a..78063d1f 100644 --- a/BE/src/auth/strategy/jwt.strategy.ts +++ b/BE/src/auth/strategy/jwt.strategy.ts @@ -5,6 +5,7 @@ import { Injectable, UnauthorizedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { UserRepository } from '../user.repository'; import { User } from '../user.entity'; +import { Request } from 'express'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { @@ -14,7 +15,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) { ) { super({ secretOrKey: configService.get('JWT_SECRET'), - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + jwtFromRequest: extractJWTFromCookie, }); } @@ -31,3 +32,10 @@ export class JwtStrategy extends PassportStrategy(Strategy) { }; } } + +function extractJWTFromCookie(req: Request): string | null { + if (req.cookies && 'accessToken' in req.cookies) { + return req.cookies['accessToken']; + } + return null; +} From d4d977b29cfb0d2bf27708e466994b58891eef4c Mon Sep 17 00:00:00 2001 From: jinddings Date: Tue, 19 Nov 2024 15:29:48 +0900 Subject: [PATCH 32/36] =?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?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/auth/auth.controller.ts | 7 ------ BE/src/auth/strategy/jwt.strategy.ts | 32 +++++++++++++++++++--------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/BE/src/auth/auth.controller.ts b/BE/src/auth/auth.controller.ts index 02c5198c..22e29cdb 100644 --- a/BE/src/auth/auth.controller.ts +++ b/BE/src/auth/auth.controller.ts @@ -43,13 +43,6 @@ export class AuthController { return res.status(200).json({ accessToken }); } - @ApiOperation({ summary: 'Token 인증 테스트 API' }) - @Get('/test') - @UseGuards(AuthGuard('jwt')) - test(@Req() req: Request) { - return 'test'; - } - @ApiOperation({ summary: 'Kakao 로그인 API' }) @Get('/kakao') @UseGuards(AuthGuard('kakao')) diff --git a/BE/src/auth/strategy/jwt.strategy.ts b/BE/src/auth/strategy/jwt.strategy.ts index 78063d1f..049e2cf2 100644 --- a/BE/src/auth/strategy/jwt.strategy.ts +++ b/BE/src/auth/strategy/jwt.strategy.ts @@ -1,11 +1,25 @@ import { PassportStrategy } from '@nestjs/passport'; import { InjectRepository } from '@nestjs/typeorm'; -import { ExtractJwt, Strategy } from 'passport-jwt'; +import { Strategy } from 'passport-jwt'; import { Injectable, UnauthorizedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; +import { Request } from 'express'; import { UserRepository } from '../user.repository'; import { User } from '../user.entity'; -import { Request } from 'express'; + +interface RequestWithCookies extends Request { + cookies: { + accessToken?: string; + [key: string]: string | undefined; + }; +} + +function extractJWTFromCookie(req: RequestWithCookies): string | null { + if (req.cookies && 'accessToken' in req.cookies) { + return req.cookies.accessToken; + } + return null; +} @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { @@ -19,7 +33,12 @@ export class JwtStrategy extends PassportStrategy(Strategy) { }); } - async validate(payload) { + async validate(payload: { email: string }): Promise<{ + userId: number; + email: string; + tutorial: boolean; + kakaoId: string | null; + }> { const { email } = payload; const user: User = await this.userRepository.findOne({ where: { email } }); if (!user) throw new UnauthorizedException(); @@ -32,10 +51,3 @@ export class JwtStrategy extends PassportStrategy(Strategy) { }; } } - -function extractJWTFromCookie(req: Request): string | null { - if (req.cookies && 'accessToken' in req.cookies) { - return req.cookies['accessToken']; - } - return null; -} From 328b1639fcc9ba87dc75763ec6a5dc853c8b4a0f Mon Sep 17 00:00:00 2001 From: jinddings Date: Tue, 19 Nov 2024 15:40:28 +0900 Subject: [PATCH 33/36] =?UTF-8?q?=F0=9F=94=A7=20fix=20:=20docker=20timezon?= =?UTF-8?q?e=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/Dockerfile | 1 + FE/Dockerfile | 2 ++ 2 files changed, 3 insertions(+) diff --git a/BE/Dockerfile b/BE/Dockerfile index 60079503..afd130d2 100644 --- a/BE/Dockerfile +++ b/BE/Dockerfile @@ -12,6 +12,7 @@ WORKDIR /var/app COPY package*.json ./ RUN npm install --only=production COPY --from=builder /app/dist ./dist +RUN sudo ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime EXPOSE 3000 CMD ["node", "dist/main.js"] \ No newline at end of file diff --git a/FE/Dockerfile b/FE/Dockerfile index e695254d..749bc07a 100644 --- a/FE/Dockerfile +++ b/FE/Dockerfile @@ -25,6 +25,8 @@ RUN npm run build FROM nginx:alpine WORKDIR /app COPY --from=builder /app/dist /usr/share/nginx/html +#time zone 설정 +RUN apk add tzdata && ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file From 650fdf27a7a2d546441deaeaa371da20b9ede35c Mon Sep 17 00:00:00 2001 From: jinddings Date: Tue, 19 Nov 2024 15:46:07 +0900 Subject: [PATCH 34/36] =?UTF-8?q?=F0=9F=94=A7=20fix=20:=20=EB=AA=85?= =?UTF-8?q?=EB=A0=B9=EC=96=B4=EC=97=90=EC=84=9C=20sudo=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BE/Dockerfile b/BE/Dockerfile index afd130d2..f530f5ad 100644 --- a/BE/Dockerfile +++ b/BE/Dockerfile @@ -12,7 +12,7 @@ WORKDIR /var/app COPY package*.json ./ RUN npm install --only=production COPY --from=builder /app/dist ./dist -RUN sudo ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime +RUN ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime EXPOSE 3000 CMD ["node", "dist/main.js"] \ No newline at end of file From c7aa9ab7274760d1d1487311dbd559594ff64aa0 Mon Sep 17 00:00:00 2001 From: jinddings Date: Tue, 19 Nov 2024 16:41:05 +0900 Subject: [PATCH 35/36] =?UTF-8?q?=F0=9F=94=A7=20fix=20:=20credential=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/BE/src/main.ts b/BE/src/main.ts index 3a48167d..83042aa6 100644 --- a/BE/src/main.ts +++ b/BE/src/main.ts @@ -20,6 +20,7 @@ async function bootstrap() { methods: 'GET, HEAD, PUT, PATH, POST, DELETE', preflightContinue: false, optionsSuccessStatus: 204, + credentials: true, }); app.use(cookieParser()); From 5b0200ca1ee92871205c212d97682cb01af06ea0 Mon Sep 17 00:00:00 2001 From: jinddings Date: Tue, 19 Nov 2024 17:54:58 +0900 Subject: [PATCH 36/36] =?UTF-8?q?=F0=9F=94=A7=20fix=20:=20accessToken?= =?UTF-8?q?=EB=8F=84=20=EC=BF=A0=ED=82=A4=EC=97=90=20=EB=8B=B4=EC=95=84?= =?UTF-8?q?=EC=84=9C=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/auth/auth.controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/BE/src/auth/auth.controller.ts b/BE/src/auth/auth.controller.ts index 22e29cdb..5c244050 100644 --- a/BE/src/auth/auth.controller.ts +++ b/BE/src/auth/auth.controller.ts @@ -38,6 +38,7 @@ export class AuthController { const { accessToken, refreshToken } = await this.authService.loginUser(authCredentialsDto); + res.cookie('accessToken', accessToken, { httpOnly: true }); res.cookie('refreshToken', refreshToken, { httpOnly: true }); res.cookie('isRefreshToken', true, { httpOnly: true }); return res.status(200).json({ accessToken });