728x90
반응형

이전 포스팅에 대하여

 

이전 포스팅에서는 간단하게 웹 크롤링을 하는 방법을 알아보았습니다

axioscheerio를 사용해서 웹 크롤링을 해보았습니다

하지만 axios는 한계가 있습니다

싱글 페이지로 만들어진 웹페이지는 axios로 정보를 받아올 수 없다는 점이

그 한계입니다 그래서 좀 더 강력한 라이브러리를 사용하여 웹크롤링을 하려 합니다

 

그래도 cheerio는 간단한 웹페이지를 크롤링할 때에는 

라이브러리가 가볍기 때문에 상황에 맞게 사용한다면 좋을 듯합니다

 

더 자세한 사항은 아래의 링크를 클릭해주시길 바랍니다

 

 

2022.08.18 - [IT_Web/Nodejs] - 웹 크롤링 axios 및 cheerio 활용해서 간단하게 적용해보기

 

웹 크롤링 axios 및 cheerio 활용해서 간단하게 적용해보기

이전 포스팅에서는 엑셀과 CSV 파일에 저장되어 있는 데이터를 불러오는 것을 알아보았습니다 다른 타 부서나 프로젝트진행에 필요한 파일들이 저장된 데이터를 가져오기 위해 엑셀 및 CSV 파일

tantangerine.tistory.com

 

 

지금 웹 크롤링 관련 포스팅이 이어지다 보니 

기존 소스코드를 사용하는 경우가 많으니 이전 포스팅에서 

어떻게 진행되었는지 꼭 확인하시고 오시길 바랍니다

 

그럼 CSV 파일의 정보를 가져와서

웹크롤링을 하도록 하겠습니다

CSV 파일에는 영화 제목과 url 링크 주소가 함께 있습니다

 

파일 구조와 처음 세팅이 궁금하시다면

아래의 링크를 확인해주세요

 

2022.08.17 - [IT_Web/Nodejs] - node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

 

node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

node.js 관련은 처음 소개하는 것 같습니다 다음에 node.js로 서버 구축하는 것도 알아볼 테니 많은 관심 부탁드립니다 우선 node 다운로드 링크 주소는 사이드바에 생성해 놓을 테니 확인해 보시길

tantangerine.tistory.com

 


 

Puppeteer 및 Promise 활용해서 웹 크롤링하기 

 

 

puppeteer의 공식문서는 아래의 링크를 확인해보세요!!

https://github.com/puppeteer/puppeteer/blob/v16.2.0/docs

 

GitHub - puppeteer/puppeteer: Headless Chrome Node.js API

Headless Chrome Node.js API. Contribute to puppeteer/puppeteer development by creating an account on GitHub.

github.com


 

이제 본론으로 들어가서

웹 크롤링을 시작해 봅시다

먼저 아래와 같이 라이브러리를 설치합니다

 

 

npm i puppeteer

 

 

 

아래와 같이 코드를 작성합니다

새로운 함수들과 부가적인 설명이 필요할 것 같습니다

 

 

const puppeteer = require('puppeteer');

const crawler = async () => {
    const browser = await puppeteer.launch({headless: false}); // 웹브라우저 노출여부
    const page1 = await browser.newPage() // 페이지 노출하기
    const page2 = await browser.newPage() // 탭으로 페이지가 여러개 노출할 수 있습니다
    const page3 = await browser.newPage()

    await page1.goto('https://naver.com'), //페이지 이동
    await page2.goto('https://google.com'),
    await page3.goto('https://www.daum.net')


    await page1.evaluate(async() => { // 그리고 잠시대기
      await new Promise(r => setTimeout(r, 3000))
    }),
    await page2.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    await page3.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    })

    await page1.goto('https://n.news.naver.com/mnews/ranking/article/437/0000310583?ntype=RANKING&sid=001'),
    await page2.goto('https://newslibrary.naver.com/search/searchByDate.naver'),
    await page3.goto('https://google.com')

    await page1.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    await page2.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    await page3.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    })
    
    await page1.close(), // 탭 페이지 닫기
    await page2.close(),
    await page3.close()
  	await browser.close(); // 브라우저 닫기
}

crawler();

 

 

 

웹 크롤링일반적으로 비동기를 사용해야 합니다

그 이유는 브라우저를 띄우고 로그인을 하고 그런 일괄된 작업들이 진행되면서 순차적으로 작업을 

진행해야 작업이 꼬이지 않기 때문입니다

 

예로 브라우저를 띄우고 로그인을 할 경우 브라우저가 노출이 되지도 않았는데

로그인을 하려고 한다면

에러를 발생하기 때문입니다

 

그래서 puppeteer를 사용하면서 얼마나 비동기를 자유자재로 사용하는지가

가중 중요한 핵심이라고 할 수 있습니다

 

이제 코드적인 설명을 해보겠습니다

아래와 같이 launch() 함수에 headless: false가 적용되어있습니다

웹브라우저를 노출해서 웹 크롤링하는 작업을 볼 수 있습니다

 

개발 초기에는 false로 적용해서 개발을 하면서

수정사항을 고쳐나갈 것입니다

 

웹크롤링-웹페이지-노출
웹크롤링을 할 경우 웹페이지 노출에 대한 여부

 

실제 프로젝트를 진행한다면 false값에

아래와 같이 적용해야 할 것입니다

그럼 env의 상태로 개발, 운영을 구분하여 웹페이지를 노출하게 될 것입니다

 

환경설정활용-웹브라우저노출여부
환경설정 값을 활용해서 웹브라우저 노출여부를 지정한다

 

 

그리고 페이지를 이동하고 나서

잠시 대기하는 시간을 갖습니다

예전에 puppeteer.waitFor을 사용하였습니다

하지만 버전이 올라가면서 해당 함수가 삭제되어서

아래와 같이 구현할 수 있습니다

 

태그에 접근할 정보가 있다면

evaluate()함수는 tagvalue값에 접근이 가능합니다

하지만 콜백함수를 활용해서 비동기로 setTimeout함수를 사용해서

함수 실행을 딜레이 합니다

 

그리고 잠시 대기하는 시간이 왜 필요한지도 궁금하실 수 있습니다

그 이유는 자동으로 웹크롤링을 진행하게 되면

속도가 엄청 빠르다는 것입니다

그럼 웹페이지를 운영하는 입장에서는

비정상적인 동작이라고 생각하기 때문에

우리에게 제재를 줄 수 있습니다

 

비정상적인 방법이라는 인식을 피하기 위함입니다

 

웹크롤링-잠시대기
웹크롤링 잠시 대기

 

 

이렇게 간단한 함수들을 정리해보았습니다

하지만 지금 이 방법은 순차적으로 진행하다 보니

소 시간이 소요됩니다

만약 더 많은 작업을 한다면 소요되는 시간은 더 클 것이라고 생각합니다

 

그래서 이제 프로미스를 적용해서 한방에 요청하고

순차적으로 가 아닌 먼저 도착한 순서대로 진행하여

작업 시간을 단축해보도록 하겠습니다


 

반응형

 


 

Promise.all을 활용해서 웹크롤링 시간 단축 하기

 

아래와 같이 코드를 변경합니다

Promise.all을 사용한 것이 보이시나요?

 

함수마다 Promise.all을 적용해서 동시 작업을 진행합니다

이때 비동기이지만 한 번에 작업할 때마다 동시에 한다는 점입니다

그래서 전체적으로 볼 때는 비동기라는 것을 인지해 주세요 

 

부가적인 설명은 필요가 없을 것 같습니다

 

그럼 이제는 CSV 파일을 읽어서 그 정보를 바탕으로 웹 크롤링을 해보겠습니다

 

const puppeteer = require('puppeteer');

const crawler = async () => {
  const browser = await puppeteer.launch({headless: process.env.NODE_ENV === 'production'});
  const [page1, page2, page3] = await Promise.all([
    browser.newPage(),
    browser.newPage(),
    browser.newPage()
  ])
  await Promise.all([
    page1.goto('https://naver.com'),
    page2.goto('https://google.com'),
    page3.goto('https://www.daum.net')
  ]);
  await Promise.all([
    page1.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    page2.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    page3.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    })
  ]);
  await Promise.all([
    page1.goto('https://n.news.naver.com/mnews/ranking/article/437/0000310583?ntype=RANKING&sid=001'),
    page2.goto('https://newslibrary.naver.com/search/searchByDate.naver'),
    page3.goto('https://google.com')
  ]);
  await Promise.all([
    page1.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    page2.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    page3.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    })
  ]);
  await Promise.all([
    page1.close(),
    page2.close(),
    page3.close()
  ]);
  await browser.close();
}

crawler();

 


 

CSV 파일을 읽어서 웹크롤링 시작하기

 

CSV 파일을 읽기 위해서는 라이브러리가 필요합니다

그리고 초기 세팅이 필요하겠지요

그것은 위에 링크를 걸어 두었던 것 기억하시지요?

그것을 참고하시길 바랍니다

 

아래와 같이 코드를 작성합니다

 

 

const parse = require('csv-parse/lib/sync');
const stringify = require('csv-stringify/lib/sync') 
const fs = require('fs');
const puppeteer = require('puppeteer');

const csv = fs.readFileSync('csv/data.csv');
const records = parse(csv.toString('utf-8'))

const crawler = async () => {
  const resultScore = []
  try {
    const browser = await puppeteer.launch({headless: false});
    console.log('첫번째: records', records)
    // Promise.all을 활용해서 csv에서 받아온 records를 모든 배열의 정보를 동시에작업을 진행합니다
    await Promise.all(records.map(async (r, i) => {
        const page = await browser.newPage();
        await page.goto(r[1]);
        const scoreEl = await page.$('.score.score_left .star_score');
        if (scoreEl) {
          const text = await page.evaluate(tag => tag.textContent, scoreEl);
          if (text) {
            console.log('두번째: ', r[0], '평점', text.trim());
            resultScore.push([r[0], r[1], text.trim()])
            // resultScore[i] = [r[0], r[1], text.trim()]
          }
        }
        await page.evaluate(async() => {
          await new Promise(r => setTimeout(r, 3000))
        })
        await page.close();
    }));
      await browser.close();
      const str = stringify(resultScore);
      console.log('3번째: ', str)
      fs.writeFileSync('csv/result.csv', str);
  } catch (e) {
    console.error(e)
  }
}


crawler();

 

 

위의 코드에서

Promise.all을 사용해서 recodes.map을 매개변수 사용해서

한 번에 작업을 진행합니다

그리고 콘솔 창에는 아래와 같이 확인되었습니다

그래서 링크 주소를 활용해서 웹 크롤링을 시작합니다

 

콘솔데이터정보
콘솔 데이터 정보 노출 상황

 

 

 

개발자 모드에서 아래와 같이 클래스명을 확인해서

데이터 값을 가져올 수 있는 위치를 확인합니다

 

 

개발자모드-선택자찾기
개발자모드 선택자찾기

 

그래서 아래와 같이 클래스명을 기입해서

그 해당하는 textContent를 가져옵니다

이때 필요한 함수는 page.evaluate를 활용합니다

 tag.textContentscoreElreturn 받아

 

text 변수에 값이 있으면 push를 해서 최종 값을 저장합니다 

 

 

데이터-가져오기
데이터 가져오기

 

 

 

그리고 웹 크롤링해온 평점을 다시 CSV 파일에 넣어보도록 하겠습니다

먼저 라이브러리를 설치합니다

 

 

npm i csv-stringify

 

 

이전에 웹 스크롤링을 해서 저장된 데이터를

stringify함수를 사용해서 간단히 String으로 만듭니다

그래서 CSV 파일 형식으로 다른 이름으로 저장합니다 

 

 

CSV파일-웹크롤링정보쓰기
웹크롤링 정보 CVS파일 쓰기

 

그 파일은 아래와 같습니다

하지만 문제가 있습니다

첫 번째 콘솔에 정렬된 순서와 저장되고 나서의 순서가

다르다는 것입니다

 

협업을 하다 보면 파일의 정보 순서가

정말 중요할 경우가 있습니다

 

저장된 CSV파일
저장된 CSV파일

 

위의 코드에서 주석 부분을 변경해서 i를 사용합니다

i는 인덱스 넘버가 저장되어있습니다

초기 배열의 저장된 순서가 i에 저장되어있어서

정렬순서가 보존될 수 있습니다

 

정렬순서보존
map의 인덱스 넘버를 활용해서 순서보존하기

 

 

 

 

아래는 웹 크롤링할 경우에 노출되는 웹페이지입니다

10개가 동시에 열려있는 것이 보이시나요?

 

 

웹크롤링-브라우저모습
웹크롤링 브라우저 모습

 

 


 

이렇게 웹 크롤링을 알아보았습니다

아직 여러 함수들을 사용하지 않고 정말 기본적인 것을 활용해서

웹크롤링을 해보았습니다

 

다음 포스팅에서는 더욱 심도 있는 기능으로 찾아뵙겠습니다

우리 IT의 대모험은 끝나지 않았습니다

 

끝나는(?) 그날까지 파이팅 하세요

 

 

728x90
반응형
728x90
반응형

 

 

이전 포스팅에서는 엑셀과  CSV 파일에 저장되어 있는 데이터를 불러오는 것을 알아보았습니다

다른 타 부서나 프로젝트진행에 필요한 파일들이 저장된 데이터를 가져오기 위해

엑셀 및 CSV 파일 파싱을 해서 가져와서 데이터를 핸들링하는 방법을 알아보았습니다

 

더 상세히 알고 싶으시면 아래의 링크를 클릭해서 확인해보시길 바랍니다

 

2022.08.17 - [IT_Web/Nodejs] - node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

 

node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

node.js 관련은 처음 소개하는 것 같습니다 다음에 node.js로 서버 구축하는 것도 알아볼 테니 많은 관심 부탁드립니다 우선 node 다운로드 링크 주소는 사이드바에 생성해 놓을 테니 확인해 보시길

tantangerine.tistory.com

 

 

웹 크롤링을 간단하게 해 보자

 

 

그럼 바로 라이브러리부터 설치해보겠습니다

 

 

npm i axios cheerio

 

 

아래의 코드도 추가합니다

파일 구조나 시작점은 위의 링크로 들어가셔서 처음에 어떻게 시작했는지 확인해 보시길 바랍니다

이전 포스팅과 연결되는 부분이 조금 있어서 꼭 한번 들어가서 확인하시고

다시 돌아오세요~

 

 

const xlsx = require('xlsx');

const workbook = xlsx.readFile('xlsx/data.xlsx');
//console.log('readFile: ', Object.keys(workbook.Sheets))

const ws = workbook.Sheets.Sheet1; // 시트 명을 
//console.log('resultSheet: ', ws)

const wsRecods = xlsx.utils.sheet_to_json(ws); // 시트에 담긴 데이터를 json파일로 변경해준다
//console.log('resultWsRecods: ', wsRecods)


const axios =  require('axios');
const cheerio = require('cheerio');

const crawler = async () => {
  for (const [i, r] of wsRecods.entries()) {
    console.log('링크', r.링크)
    const response = await axios.get(r.링크);
    if(response.status === 200){
      const htmlStr = response.data;
      console.log('htmlStr: ', htmlStr)
      const $ = cheerio.load(htmlStr) // cherrio는 태그를 접근할 수 있습니다
      const text = $('.score.score_left .star_score').text(); // 선택자로 직접적인 class명에 접근이 가능합니다
      console.log(r.제목, '평점: ', text.trim());
    }
  }
}

 

데이터링크
링크가 들어있는 모습

첫 번째 콘솔에 링크를 보시면

현재 엑셀 파일에 받아온 데이터가 링크 주소인 것을 확인할 수 있습니다

그 주소를 불러와서 axios.get()의 함수를 사용해서

링크를 매개변수로 할당하여 데이터 값을 받아옵니다

그렇게 되면 responsehtml이 문자열로 들어오게 됩니다

 

html문자열을 콘솔 창에서 확인을 하면,

아래와 같이 html문자열을 확인할 수 있습니다

이 문자열을 활용하여 정보를 가지고 옵니다

 

html문자열
html 문자열을 확인할 수 있다

 

 

반응형

 

 

 

아래의 두줄의 코드를 보시면 cheerio를 활용하여 태그에 접근 가능하며,

이때 jQery를 사용해서 선택자의 클래스명에 접근이 가능합니다

이때 선택자는 어떻게 지정하느냐가 중요합니다

 

두 번째 줄에 보시면. score.score_left. star_score 클래스 명을 사용해서

 jQery함수인. text()를 사용해서 값을 받아오고 있습니다

 

 

 

F12로 개발자 모드 창을 띄워서 

아래의 빨간색 테두리에 있는 것을 클릭해서 웹페이지의 태그를 알아볼 수 있습니다

 

 

개발자모드창
개발자 모드 창

 

 

 

그리고 아래의 이미지처럼

보고 싶은 웹사이트의 부분을 클릭하면 됩니다

 

개발자모드
개발자모드

 

 

 

아래의 이미지를 개발자 모드 창에서 확인할 수 있습니다

 

 

개발자태그코드
개발자 모드의  html

 

그럼 text()를 사용하여 그 선택자로 접근한 태그의 textContent를 가져오게 됩니다

그렇게 되면 아래와 같이 결과가 출력됩니다

 

 

 

평점출력결과
평점출력결과

 

 

지금은 for of문과 await 사용하여 순차적으로 요청하는 모습을 볼 수 있습니다

하지만 이것은 한 개씩 요청하고 받기 때문에 시간이 다소 걸릴 수 있습니다

 

시간이 걸리는 것을 해결하기 위해서는 Promise를 사용하면

모든 요청을 한 번에 다 보내게되고 한번에 받게 됩니다

하지만 그렇게 되면 순서를 보장할 수 없습니다

 

그래서 이 두 가지를 생각하고 순서를 보장해야 할 경우에는 for of문 await을 같이 사용하고

그럴 필요가 없다면 Promise를 사용하는 것이 좋을 것입니다

 

 

Promise의 방법은 아래와 같습니다

 

const crawler = async () => {
  await Promise.all(wsRecods.map(async (r) => {
    //console.log('링크', r.링크)
    const response = await axios.get(r.링크);
    if(response.status === 200){
      const htmlStr = response.data;
      //console.log('htmlStr: ', htmlStr)
      const $ = cheerio.load(htmlStr) // cherrio는 태그를 접근할 수 있습니다
      const text = $('.score.score_left .star_score').text(); // 선택자로 직접적인 class명에 접근이 가능합니다
      console.log(r.제목, '평점: ', text.trim());
    }
  }))
}

 

 

지금까지 간단한 axioscheerio 활용하면,

간단한 웹 크롤링을 할 수 있습니다

그래서 다른 중요한 웹 페이지들은 cheerio 활용하면 웹 크롤링을 할 수 없습니다

이제 오늘은 간단한 웹크롤링을 배워 보았습니다

 

cheerio 말고 더 강력한 라이브러리를 사용해서 웹 크롤링해보도록 하겠습니다

그럼 오늘도 열심히 하시고

 

다음 포스팅도 기대해주십시오!

 

 

 

 

 

728x90
반응형
728x90
반응형

node.js 관련은 처음 소개하는 것 같습니다

다음에 node.js로 서버 구축하는 것도 알아볼 테니 많은 관심 부탁드립니다

 

우선 node 다운로드 링크 주소는 사이드바에 생성해 놓을 테니 확인해 보시길 바랍니다

그리고 설치방법은 구글 검색으로 간단히 해결할 수 있습니다

그러니 그것은 그냥 스킵하도록 하겠습니다

 

그럼  아래와 같이 폴더를 생성해주시고

 

파일구조
파일구조

아래와 같이 lecture 폴더 안에서 

npm init을 하시길 바랍니다

npm설정
npm 설정하기

입력하게 되면 몇 가지 질문사항을 확인하실 수 있습니다

 

package name 패키지 명은 폴더 이름이 기본값입니다.

version는 패키지 버전입니다. (1.0.0)는 기본 값입니다.

description는 패키지에 대한 설명입니다. 

entry point는 시작 파일 명입니다. (index.js)는 기본 값입니다.

test commandnpm test를 호출할 때마다 실행되는 명령입니다.

git repository는 패키지가 저장되어 있는 Git 저장소의 URL입니다.

keywords는 패키지의 키워드입니다.

author는 원작자의 이름입니다. 여러분의 이름이나 아이디를 입력하면 됩니다.

license는 패키지 사용에 대한 라이선스입니다. (ISC)는 기본 값입니다.

 

모든 질문사항을 기본값으로 설정해도 무관합니다

 

그럼 위와 같이 package.json파일이 생성됩니다

그리고 package.json파일 내부의 코드를 변경합니다

 

package.json 구조
package.json 구조

 

npm start를 할 경우에 index를 실행시킨다는 의미입니다

그리고 추후에 라이브러리를 설치하지만 csv-parse, xlsx의 버전을 확인해주시길 바랍니다

버전이 다르면 현재 제가 하는 것과 다르게 사용해야 하니 주의해 주세요

 

 


 

csv파일 파싱 해서 데이터 가져오기

CSV 파일이란 무엇일까요?

Comma Separated Value라고 합니다

한마디로 콤마로 이루어진 파일이라고 할 수 있습니다

 

아래와 같이 파일이 구성되어있지요

정확히 말하면 콤마와 줄 바꿈 정도라고 생각하실 수 있습니다

타이타닉,https://movie.naver.com/movie/bi/mi/basic/nhn?code=18847
아바타,https://movie.naver.com/movie/bi/mi/basic/nhn?code=62266
메트릭스,https://movie.naver.com/movie/bi/mi/basic/nhn?code=24453
반지의 제왕,https://movie.naver.com/movie/bi/mi/basic/nhn?code=31794
어벤져스,https://movie.naver.com/movie/bi/mi/basic/nhn?code=72363
겨울왕국,https://movie.naver.com/movie/bi/mi/basic/nhn?code=100931
트랜스포머,https://movie.naver.com/movie/bi/mi/basic/nhn?code=61521
해리 포터,https://movie.naver.com/movie/bi/mi/basic/nhn?code=30688
다크나이트,https://movie.naver.com/movie/bi/mi/basic/nhn?code=62586
캐리비안해적,https://movie.naver.com/movie/bi/mi/basic/nhn?code=37148

 

그런데 여기서 잠깐 의문이 생길 수 있겠지요

왜 이런 작업이 필요할까?

 

우리가 프로젝트를 진행하다 보면 현업 즉 고객사와 협업을 많이 진행합니다

아니면 다른 타 부서 사람과도 협업을 하겠지요

그러다 보면 대량의 정보를 데이터 베이스에 저장을 한다거나,

이 정보에 해당하는 어떤 값을 저장해 달라고 하거나

여러 정보가 필요하게 됩니다

이때 몇 천 건의 데이터를 수기로 하기는 어려움이 있습니다

그래서 이러한 프로그램적으로 구현하여 손쉽게 하기 위함입니다

 

그럼 다시 본론으로 넘어와

csv 파싱 라이브러리를 설치합니다

우리는 현존에 개발해놓은 좋은 라이브러리를 잘 사용하기만 하면 됩니다

이거 가져다 쓴다고 흉이 아니니 잘 쓰고 빨리 개발하면 되겠습니다

 

npm i csv-parse

 

 

 

index.js에서 아래와 같이 코드를 작성합니다

CSV 파일을 파싱 할 때 주의해야 할 점은 버퍼 파일이기 때문에 인코딩을 해야 하는 것을 잊지 말아야 합니다

 

// npm 패키지 불러올때 사용하는 require함수입니다
// 또한 node_modules파일에서 경로를 그대로 따라가서 그 함수를 들고온다고 생각하면된다
// 노드서버를 만들어서 배포하는 것이 좋습니다
const parse = require('csv-parse/lib/index.js'); 
const fs = require('fs'); // 파일시스템 모듈을 불러온다
const csv = fs.readFileSync('csv/data.csv'); // 파일을 불러오는 함수

// 이때 불러오게된 csv에는 버퍼(Buffer)형식이라서 문자열로 변경해주어야한다
// 버퍼는 0,1로 이루어진 컴퓨터 친화적인 데이터이다
csv.toString('utf-8') // toString을 하면서 인코딩을 하는 것은 잊지말자

console.log('result: ', csv.toString('utf-8'));

const records = parse(csv.toString('utf-8'))
records.forEach((movieInfo, i) => {
  console.log('영화제목: ', movieInfo[0], '*** 영화링크: ', movieInfo[1]);
});

 

 

첫 번째 콘솔result에 해당하는 값을 확인해보면 아래와 같이 확인할  수 있습니다

아래는 단지 파일을 읽어온 결과 값입니다

 

csv파일읽은결과
csv파일읽은결과

 

두 번째 CSV 파일파싱 한 결과

영화 제목과 링크로 나누어 보이는 결과 값입니다

아래와 같습니다 

 

csv파일파싱결과
csv파일 파싱 결과

이제 데이터를 어떻게 사용해야 할지 감이 잡이시나요?

그럼 다음은 엑셀입니다


 

 

 

반응형

 

 


 

엑셀 파싱 해서 데이터 가져오기

 

엑셀 파일에 가져올 데이터를 채워서 파일로 준비합니다

 

엑셀파싱데이터
엑셀파싱데이터

 

 

엑셀 파싱 관련 라이브러리를 추가합니다

엑셀 파싱은 정말 혼자서 만들기 힘드니 라이브러리를 이용하시길 바랍니다

 

 

npm i xlsx

 

 

그리고 아래의 코드를 index.js에 작성하여 줍니다

그리고 npm start를 실행하게 합니다

 

const xlsx = require('xlsx');

const workbook = xlsx.readFile('xlsx/data.xlsx');
console.log('readFile: ', Object.keys(workbook.Sheets))

const ws = workbook.Sheets.Sheet1; // 시트 명(Sheet1)을 뒤에 붙여준다
console.log('resultSheet: ', ws)

// .sheet_to_json()함수로 시트에 담긴 데이터를 json파일로 변경해준다
const wsRecods = xlsx.utils.sheet_to_json(ws); 
console.log('resultWsRecods: ', wsRecods)

for (const [i, r] of wsRecods.entries()) {
  console.log(`${i}번 ${r.제목} ** ${r.링크}`)
}

 

첫 번째 콘솔readFile을 보시면 ['Sheet1']이 들어가 있는 것을 확인할 수 있습니다

그래서 workbooks.Sheet.Sheet1으로 파일을 받아올 수 있게 되는 것입니다

 

그리고 두 번째 콘솔resultSheet를 보시면 엑셀 파일의 데이터가 저장되어있는 것을 확인할 수 있습니다

엑셀의 데이터를 ws라는 변수에 저장해서  xlsx라이브러리를 사용해서 파싱 해서 데이터를 가져옵니다

 

 

 

 

세 번째 콘솔에 보시면 resultWsRecodswsRecods값을 보시면

아래와 같이 json형태로 데이터가 들어오는 것을 확인할 수 있습니다

그 이유는 xlsx.utils.sheet_to_json() 함수를 사용해서

편하게 json형태 데이터를 파싱 할 수 있게 된 것입니다

 

 

 

네 번째 콘솔에 for문을 활용해서 데이터 값을 가져올 수 있습니다

그 결과 값은 아래와 같이 노출됩니다

이렇게 간단하게 라이브러리를 사용해서 파싱을 하여

외부 데이터를 가져와 핸들링하는 방법을 알아보았습니다

 

 

 

가져온 데이터를 데이터베이스에 저장을 하거나,

다른 작업을 할 수 있겠지요?

 

그리고 중요한 것이 있습니다

웹 크롤링을 위해서 for문, forEach문을 익혀두셔야 합니다

자바스크립트는 싱글 스레드이기 때문에 크롤링할 때도 똑같이 적용됩니다

그래서 비동기로부터 벗어날 수 없어서 비동기를 조심하면서 크롤링을 해야 합니다

이때 forEach문을 사용할 때와 for문을 사용할 때의 차이가 극명하게 다르기 때문

비동기를 자유자재로 사용해야 크롤링된 데이터를 모아서 한방에 저장할 수가 있습니다

 

오늘도 유익한 정보들을 알아보았습니다

이렇게 공부를 하다 보면 언젠가는 눈감고도 할 수 있는 날이 오겠죠?

ㅎㅎ 그럼 오늘도 수고하셨고

우리의 대모험이 끝나는 날까지 힘내시고 파이팅하시길 바랍니다

그럼 다음 포스팅도 많은 관심 부탁드립니다

 

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅에서는 웹팩에 바벨을 적용해보았습니다.

그리고 바벨이 왜 중요한지에 대한 이유도 알아보았고요.

바벨이 필요한 이유는 자바스크립트의 버전이 점점 올라가면서

다양한 함수들을 구현하여 코드를 더 간편하게 작성할 수 있게 되었습니다.

하지만 브라우저 간에 호환이 안 되는 문제점이 발생하면서

바벨의 중요성은 더욱 부각되었죠

 

더 상세한 이야기가 궁금하시다면 아래의 이전 포스팅을 보시면 됩니다.

 

 

2022.08.11 - [IT_Web/Webpack] - 웹팩 바벨 설치 및 활용 webpack에서 babel이 필요한 이유는?

 

웹팩 바벨 설치 및 활용 webpack에서 babel이 필요한 이유는?

이전 포스팅에서는 HTML, Javascript, css 파일을 웹팩에서 배포 시 압축하는 방법을 알아보았습니다 혹시 못보신분이 계시다면 아래의 링크를 통해 보실 수 있습니다 2022.08.02 - [IT_Web/Webpack] - 웹팩 CSS

tantangerine.tistory.com

 

 

이전 포스팅부터 따라오셨다면

기존 코드들이 있으실 겁니다 

그 코드를 리액트로 만들어보겠습니다

 

 

리액트로 바벨을 적용해야 하기 때문에 리액트 관련 바벨을 추가하며,

리액트 관련 라이브러리를 추가하겠습니다

 

 

npm i --save-dev @babel/preset-react react react-dom

 

 

.babelrc 파일에

@babel/preset-react를 코드 추가하여 줍니다.

 

 

.babelrc

{
 "presets": ["@babel/preset-env", "@babel/preset-react"]
}

 

 

web.common.js 파일에서

jsx문법을 추가해 주시면 됩니다.

 

web.common.js 

module.exports = {
    entry: "./src/index.js",
    module: {
        rules: [
            {......},
 			{......},
            {
                test: /\.(js|jsx)$/, // 추가
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader"
                }
            }
        ]
    }
};

 

 

그리고 이제 기존에 html 파일에

React로 변경된 파일들을 적용해야 합니다

 

우선 app.js를 만들어서 index.js와 html 파일에 적용을 해줍니다

 

import React from 'react';
import ReactDOM from "react-dom";

const App = () => {
    return (
        <div>
       		<h1>React Title</h1>
        </div>
    );
};

export default App;

const wrapper = document.getElementById("app");
wrapper ? ReactDOM.render(<App />, wrapper) : false;

 

 

위의 파일을 만들어서 index.js에

컴포넌트로 전달합니다

 

 

import { btnChange } from "./app/btn";
import { changeColor } from "./app/color";
import "./css/main.css";
import App from "./js/App"; // 추가

btnChange.addEventListener("click", function() {
 changeColor("pink");
})

 

 

 

html 파일에도 코드를 추가하여줍니다

이렇게 하면 리액트가 추가됩니다

그다음은 app.js에서 리액트 라이브러리를 추가하여 코드를 작성하여

리액트 관련 개발을 하면 되겠습니다 

 

 

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <body>
        <h1 class="card-title">Card Title</h1>
        <button class="btn-change">버튼 변경</button>
        <img src="./assets/funnelback.png">
        <div id="app"></div> /* 코드 추가 */
    </body>
</html>

 

 

 

 

오늘은 정말 간단한 시간을 가졌습니다

바벨 적용도 그냥 한 줄의 라이브러리 설치와 

한 개의 파일만 만들면 간단하게 적용이 되며 설치가 됩니다

 

오늘은 이렇게 설치가 간단하게 된다는 걸 알면 조금 허무하긴 하네요

 

아무튼 그래도 이런 방법을 아는 것도 중요한 공부라고 생각합니다

그럼 다음 포스팅도 기대해주시고

 

대모험이 끝나는 그날까지 파이팅합시다!!

 

 

 

 

 

728x90
반응형
728x90
반응형

 

이전 포스팅에서는 HTML, Javascript, css 파일을

웹팩에서 배포 시 압축하는 방법을 알아보았습니다

 

혹시 못보신분이 계시다면 아래의 링크를 통해 보실 수 있습니다

2022.08.02 - [IT_Web/Webpack] - 웹팩 CSS와 html 및 webpack 활용해서 JS파일 압축하기

 

웹팩 CSS와 html 및 webpack 활용해서 JS파일 압축하기

이전 포스팅에서 웹팩에서 빌드 파일 자동 삭제 및 이미지 로딩시간을 단축하기 위해 빌드할 경우 CSS파일을 생성하는 것을 알아보았습니다 아직 못 보신 분들은 아래의 링크로 확인해 보시길

tantangerine.tistory.com

 


 

 

바벨(babel)이란?

 

바벨은 자바스크립트의 컴파일러입니다

그래서 최신버전의 자바스크립트 코드를 이전 버전의 코드로 변환시켜주는 도구입니다

하지만 몇몇 사람들은 이전 버전으로 코드 변환이 왜 필요한지에 대해 의문을 가지기도 합니다

 

그 이유는 우리가 사용하는 브라우저의 버전에 따라 자바스크립트의 호환 안될 수도 있기 때문입니다 

아래는 자바스크립트 함수인 map입니다

보시면 브라우저 별로 버전에 따라 호환되는 것이 다를 수 있기 때문입니다

그래서 만약 사용자의 버전이 낮거나 한다면 에러가 발생하게 됩니다

그렇기 때문에 바벨로 이전 버전의 낮은 버전으로 변경하여

모든 사용자 사용하기 위함입니다

 

그럼 본론으로 바벨 설치 및 설정법을 알아보겠습니다

 

https://caniuse.com/

왜바벨이필요한가
바벨이 필요한 이유

 

 


 

바벨의 라이브러리 종류 및 설치

  • @babel/core : 바벨 사용 시 필수 라이브러리
  • @babel/preset-env : ES5 트랜스 파일러
  • @babel/polyfill : ES6 새로운 객체와 메서드 사용 처리
  • @babel/plugin-proposal-class-properties : Class 사용 가능 처리
  • babel-loader : 바벨과 웹팩을 이용해 자바스크립트 파일을 트랜스파일

 

 

바벨에 필요한 라이브러리를 설치하도록 하겠습니다

npm i --save-dev @babel/core @babel/preset-env @babel/polyfill
npm i --save-dev babel-loader

 

 

반응형

 

바벨(babel) 설정하기

 

 

 

.babelrc 라는 파일을 만들게 됩니다

위의 파일명 babelrc 앞에 (. ) 콤마가 존재하는걸 꼭 확인하세요

 

 

.babelrc

{
 "presets": ["@babel/preset-env"],
 // 아래 플러그인 설치할 경우 추가
 // "plugins": ["@babel/plugin-proposal-class-properties"]
}

 

 

 

webpack.common.js 파일에 rules를 추가해줍니다

 

module.exports = {
  entry: "./src/index.js",
  module: {
    rules: [
      {
        test: /\.html$/,
        use: ["html-loader"],
      },
      {
        test: /\.(svg|png|jpg|gif)$/,
        use: {
          loader: "file-loader",
          options: {
            name: "[name].[hash].[ext]",
            outputPath: "imgs",
          },
        },
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      }, // 추가 부분
    ],
  },
};

 

 

아래와 같은 추가 부분의 뜻은

노드 모듈에 있는 자바스크립트 파일은 제외를 하고 

모든 js파일은 바벨을 적용하겠다는 rules를 추가한다는 뜻입니다

 

바벨적용
바벨 적용 코드

 

아래와 같이 function, var을 확인할 수 있습니다

이렇게 낮은 버전도 사용할 수 있는 코드들로 변환시켜

사용자의 범용성을 키우는 기능이라고 할 수 있다

 

바벨결과코드
바벨이 적용된 js 배보 파일

 


 

 

이렇게 바벨을 설치하고 설정하는 방법을 알아보았습니다

바벨은 정말 필수이니까 바벨이 무엇인지

어떤 동작을 하는지는 꼭 알아두어야겠지요?

 

그럼 지금까지 바벨의 설치 및 설정 방법 그리고 바벨이 필요한 이유까지 알아보았습니다.

인프라적인 요소가 있지만 이러한 환경설정을 하는 것도 필요한 부분이 많습니다

알아두면 언젠가는 필요한 날이 있을 거라 생각합니다

 

그럼 지금까지의 대모험은 끝이 났습니다

하지만 앞으로도 수많은 대모험이 기다리고 있으니

파이팅 하시고

같이 앞으로 나아가 보아요!

 

그럼 다음 포스팅도 기대해주세요~

728x90
반응형
728x90
반응형

상품 판매 통계 쿼리

 

오늘은 상품 판매 통계 쿼리를 만들어 보겠습니다

상품은 여러 종류가 있습니다

그것을 어떻게 구분할지는 스스로가 정해야겠지요

 

아래에서는 상품구분을 단순하게 알파벳으로 표현하였습니다

그 알파벳으로 CASE문을 사용하여 상품명을 가져왔습니다

 

보통은 공통코드가 만들어져 조인을 걸어서 사용해야만 합니다

그래야 지만 상품이 늘어날 때마다 쿼리를 수정하지 않고 데이터를 추가하는 것만으로도

상품이 추가되어서 쿼리가 작동되는 범용성을 가져야 한다는 것이지요

 

그래서 솔직히 아래의 코드는 좋은 쿼리라고 할 수 없습니다

 

하지만 CASE문을 어떻게 써서 열 데이터를 행열로 만들어서 카운팅 하는 지만 신경 써서 보시길 바랍니다

 

<select id="selectProductSaleTotalIncrease" parameterType="Map" resultType="Map">
    SELECT
           DI_POM_SP.PRD_TYPE
          ,TO_CHAR(DI_POM_SP.PAY_CNT,'FM9999,999') AS PAY_CNT
          ,TO_CHAR(DI_POM_SP.PAY_AMT,'FM999,999,999,999') AS PAY_AMT
      FROM
           (SELECT
           		   /* 상품에 대한 구분(소분류, 중분류, 대분류등등) */
                   (CASE WHEN SP.PRD_DVCD = 'A' THEN '이용권'
                         WHEN SP.PRD_DVCD = 'B' THEN '상품 구매'
                         WHEN SP.PRD_DVCD = 'C' THEN '내부 서비스 수수료'
                         WHEN SP.PRD_DVCD = 'D' THEN '외부 서비스 수수료'
                         WHEN SP.PRD_DVCD = 'E' THEN '기타 수수료'
                         ELSE 'SKIP' END) AS PRD_TYPE
                   ,SUM(NVL(DI_POM.PAY_CNT,0)) AS PAY_CNT
                   ,SUM(NVL(DI_POM.PAY_AMT,0)) AS PAY_AMT
              FROM
                  (SELECT DI.YMD
                         ,DI.MMDD
                         ,NVL(POM.PRD_DVCD,'SKIP') AS PRD_DVCD
                         ,NVL(POM.PAY_CNT,0) AS PAY_CNT
                         ,NVL(POM.PAY_AMT,0) AS PAY_AMT
                     FROM
                          (SELECT TO_CHAR(TO_DATE(YMD),'YYYYMMDD') AS YMD, TO_CHAR(TO_DATE(YMD),'MM.DD') AS MMDD
                             FROM DAY_INFO
                             /* 기간을 정해서 판매량을 추출할 수 있다 */
                            WHERE TO_CHAR(TO_DATE(YMD),'YYYYMMDD') BETWEEN #{startDt} AND #{endDt}
                          ) DI
                         ,(SELECT
                                   OD.PRD_DVCD
                                  ,PM.PAY_DT
                                  ,SUM(1) AS PAY_CNT
                                  ,SUM(NVL(PM.PAY_AMT,0)) AS PAY_AMT
                             FROM PAY_MST PM
                                  INNER JOIN ODR_DTL OD ON OD.ODR_NUM = PM.ODR_NUM
                                  LEFT OUTER JOIN (SELECT A.ODR_NUM, A.ODR_DTL_SEQ, A.TRANS_SPEC_NUM, A.SEQ_NO, A.JRNR_NUM, A.SAP_NO
                                                     FROM STTL_JRNL_DTL A
                                                    WHERE A.STTL_DIV_CD = 'S1'
                                                      AND A.JRNR_NUM = (SELECT MAX(JRNR_NUM)
                                                                          FROM STTL_JRNL_DTL B
                                                                         WHERE B.STTL_DIV_CD = A.STTL_DIV_CD
                                                                           AND B.ODR_NUM = A.ODR_NUM
                                                                           AND B.ODR_DTL_SEQ  = A.ODR_DTL_SEQ
                                                                           AND B.SEQ_NO = A.SEQ_NO)
                                                   ) SJD ON SJD.ODR_NUM = OD.ODR_NUM AND SJD.ODR_DTL_SEQ = OD.ODR_DTL_SEQ
                              WHERE PM.PAY_DT BETWEEN #{startDt} AND #{endDt}
                              AND NVL(PM.PAY_AMT,0) > 0
                              GROUP BY PM.PAY_DT, OD.PRD_DVCD
							/* union all을 사용해서  group by (특정 상품에 대한 조인이 쉽지않을 때 사용)*/
                           UNION ALL
                           
                           SELECT
                                   'BA16' AS PRD_DVCD
                                  ,FINALBUYCFDT AS PAY_DT
                                  ,SUM(1) AS PAY_CNT
                                  ,SUM(NVL(TO_NUMBER(COVINSAMT),0)) AS PAY_AMT
                             FROM HSVC_EW_INSUPRD
                             WHERE FINALBUYCFDT BETWEEN #{startDt} AND #{endDt}
                             AND NVL(TO_NUMBER(COVINSAMT),0) > 0
                             GROUP BY FINALBUYCFDT
                           ) POM
                    WHERE DI.YMD = POM.PAY_DT(+)
                  ) DI_POM,
                  (SELECT PRD_DVCD
                    FROM SL_PRD) SP
             WHERE SP.PRD_DVCD = DI_POM.PRD_DVCD(+)
             GROUP BY SP.PRD_TYPE
             ORDER BY SP.PRD_TYPE
          ) DI_POM_SP
     WHERE A.PRD_TYPE != 'SKIP'
</select>

 

 

우선 상품이 판매된 날짜와 상품 구분 값으로 SUM을 하게 됩니다

 

또 한,

상품 금액의 총합계를 구하여  상품별 총판매금액을 구하게 됩니다

 

상품 총판매금액
이용권 10000
상품구매 20000
내부 서비스 수수료 30000

 

 

그럼 쿼리를 분석하고 곱씹어보는 것도 공부가 되겠지요

그럼 오늘도 즐거운 공부 하셨길 바랍니다

그럼 다음 포스팅도 기대해주세요

 

우리의 대모험이 끝나기까지 말이지요

728x90
반응형
728x90
반응형

 

회원 가입 증가 현황 통계 쿼리

 

 

회원 가입 통계 쿼리를 만들어 보려고 합니다

저도 아직 익숙하지 않아서 조금 더 좋은 방법 있다면

댓글 남겨주세요~

 

 

등록일(REG_DT)을 기준으로 신규회원 가입현황을 알아볼 수 있게 쿼리를 만들어 봅시다

밑의 쿼리는 금일 신규가입 현황을 볼 수 있는 쿼리입니다

param값을 todayoneDay를 받아서 처리하기 때문에

기간별 신규회원 증가 수를 확인할 수 있습니다

 

여기 가장 중요한 것은 서브 쿼리를 만들 때 등록일 기준으로 카운팅을 하고 등록일 시분초로 기준으로

데이터를 추출하여 GROUP BY를 해야 한다는 것입니다

 

그럼 아래의 쿼리를 천천히 봅시다~

 

SELECT 
    decode(MB_TPCD, '0010', '일반', '0020', '개인사업자', '0030',  '법인', '0040', '단체') as memberType,
    REG_CNT AS total
  FROM (
        SELECT 
            T1.MB_TPCD
            , NVL(REG_DT, #{today}) AS REG_DT
            , REG_CNT
          FROM (
                SELECT
                    MBM.Mb_TPCD
                    , A.REG_DT
                    , COUNT(A.REG_DT) AS REG_CNT
                  FROM (
                        SELECT 
                            /* 오늘날짜를 할당해서 기간동안의 증가 현황을 볼 수 있게 추출한다 */
                            TO_CHAR(REG_DT, 'YYYYMMDD') AS REG_DT
                            , MB_TPCD
                        FROM MB_MST
                        /* 오늘날짜를 할당해서 기간동안의 증가 현황을 볼 수 있게 추출한다 */
                        WHERE REG_DT BETWEEN TO_DATE(#{today} || '000000', 'YYYYMMDDHH24MISS') AND TO_DATE(#{oneDay} || '235959', 'YYYYMMDDHH24MISS')                                    
                        ) AS MBM,
                GROUP BY MBM.MB_TPCD, MBM.REG_DT
               ) AS T1
       ) AS VW1
 /* 금일이나 특정날을 지정해서 증가 현황을 확인할 수 있다 */
 WHERE VW1.REG_DT BETWEEN TO_DATE(#{today}) AND TO_DATE(#{oneDay})                                    
 ORDER BY MB_TPCD ASC

 

이렇게 신규 회원 증가 현황 쿼리를 알아보았습니다

금일도 괜찮고 기간별로도 조회할 수 있는 쿼리를 만들어 보았습니다

통계는 비즈니스적인 요소가 많이 필요하다고 생각합니다

 

회원 통계는 그렇게 많이 필요하지 않지만

어떤 상품에 대한 판매현황, 광고효과 분석 통계 쿼리를 만들기 위해서는

조금은 더 복잡한 쿼리가 되지 않을까 생각합니다

그럼 오늘은 여기까지 하고 다음 포스팅을 기대해주세요

 

언제나 우리의 대모험을 위해

오늘도 한 걸음씩 나아갑시다

 

그럼 파이팅!!

728x90
반응형
728x90
반응형

회원 월별 분기별 년도별 통계 쿼리

 

 

오늘은 회원별 통계 쿼리를 알아보도록 하겠습니다

mybatis를 활용해서 여러 조건문을 한번에 처리하는 쿼리를 작성하도록 하겠습니다

 

selectType을 프론트 단에서 month, quarter, recentYear의 값들을 받아옵니다

그리고 회원 테이블인 MB_MST에서 등록일자와 회원구분에 대한 값을 서브쿼리로 작성합니다

 

회원 구분별로 GROUP BY ROLLUP을 하여 진행한다는 것이 핵심입니다

 

GROUP BY ROLLUP 밑에 포스팅도 걸어 놓았습니다

 

2020.03.08 - [IT_Web/Oracle] - Oracle ROLLUP, CUBE, GROUPING(), GROUPING SETS(), GROUP_ID() 함수 개념

 

Oracle ROLLUP, CUBE, GROUPING(), GROUPING SETS(), GROUP_ID() 함수 개념

ROLLUP ROLLUP은 SELECT한 컬럼들을 GROUP BY 절과 함께 사용하게 되면 소계를 구할 수 있다 그 과정에는 CARTESIAN PRODUCT를 이용한 결과물들이란 것을 알아두자 또 한 ROLLUP의 인수는 계층 구조이므로 인수

tantangerine.tistory.com

 

 

그럼 실제 코드 사례를 한번 보도록 하겠습니다

자세히 보시면 등록일로 하여 컨트롤 하는 것을 보실 수 있습니다

 

 

<select id="selectNewMemberDetail" parameterType="Map" resultType="Map">
    SELECT 
        /*  DECODE를 활용해서 회원구분별로 누적 합계를 추출한다 */
        decode(CD_ID, '0010', 'normalSum', '0020', 'individualSum', '0030',  'organizationSum', '0040', 'affilicorpSum','membertypeSum') as memberType
    <choose>
        <when test="selectType == 'month'"> /*  DECODE 함수를 활용해서 월별로 필드를 추출한다 */
            , count(decode(to_char(reg_dt, 'MM'),'01',0)) "M01"
            , count(decode(to_char(reg_dt, 'MM'),'02',0)) "M02"
            , count(decode(to_char(reg_dt, 'MM'),'03',0)) "M03"
            , count(decode(to_char(reg_dt, 'MM'),'04',0)) "M04"
            , count(decode(to_char(reg_dt, 'MM'),'05',0)) "M05"
            , count(decode(to_char(reg_dt, 'MM'),'06',0)) "M06"
            , count(decode(to_char(reg_dt, 'MM'),'07',0)) "M07"
            , count(decode(to_char(reg_dt, 'MM'),'08',0)) "M08"
            , count(decode(to_char(reg_dt, 'MM'),'09',0)) "M09"
            , count(decode(to_char(reg_dt, 'MM'),'10',0)) "M10"
            , count(decode(to_char(reg_dt, 'MM'),'11',0)) "M11"
            , count(decode(to_char(reg_dt, 'MM'),'12',0)) "M12" 
        </when>
        <when test="selectType == 'quarter'"> /*  DECODE 함수를 활용해서 분기별로 필드를 추출한다 */
            , count(decode(to_char(reg_dt, 'MM'),'01',0)) + count(decode(to_char(reg_dt, 'MM'),'02',0)) + count(decode(to_char(reg_dt, 'MM'),'03',0)) "Q1"
            , count(decode(to_char(reg_dt, 'MM'),'04',0)) + count(decode(to_char(reg_dt, 'MM'),'05',0)) + count(decode(to_char(reg_dt, 'MM'),'06',0)) "Q2"
            , count(decode(to_char(reg_dt, 'MM'),'07',0)) + count(decode(to_char(reg_dt, 'MM'),'08',0)) + count(decode(to_char(reg_dt, 'MM'),'09',0)) "Q3"
            , count(decode(to_char(reg_dt, 'MM'),'10',0)) + count(decode(to_char(reg_dt, 'MM'),'11',0)) + count(decode(to_char(reg_dt, 'MM'),'12',0)) "Q4"
        </when>
        <when test="selectType == 'recentYear'"> /*  DECODE 함수를 활용해서 년별로 필드를 추출한다 */
            , count(decode(to_char(reg_dt, 'YYYY'),to_char(to_char(sysdate,'YYYY')-3),0)) "currentYear03"
            , count(decode(to_char(reg_dt, 'YYYY'),to_char(to_char(sysdate,'YYYY')-2),0)) "currentYear02"
            , count(decode(to_char(reg_dt, 'YYYY'),to_char(to_char(sysdate,'YYYY')-1),0)) "currentYear01"
            , count(decode(to_char(reg_dt, 'YYYY'),to_char(sysdate,'YYYY'),0)) "currentYear"
        </when>
   </choose> 
   /* DECODE 함수를 활용해서 전체합계 추출한다 */
   , count(mb_tpcd) total 
    FROM  
    (
        SELECT  
            reg_dt,
            MB_TPCD
        FROM MB_MST
        WHERE SUBSTR(reg_dt,1,4)  = #{selectYear}                
    ) MBM
    GROUP BY ROLLUP(MB_TPCD) /* 합계 및 소계를 나타낼수 있는 함수 */
</select>

 

 

이렇게 회원 월별 분기별 년도별을 알아보았습니다

조금은 어려울 수 있지만

작은 쿼리를 만들어서 테이블을 생성해서 하나씩 만들어보는 것도 좋을 듯합니다

그럼 오늘도 공부 열심히 하시고!

다음 포스팅도 많은 기대부탁드립니다~

 

728x90
반응형
728x90
반응형

 

오늘은 엑셀 데이터를 업로드해서 자바에서 데이터를 받아서

insert를 하든 아니면 다시 그 데이터를 다시 조합해서 웹에 다시 전달하는 등

핸들링하는 방법을 알아보겠습니다

 

그리고 클래스명을 잘 확인하도록 하시길 바랍니다

 


 

엑셀 데이터 읽어오기

   

@Override
public List<MemberVO> excelReadData(MemberVO memberVO) throws Exception {

    String[] columnNames = new String[20];
	
    // InputStream 클래스에 가지고 있는 파일의 bytes 생성자 매개변수에 할당하게 됩니다
    InputStream inputStream = new ByteArrayInputStream(memberVO.getMbFile()[0].getBytes());

	// inputStream에 엑셀파일이 들어가게 됩니다 이때 WorkbookFactory를 사용하여 파일의 엑셀데이트를 Workbook으로 만들어줍니다 
    org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(inputStream);
	
    // 그럼 workbook에 엑셀의 대한 정보값을 읽어올 준비가 됩니다 엑셀에 sheet도 정할수 있습니다
    // getSheetAt(0)의 값으로 엑셀의 첫번째를 읽어온다는 의미가된다
    org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
    
    // row별로 이제 iterator() 메서드를 사용하여 행별로 값을 읽어옵니다
    Iterator<Row> rowItr = sheet.iterator(); 

    List<MemberVO> memberList = new ArrayList<MemberVO>();

		// 이제 rowItr를 while로 전체 행의 데이터를 불러옵니다
        while(rowItr.hasNext()) { 
            MemberVO member = new MemberVO();
            Row row = rowItr.next(); 

            if(row.getRowNum() == 0) { 
            	// 첫번째행은 제목이나 컬럼명이 들어있기 때문에 그 컬럼명을 모아서 배열로 넣어 줍니다
                // cellItr에 열에대한 데이터를 0번째 행의 전체 열에 대한 데이터를 가져올 준비를 합니다
                Iterator<org.apache.poi.ss.usermodel.Cell> cellItr = row.cellIterator(); 
                // cellItr에 0번째 행의 열에 대한 데이터를 가져옵니다 그 데이터는 컬럼명이다
                while(cellItr.hasNext()) {
                    org.apache.poi.ss.usermodel.Cell cell = cellItr.next();
                    int index = cell.getColumnIndex();
                    columnNames[index] = cell.getStringCellValue().toString();
                }
                continue; 
            } 
            Iterator<org.apache.poi.ss.usermodel.Cell> cellItr = row.cellIterator(); 

			// 컬럼명의 예상 값이다
            //String[] columnNames = {"우편번호","주소","상세주소","이름","아이디","휴대폰번호","이메일주소","회원가입구분"};
			
            // 이제 인설트할 비즈니스 코드를 우선 조합합니다
            String mbTpcd = "0020";
            String mbAprvKncd = "0010".equals(mbTpcd) ? "0020" : "0010"; 
            member.setMbSttCd("0010".equals(mbTpcd) ? "0030" : "0020");
            member.setMbAprvKncd(mbAprvKncd);
            member.setMbTpcd(mbTpcd);
            member.setSeqNo(memberVO.getSeqNo());
            member.setCtyCd(memberVO.getCtyCd());
            member.setLocCd(memberVO.getLocCd());

            String mbBrn = "";
			
            // 다시 전체 열에 대한 데이터를 가져옵니다 그 데이터를 하나씩 불러와 데이터를 셋팅한다
            while(cellItr.hasNext()) {
            
            	// 한개의 열에 대한 데이터를 가져와 cell에 담습니다
                org.apache.poi.ss.usermodel.Cell cell = cellItr.next();
                
                // 열의 인덱스 넘버를 받아와 처음 0번째 행의 컬럼명을 불러와 case문과 일치할 경우 그 데이터를 삽입합니다
                int index = cell.getColumnIndex();
                if(!"".equals(getValueFromCell(cell))) {
                
                	// columnNames 배열에는 컬럼명을 담아 놓았기때문에
                    switch(columnNames[index].replaceAll(" ","")) {
                       case "우편번호": // 우편번호
                       	   // 숫자를 읽어 올때 소수점으로 들고 올때가 있다 이 경우를 위해 분기작업을 진행한다
                           if(getValueFromCell(cell) instanceof Double) {
                               String stringValue = String.format("%f", getValueFromCell(cell));
                               int indx = stringValue.indexOf(".");
                               String value =  stringValue.substring(0, indx);
                               member.setMbZcd(value);
                               member.setMbStrZcd(value);

                           } else {
                               member.setMbZcd((String)getValueFromCell(cell)); 
                               member.setMbStrZcd((String)getValueFromCell(cell)); 
                           }
                           break; 
                       case "주소": // 주소 
                           member.setMbAddrEnc((String)getValueFromCell(cell)); 
                           member.setMbStrAddr((String)getValueFromCell(cell)); 
                           break; 
                       case "상세주소": // 상세주소
                           member.setMbDtlAddrEnc((String)getValueFromCell(cell)); 
                           member.setMbStrDtlAddr((String)getValueFromCell(cell)); 
                           break; 
                       case "이름": // 이름 
                           member.setMbNm((String)getValueFromCell(cell)); 
                           break; 
                       case "아이디": // 아이디 
                           member.setMbId((String)getValueFromCell(cell)); 
                           break; 
                       case "휴대폰번호": //  
                           member.setMbHpPnEnc(((String) getValueFromCell(cell)).replaceAll("-","")); 
                           member.setMbHpPn(((String) getValueFromCell(cell))); 
                           break; 
                       case "이메일주소": // 이메일주소 
                           member.setMbEmlAddrEnc((String)getValueFromCell(cell)); 
                           break; 
                       case "회원가입구분": // 회원가입구분 
                           String mbJoinDvcd = "";
                           String cellValue = ((String) getValueFromCell(cell)).replaceAll("\\p{Z}", "");
                           if("딜러".equals(cellValue)){
                               mbJoinDvcd = "0010";
                           } else if("대표".equals(cellValue)){
                               mbJoinDvcd = "0020";
                               member.setPeprNm(member.getMbNm());
                           }
                           member.setMbJoinDvcd(mbJoinDvcd);
                           break; 
                    }
                }
            }
            // member에 모든 데이터를 set한 상태이다 그리고 uploadType이 기능은 타입이 두가지이다
            // 데이터를 인설트 하는 것과 엑셀의 데이터를 읽어서 웹화면에 노출해주는 기능 두가지이다
            if("insert".equals(memberVO.getUploadType())){
                if(!mbBrn.equals(member.getMbBrn())){
                    member.setReprHpnoEnc(member.getMbHpPnEnc());
                    member.setEntrFaxnoEnc(member.getMbStrFaxno());
                    member.setBrn(member.getMbBrn());
                    member.setEntrZcd(member.getMbZcd());
                    member.setEntrAddrEnc(member.getMbAddrEnc());
                    member.setEntrDtlAddrEnc(member.getMbDtlAddrEnc());
                }
            }

			// List형에 member를 담아서 한방에 insert를 한다
            memberList.add(member); 

        }
        // while 끝나고 한방에 인설트 한다
        if("insert".equals(memberVO.getUploadType())){
            int resCnt = memberDealerService.insertMbInfoList(memberList);
            if(resCnt <= 0){
                memberList = new ArrayList<MemberVO>();
            }
        }
        
        // uploadType이 insert가 아닐 경우 데이트를 인설트 하지않고 그냥 데이터만 return한다
        return memberList;

}

 

 

반응형

 

 

위의 코드에서 getValueFromCell 메서드선언문을 살펴보자

String, Boolean, Numeric 등의 자료형일 경우 데이터를 return 한다

 

 

  // 셀서식에 맞게 값 읽기 
private static Object getValueFromCell(org.apache.poi.ss.usermodel.Cell cell) { 
    switch(cell.getCellType()) { 
        case STRING: return cell.getStringCellValue(); 
        case BOOLEAN: return cell.getBooleanCellValue(); 
        case NUMERIC: 
            if(DateUtil.isCellDateFormatted(cell)) { 
                return cell.getDateCellValue(); 
             } 
            return cell.getNumericCellValue(); 
        case FORMULA: return cell.getCellFormula(); 
        case BLANK: return "";
        default: 
            return ""; 
    } 
}

 

 

mybatisforeach를 활용해서 여러 개의 데이터를 insert를 합니다

아래와 같이 문법에 맞게 값을 할당하면 간편히 다중 insert가 가능합니다

 

 


다중 insert 하기

 

 

 <insert id="insertMbInfoList" parameterType="java.util.List">
        INSERT  
        INTO MB_MST 
        (
             MB_ID,                        /* 회원 ID                      */                      
            MB_NM,                        /* 회원 명                       */          
            MB_HP_PN_ENC,                 /* 회원 휴대폰 전화번호 암호화    */           
            MB_TPCD,                      /* 회원 유형코드                    */      
            MB_ZCD,                       /* 회원 우편번호                    */      
            MB_ADDR_ENC,                  /* 회원 주소 암호화              */       
            MB_DTL_ADDR_ENC,              /* 회원 상세 주소 암호화           */        
        )
        VALUES
     <foreach collection="list" index="index" item="mbInfo" separator="," >
        (
            #{mbInfo.mbId},
            #{mbInfo.mbNm},
            #{mbInfo.mbPwdEnc},
            XX1.ENC_VARCHAR2_INS(#{mbInfo.mbHpPnEnc},10,'GLVSSL'),
            #{mbInfo.mbTpcd},
            #{mbInfo.mbZcd},
            XX1.ENC_VARCHAR2_INS(#{mbInfo.mbAddrEnc},10,'GLVSSL'),
            XX1.ENC_VARCHAR2_INS(#{mbInfo.mbDtlAddrEnc},10,'GLVSSL'),
        )
     </foreach>
</insert>

 

 


 

이렇게 엑셀 데이터를 읽어와서 그 데이터를 조합하여 다중 insert를 만들어 보았습니다

이것을 활용해서 조합한 데이터를 인설트를 하지 않고 return 해서 웹 화면에 노출하는 것도 가능합니다

어드민 페이지를 만들다 보면 업무에 필요한 작업들을 코딩해야 하는 경우가 있는데

엑셀 작업을 많이 하는 작업자들의 요청은 피할 수 없는 것 같습니다

 

그럼 수고하시고!!

언제나 조금씩 조금씩 한 걸음씩 나갑시다

우리 대모험은 끝나지 않았으니까요!!

 

728x90
반응형
728x90
반응형

 

이전 포스팅에서 웹팩에서 빌드 파일 자동 삭제 및 

이미지 로딩시간을 단축하기 위해

빌드할 경우 CSS파일을 생성하는 것을 알아보았습니다

 

 

아직 못 보신 분들은 아래의 링크로 확인해 보시길 바랍니다

2022.08.01 - [IT_Web/Webpack] - 웹팩 빌드시 파일 자동 삭제 및 webpack build시 CSS파일 생성

 

웹팩 빌드시 파일자동 삭제 및 webpack build시 CSS파일 생성

이전 포스팅에서는 이미지 관련 파일 적용과 실시간 코드 반영을 설정하는 법을 알아보았습니다 이제 build시 파일 자동 삭제 및 웹팩 초기 설정 방법 2022.07.23 - 자바스크립트 파일을 하나로 Webpac

tantangerine.tistory.com

 

 

CSS 압축하기

 

우선 필요한 라이브러리를 설치합니다

 

 

npm i optimize-css-assets-webpack-plugin --save-dev

 

아래와 같이 추가 부분을  확인하시고 코드를 수정합니다

 

webpack.prod.js

onst OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); // 추가부분

module.exports = merge(common, {
    mode: "production",
    output: {……},
    optimization: {
        minimizer: [
            new OptimizeCssAssetsPlugin()
        ]
    },// 추가 부분
    plugins: [……],
    module: {……}
});

 

 

npm run build를 실행하면

CSS 파일은 압축되지만 JS파일은 압축이 해제됩니다

그래서 추가적으로 라이브러리를 한 개더 사용합니다

 

그럼 아래의 라이브러리를 설치합니다

 

npm i terser-webpack-plugin --save-dev

 

 

그리고 추가적으로 코드 작업을 아래와 같이 진행합니다

 

 

webpack.prod.js

const TerserPlugin = require('terser-webpack-plugin'); // 추가부분

module.exports = merge(common, {
    mode: "production",
    output: {
        filename: "[name].[contentHash].bundle.js",
        path: path.resolve(__dirname, "dist")
    },
    optimization: {
        minimizer: [
            new OptimizeCssAssetsPlugin(),
            new TerserPlugin()
        ]
    }, // 추가 부분
    plugins: [……],
    module: {……}
});

 

 


 

html 파일 압축하기

 

html 파일을 압축해보겠습니다

common 파일 안에 htmlWebpackPlugin 관련 코드를 dev안으로 옮겨줍니다.

 

 

const path = require("path");
module.exports = {
    entry: "./src/index.js",
    module: {
        rules: [
            {
                test: /\.html$/,
                use: ["html-loader"]
            },
            {
                test: /\.(svg|png|jpg|gif)$/,
                use: {
                	loader: "file-loader",
                    options: {
                        name: "[name].[hash].[ext]",
                        outputPath: "imgs"
                    }
                }
            }
        ]
    },
    /*plugins: [
    	new HtmlWebpackPlugin({
 			template: "./src/template.html"
 	})] 잘라내기 dev파일로 옮기기*/
};

 

 

잘라내기한 코드를 아래와 같이 붙여줍니다

 

 

webpack.dev.js

const HtmlWebpackPlugin = require('html-webpack-plugin');// 추가부분

module.exports = merge(common, {
    mode: "development",
    output: {……},
    module: {……},
    plugins: [
    	new HtmlWebpackPlugin({
    		template: "./src/template.html"
    })]// 추가부분
})

 

그리고 아래와 같이 추가하여 코드를 마무리합니다

이렇게 하면 끝입니다

 

 

webpack.prod.js

const HtmlWebpackPlugin = require('html-webpack-plugin'); //추가부분

module.exports = merge(common, {
    mode: "production",
    output: {……},
    optimization: {
        minimizer: [
        	new OptimizeCssAssetsPlugin(),
        	new TerserPlugin(),
            new HtmlWebpackPlugin({
                template: "./src/template.html",
                minify: {
                    removeAttributeQuotes: true,
                    collapseWhitespace: true,
                    removeComments: true
                }
            })// 추가 부분
        ]
    },
    plugins: [……],
    module: {……}
})

 

 

 

이것으로 웹팩을 활용하여 CSS, HTML, JS파일 압축하는 방법을 알아보았습니다

간단하면서도 어려운 것 같네요

 

이제까지 진행된 총 8개의 포스팅이 있습니다

앞으로 4개 정도 더 있습니다

이전 포스팅이 궁금하시다면 카테고리의 webpack을 클릭하셔서 보실 수 있습니다

 

그럼 오늘도 공부하느라 고생하셨습니다

그럼 끝까지 파이팅 하세요

 

대모험의 끝을 봐야겠죠? ㅎㅎ

 

그럼 다음 포스팅에서 봐요~

 

728x90
반응형
728x90
반응형

 

 

이전 포스팅에서는 이미지 관련 파일 적용과

실시간 코드 반영을 설정하는 법을 알아보았습니다

이제 build시 파일 자동 삭제 및 CSS파일을 생성하는 방법을 알아보도록 하겠습니다

 

이제 것 많은 웹팩을 만들기 위해 많은 7개의 포스팅을 했습니다

이전 포스팅들을 확인하고싶으시면 카테고리 webpack을 클릭하시면

보실 수 있습니다

 

 

이전 포스팅은 아래에 링크로 걸어 두도록 하겠습니다

 

2022.07.27 - [IT_Web/Webpack] - 웹팩 실시간 코드 반영 및 Webpack file-loader 이미지 파일 적용하기

 

웹팩 실시간 코드 반영 및 Webpack file-loader 이미지 파일 적용하기

이전 포스팅에서 개발, 운영, 공통 파일을 만들어서 관리하는 기능을 알아보았습니다 궁금하시다면 밑에 목차에서 5번을 클릭해서 확인하시길 바랍니다 웹팩 초기 설정 방법 2022.07.23 - 자바스크

tantangerine.tistory.com

 

 

 

현재 상태에서 build하면 새로운 js파일이 생성됩니다.

이렇게 생성된 파일은 불필요한 파일이라서 자동 삭제하는 방법을 알아보겠습니다

 

 


 

 

clean-webpack 기능 설정하기

 

 

 

우선 아래의 코드로 라이브러리를 설치합니다

 

pm i clean-webpack-plugin --save-dev

 

 

그리고 아래와 같이 webpack.prod.js파일을 수정합니다

그리고 npm run build을 한다면 파일이 한개만 생성되는 것을 확인할 수 있습니다.

 

 

const path = require("path");
const common = require('./webpack.common');
const { merge } = require('webpack-merge');

const { CleanWebpackPlugin } = require('clean-webpack-plugin'); //추가부분

module.exports = merge(common, {
    mode: "production",
    output: {
        filename: "main.[contentHash].js",
        path: path.resolve(__dirname, "dist")
    },
    plugins: [new CleanWebpackPlugin()] //추가부분
});

 

 

 


 

mini-css-extract-plugin 기능 설정하기

 

자바스크립트는 인터프린터언어라서 해석해주는 단계가 있기 때문에 CSS 적용하기까지 어느 정도의 시간이 소요된다

그래서 빌드를 할때에도 CSS 파일로써 디자인을 적용하는 방법인

mini-css-extract-plugin 라이브러리를 사용해서 문제를 해결합니다

 

 

우선 아래와 같이 코드를 수정하여 주시길 바랍니다

 

webpack.common.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require("path");
module.exports = {
    entry: "./src/index.js",
    module: {
        rules: [
            /*{
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },이 부분은 잘라내기하여 webpack.dev.js에 붙여넣습니다*/
            {
                test: /\.html$/,
                use: ["html-loader"]
            },
            {
                test: /\.(svg|png|jpg|gif)$/,
                use: {
                    loader: "file-loader",
                    options: {
                        name: "[name].[hash].[ext]",
                        outputPath: "imgs"
                    }
                }
         	}
        ]
    },
    plugins: [
    	new HtmlWebpackPlugin({
    		template: "./src/template.html"
    	})
    ]
};

 

 

개발 버전에서는 문제가 되지 않아 기존 작업을 유지합니다

 

 

webpack.dev.js

module.exports = merge(common, {
    mode: "development",
    output: {......},
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }
        ]
    } // webpack.common.js 파일의 css 모듈을 여기에다 붙여넣는다
});

 

 

운영에서 배포할 때가 중요합니다

CSS파일로써 디자인을 적용하기 위해 아래와 같이 코드 수정을 합니다

 

 

webpack.prod.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 추가 부분
module.exports = merge(common, {
    mode: "production",
    output: {......},
    plugins: [
        new MiniCssExtractPlugin({filename: "[name].[contentHash].css"}),
        new CleanWebpackPlugin()
    ], // 추가 부분
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, "css-loader"]
            }
        ]
    } // 추가 부분
});

 

 이렇게 설정을 하게 되면

빌드를 실행할 경우 CSS파일이 생성되는 것을 확인할 수 있습니다

 

그럼 다음 포스팅에서는 빌드를 실행할 경우  CSS, HTML, JS 파일을 하는 방법을 알아보겠습니다

이렇게 또 한 걸음씩 나아가네요!

 

꾸준히 하다 보면 달라진 우리들 모습을 볼 수 있겠죠!?

그럼 파이팅 하시고 

다음 포스팅도 기대해주세요!

 

우리 모험은 끝나지 않았습니다!

 

 

 

728x90
반응형
728x90
반응형

이번 포스팅은 mybatis를 활용해서 다중 update를 알아보려고 합니다

 

여기서 PL/SQL을 사용한다는 것이 조금은 다른점입니다

물론 아시는분은 뭐이게 달라라고 말할 수 있지만

그래도 모르는 사람들에게는 조금은 생소한 이야기입니다

 

우선 PL/SQL을 간단히 살펴보겠습니다

 

DECLARE 
    
    M_NAME VARCHAR2(50); --초기 변수 선언
    
    BEGIN
       
       M_NAME := '귤귤의대모험'; --변수에 값 대입 실시
         
       DBMS_OUTPUT.PUT_LINE('M_NAME : ' || M_NAME);
       DBMS_OUTPUT.PUT_LINE('=============================');     
       
    END;

 

  • declare begin end - PL/SQL 기본 문법을 쿼리를 실행합니다
  • declare [선언부] - 변수, 상수를 선언할 수 있습니다
  • begin [실행부] - 제어, 반복문, 함수 등 다양한 로직 기술을 실행합니다
  • end [종료부] - 실행된 로직의 종료를 선언합니다

 

실행한 결과는 DBMS_OUTPUT에서 확인할 수 있습니다

 

위와 같이 PL/SQL를 활용하여 SQL을 실행할 수 있습니다

그럼 이것을 mybatis와 같이 활용해서 update를 해겠습니다

 

 

 

우선 자바에서 서비스단에서 부터 시작해보겠습니다

 

유저의 계정제한 기능을 만드려고 합니다

 

프론트단에서 리스트형으로 아이디 정보를 

받아서 데이터를 처리한다고 합시다

 

@Override
public int updateMbList(List<Map<String, Object>> mbList) throws Exception {

    int resultCount = 0;

    try {        

    	resultCount = adminMbInfoDAO.updateMbList(mbList);

    } catch(Exception e) {
        logger.info(e.toString());
        return 0;
    }

    return resultCount;
}

 

 

아래와 같이 DAO단을 호출하여 실행하게됩니다

mybatis의 foreach를 사용하여 전체 update문을 반복 실행하게 합니다

  

이때 중요한것은 PL/SQL을 사용한다는 점입니다

 

<update id="updateMbList" parameterType="java.util.List">
     <foreach collection="list" index="index" item="mbInfo" open="DECLARE BEGIN" separator=";" close="; END;">
        UPDATE
              MB_MST
        SET
           UPDT_ID = #{mbInfo.mbId}
           , UPD_DT = SYSDATE
           , MB_TYPE= '0050'
           , RSNS_RSTRC = #{mbInfo.rsnsRstrc}
        WHERE MB_ID = #{mbInfo.mbId}
    </foreach>
</update>

 

위의 foreach문을 자세히 보시면 open, separator, close가 보일것입니다

open은 처음 쿼리를 실행할 경우 사용할 문자이며,

separator는 반복문 한개가 끝나고 붙여줄 문자입니다

close는 반복문이 끝나면 마지막에 붙여줄 문자입니다

 

그럼 최종적으로 아래와 같이 PL/SQL문이 완성됩니다

당연히 #{}은 해당 값으로 치환이 될것입니다

 

DECLARE BEGIN  
     UPDATE
          MB_MST
     SET
       UPDT_ID = #{mbInfo.mbId}
       , UPD_DT = SYSDATE
       , MB_TYPE= '0050'
       , RSNS_RSTRC = #{mbInfo.rsnsRstrc}
     WHERE MB_ID = #{mbInfo.mbId};
     UPDATE
          MB_MST
     SET
       UPDT_ID = #{mbInfo.mbId}
       , UPD_DT = SYSDATE
       , MB_TYPE= '0050'
       , RSNS_RSTRC = #{mbInfo.rsnsRstrc}
     WHERE MB_ID = #{mbInfo.mbId};
END

 

 

이 방법 이외에도 

자바에서 for문을 돌려서 for문안에서 update문을 계속적으 실행하는 방법이 있을것입니다

하지만 이 방법보다는 지금 현재의 방법이 더 성능이나 코드의 간결함이 있으니 

현재 위의 방법으로 해결하시길바랍니다

 

앞으로도 IT 개발 공부를 꾸준히 !!

아직 우리의 대모험은 끝나지 않았으니까요!!

728x90
반응형
728x90
반응형

이 전 글에서 캐시 무효화를 알아보았습니다

못 보신 분이 계시다면

아래의 링크를 확인해보세요

 

2022.07.26 - 웹팩(Webpack) 번들링 파일 자동연결 기능 사용하기(캐시 무효화 문제 해결하기)

 

웹팩(Webpack) 번들링 파일 자동연결 기능 사용하기(캐시 무효화 문제 해결하기)

이전 글에서는 캐시 무효화 작업할 수 있게 webpack.config.js에 코드를 수정하는 방법을 알아보았습니다 이전 글에 대한 리스트가 있으니 못보셨다면 확인해보세요 웹팩 초기 설정 방법 2022.07.23 -

tantangerine.tistory.com

 

그 외에도 웹팩을 설정하기위한 단계별 포스팅이 있으니 참고 하시길바랍니다

그럼 본론으로 넘어가보겠습니다

 

 


 

Webpack-merge란?

 

이제 본론으로 넘어갑시다

앱을 개발이 끝나고 운영을 넘어가게 되면

개발과 운영을 분리하여 관리하는 게 더욱 효과적입니다

이때 웹팩에서 분리하여 관리가 가능합니다

 

이 작업을 위해서는 공통 파일과 다른 파일을 연동시키기 위한 새로운 라이브러리가 필요합니다

그것이 webpack-merge입니다

 

 


 

 

Webpack-merge 기능 활용 및 설정

아래와 같이 라이브러리를 설치합니다

 

npm i webpack-merge --save-dev

 

 

 

그리고 필요한 파일들을 생성합니다

webpack.common.js, webpack.dev.js, webpack.prod.js

 

 

 

 

webpack.common.js

이 파일은 웹팩의 초기 설정값을 담당합니다

common의 설정값을 dev, prod에게 merge를 하게 됩니다

 

const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
  entry: "./src/index.js",
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/template.html",
    }),
  ],
};

 

 

 

 

webpack.dev.js

아래의 코드를 보면 common에서 설정된 플러그인과 entry, module 등이 없는 것을 확인할 수 있습니다

개발은 캐시 무효화가 필요 없기 때문에 캐시 무효화 기능은 삭제합니다

 

const path = require("path");
const common = require("./webpack.common");
const { merge } = require("webpack-merge");

module.exports = merge(common, {
  mode: "development",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  },
});

 

 

 

 

webpack.prod.js

아래의 코드에서 캐시무효화 작업이 포함된 것을 확인할 수 있습니다

 

const path = require("path");
const common = require("./webpack.common");
const { merge } = require("webpack-merge");

module.exports = merge(common, {
  mode: "production",
  output: {
    filename: "main.[contenthash].js",
    path: path.resolve(__dirname, "dist"),
  },
});

 

 

 

 

package.json

package.json 코드를 수정합니다

개발의 소스코드와 운영의 소스코드를 개별적으로 관리하여

운영에서 필요한 소스코드를 우선적으로 개발에서 수정하여

테스트를 거쳐 사이드 이펙트가 있는지 확인하기 유용한 기능입니다

 

  "scripts": {
    "start": "webpack --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },

 

 

 

이렇게 개발, 운영을 분리하여 관리하는 

webpack-merge 기능의 설정하는 방법을 알아보았습니다

 

이번 웹팩 포스팅은 그렇게 길지 않아서 피로도가 그렇게 높지는 않네요

다음 포스팅은 개발 서버에서 코드가 변경될 경우 실시간으로 반영하는 기능과

빌드를 실행할 경우 CSS 파일 생성하여 로딩시간을 단축하는 방법을 알아보겠습니다

 

또 한,

아직 많은 기능들이 남아있으니

앞으로 많은 기대부탁드립니다

그럼 힘내시고 대모험을 즐겨 보아요~

 

파이팅!!

 

 

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅에서 프로젝트 시 유용한 기능인

개발, 운영, 공통 파일을 만들어서 개별적인 관리할 수 있게 하는 방법을 알아보았습니다

 

궁금하시다면 아래의 링크를 확인하시길바랍니다

이외에도 웹팩을 처음부터 끝까지 만들어가는 과정을 담기 위해

꾸준히 포스팅을 하고 있으니 카테고리 webpack을 클릭하시면

전체 과정을 보실 수 있습니다

 

 


 

  1. Splitting Dev & Production 
 

웹팩(Webpack) - 개발, 운영, 공통 파일 분리하여 관리하는 Webpack-merge 설치 및 활용

이 전 글에서 캐시 무효화를 알아보았습니다 못 보신 분이 계시다면 아래의 링크를 확인해보세요 2022.07.26 - 웹팩(Webpack) 번들링 파일 자동연결 기능 사용하기(캐시 무효화 문제 해결하기) 웹팩(We

tantangerine.tistory.com

 


 

webpack-dev-server 실시간 코드 반영 기능 적용하기

 

우선 실시간 코드 반영에 필요한

라이브러리 설치합니다

 

npm i webpack-dev-server --save-dev

 

 

아래와 같이 webpack-dev-server, --open을 적용하여 파일을 수정합니다

 

 

 

package.json

 "scripts": {
 	"start": "webpack-dev-server --config webpack.dev.js --open", // 수정부분
 	"build": "webpack --config webpack.prod.js"
 }

 

 

이때 개발을 실행시키기 전에 dist폴더를 삭제 하합니다

이제부터는 개발에서는 새로운 폴더를 만들지 않고

로컬 서버를 실행시킬 수 있습니다

 

npm start를 합니다

그렇게 되면 서버 재시작을 하지 않고

실시간으로 코드가 반영되는 걸 확인할 수 있습니다

 

이렇게

실시간 코드 반영은 간단하게 적용할 수 있습니다

 

 


 

 

html-loader, file-loader 이미지 파일 적용하기

 

html-loader, file-loader 라이브러리의 공식 홈페이지는

사이드바에 아래의 이미지처럼 구성되어있습니다

들어가시면 확인이 가능합니다

검색창에 file-loader, html-loader을 검색하시면 됩니다

 

webpack-document
웹팩공식문서

 

 

이미지 파일을 폴더에 넣어 준비합니다

아래와 같이 형성되어있으니 확인해 주세요

 

webpack-list
폴더구조

 

로더 라이브러리를 설치해줍니다

아래와 같이 추가합니다

 

 

 

npm i html-loader file-loader --save-dev

 

 

 

template.html 파일을 수정합니다

<img src="./assets/file-loader">를 추가하여

이미지를 불러올 준비를 합니다

 

<!DOCTYPE html>
<html lang="en">
  <head> </head>
  <body>
    <h1 class="card-title">웹팩 활용법</h1>
    <button class="btn-change">버튼 변경</button>
    <img src="./assets/file-loader.png" /> 
  </body>
</html>

 

 

이제 webpack-common.js를 수정합니다

 

각각 라이브러리를  testuse에 값을 할당하여

라이브러리를 불러옵니다

 

 

 

 

반응형

 

 

 

그리고 이미지도 사용할 확장자를 지정하면

그에 맞는 확장자만 불러올 수 있습니다

 

const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.html$/,
        use: ["html-loader"],
      },
      {
        test: /\.(svg|png|jpg|gif)$/,
        use: {
          loader: "file-loader",
          options: {
            name: "[name].[hash].[ext]",
            outputPath: "imgs",
          },
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/template.html",
    }),
  ],
};

 

 

속성 값은 아래와 같은 여러 값들을 적용해보고 알아보아요

 

  • limit : 파일 사이즈가 10k(설정값) 보다 작은 경우 번들로 변환 (Data URI Scheme)
  • fallback : 파일 사이즈가 nk 초과일 시, file-loader 이용하여 파일 복사
  • name : 복사된 파일을 지정 경로/파일명으로 저장
    • [name]: 복사한 파일의 파일명
    • [ext] : 복사한 파일의 확장자명
    • [hash] : 해쉬값
  • outputPath : 복사된 파일이 출력될 위치. 기본 output path 이후의 경로
    (module.exports.output.path = path.resolve(__dirname, 'public/dist')로 설정했기 때문에, 해당 소스에서는 기본 output path가 /public/dist이다.)
  • publicPath : 복사될 파일의 경로. 즉, url을 포함하고 있는 파일(ex:*. scss)의 경로를 기준으로 함. 따라서 url의 첫 부분이 해당 path값을 포함하고 있어야 복사됨

 

 

npm run build를 실행하면

아래와 같이 파일이 생성됩니다

파일 명이 다른 것은 캐시 무효화 작업입니다

무효화는 저번 포스팅 때 알아보았죠?

 

 

file-loader
빌드 후 파일 생성 화면

 

 

 

화면을 실행할 경우 아래와 같이 노출됩니다

이미지가 잘 노출되고 있는 걸 알 수 있습니다

 

 

 

webpack-loader
화면 결과

 

 

이렇게 웹팩 file-loader를 알아보았습니다

그렇게 어렵지는 않은 것 같습니다

 

아 그리고 지금 웹팩의 구축하는 버전은

아래와 같습니다

참고해주세요

 

 

웹팩버전
버전확인

 

그럼 다음에는

clean-webpack & Extract CSS & Minify HTML / CSS / JS를

알아보도록 하겠습니다

 

정말 힘든 대모험을 위해 달려봅시다

 

힘내시고

다음 포스팅에서 뵙겠습니다~

 

파이팅

728x90
반응형
728x90
반응형

 

이전 글에서는 캐시 무효화 작업할 수 있게

webpack.config.js에 코드를 수정하는 방법을 알아보았습니다

 

못 보셨다면 아래의 링크를 클릭해서 보시길 바랍니다

 

또 한,

웹팩의 모든것을 담기 위해 많은 포스팅을 올려놓았으니

카테고리의 webpack을 확인하시면 여러 가지를 보실 수 있습니다

 

 


  1. Cache Busting an Plugins ( 캐시 무효화 )
 

웹팩(Webpack) 번들링 시 캐시 무효화 설정하기 (Cache Busting)

이전 포스팅에서 웹팩 CSS Loader를 설치하고 CSS를 웹팩으로 적용을 해보았습니다 못 보신 포스팅이 있다면 아래의 리스트에서 확인해보시길 바랍니다 웹팩 초기 설정 방법 2022.07.23 - 자바스크립

tantangerine.tistory.com


 

 

html-webpack-plugin 설치 및 활용

 

 

캐시 무효화를 작업하면 파일을 새로 만들게 되면서

index.html의 경로가 변경되어있지 않아 한 가지 문제가 있습니다

 

 

캐시무효화-문제점
캐시무효화의 문제점

 

 

그래서 위의 네모칸에 들어가는 경로를 자동으로 변경해주는 라이브러리가

html-webpack-plugin입니다

 

 

아래와 같이 라이브러리를 설치를 합니다

 

 

npm i html-webpack-plugin --save-dev

 

 

아래와 같이 플러그인의 코드를 추가합니다

 

 

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 추가 부분
const path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "main.[contenthash].js",
    path: path.resolve(__dirname, "dist"),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [new HtmlWebpackPlugin({
  	template: "./src/template.html"
  })] // 추가 부분
};

 

 


 

 

캐시 무효화 문제 해결 하기

 

 

npm start를 하게 되면

아래와 같이 index.html이 변경이 되었습니다

단, 코드가 전체적으로 변경되어서

위의 코드의 웹팩활용법과 버튼이 사라지는 문제가 있습니다 

 

 

 

웹팩-자동연결기능
자동 연결 기능

 

 

아래의 코드를 추가하여 

template.html이라는 새로운 파일을 생성합니다

 

 

캐시무효화-해결법
캐시 무효화 해결법

 

 

template.html

코드를 작성하여주시길 바랍니다

 

<!doctype html>
<html lang="en">
<head>
</head>
<body>
  <h1 class="card-title">웹팩 활용법</h1>
  <button class="btn-change">버튼 변경</button>
</body>
</html

 

 

그리고

npm start를 실행하게 되면

아래와 같이 코드가 변경되어있음을 확인할 수 있습니다

 

 

 

 

캐시무효화-해결결과
캐시 무효화 해결결과

 

 

 

이제 잘 연결되어서 변경되기 전의

코드가 남겨져있는 것을 확인할 수 있습니다

이렇게 조금씩 웹팩을 만들어가니 먼가 성취감도 있고 좋은 것 같네요

 

이제 몇 개 안 남았습니다!

이제 웹팩을 손수 만들 수 있는 그날을 위해 

그럼 다음 글에서 만나겠습니다

 

파이팅!

728x90
반응형
728x90
반응형

 

이전 포스팅에서 웹팩 CSS Loader를 설치하고 CSS를 웹팩으로 적용을 해보았습니다

못 보신 포스팅이 있다면 아래의 리스트에서 확인해보시길 바랍니다

 

 

 

  1. 웹팩 초기 설정 방법
  2. CSS & SASS 적용하기
  3. Cache Busting an Plugins ( 캐시 무효화 )
    • 현재 포스팅입니다
  4. html-webpack-plugin 설치
    • 캐시가 적용된 스크립트 파일을 자동으로 index.html 연결할 때 사용됩니다.
  5. template 파일 생성
  6. Splitting Dev & Production 
  7. webpack-dev-server
  8. 이미지 파일 적용하기 ( html-loader, file-loader ) 
  9. clean-webpack
  10. Extract CSS & Minify HTML / CSS / JS
  11. 바벨 설치하기
  12. 리액트 적용해보기
  13. Multiple Entrypoints & Vendor.js
  14. 여러 페이지별 파일을 번들링 하기

 

 

캐시 무효화란?

CSS 코드를 웹사이트에 캐시 메모리에 저장하여 사용자가 접속했을 때

이전에 저장해둔 메모리를 바탕으로 코드를 불러오며 로딩 시간을 단축합니다

그렇다 보니 간혹 CSS를 수정하면 바로 적용이 안 되는 경우가 발생합니다

그 이유가 바로 그 때문입니다

 

이러한 경우가 발생할 때 

파일명 뒤에 알파벳과 숫자 조합하여 파일을 생성하여

새로운 파일명을 업로드하여

웹사이트에서 새로운 파일이 생성되었다는

인식을 주어 기존의 캐시를 삭제하고

새로운 파일을 저장하는 방법을

그것을 캐시 무효화라고 합니다

 

 

캐시무효화
캐시 무효화하기 위해 만든 파일명

 

 

 

캐시 무효화 코드 수정은 간단합니다

아래와 같이 filename부분을 [contenthash]를 추가만 하면 됩니다

 

 

 

const path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "main.[contenthash].js",
    path: path.resolve(__dirname, "dist"),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

 

 

그럼 번들이 된 파일에는 알파벳과 숫자 조합이 되어

새로운 파일로 계속 갱신해서 생성합니다

단, CSS파일에 코드가 수정되었을 때만

파일명이 새롭게 갱신이 됩니다

 

이렇게 오늘은 웹사이트에서 캐시 메모리에 어떠한 정보를 저장하는지

그 캐시가 어떻게 작동하는지도 알아보고

캐시 무효화가 어떤 이로움을 주는지 확인해 보았습니다

 

앞으로 10가지 항목이 더 남았으니

조금만 더 힘을 내세요!

 

대모험은 이제부터 시작입니다

파이팅

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅에서 웹팩이 무엇인지와

웹팩 초기 설정하는 방법을 알아보았습니다

혹시 못 보셨다면 아래의 포스팅을 참고해주세요.

 

그리고 웹팩에 관해 알아볼 것을 리스트로 정리해보았습니다

 

 


 

웹팩(Webpack) 공부 목차

 

  1. 웹팩 초기 설정 방법
  2. CSS & SASS 적용하기
    • 현재 포스팅입니다
  3. Cache Busting an Plugins ( 캐시 무효화 )
  4. html-webpack-plugin 설치
  5. template 파일 생성
  6. Splitting Dev & Production 
  7. webpack-dev-server
  8. 이미지 파일 적용하기 ( html-loader, file-loader ) 
  9. clean-webpack
  10. Extract CSS & Minify HTML / CSS / JS
  11. 바벨 설치하기
  12. 리액트 적용해보기
  13. Multiple Entrypoints & Vendor.js
  14. 여러 페이지별 파일을 번들링 하기

 


 

 

목차에 포스팅이 끝나면 내부 링크를 걸도록 하겠습니다

앞으로 많은 설정법을 통해 웹팩을 완벽하게 파헤쳐 봅시다!!

 

 

그럼 다시 본론으로 넘어가서

css적용 하기 이전에 우선 html과 필요한 js파일을 만들도록 하겠습니다 

 

 

CSS적용 전 준비단계

 

src폴더에 app이라는 폴더를 만들어서

button.jscolor.js 파일을 생성해 줍니다

 

 

 

button.js

 

const btnChange = document.querySelector(".btn-change");
export { btnChange };

 

 

color.js

 

const cardTitle = document.querySelector(".card-title");
const changeColor = function (color) {
  cardTitle.style.backgroundColor = color;
};
export { changeColor };

 

 

마지막으로 dist폴더 안에 index.html을 생성합니다

아래의 코드와 함께 말이지요!

 

 

index.html

 

<!DOCTYPE html>
<html lang="en">
  <head>
    ......
  </head>
  <body>
    <h1 class="card-title">웹팩 활용법</h1>
    <button class="btn-change">버튼 변경</button>
    <script src="main.js"></script>
  </body>
</html>

 

 

생성하셨다면 아래와 같이

폴더가 만들어졌겠지요?

 

 

 

웹팩CSS적용
폴더구조

 

 

그럼 지금까지 현재의 html을 실행하게 된다면

 

웹팩적용하기
웹팩 적용 전 이미지

 


 

웹팩(Webpack) CSS loader 설지 및 적용하기

 

 

그럼 이제 웹팩(Webpack)에

CSS를 적용하기 위해서는 CSS Loader 설치 및 적용이 필요합니다

아래 링크 주소에서 웹팩 Loader 종류와 CSS Loader 적용 방법을 확인해 봅시다

 

오른쪽 사이드 바에 공식문서가 링크가 있으니 들어가셔서

검색창에 loaders, css loader 검색하시면 확인하실 수 있습니다

 

웹팩공식문서
웹팩 공식문서 가이드

 

 

Loader는 타입 스크립트와 같은 다른 언어를 자바스크립트로 변경하거나 이미지를 dataURL로 변경할 수 있습니다

CSS 파일을 자바스크립트 모듈에서 직접 import 할 수 있는 방법도 제공합니다

 

 

우선 라이브러리를 설치합니다

 

npm install style-loader css-loader --save-de

 

 

다음 webpack.config.js를 수정합니다

모듈의 오브젝트 객체는 외부 패키지를 사용할 경우에

testuse를 사용해서 외부 패키 지명을 넣어 줍니다

 

이때 중요한 것은 패키지는 뒤에서부터 해석이 된다는 것들을 알고 계셔야 합니다.

그래서 css-loader를 불러온 다음에 style-loader에 적용하여 불러온다는 것을 기억하셔야 합니다.

 

그럼 아래의 코드로 변경해주세요

 

 

const path = require("path");

module.exports = {
 mode: "development",
 entry: "./src/index.js",
 output: {
     filename: "main.js",
     path: path.resolve(__dirname, "dist")
 },
 module: {
     rules: [
         {
             test: /\.css$/,
             use: ["style-loader", "css-loader"]
         }
     ]
 }
};

 

 

 

아래의 이미지에 보시면 css폴더가 보이시나요

그리고 하위 디렉터리 main.css가 생성된 것이 보이실 겁니다

 

 

css폴더경로
css폴더 경로

 

 

 

생성해주고 main.css에는 아래와 같은 코드를 추가하시길 바랍니다

 

body {
  background: yellow;
}

 

 

그렇게 되고 index.html을 실행하게 되면

아래와 같이 css가 적용된 화면이 노출됩니다

 

 

웹팩CSS적용화면
CSS적용 모습

 

 

이것을 응용해서 SASS 적용해보겠습니다

 

SASS 적용하기

 

 

 

아래의 라이브러리를 추가해줍니다

 

npm install sass-loader node-sass --save-dev

 

 

그리고 아래의 코드처럼 sass-loader를 추가해주며

기존 css파일의 확장자명을 scss로 변경해줍니다

 

 

  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
    ],
  },

 

 

동작 순서는 sass-loader -> css-loader -> style-loader가 되니 참고하세요!

 

 

이렇게 CSS loader 설치와 활용 방법을 알아보았습니다

어떻게 보면 정말 간단한데 모르면 정말 힘들겠죠

앞으로 많이 남았으니 같이 힘내 봅시다

 

대모험은 아직 끝나지 않아서 갈길이 너무 멀고도 험난하네요

사실 끝나지 않을 모험이라는 것을 잘 알고 있지만

 

아무튼 파이팅 합시다

 

 

728x90
반응형
728x90
반응형

 


 

웹팩(Webpack)이란?

 

웹팩이란
웹팩의 역할

 

SPA(Single Page Application) 개발이 많아지면서 자바 스크립트의 코드 분량이 크게 증가하여

초기 단계에서 UI 컴포넌트와 자바스크립트의 코드를 분리하여 작업을 수행하였습니다

그렇게 되면서 개별적인 자바스크립트 파일을 불러와 웹사이트 로딩 속도가 문제가 되었습니다

그래서 그 문제의 해결책으로 자바스크립트 파일을 하나의 js파일로 번들링 작업을 하게 되었고

그렇게 새롭게 등장한 웹팩(Webpack)입니다.

 

공식 사이트는 오른쪽 사이드바에 등록해놓겠습니다 

 

이러한 과정에서 웹팩(Webpack)은 번들링 작업에서는 필수적인 기능으로 발전하였습니다

요즘은 프로젝트를 생성할 초기에 자동으로 만들어주기때문에 

관심이 적어지기도 합니다

하지만 중요하고 어떤 과정으로 웹팩이 작동하는지도

한번알아보는것도 정말 중요한 과정입니다

그럼 설치부터 알아볼까요

 

 


 

 

웹팩(Webpack) 설치 및 설정

 

웹팩을 설치하기 위해서는 Node가 필요합니다.

다운로드하는 사이트는 사이드 바에 등록해놓겠습니다

설치 후 아래의 코드를 실행해서 

 

버전을 확인해서 설치유무를 확인해 봅니다

 

npm -v
node -v

 

 

설치가 완료되었으면, 실습용 폴더를 생성한 후

해당 폴더 안에서 npm 초기화 및 웹팩 설치 작업을 진행합니다.

실습용 폴더명 은 webpack을 제외하고,

작성해야 npm 충돌이 일어나지 않는다.

npm init -ypackage.json 생성 시 모든 기본값을 자동으로 채워줍니다

 

 

 

npm init -y
npm install webpack webpack-cli --save-dev

 

 

그리고 package.js 파일에서 script 부분을 수정해 줍니다

 

 

{
     "name": "Webpack_Test",
     "version": "1.0.0",
     "description": "",
     "main": "index.js",
     "scripts": {
         "start": "webpack" // 이부분을 수정한다
      },
     "keywords": [],
     "author": "",
     "license": "ISC",
     "devDependencies": {
         "webpack": "^4.44.1",
         "webpack-cli": "^3.3.12"
     }
}

 

 

 

웹팩(Webpack)으로 빌드하기 위해서는 src폴더 안에서 파일 관리가 이루어져야 합니다.

그럼 src 폴더와 index.js 파일을 생성한 다음 아래와 같이 코드를 작성한다

 

 

 

console.log("Hell World");

 

 

새로운 dist폴더와 함께 main.js파일이 생성되고 압축된 코드가 작성되어있다.

그리고 그 압축된 코드 안에는 console.log("Hell World");

가 포함되어있는 것을 확인할 수 있습니다

 

src폴더 안에 있는 파일들을 바탕으로 번들링 작업이 진행된다는 것을 알고 계시면 됩니다

그래서 src안에서 프로젝트 페이지등 작업이 이루어지게 됩니다

 

 

다음은 dist폴더 안에 index.html 파일을 아래의 코드를 작성해서 생성합니다

 

 

반응형

 

 

<!doctype html>
<html lang="en">
<head>
 ......
</head>
<body>
 <h1 class="card-title">Card Title</h1>
 <button class="btn-change">버튼 변경</button>
 <script src="main.js"></script>
</body>
</html>

 

 

그리고  index.html을 크롬 브라우저로 실핼하게되면

아래와 같은 이미지가 노출되며 콘솔로 hell world가 노출되는 것을

확인할 수 있습니다

 

 

웹팩-실행이미지
웹팩 실행 이미지

 

 

 

 

이제 모든 준비는 완료되었습니다

webpack을 좀 더 설정해봅시다

 

 

webpack.config.js 파일 생성하여 아래 코드를 작성합니다

해당 파일은 모듈이나 플러그인을 관리하는 파일입니다

 

 

module.exports = {
 entry: "./src/index.js",
 
 output: {
 filename: "hello.js",
 path: path.resolve(__dirname, "COOODE")
 }
};

 

  • entry : 압축시킬 파일 
  • output : 압축한 파일을 관리할 폴더와 파일명
  • path : 절대 경로 기준을 폴더 생성 ( 이러한 이유로 __dirname을 사용할 수 있음 )
  • resolve() : 인자로 받은 경로를 하나로 합쳐 문자열 형태로 리턴한다.
  • __dirname : 현재 경로를 의미

 

 

package.json 파일 경로를 기준으로 COOODE 폴더가 생성이 되고,

해당 폴더 안에 만들어진 hello.js 안에는 압축된 형태로 코드가 작성되어있습니다.

 

 

 

 

아래와 같이 다시 명령을 수정해줍니다

이 작업은 웹팩을 사용할 때 내가 설정한 파일의 기준으로 웹팩을 동작시키겠다는 명령어라고 생각합시다

 

 

 

"scripts": {
 "start": "webpack --config webpack.config.js"
 },

 

 

webpack.config.js를 다시 수정합니다

이제는 mode를 추가하며

filename도 우리가 정할수 있습니다

path도 설정이 가능합니다

 

const path = require("path");

module.exports = {
 mode: "development",
 entry: "./src/index.js",
 output: {
     filename: "main.js",
     path: path.resolve(__dirname, "dist")
 }
}

 

development : 개발자 모드로 배포할 때 사용합니다

pathnode.js의 함수인 require을 사용합니다

그리고 output 안에 path를 지정할 수 있는데

이것은 번들링 작업을 한 main.js의 파일을 어느 위치에 있는 폴더 안에 넣을것이을 지정할 수 있습니다

path.resolve(__dirname, 폴더명)이 함수는 프로젝트의 절대 경로를 문자열로 가져와

폴더는 dist로 지정하는 것입니다

dist폴더 안에 main.js를 생성 하게됩니다

 

또 한,

main.js를 확인해보시면 압축이 안되어진 상태일것입니다

그이유는 개발자모드이기 때문입니다

 

 

 

이렇게 웹팩을 설정법을 알아보았습니다

다음에는 CSS파일등은 더욱 많은 것들을 적용해보고

알아보도록 하겠습니다

그럼! 다음 포스팅 봐요!!

 

우리의 대모험은 끝나지않았습니다!!!

728x90
반응형
728x90
반응형

오라클에서는 트리구조가 아주 중요합니다

level ~ start with ~ connect by prior ~를 많이 사용합니다

그러나 오라클을 처음 접하는 사람은 이 구문을 한 번에 이해하기가 쉽지 않습니다

그래서 한번 이 구문의 예문을 통해 같이 알아보고자 합니다

 

 

아래와 같이 테이블이 존재합니다

 

SELECT DEPTNO, ENAME FROM EMP

 

 

 

가공 전 데이터

 

위의 데이터로 아래와 같이 출력하고자 합니다

어떤 사람은 case문으로 하면 안 되지 않을까 하고 의문을 갖는 사람도 있을 거라 생각합니다

당연히 가능합니다

하지만 case문은 deptno의 값이 쿼리에 값 조건문이 들어가야 합니다

이러한 경우는 deptno가 증가할 경우에 대해 전혀 대비가 되지 않은 쿼리라고 할 수 있습니다

프로젝트를 진행하다 보면 case문을 자주 사용하게 되는데

그 함정에 빠져 큰 그림을 보지 못하고 당장 앞만 보게 되는 경우가 있습니다

그래서 대도록이면 테이블의 데이터 값이 쿼리문에는 없는 것이 범용성이 좋은 쿼리라고 할 수 있습니다

 

그리고 이러한 재밌는 쿼리를 보면

여러 가지를 생각나게 합니다

밑에 deptno별로 모든 직원을 조회해야 한다면

밑에 처럼 안 하면 java에서 map형 list로 담아서 진행해야 합니다

하지만 아래처럼 한다면 list형 map으로 간단히 담아질 것으로 보입니다

하지만 그렇게 되면 자바에서 또다시 다른 작업을 진행해야 하겠지요

아무튼!!

계속해서 보도록 하겠습니다

 

 

 

최종 데이터

 

 

 

아래의 코드로 출력이 가능합니다

처음 접하면 너무 복잡하고 어려워 보이겠지만

같이 천천히 봅시다

 

 

 

 select deptno,
         ltrim(sys_connect_by_path(ename,','),',') emps
    from (
  select deptno,
         ename,
         row_number() over
                  (partition by deptno order by empno) rn,
         count(*) over
                  (partition by deptno) cnt
   from emp
        )
  where level = cnt
  start with rn = 1
 connect by prior deptno = deptno and prior rn = rn-1;

 

 

 

 

반응형

 

 

우선 서브 쿼리를 단독 실행해서 사원에 대한 소속 부서, 이름 empno 오름차순 정렬로 결과가 노출되고 있습니다

순위에 해당 하는 RN는 트리를 이동하는 목적입니다.

 


 

start with

start with rn = 1이라는 것은 rn이 1일될때마다 새로운 트리를 만들게 됩니다

 

connect by prior

connect by prior deptno = deptno는 자기 자신을 상위 레벨로 지정을 하고

한 조건을 더 붙여서  rn = rn-1로 합니다

이 의미는 자기 이전행을 상위 레벨로 하겠다 라는 것입니다

이렇게 되면 부서별 마지막 사람이 최하위 레벨이 되며

sys_connect_by_path를 사용하여 모든 행의 이름을 한 칼럼에 노출시킬 수 있습니다

 

sys_connect_by_path

데이터를 읽어오는 그 행의 모든 상위 레벨을 나타낼 수 있게 도와주는 함수입니다

읽어오는 그 행이 최상위라면 자기 행의 데이터만 가져오게 될 것이며,

최하위라면 모든 레벨의 데이터를 가져오게 될 것입니다

 

level

connect by prior를 사용하게 되면 트리구조로 그 행의 트리 레벨을 표현할 수 있습니다

그래서 위와 같이  where level = cnt라는 것은

총인원수를 와 같은 레벨만 보여달라는 것으로

deptno 10은 cnt가 3입니다

그래서 아래와 같이 10은 cnt가 3이며 level도 3인 행만 보여달라는 where 절이였습니다

 

 


 

 

where 절인 level 조건부를 제거하여 노출한 데이터

 

 

 

  select deptno,
         ename,
         row_number() over
                  (partition by deptno order by empno) rn,
         count(*) over
                  (partition by deptno) cnt
   from emp

 

 

 

위의 서브 쿼리를 출력할 경우 아래와 같이 노출됩니다

 

서브쿼리만 출력한 데이터

 

 

이렇게 트리구조에 대해서 알아보았습니다

확실히 트리구조는 무척이나 어렵습니다

처음 접하신 분은 이해가 어려울 수도 있을 것 같습니다

하지만 각각 쓰는 구문들이 어떤 역할을 하는지만

정확하게 알고 있다면 다른 사람이 작성한 코드를 분석하는데

많은 도움이 될 것이라 생각합니다

 

힘내시고 화이팅하세요!!

아직 모험은 끝나지않았습니다!!

파이팅!!

 

728x90
반응형
728x90
반응형

프로젝트를 진행하다 보면

지역별, 지점별 기준으로 업데이트하는 경우가 많습니다

그렇때마다 지점별, 지역별 쿼리를 만들어서 inset를 하는 경우가 있습니다

 

 

DEPT 테이블 행의 값을

EAST, WEST, MID의 세 테이블에 INSERT 하고 

테이블 구조는 DEPT와 같다고 가정합시다

 

 

 

한번에 여러 테이블 데이터 삽입

 

아래와 같은 구문을 사용하여 INSERT 하게되면

한번의 결과값으로 동시에 여러개의 테이블에 INSERT합니다

 

 

INSERT ALL
INTO EAST VALUES (DEPTNO, DNAME, LOC)
INTO WEST VALUES (DEPTNO, DNAME, LOC)
INTO MID VALUES (DEPTNO, DNAME, LOC)
SELECT (DEPTNO, DNAME, LOC) FROM DEPT WHERE DEPTNO = 20;

 

 

하지만 이 방법은 어떠한 조건없이

동일하게 같은 값들이 INSERT한다는 점으로

가용성이 떨어지는 면이 있습니다

 

 

한번에 여러 테이블 조건부 인설트 하기

 

다음 방법을 사용하면 같은 행의 값으로

둘 이상의 테이블에서 삽입할 수 있습니다

 

 

그럼 코드를 작성해봅시다

아래와 같이 코드를 작성하면

한 행으로 여러 테이블에 insert 할 수 있습니다

 

 

INSERT ALL
  WHEN LOC IN ('NEW YORK', 'BOSTON') THEN 
  	INTO EAST (DEPTNO, DNAME, LOC) VALUES (DEPTNO, DNAME, LOC)
  WHEN LOC = 'CHICAGO' THEN
    INTO MID (DEPTNO, DNAME, LOC) VALUES (DEPTNO, DNAME, LOC)
  ELSE
    INTO WEST (DEPTNO, DNAME, LOC) VALUES (DEPTNO, DNAME, LOC)
SELECT DEPT, DNAME, LOC
FROM DEPT

 

 

INSERT ALL과 INSERT FIRST 제대로 알고 사용하기

위와 같은 경우는 INSERT ALL 또는 INSERT FIRST 문을 사용할 수 있습니다

두 개의 구문은 같은 결과를 생성하지만

차이점은 존재합니다

 

INSERT FIRST는 조건이 참이 되는 즉시 WHEN-THEN-ELSE에서 빠져나오는

반면 INSERT ALL은 이전 테스트가 참으로 평가되더라도  

모든 조건을 평가합니다

 

따라서 INSERT ALL을 사용하여 같은 행을 둘 이상의 테이블에 삽입할 수 있습니다

그것이 싫다면 FIRST를 사용해야 합니다

 

mybatis를 사용하여 동적 쿼리와 같이 사용한다면

가용성은 더욱 높아질 거라 생각합니다

 

프로젝트를 진행하다보면 여러 데이터를 핸들링하게 되며

어떤 쿼리가 있는지 알아야지만

적재적소에 사용하여 시간을 절약하며

코드도 간결해질수 있을거라 생각합니다

 

오늘도 코딩으로 한걸음 나아갑시다

모두 화이팅하시고 힘내세요!!

아직 대모험은 끝나지 않았으니까요!!

 

화이팅!!

 

 

 

 

 

 

728x90
반응형

+ Recent posts

Powered by Tistory, Designed by wallel