Skip to content

Commit

Permalink
Merge branch 'back/main' into feature/api/register-#26
Browse files Browse the repository at this point in the history
  • Loading branch information
jinddings authored Nov 7, 2024
2 parents eee8101 + a190aa7 commit e817f4b
Show file tree
Hide file tree
Showing 19 changed files with 1,592 additions and 653 deletions.
1 change: 0 additions & 1 deletion .github/workflows/CI → .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ jobs:
working-directory: ./BE
run: npm run build

jobs:
FE-test-and-build:
runs-on: ubuntu-latest

Expand Down
1,499 changes: 849 additions & 650 deletions BE/package-lock.json

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion BE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/platform-socket.io": "^10.4.7",
"@nestjs/schedule": "^4.1.1",
"@nestjs/swagger": "^8.0.1",
"@nestjs/typeorm": "^10.0.2",
"@nestjs/websockets": "^10.4.7",
"axios": "^1.7.7",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
Expand All @@ -37,8 +40,10 @@
"mysql2": "^3.11.3",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"socket.io": "^4.8.1",
"swagger-ui-express": "^5.0.1",
"typeorm": "^0.3.20"
"typeorm": "^0.3.20",
"ws": "^8.18.0"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
Expand All @@ -49,6 +54,7 @@
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@types/ws": "^8.5.13",
"eslint": "^8.0.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^18.0.0",
Expand Down
10 changes: 9 additions & 1 deletion BE/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { User } from './auth/user.entity';
import { StockIndexModule } from './stock/index/stock.index.module';
import { SocketService } from './websocket/socket.service';
import { SocketGateway } from './websocket/socket.gateway';
import { TopfiveModule } from './stocks/topfive/topfive.module';

@Module({
imports: [
ScheduleModule.forRoot(),
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: 'mysql', // 데이터베이스 타입
Expand All @@ -20,8 +26,10 @@ import { User } from './auth/user.entity';
synchronize: true,
}),
AuthModule,
StockIndexModule,
TopfiveModule,
],
controllers: [AppController],
providers: [AppService],
providers: [AppService, SocketService, SocketGateway],
})
export class AppModule {}
14 changes: 14 additions & 0 deletions BE/src/stock/index/dto/stock.index.list.chart.element.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';

export class StockIndexListChartElementDto {
constructor(time: string, value: string) {
this.time = time;
this.value = value;
}

@ApiProperty({ description: 'HHMMSS', example: '130500' })
time: string;

@ApiProperty({ description: '주가 지수' })
value: string;
}
17 changes: 17 additions & 0 deletions BE/src/stock/index/dto/stock.index.list.element.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ApiProperty } from '@nestjs/swagger';
import { StockIndexListChartElementDto } from './stock.index.list.chart.element.dto';

export class StockIndexListElementDto {
constructor(code: string, chart: StockIndexListChartElementDto[]) {
this.code = code;
this.chart = chart;
}

@ApiProperty({
description: '코스피: 0001, 코스닥: 1001, 코스피200: 2001, KSQ150: 3003',
})
code: string;

@ApiProperty({ type: [StockIndexListChartElementDto] })
chart: StockIndexListChartElementDto[];
}
25 changes: 25 additions & 0 deletions BE/src/stock/index/dto/stock.index.response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ApiProperty } from '@nestjs/swagger';
import { StockIndexListElementDto } from './stock.index.list.element.dto';
import { StockIndexValueElementDto } from './stock.index.value.element.dto';

export class StockIndexResponseDto {
constructor(
indexList: StockIndexListElementDto[],
indexValue: StockIndexValueElementDto[],
) {
this.indexList = indexList;
this.indexValue = indexValue;
}

@ApiProperty({
description: '주가 지수 차트 정보 (코스피, 코스닥, 코스피200, KSQ150)',
type: [StockIndexListElementDto],
})
indexList: StockIndexListElementDto[];

@ApiProperty({
description: '주가 지수 실시간 값 정보 (코스피, 코스닥, 코스피200, KSQ150)',
type: [StockIndexValueElementDto],
})
indexValue: StockIndexValueElementDto[];
}
34 changes: 34 additions & 0 deletions BE/src/stock/index/dto/stock.index.value.element.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ApiProperty } from '@nestjs/swagger';

export class StockIndexValueElementDto {
constructor(
code: string,
value: string,
diff: string,
diffRate: string,
sign: string,
) {
this.code = code;
this.value = value;
this.diff = diff;
this.diffRate = diffRate;
this.sign = sign;
}

@ApiProperty({
description: '코스피: 0001, 코스닥: 1001, 코스피200: 2001, KSQ150: 3003',
})
code: string;

@ApiProperty({ description: '주가 지수' })
value: string;

@ApiProperty({ description: '전일 대비 등락' })
diff: string;

@ApiProperty({ description: '전일 대비 등락률' })
diffRate: string;

@ApiProperty({ description: '부호... 인데 추후에 알아봐야 함' })
sign: string;
}
38 changes: 38 additions & 0 deletions BE/src/stock/index/stock.index.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Controller, Get } from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { StockIndexService } from './stock.index.service';
import { StockIndexResponseDto } from './dto/stock.index.response.dto';

@Controller('/api/stock/index')
@ApiTags('주가 지수 API')
export class StockIndexController {
constructor(private readonly stockIndexService: StockIndexService) {}

@Get()
@ApiOperation({
summary: '주가 지수 차트 정보, 현재 값 조회 API',
description: '주가 지수 차트 정보와 현재 값을 리스트로 반환한다.',
})
@ApiResponse({
status: 200,
description: '주가 지수 조회 성공',
type: StockIndexResponseDto,
})
async getStockIndex() {
const stockLists = await Promise.all([
this.stockIndexService.getDomesticStockIndexListByCode('0001'), // 코스피
this.stockIndexService.getDomesticStockIndexListByCode('1001'), // 코스닥
this.stockIndexService.getDomesticStockIndexListByCode('2001'), // 코스피200
this.stockIndexService.getDomesticStockIndexListByCode('3003'), // KSQ150
]);

const stockValues = await Promise.all([
this.stockIndexService.getDomesticStockIndexValueByCode('0001'), // 코스피
this.stockIndexService.getDomesticStockIndexValueByCode('1001'), // 코스닥
this.stockIndexService.getDomesticStockIndexValueByCode('2001'), // 코스피200
this.stockIndexService.getDomesticStockIndexValueByCode('3003'), // KSQ150
]);

return new StockIndexResponseDto(stockLists, stockValues);
}
}
11 changes: 11 additions & 0 deletions BE/src/stock/index/stock.index.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { StockIndexController } from './stock.index.controller';
import { StockIndexService } from './stock.index.service';

@Module({
imports: [],
controllers: [StockIndexController],
providers: [StockIndexService],
exports: [StockIndexService],
})
export class StockIndexModule {}
168 changes: 168 additions & 0 deletions BE/src/stock/index/stock.index.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { Injectable } from '@nestjs/common';
import { StockIndexListChartElementDto } from './dto/stock.index.list.chart.element.dto';
import { StockIndexListElementDto } from './dto/stock.index.list.element.dto';
import { StockIndexValueElementDto } from './dto/stock.index.value.element.dto';

@Injectable()
export class StockIndexService {
private accessToken: string;
private expireDateTime: number;

async getDomesticStockIndexListByCode(code: string) {
const accessToken = await this.getAccessToken();

const url =
'https://openapi.koreainvestment.com:9443/uapi/domestic-stock/v1/quotations/inquire-index-timeprice';
const queryParams = `?FID_INPUT_HOUR_1=300&FID_COND_MRKT_DIV_CODE=U&FID_INPUT_ISCD=${code}`;

const response = await fetch(url + queryParams, {
method: 'GET',
headers: {
'content-type': 'application/json; charset=utf-8',
authorization: `Bearer ${accessToken}`,
appkey: process.env.APP_KEY,
appsecret: process.env.APP_SECRET,
tr_id: 'FHPUP02110200',
custtype: 'P',
},
});

const result: StockIndexChartInterface = await response.json();
if (result.rt_cd !== '0') throw new Error('유효하지 않은 토큰');

return new StockIndexListElementDto(
code,
result.output.map((element) => {
return new StockIndexListChartElementDto(
element.bsop_hour,
element.bstp_nmix_prpr,
);
}),
);
}

async getDomesticStockIndexValueByCode(code: string) {
const accessToken = await this.getAccessToken();

const url =
'https://openapi.koreainvestment.com:9443/uapi/domestic-stock/v1/quotations/inquire-index-price';
const queryParams = `?FID_COND_MRKT_DIV_CODE=U&FID_INPUT_ISCD=${code}`;

const response = await fetch(url + queryParams, {
method: 'GET',
headers: {
'content-type': 'application/json; charset=utf-8',
authorization: `Bearer ${accessToken}`,
appkey: process.env.APP_KEY,
appsecret: process.env.APP_SECRET,
tr_id: 'FHPUP02100000',
custtype: 'P',
},
});

const result: StockIndexValueInterface = await response.json();
return new StockIndexValueElementDto(
code,
result.output.bstp_nmix_prpr,
result.output.bstp_nmix_prdy_vrss,
result.output.bstp_nmix_prdy_vrss,
result.output.prdy_vrss_sign,
);
}

private async getAccessToken() {
if (!this.accessToken || this.expireDateTime <= Date.now()) {
const url = 'https://openapivts.koreainvestment.com:29443/oauth2/tokenP';
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
body: JSON.stringify({
grant_type: 'client_credentials',
appkey: process.env.APP_KEY,
appsecret: process.env.APP_SECRET,
}),
});
const result: AccessTokenInterface = await response.json();
this.accessToken = result.access_token;
this.expireDateTime = new Date(
result.access_token_token_expired,
).getTime();
return result.access_token;
}

return this.accessToken;
}
}

// interfaces

interface AccessTokenInterface {
access_token: string;
access_token_token_expired: string;
token_type: string;
expires_in: number;
}

interface StockIndexChartInterface {
output: StockIndexChartElementInterface[];
rt_cd: string;
msg_cd: string;
msg1: string;
}

interface StockIndexChartElementInterface {
bsop_hour: string;
bstp_nmix_prpr: string;
bstp_nmix_prdy_vrss: string;
prdy_vrss_sign: string;
bstp_nmix_prdy_ctrt: string;
acml_tr_pbmn: string;
acml_vol: string;
cntg_vol: string;
}

interface StockIndexValueInterface {
output: {
bstp_nmix_prpr: string;
bstp_nmix_prdy_vrss: string;
prdy_vrss_sign: string;
bstp_nmix_prdy_ctrt: string;
acml_vol: string;
prdy_vol: string;
acml_tr_pbmn: string;
prdy_tr_pbmn: string;
bstp_nmix_oprc: string;
prdy_nmix_vrss_nmix_oprc: string;
oprc_vrss_prpr_sign: string;
bstp_nmix_oprc_prdy_ctrt: string;
bstp_nmix_hgpr: string;
prdy_nmix_vrss_nmix_hgpr: string;
hgpr_vrss_prpr_sign: string;
bstp_nmix_hgpr_prdy_ctrt: string;
bstp_nmix_lwpr: string;
prdy_clpr_vrss_lwpr: string;
lwpr_vrss_prpr_sign: string;
prdy_clpr_vrss_lwpr_rate: string;
ascn_issu_cnt: string;
uplm_issu_cnt: string;
stnr_issu_cnt: string;
down_issu_cnt: string;
lslm_issu_cnt: string;
dryy_bstp_nmix_hgpr: string;
dryy_hgpr_vrss_prpr_rate: string;
dryy_bstp_nmix_hgpr_date: string;
dryy_bstp_nmix_lwpr: string;
dryy_lwpr_vrss_prpr_rate: string;
dryy_bstp_nmix_lwpr_date: string;
total_askp_rsqn: string;
total_bidp_rsqn: string;
seln_rsqn_rate: string;
shnu_rsqn_rate: string;
ntby_rsqn: string;
};
rt_cd: string;
msg_cd: string;
msg1: string;
}
Loading

0 comments on commit e817f4b

Please sign in to comment.