728x90
반응형

 

이전 포스팅에서는 데이터 관점에 대해서 알아보았습니다

그 관점이라는 것은 우리가 필요로 하는 데이터 값을 결정할 때

여러 가지 정보가 기준이 되어 데이터 값에 영향을 주었다면 그 여러 가지 정보가 관점이 됩니다

 

이렇듯 하나의 데이터에 여러 가지의 관점이 적용되어 데이터가 돌출되는 상황을 잘 이해해야겠죠?

 

아래에 더 상세한 설명이 있으니 궁금하신 분들은 한번 읽어보시길 바랍니다

 

2022.08.04 - [Data_Modeling/Methodology] - 데이터의 관점을 제대로 이해해야 하는 이유는? 디멘션 모델링?

 

데이터의 관점을 제대로 이해해야 하는 이유는? 디멘션 모델링?

데이터를 모델링한다는 것은 데이터 관점을 읽어 모델링 하는 것 디멘션은 팩트를 그룹화 하거나 한정화(필터링)하는 목적으로 사용한다 일반적으로 디멘션 혹은 그룹핑을 위한 논리적인 계층

tantangerine.tistory.com

 

 

그럼 본론으로 데이터 모델링 시 개체의 본질과 역할의 정보가 무엇인지 제대로 알고

구분을 해야 하는 이유를 알아보도록 하겠습니다

 


 

데이터 모델링에 개체의 본질이라는 것은 무엇일까?

먼저 본질의 사전적 의미는 대상의 가장 핵심적이고 필수 적인 속성이라고 합니다

 

단어들이 어렵고 복잡할 수 있습니다

천천히 같이 알아보도록 합시다

 

우선 한 가지 사례를 살펴보고 더 이야기를 이어나가도록 합시다

 

아래의 이미지에서 셋 중에 관련이 더 높은 것을 묶어야 한다면 어떤 선택을 하시겠습니까?

 

객체의본질
객체의 본질 찾기

 

동양인의 인식체계는 현상과 관계를 중시합니다

그래서 원숭이는 바나나를 좋아한다라는 행위적 관점에서 바라보게 되고 이 둘이 더 밀접하다고 판단합니다

 

서양인의 경우는 범주에 의한 분류 사물의 본질을 중시합니다

그래서 포유동물인 원숭이와 판다가 식물인 바나나보다 가까워 원숭이와 판다가 더 밀접하다고 생각합니다

 

이러한 이유는

개인주의적이고 독립적인 서양 사회의 특성으로

전체 맥락에서 개별 사물을 떼어내어 분석하는 것이 익숙하기 때문입니다

 

그래서

고대 그리스 철학자들이 세상을 이해는 방식이 데이터 모델링의 영역과 맞닿아 있습니다

사물의 속성 자체에 주의를 기울이고,

속성에 근거하여 범주화하고,

범주들을 사용해서 규칙을 만들고,

사물의 특성과 움직임을 그 규칙으로 설명한다.

 

하지만 동양인은 규칙과 무관한 개체 간의 표면적인 유사성에 영향을 받기 쉽습니다 

겉으로 드러나는 현상과 우리가 인식하는 형식의 본질,

궁극적 원인과 분리되어야 합니다

 

본질적인 현상은 추구하지 않고 겉으로 드러나 보이는 현상에만 집착하는 것은 옳은 방법이 아닙니다

근본적인 존재, 성질, 모습을 분석해야 합니다

 

이렇게

본질을 제대로 알게 되면 그 속성을 근거하여  동일한 성격을 가진 개체들을 묶을 수 있습니다

범주화하는 것이 가능합니다

 


 

반응형

 


 

본질적 개체가 아닌 역할 별 개체가 가지는 문제

 

어느 산업은행의 정보시스템에서

직원, 고객, 공급자의 정보를 별도의 엔터티로 관리한다면

<직원> 엔터티의 B

<공급자> 엔터티 B는 완전히 다른 개체로 취급됩니다.

 

이는 고객관계 관리의 고객 통합 영역에서 항상 언급되는 동일인 인식 문제에 해당됩니다.

 

앞서 원숭이와 바나나 이야기를 하면서 본질과 현상을 구분해서 볼 수 있어야 한다고 했습니다.

그렇다면 은행 사례에서의 본질은 무엇일까요?

그리고 현상은 무엇일까요?

 

본질은 물질적으로 유일한 B라는 개체의 정보,

즉 성명과 같은 사람의 근본적인 정보가 본질에 해당합니다.

비즈니스적으로 어떤 행위에 관여하는지와 무관한 자연인으로서의 정보입니다.

 

현상은 B가 수행하는 직원, 고객, 공급자라는 3가지 배역은 현상이라고 해야 합니다.

 

이렇듯 본질적인 개체가 아닌 역할별 개체만을 관리할 경우 발생할 수 있는 문제가 바로 여기서 나오는 이야기입니다.

 

다시 말해

본질은 사람으로서의 근본적인 정보(물리적으로 유일한 개체인 나)이며,

현상은 논리적 맥락이나 역할(교수, 학생, 고객, 은행원 등)입니다

 

B라는 존재는 인터넷 뱅킹으로 이체할 때는 고객이 되고,

프린터를 조달할 때는 공급자가 되며,

연말에 인사 고과를 받을 때는 직원이 됩니다.

 

이렇듯 엔터티역할만으로 정의할 경우 개체의 본질적 정보를 관리할 때 다양한 문제가 발생합니다.

그러니 본질과 현상의 차이를 다시 한번 잘 새겨보시길 바랍니다.

 

어머니를 어머니로만 바라보지 않고 한 여인, 존엄한 인격체로서 바라보게 되면

그 전에는 보이지 않던, 풍경이 들어오기 시작합니다.

 


 

범주화와 추상화의 중요성

 

복잡한 현실 업무의 속성과 규칙을 모델로 정형화하려면

비즈니스 문제 영역의 데이터를 관찰해서 그 안에 숨어 있는 유형과 관계를 찾아내야 합니다.

 

범주화유사한 것들을 일정하게 묶는 과정이며,

추상화는 문제 영역에서 장 핵심적인 특성만 추리는 과정입니다.

 

 

추상화는 현실의 복잡함을 단순화하여 일목요연하게 파악하게 해주는 도구가 될 수도 있는 반면,

단순화하는 과정에서 구체성과 다양성이 소거되어 실재를 정확히 반영하지 못할 수도 있습니다

따라서 우리는 추상화 수준을 항상 고심하게 되며, 적절한 절충점을 결정하는 것 역시 모델러의 필수 역량입니다

 

즉, 지금까지의 이야기를 종합한다면,

데이터 모델링엔터티범주화를 통해 만들어진 집합이며,

모델링은 정보를 담는 최소 단위인 본질의 속성을 분석합니다

즉, 추상화에서 유사한 것은 모으고 독립적인 것은 분리하는 과정입니다

이러한 과정을 통하여 속성의 엔터티를 찾아낼 수 있습니다

 

즉, 관계형 데이터 모델은 나의 정보는 내가 집약해서 갖고,

남의 정보는 필요할 때 관계를 통해 찾아서 원하는 뷰를 만들 내는 구조입니다

 

데이터 모델링은 결국 어떤 개체의 본질에 맞게 범주화하며,

개체를 추상화 과정에서 분리하고 묶는 수준을 고민하는 과정입니다

 

범주화와 추상화까지 어려운 용어들로 채워진 이번 포스팅은 

타자를 치면서 몇 번을 곱씹었는지 알 수가 없었습니다

그만큼 어려웠고 이해를 하기가 힘든 부분이 너무 많았습니다

 

다른 사람은 이런 이론적인 부분은 필요 없다고 말하지만

저는 이러한 관점을 한번 생각해보는 것이 데이터 모델링에 정말 필요한 학습이라고 생각합니다

 

이렇게 데이터 모델링 공부를 한걸음 끝이 났네요

하지만 아직 갈길이 너무 머네요

 

다음 년에는 데이터 모델링 쪽으로 일을 할 수 있게 착실히 준비해 놓아야겠습니다

같이 힘내 보아요!

 

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

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
반응형

데이터를 모델링한다는 것은 데이터 관점을 읽어 모델링 하는 것

 

디멘션은 팩트를 그룹화 하거나 한정화(필터링)하는 목적으로 사용한다

일반적으로 디멘션 혹은 그룹핑을 위한 논리적인 계층구조는 1:M형태의 부모-자식 관계를 만든다

 

팩트 테이블의 상위 테이블이 되어야 하며, 디멘션은 다시 상 하위 테이블로 분리되어야한다

현대, 기아, 싸용을 묶는 상위 개념의 국산 완성차업체라고 정의 하는것 처럼

 

이렇듯 디멘션을 생각하며 아래의 테이블을 생각해보자

 

 

판매량표
판매량표

 

 

위의 표를 유심히 관찰해보면 각 셀의 값을 결정 하는 데 지역, 상품, 기간(년도)이라는 3가지 정보가 영향을 주고 있다

판매량을 집계하는 기준이 존재하는 것, 87개 판매되었다는 사실을 나타낸다

 

즉 87은 강원도 원주시라는 지역

상품유형은 B고 상품 번호는 B1인 상품

2014년이라는 기간

 

위의 3가지 관점(디멘션)이 결합된 값이다

 

87이라는 값이 정보로서 비즈니스적인 가치를 가지려면 그값을 해석하는 맥락이 명확해야한다.

그리고 디멘션이 팩트를 결정하는 기준 정보로서의 부모역활을 하게된다는 것을 명심해야한다

 

 

 

데이터모델링판매량ERD
판매량 ERD

 

 

위의 표처럼 관점(디멘션)과 그룹핑 구조를 확인할 수 있다

기간별, 지역별, 상품별, 판매량에서 '~별' 디멘션은 데이터를 해당 디멘션으로 한정하는 동시에 구조화할 수 있다

또한 지역 중분류 지역 소분류와 같은 그룹핑 구조는 유형화를 위한 데이터 계층 조직화를 구축할 수 있다

 

결론적으로 판매량의 데이터를 어떠한 관점으로 보는지가 가장 중요하며

그 관점을 지역별, 상품별, 판매량이라는 관점을 데이터로 나타낼 수 있게 데이터 모델링 하는것이 핵심이다

 

데이터 모델링은 말이 어렵고 책을 읽어도 이해하기 힘들다

하지만 제일 처음 이미지를 보면서 요건을 받고 업무를 이해할 때

관점을 파악하고 기준을 제대로 잡는것이 중요하다

 

한번에 모든 것을 이해못하지만 사례를 계속 살펴보고 읽어보는것이 가장중요한것같다

아무리 관점을 잘 파악하고 업무요건을 잘 정립한다고해도

그 업무에 대해 경험도는 다른 무엇보다 중요하다

 

그 경험이라는 것은 무시할 수 없기에 요건을 정립하고 업무 파악을 해서

데이터 모델링을 한다고해도 예기치 못한 변수는 발생할 수 밖에 없다

그것을 사전에 예방할 수 있는 방법은 경험밖에 없다고 생각한다

 

프로젝트를 수행할 때 개발자로 업무를 수행한다고해도

테이블 설계도를 보면서 미리미리 업무를 공부하며 테이블 구조를 보면서 

지금 하고 있는 업무 파악을 좀더 확실히 하고 메모하고 정리 해두어야

 

그것이 자기의 실력이 되고 누구에게도 인정받는 사람이 될 수 있을 거라 생각한다

 

데이터 모델링을 한다는 것은 데이터를 이해하고 유형화하여 구조화하는 과정이라고 생각하며

데이터에는 본질을 기준으로 한 몇 가지 유형이 존재한다고 생각한다

데이터 간에는 종속 관계와 계층관계도 존재한다는 것을 생각하고 계속 어떠한 상황에도

데이터 모델링을 관점으로 생각하자

 

 

    

 

728x90
반응형
728x90
반응형

 

애플리케이션 화면과 RDB의 테이블은 다르다

 - 데이터 모델링에 대한 이해와 경험이 부족한 사람들은 GUI 화면과 테이블을 거의 1:1로 인식하거나 데이터를 조회할 경우 테이블 조인을 직관적인 한가지만 보고 조인할 경우 다른 어떠한 경우에는 원하는 데이터가 나오지 않는 경우가 있다

이렇듯 화면에 구성되어있는 주문서 한장에도 체계적으로 조직화된 하나의 집합체로 구성되어있다

 

즉, 데이터 모델링은 최종 사용자에게 보이는 하나의 집하체에서 데이터의 구조적인 부분을 분리하는 작업이다

 

아주 기초적인 질문이지만 하나의 화면에 포함된 데이터들을 여러 테이블, 여러 행으로 분리하는 이유는 무엇일까??

그 이유는 데이터 관리(저장, 수정, 조회등)에 유리하기 때문이다.

 

중요한 점은 각 부분을 왜 분리해야하는지 그 이유를 정확하게 알고 있어야한다

  • 데이터 구조로서의 분리해야 하는 이유
  • 테이블을 분리하는 기준과 규칙등 방법론

 

위와 같은 이유를 알고 분석하려면 데이터를 이해하고 업무 데이터를 어떻게 읽고 분석해야하는지가 가장 중요하다

아래와 같은 사례로 중요한 점을 생각하며 살펴보자

 

설문 데이터 모델링, 데이터 본질을 읽어 모델링하다

- 정보 시스템으로 관리하려면 데이터 성격과 주제 유사함을 중심으로 구조화해야 한다.

- 즉, 데이터의 유형을 기준으로 관련된것을 묶어내는 과정이 필요하다

- 이를 위해서는 데이터를 이해하는 것이 무엇보다 중요하다

 

  • <회원>, <회원설문참여>, <설문응답>, <설문지>, <설문질문>, <설문보기> 릴레이션이 존재한다고 가정한다.
  • A라는 설문의 어떤 질문과 B 설문의 동일 질문을 별개로 인식하고 관리할 것인가? 
  • 회사는 업무 특성상 설문을 자주하며, 일반적인 설문을 DB화해서 들어진 질문과 보기를 패턴화할 것인가?
  • 보기는 질문에 종속되지 않고, 질문은 설문지에 종속되지 않은 독립개체로 정의하고 관리할 것인가?
  • 독립개체로 정의해서 릴레이션을 개별적으로 정의해야한다 <설문지질문구성><설문지질문보기구성>이 된다
  • 좌에서 우로 변경된 릴레이션을 확인할 수 있다.

 

위와 같이 생각을 하고 아래와 같이 테이블을 생성했다면

질문과 보기를 주변 맥락에서 분리하여 독릭된 객체로 관리해야한다면 다시 모델링을 해야한다

패턴화를 필요로 하며 그럴려면 설문질문에서 여러 질문을 조합하고 보기를 구성하여 하나의 설문지를 만들어내는 것까지

테이블 구성이 필요하기때문이다

 

 

기본설문지구성
기본 설문지 모델링

 

아래와 같이  변경이 가능하다

 

 

 

설문지테이블구조
설문지구성 테이블구조

 

위의 사례로 중요한 것은 업무요건을 보면 데이터가 보여야하고, 업무에서 흘러 다니는 데이터의 성격과 고유한 근본 성질을 이해할 수 있어야한다. 그래서 현장에서도 업무파악이 무엇보다 중요하다 데이터를 조합은 업무파악에서 시작이다 

 

 

올바른 데이터 모델링을 위한 기본기

  1. 데이터 근본 성격 파악 -> 데이터 집합과 개체 식별
  2. 데이터 종속성 분리 -> 데이터의 독립성 확인과 모델 골격조망

 

 

업무를 데이터 중심으로 바라볼 수 있는 시선을 소개하며 데이터 모델링을 위해 데이터를 이해한다는것이 무엇인지 업무적으로 어떻게 관리되고 혹은 어떻게 관리되어있는지를 명확하게 해야한다

 

 

프로젝트 성패를 결정짓는 데이터 모델링 이야기/ 김상래저

 

 

 
 
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
반응형

 

 

이번에는 구글 파이어 베이스의 Cloud Firestore 데이터 추가 기능을 구현해보겠습니다

 추가할 때 이미지도 같이 저장해보도록 하겠습니다

 

아래의 목차와 같이 진행하겠습니다

 

  1. Storage 설정 및 폴더 추가하기
  2. App 내에서 이미지 데이터 컨트롤하기
  3. Stroage에 이미지 저장하기
  4. Cloud Firestore에 데이터 추가하기

 

우선 파이어 베이스의 이미지를 파일 저장소(Storage) 설정부터 하겠습니다

 

 


Storage 설정 및 폴더 추가

폴더를 추가하고 보안설정을 진행하도록 하겠습니다

 

 

파일저장소-설정
Storage 설정 및 폴더 추가

 

 

Rules 탭을 클릭해서

allow read, write로 설정하여

읽고, 쓰기를 가능하게 합니다

 

 

파일저장소-보안설정
파일저장소 보안 설정하기

 

 

이제는 이미지를 저장할 파일 저장소의 폴더 추가 및 설정을 끝냈습니다

 

 


App 내에서 이미지 데이터 컨트롤 하기

expo-image-picker 라이브러리를 설치합니다

 

사진을 촬영해서 바로 앱에 데이터를 전달하여 이미지를 저장하는 방법

앱 내의 사진첩을 접근해서 이미지 데이터를 불러와 저장하는 방법을 알아보겠습니다

 

 

새탭열기
마우스 우 클릭 화면

 

 

그 방법은 아래의 포스팅에 자세히 설명이 되어있습니다

포스팅을 클릭할 때 마우스 우 클릭해서  새 탭에서 링크 열기를 선택하시면 더 편하게 볼 수 있습니다

 

그럼 아래의 포스팅을 클릭해봅시다!

 

 

2022.07.29 - [Mobile/React_Native] - React Native X Expo 이미지 데이터 expo-image-picker로 이미지 업로드 구현하기

 

React Native X Expo 이미지 데이터 expo-image-picker로 이미지 업로드 구현하기

이미지 업로드 기능 구현하기 앱에서 이미지나 사진을 업로드할 경우 두가지 방법이있습니다 첫번째는 사진을 찍어서 업로드를 하는 방법 두번째는 자신의 사진첩에서 불러와 업로드 하는 방

tantangerine.tistory.com

 


 

반응형

 


Storage에 이미지 저장하기

 

아래와 같이 화면이 구성되어있다고 가정합시다

 

 

이미지데이터저장
이미지 데이터 저장

 

 

  const pickImage = async () => {
    console.log('이미지 선택');
    let imageData = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.All,
      allowsEditing: true,
      aspect: [4, 4],
      quality: 1,
    });
    if(imageData.uri){
      setImageUri(imageData.uri ? imageData.uri : '');
    }
  };

 

 

위와 같이 선언된 함수를 호출하여 이미지 사진첩 내 이미지의 데이터를 가져와서 setImageUri에 저장합니다

또 한, 미리 보기 기능이 구현되어있다면 저장할 때 imageData.uri가 있을 경우에만 저장하도록 합니다

 

 

 

 

등록버튼클릭
등록버튼 클릭이미지

 

등록버튼을 클릭하게 되면 아래와 같은 함수가 호출이 됩니다

 

이때 중요한 것은 blob형식으로 변경해야지만 최종적으로 파이어 베이스 storage에 업로드할 수 있습니다

 

사이드바에 공식문서를 참조하시면 더 많은 정보를 확인할 수 있습니다

 

blob 객체는 파일류의 변하지 않는 미가공 데이터를 나타내며, 텍스트와 이진 데이터 형식으로 읽을 수 있습니다

 

 

const upload = () => {
    confirmAlert('게시글을 등록하시겠습니까?', async () => {
      console.log('업로드 준비중!');
      if(!imageUri){
        Alert.alert('이미지를 등록해주세요.');
        return;
      }
      if(!title){
        Alert.alert('제목을 입력해주세요');
        return;
      }
      if(!content){
        Alert.alert('내용을 입력해주세요');
        return;
      }
      if(!currentUser.uid){
        Alert.alert('로그인이 필요합니다');
        return;
      }
      const currentUser = firebase.auth().currentUser;
      let date = new Date();
      let getTime = date.getTime();
      let data = {
        title: title,
        author: currentUser.email,
        desc: content,
        image: image,
        date: getTime,
        uid: currentUser.uid,
      };
      const response = await fetch(imageUri);
      const blob = await response.blob();
      const imageUrl = await imageUpload(blob, getTime);
      data.image = imageUrl;
      console.log(data);
      let result = await addDiary(data);
      if (result) {
        Alert.alert('글이 성공적으로 등록되었습니다!');
        setTitle('');
        setContent('');
        setImage(tempImage);
        setImageUri('');
      }
    })
  };

 

 

blob데이터
Blob 데이터 가져오기

 

 

fetch를 활용해서 주소를 실행시켜 결괏값을 가져와 blob함수를 실행하여 그 데이터 값을 storage에 저장하게 됩니다

 

 

 

그렇게 호출된 imageUpload에서 아래와 같이 실행하게 됩니다

blob과 데이터를 매개변수로 받아 파일 저장소(storage)에 저장하게 됩니다

저장 후   snapshot.ref.getDownloadURL()를 호출하여 storage에 저장된 url주소를 불러옵니다

그 불러온 url주소를 reurn 하여 cloud firestore에 저장합니다

 

 

export async function imageUpload(blob, date) {
  const storageRef = firebase
    .storage()
    .ref()
    .child('diary/' + date);
  const snapshot = await storageRef.put(blob);
  const imageUrl = await snapshot.ref.getDownloadURL();
  blob.close();

  return imageUrl;
}

 


Cloud firestore에 데이터 추가하기

 

 

addDiary호출하기
addDiary 호출

 

 

위와 같이 return값을 기본 Object객체에 추가하여 addDiary의 매개변수를 할당하여 함수 호출을 합니다

그리고 firebase.firestore() 함수, db.collection(). doc.(). set()의 두 가지 함수를 호출하여

diary라는 데이터 베이스 테이블 생성과 data의 객체 값들을 cloud firestore에 추가합니다

 

 

export async function addDiary(content) {
  try {
    const db = firebase.firestore();
    await db.collection('diary').doc(content.date + "D").set(content);
    return true
  } catch (err) {
    Alert.alert("글 작성에 문제가 있습니다! ", err.message)
    return false
  }
}

 

 

아래와 같이 파이어 스토어에 저장된 모습을 확인할 수 있습니다

collection('diary') // 테이블을 만들게 되면서 diary를 만들면서 없을 때에는 추가를 하게 됩니다

. doc(content.date + "D") // 저장한 날짜를 가져와 'D'를 붙여서 저장합니다 그것이 테이블의 PK KEY가 됩니다

하지만 위의 방식보다는 doc(content.date + "_" + content.author) 이 방식으로 변경하는 것이 더 유니크한 값이 되지 않을까 생각합니다 여러 유저들이 들어와서 이미지를 동시에 클릭한다면 분명 오류가 발생할 것이라 생각합니다

. set(content); // content객체의 값들은 아래에 이미지에서 확인할 수 있습니다

 

cloud-firestore
클라우드 파이어스토어 저장모습

 


 

이렇게 cloud firestore에 데이터 저장하는 방법과 파일 저장소(Storage)에 이미지를 저장하는 방법을 알아보았습니다

사이드바에 파이어 베이스 관련 공식 문서가 있으니 참고하세요

 

IT 개발 공부는 끝이 없기에 항상 공부를 해야 하는 직종이지요 하지만 포기하지 마시고 끝까지? 끝이 있을지는 모르겠지만 하시길 바랍니다

 

우리들의 대모험은 끝나지 않았습니다 그럼 파이팅 하시고 다음 포스팅도 기대해주세요 감사합니다

 

 

 

 

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
반응형

이미지 업로드 기능 구현하기

 

 

앱에서 이미지나 사진을 업로드할 경우 두가지 방법이있습니다

 

첫번째는 사진을 찍어서 업로드를 하는 방법

두번째는 자신의 사진첩에서 불러와 업로드 하는 방법

 

이 두가지를 방법을 구현할 수 있는 기능을 알아보도록 하겠습니다

 

공식문서 사이드바에서

공식문서 & 유용한 툴 목차를 보자

 

Expo-image-picker
이미지 픽커 공식문서

 

 

expo-image-picker 라이브러리는 설치

사진 접근 권한 알림 팝업 기능 구현

launchImageLibraryAsync를 사용해서 사진첩에 접근해서 사진을 불러옵니다

 

 

expo-image-picker 라이브러리는 설치하겠습니다

 

expo install expo-image-picker

 


 

권한 허용 함수 구현하기

 

 

사진을 등록할 경우에

접근권한을 물어보는 것이 좋을 것같습니다 

 

아래와 같이 함수를 구현하여

사진을 업로드 할 경우 권한이 가능한지에 대해

true, false를 반환하여 

값에 따라 다음 절차를 진행 여부를 판단합니다

 

const getPermission = async () => {
    if (Platform.OS !== 'web') { // is web or mobile?
          const {
            status,
          } = await ImagePicker.requestMediaLibraryPermissionsAsync();
          if (status !== 'granted') {
            alert('게시글을 업로드하려면 사진첩 권한이 필요합니다.');
            return false
          }
          return true
    }
};

 

 

위의 코드에서 ImagePicker.requestMediaLibraryPermissionsAsync();

함수를 사용해서 팝업 창이 노출되면서 허용 권한을 설정할 수 있게됩니다

그리고 한번 허용되면 다음부터는 팝업이 노출되지 않을겁니다

 

그럼 권한 허용이 되면

다음 함수를 실행합니다

 


 

 

 

반응형

 

 


 

디바이스 내 사진첩 사진 데이터 불러오기, 찍은 사진 데이터 불러오기

 

 

그럼 아래와 같이 코드를 구현합니다

이때 launchImageLibraryAsync를 사용해서 사진첩에 접근해서 사진을 불러옵니다

그리고 사진을 선택하면 함수의 리턴값으로 이미지 데이터를 받을 수 있습니다

 

또 한, launchCameraAsync 함수를 사용하면

직접 사진을 찍어서 앱 내부로 데이터를 가져올 수 있습니다

 

아래와 같이 함수를 구현합니다

 

 

const pickImage = async () => {
    console.log('이미지 선택');
    let imageData = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.All, // 이미지 타입을 지정할 수 있다
        allowsEditing: true, // 사진을 수정허용 여부을 지정한다.
        aspect: [4, 4], // 사진의 비율을 정할 수 있다.
        quality: 1, // 현재 용량을 줄이고 높일 수 있다.
    });
    console.log('이미지 데이터: ', imageData);
};

 

 

사진을 불러올때 여러가지 옵션이 있습니다

용량을 줄이거나 사진비율을 정하거나 수정을 허용여부를 정할수도 있습니다

아래에 더 자세하게 설명해놓았습니다

확인해보세요!~

 

 

 

ImagePickOption

allowsEditing
(optional)
boolean 이미지를 선택한 후 편집할 UI를 표시할지 여부입니다. Android에서는 사용자가 이미지를 자르고 회전할 수 있고 iOS에서는 단순히 이미지를 자를 수 있습니다.
Default: false
allowsMultipleSelection
(optional)
boolean Only for: Web

한 번에 여러 미디어 파일을 선택할 수 있는지 여부입니다.
aspect
(optional)
[number, number] [x, y]사용자가 이미지를 편집할 수 있는 경우( 를 전달하여) 유지할 종횡비를 지정하는 두 개의 항목이 있는 배열입니다 allowsEditing: true. iOS에서는 자르기 사각형이 항상 정사각형이기 때문에 Android에서만 적용할 수 있습니다
base64
(optional)
boolean Base64 형식의 이미지 데이터도 포함할지 여부입니다.
exif
(optional)
boolean 이미지에 대한 EXIF ​​데이터도 포함할지 여부입니다. iOS에서 EXIF ​​데이터는 카메라 케이스에 GPS 태그를 포함하지 않습니다.
mediaTypes
(optional)
MediaTypeOptions 선택할 미디어 유형을 선택합니다.
Default: ImagePicker.MediaTypeOptions.Images
presentationStyle
(optional)
UIImagePickerPresentationStyle Only for: iOS

사진/동영상을 촬영하는 동안 보기를 사용자 지정 하려면 프레젠테이션 스타일 을 선택하세요 
Default: ImagePicker.UIImagePickerPresentationStyle.Automatic
quality
(optional)
number 에서 까지 압축 품질을 지정 0합니다 1. 0작은 크기를 위한 1압축을 의미하고 최대 품질을 위한 압축을 의미합니다.
videoExportPreset
(optional)
VideoExportPreset Only for: 
iOS 11+

선택한 비디오를 압축하는 데 사용할 사전 설정을 지정합니다.
Default: ImagePicker.VideoExportPreset.Passthrough
videoMaxDuration
(optional)
number 비디오 녹화의 최대 지속 시간(초). 제한 을 0비활성화하도록 설정합니다. 기본값은 0(무제한)입니다.
  • iOS 에서 allowsEditing가 로 설정된 true경우 최대 지속 시간은 10분으로 제한됩니다. 이 제한은 0값이 지정되거나 지정되지 않은 경우 자동으로 적용됩니다.
  • Android 에서 이 옵션의 효과는 설치된 카메라 앱의 지원에 따라 다릅니다.
  •  에서 이 옵션은 효과가 없습니다. 제한은 브라우저에 따라 다릅니다.
videoQuality
(optional)
UIImagePickerControllerQualityType Only for: 
iOS

녹화된 비디오의 품질을 지정합니다. 기본값은 장치에 사용할 수 있는 최고 품질입니다.

Default: ImagePicker.UIImagePickerControllerQualityType.High

 

 

속성을 지정하고 함수를 호출하면 아래와 같이 데이터를 받을 수 있습니다

 

 

사진불러오기
이미지 데이터

 

 

이 데이터를 받아서 클라우드에 저장하면 됩니다

그 방법은 클라우드에 맞는 여러방법들이 있을것이니

그에 맞게 기능구현이 필요할 것 같습니다

 

이렇게 expo-image-picker를 구현해보있습니다

그럼 오늘도 이렇게 글을 마치겠습니다

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

 

앞으로 같이 계속 공부해보아요~

 

 

 

 

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
반응형

프리랜서 개발자 시장 인력이 부족한 이유는?

 

코로나 시대에 접어들고

비대면 서비스가 늘어나면서 IT 업종에 대한 관심도가 높아졌다

여러 기업들이 IT에 대한 역량을 키우기 위해 애썼고

그 결과를 반영하듯 IT기업은 사상 최대 실적을 기록했다

 

그중 배달의 민족, 당근 등 IT기업들도 같이 성공을 거두면서

그 열기는 더욱 뜨거워졌다

 

그 많은 자원들은 다시 여러 신생기업에 투자가 일어났고

신생 스타트업들도 투자받은 돈으로  개발자 규모를 늘리기 시작했다

 

아래의 자료를 보더라도

그때 당시 채용규모는 작다고 말할 수 없는 정도이다

지금 보이는 기업들이 이 정도이니

다른 신생업체와 대기업들까지 포함한다면

it 개발자만 뽑는 채용규모가 정말 큰 규모가 아닐 수 없다

 

 

개발자-채용규모
2020 개발자 채용규모

 

 

당연히 대기업들도 개발자 채용을 늘리기 시작했으며

네 카 라 쿠 배라는 말까지 생길 정도였다

 

그런데 이러한 것이 프리랜서 시장에 왜 영향을 주었냐는?

조금 의아할 부분도 있겠지만

조금만 생각하면 그것은 당연한 결과 일지도 모른다

 

IT기업이 아닌 대기업들도 IT팀이 있으며,

그런 기업들은 자기 계열사에서 진행하는 프로젝트를 맡아서 진행을 한다

 

즉 사업을 하기 위해서

필요한 업무에 대한 시스템을 구축하는 것이다

이러한 프로젝트는 대규모이고 단발적이라서 프리랜서 고용률이 높은 편이다

그런 프로젝트에서 프리랜서 중에서 마음에 드는 개발자에게

스카우트 제의를 하는 경우도 있다

그렇게 프리랜서들이 대기업에 취업이 되고

아니면 대기업의 대규모 개발자 채용을 하면서 

이직하는 프리랜서도 늘어났을 것이다

 

그렇게 프리랜서가 부족한 현상이 나타나면서

 중급 고급 개발자들을 구하기 힘들게 되고

프리랜서 개발자 단가까지 높아지는 현상도 나타났다

 

 

아래에서 보면 소프트웨어 기술자 월평균 임금을 알 수 있다

 

개발자-평균임금
개발자 평균 임금

 

 

이렇게 많은 임금들이 올라가고 있고

프리랜서도 그 영향을 많이 받는다

 

하지만 지금은 상황이 조금 변한 것 같다

실적이 좋은 상황에서 채용한 개발자들로 인해

카카오 인건비 47% 네이버도 24%가 부담이 늘어났다는 

기사들을 볼 수 있다

 

 

 

개발자임금부담
개발자 임금 부담 - 황정수 기자 hjs@hankyung.com

 

 

 

인건비증가
증가하는 인건비

 

 

 

인건비가 늘어나는 건 확실하다

하지만 정말 위의 회사들이 이 정도의 인건비가

부담이 될만한 수치일까 하는 것이다

 

내부 사정은 잘 모르겠지만

지금 현재 당장은 안 좋아 보일지는 몰라도

기간이 어느 정도 지나면

이 수치는 부담이 될 정도는 아닐 것 같다

 

지금 부담돼서 하반기 채용을 줄이는 기업들도 있지만

그럼에도 불구하고 오늘의 집, 현대 오토에버와 케이 뱅크 등

경력채용과 신입 채용을 늘리는 기업들도 있다

 

기업마다 상황이 다르지만

코로나 시대에 늘린 채용이

지금 현재는 인건비가 부담이 되는 것은 확실한 것 같다

 

언제까지 임금이 오를지

채용이 계속될지는 모르지만

 

지금 이 상황은 개발자들에게 좋은 소식인 것은 분명하다

 

앞으로 국가 정책으로도 

초등학교의 코딩이 정규 교육으로 지정받으면서

인재 양성에 노력하는 모습을 보이고 있다

한 분야를 관심을 갖는다는 것은

기쁜 일이 아닐 수 없다

 

앞으로도 테크산업이 많은 성장을 하길 바라본다

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
반응형

+ Recent posts

Powered by Tistory, Designed by wallel