Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save shingonu/1c648f34253d04ca70eb3941506100de to your computer and use it in GitHub Desktop.
Save shingonu/1c648f34253d04ca70eb3941506100de to your computer and use it in GitHub Desktop.
12 tips for writing clean and scalable JavaScript

1. Isolate your code

topic과 분리해서 명백한 로직의 덩어리가 필요하다. 만약 함수를 작성하면 반드시 하나의 목적을 가지는 것으로 작성해야 한다. 하나의 함수에서 여러 목적의 기능이 들어가면 안된다.

또한 에러를 줄이기 위해서는 함수 내부에서만 상태 변경을 일으켜야 한다. 만약 함수 외부에서 상태를 변경시키고자 할 땐 함수에서 데이터를 리턴받아서 사용한다.

2. Modularization

만약 여러 개의 함수가 같은 방식으로 비슷한 것을 처리할 땐 하나의 모듈로 처리할 수 있다.

function add(a, b) {
    return a + b   
}

function subtract(a, b) {
    return a - b   
}

module.exports = {
    add,
    subtract
}
const { add, subtract } = require('./calculations')

console.log(subtract(5, add(3, 2))

3. Prefer multiple parameters over single object parameters

함수를 선언할 때 하나의 파라미터(object parameter)보단 여러 개의 파라미터들(specific parameters)을 사용하는 것이 항상 좋다.

// GOOD
function displayUser(firstName, lastName, age) {
    console.log(`This is ${firstName} ${lastName}. She is ${age} years old.`)
}

// BAD
function displayUser(user) {
    console.log(`This is ${user.firstName} ${user.lastName}. She is ${user.age} years old.`)
}

그것이 좋은 이유는 함수 선언부만 보고도 어떤 파라미터가 필요할지 정확히 알 수 있기 때문이다. 만약에 객체만 보내게 되면 함수의 파라미터가 어떻게 사용되는지 함수의 본문을 읽어야 한다.

하지만 이렇게 여러 개의 파라미터를 사용하는 것이 부작용이 있는 지점이 있다. 그 지점은 함수의 파라미터가 4개에서 5개 이상으로 늘어날 때이다. 이렇게 함수의 parameter가 많이 필요한 경우는 object parameter를 쓰는 것으로 바꿔야 한다.

파라미터들은 함수에서 정해진 순서대로 전달되어야 한다. 만약 optional parameter를 가지는 경우에는 undefined / null 파라미터를 넣을 경우도 있다. 만약 object 파라미터를 사용하면 너는 그냥 whole object를 전달하기만 하면 된다.

4. Destructuring

Destructuring은 ES6에 도입된 좋은 도구다. 이것은 object로부터 특정한 필드를 가지고 와서 변수에 할당한다.

// EXAMPLE FOR MODULES
const { add, subtract } = require('./calculations')

이런 것과 유사하게 함수의 파라미터로 object가 사용될 때 Destructuring을 사용할 수 있다.

function logCountry({name, code, language, currency, population, continent}) {
    let msg = `The official language of ${name} `
    if(code) msg += `(${code}) `
    msg += `is ${language}. ${population} inhabitants pay in ${currency}.`
    if(contintent) msg += ` The country is located in ${continent}`
}

logCountry({
    name: 'Germany',
    code: 'DE',
    language 'german',
    currency: 'Euro',
    population: '82 Million',
})


logCountry({
    name: 'China',
    language 'mandarin',
    currency: 'Renminbi',
    population: '1.4 Billion',
    continent: 'Asia',
})

object parameter를 함수에 넣지만 이러한 방식을 취함에 따라 함수에 어떤 파라미터가 필요한지 직관적으로 알 수 있다.

5. Use default values

Default values for destructuring or even basic function parameters are very useful. Firstly, they give you an example of what value you can pass to the function. Secondly, you can indicate which values are required and which are not. Using the previous example, the full setup for the function could look like this:

default value를 두는 것은 destructuring 또는 일반 함수 파라미터를 사용할 때 유용하다. 우선 함수에 어떤 값들을 넣을 수 있는지 예시로서 보여줄 수 있다. 또한 함수의 파라미터로서 optional한 건지 required한 건지 쉽게 보여줄 수 있다.

function logCountry({
    name = 'United States', 
    code, 
    language = 'English', 
    currency = 'USD', 
    population = '327 Million', 
    continent,
}) {
    let msg = `The official language of ${name} `
    if(code) msg += `(${code}) `
    msg += `is ${language}. ${population} inhabitants pay in ${currency}.`
    if(contintent) msg += ` The country is located in ${continent}`
}

logCountry({
    name: 'Germany',
    code: 'DE',
    language 'german',
    currency: 'Euro',
    population: '82 Million',
})


logCountry({
    name: 'China',
    language 'mandarin',
    currency: 'Renminbi',
    population: '1.4 Billion',
    continent: 'Asia',
})

명백하게 때론 default value를 사용하지 않고 error를 throw할 수도 있다.

6. Data scarcity

이전 팁을 통해 한 가지 결론을 내렸다. 필요하지 않은 데이터를 전달하지 말자. 특정 지점에서 어떤 값이 사용되는지 정확히 아는 것은 매우 중요하다.

7. Line and indentation limit

종종 코드를 보면 3,000줄이 넘어가는 파일을 볼 수 있다. 이런 파일을 볼 땐 logic을 찾기 매우 힘들다. 그렇기 때문에 file size를 의도적으로 제한하기도 한다. 만약 코드가 100줄이 넘어간다면 새 모듈과 파일을 주저하지 말고 만들자. 마치 알프스 산맥처럼 하나의 파일이 거대한 산맥을 이루도록 만드는 것은 불필요할 수 있다.

8. Use prettier

eslint를 그 때마다 해주지 말고 prettier를 사용해서 코드를 형식화 하자. prettier를 사용하면 코드를 작성할 때 formatting을 자동적으로 해주기 때문에 이에 대해 큰 걱정을 할 필요가 없다.

9. Use meaningful variable names

이상적으로 변수 / 함수의 이름은 컨텐트의 내용을 담고 있어야 한다.

Functions

함수는 일반적으로 어떠한 액션을 수행한다. 그렇기 때문에 함수의 이름은 동사로 짓는 것이 좋다.

e.g., convertCurrency or displayUserName.

Arrays

These will usually hold a list of items; therefore, append an s to your variable name. For example:

const students = ['Eddie', 'Julia', 'Nathan', 'Theresa']

Booleans

Simply start with is or has to be close to natural language. You would ask something like, “Is that person a teacher?” → “Yes” or “No.” Similarly:

const isTeacher = true // OR false

Array functions

forEach, map, reduce, filter, etc. are great native JavaScript functions to handle arrays and perform some actions. I see a lot of people simply passing el or element as a parameter to the callback functions. While this is easy and quick, you should also name these according to their value. For example:

const cities = ['Berlin', 'San Francisco', 'Tel Aviv', 'Seoul']
cities.forEach(function(city) {
...
})

IDs

Oftentimes, you have to keep track of the ids of specific datasets and objects. When ids are nested, simply leave it as id. Here, I like to map MongoDB _idto simply id before returning the object to the frontend. When extracting ids from an object, prepend the type of the object. For example:

const studentId = student.id
// OR
const { id: studentId } = student // destructuring with renaming

An exception to that rule is MongoDB references in models. Here, simply name the field after the referenced model. This will keep things clear when populating references documents

10. Use async / await where possible

callback은 가독성을 안좋게 만든다. promise라는 좋은 툴이 있지만 이 또한 가독성이 떨어진다. 그렇기 때문에 왠만하면 async / await를 사용한다.

11. Module import order

tips1과 2에서 보았듯이, 적절한 장소에 logic을 두는 것은 maintainability를 지키는 핵심이다. 이러한 것처럼, 다른 모듈을 어떻게 import 시키냐도 코드를 봤을 때의 혼동을 줄일 수 있다.

// 3rd party packages
import React from 'react'
import styled from 'styled-components'

// Stores
import Store from '~/Store

// reusable components
import Button from '~/components/Button'

// utility functions
import { add, subtract } from '~/utils/calculate'

// submodules
import Intro from './Intro'
import Selector from './Selector'

12. Get rid of console

console은 디버깅하는데 유용한 도구이지만, 이것이 너무 많으면 코드의 혼잡을 일으킨다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment