Bottom Sheet 라이브러리 비교

react-modal-sheet 추천:

  • 활발한 유지보수 (v5 최근 릴리즈)
  • Framer Motion 기반, Compound component 패턴
  • avoidKeyboard, disableDismiss 등 유용한 옵션
import { Sheet } from 'react-modal-sheet'

<Sheet isOpen={isOpen} onClose={() => setOpen(false)}>
  <Sheet.Container>
    <Sheet.Header />
    <Sheet.Content>콘텐츠</Sheet.Content>
  </Sheet.Container>
  <Sheet.Backdrop />
</Sheet>

react-spring-bottom-sheet는 3년간 업데이트 없음. 신규 프로젝트에선 피할 것.


#289
class CustomError extends Error {
  name = 'CustomError'

  constructor(message?: string) {
    super(message)

    Object.setPrototypeOf(this, CustomError.prototype)
  }
}

try {
  throw new CustomError('This is a custom error message.')
} catch (error) {
  if (error instanceof CustomError) {
    console.log('CustomError occurred:', error.message)
    console.log('Error name:', error.name)
  } else {
    console.log('An error occurred:', error)
  }
}
#287
enum LogLevel {
  DEBUG = 'debug',
  INFO = 'info',
  WARN = 'warn',
  ERROR = 'error',
}

type Message = string

class Logger {
  private level: LogLevel

  constructor(level: LogLevel = LogLevel.DEBUG) {
    this.level = level
  }

  private log(level: LogLevel, message: Message) {
    if (this.level === LogLevel.DEBUG || level !== LogLevel.DEBUG) {
      const label = level.toUpperCase()

      console.log(`[${label}] ${message}`)
    }
  }

  /**
   * - 개발 혹은 테스트 단계
   * - 운영 환경에서는 남기고 싶지 않은 로그 메세지
   */
  public debug(message: Message) {
    this.log(LogLevel.DEBUG, message)
  }

  /**
   * - 정상 작동에 대한 정보
   * - 시스템을 파악하는데 유익한 정보
   */
  public info(message: Message) {
    this.log(LogLevel.INFO, message)
  }

  /**
   * - 잠재적으로 문제가 될 수 있는 상황
   * - 언제든 발생할 수 있는 일반적인 문제 상황
   * - 사용자에게 노출되는 메세지에 상세한 가이드가 필요
   */
  public warn(message: Message) {
    this.log(LogLevel.WARN, message)
  }

  /**
   * - 심각한 오류나 예외 상황
   * - 즉시 조치가 필요할때
   */
  public error(message: Message) {
    this.log(LogLevel.ERROR, message)
  }
}

1. 효율적으로 로그 모니터링하기 - 로그 레벨 구분하기

#281
import z from 'zod'

const envSchema = z.object({
  REACT_APP_FEATURE_VAC_ASK: z.string(),
  REACT_APP_FEATURE_RECORDS: z.string(),
  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
})

const windowSchema = z.object({
   SOMETHING_COOL: z.string()
})

export const ENV = envSchema.parse(process.env)
export const WINDOW = windowSchema.parse(window)

#280
type Props = {
  popover: 'auto' | 'manual'
  popovertarget: string
  popovertargetaction: 'hide' | 'show' | 'toggle'
}

type State = {
  hasBackdrop: boolean
  isPopoverOpen: boolean
}

type Methods = {
  hidePopover: () => void
  showPopover: () => void
  togglePopover: () => void
}

type Events = {
  beforetoggle: () => void
  toggle: () => void
}

#279

zodios는 ZodErrorZodiosError.cause에 담아 던진다 — instanceof z.ZodError로는 안 잡힌다.

if (err instanceof ZodiosError && err.cause instanceof ZodError) {
  console.log(fromZodError(err.cause).toString())
}
#278

Swagger 2.0 스펙은 바로 못 먹인다. OpenAPI 3으로 바꾼 다음 zod 클라이언트를 만든다.

{
  "scripts": {
    "convert": "swagger2openapi ./spec.json -o ./spec.yaml",
    "zod": "openapi-zod-client -a \"./spec.yaml\" -o \"./spec.ts\""
  }
}
#277

올린 뒤 배포물에서 지운다 — Datadog은 이미 받았고 브라우저에는 노출되지 않는다.

VERSION=$(git log --pretty=format:'%h' -n 1)

yarn datadog-ci sourcemaps upload ./build \
  --service "$SERVICE" \
  --minified-path-prefix "$MINIFIED_PATH_PREFIX" \
  --release-version "$VERSION"

rm ./build/static/js/*.map
#276
import { match } from 'ts-pattern'

type Format = 'webp' | 'jpg'

type Params = {
  id: string
  quality: keyof typeof QUALITY_MAP
  format: Format
}

const QUALITY_MAP = {
  player_background: '0',
  video_frames_start: '1',
  video_frames_middle: '2',
  video_frames_end: '3',
  lowest_quality: 'default',
  medium_quality: 'mqdefault',
  high_quality: 'hqdefault',
  standard_quality: 'sddefault',
  unscaled_resolution: 'maxresdefault',
}

const BASE_URL = 'https://i.ytimg.com'

const VI = (format: Format) =>
  match(format)
    .with('jpg', () => 'vi')
    .otherwise(() => ['vi', format].join('_'))

export function getThumbnail({ id, quality, format }: Params) {
  return [BASE_URL, VI(format), id, QUALITY_MAP[quality]]
    .join('/')
    .concat(`.${format}`)
}

#275

버스 팩터 — 한꺼번에 빠지면 프로젝트가 멈추는 최소 인원 수. 1이면 그 사람이 곧 단일 장애점.

#274
31 중 18페이지