make: g++: Command not found

 

npm install을 할 때, 위와 같은 에러가 발생하곤 한다. g++ 인스톨이 필요하다.

해결방법은 yum -y install gcc-g++ 로 설치. root 권한이 필요할 수 있다.

블로그 이미지

김생선

세상의 모든것을 어장관리

개발하다보면, 특정 컬럼의 네이밍을 조회하는 경우가 있다. 가령 A 라는 컬럼을 어떤 테이블에서 더 쓰는지. 뭐 이런 경우. Oracle은 all_tab_column인지 all_object를 쓰면 되긴 하는데 mysql은 처음이다. 하지만 간단하다.


1
SELECT * FROM information_schema.columns
cs



이렇게 해주면 된다. 개꿀.

블로그 이미지

김생선

세상의 모든것을 어장관리

Tag mysql

개발하다보면 해당 페이지의 table의 데이터들을 새로고침 없이 데이터를 붙여쓰는 경우들이 있다. 이는 대부분 리스트로 구성된 페이지에서 확인이 가능한데, 가령 오픈마켓의 주문배송리스트라거나, 뭐 카드사용 내역과 같은 페이지에서 주를 이룬다.

div태그를 쓰던 table태그를 쓰던 아마 과정은 비슷할 것이고, javascript에서 데이터와 html태그들을 어떻게 만들어서 jsp에 붙여주는지에 대한 예제를 작성해보고자 한다.


먼저 JSP에서는 다음과 같이 구성한다. 기본적으로 페이지가 로딩될 때, controller 단에서 modelAndView에서 데이터를 호출, List로 이루어진 객체들을 foreach 로 계속 붙여주는 구조로 되어있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--결과 리스트-->
<div class="result-area">
  <h2 class="result-tit">거래결과 리스트</h2>
  <ul class="result-list" id="resultList">
      <c:forEach var="resultHistory" items="${resultHistory}" >
      <li class="result-item">
          <p class="result-day">${resultHistory.REG_DATE} <span>${resultHistory.ORDER_TYPE}</span></p>
          <dl class="result-detail">
              <dt>품명</dt>
              <dd>${resultHistory.PRODUCT_TYPE}</dd>
              <dt>금액</dt>
              <dd>${resultHistory.AMOUNT}</dd>
          </dl>
      </li>
      </c:forEach>
  </ul>
</div>
<div class="more-btn-wrap">
  <button type="button" id="btnResultMore">더보기</button>
</div>
cs


modelAndView 에서는 resultHistory라는 네이밍의 List 객체에 데이터들을 담아준다. 그리고 해당 jsp페이지가 호출되면, foreach로 객체들을 돌리면서(?) 각 항목들을 구성하게 된다. 이 때, btnResultMore를 클릭한다고 하자. 그럴 때 해당 페이지를 새로고침 없이, 리스트의 하단에 추가적으로 데이터가 붙는 방식이면 훨씬 보기가 좋을 것이다. 새로고침을 하게 되면 현재 입력되어있는 값들도 저장하지 않는 이상 사라질 것이고, 무엇보다 좀 더 자연스러운 화면을 뿌려줄 수 있을 것이다.


btnResultMore 버튼에 매핑되어있는 javaScript의 펑션을 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var pageNo = 0;
 
//결과 더보기
$("#btnResultMore").on("click"function () {
  $.ajax({
    type:'GET',
    url : '/resultMore.json',
    global : false,
    async : true,
    data : {
      pageNo: pageNo
    },
    success : function(json) {
      $('#resultList').empty();
 
      var html = '';
      pageNo = pageNo+1;
      json.resultList.forEach(function(item, index){
        html = $('<li class="result-item">' +
          '<p class="result-day">'+item.REG_DATE+' <span>'+item.ORDER_TYPE+'</span></p>' +
          '<dl class="result-detail">' +
          '<dt>품명</dt>' +
          '<dd>'+item.PRODUCT_TYPE+'</dd>' +
          '<dt>금액</dt>' +
          '<dd>'+item.AMOUNT+'</dd>');
        $('#resultList').append(html);
      });
    },error:function(json){
 
    }
  });
});
cs


자바스크립트에서는 ajax 호출을 통해, 데이터를 더 가져오기 위한 controller 와 매핑을 시켰다.

정상적으로 쿼리가 이루어지는 경우에는 success flag를 탈 것이고, 이 때부터 로직이 시작된다.


기본적으로 들어있는 row를 날리기 위해, 14번 라인에서는 resultList의 자식들을 모두 비워준다.

그리고 18번 라인에서 가져온 데이터를 기반으로 forEach를 돌리면서, HTML tag를 새로 만들어주는 방식으로 이루어져있다.

위의 jsp와 마찬가지로 같은 구성으로 html과 데이터를 매핑해준 후에, 26번 라인에서 append를 시켜주면 끝.


추가적으로 btnResultMore를 누르게 되면 5개씩 항목을 더 붙여주는 기능을 포함한 것인데, pageNo 변수를 추가하여 이를 쿼리에 응용했다.

기본값은 0으로 처리가 되어서 쿼리에서는 기본 5개, 1일 경우에는 10개를 가져오는 방향으로.


javascript 에서 append와 appendTo의 쓰임새가 확연히 다르다.

$('#a').append(b); 의 경우에는 a에 b를 넣는 것이고, $('#a').appendTo(b);는 b를 a에 넣는것이다.

블로그 이미지

김생선

세상의 모든것을 어장관리

개발하다보면 DB의 날짜타입을 timestamp로 저장하는 경우가 있다. 개인적으로 이거 날짜 컨버팅 하고 뭐하고 하는것이 아주 귀찮아서 제일 끔찍하게 싫어하는 데이터 타입인데, 심지어 데이터가 저장되는것도 unix타임이야. 이거 한눈에 들어오겠냐고 제일 싫다.

여튼, 이걸 또 그냥 보면 15뭐시기로 시작하니까 사람이 제대로 읽을 수나 있겠냐, 이거지. 결국에는 사람이 읽고 쓸 수 있는 날짜타입(YYYY-MM-DD HH-MM-SS)으로 변환시켜줘야 하는데 일일히 찾아다가 만드는것도 귀찮고 펑션으로 하나 만들어놨으니까 앞으로 두고두고 좀 써먹어야겠다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//TimeStamp -> Date formatter
function unixToDateFormatter(date) {
  // yyyy-mm-dd hh:mm:ss.s
  var dateFormatt = new Date ( date );
  var year = dateFormatt.getFullYear();
  var month = 0;
  if ( dateFormatt.getUTCMonth() < 9 ){
    month = '0'+ ( dateFormatt.getUTCMonth() + 1 ).toString();
  } else {
    month = dateFormatt.getUTCMonth()+ 1;
  }
  var day = dateFormatt.getUTCDate();
 
  var hour = 0;
  if ( dateFormatt.getHours() < 10 ){
    hour = '0' + (dateFormatt.getHours()).toString();
  } else {
    hour = dateFormatt.getHours();
  }
 
  var minute = 0;
  if ( dateFormatt.getMinutes() < 10 ){
    minute = '0' + ( dateFormatt.getMinutes()).toString();
  } else {
    minute = dateFormatt.getMinutes();
  }
 
  var seconds = 0;
  if ( dateFormatt.getSeconds() < 10 ){
    seconds = '0' + (dateFormatt.getSeconds()).toString();
  } else {
    seconds = dateFormatt.getSeconds();
  }
 
  var milliseconds = dateFormatt.getMilliseconds();
  var fullDateFormatt;
  fullDateFormatt = year +'-'+month+'-'+day+' '+hour+':'+minute+':'+seconds+'.'+milliseconds;
  console.log ("DateFormatt : " + fullDateFormatt);
  return fullDateFormatt;
}
cs


일단 대강 만들긴 했는데, 몇가지 짚고 넘어갈 점이 있다.

getUTCMonth의 경우에는 0~11의 값으로 리턴해준다. 숫자는 뭐다? 0부터 센다. 0은 곧 1월이고, 이말은 리턴되는 값에 + 1을 해줘야 한다는 의미이다. 두번째로는 모든 자릿수를 맞춰줘야 한다는 것이다. 0~11로 리턴해주기 때문에 0은 +1을 해서 1월인데, 우리가 쓰는 데이트 포맷은 YYYY-MM-DD의 값으로 "월"의 자릿수가 맞지 않게 된다. 그런 고로 10월, 즉 리턴되는 값이 9보다 작은 경우에는 앞에 자릿수 '0'을 붙여주게 된다.

자릿수가 안맞는 부분은 month 뿐만 아니라 getUTCDate , getHours, getMinutes , getSeconds의 경우도 마찬가지가 된다. 그래서 각각의 경우에도 10보다 작은 경우에는 앞자리에 0을 붙이는 식으로 구성했다.


이거 하나 만들어놓으면 자바스크립트에서 두고두고 써먹을거고, 자바의 경우에는 simpledateformatter가 있으니까 그냥 이거 갖다 쓰면 된다.

블로그 이미지

김생선

세상의 모든것을 어장관리

DB 또는 시스템 작업을 하다보면 여러건에 대해 한번에 업데이트를 하는 경우가 많다. 뭐 그렇다치고 이번에는 MySQL 에서 서브쿼리를 이용하는 방법을 알아보자.


이번에 할 작업은 A_Table의 a_column의 데이터와 문자열을 합친 후, A_Table의 b_column에 업데이트를 해줄 일이 생겼다. 오라클과 다르게 한차례 더 가공을 해야 해서 손이 가는 편이지만 크게 어렵지는 않았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
UPDATE 
    'A_Table' AS AliasA 
SET 
    AliasA.b_column = 
    (
    SELECT
        AliasC.a_column_text
    FROM
        (
        SELECT
            AliasB.a_column 
            , CONCAT('TEST_' , AliasB.a_column ) AS a_column_text //MySQL에서 문자열 합치기는 concat을 이용함
        FROM
            'A_Table' AS AliasB 
        ) AS AliasC
    WHERE 
        AliasC.a_column = AliasA.a_column 
    )  
WHERE 
    AliasA.b_column IS NULL;
cs


오라클 같은 경우에는 SET 구문에서 서브쿼리를 바로 날리면 되지만, MySQL의 경우에는 SELECT 구문에 서브쿼리를 한 번 더 감싸줘야 한다.

이로써 A_Table의 b_column에 'TEST_'문자열이 합쳐진 a_column값이 update 되게 된다.

블로그 이미지

김생선

세상의 모든것을 어장관리

개발환경
OS : macOS 10.14
JAVA : JDK 1.8

URL API 연동을 하는데 다음과 같은 에러를 보게 되었다.

sun.security.validator.ValidatorException:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

찾아보니 JAVA 에서 HTTPS로 연결시, 해당 사이트의 SSL 인증서가 신뢰하는 기관 인증서목록에 없거나 SSL/TLS암호화 버전이 맞지 않는다거나 하는 이유들.
다른 URL로는 정상적으로 연결이 되는 걸 보아서는 인증서 목록에 누락된 것에 무게를 두고 해결방법을 찾았다. 

해결방법은 JDK의 Cert Keystore에 해당 URL인증서를 넣어주면 된다.


1. InstallCert.java 다운로드
구글 코드에서 InstallCert.java 를 다운로드한다. 링크를 클릭하면 다운로드가 시작된다.


2. InstallCert.java 컴파일
다운로드 받은 InstallCert.java 를 컴파일 한다.
cd [다운로드 디렉토리]
javac InstallCert.java



3. InstallCert.java 구동

URL은 접속이 안되는 URL을 입력한다. 아래 예제에서는 naver.com을 샘플로 입력했다.

sudo java -cp ./ InstallCert [URL 명]

ihowon-ui-MacBook-Pro:downloads ihowon$ sudo java -cp ./ InstallCert naver.com
Loading KeyStore jssecacerts...
Opening connection to naver.com:443...
Starting SSL handshake...
[생략]
 4 Subject CN=AddTrust External CA Root, OU=AddTrust External TTP Network, O=AddTrust AB, C=SE
   Issuer  CN=AddTrust External CA Root, OU=AddTrust External TTP Network, O=AddTrust AB, C=SE
   sha1    02 fa f3 e2 91 43 54 68 60 78 57 69 4d f5 e4 5b 68 85 18 68
   md5     1d 35 54 04 85 78 b0 3f 42 42 4d bf 20 73 0a 3f
Enter certificate to add to trusted keystore or 'q' to quit: [1]

위의 cmd 화면에서 1을 입력 후 엔터키를 누르면 설정된다.


4. alias 설정
[앞부분 생략]
Added certificate to keystore 'jssecacerts' using alias 'naver.com-1’

위에서 보이는 alias 명을 꼭 기억해야 한다. 이 부분을 통해 앞으로 설정을 계속할 것이다. 


5. Cert Explort 해당 명령어를 통해 jssecacert를 export 한다
keytool -exportcert -keystore jssecacerts -storepass changeit -file output.cert -alias [naver.com-1] 

keystore의 jssecacerts 는 certs파일명, storepass의 changeit은 certs파일의 암호명이다. 이는 등록된 list를 수정/삭제할 때 필요하므로 기억하는것이 좋다.
또한, output.cert는 Java Cert에 Import할 때 필요하다.


6. jssecacert를 Java Cert에 Import
sudo keytool -importcert -keystore ${JAVA_HOME}/jre/lib/security/cacerts -storepass changeit -file [4.에서 설정된 jssecacert.cert] -alias [URL Alias-1]

이부분에서 jssecacert.cert는 사용자가 직접 입력하는 cert파일명이다. 이 파일명이 설명하는 사이트마다 당연히 다르고, 이 부분에 대해 크게 언급이 없어서 꽤 삽질했다. 이를 사용하는 JDK의 디렉토리 내에 Import를 시켜준다고 보면 된다. 사용하는 JDK 버전이 여러개라면, 각각의 버전에 대해 설정을 해줘야 할 것이라 생각된다.


>> 18.11.23 추가내용
개발하는데 접속해야 할 사이트의 SSL인증서가 변경되어, 위와 같은 방법으로 적용하려는데 새로운 오류를 보게 되었다. 해당 오류는 6. jssecacert를 Java Cert에 Import 하는 부분에서 발생하였고, 오류내용은 다음과 같다.

X.509 인증서가 아닙니다.

해당 오류에 대해 원인은 정확히 파악할 수 없었으나, 다음과 같은 방법으로 오류를 해결했다.

5. Cert Export 해당 명령어를 통해 jssecacert를 export 항목에서 생성된 jssecacerts 파일을 ${JAVA_HOME}/jre/lib/security/ 디렉토리에 복사하였더니 정상적으로 접근이 가능했다.


블로그 이미지

김생선

세상의 모든것을 어장관리

인텔리제이를 쓰는 것도 처음인데 스프링 부트 또한 처음이다. 뭐가 뭔지 모르겠지만 일단 에러부터 잡아보자.

전임자에게 이 오류에 대해 물어보니 롬복이 설치되지 않았다고 한다. 롬복을 대강 찾아보니 getter/setter를 자동화해주는 기능(?)같다. 스프링부트는 나중에 다시 별도로 공부하기로 하고, 일단 인텔리제이에서 롬북 플러그인을 설치해보고자 한다.


에러가 나는 부분. Spring Framework 에서는 getter/setter가 별도로 있는데 이쪽엔 없다. Spring Boot는 많은게 없다. tiles라거나 뭐 별별게 다 없다. 없어서 간편하고 개발도 편하고 손이 덜가는 것 같은데, 중요한건 공부를 하지 않으면 찾아가는 것이 아예 불가할 정도. 역시 장단점이 있는듯.


인텔리제이 메인에서 File -> Setting 을 클릭한다. Setting 으로 접근하는 단축키는 Ctrl+Alt+S 이다.


해당 창에서 Plugins 를 선택하고, 검색창에서 lombok을 입력한 후 검색한다.

당연히 설치된 플러그인이 없으니 검색이 안될 것이고, Search in repositories 를 클릭한다.


Broesw Repositories 창에서 Lombok Plugin을 선택, 오른쪽의 초록색 install을 클릭한다.

서드파티 플러그인 경고창에서 Accept를 클릭한다. 


설치가 다 되면 원활한 적용을 위해 리스타트를 한다.

그럼 대충 끝.


에러 뿜뿜이던 모습이 사라졌다. 일단 이걸로 고비 넘김. 

블로그 이미지

김생선

세상의 모든것을 어장관리

엑셀로 데이터를 밀어넣다 보면 데이터가 간혹 잘못들어가는 경우가 있다. 의도치 않은 tab 문자열이라거나 공백문자열이라거나.

한두건이면 그냥 update query를 실행하면 되지만 여러건일 경우에는 답이 없다.


1
2
3
UPDATE
    table_a
SET column_name = REPLACE( column_name , ' ''')
cs


table_a 라는 테이블에서 column_name 컬럼의 데이터가 공백 문자열이 있을 경우, set 구문에 replace로 해당 컬럼을 치환한 후 update 해주면 된다.

아주 간단하고 심플함.

블로그 이미지

김생선

세상의 모든것을 어장관리

[Oracle 11g Enterprise] / [Mybatis 3.2.7] 기준


varchar2 데이터타입은 최대 사이즈를 4,000byte 까지 줄 수 있다. 그런데 4,000byte 이상의 데이터를 저장할 일이 있다. base64로 인코딩된 이미지라거나, xml 파싱된 영수증내용이라거나 아주 긴 블로그의 글이라거나. 그럴 때 oracle 에서 사용하는 데이터타입은 clob 이다.


CLOB은 XML 형태로 데이터를 지정하게 되는데, DB 툴에서는 select * from table로 간단하게 데이터 확인이 가능하지만, mybatis를 사용하게 되면 일반적인 select 쿼리로는 활용이 불가능하다. 그럴 때 다음과 같이 mybatis의 select 구절에 resultmap을 설정하면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
<!-- resultMap 지정 -->
<resultMap id="QID_CLOB" type="hashMap" >
    <result property="ETC" column="etc" jdbcType="CLOB" javaType="java.lang.String"/>
</resultMap>
 
<!-- 조회쿼리 -->
<select id="QID_SELECT_INFO" parameterType="hashMap" resultMap="QID_CLOB" >
SELECT
    etc
FROM
    table
</select>
cs


사용법은 심플하다.

select 쿼리의 resultMap 파라미터는 별도로 선언할 resultMap의 ID를 지정해준다.

별도로 선언할 resultMap에서는 id를 맞춘 후, clob으로 가져올 컬럼명(column)을 지정하고, jdbcType을 clob으로 지정, 이를 어떠한 column name으로 내보낼 것인지 지정(property)해주면 된다.



1
2
3
4
5
6
SELECT
    a.etc AS a_etc
    b.etc AS b_etc
FROM
    table_1 a
    LEFT OUTER JOIN table_2 b ON a.no = b.no
cs

그런데 여기서 한가지 궁금한 점이 생기게 된다. 

각기 다른 테이블을 조인 후 가져올 때, 각 테이블의 같은 이름 clob은 어떻게 가져올까 하는 점이다.

테스트를 잠시 해보았는데 생각보다 매우 간단했다.


처음에는 같은 컬럼명이니까 위의 resultMap을 그냥 가져다 쓰면 ETC 컬럼을 ETC Property로 출력해주겠거니, 했지만 전혀 그렇지 않았다.(왜인지 Java의 Service 처럼 동작할거라 생각;)

그래서 두번째 방법을 사용해보았다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- resultMap 지정 -->
<resultMap id="QID_CLOB" type="hashMap" >
    <result property="A_ETC" column="a_etc" jdbcType="CLOB" javaType="java.lang.String"/>
    <result property="B_ETC" column="b_etc" jdbcType="CLOB" javaType="java.lang.String"/>
</resultMap>
 
<!-- 조회쿼리 -->
<select id="QID_SELECT_INFO" parameterType="hashMap" resultMap="QID_CLOB" >
SELECT
    a.etc AS a_etc
    b.etc AS b_etc
FROM
    table_1 a
    LEFT OUTER JOIN table_2 b ON a.no = b.no
</select>
cs


그랬더니 각기 다른 테이블에서 동일한 네이밍의 clob 컬럼을 정상적으로 가져올 수 있었다.

간단한 테스트 결과, resultMap의 동작구조는 SELECT가 우선적으로 실행된 결과를 가져온 뒤, 이를 resultMap에서 처리하는 구조로 여겨진다. 결과적으로 resultMap은 Alias로 잡힌 컬럼명을 인식한다는 말이다.

블로그 이미지

김생선

세상의 모든것을 어장관리

[Oracle 11g Enterprise] 기준


개발을 하다보면 산출물을 작성해야하고, 이 산출물에는 테이블 생성날짜 등도 필요한 경우가 있다. 몰아서 테이블들을 생성해두고 나중에 작성하려다보면 뭐 임의의 날짜로 작성해도 무방하나 혹여나 하는 1g의 불안감 때문에 확인하는 경우가 있다.


1
2
3
4
5
6
7
8
9
10
11
12
SELECT
    owner
    , object_name
    , object_type
    , created
    , timestamp
FROM 
    all_objects
WHERE
    owner = 'USER_NAME'
    AND object_name ='TABLE_NAME'
 
cs


뭐 대충 이정도. 검색을 하다보면 all_objects 테이블이 아니라 user_objects라느니 뭐라느니 하는데 아마 버전마다 다른게 아닌가 싶을 정도.

여튼, 위의 컬럼은 다음과 같은 구성을 가지고 있다.


 owner

개체 소유계정. sys 계정이라면 해당 DB 내 모든 계정을 조회 가능하다.

 object_name

(간단히 말해) 개체명

개체명이라고 표현하는 이유는 테이블만이 보이는 것이 아니기 때문임.

자세한 부분은 object_type 참조

object_type

개체 타입. table인지 index인지 sequence 인지를 표현함. 

created 

생성날짜 

timestamp 

최근 access 날짜.

생성한 이후에 데이터를 insert 혹은 delete 할 때 마다 해당 날짜가 갱신된다.


뭐 대충 이정도. owner 에는 SYS 혹은 EXFSYS, SYSTEM 등 수많은 시스템용 owner가 있기에, 가장 기본적인 필터를 owner로 지정해두고 select 하는 것이 정신건강에 이로울 듯.

블로그 이미지

김생선

세상의 모든것을 어장관리