본문 바로가기
javascript

똑똑하게 외부코드 사용 고고

by 윤-찬미 2021. 8. 7.

외부코드, 외부라이브러리, 외부.. 등과 같이 가져와서 붙여서 사용하는 것들은 우리에게 정말 많은 편의를 제공한다. 머리아프게 생각해야했던 로직들을 라이브러리단에서 모두 해결해주고 시간단축까지 해주며, 개발의 편의성을 높여준다.

하지만, 이런 외부코드를 사용하는 것은 늘 위험에 따른다.

우리는 외부코드를 "잘" 고를 수 있어야 하고, "똑똑하게" 사용할 줄 알아야한다.

  1. "잘" 고르기

    세상에는 수많은 라이브러리가 있으며, 어떤 기능을 수행하는 라이브러리가 필요할 때, 같은 기능이 있는 라이브러리를 여러 개 찾을 수 있을 것이다.

    그러나 선택지가 너무 많으면 우리 같은 웹 개발자는 혼란에 빠지게 된다. 제일 좋은 것이 무엇인지 어떻게 알 수 있을까? 만약 잘못된 선택을 한다면 어떡할까?

    웹 개발을 할 때 단 한 개의 "최고의 선택"은 없는 경우가 많다. 그러나 어떤 선택이 다른 선택보다 나은 경우는 있다. 아래의 두 입장을 고려해서 라이브러리를 선택하는 것은 중요하다.

    💻 개발자 입장

    • 잘 정리된 문서

    • 유연성 (내가 원하는 방향과 잘 접목이 되는가)

    • 활발한 유지 보수

    • 미래지향성

    • 테스트

    • 깨끗한 코딩:

    • 활발한 커뮤니티

      그 외에 내가 개인적으로 보는건,

    • fork 횟수

    • issue의 개수, 분위기

    • 스타 개수

      👥 사용자 입장

    • 파일 사이즈

    • 성능

    • 브라우저 지원

    • 접근성(다양한 사용층 고려)

    • 반응(모바일)

  2. "똑똑하게" 쓰기

    외부라이브러리를 쓰다보면 편리성과 별개로 사용법을 제대로 익히지 못한채로 사용한다던가,

    코드 자체에 버그가 있다던가, 코드의 기능이 변경되어 이슈를 발생시킬 수도 있다.

    따라서 외부코드에 지나치게 의존하는 것은 좋은 선택지가 아니다.

    • 외부 라이브러리를 사용할 경우 시간 단축에 효과가 있지만 잘못 사용함에 있어서 발생하는 영향도가 얼마나 있을지 알지 못하기 때문에 학습 테스트를 통해 외부 api에 대한 테스트를 미리 진행한다.

    • 외부 라이브러리에 대한 학습 테스트를 진행하여 사용하면 해당 라이브러리의 버전이 올라갔을 때 우리가 사용하는 목적에 맞게 정상적으로 돌아가는지 쉽게 테스트를 해볼 수 있어서 좋다.

    • Adapter pattern을 이용하여 변경의 요지가 있는 인터페이스를 방어할 수 있다.

    • 인터페이스 호환성 문제 - Adapter pattern*

      기존에 있던 구조를 새 구조로 유연하게 전환하거나 새 구조에서 기존의 구조로 전환하는 데 사용하는 패턴

      한국에서는 220V를 쓰는데, 일본은 110V를 사용한다. 그렇다면 220V모양으로 만들어진 전자제품을 일본에서 쓰려면 어떻게 해야 할까? 110V로 변환해주는 무언가를 사용해주면 된다(돼지코라 불리는 변압기/어댑터를 사용한다).

      const Printer = (function() {
         function Printer() {
             this.textArr = [];
         };
         Printer.prototype.pushText = function(text) {
             this.textArr.push(text)
         };
         Printer.prototype.print = function() {
             return this.textArr.join(' ');
         }
         return Printer;
      }())
      const printer = new Printer();
      printer.pushText('Hello');
      printer.pushText('Design');
      printer.pushText('Pattern');
      
      const result = printer.print();
      console.log(result); // Hello Design Pattern
      

      여기까지는 문제가 없다. 그러나 문제는 주로 변화에서 발생하며, 현실 세계는 끝임없이 환경이 변화한다. 만약 우리가 이 코드를 3개월간 사용하다가

    • 해시태그(#)를 붙여서 출력하는 프린터* 가 출시하여, 우리는 그 프린터로 교체를 해야한다고 가정해보자.

      설상가상으로 우리가 받은 새로운 프린터의 명세(Interface)는 현재와 미묘하게 다르다 고 한다.

      const HashTagPrinter = (function() {
         function HashTagPrinter() {
             this.textArr = [];
         };
         HashTagPrinter.prototype.pushText = function(text) {
             this.textArr.push(text)
         };
         HashTagPrinter.prototype.printWithHashTag = function() {
             return this.textArr.map(text => `#${text}`).join(' ');
         }
         return HashTagPrinter; 
      }())

      이제 메인 함수의 코드를 새로운 프린터로 교체해 보도록 하자.

      const printer = new HashTagPrinter();
      HashTagPrinter.pushText('Hello'); // HashTagPrinter printer로 교체
      HashTagPrinter.pushText('Design');
      HashTagPrinter.pushText('Pattern');
      
      const result = HashTagPrinter.print(); // printer는 명세가 맞지않아 에러가 발생한다.
      console.log(result);

      좋은 코드는 최소한의 변경으로 변화에 대응할 수 있어야 한다.

      일단 새로운 HashTagPrinter 객체를 생성해서 printer에 할당하는 부분은 필수로 변경해 줘야 한다.

      그러나 그 아래의 실질적인 비즈니스 로직에서는 불필요한 변경은 최소화하고 싶다.

      위 코드에서는 마치 220V로 만든 가전기기를 110V에서 사용할 수 없듯이, HashTagPrinter에는 print()가 아닌 printWithHashTag()를 사용하고 있어서 컴파일 에러가 발생한다. 그렇다면 방법은 두 가지다.

    • const result = printer.printWithHashTag()로 변경해 주던가,*

    • 아니면 변압기 역할을 하는 어떤 무언가를 만들어 주던가.*

      첫번째 방법이 쉬어 보일 수 있으나, 실제 코드에서는 저 부분만 수정한다는 보장이 없을뿐더러, 만약 우리가 다시 이전 프린터를 사용한다고 하면 또다시 같은 곳을 수정해줘야 한다. 따라서 두 번째 솔루션인 돼지코(Adapter)를 만들어 보도록 하자.

      어댑터(Adapter)를 만드는 방법은 간단하다. 명세가 맞지 않는 부분을 맞춰주기만 하면 되는 것으로, 위 프린터 예제에서는 printWithHashTag()가 printer()를 호출했을 때 동작하도록 만들어 주면 된다.

      const HashTagAdapter = (function() {
         function HashTagAdapter(hashTagPrinter) {
             this.printer = hashTagPrinter;
         };
         HashTagAdapter.prototype.pushText = function(text) {
             this.printer.pushText(text)
         };
         HashTagAdapter.prototype.print = function() {
             return this.printer.printWithHash();
         }
         return HashTagAdapter; // 220V -> 110V 변환!
      }())
      const printer = new HashTagAdapter(new HashTagPrinter());
      printer.pushText('Hello');
      printer.pushText('Design');
      printer.pushText('Pattern');
      
      const result = printer.print();
      console.log(result); // #Hello #Design #Pattern

      어댑터 패턴은 한번 익혀두면, 웬만한 인터페이스 호환성 문제는 쉽게 해결할 수 있을 것이다.