ES6 효율적인 Error Handling이란?

1

유인동님 FE세미나

실전 리팩토링은 항상 어려움으로 다가온다.
다른 사람의 코드를 많이 보고 리뷰하고 좋은 것을 내 것으로 만드는 것이 가장 빠른 길이라고 생각한다.
그래서 요새 공식 문서를 읽는 것 이외에도 다양한 컨퍼런스를 통해 학습을 하고 있다.
이 영상은 원티드 운영진분들이 강력하게 추천해주시는 영상이다!
도움이 너무너무 많이 됐기 때문에 기록으로 남겨둔다.

<!DOCTYPE html>
<html>
 <head>
   <meta charset="UTF-8" />
   <title>실전 Try-catch문</title>
   <script>
     // 미리 로딩해둘 이미지 이름 배열

     const imgs = [
       { name: 'HEART', url: 'https://placeimg.com/640/480/animal' },
       { name: '6', url: 'https://placeimg.com/640/500/animal' },
       { name: '하트', url: 'https://placeimg.com/640/520/animal' },
       { name: '도넛', url: 'https://placeimg.com/640/540/animal' },
     ];

     const loadImage = (url) =>
       new Promise((resolve) => {
         let img = new Image();
         img.src = url;
         img.onload = function () {
           resolve(img);
         };
         return img;
       });

     // generator
     function* map(f, iter) {
       for (const a of iter) {
         yield a instanceof Promise ? a.then(f) : f(a);
       }
     }
     async function reduceAsync(f, acc, iter) {
       for await (const a of iter) {
         acc = f(acc, a);
       }
       return acc;
     }

     loadImage(imgs[0].url).then((img) => console.log(img.height));

     async function f1() {
       try {
         let error = null;
         const total = await imgs
           .map(async ({ url }) => {
             if (error) return;
             try {
               const img = await loadImage(url);
               return img.height;
             } catch (e) {
               error = e;
               console.log(e);
               throw e;
             }
           })
           .reduce(async (total, height) => (await total) + (await height), 0);
         console.log('f1?', total);
       } catch (e) {
         console.log(e);
       }
     }

     // async function f2() {
     //   for await (const a of map(
     //     (img) => img.height,
     //     map(({ url }) => loadImage(url), imgs)
     //   )) {
     //     console.log(a);
     //   }
     // }

     // 아래와 같이 구현하면 error를 발생시킬 수 있다 -> try catch문 사용 가능
     async function f2() {
       console.log(
         'f2?',
         await reduceAsync(
           (a, b) => a + b,
           0,
           map(
             (img) => img.height,
             map(({ url }) => loadImage(url), imgs)
           )
         )
       );
     }

     async function f3() {
       try {
         console.log(
           await reduceAsync(
             (a, b) => a + b,
             0,
             map(
               (img) => img.height,
               map(({ url }) => loadImage(url), imgs2)
             )
           )
         );
       } catch (e) {
         console.log('서버에 에러 전달', e);
         console.log(0);
       }
     }

     f1();
     f2(); // 정상 작동
     f2().then(console.log('!'));
     f3(); // error

     // 에러 핸들링을 하지 않는 코드가 BEST
     async function f4(imgs) {
       return await reduceAsync(
         (a, b) => a + b,
         0,
         map(
           (img) => img.height,
           map(({ url }) => loadImage(url), imgs)
         )
       );
     }

     // 더 간결해지는 코드 - 표현식, 순수함수
     // 에러의 여지가 없다 imgs인자가 정확한 값이면 에러가 날 수 없음
     // 특별한 에러핸들링을 위해 if, try-catch를 사용하는 코드는 좋지 않음
     // 데이터가 특별할 수 있음(아예 에러의 가능성이 없는 데이터 or 에러의 가능성이 있거나 없는 데이터)
     // 에러를 터뜨릴 수 있도록 만들어놓는 함수가 좋은 함수
     // 어떤 개발자는 에러가 찍히길 원하고 다른 개발자는 에러가 찍히지 않는 걸 원할 수도 있다

     const f5 = (imgs) =>
       reduceAsync(
         (a, b) => a + b,
         0,
         map(
           (img) => img.height,
           map(({ url }) => loadImage(url), imgs)
         )
       );

     f5(imgs).then(console.log('f5 successed!'));

     f5(imgs2)
       .catch((_) => 0)
       .then(console.log('f5 successed!'));

   </script>
 </head>
 <body>
   <h3>ES6+ 비동기 프로그래밍과 실전 에러 핸들링</h3>
   <h4>Console을 확인해보세요</h4>
 </body>
</html>
      정리
      - Promise, async/await, try/catch를 정확히 다루는 것이 중요합니다.

      - 제너레이터/이터레이터/이터러블을 잘 응용하면 코드의 표현력을 더할 뿐 아니라 에러 핸들링도 더 잘할 수 있습니다.

      - 순수 함수에서는 에러가 발생되도록 그냥 두는 것이 더 좋습니다.

      - 에러 핸들링 코드는 부수효과를 일으킬 코드 주변에 작성하는 것이 좋습니다.

      - 불필요하게 에러 핸들링을 미리 해두는 것은 에러를 숨길 뿐입니다.

      - 차라리 에러를 발생시키는 게 낫습니다.
      setry.io같은 서비스를 이용하여 발생되는 모든 에러를 볼 수 있도록 하는 것이 고객과 회사를 위하는 더 좋은 해법입니다.

함수 f1부터 f5까지 천천히 보면서 더 효율적인 코드가 무엇인 지 고민해보자.
필요에 따라 선택을 달리할 수 있지만 f1()을 사용하는 것은 이제 벗어날 때라고 생각한다:)

profile
사랑할 이유가 가득한 코딩 일상

0개의 댓글