개발을 하다보면 변수를 어떻게 지을지 고민할 때가 있다.
비슷한 의미로 사용하다보면 다음에 맡을 사람에게 큰 고민거리가 될 것 같다.
// ex)
router.post('api/notices/', async (req,res) => {
const { target } = req.body;
// target은 개인이 될 수도, 전체가 될 수도, 그룹이 될 수도 있다.
});
여기서 방법은 다양할 것 같다.
여기서 변수의 이름을 어떻게 지어야 좋을 지 찾아보던 중 kettanaito/naming-cheatsheet 를 보고 공유하면 좋을 것 같았다.
무언가에 대해 이름을 짓는 건 어렵다. 지금부터 쉽게 짓는 연습을 해보자.
여기서 제안하는 방법이 모든 프로그래밍 언어에 적용되진 않지만 자바스크립트에서 사용하기엔 괜찮다고 본다.
변수나 함수의 이름을 지을 때, 영어를 쓰자.
/* Bad */
const primerNombre = 'Gustavo'
const amigos = ['Kate', 'John']
/* Good */
const firstName = 'Gustavo'
const friends = ['Kate', 'John']
좋든 싫든 영어는 프로그래밍에 있어 가장 보편적인 언어다. 모든 프로그래밍 언어의 문법또한 영어를 기초로 하고 있다. 영어로 작성된 코드는 응집력(이해력)을 상승시킨다.
이름을 지을 때는 CamelCase, PascalCase, snake_case, 등 그 밖에 규칙들을 이용해서 지어라. 그리고 한가지 규칙만을 주로 이용해서 코드를 작성하라.
/* Bad */
const page_count = 5
const shouldUpdate = true
/* Good */
const pageCount = 5
const shouldUpdate = true
/* Good as well */
const page_count = 5
const should_update = true
개인적으로는 camelCase를 사용하는게 편했다.
이유는 javascript eslint에서 snake_case를 이용하면 warning이 나오기도 하고 그밖에 코드를 짜다보면 습관이 되서 그런 것 같다.
이름은 짧고, 명쾌하고, 설명이 가능해야 된다.
짧다 - 이름은 입력하는데 길면 불편하다 그러니 기억하는 편이 좋다.
명쾌하다 - 읽기 자연스럽고 공통적으로 사용되는 단어를 이용해라
설명가능하다 - 단어는 반드시 효율적인 방법으로 이루어 져야한다.
/* Bad */
const a = 5 // "a" could mean anything
const isPaginatable = a > 10 // "Paginatable" sounds extremely unnatural
const shouldPaginatize = a > 10 // Made up verbs are so much fun!
/* Good */
const postCount = 5
const hasPagination = postCount > 10
const shouldPaginate = postCount > 10 // alternatively
함축적인 표현을 피하라. 코드의 가독성을 저하시키지 말자. 짧고, 설명 가능한 이름으로 작성해보자. 어려울 것으로 보이지만 함축적인 푷현은 다른사람들에게도 이해를 시키는데 시간을 쓰게 한다.
/* Bad */
const onItmClk = () => {}
/* Good */
const onItemClick = () => {}
이미 정의된 맥락에서 또다시 함수 이름에 표시되는 건 피하자. 항상 가독성에 저하가 되는지 구분하고 있다면 제거하자.
class MenuItem {
/* Method name duplicates the context (which is "MenuItem") */
handleMenuItemClick = (event) => { ... }
/* Reads nicely as `MenuItem.handleClick()` */
handleClick = (event) => { ... }
}
이름은 반드시 예상된 결과를 노출해야 된다.
/* Bad */
const isEnabled = itemCount > 3
return <Button disabled={!isEnabled} />
/* Good */
const isDisabled = itemCount <= 3
return <Button disabled={isDisabled} />
어느정도 동의한다.
그러나 버튼이 2개 이상이 있고 사용가능한지 여부에 따라 갈린다면 굳이 변수를 2개 만들 필요는 없다고 본다.
이 때, 나라면 isEnabled로만 구분을 할 것 같다.
이름 짓기 유용한 패턴을 보자
prefix? + action (A) + high context (HC) + low context? (LC)
이 패턴이 어떻게 적용되는지 테이블로 구분해보자
name | prefix | action (A) | high context (HC) | low context (LC) |
---|---|---|---|---|
getUser | get | User | ||
getUserMessages | get | User | Messages | |
handleClickOutside | handle | Click | Outside | |
shoudDisplayMessage | should | Display | Message |
context의 순서는 변수의미에 영향을 끼친다. 예를 들어보면 shouldUpdateComponent
는 당신이 컴포넌트를 업데이트 시키겠다는 의미가 강하지만 shouldComponentUpdate
는 컴포넌트가 스스로 업데이트 하겠다는 의미가 강하다. 따라서 high context에 따라 의미가 바뀐다.
앞으로 이부분에 중점을 두고 지어야 겠다.
함수 이름의 동사 부분이다. 함수가 수행할 것에 대해 설명하는 게 드러나야 한다.
get
데이터를 즉시 접근할 때, ( 내부 데이터로 부터 get 할 떄, )
function getFruitCount() {
return this.fruits.length
}
set
명시된 방법으로 변수를 넣을 때,
let fruits = 0
function setFruits(nextFruits) {
fruits = nextFruits
}
setFruits(5)
console.log(fruits) // 5
reset
값이나 상태를 되돌릴 때,
const initialFruits = 5
let fruits = initialFruits
setFruits(10)
console.log(fruits) // 10
function resetFruits() {
fruits = initialFruits
}
resetFruits()
console.log(fruits) // 5
fetch
일정 시간동안 걸려 데이터를 요청할 때, ( 비동기 통신 등 )
function fetchPosts(postCount) {
return fetch('https://api.dev/posts', {...})
}
remove
요소를 빼내거나 지울 때,
예를 들면, 검색 페이지에서 필터 요소가 있다고 하자. 만약 필터를 제거하고 싶을 때 removeFilter
가 어울리지 deleteFilter
는 삭제한다는 의미가 강하다. ( 영어의 의미에 따라 사용을 다르게 하자 )
function removeFilter(filterName, filters) {
return filters.filter((name) => name !== filterName)
}
const selectedFilters = ['price', 'availability', 'size']
removeFilter('price', selectedFilters)
delete
존재하고 있는 실체를 완전히 지울 떄,
예를 들면, 클라우드에 이미지를 업로드 했다고 하자. 거기서 특정 이미지를 제거하고 싶다. 이 때, deleteImage
가 removeImage
보다 더 낫다.
function deletePost(id) {
return database.find({ id }).delete()
}
compose
이미 존재하는 데이터로 부터 새로운 데이터를 만들어 낼 때, string, object, function에 어울린다.
function composePageUrl(pageName, pageId) {
return (pageName.toLowerCase() + '-' + pageId)
}
handle
액션을 다룰 때, 콜백 메서드로 자주 사용한다.
function handleLinkClick() {
console.log('Clicked a link!')
}
link.addEventListener('click', handleLinkClick)
영역의 의미를 보고 동작에 대해 살펴보자
이미 리스트에 대한 필터를 한다고 했을 때, filter만 적으면 되지 filterArray로 적을 필요가 없다.
/* A pure function operating with primitives */
function filter(list, predicate) {
return list.filter(predicate)
}
/* Function operating exactly on posts */
function getRecentPosts(posts) {
return filter(posts, (post) => post.date === Date.now())
}
Prefixes는 변수의 의미를 강화시켜줄 때가 있는데 함수 이름에서는 거의 사용되지 않는다.
is
현재 상태 값을 표현할 때, 자주 쓴다 (boolean)
const color = 'blue'
const isBlue = color === 'blue' // characteristic
const isPresent = true // state
if (isBlue && isPresent) {
console.log('Blue is present!')
}
has
상태나 값에서 이미 가지고 있을 때,
/* Bad */
const isProductsExist = productsCount > 0
const areProductsPresent = productsCount > 0
/* Good */
const hasProducts = productsCount > 0
array에서 특정 string을 포함할 때, 사용해도 괜찮은 것 같다.
should
상태가 변경되어야 할 때,
function shouldUpdateUrl(url, expectedUrl) {
return url !== expectedUrl
}
min
/ max
/**
* Renders a random amount of posts within
* the given min/max boundaries.
*/
function renderPosts(posts, minPosts, maxPosts) {
return posts.slice(0, randomBetween(minPosts, maxPosts))
}
prev
/ next
function fetchPosts() {
const prevPosts = this.state.posts
const fetchedPosts = fetch('...')
const nextPosts = concat(prevPosts, fetchedPosts)
this.setState({ posts: nextPosts })
}
prefix 처럼 단수 복수도 변수를 상징하는데에 중요한 역할을 한다.
/* Bad */
const friends = 'Bob'
const friend = ['Bob', 'Tony', 'Tanya']
/* Good */
const friend = 'Bob'
const friends = ['Bob', 'Tony', 'Tanya']
이 부분이 지켜져야 협업을 한다해도 Object가 오는 지, Object Array가 오는 지 구분이 되기 때문이다.
좋은 글 감사합니다:)