URL.parse() 메서드를 활용하여 URL 객체를 생성하고 처리하는 방법

  • URL.parse(url) 메서드는 주어진 URL에 따라 새 URL 객체를 생성
  • 유효하지 않은 URL 값이 주어질 경우 null을 반환
  • 두 번째 파라미터 base는 상대 URL을 해석하기 위한 기준 URL로 사용되며, 이를 통해 URL의 경로가 올바르게 조정됨
  • URL 객체나 다른 문자열을 파라미터로 사용할 수 있으며, 내부에서 문자열로 변환됨
describe('null 입력에 대한 URL API 동작 테스트', () => {
  test('new URL(null)은 TypeError를 발생시킨다', () => {
    expect(() => new URL(null)).toThrow(TypeError)
  })

  test('URL.canParse(null)은 false를 반환한다', () => {
    expect(URL.canParse(null)).toBe(false)
  })

  test('URL.parse(null)은 null을 반환한다', () => {
    expect(() => URL.parse(null)).toBe(null)
  })
})
#368
  • 검색 매개변수별로 데이터 유효성 검증
  • useSearchParamsZod를 활용해 모든 검색 매개변수를 병합하고 유효성 검증
import { useMemo } from 'react'
import { useSearchParams } from 'react-router-dom'
import { z } from 'zod'

const CombinedSchema = PaginationSchema.merge(FilterSchema)
  .merge(SortSchema)
  .merge(SearchSchema)
  .partial()

export function useSearchParamsWithSchema() {
  const [searchParams, setSearchParams] = useSearchParams()

  const parsedParams = useMemo(() => {
    // searchParams를 객체로 변환
    const paramsObject = Object.fromEntries(searchParams.entries())
    // 결합된 스키마로 유효성 검사 및 파싱
    const result = CombinedSchema.safeParse(paramsObject)

    if (result.success) {
      return result.data
    }

    console.error(result.error)

    return {}
  }, [searchParams])

  /**
   * 새로운 검색 매개변수로 업데이트하는 함수입니다.
   *
   * 1. 현재 검색 매개변수의 복사본을 생성합니다.
   * 2. 새로운 매개변수를 순회하며 값을 설정하거나 삭제합니다.
   * 3. 최종적으로 업데이트된 매개변수를 state에 설정합니다.
   */
  const updateSearchParams = (newParams: Record<string, string>) => {
    const updatedParams = new URLSearchParams(searchParams)

    Object.entries(newParams).forEach(([key, value]) => {
      if (value) {
        // 업데이트된 매개변수로 상태 갱신
        updatedParams.set(key, value)
      } else {
        // 값이 없으면 삭제
        updatedParams.delete(key)
      }
    })

    // 업데이트된 매개변수로 상태 갱신
    setSearchParams(updatedParams)
  }

  return {
    parsedParams,
    updateSearchParams,
  }
}
#367

취소 가능한 fetch 요청을 생성하는 createCancelableFetch 함수 구현

/**
 * 취소 가능한 fetch 요청
 *
 * @param url - 요청 URL
 * @param options - fetch 옵션
 * @returns {run, cancel} - run: 요청 실행 함수, cancel: 요청 취소 함수
 *
 * @example
 *
 * ```ts
 * const { run, cancel } = createCancelableFetch('/api/data')
 *
 * run()
 *   .then((data) => console.log('Fetched data:', data))
 *   .catch((err) => console.error('Error or canceled:', err))
 *
 * cancel()
 * ```
 */
function createCancelableFetch(url: string, options: RequestInit = {}) {
  const abortController = new AbortController()

  const run = async () => {
    const response = await fetch(url, {
      ...options,
      signal: abortController.signal,
    })

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    return response.json()
  }

  const cancel = () => abortController.abort()

  return {
    run,
    cancel,
  }
}
  • AbortController를 사용하여 요청을 중단할 수 있는 기능 제공
  • run 함수로 fetch 요청을 실행하고, cancel 함수로 요청을 취소할 수 있음
  • 요청 실패 시 오류를 처리하고 예외를 발생시킴

#360

field-sizing를 사용하면 콘텐츠를 기반으로 크기 조절을 사용 설정하는 데 CSS 한 줄이 필요합니다. 이 콘텐츠 기반 크기 조절 스타일은 textarea 외에도 다른 요소에도 적용됩니다.

#358

비동기 호출의 결과와 오류를 튜플 형식으로 반환

const [error, data] = await tuple(someAsyncFunction())
// 성공: [null, 결과값]
// 실패: [error] 또는 [new TupleItError(error)]

오류 처리 간소화 - 단일 체크로 오류 관리 가능

#357
💡

useStateObject는 React의 useState를 확장한 가벼운 래퍼로, 객체 상태 관리를 간편하게 할 수 있도록 설계되었습니다.


export type StateObject<T extends object> = T & {
  set: React.Dispatch<React.SetStateAction<T>>
  setItem: <K extends keyof T>(key: K, value: T[K]) => void
  merge: (newState: Partial<T>) => void
  reset: () => void
}

그렇다면 MapSet도 시도해보기

function useStateMap<K, V>(init: Iterable<[K, V]> = []) {
  const [map, setMap] = useState(new Map<K, V>(init))

  const update = useCallback(
    (updater: (currentMap: Map<K, V>) => void) => {
      setMap((prev) => {
        const newMap = new Map(prev)
        updater(newMap)
        return newMap
      })
    },
    [setMap]
  )

  return {
    map,
    set: (key: K, value: V) => update((m) => m.set(key, value)),
    delete: (key: K) => update((m) => m.delete(key)),
    clear: () => setMap(new Map()),
    has: (key: K) => map.has(key),
    get: (key: K) => map.get(key),
    entries: () => Array.from(map.entries()),
    size: map.size,
  }
}
function useStateSet<T>(init: Iterable<T> = []) {
  const [set, setSet] = useState(new Set<T>(init))

  const update = useCallback(
    (updater: (currentSet: Set<T>) => void) => {
      setSet((prev) => {
        const newSet = new Set(prev)
        updater(newSet)
        return newSet
      })
    },
    [setSet]
  )

  return {
    set,
    add: (value: T) => update((s) => s.add(value)),
    delete: (value: T) => update((s) => s.delete(value)),
    has: (value: T) => set.has(value),
    clear: () => setSet(new Set()),
    entries: () => Array.from(set),
    size: set.size,
  }
}

#354

thisisunsafe

-> 로컬 환경 또는 테스트 서버에서 강제 접근

  • 웹사이트의 SSL/TLS 인증서를 신뢰할 수 없을 때, 이 사이트에 연결할 수 없음 또는 이 연결은 비공개로 설정되지 않았습니다
  • NET::ERR_CERT_AUTHORITY_INVALID, NET::ERR_CERT_COMMON_NAME_INVALID 같은 오류 코드가 나타남
#352

템플릿 리터럴 타입을 활용하여 타입 정의하기

import * as React from 'react'

type RenderPropNames = 'Title' | 'Content' | 'Actions'

type RenderProps = {
  [K in RenderPropNames as `render${K}`]: () => React.ReactNode
}

type Props = RenderProps

/**
 * @example
 *
 * ```tsx
 * <DialogComponent
 *   renderTitle={() => <h2>Title</h2>}
 *   renderContent={() => <p>Content</p>}
 *   renderActions={() => (
 *     <div>
 *       <button onClick={handleClose}>Close</button>
 *       <button onClick={handleSubmit}>Submit</button>
 *     </div>
 *   )}
 * />
 * ```
 */
function Dialog({
  renderTitle,
  renderContent,
  renderActions,
}: Props) => {
  return (
    <div data-scope="root">
      <div data-part="content">
        {renderTitle()}
        {renderContent()}
      </div>
      <div data-part="actions">{renderActions()}</div>
    </div>
  )
}
#350

대화 상자(Dialog) 유형

Modal - 상호작용 차단

  • System Modal: 닫기 전까지 다른 작업 불가
  • Application Modal: 프로그램 일시 중단
  • Document Modal: 부모 창만 차단 (macOS 시트)

Modeless - 상호작용 허용 (예: 툴바)

모달은 워크플로우 방해 → 비필수 작업에는 모델리스 선호

313.md 참고

#349
25 중 7페이지