저자는 여성 공학자 협회를 Write for Donations 프로그램의 일환으로 기부 대상으로 선택했습니다.
소개
A CSV is a plain text file format for storing tabular data. The CSV file uses a comma delimiter to separate values in table cells, and a new line delineates where rows begin and end. Most spreadsheet programs and databases can export and import CSV files. Because CSV is a plain-text file, any programming language can parse and write to a CSV file. Node.js has many modules that can work with CSV files, such as node-csv
, fast-csv
, and papaparse
.
이 튜토리얼에서는 node-csv
모듈을 사용하여 Node.js 스트림을 통해 CSV 파일을 읽어서 메모리를 많이 사용하지 않고도 대규모 데이터 집합을 읽는 방법을 알아볼 것입니다. 프로그램을 수정하여 CSV 파일에서 구문 분석된 데이터를 SQLite 데이터베이스로 이동할 것입니다. 또한 데이터베이스에서 데이터를 검색하고 node-csv
로 구문 분석하여 Node.js 스트림을 사용하여 CSV 파일에 데이터를 묶음으로 쓸 것입니다.
DigitalOcean 앱 플랫폼을 사용하여 GitHub에서 Node 애플리케이션을 배포하세요. DigitalOcean이 앱의 확장에 집중하도록 하세요.
전제 조건
이 튜토리얼을 따르려면 다음이 필요합니다:
-
로컬 또는 서버 환경에 설치된 Node.js. Node.js 설치 및 로컬 개발 환경 생성을 따르세요.
-
로컬 또는 서버 환경에 SQLite가 설치되어 있어야 합니다. Ubuntu 20.04에서 단계 1을 따라 설치할 수 있습니다. Ubuntu 20.04에 SQLite 설치 및 사용 방법. SQLite 사용 방법에 대한 지식은 설치 가이드의 단계 2-7에서 학습할 수 있습니다.
-
Node.js 프로그램 작성에 익숙해야 합니다. Node.js에서 첫 번째 프로그램 작성 및 실행 방법을 참조하세요.
-
Node.js 스트림 사용에 익숙해야 합니다. Node.js에서 스트림을 사용하여 파일 작업하는 방법을 참조하세요.
단계 1 — 프로젝트 디렉토리 설정
이 섹션에서는 프로젝트 디렉토리를 만들고 애플리케이션을 위한 패키지를 다운로드합니다. 또한, 뉴질랜드에서의 국제 이민 데이터가 포함된 CSV 데이터 세트를 Stats NZ에서 다운로드합니다.
시작하려면 다음과 같이 csv_demo
라는 디렉토리를 만들고 해당 디렉토리로 이동하십시오:
다음으로, 다음 명령을 사용하여 디렉토리를 npm 프로젝트로 초기화하십시오: npm init
-y
옵션은 모든 프롬프트에 “예”라고 응답하도록 npm init
에 통지합니다. 이 명령은 언제든지 변경할 수 있는 기본 값이 있는 package.json
을 만듭니다.
npm 프로젝트로 디렉토리를 초기화한 후에는 이제 필요한 종속성인 node-csv
와 node-sqlite3
를 설치할 수 있습니다.
node-csv
를 설치하려면 다음 명령을 입력하십시오:
node-csv
모듈은 CSV 파일을 구문 분석하고 작성할 수 있는 모듈 집합입니다. 명령은 node-csv
패키지의 일부인 네 개의 모듈을 모두 설치합니다: csv-generate
, csv-parse
, csv-stringify
, 그리고 stream-transform
. CSV 파일을 구문 분석하기 위해 csv-parse
모듈을 사용하고 데이터를 CSV 파일에 쓰기 위해 csv-stringify
모듈을 사용할 것입니다.
다음으로, node-sqlite3
모듈을 설치하세요:
node-sqlite3
모듈을 사용하면 앱이 SQLite 데이터베이스와 상호 작용할 수 있습니다.
프로젝트에 패키지를 설치한 후에, wget
명령을 사용하여 뉴질랜드 이민 CSV 파일을 다운로드하세요:
다운로드한 CSV 파일의 이름이 너무 길다면 작업이 편리하도록 mv
명령을 사용하여 파일 이름을 짧게 바꿉니다:
새로운 CSV 파일 이름인 migration_data.csv
는 더 짧고 작업하기 쉽습니다.
nano
또는 선호하는 텍스트 편집기를 사용하여 파일을 엽니다:
열어보면 다음과 같은 내용이 있습니다:
year_month,month_of_release,passenger_type,direction,sex,age,estimate,standard_error,status
2001-01,2020-09,Long-term migrant,Arrivals,Female,0-4 years,344,0,Final
2001-01,2020-09,Long-term migrant,Arrivals,Male,0-4 years,341,0,Final
...
첫 번째 줄에는 열 이름이 있고, 그 이후의 모든 줄에는 각 열에 해당하는 데이터가 있습니다. 각 데이터 조각은 쉼표로 구분됩니다. 이 문자는 필드를 구분하는 구분 기호로 알려져 있습니다. 쉼표를 사용하는 것에만 국한되지 않습니다. 다른 인기 있는 구분 기호로는 콜론(:
), 세미콜론(;
), 그리고 탭(\t
)이 있습니다. 대부분의 모듈이 파일을 구문 분석하는 데 필요한 구분 기호를 알아야 합니다.
파일을 검토하고 구분 기호를 식별한 후에는 CTRL+X
를 사용하여 migration_data.csv
파일을 종료하십시오.
이제 프로젝트에 필요한 종속성을 설치했습니다. 다음 섹션에서는 CSV 파일을 읽어올 것입니다.
단계 2 — CSV 파일 읽기
이 섹션에서는 CSV 파일을 읽고 그 내용을 콘솔에 로그하는 데 node-csv
를 사용합니다. 데이터를 CSV 파일에서 읽고 읽기 가능한 스트림을 생성하기 위해 fs
모듈의 createReadStream()
메서드를 사용합니다. 그런 다음 데이터 조각을 구문 분석하는 csv-parse
모듈로 초기화된 다른 스트림에 스트림을 파이핑합니다. 데이터 조각이 구문 분석된 후에는 콘솔에 로그를 남길 수 있습니다.
선호하는 편집기에서 readCSV.js
파일을 만들고 엽니다:
readCSV.js
파일에서 다음 줄을 추가하여 fs
및 csv-parse
모듈을 가져옵니다:
첫 번째 줄에서는 fs
변수를 정의하고 Node.js가 모듈을 가져올 때 fs
객체를 할당합니다.
두 번째 줄에서 require()
메서드로 반환된 객체에서 parse
메서드를 해체 구문을 사용하여 parse
변수에 추출합니다.
CSV 파일을 읽기 위해 다음 줄을 추가합니다:
fs
모듈의 createReadStream()
메서드는 여기서 사용할 migration_data.csv
와 같은 파일 이름의 인수를 허용합니다. 그런 다음, 이는 큰 파일을 가져와 작은 조각으로 나누는 읽을 수 있는 스트림을 만듭니다. 읽을 수 있는 스트림을 사용하면 데이터를 읽을 수만 있고 쓸 수는 없습니다.
읽을 수 있는 스트림을 생성한 후, Node의 pipe()
메서드는 읽을 수 있는 스트림에서 데이터 조각을 다른 스트림으로 전달합니다. 두 번째 스트림은 pipe()
메서드 내에서 csv-parse
모듈의 parse()
메서드가 호출될 때 생성됩니다. csv-parse
모듈은 데이터 조각을 가져와 다른 형식으로 변환하는 변환 스트림 (읽을 수 있고 쓸 수 있는 스트림)을 구현합니다. 예를 들어, 이 메서드는 2001-01,2020-09,장기 이주자,도착,여성,0-4세,344
와 같은 조각을 배열로 변환합니다.
parse()
메서드는 속성을 허용하는 객체를 취합니다. 그런 다음, 객체는 메서드가 구문 분석할 데이터에 대해 추가 정보를 구성하고 제공합니다. 객체는 다음과 같은 속성을 사용합니다:
-
delimiter
은 행에서 각 필드를 구분하는 문자를 정의합니다.,
값은 쉼표가 필드를 구분하는 것을 구문 분석기에 알려줍니다. -
from_line
은 구문 분석기가 행을 구문 분석을 시작해야 하는 줄을 정의합니다. 값2
로 설정하면 구문 분석기가 1번째 줄을 건너뛰고 2번째 줄부터 시작합니다. 데이터를 나중에 데이터베이스에 삽입할 예정이므로, 이 속성은 데이터베이스의 첫 번째 행에 열 이름을 삽입하지 않도록 도와줍니다.
다음으로, Node.js의 on()
메소드를 사용하여 스트리밍 이벤트를 첨부합니다. 스트리밍 이벤트를 통해 특정 이벤트가 발생하면 메소드가 데이터 청크를 소비할 수 있습니다. data
이벤트는 parse()
메소드에서 변환된 데이터를 사용할 준비가 되면 트리거됩니다. 데이터에 액세스하기 위해 on()
메소드에 콜백을 전달하고, 콜백에는 row
라는 매개변수가 있습니다. row
매개변수는 배열로 변환된 데이터 청크입니다. 콜백 내에서는 console.log()
메소드를 사용하여 데이터를 콘솔에 기록합니다.
파일을 실행하기 전에 스트림 이벤트를 추가하십시오. 이러한 스트림 이벤트는 오류를 처리하고 CSV 파일의 모든 데이터가 소비될 때 콘솔에 성공 메시지를 기록합니다.
여전히 readCSV.js
파일에서 다음과 같이 코드를 추가하십시오:
end
이벤트는 CSV 파일의 모든 데이터를 읽은 경우 발생합니다. 그런 경우에 콜백이 호출되고 완료되었다는 메시지를 기록합니다.
CSV 데이터를 읽고 구문 분석하는 동안 어디서든 오류가 발생하면 error
이벤트가 발생하고, 콜백이 호출되어 콘솔에 오류 메시지를 기록합니다.
이제 전체 파일은 다음과 같아야 합니다:
readCSV.js
파일을 저장하고 나가려면 CTRL+X
를 사용하십시오.
다음으로, node
명령을 사용하여 파일을 실행하십시오:
출력은 다음과 유사할 것입니다 (간결성을 위해 편집함):
Output[
'2001-01',
'2020-09',
'Long-term migrant',
'Arrivals',
'Female',
'0-4 years',
'344',
'0',
'Final'
]
...
[
'2021-09',
...
'70',
'Provisional'
]
finished
CSV 파일의 모든 행이 csv-parse
변환 스트림을 사용하여 배열로 변환되었습니다. 스트림에서 청크가 수신될 때마다 로깅되기 때문에 데이터가 한 번에 표시되는 것이 아니라 다운로드되는 것처럼 보입니다.
이 단계에서는 CSV 파일에서 데이터를 읽어 배열로 변환했습니다. 다음으로, 데이터베이스에 CSV 파일에서 데이터를 삽입하겠습니다.
단계 3 — 데이터베이스에 데이터 삽입하기
Node.js를 사용하여 CSV 파일에서 데이터를 데이터베이스에 삽입하는 것은 데이터를 처리, 정리 또는 개선하기 위해 사용할 수 있는 방대한 모듈 라이브러리에 액세스할 수 있습니다.
이 섹션에서는 node-sqlite3
모듈을 사용하여 SQLite 데이터베이스와의 연결을 설정합니다. 그런 다음 데이터베이스에서 테이블을 생성하고 readCSV.js
파일을 복사한 다음 CSV 파일에서 읽은 모든 데이터를 데이터베이스에 삽입하도록 수정합니다.
편집기에서 db.js
파일을 만들고 엽니다:
db.js
파일에 다음 줄을 추가하여 fs
및 node-sqlite3
모듈을 가져옵니다:
세 번째 줄에서는 SQLite 데이터베이스의 경로를 정의하고 이를 filepath
변수에 저장합니다. 데이터베이스 파일은 아직 존재하지 않지만 node-sqlite3
에서 데이터베이스와의 연결을 설정하는 데 필요합니다.
동일한 파일에 다음 줄을 추가하여 Node.js를 SQLite 데이터베이스에 연결합니다:
여기서는 데이터베이스에 연결하기 위해 connectToDatabase()
라는 함수를 정의합니다. 함수 내에서는 fs
모듈의 existsSync()
메서드를 if
문에서 호출하여 프로젝트 디렉토리에 데이터베이스 파일이 있는지 확인합니다. if
조건이 true
로 평가되면, 데이터베이스 파일 경로를 사용하여 node-sqlite3
모듈의 SQLite의 Database()
클래스를 인스턴스화합니다. 연결이 설정되면 함수는 연결 객체를 반환하고 종료합니다.
그러나 if
문이 false
로 평가되면(데이터베이스 파일이 없는 경우), 실행은 else
블록으로 건너뜁니다. else
블록에서는 두 개의 인수를 사용하여 Database()
클래스를 인스턴스화합니다. 첫 번째 인수는 SQLite 데이터베이스 파일의 경로이며, ./population.db
입니다. 두 번째 인수는 데이터베이스와의 연결이 성공적으로 설정되었거나 오류가 발생했을 때 자동으로 호출될 콜백입니다. 콜백은 error
객체를 매개변수로 사용하며, 연결이 성공적인 경우 null
입니다. 콜백 내에서는 if
문이 error
객체가 설정되어 있는지 확인합니다. 이 값이 true
로 평가되면 콜백은 오류 메시지를 로깅하고 반환합니다. 이 값이 false
로 평가되면 연결이 설정되었음을 확인하는 성공 메시지를 로깅합니다.
현재, if
및 else
블록은 연결 객체를 설정합니다. 데이터베이스 클래스를 호출할 때 else
블록에서 콜백을 전달하여 데이터베이스에 테이블을 만들지만, 데이터베이스 파일이 이미 존재하는 경우에만 함수가 if
블록을 실행하여 데이터베이스에 연결하고 연결 객체를 반환합니다.
데이터베이스 파일이 없는 경우 테이블을 만들려면 다음 코드를 추가하십시오:
이제 connectToDatabase()
에서 db
변수에 저장된 연결 객체를 인수로 받는 createTable()
함수를 호출합니다.
connectToDatabase()
함수 외부에서는 연결 객체 db
를 매개변수로 사용하는 createTable()
함수를 정의합니다. db
연결 객체에서 exec()
메소드를 호출하고 SQL 문을 인수로 전달합니다. SQL 문은 7개의 열이 있는 migration
이라는 테이블을 생성합니다. 열 이름은 migration_data.csv
파일의 헤딩과 일치합니다.
마지막으로, connectToDatabase()
함수를 호출하고 함수에 의해 반환된 연결 객체를 내보내어 다른 파일에서 재사용할 수 있도록 합니다.
db.js
파일을 저장하고 종료하십시오.
데이터베이스 연결이 설정된 상태에서 이제 csv-parse
모듈이 파싱한 행을 데이터베이스에 삽입하기 위해 readCSV.js
파일을 복사하고 수정할 것입니다.
다음 명령을 사용하여 파일을 복사하고 이름을 insertData.js
로 변경하십시오:
에디터에서 insertData.js
파일을 엽니다:
강조된 코드를 추가하십시오:
세 번째 줄에서는 db.js
파일에서 연결 객체를 가져와 변수 db
에 저장합니다.
fs
모듈 스트림에 연결된 data
이벤트 콜백 내에서 연결 객체에 serialize()
메소드를 호출합니다. 이 메소드는 SQL 문이 다른 SQL 문의 실행을 마칠 때까지 기다리도록합니다. 이는 데이터베이스 경쟁 조건을 방지하는 데 도움이되는데, 여기서 시스템은 동시에 경쟁하는 작업을 실행합니다.
serialize()
메소드는 콜백을 취합니다. 콜백 내에서는 db
연결 객체에 run
메소드를 호출합니다. 이 메소드는 세 개의 인수를 수용합니다:
-
첫 번째 인수는 SQLite 데이터베이스에서 전달되고 실행 될 SQL 문입니다.
run()
메소드는 결과를 반환하지 않는 SQL 문만 수용합니다.INSERT INTO migration VALUES (?, ..., ?
문은 테이블migration
에 행을 삽입하며,?
는 나중에run()
메소드의 두 번째 인수에 있는 값으로 대체됩니다. -
두 번째 인자는 배열
[row[0], ... row[5], row[6]]
입니다. 이전 섹션에서parse()
메서드는 읽기 가능한 스트림에서 데이터 청크를 받아와 배열로 변환합니다. 데이터가 배열로 수신되기 때문에 각 필드 값을 가져오려면[row[1], ..., row[6]]
과 같이 배열 인덱스를 사용하여 액세스해야 합니다. -
세 번째 인자는 데이터가 삽입되었거나 오류가 발생했을 때 실행되는 콜백입니다. 콜백은 오류가 발생했는지 확인하고 오류 메시지를 기록합니다. 오류가 없으면 함수는 콘솔에 성공 메시지를 기록하여 행이 삽입되었음을 알려줍니다. 이때 id와 함께입니다.
마지막으로, 파일에서 end
와 error
이벤트를 제거하십시오. node-sqlite3
메서드의 비동기적 특성으로 인해, end
와 error
이벤트는 데이터가 데이터베이스에 삽입되기 전에 실행되므로 더 이상 필요하지 않습니다.
파일을 저장하고 나가십시오.
node
를 사용하여 insertData.js
파일을 실행하십시오:
시스템에 따라 시간이 걸릴 수 있지만, node
가 아래의 출력을 반환해야 합니다:
OutputConnected to the database successfully
Inserted a row with the id: 1
Inserted a row with the id: 2
...
Inserted a row with the id: 44308
Inserted a row with the id: 44309
Inserted a row with the id: 44310
특히 메시지, 특히 ID는 CSV 파일의 행이 데이터베이스에 저장되었음을 증명합니다.
이제 CSV 파일을 읽고 해당 내용을 데이터베이스에 삽입할 수 있습니다. 다음으로 CSV 파일을 작성할 것입니다.
단계 4 — CSV 파일 작성
이 섹션에서는 스트림을 사용하여 데이터베이스에서 데이터를 검색하고 CSV 파일에 쓸 것입니다.
편집기에서 writeCSV.js
를 만들고 엽니다:
writeCSV.js
파일에서 다음 줄을 추가하여 fs
및 csv-stringify
모듈 및 db.js
에서 데이터베이스 연결 개체를 가져옵니다:
csv-stringify
모듈은 객체 또는 배열에서 데이터를 CSV 텍스트 형식으로 변환합니다.
다음으로, 데이터를 쓸 CSV 파일의 이름을 포함하는 변수와 데이터를 쓸 쓰기 가능한 스트림을 정의하는 다음 줄을 추가하십시오:
createWriteStream
메서드는 데이터 스트림을 쓸 파일의 파일 이름을 인수로 취합니다. 이 파일 이름은 filename
변수에 저장된 saved_from_db.csv
파일 이름입니다.
네 번째 줄에서는 CSV 데이터의 헤더 이름을 포함하는 배열을 저장하는 columns
변수를 정의합니다. 이러한 헤더는 파일에 데이터를 쓰기 시작할 때 CSV 파일의 첫 번째 줄에 작성됩니다.
여전히 writeCSV.js
파일에서, 데이터베이스에서 데이터를 검색하고 CSV 파일의 각 행을 작성하는 다음 줄을 추가하십시오:
먼저 객체를 인수로 사용하여 stringify
메서드를 호출하면 변환 스트림이 생성됩니다. 변환 스트림은 데이터를 객체에서 CSV 텍스트로 변환합니다. stringify()
메서드에 전달된 객체에는 두 개의 속성이 있습니다:
-
header
는 부울 값을 허용하고 부울 값이true
로 설정되면 헤더를 생성합니다. -
columns
은header
옵션이true
로 설정된 경우 CSV 파일의 첫 번째 줄에 작성될 열 이름을 포함하는 배열을 가져옵니다.
다음으로, 두 개의 인자를 사용하여 db
연결 객체에서 each()
메서드를 호출합니다. 첫 번째 인자는 데이터베이스에서 한 번에 한 행씩 검색하는 SQL 문 select * from migration
입니다. 두 번째 인자는 데이터베이스에서 행을 검색할 때마다 호출되는 콜백입니다. 이 콜백은 두 개의 매개변수를 사용합니다: error
객체와 데이터베이스의 단일 행에서 검색된 데이터를 포함하는 row
객체입니다. 콜백 내에서 error
객체가 설정되었는지 if
문으로 확인합니다. 조건이 true
로 평가되면 console.log()
메서드를 사용하여 콘솔에 오류 메시지를 로그로 기록합니다. 오류가 없는 경우, stringifier
에 write()
메서드를 호출하여 데이터를 stringifier
변환 스트림에 기록합니다.
each()
메서드의 반복이 완료되면 stringifier
스트림에서 pipe()
메서드를 호출하여 데이터를 조각으로 보내고 writableStream
에 기록을 시작합니다. 쓰기 가능한 스트림은 saved_from_db.csv
파일에 데이터의 각 조각을 저장합니다. 모든 데이터가 파일에 기록된 후에는 console.log()
가 성공 메시지를 기록합니다.
이제 완전한 파일은 다음과 같이 보일 것입니다:
파일을 저장하고 닫은 다음 터미널에서 writeCSV.js
파일을 실행하십시오:
다음 출력을 받게 될 것입니다:
OutputFinished writing data
데이터가 기록되었는지 확인하려면 cat
명령을 사용하여 파일 내용을 검사하십시오:
cat
파일에 작성된 모든 행을 반환합니다 (간결성을 위해 편집됨):
Outputyear_month,month_of_release,passenger_type,direction,sex,age,estimate
2001-01,2020-09,Long-term migrant,Arrivals,Female,0-4 years,344
2001-01,2020-09,Long-term migrant,Arrivals,Male,0-4 years,341
2001-01,2020-09,Long-term migrant,Arrivals,Female,10-14 years,
...
이제 스트림을 사용하여 데이터베이스에서 데이터를 검색하고 각 행을 CSV 파일에 작성할 수 있습니다.
결론
이 기사에서는 node-csv
및 node-sqlite3
모듈을 사용하여 CSV 파일을 읽고 해당 데이터를 데이터베이스에 삽입했습니다. 그런 다음 데이터베이스에서 데이터를 검색하여 다른 CSV 파일에 씁니다.
이제 CSV 파일을 읽고 쓸 수 있습니다. 다음 단계로, 메모리 효율적인 스트림을 사용하여 대규모 CSV 데이터 세트를 처리하거나 event-stream
과 같은 패키지를 살펴보세요. 이 패키지는 스트림 처리를 훨씬 쉽게 만들어줍니다.
node-csv
에 대해 자세히 알아보려면, 문서를 방문하세요. CSV 프로젝트 – Node.js CSV 패키지. node-sqlite3
에 대해 더 알아보려면 GitHub 문서를 방문하세요. Node.js 기술을 계속 키우려면 Node.js에서 코딩하는 방법 시리즈를 참조하세요.