Node.js와 I/O
빠르게 살펴보는 Node.js 개념
JavaScript와 TypeScript를 위해서 Node.js를 런타임 환경으로 사용하는 분들을 위한 기초적인 내용입니다.
Node.js와 I/O
파일 읽기와 쓰기
파일을 다루는 일은 대부분의 애플리케이션에서 기본적으로 이루어지는 작업입니다. 예를 들어, 설정 파일을 읽거나 로그를 남기고, 데이터를 처리하는 등 다양한 상황에서 파일 입출력이 필요합니다. Node.js에서는 이러한 파일 작업을 쉽게 처리할 수 있도록 fs 모듈을 통해 동기식(synchronous)과 비동기식(asynchronous) 메서드를 모두 제공합니다.
컴퓨팅 초창기인 1950년대부터 파일 처리는 컴퓨터의 중요한 역할이었습니다. 데이터를 저장하고 찾아내기 위해 자기 테이프 드라이브부터 시작해, 오늘날에는 기가바이트 단위의 데이터도 Node.js의 논블로킹(non-blocking) 방식으로 부드럽게 처리할 수 있게 되었습니다.
1970년대 UNIX 운영체제는 우리가 지금도 사용하는 파일 시스템의 계층 구조를 만들었습니다. Node.js의
fs모듈 또한 이 구조와 자연스럽게 호환되어, 복잡한 파일 작업도 손쉽게 구현할 수 있습니다. 성능이 뛰어나기 때문에, Netflix와 같은 대규모 서비스도 방송 스트리밍 등 거대한 파일 입출력 작업에 Node.js를 활용하고 있습니다.
Node.js의 fs(File System) 모듈은 파일과 디렉터리를 읽고, 쓰고, 수정·삭제하는 등 파일 시스템과의 상호작용에 필요한 다양한 기능을 제공합니다. fs 모듈은 다음과 같이 불러올 수 있습니다:
파일 읽기
Node.js에서는 파일을 읽는 방식을 동기식과 비동기식 두 가지로 지원합니다.
동기식 읽기
동기식 방식은 파일을 다 읽을 때까지 프로그램 실행이 멈춤 상태가 됩니다. 이 방법은 소규모 파일이나 간단한 스크립트에는 편리하지만, 파일이 크거나 애플리케이션이 복잡할 때는 이벤트 루프가 차단되어 바람직하지 않습니다.
파일 경로를 쓸 때는 슬래시의 방향을 주의하세요. Windows라면 디렉터리 구분에 역슬래시(
\\)를 사용해야 하며(예:C:\\Users\\Name\\Desktop\\example.txt), 단일 역슬래시(\)를 잘못 쓰면 “ENOENT: no such file or directory” 오류가 발생할 수 있습니다.
fs.readFileSync('example.txt', 'utf8'): 지정된 파일을 동기적으로 읽어옵니다.- 두 번째 인자
'utf8'은 인코딩 방식을 지정합니다. - 오류가 있을 경우,
catch문에서 로그로 남깁니다.
비동기식 읽기
비동기식 읽기는 파일을 읽는 동안에도 프로그램의 나머지 부분이 계속 실행됩니다. 대용량 파일이나 성능이 중요한 상황에서 적합합니다.
fs.readFile('example.txt', 'utf8', callback): 파일을 비동기적으로 읽습니다.- 콜백 함수가 파일 데이터 또는 오류를 처리합니다.
'파일을 읽는 중...'문장은 파일 읽기보다 먼저 출력될 수 있는데, 이 덕분에 코드가 논블로킹 동작함을 알 수 있습니다.
파일 쓰기
파일 저장 역시 동기식과 비동기식, 모두 지원합니다.
동기식 쓰기
fs.writeFileSync('example.txt', data): 해당 파일에 데이터를 동기적으로 씁니다.- 오류 발생 시, 예외를 잡아 콘솔에 출력합니다.
비동기식 쓰기
fs.writeFile('example.txt', data, callback): 파일에 데이터를 비동기적으로 저장합니다.- 콜백 함수에서 오류 또는 성공 메시지를 출력합니다.
'파일을 쓰는 중...'이 먼저 찍혀, 논블로킹 성질이 드러납니다.
파일에 내용 추가하기
기존 파일에 내용을 추가하고 싶다면 fs.appendFile 또는 fs.appendFileSync를 사용할 수 있습니다.
fs.appendFile('example.txt', data, callback): 데이터를 파일 끝에 비동기적으로 추가합니다.- 콜백에서 오류와 성공 여부를 확인합니다.
- 논블로킹으로 동작하므로
'파일에 내용 추가 중...'이 먼저 출력됩니다.
파일 삭제하기
파일을 삭제하려면 fs.unlink(비동기) 혹은 fs.unlinkSync(동기) 메서드를 사용할 수 있습니다.
fs.unlink('example.txt', callback): 지정한 파일을 비동기적으로 삭제합니다.- 콜백 함수로 오류 및 성공 여부를 처리합니다.
'파일 삭제 중...'이 바로 찍힙니다.
파일 변경 감시하기
Node.js는 파일이나 디렉터리의 변경을 실시간으로 감시할 수 있습니다. 이는 핫 리로드 등 개발 환경 자동화에 유용하게 쓰입니다.
fs.watch('example.txt', callback): 파일 변화를 감시하며, 변경 시 콜백이 실행됩니다.- 콜백에서는 수정된 파일명을 활용해 메시지를 출력할 수 있습니다.
- 감시를 시작하면
'파일 감시 중...'메시지가 바로 나타납니다.
모범 사례(Best Practices)
- 비동기 메서드 사용: 애플리케이션 응답성을 위해, 동기식(
fs.readFileSync,fs.writeFileSync)보다 비동기식(fs.readFile,fs.writeFile) 메서드를 우선 사용하세요. - 적절한 오류 처리: 동기식 코드는
try-catch로, 비동기식 코드는 오류 우선 콜백 또는Promise등으로 반드시 예외 상황을 제대로 처리해야 합니다. - 대용량 파일은 스트림 활용: 큰 파일은
fs.createReadStream,fs.createWriteStream으로 청크 단위로 다루는 것이 메모리와 성능에 더 효율적입니다. - 인코딩 지정: 텍스트 파일 입출력 시에는 반드시
'utf8'와 같은 인코딩을 명확히 지정해 예상치 못한 오류를 피하세요.
이처럼 fs 모듈을 활용하면 Node.js에서 파일을 읽고 쓰는 작업을 쉽고 효율적으로 처리할 수 있습니다. 동기식과 비동기식 메서드 중 어떤 것을 사용할지는 애플리케이션의 목적과 상황에 따라 결정하면 됩니다. 동기식은 간단함이 장점이지만, 이벤트 루프를 막아서 성능에 악영향을 줄 수 있습니다. 반면, 비동기식은 논블로킹이므로 더 나은 성능과 확장성이 필요할 때 적합합니다. 이러한 원리와 기능을 잘 이해하면 Node.js 프로젝트에서 파일 입출력을 능숙하게 다룰 수 있게 됩니다.
디렉터리 작업
디렉터리는 파일 시스템에서 파일들을 체계적으로 정리하는 데 반드시 필요합니다. Node.js는 fs 모듈을 이용해 디렉터리를 생성, 읽기, 이름 변경, 삭제 등 다양한 방식으로 다룰 수 있습니다. 이러한 작업 방법을 익히면 애플리케이션 내 파일 구조를 훨씬 효율적으로 관리할 수 있습니다.
디렉터리 생성하기
Node.js에서는 디렉터리를 동기식과 비동기식 방식 모두로 생성할 수 있습니다.
동기식 디렉터리 생성
fs.existsSync('new-directory')는 해당 디렉터리가 이미 존재하는지 확인합니다.fs.mkdirSync('new-directory')는 존재하지 않을 때만 디렉터리를 생성합니다.- 오류가 발생하면
catch블록에서 예외를 콘솔에 기록합니다.
비동기식 디렉터리 생성
fs.mkdir('new-directory', callback)은 비동기적으로 디렉터리를 생성합니다.- 생성 요청 후 바로
'디렉터리 생성 중...'메시지가 출력되어, 논블로킹의 예시가 됩니다. - 콜백에서 오류 여부를 반드시 처리해 주세요.
디렉터리(폴더)는 거의 모든 컴퓨터 시스템에서 데이터 정리의 핵심 요소입니다. 디렉터리와 하위 디렉터리가 있는 트리 구조 개념은 1960년대 Multics 같은 운영 체제에서 탄생했습니다. 이렇게 해서 방대한 데이터를 효과적으로 분류, 탐색할 수 있게 되었죠. Node.js에서는 몇 줄의 코드만으로 복잡한 폴더 구조도 쉽게 만들 수 있습니다. NASA 등 실제로 대량의 데이터를 신속히 관리해야 하는 기업에서도 Node.js의 디렉터리 처리 기능을 활용하고 있습니다.
디렉터리 내용 읽기
디렉터리 내 파일과 하위 폴더 목록은 동기·비동기 방식으로 조회할 수 있습니다.
동기식 디렉터리 읽기
fs.readdirSync('.')를 사용하면 현재 디렉터리 내용을 동기적으로 읽어옵니다.- 읽어온 파일 목록을 반복문으로 출력합니다.
- 읽기 중 오류가 생기면 예외를 콘솔에 출력합니다.
비동기식 디렉터리 읽기
fs.readdir('.', callback)으로 비동기로 디렉터리 내용을 조회합니다.- 호출 직후
'디렉터리 읽는 중...'이 바로 출력되어 논블로킹 예시를 보여줍니다. - 콜백의
files배열로 목록을 반복 출력합니다.
디렉터리 삭제
디렉터리 삭제에도 동기식, 비동기식 모두 사용할 수 있습니다.
동기식 디렉터리 삭제
fs.existsSync('new-directory')로 디렉터리 존재 여부를 확인합니다.- 존재한다면
fs.rmdirSync('new-directory')로 바로 삭제합니다. - 예외 발생 시 catch에서 에러를 처리합니다.
비동기식 디렉터리 삭제
fs.rmdir('new-directory', callback)은 비동기로 디렉터리를 삭제합니다.- 요청 직후
'디렉터리 삭제 중...'메시지가 바로 출력됩니다. - 콜백에서 반드시 오류 처리를 해 주세요.
디렉터리 이름 변경
디렉터리의 이름도 동기·비동기 방식 모두로 바꿀 수 있습니다.
동기식 디렉터리 이름 변경
fs.renameSync로 동기식으로 디렉터리 이름을 바꿉니다.- 오류가 있으면 예외가 콘솔에 출력됩니다.
비동기식 디렉터리 이름 변경
fs.rename의 콜백에서 오류를 체크해야 합니다.- 함수 호출 직후 메시지가 바로 나오므로, 비동기 실행임을 알 수 있습니다.
디렉터리 변동 감시
Node.js에서는 디렉터리 내 파일 추가, 삭제, 수정 등 다양한 변동도 감시할 수 있습니다.
fs.watch('new-directory', callback)을 사용해 디렉터리의 변화를 실시간으로 감시합니다.- 이벤트 발생 시 타입 및 영향을 받은 파일명을 출력합니다.
- 감시 시작 후 곧바로 감시 중 메시지가 표시됩니다.
모범 사례 (Best Practices)
- 존재 여부 확인: 디렉터리를 새로 만들 때는
fs.existsSync()로 중복 생성을 방지하거나, 비동기 함수의 에러 값을 활용해 처리하세요. - 재귀 옵션 활용: 하위 디렉터리까지 한 번에 생성하려면
fs.mkdir또는fs.mkdirSync호출 시{ recursive: true }옵션을 사용하세요. - 에러 처리 필수: 생성, 읽기, 삭제 등의 작업 후에는 항상 예외(
catch나 콜백의 에러 등)가 발생할 수 있으니 철저히 오류 처리를 해 주세요. - path 모듈 적극 사용: 운영체제마다 디렉터리 경로 표기가 다르므로 Node.js 내장
path모듈을 사용하는 것을 권장합니다.
스트림과 버퍼
Node.js에서 스트림(Streams)과 버퍼(Buffers)는 효율적인 I/O 작업의 필수 요소입니다. 이 두 가지는 데이터 전체를 한 번에 메모리에 올리지 않아도 되고, 지속적으로 데이터를 읽고 쓸 수 있게 해주어 대용량 파일이나 실시간 데이터 처리가 가능합니다. 아래에서 두 개념의 역할과 실제 활용법을 알아보겠습니다.
스트림은 마치 강물처럼 데이터를 끊임없이 흘려보내는 개념에서 유래했습니다. Node.js의 스트림을 활용하면 대용량 데이터를 여러 개의 작은 조각(청크, chunk)으로 나누어 한 번에 처리할 수 있습니다. 대용량 미디어, 로그, 실시간 피드와 같은 데이터를 효율적으로 다루기에 특히 적합합니다.
버퍼의 등장은 1960년대로 거슬러 올라가며, 당시엔 컴퓨터 시스템 간 데이터 교환을 위해 도입되었습니다. Node.js에서 버퍼는 바이너리 데이터를 직접 다룰 수 있게 해주며, 효율적인 파일/스트림 처리를 가능케 합니다. 실제로, Youtube, Spotify와 같은 고성능 서비스는 스트리밍 및 버퍼링 기술에 크게 의존합니다.
스트림 이해하기
Node.js의 스트림은 소스(예: 파일, 네트워크)에서 데이터를 읽고 목적지로 순차적으로 데이터를 쓸 수 있게 해주는 객체입니다. 스트림은 데이터를 청크 단위로 처리하기 때문에 성능과 메모리 관리에서 큰 이점을 제공합니다.
스트림의 종류
- 읽기 가능 스트림 (Readable Streams): 데이터를 소스에서 읽어들입니다.
- 쓰기 가능 스트림 (Writable Streams): 데이터를 목적지에 기록합니다.
- 듀플렉스 스트림 (Duplex Streams): 읽기와 쓰기를 모두 지원합니다.
- 변환 스트림 (Transform Streams): 데이터를 읽거나 쓸 때 변환할 수 있는 듀플렉스 스트림입니다.
읽기 가능 스트림
읽기 가능 스트림을 통해 파일, 네트워크 응답 등에서 데이터를 읽어올 수 있습니다.
fs.createReadStream('example.txt', 'utf8'): example.txt 파일을 읽기 위한 스트림을 생성합니다.'data'이벤트 : 파일에서 청크 단위로 데이터가 들어올 때 발생합니다.'end'이벤트 : 파일 전체 읽기 완료 시 발생합니다.'error'이벤트 : 읽기 도중 오류 발생 시 동작합니다.
쓰기 가능 스트림
쓰기 스트림은 파일이나 네트워크로 데이터를 기록할 때 사용합니다.
const fs = require('fs');
const writeStream = fs.createWriteStream('example.txt');
writeStream.write('Hello, ', 'utf8');
writeStream.write('world!', 'utf8');
writeStream.end();
writeStream.on('finish', () => {
console.log('파일 쓰기 완료');
});
writeStream.on('error', (err) => {
console.error('파일 쓰기 오류:', err);
});fs.createWriteStream('example.txt'): example.txt 파일에 쓸 수 있는 스트림을 생성합니다..write(): 데이터를 스트림에 기록합니다..end(): 기록 종료(더 이상 쓸 데이터 없음 표시)합니다.'finish'이벤트 : 모든 데이터 기록이 완료됐을 때 발생합니다.'error'이벤트 : 쓰기 도중 오류 발생 시 동작합니다.
듀플렉스 스트림
듀플렉스 스트림은 읽기와 쓰기 모두 지원해 네트워크 통신 등에 활용됩니다.
const net = require('net');
const server = net.createServer((socket) => {
socket.write('서버에 오신 것을 환영합니다!\n');
socket.on('data', (data) => {
console.log('수신:', data.toString());
socket.write('당신이 보낸 메시지: ' + data);
});
socket.on('end', () => {
console.log('클라이언트 연결 끊김');
});
socket.on('error', (err) => {
console.error('오류:', err);
});
});
server.listen(3000, () => {
console.log('3000번 포트에서 서버 대기 중');
});net.createServer: TCP 서버를 생성하며, 각 연결은 듀플렉스 스트림(socket) 제공합니다.socket: 클라이언트의 데이터 읽기/쓰기 모두 가능합니다.
변환 스트림
변환 스트림은 스트림에 들어오는 데이터를 가공(예: 압축, 암호화 등) 할 때 사용합니다.
const fs = require('fs');
const zlib = require('zlib');
const readStream = fs.createReadStream('example.txt', 'utf8');
const writeStream = fs.createWriteStream('example.txt.gz');
const gzip = zlib.createGzip();
readStream.pipe(gzip).pipe(writeStream);
writeStream.on('finish', () => {
console.log('파일 압축 완료');
});zlib.createGzip(): gzip 압축 변환 스트림을 생성합니다..pipe(): 여러 스트림을 연결해 데이터 흐름(chain) 형성합니다.- example.txt를 읽어서 압축, example.txt.gz에 저장합니다.
버퍼 이해하기
버퍼는 바이너리 데이터를 보관하는 임시 저장소입니다. Node.js에서는 스트림, 파일 I/O 등 다양한 상황에서 버퍼가 필요합니다.
버퍼 생성
버퍼는 다양한 방법으로 만들 수 있습니다.
Buffer.from('Hello, world!', 'utf8'): 문자열을 버퍼로 생성합니다..toString('utf8'): 버퍼를 문자열로 복원합니다.
버퍼 쓰기
Buffer.alloc(16): 16바이트 공간을 가진 버퍼를 생성합니다..write(): 문자열을 버퍼에 기록합니다.
버퍼 연산 예시
버퍼 데이터는 자르기, 붙이기 등 여러 연산이 가능합니다.
버퍼 연결
둘 이상의 버퍼를 하나로 합칠 수도 있습니다.
스트림과 버퍼의 실제 활용 사례
스트림과 버퍼는 실무에서 여러 방면으로 활용됩니다.
- 파일 처리
- 전체 파일을 메모리에 올리지 않고 대용량 파일을 읽고 씀
- 대표 예: 로그 또는 미디어 파일 처리
- 네트워크 통신
- 데이터 전송을 효율적으로 처리
- 대표 예: HTTP, TCP, WebSocket 서버
- 데이터 변환
- 데이터 압축(gzip), 암호화 등 실시간 변환 작업
- 실시간 데이터 처리
- 실시간 비디오 스트리밍, 데이터 분석 등
모범 사례 (Best Practices)
- 파이프(pipe) 사용 권장: 스트림 간 연결에
.pipe()메서드를 사용하면backpressure(역압)을 자동으로 처리하여 데이터 흐름이 끊기지 않습니다. - 이벤트 핸들링:
data,end,error,finish등 스트림 이벤트를 반드시 감지하여 스트림 상태와 오류를 체계적으로 관리하세요. - 버퍼의 효율적 사용: 바이너리 데이터 작업에 버퍼를 적극적으로 활용하되, 사용 목적에 맞는 크기로 버퍼를 할당하여 메모리와 성능 모두 최적화하세요.
- 리소스 정리: 스트림 사용이 끝나면 반드시
.close()또는.destroy()등으로 자원을 해제하여 메모리 누수와 시스템 리소스 낭비를 예방하세요.
정리하자면, 스트림을 사용하면 데이터 흐름을 청크 단위로 분할해 효율적으로 다룰 수 있고, 버퍼는 바이너리 데이터를 직접 조작하는 핵심 도구입니다. 이 두 개념을 제대로 이해하고 활용하면, 대용량 데이터 처리부터 네트워크 통신, 파일 시스템 작업까지 Node.js로 더욱 견고하고 확장성 있는 애플리케이션을 구축할 수 있습니다.
파일 업로드 처리
파일 업로드 처리는 웹 애플리케이션의 일반적인 요구 사항입니다. Node.js는 이 과정을 단순화하기 위한 다양한 도구와 라이브러리를 제공합니다. Node.js에서 파일 업로드를 처리하는 가장 인기 있는 라이브러리 중 하나는 multer입니다. 이 섹션에서는 multer를 사용하여 파일 업로드를 설정하는 방법을 안내하고, 과정을 설명하기 위한 실제 예제를 제공합니다.
multer 설정하기
multer는 주로 파일 업로드에 사용되는 multipart/form-data를 처리하기 위한 미들웨어(middleware)입니다. 설정이 쉽고 Node.js의 인기 있는 웹 애플리케이션 프레임워크인 Express와 원활하게 작동합니다.
먼저 multer와 express를 설치해야 합니다:
다음은 Express와 multer를 사용하여 파일 업로드 서버를 설정하는 기본 예제입니다.
multer를 사용하면 대상 디렉터리 및 파일 이름 지정과 같은 저장 옵션을 구성할 수 있습니다.
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/'); // 업로드된 파일을 저장할 디렉터리
},
filename: function (req, file, cb) {
// 파일 이름 형식
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({ storage: storage });
const app = express();
const PORT = 3000;
// "public" 디렉터리에서 정적 파일 제공
app.use(express.static('public'));
app.post('/upload', upload.single('file'), (req, res) => {
try {
res.send('파일 업로드 성공');
} catch (error) {
res.status(400).send('파일 업로드 오류');
}
});
app.listen(PORT, () => {
console.log(`${PORT}번 포트에서 서버 실행 중`);
});public 디렉터리에 다음 내용으로 index.html 파일을 생성합니다:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
</head>
<body>
<h1>Upload a File</h1>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file" required>
<button type="submit">Upload</button>
</form>
</body>
</html>전체 예제
서버의 전체 코드는 다음과 같습니다.
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const PORT = 3000;
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({ storage: storage });
app.use(express.static('public'));
app.post('/upload', upload.single('file'), (req, res) => {
try {
res.send('파일 업로드 성공');
} catch (error) {
res.status(400).send('파일 업로드 오류');
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
public디렉터리에styles.css파일을 만들고index.html의<head>태그 안에<link rel="stylesheet" href="/styles.css">를 추가하여 페이지 스타일을 꾸밀 수 있습니다.
다중 파일 업로드
multer를 사용하여 여러 파일을 한 번에 업로드할 수도 있습니다.
저장 공간을 구성합니다. (단일 파일 업로드와 동일)
다중 파일 업로드를 위한 엔드포인트를 생성합니다.
다중 파일 업로드를 위한 HTML 양식을 생성합니다. public/index.html 파일의 내용을 다음과 같이 수정합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<h1>Upload Multiple Files</h1>
<form action="/upload-multiple" method="POST" enctype="multipart/form-data">
<input type="file" name="files" multiple required>
<button type="submit">Upload</button>
</form>
</body>
</html>- Express 설정: HTTP 요청을 처리하기 위해 Express 애플리케이션을 설정합니다.
- multer 구성: 업로드된 파일을
uploads디렉터리에 고유한 파일 이름으로 저장하도록multer를 구성합니다. - 정적 파일: HTML 양식이 있는
public디렉터리에서 정적 파일을 제공합니다. - 업로드 엔드포인트: 다중 파일 업로드를 처리하는
/upload-multiple엔드포인트를 생성합니다.upload.array('files', 10)미들웨어는 파일 업로드를 처리하며, 한 번에 최대 10개의 파일을 업로드할 수 있도록 합니다. 업로드된 파일은req.files에서 사용할 수 있습니다.
인터넷을 통해 파일을 업로드하는 기능은 우리가 데이터를 공유하고 관리하는 방식에 혁명을 일으켰습니다. 최초의 파일 전송 프로토콜(FTP)은 1970년대 초에 개발되어 현대 파일 업로드 시스템의 기반을 마련했습니다. 오늘날 Node.js는 multer와 같은 미들웨어의 도움으로 파일 업로드를 매우 효율적이고 간단하게 처리합니다. 이 기술은 간단한 문서 관리 시스템에서 복잡한 클라우드 스토리지 서비스에 이르기까지 다양한 애플리케이션에 매우 중요합니다.
파일 업로드 유효성 검사
특정 유형의 파일만 업로드되도록 파일 유효성 검사를 추가할 수 있습니다.
모범 사례 (Best Practices)
- 파일 유형 유효성 검사: 원하는 파일 유형만 업로드되도록 파일 유효성 검사를 구현하여 잠재적인 보안 위험을 방지하고 애플리케이션의 무결성을 보장하세요.
- 파일 크기 제한: 성능 문제를 일으키고 서버 리소스를 고갈시킬 수 있는 지나치게 큰 파일이 업로드되는 것을 방지하기 위해 파일 크기 제한을 설정하세요.
- 보안 경로 사용: 업로드된 파일은 안전한 비공개 디렉터리에 저장하고, 고유한 파일 이름이나 해시 기반 파일 이름을 사용하여 충돌 및 잠재적인 보안 문제를 방지하세요.
- 정상적인 오류 처리: 지원되지 않는 파일 유형, 파일 크기 제한 초과 또는 네트워크 오류와 같이 파일 업로드 과정에서 발생하는 모든 문제를 관리하기 위해 견고한 오류 처리를 구현하세요.
multer 미들웨어를 사용하면 Node.js에서 다중 파일 업로드를 간단하게 처리할 수 있습니다. 위에서 설명한 단계를 따르면 여러 파일 업로드를 처리하고, 파일을 검증하며, 업로드된 파일을 효율적으로 관리하는 서버를 쉽게 설정할 수 있습니다. 이러한 개념을 이해하는 것은 강력한 파일 처리 기능이 필요한 웹 애플리케-이션을 구축하는 데 매우 중요하며, 파일 업로드를 안전하고 효과적으로 관리할 수 있도록 보장합니다.
파일 시스템 작업을 이용한 실용 예제
파일 시스템 작업은 파일과 디렉터리를 읽고, 쓰고, 관리하는 등 다양한 작업에 필수적입니다. 이 섹션에서는 Node.js를 사용하여 이러한 작업을 수행하는 방법을 보여주는 실용적인 예제를 제공합니다.
예제 1: 파일 읽고 쓰기
const fs = require('fs');
// 'input.txt'의 내용을 읽어 'output.txt'에 쓰기
fs.readFile('input.txt', 'utf8', (err, data) => {
if (err) {
console.error('파일 읽기 오류:', err);
return;
}
fs.writeFile('output.txt', data, (err) => {
if (err) {
console.error('파일 쓰기 오류:', err);
return;
}
console.log('파일 쓰기 성공');
});
});
console.log('파일 읽고 쓰는 중...');fs.readFile('input.txt', 'utf8', callback):input.txt의 내용을 비동기적으로 읽습니다.fs.writeFile('output.txt', data, callback):input.txt에서 읽은 데이터를output.txt에 비동기적으로 씁니다.- 프로그램은
'파일 읽고 쓰는 중...'을 즉시 기록하여 논블로킹 동작을 보여줍니다.
예제 2: 디렉터리 생성 및 삭제
const fs = require('fs');
// 'newDir'라는 새 디렉터리 생성
fs.mkdir('newDir', (err) => {
if (err) {
console.error('디렉터리 생성 오류:', err);
return;
}
console.log('디렉터리 생성 성공');
// 'newDir' 디렉터리 삭제
fs.rmdir('newDir', (err) => {
if (err) {
console.error('디렉터리 삭제 오류:', err);
return;
}
console.log('디렉터리 삭제 성공');
});
});
console.log('디렉터리 생성 및 삭제 중...');fs.mkdir('newDir', callback):newDir라는 이름의 새 디렉터리를 생성합니다.fs.rmdir('newDir', callback):newDir라는 이름의 디렉터리를 삭제합니다.- 프로그램은
'디렉터리 생성 및 삭제 중...'을 즉시 기록하여 논블로킹 동작을 보여줍니다.
예제 3: 파일 복사하기
const fs = require('fs');
// 소스 및 대상 경로
const source = 'source.txt';
const destination = 'destination.txt';
// 'source.txt'의 내용을 'destination.txt'로 복사
fs.copyFile(source, destination, (err) => {
if (err) {
console.error('파일 복사 오류:', err);
return;
}
console.log('파일 복사 성공');
});
console.log('파일 복사 중...');fs.copyFile(source, destination, callback):source.txt의 내용을destination.txt로 복사합니다.- 프로그램은
'파일 복사 중...'을 즉시 기록하여 논블로킹 동작을 보여줍니다.
예제 4: 디렉터리 내 파일 목록 보기
fs.readdir('.', callback): 현재 디렉터리(.으로 표시)의 내용을 읽습니다.- 프로그램은
'디렉터리 내 파일 목록을 가져오는 중...'을 즉시 기록하여 논블로킹 동작을 보여줍니다.
예제 5: 파일 변경 감시하기
fs.watch('watchedFile.txt', callback):watchedFile.txt의 모든 변경 사항(예: 수정, 삭제)을 감시합니다.- 프로그램은
'파일 변경 사항 감시 중...'을 즉시 기록하여 논블로킹 동작을 보여줍니다.