D+52::스프링부트_ajax_학생관리 페이지만들기(merge into/this/학생점수등록/수정/삭제/트랜잭션처리/@Transactional어노테이션)/파이썬 설치

Am.Vinch·2022년 9월 7일
0

20220907_tue

실습내용

  • '점수등록'버튼 누르면 점수 입력(update)되고, '확인'버튼 누르면 점수등록(insert)시킨다
    : MERGE INTO 쿼리문 사용하기
  • 점수등록되면 '등록성공!!' alert창 띄운다

Ajax

:DB작업은 있지만 페이지이동은 없을 때, ajax사용한다
-> js파일에서 if문의 else문안에 ajax를 넣어서 실행시킨다.

  • stu_manage.js
    • setScore() 기능의 else문안에 ajax문 넣어준다. 이동경로는 updateScore(점수등록)으로 설정해주고 컨트롤러 넘어간다.
    • 그렇지 않으면('점수등록'버튼이 아닌 '확인'버튼일 때), 점수를 등록(insert)한다(DB작업)_merge into 쿼리문 사용
    • ajax문법
      else {
        //ajax start
        $.ajax({
          url: '/stu/updateScore',
          type: 'post',
          data: {},
          success: function(result) {
            alert(111);
          },
          error: function() {
            alert('실패');
          }
        });
        //ajax end
      }

  • student-mapper

MERGE INTO

: 다른 테이블에서 데이터를 비교하여 가져오는 것이 아니라, 직접 값을 넣고자 한다면 DUAL을 사용.
: ON 뒤에 나오는 매칭 조건은 PK를 사용해야 합니다. 그렇지 않으면 입력시에 중복이 발생하여 에러가 날 수 있습니다.

  • ON절에서 점수정보테이블 데이터에 학생번호가 이미 있다면, UPDATE 없으면, INSERT
  • updateScore(점수등록(수정)) : <insert>,<update> 둘 중 하나 상관없다
<insert id="updateScore">
	MERGE INTO SCORE_INFO 
	USING DUAL
	ON (STU_NUM = #{stuNum})
	WHEN MATCHED THEN
		UPDATE
		SET
		 KOR_SCORE = #{korScore}
		, ENG_SCORE = #{engScore}
		, MATH_SCORE = #{mathScore}
		WHERE STU_NUM = #{stuNum} 
	WHEN NOT MATCHED THEN
		INSERT (
			 SCORE_NUM ,KOR_SCORE , ENG_SCORE , MATH_SCORE, STU_NUM 
		 )
		VALUES (
			 (SELECT MAX(NVL(SCORE_NUM,0)) + 1 FROM SCORE_INFO) , #{korScore} , #{engScore} , #{mathScore} ,#{stuNum} 
		 )
</insert>
</mapper> 

  • stuController
    : ajax의 데이터를 받아온다 -> 확인 하는 방법은 toStriong 메소드출력해서 확인해본다

    	@ResponseBody
    	@PostMapping("/updateScore")
    	public int updateScore(StuVO stuVO) {
    		System.out.println(stuVO);
    		return stuService.updateScore(stuVO);
    	}
  • service
    : 학생점수등록 서비스
    int updateScore(StuVO stuVO);

  • serviceImpl
    :학생점수등록 서비스 임플(insert 혹은 update 둘 중하나 상관 없음!)

    	@Override
    	public int updateScore(StuVO stuVO) {
    		return sqlSession.insert("stuMapper.updateScore", stuVO);//update도 상관없다
    	}

  • stu_manage.js
    만약 버튼이 점수등록 일때는 점수 수정 update 실시(update 점수수정)
  • [setScore() 메소드 만들 시, 알아야할 점]
    : 점수등록 누르면 원래 그린 테이블 지우고 다시 그려줘야한다.(이전 목록조회와 같음)
    그래서 원래 점수값 데이터 받는 td태그부분들을 지워주고 다시 그려준다.
    !가장 먼저 태그를 먼저 선택해야한다.(id값부여)
    :선택한 태그의 속성값 데이터 불러오기
    ※주의 korScoreTd 안의 글자와 value속성값은 서로 다르다!
  • korScoreTd.vlaue;//korScoreTd의 value 속성값
  • korScoreTd.innerText;//td태그사이 text값 ${result.korScore}

  • (방법1) 반복이 많아서 다른 방법 사용
    const korScoreTd = document.querySelector('#korScoreTd');
    korScoreTd.innerText='';//원래 있는 국어점수 내용 지우기(빈값주기)
    const engScoreTd = document.querySelector('#engScoreTd');
    engScoreTd.innerText='';//원래 있는 국어점수 내용 지우기(빈값주기)
    const mathScoreTd = document.querySelector('#mathScoreTd');
    mathScoreTd.innerText='';//원래 있는 국어점수 내용 지우기(빈값주기)

  • (방법2) tr에 id값 부여하여 태그 선택(반복문 피하기위해)
    • 1개값만 들고올 때 querySelector 사용
      const scoreTr = document.querySelector('#scoreTr');
    • 여러개 td들고오니까 모두 들고올려면 querySelectorAll 사용
    • 여러개 데이터 뽑을 때-> 반복문사용 !
    • 왜? 여러 데이터있을 때, 바로 객체명 사용시 오류발생한다 ex) scoreTds.innerText='';
      • 1)원래가지고 있는 데이터(점수 score )를 저장:값을 가져올때 innerText
      • 2)내용만 지우기(빈값). 값을 설정 할 때, innerText
      • 3)scoreTd 에 input태그 넣기(`` 백팁 사용, 화면에 보이는 값은 value 속성)
      • 4) 원하는 위치에 str 넣기

  • innerHTML : 태그 사이의 태그값 / innerText : 태그 시작과 끝 사이 문자값 선택
  • afterbegin(선택태그 시작 직후) 대신 beforeend(선택태그 끝나기 직전)도 가능하다.
  • '점수등록' 버튼 '확인'으로 바꾸기 -> 눈에 보이는 속성 value값 '확인'으로 값 설정하기
function setScore() {
	const btn = document.querySelector('input[type="button"]');
	
			if (btn.value == '점수등록') {
				const scoreTds = document.querySelectorAll('#scoreTr > td');
				for (const scoreTd of scoreTds) {
					const score = scoreTd.innerText;//①
					scoreTd.innerText = '';//②
					const str = `<input class="scoreInput" 
                                        type="text" 
                                        style="width:90%;" 
                                        value="${score}">`;//③
					scoreTd.insertAdjacentHTML("afterbegin", str);//④  
				}
				btn.value = '확인';	
			}
  • 그렇지 않으면('확인'버튼일 때), 점수등록한다(DB작업)_merge into 사용
  • 각 점수의 value값 데려오기위해(여러개)
  • 위의 만든 scoreInputs 의 값이 여러개이기때문에 리스트처럼 순서대로 value값 데이터로 뽑는다
else { 
				const scoreInputs = document.querySelectorAll('.scoreInput');
				const stuNumTd = document.querySelector('.stuNumTd');
				//ajax start
				$.ajax({
					url: '/stu/updateScore',
					type: 'post',
					data: {'korScore' : scoreInputs[0].value
                           , 'engScore': scoreInputs[1].value 
                           , 'mathScore':scoreInputs[2].value 
                           ,'stuNum': stuNumTd.innerText },
					success: function(result) {
						alert('점수 등록 완료!!!');
					},
					error: function() {
						alert('실패');
					}
				});
				//ajax end
			}
}

실습 내용

  • 학급 및 학생 목록 조회 좌측화면에서 행마다 '삭제'버튼 이 들어있는 컬럼명을 하나 만든다.
  • 삭제버튼 클릭 시, 해당 학생정보 목록에서 삭제한다.
    (ajax이용해서 페이지이동없이 만든다)
  • 삭제버튼 클릭시, 해당 학생을 목록줄에서 지워준다
  • 삭제완료시 '삭제성공' alert 창을 띄운다
  • onclick 넘어갈때는 매개변수로 데이터 던져줘야한다. 타임리프방식은 [[${}]] 안에 데이터 넣어준다.
    : 단, html과 js와 같은 내용이 담긴 똑같은 td태그 여야한다!
  • stu-mapper

:학생성적 삭제 쿼리문과 학생정보 삭제 쿼리문 각각 만든다.
※(주의) 학생성적 점수를 삭제 하는 쿼리문에서 성적번호 scoreNum 이아니라 학생번호 stuNum 을 where절 조건절로 넣은 이유는 어차피 학생번호와 성적번호가 같은 번호이기 때문이다. 또 나중에 쿼리문 만들 때 매개변수 하나(int stuNum)면 되므로 편하기 때문!

<!-- 목록조회에서 학생정보 삭제 -->
<!-- 학생점수 삭제 -->
<delete id="deleteScore">
	DELETE SCORE_INFO  
	WHERE STU_NUM = #{stuNum} 
	
</delete>
<!-- 학생 정보삭제 -->
<delete id="deleteStu">
	DELETE STUDENT_INFO  
	WHERE STU_NUM = #{stuNum}
</delete>

트랜잭션

@Transactional

: 학생점수 + 학생정보삭제 동시에 처리

  • 트랜잭션 ServiceImpl에서만 사용하여 처리한다(컨트롤러 x)
    @Transactional(rollbackFor = Exception.class)
  • 트랜잭션 예외처리 어노테이션( try-catch문 대체)
  • rollbackFor: 어떤 예외가 발생했을 때 롤백을 할건가?(Exception 상속기능 때문에 어떠한 예외종류에도 다 받는다)
  • sqlSession.commit();->jsp는 자동커밋이 안되지만 스프링은 자동커밋이라 필요없다
  • StuService
    void deleteInfo(int stuNum);
    : 두개의 삭제 쿼리문 하나처럼 만들기

  • StuserviceImpl

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteInfo(int stuNum) {
		//쿼리 2개이상 실행시 무조건 트랜잭션처리한다
			sqlSession.delete("stuMapper.deleteScore", stuNum);
			sqlSession.delete("stuMapper.deleteStu",stuNum);
			
	}
  • controller

    일반적 js 변수명 표기법: 문자열 _ 사용하여 연결 ex) stu_num
    :stu_num(js방식 변수명) 이름으로 넘어온 데이터를 stuNum(자바방식)으로 넣어주겠다.

//삭제 
	@ResponseBody
	@PostMapping("/deleteStu")
	public void deleteStu(@RequestParam(name = "stu_num") int stuNum) {
		stuService.deleteInfo(stuNum);
	}
  • stu_manage.js
    삭제버튼 클릭 시, 목록조회에서 삭제
  • (아래부분) 별도 Ajax 메소드 deleteStuAjax만들어 간편하게 사용한다.
  • deleStu onclick 넘어올때 매개변수 던져준거 받기(id값 부여하면 반복문이기때문에 반복되어서 안됨)
  • selectedTag : 내가 클릭한 그(this) 버튼. html에서 this값으로 내가 선택한 태그를 말함
  • alert(selectedTag.value);//삭제
  • selectedTag.remove();//해당 삭제버튼만 제거

  • ajax를 사용하면 alert창을 띄워도 뒤에 화면이 그려진 상태에서 가능하다!
  • 삭제된 테이블 다시 그려줘야 하는데 방법 2가지가 있다.
  • 방법1_메소드 불러와서 다시 삭제된 테이블 그려준다(ajax필요없는 방법)
    changeClass();
  • 방법2_내가 삭제한 줄만 테이블에서 지워준다.(ajax사용필요 있는 방법)
    : 삭제버튼을 감싸고 있는 tr태그를 선택해 그 것만 지워준다.
    • 방법2_(1)
      selectedTag.closest('tr').remove();
      : closest('tr'): 자신을 감싸고 있는 가장 가까운 tr을 찾아감.
    • 방법2_(2)
      selectedTag.parentElement.parentElement.remove();
function deleteStuInfo(stu_num,selectedTag){
	const ans = confirm("삭제하시겠습니까?");
	if(ans) {//ans == ture 와 같다
		deleteStuAjax(stu_num,selectedTag);
	}
}
//----------------ajax 함수-----------------------//
// 삭제 클릭시, ajax 진행
function deleteStuAjax(stu_num,selectedTag){
		//ajax start
		$.ajax({
			url: '/stu/deleteStu',
			type: 'post',
			data: {'stu_num': stu_num },
			success: function(result) {
					selectedTag.closest('tr').remove();
					selectedTag.parentElement.parentElement.remove();
				alert('삭제완료!');
				},
			error: function() {
				alert('실패');
			}
		});
		//ajax end
  • stu_manage.html

this 속성

: 삭제버튼 클릭시, 해당 학생 목록줄 지워준다

  • this : 내가 하려는 행동 자체의 데이터를 던진다. this 넣어주면, 내가 선택하 태그 값을 뜻함
    <td><input type="button" value="삭제" th:onclick="deleteStuInfo([[${stuInfo.stuNum}]], this)" ></td>
  • js에서 this를 내가 원하는 매개변수명으로 받아온다. (selectedTag)

<실습결과>
프로젝트명 : AjaxTest (총 생성된 파일 내용)

  • stu-mapper
<!-- 점수등록(수정) : insert/update 둘 중 하나 상관없다-->
<!-- ON절에서 점수정보테이블에 학생번호가 이미 있다면,
 UPDATE실시하고 그렇지않다면 INSERT를 실시한다 -->
<insert id="updateScore">
	MERGE INTO SCORE_INFO 
	USING DUAL
	ON (STU_NUM = #{stuNum})
	WHEN MATCHED THEN
		UPDATE
		SET
		 KOR_SCORE = #{korScore}
		, ENG_SCORE = #{engScore}
		, MATH_SCORE = #{mathScore}
		WHERE STU_NUM = #{stuNum} 
	WHEN NOT MATCHED THEN
		INSERT (
			 SCORE_NUM ,KOR_SCORE , ENG_SCORE , MATH_SCORE, STU_NUM 
		 )
		VALUES (
			 (SELECT MAX(NVL(SCORE_NUM,0)) + 1 FROM SCORE_INFO) , #{korScore} , #{engScore} , #{mathScore} ,#{stuNum} 
		 )
</insert>

<!-- 목록조회에서 학생정보 삭제 -->
<!-- 학생점수 삭제 -->
<delete id="deleteScore">
	DELETE SCORE_INFO  
	WHERE STU_NUM = #{stuNum} 
	
</delete>
<!-- 학생 정보삭제 -->
<delete id="deleteStu">
	DELETE STUDENT_INFO  
	WHERE STU_NUM = #{stuNum}
</delete>
  • stu_manage.js
    • closest('tr'): 자신을 감싸고 있는 가장 가까운 tr을 찾아감.
      selectedTag.closest('tr').remove();
//학급이 변경 할 때(셀렉트박스) 진행되는 함수
function changeClass() {
	//classCode 값 가져오기
	// 셀렉트 태그 선택한 것의 value값가져온 것이 classCode 
	const classCode = document.querySelector('select').value;

	//ajax start
	//$로시작하는 것은 제이쿼리 문법이다.
	$.ajax({
		url: '/stu/getStuListAjax', //요청경로
		type: 'post',
		data: { 'classCode': classCode }, //필요한 데이터

		//컨트롤러가 실행하면 ajax실행으로 success구문 실행된다.
		//페이지이동이 없는 대신, 조회된 데이터로 테이블을 다시 작성해야한다(ajax단점)
		success: function(result) {
			//컨트롤러 내용 진행 후, 자동실행 구문

			//테이블을 다시 그려야한다
			//테이블 선택
			//열려있는 html파일(document)에서 테이블 선택
			const stuListTable = document.querySelector('.stuListTable');
			// 테이블에서 티바디 선택
			const tbody = stuListTable.querySelector('tbody');
			// 위 코드와 동일 : const tbody = document.querySelector('.stuListTable > tbody');
			//먼저 tbody를 지워준다
			stuListTable.removeChild(tbody);

			/*			//()test)
						str += 'java';//str = str + 'java'
						str += 'c++';//javac++ (문자열 연결)
						alert(str);
			*/
			//테이블을 다시 그린다		
			//(test)추가할 <tbody>태그 생성
			let str = '';
			str += '<tbody>';

			//학생 수만큼만 tr태그 만든다
			for (const stu of result) {
				//`` 백팁 이용해서 문자열 연결 ver.
				str += '<tr>';
				str += `<td>${stu.stuNum}</td>`;
				str += `<td>`;
				/* 타임리프 여기서는 해석 x th:text 사용불가 -> ${} 사용 */
				//상세보기 메소드 이동시, 학생번호도 넘겨줘야 실패창이 안뜬다!!
				str += `<spantoken interpolation">${stu.stuNum});">${stu.stuName}</span>`;
				str += `</td>`;
				str += `<td>${stu.stuAge}</td>`;
				str += `<td>${stu.className}</td>`;
				/*학급을 바꿔도 삭제버튼나오도록 수정! 삭제 td태그 추가하기*/
				str += `<td><input type="button" value="삭제"token interpolation">${stu.stuNum})"></td>`;
				str += '</tr>';

				//'' 홀따옴표 이용 문자열 연결 ver.
				/* str += '<tr>';
				   str += '<td>'+stu.stuNum+'</td>';//학번
				   str += '<td>'+stu.stuName+'</td>';//이름
				   str += '<td>'+stu.stuAge+'</td>';//나이
				   str += '<td>'+stu.className+'</td>';//학급명
				   str += '</tr>';*/
			}
			str += '</tbody>';

			stuListTable.insertAdjacentHTML('beforeend', str);
		},

		error: function() {
			alert('실패');
		}
	});
	//ajax end
}

// 이름을 클릭했을 시, 학생 상세정보 조회
function showDetail(stuNum) {//매개변수 학생번호 데이터 받아오기
	//테이블 그리기 전에, 먼저 컨트롤러가서 필요한 데이터 가져와야한다.
	//ajax 사용이유? 
	//디비작업 조회해야하는데 이전 목록조회한 곳에서 데이터를 가져와서 상세정보 조회를 하는데 별도로 페이지이동 필요없어서!

	//ajax start
	$.ajax({
		url: '/stu/getDetailInfo',
		type: 'post',
		data: { 'stuNum': stuNum },
		//컨트롤러가 실행하면 ajax실행으로 success구문 실행된다.
		success: function(result) {//리턴값으로 받는 결과 result(상세조회 쿼리결과값)
			let str = '';

			/*html에서 나타날 코드 여기 작성 */
			str += '<table class = "stuDetailTable">';
			str += '<tr>';
			str += '<td>학생번호</td>';
			str += '<td>학생이름</td>';
			str += '<td>소속 학급명</td>';
			str += '</tr>';
			str += '<tr>';
			str += `<td class="stuNumTd">${result.stuNum}</td>`;
			str += `<td>${result.stuName}</td>`;
			str += `<td>${result.className}</td>`;
			str += '</tr>';
			str += '<tr>';
			str += '<td>국어점수</td>';
			str += '<td>영어점수</td>';
			str += '<td>수학점수</td>';
			str += '</tr>';

			/* 점수 데이터 가져오는 이 영역을 input 태그로 바꾸기 */
			str += '<tr id="scoreTr">';
			str += `<td>${result.korScore}</td>`;
			str += `<td>${result.engScore}</td>`;
			str += `<td>${result.mathScore}</td>`;
			str += '</tr>';

			str += '</table>';
			//점수 등록 버튼 생성: 점수등록 클릭시, input태그로 변경
			str += '<div style="text-align=center; margin-top:15px;">';
			str += '<input id="scoreBtn" type="button" value="점수등록">';//id값 부여 점수등록버튼
			str += '</div>';
			const detailTd = document.querySelector('#detailTd');

			//빈값으로 바꾸고 붙이기를 반복한다
			//innerHTML : html파일에 있는 선택한 태그에 빈문자를 주면 선택태그 안의 내용(innerHTML) 모두 삭제
			detailTd.innerHTML = '';
			//insertAdjacentHTML('(위치)', 넣을 문자) : 뒤에 문자열을 어느 위치에 넣을지  	
			detailTd.insertAdjacentHTML('beforeend', str);
		},

		error: function() {
			alert('실패');
		}
	});
	//ajax end

}
//[setScore() 메소드 만들 시, 알아야할 점]
// 점수등록 누르면 원래 그린 테이블 지우고 다시 그려줘야한다.(이전 목록조회와 같음)
// 그래서 원래 점수값 데이터 받는 td태그부분들을 지워주고 다시 그려준다.
//1) 태그를 먼저 선택해야한다.(id값부여)
//선택한 태그의 속성값 데이터 불러오기
//주의) korScoreTd 안의 글자와 value속성값은 서로 다르다!
//- korScoreTd.vlaue;//korScoreTd의 value 속성값			
//- korScoreTd.innerText;//td태그사이 text값 ${result.korScore} 
//방법1) 공부용_ 반복이 많아서 다른 방법 사용				
//const korScoreTd = document.querySelector('#korScoreTd');
//korScoreTd.innerText='';//원래 있는 국어점수 내용	지우기(빈값주기)		
//const engScoreTd = document.querySelector('#engScoreTd');
//engScoreTd.innerText='';//원래 있는 국어점수 내용	지우기(빈값주기)		
//const mathScoreTd = document.querySelector('#mathScoreTd');
//mathScoreTd.innerText='';//원래 있는 국어점수 내용 지우기(빈값주기)		


//점수등록 클릭시, 점수값있는 영역을 input태그로 변경
function setScore() {
	
	const btn = document.querySelector('#scoreBtn');//수정) input태그 여러개이기때문에 id값부여하여 태그선택한다
	
			if (btn.value == '점수등록') {//만약 버튼이 점수등록 일때는 점수 수정 update 실시(update 점수수정)
		
				//방법2) tr에 id값 부여하여 태그 선택(반복문 피하기위해)
				//1개값만 들고올 때 querySelector 사용:const scoreTr = document.querySelector('#scoreTr');
				//여러개 td들고오니까 모두 들고올려면 querySelectorAll 사용
				const scoreTds = document.querySelectorAll('#scoreTr > td');
		
				//여러개 데이터 뽑을 때-> 반복문사용!!!
				//바로 객체명 사용시 오류발생_여러 데이터있을 때 -> scoreTds.innerText=''; 
		
				for (const scoreTd of scoreTds) {
					//1)원래가지고 있는 데이터(점수 score )를 저장:값을 가져올때 innerHTML
					const score = scoreTd.innerText;//td의 내용은 innerHTML
					//2)내용만 지우기(빈값). 값을 설정 할 때, innerText
					scoreTd.innerText = '';
					//3)scoreTd 에 input태그 넣기(``사용, 화면에 보이는 값은 value 속성) 
					// 각 과목별 점수데이터 가져오기 위해 class속성값 설정해준다.
					const str = `<input class="scoreInput" type="text" style="width:90%;" value="${score}">`;
					//4) 원하는 위치에 str 넣기
					scoreTd.insertAdjacentHTML("afterbegin", str);//beforeend도 가능
		
				}
		
				//점수등록 버튼 '확인'으로 바꾸기-> 태그 선택 먼저하기
				//const btn = document.querySelector('input[type="button"]');
				btn.value = '확인';//눈에 보이는 속성 value	
			}
			
			else { //그렇지 않으면('확인'버튼일 때), 점수등록한다(DB작업)_merge into 사용
				const scoreInputs = document.querySelectorAll('.scoreInput');//각 점수의 value값 데려오기위해(여러개)
				const stuNumTd = document.querySelector('.stuNumTd');
				//ajax start
				$.ajax({
					url: '/stu/updateScore',
					type: 'post',
					//위의 만든 scoreInputs 의 값이 여러개이기때문에 리스트처럼 순서대로 value값 데이터로 뽑는다
					data: {'korScore' : scoreInputs[0].value, 'engScore': scoreInputs[1].value , 'mathScore':scoreInputs[2].value ,'stuNum': stuNumTd.innerText },
					success: function(result) {
						alert('점수 등록 완료!!!');
					},
			
					error: function() {
						alert('실패');
					}
				});
				//ajax end
			}

}

//삭제버튼 클릭 시, 목록조회에서 삭제
function deleteStuInfo(stu_num,selectedTag){
	//deleStu onclick넘어올때 매개변수 던져준거 받기(id값 부여하면 반복문이기때문에 반복되어서 안됨)
	//selectedTag : 내가 클릭한 그(this) 버튼. html에서 this값으로 내가 선택한 태그를 말함
	//alert(selectedTag.value);//삭제
	//selectedTag.remove();//해당 삭제버튼만 제거
	
	const ans = confirm("삭제하시겠습니까?");
	
	if(ans) {//ans == ture 와 같다
		deleteStuAjax(stu_num,selectedTag);
	}
}

//----------------ajax 함수-----------------------//
// 삭제 클릭시, ajax 진행
function deleteStuAjax(stu_num,selectedTag){
		//ajax start
		$.ajax({
			url: '/stu/deleteStu',
			type: 'post',
			data: {'stu_num': stu_num },//stu_num:일반적 자바스크립트식 표기방식
			success: function(result) {
				//ajax를 사용하면 alert창을 띄워도 뒤에 화면이 그려진 상태에서 가능하다!
				
				//삭제된 테이블 다시 그려주기
				//방법1_메소드 불러와서 다시 삭제된 테이블 그려준다(ajax필요없는 방법)
				//changeClass();
				
				//방법2_내가 삭제한 줄만 테이블에서 지워준다.(ajax사용)
				//삭제버튼을 감싸고 있는 tr태그를 선택해 그 것만 지워준다.
					//방법2_(1) 
					selectedTag.closest('tr').remove();// closest('tr'): 자신을 감싸고 있는 가장 가까운 tr을 찾아감.
					//방법2_(2)
					selectedTag.parentElement.parentElement.remove();
				
				//알림창띄우기
				alert('삭제완료!');

				},
	
			error: function() {
				alert('실패');
			}
		});
		//ajax end
}

  • stu_manage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">

.layoutTable{
	width: 1000px;
	margin: 0 auto;
	border: 1px solid black;
	border-collapse: collapse;
	margin-top: 40px;
	text-align: center;
	
}
.layoutTable > tbody >tr, .layoutTable > tbody > tr >td{
	border: 1px solid black;
}
.layoutTable > tbody> tr{/* 자식 */
	height: 500px;
}
.layoutTable > tbody> tr > td{/* 자식 */
	vertical-align: top;
	padding: 20px;
}
.stuListTable, .stuDetailTable{
	width: 400px;
	border: 1px solid gray;
	border-collapse: collapse;
	margin: 0 auto;
	text-align: center;
}
.stuListTable tr,.stuListTable td{/* 자손 */
	border: 1px solid gray;
}
.stuDetailTable tr,.stuDetailTable td{/* 자손 */
	border: 1px solid gray;
}
span:hover {
	cursor: pointer;/* 손가락모양으로 커서변경 */
}
</style>
</head>

<body>

<table class="layoutTable">
	<colgroup>
			<col width="50%">
			<col width="50%">
	</colgroup>
	<tr>
		<td>
			<div style="margin-bottom: 10px;" align="center">
				<!-- onchange : select 값이 바뀔 때마다 "" 실행 -->
				<select onchange="changeClass()">
					<option value="0">전체</option>
					<option th:each="classInfo : ${classList}" th:text="${classInfo.className}" th:value="${classInfo.classCode}"></option>
				</select>
			</div>
		
		<div>
		<!-- 화면이 뜨자마자 이 테이블은 없다. 이름을 클릭해야만 화면에 보인다 -->
			<table class="stuListTable">
				<thead>
					<tr>
						<td>학번</td>				
						<td>이름</td>				
						<td>나이</td>				
						<td>학급명</td>	
						<!-- 삭제버튼 추가 학급별조회하면 사라지기때문에 html이아니라 js에 추가해야한다!!-->
						<td id="deleteTd"></td>	
					</tr>
				</thead>
				
				<tbody>
					<th:block th:each="stuInfo : ${stuList}" >
						<tr>
							<td th:text="${stuInfo.stuNum}" th:value="${stuInfo.stuNum}" ></td>				
							
							<!-- 이름을 클릭하면 상세정보 메소드로 이동 -->
							<!-- td태그 안에 말고 span태그안에 속성넣으면 글자만 클릭해야 상세조회 된다!(차이점) -->
							<!-- <td th:text="${stuInfo.stuName}"></td> -->
							
							<!-- 타임리프 온클릭으로 데이터 보내는 방법 -->
							<td><!-- 타임리프 온틀릭(th:onclick)을 사용해야 안에있는 문자가 변수로 인식한다! -->
								<!-- 학생이름클릭하면 학생번호 데이터를 가져가야하기때문에!! -->
								<span th:text="${stuInfo.stuName}" 
								th:onclick="showDetail([[${stuInfo.stuNum}]]);"></span>
							</td>
																						
							<td th:text="${stuInfo.stuAge}" ></td>				
							<td th:text="${stuInfo.ClassName}" ></td>		
							<!--  삭제버튼 클릭시, 해당 학생 목록줄 지워준다  
							this : 내가 하려는 행동 자체의 데이터를 던진다. this 넣어주면, 내가 선택하 태그 값을 뜻함-->
							<td><input type="button" value="삭제" th:onclick="deleteStuInfo([[${stuInfo.stuNum}]], this)" ></td>		
						</tr>
					</th:block>
				</tbody>
				
			</table>
		</div>
		</td>
		<!-- 우측화면 상세정보 테이블 js에서 메소드 실행-->
			<td id="detailTd">
				<!--상세정보 테이블 들어가는 곳 -->	
			</td>
		</tr>
</table>

<!-- jquery 문법 로딩 -->
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<!-- 자바스크립트 파일과연결 -->
<script type="text/javascript" th:src="@{/stu_manage.js}"></script>
</body>
</html>
  • StuController
package kh.study.ajax.controller;

import java.util.List;

import javax.annotation.Resource;

import org.apache.ibatis.jdbc.SQL;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;


import kh.study.ajax.service.StuService;
import kh.study.ajax.vo.ClassVO;
import kh.study.ajax.vo.ScoreVO;
import kh.study.ajax.vo.StuVO;

@Controller
@RequestMapping("/stu")
public class StuController {
	//service 객체를 만들어줘야 안에 메소드사용가능!
	
	@Resource(name = "stuService")//@Service("/stuService") 인터페이스와 연결
	private StuService stuService;
	
	//학급 및 학생 목록 조회
	@RequestMapping("/list")
	public String selectStuList(Model model) {
		model.addAttribute("classList",stuService.getClassList());
		model.addAttribute("stuList",stuService.getStuList(0));//매개변수 수정

		return "stu_manage";//페이지이동
	}
	//셀렉트박스 학급별 조회
	@ResponseBody//ajax가 진행되니 페이지이동하지말라는 어노테이션
	@PostMapping("/getStuListAjax")
	public List<StuVO> getStuListAjax(int classCode) {// ajax를 사용하면 무조건 string(페이지명_문자)이 아니다 -> 모르니까 void
		//선택된 학급의 학생 목록 조회(List<StuVO> list 자료형 선언하고 리턴해준다!)
		List<StuVO> list = stuService.getStuList(classCode);
		return list;//*자료형! 객체 데이터 보내기
		
	}
	//학생상세조회
	@ResponseBody
	@PostMapping("/getDetailInfo")
	public StuVO getDetailInfo(int stuNum) {
		//상세조회(컨트롤러 ->(ajax) js의 success 실행)
		return stuService.getDetailStu(stuNum);//쿼리실행 결과 데이터 던져주기
		
	}

	//학생점수 등록(수정)
	@ResponseBody
	@PostMapping("/updateScore")
	public int updateScore(StuVO stuVO) {
		
		//ajax의 데이터를 받아온다 -> 확인 하는 방법은 toStriong 메소드출력해서 확인해본다
		System.out.println(stuVO);
		
		return stuService.updateScore(stuVO);
	}
	
	//삭제 
	@ResponseBody
	@PostMapping("/deleteStu")
	public void deleteStu(@RequestParam(name = "stu_num") int stuNum) {//stu_num(js방식 변수명) 이름으로 넘어온 데이터를 stuNum(자바방식)으로 넣어주겠다.
		//학생점수+학생정보 삭제
		stuService.deleteInfo(stuNum);
	}
	///////연습용 메소드 /////////////
	@GetMapping("/dn")//localhost:8081/stu/dn
	public String deleteNode() {
		return "delete_node";//페이지이동
		
	}
	
}
  • stuService
package kh.study.ajax.service;

import java.util.List;

import kh.study.ajax.vo.ClassVO;
import kh.study.ajax.vo.ScoreVO;
import kh.study.ajax.vo.StuVO;

public interface StuService {
	//학급목록 조회
	List<ClassVO> getClassList();
	List<StuVO> getStuList(int classCode);//매개변수 수정
	StuVO getDetailStu(int stuNum);
	int updateScore(StuVO stuVO);
	void deleteInfo(int stuNum);//두개의 삭제 쿼리문 하나처럼 만들기(트랜잭션필요)
}
  • stuServiceImpl
package kh.study.ajax.service;

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import kh.study.ajax.vo.ClassVO;
import kh.study.ajax.vo.ScoreVO;
import kh.study.ajax.vo.StuVO;

@Service("stuService")//컨트롤러연결
public class StuServiceImpl implements StuService {

	@Autowired//어노테이션으로 객체생성
	private SqlSessionTemplate sqlSession;

	//학급목록조회
	@Override
	public List<ClassVO> getClassList() {
		return sqlSession.selectList("stuMapper.getClassList");
	}
	
	//학생목록조회
	@Override
	public List<StuVO> getStuList(int classCode) {//매개변수 수정
		return sqlSession.selectList("stuMapper.getStuList",classCode);
	}
	//학생상세정보조회
	@Override
	public StuVO getDetailStu(int stuNum) {
		return sqlSession.selectOne("stuMapper.getDetailStu",stuNum);
	}
	//학생등록(수정)
	@Override
	public int updateScore(StuVO stuVO) {
		return sqlSession.insert("stuMapper.updateScore", stuVO);//update도 상관없다
	}
	//학생점수+ 학생정보삭제
	@Override
	@Transactional(rollbackFor = Exception.class)//서비스임플에서만 사용가능하고,컨트롤러 불가능!
	//트랜잭션 예외처리 어노테이션( try-catch문 대체)
	//rollbackFor: 어떤 예외가 발생했을 때 롤백을 할건가?(Exception 상속기능 때문에 어떠한 예외종류에도 다 받는다)
	public void deleteInfo(int stuNum) {
		//쿼리 2개이상 실행시 무조건 트랜잭션처리한다
			sqlSession.delete("stuMapper.deleteScore", stuNum);
			sqlSession.delete("stuMapper.deleteStu",stuNum);
			//sqlSession.commit();->jsp는 자동커밋이 안되지만 스프링은 자동커밋이라 필요없다
	}
}
  • 첫 html 페이지: 최초의 경로(http://localhost:8081/stu/list)
  • 삭제 버튼 클릭 시,'삭제하시겠습니까?' 확인창 띄우기
  • 확인 클릭시, '삭제완료'alert
  • 삭제완료 후 결과(해당 행 삭제)
  • 다른 학급변경 조회 시에도 해당학생 삭제되며 삭제버튼도 화면에 나타난다.

파이썬(Python) 설치

  • 개발환경세팅
    java : jdk(필수) - eclipse,inteliJ(선택 :SW tool)
    Python : python(필수) - pycharm...(선택 :SW tool)
  • 웹 환경 개발(구글_colab)
    : 별도로 위의 프로그램 다운로드 설치 필요없이 구글에서 제공하는 웹사이트로 개발 가능하다
  1. 구글 로그인
  2. 구글 드라이브 이동
  3. 구글드라이브 왼쪽 화면 '새로만들기' - '더보기' -'연결할 앱 더보기'
  4. 검색창에 'colaboratory' 설치
  5. 설치완료시, 바로가기 생성
profile
Dev.Vinch

0개의 댓글