기본적으로 ESLint 를 통해 airbnb의 JavaScript 스타일을 따르며, 추가로 FE 개발자와 협의해서 정한 컨벤션을 포함합니다.
Don't use export defaultDon't use if without bracketConditional renderingArrow function vs function keywordstring / str, array / arrtype / interfacetype & interface namingEvent Handler namingFor FunctionFor PropsParameter of callback functionMapping JSX for logicType definition of variablesType definition of functionType definition of HooksuseStateuseMemouseCallbackOrder of hooksSVG import namingOnly style component함수 프롭 깔끔하기 전달하기바로 리턴하지 않기 (변수로 선언해서 반환)if return 패턴을 사용한다. ( if - else 지양 )프롭과 리턴 타입은 별도의 인터페이스로 작성한다.옵셔널한 객체도 객체 구조 분해로 사용하기
Don't use export default
모듈을 작성할 때
export default
보다는 export
를 사용하세요.// Bad 👎 const Mycomponent: FC<Props> = () => { ... } export default MyComponent // Good 👍 export const Mycomponent: FC<Props> = () => { ... }
Don't use if without bracket
if 문을 중괄호 없이 사용하지 마세요.
가독성에 악영향을 미칠 수 있습니다.
// Bad 👎 if (someFlag) return 1; // Good 👍 if (someFlag) { return 1; }
Conditional rendering
삼항 연산자(
?
) 대신 &&
연산자를 사용해주세요. &&
연산자가 가독성이 더 낫습니다.혹은 조건부로 렌더링 되는 JSX 가 크게 다르다면 if 문을 통해 아예 나누는 것도 좋은 방법입니다.
// Bad 👎 const Mycomponent: FC<Props> = () => { return ( {someFlag ? <SomeComponent /> : null} ); } // Good 👍 const Mycomponent: FC<Props> = () => { return ( {someFlag && <SomeComponent />} ); } const Mycomponent: FC<Props> = () => { if (someFlag) { return <SomeComponent /> } return <OtherComponent /> }
Arrow function vs function
keyword
가능하다면 Arrow function을 우선적으로 사용해주세요. 불가피한 상황에서만
function
키워드를 이용해주세요.string / str, array / arr
단독으로 사용되는 경우, 예약어가 아니라면 축약하지 말아주세요. suffix 혹은 prefix인 경우에는 반드시 축약어를 사용하지 말아주세요.
// Bad 👎 const someFunction = (firstStr: string) => firstStr; const otherFunction = (firstArr: string[]) => firstArr; // Good 👍 const someFunction = (firstString: string) => firstString; const otherFunction = (firstArray: string[]) => firstArray; const stringFunction = (str: string) => str;
type
/ interface
반드시
type
을 사용해야 하는 것이 아니라면 interface
를 우선적으로 사용하세요.Ref: https://medium.com/humanscape-tech/type-vs-interface-언제-어떻게-f36499b0de50
https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/basic_type_example/#types-or-interfaces
// Bad 👎 type Props = { ... } // Good 👍 interface Props { ... } type ExampleType = [...] type ExampleCustomType = Pick<SomeType, 'someProperty'>;
type
& interface
naming
type
과 interface
모두 PascalCase를 기본으로 합니다.interface
의 경우 어떠한 prefix나 suffix를 붙이지 마세요.type
은 'type' suffix를 통해 type
임을 나타내주세요.// Bad 👎 interface IProps { ... } interface PropsInterface { ... } interface props { ... } type props = { ... } type Props = { ... } // Good 👍 interface Props { ... } type SomeType = { ... }
Event Handler naming
For Function
함수를 정의하는 경우에는
handle*
네이밍을 사용합니다.// Bad 👎 const onClick = () => {} const onPress = () => {} const { onOpen } = useSomeLibraryHook() // Good 👍 const handleClick = () => {} const handlePress = () => {} const { onOpen: handleOpen } = useSomeLibraryHook()
For Props
Props 로 이벤트 핸들러를 전달받는 경우에는
on*
네이밍을 사용합니다.// Bad 👎 interface Props { handleClick: () => void; } const SomeComponent: VFC<Props> ({ handleClick }) => {} // Good 👍 interface Props { onClick: () => void; } const SomeComponent: VFC<Props> ({ onClick }) => {}
Parameter of callback function
협업하는 개발자들의 이해를 위해 가능한 한 의미 있는 이름을 사용합니다.
// Bad 👎 bodyConditions.map(v => v.id); // Good 👍 bodyConditions.map(condition => condition.id);
Mapping JSX for logic
- 되도록 컴포넌트로 분리해요(꺽쇠지향)
- 아니면 변수에 할당해줘요
- 아니면 useMemo 콜백 안에 로직을 담아요
- 아니면 함수로 만들어서 호출해요( renderThing 컨벤션 따름 )
// Bad 👎 return <Wrapper>{data.map(v => <Text>{v.text}</Text>}</Wrapper>; // GGGGood 👍 return <Wrapper><DataList/></Wrapper>; // GGGood 👍 const dataList = data.map(v => <Text>{v.text}</Text>); return <Wrapper>{dataList}</Wrapper>; // GGood 👍 const dataList = useMemo(() => { //...logic... return data.map(v => <Text>{v.text}</Text>); },[]) return <Wrapper>{dataList}</Wrapper>; // Good 👍 const renderList = () => data.map(v => <Text>{v.text}</Text>); return <Wrapper>{renderList()}</Wrapper>;
Type definition of variables
let
은 초기화 하지 않는 경우가 많기 때문에 직접 정의 해줍니다.const
는 쉽게 타입이 추론되는 원시 타입의 경우 추론에 맡기지만 필요할 경우 직접 정의하여 사용합니다.// Bad 👎 let apolloClient; // Good 👍 let apolloClient: ApolloClient<NormalizedCacheObject>;
Type definition of function
리턴 타입과 매개 변수의 타입을 따로 정의 해주는 방식과 함수를 할당하는 변수에 함수 자체의 타입을 정의하여 주는 방식을 혼용합니다.
일반적인 상황에서는 가독성을 위해 리턴 타입과 매개 변수의 타입을 따로 정의 해주는 방식을 우선시 하지만, 변수에 함수 자체의 타입을 정의해주기 편한 경우에는 그 방법을 사용합니다.
// Bad 👎 const test = () => 'hello world'; // Good 👍 const test = (): string => 'hello world'; const test2 = (str: string): string => str + 'hello wolrd'; const test2: MouseEventHandler<HTMLButtonElment> = (event) => {...}
Type definition of Hooks
useState
모든
useState
사용처에서 제너릭을 사용합니다.// Bad 👎 const [isValid, setIsValid] = useState(false) // Good 👍 const [isValid, setIsValid] = useState<boolean>(false)
useMemo
모든
useMemo
사용처에서 제너릭을 통해 타입을 정의 해줍니다.// Bad 👎 const isValid = useMemo(() => false, [deps]); const isValid2: boolean = useMemo(() => false, [deps]); // Good 👍 const isValid = useMemo<boolean>(() => false, [deps]);
useCallback
함수와 동일합니다.
// Bad 👎 const test = useCallback(() => 'hello world', [deps]); // Good 👍 const test = useCallback((): string => 'hello world', [deps]); const test2 = useCallback((str: string): string => str + 'hello wolrd', [deps]); const test2: MouseEventHandler<HTMLButtonElment> = useCallback((event) => {...}, [deps]);
Order of hooks
- 커스텀 훅 (리액트에서 제공 되지 않는)
단, 의존성이 있다면 자유로운 위치에서 사용 가능
useState
/useRef
useMemo
/useCallback
useEffect
useEffect
는 보통 return 문 바로 위, 즉 거의 가장 마지막에 작성한다.// hook 우선순위에 따른 선언 순서를 지킨다 useSatate() useRef() useMemo() useCallback // 기타 컴포넌트 로직을 작성한다. 변수선언 함수선언 ... // useEffect는 리턴 직전에 작성한다. useEffect() useEffect() useEffect() return();
SVG import naming
*Icon
과 같이 Icon suffix를 붙입니다.// Bad 👎 import X from '...' // Good 👍 import XIcon from '...'
Only style component
함수형 컴포넌트(FC)를 사용하지 않고 그대로
export
합니다.// Bad 👎 export const HeaderWrapper: FC = ({ children }) => { return <Wrapper>{children}</Wrapper>; }; const Wrapper = styled.View` ...style ` // Good 👍 export const HeaderWrapper = styled.View` ...style `;
함수 프롭 깔끔하기 전달하기
- 예시 1
// bad 👎 type onPress: () => void type onPressRadioButton: (i: number) => void; // index를 전달하고 싶어서 이렇게 선언 onPress={(): void => onPressRadioButton(index)} // <-- bad! // good 👍 type onPress: ()=>void type onPressRadioButton: (i: number) => () => void; // index를 인자로 받지만, 최종적으로 ()=>void를 반환한다는 뜻 onPress={handleRadioPress(index)} // <-- good!
- 예시 2
export type ScreenParamList = { 스크린1: undefined; 스크린2: undefined; 스크린3: undefined; 스크린4: undefined; }; // bad 👎 const handlePress스크린1 = ()=>navigate('스크린1'); const handlePress스크린2 = ()=>navigate('스크린2'); const handlePress스크린3 = ()=>navigate('스크린3'); const handlePress스크린4 = ()=>navigate('스크린4'); return( <> <스크린1 onPress={handlePress스크린1} /> <스크린2 onPress={handlePress스크린2} /> <스크린3 onPress={handlePress스크린3} /> <스크린4 onPress={handlePress스크린4} /> </> ) // good 👍 const handlePressButton = (screen: keyof ScreenParamList) => (): void => navigate(screen); return( <> <스크린1 onPress={handlePressButton('스크린1')} /> <스크린2 onPress={handlePressButton('스크린2')} /> <스크린3 onPress={handlePressButton('스크린3')} /> <스크린4 onPress={handlePressButton('스크린4')} /> </> )
바로 리턴하지 않기 (변수로 선언해서 반환)
// bad 👎 const useUnmount = () => { return useCallback(() => { ... },[]); } // good 👍 const useUnmount = () => { const unmount = useCallback(() => { ... },[]); // 변수로 선언한 후 반환하기! return unmount; }
if return 패턴을 사용한다. ( if - else 지양 )
if return
구문이 더 직관적이라고 생각해서 컨벤션으로 규정하였다.
- 가끔 명확히 두가지로 나뉘는 경우에는
if - else
패턴이 더 직관성 있을 때가 있긴 하지만, 예외를 두지 않고 일관성 있게if return
패턴으로 사용한다.
// bad 👎 if( 조건문 ){ 블라블라1 } else { 블라블라2 } // good 👍 if( 조건문 ){ 블라블라1 return ; } 블라블라2
프롭과 리턴 타입은 별도의 인터페이스로 작성한다.
- 인라인으로 작성하지 않고, 별도의 타입을 정하여 사용하며, 타입 정의는
interface
로 한다.
- 내용이 없는 경우에는 타입 정의를 생략한다.
// bad 👎 // 내용이 없는 경우에는 타입을 지정하지 않는다. interface Props {} interface Return {} // bad 👎 // 타입과 프롭을 인라인으로 지정하지 않는다. const 함수 = ({count}:{count:number}):{aaa:number, bbb:number} => { /** 블라블라 로직 **/ return { aaa: 3, bbb: 5, } } // good 👍 // Props와 Return을 상단에 별도의 타입으로 분리하여 사용한다. interface Props { count: number; } interface Return { aaa: number; bbb: number; } const 함수 = ({count}:Props):Return => { /** 블라블라 로직 **/ return { aaa: 3, bbb: 5, } }
옵셔널한 객체도 객체 구조 분해로 사용하기
오브젝트 자체가 옵셔널한 경우, 오븝젝트가
undefined
일 수 있어서 이 경우에는 프롭에 접근하는 게 말이 안되므로 타입 에러가 나온다. ( 자바스크립트 상으로는 문제 없음 )얕은 복사하여 사용한다면, 오브젝트가
undefined
일 경우에는 각각의 프롭들에 undefiend
가 들어가서 타입 에러도 없고, 깔끔하게 프롭도 추출할 수 있다.type test = { one: number; two: number; three: number; }; const testFunction = ({data}:{data?: test}):void=>{ // const {one, three} = data; // data 오브젝트 자체가 undefined 일 수 있어서 프롭 없다고 에러 const {one, three} = {...data}; // 오브젝트가 undefined라면 one, three에 각각 undefined가 들어가고, 있다면 값이 들어감 console.log("one, three", one, three); }