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

InnoDB 복합 인덱스와 Left-most Prefix 원칙 정리

InnoDB에서 여러 컬럼을 묶어 복합 인덱스를 만들면 정의된 순서대로 값을 결합해 B-Tree에 저장됨. 예를 들어 (colA, colB, colC) 인덱스면 colA 기준으로 정렬되고 같은 colA 그룹 내에서 colB, 이어서 colC 순으로 정렬됨. 이 구조 때문에 왼쪽 접두사(Left-most prefix) 원칙이 성립하며, 인덱스 정의의 선두 컬럼부터 조건이 주어질수록 효율이 높아짐 개념과 배경 복합 인덱스는 정의 순서대로 키를 묶어 B-Tree에 저장 InnoDB의 보조 인덱스는 리프에 보조키와 함께 PK를 포함함. PK 길이가 길면 보조 인덱스 크기와 I/O에 직접 영향 left-most prefix 원칙이 핵심. 인덱스의 선두 컬럼부터 연속해서 매칭될 때 탐색 범위가 급격히 줄어듦 동작 방식 예시 (colA, colB) 인덱스 구조 정렬 키 순서: colA → colB → 내부적으로 PK 참조 왼쪽 컬럼 우선 필터링 WHERE colA = ? 또는 WHERE colA = ? AND colB = ? 형태는 인덱스를 온전히 사용 가능 WHERE colB = ?처럼 선두 컬럼 없이 후행 컬럼만 조건일 때는 인덱스 이용이 제한되거나 범위가 매우 넓어짐. 최신 버전에서 옵티마이저가 제한적으로 우회 전략을 쓰는 경우가 있으나 일반적인 기대값으로 두기 어려움 왼쪽 접두사 부분 사용 (colA, colB)에서 colA만 조건으로 사용해도 인덱스 사용 가능 colA와 colB를 모두 조건으로 사용하면 탐색 범위가 더 좁아짐 colB만 조건이면 left-most prefix가 깨져 효과가 급감할 수 있음 정렬과 결합 사용의 기본 인덱스 키 순서와 같은 ORDER BY colA, colB는 추가 정렬 비용 감소에 도움 WHERE 조건이 인덱스 선두를 건너뛰거나 정렬 순서가 키 정의와 다르면 이점이 줄어듦 여러 컬럼을 묶는 이유 다중 조건 검색 성능 향상. 예: WHERE colA = ? AND colB BETWEEN ? AND ?는 (colA, colB)로 빠른 범위 스캔 가능 커버링 인덱스 활용. 예: SELECT colA, colB FROM T WHERE colA = ? AND colB = ?에서 테이블 추가 접근 없이 인덱스만으로 결과 충족 가능 쿼리 패턴 최적화. 실제 WHERE 절에서 자주 함께 쓰는 컬럼을 순서까지 고려해 묶는 설계가 유효. 드물게 쓰이거나 고카디널리티 이점이 없는 컬럼을 무리하게 포함하면 쓰기 비용만 증가 주의할 점 인덱스 정의 순서가 성능을 좌우. 자주 필터링되는 컬럼, 카디널리티가 높은 컬럼을 선두에 두는 것이 일반적 권장 너무 많은 컬럼을 묶으면 인덱스 폭과 관리 비용 증가. 보통 2~3개, 많아도 4개 이하로 목적에 맞게 제한 (A, B, C)일 때 WHERE B = ?처럼 선두가 빠지면 인덱스 사용이 제한. 패턴에 따라 (B, A) 같은 보완 인덱스 검토 InnoDB 보조 인덱스는 PK를 함께 가진다는 점을 고려. PK가 길면 복합 인덱스 크기와 캐시 효율, 쓰기 비용에 불리 ORDER BY, GROUP BY와 결합 시 인덱스 정의 순서와 일치할 때 이점. 중간에 범위 조건이 끼거나 순서가 어긋나면 효과 하락 범위 조건 이후 컬럼 활용 제한 가능. 예를 들어 colA BETWEEN ...가 선두에 오면 그 뒤 colB는 정렬이나 추가 필터에서 기대만큼 활용되지 않을 수 있음 마무리 복합 인덱스는 왼쪽부터 순차적으로 정렬되는 구조이며 left-most prefix 원칙이 적용됨 다중 컬럼 조건, 범위 스캔, 정렬이나 그룹 연산에 유용하지만 인덱스 정의 순서가 쿼리 패턴과 맞지 않으면 이점을 잃음 불필요하게 많은 컬럼을 포함하면 쓰기 부담과 스토리지 사용량만 증가. 실제 트래픽 패턴과 카디널리티를 근거로 최소한의 조합과 올바른 순서를 선택하는 것이 핵심 참고자료 https://dev.mysql.com/doc/refman/8.0/en/multiple-column-indexes.html https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html https://dev.mysql.com/doc/refman/8.0/en/covering-indexes.html

November 30, 2025

JavaScript에서 빈 배열은 truthy인가 falsy인가

개념/배경 JavaScript에서 조건식은 내부적으로 ToBoolean 연산을 거쳐 참거짓을 판정함 이때 객체 타입은 모두 truthy로 간주되며 배열도 객체에 해당함 따라서 빈 배열 []과 빈 객체 {} 역시 조건문에서 true로 평가됨 반대로 falsy는 정해진 값만 해당됨 false, 0, -0, 0n, “”, null, undefined, NaN 예시 다음은 빈 배열이 조건문에서 어떻게 평가되는지 확인하는 간단한 코드 if ([]) { console.log("빈 배열은 truthy입니다!") } else { console.log("빈 배열은 falsy입니다!") } // 출력: "빈 배열은 truthy입니다!" 배열이 비어 있는지를 논리적으로 구분하려면 길이를 확인해야 함 ...

November 29, 2025

백오프 Backoff 재시도 전략 정리 고정·선형·지수·지터

개요 백오프는 실패한 작업을 즉시 재시도하지 않고 일정 시간 대기 후 다시 시도하는 전략을 말함 연속 실패 시 대기 시간을 점진적으로 늘려 서비스와 네트워크에 가해지는 부하를 낮추는 목적 RPC, 데이터베이스, 외부 API 호출, 트랜잭션 재시도 등에서 일반적으로 사용 왜 쓰는가 과부하 방지 실패 직후 동시 재시도를 막아 서버와 네트워크 폭주를 예방 일시적 장애 흡수 순간적인 지연이나 혼잡이 해소될 시간을 벌어 성공 확률을 높임 비용 절감 불필요한 재시도 횟수와 리소스 낭비 감소 대표 백오프 패턴 고정 백오프 Fixed 매번 동일 대기 시간 사용 예) 1초 → 1초 → 1초 선형 백오프 Linear 시도할 때마다 일정 간격으로 증가 예) 1초 → 2초 → 3초 → 4초 지수 백오프 Exponential 보통 2배로 증가하는 지수 증가 사용, 실무에서 기본값으로 가장 흔함 예) 1초 → 2초 → 4초 → 8초 → 16초 지수 백오프 + 지터 Jitter 지수 증가에 무작위성을 섞어 동시 재시도 동기화를 깨뜨림 예) 1초 → 2.3초 → 4.1초 → 8.6초 실전 포인트 동시성 환경에서는 지터 필수 동일한 주기 백오프만으로는 다수 클라이언트가 같은 타이밍에 몰려 서버를 다시 두들김 상한 설정 최대 대기 시간과 최대 재시도 횟수 캡을 두어 꼬리 길어짐 방지 실패 예산과 타임아웃 연계 전체 호출 타임아웃 내에서 재시도 예산을 배분, 1회 호출과 재시도들이 전체 SLA를 초과하지 않도록 관리 멱등성 보장 재시도 가능한 작업은 멱등성을 만족해야 안전, 아니면 보상 로직 필요 서버 힌트 활용 Retry-After 등 서버가 제시하는 대기 힌트가 있으면 우선 적용 지터 방식 선택 전체 구간 무작위 분포 Full jitter가 단순하고 효과적이라는 보고가 많음 간단 예시 아래는 지수 백오프의 최소 구현 예시이며, 지터와 최대 대기 시간 제한은 상황에 맞게 추가 권장 ...

November 28, 2025