[nodejs] 8. csv로 데이터 저장, 게시판 CRUD API 만들기

2024. 2. 11. 14:17cs및 소프트스킬/nodejs

728x90
반응형
SMALL

지난 포스팅에서는 nodejs를 이용하여 csv로 데이터를 읽어들이고, 잘 저장되어지는지의 대해 csv -> json, json -> csv로 변환하면서 결과적으로 잘 적용 되는지의 대해 로직과 테스트까지 작업을 해 보았다.

 

이번 시간에는 ver07에서 기반으로 하여 추가 개발과 Back-End API 개발을 위한 작업을 진행하도록 한다.

 

 

ver08 버전 참조

https://github.com/BerkleyLim/basic_nodejs

 

GitHub - BerkleyLim/basic_nodejs: 이 프로젝트는 node.js 입문을 위한 프로젝트 중 하나입니다.

이 프로젝트는 node.js 입문을 위한 프로젝트 중 하나입니다. Contribute to BerkleyLim/basic_nodejs development by creating an account on GitHub.

github.com

 

 

 

 

0. 시작하기 전

package.json

{
  "name": "ver08",
  "version": "1.0.0",
  "description": "ver08",
  "main": "src/main/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node src/main/index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.20.2",
    "cors": "^2.8.5",
    "csvtojson": "^2.0.10",
    "express": "^4.18.2"
  }
}

 

 

 

1. 사용 API

[읽기]

- API : /readBoard

- Method : GET

- Request Parameter : {}

- Response Parameter : [

  {

    "bno": "1",

    "title": "제목1",

    "contents": "내용1",

  }, .......

]

 

[쓰기]

- API : /createBoard

- Method : POST

- Request Parameter : {

  "title":"제목2",

  "contents:"제목2",

}

- Response Parameter : [

  {

    "bno": "1",

    "title": "제목1",

    "contents": "내용1",

  }, .......

  {

    "bno": "4",

    "title": "제목2",

    "contents": "내용2",

  } 

]

 

[삭제]

- API : /createBoard

- Method : POST

- Request Parameter : {

  "bno": "1"

}

- Response Parameter : [

  // bno 제거

  {

    "bno": "2",

    "title": "제목2",

    "contents": "내용2",

  }, .......

]

 

 

[수정]

- API : /createBoard

- Method : POST

- Request Parameter : {

  "bno": "1",

  "title": "제목변경",

  "contents": "내용변경"

}

- Response Parameter : [

  {

    "bno": "1",

    "title": "제목변경",

    "contents": "내용변경",

  }, .......

]

 

 

2. csv 파일

src/resource.csv/main.csv

bno,title,contents
1,홍길동 자기소개,안녕하세요 만나서 반갑습니다. 저는 홍길동 입니다.
2,Berkley 자기소개,안녕하세요 만나서 반갑습니다. 저는 Berkley 입니다.
3,Sara 자기소개,안녕하세요 만나서 반갑습니다. 저는 Sara 입니다.

 

 

 

3. csv -> json 변환, json -> csv 변환 관련 로직

const fs = require('fs');

// 1) csv 가공하는 단 - 리스트 출력 - json 형식 반환
exports.processCsvToJsonList = (path) => {
  let csv = fs.readFileSync(path).toString('utf-8');

  // let csvData = csv.replaceAll('\r', '').replaceAll(' ', '').split("\n");
  let csvData = csv.replaceAll('\r', '').split("\n");
  let key = csvData[0].split(',');

  let dataSet = {}
  for (let i = 0; i < key.length; i++) {
    dataSet = {
      ...dataSet,
      [key[i]]: ''
    }
  }

  let csvToJSON = new Array();

  for (let gagongDataIndex = 1; gagongDataIndex < csvData.length; gagongDataIndex++) {
    let processData = csvData[gagongDataIndex].split(',');
    for (let i = 0; i < key.length; i++) {
      dataSet = {
        ...dataSet,
        [key[i]]: processData[i]
      }
    }
    
    csvToJSON.push(dataSet)
  }

  console.log(csvToJSON)

  return csvToJSON
}

// 2) csv 가공하는 단 - 객체 출력 - json 형식 반환
exports.processCsvToJsonObject = (path) => {
  let csv = fs.readFileSync(path).toString('utf-8');

  let csvData = csv.replaceAll('\r', '').replaceAll(' ', '').split("\n");
  let key = csvData[0].split(',');

  let dataSet = {}
  for (let i = 0; i < key.length; i++) {
    dataSet = {
      ...dataSet,
      [key[i]]: ''
    }
  }

  return dataSet;
}


// 3) json -> csv로 변환하여 저장
exports.jsonToCSV = (json_data) => {

  // 1-1. json 데이터 취득
  const json_array = json_data;
  // 1-2. json데이터를 문자열(string)로 넣은 경우, JSON 배열 객체로 만들기 위해 아래 코드 사용
  // const json_array = JSON.parse(json_data);
  

  // 2. CSV 문자열 변수 선언: json을 csv로 변환한 문자열이 담길 변수
  let csv_string = '';


  // 3. 제목 추출: json_array의 첫번째 요소(객체)에서 제목(머릿글)으로 사용할 키값을 추출
  const titles = Object.keys(json_array[0]);

  
  // 4. CSV문자열에 제목 삽입: 각 제목은 컴마로 구분, 마지막 제목은 줄바꿈 추가
  titles.forEach((title, index)=>{
      csv_string += (index !== titles.length-1 ? `${title},` : `${title}\r\n`);
  });


  // 5. 내용 추출: json_array의 모든 요소를 순회하며 '내용' 추출
  json_array.forEach((content, index)=>{
      
      let row = ''; // 각 인덱스에 해당하는 '내용'을 담을 행

      for(let title in content){ // for in 문은 객체의 키값만 추출하여 순회함.
          // 행에 '내용' 할당: 각 내용 앞에 컴마를 삽입하여 구분, 첫번째 내용은 앞에 컴마X
          row += (row === '' ? `${content[title]}` : `,${content[title]}`);
      }

      // CSV 문자열에 '내용' 행 삽입: 뒤에 줄바꿈(\r\n) 추가, 마지막 행은 줄바꿈X
      csv_string += (index !== json_array.length-1 ? `${row}\r\n`: `${row}`);
  })

  console.log(csv_string)
  // 6. CSV 문자열 반환: 최종 결과물(string)
  return csv_string;
}

 

 

 

4. CRUD 로직 수행

src/main/controller/board/BoardController.js

const fs = require('fs');

const {processCsvToJsonList, jsonToCSV} = require('../../service/csvLogicProcessService')

// 전체 읽기
exports.readBoard = (request, response, next) => {
  try {
    const main = processCsvToJsonList('src/resource/csv/main.csv')
    // 데이터 가공 작업 끝

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

    // csv로 변환 - 파일 테스트용
    // const csvResult = jsonToCSV(main);
    // fs.writeFile('src/resource/csv/test.csv', csvResult, err => {
    //   if (err) {
    //     console.log(err.message);
    
    //     throw err;
    //   }
    
    //   console.log('data written to file');
    // });

    response.json(main);
  } catch (err) {
    console.log(`err with csv : ${err}`);
  }
};

// 생성
exports.createBoard = (request, response, next) => {
  try {
    let params = request?.body;

    if (!!!params?.title || !!!params?.contents) {
      response.json("데이터 누락");
      return;
    }

    let main = processCsvToJsonList('src/resource/csv/main.csv')
    // 데이터 가공 작업 끝

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

    console.log(params)


    
    // 가장 마지막 uno의 +1 더하여 추가
    let lastUno = (parseInt(main[main.length-1].bno)+1).toString();

    // 배열 생성할 데이터 형식 추가
    const createMainInterface = {
      bno: lastUno,
      title: params?.title,
      contents: params?.contents
    }
    main.push(createMainInterface)

    // csv로 변환
    const csvResult = jsonToCSV(main);
    fs.writeFile('src/resource/csv/test.csv', csvResult, err => {
    // fs.writeFile('src/resource/csv/main.csv', csvResult, err => {
      if (err) {
        console.log(err.message);
    
        throw err;
      }
    
      console.log('data written to file');
    });

    response.json(main);
  } catch (err) {
    console.log(`err with csv : ${err}`);
  }
};

  
// 삭제
exports.deleteBoard = (request, response, next) => {
  try {
    let params = request?.body;

    if (!!!params?.bno) {
      response.json("데이터 누락");
      return;
    }

    let main = processCsvToJsonList('src/resource/csv/main.csv')
    // 데이터 가공 작업 끝

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

    console.log(params)

    // 삭제 수행
    main = main.filter(data => data?.bno != params?.bno)

    // csv로 변환
    const csvResult = jsonToCSV(main);
    fs.writeFile('src/resource/csv/test.csv', csvResult, err => {
    // fs.writeFile('src/resource/csv/main.csv', csvResult, err => {
      if (err) {
        console.log(err.message);
    
        throw err;
      }
    
      console.log('data written to file');
    });

    response.json(main);
  } catch (err) {
    console.log(`err with csv : ${err}`);
  }
};
  
// 갱신
exports.updateBoard = (request, response, next) => {
  try {
    let params = request?.body;

    if (!!!params?.bno || !!!params?.title || !!!params?.contents) {
      response.json("데이터 누락");
      return;
    }

    let main = processCsvToJsonList('src/resource/csv/main.csv')
    // 데이터 가공 작업 끝

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

    console.log(main)
    console.log(params)

    // 배열 생성할 데이터 형식 추가
    const createMainInterface = {
      bno: params?.bno,
      title: params?.title,
      contents: params?.contents
    }

    // 바꾸기
    for (let i = 0; i < main.length; i++) {
      // 아래는 일부로 형 고려하지 않고 조건 검사하라고 == 라고 둠
      if (main[i].bno == params?.bno) {
        // 아래는 바꾸기
        main.splice(i,1,createMainInterface)
        break;
      }
    }

    // csv로 변환
    const csvResult = jsonToCSV(main);
    fs.writeFile('src/resource/csv/test.csv', csvResult, err => {
    // fs.writeFile('src/resource/csv/main.csv', csvResult, err => {
      if (err) {
        console.log(err.message);
    
        throw err;
      }
    
      console.log('data written to file');
    });

    response.json(main);
  } catch (err) {
    console.log(`err with csv : ${err}`);
  }
};

 

 

- fs.writeFile 부분

test.csv -> main.csv로 변경하면 실제 DB처럼 질의어 조작하여 내용 변경이 가능합니다.

이부분은 test 용도로 결과 값을 수행하기 위해 보는 용도로 사용되었습니다.

차후, 실제로 운영한다고 가정하고 쓰인다고 생각하고 진행할경우 main.csv로 변경하여 사용 할 것!

 

 

5. API 등록

// 이부분 router를 지정하여 각 사용할 api 지정
const router = require('express').Router();
const MainViewController = require('./MainViewController');
const BoardController = require('./board/BoardController');

// cors 해결
const cors = require('cors');

// 아래는 특정 host만 허용 (리액트 프로젝트 적용전)
// const corsOption = {
//   origin: 'http://example.com',
//   optionsSuccessStatus: 200
// }


// 각각 api 접근 허용을 위해 cors()를 추가해줍니다.
router.get('/readBoard', cors(), BoardController.readBoard)

// post를 이용하여 데이터 불려오기
router.post('/createBoard', cors(), BoardController.createBoard)
router.post('/deleteBoard', cors(), BoardController.deleteBoard)
router.post('/updateBoard', cors(), BoardController.updateBoard)

module.exports = router;

 

 

 

6. postman Test

[읽기 : /readBoard]

 

 

[쓰기 : /createBoard]

 

 

[삭제 : /deleteBoard]

 

 

 

[수정 : /updateBoard]

 

 

 

7. 마치며

DB에 적용 전 csv를 이용하여 데이터 조작처리를 할 수 있었다.

현재 postman으로 테스트를 진행 하였지만, 차후, DB를 연동하여 진행해보도록 한다.

이 작업은 실제 현업에서 SM 업무를 담당하거나 실제 작업 시 DB 접근 제한이 있는 경우 목업형식으로 테스트 할 때 쓰는 용도로 사용 되기도 한다. 테스트 방식은 이 방식 말고도 json으로 사용하여 적용하는 방식이 있지만 아직까지 필자는 json으로 기존에 저장된 내용을 데이터 조작하는 방식은 연구 중이기도 한다.

이 부분은 실제 프로젝트 환경과 본인이 맞는 방식으로 찾아 진행하는 것으로 추천드린다. 

 

 

 

728x90
반응형
LIST