ziglog

    Search by

    12월 4-5주차 기록

    December 30, 2022 • ☕️☕️ 11 min read

    마지막은 또 다른 시작


    배워가기

    tailwind 관련

    • Tailwind ClassName을 자동으로 sort해주는 headwind라는 익스텐션이 있다. (prettier-plugin-tailwindcss 와 같은 역할)

    • Tailwind를 다른 스타일 라이브러리(styled-components, emotion) 등과 함께 사용한다면 CSS Cascading 순위에서 밀려 스타일이 원하는데로 적용되지 않을 수 있다.

      • 이 때 twin.macro 같은 라이브러리를 사용하거나
      • tailwind config파일에서 important 옵션으로 우선순위를 조정할 수 있다.
      Copy
      // tailwind.config.js
      
      module.exports = {
        important: true,
      };

    CIDR

    CIDR(Classless Inter-Domain Routing) 은 직역하면 ‘클래스 없는 도메인 간 라우팅 기법’이란 뜻으로, IPv4 대역을 효율적으로 사용하기 위해 네트워크를 class로 구분하지 않고 접두어를 통해서 구분하는 방법이다.

    서브넷은 CIDR로 인해서 부분적으로 나눈 부분 네트워크를 가리킨다. 예를 들어, IP주소가 192.168.10.0/24일 때, 서브넷 마스크가 /24이므로 앞에서부터 오는 24비트 이후에 오는 4번째 옥텟을 전부 사용할 수 있다. 하나의 옥텟은 8비트기 때문에 2^8 - 2(예약된 ip 주소: 처음과 마지막), 즉 254개의 호스트에 IP를 할당할 수 있다는 뜻이다.

    Ref https://kim-dragon.tistory.com/9

    playwright

    playwrightdms e2e 테스트를 위한 도구로, 하나의 API로 모든 최신 브라우저(크로미움, 파이어폭스, 웹킷)에서 빠르고, 안정적인 자동화를 지원하는 MS에서 만든 자동화 도구다. 여러 페이지에 걸친 시나리오를 테스트할 수 있다.

    playwright에서는 새로운 페이지가 열릴 때 순차적으로 테스트를 진행하도록 코드를 작성해야 한다.

    Copy
    // 새로운 페이지를 열어서 해당 페이지에서 테스트를 진행하는 코드
    const [newPage] = await Promise.all([
      context.waitForEvent("page"), // 1
      page.click("a[target=_blank]"), // 2
    ]);

    Ref https://ui.toast.com/posts/ko_20210818

    git rebase 팁

    Copy
    git rebase -i HEAD~<number>

    명령어로 ㄴHEAD를 포함해서 number의 커밋만큼 커밋을 수정할 수 있다.

    Copy
    - `r`: 커밋 이름 변경
    - `p`: 커밋 그대로
    - `s`, `f`: 해당 커밋을 이전 커밋과 합치는 명령어
    		- `s` - 커밋 메시지를 모두 합쳐서 새로운 메시지 생성
    		- `f` - 이전 커밋 메시지만 남김

    라이브러리 배포 전략 - Module Federation 

    Module Federation은 리모트 앱이 노출(Expose)한 원격 모듈을, 호스트 앱에서 런타임에 비동기 로딩하여 사용할 수 있는 도구다.

    Nexus와 Module Federation을 사용하여 배포할 경우 장단점을 알아보자.

    • Nexus
      • 장점
        • 구성과 활용이 간단함.
        • 제공처에선 JS로 빌드만 해주면 되고, 사용처에선 그저 쓰기만 하면 된다.
        • 경우에 따라 커스텀도 가능할 수 있음.
        • 완전한 하나의 앱으로 동작할 수 있음.
        • 노출/로딩 제어 등의 별도 처리에 비교적 자유로움.
      • 단점
        • 사용처의 일관된 사용을 관리할 수 없음.
        • 의존성을 극히 제한적으로 관리해야 함.
        • 라이브러리 제공처에서 에러/로그 수집이 불가능함.
    • Module Federation
      • 장점
        • 사용처의 일관된 사용을 엄격히 제한할 수 있음.
        • 사용처가 모듈의 업데이트를 수동으로 할 필요 없음.
        • API 인터페이스 변경 등의 상황에서, 제공처의 수정만으로 전체 반영이 가능.
        • 제공처에서 에러/로그의 독자적인 수집이 가능하며, 사용처로 에러를 전달할 수도 있음.
        • 모듈 내 의존성 선택이 자유로움.
      • 단점
        • 사용처에서 Module Federation을 위한 특수한 처리가 필요하다.
        • 특히 CRA는 eject 해야하거나, craco 사용 등이 필수임.
        • CRA에서 Module Federation을 지원하기 위한 논의가 진행중에 있으나, 근시일은 아닌 듯하다. Ref
        • 각기 다른 버전의 모듈을 활용할 수 없음.
        • 모듈 코드와 별개로 URI와 배포 인프라를 관리해야 함.
        • 복수의 endpoint에서 병렬적으로 사용될 수 있으니, 경우에 따라 가용성 확보를 위한 방안을 지금과는 다른 수준으로 마련해야 할 가능성이 있음.

    🤔 Module Federation이 번들러들에 의해 충분히 지원되고 있는지?

    • Webpack v5, rollup, vite, bit 등 적용 완료
    • parcel은 아직 안됨.
    • esbuild는 계획조차 안하고 있음을 공지함.

    이미지 리사이징

    1. input을 통해 받은 File(Blob)을 FileReader 객체를 통해 base64로 인코딩 한다.
    • base64란, Binary Data를 Text로 바꾸는 인코딩(Base64는 64진법 이라는 뜻으로 ASCII 문자들로 표시할 수 있는 가장 큰 진법) 기법이다.
    1. new Image() 생성자 함수로 HTMLImageElement 인스턴스를 생성한다 (Imagesrcbase64src를 할당한다.)

    2. Canvas 엘리먼트를 생성하고, getContext('2d')로 drawing 할 2d 컨텍스트를 가져와서, drawImage 메서드의 매개변수에 base64 이미지와 리사이징할 사이즈를 입력해서 새로운 이미지를 그려준다.

    3. Base64이미지를 다시 File로 변환하려면 중간에 Uint8Array 생성자를 이용해서 ArrayBuffer로 변환한 다음, new File() 생성자의 매개변수로 넣어주어 변환해준다.

    • 이때, 두번째 인자로 name 을 넣어주는데, .jpg 같은 확장자가 없으면 서버에서는 content-type을 모든 바이너리 타입을 포함하는 타입인 octet-stream 으로 받아서 이미지가 아니다고 판단해서 튕겨 내는 이슈가 생길수 있기 때문이다.

    TurboRepo

    모노레포에서 프로젝트가 커질수록 테스트, 빌드, 린트등의 작업들이 많아진다. 이때 turborepo를 통해 작업(린트, 빌드 테스트)의 결과를 캐시에 저장해서 다음 작업 때 중복되는 작업은 스킵할 수 있다.

    turborepo가 중복 작업을 구별하는 방법은 다음과 같다.

    • 소스파일을 보고 해시코드를 생성한다.
    • 로컬 파일 시스템에서 해시코드와 똑같은 이름을 가진 폴더를 찾는다. (e.g../node_modules/.cache/turbo/78awdk123)
    • 있으면 작업을 생략한다.

    또한 remote caching(vercel 제공)을 통해 로컬 파일시스템 캐싱을 벗어나 원격으로 캐시를 제공하여 ci 혹은 팀원의 로컬 빌드 때 빌드 속도를 향상시킬 수 있다.

    웹뷰와 앱 통신

    자바스크립트 웹뷰에서 native 코드를 호출할 때,

    • ios: window. webkit 객체로 호출해야 한다. (webkit.messageHandlers[MESSAGE_HANDLER_NAME].postMessage(message))
    • android: 안드로이드 전역객체 property로 주입시켜준다. 이때 지정해준 이름으로 메서드를 호출한다.
    Copy
    // window.injectedObject
    webView.addJavascriptInterface(new JsObject(), "injectedObject");
    webView.loadData("", "text/html", null);
    webView.loadUrl("javascript:alert(injectedObject.toString())");

    유니버셜 앱에서 클라이언트 코드와 서버 코드 구분하기

    Next.js와 같은 같은 유니버셜 앱에서 SSR이 진행되는 프로세스는

    1. 서버에서 최초 렌더링
    2. 클라이언트에서 하이드레이션 + 이후 렌더링

    의 순서로 이루어진다.

    서버의 렌더링(Node.js에서 html 코드를 만드는 것)과 클라이언트의 렌더링(브라우저에서 자바스크립트로 DOM을 만드는 것) 모두를 진행해야 하기 때문에, 코드가 Node.js와 브라우저에서 모두 평가되어야함

    Next.js에서는 _app.tsx에 서버/클라이언트 공통의 코드를 작성할 수 있는데, 여기서 클라이언트에만 유효한 코드를 작성하면 에러를 던진다.

    Copy
    // _app.tsx
    class Foo extends Event {}

    Node.js 환경에서는 브라우저 환경에 있는 Event가 없기 때문에 에러를 던짐

    따라서 이런 경우에는 분기처리가 필요하다.

    Copy
    const MaybeEventOrNot =
      typeof window === "undefined" ? function () {} : window.Event;

    번들 관련

    패키지 번들과 사용자에게 전달되는 번들 파일은 구분해야 한다.

    웹애플리케이션에서 lottie 파일을 불러온다고 가정하자.

    정적 import로 lottie 파일을 가져오면 사용하지 않아도 lottie 파일이 번들에 포함된다. 반면 동적 import를 사용하면 사용하는 시점에 lottie 파일을 불러온다.

    이는 빌드 설정에 따라 다를 수 있다. (서드파티 라이브러리를 다시 번들링하거나 dynamic import를 별도 청크로 분리하지 않는 등의 차이)

    Copy
    import(`../../../assets/lottie/checkbox/${type}.json`)
      .then(({ default: lottieData }) => setAnimationData(lottieData))
      .catch((err) => console.log(err));

    Nest.js 관련

    • Nest.js의 정적 모듈(기본)에서는 환경변수를 사용할 수 없다. process.env에 정의된 환경 변수들을 정적으로 불러올 수 없기 때문이다. 이때는 DynamicModule을 사용한다.

    • Nest.js의 ConfigModule은 앱이 동작하는 여러 환경에서 필요한 서로 다른 환경변수를 정의하기 위해 사용한다. 한 곳에 config variables를 모아두는 셈이다. ConfigModuleConfigService를 노출하여 실행환경마다 적합한 .env 파일을 만들어낸다.

    • registerAs 를 사용하여 configuration 모듈을 namespace화할 수 있다.

      Copy
      // config/database.config.ts
      export default registerAs("database", () => ({
        host: process.env.DATABASE_HOST,
        port: process.env.DATABASE_PORT || 5432,
      }));

      registerAs로 선언한 configuration을 load로 불러온 후,

      Copy
      import databaseConfig from "./config/database.config";
      
      @Module({
        imports: [
          ConfigModule.forRoot({
            load: [databaseConfig],
          }),
        ],
      })
      export class AppModule {}

      dot notation(점 표기법)으로 값을 불러올 수 있다.

      Copy
      const dbHost = this.configService.get<string>("database.host");

    Nest.js monorepo의 타입 구분

    Nest.js를 monorepo로 구성했다면, nest-cli.json에 하위 프로젝트들의 설정을 명시할 수 있다.

    이때 workspace project의 타입은 2가지로 설정할 수 있다.

    • application - main.ts를 포함하는 nest 앱. 표준 구조에서의 앱과 동일하다.
    • library - 여러 프로젝트에서 공통으로 사용할 목적으로 패키징한 앱. 단일로 실행될 수 없으며, main.ts가 없다.

    Ref https://docs.nestjs.com/cli/monorepo#workspace-projects

    Ref


    이것저것

    • 17버전 이전의 React는 native(브라우저)에서 bubble되지 않는 이벤트(ex. blur, scroll)들도 모두 SyntheticEvent로 감싸서 전파시키고 있었는데, 이로 인해 여러 이슈들이 많이 발생해서 17버전부터는 scroll 이벤트를 부모 element로 전파시키지 않는다. (Ref)

    • SQL로 Pagination을 구현할 때 offset 방식에서는 많은 행을 읽어들여오고, 이후에 삭제하는 과정을 거치기 때문에 성능상 이슈가 발생할 수 있다.

      • no-offset 방식으로 구현할 경우 이런 이슈를 해소할 수 있지만 현재 페이지에서 앞으로 가기, 뒤로 가기만 구현 가능하고, ‘특정 페이지로 이동’같은 구현은 불가능하다. Ref
    • ‘pre-major’ 버전 - major 버전의 정식 출시 전, 즉 1.0.0 버전 미만을 일컫는다.

    • React.memo() 는 기본적으로 넘겨받은 props에 대한 얕은 비교만 수행한다.

      Copy
      export default React.memo(ResultPanel, fastDeepEqual);
      • 이때 두번째 인자로 비교에 사용할 함수를 넣을 수 있다.
    • mac os에서 etc/hosts와 그 사용 예시

      • hosts 파일은 도메인의 IP를 찾을 때 컴퓨터가 맨 처음 조사하는 파일(DNS파일)이다.
      Copy
      127.0.0.1       localhost MytoryMP.local
      255.255.255.255 broadcasthost
      ::1             localhost
      127.0.0.1       localhost

    기타

    swr 2.0 릴리즈

    새로운 mutation API와 개선된 낙관적 UI 기능, 새로운 devTools, 그리고 concurrent 렌더링에 대한 더 나은 지원을 들고 왔다!

    Ref https://swr.vercel.app/blog/swr-v2

    Next.js 13.1

    사내 구성원 분들 사이에서 제일 핫한 주제는 middleware 관련인 것 같다.

    Next.js 13.1부터는 미들웨어에서 응답을 리턴받을 수 있으며, 요청에 헤더를 추가할 수 있다고 한다.

    원래 됐다가 안됐다가 다시 되는 것으로 돌아왔나보다. 👀

    Ref https://nextjs.org/blog/next-13-1

    cypress-studio

    cypress 테스트 코드를 자동으로 만들어준다! 😲 (아직 Experimental 단계)

    RWA(Real World App)에서의 테스트코드를 일부 작성하고, 브라우저 환경에서 테스트하고자 하는 시나리오를 추가적으로 실행하면 이 Command들을 기억하여 유저 액션 기반 테스트 코드를 작성해주는 것 같다.

    playwright과 비슷한 기능을 제공하는 것 같은데, 복잡한 환경에서 사람의 손 없이 제대로 동작할지는 모르겠다 🤷‍♀️

    Ref

    프론트엔드 엔지니어 커리어 로드맵

    ‘탁월한’ 시니어 개발자가 되기 위해

    • 어디서 어떤 문제가 생길지 생각하는 능력
    • 데이터에 기반하여 의사결정하는 능력
    • 동료의 효과적 의사결정을 돕는 능력

    을 추가적으로 계발해야겠다!

    그리고 ‘웹 특화 프론트엔드 엔지니어’의 역량에 더해, ‘제품 특화 프론트엔드 엔지니어’의 역량도 쌓아 프로덕트의 UI/UX에도 관심을 갖고 고객만족을 위해 일해야겠다는 생각을 한다.

    엔지니어링 레벨을 빡빡하게 평가하는 걸로 유명한 구글에서는, ‘상위 레벨의 역할을 잘 할 것 같은 사람’을 승진시키는 게 아니라 ‘이미 상위 레벨의 역할을 일정 기간 이상 충분히 수행하고 있었던 사람’의 노고를 인정해주는 차원에서 승진시킨다고 한다

    Ref https://steady-study.super.site/frontend-engineer-career-roadmap


    마무리

    작년에도 12월 마지막 두 주는 한번에 묶었는데, 올해도 그렇게 되어버렸넹. 여행을 다녀와서 한주씩 쓸 내용이 없었다.

    이렇게 또 올 한 해가 끝났다! 알차고 후회없는 2022년이었다. 다가올 2023년도 블로그 화이팅 🤗


    Relative Posts:

    1월 1주차 기록

    January 7, 2023

    12월 3주차 기록

    December 17, 2022

    zigsong

    지그의 개발 블로그

    RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon