From 5192a81da649e0159aaf597c07705ae359867fa9 Mon Sep 17 00:00:00 2001 From: DongHoonYu96 Date: Tue, 4 Feb 2025 14:07:52 +0900 Subject: [PATCH 1/7] =?UTF-8?q?docs:=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EC=9A=94=EC=95=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/docs/serviceExplain.md | 170 ++++++++++++++++++++++++ packages/backend/serviceExplain.md | 0 2 files changed, 170 insertions(+) create mode 100644 packages/backend/docs/serviceExplain.md create mode 100644 packages/backend/serviceExplain.md diff --git a/packages/backend/docs/serviceExplain.md b/packages/backend/docs/serviceExplain.md new file mode 100644 index 00000000..d5ed935f --- /dev/null +++ b/packages/backend/docs/serviceExplain.md @@ -0,0 +1,170 @@ +# 서비스 설명서 + +## 1. 서비스 구조 + +### 1.1 StockService + +- **역할**: 주식 기본 정보 관리 +- **주요 기능**: + - 주식 검색 + - 인기 주식 순위 관리 + - 사용자 관심 주식 관리 + - 조회수 관리 +- **관련 테이블**: stock, user_stock + +### 1.2 StockDetailService + +- **역할**: 주식 상세 정보 관리 +- **주요 기능**: + - 시가총액, EPS, PER 정보 제공 + - 52주 최고가/최저가 정보 제공 +- **관련 테이블**: stock_detail + +### 1.3 KoreaStockInfoService + +- **역할**: 주식 마스터 데이터 수집 +- **실행 시간**: 매일 오전 9시, 자정 (월-금) +- **주요 기능**: + - KOSDAQ/KOSPI 마스터 데이터 다운로드 + - 주식 기본 정보 업데이트 +- **관련 테이블**: stock + +### 1.4 OpenapiLiveDataService + +- **역할**: 실시간 주가 데이터 수집 +- **실행 주기**: 1분 +- **주요 기능**: + - 실시간 가격 정보 수집 + - 거래량 데이터 수집 + - 등락률 계산 +- **관련 테이블**: stock_live_data + +## 2. 크론 작업 + +### 2.1 데이터 수집 + +| 작업 | 실행 시간 | 담당 서비스 | +| ------------------ | -------------------- | --------------------------- | +| 마스터 데이터 수집 | 09:00, 00:00 (월-금) | KoreaStockInfoService | +| 실시간 데이터 수집 | 매 1분 (09:00-15:30) | OpenapiLiveDataService | +| 일일 통계 집계 | 15:40 (장 마감 후) | StockDataAggregationService | + +### 2.2 데이터 정리 + +| 작업 | 실행 시간 | 설명 | +| ------------------ | ---------- | -------------------------- | +| 실시간 데이터 정리 | 00:00 매일 | 24시간 이상 된 데이터 삭제 | +| 캐시 정리 | 매시 정각 | 만료된 캐시 삭제 | +| 임시 파일 정리 | 03:00 매일 | 다운로드된 임시 파일 삭제 | + +## 3. API 상세 + +### 3.1 주식 정보 조회 + +GET /stock + +- 설명: 주식명으로 검색 +- 권한: 없음 +- 캐시: 1시간 +- 응답시간: < 100ms + +GET /stock/top + +- 설명: 인기/상승/하락 주식 조회 +- 권한: 없음 +- 캐시: 5분 +- 응답시간: < 200ms + +GET /stock/:stockId/detail + +- 설명: 주식 상세 정보 조회 +- 권한: 없음 +- 캐시: 60초 +- 응답시간: < 150ms + +### 3.2 사용자 기능 + +POST /stock/user + +- 설명: 관심 주식 추가 +- 권한: 로그인 필요 +- 제한: 최대 100개 + +DELETE /stock/user + +- 설명: 관심 주식 제거 +- 권한: 로그인 필요 +- 검증: 소유권 확인 + +## 4. 데이터베이스 상세 + +### 4.1 테이블 구조 + +stock + +- stock_id (PK): 종목코드 +- name: 종목명 +- views: 조회수 +- is_trading: 거래가능여부 +- group_code: 그룹코드 + +stock_detail + +- id (PK): Auto Increment +- stock_id (FK): 종목코드 +- market_cap: 시가총액 +- eps: EPS +- per: PER +- high_52w: 52주 최고가 +- low_52w: 52주 최저가 +- updated_at: 갱신일시 + +### 4.2 인덱스 전략 + +- **조회 성능 최적화**: + - stock_views_idx: (views DESC, name ASC) + - stock_detail_latest_idx: (stock_id, updated_at DESC) +- **검색 성능 최적화**: + - stock_name_idx: FULLTEXT(name) + +## 5. 캐싱 상세 + +### 5.1 API 캐시 + +| 엔드포인트 | 캐시 시간 | 키 패턴 | +| ----------------- | --------- | ---------------------------- | +| /stock/top | 5분 | `stock:top:{sortBy}:{limit}` | +| /stock/:id/detail | 60초 | `stock:detail:{stockId}` | +| /stock/index | 30초 | `stock:index` | + +### 5.2 데이터베이스 캐시 + +| 쿼리 종류 | 캐시 시간 | 갱신 조건 | +| ------------- | --------- | ---------- | +| 인기 검색어 | 1시간 | 수동 갱신 | +| 차트 데이터 | 1일 | 장 마감 후 | +| 종목 상세정보 | 60초 | 자동 갱신 | + +## 6. 에러 처리 + +### 6.1 에러 코드 + +| 코드 | 설명 | 대응 방안 | +| ---- | -------------- | -------------------------- | +| 400 | 잘못된 요청 | 요청 파라미터 검증 | +| 401 | 인증 필요 | 로그인 페이지로 리다이렉트 | +| 404 | 리소스 없음 | 사용자에게 메시지 표시 | +| 429 | 요청 횟수 초과 | 잠시 후 재시도 안내 | + +### 6.2 로깅 정책 + +- **에러 레벨**: + - ERROR: 서버 오류, 외부 API 실패 + - WARN: 비즈니스 규칙 위반 + - INFO: API 호출, 크론 작업 실행 +- **로그 포맷**: + - 시간 + - 요청 ID + - 사용자 ID + - 에러 메시지 + - 스택 트레이스 (ERROR 레벨만) diff --git a/packages/backend/serviceExplain.md b/packages/backend/serviceExplain.md new file mode 100644 index 00000000..e69de29b From d816d1b56d300ad2cf933e7b0d0cbc14d346abfb Mon Sep 17 00:00:00 2001 From: DongHoonYu96 Date: Tue, 4 Feb 2025 17:25:37 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=EC=A2=85=EB=AA=A9=EB=B3=84=20?= =?UTF-8?q?=EB=89=B4=EC=8A=A4=20=EC=93=B0=EA=B8=B0/=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/app.module.ts | 2 + .../src/news/domain/stockNews.entity.ts | 53 +++++++++++ .../backend/src/news/dto/stockNews.dto.ts | 49 +++++++++++ .../backend/src/news/stockNews.controller.ts | 34 +++++++ packages/backend/src/news/stockNews.module.ts | 14 +++ .../src/news/stockNews.service.spec.ts | 88 +++++++++++++++++++ .../backend/src/news/stockNews.service.ts | 40 +++++++++ .../backend/src/stock/domain/stock.entity.ts | 4 + 8 files changed, 284 insertions(+) create mode 100644 packages/backend/src/news/domain/stockNews.entity.ts create mode 100644 packages/backend/src/news/dto/stockNews.dto.ts create mode 100644 packages/backend/src/news/stockNews.controller.ts create mode 100644 packages/backend/src/news/stockNews.module.ts create mode 100644 packages/backend/src/news/stockNews.service.spec.ts create mode 100644 packages/backend/src/news/stockNews.service.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 03c8b3c5..1ed0d15a 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -14,6 +14,7 @@ import { } from '@/configs/typeormConfig'; import { StockModule } from '@/stock/stock.module'; import { UserModule } from '@/user/user.module'; +import { StockNewsModule } from '@/news/stockNews.module'; @Module({ imports: [ @@ -31,6 +32,7 @@ import { UserModule } from '@/user/user.module'; AuthModule, ChatModule, SessionModule, + StockNewsModule, ], controllers: [], providers: [], diff --git a/packages/backend/src/news/domain/stockNews.entity.ts b/packages/backend/src/news/domain/stockNews.entity.ts new file mode 100644 index 00000000..2760eefa --- /dev/null +++ b/packages/backend/src/news/domain/stockNews.entity.ts @@ -0,0 +1,53 @@ +import { + Column, + CreateDateColumn, + Entity, + Index, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { Stock } from '@/stock/domain/stock.entity'; + +@Entity('stock_news') +export class StockNews { + @PrimaryGeneratedColumn() + id: number; + + @Index() + @Column({ name: 'stock_id' }) + stockId: string; + + @Column({ name: 'stock_name', length: 100 }) + stockName: string; + + @Column({ type: 'text' }) + link: string; + + @Column({ type: 'varchar', length: 255 }) + title: string; + + @Column({ type: 'text' }) + summary: string; + + @Column({ name: 'positive_content', type: 'text', nullable: true }) + positiveContent: string; + + @Column({ name: 'negative_content', type: 'text', nullable: true }) + negativeContent: string; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date; + + @ManyToOne(() => Stock, (stock) => stock.news) + @JoinColumn({ name: 'stock_id' }) + stock: Stock; + + getLinks(): string[] { + return this.link.split(',').map(link => link.trim()); + } +} \ No newline at end of file diff --git a/packages/backend/src/news/dto/stockNews.dto.ts b/packages/backend/src/news/dto/stockNews.dto.ts new file mode 100644 index 00000000..62ddf578 --- /dev/null +++ b/packages/backend/src/news/dto/stockNews.dto.ts @@ -0,0 +1,49 @@ +import { IsString, MaxLength } from 'class-validator'; +import { StockNews } from '@/news/domain/stockNews.entity'; + +export class CreateStockNewsDto { + @IsString() + stock_id: string; + + @IsString() + stock_name: string; + + @IsString() + link: string; + + @IsString() + @MaxLength(255) + title: string; + + @IsString() + @MaxLength(400) + summary: string; + + @IsString() + positive_content: string; + + @IsString() + negative_content: string; +} + +export class StockNewsResponse { + constructor(stockNews: StockNews) { + this.stockId = stockNews.stockId; + this.stockName = stockNews.stockName; + this.link = stockNews.link; + this.title = stockNews.title; + this.summary = stockNews.summary; + this.positiveContent = stockNews.positiveContent; + this.negativeContent = stockNews.negativeContent; + this.createdAt = stockNews.createdAt; + } + + stockId: string; + stockName: string; + link: string; + title: string; + summary: string; + positiveContent: string; + negativeContent: string; + createdAt: Date; +} \ No newline at end of file diff --git a/packages/backend/src/news/stockNews.controller.ts b/packages/backend/src/news/stockNews.controller.ts new file mode 100644 index 00000000..563c11c8 --- /dev/null +++ b/packages/backend/src/news/stockNews.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Post, Body, Get, Param } from '@nestjs/common'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { StockNewsService } from '@/news/stockNews.service'; +import { CreateStockNewsDto, StockNewsResponse } from '@/news/dto/stockNews.dto'; + +@ApiTags('Stock News') +@Controller('stock/news') +export class StockNewsController { + constructor(private readonly stockNewsService: StockNewsService) {} + + @Post() + @ApiOperation({ summary: '주식 뉴스 정보 저장' }) + @ApiResponse({ status: 201, type: StockNewsResponse }) + async create(@Body() createStockNewsDto: CreateStockNewsDto) { + const stockNews = await this.stockNewsService.create(createStockNewsDto); + return new StockNewsResponse(stockNews); + } + + @Get(':stockId') + @ApiOperation({ summary: '종목별 뉴스 조회' }) + @ApiResponse({ status: 200, type: [StockNewsResponse] }) + async findByStockId(@Param('stockId') stockId: string) { + const newsList = await this.stockNewsService.findByStockId(stockId); + return newsList.map(news => new StockNewsResponse(news)); + } + + @Get(':stockId/latest') + @ApiOperation({ summary: '종목별 최신 뉴스 조회' }) + @ApiResponse({ status: 200, type: StockNewsResponse }) + async findLatestByStockId(@Param('stockId') stockId: string) { + const news = await this.stockNewsService.findLatestByStockId(stockId); + return news ? new StockNewsResponse(news) : null; + } +} \ No newline at end of file diff --git a/packages/backend/src/news/stockNews.module.ts b/packages/backend/src/news/stockNews.module.ts new file mode 100644 index 00000000..12d3b5a3 --- /dev/null +++ b/packages/backend/src/news/stockNews.module.ts @@ -0,0 +1,14 @@ +import { TypeOrmModule } from '@nestjs/typeorm'; +import { StockNews } from '@/news/domain/stockNews.entity'; +import { StockNewsController } from '@/news/stockNews.controller'; +import { Module } from '@nestjs/common'; +import { StockNewsService } from '@/news/stockNews.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([StockNews])], + controllers: [StockNewsController], + providers: [StockNewsService], + exports: [StockNewsService], +}) + +export class StockNewsModule {} \ No newline at end of file diff --git a/packages/backend/src/news/stockNews.service.spec.ts b/packages/backend/src/news/stockNews.service.spec.ts new file mode 100644 index 00000000..9acd7303 --- /dev/null +++ b/packages/backend/src/news/stockNews.service.spec.ts @@ -0,0 +1,88 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { StockNewsService } from '@/news/stockNews.service'; +import { StockNews } from '@/news/domain/stockNews.entity'; +import { CreateStockNewsDto } from '@/news/dto/stockNews.dto'; + + +describe('NewsService', () => { + let service: StockNewsService; + let repository: Repository; + + const mockNewsDto: CreateStockNewsDto = { + stock_id: '005930', + stock_name: '삼성전자', + link: 'http://news1.com,http://news2.com', + title: '삼성전자 실적 발표', + summary: '삼성전자가 2024년 1분기 실적을 발표했다...', + positive_content: '영업이익 증가', + negative_content: '시장 불확실성 존재', + }; + + const mockNews = { + id: 1, + ...mockNewsDto, + createdAt: new Date(), + updatedAt: new Date(), + getLinks: () => mockNewsDto.link.split(','), + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + StockNewsService, + { + provide: getRepositoryToken(StockNews), + useValue: { + save: jest.fn().mockResolvedValue(mockNews), + find: jest.fn().mockResolvedValue([mockNews]), + findOne: jest.fn().mockResolvedValue(mockNews), + }, + }, + ], + }).compile(); + + service = module.get(StockNewsService); + repository = module.get>(getRepositoryToken(StockNews)); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + describe('create', () => { + it('should create a news entry', async () => { + const result = await service.create(mockNewsDto); + + expect(repository.save).toHaveBeenCalled(); + expect(result).toEqual(mockNews); + }); + }); + + describe('findByStockId', () => { + it('should return an array of news for a stock', async () => { + const stockId = '005930'; + const result = await service.findByStockId(stockId); + + expect(repository.find).toHaveBeenCalledWith({ + where: { stockId }, + order: { createdAt: 'DESC' }, + }); + expect(result).toEqual([mockNews]); + }); + }); + + describe('findLatestByStockId', () => { + it('should return the latest news for a stock', async () => { + const stockId = '005930'; + const result = await service.findLatestByStockId(stockId); + + expect(repository.findOne).toHaveBeenCalledWith({ + where: { stockId }, + order: { createdAt: 'DESC' }, + }); + expect(result).toEqual(mockNews); + }); + }); +}); \ No newline at end of file diff --git a/packages/backend/src/news/stockNews.service.ts b/packages/backend/src/news/stockNews.service.ts new file mode 100644 index 00000000..3c4dd326 --- /dev/null +++ b/packages/backend/src/news/stockNews.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { StockNews } from '@/news/domain/stockNews.entity'; +import { CreateStockNewsDto } from '@/news/dto/stockNews.dto'; + +@Injectable() +export class StockNewsService { + constructor( + @InjectRepository(StockNews) + private readonly stockNewsRepository: Repository, + ) {} + + async create(dto: CreateStockNewsDto): Promise { + const stockNews = new StockNews(); + stockNews.stockId = dto.stock_id; + stockNews.stockName = dto.stock_name; + stockNews.link = dto.link; + stockNews.title = dto.title; + stockNews.summary = dto.summary; + stockNews.positiveContent = dto.positive_content; + stockNews.negativeContent = dto.negative_content; + + return await this.stockNewsRepository.save(stockNews); + } + + async findByStockId(stockId: string): Promise { + return await this.stockNewsRepository.find({ + where: { stockId }, + order: { createdAt: 'DESC' }, + }); + } + + async findLatestByStockId(stockId: string): Promise { + return await this.stockNewsRepository.findOne({ + where: { stockId }, + order: { createdAt: 'DESC' }, + }); + } +} \ No newline at end of file diff --git a/packages/backend/src/stock/domain/stock.entity.ts b/packages/backend/src/stock/domain/stock.entity.ts index 36e37c2f..46a41458 100644 --- a/packages/backend/src/stock/domain/stock.entity.ts +++ b/packages/backend/src/stock/domain/stock.entity.ts @@ -12,6 +12,7 @@ import { Like } from '@/chat/domain/like.entity'; import { DateEmbedded } from '@/common/dateEmbedded.entity'; import { FluctuationRankStock } from '@/stock/domain/FluctuationRankStock.entity'; import { UserStock } from '@/stock/domain/userStock.entity'; +import { StockNews } from '@/news/domain/stockNews.entity'; @Entity() export class Stock { @@ -58,6 +59,9 @@ export class Stock { @OneToOne(() => StockLiveData, (stockLiveData) => stockLiveData.stock) stockLive?: StockLiveData; + @OneToMany(() => StockNews, (news) => news.stock) + news?: StockNews[]; + @OneToMany( () => FluctuationRankStock, (fluctuationRankStock) => fluctuationRankStock.stock, From 00ac14e92e305f8b04ca0951522fcf1a72278fc5 Mon Sep 17 00:00:00 2001 From: DongHoonYu96 Date: Tue, 4 Feb 2025 18:06:06 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20[FE]=20=EC=A2=85=EB=AA=A9=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=89=B4=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stock-detail/components/NewsButton.tsx | 121 ++++++++++++++++++ .../components/StockDetailHeader.tsx | 4 +- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 packages/frontend/src/pages/stock-detail/components/NewsButton.tsx diff --git a/packages/frontend/src/pages/stock-detail/components/NewsButton.tsx b/packages/frontend/src/pages/stock-detail/components/NewsButton.tsx new file mode 100644 index 00000000..389e061f --- /dev/null +++ b/packages/frontend/src/pages/stock-detail/components/NewsButton.tsx @@ -0,0 +1,121 @@ +import { useState } from 'react'; + +interface NewsButtonProps { + stockId: string; + stockName: string; +} + +export const NewsButton = ({ stockId, stockName }: NewsButtonProps) => { + const [isOpen, setIsOpen] = useState(false); + const [news, setNews] = useState([]); + + const handleClick = async () => { + try { + setIsOpen(true); + const response = await fetch(`http://localhost:3000/api/stock/news/${stockId}`); + const data = await response.json(); + setNews(data); + } catch (error) { + console.error('뉴스를 불러오는데 실패했습니다:', error); + } + }; + + return ( +
+ + + {isOpen && ( +
+
+
+

{stockName} 뉴스

+ +
+ +
+ {news.map((item, index) => ( +
+
{item.title}
+
{item.summary}
+ {item.positiveContent && ( +
+ 긍정: {item.positiveContent} +
+ )} + {item.negativeContent && ( +
+ 부정: {item.negativeContent} +
+ )} +
+ {item.link.split(',').map((link: string, i: number) => ( + + 뉴스 링크 {i + 1} + + ))} +
+
+ ))} +
+ + {news.length === 0 && ( +
+ 뉴스가 없습니다 +
+ )} +
+
+ )} +
+ ); +}; \ No newline at end of file diff --git a/packages/frontend/src/pages/stock-detail/components/StockDetailHeader.tsx b/packages/frontend/src/pages/stock-detail/components/StockDetailHeader.tsx index c050a89d..b45c0558 100644 --- a/packages/frontend/src/pages/stock-detail/components/StockDetailHeader.tsx +++ b/packages/frontend/src/pages/stock-detail/components/StockDetailHeader.tsx @@ -11,6 +11,7 @@ import { Modal } from '@/components/ui/modal'; import { UserStatus } from '@/constants/chatStatus'; import { modalMessage, ModalMessage } from '@/constants/modalMessage'; import { LoginContext } from '@/contexts/login'; +import { NewsButton } from './NewsButton'; interface StockDetailHeaderProps { stockId: string; @@ -68,8 +69,9 @@ export const StockDetailHeader = ({ }; return ( -
+

{stockName}

+ {isOpen && ( -
+

{stockName} 뉴스

From 58e0693fb57afc5a70c43e8b227d707cba5fc6fe Mon Sep 17 00:00:00 2001 From: DongHoonYu96 Date: Tue, 4 Feb 2025 19:42:14 +0900 Subject: [PATCH 5/7] =?UTF-8?q?chore:=20[FE]=20=EB=89=B4=EC=8A=A4=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=95=84=EC=9D=B4=EC=BD=98=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/stock-detail/components/NewsButton.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/frontend/src/pages/stock-detail/components/NewsButton.tsx b/packages/frontend/src/pages/stock-detail/components/NewsButton.tsx index a5501566..d5584f9c 100644 --- a/packages/frontend/src/pages/stock-detail/components/NewsButton.tsx +++ b/packages/frontend/src/pages/stock-detail/components/NewsButton.tsx @@ -56,17 +56,11 @@ export const NewsButton = ({ stockId, stockName }: NewsButtonProps) => { - - 뉴스 보기 + AI 뉴스 보기 {isOpen && ( From 1c69772eb3699ff660e1ed2eb76fca1a96c31461 Mon Sep 17 00:00:00 2001 From: DongHoonYu96 Date: Wed, 5 Feb 2025 13:48:25 +0900 Subject: [PATCH 6/7] =?UTF-8?q?chore:=20nullable=20=EC=98=B5=EC=85=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기본값은 false --- packages/backend/src/news/domain/stockNews.entity.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/news/domain/stockNews.entity.ts b/packages/backend/src/news/domain/stockNews.entity.ts index 2760eefa..03a9f396 100644 --- a/packages/backend/src/news/domain/stockNews.entity.ts +++ b/packages/backend/src/news/domain/stockNews.entity.ts @@ -31,10 +31,10 @@ export class StockNews { @Column({ type: 'text' }) summary: string; - @Column({ name: 'positive_content', type: 'text', nullable: true }) + @Column({ name: 'positive_content', type: 'text' }) positiveContent: string; - @Column({ name: 'negative_content', type: 'text', nullable: true }) + @Column({ name: 'negative_content', type: 'text' }) negativeContent: string; @CreateDateColumn({ name: 'created_at' }) From f2501c6094dc738da90790338a969bf64aa38851 Mon Sep 17 00:00:00 2001 From: DongHoonYu96 Date: Wed, 5 Feb 2025 13:56:46 +0900 Subject: [PATCH 7/7] =?UTF-8?q?chore:=20createNews=20dto=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=EA=B8=B8=EC=9D=B4=20=EB=84=89=EB=84=89=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/news/dto/stockNews.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/news/dto/stockNews.dto.ts b/packages/backend/src/news/dto/stockNews.dto.ts index 62ddf578..fe95b01f 100644 --- a/packages/backend/src/news/dto/stockNews.dto.ts +++ b/packages/backend/src/news/dto/stockNews.dto.ts @@ -16,7 +16,7 @@ export class CreateStockNewsDto { title: string; @IsString() - @MaxLength(400) + @MaxLength(10000) summary: string; @IsString()