Skip to content

Latest commit

ย 

History

History
364 lines (296 loc) ยท 7.9 KB

clean_code2.md

File metadata and controls

364 lines (296 loc) ยท 7.9 KB

Clean Code

๐Ÿ”— Junior Vs Senior Code - How To Write Better Code - Part 2๋ฅผ ๋ณด๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

๐Ÿ”— Noob-Vs-Pro-Code - Github

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)
}