Javascript 네이밍(Naming)과 클린 코드(Clean Code)

2023. 12. 22. 13:33Study/JavaScript

1. Naming Convention

1-1. 기본사항

변수명은 자체 설명적이어야 함

JavaScript에서는 camelCase, PascalCase를 사용

// bad
const value = 'Robin';
const val = 'Robin';

// good
const firstName = 'Robin';

 

1-2. Components, Class, 생성자 함수

컴포넌트, 클래스, 생성자 함수는 PascalCase를 사용

class UserProfile{
    ...
}

function UserProfile(name, year){
    this.name = name;
    this.year = year;
}

 

1-3. Arrays

변수 이름에 복수, 단수 표현하기

// bad
const fruit = ['apple', 'banana', 'cucumber'];
// okay
const fruitArr = ['apple', 'banana', 'cucumber'];
// good
const fruits = ['apple', 'banana', 'cucumber'];

// great
const fruitNames = ['apple', 'banana', 'cucumber'];
const fruits = [
  { name: 'apple', genus: 'malus' },
  { name: 'banana', genus: 'musa' },
  { name: 'cucumber', genus: 'cucumis' },
];

 

 

1-4. Booleans

is, has, can 으로 시작하여 변수의 타입을 암시하기

// bad
const open = true;
const write = true;
const fruit = true;
const equal = true;
const visible = true;

// good
const isOpen = true;
const canWrite = true;
const hasFruit = true;
const areEqual = true;
const isVisible = true;

 

만약 함수의 return 값이 boolean이라면?

함수명을 check나 get으로 시작하기

const user = { fruits: ['apple'] };
const checkHasFruit = (user, fruitName) => user.fruits.includes(fruitName);
const hasFruit = checkHasFruit(user, 'apple');

checkTodoData();

 

1-5. Numbers

변수명에 maximun, minimum, total 포함하기

// bad
const pugs = 3;

// good
const minPugs = 1;
const maxPugs = 5;
const totalPugs = 3;

 

1-6. Functions

동사를 사용하여 명명하기

// bad
userData(userId);
userDataFunc(userId);
totalOfItems(items);
elementValidator(elements);

// good
getUser(userId);
calculateTotal(items);
validateElement(elements);

 

to를 앞에 명명하는 것은 오래된 컨벤션

toDollars('euros', 20);
toUppercase('a string');

 

반복문 안에서의 변수는 단수형을 사용하기

// bad
const newFruits = fruits.map(x => {
  return doSomething(x);
});

// good
const newFruits = fruits.map(fruit => {
  return doSomething(fruit);
});

 

1-7. Constant

대문자에_를 사용

var SECONDS = 60;
var MINUTES = 60;
var HOURS = 24;
var DAY = SECONDS * MINUTES * HOURS;
var DAYS_UNTIL_TOMORROW = 1;

 

 


 

 

2. Clean Code

2-1. Object Loopup table

다수의 switch-case 문은 JSON으로 table화 하기

BAD

function getUserType(type) {
  switch (key) {
    case 'ADMIN':
      return '관리자';
    case 'INSTRUCTOR':
      return '강사';
    case 'STUDENT':
      return '학생';
    default:
      return '해당 없음';
  }
}

 

GOOD

function getUserType(type) {
  const USER_TYPE = {
    ADMIN: '관리자',
    INSTRUCTOR: '강사',
    STUDENT: '수강생',
  };

  return USER_TYPE[type] || '해당 없음';
}

 

 

2-2. 긍정 우선 코드

BAD

if(!(typeof data === 'string')){ ... }

 

 

2-3. 조건문엔 함수 사용하기

어떤 연산을 위한 조건문인지 알아보기 쉬움

BAD

if(n % 1 === 0) { ... }

 

GOOD

if(isInt(n)) { ... }

 

 

2-4. 중첩 사용 지양하기

BAD

for (let i = range - 1; i < d.length; i++) {
  let sum = 0;
  for (let j = i - range + 1; j <= i; j++) {
    sum += d[j];
  }
  result.push(sum);
}

 

GOOD

for(let i=0; i<range; i++) {
	sum += d[i];
}

for(let k 1-i; j < d.length; j++) {
	if(j === (range-1)) {
		console.log(sum);
		continue;
	}
	let start = j - range;
	sum += d[j] - d[start];
	console.log(sum);
}

 

 

2-5. 복잡한 것 숨기기

BAD

const validEmail =
  /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9+\.)+[a-zA-z]{2,}))$/.test(
    str,
  );

 

GOOD

const validEmail = isValidEmail(eMail);

 

 

2-6. Short circuiting 대신 default parameters 사용

주의할 것은 undefined만 default parameter가 적용됩니다. 즉, falsy 값인 '', "", false, null, 0, NaN은 default value로 대체되지 않습니다.

BAD

function createMicrobrewery(name) {
  const breweryName = name || 'Hipster Brew Co.';
  // ...
}

 

GOOD

function createMicrobrewery(name = 'Hipster Brew Co.') {
  // ...
}

 

 

2-7. function parameters는 2개로 제한하기

BAD

function createMenu(title, body, buttonText, cancellable) {
  // ...
}

createMenu('Foo', 'Bar', 'Baz', true);

 

GOOD

function createMenu({ title, body, buttonText, cancellable }) {
  // ...
}

createMenu({
  title: 'Foo',
  body: 'Bar',
  buttonText: 'Baz',
  cancellable: true,
});

 

 

2-8. 함수는 한 가지 일만 수행하기

BAD

function emailClients(clients) {
  clients.forEach(client => {
    const clientRecord = database.lookup(client);
    if (clientRecord.isActive()) {
      email(client);
    }
  });
}

 

GOOD

function emailActiveClients(clients) {
  clients.filter(isActiveClient).forEach(email);
}

function isActiveClient(client) {
  const clientRecord = database.lookup(client);
  return clientRecord.isActive();
}

 

 

2-9. 함수는 하나의 개념으로 추상화 하기

BAD

function parseBetterJSAlternative(code) {
  const REGEXES = [
    // ...
  ];

  const statements = code.split(' ');
  const tokens = [];
  REGEXES.forEach(REGEX => {
    statements.forEach(statement => {
      // ...
    });
  });

  const ast = [];
  tokens.forEach(token => {
    // lex...
  });

  ast.forEach(node => {
    // parse...
  });
}

 

GOOD

function parseBetterJSAlternative(code) {
  const tokens = tokenize(code);
  const syntaxTree = parse(tokens);
  syntaxTree.forEach(node => {
    // parse...
  });
}

function tokenize(code) {
  const REGEXES = [
    // ...
  ];

  const statements = code.split(' ');
  const tokens = [];
  REGEXES.forEach(REGEX => {
    statements.forEach(statement => {
      tokens.push(/* ... */);
    });
  });

  return tokens;
}

function parse(tokens) {
  const syntaxTree = [];
  tokens.forEach(token => {
    syntaxTree.push(/* ... */);
  });

  return syntaxTree;
}

 

 

2-10. Object.assign으로 object default value 설정

BAD

const menuConfig = {
  title: null,
  body: 'Bar',
  buttonText: null,
  cancellable: true,
};

function createMenu(config) {
  config.title = config.title || 'Foo';
  config.body = config.body || 'Bar';
  config.buttonText = config.buttonText || 'Baz';
  config.cancellable =
    config.cancellable !== undefined ? config.cancellable : true;
}

createMenu(menuConfig);

 

GOOD

const menuConfig = {
  title: 'Order',
  // User did not include 'body' key
  buttonText: 'Send',
  cancellable: true,
};

function createMenu(config) {
  let finalConfig = Object.assign(
    {
      title: 'Foo',
      body: 'Bar',
      buttonText: 'Baz',
      cancellable: true,
    },
    config,
  );
  return finalConfig;
  // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
  // ...
}

createMenu(menuConfig);

 

 

2-11. flag를 함수의 매개변수로 사용하지 말기

BAD

function createFile(name, temp) {
  if (temp) {
    fs.create(`./temp/${name}`);
  } else {
    fs.create(name);
  }
}

 

GOOD

function createFile(name) {
  fs.create(name);
}

function createTempFile(name) {
  createFile(`./temp/${name}`);
}

 

 

2-12. 조건문 캡슐화

BAD

if (fsm.state === 'fetching' && isEmpty(listNode)) {
  // ...
}

 

GOOD

function shouldShowSpinner(fsm, listNode) {
  return fsm.state === 'fetching' && isEmpty(listNode);
}

if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
  // ...
}

 

 

2-13. 에러 무시하지 말기

BAD

try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}

 

GOOD

try {
  functionThatMightThrow();
} catch (error) {
  // One option (more noisy than console.log):
  console.error(error);
  // Another option:
  notifyUserOfError(error);
  // Another option:
  reportErrorToService(error);
  // OR do all three!
}

 

 

2-14. 에러를 catch 하기

BAD

getdata()
  .then(data => {
    functionThatMightThrow(data);
  })
  .catch(error => {
    console.log(error);
  });

 

GOOD

getdata()
  .then(data => {
    functionThatMightThrow(data);
  })
  .catch(error => {
    // One option (more noisy than console.log):
    console.error(error);
    // Another option:
    notifyUserOfError(error);
    // Another option:
    reportErrorToService(error);
    // OR do all three!
  });

 

 

2-15. 함수 호출자와 피호출자는 가까이 두기

BAD

class PerformanceReview {
  constructor(employee) {
    this.employee = employee;
  }

  lookupPeers() {
    return db.lookup(this.employee, 'peers');
  }

  lookupManager() {
    return db.lookup(this.employee, 'manager');
  }

  getPeerReviews() {
    const peers = this.lookupPeers();
    // ...
  }

  perfReview() {
    this.getPeerReviews();
    this.getManagerReview();
    this.getSelfReview();
  }

  getManagerReview() {
    const manager = this.lookupManager();
  }

  getSelfReview() {
    // ...
  }
}

const review = new PerformanceReview(employee);
review.perfReview();

 

GOOD

class PerformanceReview {
  constructor(employee) {
    this.employee = employee;
  }

  perfReview() {
    this.getPeerReviews();
    this.getManagerReview();
    this.getSelfReview();
  }

  getPeerReviews() {
    const peers = this.lookupPeers();
    // ...
  }

  lookupPeers() {
    return db.lookup(this.employee, 'peers');
  }

  getManagerReview() {
    const manager = this.lookupManager();
  }

  lookupManager() {
    return db.lookup(this.employee, 'manager');
  }

  getSelfReview() {
    // ...
  }
}

const review = new PerformanceReview(employee);
review.perfReview();

 

 

 

'Study > JavaScript' 카테고리의 다른 글

ES6 class  (1) 2023.12.22
try...catch 에러 핸들링  (0) 2023.12.22
Spread Operator  (0) 2023.12.22
정규식, 정규 표현식 regExp  (0) 2023.05.31
웹, 앱 체크 navigator.userAgent  (0) 2023.04.06