[data-scope='slider'][data-part='thumb']

[data-scope='slider'][data-part='track']

[data-scope='slider'][data-part='control']

Ark UI의 각 컴포넌트 파트는 data-scopedata-part 속성으로 지정됩니다. data-scope 속성은 컴포넌트를 식별하고, data-part 속성은 컴포넌트의 개별 부분을 지정합니다.

ark-ui에서 사용하는 방식인데 적용해볼만한 컨셉이라고 생각한다.


#297

as로 간단하게 처리가 가능할정도의 복잡도라면 괜찮다고 보는데 더 복잡하게 된다면 asChild가 최선의 방법.

function Button({ asChild, ...props }) {
  const Comp = asChild ? Slot : 'button'

  return <Comp {...props} />
}

#295
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
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

한 마을에 개발자들이 모여 프로젝트를 진행하고 있었습니다. 그 중 한 명의 개발자인 에릭은 팀에서 핵심적인 역할을 맡고 있었고, 그의 전문적인 지식과 능력은 프로젝트의 성공에 큰 영향을 미칠 정도였습니다. 그러나 에릭은 여행을 갈 계획을 세우고 모두에게 알리지 않았습니다.

어느 날, 프로젝트 팀은 예기치 않은 문제에 직면했습니다. 시스템의 일부가 오작동을 일으켜 복구해야 할 상황이었는데, 당연히 에릭이 이를 해결할 수 있었습니다. 그러나 에릭은 이미 여행을 떠나버렸고, 팀은 그를 찾을 수 없었습니다. 에릭이 없는 상태에서는 아무도 그의 전문적인 지식을 대체할 수 없었기 때문에 팀은 큰 혼란에 빠지게 되었습니다.

프로젝트 팀은 에릭의 결석으로 인한 위기를 극복하기 위해 긴급 회의를 열었습니다. 모두가 버스 팩터에 대해 이야기하며, 이 사태로부터 배운 교훈에 대해 논의했습니다. 팀은 이제부터 지식을 공유하고 업무를 분산시키기로 결정했습니다. 각 개발자는 다른 팀원의 역할을 이해하고, 중요한 결정과 지식을 모두가 공유하도록 노력하기로 했습니다.

이제 마을의 개발자들은 에릭 없이도 프로젝트를 진행할 수 있었습니다. 에릭은 멋진 여행을 즐겼지만, 팀은 그의 결석으로 인한 위기를 극복하고 지속 가능한 개발 환경을 조성하는 데 성공했습니다. 그들은 버스 팩터를 기억하며, 이 작은 이야기는 그들에게 프로젝트 관리의 중요성을 상기시켜주었습니다.


https://www.google.com/search?q=%EB%B2%84%EC%8A%A4%ED%8C%A9%ED%84%B0

#274

Stacking Workflow는 큰 엔지니어링 작업을 작은 단위의 코드 변경으로 나누어 독립적으로 테스트, 검토 및 병합할 수 있는 프로세스입니다. 스택은 서로 의존하는 일련의 코드 변경으로 이루어집니다.

Why stack: Stacking은 작성자가 검토를 기다리는 동안 블록되지 않고 작업할 수 있게 하며, 더 품질 높은 검토 의견을 유도하고 작은 변경 사항을 빠르게 병합할 수 있게 합니다. 검토어는 작은 규모의 변경 사항을 검토하고, 재검토하는 시간을 줄이며, 무언가 잘못되었을 때 이를 분석하고 롤백할 수 있는 세분성을 갖추게 됩니다.

What is stacking: Stacking은 여러 개의 의존하는 Pull Request(PR)로 이루어진 것입니다. Stacking은 작성자가 빠른 검토를 받을 수 있도록 하고, 높은 품질의 검토 의견을 얻을 수 있게 하며, 작은 변경 사항을 빠르게 병합할 수 있습니다. 검토어는 작은 규모의 변경 사항을 검토하며, 재검토 시간이 줄어들고, 필요한 경우 분석 및 롤백할 수 있는 세분성을 갖게 됩니다.

Stacking vs feature branches: Stacking과 기능 브랜치 모두 트렁크 기반 개발을 가능하게 합니다. 그러나 Stacking은 기능 브랜치와 다른 점이 있는데, 기능 브랜치는 주로 main 브랜치에서 직접 분기되어야 하는 반면, Stacking은 서로 의존하는 PR들 간에 분기될 수 있다는 점입니다. 기능 브랜치는 비대해지고 검토하기 어려워질 수 있지만, 스택은 항상 가볍고 모듈식으로 유지될 수 있습니다. 기능 브랜치는 한꺼번에 검토되고 CI를 통과하고 한 번에 병합되어야 하지만, 스택은 작은 조각으로 검토되고 각각의 브랜치에서 CI를 통과한 후 언제든 병합될 수 있습니다.

Creating a change: Stacking은 이미 브랜치에서 분기하고 서로 의존하는 두 개의 PR을 열었다면 이미 사용한 것입니다. 이 워크플로우는 git에서 기본적으로 지원되며, main 대신에 부모 PR에서 개발을 시작하고 브랜치를 확인하면 됩니다.

Splitting: 일부 개발자는 처음에 큰 변경 사항을 작성하고 나중에 이를 작은 변경 사항의 스택으로 나누는 것을 선호합니다. 이는 최종 코드 구조를 미리 예측하기 어려울 때 도움이 되며, 작은, 독립적으로 검토 가능한 일련의 변경 사항을 제출하고자 할 때 유용합니다.

Updating a change: 스택된 브랜치를 업데이트하는 것은 전통적인 브랜치를 업데이트하는 것과 다르지 않습니다. 유일한 차이점은 스택된 브랜치를 업데이트한 후에는 의존하는 PR의 병합 베이스도 업데이트해야 한다는 것입니다. 변경 사항마다 리베이스하는 것은 불안하게 느껴질 수 있지만, 도구를 사용하면 자동화할 수 있습니다.

Opening and updating pull requests: 검토를 위해 준비되면 스택의 각 브랜치당 하나의 PR을 생성할 수 있습니다. 두 번째 브랜치에서 스택을 만들면 첫 번째 PR을 열 수 있습니다. 자주 변경 사항을 제출하면 PR이 작고 동료들이 쉽게 검토할 수 있습니다.

Merging PRs: 변경 사항이 승인되고 CI를 통과하면 병합할 준비가 됩니다. 스택된 변경 사항은 항상 트렁크로 병합되며, 스택의 하단부터 병합됩니다. PR이 병합되면 의존하는 PR의 베이스 브랜치를 트렁크로 업데이트해야 합니다.


#272

deep-object-diff와 비슷한 비교 알고리즘을 구현하는 방법은 다양할 수 있지만, 대표적인 접근 방식은 재귀적으로 객체를 탐색하면서 속성을 비교하는 것입니다. 이를 위해 일반적으로 다음과 같은 과정을 따릅니다:

  1. 입력으로 받은 두 객체를 비교합니다.
  2. 첫 번째 객체의 속성을 순회하면서 두 번째 객체에 동일한 속성이 있는지 확인합니다.
    • 동일한 속성이 있다면, 해당 속성의 값을 비교합니다.
    • 값이 같다면, 두 객체의 해당 속성은 동일하므로 비교를 종료합니다.
    • 값이 다르다면, 속성이 변경된 것으로 간주하고 변경된 값을 기록합니다.
  3. 첫 번째 객체의 속성을 순회하면서 두 번째 객체에 동일한 속성이 없는 경우, 해당 속성은 첫 번째 객체에서 삭제된 것으로 간주합니다.
  4. 두 번째 객체의 속성을 순회하면서 첫 번째 객체에 동일한 속성이 없는 경우, 해당 속성은 두 번째 객체에 추가된 것으로 간주합니다.
  5. 만약 속성이 객체나 배열인 경우, 재귀적으로 해당 객체나 배열을 탐색하면서 내부의 속성을 비교합니다.

이러한 과정을 재귀적으로 반복하면서 객체의 모든 속성을 비교하고 차이를 식별합니다. 재귀적으로 탐색하므로 중첩된 객체나 배열에 대해서도 동일한 비교 알고리즘을 적용할 수 있습니다.

이러한 비교 알고리즘을 구현하기 위해 각 언어나 라이브러리는 자체적으로 다양한 방식과 최적화 기법을 사용할 수 있습니다. 그리고 deep-object-diff나 비슷한 도구들은 이러한 알고리즘을 구현하여 사용자에게 편리한 인터페이스를 제공하는 것입니다.


#271
15 중 3페이지