- λ°°ν¬ μ¬μ΄νΈ
- λ‘컬
git clone https://github.com/Pre-Onboarding-FE-Team07/wanted-codestates-project-7-10.git
yarn
yarn start:dev
--π src
---π assets β‘ μμ΄μ½ 곡κ°
---π component β‘ νλ©΄ 곡κ°
-- π hooks β‘ ν€λ³΄λμ λν ν¨μ λ‘μ§
---π redux β‘ λ°μ΄ν°λ₯Ό λΆλ¬μ€κ³ μ μ₯νλ 곡κ°
---π utilities β‘ API νΈμΆ μ΅μ ν
μ΄λ¦ | μ§μ± | μν |
---|---|---|
β‘οΈλ°μ§μ© | νμ₯ | κ°λ°νκ²½ κ΅¬μΆ λ° λ°°ν¬ |
β¨κΉμ ν | νμ | ν€λ³΄λλ‘ μΆμ² κ²μμ΄ μ΄λ |
π¨λ¬Έμ κ²½ | νμ | UI μ»΄ν¬λνΈ κ΅¬ν λ° λ°μν μ μ© |
πμ¬μ±μ€ | νμ | κ²μμ΄ μΆμ² API νΈμΆ - axiosλ‘ λ°μ΄ν° λΆλ¬μ€κΈ° |
βοΈμν¨μ | νμ | API νΈμΆ μ΅μ ν - Debounce ꡬν |
π¨μ΄μμ§ | νμ | κ²μμ΄ μΆμ² API νΈμΆ - Redux μ¬μ© |
πμ΅λ―Όμ° | νμ | API νΈμΆ μ΅μ ν |
μ»΄ν¬λνΈ κ΅¬μ± :
<SearchArea>
<Title />
<SearchBar />
<RecommendArea />
</SearchArea>
- https://clinicaltrialskorea.com/ μ κ²μ μμμ ν΄λ‘ μ½λ©
- κ²μμ΄ μΆμ² μμμ RecommendArea μ»΄ν¬λνΈλ‘ ꡬν
- λ°μν ꡬν
- λμ μΈ κΈ°λ₯ μΆκ° μ μ°Έκ³ ν μ μλλ‘ μΌλΆ μ»΄ν¬λνΈμ μ£Όμ 첨λΆ
μΆκ°ν ν¨ν€μ§
-
@svgr/webpack: κ²μμ°½μ λ보기 μμ΄μ½μ ꡬνν svg νμΌμ React Componentλ‘ μ¬μ©νκΈ° μν΄μ μΆκ°ν¨.
- svgλ₯Ό μ¬μ©ν μ΄μ λ μ½λλ‘ μ΄λ£¨μ΄μ Έ μμ΄ λ€λ₯Έ μ΄λ―Έμ§ νμΌ(png, jpg λ±)λ³΄λ€ μ©λμ΄ μ κΈ° λλ¬Έ.
- svgλ₯Ό React Componentλ‘ μ¬μ©ν μ΄μ λ, propsλ₯Ό ν΅ν΄μ svg μμ±μ λ³κ²½ν μ μμ΄ μ μ°μ±μ΄ 보μ₯λκΈ° λλ¬Έ. μ°Έκ³ μ¬μ΄νΈ
-
react-responsive : html/cssλ§μΌλ‘ λ°μν μΉ κ΅¬νμ΄ κ°λ₯ν κ²½μ° λ―Έλμ΄μΏΌλ¦¬λ₯Ό μ¬μ©νλ©΄ λμ§λ§, νλ©΄ ν¬κΈ°μ λ°λΌ μ»΄ν¬λνΈ κ΅¬μ‘°λ μΌλΆ λ³κ²½λμ΄μΌ ν΄μ react-responsive ν¨ν€μ§ μ¬μ©.
-
svg νμΌμ μΈμνμ§ λͺ»νλ λ¬Έμ
- μΉν©μ file-loader ν¨ν€μ§ λ€μ΄λ‘λ λ° webpack.config.jsμ rule μΆκ°
- { ReactComponent as ~ } λ‘ svg νμΌ ReactComponentλ‘ importνκΈ°
- μμ κ°μ λ°©μμΌλ‘ ν΄κ²°νλ €κ³ νμ.
- κ·Έλ°λ° default importλ§ κ°λ₯νκ³ ReactComponentλ‘ importνλ©΄ μλ¬κ° λ°μν΄μ, ν΄λΉ μ¬μ΄νΈλ₯Ό μ°Έκ³ νμ¬ @svgr/webpack ν¨ν€μ§λ₯Ό λ€μ΄λ‘λνκ³ webpack.config.jsμ ruleμ μΆκ°νλ λ°©μμΌλ‘ λ€μ ν΄κ²°.
-
webpack.config.js Lint μλ¬ - require is not defined
.eslintignore
νμΌ μΆκ° ν webpack.config.js 무μνλλ‘ μμ νμ¬ ν΄κ²°.
-
eslint, prettier svg νμΌ μλ¬
-
eslint : svg νμΌμ λν΄ λ€μ μλ¬ λ°μ 'React' must be in scope when using JSX react/react-in-jsx-scope
-
prettier : [error] No parser could be inferred for file: src/assets/icon_search.svg
β .eslintignore, .prettierignore νμΌμ src/assets ν΄λ (js, jsx λ₯Ό μ μΈν 리μμ€ ν΄λ)λ₯Ό μΆκ°νμ¬ ν΄κ²°ν¨.
-
yarn add axios
- axios μ¬μ©νκΈ° μν΄μ ν¨ν€μ§ μΆκ°ν¨
- κ²μμ΄ μΆμ² APIλ₯Ό νΈμΆ(axiosλ‘ λ°μ΄ν° λΆλ¬μ€κΈ°)
- μΊμ κΈ°λ₯ μ°κ²°
-
axiosλ₯Ό ν΅ν΄ APIλ₯Ό λΆλ¬μμ νλ©΄μ 보μ¬μ£Όλ μμ μ΄ μ½μ§ μμ λ§μ μκ°μ΄ μμλ¨
- axios GETλ©μλλ₯Ό μ¬μ©νμ¬ κ²μμ΄ μΆμ² API μμ²μ νμ¬ λ°μ΄ν°λ₯Ό νλ©΄μ 보μ¬μ£Όμμ
- λν, slice ν¨μλ₯Ό ν΅ν΄ μ¬μ©μκ° μ λ ₯ μ 7κ°μ λ°μ΄ν°κ° λμ€κ²λ ν¨
-
index.jsx νμΌμ App.jsx νμΌμ΄ μ€λ₯κ° μκ²Όμ
- eslintrc.json νμΌμ μλμ μ½λλ₯Ό μΆκ°νμ¬ ν΄κ²° νμμ
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx"]
}
}
}
-
yarn add react-redux redux-thunk redux redux-promise
ν¨ν€μ§ μ€μΉ -
redux νμΌμ ꡬ쑰λ₯Ό λ€μκ³Ό κ°μ΄ μμ±ν΄μ λ‘μ§μ ꡬννμμ΅λλ€.
--π redux
-- π store.js β‘ stateκ° κ΄λ¦¬λλ μ€μ§ νλμ 곡κ°
-- π actions
-- π search.js β‘ μ μΈνλ SEARCH_RESULT μ‘μ
μ λν ν¨μ λ‘μ§ κ³΅κ°
-- π types.js β‘ μμ ννλ‘ μ¬μ©ν΄μΌ ν μ‘μ
μ μ΄λ¦λ€ μ μ₯
-- π reducers
-- π index.js β‘ μ¬λ¬ κ°μ reducerλ₯Ό κ΄λ¦¬νκΈ° μν μ μ₯μ
-- π search.js β‘ search κ΄λ ¨λ reducer
- Reduxλ₯Ό μ΄μ©ν΄ UIμ λ°μ΄ν° κ°μ Έμ€κ³ stateμ λμνλ μ½λ μμ±
// reduxμ action -> state λ³κ²½νλ λ‘μ§
const dispatch = useDispatch();
// inputμ μλ κ° κ°μ Έμ€λ onChange ν¨μ
const onchangeValue = (e) => {
dispatch(searchResult(e.target.value));
};
// κ²μ λ²νΌ λλ₯΄λ©΄ λμνλ eventν¨μ
const onSearch = () => {};
...
<input
type="text"
placeholder="μ§νλͺ
μ μ
λ ₯ν΄ μ£ΌμΈμ. "
onChange={onchangeValue}
/>
RecommendArea.jsx
κ²μ λμ΄μ§λ λΆλΆμ λν λ°μ΄ν° κ° κ°μ Έμ€κΈ° μμ±
const result = useSelector((state) => state.search.success);
if (result && result.length === 0) {
return (
<RecommendAreaStyled show={show}>
<Info>κ²μμ΄ μμ</Info>
</RecommendAreaStyled>
);
}
if (result && result.length > 0) {
return (
<RecommendAreaStyled show={show}>
<Info>μΆμ² κ²μμ΄</Info>
{result &&
result.map((data, idx) => (
<Recommends key={idx}>
<Recommend id={data.id} content={data.name} />
</Recommends>
))}
</RecommendAreaStyled>
);
}
return null;
- νλ‘μ νΈ μ forkνμ¬ μλ‘μ΄ λΈλμΉλ₯Ό λ§λ ν μμ
μ νλ λΆλΆμ΄ μμν΄μ μ΄λ €μ μμ΅λλ€.
ex) μμ νμμ΄ mergeλ₯Ό νλ©΄ κΈ°μ‘΄μ μμ±νλ μ½λλ₯Ό μΌμΌν 볡μ¬νλ μ΄λ €μμ΄ μμλ€. νμ λΆμκ² μμ²νμ
¨λλ
git remote add upstream <μλ³Έ μ격 μ μ₯μ>
νλ λ°©λ²μ μλ €μ£Όμ μ ν΄κ²°νμμ΅λλ€! - [React] regeneratorRuntime is not defined μλ¬ ν΄κ²° Reactμμ ES8 λ¬Έλ²μΈ async/awaitλ₯Ό μ¬μ©ν΄ λΉλκΈ° ν¨μλ₯Ό μμ±νλ©΄ λΈλΌμ°μ μμ λ€μκ³Ό κ°μ μλ¬κ° λ°μνμμ΅λλ€. μ΄λΆλΆμ λν μ€λ₯λ μ ν리μΌμ΄μ
μ΄ μ»΄νμΌλ λ,
regeratorRuntime
μ΄ async/await λ¬Έλ²μ λ²μνλλ‘ νλλ° ν΄λΉ regeneratorλ₯Ό μ 곡νμ§ μμμ λ°μν μλ¬μ λλ€. μ΄ λΆλΆμ ν΄κ²°νκΈ° μν΄ μ¬λ¬λ°©λ²μ μ°Ύμλ΄€μ§λ§, κ²°κ΅ μ°Ύμ§ λͺ»νκ³ νμ λΆμκ² λμμ λ°μ@babel/plugin-transform-runtime
ν¨ν€μ§λ₯Ό λ€μ΄λ‘λνμ¬webpack.config.js
μ μλμ κ°μ μ½λλ₯Ό μμ±ν μ μμμ΅λλ€.
{
test: /\.jsx?$/,
exclude: '/node_modules',
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
targets: {
esmodules: true,
},
},
],
'@babel/preset-react',
],
},
},
{
test: /\.svg$/,
use: ['@svgr/webpack'],
},
λ΄λΉμ : κΉμ ν
onKeydown μ΄λ²€νΈνΈλ€λ¬λ₯Ό μ΄μ©νμ¬ ν΄λΉ ν€(ArrowUp, ArrowDown)κ° λλ Έμ κ²½μ° activeIndex κ°μ μ¦κ° μμΌ μλ²μ ν΅μ μΌλ‘ λ°μ λ°μ΄ν°μ μΌμΉ νλ μΈλ±μ€ κ°μ λ°±κ·ΈλΌμ΄λ 컬λ¬λ₯Ό μ£Όμ΄ κ΅¬ν
- λͺ¨λν
- μ΄κΈ° μ½λ μμ±μ λͺ¨λνλ₯Ό κ³ λ €νμ§ μκ³ μμ±νμ¬ κ°μ μμ μ λλ² νλ κ²½μ°κ° λ°μνμκ³ μ΄ν νμμ μ‘°μΈμ ν΅ν΄ μμ±ν μ½λλ₯Ό λͺ¨λννλ λ°©λ²μ μ΄μ©νμ¬ νμ μ μ§νν¨.
- Git workflow
- μ¬λ¬μ¬λκ³Ό νμ μ μ§ννλ κ³Όμ μμ Git μ¬μ©μ΄ λ§€λλ½μ§ μμ upstreamμμ Pullνλ κ³Όμ μμ μλ¬κ° λ°μν¨. νμμ μ ν Git κ·μΉμ μ΄μ©νμ¬ νλ¦μ λ°λΌ μ§ννμκ³ μλ¬λ₯Ό μ€μΌ μ μμμ.
-
κ°μ ν¬λ§νλ κΈ°λ₯ ꡬνμ μ°μ νμ¬ νμμ μν μ λ°°λΆν¨.
-
νλ‘μ νΈλ₯Ό μμ± λ° μ΄κΈ°ννκ³ μ€μ ν¨.
- CRAλ₯Ό μ¬μ©νκΈ°λ³΄λ€ reactμ webpackμ μ€μΉνμ¬ μ§μ μ€μ ν¨.
- Eslintμ prettierλ₯Ό μΆκ°νμ¬ νλ‘μ νΈμ μ€νμΌκ³Ό 컨벀μ μ κ°μ νμμΌλ©°, lint-stagedμ huskyμ μ‘°ν©μΌλ‘ commitμ νκΈ° μ μ lint μμ μ΄ μνλ μ μλλ‘ μλνν¨.
-
νλ‘μ νΈ μ§νμ¬νμ νμ νκ³ νμμκ² μ΄μ λ°μ μ ν¨κ»νλ©° λ¬Έμ λ₯Ό ν΄κ²°ν¨.
- μμ λμ€ ν΄κ²°λμ§ μλ λ¬Έμ λ₯Ό μ€λ μκ° νΌμ λμ΄μκΈ°λ³΄λ€ νμλ€κ³Ό λ¬Έμ λ₯Ό 곡μ νμ¬ λΉ λ₯Έ ν΄κ²°μ μ°μ ν μ μλλ‘ μ§μν¨.
-
μ체 μ μν Git workflowλ₯Ό μ 곡νμ¬ νμλ€μ΄ Git μμ μ μνν λμ μ΄λ €μμ ν΄μν¨.
- νμ°½ μμ μ μ§ν μ€μΈλ° upstream μ μ₯μμ μ΅μ μ½λκ° μ λ°μ΄νΈλμμ λ, μμ μ΄ μ§ν μ€μ΄λ μμ μ μ΅μ μ½λλ₯Ό λ³ν©ν΄μΌ νλ κ²½μ° ν° λμμ΄ λ¨.
-
νμ κ° μμ μ΄ κΈ΄λ°νκ² μ°κ²°λμ΄ μλ κ²½μ°, μμ μ λ€λ₯Έ νμκ³Ό κ²ΉμΉμ§ μλλ‘ λͺ¨λν λλ ν¨μνλ₯Ό κ°μ‘°ν¨.
- μμ κ°μ μμ‘΄μ±μ μ κ±°νμ¬ κ° μμ μ΄ λΉλκΈ°μ μΌλ‘ μ§νλ μ μλλ‘ ν¨. λ€λ₯Έ μμ μ κΈ°λ€λ¦¬λλΌ μμ μ μμ μ μ§ννμ§ λͺ»νκ³ μλ κ²½μ°λ₯Ό μ΅μν.
- λ§μ§λ§ κ°μ ꡬνν κΈ°λ₯μ νλμ νλ‘μ νΈλ‘ μμ±ν λ νμλ€μ΄ ꡬνν λ΄μ©μ μλ‘ λ¦¬λ·°νλλ‘ νμ¬ μμ μ΄ μμΌλ‘ μ΄λ€ μμ μ μνν΄μΌ ν μ§ μ΄ν΄λ₯Ό λμ.
-
ꡬνλ
useUserInput
hookμ μ°κ²°νμ¬ μΆμ² κ²μμ΄λ₯Ό ν€λ³΄λλ‘ μ νν μ μλλ‘ νκ³ , κ²μ κ²°κ³Όλ₯Ό stateμ μ μ₯νμ¬ μ μμμ μ¬μ©ν μ μλλ‘ κ΅¬νν¨. -
μμ μ μ λΆ μλ£νκ³ main λΈλμΉμ dev λΈλμΉλ₯Ό λ³ν©ν ν, surgeλ₯Ό ν΅ν΄ λ°°ν¬ μλ£.
-
μ΄κΈ°μ νλ‘μ νΈλ₯Ό μλ²½νκ² μ€μ νμ§ λͺ»ν΄μ νμλ€μ΄ μμ μ μ§νν λ webpackμ΄λ lint κ΄λ ¨ν΄μ λ¬Έμ λ₯Ό ν΄κ²°νλλ° λ§μ μκ°μ ν μ ν΄μΌ νμ.
- js, jsx νμΌμ΄ μλλΌλ©΄ lintκ° νμΈνμ§ μλλ‘
.eslintignore
μ.prettierignore
λ₯Ό μΆκ°νμ¬ λ¬Έμ λ₯Ό ν΄κ²°ν¨. - νμμ΄ μ΄λ €μμ κ²ͺμ λ λΉ λ₯΄κ² μ°Έμ¬νμ¬ μ΄ λΆλΆ κ΄λ ¨ν΄μ μ€λ μκ° ν μ νμ§ μκ³ κ°λ°μ μ§μ€ν μ μλλ‘ ν¨.
- js, jsx νμΌμ΄ μλλΌλ©΄ lintκ° νμΈνμ§ μλλ‘
-
κ° νμ κ°μ μμ μ μμ‘΄μ±μ μ κ±°νλλ° λ§μ κ³ λ―Όμ νμ. μμ‘΄μ± μλ λ€λ₯Έ νμμ μ½λκ° νμν μν©μΌ λλ κ°λ¨ν ν μ€νΈ μ½λλ₯Ό μ μν μ μλλ‘ ν¨.
-
μ¬μ μ μ€νμΌμ΄λ 컨벀μ μ λν κ·μΉμ μ νμ§λ§, μ§§μ μκ° λ΄μ λΉ λ₯΄κ² κ°λ°μ ν΄μΌνλ κ·μΉμ μ§ν€μ§ μλ κ²½μ°κ° λ§μμ§.
- μ§§μ μκ° λ΄μ μλ£ν΄μΌνλ νλ‘μ νΈμ κ²½μ°, κ·μΉμ κ°μννκ±°λ λμ± μλ²½ν κ°μ΄λλΌμΈ λ° κ°μ μ±μ΄ νμν¨μ κΉ¨λ¬μ.
ν€λ³΄λ μ λ ₯μ λ¨μκ°μ μ§μμ μΌλ‘ μ΄λ²€νΈκ° λ°μνλ―λ‘ ν€λ³΄λ μ λ ₯μλ§λ€ APIλ₯Ό νΈμΆνκ²λλ©΄ μλ² κ³ΌλΆνκ° λ°μν μ μμ. μ΄λ₯Ό ν΄κ²°νκΈ° μν΄μ API νΈμΆ νμλ₯Ό μ€μ΄λ λ°©λ²μ΄ νμνλ©° μ΄λ debounce κΈ°λ²μ ν΅ν΄ ν΄κ²°ν μ μμ.
: μ¬λ¬λ² λ°μνλ ν¨μμμ κ°μ₯ λ§μ§λ§ ν¨μλ§μ μ€ννλ κΈ°λ²μΌλ‘ μΌμ μκ°μ λκΈ°ν ν ν¨μλ₯Ό μ€ννλ©° κ·Έ μκ° λ΄μ κ°μ ν¨μκ° λ νΈμΆλ μμλ μ΄μ ν¨μλ μ€νλμ§ μμ.
debounce ꡬνμ lodash λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νλ λ°©λ²λ μμΌλ μ΄λ² νλ‘μ νΈμμλ setTimeout μ νμ©ν ν¨μλ₯Ό κ°λ¨νκ² μ§μ ꡬν. ꡬνλ debounce ν¨μλ onchange μ΄λ²€νΈμ μ μ©νμ¬ API νΈμΆν¨μκ° λ§μ§λ§ μ΄λ²€νΈμμλ§ μ€νλλλ‘ ν¨μΌλ‘μ¨ API νΈμΆ νμλ₯Ό μ΅μ νν¨.
debounceλ₯Ό onchange μ΄λ²€νΈμ μΆκ°ν¨μΌλ‘μ¨ κ²μ μ€μΌλλ μ΄λ²€νΈκ° μ€νλμ§ μκ³ κ²μμ λ§μ§λ§ μ΄λ²€νΈλ§ μ€νλμ΄ κ²μ μ€... νμλ₯Ό λμ°μ§ λͺ»νλ€λ λ¬Έμ κ° μμμ. μ΄λ₯Ό ν΄κ²°νκΈ° μν΄μ κ²μκ²°κ³Όμλ λ³κ°λ‘ κ²μμ€μμ νμΈνλ isSearching stateλ₯Ό μλ‘ λ§λ€μκ³ , onchange μ΄λ²€νΈκ° λ°μν λ μ΄ κ°μ trueλ‘ λ³κ²½νμ¬ 'κ²μ μ€...' λ¬Έκ΅¬κ° λνλλλ‘ λ§λ€μμ.
- μΊμ±μ ν΅νμ¬ μ¬λ¬λ² API μλ²μ μ§μν νμ μμ΄ μ΄λ―Έ μ μ₯λ κ°μ νμ©ν μ μλλ‘ axios λΌμ΄λΈλ¬λ¦¬λ₯Ό λννλ μμ μ μννμλ€.
- μΊμ±μ ꡬννλ μ¬λ¬ λ°©λ²μ΄ μμ§λ§ μλ‘κ³ μΉ¨λ±μ λ³νμλ μ μ§λλ localStorageλ₯Ό μ΄μ©νλ κ²μ΄ μ’μ κ² κ°λ€λ μκ°μ νμλ€.
- 쿼리λ₯Ό JSON ννλ‘ λΉκ΅νμ¬ λμΌν μμ²μ μΊμμμ μΈμΆνμ¬ μ¬μ©νκ³ , κ·Έλ μ§ μμ κ²½μ°λ μμ²μ λ³΄λΈ λ€ μλ΅μ μ μ₯νκ³ κ°μ λλ €μ£Όλ λ°©μμΌλ‘ μ¬μ©νμλ€.
- μ΄λ κ² κ΅¬νν κ²½μ° λ€λ₯Έ νμλ€μ΄ λΌμ΄λΈλ¬λ¦¬ λ‘λλ°©μλ§ λ³κ²½νλ©΄ λμΌνκ² μμ± κ°λ₯νμ¬ μ¬μ©μ±μ΄ νΈλ¦¬νλ€.
- μ΅μ΄μλ λͺ¨λ fetch μμ²μ κ°λ‘μ±λ λ°©μμΌλ‘ μ§ννλ €κ³ νμμΌλ, λΌμ΄λΈλ¬λ¦¬μ λ‘λ μμκ°μ κ²μ μ‘°μ νλλ° μ΄λ €μμ κ²ͺμ΄μ λͺ¨λ ν΅μ μ ν΅μ νλλ° μ΄λ €μμ κ²ͺμκ³ ,
- λμ€μ νμμ΄ axiosλ₯Ό μ¬μ©νλ κ²μ νμΈνμ¬ ν΄λΉ λ°©μμ λ§κ²λ μ‘°μ νλ μμ μ΄ μμλ€.
- κ²°κ΅ λνΌν¨μ ννλ‘ μμ±νλ κ²μ΄ μ μ ν κ²μΌλ‘ νλ¨νμ¬ λ°©ν₯μ μμ νμλ€.