diff --git a/docs/API_EXAMPLE.md b/docs/API_EXAMPLE.md new file mode 100644 index 0000000..66fa2a6 --- /dev/null +++ b/docs/API_EXAMPLE.md @@ -0,0 +1,841 @@ +```json +{ + "news": [ + { + "newsId": 98, + "title": "또 녹아내린 증시…이번엔 '중국발 급락' 한때 6천피도 깨져", + "press": "MBN", + "publishedAt": "2026-07-28T10:09Z", + "url": "https://n.news.naver.com/mnews/article/057/0001960839?sid=101" + }, + { + "newsId": 112, + "title": "반도체주 급락에 ‘검은 화요일’…코스피·코스닥에 서킷브레이커까지", + "press": "KBS", + "publishedAt": "2026-07-28T10:08Z", + "url": "https://n.news.naver.com/mnews/article/056/0012226716?sid=101" + }, + { + "newsId": 99, + "title": "스페이스X 이어 SK하닉 ADR도 공모가 아래로…美 증시 수퍼루키의 굴욕", + "press": "문화일보", + "publishedAt": "2026-07-28T10:08Z", + "url": "https://n.news.naver.com/mnews/article/021/0002807585?sid=101" + }, + { + "newsId": 96, + "title": "[도청도설] 중국 반도체 굴기", + "press": "국제신문", + "publishedAt": "2026-07-28T10:07Z", + "url": "https://n.news.naver.com/mnews/article/658/0000150808?sid=110" + }, + { + "newsId": 97, + "title": "[사설] 바닥 뚫는 상권 침체 경기회복 없이는 반전 어렵다", + "press": "국제신문", + "publishedAt": "2026-07-28T10:07Z", + "url": "https://n.news.naver.com/mnews/article/658/0000150810?sid=110" + }, + { + "newsId": 95, + "title": "DUV 양산땐 기술 격차 더 좁혀…CXMT 범용 D램 점유율도 날개", + "press": "서울경제", + "publishedAt": "2026-07-28T10:05Z", + "url": "https://n.news.naver.com/mnews/article/011/0004646163?sid=101" + }, + { + "newsId": 94, + "title": "중국 반도체 ‘3번의 도약’…트럼프 제재 뛰어넘을까 [차이나 시그널]", + "press": "매일경제", + "publishedAt": "2026-07-28T10:05Z", + "url": "https://n.news.naver.com/mnews/article/009/0005713529?sid=101" + }, + { + "newsId": 92, + "title": "마이너스 '그냥 안 볼란다'…개미 절규에 정부 초강수", + "press": "JTBC", + "publishedAt": "2026-07-28T10:03Z", + "url": "https://n.news.naver.com/mnews/article/437/0000503435?sid=101" + }, + { + "newsId": 93, + "title": "[숏폼] 샌프란 AI서밋 '반도체 성장 동력 기대'", + "press": "SBS Biz", + "publishedAt": "2026-07-28T10:02Z", + "url": "https://n.news.naver.com/mnews/article/374/0000524225?sid=101" + }, + { + "newsId": 91, + "title": "[집중진단] 中 반도체 공습 공포에 무너진 코스피 ‘-10% 대폭락’…삼...", + "press": "SBS Biz", + "publishedAt": "2026-07-28T10:02Z", + "url": "https://n.news.naver.com/mnews/article/374/0000524223?sid=101" + }, + { + "newsId": 90, + "title": "‘삼전닉스 블랙홀’에 갇힌 경제·사회…경고음 커진다", + "press": "한겨레", + "publishedAt": "2026-07-28T10:02Z", + "url": "https://n.news.naver.com/mnews/article/028/0002816148?sid=101" + }, + { + "newsId": 82, + "title": "1000억 기술 빼돌려도…처벌은 고작 2년형", + "press": "서울경제", + "publishedAt": "2026-07-28T10:00Z", + "url": "https://n.news.naver.com/mnews/article/011/0004646160?sid=102" + }, + { + "newsId": 86, + "title": "1000억 기술 빼돌려도…처벌은 고작 2년형", + "press": "서울경제", + "publishedAt": "2026-07-28T10:00Z", + "url": "https://n.news.naver.com/mnews/article/011/0004646160?sid=102" + }, + { + "newsId": 84, + "title": "'AI 거품' 안 그래도 불안한데…중국, 이것까지? '쌍충격'", + "press": "JTBC", + "publishedAt": "2026-07-28T09:59Z", + "url": "https://n.news.naver.com/mnews/article/437/0000503434?sid=101" + }, + { + "newsId": 88, + "title": "'AI 거품' 안 그래도 불안한데…중국, 이것까지? '쌍충격'", + "press": "JTBC", + "publishedAt": "2026-07-28T09:59Z", + "url": "https://n.news.naver.com/mnews/article/437/0000503434?sid=101" + }, + { + "newsId": 83, + "title": "추미애 "경기남부 광역철도, 제5차 국가철도망에 반드시 반영"", + "press": "뉴스1", + "publishedAt": "2026-07-28T09:59Z", + "url": "https://n.news.naver.com/mnews/article/421/0009084170?sid=102" + }, + { + "newsId": 87, + "title": "추미애 "경기남부 광역철도, 제5차 국가철도망에 반드시 반영"", + "press": "뉴스1", + "publishedAt": "2026-07-28T09:59Z", + "url": "https://n.news.naver.com/mnews/article/421/0009084170?sid=102" + }, + { + "newsId": 100, + "title": "日 구마모토현서 7.1 강진… TSMC 등 반도체 공장 피해 확인 중", + "press": "디지털타임스", + "publishedAt": "2026-07-28T09:58Z", + "url": "https://n.news.naver.com/mnews/article/029/0003039438?sid=101" + }, + { + "newsId": 89, + "title": "삼전닉스 '와르르'… 6000피도 '위태'", + "press": "디지털타임스", + "publishedAt": "2026-07-28T09:58Z", + "url": "https://n.news.naver.com/mnews/article/029/0003039437?sid=101" + }, + { + "newsId": 85, + "title": "삼전닉스 '와르르'… 6000피도 '위태'", + "press": "디지털타임스", + "publishedAt": "2026-07-28T09:58Z", + "url": "https://n.news.naver.com/mnews/article/029/0003039437?sid=101" + } + ], + "hasNext": true, + "lastCursorId": 85 +} +``` + +```json +{ + "news_summary": [ + "코스피가 중국발 반도체 쇼크로 인해 장중 한때 6천선을 내주며 10% 넘게 폭락했다.", + "삼성전자와 SK하이닉스가 각각 13% 넘게 하락하며 주가가 크게 떨어졌다.", + "중국의 창신메모리테크놀로지(CXMT) 상장과 DUV 노광장비 개발 소식이 글로벌 메모리 공급 경쟁을 격화시킬 것이라는 우려가 커지고 있다." + ], + "source": { + "press": "MBN", + "published_at": "2026-07-28 10:09:00+00:00", + "url": "https://n.news.naver.com/mnews/article/057/0001960839?sid=101" + }, + "origin_stocks": [ + { + "ticker": "005930", + "name": "삼성전자", + "status": "down", + "reason": "삼성전자는 이번 뉴스에서 언급된 중국의 창신메모리테크놀로지(CXMT) 상장으로 인해 직접적인 영향을 받으며 주가가 크게 하락했다." + } + ], + "related_stocks": [ + { + "ticker": "403870", + "name": "HPSP", + "status": "down", + "relation_label": "공급망", + "relation_path": "삼성전자 → HPSP", + "propagation": "*HPSP*는 삼성전자와의 장비 공급 계약이 있지만, 반도체 시장의 불확실성으로 인해 주가가 하락할 가능성이 있다." + }, + { + "ticker": "005290", + "name": "동진쎄미켐", + "status": "down", + "relation_label": "지분투자", + "relation_path": "삼성전자 → 동진쎄미켐", + "propagation": "*동진쎄미켐*은 삼성전자와의 거래 비중이 높지만, 반도체 시장의 하락세로 인해 주가가 하락할 것으로 보인다." + }, + { + "ticker": "104830", + "name": "원익머트리얼즈", + "status": "down", + "relation_label": "공급망", + "relation_path": "삼성전자 → 원익머트리얼즈", + "propagation": "*원익머트리얼즈*는 삼성전자에 특수가스를 공급하고 있지만, 반도체 시장의 전반적인 하락세로 인해 주가가 하락할 가능성이 있다." + } + ], + "final_summary": "이번 뉴스는 중국의 창신메모리테크놀로지(CXMT) 상장으로 인해 반도체 시장의 경쟁이 심화되면서, *삼성전자*를 포함한 관련 기업들의 주가에 부정적인 영향을 미칠 것으로 예상된다. *삼성전자*와의 공급망 관계에 있는 *어보브반도체*, *HPSP*, *동진쎄미켐*, *원익머트리얼즈* 등도 주가 하락이 우려된다. 전반적으로 반도체 시장의 불확실성이 커지면서 관련 기업들의 주가는 하락할 가능성이 높다.", + "graph": { + "newsId": "98", + "originId": "S_SEC", + "nodes": [ + { + "id": "S_SEC", + "name": "삼성전자", + "ticker": "005930", + "capSize": "Large", + "marketType": "KOSPI" + }, + { + "id": "S_HPS", + "name": "HPSP", + "ticker": "403870", + "capSize": "Mid", + "marketType": "KOSDAQ" + }, + { + "id": "S_SOB", + "name": "솔브레인", + "ticker": "357780", + "capSize": "Mid", + "marketType": "KOSDAQ" + }, + { + "id": "S_WNM", + "name": "원익머트리얼즈", + "ticker": "104830", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_ABV", + "name": "어보브반도체", + "ticker": "102950", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_SST", + "name": "에스앤에스텍", + "ticker": "101490", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_EXC", + "name": "엑시콘", + "ticker": "092870", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_OKS", + "name": "오킨스전자", + "ticker": "080580", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_ADT", + "name": "에이디테크놀로지", + "ticker": "049080", + "capSize": "Small", + "marketType": "KOSPI" + }, + { + "id": "S_STI", + "name": "에스티아이", + "ticker": "039440", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_SKH", + "name": "SK하이닉스", + "ticker": "000660", + "capSize": "Large", + "marketType": "KOSPI" + }, + { + "id": "S_WDX", + "name": "월덱스", + "ticker": "101160", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_DIA", + "name": "디아이", + "ticker": "003160", + "capSize": "Small", + "marketType": "KOSPI" + }, + { + "id": "S_YCI", + "name": "와이씨", + "ticker": "232140", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_DJS", + "name": "동진쎄미켐", + "ticker": "005290", + "capSize": "Mid", + "marketType": "KOSPI" + }, + { + "id": "S_DDE", + "name": "대덕전자", + "ticker": "353200", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_FST", + "name": "에프에스티", + "ticker": "036810", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_COA", + "name": "코아시아", + "ticker": "045970", + "capSize": "Small", + "marketType": "KOSPI" + }, + { + "id": "S_QTB", + "name": "퀄리타스반도체", + "ticker": "432720", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_NXT", + "name": "넥스틴", + "ticker": "348210", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_ORO", + "name": "오로스테크놀로지", + "ticker": "322310", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_STH", + "name": "심텍", + "ticker": "222800", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_PSI", + "name": "파크시스템스", + "ticker": "140860", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_DIT", + "name": "디아이티", + "ticker": "110990", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_ISC", + "name": "ISC", + "ticker": "095340", + "capSize": "Mid", + "marketType": "KOSDAQ" + }, + { + "id": "S_TCW", + "name": "테크윙", + "ticker": "089030", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_ITP", + "name": "인텍플러스", + "ticker": "064290", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_HMI", + "name": "한미반도체", + "ticker": "042700", + "capSize": "Mid", + "marketType": "KOSPI" + }, + { + "id": "S_PSK_H", + "name": "피에스케이홀딩스", + "ticker": "031980", + "capSize": "Small", + "marketType": "KOSDAQ" + } + ], + "edges": [ + { + "id": "e1", + "source": "S_ABV", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e2", + "source": "S_HPS", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e3", + "source": "S_HPS", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e4", + "source": "S_DJS", + "target": "S_SKH", + "relation": "기술협력" + }, + { + "id": "e5", + "source": "S_DJS", + "target": "S_SEC", + "relation": "지분투자" + }, + { + "id": "e6", + "source": "S_WNM", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e7", + "source": "S_SOB", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e8", + "source": "S_SOB", + "target": "S_SKH", + "relation": "경쟁사" + }, + { + "id": "e9", + "source": "S_SOB", + "target": "S_SEC", + "relation": "경쟁사" + }, + { + "id": "e10", + "source": "S_WDX", + "target": "S_SKH", + "relation": "경쟁사" + }, + { + "id": "e11", + "source": "S_WDX", + "target": "S_SEC", + "relation": "경쟁사" + }, + { + "id": "e12", + "source": "S_ADT", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e13", + "source": "S_COA", + "target": "S_SEC", + "relation": "계열/관계사" + }, + { + "id": "e14", + "source": "S_SEC", + "target": "S_OKS", + "relation": "공급망" + }, + { + "id": "e15", + "source": "S_SEC", + "target": "S_EXC", + "relation": "공급망" + }, + { + "id": "e16", + "source": "S_SEC", + "target": "S_ABV", + "relation": "공급망" + }, + { + "id": "e17", + "source": "S_SEC", + "target": "S_ADT", + "relation": "공급망" + }, + { + "id": "e18", + "source": "S_SEC", + "target": "S_STI", + "relation": "공급망" + }, + { + "id": "e19", + "source": "S_SEC", + "target": "S_FST", + "relation": "지분투자" + }, + { + "id": "e20", + "source": "S_SEC", + "target": "S_YCI", + "relation": "지분투자" + }, + { + "id": "e21", + "source": "S_SEC", + "target": "S_SST", + "relation": "지분투자" + }, + { + "id": "e22", + "source": "S_SEC", + "target": "S_SOB", + "relation": "지분투자" + }, + { + "id": "e23", + "source": "S_SEC", + "target": "S_DJS", + "relation": "지분투자" + }, + { + "id": "e24", + "source": "S_SST", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e25", + "source": "S_SST", + "target": "S_SEC", + "relation": "지분투자" + }, + { + "id": "e26", + "source": "S_DIA", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e27", + "source": "S_DIA", + "target": "S_SEC", + "relation": "기타관계" + }, + { + "id": "e28", + "source": "S_DIA", + "target": "S_SKH", + "relation": "기타관계" + }, + { + "id": "e29", + "source": "S_DIA", + "target": "S_SEC", + "relation": "경쟁사" + }, + { + "id": "e30", + "source": "S_DIA", + "target": "S_SKH", + "relation": "경쟁사" + }, + { + "id": "e31", + "source": "S_SKH", + "target": "S_SEC", + "relation": "시장경쟁" + }, + { + "id": "e32", + "source": "S_SKH", + "target": "S_STH", + "relation": "공급망" + }, + { + "id": "e33", + "source": "S_SKH", + "target": "S_ISC", + "relation": "공급망" + }, + { + "id": "e34", + "source": "S_SKH", + "target": "S_PSI", + "relation": "기술협력" + }, + { + "id": "e35", + "source": "S_SKH", + "target": "S_DJS", + "relation": "기술협력" + }, + { + "id": "e36", + "source": "S_SKH", + "target": "S_ORO", + "relation": "공급망" + }, + { + "id": "e37", + "source": "S_SKH", + "target": "S_ITP", + "relation": "공급망" + }, + { + "id": "e38", + "source": "S_SKH", + "target": "S_STI", + "relation": "기술협력" + }, + { + "id": "e39", + "source": "S_SKH", + "target": "S_PSK_H", + "relation": "공급망" + }, + { + "id": "e40", + "source": "S_SKH", + "target": "S_QTB", + "relation": "기술라이선싱" + }, + { + "id": "e41", + "source": "S_SKH", + "target": "S_DIT", + "relation": "공급망" + }, + { + "id": "e42", + "source": "S_SKH", + "target": "S_NXT", + "relation": "공급망" + }, + { + "id": "e43", + "source": "S_SKH", + "target": "S_TCW", + "relation": "공급망" + }, + { + "id": "e44", + "source": "S_SKH", + "target": "S_YCI", + "relation": "공급망" + }, + { + "id": "e45", + "source": "S_SKH", + "target": "S_DIA", + "relation": "공급망" + }, + { + "id": "e46", + "source": "S_SKH", + "target": "S_HMI", + "relation": "공급망" + }, + { + "id": "e47", + "source": "S_STH", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e48", + "source": "S_ISC", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e49", + "source": "S_ISC", + "target": "S_SKH", + "relation": "경쟁사" + }, + { + "id": "e50", + "source": "S_TCW", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e51", + "source": "S_YCI", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e52", + "source": "S_YCI", + "target": "S_SKH", + "relation": "계열/관계사" + }, + { + "id": "e53", + "source": "S_YCI", + "target": "S_SEC", + "relation": "계열/관계사" + }, + { + "id": "e54", + "source": "S_YCI", + "target": "S_EXC", + "relation": "지분투자" + }, + { + "id": "e55", + "source": "S_NXT", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e56", + "source": "S_EXC", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e57", + "source": "S_EXC", + "target": "S_YCI", + "relation": "계열/관계사" + }, + { + "id": "e58", + "source": "S_EXC", + "target": "S_YCI", + "relation": "지분투자" + }, + { + "id": "e59", + "source": "S_QTB", + "target": "S_SKH", + "relation": "기술라이선싱" + }, + { + "id": "e60", + "source": "S_STI", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e61", + "source": "S_STI", + "target": "S_SKH", + "relation": "기술협력" + }, + { + "id": "e62", + "source": "S_HMI", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e63", + "source": "S_FST", + "target": "S_ORO", + "relation": "계열/관계사" + }, + { + "id": "e64", + "source": "S_FST", + "target": "S_SEC", + "relation": "지분투자" + }, + { + "id": "e65", + "source": "S_ORO", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e66", + "source": "S_ORO", + "target": "S_FST", + "relation": "계열/관계사" + }, + { + "id": "e67", + "source": "S_OKS", + "target": "S_SEC", + "relation": "공급망" + }, + { + "id": "e68", + "source": "S_DDE", + "target": "S_SEC", + "relation": "경쟁사" + }, + { + "id": "e69", + "source": "S_DIT", + "target": "S_SKH", + "relation": "기술협력" + }, + { + "id": "e70", + "source": "S_PSK_H", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e71", + "source": "S_PSI", + "target": "S_SKH", + "relation": "기술협력" + }, + { + "id": "e72", + "source": "S_ITP", + "target": "S_SKH", + "relation": "공급망" + } + ] + } +} +``` diff --git a/src/apis/analysis/mock.ts b/src/apis/analysis/mock.ts index f12ca63..db3c707 100644 --- a/src/apis/analysis/mock.ts +++ b/src/apis/analysis/mock.ts @@ -1,6 +1,8 @@ import { NEWS_RECORDS } from '#/mocks/news/data' import { ONTOLOGY_EDGES, ONTOLOGY_NODES } from '#/mocks/graph/data' import { marketCapToCapSize } from '#/shared/utils/format' +import refreshNewsExamples from '#/mocks/analysis/refreshNewsExamples.json' +import hmiSubgraph from '#/mocks/analysis/hmiSubgraph.json' import type { ImpactGraphNodeWire, NewsImpactWire, @@ -9,6 +11,17 @@ import type { import type { Direction } from '#/shared/types' import type { OntologyEdge, OntologyNode } from '#/types/graph' +/** "새로고침" 데모용 한미반도체 뉴스 3건(20001~20003) — 셋 다 실제 온톨로지 + * 2-hop 서브그래프(hmiSubgraph.json)를 공유하고, 뉴스별 요약/기점/관련종목만 다르다. */ +const REFRESH_EXAMPLES = refreshNewsExamples as unknown as Partial< + Record< + string, + Omit & { + graph: { newsId: number; originId: string } + } + > +> + /** * `docs/KOSLINK_API.md` §2 GET /news/{news_id}/impact 명세와 같은 시그니처를 갖는 * 헬퍼. 지금은 백엔드가 없어 mocks/news/data.ts + mocks/graph/data.ts를 동기적으로 @@ -81,6 +94,27 @@ function buildFinalSummary(originNames: string[], relatedCount: number): string */ export function getNewsImpact(newsId: number): NewsImpactWire | null { const record = NEWS_RECORDS.find((n) => n.id === newsId) + + // "새로고침"으로 받아온 한미반도체 뉴스 3건 — 그래프는 실제 온톨로지 2-hop + // 서브그래프(hmiSubgraph.json)를 그대로 쓰고, 발행 시각은 리스트에 실제로 + // 찍힌 값(새로고침 클릭 시점)을 그대로 맞춘다. + const example = REFRESH_EXAMPLES[String(newsId)] + if (example) { + return { + ...example, + source: { + ...example.source, + published_at: record?.publishedAt ?? example.source.published_at, + }, + graph: { + newsId: example.graph.newsId, + originId: example.graph.originId, + nodes: hmiSubgraph.nodes as ImpactGraphNodeWire[], + edges: hmiSubgraph.edges, + }, + } + } + if (!record) return null const originNames = record.originStocks.map( diff --git a/src/apis/news/mock.ts b/src/apis/news/mock.ts index b44c74c..1f9df60 100644 --- a/src/apis/news/mock.ts +++ b/src/apis/news/mock.ts @@ -1,6 +1,7 @@ import { NEWS_RECORDS, pullRefreshBatch } from '#/mocks/news/data' import type { NewsListPageWire } from './mappers' import { paginate } from '#/shared/apis/paginate' +import type { NewsListItem } from '#/types/news' /** * `docs/KOSLINK_API.md` §1 GET /news 명세와 같은 시그니처를 갖는 헬퍼 모음. @@ -45,11 +46,23 @@ export function getNews({ /** * "최신 뉴스 새로고침" 데모용 — 실 백엔드가 없어 pullRefreshBatch()로 몇 건을 - * 새 뉴스로 뽑아 NEWS_RECORDS 맨 앞에 끼워 넣고, 그 id 목록을 돌려준다. - * 호출부(NewsPanel)는 이 id로 새로 추가된 카드에 NEW 배지를 붙이고 몇 건이 - * 들어왔는지 안내한다. + * 새 뉴스로 뽑아 NEWS_RECORDS 맨 앞에 끼워 넣고, id 목록과 리스트 표시용 항목을 + * 함께 돌려준다. 호출부(NewsPanel)는 이 id로 새로 추가된 카드에 NEW 배지를 붙이고 + * addedItems를 쿼리 캐시 맨 앞에 직접 끼워 넣어(쿼리 재조회 없이) 화면에 반영한다. */ -export function refreshNews(): { addedIds: number[] } { +export function refreshNews(): { + addedIds: number[] + addedItems: NewsListItem[] +} { const added = pullRefreshBatch() - return { addedIds: added.map((n) => n.id) } + return { + addedIds: added.map((n) => n.id), + addedItems: added.map((n) => ({ + id: n.id, + title: n.title, + press: n.press, + publishedAt: n.publishedAt, + url: n.url, + })), + } } diff --git a/src/components/news/NewsPanel.tsx b/src/components/news/NewsPanel.tsx index 8757e26..3d3a651 100644 --- a/src/components/news/NewsPanel.tsx +++ b/src/components/news/NewsPanel.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from 'react' import { useIsFetching, useQueryClient } from '@tanstack/react-query' +import type { InfiniteData } from '@tanstack/react-query' import { useAtom, useSetAtom } from 'jotai' import { RefreshCw } from 'lucide-react' import { cn } from '#/shared/utils/cn' @@ -7,6 +8,7 @@ import { newsSheetOpenAtom } from '#/shared/store/atoms' import { newlyAddedNewsIdsAtom } from '#/store/news/atoms' import { refreshNews } from '#/apis/news/mock' import { newsKeys } from '#/apis/news/queries' +import type { NewsListPage } from '#/types/news' import NewsList from './NewsList' const REFRESH_TOAST_MS = 4500 @@ -26,12 +28,31 @@ export default function NewsPanel() { useEffect(() => () => window.clearTimeout(toastTimerRef.current), []) - async function handleRefresh() { - // mock 데이터 계층에서 데모용 새 뉴스 몇 건을 먼저 끼워 넣은 뒤, 목록 전체를 - // 첫 페이지부터 다시 불러온다 — 스크롤로 더 불러온 뒷페이지는 새로고침 시 - // 버리고 최신순 1페이지로 되돌아간다(실제 뉴스 피드 새로고침과 동일한 동작). - const { addedIds } = refreshNews() - await queryClient.resetQueries({ queryKey: newsKeys.all() }) + function handleRefresh() { + // mock 데이터 계층에서 데모용 새 뉴스 몇 건을 먼저 끼워 넣는다. + const { addedIds, addedItems } = refreshNews() + + // 임시 처리(TODO): 새로고침 시 쿼리를 다시 받아오는 대신, 방금 받아온 항목을 + // 캐시 첫 페이지 맨 앞에 직접 끼워 넣는다 — 선택된 뉴스(selectedNewsIdAtom)는 + // 이 쿼리 캐시와 무관해 그대로 유지된다. 실 API 연동 시 아래처럼 재조회로 + // 되돌리면 된다. + // await queryClient.resetQueries({ queryKey: newsKeys.all() }) + if (addedItems.length > 0) { + queryClient.setQueryData>( + newsKeys.all(), + (old) => { + if (!old) return old + const [firstPage, ...restPages] = old.pages + return { + ...old, + pages: [ + { ...firstPage, items: [...addedItems, ...firstPage.items] }, + ...restPages, + ], + } + }, + ) + } setNewlyAdded(addedIds) setToastCount(addedIds.length) diff --git a/src/mocks/analysis/hmiSubgraph.json b/src/mocks/analysis/hmiSubgraph.json new file mode 100644 index 0000000..d44ad6b --- /dev/null +++ b/src/mocks/analysis/hmiSubgraph.json @@ -0,0 +1,374 @@ +{ + "nodes": [ + { + "id": "S_HMI", + "name": "한미반도체", + "ticker": "042700", + "capSize": "Mid", + "marketType": "KOSPI" + }, + { + "id": "S_SKH", + "name": "SK하이닉스", + "ticker": "000660", + "capSize": "Large", + "marketType": "KOSPI" + }, + { + "id": "S_SEC", + "name": "삼성전자", + "ticker": "005930", + "capSize": "Large", + "marketType": "KOSPI" + }, + { + "id": "S_QTB", + "name": "퀄리타스반도체", + "ticker": "432720", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_HPS", + "name": "HPSP", + "ticker": "403870", + "capSize": "Mid", + "marketType": "KOSDAQ" + }, + { + "id": "S_NXT", + "name": "넥스틴", + "ticker": "348210", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_ORO", + "name": "오로스테크놀로지", + "ticker": "322310", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_YCI", + "name": "와이씨", + "ticker": "232140", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_STH", + "name": "심텍", + "ticker": "222800", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_PSI", + "name": "파크시스템스", + "ticker": "140860", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_DIT", + "name": "디아이티", + "ticker": "110990", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_ISC", + "name": "ISC", + "ticker": "095340", + "capSize": "Mid", + "marketType": "KOSDAQ" + }, + { + "id": "S_TCW", + "name": "테크윙", + "ticker": "089030", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_ITP", + "name": "인텍플러스", + "ticker": "064290", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_STI", + "name": "에스티아이", + "ticker": "039440", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_PSK_H", + "name": "피에스케이홀딩스", + "ticker": "031980", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_DJS", + "name": "동진쎄미켐", + "ticker": "005290", + "capSize": "Mid", + "marketType": "KOSPI" + }, + { + "id": "S_DIA", + "name": "디아이", + "ticker": "003160", + "capSize": "Small", + "marketType": "KOSPI" + }, + { + "id": "S_WDX", + "name": "월덱스", + "ticker": "101160", + "capSize": "Small", + "marketType": "KOSDAQ" + }, + { + "id": "S_SOB", + "name": "솔브레인", + "ticker": "357780", + "capSize": "Mid", + "marketType": "KOSDAQ" + } + ], + "edges": [ + { + "id": "e32", + "source": "S_HMI", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e51", + "source": "S_SKH", + "target": "S_HMI", + "relation": "공급망" + }, + { + "id": "e10", + "source": "S_SKH", + "target": "S_SEC", + "relation": "시장 경쟁" + }, + { + "id": "e21", + "source": "S_QTB", + "target": "S_SKH", + "relation": "기술라이선싱" + }, + { + "id": "e22", + "source": "S_HPS", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e23", + "source": "S_NXT", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e24", + "source": "S_ORO", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e25", + "source": "S_YCI", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e26", + "source": "S_STH", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e27", + "source": "S_PSI", + "target": "S_SKH", + "relation": "기술 협력" + }, + { + "id": "e28", + "source": "S_DIT", + "target": "S_SKH", + "relation": "기술 협력" + }, + { + "id": "e29", + "source": "S_ISC", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e30", + "source": "S_TCW", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e31", + "source": "S_ITP", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e33", + "source": "S_STI", + "target": "S_SKH", + "relation": "기술 협력" + }, + { + "id": "e34", + "source": "S_PSK_H", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e35", + "source": "S_DJS", + "target": "S_SKH", + "relation": "기술 협력" + }, + { + "id": "e36", + "source": "S_DIA", + "target": "S_SKH", + "relation": "공급망" + }, + { + "id": "e37", + "source": "S_WDX", + "target": "S_SKH", + "relation": "경쟁사" + }, + { + "id": "e38", + "source": "S_DIA", + "target": "S_SKH", + "relation": "기타관계" + }, + { + "id": "e39", + "source": "S_YCI", + "target": "S_SKH", + "relation": "계열/관계사" + }, + { + "id": "e40", + "source": "S_SOB", + "target": "S_SKH", + "relation": "경쟁사" + }, + { + "id": "e41", + "source": "S_ISC", + "target": "S_SKH", + "relation": "경쟁사" + }, + { + "id": "e42", + "source": "S_DIA", + "target": "S_SKH", + "relation": "경쟁사" + }, + { + "id": "e44", + "source": "S_SKH", + "target": "S_QTB", + "relation": "기술라이선싱" + }, + { + "id": "e52", + "source": "S_SKH", + "target": "S_PSK_H", + "relation": "공급망" + }, + { + "id": "e55", + "source": "S_SKH", + "target": "S_STI", + "relation": "기술 협력" + }, + { + "id": "e56", + "source": "S_SKH", + "target": "S_DIT", + "relation": "공급망" + }, + { + "id": "e57", + "source": "S_SKH", + "target": "S_YCI", + "relation": "공급망" + }, + { + "id": "e63", + "source": "S_SKH", + "target": "S_TCW", + "relation": "공급망" + }, + { + "id": "e64", + "source": "S_SKH", + "target": "S_DIA", + "relation": "공급망" + }, + { + "id": "e65", + "source": "S_SKH", + "target": "S_ORO", + "relation": "공급망" + }, + { + "id": "e67", + "source": "S_SKH", + "target": "S_NXT", + "relation": "공급망" + }, + { + "id": "e68", + "source": "S_SKH", + "target": "S_ITP", + "relation": "공급망" + }, + { + "id": "e69", + "source": "S_SKH", + "target": "S_ISC", + "relation": "공급망" + }, + { + "id": "e76", + "source": "S_SKH", + "target": "S_PSI", + "relation": "기술 협력" + }, + { + "id": "e77", + "source": "S_SKH", + "target": "S_STH", + "relation": "공급망" + }, + { + "id": "e78", + "source": "S_SKH", + "target": "S_DJS", + "relation": "기술 협력" + } + ] +} \ No newline at end of file diff --git a/src/mocks/analysis/refreshNewsExamples.json b/src/mocks/analysis/refreshNewsExamples.json new file mode 100644 index 0000000..8a05a8d --- /dev/null +++ b/src/mocks/analysis/refreshNewsExamples.json @@ -0,0 +1,131 @@ +{ + "20001": { + "news_summary": [ + "한미반도체가 인천 본사 인근 부지를 매입해 창사 이래 최대 규모인 제8공장 건설을 추진한다고 밝혔다.", + "신규 공장은 HBM 생산에 필수적인 TC본더 등 첨단 패키징 장비 생산시설로 활용될 계획이다.", + "AI 반도체 시장 확대에 따른 장비 공급 부족(쇼티지)에 선제 대응하려는 조치로 풀이된다." + ], + "source": { + "press": "뉴스핌", + "published_at": "PLACEHOLDER", + "url": "https://www.newspim.com/news/view/20260720000985" + }, + "origin_stocks": [ + { + "ticker": "042700", + "name": "한미반도체", + "status": "up", + "reason": "한미반도체는 이번 제8공장 확보로 TC본더 등 첨단 패키징 장비 생산능력을 직접 확대하는 당사자다." + } + ], + "related_stocks": [ + { + "ticker": "000660", + "name": "SK하이닉스", + "status": "up", + "relation_label": "공급망", + "relation_path": "한미반도체 → SK하이닉스", + "propagation": "*SK하이닉스*는 한미반도체의 TC본더를 HBM 생산에 쓰는 핵심 수요처라, 장비 증산은 곧 HBM 공급 안정으로 이어질 가능성이 있다." + }, + { + "ticker": "403870", + "name": "HPSP", + "status": "up", + "relation_label": "공급망", + "relation_path": "한미반도체 → SK하이닉스 → HPSP", + "propagation": "*HPSP*는 한미반도체와 같은 SK하이닉스 후공정 협력사로, 패키징 장비 투자 확대의 온기를 함께 받을 가능성이 있다." + } + ], + "final_summary": "이번 뉴스는 한미반도체가 AI·HBM向 패키징 장비 생산능력을 선제적으로 확대한다는 내용으로, *한미반도체*의 직접적인 실적 기여가 기대된다. 핵심 수요처인 *SK하이닉스*와 같은 후공정 공급망에 있는 *HPSP* 등도 함께 주목받을 것으로 보인다.", + "graph": { + "newsId": 20001, + "originId": "S_HMI" + } + }, + "20002": { + "news_summary": [ + "한미반도체가 '와이드 TC본더'를 앞세워 HBM4용 신규 수주 경쟁력을 강화하고 있다고 밝혔다.", + "TC본더 적용처가 확장되며 신규 수주 기회가 늘어날 것으로 전망된다.", + "HBM TC본더 시장 점유율 71%를 바탕으로 후속 수주 논의도 이어지고 있다." + ], + "source": { + "press": "이데일리", + "published_at": "PLACEHOLDER", + "url": "https://v.daum.net/v/20260702084432066" + }, + "origin_stocks": [ + { + "ticker": "042700", + "name": "한미반도체", + "status": "up", + "reason": "한미반도체는 TC본더 적용처 확장으로 신규 수주 경쟁력이 직접 강화되는 당사자다." + } + ], + "related_stocks": [ + { + "ticker": "000660", + "name": "SK하이닉스", + "status": "up", + "relation_label": "공급망", + "relation_path": "한미반도체 → SK하이닉스", + "propagation": "*SK하이닉스*는 한미반도체 TC본더의 주요 수요처로, 적용처 확장에 따른 후속 발주 확대 수혜가 예상된다." + }, + { + "ticker": "095340", + "name": "ISC", + "status": "up", + "relation_label": "공급망", + "relation_path": "한미반도체 → SK하이닉스 → ISC", + "propagation": "*ISC*는 SK하이닉스향 테스트 소켓 공급사로, 후공정 투자 확대 국면에서 함께 수혜를 받을 가능성이 있다." + } + ], + "final_summary": "이번 뉴스는 한미반도체의 TC본더 적용처 확장과 신규 수주 경쟁력 강화를 다루며, *한미반도체*의 실적 기여가 기대된다. 주요 수요처인 *SK하이닉스*와 후공정 협력사 *ISC* 등도 함께 주목받을 것으로 보인다.", + "graph": { + "newsId": 20002, + "originId": "S_HMI" + } + }, + "20003": { + "news_summary": [ + "한미반도체가 HBM4 제조용 'TC본더 4.5 그리핀' 장비 수주를 공시했다.", + "이번 수주 규모는 442억원으로, 매출액 대비 약 7.66%에 해당한다.", + "HBM4 양산 확대에 따른 후속 발주가 이어질지 시장의 관심이 모인다." + ], + "source": { + "press": "한국경제", + "published_at": "PLACEHOLDER", + "url": "https://news.nate.com/view/20260608n17852" + }, + "origin_stocks": [ + { + "ticker": "042700", + "name": "한미반도체", + "status": "up", + "reason": "한미반도체는 442억원 규모의 TC본더 신규 수주를 직접 공시한 당사자다." + } + ], + "related_stocks": [ + { + "ticker": "000660", + "name": "SK하이닉스", + "status": "up", + "relation_label": "공급망", + "relation_path": "한미반도체 → SK하이닉스", + "propagation": "*SK하이닉스*는 이번 TC본더 수주 물량의 실제 발주처로 추정되며, HBM4 양산 확대 기조가 재확인된다." + }, + { + "ticker": "005290", + "name": "동진쎄미켐", + "status": "up", + "relation_label": "기술 협력", + "relation_path": "한미반도체 → SK하이닉스 → 동진쎄미켐", + "propagation": "*동진쎄미켐*은 SK하이닉스와 기술 협력 관계에 있어, HBM4 양산 확대에 따른 공정 소재 수요 증가의 수혜가 기대된다." + } + ], + "final_summary": "이번 뉴스는 한미반도체가 HBM4용 TC본더 442억원 규모 수주를 공시한 내용으로, *한미반도체*의 실적 기여가 기대된다. 발주처로 추정되는 *SK하이닉스*와 기술 협력사 *동진쎄미켐* 등도 함께 주목받을 것으로 보인다.", + "graph": { + "newsId": 20003, + "originId": "S_HMI" + } + } +} diff --git a/src/mocks/news/data.ts b/src/mocks/news/data.ts index 7e621bc..3a4c1a6 100644 --- a/src/mocks/news/data.ts +++ b/src/mocks/news/data.ts @@ -333,424 +333,93 @@ const CURATED_NEWS: NewsRecord[] = [ ] /** - * "최신 뉴스 새로고침" 시연용 풀. 실 백엔드가 없으니 새로고침 버튼을 누를 때마다 - * pullRefreshBatch()가 여기서 몇 건을 순환해 뽑아 NEWS_RECORDS 맨 앞에 끼워 넣는다. - * 실 API가 준비되면 이 풀과 pullRefreshBatch는 통째로 지우고 새로고침을 그냥 + * "최신 뉴스 새로고침" 시연용 고정 3건 — 한미반도체(SK하이닉스·삼성전자 제외) 관련 + * 실제 최근 반도체 기사를 바탕으로 함. 이 3건의 실제 impact 응답은 + * apis/analysis/mock.ts가 id 20001~20003을 특별 취급해 + * mocks/analysis/refreshNewsExamples.json + hmiSubgraph.json으로 반환하므로, + * 아래 originStocks/relatedStocks/summary는 리스트 표시에는 안 쓰인다. + * 실 API가 준비되면 이 배열과 pullRefreshBatch는 통째로 지우고 새로고침을 그냥 * 쿼리 재조회로 대체하면 된다. */ -const REFRESH_POOL: NewsRecord[] = [ +const REFRESH_ADDITIONS: NewsRecord[] = [ { - id: 30001, - title: '유니테스트, HBM 검증 수요 급증에 테스트 장비 수주 확대', - press: '전자신문', - publishedAt: TODAY, - url: 'https://www.etnews.com/', - summary: [ - '유니테스트가 HBM 검증용 테스트 장비 수주를 확대했다고 밝혔다.', - 'HBM 검증 물량 증가가 수주 확대의 배경으로 꼽힌다.', - '하반기까지 관련 수주가 이어질 것으로 전망된다.', - ], - originStocks: [ - { - nodeId: 'unitest', - direction: 'UP', - reason: 'HBM 검증용 테스트 장비 수주 확대의 직접 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'testinsp', - direction: 'UP', - relationLabel: '테스트 장비 공급', - chain: ['unitest', 'testinsp'], - }, - { - nodeId: 'testna', - direction: 'UP', - relationLabel: '테스트 서비스', - chain: ['unitest', 'testinsp', 'testna'], - }, - { - nodeId: 'isc', - direction: 'UP', - relationLabel: '테스트 소켓 공급', - chain: ['unitest', 'testinsp', 'isc'], - }, - ], - }, - { - id: 30002, - title: '삼성전자, EUV 노광 장비 신규 발주 확정', - press: '한국경제', + id: 20001, + title: '한미반도체, 5천여평 규모 제8공장 확보…AI 반도체 장비 쇼티지 선제 대응', + press: '뉴스핌', publishedAt: TODAY, - url: 'https://www.hankyung.com/', + url: 'https://www.newspim.com/news/view/20260720000985', summary: [ - '삼성전자가 선단공정 EUV 노광 장비 신규 발주를 확정했다.', - '미세공정 전환 대응이 목적으로 파악된다.', - '계측 장비 협력사 수주도 함께 늘어날 전망이다.', + '한미반도체가 인천 본사 인근 부지를 매입해 창사 이래 최대 규모인 제8공장 건설을 추진한다고 밝혔다.', + '신규 공장은 HBM 생산에 필수적인 TC본더 등 첨단 패키징 장비 생산시설로 활용될 계획이다.', + 'AI 반도체 시장 확대에 따른 장비 공급 부족(쇼티지)에 선제 대응하려는 조치로 풀이된다.', ], originStocks: [ { - nodeId: 'ss', - direction: 'UP', - reason: 'EUV 장비 신규 발주를 직접 확정한 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'euv', - direction: 'UP', - relationLabel: '선단공정 노광 도입', - chain: ['ss', 'euv'], - }, - { - nodeId: 'park', - direction: 'UP', - relationLabel: '계측 장비 공급', - chain: ['ss', 'euv', 'park'], - }, - { - nodeId: 'fdry', - direction: 'UP', - relationLabel: '미세공정 필수', - chain: ['ss', 'euv', 'fdry'], - }, - ], - }, - { - id: 30003, - title: 'SK하이닉스, 첨단패키징 라인 증설 검토', - press: '연합뉴스', - publishedAt: TODAY, - url: 'https://www.yna.co.kr/', - summary: [ - 'SK하이닉스가 첨단패키징 라인 증설을 검토 중이라고 밝혔다.', - 'HBM 후공정 대응 능력 확대가 목적이다.', - '후공정 협력사 수주 기대감이 커지고 있다.', - ], - originStocks: [ - { - nodeId: 'sk', - direction: 'UP', - reason: '첨단패키징 라인 증설을 직접 검토하는 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'adpkg', - direction: 'UP', - relationLabel: 'HBM 패키징 공급', - chain: ['sk', 'adpkg'], - }, - { - nodeId: 'hanamc', - direction: 'UP', - relationLabel: '첨단패키징 후공정', - chain: ['sk', 'adpkg', 'hanamc'], - }, - { - nodeId: 'nepes', - direction: 'UP', - relationLabel: '첨단패키징 후공정', - chain: ['sk', 'adpkg', 'nepes'], - }, - { - nodeId: 'sfa', - direction: 'UP', - relationLabel: '첨단패키징 후공정', - chain: ['sk', 'adpkg', 'sfa'], - }, - ], - }, - { - id: 30004, - title: '웨이퍼 공정 소재 가격 상승… 한솔케미칼 수혜', - press: '머니투데이', - publishedAt: TODAY, - url: 'https://www.mt.co.kr/', - summary: [ - '웨이퍼 공정 소재 가격이 오르며 관련주가 강세다.', - '수급 타이트로 소재 업체 협상력이 개선되고 있다.', - '증설 투자로 이어질지 시장이 주목하고 있다.', - ], - originStocks: [ - { - nodeId: 'hansolchem', - direction: 'UP', - reason: '웨이퍼 공정 소재 가격 상승의 직접 수혜 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'wafermat', - direction: 'UP', - relationLabel: '웨이퍼 공정 소재 공급', - chain: ['hansolchem', 'wafermat'], - }, - { - nodeId: 'ss', - direction: 'UP', - relationLabel: '공정 소재 조달', - chain: ['hansolchem', 'wafermat', 'ss'], - }, - { - nodeId: 'enf', + nodeId: 'hanmi', direction: 'UP', - relationLabel: '포토레지스트 공급', - chain: ['hansolchem', 'wafermat', 'enf'], + reason: '제8공장 확보로 TC본더 생산능력이 직접 확대되는 당사자', }, ], + relatedStocks: [], }, { - id: 30005, - title: '텔레칩스, 온디바이스 AI 확산에 차량용 구동칩 수주 확대', + id: 20002, + title: '한미반도체, 와이드 TC본더 앞세워 HBM4용 신규 수주 경쟁력 강화', press: '이데일리', publishedAt: TODAY, - url: 'https://www.edaily.co.kr/', + url: 'https://v.daum.net/v/20260702084432066', summary: [ - '텔레칩스가 차량용 온디바이스 AI 구동칩 수주를 확대했다.', - '온디바이스 AI 탑재 차량이 늘어난 영향으로 풀이된다.', - 'AI 반도체 팹리스 전반으로 온기가 확산되는 모습이다.', + "한미반도체가 '와이드 TC본더'를 앞세워 HBM4용 신규 수주 경쟁력을 강화하고 있다고 밝혔다.", + 'TC본더 적용처가 확장되며 신규 수주 기회가 늘어날 것으로 전망된다.', + 'HBM TC본더 시장 점유율 71%를 바탕으로 후속 수주 논의도 이어지고 있다.', ], originStocks: [ { - nodeId: 'telechips', - direction: 'UP', - reason: '차량용 온디바이스 AI 구동칩 수주 확대의 직접 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'ondevice', - direction: 'UP', - relationLabel: '차량용 온디바이스 AI 칩 개발', - chain: ['telechips', 'ondevice'], - }, - { - nodeId: 'anapass', - direction: 'UP', - relationLabel: '온디바이스 AI 구동칩 개발', - chain: ['telechips', 'ondevice', 'anapass'], - }, - { - nodeId: 'abov', - direction: 'UP', - relationLabel: 'MCU 공급', - chain: ['telechips', 'ondevice', 'abov'], - }, - ], - }, - { - id: 30006, - title: '심텍, 유리기판 상용화 임박에 시제품 검증 마무리', - press: '파이낸셜뉴스', - publishedAt: TODAY, - url: 'https://www.fnnews.com/', - summary: [ - '심텍이 차세대 패키징용 유리기판 시제품 검증을 마무리했다.', - '상용화 일정이 당초 계획보다 앞당겨질 전망이다.', - 'AI 반도체 패키징 수요와 맞물려 주목받고 있다.', - ], - originStocks: [ - { - nodeId: 'simmtech', - direction: 'UP', - reason: '유리기판 시제품 검증 마무리의 직접 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'glass', - direction: 'UP', - relationLabel: '유리기판 개발', - chain: ['simmtech', 'glass'], - }, - { - nodeId: 'daeduck', - direction: 'UP', - relationLabel: '유리기판 개발', - chain: ['simmtech', 'glass', 'daeduck'], - }, - { - nodeId: 'aidc', - direction: 'UP', - relationLabel: '차세대 패키징 수요', - chain: ['simmtech', 'glass', 'aidc'], - }, - ], - }, - { - id: 30007, - title: 'SK하이닉스, CXL 표준 확산에 차세대 메모리 개발 박차', - press: '서울경제', - publishedAt: TODAY, - url: 'https://www.sedaily.com/', - summary: [ - 'SK하이닉스가 차세대 메모리 인터페이스 CXL 제품 개발에 속도를 내고 있다.', - '데이터센터향 메모리 확장 수요 대응이 목적이다.', - '관련 생태계 투자도 함께 확대될 전망이다.', - ], - originStocks: [ - { - nodeId: 'sk', - direction: 'UP', - reason: 'CXL 차세대 메모리 개발을 직접 주도하는 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'cxl', - direction: 'UP', - relationLabel: '차세대 메모리 인터페이스 개발', - chain: ['sk', 'cxl'], - }, - { - nodeId: 'ss', - direction: 'UP', - relationLabel: '차세대 메모리 인터페이스 개발', - chain: ['sk', 'cxl', 'ss'], - }, - { - nodeId: 'dram', - direction: 'UP', - relationLabel: '메모리 확장 표준', - chain: ['sk', 'cxl', 'dram'], - }, - ], - }, - { - id: 30008, - title: '후성, 특수가스 공급 부족 심화에 반사이익', - press: '뉴스1', - publishedAt: TODAY, - url: 'https://www.news1.kr/', - summary: [ - '후성이 반도체 공정용 특수가스 공급 부족의 반사이익을 누리고 있다.', - '증착·파운드리 공정 전반의 수요 확대가 배경이다.', - '가격 협상력도 함께 개선되는 모습이다.', - ], - originStocks: [ - { - nodeId: 'foosung', - direction: 'UP', - reason: '특수가스 공급 부족 심화의 직접 반사이익 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'specgas', - direction: 'UP', - relationLabel: '특수가스 공급', - chain: ['foosung', 'specgas'], - }, - { - nodeId: 'sk', - direction: 'UP', - relationLabel: '증착 공정 필수 가스', - chain: ['foosung', 'specgas', 'sk'], - }, - { - nodeId: 'fdry', - direction: 'UP', - relationLabel: '파운드리 공정 필수', - chain: ['foosung', 'specgas', 'fdry'], - }, - ], - }, - { - id: 30009, - title: '피에스케이, 대형 식각장비 수주 공시', - press: '아시아경제', - publishedAt: TODAY, - url: 'https://www.asiae.co.kr/', - summary: [ - '피에스케이가 대형 식각장비 공급 계약을 공시했다.', - '고객사 라인 증설에 대응하는 수주로 파악된다.', - '경쟁사 대비 수주 모멘텀이 부각되고 있다.', - ], - originStocks: [ - { - nodeId: 'psk', - direction: 'UP', - reason: '대형 식각장비 수주 공시의 직접 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'sk', + nodeId: 'hanmi', direction: 'UP', - relationLabel: '식각 장비 공급', - chain: ['psk', 'sk'], - }, - { - nodeId: 'jusung', - direction: 'DOWN', - relationLabel: '경쟁', - chain: ['psk', 'jusung'], + reason: 'TC본더 적용처 확장으로 신규 수주 경쟁력이 직접 강화되는 당사자', }, ], + relatedStocks: [], }, { - id: 30010, - title: '코미코, 반도체 부품 세정 수주 확대', - press: '매일경제', + id: 20003, + title: "한미반도체, HBM4 'TC본더 4.5 그리핀' 442억원 수주 공시", + press: '한국경제', publishedAt: TODAY, - url: 'https://www.mk.co.kr/', + url: 'https://news.nate.com/view/20260608n17852', summary: [ - '코미코의 반도체 부품 세정·코팅 수주가 확대됐다.', - '고객사 가동률 상승이 배경으로 꼽힌다.', - '경쟁사 대비 점유율 확대가 기대된다.', + "한미반도체가 HBM4 제조용 'TC본더 4.5 그리핀' 장비 수주를 공시했다.", + '이번 수주 규모는 442억원으로, 매출액 대비 약 7.66%에 해당한다.', + 'HBM4 양산 확대에 따른 후속 발주가 이어질지 시장의 관심이 모인다.', ], originStocks: [ { - nodeId: 'comico', - direction: 'UP', - reason: '부품 세정 코팅 수주 확대의 직접 당사자', - }, - ], - relatedStocks: [ - { - nodeId: 'sk', + nodeId: 'hanmi', direction: 'UP', - relationLabel: '부품 세정 코팅 공급', - chain: ['comico', 'sk'], - }, - { - nodeId: 'tck', - direction: 'DOWN', - relationLabel: '경쟁', - chain: ['comico', 'tck'], + reason: '442억원 규모의 TC본더 신규 수주를 직접 공시한 당사자', }, ], + relatedStocks: [], }, ] -const REFRESH_BATCH_SIZES = [3, 2, 2, 3] -let refreshClickCount = 0 -let refreshPoolCursor = 0 +let refreshAlreadyPulled = false /** - * "새로고침" 클릭 데모용 — REFRESH_POOL을 순환하며 몇 건을 뽑아 발행 시각을 - * 지금으로 찍고 NEWS_RECORDS 맨 앞에 끼워 넣는다. 이미 나온 뉴스라도 풀을 - * 다 돌면 새 id로 다시 등장한다(데모를 몇 번이고 반복할 수 있도록). + * "새로고침" 클릭 데모용 — REFRESH_ADDITIONS 3건을 발행 시각만 지금으로 찍어 + * NEWS_RECORDS 맨 앞에 한 번만 끼워 넣는다. 이미 넣었으면 빈 배열을 반환해 + * "새 뉴스가 없습니다" 상태를 보여준다. */ export function pullRefreshBatch(): NewsRecord[] { - const batchSize = - REFRESH_BATCH_SIZES[refreshClickCount % REFRESH_BATCH_SIZES.length] - refreshClickCount += 1 + if (refreshAlreadyPulled) return [] + refreshAlreadyPulled = true const now = Date.now() - const items: NewsRecord[] = [] - for (let i = 0; i < batchSize; i++) { - const template = REFRESH_POOL[refreshPoolCursor % REFRESH_POOL.length] - const cycle = Math.floor(refreshPoolCursor / REFRESH_POOL.length) - refreshPoolCursor += 1 - items.push({ - ...template, - id: template.id + cycle * 10000, - publishedAt: new Date(now - i * 45_000).toISOString(), - }) - } + const items = REFRESH_ADDITIONS.map((template, i) => ({ + ...template, + publishedAt: new Date(now - i * 45_000).toISOString(), + })) NEWS_RECORDS.unshift(...items) return items