공통
제외 예정이거나(deprecated) 제외된(obsolute) 기능, 비표준 구문은 사용하지 않는다.
항상 적용할 순 없지만 가능하면 SOLID 규칙을 따르는 것을 권장한다.
SOLID (객체 지향 설계) - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. -->
ko.wikipedia.org
쓰나 안 쓰나 결과에 영향을 미치지 않는 코드는 최대한 제거한다. 작성된 코드엔 항상 작성된 이유가 있는 것이 좋다.
JavaScript
주석
짧은 1줄 주석(// ...)은 가능하면 변수, 함수 등의 이름에 반영하는 것을 권장한다.
다른 사람의 이해를 돕기 위해 주석을 다는 것을 권장한다.
주석엔 복잡한 로직에 대한 설명 등을 담을 수 있다.
네이밍
- PascalCase : type, interface, React component(function, class)
- camelCase : variable(const, let), JavaScript function, custom hook, props
- UPPER_SNAKE_CASE : tuple(as const 등)
- 어떤 값으로부터 다른 값을 계산하는 함수 : get___From( ... )
- e.g. getTodayFrom(date: Date, index: number)
중괄호
if ( ) {
...
} else if ( ) {
...
} else {
...
}
function functionName() {
...
}
원활한 수정과 불필요한 diff 생성을 방지하기 위해 for, if, function 등에서 중괄호는 생략하지 않는다.
import
import ... from ... 문의 순서는 신경쓰지 않는다. (vscode '빠른 수정' 기본값은 패키지 이름 기준 ABC순)
함수 정의
익명 함수(anonymous function)는 화살표 함수로(arrow function), 이름 있는 함수(named function)는 function 키워드로 정의한다.
화살표 함수 - JavaScript | MDN
화살표 함수 표현(arrow function expression)은 전통적인 함수표현(function)의 간편한 대안입니다. 하지만, 화살표 함수는 몇 가지 제한점이 있고 모든 상황에 사용할 수는 없습니다.
developer.mozilla.org
함수 - JavaScript | MDN
보통 함수란 자신의 외부(재귀 함수의 경우 스스로) 코드가 '호출'할 수 있는 하위 프로그램입니다. 프로그램과 마찬가지로, 함수 역시 명령문의 시퀀스로 구성된 함수 본문을 가집니다. 함수에
developer.mozilla.org
함수는 언제 만드는 것이 좋을까? 중복되는 코드가 있을 때? 너무 많이 만들어도 가독성이 떨어진다.
undefined와 falsy 값
변수에 직접 undefined를 할당하는 것은 그냥 없는 변수로 만든다는 뜻이어서 주의해서 사용해야 한다.
어떤 값이 존재하지 않음을 나타낼 땐 아래와 같이 각 자료형 별 falsy 값을 이용한다.
자료형 | falsy |
정수 | 0 |
불린 | false |
문자열 | '' |
객체 | null |
배열 | [].length |
물론 0이나 false가 의미있는 값이면 정수나 불린 자료형에도 null을 사용할 수 있다. 그리고 빈 배열 자체는 truthy 값이다.
// 매개변수 초기값 설정에서 null과 undefined의 차이
function hello(name = 'World') {
console.log(`Hello, ${name}!`)
}
hello(null) // Hello, null!
hello(undefined) // Hello, World!
기타
// 문자열 → 숫자
const str = '1000000'
const str2num = +str // 또는 Number(str)
// 숫자 → 문자열
const num = 1_000_000
const num2str = `${num}`
// 문자열 합치기
const str2 = '1 문자열'
const stringConcatination = `${str}과 ${num}, ${str2}` // '1000000과 1000000, 1 문자열'
// null, undefined 체크는 || 대신 ?? 사용
const nullableString: string | null = getNullableString()
const result = `${nullableString ?? ''}`
React
컴포넌트 정의
// FunctionComponent.tsx
type Props = {
...
}
function FunctionComponent({ ... }: Props) {
...
}
export default FunctionComponent
위와 같이 CRA 템플릿에 있는 방식으로 컴포넌트를 정의한다.
GitHub - facebook/create-react-app: Set up a modern web app by running one command.
Set up a modern web app by running one command. Contribute to facebook/create-react-app development by creating an account on GitHub.
github.com
클래스 컴포넌트의 사용은 지양한다. 클래스 컴포넌트는 함수 컴포넌트에서 지원하지 않는 일부 컴포넌트 생명주기 함수를 사용하기 위해서 사용한다.
React 17에선 번들 사이즈 최적화를 위해 컴포넌트를 정의할 때 import React from 'react'문을 넣지 않는 것을 권장한다.
컴포넌트 props의 자료형은 PropTypes로 정의하거나 TypeScript로 정의할 수 있다.
TypeScript는 자료형을 정적으로 검사하지만, PropTypes는 동적으로 검사한다는 차이가 있다.
근데 TypeScript가 자동으로 PropTypes 코드를 생성한다고 하니 TypeScript만 사용해도 문제없다.
그리고 동적 자료형 검사는 실행 시 약간의 오버헤드가 생긴다.
컴포넌트 props의 기본값을 정의할 땐 defaultProps 대신 ES6의 매개변수 기본값을 사용한다.
기본값 매개변수 - JavaScript | MDN
기본값 함수 매개변수 (default function parameter)를 사용하면 값이 없거나 undefined가 전달될 경우 이름붙은 매개변수를 기본값으로 초기화할 수 있습니다.
developer.mozilla.org
defautProps는 나중에 deprecated될 예정이라고 한다.
type Props = {
prop1: number | undefined
prop2?: number
}
prop1은 컴포넌트에 명시적으로 전달해야 하지만 number 또는 undefined 일 수 있는 값이고, prop2는 전달하지 않아도 되는 number형 데이터다.
이 props가 컴포넌트 내부에서 매개변수로 활용될 때의 자료형은 둘 다 number | undefined로 동일하다.
메모이제이션
컴포넌트 props로 넘겨주는 값은 useMemo()를 통해, 함수는 useCallback()를 통해 메모이제이션하는 것을 권장한다. 만약 props를 받는 컴포넌트가 memo()로 감싸진 컴포넌트(순수 컴포넌트)면 무조건 메모이제이션을 해야 한다.
React.memo() 현명하게 사용하기
유저들은 반응이 빠른 UI를 선호한다. 100ms 미만의 UI 응답 지연은 유저들이 즉시 느낄 수 있고, 100ms에서 300ms가 지연되면 이미 유저들은 상당한 지연으로 느낀다. UI 성능을 증가시키기 위해, React
ui.toast.com
이벤트 핸들러 네이밍
import { MouseEvent as ReactMouseEvent, useCallback, useState } from 'react'
function FunctionComponent() {
const [searchTerm, setSearchTerm] = useState('')
// 이벤트 핸들러 함수
const handleClickSearchButton = useCallback(
(e: ReactMouseEvent<HTMLElement, MouseEvent>) => {
...
},
[...]
})
const handleChangeSearchTerm = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
setSearchTerm(e.target.value)
},
[],
)
// 이벤트 핸들러 prop
return (
<>
<SearchInput searchTerm={searchTerm} onChange={handleChangeSearchTerm} />
<SearchButton onClick={handleClickSearchButton} />
</>
)
}
export default FunctionComponent
이벤트 핸들러를 받는 prop 이름은 on___으로 짓고, 이벤트 함수 이름은 handle___로 짓는다.
Custom Hook 정의
// use___.ts
type Options = {
...
}
function use___({ ... }: Options) {
...
}
export default use___
Custom Hook의 이름은 use___로 지정한다. Custom Hook 안에선 JSX 사용을 지양하고, 다른 React Hook이나 JavaScript 로직 위주로 작성한다.
자신만의 Hook 만들기 – React
A JavaScript library for building user interfaces
ko.reactjs.org
상태 관리
웬만하면 전역 상태 대신 지역 상태로 처리하는 것을 권장한다. 이유는 다른 언어에서 전역 변수 대신 지역 변수 사용을 권장하는 것과 비슷하다. 전역 상태를 사용하려면 React Context API를 사용한다. (Redux, Mobx 대신 React Context만 쓸지 논의 필요)
한 상태를 여러 컴포넌트가 공유해야 할 땐 상태 관리 로직을 공통된 상위 컴포넌트로 올리는 것을 권장한다.
Next
페이지 컴포넌트
// pages/index.tsx
function HomePage() {
return <div>Welcome to Next.js!</div>
}
export default HomePage
위와 같이 pages 폴더에 포함된 페이지 컴포넌트 이름은 ___Page로 지정한다.
Getting Started | Next.js
Get started with Next.js in the official documentation, and learn more about all our features!
nextjs.org
페이지 컴포넌트 경로는 원활한 수정과 불필요한 diff 생성을 방지하기 위해 [id].tsx 대신 항상 [id]/index.tsx 파일을 통해 동적 라우팅을 정의한다.
Styled Components
모든 styled 변수는 해당 컴포넌트 파일에 정의하는 것을 권장한다.
다른 컴포넌트랑 같은 css를 공유할 땐 export를 활용한다? (아님 src/components/layouts에 모아 놓을까?)
Wrapper
import styled from 'styled-components'
import { ThirdPartyStyledComponent } from 'third-party-css-library'
const StyledComponent = styled(ThirdPartyStyledComponent)`
margin: 1rem;
...
`
DOM 트리가 깊어질수록 렌더링 속도가 느려지기 때문에 외부 CSS 라이브러리에 있는 컴포넌트는 Wrapper 컴포넌트를 사용하지 않고 가능하면 해당 컴포넌트를 상속해 스타일을 적용하는 것을 권장한다.
Avoid an excessive DOM size - Chrome Developers
Learn how a large DOM can reduce your web page's performance and how you can reduce the size of your DOM at load time.
developer.chrome.com
Apollo GraphQL
Apollo Codegen을 위해 Query와 Mutation의 이름(operation 이름)은 익명이지 않고 유일해야 한다.
Developer tools
Improve your developer experience with these services and extensions
www.apollographql.com
useQuery의 fetchPolicy는 cache-first를 사용하고, 캐시 값을 최신 상태로 업데이트 해야 하면 refetch를 사용한다.
캐시 값을 최신 상태로 업데이트하기 위해서 fetchPolicy: network-only를 사용하진 않는다.
gql 변수
// src/graphql/queries/User.ts
const GET_USER = gql`
query User($id: Int!) {
user(id: $id) {
id
name
}
}
`
export default GET_USER
모든 gql 변수는 src/graphql 폴더 안의 fragments, mutations, queries 중 적절한 곳에 정의한다.
파일 이름은 operation 이름이랑 일치시킨다.
client query는 /operations/queries/client/ 에 정의한다.
- gql 변수 이름 : UPPER_SNAKE_CASE
- operation 이름 : PascalCase 명사형 (Apollo Codegen이 operation 이름 단위로 type을 생성하기 때문에)
- GraphQL 필드 이름 : camelCase
Fragment
GraphQL 필드 중 반복되는 필드 집합은 Fragment로 정의하는 것을 권장한다. 중복되는 로직을 함수, React 컴포넌트로 정의하는 맥락과 비슷하다.
CSS
Wrapper, Container 네이밍 규칙
CSS Language Speak: Container vs Wrapper?
What's the difference between container and wrapper? And what is meant by each?
stackoverflow.com
프로퍼티 작성 순서
CSS 속성 순서 | Today Yurim Learned
CSS 속성 순서 Intro CSS 속성은 보통 편한대로 적을 수 있다. 왜냐하면 -속성이 겹치면 뒤에 있는 것이 적용-된다는 것 말고는 딱히 규칙이 없으니. 그래서 다른 사람이 작성해 둔 CSS Stylesheet를 보면
milooy.github.io
CSS Reset 통일해보기
2022 CSS Reset 다시 써보기!
..2022년 지금은 CSS Reset을 어떻게 만들어야 할까? 하는 생각이 들었습니다. 그래서 여러가지 관련자료와 애플, 네이버, 다음, 인프런, 오늘의 집, MS, 에어비엔비, 디스코드, 슬랙등의 CSS를 보면서
velog.io