Server/Node.js (Express)

[Node.js] Express와 CSV를 이용해 조 편성 애플리케이션 만들기

ooeunz 2019. 10. 17. 14:58
반응형

이번 포스팅에서는 express를 이용해서 조원들을 검색해보고, 조를 섞어보는 간단한 애플리케이션을 만들어보도록 하겠다.

완성된 프로젝트의 구조는 아래와 같다.

 

public 디렉토리에 csv라는 디렉토리를 만들어주고 해당 파일에 member와 group이라는 이름의 csv파일을 만들어주도록 한다.

 

group.csv(좌), member.csv(우)

※ CSV란?

Excel과 DB와 호환되는 텍스트 형식이다. 첫 번째 줄에는 필드 이름이 들어가고, 2번째 줄부터 각 row에 해당하는 값이 들어 있다.

 

우리는 이제부터 여기있는 csv파일을 데이터로 사용하여 데이터를 불러와 조회 및 조작해보도록 하겠다.

먼저 프로젝트 설계를 해보도록하겠다.

서버를 작동시켰을 때, 

 

localhost:3000/api/group
group전체의 정보를 조회한다.

 

localhost:3000/api/group/:groupIdx

groupIdx에 들어간 숫자에 맞는 조의 구성원들을 조회한다.

 

localhost:3000/api/mixer

group구성원들의 조를 섞고, 전체 정보를 조회한다.

 

 

먼저 routes 디렉토리안에 api 디렉토리를 만들고, 그 안에 index.js와 group.js, mixer.js를 만든다.

 

그 후, route/index.js에 4번 라인과 같이 api 디렉토리로 라우팅을 해주도록 한다.

그리고 역시 routes/api/index.js에서 4번, 5번 라인과 같이 group.js와 mixer.js로 라우팅을 해주도록 한다.

 

라우팅이 끝나면 이제 본격적으로 로직을 만들어 줄 것이다.

group.js는 csv파일을 json으로 변환 후 불러온 후, 그 결과를 조회해주는 기능과, get방식 파라미터로 넘어온 숫자에 해당하는 조원들을 조회할 것이다.

 

먼저, csv파일을 json으로 변환하기 위한 패키지 csvtojson를 설치해 주도록 한다.

 

npm install csvtojson

(만약 mac os에서 permission error가 난다면 앞에 sudo를 붙여 관리자 권한으로 install 하도록 하자)

 

 

routes/api/group.js

2번 : csv 변수에 csvtojson 패키지를 넣어준다.

5, 6번 : 매번 path를 입력하여 주기보다 변수에 path 값을 저장해두어 가독성을 높인다.

 

전체를 조회하는 코드는 간단하다.

30번 : csv.fromFile()을 이용해서 비동기로 member.csv에 저장된 member의 리스트를 json 형식으로 member 변수를 초기화해준다.

32번 : err를 처리해주기 위한 코드

33번 : err가 발생하지 않는다면, res.send(member);를 통해 브라우저에 member 전체를 출력한다.

 

이번엔 localhost:3000/api/group/4 와 같이 코드가 들어온다면, 

위의 이미지와 같이 그 조의 인원들을 조회할 수 있도록 하는 코드를 작성해보도록 하겠다.

 

11~14번 : 전체를 조회하는 코드와 의미가 같다.

15번 : 파라미터로 들어오는 값을 IDX 상수 값에 초기화해준다.

17번 : group.csv 파일을 조회하여, IDX에 해당하는 조의 이름을 groupNum 변수에 넣어준다.

19번 : 변수에 member전체에서 groupIdx가 파라미터로 받아온 IDX값과 같은 명단을 filter를 통해 걸러내고, 그 객체의 name에 해당하는 값들을 map을 통해 일괄적으로 people 변수에 넣어준다.

21번 : group.csv에서 가져온 조의 이름과, member.csv에서 받아온 조원들의 브라우저에 리스트를 출력한다.

 

※ group.js 전체 코드

const express = require('express');
const csv = require('csvtojson');
const router = express.Router();

const memberFilePath = __dirname + '/../../public/csv/member.csv'
const groupFilePath = __dirname + '/../../public/csv/group.csv'

// find group member
router.get('/:groupIdx', async (req, res) => {
    try {
        const member = await csv().fromFile(memberFilePath);
        const group = await csv().fromFile(groupFilePath);

        if (!member || !group) console.log(`file read err : ${err}`);   // debug     

        IDX = req.params.groupIdx;
        let groupNum = group[String(Number(IDX - 1))].name;

        let people = member.filter(it => it.groupIdx === IDX).map(it => it.name);
        
        res.send(`${groupNum} : ${people}`);
    } catch (err) {
        console.log(`err with csv : ${err}`);
    }
});

// find all
router.get('/', async (req, res) => {
    try {
     const member = await csv().fromFile(memberFilePath);

        if (!member) console.log(`file read err : ${err}`); // debug   
        else res.send(member);  // running

    } catch (err) {
        console.log(`file read err : ${err}`);
    }
});

module.exports = router;

 

routes/api/mixer.js

이번엔 전체 member의 조를 랜덤으로 섞어준 다음 그것을 출력해보도록 하겠다.

이번에는 몇 가지 다른 패키지가 필요하다.

json2csv : json을 csv로 변환해주는 패키지

csv-stringify : 레코드를 csv 텍스트로 변환해주는 패키지

 

npm install json2csv

npm install csv-stringify

 

를 통해 install 해준 후 위의 이미지와 같이 패키지를 불러온다. 위의 두 개의 패키지 외에도 fs패키지가 있는데, fs는 기본적으로 내장되어있기 때문에 따로 install 하지 않아도 된다.

 

다음으로 랜덤 알고리즘을 작성해준다. 랜덤 알고리즘은 코드를 보면 이해할 수 있을 거라 믿고 설명하지 않도록 하겠다.

랜덤 알고리즘

마지막으로 랜덤 함수와 불러온 패키지들을 이용해서 조원들을 섞은 후 출력하는 코드를 작성해보겠다.

 

28, 29번 : 에러 처리 코드. 클라이언트와 서버에 모두 에러를 알리기 위해 console.log와 res.send 모두에 err를 출력해준다.

32번 : arr이라는 배열을 만들어서 그 안에 조들의 리스트를 입력해준다.

33번 : 랜덤 함수를 사용해 조를 섞어준다.

35, 37번 : member에 들어있는 객체에 차례대로 섞인 조를 입력해준다.

43번 : csv에 다시 입력하기 위해 csv-stringify 패키지를 이용해 csv 텍스트로 변환해 준다.

46번 : member.csv에 뒤섞인 조를 저장해준다.

55번 : 뒤섞인 member 전체를 브라우저에 출력한다.

 

※ mixer.js 전체코드

const express = require('express');
const router = express.Router();

const csv = require('csvtojson');
const json2csv = require('json2csv');
const fs = require('fs');
const stringify = require('csv-stringify');

// path
const memberFilePath = __dirname + '/../../public/csv/member.csv'

// function
const shuffle = (arr) => {
    let delta;
    for (let i = arr.length; i; i -= 1) {
        delta = Math.floor(Math.random() * i);
        [arr[delta], arr[i-1]] = [arr[i-1], arr[delta]];
    }
    return arr;
}

router.get('/', async (req, res) => {
    try {
        const member =  await csv().fromFile(memberFilePath);

        // csv to json
        if (!member) {
            console.log(`member scv file is empty`);
            res.send(`member scv file is empty`);
        } else {
            // running
            let arr = member.map(n => n.groupIdx);
            arr = shuffle(arr);

            for (let i in member) {
                member[i].groupIdx = arr[i];
            }  
            
            // json to csv
            try {
                const resultCsv = json2csv.parse(member);
                // res.send(resultCsv);
                stringify(member, {header: true, columns: ['name','groupIdx']}, (err, output) => {
                    if (err) throw err;
                    else {
                        fs.writeFile(memberFilePath, output, (err) => {
                            if (err) throw err;
                        });const express = require('express');
const router = express.Router();

const csv = require('csvtojson');
const json2csv = require('json2csv');
const fs = require('fs');
const stringify = require('csv-stringify');

// path
const memberFilePath = __dirname + '/../../public/csv/member.csv'

// function
const shuffle = (arr) => {
    let delta;
    for (let i = arr.length; i; i -= 1) {
        delta = Math.floor(Math.random() * i);
        [arr[delta], arr[i-1]] = [arr[i-1], arr[delta]];
    }
    return arr;
}

router.get('/', async (req, res) => {
    try {
        const member =  await csv().fromFile(memberFilePath);

        // csv to json
        if (!member) {
            console.log(`member scv file is empty`);
            res.send(`member scv file is empty`);
        } else {
            // running
            let arr = member.map(n => n.groupIdx);
            arr = shuffle(arr);

            for (let i in member) {
                member[i].groupIdx = arr[i];
            }  
            
            // json to csv
            try {
                const resultCsv = json2csv.parse(member);
                // res.send(resultCsv);
                stringify(member, {header: true, columns: ['name','groupIdx']}, (err, output) => {
                    if (err) throw err;
                    else {
                        fs.writeFile(memberFilePath, output, (err) => {
                            if (err) throw err;
                        });
                    }
                })   
            } catch {
                console.log(`fs.writeFile err : ${err}`);
                res.send(`fs.writeFile err : ${err}`);
            }
            res.send(member);
        }
    } catch {
        console.log(`err with csv : ${err}`);
        res.send(`err with csv : ${err}`);      
    }
});


module.exports = router;
                    }
                })   
            } catch {
                console.log(`fs.writeFile err : ${err}`);
                res.send(`fs.writeFile err : ${err}`);
            }
            res.send(member);
        }
    } catch {
        console.log(`err with csv : ${err}`);
        res.send(`err with csv : ${err}`);      
    }
});


module.exports = router;

 

마지막으로 npm start를 통해 결과 값을 확인할 수 있다. :)

반응형