2017년 11월 30일 목요일

mybatis List객체에 제대로 select 데이터를 못 가져오는 경우.

기본적으로 mybatis 사용시 select 데이터는 아래와 같이 리스트로 받는다.

List<ConfirmingReceiptT> result2 = service.selectListConfirmingReceiptT(map);



이걸 객체에 List 변수로 받고자 하면 아래와 같이 한다.
public class GetConfirmingReceiptListResponseT {

@XmlElement(name = "ConfirmingReceipt")
protected List<ConfirmingReceiptT> confirmingReceipt;
...



여기서 문제가 발생.
최상위 객체인 GetConfirmingReceiptListResponseT의 confirmingReceipt에 내용이 담기지 않는 문제가 발생.

대략 일주일 정도 삽질 한 결과 최상위 객체에 달랑 List 변수 하나만 있으면 mybatis가
이걸 인식을 못한다.

해결책은 필요는 없지만 result를 한개더 추가 해 준다.

해결한 resultMap
<resultMap type="kr.co.auction.schema.arche_apisvc.ConfirmingReceiptT" id="ConfirmingReceiptT">
  <result property="receiptStatus" column="receiptStatus" />
  <result property="sellingType" column="SELLINGTYPE" />
  <result property="paymentTimeLimit" column="paymentTimeLimit" />
  <association property="orderBase" javaType="kr.co.auction.schema.arche_apisvc.OrderBaseT" >
   <id property="itemID" column="itemID" />
   <result property="orderNo" column="orderNo" />
   <result property="itemName" column="itemName" />
   <result property="awardQty" column="awardQty" />
   <result property="awardAmount" column="awardAmount" />
   <result property="deliveryFeeAmount" column="deliveryFeeAmount" />
   <result property="buyerName" column="buyerName" />
   <result property="buyerID" column="buyerID" />
   <result property="groupOrderSeqno" column="groupOrderSeqno" />
   <result property="requestOption" column="requestOption" />
   <result property="requestOptionPrice" column="requestOptionPrice" />
   <result property="sellerStockCode" column="sellerStockCode" />
   <result property="orderDate" column="orderDate" />
  </association>
  <association property="addressBase" javaType="kr.co.auction.schema.arche_apisvc.AddressBaseT" >
   <result property="name" column="name" />
   <result property="tel" column="tel" />
   <result property="mobileTel" column="mobileTel" />
   <result property="email" column="email" />
   <result property="postNo" column="postNo" />
   <result property="addressPost" column="addressPost" />
   <result property="addressDetail" column="addressDetail" />
   <result property="addressRoadName" column="addressRoadName" />
  </association>

</resultMap>


<resultMap type="kr.co.auction.schema.arche_apisvc.GetConfirmingReceiptListResponseT" id="GetConfirmingReceiptListResponseT" autoMapping="true">
 <result column="ADDUSERCD"  /><!-- temp, collection 한개만 있으면, List객체로 반환이 안됨. -->
 <collection property="confirmingReceipt" ofType="kr.co.auction.schema.arche_apisvc.ConfirmingReceiptT" resultMap="ConfirmingReceiptT" />
</resultMap>

2017년 11월 28일 화요일

mybatis 에서 association를 사용하면 중복 제거 문제.

mybatis 에서 association를 사용하면 중복된 값이 출력되지 않는 문제가 있다.
<resultMap type="kr.co.auction.schema.arche_apisvc.ConfirmingReceiptT" id="ConfirmingReceiptT" >
  <result property="receiptStatus" column="RECEIPTSTATUS" />
  <result property="sellingType" column="SELLINGTYPE" />
  <result property="paymentTimeLimit" column="PAYMENTTIMELIMIT" />
  <association property="orderBase" javaType="kr.co.auction.schema.arche_apisvc.OrderBaseT" >
   <result property="itemID" column="ITEMID" />
   <result property="orderNo" column="ORDERNO" />
   <result property="itemName" column="ITEMNAME" />
   <result property="awardQty" column="AWARDQTY" />
   <result property="awardAmount" column="AWARDAMOUNT" />
   <result property="deliveryFeeAmount" column="DELIVERYFEEAMOUNT" />
   <result property="buyerName" column="BUYERNAME" />
   <result property="buyerID" column="BUYERID" />
   <result property="groupOrderSeqno" column="GROUPORDERSEQNO" />
   <result property="requestOption" column="REQUESTOPTION" />
   <result property="requestOptionPrice" column="REQUESTOPTIONPRICE" />
   <result property="sellerStockCode" column="SELLERSTOCKCODE" />
   <result property="orderDate" column="ORDERDATE" />
  </association>
  <association property="addressBase" javaType="kr.co.auction.schema.arche_apisvc.AddressBaseT" >
   <result property="name" column="NAME" />
   <result property="tel" column="TEL" />
   <result property="mobileTel" column="MOBILETEL" />
   <result property="email" column="EMAIL" />
   <result property="postNo" column="POSTNO" />
   <result property="addressPost" column="ADDRESSPOST" />
   <result property="addressDetail" column="ADDRESSDETAIL" />
   <result property="addressRoadName" column="ADDRESSROADNAME" />
  </association>

</resultMap>

예를 들어 위와 같은 resultMap이 있을 경우, RECEIPTSTATUS, SELLINGTYPE, PAYMENTTIMELIMIT 3개의 필드의 내용이 모두 같을 경우는 총 row가 10개든, 100개든 한개의 row만 출력된다.

검색하면 <result 를 <id 로 바꾸라는 내용이 있는데, 이건 중복체크에 사용할 필드를 지정하는 것이다. <id로 지정시에는 해당 필드만 중복체크에 사용한다.

<result를 사용하면 나처럼 모두 비교한다.
나의 경우는 id 역할을 하는 필드(ex.primari key)가 없어서 문제가 된다.

해결책은 여기서 찾았다.
association을 쓰지 말라는 거다.
어떻게 보면 더 깔끔한 것 같기도 하다.
아무튼 아래와 같이 변경하면 중복된 내용을 제외하고 않고 전체를 잘 가져온다.
<resultMap type="kr.co.auction.schema.arche_apisvc.ConfirmingReceiptT" id="ConfirmingReceiptT" >
  <result property="receiptStatus" column="RECEIPTSTATUS" />
  <result property="sellingType" column="SELLINGTYPE" />
  <result property="paymentTimeLimit" column="PAYMENTTIMELIMIT" />
  <result property="orderBase.itemID" column="ITEMID" />
  <result property="orderBase.orderNo" column="ORDERNO" />
  <result property="orderBase.itemName" column="ITEMNAME" />
  <result property="orderBase.awardQty" column="AWARDQTY" />
  <result property="orderBase.awardAmount" column="AWARDAMOUNT" />
  <result property="orderBase.deliveryFeeAmount" column="DELIVERYFEEAMOUNT" />
  <result property="orderBase.buyerName" column="BUYERNAME" />
  <result property="orderBase.buyerID" column="BUYERID" />
  <result property="orderBase.groupOrderSeqno" column="GROUPORDERSEQNO" />
  <result property="orderBase.requestOption" column="REQUESTOPTION" />
  <result property="orderBase.requestOptionPrice" column="REQUESTOPTIONPRICE" />
  <result property="orderBase.sellerStockCode" column="SELLERSTOCKCODE" />
  <result property="orderBase.orderDate" column="ORDERDATE" />
  <result property="addressBase.name" column="NAME" />
  <result property="addressBase.tel" column="TEL" />
  <result property="addressBase.mobileTel" column="MOBILETEL" />
  <result property="addressBase.email" column="EMAIL" />
  <result property="addressBase.postNo" column="POSTNO" />
  <result property="addressBase.addressPost" column="ADDRESSPOST" />
  <result property="addressBase.addressDetail" column="ADDRESSDETAIL" />
  <result property="addressBase.addressRoadName" column="ADDRESSROADNAME" />

</resultMap>

2017년 11월 24일 금요일

리눅스 ls 자주 쓰는 옵션 정리.

-l 옵션이 있어야 되는 옵션도 있고, 없어도 되는 옵션도 있지만
기본적으로 전부 붙임. 더 자세한 건 man
#디렉토리만
ls -d */
ls -l | grep `^d'

#파일만
ls -l | egrep -v '^d'


#파일 크기별 (큰 파일부터...)
ls -lS

#파일 크기별 역순(작은 파일 부터...)
ls -lSr


#일자 전체 출력
ls --full-time
ls -l --time-style=full-iso


#크기를 읽기 좋게(e.g., 1K 234M 2G)
ls -lh

#하위 디렉토리 포함
ls -lR


 

timestamp별 정렬
#접속일자 순
ls -ult

#접속일자 역순(-r 옵션이 순서를 reverse함)
ls -ultr


#변경일자 순
ls -clt

#변경일자 역순(-r 옵션이 순서를 reverse함)
ls -cltr


#수정일자 순
ls -lt

#수정일자 역순(-r 옵션이 순서를 reverse함)
ls -ltr

접속일자, 수정일자, 변경일자가 뭔지는 여기서 확인.

리눅스의 timestamp

리눅스의 timestamp는 3가지가 있다.

예전 글에도 ctime과 mtime의 차이점에 대해 썼었는데, 그에 대한 연장선이다.

Access(atime) - 파일을 마지막으로 읽은 시간
Modify(mtime) - 파일 내용이 마지막으로 바뀐 시간
Change(ctime) - 파일의 메타 데이터가 마지막 으로 변경된 시간 (e.g. 권한)

 

stat 명령어로 timestamp를 확인 할 수 있다.

toucth 명령어로 test.txt 파일을 하나 생성 후 확인
[root@df test]# touch test.txt
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-11-23 17:36:11.380000000 +0900
Modify: 2017-11-23 17:36:11.380000000 +0900
Change: 2017-11-23 17:36:11.380000000 +0900
Birth: -

처음 생성시 Access, Modify, Change가 모두 동일 한 걸 볼 수 있다.

 

touch 명령어로 Access 수정 후 확인.
[root@df test]# touch -a -d '1 Jan 2000 12:34' test.txt
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2000-01-01 12:34:00.000000000 +0900
Modify: 2017-11-23 17:36:11.380000000 +0900
Change: 2017-11-23 17:39:18.467000000 +0900
Birth: -

 

touch 명령어로 Modify 수정 후 확인.
[root@df test]# touch -m -d '1 Jan 2006 12:34' test.txt
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2000-01-01 12:34:00.000000000 +0900
Modify: 2006-01-01 12:34:00.000000000 +0900
Change: 2017-11-23 17:39:52.616000000 +0900
Birth: -

 

권한 변경후 확인(Change)
[root@df test]# chmod 777 test.txt 
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2000-01-01 12:34:00.000000000 +0900
Modify: 2006-01-01 12:34:00.000000000 +0900
Change: 2017-11-23 17:40:18.557000000 +0900
Birth: -

 

cat 명령어로 해당 파일을 읽어 확인.
[root@df test]# cat test.txt 
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-11-23 17:48:21.985000000 +0900
Modify: 2006-01-01 12:34:00.000000000 +0900
Change: 2017-11-23 17:40:18.557000000 +0900
Birth: -

Access 만 변경 된 걸 볼 수 있다.

 

echo 명령어로 내용 추가 후 확인.
[root@df test]# echo test >> test.txt 
[root@df test]# stat test.txt
File: `test.txt'
Size: 5 Blocks: 8 IO Block: 4096 일반 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-11-23 17:48:21.985000000 +0900
Modify: 2017-11-23 17:50:20.241000000 +0900
Change: 2017-11-23 17:50:20.241000000 +0900
Birth: -

Access는 그대로, Modify와 Change가 같이 변했다.

 

그니까 find로 파일 내용이 변경된 파일을 찾으려면 mtime을 이용해야 한다.
# 수정한지 20일 이상( -mtime +20 )된 파일과 디렉토리
find . -mtime +20 -ls

2017년 11월 21일 화요일

공격 아이피 iptable 등록 스크립트.

mail log를 점검하다 보니, 지속적으로 접근시도하는 부분이 보인다.

보안장비를 사용하면 간단하게 막으련만... 여의치 않아 스크립트를 작성하였다.
#/bin/bash


help()
{
echo ""
echo "/var/log/maillog is default"
echo "iptable apply is not default"
echo "banCount 100 is default"
echo "-a : iptable applied"
echo "-f : maillog FILE PATH"
echo "-c : ban count"
echo ""
echo "Usae : $0 -a -c 100 -f /var/log/maillog "
exit 0
}

ignoreIPs=("111.222.333.444" "111.111.111.111")
maillog="/var/log/maillog"
banCount=100
fw=false

# 옵션이름 뒤에 :이 붙은 것은 값을 필요로 함을 의미합니다.
while getopts ac:f: opt
do
case $opt in
a)
fw=true
;;
c)
banCount=$OPTARG
;;
f)
maillog=$OPTARG
;;
*)
help
exit 0
;;
esac
done


banIPs=`grep -E 'user not found|password fail' ${maillog} | awk -F: '{print $NF}' | sort | uniq -dc | awk '{if ($1 > '"${banCount}"') print $NF}'`

for banIP in $banIPs
do
for ignoreIP in ${ignoreIPs[@]}
do
if [ $ignoreIP == $banIP ]
then
# echo "ignore IP: ${ignoreIP}"
# echo "ban IP : $banIP"
break
fi
done

echo "ban IP : $banIP ,whois : $(geoiplookup $banIP)"
if [ $fw = true ] ; then
iptables -A INPUT -s ${banIP}/24 -j DROP
fi

done


if [ $fw = true ] ; then
#remove duplicate iptables rules
#http://www.krazyworks.com/remove-duplicate-iptables-rules/
/sbin/service iptables save
/sbin/iptables-save | awk '!x[$0]++' > /tmp/iptables.conf
/sbin/iptables -F
/sbin/iptables-restore < /tmp/iptables.conf
/sbin/service iptables save
/sbin/service iptables restart
if [ -f /tmp/iptables.conf ] ; then /bin/rm -f /tmp/iptables.conf ; fi
fi

qmail 큐(queue) 체크 스크립트.

계정의 패스워드가 노출되었거나, 사용자의 pc가 해킹 당했을 경우
해당 계정을 통해서 스팸 메일 발송이 자주 일어난다.

이렇게 되면 큐가 1000개는 기본으로 넘어가게 되어 정상적인 메일도
같이 안나게 된다.

정상적인 메일서버면 큐개수가 많아야 20~30개이므로
큐가 100개를 넘어가면 경고 메일을 보내도록 하였다.

아래 스크립트는 큐메일 용도이다.
#!/bin/sh
# Send an email when there are more then 1000 messages in the mail queue
# This is counted by the amount of lines in the qmail-qread output, so it's an indication...

show=$1
qread="/var/qmail/bin/qmail-qread"
qreadIDs=`/var/qmail/bin/qmail-qread | awk '{print $6}' | sed 's/#//' | grep -v '^$' | uniq`
len=`$qread | wc -l`
SUBJECT="WARNING: There are $len messages in the mail queue!----Country Check Version"
EMAIL="your@mail.com"
EMAILMESSAGE="/tmp/emailmessage.txt"
echo "" > $EMAILMESSAGE
chmod 777 $EMAILMESSAGE


if [ $len -gt 100 ]; then

for id in $qreadIDs
do
case "$id" in
"0.0.0.0"|"127.0.0.1"|"8.8.8.8")
continue;;
*)
#find /var/qmail/queue/mess/ -name 1449183 | xargs cat | grep mysolution-remoteip | awk '{print $2}' | xargs geoiplookup
queueFile=`find /var/qmail/queue/mess/ -name $id`
IP=`cat $queueFile | grep mysolution-remoteip | awk '{print $2}'`
# FROM=`cat $queueFile | grep From:`
# Subject=`cat $queueFile | grep Subject:`
countryChk=`geoiplookup $IP`
receiveChk=`cat $queueFile | grep Received:`

echo "Queue ID : $id --------------" >> $EMAILMESSAGE
echo "$IP : $countryChk" >> $EMAILMESSAGE
echo $FROM >> $EMAILMESSAGE
echo $Subject >> $EMAILMESSAGE
echo "$receiveChk" >> $EMAILMESSAGE
echo "-----------------------------" >> $EMAILMESSAGE
echo "" >> $EMAILMESSAGE

if [ 1 -eq $# ]; then

if [ $show = "show" ]; then
cat $EMAILMESSAGE
fi
fi
esac
done

MESSAGE=$(cat $EMAILMESSAGE)
# send an email using /bin/mail
#mail -s "$SUBJECT" "$EMAIL" < $EMAILMESSAGE
printf "To: ${EMAIL}\nSubject: ${SUBJECT}\n\n $MESSAGE" | /var/qmail/bin/qmail-inject
rm -rf $EMAILMESSAGE

fi

2017년 11월 16일 목요일

mybatis hashmap으로 값 가져오기.

한개의 필드의 값을 hashmap으로 가져오려고 한다.

DB 내용
key1:value1,key2:value2,key3:value3

List<String> 으로 가져온 것과 마찬가지로 typeHandler를 사용하여 처리하였다.

 

DTO
HashMap<String, Object> extraProperties;


public HashMap<String, Object> getExtraProperties() {
return extraProperties;
}

public void setExtraProperties(HashMap<String, Object> extraProperties) {
this.extraProperties = extraProperties;
}

 

mybatis resultmap
<result property="extraProperties" column="extraProperties" typeHandler="com.my.handler.StringSplitMapTypeHandler" />

 

handler
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import com.my.util.CommonUtil;

public class StringSplitMapTypeHandler implements TypeHandler<HashMap<String,Object>> {

@Override
public void setParameter(PreparedStatement ps, int i, HashMap<String,Object> parameter, JdbcType jdbcType) throws SQLException {
if (parameter != null) {
ps.setString(i, parameter.toString());
}
}

@Override
public HashMap<String,Object> getResult(ResultSet rs, String columnName) throws SQLException {
String columnValueStr = rs.getString(columnName);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");

}

@Override
public HashMap<String,Object> getResult(ResultSet rs, int columnIndex) throws SQLException {
String columnValueStr = rs.getString(columnIndex);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");
}

@Override
public HashMap<String,Object> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String columnValueStr = cs.getString(columnIndex);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");
}
}

 

getStringSplitHashMap 코드
	public static HashMap<String,Object> getStringSplitHashMap(String targetStr, String fieldSeparator, String mapSeparator){
if (targetStr == null) return null;
String[] fields = targetStr.split(fieldSeparator);
HashMap<String,Object> returnData = new HashMap<String,Object>();
for(String str : fields){
String[] splitStr = str.split(mapSeparator);
if(splitStr.length < 2) continue;
String key = splitStr[0];
String value = splitStr[1];
returnData.put(key, value);
}
return returnData;
}

mybatis List으로 값 가져오기.

DB 테이블 한개의 필드에 저장된 값을 구분자로 나눠서 list로 가져오려고 한다.

typeHandler를 써서 간단히 적용 완료.

DB 내용 searchTags 필드
value1, value2, value3

,를 구분자로 각각의 value들이 하나의 List 값이 된다.

DTO
List<String> searchTags;

public List<String> getSearchTags() {
return searchTags;
}
public void setSearchTags(List<String> searchTags) {
this.searchTags = searchTags;
}

 

mybatis resultmap
<result property="searchTags" column="searchTags" typeHandler="com.my.handler.SplitTypeHandler" />

 

StringSplitTypeHandler
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

public class SplitTypeHandler implements TypeHandler<List<String>> {

@Override
public void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
if (parameter != null) {
ps.setString(i, parameter.toString());
}
}

@Override
public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
String columnValueStr = rs.getString(columnName);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}

@Override
public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
String columnValueStr = rs.getString(columnIndex);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}

@Override
public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String columnValueStr = cs.getString(columnIndex);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}
}

 

 

 

2017년 11월 14일 화요일

자바 json 데이터 정렬(?) 예쁘게(?) 출력하기.

GSON 사용시.
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser jp = new JsonParser();
JsonElement je = jp.parse(uglyJSONString);
String prettyJsonString = gson.toJson(je);

 

jettison 사용시
JSONObject json = new JSONObject(jsonString); // Convert text to object
System.out.println(json.toString(4)); // Print it with specified indentation

 

jackson 사용시
String test = "{\"age\":29,\"messages\":[\"msg 1\",\"msg 2\",\"msg 3\"],\"name\":\"mkyong\"}";
Object json = mapper.readValue(test, Object.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json));

2017년 11월 3일 금요일

casperjs 팁

casperjs 로 대여섯개 사이트 자동 출첵을 걸어놨다.

삽질 중에 얻은 팁을 공유한다.

 

1. <input> 에 type 이 없을 경우 fill 이 안 먹힘. << 이런 개 같은 경우가 있음  그럴 때는 sendKey를 활용.

2. alert 한글이 깨지는 경우 setting 옵션에 encoding: "utf8", 를 넣으면 됨.

3. verbose: true로 해야 syntax 에러 나옴.

4.onsubmit="INVEN.Outlogin.login(this); return false;"   << 이런식으로 되어 있으면 fill true 가 제대로 동작 안함. click 이벤트로 처리 하도록

(INVEN이네..;;)

 

11번가 처리시
5. iframe 처리iframe접근은 casper.withFrame 를 사용.

 
==================
[debug] [phantom] opening url: http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965, HTTP GET
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965, type=Other, willNavigate=true, isMainFrame=true << isMainFrame이 true
[debug] [phantom] url changed to "http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965"
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/html/blank.html, type=Other, willNavigate=true, isMainFrame=false << isMainFrame이 false임. iframe이란 소리.
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/jsp/event15/150625_benefitZoneAttend/iframe.jsp, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Navigation requested: url=about:blank, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Navigation requested: url=https://vars.hotjar.com/rcj-99d43ead6bdf30da8ed5ffcb4f17100c.html, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Successfully injected Casper client-side utilities
[info] [phantom] Step anonymous 5/8 http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965 (HTTP 200)
[info] [phantom] Step anonymous 5/8: done in 6937ms.
[info] [phantom] Step _step 6/8 http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965 (HTTP 200)
[info] [phantom] Step _step 6/8: done in 6955ms.
==================

위의 로그를 보면 iframe이 4개임.
iframe 이름이 있으면 casper.withFrame('이름'... 이런식으로 주지만, 이름이 없으면 0부터 index를 준다.

11번가 자동 출첵, 2017.11.03 (casperjs)

기존에 curl 을 이용한 방식은 onclick등 자바스크립트 함수가 들어가면 매우 골치아파지는 문제가 있었다.

그래서 검색하다 'headless browser' 라는 걸 알게 됐고, casperjs 란 알게 됐다.


 

11번가 자동 출첵은 casperjs 로 만들어서 기존에 사용하고 있었는데,

9월 30일 이후로 출첵이 안된 거 보니 그 때 출석체크 페이지가 바뀐듯 하다.

 

11번가 자동 출첵은 왜 하느냐?

마일리지를 받기 위해서다. 마일리지로 뭘 할 수 있는냐?

사람들이 잘 모르는데, 11번가 마일리지로 할 수 있는게 많다.

상품쿠폰,배송비쿠폰, 핸드폰 데이터 쿠폰 그리고 상품 교환도 된다.

베스킨라빈스 레귤러 한번 먹어 봤다.

 

각설하고 11번가 자동 출체 코드는 아래와 같다.

var casper = require('casper').create({ pageSettings: { "userAgent": 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.10 (KHTML, like Gecko) Chrome/23.0.1262.0 Safari/537.10', "loadImages": true, "loadPlugins": false, "webSecurityEnabled": false, "ignoreSslErrors": true }, onWaitTimeout: function () { //throw new Error }, onStepTimeout: function () { //throw new Error }, encoding: "utf8", waitTimeout: 10000, stepTimeout: 10000, logLevel: "debug", // Only "info" level messages will be logged verbose: true // log messages will be printed out to the console }); var login_id = casper.cli.get("id"); var login_pw = casper.cli.get("pw"); var login_url = 'https://login.11st.co.kr/login/Login.tmall'; var attendance_url = 'http://www.11st.co.kr/browsing/MallPlanDetail.tmall?method=getMallPlanDetail&planDisplayNumber=935566'; //temp login_id = 'myid'; login_pw = 'mypass'; if(!login_id){ casper.echo("require id parameter"); // casper.exit(); //not working phantom.exit(); } if(!login_pw){ casper.echo("require pw parameter"); phantom.exit(1); } casper.start(login_url, function() { this.fill('form[name="login_form"]', { 'loginName' : login_id, 'passWord': login_pw }, false); this.click('#memLogin > div.save_idW > input'); this.wait(1000, function() { //this.echo("I've waited for a second."); }); }); //출석 casper.thenOpen(attendance_url, function(){ //iframe this.withFrame(1, function () { this.click('#regForm > div > div.sect03 > div.dev04 > a.get04 > img'); this.wait(1000, function() { this.setFilter("page.confirm", function(msg) { return true; }); }); }); }); //casper.run(); casper.run(function() { require('utils').dump(this.result.log); this.exit(); });

 

당연하지만 매일 자동으로 실행하려면 매일 켜져있는 pc(server)가 필요하다.

테스트용 리눅스 pc가 있어서 casperjs 설치 후 cron으로 매일 실행 되게끔 설정하였다.

 

없으신 분들은 알아서...;;

spring standalone application 에서 @service 실행

spring standalone application 에서 @service 실행

스프링 프로젝트에서 Main 함수안에서 기존에 구축된 @service 를 사용하려면

다음과 같이 하면 된다.
package xxx.xxx.www.test.console;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import xxx.xxx.www.test.service.TestService;

@Component
public class Main {

private static final String CONFIG_PATH = "classpath*:spring/all-config.xml";

public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext(CONFIG_PATH);

Main p = context.getBean(Main.class);
p.start(args);
}

@Autowired
private TestService serv;
private void start(String[] args) throws Exception {
Map map = new HashMap();
map.put("docnum", "108");
List list = serv.selectList(map);
System.out.println("결과 logger:::::::::::::::: " + list);
}
}

 

한참 해맸었던 이유가 있는데, 기존에 스프링 환경은 아래와 같이 각 파트별로 구분지어 만들어진 환경이다.

spring-common.xml

spring-datasource.xml

....

이걸 부분만 적용하려니 잘 되지 않았다. 아래와 같이 한꺼번에 가져오도록 xml 파일을 하나 가져와서 해결.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<import resource="classpath*:spring/spring-*.xml"/>
</beans>