본문 바로가기
공부/JS

Clean Code Study - 1

by Piva 2022. 10. 4.
  • 회사에서 진행한 Clean Code 스터디에서 발표한 내용을 글로 정리해서 기록.
  • 공부한 내용은 여기, 첨부된 코드 예시도 여기서 가져옴 : https://github.com/ryanmcdermott/clean-code-javascript#table-of-contents
  • 내 파트는 Concurrency ~ Formatting(일부). 나머지 부분도 스스로 복습하고 여건이 되는 대로 정리해볼 예정.

Concurrency

들어가기 전에 Concurrency란?

  • 동시성(병행성). 하나의 프로그램이 하나 이상의 일을 한 번에 수행할 수 있음을 의미.
  • Parallelism(병렬성)과 비슷해보이나 다른 개념.
    • Parallelism은 동시에 여러 작업을 한꺼번에 처리하는 것을 의미한다면, Concurrency는 여러 작업을 한꺼번에 처리하는 것처럼 ‘보이는 것’을 의미함.
  • 동시성은 여러 작업을 번갈아가며 처리함으로써 사용자로 하여금 다수의 작업이 동시에 처리되는 것처럼 보이는 것.
    (참고 자료 : https://www.baeldung.com/cs/concurrency-vs-parallelism)
    • JS는 싱글 스레드 언어지만, Event loop를 통해 동시성을 지원함. 이는 JS의 비동기처리 개념과도 연결됨.
  • 요 섹션의 소제목이 Concurrency 인건 동시성에서 비롯된 JS의 비동기처리(콜백, Promise, Async/Await)를 다루기 때문.

 

 

Use Promises, not callbacks

  • 비동기처리 시, 콜백을 사용하면 다음의 단점 존재.
    • 콜백의 중첩으로 인한 콜백 지옥(Callback Hell)의 형성.
    • 각 콜백에서 발생한 에러는 그 콜백에서 해결해야 해서, 에러 핸들링의 시점에서도 복잡함이 증가.

Callback Hellllll

// Bad Example

import { get } from "request";
import { writeFile } from "fs";

get(
  "https://en.wikipedia.org/wiki/Robert_Cecil_Martin",
  (requestErr, response, body) => {
    if (requestErr) {
      console.error(requestErr);
    } else {
      writeFile("article.html", body, writeErr => {
        if (writeErr) {
          console.error(writeErr);
        } else {
          console.log("File written");
        }
      });
    }
  }
);

requestErrwriteErr를 처리하기 위해 각 콜백 내에서 if문을 통해 에러를 잡아내고 있음 ⇒ 에러 처리 중복

get함수에 전달되는 첫번째 콜백 내에서 writeFile의 두번째 콜백이 중첩되고 있음. ⇒ 복잡성 증가

 

  • 콜백 대신 ES2015/ES6부터 내장 global 타입이 된 Promise를 사용하는 것을 권장.
    • 콜백 지옥으로 인한 복잡한 코드를 훨씬 간단하게 만들 수 있음.
// Good Example

import { get } from "request-promise";
import { writeFile } from "fs-extra";

get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
  .then(body => {
    return writeFile("article.html", body);
  })
  .then(() => {
    console.log("File written");
  })
  .catch(err => {
    console.error(err);
  });

getwriteFile에서 사용되던 콜백이 then문 chaining으로 중첩없이 깔끔히 정리.

→ 전체 과정에서 발생하는 에러는 마지막 catch 에서 한 번에 처리하는 것이 가능.

 

 

Async/Await are even cleaner than Promises

  • Promise를 사용하면 콜백에 비해 더 깨끗한 코드를 만들 수 있음.
  • 그러나 여전히, then문의 chaining으로 인한 중복 코드가 발생.
// Bad Example
// Actually same with good example in previous part...

import { get } from "request-promise";
import { writeFile } from "fs-extra";

get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
  .then(body => {
    return writeFile("article.html", body);
  })
  .then(() => {
    console.log("File written");
  })
  .catch(err => {
    console.error(err);
  });

 

  • ES2017/ES8에서 추가된 Async/Await를 사용함으로써, then chaining으로 인한 중복을 없앨 수 있음.
// Good Example

import { get } from "request-promise";
import { writeFile } from "fs-extra";

async function getCleanCodeArticle() {
  try {
    const body = await get(
      "https://en.wikipedia.org/wiki/Robert_Cecil_Martin"
    );
    await writeFile("article.html", body);
    console.log("File written");
  } catch (err) {
    console.error(err);
  }
}

getCleanCodeArticle()

→ 새로운 async 함수 getCleanCodeArticle이 추가됨. async 함수이기에 내부에서 await를 사용할 수 있음.

→ await를 사용하여, 위키 페이지 데이터를 가져오는 작업이 끝날 때까지 대기.

→ 이 과정에서 발생하는 에러를 try-catch를 사용하여 처리.

 

Async/Await를 사용하는 것이 모든 상황에서 더 나은 퍼포먼스를 보장하지는 않는다고 함.
→ await를 사용하면 해당 작업이 끝날 때까지 강제로 멈추고 기다려야하기 때문에, await를 다수 사용할 경우 수행시간이 더 늘어날 수 있음.
→ 실행해야 하는 작업이 서로 독립적일 경우, Promise.all를 사용하여 한 번에 실행하는 것이 수행시간을 줄이는 데 기여할 수 있음.

 

 

Error Handling

  • 에러가 던져졌다는 것은 다음을 의미함.
    • 오류 상황에서 런타임이 성공적으로 확인되었음.
    • 프로세스를 kill하고, 실행을 멈춤으로써 에러를 인지시킴.
    • 스택 트레이스와 하께 콘솔로 에러에 대해 알림.

Don’t ignore caught errors

  • 에러를 단순히 console.log로 처리하는 것은 좋은 해결방법이 아님.
  • 에러를 잡아내기 위해 try-catch문을 쓴다는 것은 해당 부분에 에러가 발생할 가능성이 있음을 인지하고 있다는 것.
    ⇒ 따라서 에러를 처리할 방안 또한 마련해야 함.
// Bad Example

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

try-catch로 에러를 잡아내나 이를 그냥 console.log로 출력만 하고 있음.

 

// Good Example

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

→ 위 코드에서 나타나는 에러 처리법은 3가지.

  1. console.log 대신 console.error를 사용하기(더 눈에 돋보이고 위험하고 시끄럽게 생김)
  2. 사용자에게 에러에 대해 알리기 (Toast 메시지 등)
  3. 에러를 서비스(아마 에러 트래킹 서비스?)에 알리기

console.error와 console.log의 차이

→ 물론, 위의 3가지 모두 다 해도 됨!

 

 

Don’t ignore rejected Promises

  • Promise 에서 try-catch문을 통해 찾은 에러를 무시해선 안 됨.
  • 이유는 위와 동일.
// Bad Example

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

→ 위와 마찬가지로 에러를 그냥 console.log로 출력만 함.

 

// Good Example

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

→ 해결 방안은 지난 파트와 동일.

 

 

Formatting

  • Coding convention은 주관적임. 때문에 어느 것이 더 나은지 저울질할 필요 없으며, 중요한 것은 사용하기로 정한 규칙을 일관되게 지키는 것.

 

Use consistent capitalization

  • JS는 타입이 유연한 언어이기에, 변수 이름의 convention을 통해 해당 변수에 대해 많은 것을 파악하고 있음.
  • 변수 이름 규칙 또한 여러가지가 있기 때문에, 팀에서 사용할 규칙을 정하고 이를 일관성있게 지키는 것이 중요.
// Bad Example

const DAYS_IN_WEEK = 7;
const daysInMonth = 30;

const songs = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
const Artists = ["ACDC", "Led Zeppelin", "The Beatles"];

function eraseDatabase() {}
function restore_database() {}

class animal {}
class Alpaca {}

→ 함수 및 상수 이름에 대해 Camel case 와 Snake case를 섞어 사용하고 있음.

→ 배열과 클래스 이름의 첫 글자로 대문자와 소문자를 섞어 사용하고 있음.

 

// Good Example

const DAYS_IN_WEEK = 7;
const DAYS_IN_MONTH = 30;

const SONGS = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
const ARTISTS = ["ACDC", "Led Zeppelin", "The Beatles"];

function eraseDatabase() {}
function restoreDatabase() {}

class Animal {}
class Alpaca {}

→ 상수/함수/배열/클래스 이름을 일정한 규칙으로 통합.

'공부 > JS' 카테고리의 다른 글

[Javascript] 클로저 알아보기 - 1  (1) 2024.01.05
[JavaScript] 실행 컨텍스트에 대하여  (2) 2023.12.23
Typescript 정리 - 2  (1) 2023.01.16
Typescript 정리 - 1  (1) 2023.01.10
Clean Code Study - 2  (0) 2023.01.03