Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
366c933
chore: 초기설정
Leeseunghwan7305 May 5, 2024
a8c649c
design: 공용 컴포넌트 Nav바 퍼블리싱
Leeseunghwan7305 May 6, 2024
33c83c4
design: 검색 바 퍼블리싱
Leeseunghwan7305 May 6, 2024
f20f4e9
design: 검색 결과없음 컴포넌트 퍼블리싱
Leeseunghwan7305 May 6, 2024
498d193
design: 메인페이지 검색창 배너 레이아웃
Leeseunghwan7305 May 6, 2024
46c55ba
feat: 추천 검색어 api 연동
Leeseunghwan7305 May 7, 2024
748a623
feat: 최근 검색어 기능 추가
Leeseunghwan7305 May 8, 2024
f72d4d5
feat: 현재 검색어 bold 처리
Leeseunghwan7305 May 8, 2024
d67acf6
design: 검색 리스트 퍼블리싱
Leeseunghwan7305 May 8, 2024
da95d13
refactor: localStorage에 저장한 최근검색어 최신순으로 정렬되게 변경
Leeseunghwan7305 May 8, 2024
c36960b
design: 모달창 퍼블리싱
Leeseunghwan7305 May 8, 2024
3dc6e29
fix: 검색어가 없을 때는 검색을 못하게 수정
Leeseunghwan7305 May 8, 2024
012a24c
feat: 즐겨찾기 기능 추가
Leeseunghwan7305 May 9, 2024
5c5e8d2
fix: 클러져로 인한 index 오류 해결
Leeseunghwan7305 May 9, 2024
a0b480c
feat: intersection observer을 활용해 결과 리스트를 무한스크롤로 구현
Leeseunghwan7305 May 10, 2024
9b6769b
feat: 결과 리스트를 누르면 해당 사이트로 이동
Leeseunghwan7305 May 10, 2024
5e02ef2
chore: 스토리북 초기 설정
Leeseunghwan7305 May 10, 2024
c6bb7d8
feat: react-query 캐시추가
Leeseunghwan7305 May 10, 2024
e824444
refactor: 결과 리스트 API 바인딩
Leeseunghwan7305 May 10, 2024
94d68e8
style: 리드미 수정
Leeseunghwan7305 May 10, 2024
8f0df5f
Merge pull request #1 from Leeseunghwan7305/tmdghks7305
Leeseunghwan7305 May 10, 2024
9abdc2c
style: 안쓰는 코드 삭제
Leeseunghwan7305 May 10, 2024
25159e4
Merge pull request #2 from Leeseunghwan7305/tmdghks7305
Leeseunghwan7305 May 10, 2024
b5abcf6
Update README.md
Leeseunghwan7305 May 10, 2024
70af90c
Update README.md
Leeseunghwan7305 May 10, 2024
65e8149
Update README.md
Leeseunghwan7305 May 10, 2024
402a9da
feat: [Storybook] SearchBar components add interaction test
Leeseunghwan7305 May 12, 2024
7f894f5
feat: 검색어 없음 컴포넌트 스토리북에 추가
Leeseunghwan7305 May 13, 2024
c5096d9
feat: 결과 리스트 스토리북에 추가
Leeseunghwan7305 May 13, 2024
610f0b2
feat: Nav바 스토리북 추가
Leeseunghwan7305 May 13, 2024
426d23d
feat: 스토리북 배포
Leeseunghwan7305 May 13, 2024
17e7cf7
feat: 스토리북 cd 적용
Leeseunghwan7305 May 13, 2024
f7679d8
Merge branch 'main' into tmdghks7305
Leeseunghwan7305 May 13, 2024
97fc49b
Merge pull request #3 from Leeseunghwan7305/tmdghks7305
Leeseunghwan7305 May 13, 2024
27031e5
feat: dotenv 추가
Leeseunghwan7305 May 13, 2024
a6af953
Merge branch 'main' of https://github.com/Leeseunghwan7305/Infinite_C…
Leeseunghwan7305 May 13, 2024
e5780b1
fix: 스토리북 CD 명령어 수정
Leeseunghwan7305 May 13, 2024
558a1e2
Update README.md
Leeseunghwan7305 May 13, 2024
eeaa602
Update README.md
Leeseunghwan7305 May 13, 2024
3f31c76
refactor: 즐겨찾기 기능 -> render props 패턴 적용
Leeseunghwan7305 May 14, 2024
9370416
fix: 스토리북 빌드 오류 해결
Leeseunghwan7305 May 14, 2024
a2164ed
fix: 스토리북 빌드 오류 해결
Leeseunghwan7305 May 14, 2024
46ccdac
fix: 스토리북 빌드 오류 해결
Leeseunghwan7305 May 14, 2024
a815aba
feat: Resultlist 스토리북 변경
Leeseunghwan7305 May 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
24 changes: 24 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Workflow name
name: "Chromatic Deployment"

# Event for the workflow
on: push

# List of jobs
jobs:
test:
# Operating System
runs-on: ubuntu-latest
# Job steps
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- run: npm install
#👇 Adds Chromatic as a step in the workflow
- uses: chromaui/action@v1
# Options required for Chromatic's GitHub Action
with:
#👇 Chromatic projectToken, see https://storybook.js.org/tutorials/intro-to-storybook/react/en/deploy/ to obtain it
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.env
*storybook.log

*.local
20 changes: 20 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { StorybookConfig } from "@storybook/react-vite";

const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-onboarding",
"@storybook/addon-links",
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-vite",
options: {},
},
docs: {
autodocs: "tag",
},
};
export default config;
31 changes: 31 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Preview } from "@storybook/react";
import React from "react";
import { MemoryRouter } from "react-router-dom";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import GlobalStyle from "../src/styles/GlobalStyles";

const queryClient = new QueryClient();

export const decorators = [
(Story) => (
<MemoryRouter>
<QueryClientProvider client={queryClient}>
<GlobalStyle />
<Story />
</QueryClientProvider>
</MemoryRouter>
),
];

const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};

export default preview;
8 changes: 8 additions & 0 deletions .vite/deps/_metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"hash": "e7e3236d",
"configHash": "a60e52a9",
"lockfileHash": "e3b0c442",
"browserHash": "2938a4c8",
"optimized": {},
"chunks": {}
}
3 changes: 3 additions & 0 deletions .vite/deps/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
247 changes: 85 additions & 162 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,171 +1,94 @@
# 기능 구현 챌린지 - 프론트엔드
## 테커 프론트엔드 기능 구현 챌린지

### 챌린지 기간

2024년 5월 3일 ~ 2024년 5월 19일

### 구현 목표

- [한국임상정보](https://clinicaltrialskorea.com/) 페이지의 검색영역을 클론하기

### 참고자료

- 과제 요구 UI [[피그마 링크]](https://www.figma.com/file/2I7T132M48F6FbhJHwIB0r/Techeer-Infinite-Challenge-FE?type=design&node-id=0-1&mode=design&t=5ogNibInEDjJplQd-0)

- API
- [https://api.clinicaltrialskorea.com/api/v1/search-conditions/?name={검색어}](https://api.clinicaltrialskorea.com/api/v1/search-conditions/?name={검색어)
- 예제) 검색어에 ‘갑상선’을 넣었을 경우
```tsx
[
{
name: "갑상선암",
id: 4373,
},
{
name: "갑상선염",
id: 4376,
},
{
name: "갑상선중독증",
id: 4378,
},
{
name: "갑상선 중독",
id: 4381,
},
{
name: "갑상선암종",
id: 4375,
},
{
name: "갑상선염증",
id: 4377,
},
...
];
```
- https://api.clinicaltrialskorea.com/api/v1/studies/?offset=0&limit=10&conditions={검색어}
- 예제) 검색어에 ‘갑상선’을 넣었을 경우
```jsx
{
"count": 8,
"next": null,
"previous": null,
"sponsored_studies": [],
"results": [
{
"from_type": 1,
"url": "https://api.clinicaltrialskorea.com/api/v1/studies/29262/",
"id": 29262,
"ct_id": "201900132",
"locations": [],
"phases": [
"3상"
],
"minimum_age_display": "18세",
"maximum_age_display": null,
"title": "이전 VEGFR 표적 요법 후 진행한 방사성요오드 치료저항성 분화 갑상선암 시험대상자에서 카보잔티닙(XL184)에 대한 제3상, 무작위배정, 이중 눈가림, 위약 대조 시험",
"start_date": "2019-01-01",
"completion_date": "2022-08-01",
"lead_sponsor_name": "파머수티컬리서치어소시에이츠코리아",
"brief_summary": "본 시험의 목적은 이전 VEGFR 표적 요법 후 진행한 RAI 저항성 DTC 시험대상자에서 위약과 비교하여 카보잔티닙이 PFS 및 ORR에 미치는 영향을 평가하는 것이다.",
"gender": "남녀모두",
"is_sponsor": false,
"survey_id": null,
"is_new": false,
"created_at": "2021-10-26T19:18:06.531105"
},
{
"from_type": 1,
"url": "https://api.clinicaltrialskorea.com/api/v1/studies/27142/",
"id": 27142,
"ct_id": "202100156",
"locations": [
{
"city": "서울"
}
],
"phases": [
"연구자 임상시험"
],
"minimum_age_display": "18세",
"maximum_age_display": "65세",
"title": "갑상선 전절제술을 시행받는 환자에서 수술 전 비타민 D(디맥정 30,000 IU) 경구 투여의 수술 후 저칼슘혈증 예방 효용성 연구",
"start_date": "2020-12-01",
"completion_date": "2022-12-01",
"lead_sponsor_name": "서울대학교병원",
"brief_summary": "수술 전 비타민 D3(cholecalciferol) 경구 복용의 수술 후 저칼슘혈증 예방효과를 증명하고자 하는 연구자 임상시험이다.",
"gender": "남녀모두",
"is_sponsor": false,
"survey_id": null,
"is_new": false,
"created_at": "2022-05-12T13:47:12.640427"
},
...
]
}
```
한국임상정보 페이지의 검색영역을 클론하기

### 피그마

과제 요구 UI
[피그마](https://www.figma.com/design/2I7T132M48F6FbhJHwIB0r/Techeer-Infinite-Challenge-FE?node-id=0-1&t=jbsiLdOPrqVuuMqO-0)

### 스토리북
[스토리북](https://6641c1126df24c244d462699-hgqohmnawo.chromatic.com/?path=/story/components-searchbar--basic)

### 기술스택

- React
- Vite
- Typescript
- Tanstack-query v5
- Axios
- React Router Dom
- Styled-components
- StoryBook

### 필수 구현 사항

- 질환 명 검색시 API를 호출하여 드롭박스를 통해 추천 검색어를 보여주는 기능을 구현합니다.
- 검색어가 없을 시 “검색어 없음” 표출
- 최근에 검색어를 보여줍니다.
- 검색어를 검색 시 결과를 리스트로 보여줍니다.
- 검색어가 없을 시 화면도 구현
- 검색 결과 컴포넌트를 클릭 시 `https://clinicaltrialskorea.com/studies/{검색어ID}` 링크로 이동
- 입력마다 API 호출하지 않도록 API 호출 횟수를 줄이는 전략 수립 및 실행
- README에 전략에 대한 설명 기술
- API를 호출할 때 마다 `console.info("calling api")` 출력을 통해 콘솔창에서 API 호출 횟수 확인이 가능하도록 설정
- 과제를 수행하면서 진행하셨던 고민, 원래 구현하고자 했던 설계의 방향성 등을 Pull Request Body(PR Comment)에 적어서 제출해 주시면 감안하여 과제 검토를 진행할 예정이에요.
- [x] 질환 명 검색시 API를 호출하여 드롭박스를 통해 추천 검색어를 보여주는 기능을 구현합니다.
- [x] 검색어가 없을 시 “검색어 없음” 표출
- [x] 최근에 검색어를 보여줍니다.
- [x] 검색어를 검색 시 결과를 리스트로 보여줍니다.
- [x] 검색어가 없을 시 화면도 구현
- [x] 검색 결과 컴포넌트를 클릭 시 https://clinicaltrialskorea.com/studies/{검색어ID} 링크로 이동
- [x] 입력마다 API 호출하지 않도록 API 호출 횟수를 줄이는 전략 수립 및 실행
- [x] API를 호출할 때 마다 console.info("calling api") 출력을 통해 콘솔창에서 API호출 횟수 확인이 가능하도록 설정

### 선택 구현 사항

- **[선택 사항 1]** 키보드만으로 추천 검색어들로 이동 가능하도록 구현합니다. (+2점)
- ex) 키보드 방향키, 탭을 사용하여 다음 추천 검색어로 이동
- **[선택 사항 2]** React-Query를 활용하여 캐싱을 구현합니다. (+2점)
- **[선택 사항 3]** 검색어 결과는 페이지네이션 또는 무한스크롤 선택합니다. (+1점)
- 외부 라이브러리 없이 구현할 경우 추가 점수 (+2점)
- **[선택 사항 4]** 스크랩 저장 기능을 구현합니다. (+3점)
- 페이지를 새로고침 해도 리스트가 남아있도록 구현.
- 즐겨찾기 페이지에서 스크랩한 결과물 리스트 보여주도록 구현.
- 확인 모달을 통해 스크랩을 삭제.
- **[선택 사항 5]** 뷰포트 크기에 따른 반응형 UI를 구현합니다. (+2점)
- **[선택 사항 6]** Storybook을 사용하여 UI 인터렉션 테스팅을 구현합니다. (+2점)

### 프로그래밍 요구사항

- `useEffect`의 사용을 최소화하고, 사용 시 의존성을 명확히 정의한다.
- 모든 상수는 컴포넌트 외부에서 선언하여 관리한다.
- `if` 조건문을 활용해 값을 바로 반환함으로써 `else`를 사용하지 않도록 한다. 경우에 따라 `if/else` 또는 `switch` 문을 사용할 필요가 있을 때는 그 사용이 적절한지 고민한다.
- 들여쓰기(depth)는 최대 2단계까지만 허용한다. 이를 위해 함수나 메서드를 분리하는 방법을 고려한다.
- 모든 숫자 및 문자 리터럴은 명확한 이름을 가진 상수로 정의하여 사용한다.
- UI와 데이터 처리 로직을 명확히 분리하여 관리한다.
- 재사용 가능하도록 컴포넌트를 설계하고 구현한다.
- Styled-components 관련 코드는 각 컴포넌트의 하단에 위치시킨다.
- 컴포넌트와 함수의 이름은 그 목적이나 기능을 분명하게 반영할 수 있도록 명명한다.

> **제한된 시간 안에 과제를 완성하는 것은 많은 집중력이 필요해서 평소만큼 실력 발휘를 못 하셨을 수 있음을 충분히 이해하고 있어요.** 🙇🏻‍♀️

### 개발 조건 및 환경

- 언어
- JavaScript
- TypeScript (+1점)
- 프레임워크
- React
- Next.js
- 사용가능한 기술
- 상태 관리 라이브러리 (Redux, Zustand, Jotai 등)
- 스타일 관련 라이브러리 (styled-components, emotion, UI kit, tailwind, antd 등)
- HTTP Client (axios 등)
- 이외 과제 구현에 필요한 외부 라이브러리
## 📈 진행 요구사항
- 미션은 [Infinite_Challenge_FE](https://github.com/techeer-sv/Infinite_Challenge_FE) 를 fork/clone해 시작한다.
- 기능을 구현하기 전에 Infinite_Challenge_FE 레포지토리 하위에 README.md 파일을 생성해 구현할 기능 목록을 정리해 추가한다.
- git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다.
- [AngularJS Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 참고해 commit log를 남긴다.
- 아래 절차를 따라 미션을 제출한다.
- 본 repository를 개인 repository로 fork한다.
- fork한 저장소를 자신의 컴퓨터로 clone한다.
- 기능 구현을 위한 브랜치를 생성한다. 브랜치 이름은 본인의 github ID를 이용한다.
- 생성한 브랜치에서 기능을 구현한다.
- 최상위 디렉토리에 바로 소스코드가 보이도록 해주세요, 불필요한 depth가 존재하면 안됩니다.
- 기능 구현이 종료되면, [Infinite_Challenge_FE](https://github.com/techeer-sv/Infinite_Challenge_FE)로 Pull Request를 남긴다.
- [x] [선택 사항 1] 키보드만으로 추천 검색어들로 이동 가능하도록 구현합니다.
- [x] [선택 사항 2] React-Query를 활용하여 캐싱을 구현합니다.
- [x] [선택 사항 3] 검색어 결과는 페이지네이션 또는 무한스크롤 선택합니다.
- [x] [선택 사항 4] 스크랩 저장 기능을 구현합니다.
- [x] [선택 사항 5] 뷰포트 크기에 따른 반응형 UI를 구현합니다.
- [ ] [선택 사항 6] 스토리북을 사용해 인터렉션 UI 테스팅
### 구현 내용

- 비즈니스 로직과 뷰 로직을 분리해 관심사 분리 및 재사용

- **useSearchBar** <br/>
=> 검색창에서 쓰이는 UI로직에 관련된 비즈니스 로직 관리
- **useSearchResult** <br/>
=> 검색창에서 반환하는 검색결과에 관련된 비즈니스 로직 관리
- **useDebouncedSearch** <br/>
=> useDebouncedSearch을 생성하여 검색어에 debounce를 적용해 사용자가 검색어 입력을 시작한 후 500ms동안 입력이 없으면 API가 호출되도록 구현
- **useHandleModal** <br/>
=> modal custom hook을 활용해 모달의 상태 관리
- **useInfiniteScroll** <br/>
=> Intersection Observer API를 활용해 요소를 관찰하고 관찰하는 요소의 관찰여부에 따라 callback 함수 실행
- **useInput** <br/>
=> input custom hook을 활용해 input 상태 관리

- 서버 부하를 고려해 React-Query 캐싱 적용

- 검색창, 검색 결과, 검색 없음 컴포넌트 재사용해 개발 기간 단축



- Intersection observer를 활용해 무한스크롤 구현

- 키보드만으로 추천 검색어들로 이동 가능하도록 구현
- 검색창에서 onKeyDown 이벤트가 발생했을 때 event.key 값이 ArrowDown, ArrowUp일 경우 selectedIndex가 변경되게 했고 추천 검색어의 index와 selectedIndex가 같을 때 background-color가 변경되도록 구현.

### 검색창
![2024-05-1010 09 48-ezgif com-video-to-gif-converter](https://github.com/Leeseunghwan7305/Infinite_Challenge_FE/assets/78102507/378ec5b8-452d-4c64-8147-4f8a909deea7)

### 검색 리스트 무한 스크롤

![ezgif com-video-to-gif-converter](https://github.com/Leeseunghwan7305/Infinite_Challenge_FE/assets/78102507/a4e5f022-7ccc-41f7-b7ab-3aa66b7b98de)

### 검색 리스트 즐겨찾기 스크랩 등록 및 삭제

![ezgif com-video-to-gif-converter (1)](https://github.com/Leeseunghwan7305/Infinite_Challenge_FE/assets/78102507/1e74507b-1608-4f5f-9b9b-7fc9159fe29d)







Loading