2-4. 클릭 이벤트(영화)
- 오른쪽 사진과 같이 영화(Movie Database에 저장되어있는 영화값들)Title 값들을 모두 표시해준 부분이다. 캡쳐본 상단의 날짜의 선택(최초 예매 페이지 접속시 오늘 날짜를 기준으로 selected되어있음.)을 기준으로 영화의 개봉일 및 상영 종료일에 따라 영화 표시의 유무를 나타내고 있다.
↓↓↓↓↓↓↓↓↓ [ 영화 관련 JavaScript&Code ] ↓↓↓↓↓↓↓↓↓
더보기
const drawListBox = () => {
movieTitles.forEach(movie => {
const listBoxElement = window.document.createElement('div');
listBoxElement.classList.add('list-box');
listBoxElement.dataset.value = movie['movieTitle'];
listBoxElement.dataset.date = movie['movieReleaseDate'];
const ageLimitElement = window.document.createElement('div');
ageLimitElement.classList.add(movie['movieAgeLimit']);
const movieTitleElement = window.document.createElement('span');
movieTitleElement.classList.add('movie-title');
movieTitleElement.innerText = movie['movieTitle'];
listBoxElement.append(ageLimitElement, movieTitleElement);
listCollect.append(listBoxElement);
listBoxElement.addEventListener('click', () => {
listBoxElement.classList.toggle('on');
if (listBoxElement.classList.contains('on')) {
value++;
listBoxElement.setAttribute('selected', 'selected');
} else {
value--;
listBoxElement.removeAttribute('selected');
}
if (value > 3) {
swal("알림", "영화는 최대 3개까지 선택이 가능합니다.");
listBoxElement.classList.remove('on');
listBoxElement.removeAttribute('selected');
value = 3;
}
if (!(listBoxElement.classList.contains('on'))) {
drawSubs();
} else {
drawSubs();
}
});
});
}
- 우선 drawListBox라는 실행함수를 만들었다. 이 실행 함수는 클릭될때 실행되는 함수로 사용이 되게 된다. createElement를 통해 특정 이벤트로 인해 실행이 될때마다 모든 html태그를 생성하게 된다. 이때 조건으로는 영화가 최대 3개까지 선택할 수 있으며 3개를 초과한 클릭시 alert가 뜨게 된다. 각 if구문 마다 value를 통해 카운트를 세는데 listBoxElement에 selected가 되어있을때는 value++, remove 되었을때는 value--를 해주었다. 여기서 key-point는 value가 3보다 초과할때의 문제이다. 이부분에서 value를 3으로 지정해 놓지 않으면 selected가되었다가 remove되기 때문에 count가 4로 올라가게 되어 다른 부분에 있어서 오류가 발생하게 된다. 이부분을 해결하기 위해서는 value가 3을 초과하는 시점을 강제로 value값이 3이라고 지정하였다. 이를 통해 초과되는 모든 시점이 value 값이 3으로 고정되기 때문에 오류가 발생 하지 않는다.
2-5. 클릭 이벤트(극장,지점)
- 극장,지점은 범위가 매우 광범위 했다. 모든 시,구,특별시를 통틀어 그안에 있는 모든 영화관을 표시하는 것은 프로젝트 기간상 어려웠기 때문에 현재 거주하고 있는 대구지역을 기준으로 5개 정도의 지점만 표시하기로 하였다. 극장, 지점에서 가장 중요했던 점은 지점 클릭시에는 오른쪽 화면에 영화 시간표에 기준에 적합한 영화 시간표가 표시 되게 구현해야 했다.
↓↓↓↓↓↓↓↓↓ [ 지점, 극장 관련 JavaScript&Code ] ↓↓↓↓↓↓↓↓↓
더보기
const drawBranches = () => {
quickCity.innerHTML = '';
branches.forEach(branch => {
const branchElement = window.document.createElement('div');
branchElement.classList.add('city');
branchElement.dataset.value = branch['index'];
branchElement.innerText = branch['text'];
branchElement.addEventListener('click', e => {
e.preventDefault();
branchElement.classList.toggle('on');
if (branchElement.classList.contains('on')) {
count++;
region.addEventListener('click', () => {
selectMovieTime.innerHTML = '';
count = 0;
});
branchElement.setAttribute('selected', 'selected');
} else {
count--;
branchElement.removeAttribute('selected');
}
if (count > 3) {
swal("알림", "극장은 최대 3개까지 선택이 가능합니다.");
branchElement.classList.remove('on');
branchElement.removeAttribute('selected');
count = 3;
}
beforeSelectMovieTime.classList.add('off');
drawSubs();
const listBox = window.document.querySelectorAll('.list-box');
const listBoxOn = window.document.querySelectorAll('.list-box.on');
listBox.forEach(x => {
x.addEventListener('click', () => {
if (listBoxOn.length < 1) {
selectMovieTime.innerHTML = '';
drawSubs();
}
})
});
});
quickCity.append(branchElement);
});
};
- 영화와 마찬가지로 실행함수로 만들었다. 실행 함수안 가장 상단에 innerHtml = '' 을 통해 클릭될때마다 모두 초기화 되는 듯한 모션을 주었다. 구조 자체는 영화 클릭이벤트 함수를 만들때와 비슷하였다. 3개 초과되는 부분을 가리기 위한 count 관련 부분만 if구문에 추가해주었다. 영화 이벤트 함수와 한가지 다른 점은 listBox(하나의 극장에 여러개의 영화시간표가 있기 때문에 복수형)를 forEach를 통해 각각의 값을 x로 두어 시간표마다 클릭시의 이벤트 함수를 구현하였는데 이때 listBox중 on 클래스도 포함되어 있는 경우 그것의 length가 1보다 작을때(즉, 하나도 선택이 되지 않았을 경우) selectMovieTime의 innerHtml의 값을 빈 문자로 지정하여 초기화 시켜주었다.
2-6. 클릭이벤트(영화 시간표)
- 사실상 예매 페이지에서 가장 중요한 포인트가 되는 곳이라고 생각한다. 두번의 절차에 걸쳐서 표시되는 영화, 극장,지점은 하나의 DB 테이블과 연결되어 있었지만 영화시간표에는 영화예매관련 모든 테이블과 연결되어있었기 때문에 상당히 까다로웠다.
↓↓↓↓↓↓↓↓↓ [ 영화 시간표 관련 JavaScript&Code ] ↓↓↓↓↓↓↓↓↓
더보기
const drawSubs = () => {
if (quickCity.classList.contains('on')) {
selectMovieTime.innerHTML = '';
const citySelected = Array.from(quickCity.querySelectorAll('.city[selected]'));
const selectedCityIndexes = citySelected.map(x => parseInt(x.dataset.value));
const listTitle = Array.from(window.document.querySelectorAll('.list-box[selected]'));
const selectListTitle = listTitle.map(x => x.innerText);
const deleteListBox = window.document.querySelectorAll('.list-box.on');
const deleteCity = window.document.querySelectorAll('.city.on');
const daySelected = Array.from(timeBox.querySelectorAll('.day[selected]'));
const selectedDayValue = daySelected.map(x => x.dataset.value);
if (deleteListBox.length < 1 && deleteCity.length > 0) {
allScreenInfos
.filter(allScreenInfo => selectedCityIndexes.indexOf(allScreenInfo['screenInfoBranchIndex']) > -1 &&
selectedDayValue.indexOf(allScreenInfo['screenInfoDate']) > -1)
.forEach(allScreenInfo => {
const movieTimeCoverElement = window.document.createElement('div');
movieTimeCoverElement.classList.add('movie-time-cover');
movieTimeCoverElement.dataset.value = allScreenInfo['screenInfoBranchIndex'];
movieTimeCoverElement.dataset.date = allScreenInfo['screenInfoDate'];
movieTimeCoverElement.dataset.time = allScreenInfo['screenInfoMovieTime'];
movieTimeCoverElement.dataset.mvStartTime = allScreenInfo['screenInfoMovieStartTime'];
movieTimeCoverElement.dataset.mvEndTime = allScreenInfo['screenInfoMovieEndTime'];
movieTimeCoverElement.dataset.audIndex = allScreenInfo['screenInfoAuditoriumIndex'];
movieTimeCoverElement.dataset.screenInfoIndex = allScreenInfo['screenInfoIndex'];
const movieTimeInfoBoxElement = window.document.createElement('div');
movieTimeInfoBoxElement.classList.add('movie-time-info-box');
const movieTimeElement = window.document.createElement('div');
movieTimeElement.classList.add('movie-time');
const timeIconElement = window.document.createElement('div');
timeIconElement.classList.add('time-icon');
const timeBoxElement = window.document.createElement('div');
timeBoxElement.classList.add('time-box');
const screenDateElement = window.document.createElement('span');
screenDateElement.classList.add('screen-date');
screenDateElement.setAttribute('rel', 'screen-date');
screenDateElement.innerText = allScreenInfo['screenInfoMovieStartTime'];
const screenEndDateElement = window.document.createElement('span');
screenEndDateElement.classList.add('screen-end-date');
screenEndDateElement.innerText = '~' + allScreenInfo['screenInfoMovieEndTime'];
timeBoxElement.append(screenDateElement, screenEndDateElement);
timeIconElement.append(timeBoxElement);
movieTimeElement.append(timeIconElement, timeBoxElement);
const movieTitleStateElement = window.document.createElement('div');
movieTitleStateElement.classList.add('movie-title-state');
const movieTitleElement = window.document.createElement('span');
movieTitleElement.classList.add('movie-title');
movieTitleElement.innerText = allScreenInfo['screenInfoMovieTitle'];
const movieStateElement = window.document.createElement('span');
movieStateElement.classList.add('movie-state');
movieStateElement.innerText = allScreenInfo['screenInfoMovieState'];
movieTitleStateElement.append(movieTitleElement, movieStateElement);
const moviePlaceElement = window.document.createElement('div');
moviePlaceElement.classList.add('movie-place');
const movieBranchElement = window.document.createElement('span');
movieBranchElement.classList.add('movie-branch');
movieBranchElement.innerText = allScreenInfo['screenInfoBranchText'];
const movieAuditoriumElement = window.document.createElement('span');
movieAuditoriumElement.classList.add('movie-auditorium');
movieAuditoriumElement.innerText = allScreenInfo['screenInfoAuditoriumText'];
const seatBoxElement = window.document.createElement('div');
seatBoxElement.classList.add('seat-box');
const remainSeatElement = window.document.createElement('span');
remainSeatElement.classList.add('remain-seat');
remainSeatElement.innerText = allScreenInfo['screenInfoSeatCountAll'];
completeSeatBooking.forEach(complete => {
if (complete['bookingSeatScreenInfoIndex'] === allScreenInfo['screenInfoIndex']) {
remainSeatElement.innerText = parseInt(allScreenInfo['screenInfoSeatCountAll']) - parseInt(allScreenInfo['screenInfoSeatRemain']);
}
})
allSeat.forEach(allSeat => {
if (allSeat['seatAudIndex'] === allScreenInfo['screenInfoAuditoriumIndex']) {
const allSeatElement = window.document.createElement('span');
allSeatElement.classList.add('all-seat');
allSeatElement.innerText = '/' + allSeat['seatCountAll'];
seatBoxElement.append(remainSeatElement, allSeatElement);
}
});
moviePlaceElement.append(movieBranchElement, movieAuditoriumElement, seatBoxElement);
movieTimeInfoBoxElement.append(movieTimeElement, movieTitleStateElement, moviePlaceElement);
movieTimeCoverElement.append(movieTimeInfoBoxElement);
selectMovieTime.append(movieTimeCoverElement);
movieTimeCoverElement.classList.add('on');
if (selectedCityIndexes.length === 0) {
selectMovieTime.innerHTML = '';
}
region.addEventListener('click', () => {
drawBranches();
drawSubs();
});
const readScreenInfo = window.document.querySelectorAll('.movie-time-cover.on');
readScreenInfo.forEach(x => {
x.addEventListener('click', () => {
if (hiddenEmail === null) {
window.location.href = '/member/login';
return false;
}
timeContainer.classList.add('off');
paymentContainer.classList.remove('off');
seatContainer.classList.add('on');
})
});
movieTimeCoverElement.addEventListener('click', () => {
movieTimeCoverElement.setAttribute('selected', 'selected');
drawSeatResult();
drawSeat();
});
});
}
if (deleteListBox.length > 0 && deleteCity.length > 0) {
allScreenInfos
.filter(allScreenInfo => selectedCityIndexes.indexOf(allScreenInfo['screenInfoBranchIndex']) > -1 &&
selectListTitle.indexOf(allScreenInfo['screenInfoMovieTitle']) > -1 &&
selectedDayValue.indexOf(allScreenInfo['screenInfoDate']) > -1)
.forEach(allScreenInfo => {
const movieTimeCoverElement = window.document.createElement('div');
movieTimeCoverElement.classList.add('movie-time-cover');
movieTimeCoverElement.dataset.value = allScreenInfo['screenInfoBranchIndex'];
movieTimeCoverElement.dataset.date = allScreenInfo['screenInfoDate'];
movieTimeCoverElement.dataset.time = allScreenInfo['screenInfoMovieTime'];
movieTimeCoverElement.dataset.mvStartTime = allScreenInfo['screenInfoMovieStartTime'];
movieTimeCoverElement.dataset.mvEndTime = allScreenInfo['screenInfoMovieEndTime'];
movieTimeCoverElement.dataset.audIndex = allScreenInfo['screenInfoAuditoriumIndex'];
movieTimeCoverElement.dataset.screenInfoIndex = allScreenInfo['screenInfoIndex'];
const movieTimeInfoBoxElement = window.document.createElement('div');
movieTimeInfoBoxElement.classList.add('movie-time-info-box');
const movieTimeElement = window.document.createElement('div');
movieTimeElement.classList.add('movie-time');
const timeIconElement = window.document.createElement('div');
timeIconElement.classList.add('time-icon');
const timeBoxElement = window.document.createElement('div');
timeBoxElement.classList.add('time-box');
const screenDateElement = window.document.createElement('span');
screenDateElement.classList.add('screen-date');
screenDateElement.setAttribute('rel', 'screen-date');
screenDateElement.innerText = allScreenInfo['screenInfoMovieStartTime'];
const screenEndDateElement = window.document.createElement('span');
screenEndDateElement.classList.add('screen-end-date');
screenEndDateElement.innerText = '~' + allScreenInfo['screenInfoMovieEndTime'];
timeBoxElement.append(screenDateElement, screenEndDateElement);
timeIconElement.append(timeBoxElement);
movieTimeElement.append(timeIconElement, timeBoxElement);
const movieTitleStateElement = window.document.createElement('div');
movieTitleStateElement.classList.add('movie-title-state');
const movieTitleElement = window.document.createElement('span');
movieTitleElement.classList.add('movie-title');
movieTitleElement.innerText = allScreenInfo['screenInfoMovieTitle'];
const movieStateElement = window.document.createElement('span');
movieStateElement.classList.add('movie-state');
movieStateElement.innerText = allScreenInfo['screenInfoMovieState'];
movieTitleStateElement.append(movieTitleElement, movieStateElement);
const moviePlaceElement = window.document.createElement('div');
moviePlaceElement.classList.add('movie-place');
const movieBranchElement = window.document.createElement('span');
movieBranchElement.classList.add('movie-branch');
movieBranchElement.innerText = allScreenInfo['screenInfoBranchText'];
const movieAuditoriumElement = window.document.createElement('span');
movieAuditoriumElement.classList.add('movie-auditorium');
movieAuditoriumElement.innerText = allScreenInfo['screenInfoAuditoriumText'];
const seatBoxElement = window.document.createElement('div');
seatBoxElement.classList.add('seat-box');
const remainSeatElement = window.document.createElement('span');
remainSeatElement.classList.add('remain-seat');
remainSeatElement.innerText = allScreenInfo['screenInfoSeatCountAll'];
completeSeatBooking.forEach(complete => {
if (complete['bookingSeatScreenInfoIndex'] === allScreenInfo['screenInfoIndex']) {
remainSeatElement.innerText = parseInt(allScreenInfo['screenInfoSeatCountAll']) - parseInt(allScreenInfo['screenInfoSeatRemain']);
}
})
allSeat.forEach(allSeat => {
if (allSeat['seatAudIndex'] === allScreenInfo['screenInfoAuditoriumIndex']) {
const allSeatElement = window.document.createElement('span');
allSeatElement.classList.add('all-seat');
allSeatElement.innerText = '/' + allSeat['seatCountAll'];
seatBoxElement.append(remainSeatElement, allSeatElement);
}
});
moviePlaceElement.append(movieBranchElement, movieAuditoriumElement, seatBoxElement);
movieTimeInfoBoxElement.append(movieTimeElement, movieTitleStateElement, moviePlaceElement);
movieTimeCoverElement.append(movieTimeInfoBoxElement);
selectMovieTime.append(movieTimeCoverElement);
movieTimeCoverElement.classList.add('on');
if (selectedCityIndexes.length === 0) {
selectMovieTime.innerHTML = '';
}
region.addEventListener('click', () => {
drawBranches();
drawSubs();
});
const readScreenInfo = window.document.querySelectorAll('.movie-time-cover.on');
readScreenInfo.forEach(x => {
x.addEventListener('click', () => {
if (hiddenEmail === null) {
window.location.href = '/member/login';
return false;
}
timeContainer.classList.add('off');
paymentContainer.classList.remove('off');
seatContainer.classList.add('on');
})
});
movieTimeCoverElement.addEventListener('click', () => {
movieTimeCoverElement.setAttribute('selected', 'selected');
drawSeatResult();
drawSeat();
});
});
}
}
}
- 코드를 다시 보니까 정말 가독성은 하나도 생각하지 않은 코드인것 같다. domParser를 썼다면 훨씬 깔끔하고 값을 넘기고 받는 부분에 있어서도 위 방법보다 쉬웠을것 같은데 많은 후회가 생겼다. 하지만 위의 코드를 짜봤기 때문에 다음 비슷한 사례가 생겼을 땐 실수를 반복하지 않을 수 있을 것 같다. 위 코드를 보자면 우선 크게 두가지의 분류로 나누어진다. 메가박스 공식 사이트에서와 마찬가지로 날짜는 최초 예매 페이지 접속시 오늘날짜를 기준으로 이미 선택되어져 있기 때문에 첫번째는 날짜 + 극장을 클릭시 영화시간표가 생기는 구조, 두번째는 날짜 + 극장 + 영화 세가지가 다 클릭되었을 경우 이다. 가장 큰 if문으로 우선 두가지의 경우를 나누었다(length의 길이를 통한 구분)
ArrayFrom & Map 메서드
- ArrayFrom을 처음 알게 되어 사용하게 되었는데 매우 유용했다. 최초 querySelector로 묶인 값들은 NodeList 형태로 생성되게 되는데 NodeList는 배열과 성질은 비슷하지만 엄연히 다르다. NodeList와 Array의 가장 큰 차이점은 NodeList는 JavaScript API가 아닌 browser가 제공하는 API라는 것이다. 그래서 배열관련 메서드는 사용하지 못하게 된다. 이러한 불편함을 해소하기 위해서는 NodeList를 배열로 바꿔주어 배열관련 메서드를 사용하는것이 코드를 작성하는데 있어서 매우 편리하였다. 특정 조건에 부합한 값들을 querySelectorAll로 묶어준 후 그 모든 값들을 ArrayFrom을 통해 배열로 바꿔주었다. 하지만 여기서도 문제가 발생하였다. 배열로 바꿔 주는것 까지는 좋았으나 클릭이벤트에 따라 값이 비었음에도 불구하고 빈값으로 배열의 길이 및 배열의 방을 차지하고 있었다. map 메서드는 위 배열중 true와 false(boolean의 값)로 배열에 담게 되는데 이때 map에 조건에 맞는 값만 뽑기 위해서는 filter메서드도 같이 사용해야 했다.
filter 메서드
- filter 메서드는 위 map 메서드를 사용해 나온 값들 중 true를 반환하는 값만 담아 새로운 배열을 만드는 역할을 한다. 예를 들어 map메서드를 사용한 조건에서 조건에 부합하지 않는 값들은 undefined로 배열에 담기게 되는데 이는 오류를 발생시킬 수 있게 된다. 그래서 true 즉, 조건에 부합하는 값들만 가지기 위해서는 filter를 통해 true의 결과가 나온 값들만 새로운 배열에 담아 주어야 한다. 위 코드에서 indexOf를 사용하여 -1보다 클 경우만 표시하였는데 indexOf 특성상 -1이 나오는 경우는 값이 없는 경우 즉, undefined를 나타내기 때문에 0과 1의 경우만 나타내기 위한 조건이다.
- createElement를 통해 각각의 조건에 맞게 태그와 태그안에 올바른 값이 들어감으로써 영화시간표 한블럭이 완성 되는 구조이다. 앞에서 만든 drawListBox 함수, drawBranches 함수를 통해 각각의 조건이 맞게 drawSubs 함수를 시켜주면 영화,극장의 선택 여부에 따른 영화시간표 구현하기가 완성이 된다. 가장 많이 사용된 allScreenInfos는 예매 페이지 최초 접속시 PATCH요청으로 Controller에서 JSONArray로 보내지는 모든 값들을 조건에 맞게 나뉜 값이다. Controller에서 put메서드를 통해 이름과 값을 지정하여 그 이름을 allScreenInfos['index'] 이런 식으로 작성하여 사용하면 값이 표시가 된다.
'MegaBoxProject' 카테고리의 다른 글
TeamProject Code Review 1 (0) | 2023.02.25 |
---|---|
MegaBox Project(회고) (0) | 2023.02.20 |