콘텐츠로 이동

2024 01 23

2024-01-23

2024-01-23 코드리뷰

js 빌드/번들링

참고: https://github.com/joelonsw/TIL/blob/master/2023-kakao-1st/2023-12/2023-12-15.md
참고: https://velog.io/@ye-ji/%EB%B2%88%EB%93%A4%EB%A7%81%EA%B3%BC-%EB%B9%8C%EB%93%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C

[번들러]

  • 해결하고자 하는 문제
    • 전역 범위를 갖는 수백개의 자바스크립트 파일 중복 선언
    • 수백개에 달하는 자바스크립트 파일로 인한 느린 로딩
    • 수동적인 웹개발 루틴 (파일/이미지 압출, css pre-processing 등)
  • 번들러란...
    • 웹 어플리케이션을 구성하는 모든 자원을 하나의 파일로 묶는 도구
    • ex) webpack(리액트), rollup(옵트인 팝업)
  • 모듈 번들러
    • 모듈 : 특정 기능을 갖는 작은 코드 단위 (JS, HTML, CSS, images, font)
      • 수십개의 파일을 하나의 JS 파일로 압축/축소

[옵트인 팝업에서 번들링]

  • Rollup을 사용한 JS 모듈 번들러
    // 트랜스파일링을 통해 호환성 확보
    const babel = require('rollup-plugin-babel');
    // 노드 모듈 어디에 있는지
    const resolve = require('rollup-plugin-node-resolve');
    import {version} from './package.json';
    
    // 위 주석
    const libraryBanner = `/*
    
     * Name: popup
     * Version: v${version}
     */
    `;
    
    // IIFE: 
    const IIFE_CONFIG = {
      // Entry Point
      input: 'src/index.js',
      // Output File
      output: [
        {
          file: 'dist/popup.js',
          format: 'iife',
          name: 'Popup',
          globals: {
            Popup: 'Popup'
          },
          banner: libraryBanner
        }
      ],
      plugins: [
        // node_module 디렉토리 찾기
        resolve({
          browser: true,
        }),
        // ES5 syntax로 변경
        babel({
          presets: ['@babel/preset-env']
        })
      ],
    };
    
    // UMD: 
    const UMD_CONFIG = {
      input: 'src/index.js',
      output: [
        {
          file: 'dist/popup.umd.js',
          format: 'umd',
          name: 'Popup',
          globals: {
            Popup: 'Popup'
          },
          banner: libraryBanner
        }
      ],
      plugins: [
        resolve({
          browser: true,
        }),
        babel({
          presets: ['@babel/preset-env']
        })
      ],
    };
    
    module.exports = [
      IIFE_CONFIG, UMD_CONFIG
    ];
    
  1. IIFE (Immediately Invoked Function Expression)

    • Self Executing Function으로 함수가 직접 바로 호출되어 Script 태그에서 쓰일 수 있도록 함.
    • 모듈 시스템이라기 보다는 디자인 패턴에 가까움 (모듈 시스템 등장 전에 사용되던 패턴)
    • 바로 정의되자 마자 호출되어 전역 스코프를 오염시키지 않도록 하는 목표
      (function() {
        var x = 20;
        console.log(x);
      })();
      
      console.log(typeof x);
      
  2. UMD (Universal Module Definition)

    • 다양한 모듈 시스템인 AMD, CJS 등 올인원으로 사용할 수 있음

js 모듈 시스템

  • JS가 브라우저 안의 언어였다보니... 밖으로도 빼보고, 안에서도 잘쓰이게 수정해보고, 모듈화해보고 등등의 시도들이 역사적으로 있었던 것 같음
  • CJS: JS를 브라우저뿐만이 아니라 범용언어로 사용하고자 만들어진 그룹 (Node.js, 동기적인 특징으로 서버사이드에서 사용하기 용이)
  • AMD: 브라우저 안에서의 동작을 중점을 둔 그룹임 (브라우저, 비동기적인 특징으로 클라이언트 사이드 개발에 용이)
  • UMD: AMD, CommonJS 호환을 위해 해결. 브라우저/node 환경 둘 다 사용될 수 있도록

ShadowDOM을 활용한 독립된 스타일 적용

참고: https://github.com/joelonsw/TIL/blob/master/2023-kakao-1st/2023-11/2023-11-22.md

  • 숨겨진 DOM 트리로써, 진짜 DOM에 붙음
  • Shadow DOM이 독립적으로 컨트롤하도록 지원

Scala/Play에서 CorsAction 만들기

참고: https://github.com/joelonsw/TIL/blob/master/2023-kakao-1st/2023-12/2023-12-04.md

  • 요약하자면...
    • CORS의 해결책은 결국 서버에서 해당 Origin은 우리 서버랑 합의된 Origin 이니까~ 허용 헤더 끼워서 보내드릴게요! 라고 해줘야 함
      • Access-Control-Allow-Origin
  • ActionBuilder 상속 받아서 CorsAction 만들기
    • 브라우저는 타 Origin API를 찌를때 Origin 헤더를 http request에 포함
      • Origin이 있다면 -> 이미 등록된 친구인지 검사
      • Origin이 없다면 -> 같은 Origin에서 요청온 것이므로 그냥 API 처럼 동작 수행
        override def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]): Future[Result] = {
            request.headers.get("origin") match {
                case Some(origin) =>
                    if (corsAllowService.isAllowedOrigin(origin)) {
                        block(request).map { result =>
                            result.withHeaders(
                                HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN -> origin,
                                HeaderNames.ACCESS_CONTROL_ALLOW_METHODS -> "POST, GET, OPTIONS",
                                HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS -> "Origin, X-Requested-With, Content-Type, Accept, Referer, User-Agent",
                                HeaderNames.ACCESS_CONTROL_MAX_AGE -> Integer.toString(60 * 60 * 24)
                            )
                        }
                    } else {
                        throw new ForbiddenException
                    }
        
                case None => block(request)
            }
        }