분류

2019년 3월 2일 토요일

2018년 니로 주행 1년 실 연비 및 기존 차량과 비교

1. 서론 

니로를 운행하기 시작한지 만 1년이 되었습니다. 기존 차량의 주유 기록과 니로의 주유 기록을 해보려 합니다. 기존 차량은 중고로 샀던 라세티프리미어 차량이었습니다. 연비가 매우 안 좋아서 가계 경제에 매우 악영향을 미쳤고, 기름 값 때문에 어딘가 놀러가는 것이 망설여지는 차였습니다. 심지어 조금 멀리 놀러 가면 기름 값이 12만원씩 들어 여행지에서 밥값을 아끼기 위해 무척 궁상맞게 지냈죠. 그에 반해 니로는 주유 부담이 없기에 장거리 여행에서도 맘껏 먹고 즐길 수 있어 여행이 더욱 즐거워졌습니다.

2. 각 차량별 실제 주유 기록 

가) 니로 

년도 월일 리터 금액 단가 차종
2018 03-23 32.10  ₩        50,000  ₩     1,558 니로
2018 03-28 33.14  ₩        50,000  ₩     1,509 니로
2018 04-07 33.14  ₩        50,000  ₩     1,509 니로
2018 04-17 33.44  ₩        50,000  ₩     1,495 니로
2018 04-30 32.70  ₩        50,000  ₩     1,529 니로
2018 05-05 32.92  ₩        50,000  ₩     1,519 니로
2018 05-18 32.28  ₩        50,000  ₩     1,549 니로
2018 06-03 31.67  ₩        50,000  ₩     1,579 니로
2018 06-20 31.67  ₩        50,000  ₩     1,579 니로
2018 07-07 31.67  ₩        50,000  ₩     1,579 니로
2018 07-21 19.00  ₩        30,000  ₩     1,579 니로
2018 07-28 31.67  ₩        50,000  ₩     1,579 니로
2018 08-16 31.35  ₩        50,000  ₩     1,595 니로
2018 09-10 31.47  ₩        50,000  ₩     1,589 니로
2018 09-25 30.69  ₩        50,000  ₩     1,629 니로
2018 10-16 30.14  ₩        50,000  ₩     1,659 니로
2018 10-27 24.26  ₩        40,000  ₩     1,649 니로
2018 11-03 17.76  ₩        30,000  ₩     1,689 니로
2018 11-14 32.92  ₩        50,000  ₩     1,519 니로
2018 11-24 27.05  ₩        40,000  ₩     1,479 니로
2018 12-06 35.58  ₩        50,000  ₩     1,405 니로
2018 12-16 37.18  ₩        50,000  ₩     1,345 니로
2018 01-12 40.89  ₩        55,000  ₩     1,345 니로
2019 01-03 39.37  ₩        52,317  ₩     1,329 니로
2019 01-12 39.78  ₩        51,323  ₩     1,290 니로
2019 02-22 37.77  ₩        49,059  ₩     1,299 니로
2019 02-23 37.94  ₩        49,279  ₩     1,299 니로
869.55  ₩    1,296,978  ₩   40,683
평균   32.21  ₩      48,036  ₩   1,507  



글을 쓰는 사이 수 일이 지나 니로는 16117km 주행, 
잔여 기름이 95% 상태 실 주유기록을 엑셀에 입력하였습니다. 

나) 라세티프리미어

년도 월일 리터 금액 단가 차종
2017 04-04 34.27  ₩        50,000  ₩     1,459 라세티프리미어
2017 04-10 27.42  ₩        40,000  ₩     1,459 라세티프리미어
2017 04-16 34.27  ₩        50,000  ₩     1,459 라세티프리미어
2017 04-22 27.42  ₩        40,000  ₩     1,459 라세티프리미어
2017 04-29 27.42  ₩        40,000  ₩     1,459 라세티프리미어
2017 05-08 27.42  ₩        40,000  ₩     1,459 라세티프리미어
2017 05-15 27.82  ₩        40,000  ₩     1,438 라세티프리미어
2017 05-21 27.80  ₩        40,000  ₩     1,439 라세티프리미어
2017 05-25 27.80  ₩        40,000  ₩     1,439 라세티프리미어
2017 05-30 27.80  ₩        40,000  ₩     1,439 라세티프리미어
2017 06-03 27.80  ₩        40,000  ₩     1,439 라세티프리미어
2017 06-08 27.80  ₩        40,000  ₩     1,439 라세티프리미어
2017 06-13 28.01  ₩        40,000  ₩     1,428 라세티프리미어
2017 06-18 28.21  ₩        40,000  ₩     1,418 라세티프리미어
2017 06-24 28.21  ₩        40,000  ₩     1,418 라세티프리미어
2017 06-27 20.55  ₩        30,000  ₩     1,460 라세티프리미어
2017 07-01 27.49  ₩        40,000  ₩     1,455 라세티프리미어
2017 07-12 27.42  ₩        40,000  ₩     1,459 라세티프리미어
2017 07-22 27.42  ₩        40,000  ₩     1,459 라세티프리미어
2017 07-28 20.48  ₩        30,000  ₩     1,465 라세티프리미어
2017 08-02 34.01  ₩        50,000  ₩     1,470 라세티프리미어
2017 08-09 33.78  ₩        50,000  ₩     1,480 라세티프리미어
2017 08-17 26.85  ₩        40,000  ₩     1,490 라세티프리미어
2017 08-20 26.85  ₩        40,000  ₩     1,490 라세티프리미어
2017 08-25 26.49  ₩        40,000  ₩     1,510 라세티프리미어
2017 09-01 19.87  ₩        30,000  ₩     1,510 라세티프리미어
2017 09-09 26.32  ₩        40,000  ₩     1,520 라세티프리미어
2017 09-15 32.79  ₩        50,000  ₩     1,525 라세티프리미어
2017 09-20 32.28  ₩        50,000  ₩     1,549 라세티프리미어
2017 09-25 25.82  ₩        40,000  ₩     1,549 라세티프리미어
2017 10-05 32.05  ₩        50,000  ₩     1,560 라세티프리미어
2017 10-13 12.82  ₩        20,000  ₩     1,560 라세티프리미어
2017 10-15 32.05  ₩        50,000  ₩     1,560 라세티프리미어
2017 10-22 31.27  ₩        50,000  ₩     1,599 라세티프리미어
2017 10-27 31.27  ₩        50,000  ₩     1,599 라세티프리미어
2017 11-03 25.02  ₩        40,000  ₩     1,599 라세티프리미어
2017 11-09 25.02  ₩        40,000  ₩     1,599 라세티프리미어
2017 11-14 18.76  ₩        30,000  ₩     1,599 라세티프리미어
2017 11-18 18.76  ₩        30,000  ₩     1,599 라세티프리미어
2017 11-23 18.76  ₩        30,000  ₩     1,599 라세티프리미어
2017 11-25 18.76  ₩        30,000  ₩     1,599 라세티프리미어
2017 12-01 18.76  ₩        30,000  ₩     1,599 라세티프리미어
2017 12-04 18.76  ₩        30,000  ₩     1,599 라세티프리미어
2017 12-08 25.02  ₩        40,000  ₩     1,599 라세티프리미어
2017 12-12 25.02  ₩        40,000  ₩     1,599 라세티프리미어
2017 12-17 18.76  ₩        30,000  ₩     1,599 라세티프리미어
2017 12-22 25.02  ₩        40,000  ₩     1,599 라세티프리미어
2018 01-01 25.02  ₩        40,000  ₩     1,599 라세티프리미어
2018 01-08 31.27  ₩        50,000  ₩     1,599 라세티프리미어
2018 01-11 19.75  ₩        30,000  ₩     1,519 라세티프리미어
2018 01-19 31.27  ₩        50,000  ₩     1,599 라세티프리미어
2018 01-23 18.76  ₩        30,000  ₩     1,599 라세티프리미어
2018 01-27 31.27  ₩        50,000  ₩     1,599 라세티프리미어
2018 02-07 25.02  ₩        40,000  ₩     1,599 라세티프리미어
2018 02-25 31.67  ₩        50,000  ₩     1,579 라세티프리미어
2018 03-05 25.33  ₩        40,000  ₩     1,579 라세티프리미어
2018 03-10 25.16  ₩        40,000  ₩     1,590 라세티프리미어
2018 03-17 25.64  ₩        40,000  ₩     1,560 라세티프리미어
2018 03-21 32.05  ₩        50,000  ₩     1,560 라세티프리미어
1555.9  ₩    2,370,000  ₩   90,089
평균   26.37  ₩      40,169  ₩   1,527  

뱅크셀러드 가계부 앱과, 클럽 모스트앱의 주유 기록을 입력하였습니다. 

3. 주유 기록 비교 

가. 총 운행거리와 실제 사용한 비용 

실제1년 운행거리 총 주유비 총리터 단가 1KM당 금액 실 연비
라프          9,600  ₩    2,370,000       1,556  ₩      1,527  ₩       246.88        6.17
니로        16,117  ₩    1,296,978 869.55  ₩      1,507  ₩        80.47       18.53
니로와 차액  ₩    1,073,022 686.35   ₩            -   ₩           166 -    12.36
실제 운행 한 결과 운행 거리가 55% 증가했음에도 주유 비용은 오히려 감소하였습니다. 

나. 실제 운행 거리를 니로와 동일하게 환산 할 경우

유류비 단가와 운행거리를 동일하게 산정해보겠습니다. 
유류비 단가 1527, 운행거리 16117을 적용하고 각각의 연비에 따른 주유량과 주유비를 계산해보았습니다. 
환산1년 운행거리 총 주유비 총리터 단가 1KM당 금액 실 연비
라프        16,117  ₩    3,988,524       2,612  ₩      1,527  ₩       247.47        6.17
니로        16,117  ₩    1,296,978 869.55  ₩      1,527  ₩        80.47       18.53
니로와 차액              -   ₩    2,691,546       1,742             -                167 -    12.36
동일 거리로 환산 할 경우의 약 269만원을 니로가 더 적게 씁니다. 제법 여유가 있어, 여행도 자주 다니고 여행지에서 맛집도 더 찾게 됩니다. 

다. 10년 운행 추정치

나. 를 바탕으로 10년치를 산정해보았습니다. 
환산10년 운행거리 총 주유비 총리터 단가 1KM당 금액 실 연비
라프       161,170  ₩  39,885,240     26,120  ₩      1,527             247        6.17
니로       161,170  ₩  12,969,780       8,696  ₩      1,527               83       18.53
니로와 차액              -   ₩  26,915,460     16,426                 -              1,580 -    12.36
10년간 운행했을 경우 니로 한대를 더 구입할 수 있는 비용이 절감되었습니다. 


4. 개인적인 생각 

중고차로 샀던 라프는 1년간 수리비가 260만원이나 들었습니다. 거기에 연비가 5.5km 정도밖에 나오지 않는 것은 큰 스트레스였습니다. 그리고 1년간 들어간 수리비로 완전히 고쳐진 것이 아니라 엔진계통의 잦은 이상 때문에 자주 정비소에 가면서 앞으로 들어갈 수리비와 기름 값을 생각하니 니로를 구매할 경우 3년간 월 15만원 정도만 더 내더라도 맘 편하게 탈 수 있겠구나 싶어 구매했는데 예상보다 더 마음이 편한 한해였습니다.

1년이란 기간 동안 니로를 타면서 연비주행 등은 신경 쓰지 않고 타고 있습니다. 처음에는 이런저런 사람들의 연비주행 이야기들을 보면서 따라 해보곤 했는데. 3,4개월 지나니 모든 것을 내려놓고 평소 주행으로 돌아가게 되었습니다. 그럼에도 불구하고 니로가 보여주는 연비는 매우 뛰어납니다. 그리고 그 뛰어난 연비는 생활의 여유로 돌아오게 되는군요. 맛있는 것을 더 먹을 수 있고, 더욱 자주 여행할 수 있게 해줍니다.

또한 요즘처럼 미세먼지가 많은 날이면 미세먼지 저감조치에 의해 승용차 홀짝제가 시행되는데 하이브리드 차량은 면제이기에 마음 편하게 차량을 운행하고 있습니다. 가끔 QM3가 니로와 경쟁 대상으로 거론되곤 하는데.... QM3가 5%정도 연비 더 좋다 하더라도 미세먼지 저감조치에 차량을 끌 수 없는 것이 불편한 것 같습니다. 실내 공간도 부족하고, 옵션도 부족하다던데 그런 건 상관없습니다. 그냥 매일 탈 수 있고, 기름 덜 먹는 차로서는 니로가 좋은 것 같습니다. 덤으로 공영주차장 반값도 좋습니다.

전기차와 비교했을 때는 전기차는 전기차의 매력, 하이브리드는 하이브리드의 매력이 있는 것 같습니다. 전기차의 경우 1km당 소요 금액이 20원으로 니로 vs 니로EV를 하더라도 니로 EV가 4배는 더 절감됩니다. 하지만 전기차는 아직 장거리 운행을 할 수 없고, 또한 웬만한 거리는 갔다가 돌아올 수 없는 (당일 여행 불가) 문제가 있죠. 하지만 이것도 어떤 면에선 매력입니다. 연인과 여행 갈 때 무조건 1박을 해야 한다는 것은 관계 발전에 도움이 될 것 같기도 합니다. ㅎㅎ;;

이상입니다. 읽어주셔서 감사합니다.



니로에 대한 다른 글도 있습니다.

2019년 2월 26일 화요일

대용량 데이터 처리 2. 오라클 병렬처리 parallel

1.PARALLEL은 언제 사용합니까?

병렬처리는 한 번에 대용량의 데이터를 처리할 경우 발생되는 부하를 여러 쓰레드에 분산처리 하는 기술입니다. 따라서 일반적인 조회나, 데이터 처리에는 INDEX를 통한 범위 처리를 주로 사용하게 됩니다. 병렬처리의 사용 빈도는 낮으며, 대용량 데이터 처리를 위한 프로시저나 프로그램에서 조심스럽게 사용됩니다. 개념적인 그림은 API에 잘 나와있어 참조하시면 되겠습니다.

오라클 병렬처리 API

이전 기술한 java 다중 쓰레드와 비슷한 구조를 갖고 있습니다. 자바의 thread와 비교한다면 thread.submit() 정도의 처리를 통해 개별 쓰레드에서 처리된 결과 집합을 재처리하는 기술이 잘 만들어져있다. 정도로 생각해도 괜찮을 것 같습니다.

2. 병렬처리 환경 정보 확인 

oracle 시스템의 환경 변수는 간단한 명령을 통해 확인할 수 있습니다. 
  select * from 
    v$parameter
  where 
  name like '%parallel%'; 

ORACLE 병렬처리 환경 문서 API
1. parallel_server : true 로 설정될 경우 병렬 서버 모드로 시작됨.
2. parallel_adaptive_multi_user : 시스템 과부하를 방지하기 위해 사용자의 병렬처리 수준을 제어합니다.

 예 : SELECT /+*PARALLEL(8)*/ * FROM DUAL; 명령의 PARALLEL(차수)에 해당함
      혹은 alter table emp parallel 8;
이처럼 차수를 지정하여 사용하는 것을 '수동 DOP' 옵티마이저가(쿼리 코디네이터라고 하기도 함) 자동으로 병렬 처리를 지정하는 것을 '자동 DOP'라고 합니다.

3. parallel_degree_limit : 자동 DOP가 사용 중일 때 한계 병렬처리 차수를 설정합니다.
(기본 값은 cpu 수 x cpu당 병렬처리 쓰레드 수 x 활성 인스턴스 수)
4. parallel_degree_policy : 자동 DOP 사용 여부를 제어, 병렬처리 큐 및 메모리 내 병렬처리에 사용됩니다. 기본적으로 비활성화 되어있습니다.
5. parallel_execution_message_size : 병렬 처리 시 서버와 쓰레드, 옵티마이저간에 사용되는 버퍼사이즈. 공유 메모리풀에서 할당됨.
6. parallel_force_local : 병렬 실행을 현재 인스턴스로 제한합니다.
7. parallel_max_servers : 한 인스턴스의 병렬처리와 병렬 복구처리에 사용될 최대 병렬 차수를 지정합니다. (인스턴스 시작 시점에 발생하는 미확정 트랜잭션의 처리와 복구에 관련된 변수인 것 같습니다.) 인스턴스 시작 시 해당 수치까지 프로세스를 증가시킵니다.
이 변수를 너무 작게 설정할 경우 일부 쿼리에서 병렬처리를 수행할 수 없게 됩니다.
8. parallel_min_servers : 오라클 데이터베이스 시작을 위해 시작 시와 병렬처리 제한에 사용될 병렬처리 프로세스 수를 지정합니다. 이러한 세팅은 병렬처리의 시작 비용을 균형적으로 만들지만 병렬 처리가 종료된 이후에도 메모리와 병렬 프로세스를 반환하지 않습니다. 데이터베이스 셧다운시 반환됩니다.
9. parallel_min_percent : 병렬 실행에 허용된 최소 쓰레드 비율, 기본 값은 0이며 병렬처리 에 사용할 수 있는 프로세스가 없는 경우 직렬 처리됨.
10. parallel_servers_target : 병렬처리가 사용되기 전 서버에서 병렬 처리 대기열이 사용할 수 있는 프로세스 수.  병렬 처리 대기열은 PARALLEL_DEGREE_POLICY가 AUTO로 설정되었을 경우 사용 가능합니다.
11. parallel_threads_per_cpu : CPU당 병렬처리에 사용가능한 쓰레드 수.

3. 병렬처리 사용 시 주의사항

ORACLE API 문서와 병렬처리 환경설정은 대게 사용자가 직접 작성한 힌트를 통한 SQL을 처리를 하는 것 보다는 자동 처리에 초점이 맞춰진 것 같습니다. 하지만 실무 환경에서 이런 옵션 설정만으로 병렬 처리를 수행 할 경우 위험할 수 있습니다.

예를 들자면 동시접속자가 800명인 시스템에서 자동 병렬처리를 16개씩 자동으로 사용하게 했다고 칩시다. 물론 동시에 조회 버튼을 땅~ 하고 칠 리는 없지만 그래도 동시에 조회버튼을 눌렀다고 상정 할 경우 사용해야하는 쓰레드의 수는 12800개입니다. 당연히 시스템에 과부하가 생길 것 입니다. 그래서 OLTP환경에서는 대게 사용하지 않습니다. 또한 동시접속자가 많을 것으로 예상되는 데이터베이스의 경우 병렬모드를 사용하지 않게 설정되었을 수 있습니다. 가장 최근 확인한 것은 v$parameter 테이블을 조회했을 경우 parallel_dgree_limit 을 변수가 존재하지 않는 환경을 확인했습니다.

DBA에게 병렬처리 수준에 대한 질의를 해야 하는 것도 이런 부분입니다. 데이터베이스 관리 시스템을 도입하는 시점에서 적정 성능을 보장받기 위해 여러가지 성능에 대한 테스트를 하는데 이 때, 병행 제어 수준에 대한 테스트도 수행하기에 해당 문서를 갖고 있다면 현행 DBMS의 사용자 수와 그에 맞는 병행 제어 수준을 도출할 수 있기 때문입니다.

OLTP 환경의 조회 SQL에서 수행이 느릴 경우 대책으로 가장 많이 사용되는 것은 PARALLEL이 아닌 SQL에 대한 제약조건 강화를 하는 것이 보편적이고, DELETE 속도가 느릴 경우 역시 PARALLEL처리보다는 해당 데이터 영역을 파티션 테이블로 만들어 파티션에 대한 TRUNCATE를 수행하는 경우가 더 많습니다.
(물론 TRUNCATE 역시 복구 불가에 대한 공포증으로 사용하지 않는 사람도 많습니다.)

병렬 처리는 만능이 아니며 부주의하게 사용할 경우 문제가 발생하기에 아예 기피하는 사람도 있습니다.

개인적으로는 파라미터 설정 중 parallel_max_server 값의 2.5%정도 수준에서 시작하여 5%내에서 사용하고 있습니다. 통상적으로 동시접속자 30인 이내의 데이터 가공 및 집계처리 환경의 DBMS를 사용하고 있고, 일과시간에 작업하는 프로그램이 대부분이기 때문입니다. 하지만 일과 외 사긴에 사용될 경우에는 병렬처리 수준을 더 높이기도 합니다. 정확한 기준이 없는 경우가 많기 때문이기도 합니다.

4. parallel 명령 사용 예 

가. DML 사용 예

1) SELECT , DELETE, INSERT, UPDATE, MERGE 명령의 바로 뒤에
   SELECT /*+PARALLEL(16)*/ * FROM 테이블;
   DELETE /*+PARALLEL(16)*/  FROM 테이블;

2) JOIN 시에는 테이블 마다(그렇지 않을 경우 테이블 수 X PARALLEL 개수만큼의 병렬 처리가 생성됨.
  SELECT /*+PARALLEL(A 16)(B 16)*/  * FROM 테이블 A , 테이블 B  WHERE A.ID = B.ID;

※ DBMS 설정에 따라 병렬처리 사용을 세션단위로 허용해야 하는 경우 아래 명령을 사용
ALTER SESSION ENABLE PARALLEL DML;

3) 병렬 처리와 일반처리의 실행계획 차이
SELECT * FROM TABLE 플랜 (병렬 힌트를 줬으나 미 적용시에도 동일)
-----------------------------------------------------------------------------------------
 SELECT STATMENT ALL_ROWS
    Cost : 332,704 Bytes:8,402,920,050 Cardinality : 52,192,050
      PARTITION LIST ALL
        Cost : 332,704 Bytes:8,402,920,050 Cardinality : 52,192,050
          Partition #: Partition accessed #1 - #7 
            TABLE ACCESS FULL TABLE...
             Cost : 332,704 Bytes:8,402,920,050 Cardinality : 52,192,050
                Partition #: Partition accessed #1 - #7
--------------------------------------------------------------------------------------------------------------

SELECT /*+PARALLEL(16)*/ FROM TABLE (병렬힌트가 적용되었을경우) 
병렬처리 코디네이터가 실행계획에 등장하게 됩니다. 
--------------------------------------------------------------------------------------------------------------
SELECT STATMENT ALL_ROWS
    Cost : 332,704 Bytes:8,402,920,050 Cardinality : 52,192,050
      PX COORDINATOR
          PX SEND QC(RANDOM) PARALLEL_TO_SERIAL_SYS.:TQ1000:Q1000
            Cost : 332,704 Bytes:8,402,920,050 Cardinality : 52,192,050
                   PX BLOCK ITERATOR PARALLEL_COMBINED_WITH_CHILD.:TQ1000:Q1000
                   Cost : 332,704 Bytes:8,402,920,050 Cardinality : 52,192,050
                        Partition #: Partition accessed #1 - #7 
                      TABLE ACCESS FULL TABLE...
                         Cost : 332,704 Bytes:8,402,920,050 Cardinality : 52,192,050
                               Partition #: Partition accessed #1 - #7
--------------------------------------------------------------------------------------------------------------

나. DDL 사용 예

테이블의 정의나 인덱스 정의에 병렬처리 수준을 지정할 경우 DML문을 사용할때 병렬처리 수준을 선언하지 않아도 자동적용이 됩니다. 조인 처리가 될 경우의 병렬 수준에 대한 고려는 직접 해본적이 없기에 저도 잘 모르겠습니다. 알게되면 좀더 보완하도록 하겠습니다.
1) 테이블, 인덱스 등의 변경
ALTER TABLE 테이블명 PARALLEL 16;
ALTER INDEX 인덱스명 PARALLEL 16;

2) 테이블 인덱스 등의 생성
CREATE TABLE 테이블명 (컬럼명 컬럼타입+길이,...) PARALLEL (DEGREE 16 INSTANCES 1)
CREATE INDEX 인덱스명 ON 테이블명(컬럼명...) PARALLEL  (DEGREE 16 INSTANCES 1)

5. 기타 

실행계획의 차이에 대해서 기술해보려 했으나 PARALLEL로 생성된 테이블이나 인덱스와, 힌트를 통한 /*+PARALLEL(16)*/의 실행 계획 차이는 정말 미묘했습니다. COST가 1정도 차이가 나거나, 거의 차이가 없는 유사한 모습이 보여, 차이라고 할 수 없는 애매모호한 상황이 되었습니다.

개인적으로는 OLTP환경에서는 DML을 이용하여 PARALLEL을 사용하는 것을 금하고 있습니다. 다중 사용자의 트랜잭션에 의해 발생할 수 있는 병렬처리 락 때문입니다. 반면 동시 접속자가 적고 통계 위주의 시스템 환경이라면 PARALLEL을 적극적으로 사용하는 편입니다.

이상입니다. 수정이나 의견이 있으신 분은 댓글 달아주시면 고맙겠습니다.

연관된 문서가 있습니다.
대용량 데이터 처리 1. java 다중쓰레드 활용
대용량 데이터 처리 2. 오라클 병렬처리 parallel
대용량 데이터 처리 3. 테이블 파티셔닝
대용량 데이터 처리 4. DBMS_JOB

2019년 2월 21일 목요일

대용량 데이터 처리 1. java 다중쓰레드 활용

1. multi thread 는 어디에 씁니까? 

개인적인 용도는 이렇습니다.

데이터 처리 위주의 웹서비스를 운영하다 보면 항상 발생하는 문제가 있습니다. 데이터 처리 시간이 지연되면서 발생하는 web의 응답 지연 현상. 단순하게 select 구문을 사용한 데이터 조회라면, SQL을 수정하면 되지만, 그렇지 않을 경우가 있습니다.

바로 대용량 text, json, excel 파일을 입력하는 경우 발생하는 문제입니다. 이중 가장 처리시간을 획기적으로 줄일 수 있는 것은 EXCEL입니다. EXCEL 파일은 스트림을 열어 최대 로우수와 시트수를 확인하고 여러 쓰레드로 분할하여 처리 할 수 있기 때문입니다.

그리고 TEXT, CSV, JSON파일의 경우 파일의 사이즈를 알 수는 있지만, 최대 행수를 미리 알 수 있는 방법이 없어 단일 스레드 처리를 하게 되며 처리 시간이 더욱 증대 되는 문제가 있습니다. 하지만 WEB에서 많이 사용하는 방식이기도 합니다.

WEB서비스는 SESSION의 제한 시간을 30분으로 설정하고 있는 경우가 많습니다. 세션 시간이 30분으로 제한되어있는 서비스의 경우 웹을 통한 요청이 30분이 초과되면 해당 프로세스를 종료시키는 로직이 추가되는 경우가 많기에 대용량의 CSV, TEXT 파일의 처리에는 대게 WEB서비스에서 분리시키는 쓰레드를 사용하게 됩니다.


2.multi thread 사용 시 주의사항 

WAS 서버의 환경을 우선 알아야 합니다. 대게 2가지 정도만 알면 되는데 적용할 서버의 HEAP SPACE와 THREAD POOL의 개수입니다. 둘 중 하나가 초과될 경우 심각한 오류와 함께 서버가 셧다운 되는 현상이 발생할 수 있으므로, 서버관리자에게 문의 후 결정하는 것이 좋습니다.

프로그래밍 시에도 thread를 무한정 늘리는 코드를 만드는 것은 하지 않는 것이 좋습니다.

일반적으로 가장 많이 범하는 오류가 thread.sleep(1000); 명령을 통해 쓰레드의 종료시간을 예상하여 쓰레드의 개수를 유지하는 방법입니다. 이런 방법으로 프로그래밍 할 경우 다양한 외부 환경의 변화로 인해 처리 속도가 지연될 경우 역시 시스템의 셧다운이 발생할 수 있습니다.

3. thread에 대한 설명

그래서 간략하게 몇 가지만 이야기 하겠습니다. 

1) 개념 적 이야기 

대략적인 내용을 그림으로 그리자면 아래와 같습니다. 


메인 쓰레드에서는 동시 처리를 수행할 작업량과, 수행 작업을 담은 작은 단위의 작업을 생성합니다. 생성된 작업은 메모리(힙 스페이스)에 적재되고, 작업 대기 중인 쓰레드에 할당되어 작업 처리를 수행합니다. 위와 같은 개념적인 구조를 갖고 있기에 다중 쓰레드 처리를 잘못 할 경우 쓰레드 문제가 발생하는 것 입니다. 

2) 흔히 발생하는 오류 

발생하는 문제는 대게 아래와 같습니다.

1. java.lang.OutOfMemoryError : unable to create new native thread 
  at java.lang.Thread.start0(Native Method)
  at java.lang.Thread.start(Thread.java:597)
  at java.util.concurrent.ThreadPoolExecutor.addifUnderMaximumPoolSize(ThreadPoolExecutor.java:727)
  at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:657)

2. java.lang.OutOfMemoryError : Failed to create a thread retVal -1073741830, ernrno 11
  at java.lang.Thread.startImpl(Native Method) 
  at java.lang.Thread.start(Thread.java:886)

※ 1,2는 유사한 오류입니다. 너무 많은 쓰레드가 한 번에 호출되어 더 이상 쓰레드를 만들 수 없음. (시스템 사양에 따라 다른 것 같습니다. 개인 pc는 600개까지 되며 was 시스템에서 200개 까지 확인하였습니다.) 즉. 쓰레드의 종료 처리가 되지 않아 발생하는 문제 입니다. 

3. "Thread-20" java.lang.OutOfMemoryError : java heap space 
※ 개별 쓰레드에 할당된 작업의 크기가 heap 스페이스의 크기를 초과했을 경우 발생합니다. 

위 오류들의 패턴으로 봤을 때 쓰레드의 오류는 대게 할당된 메모리 영역을 벗어났을 때 발생하는 것 같습니다. 1,2번의 경우 할당할 수 있는 쓰레드 개수를 초과할 경우 (제 개인 pc에서는 600개의 쓰레드가 할당되지만, 타 시스템 적용시 200개밖에 되지 않음), 그리고 3번은 쓰레드에 할당할 힙 메모리가 없을 경우 발생했습니다.

4. thread 코드 작성

JDK 8버전입니다.
멀티 쓰레드를 호출할 메인 클래스 생성 TestMain.java 
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class TestMain {

static int maxThreadCnt = 4; //최대 쓰레드 수 
static int inPutRowCnt = 1000; //쓰레드별 입력행수
static boolean threadState = true; //쓰레드 그룹개수제한
        //쓰레드 그룹을 컨트롤 할 서비스 생성
private static ExecutorService ex = Executors.newCachedThreadPool(new ThreadFactory(){
@Override
public Thread newThread(Runnable r){
return new Thread(r);
}
}); 

//파일 처리를 위한 메인 쓰레드 
public static void main(String[] arg) {
String fPath = "D:/testwork/testdata/IFS_02-11-2019 20-54-22-00_timeSeries.dat"
try {
FileReader rd = new FileReader(fPath);
BufferedReader br = new BufferedReader(rd); 
String readLine
int rowcount = 0; 
ArrayList<String> list = new ArrayList<String>();
while ((readLine= br.readLine()) != null) {
rowcount++;
list.add(rowcount+","+readLine); 
if(rowcount%inPutRowCnt == 0){ 
//설정된 파일 행 수 만큼 분할하여 개별 쓰레드에 적재
Runnable r = new Testsub((ArrayList<String>) list.clone()); 
threadManage(r); 
list.clear();
}
}
//미처리 된 데이터에 대해 처리 
if(list.isEmpty()){
System.out.println("파일 처리 완료 ");
}else{
Runnable r = new Testsub((ArrayList<String>) list.clone());
r.run();

}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

//쓰레드 관리를 위한 쓰레드 관리함수 
private static void threadManage(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
               
//활성중인 쓰레드의 개수가 최대 쓰레드 개수보다 작을 경우 EXECUTOR사용
if(t.activeCount() < maxThreadCnt && threadState){
System.out.println("Active Thread count is : "+t.activeCount());
ex.execute(t);
}else{
//최대 쓰레드 개수에 도달 시 executor에게 작업완료 후 종료명령
if(ex.isTerminated()){
//활성 쓰레드가 모두 종료되었을 경우 새 Executor 할당 후 실행
threadState = true;
ex = Executors.newCachedThreadPool(new ThreadFactory(){
@Override
public Thread newThread(Runnable r){
return new Thread(r);
}
}); 
ex.execute(t);
}else{
if(threadState){  //쓰레드 종료 명령이 호출되지 않은 경우만 수행
threadState = false;
System.out.println("Thread group Shutdown");
ex.shutdown();
}
t.run(); //쓰레드 종료 명령이 완료될 때까진 메인 클래스가 직접수행 
}
}
}
}

하위 생성될 보조 쓰레드 Testsub.java
import java.util.ArrayList;
import java.util.HashMap;

public class Testsub implements Runnable{

ArrayList<String> pList

public Testsub(ArrayList<String> arrayList) {
pList = arrayList//전달받은 파라미터를 할당
}

@Override
public void run() {
ArrayList dataArray = new ArrayList();
for(int i = 0; i < pList.size(); i++){
  //개별 비즈니스 로직을 탑재 
String[] strArr pList.get(i).split(","); 
HashMap<String, Object> tmpMap new HashMap<>();
tmpMap.put("0"strArr[0]);
tmpMap.put("1"strArr[1]);
tmpMap.put("2"strArr[2]);
tmpMap.put("3"strArr[3]);
tmpMap.put("4"strArr[4]);
dataArray.add(tmpMap);
}
//DB적재를 위한 코드 작성해서 사용 
System.out.println("work complete dataCnt : "dataArray.size());
}
}

수행결과 콘솔
Active Thread count is : 1
Active Thread count is : 2
Active Thread count is : 3
Thread group Shutdown
work complete dataCnt : 1000
work complete dataCnt : 1000
work complete dataCnt : 1000
work complete dataCnt : 1000
....
....
work complete dataCnt : 150
파일 처리 완료 
work complete dataCnt : 1000

※ 참고사항
TestMain.java
일단 스트림에서 바로 데이터를 처리하지 않는 이유는 단건 insert의 경우 부하가 심하고, 데이터에 대한 비즈니스 로직이 탑재 될 경우 해당 연산처리 덕에 메인 프로세스가 느려지는 것을 방지하기 위해 메인 클래스에서는 데이터를 분할하여 전송만, 보조 쓰레드에서는 데이터를 입력하는 비즈로직을 수행하는 방식을 취하고 있습니다.

ExecutorService ex 를 사용하는 이유는 수행되고 있는 쓰레드를 그룹화 하여 수행 shutdown() 명령 하나로 '작업 완료 후 종료해' 라는 명령을 간결하게 내릴 수 있습니다. JDK나 WAS시스템의 환경에 따라 쓰레드의 개수를 제어하기 편리 합니다.

shutdown()의 명령 이후에는 isTerminated() 명령을 사용하여 쓰레드의 종료를 확인해야 합니다. 서비스의 종료가 완료되지 않은 상태에서 새 서비스를 추가 할 경우 TestSub.java에서 수행되는 개별 비즈니스 로직의 수행 속도에 따라 쓰레드가 무한하게 증식할 수 있습니다. 

Runnable new Testsub((ArrayList<String>) list.clone()); 
러너블은 생성하는 하는 단계에서 파라미터를 전달할 수 있습니다. 전달할 수 있는 것은 파라미터뿐만 아니라 SPRINGFRAMEWORK에서의 SERVICE도 가능합니다. VO도 가능하겠죠? (이 부분은 SQL 사용을 위한 Service만 전달해봐서 정확히 모르겠습니다. ) 

또한 list 자체를 전달하는 것이 아닌 list.clone()으로 복제를 생성하는 이유는 가공중인 상태의 리스트를 던질 경우 바로 뒤에 오는 초기화 부분 list.clear(); 에서 초기화 되면서 리스트에 담겼던 데이터가 사라집니다. 

쓰레드 호출 시 t.setDaemon(true);를 사용한 이유는 이상한 서버의 오류 때분입니다. 자바의 쓰레드는 데몬 쓰레드 이건 아니건 종료되면 가비지 컬렉터에 의해 자원이 회수되는 것이 정상이지만, 일부 was 서버에서 사용자 쓰래드가 종료되지 않아서 할당된 쓰레드가 모두 종료되지 않는 오류가 있었습니다. 그 이후 이렇게 쓰고 있습니다.

Testsub.java
Thread를 만든다더니 Runnable을 만든 이유는 상속이나 수행 측면에서 이용이 편리해서입니다. 

dataArray 를 사용하는 이유는 대량의 데이터 입력 시oracle.jdbc.driver.OracleDriver 의 배열 처리가 한 건씩 데이터 입력 SQL을 수행하는 것 보다 수백 배 속도가 빠르기 때문 입니다. 

1건씩 insert 처리 시 접속 -> SQL전송 -> 커밋 -> 접속해제를 반복하기에 자원의 소모가 심합니다.  그리고 속도도 느리기 때문에 스프링 환경에선 array, 기타 환경에선 executeBatch를 사용하는 것을 개인적으로 선호합니다. 

프로그램 구동 영상 (뭐 볼 필요 없겠지만 첨부합니다.)

이상입니다. 수정이나 의견이 있으신 분은 댓글 달아주시면 고맙겠습니다.

연관된 문서가 있습니다.
대용량 데이터 처리 1. java 다중쓰레드 활용
대용량 데이터 처리 2. 오라클 병렬처리 parallel
대용량 데이터 처리 3. 테이블 파티셔닝
대용량 데이터 처리 4. DBMS_JOB

2019년 1월 26일 토요일

무지개는 어른이 되어서 보아도 신기합니다. ㅎㅎ;;

2018년 10월 1일 사무실 창밖에 무지개가 너무 예쁘게 자리 잡고 있었다. 



2018년 12월 30일 구름 낀 하늘에 보였던 무지개 


최근 들어 무지개가 자주 보이는 걸 보면 개인적으로 좋은 일이 있을까? 싶기도 하고 2019년에는 소망이 좀 이루어지나 싶기도 합니다 ㅎㅎ

2019년 1월 25일 금요일

log4j 로그 레벨에 의한 데이터 입출력 시간 실험

이 문서는 tomcat 서버 혹은 JEUS 서버에서 나타나는 대용량 파일에 대한 (3.78GB, 약2500만 로우의 데이터가 수록된) 데이터베이스 적재 작업에서 발생한 성능 저하 현상이 발생해 해당 작업에 로거가 미치는 영향을 정확한 결과로 기록하기 위해 수행하였습니다.

1. log4j 간략정보

아파치의 오픈소스 도구라고 합니다. JAVA기반이기에 웹/와스 기반의 시스템에서 많이 사용합니다. C와 PYTHON등 다양한 언어를 지원한다고 합니다.  프레임워크에 라이브러리로 참조 되는 경우가 많고 환경설정은 대체로 log4j.xml 혹은 log4j2.xml 파일을 통해 로거레벨을 설정하고 있습니다. 로깅 수준에 따라 등록된 다양한 로그들을 유형별로 보여주거나, 숨길 수 있습니다.

로거 레벨에 대한 정보는 아래와 같습니다.
인용 : https://www.tutorialspoint.com/log4j/ 

LevelDescription
ALL사용자 정의를 포함한 모든 레벨 
DEBUG응용 프로그램 수정에 필요한 세부 정보를 지정 
INFO응용 프로그램 진행에 해당하는 정보를 굵은 글씨로 강조하여 지정
WARN잠재적 유해 사항을 지정
ERROR응용 프로그램 진행에는 영향이 없으나, 악영향을 줄 수 있는 오류를 지정
FATAL프로그램 중단이 예상되는 치명적인 오류를 지정 
OFF로그를 해제 
TRACE디버그 보다 상세한 이벤트 정보를 지정

2. 로거 성능 테스트 환경

사용 pc환경 :
프로세서 : Intel(R) Core(TM) i5-3230M@2.60Ghz
설치된 메모리: 8.0GB ddr3
os : windows 7 64bit

대상파일 :
출처 : http://data.imf.org   > Download Imf Data > InterNational Financial Statistics (IFS)
용량 : 3.78GB
행수 : 25,691,217

데이터 추출용 프로그램은 자작으로  4개의 멀티 스레드를 사용하며 jdbc의 array parsing 을 이용한 인서트 작업을 수행합니다. 전체 데이터를 넣는데 1시간이 걸리므로 약 400건씩 데이터를 쪼개 넣는 작업을 1000회 수행 후 종료하도록 한 후 소요시간을 체크해보았습니다.

3. LEVEL에 따른 성능 측정 


Level총 수행정보
ALL224초  입력 - 총 : 312843, 분당 : 83797, 초당: 1396
DEBUG222초  입력 - 총 : 312843, 분당 : 84552, 초당: 1409 
INFO101초  입력 - 총 : 312843, 분당 :185847, 초당: 3097
WARN101초  입력 - 총 : 312843, 분당 : 185847, 초당: 3097 
ERROR97초  입력 - 총 : 312843, 분당 : 191536, 초당 : 3192
FATAL100초  입력 - 총 : 312843, 분당 : 187705, 초당 : 3128
OFF104초  입력 - 총 : 312843, 분당 : 180486, 초당 3008 
TRACE226초  입력 - 총 : 312843, 분당 : 83055, 초당 1384
각각 1회씩 수행한 결과입니다. off만 예측과 너무 다른 결과가 나와서 3회 수행하였습니다. 하지만 3회 모두 유사한 결과가 도출되었습니다.

처리속도에 연관되는 debugger level 별 성능 순위는 아래와 같습니다.
성능은 초당 입력 건수를 기준으로 상승률을 %로 뽑아봤습니다.

1위 ERROR                    :  5위 대비 130% 성능 상승
2위 FATAL                     :  5위 대비 126% 성능 상승
3위 OFF                        :  5위 대비 123% 성능 상승
4위 INFO, WARN             :  5위 대비    7% 성능 상승
5위 ALL, DEBUG, TRACE   

운영 시스템의 성능 최적화를 위한 로거 레벨은 ERROR, FATAL을 사용하는 것이 나은 것 같습니다. OFF의 성능이 생각보다 낮은 이유는... 언젠가 알고 싶네요 ㅎㅎ;

4. 기타 

최근 사무실에서 사람들이 SQL오류를 확인하기 위해서 core.log.jdbc.driver.OracleDriver 라는 드라이버 클래스를 사용하기 시작했습니다. 줄 바꿈과 파라미터 인풋을 지원하는 드라이버 클래스인데 개발자 중 일부가 이것을 운영 서버에 적용하면서 어마어마한 일이 일어났었습니다. 파일 적재 시스템 마비가 왔었죠.... 약 3천만 건 처리 예상 시간이 21일 16시간이었습니다. ;

일상적으로 사용하는 oracle 용 jdbc드라이버 클래스는 oracle.jdbc.driver.OracleDriver인데 상위 드라이버를 사용할 시 로거 레벨과 관계없이 속도가 저하됩니다. CPU와 메모리의 사용율도 100%가 유지되며, 시스템의 각 기능이 정상동작하지 않게 됩니다.

core.log.jdbc.driver.OracleDriver의 성능

경과시간 1950초 총 입력 건수 23108건, 분당입력 710, 초당입력 11건

1000번은커녕 77회 수행 후 종료해야 했습니다. 저도 일은 해야 하니까요 ㅎㅎ;;;

1위 대비 29018% 성능 저하

이 정도면 사람 하나 죽어나갈 속도입니다. 부디 운영 중인 시스템에 이런 로그 클래스를 사용하는 일이 없으시길 바랍니다.

이상 log4j에 대한 간결한 시험기였습니다. 읽어주셔서 감사합니다. 행복하세요 ㅎㅎ


2019년 1월 18일 금요일

Iframe 을 이용한 cross - domain 메시지 교환

하나의 세션에 대한 인증 결과와 경과 시간에 대한 주기적인 체크를 할 수 있는 방법이 필요했습니다.

여러 다른 url을 가진 사이트간의 통신은 cross-domain 보안 정책에 의해 기본적으로 차단되어 있는 상황에서 보안정책을 변경하는 것 보단 간단한 메시지를 전달하는 정도의 방식으로 프로그래밍을 하는 것이 수월하기에 iframe을 이용해 서로 다른 도메인간의 통신을 유도하는 방법을 찾아보았습니다.

일단 메시지를 보내는 주 사이트와, 메시지를 받는 사이트 측으로 분리해서 아래와 같은 스크립트를 만들어보았습니다.

크로스 도메인을 위한 스크립트 구성 

부모페이지에 추가된 script (메시지를 받는 쪽)
//iframe를 첨부할 jsp 내에 아래와 같은 스크립트와 iframe을 추가합니다. 

<script type="text/javascript">
//iframe에서 전송하는 메시지를 받을 리스너를 생성 
//(iframe과 동일한 페이지 현재 스크립트가 있어야함)
function receiveMessage(event){
//리스너 이벤트 전송자와, 명령어가 동일할 경우 수행 
if(event.data === "testMessage" && event.origin === "http://testsite"){ 
//event.data는 리스너를 통해 전달받는 데이터입니다. 
// ===은 데이터 타입과 데이터 내용이 모두 일치하는지 비교하는 연산자 
//event.origin은 지정된 주소를 입력하여 지정된 도메인에서 발신한 것인지 확인합니다.
alert("메시지 전달 받았습니다.");//메시지 수신 후 실행할 스크립트를 입력합니다.
}
}
//현재창에 메시지 이벤트 리스너를 추가 
window.addEventListener("message", receiveMessage); 
</script>
//현재창에 메시지 이벤트 리스너를 추가 
<iframe id="test" name="test" src="http://testsite/innerFrame.jsp"></iframe>

메시지를 주는 쪽인 http://testsite/의 innerFrame.jsp
<script type="text/javascript">
var parent = window.parent; 
if(parent && parent.postMessage){
        //postMessage("전송메시지","전송 도메인") 
parent.postMessage("testMessage",  "*");
}else{
console.log("post Message is not allow"); 
}
</script>

위와 같은 비교적 단순한 구성을 통해 서로 다른 도메인 간의 메시지를 주고받을 수 있습니다.

postMessage 함수는 메시지와 함께 전송할 대상 도메인을 특정할 수 있습니다. 이유는 불필요한 도메인에서 해당 소스를 붙여 메시지를 가로채거나 하는 문제를 방지하기 위해서 입니다.

이 스크립트는 iframe내부 문서와 부모창의 구성을 바꿔서 사용할 수도 있습니다.

postMessage구성 

postMessage(메시지, 대상도메인)

메시지 : 다른 창으로 보낼 데이터를 담는 부분입니다. (구조화된 복제 알고리즘을 통해 정해진 양식으로 변형되어 전송된다고 합니다. )

대상도메인 : "*"을 입력한경우 대상을 특정하지 않고 아무에게나 보내는 것입니다. 공개된 메시지가 되는 것이므로, 중요한 데이터를 보내면 위험할 수 있습니다. 아무나 iframe을 사용하여 메시지를 확인할 수 있으니까요.
사용 예) document.postMessage("메시지", "http://test.kr") 의 형태로 메시지를 보낼경우 http://test.kr이 아닌 도메인에선 해당 메시지를 확인할 수 없습니다. 

SANDBOX 


HTML5에서는 SANDBOX라는 속성이 생겼습니다. 이것을 이용해 크로스도메인 정책을 우회할 수 있다는 인터넷의 일부 문서를 확인했습니다만. 적용결과 실패하였습니다. 안됩니다.

샌드박스 속성과 값 

1. (값없음)                   모든 제한 사항 적용
2. allow-forms               폼 전송을 사용할 수 있습니다.(form.submit())
3. allow-pointer-lock       api를 사용할 수 있습니다.
4. allow-popups             팝업을 사용할 수 있습니다.
5. allow-same-origin        iframe내부의 콘텐츠가 같은 도메인으로 취급됩니다.
6. allow-scripts               스크립트를 사용할 수 있습니다.
7. allow-top-navigation     iframe 내부 콘텐츠가 top.document의 location을 제어할 수 있습니다.

위와 같은 태그 속성이 있고 iframe을 선언할 때 사용하는 코드입니다. 사용방법은 아래와 같습니다.
1. (값없음)  <iframe sandbox src=""> </iframe>
2. allow-forms   <iframe sandbox="allow-forms" src=""> </iframe>
3. 복합 <iframe sandbox="allow-forms allow-top-navigation" src="">  </iframe>

단일 속성을 사용할 수도 있고, 복합 속성을 사용할 수도 있습니다. allow-same-origin  의 경우 서로 다른 도메인일 경우에도 같은 출처로 취급할 수 있다고 설명되어있지만, 실제 사용결과는 다음 오류가 발생합니다.

예) iframe 내에서 top.window.document.reload()를 수행한경우 오류
Uncaught DOMException: Blocked a frame with origin "" from accessing a cross-origin frame.

도메인이 다르기 때문에 발생하는 오류입니다. 이것이 postMessage를 사용하게 된 이유이기도 합니다. 같은 도매인 내에서의 iframe호출은 성공적이었지만, 서로 다른 도메인에 적용한 결과 위와 같은 오류가 발생하여 사용할 수 없습니다.

w3schools에서는 모든 브라우저에서 적용된다고 되어있지만, 실제로 되지 않고 있습니다. 이유를 아시는 분이 계신다면 댓글을 달아주시면 감사하겠습니다.


인터넷 익스플로러 오류 해결 SCRIPT5009: 'JSON'이(가) 정의되지 않았습니다.

최근 여러 사이트에 대한 연동 시스템을 구축하면서 다양한 개발 환경 문제에 직면하게 되었습니다. 그중 ifame을 통한 부모와 자식 창간의 데이터 통신을 하는 과정에서 특정 사이트만 되지 않는 오류를 확인하였는데 이유는 javascript를 해석하는 엔진 버전의 문제인 것 같습니다.


인터넷 익스플로러 오류 SCRIPT5009: 'JSON'이(가) 정의되지 않았습니다.
웹 호환성을 위해 모든 브라우저에서 테스트 하지만 특정사이트만 Internet Explorer에서 지원되지 않는 현상이 있었습니다. 확인하려고 로그를 열어보니 저 빨간색 동그라미 2개가 신경이 쓰입니다. 문서모드7 ? 그리고 우측에 7?

정체는 바로 Internet Explorer 7 버전의 문서모드를 사용하겠다는 선언이  jsp문서에 있었습니다.

<meta http-equiv="X-UA-Compatible" content="IE=7" />

메타 테그로 설정되어있는 이 모드는 일부 html5 기능과 javascript의 동작을 방해하게 됩니다. 최근 트랜드인 웹표준에도 당연히 저해되는 코드입니다. 비슷한 유형의 오류가 발생하고 있다면 해당 태그를 삭제하시면 해결 됩니다.

JSON 데이터 타입은 HTML5에서부터 표준으로 제공되고 있습니다. 웹표준으로 지정되기 이전에는 JSON.ORG를 통해 JSON.JS 라이브러리를 다운받거나, 웹 문서에 링크해 사용했었습니다. https://www.json.org/json-ko.html

html5 라는 웹 표준이 생기면서 여러 라이브러리들이 통합적으로 브라우저에서 지원되는 환경으로 바뀌면서 생기는 오류로, ie도 8버전에서부턴 기본 스크립트로 지원하지만, 하필 7 이하에선 지원하지 않아 생기는 오류입니다.

2018년 12월 28일 금요일

어설픈 사기꾼의 감언이설 6탄 - 비트코인 사기

2018년 12월 26일 크리스마스가 지난 직후 대전 비래동의 한 술집에서 옆자리에 있었던 사람들의 이야기가 너무 수상해 차분히 들어보기로 했다. 2개의 테이블을 합쳐서 약 8명의 무리를 만든 이 분들은 명백하게 사기를 당하는 중이셨다.

일단 해당 가족은 5인, 할머니, 할아버지와 그 자식 부부와 초등학생으로 이루어져 있었으며, 3인의 다단계 판매책이 이들에게 감언이설을 속삭이고 있었다. 그리고 이것은 비트코인 사기 만으로 표현하기엔 너무 여러 내용의 사기를 포함하고 있었다.

옆자리에서 들은 내용은 이러했다.

주요 홍보 내용 

1. 최소 월 300,0000원에서 최대 월 3,000,000원 까지 저축 계좌를 개설할 수 있다.

2. 월 납입금에 따라 등급이 나뉜다.  (실버, 골드, 플래티넘)

3. 기존의 다단계 방식이 아닌 특정 어플을 사용하며, 해당 어플에 달러화로 입금을 하면 입금 금액에 대한 이자를 제공한다.

4. 이율은 월 40%라고 소개하고 있으며, 300만원이 2년 후에는 월 500만원으로 둔갑한다고 한다.

5. 계좌는 1인 1계좌 이상 만들 수 없기에 가족을 모두 오라고 했다. 미성년자도 계좌를 개설할 수 있다고 한다.

6. 비트코인(혹은 인터넷 뱅킹) 처럼 휴대폰 어플을 통해 실시간으로 계좌 정보를 확인할 수 있다.

7. 비트코인 지갑을 보여주며 2년전에 4달러를 입금했는데 지금 1600달러가 되었다고 했다.

이 정도만 들어도 수상하지 않은가? 유사수신행위이다.  유사수신은 법률상으로 금지되어있는 은행이 아닌 자의 이자 제공을 빌미로 한 자금 모집을 총칭한다.

여기서 가장 말이 안되는 건 월 수익이 40%라고 했는데 2년전의 4달러가 어떻게 1600달러가 되었는지 의문이다. 이건 수학으로 풀 수 없다.

휴대폰 어플을 통해 계좌 정보를 확인할 수 있는 것은 비트코인과 비슷한 형태를 띄고 있어 모집책이 xx코인 이라고 이야기 하고있는데 무슨 코인인지는 정확히 듣지 못했다.

이 어플 때문에 기존의 사기보다 발전됐다는 평가를 내릴 수 있는데 이전의 사기꾼들은 보이지 않는 것은 보이지 않는 그대로 두었다면, 이제는 어플이 있으므로 보이지 않는 것을 보이는 것 처럼 가장하고 있다는 것이 다르다. 사람들의 신뢰를 얻는 측면에서 발전했다고 할 수 있다.  즉 사기로서의 위험도와 성립 가능성이 높아졌다.

사기꾼과 아들의 질의응답 

법적으로 불가능한 행위를 하는 이들. 제법 똑똑해보이는 아들이 몇 가지의 질문을 던졌고, 대답은 이러했다.

A : 입금된 어플로 돈을 확인할 수 있다고 했는데 만질 수 없는 것이 아닌가?
B : 그건 은행에 입금된 돈도 마찬가지 아닌가?

A : 사람들이 돈을 넣는 행위 만으로는 이자가 발생할 수 없는데 어떤 사업 구조를 갖고 있는가?
B : 광고 수익이다. 지금은 30만의 회원이 있는데 광고주 한 명에게서 50,000,000원의 광고 의뢰를 받고 어플에 개제하면 어플의 각 사용자에게 수익이 돌아가게 된다. 따라서 사용자가 조금이라도 적을 때 가입하는 것이 더 많은 수익을 창출하게 된다.

A : 고작 30만명 밖에 사용하지 않는 플랫폼에 누가 5000만원이나 주고 광고를 하나?
B : 우리 입장에선 큰 돈이지만, 기업 입장에선 큰 돈이 아니기 때문에 가능하다.

B : 왜 부정적인 것만 보려고 하나? 그렇게 생각하면 끝없이 부정적이 된다.
A : 부정적인 걸 완전히 알아야 긍정이 무엇인지 제대로 보이지 않나?

A : 정 그렇게 의심가면 다음 달 11일에 집체교육이 있으니 나오라. 아버님 여기 300넣어놓고 2년후엔 500씩 매달 받을 수 있어요 자 건배한번 하시죠.

라며 말을 돌리곤 더이상 질의를 받지 않았다.

약 1시간 가까이 밥을 먹으면서 신고를 할까 말까 고민을 하다 이 집은 나름 아들을 잘 둬서 속지 않겠구나 싶었는데 문득 기억을 더듬어보니.. 모집책으로 활동하는 분이 이전 10년전 SK텔레콤을 빙자한 다단계에서 본 모집책이다.  개가 똥을 못 끊는다... 다단계 사기꾼도 사기를 끊을 수 없는 모양이다.

비트코인 사기의 모순 

1. 불법이다.
유사수신은 무조건 불법이다.

2. 월 40%의 이자를 지급한다고 하고, 회원 수가 늘어날수록 지급되는 이자가 늘어난다고 하는데 이들의 말대로 라면 절대로 이루어질 수 없는 구조다.

광고 비용은 50,000,000원으로 확정되어있다. 그런데 회원 수가 늘어난다고 하면 이것을 분할하는 분모가 커지는 것이므로, 더 많은 금액을 줄 수 없게 된다. (즉, 적은 회원 수를 유지해야만 수익이 유지된다. )

3. 40%의 이자를 확보할 수단이 없다.
광고 수익에 의존하고 있으므로, 플랫폼 사용자가 적으면 광고 모집이 불가능하고, 사용자가 많으면 수입이 줄어들기 때문에 어떤 방향으로든 적절한 수익을 보장할 수 없다.

4. 실물이 없다.
내가 돈을 입금하는 순간 그것은 가짜돈으로 치환될 뿐이다. 사기이기때문에 금융기관의 예금처럼 예금자 보호를 받을 수 없으므로 실제로는 0원이 되는 것이다. 어플에 10억이 찍혀있어도 0원인것이다.

합법적으로 등록하고 자산을 운용하는 기관이 괜히 있는것이 아닙니다. 주위에 이런 말을 하면서 이자 수익을 운운하는 사람이 있다면 연을 끊으시고, 연락처를 차단하는 것이 인생에 도움이 된다고 생각됩니다. 또한 약 10년전에 보았던 사기꾼이 다시 등장하는 것으로 보아 사기를 치는 사람도 쉽게 끊지 못한는 것 같습니다. 이런 일을 하는 사람은 가족이라도 연을 끊는 것이 인생에 도움이 될 것입니다.

아쉽게도 이분이 말하는 비트코인 어플과, 해당 이미지를 구하지 못한게 조금 아쉽긴 합니다. 차후 기회가 된다면 해당 코인 이름과 거래 어플에 대한 스샷을 첨부하도록 하겠습니다.

이상입니다. 읽어주셔서 감사합니다.
어설픈 사기꾼의 감언이설 1탄 - 법원경매
어설픈 사기꾼의 감언이설 2탄 - 다단계(네트워크 마케팅)
어설픈 사기꾼의 감언이설 3탄 - 취업사기
어설픈 사기꾼의 감언이설 4탄 - 기획부동산 사기
어설픈 사기꾼의 감언이설 5탄 - 유사 수신 사기
어설픈 사기꾼의 감언이설 6탄 - 비트코인 사기
다단계 소득에 대해 분석해보았습니다.

2018년 12월 13일 목요일

windows 크랙등의 인증 크랙을 사용하면 나타나는 문제점

개요 

인터넷에 보면 여러가지 정품인증 프로그램이 있습니다. 최근들어서는 이러한 정품 인증 프로그램이 정품 인증이 안되거나 쉽게 풀리는 현상이 있을 뿐더러, 확인 결과 악성코드나, 바이러스, 스미싱 등을 유포하는 창구로 활용되고 있습니다.

정품 윈도우 라이센스는 고작 5~6,000원이면 구매할 수 있음에도 아직도 크랙을 선호하는 사람들은 여전히 많습니다.

소프트웨어 업계에 일하는 사람으로서가 아니라. 스미싱의 피해자가 되어본 입장에서 정품 windows의 설치를 권장하고 있습니다.

개인적인 피해사례 

개인적인 스미싱, 악성코드 피해 사례는 몇 차례 있습니다.
1. 돈을 주고 산 인터넷 강의 영상이 암호화되어 볼 수 없게 되었다.
- 약 35만원 손실
2. 공모전을 준비하던 도면이 사용할 수 없는 상태가 되었다.
- 손실액 추정 불가 (대상을 받았다면 상금 2천만원 )
3. 불필요하게 윈도우즈를 다시 설치해야 했다.
7년간 약 30여차례 개별 pc 사용 환경 구축 5시간을 포함 1회 8시간 일급 20만원으로 계산시 약 600만원

이런 일들이 소프트웨어를 정품으로 사용하면 없어집니다. 정품으로 사용하지 않은 컴퓨터에서는 백도어(해킹을 위한 통로)가 주기적으로 열리고, 각종 adware등을 몰래 설치하는 코드가 시스템에 삽입되어있는 상태기 때문에 경우에 따라서는 2,3개월마다 pc의 os를 초기화 해야 할 경우도 있습니다.

그중 여러 사람들이 가장 흔하게 비정품으로 사용하는 windows 의 경우 시스템 영역에 악성 코드가 설치되기에 바이러스 백신에서도 근본적인 처리를 하지 못하고, 주기적으로 임시조치만 하기도 합니다.

크랙 사용 시 악성 프로그램 유포 사례 예 

크랙을 사용하게 되면 주기적으로 볼 수 있게 되는 바이러스 백신의 실시간 검사 결과입니다.  매달 adware가 몰래 설치되는 비교적 귀여운 크랙입니다.

adware는 광고 프로그램입니다. 비교적 피해가 적습니다. 어느 날 pc 작업표시 줄 바로 위에 광고가 표시되는 것이 대부분 입니다. 그런데 가끔 광고 대신 이런 것이 뜨기도 합니다.

'당신의 컴퓨터는 암호화 되었습니다. xxx 에게 1비트코인을 입금하면 해제할 수 있습니다.'

윈도우 켜자마자 작업표시줄에 이런게 뜬다면 당황스럽겠죠?
'아 그 유명한 스미싱인가?' 하면서 서둘러 이것저것 확인해보겠지만. 그땐 이미 늦었죠. 문제는 원하는 돈을 줘도 암호화가 해제될 수 없는 경우가 다반사라는 것이 문제입니다.

어떤 크랙 프로그램에 이렇게 주기적으로 광고를 설치하는 프로그램이 탑재되어 있다면, 또 다른 크랙 유포자는 조금 더 위험한 코드를 설치할 수 있습니다. 자기가 크랙 개발자는 아니지만, 크랙을 조금 변경해서 애드웨어가 아닌 진짜 스미싱 프로그램을 넣는 것이죠. 혹은 pc를 모니터링하고, 중요한 작업을 하는데 사용하는지 확인하기 위해 백도어를 설치할 수 있습니다.

그리고 프로그램에서 특정 한 부분만 변경하는 건 완전히 새로 개발하는 것 보다 무척 쉬운 일이지요.

WINDOWS 크랙에 대한 설명 


저 애드웨어를 설치하는 프로그램은 여러분이 제법 많이 사용하는 크랙인 KMSAUTO라는 프로그램입니다. 어떤 크랙의 경우 더 위험한 백도어를 설치하고 있습니다. 또한 같은 KMSAUTO라도 유포자에 따라서 악성 프로그램 유형이 다릅니다.

저 프로그램을 수박 껍데기 정도 해석해보면 WINDOWS에서 발생하는 소프트웨어 키 인증을 WINDOWS 서버가 아닌 자신의 서버에서 받게 해 놓은 것 같습니다. 그리고 WINDOWS 정품 인증은 주기적으로 WINDOW 자체에서 실행되기에 바이러스로 볼 수 없죠. 그래서 백신에 걸리지 않습니다.
------------------------------------------------------------------------------------------
KMS AUTO의 악성 코드 설치 이력
노란색 색칠한 부분이 수행 일시이고, 올 한해만 32회나 악성코드를 설치하는 작업을 수행했습니다. 그리고 친절하게 로그도 남겨주셨네요.


KMS AUTO는 WINDOWS에서 주기적으로 정품 라이선스와 업데이트를 체크하는 프로그램의 접속 경로를 자신이 만든 특정 사이트로 바꾼 뒤, 정품 인증, 업데이트에 대한 응답 대신, 악성 프로그램을 다운로드 하게 하는 것이 현재 설명하는 크랙이 하는 일 입니다. AI 크랙도 마찬가지 입니다. 그래서 WINDOWS 업데이트 로그와 동일하게 생성됩니다. 다만 OS버전이 달랐습니다. 설치된 버전의 업데이트 로그는 170회이지만 설치되지 않은 버전의 업데이트 횟수와 바이러스 검출 횟수가 동일하게 32회였습니다.
-------------------------------------------------------------------------------------------

다만 인증 받는 곳에서 (자신의 PC내의 특정 주소) 악성 프로그램을 다운 받는 주소와 연결되어있고, 해커는 이곳에 백신에 걸리지 않는 악성 프로그램을 설치하는 것입니다.

그리고 이런 프로그램은 크랙을 이용할 때 같이 설치되고, 결코 지워지지 않죠. 음... 사용자가 지우지 않는다가 맞는 것 같습니다. 몰라서 못지우는 경우도 있지만, 알아도 지우면 정품인증을 하라고 귀찮게 굴기 때문이죠.

그렇게 방치하다 어느 날... 10일 밤샘한 자료가 날아가면 득도를 하지 않을까 합니다.
'아~ 그때 그 말 좀 믿을걸... '

이런 공격의 가장 큰 문제점은 어느 날 만 하는 것이 아니라 지속적으로 매달 몇 년이고 이루어지기 때문에 방심하고 중요한 자료를 pc에 넣은 다음날부터 여러 공격의 대상이 될 수 있다는 것이 문제입니다. 그리고 바이러스 테스트의 주요 경로가 됩니다. 백신에 걸리지 않는 바이러스를 만들어내는 실험 대상이 되는 것 이지요.

끝으로

전에 어떤 사람이 WINDOWS 중고 키를 사는 것이 불법이고 크랙을 까는 것과 다른 것이 없다고 저에게 댓글을 달았는데 기본적으론 백도어가 열리지 않는 부분이 다릅니다. (여러 공격으로부터 자유롭죠.) 크랙을 설치하는 행위는 스스로의 PC를 바이러스 제작자가 새로운 바이러스를 실험하는 실험대로 제공하는 것과 같습니다.

그리고 WINDOWS 중고 라이선스를 구매하는 것은 한국 국내에선 무법이고, 해외에선 합법입니다. 불법은 아닙니다. 법이 없는 부분이죠. 다만 한국 MS정책에는 위배될 수 있습니다. 하지만 전화 문의 결과 그쪽에도 이것에 대한 명확한 정책이 없습니다.

정품 15만원이 비싸다면, 중고라도 사서 쓰세요. 컴퓨터에 중요한 게 아무것도 없다면 적어도 windows 설치 영역은 세팅 완료 이후 ghost 이미지 파일을 만드세요.

그리고 웬만하면 정품이나 중고 라이선스를 사셔서 자신의 PC를 지키세요.
모든 소프트웨어 크랙에 이런 코드가 숨어있습니다.
크랙 사용은 스스로를 보안 위협에 빠뜨리는 지름길입니다.