반응형

JavaScript에서 fallback 패턴은 일반적으로 어떤 작업이 실패하거나 특정 기능이 지원되지 않을 경우, 다른 방법을 제공하여 프로그램이 정상적으로 동작하도록 만드는 방식을 의미합니다. 이를 통해 코드가 예외를 처리하거나 대체 경로로 진행될 수 있게 합니다.

fallback 패턴은 다음과 같은 상황에서 유용하게 사용됩니다:

  1. 브라우저 호환성: 최신 브라우저에서만 지원하는 기능이 있을 때, 오래된 브라우저에서는 이를 대체할 수 있는 방식으로 코드를 처리하는 경우.
  2. API 호출 실패: 외부 API를 호출했을 때 실패할 경우, 로컬 데이터를 사용하거나 기본값을 제공하는 방식.
  3. 옵션이 없는 경우 기본값 제공: 사용자가 특정 값을 제공하지 않았을 때 기본값을 제공하는 경우.

1. 기본값 제공 (Fallback to Default Value)

기본값을 제공하는 패턴은 자주 사용됩니다. 예를 들어, 함수에 인자가 전달되지 않았을 때 기본값을 설정하는 방식입니다.

예시

function greet(name = "Guest") {
  console.log(`Hello, ${name}!`);
}

greet(); // Hello, Guest!
greet("Alice"); // Hello, Alice!
  • 위 코드에서 name 파라미터가 전달되지 않으면, "Guest"라는 기본값이 사용됩니다.

2. 조건문을 통한 Fallback 처리

어떤 작업이 실패할 때 대체 작업을 수행하는 패턴입니다. 예를 들어, 특정 기능이 브라우저에서 지원되지 않을 경우, 대체할 기능을 제공하는 방식입니다.

예시

if ('localStorage' in window) {
  // localStorage를 지원하는 경우
  localStorage.setItem('user', 'John Doe');
} else {
  // localStorage를 지원하지 않는 경우
  console.log("localStorage is not supported, using cookies as fallback.");
  document.cookie = "user=John Doe; path=/";
}
  • 이 예시에서는 localStorage가 브라우저에서 지원되지 않으면, 쿠키를 사용하여 데이터를 저장합니다.

3. try-catch 구문을 통한 Fallback 처리

API 호출이나 외부 리소스 접근 시 실패할 수 있는 부분에서 try-catch 구문을 사용하여 예외를 처리하고, 실패 시 대체 방법을 제공하는 패턴입니다.

예시

try {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error("API call failed. Using local data instead.");
  const localData = { name: "Fallback Name", age: 30 };
  console.log(localData);
}
  • API 호출이 실패할 경우, catch 블록에서 로컬 데이터를 사용하여 대체합니다.

4. Optional Chaining과 Nullish Coalescing을 통한 Fallback

JavaScript의 최신 문법인 optional chaining (?.)과 nullish coalescing (??) 연산자를 사용하여, 객체나 배열의 값이 null 또는 undefined일 때 fallback 값을 제공할 수 있습니다.

예시

const user = {
  name: "Alice",
  address: {
    city: "Wonderland",
  },
};

// Optional chaining과 nullish coalescing을 사용하여 fallback 처리
const city = user.address?.city ?? "Unknown City";
console.log(city); // Wonderland

const country = user.address?.country ?? "Unknown Country";
console.log(country); // Unknown Country
  • user.address?.city는 address가 null이나 undefined일 경우, undefined를 반환하고, 그 후 ?? 연산자를 통해 fallback 값 "Unknown City"가 사용됩니다.

5. 대체 함수 사용 (Fallback to Alternative Function)

어떤 함수나 메서드가 실패했을 경우, 다른 대체 함수를 호출하여 처리하는 방법입니다. 예를 들어, 데이터베이스 접근 실패 시 로컬 캐시에서 데이터를 가져오는 방식입니다.

예시

function fetchDataFromAPI() {
  return fetch('https://api.example.com/data').then(response => response.json());
}

function fetchDataFromCache() {
  return { id: 1, name: 'Fallback Data' };
}

function getData() {
  return fetchDataFromAPI()
    .catch((error) => {
      console.error('API call failed, falling back to cache.');
      return fetchDataFromCache();
    });
}

getData().then(data => console.log(data));
  • fetchDataFromAPI() 호출이 실패하면, fetchDataFromCache()가 호출되어 대체 데이터를 제공합니다.

결론

JavaScript에서 fallback 패턴은 여러 상황에서 실패를 처리하고 대체 경로를 제공하는 중요한 기법입니다. 이는 코드의 안정성을 높이고, 다양한 환경에서도 코드가 예기치 않게 동작하지 않도록 보장해줍니다. 올바르게 사용하면 브라우저 호환성 문제나 외부 API 호출 실패 등 다양한 예외 상황을 처리할 수 있습니다.

 
 
728x90
반응형
반응형

JavaScript에서 숫자 계산을 할 때, 소수점을 처리하는 다양한 수학 함수들이 있습니다. 그 중 Math.ceil() 함수는 주어진 숫자보다 크거나 같은 가장 작은 정수를 반환하는 함수로, 주로 올림 처리에 사용됩니다. 이번 포스트에서는 Math.ceil() 함수의 사용법, 동작 원리, 그리고 예시를 함께 살펴보겠습니다.

1. Math.ceil() 함수란?

Math.ceil() 함수는 주어진 숫자보다 크거나 같은 가장 작은 정수를 반환합니다. 이는 소수점을 처리할 때, 항상 올림 처리를 하여 결과를 정수로 변환하는 함수입니다.

구문:

Math.ceil(x)
  • x: 올림할 숫자입니다. 이 숫자는 정수든 실수든 상관없습니다.

2. Math.ceil()의 동작 원리

Math.ceil() 함수는 다음과 같은 방식으로 동작합니다:

  • 양수일 때: 숫자가 소수점을 포함하고 있으면, 해당 숫자보다 크거나 같은 가장 작은 정수로 변환합니다.
  • 음수일 때: 음수일 경우에도 올림 처리가 적용되며, 그 절댓값이 더 큰 방향으로 올림됩니다.

3. 예시

Math.ceil() 함수의 동작을 예시를 통해 더 자세히 살펴보겠습니다.

console.log(Math.ceil(4.1));   // 5
console.log(Math.ceil(4.9));   // 5
console.log(Math.ceil(-4.1));  // -4
console.log(Math.ceil(-4.9));  // -4
console.log(Math.ceil(0.0001)); // 1

결과 설명:

  • Math.ceil(4.1)은 4.1보다 크거나 같은 가장 작은 정수인 5를 반환합니다.
  • Math.ceil(4.9)는 4.9보다 크거나 같은 가장 작은 정수인 5를 반환합니다.
  • Math.ceil(-4.1)은 -4.1보다 크거나 같은 가장 작은 정수인 -4를 반환합니다. (음수도 올림 처리)
  • Math.ceil(-4.9)는 -4.9보다 크거나 같은 가장 작은 정수인 -4를 반환합니다.
  • Math.ceil(0.0001)은 0.0001보다 크거나 같은 가장 작은 정수인 1을 반환합니다. (소수점이 매우 작은 값일지라도 올림 처리)

4. Math.ceil()과 Math.floor() 비교

Math.ceil()과 Math.floor()는 비슷한 역할을 하지만, 차이점이 있습니다. Math.ceil()은 올림 처리를 하여 숫자를 "위로" 변환하고, Math.floor()는 내림 처리를 하여 숫자를 "아래로" 변환합니다.

console.log(Math.ceil(4.1));   // 5
console.log(Math.floor(4.1));  // 4

5. Math.ceil() 사용 용도

Math.ceil() 함수는 다양한 상황에서 유용하게 사용될 수 있습니다. 예를 들어:

  • 페이지네이션: 데이터가 여러 페이지에 걸쳐 표시될 때, 데이터의 개수를 페이지 수로 나눈 후 올림 처리를 통해 마지막 페이지를 계산할 때 사용합니다.
  • 금액 계산: 사용자가 결제할 금액을 올림 처리하여 결제 금액이 항상 일정하게 유지될 수 있도록 할 때 유용합니다.
  • 시간 계산: 특정 시간 단위로 올림 처리하여 최소 시간을 계산할 때 사용됩니다.

결론

Math.ceil() 함수는 JavaScript에서 숫자를 올림 처리하여 가장 작은 정수로 변환할 수 있는 유용한 함수입니다. 양수와 음수 모두에서 올림 처리가 적용되며, 소수점 값이 매우 작은 경우에도 유용하게 사용할 수 있습니다. 다양한 계산에서 활용할 수 있기 때문에, 숫자 처리에 있어 중요한 역할을 합니다.

728x90
반응형
반응형

JavaScript에서 날짜와 시간을 다루다 보면, 타임존(Timezone) 관련 코드에서 종종 헷갈리는 순간이 있습니다.
특히 dayjs를 사용할 때, 아래 두 코드의 차이를 아는 것이 정말 중요해요:

const t1 = dayjs('202505271800', 'YYYYMMDDHH').tz("Asia/Seoul");
const t2 = dayjs.tz('202505271800', 'YYYYMMDDHH', 'Asia/Seoul');

겉보기엔 비슷해 보이지만, 내부적으로는 아주 다른 동작을 하게 됩니다.
이 글에서는 그 차이를 최대한 쉽게 설명해볼게요.


우선 두 코드의 차이부터 볼게요

① dayjs('202505271800', 'YYYYMMDDHH').tz("Asia/Seoul")

  • 이 코드는 먼저 '202505271800'을 UTC 기준으로 해석합니다.
    즉, '2025년 5월 27일 18시'는 UTC 시간대의 6시라고 인식합니다.
  • 그 다음, .tz("Asia/Seoul")을 적용하면서 UTC 6시 → KST 기준 15시로 변환됩니다.
  • 결과적으로 날짜와 시간이 변형됩니다.

② dayjs.tz('202505271800', 'YYYYMMDDHH', 'Asia/Seoul')

  • 이 코드는 처음부터 '202505271800'을 "Asia/Seoul" 시간대로 해석합니다.
  • 즉, '2025년 5월 27일 18시 (한국시간 기준)' 그대로 유지됩니다.
  • 시간 값은 그대로이고, 정확한 타임존이 적용됩니다.

예시로 비교해볼게요

// 1번 방식 (시간이 바뀜)
const t1 = dayjs('202505271800', 'YYYYMMDDHH').tz("Asia/Seoul");
console.log(t1.format()); // 2025-05-28T03:00:00+09:00 ❌ 원래 18시 아님

// 2번 방식 (정확하게 해석됨)
const t2 = dayjs.tz('202505271800', 'YYYYMMDDHH', 'Asia/Seoul');
console.log(t2.format()); // 2025-05-27T18:00:00+09:00 ✅ 원하던 시간

그래서 어떤 걸 써야 할까?

목적추천 방식

 

목적 추천 방식
입력된 시간이 이미 KST라고 가정하고 싶다면 ✅ dayjs.tz(..., ..., 'Asia/Seoul')
입력된 시간이 UTC인데 KST로 변환해서 보여주고 싶다면 ✅ .tz('Asia/Seoul')
 

 


📝 마무리 정리

  • dayjs(...).tz(...)는 UTC로 해석 후 시간대 변환
  • dayjs.tz(..., ..., timezone)는 처음부터 그 시간대를 기준으로 파싱
  • 사용자 입력이 "한국 시간"이라는 것이 확실하다면, 무조건 dayjs.tz(...)를 쓰는 것이 안전합니다
728x90
반응형
반응형

Node.js 개발에서 TypeScript를 도입할 때 가장 먼저 마주치는 고민 중 하나는 바로 이것입니다.

TypeScript를 전역으로 설치할까? 로컬로 설치할까?

이 글에서는 두 방식의 차이점, 장단점, 실제 추천 시나리오까지 한번에 정리해드립니다.


✅ 전역 설치란?

전역 설치는 시스템 전체에 TypeScript를 설치하여 어느 프로젝트 디렉토리에서도 tsc 명령어를 사용할 수 있게 하는 방식입니다. 다음과 같은 명령어로 설치합니다:

npm install -g typescript

설치 후에는 터미널 어디에서나 다음 명령이 작동합니다:

tsc --version
tsc yourfile.ts

전역 설치의 장점

  • tsc 명령을 터미널 어디서든 바로 쓸 수 있다
  • CLI 툴 사용 시 빠르게 실행 가능
  • 전역 설정에 익숙한 개발자에게 편리

전역 설치의 단점

  • 버전 충돌 가능성: 프로젝트마다 TypeScript 버전이 다를 경우 문제 발생
  • 팀 협업에서 위험: 다른 개발자의 시스템에 같은 버전이 설치되어 있다고 보장할 수 없음
  • CI/CD 파이프라인에서 재현 불가

✅ 로컬 설치란?

로컬 설치는 해당 프로젝트 폴더 안에 TypeScript를 설치하는 방식입니다. 보통 개발 의존성으로 추가합니다:

npm install --save-dev typescript

설치 후에는 전역 명령 대신 npx를 사용해 실행합니다:

npx tsc
npx tsc --init

로컬 설치의 장점

  • 프로젝트별로 TypeScript 버전을 고정할 수 있어 안정성 확보
  • 모든 팀원이 동일한 환경에서 작업 가능
  • CI/CD 환경에서도 100% 재현 가능
  • package.json만 보면 어떤 버전이 쓰였는지 알 수 있다

로컬 설치의 단점

  • tsc를 직접 실행하려면 npx 또는 경로를 지정해야 한다
  • 여러 프로젝트를 오가며 전역 설치처럼 쓰고 싶으면 약간 번거로움

🔍 어떤 상황에 어떤 설치를 써야 할까?

  • 혼자 실습하거나 빠르게 테스트하는 경우
    전역 설치도 무방합니다.
  • 팀 프로젝트, 버전 관리, 협업, CI/CD 구성이 필요한 경우
    반드시 로컬 설치가 권장됩니다.

요즘 대부분의 실제 프로젝트는 typescript를 로컬로 설치하고 npx tsc 또는 tsc npm script로 실행합니다.


🧙 마무리

전역 설치는 빠르고 편리하지만, 협업과 재현성을 위해서는 로컬 설치가 훨씬 안전하고 추천되는 방식입니다.

최종적으로는 다음과 같은 전략을 추천합니다:

  • CLI 툴 실습이나 글로벌 테스트용 → 전역 설치
  • 실제 서비스, 팀 프로젝트, 오픈소스, CI/CD → 로컬 설치 (with --save-dev)

🎯 참고 명령어 요약

# 전역 설치
npm install -g typescript

# 로컬 설치
npm install --save-dev typescript

# 로컬 실행
npx tsc

 

728x90
반응형
반응형

🔍 ??란?

??는 null 병합 연산자 (Nullish Coalescing Operator) 입니다.
JavaScript(ES2020)부터 도입된 문법으로, 값이 null 또는 undefined일 때 대체값(default value) 을 설정하는 데 사용됩니다.

const result = value ?? defaultValue;

📘 동작 방식

왼쪽 값 ?? 결과
null 오른쪽 값 반환
undefined 오른쪽 값 반환
나머지 값들 (0, '', false) 왼쪽 값 그대로 사용
let name = null ?? "익명";       // "익명"
let age = undefined ?? 30;       // 30
let isActive = false ?? true;    // false ← 주의!
let count = 0 ?? 10;             // 0 ← 주의!

✅ false, 0, "" 는 null이 아님! → ??는 무시하고 그대로 반환


🔁 || (OR 연산자)와 차이점?

||는 falsy 값 전체를 체크

let title = "" || "기본 제목";  // "기본 제목"

??는 null / undefined 만 체크

let title = "" ?? "기본 제목";  // ""​
표현식 결과 이유
`null   '기본'`
`0   100`
0 ?? 100 0 null/undefined 아니므로 유지

결론:

  • ||는 많이 비우고 싶을 때
  • ??는 정말 null/undefined일 때만 대체하고 싶을 때 사용하세요.

🧱 실전 예시

✅ 환경 변수 기본값 설정

const PORT = process.env.PORT ?? 3000;

→ PORT가 설정되지 않았을 때만 기본값 3000 사용


✅ 사용자 입력 처리

function getUserName(user) {
  return user.name ?? "손님";
}

→ user.name이 null 또는 undefined일 때만 "손님" 반환


✅ 객체 병합

const defaultOptions = {
  retry: 3,
  timeout: 5000
};

const userOptions = {
  timeout: null
};

const config = {
  retry: userOptions.retry ?? defaultOptions.retry,
  timeout: userOptions.timeout ?? defaultOptions.timeout
};

console.log(config); // { retry: 3, timeout: 5000 }

→ timeout: null은 무시하고 default 사용


❌ 에러 주의: 옵셔널 체이닝과 혼용 시

let username = user?.info?.name ?? "익명";
  • user 또는 info가 없으면 undefined
  • ??로 "익명" 설정됨 → 매우 강력한 조합
728x90
반응형
반응형

기본적으로 new Date()는 UTC 기준으로 동작합니다. 이를 **KST(한국 표준시, UTC+9)**로 변환하는 방법을 설명합니다.

🚀 1. Intl.DateTimeFormat 사용 (권장)

JavaScript의 Intl.DateTimeFormat API를 사용하면 타임존 변환이 간단합니다.

const date = new Date();

const kstDate = new Intl.DateTimeFormat('ko-KR', {
  timeZone: 'Asia/Seoul',  // ✅ KST 적용
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  hour12: false, // ✅ 24시간제 사용
}).format(date);

console.log(`📌 KST 시간: ${kstDate}`);

 

💡 출력 예시:

📌 KST 시간: 2024. 02. 25. 15:30:45

Intl.DateTimeFormat을 사용하는 이유

  • 최신 표준 지원 (ECMAScript 국제화)
  • 시간대 자동 적용
  • 성능 최적화

 

🚀 2. toLocaleString() 사용

toLocaleString()을 사용하여 KST로 변환할 수도 있습니다.

const date = new Date();
const kstDate = date.toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' });

console.log(`📌 KST 시간: ${kstDate}`);

💡 출력 예시:

📌 KST 시간: 2024. 2. 25. 오후 3:30:45

✅ toLocaleString()은 간편하지만, 시간 형식이 다소 달라질 수 있음.

 

 

🚀 3. moment-timezone 사용 (강력한 기능 제공)

moment-timezone을 사용하면 더 다양한 포맷 지정 가능.

📌 설치

npm install moment-timezone

📌 코드 예제

const moment = require('moment-timezone');

const kstDate = moment().tz("Asia/Seoul").format("YYYY-MM-DD HH:mm:ss");

console.log(`📌 KST 시간: ${kstDate}`);

💡 출력 예시:

📌 KST 시간: 2024-02-25 15:30:45

moment-timezone 사용 시 장점

  • 여러 타임존 변환 가능
  • 포맷 지정 가능
  • Date 객체와 연동 가능

 

🚀 4. Date 객체에서 직접 변환 (UTC 오프셋 적용)

UTC 시간에 9시간을 추가하여 KST로 변환할 수도 있습니다.

const date = new Date();
const kstDate = new Date(date.getTime() + (9 * 60 * 60 * 1000)); // ✅ UTC+9 적용

console.log(`📌 KST 시간: ${kstDate.toISOString().replace('T', ' ').slice(0, 19)}`);

💡 출력 예시:

📌 KST 시간: 2024-02-25 15:30:45

✅ 직접 Date 객체를 변환하는 방법이지만, Intl.DateTimeFormat보다 추천하지 않음.

 

 

🎯 정리 (어떤 방법을 선택할까?)

방법코드 예제장점단점
Intl.DateTimeFormat ✅ new Intl.DateTimeFormat('ko-KR', { timeZone: 'Asia/Seoul' }) 표준 지원, 성능 최적화 포맷 지정이 다소 제한적
toLocaleString() ✅ date.toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' }) 간편 사용 출력 형식이 다를 수 있음
moment-timezone ✅ moment().tz("Asia/Seoul").format("YYYY-MM-DD HH:mm:ss") 강력한 포맷팅 기능 패키지 설치 필요
UTC 오프셋 변환 ✅ new Date(date.getTime() + (9 * 60 * 60 * 1000)) 직접 변환 가능 DST(일광 절약 시간) 고려 안됨

📌 추천 방법

  • 최신 프로젝트 & 표준 지원이 필요하면 → Intl.DateTimeFormat()
  • 간단하게 변환하려면 → toLocaleString()
  • 강력한 포맷팅이 필요하면 → moment-timezone
  • UTC에서 오프셋 변환하려면 → Date + 9시간 추가
728x90
반응형
반응형

fs.createReadStream + csv-parser 사용 시 비동기 처리에 주의하세요!


📌 문제 상황: 비동기 스트림에서 예상치 못한 동작

CSV 파일을 읽고, 특정 수량(quantity)만큼 데이터를 처리하려고 했지만, 모든 데이터가 처리됨. 😲

목표:

  • CSV 데이터에서 특정 개수(quantity)만 읽고 중단
  • 비동기 함수(await)가 정확히 작동하도록 수정

💥 문제 발생 코드: 비동기 흐름 제어 실패

import fs from 'fs';
import csv from 'csv-parser';

let count = 0;
const quantity = 20;
const winDataList: any[] = [];

fs.createReadStream(filePath)
  .pipe(csv())
  .on("data", async (data: CsvRow) => {
    if (count < quantity) {
      const email = data.email;
      const point = data.point;
      const choice = data.choice;
      const choiceImage = { a: "imagea", b: "imageb" }[choice]!;

      winDataList.push({ email, point, choice, choiceImage });
      console.log("Processing:", email, point);

      await updateEmailFile(email, fileName); // 비동기 작업
      await updateEmail(email);              // 비동기 작업

      count++;
    } else {
      return; // ❌ 스트림은 계속 실행됨
    }
  });

⚠️ 문제 원인:

  1. csv-parser는 비동기 흐름을 기다리지 않음.
  2. await 사용했지만, 스트림은 비동기 작업과 무관하게 계속 실행.
  3. 결과: 모든 CSV 데이터가 처리됨. 😱

해결 방법: 스트림 흐름을 명확히 제어

### 🎯 방법 1: stream.destroy()로 스트림 종료

count가 quantity에 도달하면 스트림 자체를 종료하여 추가 데이터를 읽지 않도록 합니다.

import fs from 'fs';
import csv from 'csv-parser';

let count = 0;
const quantity = 20;
const winDataList: any[] = [];

const stream = fs.createReadStream(filePath)
  .pipe(csv());

stream.on("data", async (data: CsvRow) => {
  if (count < quantity) {
    const email = data.email;
    const point = data.point;
    const choice = data.choice;
    const choiceImage = { a: "imagea", b: "imageb" }[choice]!;

    winDataList.push({ email, point, choice, choiceImage });
    console.log("Processing:", email, point);

    await updateEmailFile(email, fileName);
    await updateEmail(email);

    count++;

    if (count >= quantity) {
      stream.destroy(); // ✅ 스트림 완전 종료
    }
  }
});

stream.on("close", () => {
  console.log("Stream closed after processing", count, "items.");
});

stream.on("error", (err) => {
  console.error("Stream error:", err);
});

💡 장점:

  • 간단하고 빠른 구현
  • quantity에 도달하면 즉시 스트림 종료

단점:

  • 스트림 완전 종료: 이후 데이터는 절대 다시 읽을 수 없음

### 🎯 방법 2: pause() & resume()으로 정교한 제어

각 데이터 처리 시 스트림을 일시정지하고, 비동기 작업 완료 후 다시 재개합니다.

import fs from 'fs';
import csv from 'csv-parser';

let count = 0;
const quantity = 20;
const winDataList: any[] = [];

const stream = fs.createReadStream(filePath)
  .pipe(csv());

stream.on("data", async (data: CsvRow) => {
  if (count < quantity) {
    stream.pause(); // ✅ 데이터 흐름 일시 정지

    const email = data.email;
    const point = data.point;
    const choice = data.choice;
    const choiceImage = { a: "imagea", b: "imageb" }[choice]!;

    winDataList.push({ email, point, choice, choiceImage });
    console.log("Processing:", email, point);

    await updateEmailFile(email, fileName);
    await updateEmail(email);

    count++;

    if (count >= quantity) {
      stream.destroy(); // ✅ 최대 수량 도달 시 스트림 종료
    } else {
      stream.resume(); // ✅ 다음 데이터 읽기 재개
    }
  }
});

stream.on("close", () => {
  console.log("Stream closed after processing", count, "items.");
});

stream.on("error", (err) => {
  console.error("Stream error:", err);
});

💡 장점:

  • 비동기 흐름 제어 가능 (데이터 순서 보장)
  • 중간에 작업 재개 가능

단점:

  • 코드 복잡성 증가

🎯 방법 3: pipeline()으로 스트림 관리 최적화

Node.js 10+에서는 **stream.pipeline()**을 사용해 에러 핸들링비동기 흐름 관리를 더 효율적으로 처리할 수 있습니다.

import { createReadStream } from 'fs';
import { pipeline } from 'stream';
import csv from 'csv-parser';
import { promisify } from 'util';

const asyncPipeline = promisify(pipeline);

async function processCSV(filePath: string, quantity: number) {
  let count = 0;
  const winDataList: any[] = [];

  await asyncPipeline(
    createReadStream(filePath),
    csv(),
    async function* (source) {
      for await (const data of source) {
        if (count >= quantity) break;

        const email = data.email;
        const point = data.point;
        const choice = data.choice;
        const choiceImage = { a: "imagea", b: "imageb" }[choice]!;

        winDataList.push({ email, point, choice, choiceImage });
        console.log("Processing:", email, point);

        await updateEmailFile(email, fileName);
        await updateEmail(email);

        count++;
      }
    }
  );

  console.log("Processed", count, "records");
}

processCSV(filePath, 20).catch(console.error);

💡 장점:

  • 에러 핸들링 포함
  • 비동기 흐름 자연스럽게 제어 가능
  • 중간에 멈추거나 재시작 가능

단점:

  • Node.js 10+ 필요
  • 복잡한 로직에는 적합하지 않을 수 있음

결론: 어떤 방법을 선택할까?

방법장점단점추천 상황

stream.destroy() 빠르고 단순 이후 데이터 다시 읽기 불가 간단한 데이터 처리
pause() & resume() 비동기 흐름 제어, 순서 보장 코드 복잡성 증가 정교한 흐름 제어 필요 시
pipeline() 에러 핸들링, 비동기 흐름 최적화 Node.js 10+ 필요 대규모 스트림 작업

📊 💡 최종 추천

  • 빠르게 끝내야 한다면 → destroy()
  • 비동기 흐름을 제어하고 싶다면 → pause() & resume()
  • 에러 핸들링과 확장성을 고려한다면 → pipeline()

💬 마무리

Node.js의 스트림은 효율적인 데이터 처리를 가능하게 하지만, 비동기 흐름 관리에 유의해야 합니다. 특히 대용량 CSV 처리에서는 pause()와 destroy()를 적절히 사용하여 메모리 사용을 줄이고, 필요 시 pipeline()으로 에러 핸들링까지 고려하세요. 🚀

 

#NodeJS #JavaScript #CSVParsing #StreamAPI #AsyncProgramming #DataProcessing #BackendDevelopment #NodeJSTips #FullStackDevelopment #WebDevelopment #Asynchronous #CodingTips #DeveloperLife #CodeOptimization #ErrorHandling #NodeJSTutorial #BackendEngineer #ProgrammingTips #SoftwareEngineering #TechBlog

💡 활용 팁:

  • 기술 태그 (#NodeJS, #JavaScript, #StreamAPI) → 개발자 타겟팅
  • 문제 해결 태그 (#ErrorHandling, #CodeOptimization) → 검색 최적화
  • 커뮤니티 태그 (#DeveloperLife, #TechBlog) → 더 넓은 도달범위 확보
728x90
반응형
반응형

JavaScript를 포함한 대부분의 프로그래밍 언어는 IEEE 754 표준에 따른 이중 정밀도 부동소수점(floating point) 방식을 사용합니다. 이 방식은 실수를 이진수로 표현하는데, 모든 소수를 정확하게 표현할 수 없기 때문에 내부적으로 약간의 오차가 발생합니다.

예를 들어, 아래와 같이 0.2와 0.4를 더하면:

const result = 0.2 + 0.4;
console.log(result); // 0.6000000000000001

 

왜 정확히 0.6이 나오지 않을까요? 그 이유는 0.2와 0.4를 이진수로 표현할 때 무한소수로 나타나기 때문에 반올림 과정에서 미세한 오차가 누적되기 때문입니다.

부동소수점 오차의 원인

IEEE 754 표준에서는 모든 숫자를 2진법으로 표현합니다. 그러나 10진법의 소수 중에는 2진법으로 정확하게 표현할 수 없는 수들이 있습니다. 예를 들어,

  • 0.2는 2진수로 근사치로 표현되고,
  • 0.4 역시 근사치로 표현됩니다.

이러한 근사치 계산 과정에서 오차가 발생하고, 두 값을 더할 때 오차가 누적되어 예상치 못한 결과가 나오게 됩니다.

해결 방법

부동소수점 연산 오차 문제를 완화하기 위한 몇 가지 방법을 소개합니다.

1. 반올림 사용하기

연산 결과를 원하는 소수점 자리수로 반올림하는 방법입니다. Number.prototype.toFixed() 메서드를 사용하면 문자열로 반환되므로, 이를 Number() 함수를 통해 숫자형으로 변환할 수 있습니다.

const result = 0.2 + 0.4;
const rounded = Number(result.toFixed(2)); // 소수점 둘째 자리까지 반올림
console.log(rounded); // 0.6

이 방법은 결과 값을 표현할 때는 유용하지만, 연산 자체의 정확도를 높이는 방법은 아닙니다.

2. 정수 연산 후 나누기

실수를 직접 계산하는 대신 정수 연산을 한 후 최종 결과를 원하는 단위로 변환하는 방법입니다.

const result = (2 + 4) / 10;
console.log(result); // 0.6

이 방법은 소수점 이하의 연산을 정수로 처리하여 부동소수점 오차를 피할 수 있는 장점이 있습니다. 그러나 모든 경우에 적용하기는 어려울 수 있습니다.

3. 수학 라이브러리 사용하기

보다 정밀한 계산이 필요하거나 부동소수점 오차를 완전히 제어하고 싶다면, Decimal.jsBig.js와 같은 라이브러리를 사용하는 것이 좋습니다.

Big.js 예제:

const Big = require('big.js');

const result = Big(0.2).plus(Big(0.4));
console.log(result.toString()); // "0.6"

// 결과를 number 타입으로 변환하려면:
const numberResult = Number(result);
console.log(numberResult); // 0.6

이 방법은 라이브러리 내부에서 소수 연산의 오차를 보완하는 알고리즘을 사용하므로, 보다 신뢰할 수 있는 결과를 제공합니다.


결론

JavaScript의 부동소수점 연산은 IEEE 754 표준에 의해 발생하는 오차 때문에, 0.2 + 0.4의 결과가 0.6000000000000001로 나타납니다. 이러한 문제를 해결하기 위해서는:

  1. 반올림을 통해 원하는 자리수로 결과를 조정하거나,
  2. 정수 연산 후 나누기 방식을 사용하거나,
  3. Decimal.js, Big.js와 같은 수학 라이브러리를 활용하여 보다 정밀한 계산을 수행하는 방법이 있습니다.

상황에 따라 적절한 방법을 선택하여 부동소수점 연산 오차 문제를 완화할 수 있습니다.

 

 

728x90
반응형

+ Recent posts