diff --git a/README.md b/README.md
index 184aa75..f6f60bb 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,14 @@
![Javascript](https://img.shields.io/badge/javascript-ES6+-yellow?logo=javascript)
![NodeJS](https://img.shields.io/badge/node.js-v18-green?logo=node.js)
-![데모](https://user-images.githubusercontent.com/25934842/207359316-f7056911-d26a-4671-bc3c-2a80e46f24b8.gif)
-### 서비스 링크 : https://paperef.com
+### 서비스 배포
+
+- Dev 서버 : http://49.50.172.204:3000/
+
+- Production 서버 : http://101.101.217.49:3000/
### 팀원
@@ -36,6 +39,7 @@
최예윤
+ |
### 개발 환경 세팅
@@ -80,7 +84,6 @@ ELASTIC_USER=
ELASTIC_PASSWORD=
ALLOW_UPDATE=
MAIL_TO=
-SHOULD_RUN_BATCH=
```
## 기술스택
@@ -97,7 +100,7 @@ SHOULD_RUN_BATCH=
- 키워드 자동완성 검색 서비스 제공
- 키워드 검색 서비스 제공
- 논문 DOI를 통한 인용관계 시각화 서비스 제공
-- 사용자는 키워드 검색시 PRV 데이터베이스에 있는 정보 혹은 Crossref API를 통해 요청한 정보를 조회할 수 있으며, 데이터베이스에 없는 논문에 대한 데이터 수집은 Request batch에 의해 처리되므로 검색 결과를 즉시 받아보지 못할 수 있습니다.
+- 사용자는 키워드 검색시 PRV 데이터베이스에 있는 정보만 조회할 수 있으며, 데이터베이스에 없는 논문에 대한 데이터 수집은 Request batch에 의해 처리되므로 검색 결과를 즉시 받아보지 못할 수 있습니다.
- Request batch에 의해 수집된 결과는 데이터베이스에 저장됩니다.
- 추가 문의사항은 viewpoint.prv@gmail.com 로 연락바랍니다.
diff --git a/backend/src/ranking/ranking.controller.ts b/backend/src/ranking/ranking.controller.ts
index 816155d..16d7d3f 100644
--- a/backend/src/ranking/ranking.controller.ts
+++ b/backend/src/ranking/ranking.controller.ts
@@ -12,4 +12,8 @@ export class RankingController {
async getTen() {
return await this.rankingService.getTen();
}
+ @Get('/insert')
+ async insertCache(@Query('keyword') searchStr: string) {
+ return this.rankingService.insertRedis(searchStr);
+ }
}
diff --git a/backend/src/ranking/tests/ranking.controller.spec.ts b/backend/src/ranking/tests/ranking.controller.spec.ts
index a329a4d..ee0fcc5 100644
--- a/backend/src/ranking/tests/ranking.controller.spec.ts
+++ b/backend/src/ranking/tests/ranking.controller.spec.ts
@@ -21,23 +21,23 @@ describe('RankingServiceTest', () => {
describe('/keyword-ranking', () => {
it('redis date가 10개 이하인 경우', async () => {
//Case 1. redis date가 10개 이하인 경우
- const topTen = await service.getTen();
+ const topTen = await controller.getTen();
expect(topTen.length).toBeLessThanOrEqual(10);
});
it('데이터 삽입 후 topTen 체크', async () => {
//Case 2. 데이터 삽입 후 topTen 체크
- const flag = await service.insertRedis('9번째 데이터');
+ const flag = await controller.insertCache('9번째 데이터');
expect(flag).toBe('new');
const topTen = await controller.getTen();
expect(topTen.length).toBe(9);
- const flag2 = await service.insertRedis('10번째 데이터');
+ const flag2 = await controller.insertCache('10번째 데이터');
expect(flag2).toBe('new');
const topTen2 = await controller.getTen();
expect(topTen2.length).toBe(10);
});
it('2위인 "사랑해요" 데이터가 한번 더 검색시 1위로 업데이트', async () => {
//Case 3. 2위인 "사랑해요" 데이터가 한번 더 검색시 1위로 업데이트
- const flag = await service.insertRedis('사랑해요');
+ const flag = await controller.insertCache('사랑해요');
expect(flag).toBe('update');
const topTen = await controller.getTen();
expect(topTen[0].keyword).toBe('부스트캠프');
@@ -46,23 +46,23 @@ describe('RankingServiceTest', () => {
describe('/keyword-ranking/insert', () => {
// Case1. 기존 redis에 없던 데이터 삽입
it('기존 redis에 없던 데이터 삽입', async () => {
- const result = await service.insertRedis('newData');
+ const result = await controller.insertCache('newData');
expect(result).toBe('new');
});
// Case2. 기존 redis에 있던 데이터 삽입
it('기존 redis에 있던 데이터 삽입', async () => {
- const result = await service.insertRedis('부스트캠프');
+ const result = await controller.insertCache('부스트캠프');
expect(result).toBe('update');
});
//Case3. redis에 빈 검색어 입력
it('빈 검색어 redis에 삽입', async () => {
- await expect(service.insertRedis('')).rejects.toEqual(
+ await expect(controller.insertCache('')).rejects.toEqual(
new BadRequestException({ status: 400, error: 'bad request' }),
);
});
//Case4. insert 실패시 타임 아웃 TimeOut
it('insert 실패시 타임 아웃 TimeOut', async () => {
- await expect(service.insertRedis('')).rejects.toEqual(
+ await expect(controller.insertCache('')).rejects.toEqual(
new BadRequestException({ status: 400, error: 'bad request' }),
);
});
diff --git a/backend/src/search/search.controller.ts b/backend/src/search/search.controller.ts
index 5d6ab75..2a2cf85 100644
--- a/backend/src/search/search.controller.ts
+++ b/backend/src/search/search.controller.ts
@@ -46,18 +46,6 @@ export class SearchController {
const keywordHasSet = await this.batchService.setKeyword(keyword);
if (keywordHasSet) this.batchService.searchBatcher.pushToQueue(0, 0, -1, true, keyword);
- // Elasticsearch 검색 결과가 없을 경우, Crossref 검색
- if (totalItems === 0) {
- const { items: papers, totalItems } = await this.searchService.getPapersFromCrossref(keyword, rows, page);
- return {
- papers,
- pageInfo: {
- totalItems,
- totalPages: Math.ceil(totalItems / rows),
- },
- };
- }
-
const papers = data.hits.hits.map((paper) => new PaperInfoExtended(paper._source));
return {
papers,
diff --git a/backend/src/search/search.service.ts b/backend/src/search/search.service.ts
index b2e2b9a..007c227 100644
--- a/backend/src/search/search.service.ts
+++ b/backend/src/search/search.service.ts
@@ -1,16 +1,15 @@
-import { Injectable, NotFoundException, RequestTimeoutException } from '@nestjs/common';
+import { Injectable, NotFoundException } from '@nestjs/common';
import {
CrossRefItem,
PaperInfoExtended,
PaperInfo,
PaperInfoDetail,
CrossRefPaperResponse,
- CrossRefResponse,
} from './entities/crossRef.entity';
import { ElasticsearchService } from '@nestjs/elasticsearch';
import { MgetOperation, SearchHit } from '@elastic/elasticsearch/lib/api/types';
import { HttpService } from '@nestjs/axios';
-import { CROSSREF_API_PAPER_URL, CROSSREF_API_URL } from '../util';
+import { CROSSREF_API_PAPER_URL } from '../util';
import { ELASTIC_INDEX } from 'src/envLayer';
@Injectable()
@@ -76,17 +75,6 @@ export class SearchService {
return new PaperInfoDetail(data);
};
- async getPapersFromCrossref(keyword: string, rows: number, page: number, selects?: string[]) {
- const crossRefdata = await this.httpService.axiosRef
- .get(CROSSREF_API_URL(keyword, rows, page, selects))
- .catch((err) => {
- throw new RequestTimeoutException(err.message);
- });
- const items = crossRefdata.data.message.items.map((item) => this.parsePaperInfoExtended(item));
- const totalItems = crossRefdata.data.message['total-results'];
- return { items, totalItems };
- }
-
async getPaperFromCrossref(doi: string) {
try {
const item = await this.httpService.axiosRef.get(CROSSREF_API_PAPER_URL(doi));
diff --git a/backend/src/util.ts b/backend/src/util.ts
index d4a99fe..18ac270 100644
--- a/backend/src/util.ts
+++ b/backend/src/util.ts
@@ -1,12 +1,7 @@
import { MAIL_TO } from './envLayer';
const BASE_URL = 'https://api.crossref.org/works';
-export const CROSSREF_API_URL = (
- keyword: string,
- rows = 5,
- page = 1,
- selects: string[] = ['title', 'author', 'created', 'is-referenced-by-count', 'references-count', 'DOI'],
-) =>
+export const CROSSREF_API_URL = (keyword: string, rows = 5, page = 1, selects: string[] = ['author', 'title', 'DOI']) =>
`${BASE_URL}?query=${keyword}&rows=${rows}&select=${selects.join(',')}&offset=${rows * (page - 1)}&mailto=${MAIL_TO}`;
export const MAX_ROWS = 1000;
diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx
index 118b2f7..3e5c5a9 100644
--- a/frontend/src/App.test.tsx
+++ b/frontend/src/App.test.tsx
@@ -23,5 +23,5 @@ it('Footer 렌더링 테스트', () => {
);
});
const span = container?.querySelector('span');
- expect(span?.textContent).toBe('문의사항, 버그제보: viewpoint.prv@gmail.com');
+ expect(span?.textContent).toBe('문의사항, 버그제보: vp.prv@gmail.com');
});
diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx
index 90d3733..72e20f6 100644
--- a/frontend/src/components/Footer.tsx
+++ b/frontend/src/components/Footer.tsx
@@ -9,7 +9,7 @@ interface FooterProps {
const Footer = ({ bgColor, contentColor }: FooterProps) => {
return (
- 문의사항, 버그제보: viewpoint.prv@gmail.com
+ 문의사항, 버그제보: vp.prv@gmail.com
{
};
const Button = styled.button`
- display: flex;
- align-items: center;
background-color: transparent;
cursor: pointer;
`;
diff --git a/frontend/src/components/search/AutoCompletedList.tsx b/frontend/src/components/search/AutoCompletedList.tsx
index 02a4cbb..e9fb8d3 100644
--- a/frontend/src/components/search/AutoCompletedList.tsx
+++ b/frontend/src/components/search/AutoCompletedList.tsx
@@ -79,7 +79,6 @@ const AutoCompleted = styled.li<{ hovered: boolean }>`
const Title = styled.div`
${({ theme }) => theme.TYPO.body1}
- line-height: 1.1em;
`;
const Author = styled.div`
diff --git a/frontend/src/components/search/RecentKeywordsList.tsx b/frontend/src/components/search/RecentKeywordsList.tsx
index f981934..2b83dc3 100644
--- a/frontend/src/components/search/RecentKeywordsList.tsx
+++ b/frontend/src/components/search/RecentKeywordsList.tsx
@@ -1,6 +1,5 @@
import { IconButton } from '@/components';
import { ClockIcon, XIcon } from '@/icons';
-import { Ellipsis } from '@/style/styleUtils';
import { setLocalStorage } from '@/utils/storage';
import { isEmpty } from 'lodash-es';
import { Dispatch, SetStateAction, useEffect } from 'react';
@@ -43,7 +42,7 @@ const RecentKeywordsList = ({
onMouseDown={() => handleMouseDown(keyword)}
>
- {keyword}
+ {keyword}
}
onMouseDown={(e) => handleRecentKeywordRemove(e, keyword)}
@@ -72,11 +71,6 @@ const Keyword = styled.li<{ hovered: boolean }>`
background-color: ${({ theme, hovered }) => (hovered ? theme.COLOR.gray1 : 'auto')};
`;
-const KeywordText = styled(Ellipsis)`
- width: 100%;
- display: block;
-`;
-
const NoResult = styled.div`
padding-top: 25px;
text-align: center;
diff --git a/frontend/src/hooks/graph/useGraphData.ts b/frontend/src/hooks/graph/useGraphData.ts
index 5e7de6b..a2967ad 100644
--- a/frontend/src/hooks/graph/useGraphData.ts
+++ b/frontend/src/hooks/graph/useGraphData.ts
@@ -1,10 +1,9 @@
import { IPaperDetail } from '@/api/api';
-import { Link, Node } from '@/pages/PaperDetail/components/ReferenceGraph';
import { useEffect, useRef, useState } from 'react';
-export default function useGraphData(data: IPaperDetail) {
- const [links, setLinks] = useState([]);
- const nodes = useRef([]);
+export default function useGraphData(data: IPaperDetail) {
+ const [links, setLinks] = useState([]);
+ const nodes = useRef([]);
const doiMap = useRef