The Chrome DevTools Protocol (CDP) is a JSON-based protocol that connects debug and automation targets (such as browsing contexts and workers) with trusted debug and automation clients. It allows tools to instrument, inspect, debug, and profile Chromium, Chrome, and other Blink-based browsers.




Footnotes

  1. DevTools 화면 자체(TypeScript·CSS 웹앱). 이 프런트엔드가 CDP 로 대상과 대화하므로, 떼어다 다른 곳에 붙이면 자기만의 디버거가 된다 — 아래 chii 가 그렇게 쓴다. ↩

  2. Node 에서 CDP 를 부르는 클라이언트. 명령·이벤트를 JS API 로 감싸서, 예컨대 페이지를 열고 나가는 요청을 전부 덤프하는 걸 몇 줄로 짠다. ↩

  3. DevTools·CDP 생태계 링크 모음 — 학습 자료, 프로토콜 클라이언트, DevTools 프런트엔드를 다른 플랫폼에 쓰는 법, 확장. 마지막 커밋이 2021-01 이라 최신 도구는 빠져 있다. ↩

  4. 모바일 웹 페이지 안에 띄우는 가벼운 콘솔 패널(로그·네트워크·요소·스토리지). CDP 없이 페이지 안에서 돈다 — WeChat 미니프로그램의 공식 디버깅 도구. ↩

  5. weinre 같은 원격 디버거인데 화면을 최신 devtools-frontend 로 바꿨다. 디버깅할 페이지에 스크립트를 넣고, 다른 기기의 브라우저에서 DevTools 로 붙는다. ↩

#452

브라우저에서 PDF 및 이미지 파일에 대한 OCR 실행

  • PDF.js를 사용하여 PDF에서 이미지를 추출
  • Tesseract OCR로 추출된 이미지에서 텍스트를 인식
  • 직접 브라우저 상에서 OCR 작업을 실행
#431
getServerSideProps
  |> getServerSideSitemapLegacy
  |> withXMLResponseLegacy
  |>
    res.setHeader('Content-Type', 'text/xml')
    res.write(content)
    res.end()

https://github.com/iamvishnusankar/next-sitemap/blob/master/packages/next-sitemap/src/ssr/response.ts

SSR에서 XML 형식의 사이트맵을 생성하기.

#410
import * as React from 'react'

type ContainerProps = {
  children: typeof Body
}

type BodyProps = {
  id: string
}

function Container({ children }: ContainerProps) {
  const id = '1234'

  return <>{children({ id })}</>
}

export function Body({ id }: BodyProps) {
  return <>{id}</>
}

export function Page() {
  return <Container>{Body}</Container>
}

컨테이너/프레젠테이션 패턴을 활용한 React 컴포넌트 구조.

  • 컨테이너 컴포넌트(Container)는 상태를 관리하고 데이터를 하위 컴포넌트에 전달함.
  • 프레젠테이션 컴포넌트(Body)는 데이터를 받아서 UI를 렌더링함.
  • 이 구조는 테스트를 쉽게 하고, 컴포넌트의 역할을 명확하게 분리함.
#386
/**
 * - 인자 수 확인: `curry` 함수는 전달된 함수 `fn`의 인자 수를 확인
 * - 인자 수가 충분한 경우: 만약 `args`의 길이가 `fn`의 인자 수 이상이면, `fn`을 호출하고 결과를 반환
 * - 인자 수가 부족한 경우: 그렇지 않으면 추가 인자(`moreArgs`)를 받을 수 있는 새 함수를 반환. 이 함수는 기존 인자(`args`)와 새로운 인자(`moreArgs`)를 합쳐 다시 `curry(fn)`을 호출하여 최종적으로 인자가 충분할 때까지 이 과정을 반복
 */
const curry =
  (fn: Function) =>
  (...args: any[]) =>
    args.length >= fn.length
      ? fn(...args)
      : (...moreArgs: any[]) => curry(fn)(...args, ...moreArgs)

/**
 * - `...fns: Function[]` 여러 개의 함수를 인자로 받는다
 * - 내부에서 `reduce` 메서드를 사용하여 함수 배열을 순회 초기값으로 `x`를 사용하며, 각 함수 `fn`을 차례로 호출하여 결과를 다음 함수로 전달
 */
const pipe =
  (...fns: Function[]) =>
  (x: any) =>
    fns.reduce((acc, fn) => fn(acc), x)

/**
 * - `...fns`: 여러 개의 함수를 매개변수로 받음
 * - `(x)`: 초기값 `x`를 인자로 받아 결과를 리턴
 * - `fns.reduceRight(...)`: 배열의 오른쪽부터 왼쪽으로 각 함수를 적용합니다. `y`는 이전 함수의 결과이며, `f`는 현재 함수
 * - `f(y)`: 현재 함수 `f`를 이전 함수의 결과 `y`에 적용
 */
const compose =
  (...fns) =>
  (x) =>
    fns.reduceRight((y, f) => f(y), x)

  1. 함수 조합(Composition): 작은 함수들을 조합하여 새로운 함수를 만들며, 예를 들어 compose(f, g, h)는 f(g(h(x)))로 실행
  2. compose: 오른쪽에서 왼쪽으로 함수를 실행하며, compose(square, double)(3)은 36을 반환
  3. pipe: 왼쪽에서 오른쪽으로 함수를 실행하여, pipe(double, square)(3)의 결과는 36
  4. 커링(Currying): f(a, b, c)를 f(a)(b)(c) 형태로 변환하고, 예를 들어 add(1)(2)(3)의 결과는 6
  5. 부분 적용(Partial Application): 일부 인자만 미리 적용하여 새로운 함수를 만들 수 있으며, const double = multiply(2, _)로 정의
  6. 포인트-프리 스타일: 변수를 사용하지 않고 함수를 조합하여 작성하며, 예를 들면 compose(square, double)와 같은 형태
  7. 데이터 마지막 원칙(Data Last): 데이터를 마지막 인자로 배치하여 조합성을 높이는데, 예를 들어 const halve = divideDataLast(2)와 같이 사용

#385

1 MP4 는 아톰의 계층이고 moov 안의 트랙(trak)마다 mdia 안에 hdlr 아톰이 하나 있다. hdlr 은 크기 4바이트, 타입 ‘hdlr’ 4바이트, version 1 과 flags 3, component type 4, component subtype 4 순이다. MP4 에서는 version·flags·component type 이 모두 0 이라 ‘hdlr’ 바로 8바이트 뒤가 subtype 이고, 오디오 트랙이면 ‘soun’, 비디오 트랙이면 ‘vide’ 다. 그래서 파일 바이트에 hdlr 다음 0 이 8개, 그다음 soun 이 이어진 16바이트가 있으면 오디오가 있다. MOV 는 component type 에 ‘mhlr’ 이 들어갈 수 있어 이 패턴이 맞지 않는다. 2 파일 전체를 받지 않고 Range 로 앞에서부터 66KB 를 받아 패턴을 찾는다. 있으면 오디오 있음. 없는데 응답이 요청한 만큼 꽉 찼고 다음 단계가 남았으면 범위를 1MB, 2MB, 4MB, 8MB, 16MB 로 늘려 다시 받는다. 응답이 덜 왔거나(파일 끝) 마지막 단계까지 없으면 오디오 없음. soun 은 큰 파일일수록 뒤에 있다: 13MB 파일은 7.3KB, 742MB 파일은 573KB 안에서 나왔다. 3 함정: Range 헤더는 예전에는 CORS safelist 밖이라 교차 출처 요청에 preflight 가 나가고, 서버의 Access-Control-Allow-Headers 에 Range 가 없으면 실패한다. safelist 에 들어온 건 Chrome 99, Safari iOS 16.1, Firefox 117 부터다.

  • 프런트엔드(클라이언트)에서 MP4 파일의 오디오 존재 여부를 확인하기
  • 브라우저 API 사용 시 호환성 문제 발생 (Chrome, Safari, Firefox 각각 다른 API 사용)
    • MP4 파일의 구조를 분석하는 방법으로 방향 전환
      • hdlr 아톰에서 오디오 존재 여부를 나타내는 Component subtype 필드가 soun일 경우 오디오가 존재할 것임.
      • MP4 파일의 특정 바이트를 선택적으로 요청하기 위해 Range 필드 사용.
      • 다양한 파일 크기에 따라 ’soun’의 예상 위치를 조사하고, 적절한 범위 값 설정.
  • FileReader API를 사용하여 서버에서 응답받은 바이너리 데이터를 읽고, 이를 분석해 오디오 여부 확인.
    • 오디오 정보가 발견되지 않으면 추가 데이터 요청하여 확인 범위를 증가시켜 반복.

await ffmpeg.writeFile('input.mp4', await fetchFile(file))
await ffmpeg.ffprobe(['-i', 'input.mp4', '-show_streams', '-o', 'output.txt'])

const data = await ffmpeg.readFile('output.txt')

…하지만 ffmpeg의 중요성을 깨달았다.


#383

Figma의 권한 관리 DSL - JSON 직렬화 가능한 DSL로 정책 표현, TypeScript 기반 평가 엔진 구현.

기존 문제: 불필요한 복잡성, 계층적 권한 비효율, DB 부하, 여러 진실 소스

type ExpressionDef = BinaryExpressionDef | OrExpressionDef | AndExpressionDef

// 바이너리 표현식: [필드, 연산자, 값]
const binaryExpression = ['file.id', '<>', null] satisfies ExpressionDef

// AND/OR 조합
const andExpression = {
  and: [
    ['file.id', '<>', null],
    ['team.permission', '=', 'open'],
  ],
} satisfies ExpressionDef
#382
33 중 10페이지