JavaScript 깊은 복사 vs 얕은 복사, structuredClone 사용 가이드

개요 값 복사에서 가장 흔한 실수는 얕은 복사와 깊은 복사의 차이를 간과하는 것임 이 글은 JSON 해킹이나 라이브러리 대신 브라우저와 런타임이 제공하는 structuredClone으로 깊은 복사를 안정적으로 수행하는 방법과 주의점을 정리함 얕은 복사 JavaScript의 전개 연산자 …로 만드는 복사는 기본적으로 얕은 복사임 겉 모양과 1단계 속성은 복제되지만 중첩된 객체는 동일 참조를 공유함 const myOriginal = { someProp: "with a string value", anotherProp: { withAnotherProp: 1, andAnotherProp: true } } const myShallowCopy = { ...myOriginal } myShallowCopy.aNewProp = "a new value" console.log(myOriginal.aNewProp) // ^ logs `undefined` myShallowCopy.anotherProp.aNewProp = "a new value" console.log(myOriginal.anotherProp.aNewProp) // ^ logs `a new value` 기본형 값은 값 자체가 복사되지만 객체 같은 참조형은 레퍼런스가 복사됨 따라서 중첩된 구조를 안전하게 분리하려면 깊은 복사가 필요함 ...

February 5, 2026

Ramda 핵심 개념 정리와 R.prop 안전한 속성 접근, 커링·조합·주의사항

개요 Ramda는 JavaScript와 TypeScript에서 함수형 프로그래밍을 실용적으로 적용하기 위한 유틸리티 모음집임 순수 함수, 불변성, 커링, 함수 조합을 일관된 인터페이스로 제공하며, 매개변수 순서를 함수 → 데이터로 통일해 조합과 부분 적용을 자연스럽게 만듦 이 글은 R.prop을 중심으로 Ramda 핵심 함수의 개념과 사용법, 타입 고려사항, 흔한 함정과 우회 전략을 정리함 R.prop 개념과 문법 목적과 동작 원리 객체의 특정 속성 값을 안전하게 조회하는 읽기 전용 도구 속성이 없거나 undefined일 수 있는 상황에서 런타임 예외 없이 undefined 반환 커링 지원으로 속성 이름을 고정해 재사용 가능한 픽커 함수 구성에 유리함 시그니처 ...

February 4, 2026

JavaScript 배열 순회 패턴과 forEach 비동기 한계, 값·참조 전달 정리

배열을 순회하는 방법은 여러 가지가 있음. 전통적인 for, Array.prototype.forEach, ES6 for…of, 그리고 객체 속성에 적합한 for…in. 여기에 forEach의 비동기 제어 한계와 값/참조 전달 의미 차이를 함께 정리함 개념/배경 자바스크립트에서 순회는 목적과 제약에 맞춰 선택하는 문제임 for 인덱스 제어, 경계 조건, 성능 미세 조정, 조기 종료 필요 시 유용 forEach 선언적 스타일, 반환값 없음, 조기 종료 불가, 비동기 완료 제어 불가 for…of 이터러블 값 순회에 적합, 인덱스 필요 없을 때 간결, await와 결합 가능 for…in 객체의 열거 가능 속성 순회용. 배열에는 권장하지 않음. 키 순서와 상속 프로퍼티 이슈 존재 이터러블과 이터레이터 프로토콜을 따르는 객체에 for…of를 적용 가능. 배열, 문자열, Map, Set 등이 대표적임 ...

January 27, 2026

TypeScript/ESM import 경로 정리: '@', '#', 상대 경로의 의미와 설정

개요 TypeScript와 ESM에서 자주 보이는 세 가지 import 패턴 import … from ‘@…’ import … from ‘…’ import … from ‘#…’ 표기만 비슷할 뿐, 해석 주체와 동작 범위가 다름 ‘@‘는 경로 별칭 또는 npm 스코프 패키지 의미 가능 ‘…‘는 상대·절대 경로로 파일 시스템 기준 해석 ‘#‘는 Node.js 패키지 imports 또는 브라우저 import maps에서의 별칭으로 사용 아래에서 각 패턴의 의미, 설정 지점, 주의사항을 정리함 ‘@…’ 경로의 두 가지 의미 1) 경로 별칭 path alias 의도: 길고 복잡한 상대 경로를 짧게 추상화 설정 지점: tsconfig.json 의 compilerOptions.paths와 baseUrl 예시 { "compilerOptions": { "baseUrl": "./", "paths": { "@models/*": ["src/models/*"], "@utils/*": ["src/utils/*"] } } }import { User } from '@models/User' import { calculate } from '@utils/math' 장점 상대 경로를 단순화, 가독성 및 리팩터링 내성 향상 주의 tsconfig paths는 타입 체크러와 에디터가 이해하는 별칭일 뿐, 런타임 해석자는 아님 Node.js 런타임은 tsconfig paths를 모름. 번들러 설정별 alias 또는 전용 로더를 함께 구성 필요 예: Vite, Webpack, ts-node, tsconfig-paths 등 도구별 설정 일치 필요 2) 스코프된 패키지 scoped package 의미: npm 조직·팀·프로젝트 네임스페이스로 묶인 패키지 집합 표기: @scope/package-name 형태 @nestjs/swagger, @angular/core 등 사용 이유 네임스페이스로 이름 충돌 회피 관련 패키지의 그룹화와 공개·비공개 관리 설치 npm install @nestjs/swagger 해석 이 경우 ‘@‘는 경로 별칭이 아닌 패키지 이름의 일부로 동작 tsconfig paths와 무관. Node/npm가 패키지 이름으로 직접 해석 ‘…’ 상대·절대 경로 import 상대 경로 ‘./’, ‘../’ 기준으로 현재 파일 위치에서 탐색 절대 경로 ‘/path’는 실행 환경마다 기준이 다름 브라우저 ESM에서는 오리진 기준 절대 URL 경로 해석 Node.js에서는 파일 시스템 루트 절대 경로로 해석되어 이식성 낮음 예시 import { User } from './models/User' import { calculate } from '../utils/math' 장점 추가 설정 없이 즉시 동작, 모든 환경 공통 동작 모델 모듈 간 물리적 의존 관계가 드러남 단점 디렉터리 깊어질수록 ../../../ 형태로 복잡도 상승 구조 변경 시 경로 대량 수정 발생 ‘#…’ 경로의 의미 ‘#{name}’ 표기는 두 가지 서로 다른 맥락에서 등장함. 혼동 주의 ...

January 23, 2026

자바스크립트 비동기와 이터레이터 정리 — 기다림, 동시성, 백프레셔

개요 비동기는 강력한 도구임. 다만 배열과 스트림 같은 이터레이터와 결합되면 누가 무엇을 언제 기다리는지 불명확해지기 쉬움 핵심 포인트 세 가지 기억 완료 보장 확보했는지 동시성 제어를 명시했는지 백프레셔로 생산 속도 ≤ 소비 속도 유지했는지 비동기가 의도대로 동작하지 않는 케이스 forEach + async 사용 콜백이 반환한 프로미스를 외부가 수집하지 않음 → 완료 보장 깨짐, 레이스와 누락 가능성 증가 items.forEach(async (x) => { await doAsync(x) // 외부에서 기다리지 않음 }) map + async 이후 기다리지 않음 ...

January 10, 2026

TypeScript 객체 구조 분해 할당으로 프로퍼티 이름 변경하기

개념/배경 객체 구조 분해 할당에서 프로퍼티 이름을 바꾸는 기본형은 { 기존프로퍼티명: 새변수명 } 형태임 사용법/예시 아래는 request 프로퍼티를 revealRequest라는 이름으로 받는 예시 const { request: revealRequest } = await client.doSomething({...})의미는 다음과 같음 doSomething이 반환하는 객체에서 request 프로퍼티 추출 추출한 값을 revealRequest 변수에 바인딩 아래 전개 코드와 동일함 const result = await client.doSomething({...}) const revealRequest = result.request왜 쓰는가 코드 간결화 필요한 프로퍼티만 선택 추출 문맥에 맞게 변수명 명확화, 의도 드러남 참고자료 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

December 30, 2025

Object.entries 사용법과 주의사항

개요 Object.entries는 객체의 키-값 쌍을 [key, value] 형태의 배열 리스트로 반환하는 표준 메서드임. 객체를 배열로 바꿔 순회, 변환, 자료구조 간 변환 같은 작업을 단순화하는 데 유용함 문법 Object.entries(obj) 매개변수 obj: 키-값 쌍을 가진 객체 값 반환값: [key, value] 쌍의 2차원 배열 기본 예시 const user = { name: 'Alice', age: 25 } Object.entries(user) // [['name', 'Alice'], ['age', 25]] 배열로 변환되므로 for…of, map 같은 배열 API에 바로 연결 가능 순회와 변환 for…of로 키와 값을 동시에 순회 가능 ...

December 27, 2025

자바스크립트에서 균등한 배열 셔플 구현 피셔‑예이츠와 sort+random 편향 이슈

개념과 배경 배열을 무작위로 섞을 때 Array.prototype.sort와 Math.random을 조합한 패턴이 흔히 보임 array.sort(() => Math.random() - 0.5) 형태는 간단해 보이지만 결과 분포가 한쪽으로 치우치는 편향이 발생함 간단한 실험으로 [1, 2, 3]을 백만 회 섞어 빈도를 집계해 보면 특정 순열이 과도하게 많이 나오거나 적게 나오는 경향 관찰됨 핵심은 Math.random 자체보다 sort에 전달된 비교 함수가 랜덤하게 일관성 없이 값을 내놓는 구조라는 점임 왜 sort + random이 편향되는가 정렬 알고리즘은 비교 함수가 다음 성질을 만족한다고 가정함 ...

December 23, 2025

JavaScript에서 객체 대신 Map/Set을 선택해야 하는 경우와 이유

개요 객체는 만능처럼 보이지만 모든 키-값 저장에 적합한 도구는 아님 키를 자주 추가·삭제하고 임의 키로 조회하는 해시맵 유사 패턴에서는 Map이 더 안전하고 일관된 성능을 제공함 중복 없는 집합 연산에는 Set이 자연스러운 선택지 언제 객체 대신 Map을 고려할까 키를 자주 추가·삭제하는 동적 해시맵 패턴 안전한 반복과 구조 분해가 필요한 경우 키의 삽입 순서 유지가 중요한 경우 비문자열 키가 필요한 경우 // 객체 기반 임의 키-값 해시맵 패턴은 delete에서 성능 불리할 수 있음 const mapOfThingsObj = {}; mapOfThingsObj[thing.id] = thing; delete mapOfThingsObj[thing.id]; // Map은 동적 추가·삭제에 최적화 const mapOfThings = new Map(); mapOfThings.set(thing.id, thing); mapOfThings.delete(thing.id);성능 배경 객체는 VM이 숨은 클래스/셰이프를 가정해 최적화하는 경향이 있어 구조가 변동하면 디옵티마이즈 유발 가능성 존재 Map은 해시맵 사용에 맞춰 키 추가·삭제가 빈번한 경우를 목표로 설계됨 마이크로벤치마크는 한계가 있으므로 실제 워크로드에서 측정 권장, 다만 Map이 해당 패턴에 맞춰 설계된 점은 문서로 확인 가능 ...

December 21, 2025

date-fns로 자바스크립트 날짜·시간 처리 23가지 실전 패턴

개요 자바스크립트에서 날짜·시간을 다루는 일은 사소해 보이지만 버그가 가장 잦은 영역 중 하나임 day.js, moment.js, luxon 등 대안 존재하나 date-fns는 모듈화, 작은 크기, 불변성 중심 설계로 프런트엔드와 백엔드 모두에 적합함 아래는 date-fns와 date-fns-tz를 활용해 바로 써먹을 수 있는 23가지 패턴 정리 핵심 개념 Date 객체는 타임존이 아닌 UTC 타임스탬프를 보유하는 값이라는 점이 핵심 date-fns 함수는 대부분 불변 동작, 인자로 받은 Date를 변경하지 않음 포맷팅은 format, 상대 표기는 formatDistance 계열, 구간 계산은 differenceIn*, intervalToDuration 등 사용 파싱은 parseISO 또는 parse로 명시적 포맷 지정 권장 타임존 변환은 date-fns-tz의 utcToZonedTime, zonedTimeToUtc, formatInTimeZone 사용 실전 패턴 현재 날짜와 시간 가져오기 const now = new Date() 특정 날짜·시간 설정 import { setMinutes, setHours } from 'date-fns' const specific = setHours(setMinutes(new Date(), 30), 17) // 17:30 날짜 포맷팅 import { format } from 'date-fns' const ymd = format(new Date(), 'yyyy-MM-dd') 기간 더하기·빼기 import { addDays, subMonths } from 'date-fns' const inAWeek = addDays(new Date(), 7) const aMonthAgo = subMonths(new Date(), 1) 특정 요일까지 남은 일수 import { nextDay, differenceInDays } from 'date-fns' const nextMon = nextDay(new Date(), 1) // 0 일요일, 1 월요일 const d = differenceInDays(nextMon, new Date()) 두 날짜 차이 계산 import { differenceInDays } from 'date-fns' const diff = differenceInDays(new Date(2023, 11, 31), new Date(2023, 0, 1)) 동일한 날짜 비교 import { isSameDay } from 'date-fns' const same = isSameDay(new Date(2023, 0, 1), new Date('2023-01-01T05:00:00Z')) 윤년 확인 import { isLeapYear } from 'date-fns' const isLeap = isLeapYear(new Date(2024, 0, 1)) 날짜 유효성 검사 import { isValid } from 'date-fns' const ok = isValid(new Date('2023-02-30')) // false 로컬과 특정 타임존 간 변환 import { utcToZonedTime, zonedTimeToUtc, formatInTimeZone } from 'date-fns-tz' const tz = 'America/New_York' const utc = zonedTimeToUtc(new Date(), tz) const inNY = utcToZonedTime(new Date(), tz) const shown = formatInTimeZone(new Date(), tz, 'yyyy-MM-dd HH:mm:ssXXX') 문자열 파싱 import { parseISO, parse } from 'date-fns' const a = parseISO('2023-01-01') const b = parse('01/02/2023 17:30', 'MM/dd/yyyy HH:mm', new Date()) 상대 시간 표현 import { formatDistanceToNow } from 'date-fns' import { ko } from 'date-fns/locale' const rel = formatDistanceToNow(new Date(Date.now() - 3 * 24 * 60 * 60 * 1000), { addSuffix: true, locale: ko }) 과거·미래 판별 import { isPast, isFuture } from 'date-fns' const past = isPast(new Date('2023-01-01')) const future = isFuture(new Date(Date.now() + 60_000)) 특정 기간의 날짜 배열 생성 루프 대신 eachDayOfInterval 사용 권장 ...

December 17, 2025