Clean Code
🔗 Junior Vs Senior Code - How To Write Better Code - Part 2를 보고 정리한 내용입니다.
3. Async/Await
Step 1️⃣
콜백 함수 - 콜백이 계속 중첩되는 🔥콜백 지옥🔥
const readline = require("readline")
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout
})
readlineInterface.question("What is your name? ", name => {
readlineInterface.question("What is your job? ", job => {
readlineInterface.question("How old are you? ", age => {
console.log("Hello " + name + ". You are a " + age + " year old " + job + ".")
readlineInterface.close()
})
})
})
Step 2️⃣
Async/Await - 콜백이 중첩되는 문제 해결
const readline = require("readline")
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout
})
async function main() {
const name = await askQuestion(realineInterface, "What is your name? ")
const job = await askQuestion(realineInterface, "What is your job? ")
const age = await askQuestion(realineInterface, "How old are you? ")
console.log("Hello " + name + ". You are a " + age + " year old " + job + ".")
readlineInterface.close()
}
main()
function askQuestion(readlineInterface, question) {
return new Promise(resolve => {
readlineInterface.question(question, answer => {
resolve(answer)
})
})
}
Step 3️⃣
readlineInterface
리팩토링, 함수 분리 - 모듈화
// async.js
const askQuestion = require("./askQuestion")
async function main() {
const name = await askQuestion("What is your name? ")
const job = await askQuestion("What is your job? ")
const age = await askQuestion("How old are you? ")
console.log(`Hello ${name}. You are a ${age} year old ${job}.`)
}
main()
// askQuestion.js
const readline = require("readline")
function askQuestion(question) {
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout
})
return new Promise(resolve => {
readlineInterface.question(question, answer => {
resolve(answer)
readlineInterface.close()
})
})
}
module.exports = askQuestion
4. Single Responsibility Principle(SRP, 단일 책임 원칙)
SOLID: Single Responsibility Principle in JavaScript
"class(a.k.a module) 은 오직 한 가지 이유 때문에 변경되어야 한다."
Robert C. Martin
SRP는 객체 지향 언어에 국한되어 있지 않고, 프로토타입 상속을 하는 자바스크립트에도 적용된다.
하나의 class는 오직 한 가지 책임만 가져야 한다(단일 책임 원칙)
Employee class는 SRP 원칙에 따라 여러 class로 분리되어야 한다.
// 나쁜 예시 // 💣 함수 변경 시 class 내부의 코드 변경 해야 함 class Employee { calculatePay() { //... } reportHours() { //... } saveToDB() { //... } }
Step 1️⃣
의도하지 않은 🚫side effects🚫 존재
// users.js
function createUser(user) {
return {
...user,
id: Date.now(),
createdAt: new Date(),
updatedAt: new Date()
}
}
function updateUser(user) {
return {
...user,
updatedAt: new Date()
}
}
module.exports = { createUser, updateUser }
// main.js
const { updateUser, createUser } = require('./api/users')
function saveUser(user) {
// 🚫 validation
// 🚫 error printing
const errors = []
if (user.username) {
if (user.username.length < 3) {
errors.push("Username must be 3 or more characters")
}
} else {
errors.push("Username is required")
}
if (user.password) {
if (user.password.length < 8) {
errors.push("Password must be 8 or more characters")
}
} else {
errors.push("Password is required")
}
if (errors.length > 0) {
errors.forEach(error => console.error(error))
return
}
// save users
if (user.id == null) {
console.log('Created User')
createUser(user)
} else {
console.log('Updated User')
updateUser(user)
}
}
const user = {
username: '',
password: 'password'
}
saveUser(user)
Step 2️⃣
기능별 함수 분리(
validateUser
,saveUser
)💣 validation 관련 코드 반복(비효율적)
// main.js
const { updateUser, createUser } = require('./api/users')
// 1️⃣
function saveUser(user) {
if(user.id == null) {
console.log("Created User")
createUser(user)
} else {
console.log("Updated User")
updateUser(user)
}
}
// 2️⃣
function validateUser(user) {
return [
...validateUsername(user.username),
...validatePassword(user.password)
]
}
// --- helper function (returns an array of errors) ---
function validateUsername(username) {
const errors = []
if(!username) errors.push("Username is required")
if(username != null && username.length < 3) {
errors.push("Username must be 3 or more characters")
}
return errors
}
function validatePassword(password) {
const errors = []
if(!password) errors.push("Password is required")
if(password != null && password.length < 8) {
errors.push("Password must be 8 or more characters")
}
return errors
}
// -----------------------------------------------------
const user = {
username: 'USER',
password: 'password'
}
// 2️⃣
const errors = validateUser(user)
if(errors.length > 0) {
errors.forEach(error => console.error(error))
return
}
// 1️⃣
saveUser(user)
Step 3️⃣
🔑 SRP(단일 책임 원칙) 충족하도록 validation 관련 코드 분리
// validation.js
// 1️⃣
function validationMessages(validations, object) {
return Object.entries(validations).reduce((errors, [property, requirements]) => {
errors[property] = []
if (requirements.required) {
const errorMessage = validateRequiredMessage(object[property])
if (errorMessage) errors[property].push(errorMessage)
}
if (requirements.length) {
const errorMessage = validateLengthMessage(object[property], requirements.length)
if (errorMessage) errors[property].push(errorMessage)
}
return errors
}, {})
}
// --- helper function (returns error messages) ---
function validateLengthMessage(value, length) {
if (value == null) return
if (value.length >= length) return
return `must be ${length} or more characters`
}
function validateRequiredMessage(value) {
if (value) return
return 'is required'
}
// ---------------------------------------------------
// 2️⃣
function printErrors(errors) {
Object.entries(errors).forEach(([property, messages]) => {
messages.forEach(message => {
console.error(`${property} ${message}`)
})
})
}
module.exports = {
validationMessages,
printErrors
}
// main.js
const { updateUser, createUser } = require('./api/users')
const { validationMessages, printErrors } = require('./pro/validation')
function saveUser(user) {
if (user.id == null) {
console.log('Created User')
createUser(user)
} else {
console.log('Updated User')
updateUser(user)
}
}
function validateUser(user) {
// hash of validations
const validations = {
username: {
required: true,
length: 3
},
password: {
required: true,
length: 8
}
}
const errors = validationMessages(validations, user)
return {
valid: Object.values(errors).every(messages => messages.length === 0),
errors: errors
}
}
const user = {
id: 1,
username: 'USER',
password: 'password'
}
const { errors, valid } = validateUser(user)
if (valid) {
saveUser(user)
} else {
printErrors(errors)
}
Last updated