[Node.js] Next.js(Node.js)에서 mysqldump 시도하기
Spring 백엔드단에서 Node.js 프레임워크로 옮겨오면서 mysql dump 기능을 node로 만들게 되었다.
try 하면서 시도한 삽질을 남기기 위해 포스팅.
시도한 프로젝트는 Next.js 프레임워크를 사용한 웹이고, mysql dump는 (사실상 DB는 MariaDB이긴함) client 단에서 처리 할 수 없는 command 이기 때문에 Node.js로 api를 호출하게끔 구현했다
https://nodejs.org/dist/latest-v14.x/docs/api/child_process.html
Child process | Node.js v14.21.2 Documentation
Child process# Source Code: lib/child_process.js The child_process module provides the ability to spawn subprocesses in a manner that is similar, but not identical, to popen(3). This capability is primarily provided by the child_process.spawn() function: c
nodejs.org
pages/api/util/backup.js 로 js 파일을 하나 만들었다.(Next.js는 파일라우터이기 때문에 해당 디렉토리에 파일을 만들고, 그 경로 그대로 fetch 하면 별도 라우터 처리없이 호출 가능하다.)
① 첫번째 시도
child_process 에서 exec 으로 실행.
node의 child process에 대한 설명을 참고하기 좋은 글이 있어서 참조하겠다.
https://sungmun.github.io/TIL/NodeJs/2018-04-19_ChildProcessManual.html
2018-04-19. Child Process사용법 · GitBook
No results matching ""
sungmun.github.io
일단 구현해서 실행해본 소스는 아래와 같은데,
export default async function handler(req, res){
const { exec } = require('child_process');
const iconv = require('iconv-lite');
const dumpMariaDB = async (dbName, username, password, filePath) => {
const dumpMariaDB = async () => {
try {
let rs = await exec('mysqldump -u root -p 1234 test > ./backup/dump.sql', { encoding: 'utf8',
timeout: 0,
maxBuffer: 1024*1024, //increase here
killSignal: 'SIGTERM',
cwd: null,
env: null }, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: `, iconv.decode(error, 'euc-kr'));
return;
}
console.log(`stdout: ${stdout}`);
console.error(`stderr: ${stderr}`);
});
} catch (error) {
console.error(`Error: ${error}`);
}
};
// Example usage:
dumpMariaDB();
}
(해당 소스 실행 전 npm install child_process를 한번 해주긴 했다)
해당 방법으로 시도하면 node 커멘드 창에 자꾸 에러가 발생했다.
uncaughtException: RangeError [ERR_INVALID_OPT_VALUE]: The value "NaN" is invalid for option "size"
해당 에러 내용을 서치해보면
하위 프로세스 모듈의 NaN값은 크기 옵션에 유효하지 않고, 크기옵션은 프로세스에서 데이터를 읽기 위한 버퍼 크기를 설정하며 양의 정수여야 한다고 하는데
exec에 그래서 maxBuffer도 주고 구글링해서 나오는 옵션들을 오만가지 다 넣어봐도 해결이 안되었다.. 저 에러가 계속남...
②두번째 시도
child_process 에서 spawn으로 실행.
exec랑 spawn의 차이를 간단히 짚고 넘어가보겠다.
* spawn
1. 쉘을 생성하지 않음
2. 하위 프로세스에서 반환된 데이터 스트리밍(데이터 흐름이 일정)
3. 데이터 전송크기 제한이 없어서 출력이 큰 장기 실행 프로세스에 더 적합함
* exec
1. 쉘을 생성.
2. 데이터를 버퍼링(프로세스가 닫힐 때까지 기다렸다가 데이터를 덩어리로 전송)
3. Node.js v.12.x까지의 최대 데이터 전송량은 200kb(기본값)였지만 Node.js v.12x가 1MB(기본값)로 증가됨
실행 소스
export default async function handler(req, res){
const { spawn } = require('child_process');
const mysqldump = spawn('mysqldump', ['-u', 'root', '-p<password>', '<database>', '>', '<dump-file>.sql']);
mysqldump.stdout.on('data', (data) => {
console.log(`mysqldump output: ${data}`);
});
mysqldump.stderr.on('data', (data) => {
console.error(`mysqldump error: ${data}`);
});
mysqldump.on('close', (code) => {
console.log(`mysqldump process exited with code ${code}`);
});
}
역시나 에러발생,,

MySQL Client에 비번입력하고 들어가서 ctrl+C 누르면 Bye! 하고 요런식으로 경로가 노출된다.
저 경로를 가져와서 소스코드에 넣어주었다.
export default async function handler(req, res){
const { spawn } = require('child_process');
const mysqldump = spawn('C:/Program Files/MariaDB 10.4/bin/mysqldump', ['-u', 'root', '-p<password>', '<database>', '>', '<dump-file>.sql']);
mysqldump.stdout.on('data', (data) => {
console.log(`mysqldump output: ${data}`);
});
mysqldump.stderr.on('data', (data) => {
console.error(`mysqldump error: ${data}`);
});
mysqldump.on('close', (code) => {
console.log(`mysqldump process exited with code ${code}`);
});
}
③ 세번째 시도..
앞선 에러는 고쳤는데 이번엔 새로운 에러가 발생
mysqldump error: mysqldump: Couldn't find table: ">"
해당 에러는 mysqldump 명령을 > 연산자와 함께 사용하여 명령 출력을 파일로 리디렉션할 때 발생한다. 오류 메시지는 mysqldump 명령이 > 기호를 출력을 리디렉션하는 연산자가 아니라 덤프하려는 테이블의 이름으로 해석하고 있음을 나타낸다.
해결뱡법은 fs 모듈을 사용하여 mysqldump 명령의 출력을 파일로 리디렉션할 수 있다.
아래와 같은 방법으로 fs 모듈을 사용해 stream 으로 만들어 stdout 에 붙여서 사용한다.
const fs = require('fs');
const writeStream = fs.createWriteStream('dump.sql');
mysqldump.stdout.pipe(writeStream);
해당 소스를 추가해주면 됨.
최종소스
export default async function handler(req, res){
//child_process, fs 외부모듈 가져오기
const { spawn } = require('child_process');
const fs = require('fs');
//mysql bin 전체경로 뒤에 mysqldump 명령어 이어붙임
//DB는 아이디 root, 비번 1234, 데이터베이스이름 test (-p비번 띄어쓰기없이 입력)
const mysqldump = spawn('C:/Program Files/MariaDB 10.4/bin/mysqldump', ['-u', 'root', '-p1234', 'test']);
//덤프파일 저장할 위치 (프로젝트 내부경로로 입력해줌, window절대경로도 상관없음)
const writeStream = fs.createWriteStream('./backup/dump.sql');
mysqldump.stdout.pipe(writeStream);
mysqldump.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
mysqldump.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
}
요렇게 구현했고, 이 api를 호출하면 node command 창에
요렇게 뜨고, 지정한 ./backup 경로로 가보면(프로젝트 내부 경로)
요렇게 덤프파일이 받아져있는걸 확인할 수 있다!
세시간 삽질 끝,,
(+ 파일이름은 겹치지 않도록 생성해주어야 하고, 해당 api 호출시 웹단에 처리중이라는 로딩창 보여주는 구현이 남았다. 얼마나 대용량 데이터까지 처리할 수 있는지 시간은 얼마나 걸리는지 성능 체크도 남았다..)
**삽질의 80%는 ChatGPT가 공헌해주었다. AI 만세
이런식으로 물어보믄 편하다!