Node.js 바인딩
MeCab-Ko는 Neon을 사용하여 Node.js 바인딩을 제공합니다.
설치
npm install mecab-ko
# or
yarn add mecab-ko
# or
pnpm add mecab-ko
빠른 시작
const { Tagger } = require('mecab-ko');
// Tagger 생성
const tagger = new Tagger();
// 문장 분석
const result = tagger.parse('안녕하세요');
console.log(result);
// 노드 단위 처리
const nodes = tagger.parseToNodes('형태소 분석');
nodes.forEach(node => {
console.log(`${node.surface}\t${node.pos}\t${node.feature}`);
});
TypeScript:
import { Tagger, Node, TaggerConfig } from 'mecab-ko';
const tagger = new Tagger();
const nodes: Node[] = tagger.parseToNodes('안녕하세요');
API 레퍼런스
Tagger 클래스
형태소 분석기의 메인 클래스입니다.
class Tagger {
/**
* 새로운 Tagger를 생성합니다.
* @param config - Tagger 설정
*/
constructor(config?: TaggerConfig);
/**
* 텍스트를 분석하고 문자열로 반환합니다.
* @param text - 분석할 텍스트
* @returns 분석 결과 문자열
*/
parse(text: string): string;
/**
* 텍스트를 분석하고 Node 배열로 반환합니다.
* @param text - 분석할 텍스트
* @returns Node 객체 배열
*/
parseToNodes(text: string): Node[];
/**
* 텍스트를 분석하고 객체로 반환합니다.
* @param text - 분석할 텍스트
* @returns 분석 결과 객체
*/
parseToObject(text: string): ParseResult;
/**
* N-best 결과를 반환합니다.
* @param text - 분석할 텍스트
* @param n - 반환할 후보 개수
* @returns N개의 분석 결과 문자열 배열
*/
parseNBest(text: string, n?: number): string[];
/**
* 비동기로 텍스트를 분석합니다.
* @param text - 분석할 텍스트
* @returns Promise<분석 결과 문자열>
*/
parseAsync(text: string): Promise<string>;
/**
* 비동기로 Node 배열을 반환합니다.
* @param text - 분석할 텍스트
* @returns Promise<Node 객체 배열>
*/
parseToNodesAsync(text: string): Promise<Node[]>;
}
TaggerConfig 인터페이스
interface TaggerConfig {
/** 사전 디렉토리 경로 */
dictDir?: string;
/** 사용자 사전 파일 경로 */
userDict?: string;
/** 출력 포맷 ("mecab" | "wakati" | "json" | "csv") */
outputFormat?: string;
/** 띄어쓰기 패널티 (기본값: -1000) */
spacePenalty?: number;
/** 부분 처리 활성화 */
partial?: boolean;
/** 전부 출력 활성화 */
allMorphs?: boolean;
/** N-best 개수 */
nbest?: number;
/** theta 값 (N-best용) */
theta?: number;
}
Node 인터페이스
interface Node {
/** 표면형 (실제 텍스트) */
surface: string;
/** 품사 및 의미 정보 */
feature: string;
/** 품사 태그 */
pos: string;
/** 시작 위치 (바이트 단위) */
start: number;
/** 길이 (바이트 단위) */
length: number;
/** 비용 */
cost: number;
/** 기본형 (원형) */
baseForm?: string;
/** 읽기 정보 */
reading?: string;
}
ParseResult 인터페이스
interface ParseResult {
/** 원본 텍스트 */
text: string;
/** 분석된 노드 배열 */
nodes: Node[];
}
사용 예제
기본 사용 (CommonJS)
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
const text = '아버지가방에들어가신다';
const result = tagger.parse(text);
console.log(result);
기본 사용 (ES Modules)
import { Tagger } from 'mecab-ko';
const tagger = new Tagger();
const result = tagger.parse('형태소 분석을 시작합니다');
console.log(result);
TypeScript
import { Tagger, Node, TaggerConfig } from 'mecab-ko';
const config: TaggerConfig = {
outputFormat: 'json',
spacePenalty: -1000,
};
const tagger = new Tagger(config);
const nodes: Node[] = tagger.parseToNodes('안녕하세요');
nodes.forEach((node: Node) => {
console.log(`표면형: ${node.surface}`);
console.log(`품사: ${node.pos}`);
if (node.baseForm) {
console.log(`기본형: ${node.baseForm}`);
}
console.log('---');
});
Node 단위 처리
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
const nodes = tagger.parseToNodes('형태소 분석을 시작합니다');
for (const node of nodes) {
console.log(`표면형: ${node.surface}`);
console.log(`품사: ${node.pos}`);
console.log(`특성: ${node.feature}`);
console.log('---');
}
객체 형식 출력
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
const result = tagger.parseToObject('안녕하세요');
console.log('원본 텍스트:', result.text);
console.log('노드 개수:', result.nodes.length);
result.nodes.forEach((node, index) => {
console.log(`${index + 1}. ${node.surface} (${node.pos})`);
});
사용자 사전 사용
const { Tagger } = require('mecab-ko');
const tagger = new Tagger({
userDict: './user.csv',
});
const result = tagger.parse('딥러닝과 머신러닝');
console.log(result);
user.csv:
딥러닝,NNG,-1000,딥러닝
머신러닝,NNG,-1000,머신러닝
Wakati 출력 (형태소만)
const { Tagger } = require('mecab-ko');
const tagger = new Tagger({ outputFormat: 'wakati' });
const result = tagger.parse('형태소만 추출합니다');
console.log(result); // "형태소 만 추출 하 ㅂ니다"
JSON 출력
const { Tagger } = require('mecab-ko');
const tagger = new Tagger({ outputFormat: 'json' });
const result = tagger.parse('JSON 형식으로 출력');
const data = JSON.parse(result);
console.log(JSON.stringify(data, null, 2));
N-best 분석
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
const candidates = tagger.parseNBest('아버지가방에들어가신다', 3);
candidates.forEach((candidate, index) => {
console.log(`=== 후보 ${index + 1} ===`);
console.log(candidate);
});
비동기 처리
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
async function analyze() {
try {
const result = await tagger.parseAsync('비동기 처리 예제');
console.log(result);
const nodes = await tagger.parseToNodesAsync('또 다른 문장');
console.log(nodes);
} catch (error) {
console.error('분석 실패:', error);
}
}
analyze();
Promise 체이닝
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
tagger.parseAsync('첫 번째 문장')
.then(result => {
console.log('결과 1:', result);
return tagger.parseAsync('두 번째 문장');
})
.then(result => {
console.log('결과 2:', result);
})
.catch(error => {
console.error('오류:', error);
});
병렬 처리
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
const texts = [
'첫 번째 문장',
'두 번째 문장',
'세 번째 문장',
];
Promise.all(texts.map(text => tagger.parseAsync(text)))
.then(results => {
results.forEach((result, index) => {
console.log(`결과 ${index + 1}:`, result);
});
})
.catch(error => {
console.error('오류:', error);
});
Express 서버
const express = require('express');
const { Tagger } = require('mecab-ko');
const app = express();
const tagger = new Tagger();
app.use(express.json());
app.post('/analyze', async (req, res) => {
try {
const { text } = req.body;
if (!text) {
return res.status(400).json({ error: 'No text provided' });
}
const result = await tagger.parseToNodesAsync(text);
res.json({ text, nodes: result });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
사용 예:
curl -X POST http://localhost:3000/analyze \
-H "Content-Type: application/json" \
-d '{"text": "안녕하세요"}'
파일 처리
const fs = require('fs');
const readline = require('readline');
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
async function processFile(filename) {
const fileStream = fs.createReadStream(filename);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity,
});
for await (const line of rl) {
if (line.trim()) {
const result = tagger.parse(line);
console.log(result);
}
}
}
processFile('large_file.txt')
.catch(console.error);
스트림 처리
const { Tagger } = require('mecab-ko');
const { Transform } = require('stream');
const tagger = new Tagger();
class MecabTransform extends Transform {
constructor(options) {
super(options);
}
_transform(chunk, encoding, callback) {
try {
const text = chunk.toString();
const result = tagger.parse(text);
this.push(result + '\n');
callback();
} catch (error) {
callback(error);
}
}
}
// 사용
process.stdin
.pipe(new MecabTransform())
.pipe(process.stdout);
명사/동사 추출
const { Tagger } = require('mecab-ko');
const tagger = new Tagger();
const text = '저는 오늘 학교에 가서 공부를 했습니다.';
const nodes = tagger.parseToNodes(text);
// 명사 추출
const nouns = nodes
.filter(node => ['NNG', 'NNP'].includes(node.pos))
.map(node => node.surface);
console.log('명사:', nouns);
// 출력: ['오늘', '학교', '공부']
// 동사 추출
const verbs = nodes
.filter(node => ['VV', 'VA'].includes(node.pos))
.map(node => node.surface);
console.log('동사:', verbs);
// 출력: ['가', '하']
Next.js API Route
// pages/api/analyze.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { Tagger } from 'mecab-ko';
const tagger = new Tagger();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const { text } = req.body;
if (!text) {
return res.status(400).json({ error: 'No text provided' });
}
try {
const nodes = tagger.parseToNodes(text);
res.status(200).json({ text, nodes });
} catch (error) {
res.status(500).json({ error: 'Analysis failed' });
}
}
성능 최적화
Tagger 재사용
// Bad - 매번 생성
for (const text of texts) {
const tagger = new Tagger(); // 비효율적
const result = tagger.parse(text);
}
// Good - 재사용
const tagger = new Tagger(); // 한 번만 생성
for (const text of texts) {
const result = tagger.parse(text);
}
비동기 배치 처리
const { Tagger } = require('mecab-ko');
async function batchAnalyze(texts, batchSize = 100) {
const tagger = new Tagger();
const results = [];
for (let i = 0; i < texts.length; i += batchSize) {
const batch = texts.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(text => tagger.parseAsync(text))
);
results.push(...batchResults);
}
return results;
}
에러 처리
const { Tagger } = require('mecab-ko');
try {
const tagger = new Tagger({
dictDir: '/invalid/path',
});
} catch (error) {
console.error('Tagger 생성 실패:', error.message);
}
const tagger = new Tagger();
try {
const result = tagger.parse('분석할 텍스트');
console.log(result);
} catch (error) {
console.error('분석 실패:', error.message);
}
타입 정의 파일
TypeScript 사용 시 타입 정의가 자동으로 제공됩니다:
import { Tagger, Node, TaggerConfig, ParseResult } from 'mecab-ko';