Node.js 글로벌 에러 핸들러 가이드

개념/배경 Node.js 프로세스 레벨에서 잡히지 않은 에러를 마지막으로 처리하는 안전망을 두는 목적 정리 애플리케이션 어디에서도 처리되지 못한 오류를 기록하고 종료 경로를 일관되게 관리하기 위함 프로덕션 환경에서 원인 파악과 사후 조치 자동화를 위한 최소 장치로 간주 핵심 이벤트 두 가지 이벤트가 글로벌 핸들러의 대상 uncaughtException try-catch로 포착되지 않은 동기 오류가 호출 스택 끝까지 전파될 때 발생 핸들러가 없으면 프로세스가 비정상 종료됨 // 동기 에러가 상위에서 잡히지 않은 경우 function boom() { throw new Error('폭발') } boom() unhandledRejection Promise가 reject되었으나 await 혹은 .catch로 처리되지 않은 경우 발생 Node.js 15+ 기본 동작은 핸들러가 없을 때 프로세스 종료 // reject가 소비되지 않은 경우 async function asyncBoom() { throw new Error('비동기 폭발') } asyncBoom()왜 글로벌 핸들러인가 코드 최상위에서 아무도 잡지 못한 에러에 대한 마지막 안전망 역할 ...

December 12, 2025

Kubernetes에서 Node.js Liveness와 Readiness Probe 설계 가이드

개요 Kubernetes 환경에서 Liveness Probe와 Readiness Probe는 애플리케이션 상태를 주기적으로 점검해 자동 복구와 트래픽 차단을 수행하는 안전 장치 역할 수행 Node.js는 싱글 스레드 기반으로 이벤트 루프 차단이나 메모리 누수 상황이 치명적이라 두 프로브의 설계가 안정성에 직접적인 영향 미침 핵심 차이 Liveness Probe의 질문은 너 살아있니, 실패 시 컨테이너 재시작 수행 Readiness Probe의 질문은 일할 준비 됐니, 실패 시 서비스 엔드포인트에서 제외해 트래픽 차단 수행 본질적 차이는 실패 시 K8s가 취하는 액션이며 재시작과 트래픽 제어로 구분됨 Node.js 맥락 Liveness Probe 대상 상황 이벤트 루프 블로킹으로 요청 처리 불가 상태 무한 루프 또는 데드락과 유사한 좀비 상태로 PID는 있으나 응답 불능 메모리 누수로 OOM 임박하여 응답 지연 또는 멈춤에 가까운 상태 Readiness Probe 대상 상황 프로세스는 떠 있으나 초기화 작업 진행 중인 상태 DB 연결 수립 중, 대용량 설정 로딩, 캐시 워밍업 등으로 실제 서비스 처리 불가 상태 유의점 Liveness는 가벼운 체크로 한정, 외부 의존성까지 포함 시 불필요한 재시작 유발 위험 Readiness는 실제 트래픽 처리 가능 여부를 반영해야 하며 의존성 준비 상태를 포함하는 편이 안전함 구현 스니펫 Node.js에서 Liveness는 최소한의 핑 수준으로, Readiness는 의존성 준비 여부를 반영하는 형태 권장 ...

December 11, 2025

JavaScript Array.prototype.flatMap 개념과 사용 예시

개념 flatMap은 각 요소를 매핑한 결과를 한 단계만 평탄화하는 배열 메서드 arr.map(fn).flat(1)과 동등 동작 콜백 시그니처 (value, index, array) 콜백이 배열을 반환하면 concat 후 depth 1 평탄화 더 깊은 중첩은 유지됨 희소 배열의 빈 슬롯은 평탄화 과정에서 제거됨 ES2019 이후 표준 지원 범위 넓음 예시 const arr = [1, 2, 3, 4] arr.flatMap(x => [x * 2]) // => [2, 4, 6, 8] arr.flatMap(x => [[x * 2]]) // => [[2], [4], [6], [8]] const s = ["it's Sunny in", "", "California"] s.flatMap(x => x.split(" ")) // => ["it's","Sunny","in","","California"] 주의와 팁 깊이 2 이상 평탄화 필요 시 flat(depth) 사용 또는 map + flat 조합 고려 콜백은 배열을 반환하는 패턴 권장, 스칼라를 섞으면 의도치 않은 구조 발생 가능 희소 배열 처리 시 구멍이 사라지는 특성 주의 성능상 불필요한 중첩 생성은 피하고 필요한 경우에만 flatMap 사용 권장 참고자료 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap

December 10, 2025

NestJS forRoot()의 동작 원리와 싱글톤에 대한 오해

개요 NestJS을 다루다 보면 ConfigModule.forRoot(), TypeOrmModule.forRoot() 같은 코드를 보게 됨 보통 “이건 전역 설정이니까 한 번만 하면 끝이고 알아서 싱글톤 유지되겠지?“라고 생각하기 쉬움 하지만 forRoot()를 호출한다고 프레임워크가 알아서 물리적인 싱글톤 인스턴스를 강제하는 건 아님 특히 ScheduleModule처럼 사이드 이펙트(이벤트 리스너, 타이머 등)를 유발하는 모듈을 잘못 다루면, 기능이 중복 실행되는 심각한 버그가 터질 수 있음 이 글에서는 forRoot()의 진짜 의미와 내부 동작, 그리고 ScheduleModule 중복 실행 문제가 왜 생기는지 코드로 뜯어보겠음 forRoot()란 무엇인가 forRoot()는 NestJS의 동적 모듈(Dynamic Module)을 생성하기 위해 관례적으로 쓰는 메서드 이름임 ...

December 7, 2025

전략 패턴 (Strategy Pattern) - 유연한 코드 만들기

전략 패턴이란? 객체의 특정 행동(알고리즘)을 직접 구현하지 않고, 외부에서 ‘전략’을 주입받아 갈아 끼우는 방식임 쉽게 말해 “본체는 그대로 두고, 부품만 바꿔서 기능을 바꾸는 것"이라 보면 됨 전략 패턴이 적용되지 않은 코드 전략 패턴을 쓰지 않으면 보통 이렇게 구현하게 됨 class Vehicle { constructor(private type: "road" | "rail") {} move() { if (this.type === "road") { console.log("부릉부릉 도로로 감 🚌"); } else if (this.type === "rail") { console.log("칙칙폭폭 선로로 감 🚂"); } } }이 방식이 가지는 문제는 다음과 같음 조건문이 계속 늘어남 새로운 이동 방식이 생길 때마다 move()를 수정해야 함 → OCP(Open-Closed Principle) 위반 ...

December 6, 2025

VARCHAR(n) 길이 기준 정리 — 글자 수인가 바이트 수인가

개념/배경 VARCHAR(n)에서 n을 글자 수로 볼지 바이트 수로 볼지 혼동 많음 표준 SQL의 character varying(n)은 최대 글자 수 의미이나, 실제 구현은 DBMS와 문자셋 설정에 따라 달라짐 멀티바이트 문자셋에서는 저장 바이트 수와 글자 수가 다름. 길이 제한은 글자 수 기준일 수 있으나 내부 저장은 바이트 단위로 이뤄짐 DBMS별 동작 MySQL VARCHAR(n)에서 n은 글자 수 의미 utf8mb4 사용 시 글자 하나가 최대 4바이트까지 소요. 행 크기 제한 등으로 인해 저장 가능 여부는 바이트 한계에도 영향 받음 PostgreSQL character varying(n)에서 n은 글자 수 의미 저장은 바이트 단위이나 제약은 글자 수 기준으로 평가 SQL Server varchar(n)은 n이 바이트 수. 멀티바이트 문자 사용 시 같은 n이라도 담을 수 있는 글자 수 감소 nvarchar(n)은 n이 글자 수. 유니코드 2바이트 단위 저장. 글자 수 기준 제약 필요 시 nvarchar 사용 권장 Oracle VARCHAR2(n)은 기본이 바이트 기준. 세션/시스템에서 CHAR semantics 또는 컬럼 정의 시 VARCHAR2(n CHAR)로 명시하면 글자 수 기준 실무 팁 한글 100자, 영어 100자 모두 허용 기대라면 글자 수 기준 타입 필요 MySQL VARCHAR(100), PostgreSQL varchar(100), SQL Server에서는 nvarchar(100), Oracle에서는 VARCHAR2(100 CHAR) 선택 저장 바이트 한계 고려 필요. MySQL은 행 크기 한계, Oracle/SQL Server도 페이지 크기 등 제약 존재 길이 함수 차이 주의. 바이트 길이와 글자 길이 함수가 다른 경우 존재. 예를 들어 글자 길이 검증은 문자 길이 함수 사용 권장 이모지, 결합 문자 등 특수 유니코드 조합은 사용자 체감 글자 수와 코드 포인트 수가 다를 수 있음. 제품 요구사항에 맞는 길이 기준 정의 필요 정리 VARCHAR(n)이 항상 바이트 무관이라는 주장은 오해 많은 DBMS에서 n은 글자 수지만, SQL Server의 varchar처럼 바이트 기준인 구현 존재 문자셋과 저장 한계를 함께 고려해야 안정적인 길이 설계 가능 한글도 100글자, 영어도 100글자라는 기대를 보장하려면 글자 수 기준 타입과 설정을 명시적으로 선택할 것 참고자료 https://dev.mysql.com/doc/refman/8.0/en/char.html https://www.postgresql.org/docs/current/datatype-character.html https://learn.microsoft.com/sql/t-sql/data-types/char-and-varchar-transact-sql https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Data-Types.html#GUID-3B0B0A24-FA05-4A1F-902E-2E6D0BF85673

December 5, 2025

Kubernetes Controllers 핵심 개념과 실전 가이드

개요 쿠버네티스에서 컨트롤러는 제어 루프를 통해 원하는 상태와 현재 상태를 맞추는 두뇌 역할 수행 사용자가 선언한 스펙을 지속 관찰하고 차이를 줄이기 위해 리소스를 생성·수정·삭제하는 자동화 구성 요소 이 문서는 주요 컨트롤러를 실무 맥락과 비유를 곁들여 정리하며, 디버깅 관점에서의 관계와 주의점을 함께 정리 워크로드 컨트롤러 Deployment 역할: 상태 비저장 앱의 표준 배포 단위, 버전 관리와 롤링 업데이트 담당 특기: 롤링 업데이트로 무중단에 가까운 배포, 이슈 시 손쉬운 롤백 관계: 파드 직접 관리 대신 ReplicaSet을 통해 간접 관리 ReplicaSet ...

December 4, 2025

이벤트 루프와 비동기, await에 대한 오해

Node.js 기본 구조 graph TB subgraph STACK[Call Stack] S1[현재 실행 중인 함수] end subgraph LOOP[Event Loop] EL[Call Stack 비었나?<br/>→ Queue에서 가져오기] end subgraph QUEUES[Queues] MQ[Microtask Queue<br/>Promise, await 완료] TQ[Task Queue<br/>setTimeout, I/O, Cron] end STACK --> |비어있을 때만| LOOP LOOP --> MQ MQ --> |비었으면| TQ TQ --> STACK style STACK fill:#ff6b6b style MQ fill:#4ecdc4 style TQ fill:#45b7d1핵심 규칙: Call Stack이 비어야만 다음 작업 실행 오해 1: “setTimeout(0)은 즉시 실행된다” 코드 console.log("1"); setTimeout(() => console.log("2"), 0); console.log("3");실행 흐름 sequenceDiagram participant CS as Call Stack participant TQ as Task Queue participant OUT as 출력 Note over CS: console.log('1') CS->>OUT: "1" Note over CS: setTimeout 등록 CS->>TQ: 콜백 등록 (0ms여도!) Note over CS: console.log('3') CS->>OUT: "3" Note over CS: Call Stack 비었음! TQ->>CS: 콜백 가져오기 Note over CS: 콜백 실행 CS->>OUT: "2"출력: 1 → 3 → 2 setTimeout(0)은 “다음 Event Loop에 실행"이라는 뜻! ...

December 3, 2025

TypeScript Record 제대로 이해하기 — 인덱스 시그니처와 맵드 타입 비교, 선택 기준

개요 Record<Key, Value>는 키와 값 타입을 고정해 객체 형태를 만드는 타입스크립트 유틸리티 타입임 인덱스 시그니처와 유사하지만 문자열 리터럴 유니온을 키로 직접 사용할 수 있고, 맵드 타입으로도 같은 효과를 낼 수 있음 핵심 개념과 정의 Record<Key, Value> 키 타입 Key, 값 타입 Value를 갖는 객체 타입 생성 인덱스 시그니처와의 차이 [key: string]: T 형태는 키 집합을 특정 리터럴 유니온으로 제한 불가 Record는 ‘A’ | ‘B’ 같은 리터럴 유니온을 키로 직접 지정 가능 맵드 타입과의 관계 { [K in Keys]: V }와 Record<Keys, V>는 구조적으로 동일한 결과를 만들 수 있음 간단 스니펫 ...

December 2, 2025

스프레드 연산자로 배열 요소와 객체 속성 조건부 추가

개요 리터럴 선언 시 조건에 따라 요소나 속성을 넣었다 뺄 수 있는 패턴 정리 분기문 없이 스프레드 연산자만으로 가독성 유지 사용법/예시 배열은 피연산자가 이터러블이어야 하므로 삼항 연산자 사용 const arr = [ ...(cond ? [1, 2, 3] : []), 4, 5, 6, ] // cond === true => [1, 2, 3, 4, 5, 6] // cond === false => [4, 5, 6] 객체는 원시값을 스프레드해도 자체 속성이 없어 무시됨 불리언화로 안전성 확보 const obj = { ...(!!cond && { a: 1, b: 2 }), c: 3, d: 4, } // cond === true => { a: 1, b: 2, c: 3, d: 4 } // cond === false => { c: 3, d: 4 } 주의 사항 배열에서 …(cond && [1,2,3]) 사용 금지, cond가 false면 이터러블 아님 오류 발생 객체에서 null이나 undefined를 그대로 스프레드하면 오류 가능, !!cond로 불리언화하거나 cond ? {..} : {} 형태 권장 타입스크립트에서는 && 패턴이 타입 좁히기 미흡할 수 있음, 삼항으로 빈 객체 반환 또는 적절한 타입 주석 권장 ...

December 1, 2025