링크 : 프로젝트파일 보기(GitHub)
콘서트에 대한 정보를 제공하고 콘서트 예약까지 완료할 수 있는 웹페이지를 제작한다.
1. Concert - Singer
특정 Concert에 참가하는 Singer는 0명, 1명, 여러명일 수 있다. 또한 특정 Singer가 참가하는 Concert는 0개, 1개, 여러개일 수 있다. 즉, Concert와 Singer는 **다대다(N:M) 관계**고 **Optional**이다(특정 Concert에 참가하는 Singer가 한명도 없을 수 있고, 특정 Singer가 참여하는 Concert가 한개도 없을 수 있다는 뜻). 그래서 위와 같이 Concert와 Singer테이블 사이에 Concert_Singer테이블을 추가로 생성해 **1:N 관계**로 다대다 관계를 표현했다. 특정 Concert_Singer은 무조건 Concert와 Singer에 대한 내용을 가져야 하므로 Concert와 Singer가 **Mandatory**하고, Concert_Singer에는 특정Concert나 특정Singer에 대한 정보가 아예 없거나 1개, 여러개일 수 있기 때문에 **Optional**하다.
기본적으로 w3schools에서 제공하는 무료 템플릿을 이용했다. 'concert/src/main/webapp/tryw3css_templates_band.htm'이 최종적인 웹사이트다.
참조https://www.w3schools.com/w3css/w3css_templates.asp
비동기 기술을 이용해 콘서트에 참여하는 가수들의 정보를 웹페이지 중간에 계속해서 불러오게 설정했다. 자바스크립트로 처리한 내용은 다음과 같다.
var count = 0;
var jsonConcert = null;
var concertCount = null;
var jsonSinger = null;
function loadAllSinger() {
console.log("카운트 : "+count);
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
if (xhttp.status == 200) {
if (count == 0) {
console.log("0번");
let res_data = this.responseText;
console.log(res_data);
jsonConcert = JSON.parse(res_data);
concertCount = Object.keys(jsonConcert).length;
let imageBox = document
.getElementById("showAllSinger");
imageBox.innerHTML = "<h2>콘서트 라인업이 궁금하신가요?</h2><br>";
} else if (count == 1) {
console.log("1번");
let res_data = this.responseText;
jsonSinger = JSON.parse(res_data);
let imageBox = document
.getElementById("showAllSinger");
imageBox.innerHTML = "<h2>전체가수</h2><br>";
for (i in jsonSinger) {
console.log("확인용@");
let singerName = jsonSinger[i];
imageBox.innerHTML += `<div class="w3-third"><p>${singerName}</p><img src="images/가수${i}_${singerName}.jpg" class="w3-round w3-margin-bottom" alt="Random Name" style="width:30%"></div>`;
}
} else if (count <= concertCount + 1) {
console.log("0,1번아님");
let res_data = this.responseText;
let imageBox = document.getElementById("showAllSinger");
let num = count-1;
imageBox.innerHTML = `<h2>콘서트${num} 가수</h2><br>`;
console.log(res_data);
for (i in res_data){
console.log("확인용%");
let singerId = res_data[i];
console.log(singerId);
let singerName = jsonSinger[singerId];
imageBox.innerHTML += `<div class="w3-third"><p>${singerName}</p><img src="images/가수${singerId}_${singerName}.jpg" class="w3-round w3-margin-bottom" alt="Random Name" style="width:30%"></div>`;
}
}
count += 1;
if (count >= concertCount + 2) {
count = 0;
}
}else{
console.log("통신대기중..");
}
}
///////////////////////////////////////////////////////////////
if (count == 0) {
xhttp.open("get", "concert?command=getAllConcert", true);
xhttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded;charset=UTF-8");
xhttp.send();
} else if (count == 1) {
xhttp.open("get", "concert?command=getAllSingers", true);
xhttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded;charset=UTF-8");
xhttp.send();
} else if (count <= concertCount + 1) {
xhttp.open("get", `concert?command=getSingersByConcert&data=${count}`,
true);
xhttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded;charset=UTF-8");
xhttp.send();
}
}
let interval = setInterval(loadAllSinger, 3000);
큰 틀로 설명하면 웹페이지 중간부분에 콘서트에 참여하는 가수에 대한 정보를 불러오는 loadAllSinger()함수를 만들고, 이를 setInterval함수로 설정 시간마다 연속적으로 호출하는 방식이다. 위 자바스크립트를 뜯어보면서 동작방식을 알아본다.
위 변수를 통해 전체적인 흐름을 유추할 수 있다.
count 0(총 콘서트 개수와 종류 불러오기) ==> count 1(총 가수 정보 불러오기) ==> count 2,3,4 ... 총 콘서트 개수만큼 count 진행(해당 콘서트에 참여하는 가수 정보 화면에 출력)
function loadAllSinger() {
console.log("카운트 : "+count);
const xhttp = new XMLHttpRequest();
// (중략)
if (count == 0) {
xhttp.open("get", "concert?command=getAllConcert", true);
xhttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded;charset=UTF-8");
xhttp.send();
} else if (count == 1) {
xhttp.open("get", "concert?command=getAllSingers", true);
xhttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded;charset=UTF-8");
xhttp.send();
} else if (count <= concertCount + 1) {
xhttp.open("get", `concert?command=getSingersByConcert&data=${count}`,
true);
xhttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded;charset=UTF-8");
xhttp.send();
}
}
가운데 xhttp.onload = function() 를 중략한 이유는 이 코드는 XMLHttpRequest객체가 서버와의 통신이 마치고 돌아온 경우 실행되는 코드이기 때문이다.
위 내용을 보면 loadAllSinger함수는 실행되면 카운트정보를 콘솔에 출력한 뒤, XMLHttpRequest객체 xhttp를 생성한다. 그리고 count가 0일 때, 1일 때, 0도 1도 아니며 concertCount + 1 보다 작거나 같을 때로 나뉘어 다른 명령을 서버로 전송한다. 이때 xhttp.open함수로 각 경우에 따라 command에 대한 값으로 다른 값을 넣어 서버로 보내는 모습을 확인할 수 있다. count가 0일 때는 'getAllconcert', count가 1일 때는 'getAllSingers', 나머지 경우에는 'getSingersByConcert' 그리고 이 경우에는 data에 대한 값으로 count값도 넣어 보냄을 확인할 수 있다. 서버에서는 해당 command값에 맞춰 작업을 해주는데 간단하게 'getAllConcert'를 command값으로 받았을 때, 'getSingersByConcert'를 command값으로 받았을 때 서버에서 작동하는 코드를 확인해본다.
// 'getAllConcert'를 command값으로 받았을 때
private void getAllConcert(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String url = "showError.jsp";
try {
request.setAttribute("getAllConcert", service.getAllConcert());
ArrayList<ConcertDTO> getAllConcert = service.getAllConcert();
url = "showSuccess.jsp";
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw= response.getWriter();
JSONObject result = new JSONObject();
System.out.println(getAllConcert);
for(ConcertDTO concert:getAllConcert) {
result.put(Integer.toString(concert.getConcertId()), concert.getConcertName());
}
System.out.println(result);
System.out.println(result.getClass());
pw.print(result);
} catch (Exception e) {
request.setAttribute("errMsg", e.getMessage());
e.printStackTrace();
}
}
// 'getSingersByConcert'를 command값으로 받았을 때
private void getSingersByConcert(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("확인용메시지123");
int concertId = Integer.parseInt(request.getParameter("data"))-1;
response.setContentType("text/html;charset=UTF-8");
ArrayList<Integer> result = new ArrayList<>();;
StringBuilder result2 = new StringBuilder();
String result3 = null;
try {
result = service.getSingersByConcert(concertId);
System.out.println(result);
} catch (SQLException | NotExistException e) {
e.printStackTrace();
}
for (Integer i: result) {
result2.append(i);
}
result3 = result2.toString();
PrintWriter pw= response.getWriter();
pw.print(result3);
}
위 코드만으로는 정확한 동작방식을 이해하기 어렵다. 간단하게 service객체에서 특정 메서드를 이용하면 DB에서 원하는 정보를 가져온다고 생각하면 이해가 쉽다. 두 경우 모두 원하는 값을 불러오고 나서는 PrintWriter 객체에 해당 정보를 넣은 뒤 다시 자바스크립트로 신호를 보낸다. 그럼 위에서 중략했었던 xhttp.onload에서 해당 신호를 받아 익명함수를 실행하게 된다.
xhttp.onload = function() {
if (xhttp.status == 200) {
if (count == 0) {
console.log("0번");
let res_data = this.responseText;
console.log(res_data);
jsonConcert = JSON.parse(res_data);
concertCount = Object.keys(jsonConcert).length;
let imageBox = document
.getElementById("showAllSinger");
imageBox.innerHTML = "<h2>콘서트 라인업이 궁금하신가요?</h2><br>";
} else if (count == 1) {
console.log("1번");
let res_data = this.responseText;
jsonSinger = JSON.parse(res_data);
let imageBox = document
.getElementById("showAllSinger");
imageBox.innerHTML = "<h2>전체가수</h2><br>";
for (i in jsonSinger) {
console.log("확인용@");
let singerName = jsonSinger[i];
imageBox.innerHTML += `<div class="w3-third"><p>${singerName}</p><img src="images/가수${i}_${singerName}.jpg" class="w3-round w3-margin-bottom" alt="Random Name" style="width:30%"></div>`;
}
} else if (count <= concertCount + 1) {
console.log("0,1번아님");
let res_data = this.responseText;
let imageBox = document.getElementById("showAllSinger");
let num = count-1;
imageBox.innerHTML = `<h2>콘서트${num} 가수</h2><br>`;
console.log(res_data);
for (i in res_data){
console.log("확인용%");
let singerId = res_data[i];
console.log(singerId);
let singerName = jsonSinger[singerId];
imageBox.innerHTML += `<div class="w3-third"><p>${singerName}</p><img src="images/가수${singerId}_${singerName}.jpg" class="w3-round w3-margin-bottom" alt="Random Name" style="width:30%"></div>`;
}
}
count += 1;
if (count >= concertCount + 2) {
count = 0;
}
}else{
console.log("통신대기중..");
}
}
if (xhttp.status == 200) 는 통신상태가 양호한지 체크하는 부분이다. 이후에는 위에서 했던 작업과 비슷하게 count가 0일 때, 1일 때, 나머지 경우로 나누어 따로 작업을 해준다.
(데이터베이스에 레코드를 넣는 부분에서 잘못해서 중복된 내용을 넣어 위와 같이 콘서트1에 마이클 잭슨이 두 번 들어가는 실수가 발생했습니다..)
이번 프로젝트는 시간부족으로 많은 기능을 넣지 못했다. 초반에 생각했던 예약기능도 넣지 못했다. 다음에 웹프로젝트가 한번 더 있는데, 그때는 더 알차게 프로젝트를 진행해야 겠다.