📓

코드 컨벤션

기본적으로 ESLint 를 통해 airbnb의 JavaScript 스타일을 따르며, 추가로 FE 개발자와 협의해서 정한 컨벤션을 포함합니다.
 
 

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 를 우선적으로 사용하세요.
// Bad 👎 type Props = { ... } // Good 👍 interface Props { ... } type ExampleType = [...] type ExampleCustomType = Pick<SomeType, 'someProperty'>;

type & interface naming

typeinterface 모두 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

  1. 되도록 컴포넌트로 분리해요(꺽쇠지향)
  1. 아니면 변수에 할당해줘요
  1. 아니면 useMemo 콜백 안에 로직을 담아요
  1. 아니면 함수로 만들어서 호출해요( 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

  1. 커스텀 훅 (리액트에서 제공 되지 않는)
    1. 단, 의존성이 있다면 자유로운 위치에서 사용 가능
  1. useState / useRef
  1. useMemo / useCallback
  1. useEffect
    1. 💡
      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); }