20220906_tue
실습내용
- 학급 및 학생 조회 화면에서 학생이름을 클릭 시, 우측화면에서 학번,학생이름,학급명,국어점수,영어점수,수학점수가 조회되는 테이블을 만들자
- 우선 해당학생 상세정보조회 쿼리문을 만들어야한다.
- DB에서 먼저 쿼리가 실행되는지 만들어 본다.
INSERT INTO SCORE_INFO VALUES (1, 90,80,70, 1);
INSERT INTO SCORE_INFO VALUES (2, 89,87,70, 2);
INSERT INTO SCORE_INFO VALUES (3, 56,80,70, 3);
INSERT INTO SCORE_INFO VALUES (4, 79,85,70, 4);
INSERT INTO SCORE_INFO VALUES (5, 45,50,70, 5);
SELECT SC.STU_NUM
, STU_NAME
, CLASS_NAME
, KOR_SCORE
, ENG_SCORE
, MATH_SCORE
FROM STUDENT_INFO STU , SCORE_INFO SC , CLASS_INFO C
WHERE SC.STU_NUM = STU.STU_NUM
AND STU.CLASS_CODE = C.CLASS_CODE
AND SC.STU_NUM = 10;
- 위의 쿼리 문제점 발생
: 문제점) 학생정보테이블에서는 10번 학생이 있지만,
점수정보테이블에는 5번까지의 학생들만 점수가 있어서
10번 학생은 상세조회가 완전 안된다
SELECT SC.STU_NUM
, STU_NAME
, KOR_SCORE
, ENG_SCORE
, MATH_SCORE
FROM STUDENT_INFO STU , SCORE_INFO SC
WHERE SC.STU_NUM = STU.STU_NUM;
OUTER JOIN
(참고)INNER JOIN (이전 조인방법))
- 해결방법)
: 학생 수만큼 15개가 나오도록 만들어보자- OUTER JOIN
- 점수정보테이블(+)를 조건절에 넣어주면 점수는 나오지않더라도 학생수만큼 나오도록 만들수있다
cf) 오라클 문법 : (+)- NULL값인 점수는 0을 디폴트로 넣어주고싶으면, NVL(컬럼명,0)함수 사용한다
SELECT SC.STU_NUM
, STU_NAME
, NVL(KOR_SCORE,0) KOR_SCORE
, NVL(ENG_SCORE,0) ENG_SCORE
, NVL(MATH_SCORE,0) MATH_SCORE
FROM STUDENT_INFO STU , SCORE_INFO SC
WHERE STU.STU_NUM = SC.STU_NUM(+);
ANSI JOIN
:(디비 종류(오라클..)와 상관없이 모든 디비에서 사용가능한 조인 문법)
-- SELECT,UPDATE,DELETE, INSERT..
-- 1) FROM절에서 , 사용 하지않고 INNER JOIN
-- 2) WHERE절에서 WHERE이 아닌 ON 사용.SELECT SC.STU_NUM , STU_NAME , KOR_SCORE , ENG_SCORE , MATH_SCORE FROM STUDENT_INFO STU INNER JOIN SCORE_INFO SC ON STU.STU_NUM = SC.STU_NUM -- 조인조건을 위해 필요한 조건절 ON//5개 데이터 조회() WHERE STU.STU_NUM = 1;-- 추가적인 조건을 주기위한 조건절// 1개의 해당학생만 조건조회
- LEFT OUTER JOIN
- 왼쪽 학생테이블(15개 데이터)기준 아웃터 조인,부족한 데이터는 NULL값
SELECT SC.STU_NUM , STU_NAME , KOR_SCORE , ENG_SCORE , MATH_SCORE FROM STUDENT_INFO STU LEFT OUTER JOIN SCORE_INFO SC ON STU.STU_NUM = SC.STU_NUM;
- 다시 본론으로 학급명도 넣어서 해보자
-- 학번 1은 조회되지만 10은 조회 안된다
--하지만 점수정보가 없는 학생이더라도 학급명과 함께 점수정보도 조회해보자
-- OUTER JOIN , NVL 사용SELECT STU.STU_NUM , STU_NAME , CLASS_NAME , NVL(KOR_SCORE,0) KOR_SCORE , NVL(ENG_SCORE,0) ENG_SCORE , NVL(MATH_SCORE,0) MATH_SCORE FROM STUDENT_INFO STU , CLASS_INFO C ,SCORE_INFO SC WHERE STU.CLASS_CODE = C.CLASS_CODE AND STU.STU_NUM =SC.STU_NUM(+) AND STU.STU_NUM = 10;
실습내용
- 해당학생 상세정보 조회
- 위 쿼리문 이용해서 학생상세조회 테이블을 띄우고 밑에 '점수등록' 버튼 생성
- 점수등록 버튼 클릭하면 국어,영어,수학 점수를 입력할 수 있도록 점수 영역을 INPUT 태그로 바꾼다.
- 단,INPUT 태그에는 기존의 점수는 그대로 유지가 되어야한다.
- 점수등록이라는 버튼이 '확인'이라는 버튼으로 바뀌어야한다.(여기까지 진도)
(내일 실습 확인)
- 확인 버튼 클릭 시, 점수 정보가 등록 되어야 한다.
- 점수등록(수정)이 완료되면 ALERT으로 '점수변경 성공!'띄운다!
이름을 클릭했을 시, 해당학생 상세정보 조회
:function showDetail(stuNum)
- 테이블 그리기 전에, 먼저 컨트롤러가서 필요한 데이터 가져와야한다. 매개변수 stuNum 이 넘어와서 학생번호 데이터를 받아와야한다.
- ajax사용 이유? 디비작업 조회해야하는데 이전 목록조회한 곳에서 데이터를 가져와서 상세정보 조회를 하는데 별도로 페이지이동 필요없어서!
- 요청경로:컨트롤러 경로
url: '/stu/getDetailInfo'
function showDetail(stuNum) {//매개변수 학생번호 데이터 받아오기
$.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>${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 type="button" value="점수등록">';
str += '</div>';
const detailTd = document.querySelector('#detailTd');
//빈값으로 바꾸고 붙이기를 반복한다
//innerHTML : 선택한 태그에 빈문자를 주면 선택태그 안의 내용(innerHTML) 모두 삭제
detailTd.innerHTML= '';
//insertAdjacentHTML('(위치)', 넣을 문자) : 뒤에 문자열을 어느 위치에 넣을지
detailTd.insertAdjacentHTML('beforeend',str);
},
error: function() {
alert('실패');
}
});
}
@ResponseBody
@PostMapping("/getDetailInfo")
public StuVO getDetailInfo(int stuNum) {
//상세조회(컨트롤러 ->(ajax) js의 success 실행)
return stuService.getDetailStu(stuNum);//쿼리실행 결과 데이터 던져주기
}
- 이름을 클릭하면 상세정보 메소드로 이동
- (td태그 - span태그 차이점)
: 학생이름뿐만 아니라, 옆 공백을 클릭했을 때도 이동하는 td태그와는 달리, span태그안에 속성넣으면 컴팩트하게 글자만 클릭했을 때, 상세조회 된다!
<td th:text="${stuInfo.stuName}"></td>
- 타임리프 온클릭으로 데이터 보내는 방법
타임리프 온틀릭(th:onclick)
을 사용해야 안에있는 문자가 변수로 인식한다!- 학생이름클릭하면 학생번호 데이터를 가져가야하기때문에 상세보기 메소드(js)이동시, 메소드가 필요로하는 매개변수 학생번호를 가져가야한다.
<span th:text="${stuInfo.stuName}" th:onclick="showDetail([[${stuInfo.stuNum}]]);"></span></td>
<!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>
</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>
</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>
- stu_manage.js
: 처음 그려질때는 html 으로 테이블내용이 그려지지만, 이후 부터는 js로 그려진다. 때문에 다시 지워지고 그리는 것이다.
:그래서 학급 선택 조회하면, 자스에서 changeClass 함수가 실행이되는데 이 함수를 통해 데이터 테이블이 그려진다.
: 학급변경 조회했을 때에도 (00반) 실패창이 안뜨고 데이터가 조회되려면, 자스에서 만든 str구문에서 showDetail() 상세보기 메소드 안에 반드시 학생번호 데이터를 넘겨줘야한다!!
str += `<span>${stu.stuName}</span>`;
점수등록 메소드
:function 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값 부여하여 태그 선택(반복문 피하기위해)
//const scoreTr = document.querySelector('#scoreTr');//1개 들고오니까
const scoreTds = document.querySelectorAll('#scoreTr > td');//여러개 td들고오니까 모두 들고올려면 all
//scoreTd 에 input태그 넣기
const str = '<input type="text" style="width:90%;" placeholder="${scoreTd.innerText}">';
//오류발생_여러데이터있을 때 ->scoreTds.innerText='';
//여러개 데이터 뽑을 때-> 반복문사용!!!
for(const scoreTd of scoreTds){scoreTd.innerText='';scoreTd.insertAdjacentHTML("afterbegin",str);//beforeend도 가능}
function setScore(){
const btn = document.querySelector('input[type="button"]');
//만약 버튼이 점수등록 일때는 점수 수정 update 실시
if(btn.vlaue == '점수등록'){//update 수정
const scoreTds = document.querySelectorAll('#scoreTr > td');//여러개 td들고오니까 모두 들고올려면 all
//오류발생_여러데이터있을 때 -> scoreTds.innerText='';
//여러개 데이터 뽑을 때-> 반복문사용!!!
for(const scoreTd of scoreTds){
//1)원래가지고 있는 데이터(점수)를 저장
const score= scoreTd.innerHTML;//td의 내용은 innerHTML
//2)내용지우기(빈값)
scoreTd.innerText= '';
//3)scoreTd 에 input태그 넣기(``사용, 화면에 보이는 값은 value 속성)
const str = `<input type="text" style="width:90%;" value="${score}">`;
//4) 원하는 위치에 str 넣기
scoreTd.insertAdjacentHTML("afterbegin",str);//beforeend도 가능
}
//점수등록 버튼 '확인'으로 바꾸기
btn.value ='확인';//눈에 보이는 속성 value
}
//그렇지 않으면('확인'버튼일 때), 점수등록한다(DB작업)_merge into 사용
else{
}
}
innerText VS innerHTML
- 1. 값 가져오기 (innerText vs innerHTML)
element.innerText;
예시)A
이 속성은 element 안의 text 값들만을 가져옵니다.element.innerHTML;
예시)<div>A</div>
innerText와는 달리 innerHTML은 element 안의 HTML이나 XML을 가져옵니다.
- 2. 값 설정하기 (innerText vs innerHTML)
element.innerText = "<div style='color:red'>A</div>";
예시)<div style='color:red'>A</div>
element.innerText에 html을 포함한 문자열을 입력하면,
html코드가 문자열 그대로 element안에 포함됩니다.element.innerHTML = "<div style='color:red'>A</div>";
예시) A
위와 같이 element.innerHTML 속성에 html코드를 입력하면,
html element가 element안에 포함되게 됩니다.
위 예제에서 'innerHTML()'을 클릭하면,
입력된 html태그가 해석되어 빨간색A 가 나타나는 것을 확인 할 수 있습니다.
참고>https://hianna.tistory.com/480