어떻게 하면 주니어 개발자로서 성장할 수 있을까? 더 낫은 개발자가 될 수 있을 까 고민하던 중에 인프런에 그랩님이 강의하신 ‘모든 개발자의 실무를 위한 올인원 기본기 클래스’ 강의가 있어 강의를 듣고 Step by Step 공부하고 정리하는 글을 쓰려고 합니다.



1.네이밍


컨벤션

  • snake_case : Python, Ruby 등에서 권장
  • cameCase : Java, Javascript 등에서 권장
  • PascalCase : 대부분의 프로그래밍 언어에서 클래스를 네이밍할 때 사용
  • kebab-case : HTML Element를 표현할 때 사용

네이밍

  • 변수 : 명사 혹은 형용사로 작성

    const userData = ... // 명사
    const isValid = ... // 형용사
  • 함수와 메서드 : 동사 혹은 형용사로 작성

    function sendData() {
    	...
    }
    
    function inputIsValid() {
    	...
    }
  • 클래스 : 일반적으로 명사로 작성

    let Client = new Object();

Tip

구체적이고 명시적으로 적을 것, 짧고 애매한 표현보다 길고 직관적인 표현이 낫다.

// as-is
const dt = '20211219KST';

// to-be
const datetimeWithTimezone = '20211219KST';


2. 주석, 포맷팅


2. 1 주석

네이밍으로 표현할 수 없는 영역을 주석으로 표현

  • 법적인 정보를 담을 때

    // Copyright (c) 2021...
  • 의도를 명확하게 설명할 떄

    // throughput을 늘리기 위해 스레드를 10개까지 늘린다.
    for (let idx = 0; idx < 11; idx++) {
    	thread = threading.Thread(target=...)
    	thread.start()
    }
  • 중요성을 강조할 떄

    // 최종 결재를 하기 전에 진행해야 하는 validation gkatn
  • 결과를 경고할 떄

    // WARNING: API 서버가 항상 양호한지 알 수 없음
    function connectApiServer() {
    	...
    }

2. 2 관용적으로 사용되는 키워드

  • TODO : 당장은 아니지만 다음에 해야할 때
  • FIXME : 치명적인 에러를 발생하는 코드는 아지미나 수정해야 할 때
  • XXX : 더 생각해볼 필요가 있을 떄

2. 3 포맷팅

Vertical Formatting

  • 한 파일에 코드를 다 넣지 말고, 개념에 맞게 파일을 나눠서 사용

    // as-is
    // store.js에 전부 있음
    const FruitsStore = new Object();
    
    const DesertStore = new Object();
    
    // to-be
    // FruitStore.js
    const FruitsStore = new Object();
    
    // DesertStore.js
    const DesertStore = new Object();
  • 다른 개념의 코드는 Spacing으로 분리

  • 비슷한 개념의 코드는 붙여서 사용

    function testUserByProduct() {
    	const user = new User()
    	const	product = new Product()
    
    	product.setSoldOut(true)
    	user.get(product)
    
    }

Horizontal Formatting

  • 한 줄에 코드를 다 넣기보단 변수 등을 활용해서 가독성 높이기

    // as-is
    personList.extend([Person('adam'), Person('even'), Person('noa')])
    
    // to-be
    const items = [Person('adam'), Person('even'), Person('noa')];
    personList.extend(items);


3. 함수


3. 1 함수의 역할은 하나만 - SRP (Single Responsibility Principle)

  • as-is

    const Database = new Object();
    
    function createUser(email, password) {
    	if (!email.includes('@') || email.length < 6) {
    	    throw new Error('유저 정보를 제대로 입력하세요')
    	} 
    	
    	const user = {"email" : email, "password" : password};
    	
    	const database = Database("mysql")
    	database.add(user)
    	
    	const emailClient = EmailClient()
    	emailClient.setConfig(...)
    	emailClient.send(email, "회원가입을 축하합니다")
    	
    	return true
    }
    
    createUser('@faeafaa', 'af')

  • to-be

    function createUser(email, password) {
      validateCreateUser(email, password);
    
      const user = buildUser(email, password)
    
      saveUser(user)
      sendEmail(email)
    
      return
    }
    
    function validateCreateUser(email, password) {
      if (!email.includes('@') || email.length < 6) {
        throw new Error('유저 정보를 제대로 입력하세요')
      }
    }
    
    function buildUser(email, password) {
      return {
        "email": email,
        "password": password
      }
    }
    
    function saveUser(user) {
      const database = Database("mysql")
      database.add(user)
    }
    
    function sendEmail(email) {
      const emailClient = EmailClient()
    	emailClient.setConfig(...)
    	emailClient.send(email, "회원가입을 축하합니다")
    }

3. 2 반복하지 말자 - DRY (Don’t Repeat Yourself)

  • as-is

    function createUser(email, password) {
      if (!email.includes('@') || email.length < 6) {
        throw new Error('유저 정보를 제대로 입력하세요')
      }
    }
    
    function updateUser(email, password) {
      if (!email.includes('@') || email.length < 6) {
        throw new Error('유저 정보를 제대로 입력하세요')
      }
    }

  • to-be

    function validateCreateUser(email, password) {
      if (!email.includes('@') || email.length < 6) {
        throw new Error('유저 정보를 제대로 입력하세요')
      }
    }
    
    function createUser(email, password) {
      validateCreateUser(email, password)
    }
    
    function updateUser(email, password) {
      validateCreateUser(email, password)
    }

3. 3 파리미터 수는 적게 유지하자

  • as-is

    const User = function (name, age, sex) {
      console.log(`안녕하세요 저의 이름은${name}이고, 나이는 ${age}, ${sex} 입니다.`)
    }
    
    const Adam = User('adam', '30', 'male') 

  • to-be

    const Person = function (name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    
    const User = function (person) {
      console.log(`안녕하세요 저의 이름은${person.name}이고, 나이는 ${person.age}, ${person.sex} 입니다.`)
    }
    
    const Eve = new Person('eve', 25, 'female')
    const newUser = User(Eve);

3. 4 사이드 이펙트를 잘 핸들링하자


사이드 이펙트(Side Effect)는 함수가 실행됐을 때 함수 이외의 어떤 것들에 변화를 주는 것을 뜻한다. 사이드 이펙트를 잘 다루지 못하면, 예측하지 못하는 문제들이 발생할 수 있다.


// 사이드 이펙트 X
function getUserInstance(email, password) {
  user = User(email, password)
  return user
}

// 사이드 이펙트 O
function updateUserInstance(user) {
  user.email = 'new email';
}

// 사이드 이펙트 O
function createUser(email, password) {
  user = User(email, password)
  startDBSession(); // 외부 DB Session에 변화를 줄 수 있다.
}


사이드 이펙트를 잘 핸들링 하는 방법

  • 코드를 통해 충분히 예측할 수 있도록 네이밍을 잘하는 것이 중요
  • update, set 같은 직관적인 prefix를 붙여서 사이드 이펙트가 있을 수 있음을 암시

  • 함수의 사이드 이펙트가 있는 부분과 없는 부분으로 잘 나눠서 관리
  • 명령(side effect O)과 조회(side effect X) 를 분리하는 CQRS 방식이 있다

  • 일반적으로 update를 남발하기 보단 순수 함수 형태로 사용하는 것이 더 직관적이고 에러를 방지할 수 있다.

  • as-is

    const carts = []
    
    function addCart(product) {
    	carts.push(product)
    }
    
    const product = Product(...)
    addCart(product)

  • to-be

    const cart = ['apple', 'orange']
    
    function getAddedCart(product) {
      return [...cart, product]
    }
    
    const carts = getAddedCart('mango')
    
    console.log(carts)

순수함수란?

  • 어떤 함수에 동일한 인자를 주었을 때 항상 동일한 값을 리턴하는 함수
  • 외부의 상태를 변경하지 않는 함수
  • 참조 블로그