Ajax으로 DB 데이터를 받아오면서 계속 글을 올려와야 하는 경우가 있다. 이럴경우 처음엔 계속 그냥 APPEND사용해서 받았는데...화면 깜빡임이 아주 심하다.

 /// 리스트 반복 출력구간  //이때 db on/off버튼도 컨트롤러에서 변경한다.
    
   function printList(){
	    $('tbody').html('');
    	$.ajax({
         url:"/consult/chatSession.kh",
         type:'get',
         //data:requestTime,                   
         dataType:"json",
         success:function(result) {
        	 var count=0;
         	 for (var i in result){
            	   var a='<tr>'+
            			'<td name="info'+i+'" scope="row">'+result[i].titleNo+'</td>'+
                		'<td name="info'+i+'" scope="row">'+result[i].csMemberId+'</td>'+
                        '<td name="info'+i+'" scope="row">'+result[i].csTitle+'</td>'+                            
                        '<td name="info'+i+'" scope="row">'+result[i].csDate+'</td>'+ 
                        '<td name="info'+i+'" scope="row">'+result[i].csResult+'</td>';
			    if(result[i].csResult=='N'){
			    		count+=1;
            			a+='<td><input type="button" onclick="serverchat('+i+');" value="상담시작"></td>';
                }else{
                	a+='<td></td>';
                }
			   
                $('tbody').append(a);
                $('#count').html(count);
              }
                
		  }        	
      })
   };

데이터를 없애고 

$('tbody').html('');
 
다시 넣고.
$('tbody').append(a);
 
컴입장에서는 뭐하는 것이냐..........
회사 기업도 아니고 주는 데이타도 코딱지 만하고 새로 들어올 데이터도 없는데 계속 산을 옮기라고 하는것 같다.....옛날 아저씨들 이해한다...ㅋㅋ
 
그래서 새로 배웠던 데이터를 써먹어 본다.
전에 배웠는데 그냥 듣기만 한거라 다시 떠듬떠듬 검색 했다..
 
var $chatList=$('tbody>tr[data-titleNo="'+ result[i].titleNo +'"]');

https://www.nextree.co.kr/p10155/

 

jQuery: data() 이해와 활용

jQuery를 조금이나마 접해보신 분들은 다양한 방법으로 DOM을 select하거나 이벤트 제어,  Ajax통신 등 jQuery가 지원하는 편리한 기능에 많이 익숙해지셨을 거라 생각됩니다. 이번에 준비한 글은 매우

www.nextree.co.kr

https://developer0513.tistory.com/199

 

jquery data() 함수

jQuery data-xx 속성을 추가해주는 data()함수에 대해 HTML5는 개발자가 DOM 객체에 데이터를 저장해두기 위해 속성을 아무렇게나 정의해서 사용하지 않도록 data-xxx와 같이 data-로 시작하는 속성을 사용

developer0513.tistory.com

이렇게 추가했더니. 깜박거림이 없어졌다...일단 추가한 데이터가 없다.ㅋㅋㅋ

/// 리스트 반복 출력구간  //이때 db on/off버튼도 컨트롤러에서 변경한다.
		var count = 0;
		function printList() {
			//  $('tbody').html('');
			$.ajax({
				url : '/consult/chatSession.kh',
				type : 'get',
				dataType : 'json',
				success : function(result) {
					for ( var i in result) {
						var $chatList = $('tbody>tr[data-titleNo="'
								+ result[i].titleNo + '"]');
						if ($chatList.length < 1) {
							addChatList(i, result[i].titleNo,
									result[i].csMemberId, result[i].csTitle,
									result[i].csDate, result[i].csResult);
						};
					};					
				}
			})
		};
		//리스트 넘겨받아서 출력해준다.
		function addChatList(i, titleNo, csMemberId, csTitle, csDate, csResult) {
			console.log("titleNo : " + titleNo);
			var a = '<tr data-titleNo="'+ titleNo +'">'
					+ '<td name="info'+i+'" scope="row">' + titleNo + '</td>'
					+ '<td name="info'+i+'" scope="row">' + csMemberId
					+ '</td>' + '<td name="info'+i+'" scope="row">' + csTitle
					+ '</td>' + '<td name="info'+i+'" scope="row">' + csDate
					+ '</td>' + '<td name="info'+i+'" scope="row">' + csResult
					+ '</td>';
			if (csResult == 'N') {
				count += 1;
				a += '<td><input type="button" onclick="serverchat(' + i
						+ ');" value="상담시작"></td></tr>';
			} else {
				a += '<td></td></tr>';
			}

			$('tbody').append(a);
			$('#count').html(count);
		}

for문에서 비교해주고 addChatList() 로 넘어간다 화면 깜빡임이 줄어들었다.

TITLENO이 계속 출력되지 않는다. 성공!!

하단거 결국은 다른 페이지에서 똑같은 문제가 발생하였다.

날짜가 너무 길다.

/* '<td><fmt:formatDate value="${'+result[i].putDate+'}" pattern="yyyy년MM월dd일" /></td>'+ */

 

10월 27, 2022 11:41:30 오전 org.apache.catalina.core.ApplicationDispatcher invoke
심각: 서블릿 [jsp]을(를) 위한 Servlet.service() 호출이 예외를 발생시켰습니다.
javax.el.ELException: Cannot convert [+result[i].putDate+] of type [class java.lang.String] to [class java.util.Date]

역시나...안된다......데이터를  json으로 받아와서 json이 String인가....그런가........그런가보다..싶다.

핑퐁게임이 시작되었다..!!!

 

for(int i=0;i<wrList.size();i++) {
				WriterPay wp=wrList.get(i);
				String date=sdf.format(wp.getPutDate());
				try {
					wp.setPutDate(sdf.parse(date));
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				wrList.set(i,wp);

controller에서 다시 날짜 형식을 변경해본다.

뭐지...되긴했는데.....뭔가 망가진 느낌!!!!!!!!!!!!

다시 data와 시키면 원상복구됨.........아무리 뭘해도 원상복구된다.. 된장!

다시 검색과의 씨름.......도대체 변경은 왜이렇게 안되는거야!!!!!!!!

그러던 와중!! 드뎌 발견..

https://jsikim1.tistory.com/196

 

day.js 사용 방법 - JavaScript 날짜 라이브러리

day.js 사용 방법 - JavaScript 날짜 라이브러리 day.js 는 많은 JavaScript 날짜 관련 라이브러리중 가장 가벼운 라이브러리입니다. 업데이트가 중단된 moment.js 보다 약 33배 가벼우며, immutable 한 구조라서.

jsikim1.tistory.com

 

드뎌 이분의 글을 발견하게 되었다.

<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.10.7/dayjs.min.js"></script> 한줄 넣고

var date=dayjs(result[i].putDate,"yyyy-MM-dd HH:mm:ss");
var wrdate=date.format("YYYY.MM.DD. HH:mm:ss");
var a='<tr>'+
 '<td>'+result[i].wrpayNo+'</td>'+
 '<td>'+result[i].memberId+'</td>'+
 '<td>'+result[i].seriesNo+'</td>'+
 '<td>'+result[i].ori_bookNo+'</td>'+
 '<td>'+result[i].changeP+'</td>'+
 '<td>'+result[i].payment+'</td>'+							
 '<td>'+wrdate+'</td>'+
 '<td>'+result[i].bankName+'</td>'+
 '<td>'+result[i].bankNo+'</td>';

변경을 직접 td사이에 넣으려니 에러난다........돌아가자...현명하게....점심시간 다 되어가서 지쳐간다.....ㅋㅋㅋvar로 넣고

돌렸더니.

 

행복해졌다!!!!!!!!!! 이제 밥먹으러 가야것다.

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

그동안 배웠던 챕터를 기준으로 올렸었는데....지금은 그렇게 나누기가 모호해서 프로젝트 부분으로 올려본다.

일을 하다보면 날짜 표기를 내맘대로 해야하는데 그방법을 정리 해본다.

 

List<Consult> conList = cService.nowChatList(titleNo);		
		JSONArray jsonArr = new JSONArray();

		if (!(conList.isEmpty())) {
			for( int i=0; i<conList.size(); i++) { 
				Consult consult = conList.get(i);				
				JSONObject jsonObj = new JSONObject();
				jsonObj.put("consultNo",consult.getConsultNo()); 	
				jsonObj.put("cMemberId",consult.getcMemberId()); 			
				jsonObj.put("cContexts", consult.getcContexts());
				//데이트형 문자열로 바꾸기
				SimpleDateFormat format1=new SimpleDateFormat("HH:mm:ss");
				String cDate="";					 
				cDate=format1.format(consult.getcDate());					
				jsonObj.put("cDate",cDate); 
				jsonArr.add(jsonObj);				
			}

simpleDateFormat....형식으로 바꿔서 ...잘 써먹었으나.....json이아니라 conList인  list에 다시 넣어주려니 

string이라고 빨간 x가 난리다. data로 바꾸면 string으로 바꾸고 string으로 하면 date 안된다고 한다....어쩌라는 것인가!!!!!

아직 지식부족으로 해결이 안되어서 강사님 찬스!!!!!

<fmt:formatDate value="${PeanutPoint.ppDate }" pattern="yyyy년MM월dd일 HH시 mm분 ss초" />

jsp화면에서 이거 한줄 넣으니 간편하게 변경되었다........

오라클에서 입력시 변경하는 것은 앞서서 했는데 그냥 sysdaya 쓰고 jsp에서 하는게 빠르고 간편할것같다....

괜시리 controller에서 바꾸느라 이것도 2시간 걸렸다.

1. html부분 : 구매할 구독권과 포인트를 나눠서 진행

<main>        
        <section>
        <input type="hidden" value="${sessionScope.loginMember}">
			<div id="ssTicket">
				<table>
					<tr>
						<td colspan="3" class="payName">구독권</td>
					</tr>
					<tr>
						<td class="payOne"><label for="seasonticket"><input
								type="radio" id="seasonticket" name="payName" value="10000"></label></td>
						<td class="payTwo" name="seasonticket"><label
							for="seasonticket">한달이용권</label></td>
						<td class="payTwo" value="10000"><label
							for="seasonticket">10,000원</label></td>
					</tr>
				</table>
			</div>

			<div id="peanetCharge">
				<table>			
					<tr>
						<td colspan="3" class="payName">땅콩충전</td>
					</tr>
					<tr>

						<td class="payOne"><label for="peanutpoint100"><input
								type="radio" id="peanutpoint100" name="payName" value="10000"></label></td>
						<td class="payTwo" name="peanetpoint"><label
							for="peanutpoint100">100 땅콩</label></td>
						<td class="payTwo"><label for="peanutpoint100">10,000원</label></td>

					</tr>
					<tr>

						<td class="payOne"><label for="peanutpoint200"><input
								type="radio" id="peanutpoint200" name="payName" value="20000"></label></td>
						<td class="payTwo" name="peanetpoint"><label
							for="peanutpoint200">200 땅콩</label></td>
						<td class="payTwo"><label for="peanutpoint200">20,000원</label></td>

					</tr>
					<tr>
						<td class="payOne"><label for="peanutpoint300"><input
								type="radio" id="peanutpoint300" name="payName" value="30000"></label></td>
						<td class="payTwo" name="peanetpoint"><label
							for="peanutpoint300">300 땅콩</label></td>
						<td class="payTwo"><label for="peanutpoint300">30,000원</label></td>
					</tr>

				</table>

			</div>

			<div id="Confirmation"><button id="ordercheck">주문확정</button></div>
            <div id="history">
                <table>                  
                    <tr>
                        <td colspan="2" class="payName">주문내역</td>
                    </tr>                    
                    <tr>
                        <td class="history1"> 주문번호 :</td>
                        <td class="history2" id="oderno"></td>
                    </tr>
                    <tr>
                        <td class="history1">구매내역 : </td>
                        <td class="history2" id="contents"></td>
                    </tr>
                    <tr>
                        <td class="history1">구매금액 : </td>
                        <td class="history2" id="paymoney" value=""></td>
                    </tr>
              
                </table>     
            
            
                <div id="listCheck">
                    <p>주문내용을 확인하셨습니까? </p>
                    <p>구독권은 구매후 취소불가합니다.</p>
                    <p>땅콩은 구매후 15일 안에 사용하지 않을 경우만 </p>
                    <p>취소가능하며 잔여땅콩은 환불되지 않습니다.</p>
                    <p>위의 내용에 동의하십니까?<input type="checkbox" value="" id="payCheck"></p> 
                </div>
            </div>
	        <div id="pay"><button onclick="requestPay()">결제진행</button></div>
        </section>
    </main>

실수 1: label은 id와 for가 연결되는 것인데 각각 다 걸어야 한다....div 통으로 걸었더니 안먹힌다.

실수2 : radio는 name명이 같이것끼리만 가능하다....name이 다르니깐 둘다 선택된다.

 

여기서 부터 스트립트 단!!!!!

 var orderName='';  //결제 분류명
	    var payMoney='';  // 결제할 값
	    var d=new Date();
	    var dm='';         // 년월일 합칠 변수
	    var orderNo='';   //주문번호 분류명과 날짜 합친것
	    var orderContents='' // 주문내용
	    var merchant_uid=''; //결제 주문번호 넣을 곳
	    var memberId= '${sessionScope.loginMember.memberId}';
	    var mEmail= '${sessionScope.loginMember.mEmail}';  
	    
	    $('#seasonticket').on('click', function(){
	    	if(!${empty sessionScope.lastDate}){
				alert("구독권 만료후 구매 부탁드립니다. 이용해 주셔서 감사합니다.");
				$('#seasonticket').prop("checked",false);
			};
	    })

로그인한 정보 가져 오기....session은 정말 편리한것이네.~~~~~~~ controller에서 안보내져도 읽혀진다....그러라고 만든거니깐...ㅋㅋㅋ

구독권만료기간이 남았는데 또 클릭할것을 대비해서 분류!!!!! 

prop으로 체크 값을 해제하기..........사용자입장에서 생각하면 이것저것 자꾸 떠오르게 된다.

$('#ordercheck').on('click', function(){
	
		    if ($('#history').css('display')=='none'){
			    $('#ordercheck').css('background-color','red');
			    $('#ordercheck').css('color','#fff');
			    $('#ordercheck').html('주문취소');    
			    $('#history').css('display','block');
			    $('#pay').css('display','block');
			    orderList(); 
			    
			  } else{           
			    location.href='/pay/start.kh';
			  }	
				
	})

주문확정을 하게 되면 주문정보를 불러와야하니깐 숨겨놨던 div를 활성화시키는것...

toggle로도 해봤는데 부자연스럽고 의도처럼 먹히지 않고 되다 말다 하는 느낌이다....

확실히....block으로 각각 모두 부여하니깐 의도되로 됨....더 정확한 방법이 있겠지만....오늘은 이만큼만...

선택한 정보를 결제테이블에 db에 저장하여 그값을 가져오기....

function orderList(){
	    
	    orderName=$('input[type="radio"]:checked').parent().parent().next().attr('name');  //결제 분류명
	    payMoney=$('input[type="radio"]:checked').val();  // 결제할 값   	    
	    dm=String(d.getFullYear());
	    dm+=String(d.getMonth()+1);
	    dm+=String(d.getDate());
	    orderNo=orderName+"-"+dm;                        //주문번호 분류명과 날짜 합친것
	    orderContents=$('input[type="radio"]:checked').parent().parent().next().text()+"구매"; // 주문내용
	    
	    $.ajax({
	    	url:'/pay/orderIN.kh',
	    	type:'post',
	    	dataType:'json',
	    	data:{
	    		'orderNo':orderNo,
	    		'memberId':memberId,
	    		'orderContents':orderContents,	    		
	    		'payMoney':payMoney
	    	},
	    	success:function(pay){          //주문번호 받아오기
	    		$('#oderno').html(pay.orderNo);
	    		$('#contents').html(pay.orderContents);
	    		$('#paymoney').html(pay.pay+'원');
	    		$('#paymoney').val(pay.pay);
	    	
	    	},
	    	error : function(data){
	    		console.log(data);
	    		alert('주문실패: 잠시후 다시 부탁드립니다!'+data.statusText);
	    	}
	    })
	    
	}
// 주문 번호 만들어서 보내기
	@ResponseBody
	@RequestMapping(value = "/pay/orderIN.kh", produces = "application/json;charset=utf-8", method=RequestMethod.POST)
	public String orderIn(String orderNo,String payMoney,String memberId, String orderContents) {			 
		Pay pay=new Pay();
		pay.setOrderNo(orderNo);
		pay.setPay(Integer.valueOf(payMoney));
		pay.setMemberId(memberId);		
		pay.setOrderContents(orderContents);
		int result=pService.orderin(pay);
		
	
		if(result>0) {
			pay=pService.orderNoOne(pay);			
			return new Gson().toJson(pay);
			
		}else {
			JSONObject json=new JSONObject();
			json.put("error","error");
			return json.toJSONString();
		}	
		
	}

controller 까지 다녀왔다..... 드뎌 결제 준비 된것이다...

다시보니 새록하다...앞서 올린 headers: { "Content-Type": "application/json" }, 오류.......나의 2시간.!!!!!!

 

	var IMP = window.IMP; 
	IMP.init('------------');
	
	function requestPay() {
		
	if ($('#payCheck').is(':checked')) {
			IMP.request_pay({
				pg : 'html5_inicis',
				pay_method : 'card',
				merchant_uid : merchant_uid, //주문번호 
				name : orderContents, //주문내용
				amount : 1000, //주문금액
				buyer_email : mEmail, // 고객이메일
				buyer_name : memberId, // 고객id
				buyer_tel : '01011112222', //고객연락처
				buyer_addr : '서울특별시 강남구 삼성동', //고객주소
				buyer_postcode : '123-456'
			}, function(rsp) { // callback
				if (rsp.success) { // 결제 성공 시: 결제 승인 또는 가상계좌 발급에 성공한 경우
					// jQuery로 HTTP 요청
					$.ajax({
						url : '/pay/success.kh', // 예: https://www.myservice.com/payments/complete
						type : 'POST',
						// headers: { 'Content-Type': 'application/json' },
						data : {
							'imp_uid' : rsp.imp_uid,//결제번호
							'orderNo' : $('#oderno').html(),
							'memberId' : memberId,
							'pay' : $('#paymoney').val()
						},
						success : function(result) {
							alert('결제 성공');
							location.href = '/';
						},
						error : function(e) {
							console.log(e)
							alert('에러: 관리자에게 문의하세요');
						}
					}).done(function(data) {
						//가맹점 서버 결제 aip성공시 로직
						switch (data.status) {
						case 'vbankIssued':
							// 가상계좌 발급 시 로직
							break;
						case 'success':
							// 결제 성공 시 로직
							break;
						}
					});
				} else {
					alert('결제에 실패하였습니다. 에러 내용: ' + rsp.error_msg);
					location.href = '/pay/start.kh';
				}
			});
			
		} else {
			alert("동의 체크 후 결제 가능합니다.");
		}

ajax으로 db보내는건 내가 하는거고 done까지는 아직 필요 없을것 같아 멈췄다.

마지막 controller에서 결제내용을 db로 보내기 해본다.

	// API결제 성공시 데이터 DB전달
	@ResponseBody
	@RequestMapping(value="/pay/success.kh", method=RequestMethod.POST)
	public String paySussess(String orderNo, String memberId, Integer pay, String imp_uid, Pay payApi) {
		String[] arr = orderNo.split("-");
		String table = arr[0];
		payApi.setImp_uid(imp_uid);
		payApi.setMemberId(memberId);
		payApi.setOrderNo(orderNo);
		int result = pService.orderSuccess(payApi);
		int p_t_input;
		int m_st_YN=1;
		if (result > 0) {
			if (table.equals("seasonticket")) {
				SeasonTicket st = new SeasonTicket();
				st.setOrderNo(orderNo);
				st.setMemberId(memberId);
				p_t_input = pService.seasonticketInput(st);
				m_st_YN = pService.memberStChange(memberId);
				
			} else {
				PeanutPoint pp = new PeanutPoint();
				pp.setMemberId(memberId);
				pp.setOrderNo(orderNo);
				pp.setPeanutPoint(pay / 100);
				p_t_input = pService.peanutTableInput(pp);
			}

		} else {
			return "failure";
		}

		if ((p_t_input > 0) && (m_st_YN > 0) ) {
			return "success";
		} else {
			return "failure";
		}
	}

땅콩포인트는 땅콩으로 

구독권은 구독권으로 나눠서 보내는거.............

매핑 잘 쳐야 한다......오타와의 전쟁.........!!

 

이걸 또 잘 걸러서 member클래스랑 연결하기......

결제api가 쉬운거지.....결제 자체가 쉬운것은 아니다.....

역시나 돈이 연결되었는데 간단할리가 없다......이건 인생진리!!!!

		$.ajax({ /*{} 객체를 의미함 key: value값을 ,로 구분하여 객체의 속성이 만들어짐 */
			url : '/client/start.kh', /* url파일로 접근, 컨트롤러에서 대기중인 url주소 */
			dataType : 'json', /* 검사/net/응답을 보면{'result',true:,'msg':'보낸 메세지...input의 text임'}) 받은걸 자바스크립트가 알아서 변환해준다. */
			type : 'POST', /* 폼에서 메소드형식을 생각하면됨 */
			data : msg, /* 서버로 부터 받은 msg의 val를 메세지변수에 넣음 */
			success : function(result) { /* 이벤트 핸들러 result에 서버가 보낸준 값이 리턴됨. */
				console.log('채팅전송성공:' + result);				
				printer = setInterval(collList, 500);
			},
			error : function(e) {
				alert('error:' + e);
			},
		});

 처음 채팅프로그램 한다고 하고 공부하면서 하나하나 기록한것이다.

ajax이 너무 어려워서 이게 뭔소리가 하며 이것저것 찾아가 보며 한것이다....

그중 기본 양식 쓰는 설명은 역시나 생활코딩이 짱이다!!!!!!!!

여기서 주의점......입력한 대화 내용을 어떻게 불러오나 고민하다가

setInterval로 하면 설정시간 만큼 반복해서 출력한다고 해서...써봤다....

//종료 버튼 누를경우
	function chatfinish() {
		var titleNo= ${param.titleNo};
		if (confirm("정말로 종료하시겠습니까?")) {			
			var csResult=prompt('상담결과를 입력하세요.');		
			$.ajax({
				url : "/consult/finish.kh",
				type : "post",		
				dataType:"json",
				data : {
				 titleNo : titleNo,
				 csResult:csResult				
				},
				success : function(data) {				
						//data = JSON.stringify(data);  //종료시 object에러가 나서 첨부
						if(data.result>0){
							clearInterval(printer);							
							self.close();
							
						}else {							
							alert("종료 되지 않았습니다. 다시 해주세요");		
						
						};
				},					
				error: function(e) {
					alert('error : ' + e.statusText);
				}
			});	
		};

그걸 종료 하는 구문이다........

script 들어오자 마자 var printer=' '; 를 선언하고

반복시킨다음 종료 버튼 누르면 clearInterval()로 종료하는건데.....

이넘이 종료는 되긴하는데 계속 에러가 발생하는 것이다.

object object에러....뭔지도 모르겠는데 계속 난다...찾아지지도 않는다.

DB는 저장되고 종료도 되는데 왜그런거야!!!!!!!!!

 

알고 봤더니 Setinterval로 각각의 출력문이 있는것이였다. 전체 데이터를 가져오는데다가

계속 printer에 덮여지는게 아니고 참조변수니깐 계속 연결되어 쌓이고 있던것이다.

그런데 clearinterval로 일하고 Serinterval은 무시하고 막 지혼자 나간다고 한것이였던 것이다....

일하던 녀석들이 어이가 없어서 욕을 하 는 것 이 였 다 !!!!!!!!!!미안혀라....

이럴경우. 

clearInterval(printer);
setTimeout(function() {
self.close();
}, 50)

종료도 하고.....

if(!printer){
printer = setInterval(collList, 500);
}

앞서서 조건을 달면 될것 같다........

모르면 무식하게 쓴다!!!!!! 공부하자!! 조심하자!!!

 

 

실제로 사용하니  api의 연동은 간단한것은 사실이다.

하지만 이몸은 일단 결제창이 뜨는데 하루~~~~~~

결제창이 뜨고 나서  어떻게 처리 해야하는지 하루......

그걸 다시 DB연동과 화면에 출력하는데 까지 하루.............어쩌구 저쩌구 주말쉬고....

그러다 보니 일주일 후딱이다...

 

그동안 아임포트 결제 진행한것을 정리한다...........ㅠㅠ

 

개발 가이드가 있어서 편하다...... 그대로 따라하면된다.

1. 아임포트 회원가입하기....부터.....그건 알아서들......^^

인터넷 검색했을때 관리자 체험하기가 이 따로 있다고 해서 그런줄 알았떠니....아무리 검색하고 찾아봐도 없다.

이거 하루 걸리고 알아낸 결론이다...미련퉁이!!!!

그런거 없다... 회원가입하고 2차 인증한다.

여기는 2차 인증으로 구글 OTP방식을 쓴다.....처음해본거라....엄청 낯설었다.

 

결제 연동을 하면 그렇게나 검색되던 관리자 체험하기가 나오나 했더니 아니다.

어드민 콘솔이 이거다..........2차로 otp도 관리자 계정이 있으니깐 참고!!

인터넷에서 검색되더 이런 내역이 뜨는데....여기서 할껀 없다....시스템 설정에서 관리자계정 안넘어간다....

주소가 docs.iamport.kr 이다....

가만히 있으면 그냥 클릭해도 나오는데 필요하거나 잘모르면 아무리 해도 안보이는게 이런거다....

설명해주는 페이지 내용이 쬐매 다르다....

난 이페이지가 나으서....ㅁ^^

화면 들어가면 step1, 2, 3, 4까지 하면 왠만 한거 됩니다.

1.2번까지만 해도 결제창...새빨간 창이 뜨1뜨1뜹니다.......감격!!!!!!!!!!정말감격.......하루를 싸이트에다 바치면 이렇게 됩니다.

내가 만든 프로그램은......포인트( 우린 땅콩이라 불렀다) 구매를 위해 결제화면에서 상품 클릭하고

주문번호 만들어서 그걸 결제데이터에 보내 결제 한 다음

결제 데이터를 db로 보내는 것이다.

<main>        
        <section>
        <input type="hidden" value="${sessionScope.loginMember}">
            <div id="ssTicket">
                <table>
                    <tr>
                        <td colspan="3" class="payName">구독권</td>
                    </tr> 
                    <tr>
                        <td class="payOne"><input type="radio" name="seasonticket" value="10000"></td>
                        <td class="payTwo" >한달이용권</td>
                        <td class="payTwo" name="pay" value="10000">10,000원</td>
                    </tr>
              
                </table>
            </div>
            
            <div id="peanetCharge">
                <table>
                    <tr>
                        <td colspan="3" class="payName">땅콩충전</td>
                    </tr>
                    <tr>
                        <td class="payOne"><input type="radio" name="peanetpoint" value="10000"></td>
                        <td class="payTwo" >100 땅콩</td>
                        <td class="payTwo" >10,000원</td>
                    </tr>
                    <tr>
                        <td class="payOne"><input type="radio" name="peanetpoint" value="20000"></td>
                        <td class="payTwo" >200 땅콩</td>
                        <td class="payTwo"> 20,000원</td>
                    </tr>
                    <tr>
                        <td class="payOne"><input type="radio" name="peanetpoint" value="30000"></td>
                        <td class="payTwo" >300 땅콩</td>
                        <td class="payTwo" >30,000원</td>
                    </tr>
              
                </table>             
                
            </div>

            <div id="Confirmation"><button id="ordercheck">주문확정</button></div>
            <div id="history">
                <table>                  
                    <tr>
                        <td colspan="2" class="payName">주문내역</td>
                    </tr>                    
                    <tr>
                        <td class="history1"> 주문번호 :</td>
                        <td class="history2" id="oderno"></td>
                    </tr>
                    <tr>
                        <td class="history1">구매내역 : </td>
                        <td class="history2" id="contents"></td>
                    </tr>
                    <tr>
                        <td class="history1">구매금액 : </td>
                        <td class="history2" id="paymoney" value=""></td>
                    </tr>
              
                </table>     
            
            
                <div id="orderCheck">
                    <p>주문내용을 확인하셨습니까? </p>
                    <p>구독권은 구매후 취소불가합니다.</p>
                    <p>땅콩은 구매후 15일 안에 사용하지 않을 경우만 </p>
                    <p>취소가능하며 잔여땅콩은 환불되지 않습니다.</p>
                    <p>위의 내용에 동의하십니까?<input type="checkbox" value="" id="payCheck"></p> 
                </div>
            </div>
	        <div id="pay"><button onclick="requestPay()">결제진행</button></div>
        </section>
    </main>

요건 jsp화면

<script type="text/javascript">

	    var orderName="";  //결제 분류명
	    var payMoney="";  // 결제할 값
	    var d=new Date();
	    var dm="";         // 년월일 합칠 변수
	    var orderNo="";   //주문번호 분류명과 날짜 합친것
	    var orderContents="" // 주문내용
	    var merchant_uid=""; //결제 주문번호 넣을 곳
	    var memberId= "${sessionScope.loginMember.memberId}";
	    var mEmail= "${sessionScope.loginMember.mEmail}";  
	    
	$("#ordercheck").on("click", function(){
	    if ($("#history").css("display","none")){
		    $("#ordercheck").css("background-color","red");
		    $("#ordercheck").css("color","#fff");
		    $("#ordercheck").html("주문취소");    
		    $("#history").show();
		    $("#pay").show();
		    
		  } else{           
		    $("#ordercheck").css("background-color","#fff9b0");
		    $("#ordercheck").css("color","black");
		    $("#ordercheck").html("주문확정"); 
		    $("#history").hide();   
		    $("#pay").hide();  
		   }
	    
	    orderName=$('input[type="radio"]:checked').attr('name');  //결제 분류명
	    payMoney=$('input[type="radio"]:checked').val();  // 결제할 값   	    
	    dm=String(d.getFullYear());
	    dm+=String(d.getMonth()+1);
	    dm+=String(d.getDate());
	    orderNo=orderName+"-"+dm;                        //주문번호 분류명과 날짜 합친것
	    orderContents=$('input[type="radio"]:checked').parent().next().text()+"구매"; // 주문내용
	    
	    $.ajax({
	    	url:"/pay/orderIN.kh",
	    	type:"post",
	    	dataType:"json",
	    	data:{
	    		"orderNo":orderNo,
	    		"memberId":memberId,
	    		"orderContents":orderContents,	    		
	    		"payMoney":payMoney
	    	},
	    	success:function(pay){          //주문번호 받아오기
	    		$("#oderno").html(pay.orderNo);
	    		$("#contents").html(pay.orderContents);
	    		$("#paymoney").html(pay.pay+"원");
	    		$("#paymoney").val(pay.pay);
	    	
	    	},
	    	error : function(data){
	    		console.log(data);
	    		alert("주문실패: 잠시후 다시 부탁드립니다!"+data.statusText);
	    	}
	    })
	    
	})
	// -------------------------------------*********주문 확정 후 번호 받아 오기
	var IMP = window.IMP; 
	IMP.init("가맹점 식별코드 그대로 넣으면 됩니다.");
	
	function requestPay() {
	    IMP.request_pay({
	        pg : 'html5_inicis',
	        pay_method : 'card',
	        merchant_uid: merchant_uid,   //주문번호 
	        name : orderContents,             //주문내용
	        amount : 1000,                 //주문금액
	        buyer_email :mEmail,       // 고객이메일
	        buyer_name : memberId,         // 고객id
	        buyer_tel : "01011112222",        //고객연락처
	        buyer_addr : '서울특별시 강남구 삼성동', //고객주소
	        buyer_postcode : '123-456'
	    }, function (rsp) { // callback
	        if (rsp.success) {                // 결제 성공 시: 결제 승인 또는 가상계좌 발급에 성공한 경우
	        	// jQuery로 HTTP 요청
	            $.ajax({
	                url: "/pay/success.kh", // 예: 서버로 보내는 url이다....내경우는 controller연결할 url
	                method: "POST",
	                headers: { "Content-Type": "application/json" },
	                data: {
	                    "imp_uid": rsp.imp_uid,//결제번호
	                    "orderNo": $("#oderno").html(),	                    
              		    "memberId":memberId,
              		    "pay": $("#paymoney").val()	                  		  
              		  },              		 
              		 success:function(result){
               		  alert("result");
               		  location.href="/ej.kh";
               		 },
	               	 error:function(e){
	               		 console.log(e)
	           		  alert("에러: 관리자에게 문의하세요");
	           	  	 }
	            }).done(function (data) {  
	            	//가맹점 서버 결제 aip성공시 로직
	            	switch(data.status) {
		            case  "vbankIssued":
		              // 가상계좌 발급 시 로직
		              break;
		            case "success":
		              // 결제 성공 시 로직
		              break;
		          }
	            });
	        } else {
	        	alert("결제에 실패하였습니다. 에러 내용: " +  rsp.error_msg); 
	        	location.href="/pay/start.kh";
	        }
	    });
                          
	}

앞서말한 step1,2,3,4를 합친다음 차이점을 보면 내꺼와 공통것을 알게되니깐....

사이트를 열시미 째려보심 됩니다...^^

여기서 문제점.......

controller에서 json 데이터 값을 못읽는 겁니다...

배우데로 기재했고.....오류날것도 없는데....왜 null이냐고!!!!!!!!!!!!!!!!!!!!!!!!

한시간 이상을 소비해서 JSON.stringity()도 넣보고.

ResponseBody 있나 없나 확인도 해보고....,mapper 어디 잘못됐나....보고 또 보고 해도 안되는것이다.

혹시나 해서 안배웠던 headers: { "Content-Type": "application/json" }, 주석처리 해봣다.......

된다.....이런 쓰...........벌......왜되는지는 모르겠다....

참고로 jsp에서 서버로 데이터를 보낼때 json으로 보내겠다는 것이다......json맞는데...

아무래도 아임포트는 어떤 환경에도 되어야 하니깐 공통적으로 기재해서 그런게 아닌가 싶다.

 headers: { "Content-Type": "application/json" }, :주석처리하고

method : "post"는 type : "post"   ==이건 안해도 post방식으로 받는다..

headers 삭제하니깐 null에서 정상적으로 데이터를 받아왔다....

참고 하시라고 올린다^^

 

 

	// API결제 성공시 데이터 DB전달
	@ResponseBody
	@RequestMapping(value="/pay/success.kh", method=RequestMethod.POST)
	public String paySussess(String orderNo, String memberId, Integer pay,String imp_uid
			,Pay payApi){
		String[] arr=orderNo.split("-");
		String table=arr[0];
		payApi.setImp_uid(imp_uid);
		payApi.setMemberId(memberId);
		payApi.setOrderNo(orderNo);
		int result=pService.orderSuccess(payApi);
		int p_t_input ;
		if(result>0) {
			if(table.equals("seasonticket")) {
					SeasonTicket st=new SeasonTicket();
					st.setOrderNo(orderNo);
					st.setMemberId(memberId);
					
				p_t_input = pService.seasonticketInput(st);
			}else {
				PeanutPoint pp=new PeanutPoint();
				pp.setMemberId(memberId);
				pp.setOrderNo(orderNo);
				pp.setPeanutPoint(pay/100);				
				p_t_input = pService.peanutTableInput(pp);
			}			
			
		}else {
			return "failure";
		}
		
		if(p_t_input>0) {
			return "success";
		}else {
			return "failure";
		}
	}

 

Logging 이란 :  오류에 대해 디버깅 하거나 프로그램 상태를 모니터링 하기위해 필요한 정보를 기록하는 것/ 실행->추적-> 기록(콘솔, 파일, 데이터베이스) 메세지를 작성하는 것

 

설정//pom.xml

	<dependency>
	<groupId>org.bgee.log4jdbc-log4j2</groupId>
	<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
	<version>1.16</version>
	</dependency>

설정2 : root-context.xml

	<!-- Root Context: defines shared resources visible to all other web components -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"> </property>  <!--set메소드 사용시 적었던것   -->
		<property name="url"  			 value="jdbc:oracle:thin:@localhost:1521:xe"></property>
		<property name="username" 		 value="STRING"></property>
		<property name="password" 		 value="STRING"></property>
	</bean>

이랫던 내용을

<!-- Root Context: defines shared resources visible to all other web components -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"> </property>  <!--set메소드 사용시 적었던것   -->
		<property name="url"  			 value="jdbc:log4jdbc:oracle:thin:@localhost:1521:xe"></property>
		<property name="username" 		 value="STRING"></property>
		<property name="password" 		 value="STRING"></property>
	</bean>

변경한다.  ///위에 오라클 빼먹고 갭쳐했다...아래 보면 value값에 oracle 정상 기재되어 있다 주의!!!221109 다시해보려고 했더니.....DB접속이 안되서 봤더니. 저런실수가 있었다........주의!!!!!

 

설정3 : log4파일들

log4jdbc.log4j2.properties파일을 만들고 파일안에

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

붙여 넣는다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<!-- Appenders -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%-5p: %c - %m%n" />
		</layout>
	</appender>
	
	<!-- Application Loggers -->
	<logger name="com.kh.junspring">
		<level value="info" />
	</logger>
	
<!-- 	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.beans">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="info" />
	</logger>

	<logger name="org.springframework.web">
		<level value="info" />
	</logger>
 	
	<!-- SQL Logger part2 -->
	<!--열려 있는 연결 수립 및 해제 이벤트를 기록, 연결문제를 찾아내는데 유요함  -->
	<logger name="jdbc.connection" additivity="false">
		<level value="warn"></level>
		<appender-ref fer="console"></appender-ref>
	</logger>
	
	<!--SQL문고 해당 SQL을 실행시키는 수행된 시간 정보를 포함  -->
	<logger name="jdbc.sqltiming" additivity="false">
		<level value="debug" />
		<appender-ref ref="console" />
	</logger>

	<!--SQL문만 로그를 남김, PreparedStatement일 경우 ?(위치 홀더)값이 완전히  보임 -->
	<logger name="jdbc.sqlonly" additivity="false">
		<level value="warn" />  
		<!-- debug(info_debug까지 다 보임)로 하면 너무 많이 조회됨  warn으로 변경-->
		<appender-ref ref="console" />
	</logger>

	<!--debug였으나 info로 변경  -->
	<logger name="jdbc.audit" additivity="false">
		<level value="info" />
		<appender-ref ref="console" />
	</logger>
	
	<!--ResultSet을 제외한 모든 JDBC호출 정보를 로그로 남김, 로그양이 많고 필요하지 않으면 사용 권장안함  debug였으나 warn로 변경 -->
	<logger name="jdbc.resultset" additivity="false">
		<level value="warn" />
		<appender-ref ref="console" />
	</logger>
	
	<!--SQL결과 조회된 데이터의 table를 그려줌 / debug였으나 info로 변경 -->
	<logger name="jdbc.resultsettable" additivity="false">
		<level value="info" />
		<appender-ref ref="console" />
	</logger>


	<!-- Root Logger -->
	<root>
		<priority value="warn" />
		<appender-ref ref="console" />
	</root>
	
</log4j:configuration>

log4j.xml파일을 수정한다.

오타나면 당근 안됨/ 서버 수정했기때문에 오타나면 연결안됨

저장 후 게시판을 실행시켜 보면

잘된건지 안된건지 모르겠다.....일단 테이블이 나오니깐 된것같기도^^

수정시에는 value값을 변경해주기....난error로 할까나??

'SPRING' 카테고리의 다른 글

LOG를 대체하는 logger  (0) 2022.11.10
JUNIT TEST 2-controller//  (0) 2022.11.10
AOP를 이용한 트랜젝션  (0) 2022.10.11
예외처리 어노테이션  (0) 2022.10.07
AOP 심화 : 메소드 실행시간  (0) 2022.10.07

AOP를 이용한 트랜젝션 처리:한번에 처리 해야할 업무단위.
1.JDBC : Connection 객체/conn.commti(),conn.rollback()-> Service 
2. Mybaits : :SqlSession객체/ session.commit(),session.rollback() ->Service
3. Sprig : TransactionManager / 객체

 

사용위한 설정 : root-context.xml

http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd "

붙인다....추가추가

사용할때 가장 중요한 것이 이부분이다. 여기가 정상기재시 일을 할수 있다...

 

이제 매니저 등록

<!-- 내가 트랜잭션 매니저다 선언 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 트랜재션 XML방식-->
	<tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--매니저가 누구다. -->
		<!-- 하단의 메소드에서 작동해야한다.  -->
		<tx:attributes> 
			<tx:method name="print*" read-only="true"/>
			<tx:method name="register*" rollback-for="Exception"/>
			<tx:method name="modify*" rollback-for="Exception"/>
			<tx:method name="remove*" rollback-for="Exception"/>
		</tx:attributes>
	</tx:advice> 
	<!-- 매니저를 aop에 적용 -->
	<aop:config proxy-target-class="true">
		<aop:pointcut expression="execution(* com.kh.junspring..*Impl.*(..))" id="serviceMethod"/>
		<!--com.kh.junspring패키지의 하위 클래스까지포함해 Impl로 끝나는 클래스의 모든매변변수 메소드에 적용  -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
	</aop:config>
	</beans>

일부러 회원가입할때 한번 더 되도록 에러구문을 만들었다...

오늘 회원가입 엄청했다..일부러 에러 발생시켜 봤는데 에러메세지가 나와도 ROOT파일에 오타가 있다며 회원가입이 된다....그증거들..ㅋㅋㅋ

이후 오타 찾아내서 수정하니깐 에러메시지와함께 회원가입이 되지 않았다...

정리하면 앞서 잘 진행했던것도 EXCEPTION발생이 되면 자동으로 취소시켜 주는 것이다...ROLL-BACK.

'SPRING' 카테고리의 다른 글

JUNIT TEST 2-controller//  (0) 2022.11.10
LOG4j 등록  (0) 2022.10.11
예외처리 어노테이션  (0) 2022.10.07
AOP 심화 : 메소드 실행시간  (0) 2022.10.07
AOP 배우기  (0) 2022.10.07

// Controller 어노테이션이 있는 클래스에 한해서 예외 처리를  진행해주는 어노테이션 

 

@ExceptionHandler({NullPointerException.class, NumberFormatException.class})
public String errorHandler() {
return "redirect:/home.kh";
}

 

그래서 뭐 service나 store 클래스에서 안됨.....

try~catch를 대행하는 내용...

@ExceptionHandler 뒤에 예외처리되는 클래스 내용을 쓰면됩.

	// Controller 어노테이션이 있는 클래스에 한해서 예외 처리를  진행해주는 어노테이션 
	@ExceptionHandler({NullPointerException.class, NumberFormatException.class, SQLException.class})
	public String errorHandler() {
		return "redirect:/home.kh";
	}

'SPRING' 카테고리의 다른 글

LOG4j 등록  (0) 2022.10.11
AOP를 이용한 트랜젝션  (0) 2022.10.11
AOP 심화 : 메소드 실행시간  (0) 2022.10.07
AOP 배우기  (0) 2022.10.07
220916 스프링 게시판_댓글 8-2 댓글리스트출력  (0) 2022.09.16

root-context,xml에 적었던걸 삭제 한다.

어노테이션 @Component와 @Aspect를 붙이고......

Id로 썼던 allPointCut 메소드를 만들어서 @Pointcut태그를 붙인다.

실행해야할 메소드의 어노테이션은 Advice를 정의했던 그 태그로 붙이고 id 메소드 명을 붙여주면 간단히 실행된다.

advice가 around였으니깐 어노테이션만 변경하니 간단히 실행된다.

 

시간 확인하는데 필요한 메소드들...참고만 한다...... 참고로 index.html 에 뭘 넣지는 않았음...

package com.kh.junspring.common;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

public class AroundLog {
	public Object aroundLogs(ProceedingJoinPoint pp) throws Throwable {
		//ProceedingJoinPoint는 JoinPoint를 상속받아 구현된 인터페이스
		//ProceedingJoinPointfmf 사용하는 이유는 advice가 실행되는 시점을 프로그램밍 하기위해
		
		StopWatch stopWatch = new StopWatch();
		//start
		stopWatch.start();
		Object obj=pp.proceed(); //pointCut가 실행되는 시점을 잡는 것임
		//stop 
		stopWatch.stop();
		//메소드가 실행되는 시간 출력
		String methodName=pp.getSignature().getName();
		System.out.println(methodName +"()메소드 수행에 걸린시간 : "
				+ stopWatch.getTotalTimeMillis()+"(ms)");
		return obj;		//aroundLogs는 object을 리턴해야지 된다고 하지만 void도 상관없을듯
	}

}

root-context.xml에 추가

	<!--AOP XML -->
	<bean id="log" class="com.kh.junspring.common.LogAdvice"></bean> 
	<bean id="aroundLog" class="com.kh.junspring.common.AroundLog"></bean>
	<aop:config>
		<aop:pointcut expression="execution(* com.kh.junspring..*Impl.*(..))" id="allPointCut"/>
		<aop:aspect ref="log">
			<aop:before method="printLog" pointcut-ref="allPointCut"/>
			<aop:after method="printAfterLog" pointcut-ref="allPointCut"/>
		</aop:aspect>
		<aop:aspect ref="aroundLog">
			<aop:around method="aroundLogs" pointcut-ref="allPointCut"/>			
		</aop:aspect>
	</aop:config>

 

결과...

"execution(* com.kh.junspring..*Impl.*(..)) 의미는 무엇일까?

*포인트 컷 표현식, 범위로 PointCut를 설정할 수 있음..

* 리턴형, 패키지명, 클래스명, 메소드명(매개변수) 이렇게 표시되는 것이다.

1. * org.kh.com.member.model.service.*.*(..)

모든 리턴형이며 service패키지에 있는 모든 클래스 및 모든 메소드(매개변수 0개이상)

2. * org.kh.com.member.model.service..*.*(..)

service패키지의 하위 패키지까지 포함 모든 클래스 및 모든 메소드(매개변수 0개이상)

3. * org.kh.com.member.model.service.*.*()

service패키지만의 모든 클래스+메소드(매개변수가 없는 것만)

4.* org.kh.com.member.model.service..*.*(*)

모든리턴형의 service 하위패지키까지 모든 클래스+메소드( 매개변수 한개만)

5. * org.kh.com.member.model.service..*.*(Integer,..)

모든리턴형의 service 하위패지키까지 모든 클래스+메소드( 1번째파라미터는 정수+매개 변수0개이상)

6. int org.kh.member.service..*.*(integer, ..)

int리턴형의 service 하위패지키까지 모든 클래스 + 메소드(1번째파라미터는 정수+매개 변수0개이상)

7 int org.kh.member.service..*Impl.*(..)

int리턴형의 service 하위패지키까지 모든 Impl 클래스 +모든메소드(매개변수 0개이상)

 

<aop:befor> 메소드 실행 전에 적용되는 어드바이스를 정의
<aop:around> 메소드 호출 이전, 이후, 예외 발생 등 모든 시점에 적용가능한 어드바이스를 정의
<aop:after> 메소드가 정상적으로 실행되는지 또는 예외를 발생시키는지 여부에 상관없는 어드바이스를 정의
<aop:after-returning> 메소드가 정상적으로 실행된 후에 적용되는 어드바이스를 정의
<aop:after-throwing> 메소드가 예외를 발생시킬때 적용되는 어드바이스를 정의
Try-catch 블록에서 catch블록과 비슷함

<aop:around method="aroundLogs" pointcut-ref="allPointCut"/> 은

어드바이스Advice 를 정의하는 태그이고 이런의미이다.

 

'SPRING' 카테고리의 다른 글

AOP를 이용한 트랜젝션  (0) 2022.10.11
예외처리 어노테이션  (0) 2022.10.07
AOP 배우기  (0) 2022.10.07
220916 스프링 게시판_댓글 8-2 댓글리스트출력  (0) 2022.09.16
220916 스프링 게시판_댓글 8-1 댓글올리기  (1) 2022.09.16

+ Recent posts