연우, 장을 열다.

쿼리문은 아래와 같다. 


select 

a. product_code as product_code, 

c.head_code as head_code,

(

select rt_flag from marketprd_return_list where product_code=a.product_code and a.org_marketprd_idx='test1234'

) as rt_flag,

a.as_info as asDetail,

a.rex_info as rtngExchDetail,

'02' as dlvClf,

c.weight as prdWght,

IF(c.head_code='02' && c.center_code='A','03','01')as orgnTypCd,

a.deli_cost as dlvCst1,

a.prd_noti_type as prd_noti_type

 from

 marketprd_header_list a, 

 marketprd_return_list b,

 product_list c 

 where 

a.org_marketprd_idx = b.org_marketprd_idx 

and a.product_code=c.product_code 

and a.org_marketprd_idx='test1234' 

and b.rt_flag in('0','')  group by a.product_code order by a.order_no; 


에러가 났던 지점은 위 굵은 글씨다. 


select rt_flag from marketprd_return_list where product_code=a.product_code and a.org_marketprd_idx='test1234'


테이블 marketprd_return_list 에서는 컬럼 product_code와 org_marketprd_idx가 존재한다. 하지만 .. 위 쿼리에서는 

에러 " SQL 오류 (1242): Subquery returns more than 1 row " 가 뜬다. 


에러 문구의 뜻은 서브 쿼리에 1개 로우 이상이 조회가 된다는 뜻이다. 


왜인지 고민하다가 찾은 답은 




서브쿼리인 select rt_flag from marketprd_return_list where product_code=a.product_code and a.org_marketprd_idx='test1234' 이곳에서는 a.org_marketprd_idx가 무엇인지 모른다.. 심지어 


A='' 라고 했을때 좌항에 있는 A 컬럼은 테이블 marketprd_return_list 여야 하는데 컬럼을 다른 테이블 a 에서 요청해서 에러가 떴던 부분이다. 


결론적으로는... 




select 

 a. product_code as product_code, 

 c.head_code as head_code,

(

 select rt_flag from marketprd_return_list where product_code=a.product_code and org_marketprd_idx=a.org_marketprd_idx 

) as rt_flag,

 a.as_info as asDetail,

 a.rex_info as rtngExchDetail,

 '02' as dlvClf,

 c.weight as prdWght,

 IF(c.head_code='02' && c.center_code='A','03','01')as orgnTypCd,

 a.deli_cost as dlvCst1,

 a.prd_noti_type as prd_noti_type

 from

 marketprd_header_list a, 

 marketprd_return_list b,

 product_list c 

 where 

 a.org_marketprd_idx = b.org_marketprd_idx 

 and a.product_code=c.product_code 

 and a.org_marketprd_idx='test1234' 

 and b.rt_flag in('0','')    group by a.product_code order by a.order_no; 


위 처럼 조회하니, 원하는 결과값이 도출됐다.




여기서 얻을 결론은 조인하여 서브쿼리를 조회해올때에는 A=B 라고 지칭하면, A에 해당하는 좌항은 서브 쿼리의 테이블이어야 한다는 점이다. 






이건 좀 어이없어서... ㅋㅋㅋ 메모합니다. 



말 그대로입니다. mysql 이 넘이, "나는 컬럼 fail_cnt를 못찾겠어 ㅠㅠ "

라고 하는겁니다. 


 select 

  count(CASE WHEN rt_flag=0 THEN 1 END) as fali_cnt ,

count(CASE WHEN rt_flag=1 THEN 1 END) as succ_cnt ,

count(CASE WHEN rt_flag IS NULL THEN 1 END) as null_cnt 

from tbl_trn_return_list where org_mkprd_idx='MKH20171208114012'


위처럼 조회를 했는데 


왜!! 자꾸 없다고 나오는지, 



실수는 여기부터였습니다. 



위처럼 쿼리를 짜놓고, 


 stmt = conn.prepareStatement(sql);

rs = stmt.executeQuery();

rs.next();

result = rs.getInt("succ_cnt")+"|"+rs.getInt("fial_cnt")+"|"+rs.getInt("null_cnt");


위 처럼.. 불러왔습니다. 



잘 보시면,  rs.getInt(""); 여기 안에 들어가는 컬럼명을 잘못 적은겁니다.


쉬운건데.. 당연히 나와야 하는건데... 안나오니까 참 ㅋㅋㅋㅋ 




여러분들도 혹시 저처럼 이런 에러를 만나면!  아 어디 분명오타가 있구나 생각해보십시오.. :) ㅋㅋㅋ 

이 에러가 발생했던 건 


stmt = conn.prepareStatement(sql);

rs = stmt.executeQuery();


market_name =rs.getString(1);


위처럼 써서 에러가 떴다. 


잘 보시면 rs.next();가 없다. 


에러가 발생한 이유는 결국에  rs.next(); 가 없어서였다! 


따라서, 


if(rs.next()){   market_name =rs.getString(1);  } 


라던가 



while(rs.next()){

.

.

.

.

}


라던가 ! 작성하시고~ 쓰자



오늘 만났던 에러는 바로 

illegal operation on empty result set mysql


상황은 이러했다. 

포문을 돌면서, 조회했던 정보를 예쁘게 잘 INSERT 구문을 만들어서 


SQL문을 실행해주려고 했던 것이었음. 




sql = "select dispNo, dispNm, parentDispNo from tbl_mst_api_category_temp where market_code='1111' and dispNo ='"+forwardDispNo+"' "; //1건 조회stmt=conn.prepareStatement(sql);

rs1=stmt.executeQuery();


이렇게 결과값을 rs1에 담고, 


rs1.next();


를 위처럼 찍은 다음... 


sql = "insert tbl2 values (rs1.getString('aaa'),rs1.getString('bbb'),rs1.getInt('ccc'))";



위처럼.. sql문을 작성한 다음 실행 했더니 만났던 에러였다. 


illegal operation on empty result set mysql


요놈 참 깨름찍한 용어다. 


즉 mysql에서  빈결과( empty result set ) 를  돌려받는 것에 대한  불법적인 작용 정도로 인식하면 될것이다. .. . . .. 




empty result set 에 집중해서 생각해보니까, 

결국에, 조회했던 sql문인 

sql = "select dispNo, dispNm, parentDispNo from tbl_mst_api_category_temp where market_code='1111' and dispNo ='"+forwardDispNo+"' "; //1건 조회stmt

이것에서 조회되는 값이 없다는 의미였다 ㅠㅠ 

조회하는 값이 없어서 문제였던 것이 아니라, 

그 조회한 값이 마치 있다고 가정하고 내가 코딩을 했던 것이다. 


그래서 boolean값을 리턴하는 rs1.next로 실행하고자 하는 부분을 이프문으로 쌌더니, 자알 된다^^ 
if(rs1.next()){
  forwardDispNo =  rs1.getString("parentDispNo");
if(i==tempDepth -1){
  tempPrefix="";
}else{
  tempPrefix=" , ";
}
  tempStr += tempPrefix +" cate_code_" + i + "=" + rs1.getString("dispNo")   ;
  tempStr +=" , cate_name_" + i + "='" + rs1.getString("dispNm")+"'";
}








html 제어하다보면, 


<input type="text" name="test" value="test" disabled>


위 처럼 disabled를 먹일 수 있다. 


disabled를 먹으면, 그 인풋 태그는 클릭하거나 입력 할 수 없는 상태가 되는데,


사용자에게 알려줘야하는 이유로 


따로! style을 먹이고 싶은 게 있다. 



익스플로러 8을 고려하지 않은 css는... 



<style type="text/css">
input[type="text"]:disabled {
background: red;
}
</style>



이렇게 먹이면 된다. 하지만 익스 플로러 8버전을 준수해야한다면... 



<style type="text/css">
input[disabled='disabled']{
background: red;
}
</style>



이렇게 하면 된다. 맘 편하게 !! 



Finally


<style type="text/css">
input[type="text"]:disabled {
background: red;
}

input[disabled='disabled']{
background: red;
}
</style>




위 처럼 선언해주면 익스8버전이든 아니든 disabled attribute에 먹게된다!! 



작년이던가? 

인스타그램의 연동을 진행했었다. 

라테스트 처럼 메인 페이지에 홈페이지 의뢰인의 아이디로 올리던 게시물을 전부 노출하는 개념의 갤러리 리스트였다. 

그러나 갑자기 그쪽 부분에서 사진 출력이 되지 않았다. 


그 이유를 알아봐야 했는데, 

찾아보니까. 2016년 6월 1일인가? 그때 이후로 인스타그램의 정책이 변경됐다고 한다. 

보통 api 연동할떄, 샌드박스 모드 라고 해서 쉽게 테스트 베타버전(?) 정도로 생각하면 된다. 


네이버  api나 페이스북api, 카카오톡 api 모두 각각 명칭을 다르지만 개발중의 베타 버전 오픈은 특정 계정에 한해서 

가능하게한다. 


인스타그램은, 샌드박스 모드라고 말하는데, 이 상태로 개발을 했다가 대게 다른 회사들은 오픈상태로 바꿔야한다. 

근데 인스타는 2016년 6월 이후로 웹사이트만 샌드박스 모드로 사용을 하게 된다. 


20개의 이미지만 불러올 수 있으며, 그 외 다른 방식과 규칙에 어긋나면 

에.러.가.난.다. 

ㅠㅠ



오늘 만났던 오류는 

Uncaught RangeError: Maximum call stack size exceeded

at RegExp.[Symbol.replace] (<anonymous>)

    at String.replace (native)


이런 에러였습니다. 




너무나 슬픕니다. 왜 이런 오류들이 뜨는지에 관련해서 이유를 찾아보니, 


자바스크립트의 함수의 재귀호출로 인해 스택영역의 사이즈가 오바했단 의미였습니다. 


"그럼 대체 왜 자바스크립트의 호출이 재귀로 여러번 혹은 무한 루프가 돌았을까?"  라고 생각해보고, 마우스 우클릭 - 소스 보기를 눌러서 소스코드를 확인해보았습니다. 


위 경우에는 


<head> a source code </head>

<body></body>


이렇게 태그가 마치고 난 다음 다시 


<head>b source code </head>

<body></body>



이렇게 시작하여 헤드 태그가 중복되어 나열되어 있었습니다. 


따라서 현 사이트에 알맞게 하나의 태그만 존재하게끔 제거 하고 나니, 


무한 재귀 호출따위는 사라졌습니다.


Uncaught RangeError: Maximum call stack size exceeded 의 경우에는 무한으로 함수가 호출이 된다는 의미이므로, 


현재 같은 오류가 발생했다면, 어떤 함수가 국소적으로 자꾸 호출되는지 검사해야하며, 동시에 필자처럼 태그들이 정상적으로 선언되어 있는지 


확인해봐야합니다. 




dbcp가 무엇입니까. 


dbcp란 데이터 베이스와 미리 연결된 커넥션을 만들어서 pool에 저장해두었다가 커넥션에게 요청이 있을때, 

pool에서 가져다 쓰고 다시 반환해주는 기법입니다. 


미리 생성해두고 있기때문에 데이터베이스의 부하를 줄이고 , 유동적으로 연결관리가 가능한 아주 유용한 기술입니다. 


학원 선생님께서 알려주신대로 dbcp를 잘 이해했으나, 한가지 셋팅의 부족함때문에 나는 이런 에러를 마주할 수 밖에 없었습니다. 





빨간 줄의 에러 메시지를 보면, 


Cannot create JDBC driver of class '' for connect URL 'null' 로써, 연결할 URL이 없다 라고 뜹니다. 


이 jdbc란 기법을 사용하기 위해선 몇가지 설정이 필요합니다.



JDBC 환경 설정하기 


1.라이브러리 추가

 - 톰캣 6.0 이전 버전  : commons-dbcp.jar, commons-pool.jar, commons-collections.jar

 - 톰캣 6.0 이후 버전  : tomcat-dbcp.jar


이 jar 파일을 찾아서 webContent - Web-INF - lib  내부에 넣어줍니다. 




2.Servers - tomcat - context.xml 파일 설정




위 파일을 열면,


<context></context> 태그 내부에 아래와 같은 설정이 필요합니다. 


<Resource name="jdbc/basicjsp(데이타베이스이름 , JNDI로 호출될 이름을 설정)

auth="Container(Container 거나 Application이거나 - DBCP를 관리할 관리자)

type="javax.sql.DataSource(해당 resource의 return type)"

  factory="org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory(dbcp를 유용하는 관리 클래스)"

  driverClassName="com.mysql.jdbc.Driver(dbcp를 이용하기 위한 드라이버클래스)"

  url="jdbc:mysql://localhost:3306/basicjsp(DB의 접속 유알엘)"

  username="test(db 접속 아이디)" password="test(db 접속 비번)

  maxActive="100" maxIdle="30" maxWait="10000"

  removeAbandoned="true" removeAbandonedTimeout="60"/> 


3.WEB-INF - web.xml 설정


dbcp를 사용하기위해 드라이버를 만졌으니, 이용하는 주체인 웹컨텐트에도 설정을 해줘야 합니다. 


<resource-ref>

<description>My SQL Resource</description><!-- 리소스 설명 -->

<res-ref-name>jdbc/basicjsp</res-ref-name><!-- 리소스 이름(JNDI명) -->

<res-type>javax.sql.DataSource</res-type><!-- 리턴 Type -->

<res-auth>Container</res-auth><!-- 관리 계층 -->

</resource-ref>




위 3가지 절차를 밟아서 dbcp를 사용할 준비를 모두 마쳤습니다. 


이제 코딩을 분석하면 됩니다.




요약정리 


첫째 DBCP를 사용하려면,  톰캣 서버에 맞는 jar파일을 넣습니다. 

둘째 톰캣서버에게 DBCP에 걸맞는 설정을 .<context></context> 내부에 넣어줍니다.

셋재 사용의 주체가 되는 프로젝트에게도 web.xml에 두번째 작업에서 설정한 값을 바라볼 수 있도록 함께 설정해줍니다.



자 이제 그럼 모두 DBCP 풀에 빠져봅시다!