👨‍💻

Clean Code

아래의 글은 다음 글을 참고, 해석해서 작성한 글입니다

Clean Code 의 의미

"Write code for your later self and for your co-workers in the first place - not for the machine." via @RisingStack 기계가 작동하게 하는 것이 아니라, 이후의 나와 동료들을 첫번째로 생각해서 짜는 코드
"궁극적으로 코드는 요구사항을 표현하는 언어라는 사실을 명심한다" via Clean Code - Robert C. Martin
결론 ⇒ 사람이 쉽게 이해할 수 있고, 충분히 예측 가능한 형태로(일반적으로) 구성된 코드

변수, 함수명을 어떻게 지어야할까?

의도가 명확한 이름을 사용하자

이름이 길어지는 것을 걱정하지 말고 의도가 명확히 드러나는 이름을 사용한다

의도가 명확하면 검색하기 쉬워진다

리팩토링 및 코드를 찾을 때 검색하기 쉬워질 것이다
// DON'T let d let elapsed const ages = arr.map((i) => i.age) // DO let daysSinceModification const agesOfUsers = users.map((user) => user.age)
JavaScript
복사

의미 있는 단어를 사용하고, 필요없는 명사 및 기타 용어는 제외한다

// DON'T let nameString let theUsers // DO let name let users // DON'T let fName, lName let cntr let full = false if (cart.size > 100) { full = true } // DO let firstName, lastName let counter const MAX_CART_SIZE = 100 // ... const isFull = cart.size > MAX_CART_SIZE
JavaScript
복사

변수명에 데이터 타입 등 너무 많은 정보를 넣지 마라

변수명에는 이미 입력할 정보가 많다. 가능하다면 데이터 유형을 pre, suffix 하지 말자. 만약 데이터 유형이 바뀐다면 변수명도 전부 바꿀 것인가?? 물론 javascript 같이 데이터 타입이 모호한 경우가 있긴 하지만 타입을 지정하는 언어라면 가능하면 안사용하는 것이 좋을 것 같다.

함수는 어떻게 만들어야할까?

하나의 함수는 추상적인 레벨에서 오직 하나의 작업만 수행한다.

// DON'T function getUserRouteHandler (req, res) { const { userId } = req.params // inline SQL query knex('user') .where({ id: userId }) .first() .then((user) => res.json(user)) } // DO // User model (eg. models/user.js) const tableName = 'user' const User = { getOne (userId) { return knex(tableName) .where({ id: userId }) .first() } } // route handler (eg. server/routes/user/get.js) function getUserRouteHandler (req, res) { const { userId } = req.params User.getOne(userId) .then((user) => res.json(user)) }
JavaScript
복사

길다고 해도, 설명이 가능한 이름을 사용하자

함수명은 동사, 동사구여야하고, 함수의 의도를 전달할 뿐만 아니라, 인자(arguments)들의 의도도 잘 전달해야한다.
짧지만 알 수 없는 함수명보다는 길어도 설명적인 이름이 좋다
// DON'T /** * Invite a new user with its email address * @param {String} user email address */ function inv (user) { /* implementation */ } // DO function inviteUser (emailAddress) { /* implementation */ }
JavaScript
복사

함수의 인자를 너무 많이 만들지 않는다.

함수의 인자를 많이 쓰는 것 보다는 하나의 Object 인자를 전달하고, 이것을 destrunturing 하는 것이 옵션적인 매개변수를 다루기 더 쉽다.
// DON'T function getRegisteredUsers (fields, include, fromDate, toDate) { /* implementation */ } getRegisteredUsers(['firstName', 'lastName', 'email'], ['invitedUsers'], '2016-09-26', '2016-12-13') // DO function getRegisteredUsers ({ fields, include, fromDate, toDate }) { /* implementation */ } getRegisteredUsers({ fields: ['firstName', 'lastName', 'email'], include: ['invitedUsers'], fromDate: '2016-09-26', toDate: '2016-12-13' })
JavaScript
복사

부수효과(side-effects)를 최소화한다

순수 함수를 통해서 부수효과를 최소화한다. 이는 안정성과 모듈화 및 테스트에 매우 큰 이점이 될 수 있다.
// DON'T function addItemToCart (cart, item, quantity = 1) { const alreadyInCart = cart.get(item.id) || 0 cart.set(item.id, alreadyInCart + quantity) return cart } // DO // not modifying the original cart function addItemToCart (cart, item, quantity = 1) { const cartCopy = new Map(cart) const alreadyInCart = cartCopy.get(item.id) || 0 cartCopy.set(item.id, alreadyInCart + quantity) return cartCopy } // or by invert the method location // you can expect that the original object will be mutated // addItemToCart(cart, item, quantity) -> cart.addItem(item, quantity) const cart = new Map() Object.assign(cart, { addItem (item, quantity = 1) { const alreadyInCart = this.get(item.id) || 0 this.set(item.id, alreadyInCart + quantity) return this } })
JavaScript
복사

한 파일 내에서 함수들을 스탭다운 룰 을 적용해서 구성한다

다른 함수를 사용하는 고위(high-level) 함수를 상단에, 사용되는 (low-level) 함수는 아래쪽에 배치한다. 위 구성은 코드를 읽기 자연스럽게 만든다.
// DON'T // "I need the full name for something..." function getFullName (user) { return `${user.firstName} ${user.lastName}` } function renderEmailTemplate (user) { // "oh, here" const fullName = getFullName(user) return `Dear ${fullName}, ...` } // DO function renderEmailTemplate (user) { // "I need the full name of the user" const fullName = getFullName(user) return `Dear ${fullName}, ...` } // "I use this for the email template rendering" function getFullName (user) { return `${user.firstName} ${user.lastName}` }
JavaScript
복사

질문, 혹은 수정(수행)

함수는 무언가를 수행하거나 무언가에 대답해야한다. 그러나 두개를 동시에 하지는 않는다.

주석

개발자들은 대부분(아마도?) 코드나 프로젝트에 대해서 따로 문서화를 하지 않는다. 때문에 코드 자체가 문서가 되는 것이 제일 좋다고 생각한다. 주석을 잘 달려고 하는 것 보다 코드가 의미가 더 명확하게 보이도록 하는데 노력을 하는 것이 좋다. 과연 개발자들이 수정된 코드에 주석까지 관리를 해나갈 것인가 생각해보면 그 이유를 알 수 있다. 결국 마지막 까지 남는 것은 코드다! 그럼에도 불구하고 다른 개발자를 위해서 추가적인 설명이 필요하다고 생각되는 경우, 아래와 같이 주석을 달자.
각 함수나 모듈은 ScriptDoc 의 형식을 참고한다.
형식이 있다고 무조건 다는 것 보다는 저작권 표시 및 코드 외적인 사이드 이펙트에 대해서 주의 사항 등만 표시하는 것이 좋다. 로직에 대한 설명 등에 대한 것은 그냥 코드에 명시적으로 하는 것이 좋다