• 프런트엔드(클라이언트)에서 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

static create 패턴 - 생성자 대신 정적 메서드로 객체 생성

언제 사용?

  • 생성 로직이 복잡하거나 유효성 검사 필요
  • 생성 실패 시 null/Result 반환 (생성자는 항상 인스턴스 반환)
  • 팩토리 패턴, 싱글톤
class User {
  private constructor(private readonly name: string) {}

  // 유효성 검사 + 실패 시 null 반환
  static create(name: string): User | null {
    if (!name || name.length < 3) return null
    return new User(name)
  }
}

// 팩토리 패턴
class Shape {
  static create(type: 'circle' | 'rect', size: number): Shape {
    return type === 'circle' ? new Circle(size) : new Rectangle(size)
  }
}

// 싱글톤
class Config {
  private static instance: Config
  private constructor() {}
  static create() {
    return Config.instance ??= new Config()
  }
}

단순 초기화만 필요하면 일반 생성자가 더 직관적. 복잡한 생성 로직에만 사용.

#381

Every type is defined by its intro and elim forms

  • Intro forms: 타입의 인스턴스를 어떻게 “생성”하는지 정의.
  • Elim forms: 생성된 타입 인스턴스를 어떻게 “사용”하거나 “해체”할지 정의.

타입을 정의할 때, 생성과 사용의 명확한 경계를 설정해서 Intro/Elim 설계를 명시적으로 표현하기

class Rectangle {
  private constructor(public width: number, public height: number) {}

  static create(width: number, height: number) {
    if (width <= 0 || height <= 0) {
      return null
    }

    return new Rectangle(width, height)
  }

  getArea() {
    return this.width * this.height
  }
}

const rect = Rectangle.create(10, 20)

if (rect) {
  console.log(rect.getArea())
}

Types are not their elim forms

  • interfaceclass를 통해 Intro/Elim 모두를 명시적으로 정의
  • 팩토리 메서드 같은 패턴을 활용해 생성 방식을 추상화
interface Shape {
  getArea(): number
}

class Circle implements Shape {
  constructor(private radius: number) {}

  getArea() {
    return Math.PI * this.radius ** 2
  }
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}

  getArea() {
    return this.width * this.height
  }
}

function createShape(type: 'circle' | 'rectangle', ...args: number[]) {
  if (type === 'circle' && args.length === 1) {
    return new Circle(args[0])
  }

  if (type === 'rectangle' && args.length === 2) {
    return new Rectangle(args[0], args[1])
  }

  return null
}

const shape = createShape('circle', 10)

if (shape) {
  console.log(shape.getArea())
}
#380
type JsonPrimitive = string | number | boolean | null
type JsonObject = { [Key in string]: JsonValue } & {
  [Key in string]?: JsonValue | undefined
}
type JsonArray = JsonValue[] | readonly JsonValue[]
type JsonValue = JsonPrimitive | JsonObject | JsonArray

예전에 JSON 타입 정의가 필요해서 찾아봤던 내용

  • JsonObject: 문자열 키와 JsonValue 타입의 값을 가진 JSON 객체를 정의
  • JsonArray: JsonValue 타입의 요소를 포함하는 JSON 배열을 정의
  • JsonPrimitive: 문자열, 숫자, 불린, 또는 null과 같은 유효한 JSON 기본 값을 정의
  • JsonValue: 유효한 JSON 값을 나타내며, JsonPrimitive, JsonObject, 또는 JsonArray로 구성

#376

document.activeElement — 현재 포커스된 요소

const el = document.activeElement

if (el.tagName === 'INPUT') console.log(el.value)

// 모달 열릴 때 닫기 버튼으로 포커스 이동
document.querySelector('.modal .close-button').focus()
#375

https://www.emgoto.com/jest-partial-match/

objectContaining·arrayContainingtoEqual·toHaveBeenCalledWith 안에 중첩해서 쓴다.

test('호출 인자 부분 매칭', () => {
  const mockFunction = jest.fn()

  mockFunction({ id: 1, name: 'Alice', tags: ['developer', 'designer'] })

  expect(mockFunction).toHaveBeenCalledWith(
    expect.objectContaining({
      name: 'Alice',
      tags: expect.arrayContaining(['designer']),
    })
  )
})

test('배열 안 객체 부분 매칭', () => {
  const receivedArray = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ]

  expect(receivedArray).toEqual(
    expect.arrayContaining([
      expect.objectContaining({ name: 'Alice' }),
      expect.objectContaining({ id: 2 }),
    ])
  )
})
#374
  • navigate(-1)의 위험성: 브라우저 히스토리에서 이전 위치로 이동, 앱 내부 네비게이션과 혼란을 초래할 수 있음
  • 대신 Link 컴포넌트의 state 속성을 활용하여 안전하게 앱 내에서의 “Back” 네비게이션 구현 가능
  • 사용자에게 현재 URL을 반환하는 커스텀 훅 useCurrentURL 구현 및 재사용의 용이성을 제공하는 useBackNavigation 훅 정의
function PreserveStateLink(props) {
  const location = useLocation()
  const currentURL = location.pathname + location.search

  return (
    <Link state={{ back: currentURL }} {...props}>
      {children}
    </Link>
  )
}

function BackLink() {
  const navigate = useNavigate()
  const location = useLocation()

  const handleClick: LinkProps['onClick'] = (e) => {
    const back = location.state?.back

    if (back) {
      e.preventDefault()

      navigate(back)
    }
  }

  return (
    <Link to="/todos" onClick={handleBack}>
      Back
    </Link>
  )
}
#373
@value b from "./b.module.css";

.root {
  color: aquamarine;
}

.root :global(.b) {
  text-decoration: line-through;
}

CSS 모듈에서 변수를 값으로 내보내고 사용하는 방법

  • PostCSS와 postcss-modules-values 플러그인을 사용하여 CSS 모듈 내에서 변수 값 내보내기 지원
  • 색상 변수를 정의하는 파일 생성
    • 변수 선언: @value 구문 사용
  • 다른 CSS 모듈 파일에서 해당 변수를 가져와서 사용
    • 변수 가져오기 및 CSS 클래스에 적용
#371
30 중 10페이지