Today I learned...

개인적으로 그날 그날 배운것들 + 데일리 회고를 기록합니다. 정제되지 않은 경우가 많습니다.

2024-01-30

  • image
  • dev
  • optimization

Device Pixel Ratio(DPR)에 대해서

Device Pixel Ratio에 따른 픽셀의 차이

우리가 웹개발에서 픽셀을 얘기할 때는 보통 2가지로 정의할 수 있다.

  1. CSS Pixel
    • 웹에서(코드에서) 사용하는 추상적인 개념의 픽셀단위
    • 디스플레이 종류(일반, 레티나) 상관없이 동일한 크기를 유지
  2. Device Pixel (물리적 픽셀)
    • 물리적 디스플레이 기기에서 색을 표현할 수 있는 가장 작은 단위.
    • 디스플레이의 밀도에 따라 크기가 달라짐

DPR은 디바이스 픽셀과 CSS픽셀 사이의 비율을 나타낸다. DPR값이 커질수록 실제 디스플레이는 더 작고 촘촘한 물리적 픽셀로 이루어진다. 이는 사용자가 디스플레이를 보는 거리와도 관련이 있다. 모바일 디바이스의 경우 얼굴 가까이 에서 화면을 보기 때문에 작은 화면에서도 세밀한 디테일을 구분할 수 있도록 더 높은 DPR이 필요하고 반면에 TV, 모니터일 경우 그보다 멀리서 보기 때문에 DPR이 낮아도 적당히 선명하게 보이며 보통 DPR 1을 가지고 있다. (사용자에게 가까울수록 밀도가 높아지고 멀어질수록 밀도도 낮아진다.)

최근 출시된 아이폰 15 pro의 스펙을 알아보면 다음과 같다.

Viewport Size: 430px × 932px
Screen Size: 1290px × 2796px

뷰포트 사이즈는 CSS Pixel을 의미하고 스크린 사이즈는 물리적 픽셀인 Device Pixel을 의미하다. 스크린 사이즈가 뷰포트의 3배이므로 아이폰 15 pro는 DPR이 3이 된다.

비트맵 이미지(예: PNG, JPG, GIF 등)는 물리적 픽셀의 해상도에 따라 다르게 표시되고, Device Pixel Ratio(DPR)와 밀접하게 연결되어 있다. DPR이 1인 화면에서는 이미지의 픽셀 크기도 그대로 유지된다. 예를 들어, 1000px 너비의 이미지는 1,000개의 물리적 픽셀을 사용하여 표시된다. 그러나 DPR이 2인 디스플레이에서는 동일한 이미지가 실제로는 500px 너비로 줄어든다. 이는 각 CSS 픽셀이 실제로 2x2 물리적 픽셀로 표현되기 때문이다. DPR이 높은 디스플레이에서는 더 많은 물리적 픽셀이 하나의 CSS 픽셀을 형성하기 때문에, 동일한 크기의 비트맵 이미지는 더 작게 표시된다. 이는 이미지가 뷰포트의 100%를 차지하도록 스타일링되었을 때 중요한 문제가 될 수 있다. 예를 들어, DPR이 2인 디스플레이에서는 원본 이미지 크기의 두 배에 해당하는 해상도가 필요하여 이미지가 선명하게 표시되도록 해야 한다. 그렇지 않으면 이미지가 흐릿하거나 픽셀화되어 보일 수 있다.

2024-01-20

  • react
  • dev

Server Component, SPA, SSR

리액트 서버 컴포넌트는 리액트18에서 소개된 새로운 패러다임이고 Nextjs 13 에서 이를 지원하기 위해 새로운 앱 디렉토리를 도입했다.

SPA

Single Page Application (단일 페이지 애플리케이션)으로 렌더링과 데이터 패칭이 브라우저 (클라이언트)사이드에서 일어난다. 이로 인해 웹의 성능을 판단하는 Core Web Vitals중 First Contentful Paint가 오래 걸리게 되고 사용자 경험에 영향을 끼칠 수 있다. 또한 모든 컨텐츠가 자바스크립트에 의해 핸들링 되기때문에 크롤러가 제대로 동작하지 않는다.

SSR

초기 페이지의 렌더링이 서버사이드에서 일어난다. FCP 성능이 개선되고 유저가 화면을 바로 볼 수 있어 사용자 경험을 크게 개선할 수 있다. 하지만 초기 렌더링과 동시에 모든 자바스크립트 번들을 내려받으므로 다운로드가 완료되고 인터렉션이 가능해질 때까지 기다려야한다. (하이드레이션 단계) 또한 서버사이드 렌더링은 일반적으로 초기 페이지 로드에만 사용되므로 하이드레이션이 완료되면 그 후에는 다시 사용되지 않는다. (라우팅을 또해 다시 같은 페이지에 진입했을때는 클라이언트사이드 렌더링이 적용된다.)

RSC

서버 컴포넌트와 클라이언트 컴포넌트로 작성될 수 있다. 서버는 이러한 컴포넌트를 렌더링하고 브라우저로 스트리밍해야 한다. 서버는 클리어언트 컴포넌트만 렌더링하는데 필요한 자바스크립트 코드를 전송하므로 그 크기가 훨씬 작아져 다운로드 속도도 빨라진다. 이는 사용자 인터페이스가 빠르게 상호 작용 가능하도록 한다.

2024-01-19

  • javascript
  • dev

자바스크립트의 로딩 방식

자바스크립트는 기본적으로 Render-Blocking 으로, 브라우저는 HTML파싱 중 script태그를 만나면 파싱을 중단하고 해당 스크립트를 불러오고 실행하게 된다. 하지만 asyncdefer 속성을 통해 로딩과 실행 방식을 컨트롤 할 수 있다.

Render Blocking

Async (비동기)

Async script
  • async 속성이 있으면 해당 스크립트는 병렬로 로드된다. 스크립트 로딩이 완료되면 파싱이 잠시 중단되고 실행된다.
  • 스크립트 간에 종속성이 없고, DOM 이 완전히 구성되기 전에 실행되어야 하는 경우에 적합하다.

Defer (지연)

Defer script
  • defer 속성은 지연을 의미하며, async 와 마찬가지로 병렬로 로드되지만 실행은 페이지 파싱이 완전히 끝난 후에 이루어진다.
  • 스크립트가 DOM 에 의존하거나, 스크립트의 실행 순서가 중요할 때 유용하다.
<script async src="async1.js" />// 300ms
<script defer src="defer1.js" />// 200ms
<script defer src = "defer2.js" />// 300ms
<script async src = "async2.js" />// 50ms
<script async defer src="async_defer.js" />// 60ms

다음과 같은 스크립트와 해당 스크립트의 로딩 속도는 다음과 같다면, 어떤 순서로 실행될까?

acync2 ,async_defer ,async1, defer1, defer2

asyncdefer가 같이 작성된 경우는 defer의 속성을 무시되고 async로 동작한다. 따라서 모든 스크립트는 병렬로 로딩되면 async 스크립트는 로딩이 완료되자마자 실행되기 때문에 async1, async2, async_defer 스크립트 순서로 빠르게 로딩되는 순서대로 실행된다. 그 후에 defer 스크립트의 순서대로 실행된다.

2024-01-02

  • design-system
  • dev
  • design

JS Type Conversion

빈 스트링 ""falsy 빈 배열 [] 과 빈 객체 {}trusy

2023-02-13

  • css
  • dev
  • fallback

aspect-ratio 사파리에서 대응하기

문제적 사파리는 버전14까지는 aspect-ratio를 지원하지 않는다.

Padding Hack

@supports 를 통해서 aspect-ratio 를 지원하지 않는 브라우저의 경우에는 padding-top과 float를 사용한 fallback을 적용한다.

export const aspectRatio = (width: number, height: number) => css`
  aspect-ratio: ${width} / ${height};
 
  @supports not (aspect-ratio: 1 / 1) {
    &::before {
      content: '';
      float: left;
      padding-top: calc(100% * ${height} / ${width});
    }
 
    &::after {
      content: '';
      display: block;
      clear: both;
    }
  }
`;

2023-02-09

  • git
  • dev

Git Reset을 Reset하기

실수로 하드 리셋을 되돌리는 법

어제 로컬에서 작업 후 커밋 남겨놓고, 오늘 실수로 리모트 브랜치로 리셋을 했다. 그런 실수를 하는 일이 생길까 싶지만, 습관적으로 리모트를 기준으로 생각했더니 이런 일이 발생했다. 하드리셋이어서 당연히 기록은 남아있지 않았지만, git reflog를 통해서 무사히 아주 간단하게 어제 작업한 커밋을 되살릴 수 있었다.

깃은 모든 업데이트에 대해서 로그를 남기고 있고 git reflog 커맨드를 통해서 내용을 확인할 수 있다.

과거의 커밋의 헤드 넘버를 찾아서 다시 리셋을 해주면 된다. (깃 만쉐)

git reset HEAD@{2}