2011년 9월 22일 목요일

바닷가풍경

살다보면 가끔은 '그동안 내가 어떻게 살았던거지?' 라는 생각이 들때가 있다. 

참 별거 아닌 일에 끙끙거리며 살고 있구나... 

2011년 8월 10일 수요일

네이트온이 해킹당했다고해서...


아.. 네이트온이 해킹당했다..

왠지 S자 들어가는 기업과 별로 안 친한 관계로.. (사실 대인 관계의 폭이 무척이나 경색된 관계로 인해)

별로 하지도 않는 네이트의 비밀 번호를 변경하러 갔다..

그런데...













젠장.. 원래 비밀 번호를 기억 못했다..
... 30분 동안 삽질하다가 포기..ㅠㅠ



뭐야..이거 한편으로는 내 비밀번호를 해커가 알아도 쓸모가 없다는 안심!
근데..진짜 내 비밀번호는 뭘까?

2011년 6월 23일 목요일

게시판용 SQL

create table tbl_bbs (seqno number,
title varchar2(500) not null,
content varchar2(2000) not null,
readcnt number default 0,
writer varchar2(50) not null,
regdate date default sysdate,
updatedate date default sysdate
);

alter table tbl_bbs add constraint pk_bbs primary key (seqno);

create sequence seq_bbs;

insert into tbl_bbs(seqno, title, content,writer) values (seq_bbs.nextval, 'title......'||seq_bbs.currval,'contents....'||seq_bbs.currval, 'user00');

create table tbl_user (userid varchar2(50), userpw varchar2(50), username varchar2(100));

alter table tbl_user add constraint pk_user primary key (userid);

insert into tbl_user (userid, userpw, username) values ('user00','user00','Hong Gil Dong');
insert into tbl_user (userid, userpw, username) values ('user01','user01','Gun wo chi');

insert into tbl_user (userid, userpw, username) values ('user02','user02','Sim Chung');
insert into tbl_user (userid, userpw, username) values ('user03','user03','Houng Bu');

insert into tbl_bbs(seqno, title, content,writer)
(select seq_bbs.nextval, 'title......'||seq_bbs.currval,'contents....'||seq_bbs.currval, 'user0'||MOD(seq_bbs.currval,4) from tbl_bbs);

create table tbl_bbs_reply
(bbsno number ,
replydate date default sysdate,
replyer varchar2(50) not null,
reply varchar2(1000)
);


select round(dbms_random.value(0,3)) from dual;

insert into tbl_bbs_reply (bbsno, replyer, reply)
values ( round(dbms_random.value(0,seq_bbs.currval)), 'user0'||round(dbms_random.value(0,3)), 'reply.......' );

insert into tbl_bbs_reply (bbsno, replyer, reply)
(select round(dbms_random.value(0,seq_bbs.currval)), 'user0'||round(dbms_random.value(0,3)), 'reply.......' from tbl_bbs_reply );



select /*+INDEX_DESC(tbl_bbs pk_bbs)*/ seqno,title, content, regdate
from tbl_bbs
where rownum < 20

2011년 6월 21일 화요일

네이버 새로운 지도서비스 마커추가하기 예제


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<html >
 <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
<title>OpenAPI Map Test - 마커, InfoWindow 테스트</title>
<!-- prevent IE6 flickering -->
<script type="text/javascript">
try {document.execCommand('BackgroundImageCache', false, true);} catch(e) {}
</script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script type="text/javascript">

var tList;

jQuery(document).ready(
function () {
loadTerminatorLocations();
loadMap();
}
);

function loadTerminatorLocations(){
jQuery.ajax({
    type: 'GET',
    url: 'http://localhost:8080/satellite/pos/all',
    dataType: 'json',
    success: function(data)  {
    tList = data.terminatorGeoList;
    },
    data: {},
    async: false
});
}

function loadMap(){
var oCenterPoint = new nhn.api.map.LatLng(37.5010226, 127.0396037);
nhn.api.map.setDefaultPoint('LatLng');
oMap = new nhn.api.map.Map('testMap', {
point : oCenterPoint,
zoom : 10, // - 초기 줌 레벨은 10으로 둔다.
enableWheelZoom : false,
enableDragPan : true,
enableDblClickZoom : false,
mapMode : 0,
activateTrafficMap : false,
activateBicycleMap : false,
minMaxLevel : [ 5, 12 ],
size : new nhn.api.map.Size(500, 400)
});
var mapZoom = new nhn.api.map.ZoomControl(); // - 줌 컨트롤 선
mapZoom.setPosition({left:80, bottom:40}); // - 줌 컨트롤 위치 지
oMap.addControl(mapZoom); // - 줌 컨트롤 추가.
var markerCount = 0;
var oSize = new nhn.api.map.Size(28, 37);
var oOffset = new nhn.api.map.Size(14, 37);
var oIcon = new nhn.api.map.Icon('terminator_cyborg.gif', oSize, oOffset);
var mapInfoTestWindow = new nhn.api.map.InfoWindow(); // - info window 생
mapInfoTestWindow.setVisible(false); // - infowindow 표시 여부 지정.
oMap.addOverlay(mapInfoTestWindow); // - 지도에 추가.

var oLabel = new nhn.api.map.MarkerLabel(); // - 마커 라벨 선언.
oMap.addOverlay(oLabel); // - 마커 라벨 지도에 추가. 기본은 라벨이 보이지 않는 상태로 추가됨.

mapInfoTestWindow.attach('changeVisible', function(oCustomEvent) {
if (oCustomEvent.visible) {
oLabel.setVisible(false);
}
});


  oMap.attach('mouseenter', function(oCustomEvent) {
var oTarget = oCustomEvent.target;
// 마커위에 마우스 올라간거
if (oTarget instanceof nhn.api.map.Marker) {
var oMarker = oTarget;
oLabel.setVisible(true, oMarker); // - 특정 마커를 지정하여 해당 마커의 title을 보여준다.
}
});

oMap.attach('mouseleave', function(oCustomEvent) {
var oTarget = oCustomEvent.target;
// 마커위에서 마우스 나간거
if (oTarget instanceof nhn.api.map.Marker) {
oLabel.setVisible(false);
}
});

  jQuery(tList).each(
function(idx){
var oMarker1 = new nhn.api.map.Marker(oIcon,  { title : '터미네이터  : ' + this.tcode});
oMarker1.setPoint(new nhn.api.map.LatLng(tList[idx].latitude, tList[idx].longitude));
jQuery(oMap).click( function(oEvent){
alert('마커 ' + oEvent.target.getTitle() + '를 클릭했습니다');
});

oMap.addOverlay(oMarker1);

  }
); 

 
  oMap.attach('click', function(oEvent) {
     
    mapInfoTestWindow.setPoint(oEvent.target.getPoint());
    mapInfoTestWindow.setContent('<DIV style="border-top:1px solid; border-bottom:2px groove black; border-left:1px solid; border-right:2px groove black;margin-bottom:1px;color:black;background-color:white; width:auto; height:auto;">'+
  '<span style="color: #000000 !important;display: inline-block;font-size: 12px !important;font-weight: bold !important;letter-spacing: -1px !important;white-space: nowrap !important; padding: 2px 2px 2px 2px !important">'
  ''+oEvent.target.getTitle()+' <br /> <span></div>');
    mapInfoTestWindow.setVisible(true);
  });



}



</script>
<script type="text/javascript" src="http://openapi.map.naver.com/openapi/naverMap.naver?ver=2.0&key=4e4cc2effff6e97f9c04f5dbad16f0eb"></script>
 <body>
 <div id = "testMap" style="border:1px solid #000; width:500px; height:400px; margin:20px;"></div>

</body>

</html>


2011년 6월 16일 목요일

PL/SQL가벼운 예제

CREATE OR REPLACE PROCEDURE THINKER.p_emp(empno in number)
 IS
BEGIN
 NULL;
END p_emp;


-------------------------------------------------------------

CREATE OR REPLACE PROCEDURE THINKER.p_emp(empno in number)
 IS
BEGIN
 DELETE TBL_EMP WHERE employee_id = empno;
END p_emp;

-------------------------------------------------------------

CREATE OR REPLACE PROCEDURE THINKER.p_log(v_param IN number)
 IS
BEGIN
 INSERT INTO tbl_log(param) VALUES (v_param);

END p_log;

-------------------------------------------------------------

CREATE OR REPLACE PROCEDURE THINKER.p_emp(empno in number)
 IS
BEGIN
 p_log(empno);
 DELETE TBL_EMP WHERE employee_id = empno;
END p_emp;



-------------------------------------------------------------









CREATE OR REPLACE PROCEDURE THINKER.p_emp2(empno IN tbl_emp.employee_id%type)
 IS
BEGIN
 NULL;

END p_emp2;

-------------------------------------------------------------


CREATE OR REPLACE PROCEDURE THINKER.p_emp2(empno IN tbl_emp.employee_id%type)
 IS
BEGIN
 IF TRUE then
   NULL;

 END IF;

END p_emp2;


-------------------------------------------------------------


CREATE OR REPLACE PROCEDURE THINKER.p_emp2(empno IN tbl_emp.employee_id%type)
 IS
  v_job tbl_emp.job_id%TYPE;
BEGIN

 DBMS_OUTPUT.put_line('TARGET employee_id : '||empno);


 IF TRUE then
 
   NULL;

 END IF;

END p_emp2;



-------------------------------------------------------------



CREATE OR REPLACE PROCEDURE THINKER.p_emp2(empno IN tbl_emp.employee_id%type)
 IS
  v_job tbl_emp.job_id%TYPE;
BEGIN

 SELECT job_id INTO v_job FROM TBL_EMP WHERE employee_id = empno;

 DBMS_OUTPUT.put_line('TARGET employee_id : '||empno);


 IF TRUE then
 
   NULL;

 END IF;

END p_emp2;


-------------------------------------------------------------



CREATE OR REPLACE PROCEDURE THINKER.p_emp2(empno IN tbl_emp.employee_id%type)
 IS
  v_job tbl_emp.job_id%TYPE;
BEGIN

 SELECT job_id INTO v_job FROM TBL_EMP WHERE employee_id = empno;

 DBMS_OUTPUT.put_line('TARGET employee_id : '||empno);

 IF v_job = 'IT_PROG' then

  DBMS_OUTPUT.put_line('UPDATE employee_id : '||empno);
  UPDATE TBL_EMP SET salary = salary * 2 WHERE job_id = v_job;

 END IF;

END p_emp2;

-------------------------------------------------------------


CREATE OR REPLACE PROCEDURE THINKER.p_emp3(cnt in number)
 IS
  v_cnt NUMBER := 0;
BEGIN
 FOR v_cnt IN 1.. cnt LOOP

  DBMS_OUTPUT.put_line(v_cnt);

 END LOOP;

END p_emp3;

--------------------------------------------------------------


CREATE OR REPLACE PROCEDURE THINKER.p_emp4
 IS
 CURSOR emp_cursor IS
 SELECT employee_id, first_name ||' '|| last_name, salary FROM TBL_EMP;
 
BEGIN

 OPEN emp_cursor ;


 CLOSE emp_cursor;
 

END p_emp4;










CREATE OR REPLACE PROCEDURE THINKER.p_emp4
 IS
 v_id tbl_emp.EMPLOYEE_ID%TYPE;
 v_name VARCHAR2(200);
 v_sal number;

 CURSOR emp_cursor IS
 SELECT employee_id, first_name ||' '|| last_name, salary FROM TBL_EMP;
 
BEGIN

 OPEN emp_cursor ;

 LOOP
   FETCH emp_cursor INTO v_id, v_name, v_sal;
 
   DBMS_OUTPUT.put_line(v_id||' '||v_name);
 
   EXIT WHEN emp_cursor%NOTFOUND;

 END LOOP;


 CLOSE emp_cursor;
 

END p_emp4;

-------------------------------------------------------------

CREATE OR REPLACE PROCEDURE THINKER.p_emp5(empno in number)
 IS
  v_name VARCHAR2(200);
  v_job VARCHAR2(30);
BEGIN

 SELECT first_name||''||last_name, job_id INTO v_name, v_job  FROM TBL_EMP WHERE employee_id = empno;

 DBMS_OUTPUT.put_line(v_name||'- '|| v_job);

EXCEPTION
 WHEN NO_DATA_FOUND THEN
  DBMS_OUTPUT.put_line('NO DATA FOUND');
  p_log2(empno);  


END p_emp5;



CREATE OR REPLACE PROCEDURE THINKER.p_emp5(empno in number)
 IS
  v_name VARCHAR2(200);
  v_job VARCHAR2(30);
BEGIN

 SELECT first_name||''||last_name, job_id INTO v_name, v_job  FROM TBL_EMP WHERE employee_id = empno;

 DBMS_OUTPUT.put_line(v_name||'- '|| v_job);

 UPDATE TBL_EMP SET salary = salary + 100;

EXCEPTION
 WHEN NO_DATA_FOUND THEN
  DBMS_OUTPUT.put_line('NO DATA FOUND');


END p_emp5;

2011년 6월 15일 수요일

스프링..트랜잭션 어노테이션 분산 DB 처리

<resource>
<data-source>
<database>
<vendor>oracle</vendor>
<export-name>datasource1</export-name>
<data-source-class-name>oracle.jdbc.pool.OracleConnectionPoolDataSource</data-source-class-name>
<data-source-type>ConnectionPoolDataSource</data-source-type>
<database-name></database-name>
<data-source-name>oracle.jdbc.pool.OracleConnectionPoolDataSource</data-source-name>
<port-number></port-number>
<server-name></server-name>
<user></user>
<password></password>
<driver-type>thin</driver-type>
<connection-pool>
<pooling>
<min>20</min>
<max>30</max>
<step>10</step>
<period>3600000</period>
</pooling>
<wait-free-connection>
<enable-wait>true</enable-wait>
</wait-free-connection>
</connection-pool>
</database>
<database>
<vendor>oracle</vendor>
<export-name>datasource3</export-name>
<data-source-class-name>oracle.jdbc.pool.OracleConnectionPoolDataSource</data-source-class-name>
<data-source-type>ConnectionPoolDataSource</data-source-type>
<database-name></database-name>
<data-source-name>oracle.jdbc.pool.OracleConnectionPoolDataSource</data-source-name>
<port-number></port-number>
<server-name></server-name>
<user></user>
<password></password>
<driver-type>thin</driver-type>
<connection-pool>
<pooling>
<min>5</min>
<max>10</max>
<step>10</step>
<period>3600000</period>
</pooling>
<wait-free-connection>
<enable-wait>true</enable-wait>
</wait-free-connection>
</connection-pool>
</database>
</data-source>
</resource>

설정후 was jndi
resource>
<data-source>
<database>
<vendor>oracle</vendor>
<export-name>datasource1</export-name>
<data-source-class-name>oracle.jdbc.xa.client.OracleXADataSource</data-source-class-name>
<data-source-type>XADataSource</data-source-type>
<database-name>db명</database-name>
<data-source-name>oracle.jdbc.xa.client.OracleXADataSource</data-source-name>
<port-number>port</port-number>
<server-name>ip</server-name>
<user></user>
<password></password>
<driver-type>thin</driver-type>
<connection-pool>
<pooling>
<min>20</min>
<max>30</max>
<step>10</step>
<period>3600000</period>
</pooling>
<wait-free-connection>
<enable-wait>true</enable-wait>
</wait-free-connection>
</connection-pool>
</database>
<database>
<vendor>oracle</vendor>
<export-name>datasource3</export-name>
<data-source-class-name>oracle.jdbc.xa.client.OracleXADataSource</data-source-class-name>
<data-source-type>XADataSource</data-source-type>
<database-name>db명</database-name>
<data-source-name>oracle.jdbc.xa.client.OracleXADataSource</data-source-name>
<port-number>port</port-number>
<server-name>ip</server-name>
<user></user>
<password></password>
<driver-type>thin</driver-type>
<connection-pool>
<pooling>
<min>5</min>
<max>10</max>
<step>10</step>
<period>3600000</period>
</pooling>
<wait-free-connection>
<enable-wait>true</enable-wait>
</wait-free-connection>
</connection-pool>
</database>
</data-source>

그리고 spring applicationContext.xml 설정입니다.
이부분이 변경 정 설정이구요 ㅎ보라색 부분을 빨간색 부분으로 수정하였어요

<jee:jndi-lookup id="dataSource-totd" jndi-name="jdbc/datasource1" resource-ref="true" />
<jee:jndi-lookup id="dataSource-hris" jndi-name="jdbc/datasource3" resource-ref="true" />
<jee:jndi-lookup id="dataSource-another" jndi-name="jdbc/datasource1" resource-ref="true" />
<!-- dataSource-totd datasource의 transaction 처리를 위한 transactionmanager설정 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource-totd" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager_hris" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource-hris" />
<tx:annotation-driven transaction-manager="transactionManager_hris"/>


<bean id="sqlMapClient-totd"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">
<value>classpath:sqlMapConfig.xml</value>
</property>
<property name="useTransactionAwareDataSource">
<value>true</value>
</property>
<property name="dataSource">
<ref bean="dataSource-totd" />
</property>
</bean>
<bean id="sqlMapClient-hris"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">
<value>classpath:sqlMapConfig.xml</value>
</property>
<property name="useTransactionAwareDataSource">
<value>true</value>
</property>
<property name="dataSource">
<ref bean="dataSource-hris" />
</property>
</bean>
<bean id="sqlMapClient-another"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">
<value>classpath:sqlMapConfig.xml</value>
</property>
<property name="useTransactionAwareDataSource">
<value>true</value>
</property>
<property name="dataSource">
<ref bean="dataSource-another" />
</property>
</bean>

변경후
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<tx:annotation-driven transaction-manager="transactionManager"/>


이렇게 JtaTransactionManager 를 쓰니까 datasource 를 참조 안시켜도 

- DAO 부분
DAO
@Repository
public class RecruitDAO extends WMBaseDAO {

private static String packageName = "JobInfo.";

@Autowired
@Override
public void setSqlMapClient(@Qualifier("sqlMapClient-hris")SqlMapClient sqlMapClient) {
super.setSqlMapClient(sqlMapClient);
}

public void insertRecruitStep02(HashMap<String, Object> mapParams) throws Exception{
delete(packageName+"deletePhoto",mapParams); // 사진 삭제
insert(packageName+"insertPhoto", mapParams); // 사진 등록

insert(packageName+"insertLGADDRM", mapParams); // 지원자 신상1 정보 등록..
insert(packageName+"insertLGMASTM", mapParams); // 지원자 신상2
insert(packageName+"insertLGBDSTM", mapParams); // 지원자 신체사항
insert(packageName+"insertLGMILM", mapParams); // 지원자 병력사항

//지원자 가족사항
List<HashMap<String, Object>> famList = (List<HashMap<String, Object>>)mapParams.get("LGFAMLM");

for(int i = 0 ; i < famList.size(); i++)
{
HashMap<String, Object> famObj = (HashMap<String, Object>)famList.get(i);
insert(packageName+"insertLGFAMLM", famObj); // 지원자 병력사항
}
}
}

- Service 부분

@Service
public class RecruitService {

@Autowired
private RecruitDAO dao;

@Transactional(propagation=Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertRecruitStep02(HashMap<String, Object> mapParams) throws Exception{
dao.insertRecruitStep02(mapParams);
}
}



서버 재가동

 <!-- jeus connection pool 사용을 위한 jndi lookup -->
<jee:jndi-lookup id="dataSource-totd" jndi-name="jdbc/datasource1" resource-ref="true" />
<jee:jndi-lookup id="dataSource-hris" jndi-name="jdbc/datasource3" resource-ref="true" />
<jee:jndi-lookup id="dataSource-another" jndi-name="jdbc/datasource1" resource-ref="true" />

<!-- dataSource-totd datasource의 transaction 처리를 위한 transactionmanager설정 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource-totd" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager_hris" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource-hris" />
<!-- <tx:annotation-driven transaction-manager="transactionManager_hris"/> -->

<aop:config>
<aop:pointcut id="recruitOperation"
expression="execution(* wm.ir.jobInfo.service.RecruitService.*(..))" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="recruitOperation" />
</aop:config>

<tx:advice id="txAdvice" transaction-manager="transactionManager_hris">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>

2011년 6월 14일 화요일

sql실습

SELECT employee_id, department_id FROM TBL_EMP


SELECT 1 a, 2 b, 3 c FROM dual
UNION ALL
SELECT 4 a, 5 b, 6 c FROM dual


SELECT SUM(d10), SUM(d20), SUM(d30)
FROM
(SELECT COUNT(employee_id) d10 , 0 d20, 0 d30 FROM TBL_EMP WHERE department_id = 10
UNION ALL
SELECT 0 d10, COUNT(employee_id) d20, 0 d30 FROM TBL_EMP WHERE department_id = 20
UNION ALL
SELECT 0 d10, 0 d20, COUNT(employee_id) d30 FROM TBL_EMP WHERE department_id = 30 ) dept



SELECT '10' d10, '20' d20, '30' d30, '40' d40, '50' d50 FROM dual
UNION ALL
SELECT
SUM( DECODE(department_id, 10, 1, 0)) d10 ,
SUM( DECODE(department_id, 20, 1, 0)) d20 ,
SUM( DECODE(department_id, 30, 1, 0)) d30 ,
SUM( DECODE(department_id, 40, 1, 0)) d40 ,
SUM( DECODE(department_id, 50, 1, 0)) d50
FROM TBL_EMP




SELECT * FROM hr.EMPLOYEES, hr.DEPARTMENTS WHERE employee_id = 100


SELECT 2889/107 FROM dual

SELECT *
FROM
(SELECT * FROM hr.EMPLOYEES WHERE employee_id = 100 ) emp, hr.DEPARTMENTS



SELECT
*
FROM
(
SELECT ROWNUM rn, seqno, title, writer, regdate, updatedate
FROM
TBL_BOOK
WHERE seqno > 0
AND ROWNUM > 0 AND ROWNUM <= (3 * 10) ORDER BY seqno desc ) WHERE rn > ( (3-1) * 10)



SELECT * FROM hr.DEPARTMENTS

SELECT employee_id, dept.department_id
FROM hr.EMPLOYEES emp, hr.DEPARTMENTS dept
WHERE emp.DEPARTMENT_ID(+) = dept.DEPARTMENT_ID

2011년 6월 13일 월요일

100만건 만들기

CREATE table tbl_book (
seqno NUMBER,
title VARCHAR2(300) NOT NULL,
writer VARCHAR2(100) NOT NULL,
regdate DATE DEFAULT SYSDATE,
updatedate DATE DEFAULT sysdate
);

ALTER TABLE tbl_book ADD CONSTRAINT pk_book PRIMARY KEY (seqno);

CREATE SEQUENCE seq_book;


INSERT INTO tbl_book (seqno, title, writer )
VALUES (SEQ_BOOK.NEXTVAL, 'The old man and the sea', 'Honest Hamingway');

SELECT * FROM tbl_book;

INSERT INTO tbl_book(seqno, title,writer) (SELECT SEQ_BOOK.NEXTVAL, title, writer FROM tbl_book);

100만건 만들기

CREATE table tbl_book (
seqno NUMBER,
title VARCHAR2(300) NOT NULL,
writer VARCHAR2(100) NOT NULL,
regdate DATE DEFAULT SYSDATE,
updatedate DATE DEFAULT sysdate
);

ALTER TABLE tbl_book ADD CONSTRAINT pk_book PRIMARY KEY (seqno);

CREATE SEQUENCE seq_book;


INSERT INTO tbl_book (seqno, title, writer )
VALUES (SEQ_BOOK.NEXTVAL, 'The old man and the sea', 'Honest Hamingway');

SELECT * FROM tbl_book;

INSERT INTO tbl_book(seqno, title,writer) (SELECT SEQ_BOOK.NEXTVAL, title, writer FROM tbl_book);

2011년 6월 9일 목요일

루저의 말을 듣지 말고, 위너의 말을 듣기를...

살다보면 누구나 힘들거나, 지겹거나, 나태한 시기가 오는 것 같습니다.

그런데.. 주변의 사람들 중에서는 그 상황을 벗어나기 위한 편한 방법을 쓰는 사람들에게 혹하는 것 같습니다.

즉 불평 불만을 하고, 같이 투덜거리고, 남의 탓을 하는 겁니다.



그게 더 쉬우니까요..


혼자 할 일을 찾고, 묵묵하게 자신의 일을 하는 것은 참으로 어렵고 힘든 일인것 같습니다.

하지만 모든 역사가 증명해 줍니다.

불평불만은 결국 루저들의 몫입니다.

그걸로 해결되는 것은 아무것도 없습니다.
좀 더 높은 세계를 봐야 합니다.

위너라고 불리는 사람들이 대처하는 방식을 ..


부모님들이 좋은 학교에 가라.. 좋은 직장에 가라는 말은 결국은
그런 위너들이 더 많은 곳에서 배우길 바라는 마음이 아닐까..


바보처럼 투덜거리지 마십시요..
그건 루저들이나 하는 짓입니다.

맨 마지막 승부는 자신이 하는 겁니다.
그 평가는 아무도 손댈수 없습니다. 본인이 스스로 잘 느끼기 때문에...
마음에 빚을 지고 살지 맙시다...

2011년 6월 8일 수요일

제네릭의 본질에 대하여..

흔히들 Java기초를 공부하는 동안 Generic문법을 피상적으로 공부하게 됩니다.
예를들어

List list = new ArrayList();

방식의 코드입니다.

그럼 이것이 Generic문법인가요? 아닙니다.

Generic은 코드를 작성할 때 형(타입)을 결정하는 것이 아니라,
코드가 활용될 때 타입을 결정하는 방식이라는 겁니다.

예를 들어 볼까요?


public class GenericEx {

public void add(E p1, K p2){

System.out.println(p1);
System.out.println(p2);

}
}


위의 코드에 대한 해석은 나중에 E, K라는 타입을 결정해 주겠다는 코드입니다.
이것이 객체화 될 때는 다음처럼 다양한 방식의 코드를 생성해 줄 수 있게 됩니다.

GenericEx ex1 = new GenericEx();

ex1.add("AAAAAAAAAA", 1234);

GenericEx ex2 = new GenericEx();

ex2.add( 1234.12, 1234F);


코드를 보면 나중에 파라미터의 타입을 전혀 다르게 결정하는 모습을 볼 수 있습니다.

사실 제네릭은 자바의 기존의 Object타입의 모호성과 오버로딩의 제약을 해결하기 위한 하나의 방식이라고 보는 것이 더 좋지 않을까 싶습니다.


--------------------------------------------
한동안 정신적으로, 육체적으로 힘든 시기였습니다.
때문에 이 블로그 작성역시 소홀했습니다.
아직 온전하지 않은 육체와 정신이지만 다시 노력해 보려 합니다.

2011년 5월 11일 수요일

오라클페이징

2011년 5월 5일 목요일

안드로이드 SMS 메시지 가로채기

Bundle bundle = intent.getExtras();

SmsMessage[] msgs = null;
String str = "";
if (bundle != null)
{
Object[] pdus = (Object[]) bundle.get("pdus");
msgs = new SmsMessage[pdus.length];
for (int i=0; i msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
str += "SMS from " + msgs[i].getOriginatingAddress();
str += " :";
str += msgs[i].getMessageBody().toString();
str += "\n";
}
Toast.makeText(context, str, Toast.LENGTH_SHORT).show();

2011년 4월 15일 금요일

오라클 계층형 게시판용 SQL

예를들어 다음과 같은 구조로 게시판을 만들어야 한다면?


1

  2
 
    3
    
       6
    
    4


  5
 
  7


create table tbl_board (
  seqno number,
  title varchar2(300) not null,
  content varchar2(4000) not null,
  writer varchar2(50) not null,
  regdate date default sysdate,
  updatedate date default sysdate,
  gid number,
  ord number,
  pid number,
  depth number
);

alter table tbl_board add constraint pk_board primary key (seqno);





drop sequence seq_board;

create sequence seq_board increment by 1;

delete  tbl_board;
--원글 등록--
insert into tbl_board (seqno, title, content, writer, gid, ord, pid, depth)
values (seq_board.nextval, 'title1','content1','user00', seq_board.currval, 0, 0, 0);

--2
--select * from tbl_board start with pid= 1 connect by pid = prior seqno;

--select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1;

update tbl_board set ord = ord + 1 where gid = 1 and ord >= (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1);

insert into tbl_board (seqno, title, content, writer, gid, ord, pid, depth)
values (seq_board.nextval, 'title2','content2','user00', 1, (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1), 1, 1);

--3
--select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 2 connect by pid = prior seqno) ) from tbl_board where seqno = 2;

update tbl_board set ord = ord + 1 where gid = 1 and ord >= (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 2 connect by pid = prior seqno) ) from tbl_board where seqno = 2);

insert into tbl_board (seqno, title, content, writer, gid, ord, pid, depth)
values (seq_board.nextval, 'title3','content3','user00', 1, (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 2 connect by pid = prior seqno) ) from tbl_board where seqno = 2), 2, 2);

--4

--select * from tbl_board start with pid= 1 connect by pid = prior seqno;

--select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1;

update tbl_board set ord = ord + 1 where gid = 1 and ord >= (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 2 connect by pid = prior seqno) ) from tbl_board where seqno = 2);

insert into tbl_board (seqno, title, content, writer, gid, ord, pid, depth)
values (seq_board.nextval, 'title3','content3','user00', 1, (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1), 2, 2);


--5

--select * from tbl_board start with pid= 1 connect by pid = prior seqno;

--select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1;

update tbl_board set ord = ord + 1 where gid = 1 and ord >= (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1);

insert into tbl_board (seqno, title, content, writer, gid, ord, pid, depth)
values (seq_board.nextval, 'title3','content3','user00', 1, (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1), 1, 1);


--6

--select * from tbl_board start with pid= 3 connect by pid = prior seqno;

--select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 3 connect by pid = prior seqno) ) from tbl_board where seqno = 3;

update tbl_board set ord = ord + 1 where gid = 1 and ord >= (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 3 connect by pid = prior seqno) ) from tbl_board where seqno = 3);


insert into tbl_board (seqno, title, content, writer, gid, ord, pid, depth)
values (seq_board.nextval, 'title6','content6','user00', 1, (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 3 connect by pid = prior seqno) ) from tbl_board where seqno = 3), 3, 3);


--7

select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1;

update tbl_board set ord = ord + 1 where gid = 1 and ord >= (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1);

insert into tbl_board (seqno, title, content, writer, gid, ord, pid, depth)
values (seq_board.nextval, 'title7','content7','user00', 1, (select (ord + (select count(seqno) + 1  r from tbl_board start with pid= 1 connect by pid = prior seqno) ) from tbl_board where seqno = 1), 1, 2);

2011년 4월 12일 화요일

의존성 주입의 개념에 관하여

객체지향 프로그래밍에서 가장 신경을 많이 쓰는 것은 역시나 '객체의 구성'입니다.

예를 들어  A,B,C 라는 객체가 있다고 가정해 보면 가장 쉬운 방식의 구성은 '기차'와 같은 방식으로 연결하는 구조라고 할 수 있습니다.

이 방식을 전문적으로 ' Demeter'방식이라고 합니다.

이 방식은  A클래스안에  B b = new B( );  와 같은 방식으로 다음에 연결될 객체를 의존적으로 사용하는 방식을 의미합니다.

 Demeter 방식은 하나의 클래스를 여러 곳에서 사용하는 경우(그물망 구조)에는 아주 최악의 복잡성을 가지게 됩니다.

따라서 이런 상태에서는 효율적인 설계를 위해서 '기차'대신에 'G마켓'방식을 사용합니다.
(당근 공식용어일리가 없잖앗!)


전문적으로 'Facade'패턴이라고 하기도 하는데 우리가  G마켓을 통해서 물건을 구매하면 안쪽에서 실제 업체를 몰라도 되는 것과 비슷한 방식입니다.

우리가 알아야 하는 것은 'G마켓'의 역할을 하는 객체만 알고 사용하면 됩니다.

이 방식의 복잡성을 하나로 모으는 효과를 가집니다.(간단히 여러분이 여러개의 인터넷 쇼핑몰을 돌아다닐 필요가 없는것과 마찬가지 입니다)


이 방식의 코드는 예를 들어 A클래스 안에서  B target = Facade.get("B") 처럼 A가 G마켓 역할을 하는 존재만 알면 되도록 하는 구조를 사용합니다.

하지만 Facade역할을 하는 코드가 없다면 이 역시 무용지물이므로 전체구조가 경직되는 것을 피할 수는 없습니다.

이래서 등장한 방식이.. 음..


이해를 위한 거니까..

'시라노 연애 조작단' 방식과 같은 방식입니다.

즉 어떤 객체가 알아채지 못하게 알아서 모든 상황을 준비하는겁니다.

예를 들어 A가 B객체가 필요하다면

누군가  A를 필요로 할때  B객체를 미리 준비 시켜서 동행시키는 방식입니다.

즉 외부에서 필요한 객체(dependency) 를 집어넣는다(injection)는 개념으로 봐야합니다.

영화에서 보면 한 커플을 맺어주기 위해서 많은 스텝들이 움직이듯이..

외부에 거대한 존재가 A가 필요한 모든 다른 객체들을 준비해 두었다가 A를 사용하는 순간에 완벽한 상황을 만들어서 처리하는 방식이라고 생각할 수 있습니다.

의존성 주입은 A안에는 B 를 받아들이는 코드만 준비해 두고..

외부에서 B를 A에 넣은 상태로 만들어서 사용하는 방식이므로

A의 코드안에서 실제 new 하는 코드를 없앨 수 있게 됩니다.

2011년 4월 10일 일요일

상속을 어떻게 적용하면 좋을까? 문제입니다.

패스트 푸드(맥도널드나 버거킹)에 가보면 많은 메뉴가 있습니다.

예를 들어 맥도널드 메뉴를 몇 개만 가정해 봅니다.

빅맥, 불고기버거, 새우버거

이 모든 메뉴들은 세트메뉴를 지원하고 있습니다.

그리고 이 메뉴들은 특별히 활인이나 적립이 허락됩니다.

보통의 통신사 카드들은 10%의 할인율을 가지고,


적립카드는 5%의 금액을 적립해 줍니다.

그렇다면 위의 데이터들을 기초로 해서 클래스로 만들려면 어떻게 해야 할까요?

상속인가? 조합인가?

2011년 3월 24일 목요일

자바는 구현하는 언어가 아닌 설계의 언어

흔히들 Java는 메모리 관리를 하지 않아도 되기 때문에 프로그래밍이 참 쉽다고 생각한다.

음.. 뭐 굳이 비유를 들자면 '오토'자동차가 점점 좋아지니 '수동'기어의 운전을 하는 사람들이
점점 드물어 진다고 할까?

그럼 반대로 생각해보자.

'오토'는 나쁘고 '수동'은 좋은가?

여기에 무작정 '그렇다'고 자신있게 대답할 사람은 드물것이다.

만일 여러분이 스포츠 카의 매니아라면 SUV같은 차는 쓰레기 같은 차라고 생각할 지도 모른다.
만일 여러분이 음악에 문외한이라면 세계적인 거장이 지하철에서 연주해도 그냥 지나칠지도 모른다.

즉 모든 판단은 그때 자신이 가진 정보와 상황에 맞출수 밖에 없다고 생각해 볼 수 있다.

Java역시 마찬가지다.

프로그램의 속도와 깊이를 따지는 사람에게 Java는 그냥 오토매틱 자동차이겠지만,

모든 사람들이 프로그램을 모시고 사는 것은 아니지 않은가?


요즘 나오는 언어들을 보면 Java보다 훨씬 더 간단하고 우아하게 코드를 작성한다.
짧은 코드와 간단한 실행..

그렇다면 Java라는 언어는 '빠르지도 않고, 간편하지도 않은' 언어가 되고 만다.


그런 언어가 아직까지 세계에서 가장 많이 쓰이는 언어가 되었을까?



내가 생각하는 것은
Java라는 언어가 설계에 아주 적합한 언어이기 때문이 아닐까 싶다.

즉 Java는 뼈대를 세우고, 여러 명이 협업해야 하는 상황에서 거의 공통적인 코드를 만들어 낼 수 있는 정형화된 방법을 아주 많이 제공하고 있다.

'인터페이스'나 '추상클래스'와 같은 구조의 뼈대에
다른 개발자들이 만들어도 큰 뼈대를 벗어나지 않는 느낌..

Java를 처음 공부하는 사람들은 반드시 생각해 봐야 하는 사실이 바로 이런 점이다.

'내가 왜 Java를 선택했지?'
'그냥 유행따라서?'

제발 대학에서 이런 생각을 좀 가르쳐주면 좋겠다.
뭘 만들기 전에 어떻게 언어를 공부해야 하는지..
어떤 언어가 어떤 특징이 있는지..

가끔은 '뭐 언어가 다 똑같지... '
라고 말하는 개발자들을 본다.
겉으로 그들에게 뭐라고 반기를 들지 않지만..

속으로는 이렇게 말한다.

'람보가 불어로 중얼거리면 얼마나 웃길까?'

적어도 언어를 공부하면 그 언어에 맞게 만들어 내기는 해야 하지 않을까?

2011년 3월 23일 수요일

노력도 재능이다..

예전에 명계남씨가 '노력도 재능이다'라고 하시던 말씀이 생각나네요.

요즘들어 왠지 뭔가 막혀있고, 잘 안풀리는 일들이 많아서
나름 슬럼프에 빠진듯 합니다.

음.. 제네릭 원고도 완성을 못 시키고 있고, 앞으로 강의 쉬는 동안 무얼 할지 고민도
많이 되고..

새로운 분야에 대한 걱정도 됩니다.

그래서 요즘 소원은요..

정말 노력도 재능이면 좋겠다


입니다.

2011년 3월 7일 월요일

Generic에 대해서 정리하려고 합니다.

가능하면 이번주(3월 12일이전까지)까지 정리하려 합니다.

정리할 내용은 다음과 같습니다.


Generic 문법

Generic문법 이전의 기법

다형성의 한계 - java.lang.Object의 사용

Generic문법은 결국은 컴파일러가 체크하는 정확한 타입

컴파일러가 인식하는 Generic문법

Bound/ Unbound  표기법

Wildcard 문자 사용법

Mac에서 Pages로 작성하고 있으니 출판의 형태는  eBook (.epub)의 형태가 될 듯 합니다.

2011년 2월 20일 일요일

새로운 방식의 강의가 업데이트 되고 있습니다.

안녕하세요. 강요천입니다.

예전에 공지해 드린 대로 강의장에서 녹화되는 강좌를 추가하고 있습니다.

강의 작업은 매주 토요일에 녹화가 되고 있습니다.


chap12 이후의 모든 강의는
강의장에서 녹화되는 방식으로 우선 진행 됩니다.

상속과 인터페이스는 워낙 중요하고
어렵기 때문에
한 번이라도 다시 정리해 드리는 것이 좋다고 생각하여
다시 녹화를 하여서 강의실에 업데이트 됩니다.

2월 20일 현재 새로 갱신된 강의는 다음과 같습니다.

chap 7. 객체지향 패러다임
chap 10. 상속
chap 11. 인터페이스
chap 12. 문자열

강의가 고르지 못한 점 죄송합니다.
하지만 지난번 공지해 드린 것처럼 항상 여러분들이 더 좋은 자료를 얻으실 수 있도록
노력하고 있으니
많은 시행착오가 있더라도 너그러이 양해해 주시면 감사하겠습니다.

이제 또 저는 다음 약속처럼
추가적인 원고를 제작하려고 합니다.

객체중심Java 원고를 만들면서 몇 가지 제 스스로 약속한 것이 있습니다.
* 하나의 평가라도 공정한 평가를 받겠다.
* 공부하는 사람들이 계속해서 도움을 받을 수 있는 책을 만들겠다.
* 공부하고 싶은데 현실적으로 제약이 많은 사람들에게 도움이 되겠다.

앞으로도 많은 시행착오와 변경이 있겠지만
위의 세 가지 사항은 제 스스로의 자존심입니다.

부디 고르지 못한 강의에 언짢아 하지 마시고..
앞으로 더 노력하겠습니다.
감사합니다.

2011년 2월 19일 토요일

안드로이드 지도 프로그래밍 자료 올립니다.

안드로이드를 이용하는 지도 프로그램 만드는 부분

추가 원고로 제공용으로 만든 것입니다.

주요 내용은 다음과 같습니다.

- Activity에 지도 보이는 설정 및 예제 만드는 요령
- Location Manager를 이용해서 자신의 위치를 파악하는 방법
- 지도 상에서 원하는 좌표로 이동하기

- Overlay를 이용해서 지도위에 마커 표시하기
- 일반 Overlay와 ItemizedOverlay의 차이 및 사용법
- 지도 상의 주소 데이터 얻기, 주소 데이터 변환하기

안드로이드지도프로그래밍자료얻기


- 파일 용량이 4M정도인데 Google 문서로 열면 조금 로딩 속도가 걸리네요..

- 다운로드 받으실 분들은 Freelec 출판사의 강의실에서 다운로드 받으실 수 있습니다.

2011년 2월 18일 금요일

헥...헥

보통 겨울이 끝날때 쯤이면 한번씩
몸이 쳐지곤 하는데..

이번 겨울은 유난히 춥고, 바빠서 그랬는지..
좀 심하네요,,

게다가 치아도 망가져 있고. 귀도 망가져 있고..

이거 완전히 사고난 중고차 같네요..

2월말까지 해야하 일들이 많네요..

우선 PPT자료 완성해서 출판사에 보내줘야 하고..

공개 강의 녹화해야 하고..

지금 운영하는 고급반도 신경써야 하고.. 지난 기수 프로젝트도 봐야하고...

이래저래 하는 거 없이 치여서 사네요..

2월 지나면 좀 많이 블로깅 할 예정이구요..

현재 강의하는 내용들도 많이 정리해서 올리도록 하겠습니다.

2011년 2월 10일 목요일

clone( ) 방식 - 깊은 복사/ 얕은 복사- deep copy shallow copy

사실 예전의 자바가 속도가 좋지 않았다는 사실은 누구나 공감하는 사실이다. Hotspot VM 이 나온 이유역시 이러한 데에서 비롯되었고..

자바가 느린 이유중에 하나는 역시 객체 생성이다. 메모리에 새로운 공간을 할당하고 거기에 데이터를 넣는 방식의 처리는 OOP에서는 피해갈 수 없지만 또한 이때문에 발생하는 성능의 저하는 어쩔 수 없다.

결국 OOP는 속도를 개선하기 위해서 다양한 방법을 시도했는데.. 대표적인 것들이 바로 pooling, caching기법, clone기법이라고 할 수 있다.

Cloneable 인터페이스를 이용하면 우리는 객체를 손쉽게 복사할 수 있다. 객체 생성을 해서 넣는 과정을 생략하고도 원본에 대한 또 다른 객체를 만들 수 있다. clone의 스펙을 보면 equals()가 반드시 true여야 한다는 보장은 하지 않아도 되기 때문에 복사본 객체가 아니라 데이터의 카피로도 사용할 수 있다는 얘기이다.

Shallow Copy

간단히 말하면 객체를 복사하는데 그 안의 내용물 마저 복사하지는 않는다는 얘기이다.

public class Origin implements Cloneable
{

 private String id;

 private String pw;

 private SubOrigin sub;

 ...

위와 같은 객체를 복사한다고 가정하자.

  Origin origin = new Origin();


  Origin cloneObj = (Origin) origin.clone();


위의 객체를 System.out.println()으로 보면 두 객체는 다른 주소값을 사용한다. 하지만 문제는 SubOrigin 에 있다.

ShallowCopy는 특정 객체를 복사할 때 피상적인 복사만 하게 된다.

ShallowCopy는 복사본 객체안에 있는 sub의 주소값이 원복 객체의 sub의 주소값과 동일하게 나온다.

이렇게 표면만 복사하는 것을 Shallow Copy라 하고, sub와 같은 하위 객체마저도 복사하는 방식을 deep copy라고 한다.


 protected Object clone(){
  try {
   //shallow copy
   //return super.clone();
   Origin cloneObj = (Origin)super.clone();
   cloneObj.setSub((SubOrigin)this.sub.clone());
  
   return cloneObj;
  
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
  return null;
 }


위에 있는 소스를 보면 현재 객체의 복사본을 만든후에 하위 객체의 복사본을 만들어 주입하는 방식으로 작성한다.

2011년 2월 5일 토요일

Android에서 Context의 개념

 사실 Android쪽 프로그램을 만들다 보면 계속해서 이 Context라는 용어에 마주하게 됩니다. Context라는 클래스가 존재하기도 하고, Context라는 용어가 존재하기도 하기 때문에 여러분이 개념을 잡는 수준에 설명해 볼까 합니다.
개인적으로 Android를 공부하기 전에 Web Programming이나 Framework들을 공부할 필요가 있다고 생각하는데, 그 이유 중에 하나가 바로 이 Context라는 개념 때문입니다. 아니면 운영체제를 공부해 보신 분들에게는 Context라는 개념이 그리 낯선 개념은 아닙니다.

 Context를 한국말로 번역하자면 '문맥'이라는 우스운 말이 됩니다만, 디자인 패턴과 같은 곳에서는 '상황, 정황'이라는 말로도 사용됩니다. 하지만 좀 더 쉬운 개념으로 설명하자면 개인적으로 '울타리'라는 표현이 가장 좋다고 생각합니다. 즉 어떤 객체 n개가 동일한 Context에 놓여 있다는 것은 다른 표현으로는 'n개의 객체가 동일한 실행 환경에 있다'는 것을 의미합니다.

 예를 들어 static 변수를 생각해 봅니다.  static 변수(클래스변수)를 사용한다는 것은 현재 JVM내의 모든 객체가 동일한 값을 사용하게 됩니다. 반면에 여러 객체가 두 개의 Context내에 있다는 것을 그림으로 표현하면 아래의 그림처럼 됩니다.



따라서 어떤 객체가 어느 Context에 놓여 있느냐에 따라서 사용하는 값이 달라지게 됩니다.       
 Android의 경우는 거의 모든 객체를 단일한 Singleton객체로 관리합니다. 만일 다른 프로세스나 App에 의해서 특정한 데이터가 계속해서 변경된다면 이 때문에 App이 실행 도중에 원치 않는 결과를 만들어 낼 수 있습니다. 따라서 현재 App이 실행되는 하나의 '울타리'가 필요하게 되는데 이런 존재를 Context라고 합니다. Android에서는 코드를 물려주는 상속을 주로 사용하기 때문에 모든 Activity들은 자신이 어떠한 Context에서 동작해야 하는지를 알 필요가 있습니다.

2011년 2월 1일 화요일

간단한 디자인패턴-Command패턴

음.. 한 6년전에 정리하면서 만든 자료지만..
정리차 올립니다.

Command 패턴
Command 패턴은 인터페이스를 이용해서 내부에서 실행되는 객체가 무언지 모르는 상태에서
동일한 과정으로 메쏘드를 수행하고 싶을 때 사용하는 패턴이다.



다음과 같은 경우를 생각해 보자.
. Escm에서 여러개의 매니저가 존재한다.

. 각 매니저는 자신이 수행하기에 적합한 메쏘드가 있다.
. 매니저에게 동작을 시키고자 할 때 통일성을 갖게 하고자 하고 싶다.
예를 들어 파라미터의 값에 따라 자동적으로 특정 객체를 찾아내는 로직을 분리하고
특정 객체의 메쏘드는 인터페이스를 구현하도록 하면 다음과 같은 장점이 생길 수 있다.
. 어떤 객체이건간에 동일한 메쏘드 호출하면 된다.
. 새로운 객체를 추가할 때에도 인터페이스에 맞게 구현해 주면 된다.
이를 위해서 개발자가 먼저 해야 하는 것은 단 하나이다.

****인터페이스에서 코딩을 시작하라 *****
-----------------------------------------------------
Processable
-----------------------------------------------------
package command;
public interface Processable {

 public Object process()throws Exception;
}
-----------------------------------------------------
DocManager
-----------------------------------------------------
package command;
public class DocManager implements Processable {
 public Object process() throws Exception {
  System.out.println("DocManager의 처리 ");

  return null;
 }
}
-----------------------------------------------------
PodManager
-----------------------------------------------------
package command;
public class PodManager implements Processable {
 public Object process() throws Exception {
  System.out.println("PodManager의 처리 ");
  return null;
 }
}

-----------------------------------------------------
CommandTest
-----------------------------------------------------
package command;
public class CommandTest {

 Processable process = null;
 public static void main(String[] args) {

  CommandTest ct = new CommandTest();
  if(args.length < 1){
   System.out.println("args is null");
   return;
  }
  ct.test(args[0]);
 }
 private void test(String param) {

  Executor executor = new Executor();

  if(param.equals("1") == true ){
   process = new DocManager();
  }else{
   process = new PodManager();
  }

  executor.doJob(process);
 }
}
-----------------------------------------------------
Executor
-----------------------------------------------------
package command;
public class Executor {
 Processable command = null;

 public void doJob(Processable command){
  this.command = command;

  try{
   Object result = command.process();
 
  }catch(Exception e){
   e.printStackTrace();
  }
 }
}
디자인 패턴의 원칙이 '구현이 아니라 구성이다'이라는 원칙대로 인터페이스를 설계하고
인터페이스에 따라서 동작이 가능하게 하는 방식이다.
일반적인 경우에 Command패턴과 Strategy Pattern을 적용해서 많이 사용한다.

2011년 1월 25일 화요일

Java로 Twitter4j를 이용해서 트위터읽기

우선은 코드만 올려두고 나중에 인증과정에 대한 설명이나 실행 방법 설명 추가할 예정


import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;


public class OAuthEx {

 public static void main(String args[]) throws Exception{
   // The factory instance is re-useable and thread safe.
   Twitter twitter = new TwitterFactory().getInstance();
   twitter.setOAuthConsumer("소비키", "소비비밀키");
   RequestToken requestToken = twitter.getOAuthRequestToken();
   AccessToken accessToken = null;
   BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
   while (null == accessToken) {
     System.out.println("Open the following URL and grant access to your account:");
     System.out.println(requestToken.getAuthorizationURL());
     System.out.print("Enter the PIN(if aviailable) or just hit enter.[PIN]:");
     String pin = br.readLine();
     try{
        if(pin.length() > 0){
          accessToken = twitter.getOAuthAccessToken(requestToken, pin);
        }else{
          accessToken = twitter.getOAuthAccessToken();
        }
     } catch (TwitterException te) {
       if(401 == te.getStatusCode()){
         System.out.println("Unable to get the access token.");
       }else{
         te.printStackTrace();
       }
     }
   }
   //persist to the accessToken for future reference.
   storeAccessToken(twitter.verifyCredentials().getId() , accessToken);
   Status status = twitter.updateStatus("아... 이거 때문인가????");
   System.out.println("Successfully updated the status to [" + status.getText() + "].");
 

   System.exit(0);
 }
 private static void storeAccessToken(int useId, AccessToken accessToken){

 System.out.println("useId : " + useId);
 System.out.println("AccessToken : " + accessToken);

 FileOutputStream fos = null;
 ObjectOutputStream oos = null;
 try{
 fos = new FileOutputStream("key.data");
 oos = new ObjectOutputStream(fos);

 oos.writeInt(useId);
 oos.writeObject(accessToken);

 }catch(Exception e){
 e.printStackTrace();
 }finally{

 try {
oos.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

 try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
 }
 }

}

2011년 1월 24일 월요일

초간단 이미지 만들기

JDK1.4에서부터는 공식 API에서 이미지 입출력 기능이 제공되기 시작한다.
따라서 아주 간단한 몇 라인의 코드만으로 이미지를 생성하는 것이
가능한데..

약간만 활용하면 카페나 사이트에서 사용하는 해킹 방지용 문자를 만들때 유용하다.




package com.util;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
public class TempPasswordMaker {

 public static void makeImage(OutputStream out, String msg)throws Exception{
  BufferedImage image = null;
  image = new BufferedImage(300,150, BufferedImage.TYPE_INT_RGB);
  Graphics g = image.createGraphics();
  g.setColor(Color.WHITE);
 
  g.setFont(new Font("Courier",Font.BOLD,22));
  g.fillRect(0, 0, 300, 150);
  g.setColor(Color.black);
  g.drawString(msg, 10, 70);
 
  Iterator imageWriters = ImageIO.getImageWritersBySuffix("jpg");
  ImageWriter imgWriter = null;
 
  while(imageWriters.hasNext()){
   imgWriter = (ImageWriter)imageWriters.next();
   System.out.println("IMGWRITER:"+ imgWriter.toString());
  }
  ImageIO.write(image, "jpg", out);
 }

 public static void main(String[] args)throws Exception{
 
  FileOutputStream fos = new FileOutputStream("aaa.jpg");
  makeImage(fos,"AAAAAAAAAAA");
 
 }
}

2011년 1월 18일 화요일

[도서추천] Writing Effective Use cases


절판되어서 너무나 아쉬웠던 책이 다시 나왔다.
예전에 단순코더에서 개발자의 세계로 나를 이끌어 주었던 책!

중간에 잃어버려서 너무 맘아팠던 책!

프로그래밍이란 하나의 글쓰기이다.
그리고 그 글을 어떻게 써야하는지를 설명해 준 책이다.

단순히 프로그래밍 뿐 만이 아니라
논리적인 업무를 하는 사람이라면 한번쯤 읽어보라고 추천하고 싶은 책이다.

왜 수많은 디자인패턴은 인터페이스로 구현하게 되는가?

아마도 Java를 공부하는데 있어서 가장 험난한 고비는 역시 interface가 아닌가 싶다.
하긴 나도 인터페이스를 10년이 넘은 지금도 '이게 최선입니까? , 확실해요? '라는 질문을 던지고 있으니 초급자들에게는 가장 넘기 어려운 산일 것이다.

상속이라는 것은 초급자들이 좀 쉽게 적응을 할 수 있다는 것은 너무나 명확한 사실인듯하다.

하지만 인터페이스의 경우는 어떤가? 솔직히 전혀 아니올시다..

그럼 여기서 우리가 한번 생각해 봐야하는 것은 '왜 그 수많은 객체지향 디자인 패턴은 상속보다는 인터페이스를 사용하는가?'이다.

우선 인터페이스의 몇 가지 특징을 생각해 보자.

인터페이스는 컴파일러가 인식하는 하나의 코드에 대한 스펙을 의미한다.

즉 어떤 객체이건 간에 특정 메소드를 오버라이드하고 있는 객체라면 컴파일러는 지나칠 수 있다는 뜻이다.
사실 이 특징은 다형성이라는 것이 가지는 장점이라고 생각해도 되고, 상속 역시 다형성의 일부이기 때문에 굳이 인터페이스에 국한된 설명이라고 할 수는 없다.


인터페이스는 온갖 다른 클래스를 하나의 타입과 동일한 메소드의 오버라이드라는 놀라운 효과를 가져온다.
아무리 다른 로직들(메소드들)과 데이터를 가진 객체들이라고 할 지라도 동일한 인터페이스를 구현하므로 인해서 모든 객체들을 하나의 타입으로 아우룰수 있는 최고의 방법이다.
흔히들 Java에서 인터페이스를 'Java의 꽃'이라고 부르는 것도 이러한 이유이다.

켄트 백의 말처럼 인터페이스의 가장 무서운 점은 '인터페이스를 바라보고 코드를 작성할 때에는 실제 인터페이스 너머에는 어떤 객체가 존재하는지 신경쓰지 않아도 된다'는 점이다.

-------------------------------------------------------------------------------
인터페이스가 왜 유연한지 생각해보자면 가장 쉽게 점심메뉴를 생각해 보시라..

'오늘은 카레를 먹을꺼야' VS '오늘은 따끈한 국물요리를 먹을꺼야'

'카레'를 먹는 방식은 클래스와 객체 방식이다. 왜냐하면 이 말을 통해서 우리가 바로 어떤
실체를 떠올릴 수 있기 때문이다.

반면에 '따끈한 국물요리'는 인터페이스 방식이다. 이 말을 통해서는 실체를 떠올릴 수는 없다. 왜냐하면 너무나 많은 국물 요리 후보 객체가 생겨버리기 때문이다.

객체지향이 현실세계를 모델로 한다고 하자. 우리가 점심메뉴는 어떤 방식을 선택하는가?
클래스인가? 아님 인터페이스 인가?

2011년 1월 12일 수요일

상속은 위험하다(1)

자바나 C#등 모든 OOP에서 모든 개발자들이 가장 쉽게 접근하여 사용할 수 있는 개념은 바로 상속이다.

상속은 쉬울뿐 아니라 강력한 기능을 가지고 있다는 데에는 의견이 없다.

하지만 이러한 구현 클래스에 의한 상속은?

-----------------------------------------------------------------------------
public class PClass {
 
 
 private PClass target;
 
 public void setTarget(PClass target){
  this.target = target;
 }
 
 public void doA(){
  target.doA();
 }
}

-----------------------------------------------------------------------------

public class SClass extends PClass {
 
 public void init(){
  super.setTarget(this);
 }
 public void doA(){
  System.out.println("sub....");
  super.doA();
 }
 
 
 public static void main(String[] args) {
  
  SClass s = new SClass();
  s.init();
  s.doA();
  
 }
}
-----------------------------------------------------------------------------

위의 소스를 보자.
물론 예제를 만들기 위한 편협한 소스이지만 분명한 건 컴파일 시에는 위의 코드는 문제가 없다.

만일 당신이 이 소스의 문제가 클래스라고 말한다면 어쩔 수 없다. 반쯤은 동의할 의향이 있기 때문에..
하지만 좀 더 생각해 보면 상속이라는 구조가 성립되지 않는다면 위의 소스는 컴파일 자체가 불가능하다는 사실을 인정하기 바란다.

실행결과는 명확하다. StackOverflow가 발생한다.

이러한 상속을 소위 '깨지기 쉬운 상속'이라고 얘기할 수 있다.


구현 클래스를 이용해서 하는 코딩이라면 극단적으로 위와 같은 방식의 코딩도 가능하다는 얘기다.
따라서 상속을 이용하는 경우에는 하위에서 어떤 방식으로 오버라이딩이 되었는지 모르기 때문에 발생하는 문제가 더 크다는 얘기다.

추상클래스는 객체생성이 안될까?

사람들은 흔히들 추상클래스는 객체 생성이 불가능하다고 알고 있습니다.
하지만 정말 그럴까요?

public  abstract class AAA {

 {
  System.out.println("aaaa");
 }

 private String name;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }


}

우선 부모 클래스  AAA 라고 만들었습니다(네이밍룰은 레드썬!)
자세히 보면 기본블럭(default block) 을 이용해서 객체가 생성되면 자동적으로 aaaa 라는 문자열을 찍도록 해 두었습니다.

자식 클래스는 당연히 AAA클래스를 상속해서 만들었습니다.

public class BBB extends AAA {
 {
  System.out.println("BBB");
 }


 public static void main(String[] args) {

  BBB obj1 = new BBB();
  System.out.println(obj1);

  BBB obj2 = new BBB();
  System.out.println(obj2);

 }
}
별다른 내용은 없고 메인 메소드를 작성해서 객체를 두개 만들어 보았습니다. 다만 객체 생성을 확인하기 위해서 BBB클래스 역시 기본 블럭으로 BBB라는 문자열을 출력하도록 수정했습니다. 이 코드의 컴파일 결과 부터 살펴봅니다.
[parsing started BBB.java]
[parsing completed 32ms]
[search path for source files: .]

역시 BBB.java 파일을 메모리로 파싱합니다.
그런데 BBB클래스는 AAA를 상속하기 때문에 AAA역시 필요하게 됩니다.

[loading .\AAA.java]
[parsing started .\AAA.java]
[parsing completed 0ms]
[loading java\lang\Object.class(java\lang:Object.class)]
[loading java\lang\String.class(java\lang:String.class)]
[checking BBB]
[loading java\lang\System.class(java\lang:System.class)]
[loading java\io\PrintStream.class(java\io:PrintStream.class)]
[loading java\io\FilterOutputStream.class(java\io:FilterOutputStream.class)]
[loading java\io\OutputStream.class(java\io:OutputStream.class)]
[checking AAA]
[wrote .\AAA.class]
[checking BBB]
[wrote BBB.class]
[total 281ms]

이 코드를 가지고 실행하게 될 코드는 어떻게 될까요?
javap 명령을 이용해서 역컴파일 시킨 결과는 다음과 같습니다.


Compiled from "BBB.java"
public class BBB extends AAA
  SourceFile: "BBB.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #8.#17; //  AAA."<init>":()V
const #2 = Field        #18.#19;        //  java/lang/System.out:Ljava/io/PrintS
tream;
const #3 = String       #20;    //  BBB
const #4 = Method       #21.#22;        //  java/io/PrintStream.println:(Ljava/l
ang/String;)V
const #5 = class        #20;    //  BBB
const #6 = Method       #5.#17; //  BBB."<init>":()V
const #7 = Method       #21.#23;        //  java/io/PrintStream.println:(Ljava/l
ang/Object;)V
const #8 = class        #24;    //  AAA
const #9 = Asciz        <init>;
const #10 = Asciz       ()V;
const #11 = Asciz       Code;
const #12 = Asciz       LineNumberTable;
const #13 = Asciz       main;
const #14 = Asciz       ([Ljava/lang/String;)V;
const #15 = Asciz       SourceFile;
const #16 = Asciz       BBB.java;
const #17 = NameAndType #9:#10;//  "<init>":()V
const #18 = class       #25;    //  java/lang/System
const #19 = NameAndType #26:#27;//  out:Ljava/io/PrintStream;
const #20 = Asciz       BBB;
const #21 = class       #28;    //  java/io/PrintStream
const #22 = NameAndType #29:#30;//  println:(Ljava/lang/String;)V
const #23 = NameAndType #29:#31;//  println:(Ljava/lang/Object;)V
const #24 = Asciz       AAA;
const #25 = Asciz       java/lang/System;
const #26 = Asciz       out;
const #27 = Asciz       Ljava/io/PrintStream;;
const #28 = Asciz       java/io/PrintStream;
const #29 = Asciz       println;
const #30 = Asciz       (Ljava/lang/String;)V;
const #31 = Asciz       (Ljava/lang/Object;)V;
{
public BBB();
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method AAA."<init>":()V
   4:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   7:   ldc     #3; //String BBB
   9:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   12:  return
  LineNumberTable:
   line 2: 0
   line 5: 4
   line 6: 12

public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=3, Args_size=1
   0:   new     #5; //class BBB
   3:   dup
   4:   invokespecial   #6; //Method "<init>":()V
   7:   astore_1
   8:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   11:  aload_1
   12:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/Obj
ect;)V
   15:  new     #5; //class BBB
   18:  dup
   19:  invokespecial   #6; //Method "<init>":()V
   22:  astore_2
   23:  getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   26:  aload_2
   27:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/Obj
ect;)V
   30:  return
  LineNumberTable:
   line 12: 0
   line 13: 8
   line 15: 15
   line 16: 23
   line 18: 30

}

코드를 보면 내부적으로  AAA클래스 역시 로딩하는 것을 확인할 수 있습니다.
실행된 결과는 더욱 재밌습니다.

aaaa
BBB
BBB@c17164
aaaa
BBB
BBB@1fb8ee3

객체를 두 개 만들었더니 AAA안의 기본 블럭 역시 두번(즉 AAA클래스의 객체도 두번 만들어진다) 실행되는 것을 볼 수 있습니다.

Java에서 추상 클래스는 문법적으는 객체 생성을 제약하는 것이 맞습니다. 즉 직접 객체를 생성할 수 없도록 하는 장치라는 겁니다.

하지만 상속이라는 것이 실제로 객체를 한번에 부모 클래스까지 만들어 내기 때문에 추상클래스 역시 그 예외가 아니라는 겁니다.

2011년 1월 10일 월요일

java.lang.Object에 대한 단상

Java에서 상속과 관련해서 반드시 언급해야 하는 클래스가 java.lang.Object클래스일 것이다.

초급자의 입장에서는 가장 많이 의문을 가지는 클래스이기도 할 것이다.
어째서 java.lang.Object라는 클래스가 존재하는가?

어째서 java.lang.Object클래스에 몇 가지 메소드들이 정의되어 있는가?

    public native int hashCode();
    public final native Class<?> getClass();
    public boolean equals(Object obj) {
      return (this == obj);
    }

native라는 키워드는 언제 사용되는 것인가?

native 키워드는 JNI를 이용한 통신에 사용되는 키워드라고 생각하면 된다.
JNI를 이용하는 프로그래밍의 순서는 다음과 같다
  • Write Java code
  • Compile Java code
  • Create C header (.h file)
  • Create C stubs file
  • Write C code
  • Create shared code library (or DLL)
  • Run application

native 메소드가 java.lang.Object에 정의되어 있다는 것은 간단하게 말해서
소위 말하는 운영체제와 연결되는 코드를 가지고 있다는 것을 의미한다.

상속에서 가장 상위의 클래스는 java.lang.Object이다. 그렇다면 어떤 객체를 만들때 마다
java.lang.Object 클래스의 객체 역시 만들어 진다는 것을 의미한다.

상속이 가장 많이 사용되는 것은 당연히 원래의 코드를 그대로 물려주는 기능이다.
그렇다면 모든 객체가 운영체제와 통신하는 native 메소드를 물려주었다는 것은 Java에서 만들어진 모든 객체는 직접 운영체제와 통신할 필요가 있다는 것을 의미한다.


운영체제와 직접 통신해야 하는 부분이 무엇인가?
이 질문에 해답은 운영체제의 스레드에 있다.

즉 JVM을 벗어나 운영체제와 통신하는 코드가 있다는 것은 스레드와 같은 이슈의 해결을 위해서는 결과적으로 직접 호출하는 방식을 사용한다는 것을 의미한다.

결국에 JVM에서 객체가 생성되는 것은 순수하게 Java Virtual Machine만을 사용하는 것이 아니라 운영체제의 메모리의 구조도 같이 사용하게 된다는 것을 의미한다.

2011년 1월 7일 금요일

오버라이딩의 실체

동영상 강의 제작하다가 문득 내용이 괜찮을 듯 해서 블로그로 다시 정리합니다. 


public class Book2 {

public void doA(){
System.out.println("book2 class doA method.... ");
}
}

라는 클래스가 있습니다. 
그리고 이것을 상속받는 하위클래스가 있습니다. 


public class Magazine2 extends Book2{

public void doA(){
System.out.println("Magazine2 class doA method.... ");
}
}

물론 이것을 테스트 하는 존재도 만들었습니다. 

public class Magazine2Test {

public static void main(String[] args) {
Book2 obj  = new Magazine2();
obj.doA();
}
}

이 코드를 실행하는 엔진의 해석은 다음과 같습니다. 

public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=2, Args_size=1
   0: new #16; //class Magazine2
   3: dup
   4: invokespecial #18; //Method Magazine2."<init>":()V
   7: astore_1
   8: aload_1
   9: invokevirtual #19; //Method Book2.doA:()V
   12: return
  LineNumberTable: 
   line 6: 0
   line 8: 8
   line 10: 12

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      13      0    args       [Ljava/lang/String;
   8      5      1    obj       LBook2;


}

컴파일러가 체크하는 로직을 보면 상당히 재밌습니다. 

[loading java/lang/Object.class(java/lang:Object.class)]
[loading java/lang/String.class(java/lang:String.class)]
[checking Magazine2Test]
[loading ./Book2.java]
[parsing started ./Book2.java]
[parsing completed 1ms]
[loading ./Magazine2.java]
[parsing started ./Magazine2.java]
[parsing completed 1ms]
[wrote Magazine2Test.class]
[checking Book2]
[loading java/lang/System.class(java/lang:System.class)]
[loading java/io/PrintStream.class(java/io:PrintStream.class)]
[loading java/io/FilterOutputStream.class(java/io:FilterOutputStream.class)]
[loading java/io/OutputStream.class(java/io:OutputStream.class)]
[wrote ./Book2.class]
[checking Magazine2]
[wrote ./Magazine2.class]



컴파일러는 보는 바처럼 Book2클래스부터 메모리로 체크하기 시작합니다. 
즉 변수의 타입에 나온 것을 보고 메모리로 올리는 겁니다. 

클래스라는 것은 컴파일러가 사용하는 정보이기 때문입니다. 

즉 Java가 overriding이라는 기법을 사용하는 것은 컴파일러가 보는 클래스와 실제 실행엔진이 실행하는 클래스의 객체와의 불일치를 가져오게 됩니다. 

음..좀 더 길게 쓰고 싶었는데.. 우선 커피숖에서 원고쓰기는 너무 힘들어요...