개발자는 반복적인 작업을 싫어합니다.
개발자는 간단한 것을 좋아합니다.
개발자는 간편한 것을 좋아합니다.

개발자는 반복적으로 작성하는 필수적인 코드, 보기힘든 코드, 복잡한 타입으로 인하여 개발하면서 불편함을 겪게되는데요. 저도 개발자로서 그러한 불편함을 계속 겪고있습니다. 코딩을하면서 그러한 불편함을 줄이기 위해 여러가지 시도를 계속 하였습니다. 이번글에서는 제가 코드를 작성하면서 불편함을 줄이기위해 사용하는 방식들을 공유해보도록 하겠습니다.

앞으로 소개하는 방식들은 주관적이며, Typescript를 사용하는 리액트 개발에 맞추어져 있습니다.

반복되는 ?(Optional Chaining)

값이 null이거나 undefined일 수 있는 값을 편하게 사용하기 위해서 ?를 자주 사용합니다.
ex) user?.profile?.contact?.email
리액트에서 값을 화면에 출력할때에 값이 없을때의 에러를 방지하기 위해 다음과 같이 작성하게 됩니다.

{user && user.profile && user.profile.contact && user.profile.contact.email}

이것을 옵셔널 체이닝을 이용하여 간단하게 할 수 있습니다.

{user?.profile?.contact?.email }

하지만 문제는 이 ?가 너무 많이 사용된다는 것입니다.


<p>{user?.profile?.contact?.email }</p>
<p>{user?.profile?.contact?.phone }</p>
<p>{user?.profile?.company?.name }</p>

?를 사용하지 않아도 되는 상황에서 사용하는 것은 가독성을 좋지않게만듭니다.
만약에 user의 값이 정말로 null이라면 어떻게 될까요?
그렇게 된다면, 비어있는 <p>태그만 출력되게 됩니다.

그런데 유저에게 비어있는 <p>태그를 보여주어야 할까요?
유저에게 비어있는 박스를 보여주는 것을 의도하였다면 앞에서 한 것처럼 ?를 일일히 붙여주는 것이 맞습니다.
하지만 유저에게 비어있는 박스를 보여 줄 필요가 없다면 다음과같이 ?를 사용하지 않아도 됩니다.

if(!user){
	return null;
}

<p>{user.profile?.contact?.email }</p>
<p>{user.profile?.contact?.phone }</p>
<p>{user.profile?.company?.name }</p>

저의 경우에는 값이 없을때 보여줄 필요가 없다면 화면에 출력을 하지 않게 하여 ? 사용을 줄이고, 다른 화면을 표시하거나 404페이지로 이동하게합니다.
주의할 것은 다음과같이 에러가 발생할 수 있습니다.

return 으로 인하여 모든 훅스가 렌더되지 않을 수 있기에 가급적 jsx를 return하기 전에 사용하고 있습니다.

타입사용

타입스크립트를 사용할때에 타입을 옳바르게 설정하는 것은 매우 중요합니다. 옳바르지 않은 타입을 사용한다면 오히려 개발할 때 불편함을 유발합니다. 때로는 타입이 지정되지 않아서 불편함을 겪습니다. 예제와 함께 제가 타입을 사용하는 방법들을 소개하도록 하겠습니다.

// 돔 접근하기
document.querySelector<HTMLDivElement>("div")

// axios
axios.get<CustomResponse>(url)

// promise
new Promise<Result>((resolve)=>())

// Object key index
const object:{[key:string]:number} = {what:0}

// useState
const [value,setValue] = useState<number>(0);

// useRef
const ref = useRef<HTMLDivElement>(null);
                    

리덕스

루트리듀서로부터 스토어의 타입을 얻을 수 있습니다.

export type RootState = ReturnType<typeof rootReducer>;

useSelector의 기본 타입을 RootState로 변경할 수 있습니다.


//* 타입 지원되는 커스텀 useSelector 만들기
declare module 'react-redux' {
  interface DefaultRootState extends RootState {}
}

커스텀 hoooks

자주 사용하는 기능들은 커스텀 훅스를 이용하여 간편하게 재사용하며, 코드가독성을 높일 수 있습니다.
저는 모달, 토스터, 에러 헨들러 등의 기능을 자주 사용하게 되었습니다.
ex)

const { openToast } = useToaster();
const { openModal } = useModal();
const { handleError } = useErrorHandler();

훅스를 이용하여 코드가 깔끔해 보이게 되었습니다.

isEmpty

배열을 사용하게 될때에 배열이 있으면서 배열의 값이 1개이상이어야 하는 경우가 많았습니다.

{array && array.length>0 && (

저는 isEmpty를 사용하여 이러한 구문을 대체했을때 가독성이 좋다는 느낌을 받아 isEmpty를 즐겨 사용합니다.

{!isEmpty(array)&&}

리덕스 툴킷

리덕스를 사용하신다면 리덕스 툴킷을 정말 필수라고 생각합니다.
기존에는 'typesafe-actions'를 사용하여 다음과 같이 사용하였습니다.

//타입정의
const SET_PROJECT = 'project/SET_PROJECT';

//액션생성
export const projectActions = {
  setProject: createStandardAction(SET_FOUCS_EVENT)<Project>()
}


//액션타입

type SetProjectAction = ReturnType<typeof projectActions.setProject>;
  
//리듀서   
    [SET_PROJECT]: (state, action: SetProjectAction) =>
      produce(state, draft => {
        draft.project = action.payload;
      }),

리덕스 툴킷을 사용한다면 같은 기능을 간단하게 만들 수 있습니다.

const project = createSlice({
  name: 'project',
  initialState,
  reducers: {
    setProject(state, action: PayloadAction<Project>) {
      state.project = action.payload;
    },
  }
})

스니펫 사용

'스니펫'이란 재사용 가능한 소스 코드를 의미한다. 즉, 자주 쓰는 코드를 저장해두고, 필요할 때마다 별칭을 통해 불러올 수 있다. 저는 반응형에서 동작하는 컴포넌트를 자주 만들고있습니다.
vscode에서는 이러한 스니펫을 지원하는데 언어별로 스니펫을 지정할 수 있습니다.

그중 css.json에서 반복되는 @media query문을 작성하는 번거로움을 줄이기 위해 스니펫을 사용하고 있습니다.
css.json

{
	"responsive medium snippet": {
	  "prefix": "medium",
	  "body": [
		"@media (max-width: 1024px) {",
		"\t${1: CssProperty}",
		"}"
	  ]
	}
  }

css 코드에서 medium이라고 작성하게 되면 지정해둔 코드가 나타나게 됩니다.

리액트 컴포넌트를 빠르게 만들기 위한 스니펫을 소개하도록 하겠습니다.
typescriptreact.json

{
	"react functional component": {
	  "prefix": "jerryFC",
	  "body": [
		"import React from 'react';",
		"",
		"const $TM_FILENAME_BASE: React.FC = () => {",
		"\treturn (",
		"\t\t<div>",
		"\t\t\thello world",
		"\t\t</div>",
		"\t);",
		"};",
		"",
		"export default $TM_FILENAME_BASE;"
	  ]
	},

스니펫을 사용하면 다음과같이 코드가 작성되어 빠르게 컴포넌트를 만들 수 있습니다.

import React from 'react';
import styled from 'styled-components';

const AuthInfo: React.FC = () => {
  return (
    <div>
      hello world
    </div>
  );
};

export default AuthInfo;

파일명과 동일한 리액트 컴포넌트 스니펫을 만들어주게 됩니다.

글로벌 타입

다음과같이 window의 타입을 확장할 수 있습니다.

declare global {
  interface Window {
    google: any;
    initMap: any;
  }
}

타입을 지원하는 라이브러리는 타입을 추가적으로 설치하여 사용할 수 있습니다.
ex) @types/googlemaps

profile
Basic in the end👻

1개의 댓글

comment-user-thumbnail
2021년 2월 17일

ts 스니펫 유용하게 잘 쓸게요! 감사합니다 😀

답글 달기