[Javascript Toy Project] Movie Booking Page | 영화 예매 페이지 만들기 토이프로젝트

1. Movie Booking Page UI/UX

인터넷 강의를 보면서 영화관 자리 선택 페이지를 만들어보았습니다. 좌석을 선택하면 데이터가 저장되는 기능 위주로 초점이 맞추어진 강의였기 때문에 결과물이 마음에 차지 않아 몇 가지 기능을 추가하고 디자인을 개선했습니다. 영화를 선택하면 왼쪽 포스터가 바뀌고, 좌석을 선택할 수 있게 됩니다. 이미 예약된 좌석은 회색으로, 예약 가능한 좌석은 노란색으로 표현했으며, 선택한 좌석은 눈에 가장 잘 들어오는 빨간색 계열로 정했습니다. 선택한 좌석은 실시간으로 로컬스토리지에 저장되어 나중에 활용할 수 있도록 합니다.

2. Main Features

2-1. Responsive web

영화 예매창은 반응형으로 만들되 브라우저의 가로길이에 민감하게 반응할 필요는 없다고 생각했습니다. 그래서 간단히 포스터를 보여주었다가 삭제하는 정도로 구현해보았습니다.

@media screen and (max-width: 980px) {
  .ultimate-container {
    width: 500px;
  }
  .visual-section {
    display: none;
  }
}

2-2. Data saved in localStorage

localStorage.setItem()으로 데이터를 저장해줍니다. movieIndexmoviePrice의 경우 애초에 string 형태기 때문에 JSON.stringify() 과정을 생략하였고, seatsIndex는 배열이기 때문에 stringify 해주었습니다. 좌석을 클릭하면 각 정보가 로컬스토리지에 저장됩니다.

function setMovieData(movieIndex, moviePrice) {
  localStorage.setItem('selectedMovieIndex', movieIndex) 
  localStorage.setItem('selectedMoviePrice', moviePrice) 
}

function updateSelectedCount() {
  const selectedSeats = document.querySelectorAll('.row .seat.selected')
  const selectedSeatsCount = selectedSeats.length 
  const seatsIndex = [...selectedSeats].map((seat) => [...seats].indexOf(seat))
  
  localStorage.setItem('selectedSeats', JSON.stringify(seatsIndex))
}

movieSelect.addEventListener('change', (e) => {
  ticketPrice = +e.target.value
  setMovieData(e.target.selectedIndex, e.target.value)
  updateSelectedCount() 
})

3. Details

3-1. Posters and the price change as we select the movie

영화를 선택하면, 선택한 요소의 index값을 가지고 영화와 일치하는 포스터 클래스를 추가해보았습니다. 썩 마음에 드는 코드는 아니지만 개인적으로 e.target.selectedIndexclassList 등 DOM 공부를 할 수 있었습니다.

.poster1 {
  background-image: url(https://images-na.ssl-images-amazon.com/images/I/91IlgV6rVtL._AC_SY741_.jpg);
  background-size: cover;
}
.poster2 {
  background-image: url(https://xl.movieposterdb.com/11_07/1992/103639/xl_103639_2c39a065.jpg);
  background-size: cover;
}
.poster3 {
  background-image: url(https://wdwnt.com/wp-content/uploads/2018/11/DtGT1ZmW0AASOkw1.jpg);
  background-size: cover;
}
.poster4 {
  background-image: url(https://images-na.ssl-images-amazon.com/images/I/81S36TZzg9L._AC_SL1500_.jpg);
  background-size: cover;
}
const visualSection = document.querySelector('.visual-section')

movieSelect.addEventListener('change', (e) => {
  ticketPrice = +e.target.value
  setMovieData(e.target.selectedIndex, e.target.value)
  
  visualSection.classList.remove(visualSection.classList[1])
  visualSection.classList.add(`poster${e.target.selectedIndex}`)

  updateSelectedCount() 
})

3-2. Selected seats stay when refreshed

새로고침을 해도 정보가 남아있도록 만들었습니다. 로컬스토리지에 저장된 데이터를 parse 해서 결과물이 유효한 값이면 forEach() 메서드로 하나씩 selected 클래스를 추가해, 선택한 좌석은 빨간색으로 여전히 표기될 수 있도록 했습니다.

function populateUI() {
  const selectedSeats = JSON.parse(localStorage.getItem('selectedSeats'))
  if (selectedSeats !== null && selectedSeats.length > 0) {
    seats.forEach((seat, index) => {
      if (selectedSeats.indexOf(index) > -1) {
        seat.classList.add('selected')
      }
    })
  }
}

populateUI()

3-3. No click events when movie is not selected

선택한 좌석에 대한 데이터를 가지고 저장하고 출력하는 데 집중하다 보니, 영화를 고르지 않은 상태에서도 좌석를 선택할 수 있는 등 디테일이 부족했습니다. option value0이 아닌 경우에만 클래스를 toggle할 수 있도록 기능을 추가했습니다.

    <div class="movie-container">
      <label>Pick a movie</label>
      <select id="movie">
        <option value="0" selected >Select a movie</option>
        <option value="10000">Frozen 2 (₩10,000)</option>
        <option value="12000">Aladdin (₩12,000)</option>
        <option value="8000">Toy Story4 (₩8,000)</option>
        <option value="9000">The Lion King (₩9,000)</option>
      </select>
    </div>
const movieSelect = document.getElementById('movie')

container.addEventListener('click', (e) => {
  if ( movieSelect.value > 0 && e.target.classList.contains('seat') && !e.target.classList.contains('occupied')
  ) {
    e.target.classList.toggle('selected')
    updateSelectedCount()
  }
})

3-4. Total price with a comma

총 영화값을 천 자리 단위로 끊어서 출력하기 위해 구글링해서 아래와 같은 함수를 찾아 적용했습니다.

const count = document.getElementById('count')
const total = document.getElementById('total')

function numberWithCommas(price) {
    return price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

count.innerText = selectedSeatsCount
total.innerText = numberWithCommas(selectedSeatsCount * ticketPrice)

좋은 웹페이지 즐겨찾기