-
Notifications
You must be signed in to change notification settings - Fork 8
FE 팀 컨벤션
2024-momo FE 팀의 코드 컨벤션을 소개해요 :) 😊
- 컴포넌트의 경우
export default function
을 수행해요. - 컴포넌트를 만듦과 동시에 export 할 수 있도록 해요.
export default function Component(){
return <></>
}
컴포넌트가 받는 props의 타입을 정의할 때는
interface <컴포넌트이름>Props {
//..
}
으로 정의해요. 그리고, 하나의 파일에서만 사용되는 타입 또는 인터페이스(TS 인터페이스)는 해당 파일에 작성해야함을 유의해요!
- 이벤트 핸들러 함수 콜 시그니쳐
- 이벤트 핸들러 함수가 받는 이벤트 객체의 타입을 명시하고, react에서 import하는 방식을 사용해요.
import type { ChangeEvent } from 'react';
const doSomething = (e : ChangeEvent<HTMLDivElement>) => {
// 잠 온다...
}
- 컴포넌트 내부에서 정의한 props를 사용할 때는 미리 구조 분해 할당해요.
interface AppProps {
name: number;
age: number;
}
// BAD 👎
export default function App(props: AppProps) {
const {name, age} = props
//...
}
// GOOD 👍
export default function App({ name, age }: AppProps) {
//...
}
- 이벤트 핸들러 함수 props
- 컴포넌트의 prop으로 넘기는 이벤트 핸들러는
on
접두사를 붙여주세요. - 이벤트를 직접 처리하는 함수는
handle + 명사 + 동사
를 사용해주세요. - 같은 파일이면
handle
을, 다른 파일에 내려주는거면on
접두사를 사용해주세요.
// BAD 👎
export default Harry(){
const onInputChange = () => //...
return <button onClick={onInputChange}></button>
}
// GOOD 👍
export default Harry(){
const handleInputChange = () => //...
return <button onClick={handleInputChange}></button>
}
// BAD 👎
<Harry
//...
handleSomething={onSomething}
/>
// GOOD 👍
<Harry
//...
onSomething={handleSomething}
/>
// BAD 👎: handle + 동사 + 동사 + 동사
const handleDODODO = () => //...
// GOOD 👍: handle + 명사 + 동사
const handleLunchMenuDecide = () => //...
이유 살펴보기
handleClick
함수를 정의하였고 이를<button>
에 prop 형태로 전달하였습니다. 여기서handleClick
은 이벤트 핸들러입니다. 이벤트 핸들러 함수는 다음 특징을 가집니다.
- 주로 컴포넌트 내부에서 정의됩니다.
handle
로 시작하고 그 뒤에 이벤트명을 붙인 함수명을 가집니다.*관습적으로
handle
로 시작하며 이벤트명을 이어 붙인 이벤트 핸들러 명명법이 일반적입니다.onClick={handleClick}
,onMouseEnter={handleMouseEnter}
와 같은 경우를 자주 볼 수 있을 것입니다.…*
위 코드에서는
Toolbar
컴포넌트가PlayButton
과UploadButton
을 렌더링합니다.
PlayButton
은handlePlayClick
을Button
내onClick
prop으로 전달합니다.UploadButton
은() => alert('Uploading!')
을Button
내onClick
prop으로 전달합니다.최종적으로,
Button
컴포넌트는onClick
prop을 받습니다. 이후 받은 prop을 브라우저 빌트인<button>
의onClick={onClick}
으로 직접 전달합니다. 이를 통해 React가 전달받은 함수를 클릭 시점에 호출함을 알 수 있습니다.만약 디자인 시스템을 적용한다면 버튼과 같은 컴포넌트는 동작을 지정하지 않고 스타일만 지정하는 것이 일반적입니다. 그 대신,
PlayButton
과UploadButton
같은 컴포넌트가 이벤트 핸들러를 전달하도록 합니다.
- 원시 타입의 경우 타입 추론을 활용해요.
const [count, setCount] = useState(0);
- 객체 타입의 경우, 특히 객체의 프로퍼티가 많을 경우 타입을 직접 정의하고 제네릭을 사용해요.
const [harry, setHarry] = useState<Harry>({
name: 'harry',
age: '26',
address: 'busan',
});
공식 문서에서는 두 타입 키워드의 차이를 다음과 같이 설명해요.
*타입 별칭과 인터페이스는 매우 유사하며, 대부분의 경우 둘 중 하나를 자유롭게 선택하여 사용할 수 있습니다.
interface
가 가지는 대부분의 기능은type
에서도 동일하게 사용 가능합니다. 이 둘의 가장 핵심적인 차이는, 타입은 새 프로퍼티를 추가하도록 개방될 수 없는 반면, 인터페이스의 경우 항상 확장될 수 있다는 점입니다. 대부분의 경우 개인적 선호에 따라 인터페이스와 타입 중에서 선택할 수 있으며, 필요하다면 TypeScript가 다른 선택을 제안할 것입니다. 잘 모르겠다면, 우선interface
를 사용하고 이후 문제가 발생하였을 때type
을 사용하기 바랍니다.
-
interface
- 컴포넌트의 props 타입을 정할 때 사용해요.
- 확장성을 고려해야 하는 타입의 경우(즉,
extends
키워드를 사용할 것이라 예상되는 경우) 사용해요. - 보통, 객체를 정의할 때 사용해요.
- interface의 선언 병합(extends)을 통해 속성을 확장시킬 수도 있어요.
-
type
- 객체 타입을 정의할 때, 매핑된 타입을 활용해야 하는 경우라면 사용해요.
- 복잡한 타입을 다룰 때 활용하면 좋아요.
- Union, Intersection, Mapped Type… etc…
- 타입의 확장 가능 / 불가능 여부이다.
- 인터페이스는 확장이 가능한데 반해 타입 별칭은 확장이 불가능하다. 따라서, 가능한한
type
보다는interface
로 선언해서 사용하는 것을 추천한다.
- 인터페이스는 확장이 가능한데 반해 타입 별칭은 확장이 불가능하다. 따라서, 가능한한
- 복잡한 타입을 다룰 때는 type을 사용하는 것이 좋다.
- 매핑 타입 지원도 된다.
타입스크립트 type과 interface의 차이점 - 버건디의 블로그 ^^
-
any
… 사용하지 말아요 ㅎ - 모듈화 한 타입을 import 할 경우에는
import type
으로 가져와요.- consistent-type-imports 린트로 설정했어요 :) 🤗
- 상수 :
UPPER_SNAKE_CASE
- 매직 넘버를 피해요 ^^
- 문자열에도 의미있는 상수를 적용해요 ^^
- 클라이언트 url 경로에도 적용하면 좋아요, 사용자를 어디로 보내는지 파악하기 쉬워요^^
상수 네이밍 컨벤션은 아래 예시를 따라줘요.
const CONSTANTS = {
something: "상수를 입력해주세요!"
}
- 컴포넌트 이름은
PascalCase
로 해요. - 컴포넌트를 제외한 다른 파일 이름은
camelCase
로 해요.- 유틸 함수, 상수 등등…
/Button
ㄴ index.tsx
ㄴ Button.styles.ts
ㄴ Button.stories.tsx
/hooks
ㄴ useCardNumber
ㄴ useCardNumbers.ts
ㄴ useCardNumbers.test.ts
모든 css prop 변수 네이밍은 s_
prefix를 사용한다.
// example
const s_name = css``;
<div css={s_name}></div>