반응형

데이터를 자동으로 분류할 수 있는 클러스터링 알고리즘 중 가장 널리 사용되는 K-means Clustering!
이번 포스트에서는 이론적인 배경부터, 저사양 CPU에서도 무리 없이 돌릴 수 있는 실습 코드까지 깔끔하게 정리해볼게요.

목차

  1. K-means Clustering이란?
  2. 작동 원리 (알고리즘 흐름)
  3. 수학적 개념 정리
  4. 장점과 단점
  5. 실행 가능한 예제 코드 (CPU 저사양 OK!)
  6. 마무리 요약

1.  K-means Clustering 이란?

K-means는 데이터를 **K개의 클러스터(군집)**로 나누는 대표적인 비지도 학습(Unsupervised Learning) 알고리즘입니다.
각 클러스터는 중심점(centroid)을 가지며, 데이터는 가장 가까운 중심점에 소속됩니다.

📌 예를 들어, 고객을 3개의 소비 성향 그룹으로 나누고 싶을 때 사용할 수 있어요.


2.  작동 원리

K-means는 아래 4단계를 반복하며 군집을 형성합니다.

  1. 초기 중심점 설정 (보통 랜덤 또는 K-means++ 방식)
  2. 데이터 포인트를 가장 가까운 중심점에 할당
  3. 각 클러스터의 평균으로 중심점 업데이트
  4. 중심점 변화가 없으면 종료 (또는 max 반복 횟수까지)

3.  수학적으로 살펴보기

 유클리드 거리

데이터 간의 거리는 보통 **유클리드 거리(Euclidean Distance)**로 계산합니다:

 최적화 목표 (Objective)

K-means는 Within-Cluster Sum of Squares (WCSS) 를 최소화하는 것이 목표입니다:

$$J = \sum_{i=1}^{k} \sum_{x \in C_i} \|x - \mu_i\|^2$$

  • $C_i$: i번째 클러스터
  • $\mu_i$: 클러스터 중심점

4.  장점과 단점

장점

  • 구현이 매우 간단하고 빠름
  • 대용량 데이터에서도 확장 가능
  • 널리 사용됨

단점

  • K를 미리 알아야 함
  • 이상치(outlier)에 민감
  • 구형(원형)이 아닌 클러스터에 부적합

5.  실습 코드: 저사양 CPU에서도 OK!

아래 코드는 scikit-learn을 이용한 간단한 2D 데이터 클러스터링 예제입니다.
저사양 환경(CPU 1코어, 1GB RAM 정도)에서도 무리 없이 실행됩니다.

필요한 라이브러리 설치

pip install numpy matplotlib scikit-learn

 

전체 코드

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

# 1. 2D 클러스터 데이터 생성
X, y_true = make_blobs(n_samples=300, centers=3, cluster_std=0.60, random_state=0)

# 2. K-means 모델 학습
kmeans = KMeans(n_clusters=3, random_state=0, n_init='auto')
kmeans.fit(X)

# 3. 클러스터 예측 결과
y_kmeans = kmeans.predict(X)

# 4. 시각화
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=40, cmap='viridis')

# 중심점 시각화
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.5, marker='X')
plt.title("K-means Clustering 결과")
plt.xlabel("X1")
plt.ylabel("X2")
plt.show()

결과

  • 3개의 색깔로 나뉜 점들이 각각 클러스터
  • 빨간 X는 각 클러스터의 중심점

마무리 요약

항목설명
알고리즘 종류 비지도 학습 (Unsupervised)
사용 목적 자동으로 데이터 분류
주요 개념 중심점, 유클리드 거리, WCSS 최소화
실습 환경 저사양 CPU에서도 실행 가능

🔗 참고 자료

 

https://matplotlib.org/stable/tutorials/introductory/pyplot.html

 

matplotlib.org

 

 

make_blobs

Gallery examples: Release Highlights for scikit-learn 1.1 Release Highlights for scikit-learn 0.23 Probability Calibration for 3-class classification Probability calibration of classifiers Normal, ...

scikit-learn.org

 

 

KMeans

Gallery examples: Release Highlights for scikit-learn 1.1 Release Highlights for scikit-learn 0.23 A demo of K-Means clustering on the handwritten digits data Bisecting K-Means and Regular K-Means ...

scikit-learn.org

 

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
반응형
반응형

Vue.js를 개발할 때 yarn run dev 또는 npm run dev 명령어를 실행하면, 로컬에서 개발 서버가 실행됩니다. 하지만 실수로 Control + Z (^Z)를 눌러 suspended(정지됨) 상태가 되면, 터미널에서 다시 실행하거나 정상적으로 종료하는 방법이 필요합니다.

이 글에서는 Mac에서 Vue.js 개발 서버가 Suspended 상태가 되었을 때 해결하는 4가지 방법을 소개합니다.


✅ Suspended 상태란?

Suspended(정지됨) 상태란?
터미널에서 yarn run dev 실행 중 Control + Z 키를 눌렀을 때, 실행 중이던 프로세스가 중지(Suspended)되고 백그라운드로 이동하는 상태를 말합니다.

이 상태에서는 서버가 멈춘 것처럼 보이지만, 여전히 프로세스가 살아있습니다. 이를 해결하기 위해 다시 실행하거나 종료하는 방법을 알아보겠습니다.


🔥 해결 방법 1: fg를 사용해 다시 실행 후 종료

1.1 정지된 프로세스를 다시 실행

터미널에서 아래 명령어를 입력하면, 중지된 프로세스를 다시 실행할 수 있습니다.

fg

이후 Control + C (^C)를 눌러 정상 종료합니다.

📝 설명:
fg(foreground) 명령어는 suspended 상태에서 프로세스를 다시 실행합니다.


🔥 해결 방법 2: 백그라운드 실행 후 종료

2.1 정지된 프로세스를 백그라운드에서 실행

bg

이 명령어를 입력하면, 중지된 프로세스가 백그라운드에서 실행됩니다.

2.2 실행 중인 백그라운드 작업 확인

jobs -l

이 명령어를 입력하면, 백그라운드에서 실행 중인 프로세스 목록이 표시됩니다.

[1]  +  12345 Suspended   yarn run dev

위에서 12345는 프로세스 ID(PID)입니다.

2.3 특정 프로세스 종료

kill 12345

또는 아래 명령어를 사용해 백그라운드 프로세스를 종료할 수도 있습니다.

kill %1

📝 설명:
%1은 jobs 명령어에서 [1]로 표시된 백그라운드 작업의 번호입니다.


🔥 해결 방법 3: 강제 종료 (kill -9 사용)

만약 fg 또는 kill로도 프로세스가 종료되지 않는다면, 강제로 종료하는 방법이 있습니다.

3.1 실행 중인 프로세스 ID(PID) 찾기

ps aux | grep yarn

이 명령어를 실행하면 실행 중인 yarn run dev의 프로세스 ID(PID)를 찾을 수 있습니다.

user     12345   0.0  0.1  2458908  8000 s001  S+   10:34AM   0:02.34 node /path/to/yarn run dev

위에서 12345가 프로세스 ID입니다.

3.2 해당 PID를 강제 종료

kill -9 12345

📝 설명:
kill -9는 강제 종료 명령어로, 해당 프로세스를 즉시 중단시킵니다.


🔥 해결 방법 4: 터미널을 닫고 새로 열기

만약 위 방법으로도 해결되지 않는다면, 가장 간단한 방법은 터미널을 닫고 다시 여는 것입니다.

  1. 현재 실행 중인 터미널을 닫습니다.
  2. 새 터미널을 열고 다시 yarn run dev를 실행합니다.

이 방법을 사용하면, 기존에 중지된 프로세스가 자동으로 종료되면서 새로운 프로세스로 실행됩니다.


🚀 최종 정리: 어떤 방법을 선택할까?

해결 방법명령어추천 상황

Suspended 상태에서 다시 실행 후 종료 fg → Control + C 가장 일반적인 해결책
백그라운드에서 실행 후 종료 bg → jobs -l → kill %1 여러 개의 프로세스가 있을 때
프로세스 ID로 강제 종료 `ps aux grep yarn→kill -9 <PID>`
터미널 닫고 재시작 터미널 종료 후 다시 실행 모든 방법이 안될 때

💡 가장 추천하는 방법:

  • fg로 복구한 후 Control + C로 종료하는 것이 가장 빠르고 안정적인 해결책입니다.
  • 하지만 프로세스가 종료되지 않는다면 kill -9 <PID>를 사용하세요.

이제 Vue.js 개발 서버가 suspended 상태에 빠졌을 때 당황하지 말고, 위 방법을 활용하여 빠르게 해결해보세요! 🚀

 

#VueJS #MacOS #TerminalCommands #Debugging #DevTips #FrontendDev #WebDevelopment #JavaScript

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
반응형
반응형

1. CA 생성 시 입력하는 패스워드

역할 및 목적
CA(Certificate Authority)를 생성할 때 입력하는 패스워드는 CA의 개인 키를 암호화하는 데 사용됩니다. 예를 들어, elasticsearch-certutil ca 명령어를 통해 CA 인증서를 만들 때, 이 패스워드는 CA의 개인 키(예: http_ca.key)를 안전하게 암호화하여 저장합니다. 이후, CA를 이용해 다른 인증서를 서명할 때도 이 암호가 필요할 수 있습니다.

사용 시점과 영향

  • 사용 시점: 주로 CA 관련 작업, 예를 들어 새로운 인증서를 서명하거나 CA 인증서를 재발급할 때 필요합니다.
  • 실행 후 영향: 이 패스워드는 CA의 개인 키를 보호하는 역할을 하며, 운영 중에는 CA 관련 작업이 발생하지 않는 한 별도의 런타임 영향은 없습니다.

2. Elasticsearch keystore의 xpack.security.http.ssl.keystore.secure_password

역할 및 목적
이 설정은 Elasticsearch가 HTTPS 통신에 사용하는 HTTP keystore(예: http.p12) 파일을 열 때 사용됩니다. keystore 파일에는 HTTP 인증서와 그에 대응하는 개인 키가 포함되어 있으며, 이를 통해 Elasticsearch는 안전한 HTTPS 연결을 설정합니다.

사용 시점과 영향

  • 사용 시점: Elasticsearch가 시작할 때 keystore 파일을 열어 HTTPS 연결을 위한 인증서와 개인 키를 읽어들일 때 이 패스워드가 사용됩니다.
  • 실행 후 영향: 만약 이 패스워드가 올바르지 않다면, Elasticsearch는 keystore를 열 수 없게 되어 HTTPS 연결을 설정하지 못합니다. 그 결과 클라이언트(예: Kibana)와의 보안 통신에 문제가 발생할 수 있습니다.

3. 두 패스워드는 서로 달라도 괜찮은가?

두 패스워드는 각각 완전히 다른 역할을 수행합니다.

  • CA 패스워드: CA의 개인 키를 암호화하여 보호하며, CA 관련 서명 작업에 사용됩니다.
  • Keystore 패스워드: Elasticsearch가 HTTPS 통신에 필요한 인증서와 개인 키가 저장된 keystore 파일을 열 때 사용됩니다.

따라서 보안 정책에 따라 두 패스워드는 반드시 동일할 필요는 없으며, 서로 독립적으로 관리하는 것이 일반적입니다.


결론

보안 설정을 보다 명확하게 구성하기 위해서는 각 패스워드의 역할과 사용 시점을 정확히 이해하는 것이 중요합니다.

  • CA 생성 시 입력하는 패스워드: CA의 개인 키 암호화 및 서명 작업에 사용.
  • xpack.security.http.ssl.keystore.secure_password: Elasticsearch가 HTTPS 통신을 위해 keystore 파일을 열 때 사용.
728x90
반응형

+ Recent posts