- 프런트엔드(클라이언트)에서 MP4 파일의 오디오 존재 여부를 확인하기
- 브라우저 API 사용 시 호환성 문제 발생 (Chrome, Safari, Firefox 각각 다른 API 사용)
- 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의 중요성을 깨달았다.
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 ExpressionDefstatic 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()
}
}
단순 초기화만 필요하면 일반 생성자가 더 직관적. 복잡한 생성 로직에만 사용.
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
interface와class를 통해 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())
}- https://stackoverflow.com/questions/58136102/deploy-individual-services-from-a-monorepo-using-github-actions
- https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#example-including-paths
최소한 하나의 경로가 paths 필터의 패턴과 일치하면 워크플로가 실행
on:
push:
paths:
- '**.js'
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로 구성
document.activeElement — 현재 포커스된 요소
const el = document.activeElement
if (el.tagName === 'INPUT') console.log(el.value)
// 모달 열릴 때 닫기 버튼으로 포커스 이동
document.querySelector('.modal .close-button').focus()
https://www.emgoto.com/jest-partial-match/
objectContaining·arrayContaining 은 toEqual·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 }),
])
)
})
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>
)
}@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 클래스에 적용