시스템에서 하나의 Unique Value를 여러테이블에 Insert 하는 경우가 생긴다. 간단하게 게시판을 예로 들 수 있는데, 글 작성과 첨부파일이 있는 경우다.

글 내용과 제목, 작성자 정보등을 담는 board_table과 첨부파일만을 저장하는 file_table 이라고 예시를 들어보자. board_table은 글 번호(PK)가 있을 것이고 이 글 번호로 join이 걸리는 file_table의 key_column 이 있다. 하나의 글에는 여러개의 파일이 첨부가 될 수 있다.


board_table의 글번호는 아주 특수한 경우가 아니고서야 Unique로 가져간다. 그리고 글을 작성할 때, Insert 쿼리를 쓰며, 일일히 글번호를 만들어주기 귀찮고 무엇보다 크나큰 오류가 생기는 경우가 있을 수 있으므로 아래와 같이 대부분 sql의 sequence를 이용해 글번호를 유일하게 가져간다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<insert id="QID_INSERT_BOARD" parameterType="hashMap">
    INSERT INTO board_table
    ( 
        board_no -- 글 번호
        , title    -- 글 제목        
        , content -- 글 내용
        , insert_date -- 작성일시
        , insert_user -- 작성자
    )
    VALUES
    (
        seq_board_no.NEXTVAL -- 글번호를 가져오는 시퀀스
        , #{TITLE}
        , #{CONTENT}
        , #{INSERT_DATE}
        , #{INSERT_USER}
    )
</insert>
cs


이 때, 해당 글번호로 작성된 파일들의 파일첨부는 어떻게 Key를 가져와야 할까? 위의 쿼리는 글이 작성되는 시점(insert query가 commit이 되는 시점)에서 글 번호를 생성한다. 쿼리를 날리기 직전에 sequence를 호출, 이를 board_table과 file_table에 key_parameter로 insert 하는 방법도 있겠지만 권장하는 방법은 아니다. 만약, 랜덤키를 쿼리 내에서 생성하는 경우에는? 그렇다면 Key를 쿼리 수행 전에 가져올 수도 없다. 이럴 때 사용하는 것이 ibatis(mybatis)의 펑션인 select key 이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<insert id="QID_INSERT_BOARD" parameterType="hashMap">
    INSERT INTO board_table
    ( 
        board_no -- 글 번호
        , title    -- 글 제목        
        , content -- 글 내용
        , insert_date -- 작성일시
        , insert_user -- 작성자
    )
    VALUES
    (
        seq_board_no.NEXTVAL -- 글번호를 가져오는 시퀀스
        , #{TITLE}
        , #{CONTENT}
        , #{INSERT_DATE}
        , #{INSERT_USER}
    )
    <selectKey keyProperty="BOARD_NO" resultType="int" order="AFTER">
        SELECT seq_board_no.CURRVAL FROM dual
    </selectKey>
</insert>
cs


사용법은 심플하다. insert 쿼리를 실행하기 전에 가져올 것인지 혹은 후에 가져올 것인지에 따라 selectKey의 위치가 Insert 쿼리의 앞 혹은뒤로 옮겨진다. 이 쿼리에서는 Insert를 수행 후에 가장 최근 sequence를 가져올 것이므로, Insert 쿼리의 뒤에 배치했다.

Insert 쿼리가 수행된 후에 selectKey 구문이 실행될 것이고, 조회된 sequence 값이 BOARD_NO 에 세팅, 이후에 사용할 수 있게 된다. 파라미터는 다음과 같다.


Parameter 

설명 

 keyProperty

selectKey가 수행된 이후에 결과값이 세팅되는 key값. 

 resultType

selectKey가 수행된 결과값의 type. string 이라거나 int 라거나...

 order

BEFORE 혹은 AFTER로 설정 가능. selectKey 구문을 먼저(BEFORE) 실행 할 것인지 혹은 나중에(AFTER) 실행할 것인지를 지정한다.


위의 쿼리는 종합적으로 Insert 구문이 실행된 후, sequence가 nextVal이 된 상태이며, 이 상태값을 바로 selectKey가 currval을 통회 최근 sequence 값을 조회, 이후 이 리턴값을 받아 쓰면 되는 구조이다.


덕분에 board_table에 Insert를 수행한 이후, 해당 board_no를 바로 리턴받고 file_table에 Insert를 하게 되었다.

블로그 이미지

김생선

세상의 모든것을 어장관리

DB에서 데이터를 가져온 후, TextArea에 value 값을 지정할 때 줄바꿈 문자열을 넣어줘야 하는 경우가 있다.

<br>이라거나 \r\n 이라거나 잔뜩 있긴 하지만서도, jsp 안에서 html로 그려질 때에는 먹히지 않는다.


가볍게 &#10; 를 넣어주면 해결된다.

블로그 이미지

김생선

세상의 모든것을 어장관리

tomcat을 포함한 WAS 들은 root로 구동하게 되면 해킹당했을 때 root 권한에 대해 보안취약점이 발생하게 된다. 기본 웹포트는 80이며, SSL이 적용되는 포트는 443 포트를 사용하게 되는데, 리눅스에서는 1000 포트 이하의 경우에는 root 에서만 제어할 수 있다.

그래서 80포트를 8080 포트로, 443 포트를 8443 포트로 routing 설정을 해줘야한다. 


여기까지는 기본적인 설정인데, 요사이 보안이슈로 인하여 기본적으로 널리 알려진 8080 같은 포트는 사용하지 말라는 권고가 떨어지곤 한다. 그래서 나는 58080과 같은 포트로 변경함.

iptables 에서만 변경할 부분이 아니라, was에서도 변경을 해야한다. tomcat의 경우에는 conf/server.xml 에서 수정 가능하다.


1. iptables 설정 확인

iptables -t nat -L


2. iptables 포트 삭제

iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080


3. iptables 포트 등록

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 58080


4. iptables 포트 저장

# service iptables save


5. iptables 재시작

# service iptables restart


위의 순서로 작업을 해야한다. 인터넷에서는 삭제/저장 절치 없이 등록/재시작을 하라고 나오는데 내가 뭔가 잘못한건지 정상동작이 안되길래 전형적인 순서로 작업함.

결과는 대성공.


참고로, iptables 에서 80->8080 routing 설정을 하지 않거나 혹은 tomcat/conf/server.xml 에서 8080 관련 redirect 설정을 삭제한다면 해당 포트로 접속하는 모든 방법은 차단될 수 있다.

SSL을 씌운 상태였는데 http://URL 이라거나... 혹은 서버 IP:8080 이라거나...


블로그 이미지

김생선

세상의 모든것을 어장관리

Java 에서 PrintWriter 를 사용하여 한글 출력할 때 한글이 깨지는 경우가 존재한다.

한글인코딩이 맞지 않아서 생기는 일.


1
2
3
4
5
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
PrintWriter outs = response.getWriter();
outs.println"<script>alert('로그인 권한이 존재하지 않습니다..');history.back();</script>" );
return;
cs


주의할 점은, PrintWriter 에서 out 으로 꺼내기 전에 response의 인코딩 타입을 미리 정해줘야 한다는 것. 


블로그 이미지

김생선

세상의 모든것을 어장관리

쉘 스크립트에 대한 간략정리


awk - 패턴 검색 및 처리


ex ) awk '{action}' filename


grep uname -u /etc/hosts/ | awk '{print $1} 


#uname -u 의 호스트명 HOSTSERVER

#hosts 파일 내용

127.0.0.1   localhost

**1.**2.**3.**4  HOSTSERVER HOSTNAME logHOST

#**5.**6.**7.**8  HOSTNAME  #eri0 set ip. 20080122 delete.

**9.**0.*10.*11 HOSTNAME HOSTNAME


**1.**2.**3.**4  HOSTSERVER HOSTNAME logHOST #grep 대상 , HOSTSERVER 를 awk

        $1                $2              $3           $4      # $ 마킹 대상

                               $0                                


블로그 이미지

김생선

세상의 모든것을 어장관리

플렉스에서 페이징을 구현하다가 로그에 저런 에러가 무수히 찍히는 걸 보고 기겁했다.

로그가 찍히다보니 웹에서 딜레이도 살짝 있기에 해결하고싶었고, 로그창이 더러워져서...-_-;;


var objPage:Object = new Object(); objPage.pageNo = 1;

위의 부분에서 문제가 있었으며,


warning: unable to bind to property 'pageNo' on class 'Object' (class is not an IEventDispatcher)


라는 로그가 계속 출력되는 상황. 구글링을 해보니, 

리스트 기반의 컨트롤러의 dataProvider로 Object/ArrayCollection을 지정하면 플래시 플레이어가 타입 형변환을 제대로 인지하지 못하고, 그래서 해당 로그가 출력되었다는 점.

이걸 해결하기 위한 방법은 다음과 같다.


var objPage:ObjectProxy = new ObjectProxy();
objPage.pageNo = 1;

라고, Object 타입을 ObjectProxy로 변경해주면 끝 -_-;;


ObjectProxy 클래스는 등록되어있는 데이터의 변경을 추적하는 용도로 사용한다고...

블로그 이미지

김생선

세상의 모든것을 어장관리

지난글, 2013/06/26 - [어장 프로그래밍/어장 DBA] - [MSSQL] MAX 함수 NULL 일 때 치환 에서는 MSSQL을 기준으로 글을 작성하였다.

현재 개발중인 플젝의 DB는 informix로 되어있고, 위와 같은 방식으로 max null 값을 구하려 했더니 informix 에서는 isnull 함수가 사용불가능하더라.


검색 해 보니 NVL 이란 함수가 있었고, 아래와 같은 방식으로 사용하였다.



SELECT NVL(MAX(testColumn), '0')+1 tempName FROM testTableName


testColumn의 MAX 값을 구하고, 이 값이 null 일 경우에는 0으로 치환한 후, 그 값에 +1을 하여 tempName 이라는 임시 컬럼명으로 Select 한다는 내용이다.(내가 써놓고 뭔가 어정쩡한 설명이란 생각이 든다.)


아무튼, 테스트를 해 보니 testColumn 값이 null 이 아니더라도 정상적으로 쿼리가 작동하기 때문에 만족하고 사용중. 뭐 이렇게 하면 된다.


블로그 이미지

김생선

세상의 모든것을 어장관리

where 조건을 줄 때, form 에서 영문자를 받을 일이 흔하다.

그리고 이 영문자로 where 조건을 주게 되는데, like 검색을 할 때에는 대소문자를 구분한다.


가령 컬럼에 KimFish 라고 값이 입력이 되어있고, form 에 입력된 조건이 kimfish 일 경우엔 조회가 안된다는 말.

따라서 like 쿼리에 대소문자 구분 없이 값을 입력해주는 function을 지정해줘야 하는데, 이것이 바로 upper function 되시겠다.


SELECT 

*

FROM

   테이블명 

WHERE 

upper(조회할 컬럼명) LIKE '%'||upper('입력받은 조건')||'%'


뭐 이런식.


내가 알기로는 대부분의 디비툴에서 사용 가능한 것으로 알고 있다.


** 추가 **

검색해본 적용 가능한 DBA

Oracle , MySql , MsSql, Informix

블로그 이미지

김생선

세상의 모든것을 어장관리

java.lang.OutOfMemoryError: Java heap space


2013:08:02 14:19:00



프로그램에서 서버 쿼리를 실행중에 위와 같은 에러가 발생하였다면 자신의 톰캣 서버 메모리를 의심해보아야 한다.


그리고 문제 해결 방법은 아래와 같다.


1. 이클립스 서버탭


이클립스 서버탭의 해당 톰캣 서버를 더블클릭한다.


2013:08:02 14:22:00

(이미지 설명 : 이 글에서는 Started 된 서버를 더블클릭 한다)



2. Server Overview


2013:08:02 14:24:39

(이미지 설명 : 톰캣 서버를 더블클릭하면 보이는 서버 오버뷰)



여기에서 파랑색으로 삐뚤삐둘 밑줄 쳐진, Open launch configuration 링크를 클릭한다.



3. Arguments


2013:08:02 14:28:45

(이미지 설명 : 사실 톰캣 서버는 여기에서 선택 가능하다)


선택한 서버가 실행중이라면 저러한 붉은 표시로 Server already running 이란 메시지를 띄우게 된다. 

해당 톰캣 서버를 멈추고, 밑줄쳐진 Arguments로 이동하자.



4. 설정 추가


2013:08:02 14:31:18


붉은 밑줄 부분이 아마도 추가가 안되어있을텐데, 현 시스템 상황에 고려하여 메모리 항목을 추가해주면 된다.

필자는 아래와 같은 형식으로 추가를 하였다.


 -XX:MaxPermSize=128m -Xms128m -Xms512m




그리고 적용시키면 방금 전 에러는 빠이염!

여러분, 즐프하십셔~



블로그 이미지

김생선

세상의 모든것을 어장관리

개발을 하다보면 현재날짜와 지정날짜간의 차이를 구해야 하는 경우가 있다.

가령, 회사 플젝의 경우 현재 날짜와 24시간 이내에만 작업을 등록해야 하는 경우와 같다.


잡설 빼고 간단히 하자면 Date 메서드의 parse를 이용하면 된다.

parse는 YYYY/MM/DD HH:MM:SS 를 밀리세컨 단위로 변환시켜주는 함수이다.


YYYYMMDDHHMMSS라거나 기타 여러 커스텀 적인 날짜를 위의 함수를 이용하여 변환시켜주면 밀리세컨 단위의 결과값이 나온다.


아래의 예제를 보자.


//날짜 차이 구하는 부분 * 작업시작일과 작업마감일은 24시간 이내를 기준으로 한다.

var checkFirstStTime:String = sttime.substr(0,2); var checkSecondStTime:String = sttime.substr(2,2); var startTimeDate:String = startDate.replace("-","/").replace("-","/") +" "+                          checkFirstStTime + ":" + checkSecondStTime + ":" + "00";


sttime은 HHMM으로 저장된 시간이고 따라서 HH와 MM으로 분리하여 checkFirstStTime 과 checkSecondStTime에 저장한다.

그리고 YYYY-MM-DD로 저장된 startDate의 -를 replace를 이용하여 /로 치환하고, parse 형식에 맞춰 공백 입력 후 저장된 값들을 주어진 형식에 맞춰 startTimeDate에 입력해준다.


그럼 기존의 YYYY-MM-DD와 HHMM으로 저장된 각각의 값들이 YYYY/MM/DD HH:MM:SS로 저장이 된다.


시작 날짜를 위와 같이 변환해주고, 마감 날짜또한 변환해준 다음에 체크를 이용해주면 날짜간 차이가 확실해진다.

주의할점은 밀리세컨이기 때문에 날짜를 초로 변환하고 1000을 곱해줘야 한다는 사실.



+++덧

내가 써놓고 뭔말인지 모르겠지만 개발자는 코드로 말한다고 한다. 코드만 보면 이해가 가실듯.

블로그 이미지

김생선

세상의 모든것을 어장관리