🎯
최종 프로젝트
의논할 내용
- tanstack-query에서 query-key-factory로 querykey랑 queryFn 잘 묶어서 옵션만 공유하는 방식으로 깔끔하게 적용해보기.
패키지 매니저로 어떤 걸 사용할까요?
1. npm
2. yarn
3. yarn berry
4. pnpm
초기 개발 환경을 어떤 방법으로 세팅할까요?
1. 직접 번들러 구성하기
2. CRA
3. Vite
4. Next.js
5. Remix
컨벤션을 위한 라이브러리는 어디까지 사용할까요?
(eslint, prettier의 경우 세부 옵션도 논의하면 좋을 것 같아요!)
eslint: vite 기본 설정에서 몇몇 필요한 규칙만 추가.
prettier:
1. eslint
2. prettier
3. husky + lint-staged
4. commitlint
husky pre-commit
commitlint
폼 데이터 및 유효성 관리 라이브러리는 어디까지 사용할까요?
1. react-hook-form or formik
2. zod or yup or …..
네트워크 클라이언트나 패칭 및 캐싱 클라이언트를 사용할까요?
1. axios
2. tanstack-query/react or swr
axios
tanstack-query
query-key-factory
상태 관리 라이브러리를 사용할까요?
1. redux or redux-toolkit
2. zustand
3. recoil
4. jotai
스타일링 관련 라이브러리는 어떤 걸 사용할까요?
1. css vs css-in-js
2. 디자인 제공 vs 미 제공
3. 컴포넌트 제공 vs 미 제공
1. scss
2. emotion or styles-components or vanilla-extract or panda-css …
3. tailwind or radix or shadcn or …
4. chakra or mui or bootstrap or …
테스팅 라이브러리나 문서화 라이브러리는 사용할까요?
⇒ jest, react-testing-library는 추후 멘토님께 의견 공유
1. storybook (공통 컴포넌트만)
2. jest
3. react-testing-library
그 외 라이브러리들을 사용할까요?
⇒ 인터랙션 동작은 선 라이브러리 후 구현 방식으로 도전해봐도 좋을듯.
1. React Router
2. Swiper
3. LottieFiles Animation
…
커스텀 prettier 설정
이상훈
차세진
박나연
노성래
코드 컨벤션
1-1 네이밍
변수명, 함수명
변수명, 함수명은 camelCase
로 한다.
상수
상수명은 UPPER_CASE
로 한다.
컴포넌트, 객체, 클래스
컴포넌트, 객체, 클래스는 PascalCase
로 한다.
축약
축약하여 네이밍을 하지 않는다.
이벤트 핸들러
컴포넌트 내부의 이벤트 핸들러를 작성할때는 handle로 시작하고
상위 컴포넌트에서 받는 이벤트 핸들러는 on으로 시작
path alias
path alias는 /src 만 @
로 하타
1-4. 타입
interface
interface
는 다음의 상황에서 사용된다.
- 확장 가능성이 높은 타입
- api의
input
, output
값처럼 외부로 연결 또는 확장되는 경우
type vs interface
타입의 보강, 확장이 필요한 경우는 제외하고는 type
를 사용한다.
확장자
jsx
문법을 다루는 경우 tsx
, 그렇지 않는 경우 ts
로 작성한다.
컴포넌트의 선언
컴포넌트는 화살표 함수
로 선언한다.
컴포넌트의 props
- props의 개수 상관없이 하나라도 있으면 타입을 만들어 명시한다.
- 컴포넌트 props의 타입명은
Props
로 한다.
컴포넌트의 export default
- 컴포넌트(혹은 custom hook)를 내보낼 때에
export default
를 사용한다.
export
는 사용하지 않는다. 사용이 된다면 파일 분리를 고려한다.
컴포넌트 파일
- 하나의 컴포넌트 파일에는 로직이 포함된 컴포넌트는 하나만 들어갈 수 있다.
react event handler
reactEventHandler를 사용한다.
API
api 함수를 만들때 api method
를 어두에 작성한다. api method
가 아닌 단어는 어두에 작성하지 않는다.
단, 다양한 api method
를 추상화 해야 하는 경우에는 fetch
를 어두에 작성한다. api method
에 대한 구분은 인자로 한다.
- custom hook의 접두사는
use
로 시작한다.
상수 범위
다음과 같은 종류 변수는 상수
로 한다.
- 서비스 메세지(errorMessage, successMessage)
index 파일의 용도
- components랑 pages에서는 index 파일 자체에 컴포넌트 정의.
PostViewer/index.tsx
⇒ PostViewer 컴포넌트
PostViewer/components/index.ts
⇒ 종속 컴포넌트 export 용도
파일 이름 네이밍
- 컴포넌트나 페이지 등
tsx
파일의 이름은 파스칼 케이스를 사용합니다.
- 그 외의 로직에 해당하는
ts
파일은 카멜 케이스를 사용합니다.
- 커스텀 훅의 이름은
use
접두사로 시작합니다.
- HOC 컴포넌트의 이름은
with
접두사로 시작합니다.
앞으로 나눠볼 내용
- Git 컨벤션 (Issue, 커밋 규칙, PR 방식 등등..)
{
"printWidth": 80,
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": false,
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"singleAttributePerLine": true,
"trailingComma": "none",
"quoteProps": "as-needed",
"endOfLine": "lf"
}
{
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": false,
"quoteProps": "as-needed",
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "always",
"proseWrap": "preserve",
"endOfLine": "auto",
"bracketSameLine": false
// 적용은 안해봤지만, 세진님 규칙을 보고 저도 평소에 import하는 순서를 적어볼게요!
"importOrder": [
"^react(.)",
"^react-router-dom(.)"
"^react-error-boundary(.)"
"^react-hook-form(.)"
"<THIRD_PARTY_MODULES>",
"^@apis/(.)$",
"^@hooks/(.)$",
"^@store/(.)$",
"^@utils/(.)$",
"^@components/(.)$",
"^@routes/(.)$",
"^@type/(.)$",
"^@constants/(.)$",
"^@pages/(.)$",
"^@styles/(.)$",
"^[./]"
],
}
{
"tabWidth": 2,
"printWidth": 80,
"singleQuote": true,
"trailingComma": "all",
"semi": true,
"bracketSpacing": true,
"arrowParens": "always",
"singleAttributePerLine": true,
"endOfLine": "auto",
"bracketSameLine": true,
// 이 아래는 import 관련 내용입니다!
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"importOrder": [
"^react(.)",
"<THIRD_PARTY_MODULES>",
"^@pages/(.)$",
"^@components/(.)$",
"^@apis/(.)$",
"^@hooks/(.)$",
"^@store/(.)$",
"^@storage/(.)$",
"^@utils/(.)$",
"^@routes/(.)$",
"^@styles/(.)$",
"^@type/(.)$",
"^@constants/(.)$",
"^[./]"
],
"importOrderSortSpecifiers": true
}
{
"semi": true,
"singleQuote": true,
"endOfLine": "lf",
"printWidth": 80,
"tabWidth": 2,
"singleAttributePerLine": true,
"bracketSameLine": true,
"trailingComma": "none",
"arrowParens": "always",
"bracketSpacing": true,
"jsxSingleQuote": true,
"importOrder": [
"^@api/(.*)$",
"^@components/(.*)$",
"^@constants/(.*)$",
"^@hooks/(.*)$",
"^@lib/(.*)$",
"^@pages/(.*)$",
"^@store/(.*)$",
"^@styles/(.*)$",
"^@types/(.*)$",
"^@utils/(.*)$",
"^@views/(.*)$",
"^[./]"
],
"importOrderSeparation": "true",
"importOrderSortSpecifiers": "true"
}
// bad
const this_is_my_object = {}
const this_is_my_function = () => {}
// good
const thisIsMyObject = {}
const thisIsMyFunction = () => {
// ...
}
// bad
const myConstants = "상수"
// good
const MY_CONSTANTS = "상수"
// bad
const myReactComponent = () => {
// ...
}
class person() {
// ...
}
// good
const MyReactComponent = () => {
// ...
}
class Person() {
// ...
}
// bad
const myBtn = document.querySelector('.my-button')
const myFn = () => {
// ...
}
// good
const myButton = document.querySelector('.my-button')
const myFunction = () => {
// ...
}
function App(){
const handleClick = ()=>{}
<button onClick={handleClick}></button>;
}
function App(){
const doAnything = () =>{}
<Button onClick={doAnyThing}/>;
}
function Button({onClick}){
const handleClick = () =>{
onClick && onClick()
}
<button onClick={handleClick}></button>;
}
// allowed but does not use
function MyComponent() {
// ...
}
// good
const MyComponent = () => {
// ...
}
interface MyComponentProps {
// ...
}
const MyComponent = ({ a, b }: MyComponentProps ) => {
// ...
}
export default MyComponent
someFunction: React.MouseEventHandler<HTMLButtonElement>;
// bad
const userGet = () => {
// ...
}
const searchUser = () => {
// ...
}
// good
const getUser = () => {
// ...
}
// bad
const getPostUser = () => {
// ...
}
// good
const fetchUser = () => {
// ...
}
// bad
const getPostUser = () => {
// ...
}
// good
const useUserQuery = () => {
// ...
}
export { useArchives } from '@hooks/useArchives';
export { useCurrentPage } from '@hooks/useCurrentPage';
export { useScrollToTop } from '@hooks/useScrollToTop';
export { useWindowWidth } from '@hooks/useWindowWidth';