// Bad
if (isSomeState1) {
anyAction1()
} else if (isSomeState2) {
anuAction2()
} else {
req = {...}
if (!loading) {
anyRequest(req)
}
}
// Good
// return 을 사용함으로써, 대부분의 경우에서 else 와 else if 문을 피할 수 있습니다.
if (isSomeState1) {
anyAction1()
return;
}
if (isSomeState2) {
anyAction2()
return;
}
if (loading) return;
const req = {...}
anyRequest(req)
예외케이스를 우선적으로 리턴해주세요
// Bad
if(isShow) {
handleScroll();
document.addEventListener('scroll', handleScroll);
if(outerRef.current) {
if(window.scrollY < outerRef.current.offsetTop) {
setImageY(IMAGE_SHOW);
setBoxTransFormY([BOX_DOWN, BOX_DOWN, BOX_DOWN, BOX_DOWN]);
}
}
// Good
// 예외 상황일때 우선적으로 return 해줌으로써, 코드에 대한 파악이 더욱 쉬워집니다.
if (!isShow) return;
handleScroll();
document.addEventListener('scroll', handleScroll)
if (!outerRef.current) return;
if (window.scrollY >= outerRef.current.offsetTop) return;
setImageY(IMAGE_SHOW);
setBoxTransFormY([BOX_DOWN, BOX_DOWN, BOX_DOWN, BOX_DOWN]);
함수에서 권장되는 규칙입니다.
하나의 함수는 한가지 일만 하게 해주세요
// Bad
function sendEmailToClient(clients: Client[]) {
clients.forEach((client) => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
// Good
function sendEmailToClient(clients: Client[]) {
clients.filter(isActiveClient).forEach(email);
}
function isActiveClient(client: Client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
되도록 순수함수로 작성해주세요
//Bad
let name = 'Robert C. Martin'; // 아래의 함수에서 참조하는 전역 변수입니다.
function convertToBase64() {
name = btoa(name);
}
convertToBase64(); // 이 이름을 사용하는 다른 함수가 있다면, 그것은 Base64 값을 반환할 것입니다
console.log(name); // 'Robert C. Martin'이 출력되는 것을 예상했지만 'Um9iZXJ0IEMuIE1hcnRpbg=='가 출력됨
// Good
const name = 'Robert C. Martin';
function convertToBase64(text: string): string {
return btoa(text);
}
const encodedName = convertToBase64(name);
console.log(name);\
함수의 매개변수는 2개 이하로 작성해주세요. 많아진다면 하나의 객체로 넘겨줄 수 있습니다.
import { UseQueryParams } from '@/types/module/react-query/use-query-params';
// custom type 을 사용해서 선언하기
export function useGetExampleListQuery(
params: UseQueryParams<typeof exampleApi.getList>,
) { ... }
// 사용 시
const { data } = useGetExmplaeListQuery({
variables: { offset: 0, limit: 10 } // 넘겨준 함수의 parameter 로 타입정의가 되어 type-chcking 이 가능해집니다.
options: {
onSuccess: (res) => {
res.title // 넘겨준 함수의 return type 으로 타입 정의가 되어서, type-checking 이 가능해집니다.
}
}
})
import { UseMutationParams } from '@/types/module/react-query/use-mutation-params';
// custom type 을 사용해서 선언하기
export function useCreateExampleMutation(
params?: UseMutationParams<typeof exampleApi.create>,
) { ... }
// 사용 시
const { mutate } = useCreateExampleMutation({
options: {
onSuccess: (res) => {
res.title // 넘겨준 함수의 return type 으로 타입 정의가 되어서, type-checking 이 가능해집니다.
}
}
})
mutate({ title: "내 제목" }) // 넘겨준 함수의 parameter 로 타입정의가 되어 type-chcking 이 가능해집니다.
class 의 method 의 parameter 는 반드시 1개 이하로 작성하기
export class ExampleApi {
// bad : 커스텀 타입인 UseQueryParams 이 제대로 작동하지 않을 수 있습니다.
getList = async (offset: number, limit: number) => {...};
// good
getList = async (params: {offset: number, limit: number}) => {...};
}
전역적으로 사용하는 util 은 test 코드를 포함하기
전역적으로 사용하는 함수의 경우엔 재 사용성이 높고, 타인이 사용할 가능성이 높기 때문에 검증이 필수적입니다.
테스트 코드를 통해 브라우저를 실행하지 않고도 검증을 확인 할 수 있으며, 결과값을 알 수 있기 때문에 문서화 효과를 누릴 수 있습니다.
import { formatNumberKR } from '../format-number-kr';
describe('formatNumberKR', () => {
it('should format number to Korean locale string', () => {
expect(formatNumberKR(1000000)).toBe('1,000,000');
expect(formatNumberKR(123456789)).toBe('123,456,789');
expect(formatNumberKR(1234.567)).toBe('1,234.567');
expect(formatNumberKR(-987654321)).toBe('-987,654,321');
expect(formatNumberKR(0)).toBe('0');
// 주의 : 소수점4째 자리 부터는 반올림 처리 됩니다.
expect(formatNumberKR(1234.5674)).toBe('1,234.567');
expect(formatNumberKR(1234.5676)).toBe('1,234.568');
});
});
전역적으로 사용하는 Utility(Generic) 타입은 예시코드를 사용하기
전역적으로 사용하는 utility 타입의 경우엔 타인이 사용할 가능성이 높기 때문에, 미리 결과를 알 수 있는 예시코드를 포함해 줍니다.
// type Example = ItemOf<['a', 'b', 'c']>;
// Example = "a" | "b" | "c"
export type ItemOf<T extends Array<any> | readonly any[]> = T[number];