👇 Github
GitHub - seungHee-cat/LUNAR
Contribute to seungHee-cat/LUNAR development by creating an account on GitHub.
github.com
👇 Notion
추후에 추가 예정입니다.
개발 환경
java 버전 : 17
IDE : InteliJ
DB : MySQL
Spring Boot
프로젝트 소개
달빛 아래에서 영화를 만나다
작년 이맘때 쯤 완성했던 개인 프로젝트 MovieHub를 리팩토링한 LUNAR 프로젝트를 소개합니다. 밤하늘을 밝게 비춰주어 세상의 다른 면을 보게 해주는 달빛처럼, 영화는 우리가 살아가는 익숙한 현실의 특별한 모습을 보게 해줍니다. LUNAR는 다양한 영화와 리뷰, 그리고 코멘트를 통해 새로운 달빛을 발견할 수 있는 플랫폼입니다. 영화를 사랑하는 사람들이 모여 감상을 공유하고 느꼈던 점을 기록할 수 있는 커뮤니티입니다.
기능 소개
- 🔍 영화 검색: 별처럼 끝없는 영화 세상 속에서 원하는 작품을 검색해보세요.
- ⭐️ 영화 평가: 감상한 영화의 평가를 별점으로 남겨보세요.
- ✏️ 리뷰 작성: 영화를 보며 느낀 점을 공유하고 싶으신가요? 글로 남김으로써 기록하고, 다른 사람과 소통하세요.
- 💬 코멘트 작성: 다른 사람의 리뷰에 대해 생각과 감정을 나눠보세요.
- 🗓️ 캘린더: 평가한 작품을 달력에 기록하여 한 눈에 보기 쉽게 확인해 보세요. 자신의 기록 뿐 아니라 다른 사람의 캘린더를 볼 수도 있습니다.
소개 목차
코드 소개
- 공통 기능 관리
- Modal, Toast, Ajax와 같은 공통 함수 처리 및 관리
- 라이브러리 활용
- 외부 라이브러리 : star-rating.js, chart.js 등 외부 라이브러리 활용
- 커스텀 라이브러리 : calender, card.slide 등 프로젝트에 특화된 커스텀 컴포넌트 제작 및 적용
- TMDB API를 활용한 Movie 서비스 개발
- TMDB API 연동 및 MovieService 구성
- 다국어 지원 및 국제화 처리
- 프로젝트에서의 다국어 처리 및 국제화 적용
페이지 스크린샷 (MovieHub / Lunaer)
- 메인 화면
- 영화 상세 페이지
- 영화 검색 페이지
- 리뷰 상세 페이지
- 전체 리뷰 검색 페이지
- 유저 페이지
- 마이 리뷰 페이지
코드 소개
1. 공통 기능 관리
1-1. 모달 (Modal)
/*--------------------------------------------------------
| 리뷰 등록/수정 모달
--------------------------------------------------------*/
function reviewModal() {
let formData = new FormData();
formData.append("movieId", $("#movieDetailFrm input[name=movieId]").val());
formData.append("title", $("#movieDetailFrm input[name=title]").val());
formData.append("releaseYear", $("#movieDetailFrm input[name=releaseYear]").val());
modal.openModal("<c:url value='/review/reviewModal'/>", formData, "medium");
}

공통으로 사용하는 모달은 지정된 form 이름에 따라 동적으로 작동하며, 해당 form 내에 포함된 input hidden 요소의 속성 값을 기반으로 formData 객체에 데이터를 구성합니다. 이후, 이 formData는 openModal 함수에 인자로 전달되어 모달을 여는 동작을 수행합니다. 이를 통해 모달의 재사용성과 데이터 관리의 일관성을 확보할 수 있습니다.
modal.js 중 일부
// modal.openModal = function(url, formData, size)
if (modalElement.length === 0) {
// 모달이 없다면, 새로운 모달 HTML을 생성
$('body').append(`<div class="modal fade" id="${modalId}" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog d-flex align-items-center justify-content-center" style="min-height: 90vh;">
<div class="modal-content"></div>
</div>
</div>`);
modalElement = $(`#${modalId}`);
}
openModal 함수가 실행될 때마다 동적으로 모달 HTML 요소를 생성합니다. 만약 지정된 ID를 가진 모달이 이미 존재하지 않는 경우, 새로운 모달 HTML 구조를 동적으로 생성하여 <body> 태그 내부에 추가합니다
// modal.closeModal = function()
if (modalInstance) {
modalElement.one('hidden.bs.modal', function () {
// 모달이 닫힌 후 관련 DOM 제거
modalElement.remove(); // 모달 HTML 삭제
$('.modal-backdrop').remove(); // 백드롭 삭제
$('body').removeClass('modal-open'); // 스크롤 제어 클래스 제거
$('body').css('padding-right', ''); // 추가된 패딩 초기화
});
modalInstance.hide(); // 모달 닫기
}
모달을 닫는 버튼을 클릭하면 closeModal 함수가 실행되어 동적으로 추가된 모달을 추적하고 DOM에서 완전히 제거합니다. 생성 시 설정된 고유 id 속성을 통해 정확히 모달을 식별하며, 이를 기반으로 지정된 모달 요소만 삭제합니다.
이 방식은 모달을 닫을 때마다 해당 요소를 완전히 제거하여 DOM 내 불필요한 요소가 남아있는 것을 방지하고 메모리 사용을 최적화합니다. 또한, 새로운 모달을 열고 닫는 반복적인 동작 중에도 중복이나 충돌이 발생하지 않도록 보장합니다.
1-2. 토스트 (Toast)
function fn_showToastMessage(string) {
let toast = $('#base-toast');
toast.find('.toast-body').html(string); // 토스트 메시지 세팅
toast.toast('show'); // 토스트 표시
}
// modal.js 중 일부
modal.closeModalAndReload = function(message)
{
// 모달 닫기
modal.closeModal();
// 토스트 표시
let toast = fn_showToastMessage(message);
토스트 메시지의 경우, 별도의 공통 함수를 만들어 코드의 중복을 줄이고 필요한 곳에서 간단히 호출할 수 있도록 설계되었습니다. 이 함수는 다양한 상황에서 일관된 방식으로 토스트 메시지를 표시할 수 있도록 구현되었습니다. 또한, 모달을 닫는 동작과 토스트 메시지 호출을 하나의 함수로 결합하여 처리하는 공통 함수를 추가로 작성했습니다. 이를 통해 모달을 닫음과 동시에 사용자에게 특정 메시지를 제공하는 작업이 일관성 있게 이루어질 수 있습니다.
1-3. 비동기 처리 (Ajax)
$(document).ready( function() {
fn_myReviewAjax(); // 리뷰 조회
fn_reviewListAjax(); // 리뷰 리스트 조회
js_rateChart(); // 원형차트 조회
js_starRatingAjax(); // 별점 조회
});
// 예시: fn_reviewListAjax
/*--------------------------------------------------------
| 모든 리뷰 리스트
--------------------------------------------------------*/
function fn_reviewListAjax(){
let movieId = $("#movieDetailFrm input[name=movieId]").val();
let limit = 5;
let params = "movieId=" + movieId + "&limit=" + limit;
fn_ajaxRoadDiv("<c:url value='/review/reviewListAjax'/>", params, "reviewListAjax");
}
웹 페이지가 처음 로드되면, JavaScript가 해당 div의 id를 확인하고, 그에 맞는 JSP 파일을 AJAX 호출을 통해 서버에 요청합니다. 서버는 요청에 따라 필요한 데이터를 반환하고, 클라이언트는 이 데이터를 이용하여 화면을 갱신합니다. 이 방식은 페이지를 새로고침하지 않고도 동적으로 콘텐츠를 업데이트할 수 있어 사용자 경험을 개선하고, 성능을 높이는데 도움이 됩니다.
2. 라이브러리
2-1. 외부 라이브러리 (1) star-rating.js
<select id="star-rating" class="star-rating" onchange="loginModal();">
<option value=""></option>
<option value="5"></option>
<option value="4"></option>
<option value="3"></option>
<option value="2"></option>
<option value="1"></option>
</select>

영화 별점 평가에 사용된 star-rating 라이브러리는 기본적으로 별점이 등록되지 않으면 빈 값("")으로 설정됩니다. 사용자가 별을 클릭하면 해당 별점이 등록되며, 이미 등록된 별점을 다시 클릭하면 해당 별점이 삭제됩니다. 이를 통해 사용자에게 직관적인 별점 평가 인터페이스를 제공하며, 별점의 등록과 삭제를 간편하게 처리할 수 있습니다.
2-2. 외부 라이브러리 (2) Chart.js
/*--------------------------------------------------------
| 평점 차트
--------------------------------------------------------*/
function js_rateChart(){
let ctx = document.getElementById('chartAjax').getContext('2d');
let percentage = (${movie.voteAverage / 5.0 * 100}).toFixed(0);
let voteCount = $("input[name=voteCount]").val();
let cntText = "<spring:message code='chart.text.count'/>";
// ...생략



TMDB에서 가져온 영화의 평점을 Chart.js를 사용하여 시각화한 결과입니다. 평점에 따라 차트의 색상이 달라지도록 설정하여, 사용자가 점수를 쉽게 인식할 수 있도록 했습니다. 0~30점은 빨간색, 31~70점은 노란색, 그 이상은 초록색으로 평가 점수에 따라 차트 색이 변하도록 하여 각 구간에 맞는 색상을 통해 평가를 한눈에 파악할 수 있도록 했습니다.
2-3. 커스텀 라이브러리 (1) calender
// usrCalenderAjax.jsp
/* 캘린더 데이터 전달 */
window.initRatingList = function() {
return ${ratingList};
};
// Calender.js 중 일부
const matchingRatings = Array.isArray(ratingList)
? ratingList.filter(rating => {
const ratingDate = new Date(rating.wrtTime);
const formattedRatingDate = ratingDate.getFullYear() +
"-" +
String(ratingDate.getMonth() + 1).padStart(2, "0") +
"-" +
String(ratingDate.getDate()).padStart(2, "0");
return formattedRatingDate === currentDate;
})
: [];

유저 페이지에 사용한 캘린더는 이전 MovieHub 프로젝트에서 사용했던 코드를 재사용하였습니다. 계산된 날짜 정보를 캘린더에 표시된 각 날짜와 비교하여, 사용자가 평가를 남긴 날짜가 캘린더에 해당하는 날짜와 일치하는지 확인합니다. 일치할 경우, 해당 날짜의 셀에 해당 영화의 포스터 이미지를 동적으로 삽입합니다.
2-4. 커스텀 라이브러리 (2) card.slide

메인 페이지에서 사용한 카드 슬라이드 효과는 이전 MovieHub 프로젝트에서 사용했던 코드를 재사용하여 구현한 것입니다. 이 코드에서는 박스오피스, 넷플릭스, 왓챠 목록을 각각 슬라이드로 표시하고, 각 목록마다 고유한 슬라이더 이름과 버튼 이름을 부여하여 혼동 없이 동작하도록 했습니다. 이렇게 함으로써 각 슬라이드는 독립적으로 작동하고, 각 버튼이 해당 슬라이드만 제어하도록 하여 여러 목록이 동시에 존재하는 상황에서도 문제가 발생하지 않도록 했습니다.
3. TMDB API (MovieService)
/**
* 스케줄러를 적용하여 매일 오후 15시마다 영화 DB 업데이트
*/
@Scheduled(cron = "0 0 15 * * *")
public void updateMovieList(){
/* MOVIE SETTING START */
final String API_KEY = /* 본인의 API 키를 이곳에 입력합니다. */;
List<String> apiURL_list = new ArrayList<>();
List<Integer> movieIdLists = new ArrayList<>();
List<String> detailLists = new ArrayList<>();
apiURL_list.add("https://api.themoviedb.org/3/movie/now_playing?api_key="
+ API_KEY+"&language=ko-KR&page=1");
apiURL_list.add("https://api.themoviedb.org/3/discover/movie?api_key="
+ API_KEY+"&language=ko-KR&page=1&sort_by=popularity.desc&watch_region=KR&with_watch_providers=8");
apiURL_list.add("https://api.themoviedb.org/3/discover/movie?api_key="
+ API_KEY+"&language=ko-KR&page=1&sort_by=popularity.desc&watch_region=KR&with_watch_providers=97");
StringBuilder detailStringBuilder = new StringBuilder();
for(String apiURL : apiURL_list) {
try {
URI uri = new URI(apiURL);
URL url = uri.toURL();
BufferedReader bf = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
String result = bf.readLine();
movieIdLists.clear();
JSONObject jsonObject = new JSONObject(result);
JSONArray list = jsonObject.getJSONArray("results");
for(int j=0; j < list.length(); j++) {
JSONObject contents = list.getJSONObject(j);
int movieId = contents.getInt("id");
movieIdLists.add(movieId);
detailStringBuilder.append("https://api.themoviedb.org/3/movie/").append(movieIdLists.get(j))
.append("?api_key=").append(API_KEY).append("&language=ko-KR").append("\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
detailLists.addAll(Arrays.asList(detailStringBuilder.toString().split("\n")));
getDetailMovie(detailLists);
/* MOVIE SETTING END */
}
TMDB API를 활용하여 영화 정보를 가져오는 코드에서는 각 플랫폼별로 특화된 방식으로 데이터를 조회하고 처리합니다. 박스오피스 리스트는 현재 상영 중인 영화(Now Playing)를 기준으로 가져오며, 넷플릭스와 왓챠의 경우 각 OTT 플랫폼의 고유 번호를 기반으로 데이터를 조회했습니다. 이렇게 조회한 영화 리스트는 각각 ArrayList에 담아 관리하며, 이후 JSONObject를 사용해 JSON 데이터를 가공하여 필요한 형식으로 변환한 뒤 데이터베이스에 저장합니다.
최신 데이터를 유지하기 위해 스케줄링 기능을 적용하여 매일 오후 3시에 자동으로 영화 정보를 업데이트하도록 설정했습니다. 이 스케줄링은 Spring Framework에서 제공하는 스케줄러 어노테이션(@Scheduled)을 활용하여 구현되었으며, 이를 통해 별도의 관리자 개입 없이 데이터베이스에 최신 정보를 주기적으로 갱신할 수 있도록 자동화했습니다.

또한 Log4j2를 활용하여 애플리케이션의 로그 관리를 개선하였으며, 특히 콘솔에서 쿼리 실행 과정을 더욱 명확하게 확인할 수 있도록 설정을 최적화하였습니다. 이를 통해 쿼리 실행 시점과 내용을 직관적으로 파악할 수 있어 디버깅과 성능 분석이 한층 수월해졌습니다.
4. 다국어 지원 및 국제화 처리

다국어 지원 기능을 적용하여 사용자가 애플리케이션에서 한국어와 영어를 자유롭게 선택할 수 있도록 구현하였습니다. 언어 전환 시, 메시지 프로퍼티 파일에 저장된 다국어 리소스를 기반으로 동적으로 콘텐츠를 변경하여, 영어 환경에서도 자연스럽고 일관된 사용자 경험을 제공합니다. 이를 통해 글로벌 사용자 대상의 접근성과 편의성을 한층 강화하였으며, 다국어 확장 가능성 또한 고려한 구조로 설계되었습니다.
페이지 스크린샷
1. 메인 화면 (MovieHub / Lunar)

검색 기능을 공통 헤더로 분리하여 사용자들이 어느 페이지에서든 손쉽게 영화를 검색할 수 있도록 개선하였습니다. 이로 인해 검색의 접근성과 편의성이 대폭 향상되었습니다. 또한, 슬라이드 목록을 확장하여 더 많은 영화 콘텐츠를 제공함으로써 사용자들이 다양한 영화를 탐색할 수 있도록 했습니다. 이와 함께, 페이지의 오른쪽 하단에 다국어 기능을 추가하여 사용자가 원하는 언어로 애플리케이션을 즉시 변경할 수 있도록 구현, 글로벌 사용자 경험을 강화하였습니다.
2. 영화 상세 페이지 (MovieHub / Lunar)

별점 평가 기능에서 기존의 rate_yo 라이브러리를 star_rating 라이브러리로 변경하였습니다. 이전에 별점이 중복으로 삽입되거나 올바르게 삭제되지 않던 문제를 수정하여 기능의 안정성을 높였습니다. 또한, Chart.js 라이브러리를 적용하여 영화 평가 지수를 시각적으로 표현함으로써 데이터를 직관적으로 확인할 수 있도록 개선하였습니다.
3. 영화 검색 페이지 (MovieHub / Lunar)

초기 UI 설계 단계에서는 왓챠 피디아를 참고하여 사용자 인터페이스를 구성하였으나, 리팩토링 과정에서는 로튼토마토의 디자인 요소를 참고하여 UI를 보다 직관적이고 세련되게 개선하였습니다. 또한, 메인 화면에서 영화 카테고리가 표시되던 순서를 기준으로 검색 결과를 정렬하고 분류하여, 사용자가 원하는 영화를 더 쉽게 찾을 수 있도록 검색 기능을 최적화하였습니다.
4. 리뷰 상세 페이지 (MovieHub / Lunar)

기존에 사용하던 대댓글 기능은 필요성이 낮다고 판단하여 과감히 제거하였습니다. 대신, 사용자 리뷰에 대해 댓글을 작성하는 단순하고 직관적인 방식으로 변경하여 사용성을 개선하였습니다. 특히, 사용자가 작성한 댓글은 댓글 목록의 최상단에 위치하도록 하여 가시성을 높였으며, 댓글 작성 시간을 ‘몇 초 전’, ‘몇 분 전’, ‘몇 달 전’과 같이 상대적인 시간 형식으로 표시하여 직관적이고 실시간에 가까운 사용자 경험을 제공하도록 구현하였습니다.
5. 전체 리뷰 검색 페이지 (MovieHub / Lunar)

전체 리뷰를 검색할 수 있는 페이지에서는 기존에 스크롤 시 서서히 나타나는 애니메이션 효과를 적용했으나, 리팩토링 과정에서 이 기능을 제거하고 검색 기능과 코드 최적화에 더 많은 집중을 하였습니다. 이를 통해 검색 성능과 시스템 효율성을 크게 개선하였으며, 사용자 경험을 고려한 빠르고 안정적인 검색 기능을 제공하게 되었습니다. 최적화된 코드 구조로 유지보수성을 높이고, 페이지 로딩 속도를 개선하여 보다 원활한 사용자 인터페이스를 구현하였습니다.
6. 유저 페이지 (MovieHub / Lunar)


기존에 로그인한 유저만 자신의 프로필을 볼 수 있던 "마이 페이지"를 "유저 페이지"로 변경하여, 다른 유저들의 페이지도 열람할 수 있도록 기능을 확장하였습니다. 이를 통해 유저 간의 상호작용을 촉진하고, 유저 간 정보를 탐색할 수 있는 기회를 제공하게 되었습니다.
또한, 캘린더 기능은 기존에 작성했던 순수 JavaScript 캘린더를 그대로 사용하면서, 별점 평가를 한 날짜에 영화 포스터가 추가되도록 하여, 사용자가 월별로 자신의 영화 기록을 시각적으로 쉽게 확인할 수 있도록 개선했습니다. 이로써, 영화 평가 기록이 캘린더에 효과적으로 표시되어 보다 직관적이고 유용한 사용자 경험을 제공할 수 있게 되었습니다.

추가적으로, "평균 별점"을 클릭하면 사용자가 별점을 평가한 영화 리스트를 확인할 수 있도록 기능을 구현하였습니다. 이를 통해 사용자는 자신이 평가한 영화들을 한 눈에 볼 수 있으며, 평가 내역을 쉽게 관리할 수 있습니다.
또캘린더에서 특정 날짜의 영화 포스터를 클릭했을 때에는 해당 날짜에 대한 평가 리스트로 스크롤 효과를 통해 바로 이동할 수 있도록 하여 사용자 경험을 개선하였습니다.
7. 마이 리뷰 페이지 (MovieHub / Lunar)

MovieHub: 댓글 기능이 주를 이루어 마이 페이지에 코멘트 페이지가 존재하였습니다.
Lunar: 코멘트 페이지를 삭제하고 평가 페이지와 평점 페이지로 구분하여 리뷰와 평가를 중심으로 한 기능을 강조하는 구조로 변경하여 사용자들이 더 직관적이고 상세하게 영화 정보를 평가하고 기록할 수 있도록 하였습니다.
또한, 조회 페이지도 리뷰와 평가에 초점을 맞추어 구조를 개선하였으며, 사용자가 리뷰한 작품을 다양한 기준으로 조회할 수 있도록 [제목 순], [댓글 순], [최신 순] 등의 정렬 기준을 추가하였습니다. 이로 인해 사용자는 자신의 리뷰와 평가를 더욱 효과적으로 관리하고, 원하는 방식으로 콘텐츠를 탐색할 수 있게 되었습니다.
'Backend > LUNAR' 카테고리의 다른 글
회원정보 수정 모달 추가, 이메일 유효성검사 (0) | 2024.11.24 |
---|---|
상세페이지 UI 수정, Chart.js 추가 (0) | 2024.11.22 |
헤더 pixed, 회원가입 유효성검사, 상세페이지 ing (1) | 2024.11.19 |
메인 페이지 데이터 구성 완, 스케줄러 적용 (0) | 2024.11.19 |
로그아웃, 회원가입, Messages.properties 적용! (0) | 2024.11.16 |
👇 Github
GitHub - seungHee-cat/LUNAR
Contribute to seungHee-cat/LUNAR development by creating an account on GitHub.
github.com
👇 Notion
추후에 추가 예정입니다.
개발 환경
java 버전 : 17
IDE : InteliJ
DB : MySQL
Spring Boot
프로젝트 소개
달빛 아래에서 영화를 만나다
작년 이맘때 쯤 완성했던 개인 프로젝트 MovieHub를 리팩토링한 LUNAR 프로젝트를 소개합니다. 밤하늘을 밝게 비춰주어 세상의 다른 면을 보게 해주는 달빛처럼, 영화는 우리가 살아가는 익숙한 현실의 특별한 모습을 보게 해줍니다. LUNAR는 다양한 영화와 리뷰, 그리고 코멘트를 통해 새로운 달빛을 발견할 수 있는 플랫폼입니다. 영화를 사랑하는 사람들이 모여 감상을 공유하고 느꼈던 점을 기록할 수 있는 커뮤니티입니다.
기능 소개
- 🔍 영화 검색: 별처럼 끝없는 영화 세상 속에서 원하는 작품을 검색해보세요.
- ⭐️ 영화 평가: 감상한 영화의 평가를 별점으로 남겨보세요.
- ✏️ 리뷰 작성: 영화를 보며 느낀 점을 공유하고 싶으신가요? 글로 남김으로써 기록하고, 다른 사람과 소통하세요.
- 💬 코멘트 작성: 다른 사람의 리뷰에 대해 생각과 감정을 나눠보세요.
- 🗓️ 캘린더: 평가한 작품을 달력에 기록하여 한 눈에 보기 쉽게 확인해 보세요. 자신의 기록 뿐 아니라 다른 사람의 캘린더를 볼 수도 있습니다.
소개 목차
코드 소개
- 공통 기능 관리
- Modal, Toast, Ajax와 같은 공통 함수 처리 및 관리
- 라이브러리 활용
- 외부 라이브러리 : star-rating.js, chart.js 등 외부 라이브러리 활용
- 커스텀 라이브러리 : calender, card.slide 등 프로젝트에 특화된 커스텀 컴포넌트 제작 및 적용
- TMDB API를 활용한 Movie 서비스 개발
- TMDB API 연동 및 MovieService 구성
- 다국어 지원 및 국제화 처리
- 프로젝트에서의 다국어 처리 및 국제화 적용
페이지 스크린샷 (MovieHub / Lunaer)
- 메인 화면
- 영화 상세 페이지
- 영화 검색 페이지
- 리뷰 상세 페이지
- 전체 리뷰 검색 페이지
- 유저 페이지
- 마이 리뷰 페이지
코드 소개
1. 공통 기능 관리
1-1. 모달 (Modal)
/*--------------------------------------------------------
| 리뷰 등록/수정 모달
--------------------------------------------------------*/
function reviewModal() {
let formData = new FormData();
formData.append("movieId", $("#movieDetailFrm input[name=movieId]").val());
formData.append("title", $("#movieDetailFrm input[name=title]").val());
formData.append("releaseYear", $("#movieDetailFrm input[name=releaseYear]").val());
modal.openModal("<c:url value='/review/reviewModal'/>", formData, "medium");
}

공통으로 사용하는 모달은 지정된 form 이름에 따라 동적으로 작동하며, 해당 form 내에 포함된 input hidden 요소의 속성 값을 기반으로 formData 객체에 데이터를 구성합니다. 이후, 이 formData는 openModal 함수에 인자로 전달되어 모달을 여는 동작을 수행합니다. 이를 통해 모달의 재사용성과 데이터 관리의 일관성을 확보할 수 있습니다.
modal.js 중 일부
// modal.openModal = function(url, formData, size)
if (modalElement.length === 0) {
// 모달이 없다면, 새로운 모달 HTML을 생성
$('body').append(`<div class="modal fade" id="${modalId}" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog d-flex align-items-center justify-content-center" style="min-height: 90vh;">
<div class="modal-content"></div>
</div>
</div>`);
modalElement = $(`#${modalId}`);
}
openModal 함수가 실행될 때마다 동적으로 모달 HTML 요소를 생성합니다. 만약 지정된 ID를 가진 모달이 이미 존재하지 않는 경우, 새로운 모달 HTML 구조를 동적으로 생성하여 <body> 태그 내부에 추가합니다
// modal.closeModal = function()
if (modalInstance) {
modalElement.one('hidden.bs.modal', function () {
// 모달이 닫힌 후 관련 DOM 제거
modalElement.remove(); // 모달 HTML 삭제
$('.modal-backdrop').remove(); // 백드롭 삭제
$('body').removeClass('modal-open'); // 스크롤 제어 클래스 제거
$('body').css('padding-right', ''); // 추가된 패딩 초기화
});
modalInstance.hide(); // 모달 닫기
}
모달을 닫는 버튼을 클릭하면 closeModal 함수가 실행되어 동적으로 추가된 모달을 추적하고 DOM에서 완전히 제거합니다. 생성 시 설정된 고유 id 속성을 통해 정확히 모달을 식별하며, 이를 기반으로 지정된 모달 요소만 삭제합니다.
이 방식은 모달을 닫을 때마다 해당 요소를 완전히 제거하여 DOM 내 불필요한 요소가 남아있는 것을 방지하고 메모리 사용을 최적화합니다. 또한, 새로운 모달을 열고 닫는 반복적인 동작 중에도 중복이나 충돌이 발생하지 않도록 보장합니다.
1-2. 토스트 (Toast)
function fn_showToastMessage(string) {
let toast = $('#base-toast');
toast.find('.toast-body').html(string); // 토스트 메시지 세팅
toast.toast('show'); // 토스트 표시
}
// modal.js 중 일부
modal.closeModalAndReload = function(message)
{
// 모달 닫기
modal.closeModal();
// 토스트 표시
let toast = fn_showToastMessage(message);
토스트 메시지의 경우, 별도의 공통 함수를 만들어 코드의 중복을 줄이고 필요한 곳에서 간단히 호출할 수 있도록 설계되었습니다. 이 함수는 다양한 상황에서 일관된 방식으로 토스트 메시지를 표시할 수 있도록 구현되었습니다. 또한, 모달을 닫는 동작과 토스트 메시지 호출을 하나의 함수로 결합하여 처리하는 공통 함수를 추가로 작성했습니다. 이를 통해 모달을 닫음과 동시에 사용자에게 특정 메시지를 제공하는 작업이 일관성 있게 이루어질 수 있습니다.
1-3. 비동기 처리 (Ajax)
$(document).ready( function() {
fn_myReviewAjax(); // 리뷰 조회
fn_reviewListAjax(); // 리뷰 리스트 조회
js_rateChart(); // 원형차트 조회
js_starRatingAjax(); // 별점 조회
});
// 예시: fn_reviewListAjax
/*--------------------------------------------------------
| 모든 리뷰 리스트
--------------------------------------------------------*/
function fn_reviewListAjax(){
let movieId = $("#movieDetailFrm input[name=movieId]").val();
let limit = 5;
let params = "movieId=" + movieId + "&limit=" + limit;
fn_ajaxRoadDiv("<c:url value='/review/reviewListAjax'/>", params, "reviewListAjax");
}
웹 페이지가 처음 로드되면, JavaScript가 해당 div의 id를 확인하고, 그에 맞는 JSP 파일을 AJAX 호출을 통해 서버에 요청합니다. 서버는 요청에 따라 필요한 데이터를 반환하고, 클라이언트는 이 데이터를 이용하여 화면을 갱신합니다. 이 방식은 페이지를 새로고침하지 않고도 동적으로 콘텐츠를 업데이트할 수 있어 사용자 경험을 개선하고, 성능을 높이는데 도움이 됩니다.
2. 라이브러리
2-1. 외부 라이브러리 (1) star-rating.js
<select id="star-rating" class="star-rating" onchange="loginModal();">
<option value=""></option>
<option value="5"></option>
<option value="4"></option>
<option value="3"></option>
<option value="2"></option>
<option value="1"></option>
</select>

영화 별점 평가에 사용된 star-rating 라이브러리는 기본적으로 별점이 등록되지 않으면 빈 값("")으로 설정됩니다. 사용자가 별을 클릭하면 해당 별점이 등록되며, 이미 등록된 별점을 다시 클릭하면 해당 별점이 삭제됩니다. 이를 통해 사용자에게 직관적인 별점 평가 인터페이스를 제공하며, 별점의 등록과 삭제를 간편하게 처리할 수 있습니다.
2-2. 외부 라이브러리 (2) Chart.js
/*--------------------------------------------------------
| 평점 차트
--------------------------------------------------------*/
function js_rateChart(){
let ctx = document.getElementById('chartAjax').getContext('2d');
let percentage = (${movie.voteAverage / 5.0 * 100}).toFixed(0);
let voteCount = $("input[name=voteCount]").val();
let cntText = "<spring:message code='chart.text.count'/>";
// ...생략



TMDB에서 가져온 영화의 평점을 Chart.js를 사용하여 시각화한 결과입니다. 평점에 따라 차트의 색상이 달라지도록 설정하여, 사용자가 점수를 쉽게 인식할 수 있도록 했습니다. 0~30점은 빨간색, 31~70점은 노란색, 그 이상은 초록색으로 평가 점수에 따라 차트 색이 변하도록 하여 각 구간에 맞는 색상을 통해 평가를 한눈에 파악할 수 있도록 했습니다.
2-3. 커스텀 라이브러리 (1) calender
// usrCalenderAjax.jsp
/* 캘린더 데이터 전달 */
window.initRatingList = function() {
return ${ratingList};
};
// Calender.js 중 일부
const matchingRatings = Array.isArray(ratingList)
? ratingList.filter(rating => {
const ratingDate = new Date(rating.wrtTime);
const formattedRatingDate = ratingDate.getFullYear() +
"-" +
String(ratingDate.getMonth() + 1).padStart(2, "0") +
"-" +
String(ratingDate.getDate()).padStart(2, "0");
return formattedRatingDate === currentDate;
})
: [];

유저 페이지에 사용한 캘린더는 이전 MovieHub 프로젝트에서 사용했던 코드를 재사용하였습니다. 계산된 날짜 정보를 캘린더에 표시된 각 날짜와 비교하여, 사용자가 평가를 남긴 날짜가 캘린더에 해당하는 날짜와 일치하는지 확인합니다. 일치할 경우, 해당 날짜의 셀에 해당 영화의 포스터 이미지를 동적으로 삽입합니다.
2-4. 커스텀 라이브러리 (2) card.slide

메인 페이지에서 사용한 카드 슬라이드 효과는 이전 MovieHub 프로젝트에서 사용했던 코드를 재사용하여 구현한 것입니다. 이 코드에서는 박스오피스, 넷플릭스, 왓챠 목록을 각각 슬라이드로 표시하고, 각 목록마다 고유한 슬라이더 이름과 버튼 이름을 부여하여 혼동 없이 동작하도록 했습니다. 이렇게 함으로써 각 슬라이드는 독립적으로 작동하고, 각 버튼이 해당 슬라이드만 제어하도록 하여 여러 목록이 동시에 존재하는 상황에서도 문제가 발생하지 않도록 했습니다.
3. TMDB API (MovieService)
/**
* 스케줄러를 적용하여 매일 오후 15시마다 영화 DB 업데이트
*/
@Scheduled(cron = "0 0 15 * * *")
public void updateMovieList(){
/* MOVIE SETTING START */
final String API_KEY = /* 본인의 API 키를 이곳에 입력합니다. */;
List<String> apiURL_list = new ArrayList<>();
List<Integer> movieIdLists = new ArrayList<>();
List<String> detailLists = new ArrayList<>();
apiURL_list.add("https://api.themoviedb.org/3/movie/now_playing?api_key="
+ API_KEY+"&language=ko-KR&page=1");
apiURL_list.add("https://api.themoviedb.org/3/discover/movie?api_key="
+ API_KEY+"&language=ko-KR&page=1&sort_by=popularity.desc&watch_region=KR&with_watch_providers=8");
apiURL_list.add("https://api.themoviedb.org/3/discover/movie?api_key="
+ API_KEY+"&language=ko-KR&page=1&sort_by=popularity.desc&watch_region=KR&with_watch_providers=97");
StringBuilder detailStringBuilder = new StringBuilder();
for(String apiURL : apiURL_list) {
try {
URI uri = new URI(apiURL);
URL url = uri.toURL();
BufferedReader bf = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
String result = bf.readLine();
movieIdLists.clear();
JSONObject jsonObject = new JSONObject(result);
JSONArray list = jsonObject.getJSONArray("results");
for(int j=0; j < list.length(); j++) {
JSONObject contents = list.getJSONObject(j);
int movieId = contents.getInt("id");
movieIdLists.add(movieId);
detailStringBuilder.append("https://api.themoviedb.org/3/movie/").append(movieIdLists.get(j))
.append("?api_key=").append(API_KEY).append("&language=ko-KR").append("\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
detailLists.addAll(Arrays.asList(detailStringBuilder.toString().split("\n")));
getDetailMovie(detailLists);
/* MOVIE SETTING END */
}
TMDB API를 활용하여 영화 정보를 가져오는 코드에서는 각 플랫폼별로 특화된 방식으로 데이터를 조회하고 처리합니다. 박스오피스 리스트는 현재 상영 중인 영화(Now Playing)를 기준으로 가져오며, 넷플릭스와 왓챠의 경우 각 OTT 플랫폼의 고유 번호를 기반으로 데이터를 조회했습니다. 이렇게 조회한 영화 리스트는 각각 ArrayList에 담아 관리하며, 이후 JSONObject를 사용해 JSON 데이터를 가공하여 필요한 형식으로 변환한 뒤 데이터베이스에 저장합니다.
최신 데이터를 유지하기 위해 스케줄링 기능을 적용하여 매일 오후 3시에 자동으로 영화 정보를 업데이트하도록 설정했습니다. 이 스케줄링은 Spring Framework에서 제공하는 스케줄러 어노테이션(@Scheduled)을 활용하여 구현되었으며, 이를 통해 별도의 관리자 개입 없이 데이터베이스에 최신 정보를 주기적으로 갱신할 수 있도록 자동화했습니다.

또한 Log4j2를 활용하여 애플리케이션의 로그 관리를 개선하였으며, 특히 콘솔에서 쿼리 실행 과정을 더욱 명확하게 확인할 수 있도록 설정을 최적화하였습니다. 이를 통해 쿼리 실행 시점과 내용을 직관적으로 파악할 수 있어 디버깅과 성능 분석이 한층 수월해졌습니다.
4. 다국어 지원 및 국제화 처리

다국어 지원 기능을 적용하여 사용자가 애플리케이션에서 한국어와 영어를 자유롭게 선택할 수 있도록 구현하였습니다. 언어 전환 시, 메시지 프로퍼티 파일에 저장된 다국어 리소스를 기반으로 동적으로 콘텐츠를 변경하여, 영어 환경에서도 자연스럽고 일관된 사용자 경험을 제공합니다. 이를 통해 글로벌 사용자 대상의 접근성과 편의성을 한층 강화하였으며, 다국어 확장 가능성 또한 고려한 구조로 설계되었습니다.
페이지 스크린샷
1. 메인 화면 (MovieHub / Lunar)

검색 기능을 공통 헤더로 분리하여 사용자들이 어느 페이지에서든 손쉽게 영화를 검색할 수 있도록 개선하였습니다. 이로 인해 검색의 접근성과 편의성이 대폭 향상되었습니다. 또한, 슬라이드 목록을 확장하여 더 많은 영화 콘텐츠를 제공함으로써 사용자들이 다양한 영화를 탐색할 수 있도록 했습니다. 이와 함께, 페이지의 오른쪽 하단에 다국어 기능을 추가하여 사용자가 원하는 언어로 애플리케이션을 즉시 변경할 수 있도록 구현, 글로벌 사용자 경험을 강화하였습니다.
2. 영화 상세 페이지 (MovieHub / Lunar)

별점 평가 기능에서 기존의 rate_yo 라이브러리를 star_rating 라이브러리로 변경하였습니다. 이전에 별점이 중복으로 삽입되거나 올바르게 삭제되지 않던 문제를 수정하여 기능의 안정성을 높였습니다. 또한, Chart.js 라이브러리를 적용하여 영화 평가 지수를 시각적으로 표현함으로써 데이터를 직관적으로 확인할 수 있도록 개선하였습니다.
3. 영화 검색 페이지 (MovieHub / Lunar)

초기 UI 설계 단계에서는 왓챠 피디아를 참고하여 사용자 인터페이스를 구성하였으나, 리팩토링 과정에서는 로튼토마토의 디자인 요소를 참고하여 UI를 보다 직관적이고 세련되게 개선하였습니다. 또한, 메인 화면에서 영화 카테고리가 표시되던 순서를 기준으로 검색 결과를 정렬하고 분류하여, 사용자가 원하는 영화를 더 쉽게 찾을 수 있도록 검색 기능을 최적화하였습니다.
4. 리뷰 상세 페이지 (MovieHub / Lunar)

기존에 사용하던 대댓글 기능은 필요성이 낮다고 판단하여 과감히 제거하였습니다. 대신, 사용자 리뷰에 대해 댓글을 작성하는 단순하고 직관적인 방식으로 변경하여 사용성을 개선하였습니다. 특히, 사용자가 작성한 댓글은 댓글 목록의 최상단에 위치하도록 하여 가시성을 높였으며, 댓글 작성 시간을 ‘몇 초 전’, ‘몇 분 전’, ‘몇 달 전’과 같이 상대적인 시간 형식으로 표시하여 직관적이고 실시간에 가까운 사용자 경험을 제공하도록 구현하였습니다.
5. 전체 리뷰 검색 페이지 (MovieHub / Lunar)

전체 리뷰를 검색할 수 있는 페이지에서는 기존에 스크롤 시 서서히 나타나는 애니메이션 효과를 적용했으나, 리팩토링 과정에서 이 기능을 제거하고 검색 기능과 코드 최적화에 더 많은 집중을 하였습니다. 이를 통해 검색 성능과 시스템 효율성을 크게 개선하였으며, 사용자 경험을 고려한 빠르고 안정적인 검색 기능을 제공하게 되었습니다. 최적화된 코드 구조로 유지보수성을 높이고, 페이지 로딩 속도를 개선하여 보다 원활한 사용자 인터페이스를 구현하였습니다.
6. 유저 페이지 (MovieHub / Lunar)


기존에 로그인한 유저만 자신의 프로필을 볼 수 있던 "마이 페이지"를 "유저 페이지"로 변경하여, 다른 유저들의 페이지도 열람할 수 있도록 기능을 확장하였습니다. 이를 통해 유저 간의 상호작용을 촉진하고, 유저 간 정보를 탐색할 수 있는 기회를 제공하게 되었습니다.
또한, 캘린더 기능은 기존에 작성했던 순수 JavaScript 캘린더를 그대로 사용하면서, 별점 평가를 한 날짜에 영화 포스터가 추가되도록 하여, 사용자가 월별로 자신의 영화 기록을 시각적으로 쉽게 확인할 수 있도록 개선했습니다. 이로써, 영화 평가 기록이 캘린더에 효과적으로 표시되어 보다 직관적이고 유용한 사용자 경험을 제공할 수 있게 되었습니다.

추가적으로, "평균 별점"을 클릭하면 사용자가 별점을 평가한 영화 리스트를 확인할 수 있도록 기능을 구현하였습니다. 이를 통해 사용자는 자신이 평가한 영화들을 한 눈에 볼 수 있으며, 평가 내역을 쉽게 관리할 수 있습니다.
또캘린더에서 특정 날짜의 영화 포스터를 클릭했을 때에는 해당 날짜에 대한 평가 리스트로 스크롤 효과를 통해 바로 이동할 수 있도록 하여 사용자 경험을 개선하였습니다.
7. 마이 리뷰 페이지 (MovieHub / Lunar)

MovieHub: 댓글 기능이 주를 이루어 마이 페이지에 코멘트 페이지가 존재하였습니다.
Lunar: 코멘트 페이지를 삭제하고 평가 페이지와 평점 페이지로 구분하여 리뷰와 평가를 중심으로 한 기능을 강조하는 구조로 변경하여 사용자들이 더 직관적이고 상세하게 영화 정보를 평가하고 기록할 수 있도록 하였습니다.
또한, 조회 페이지도 리뷰와 평가에 초점을 맞추어 구조를 개선하였으며, 사용자가 리뷰한 작품을 다양한 기준으로 조회할 수 있도록 [제목 순], [댓글 순], [최신 순] 등의 정렬 기준을 추가하였습니다. 이로 인해 사용자는 자신의 리뷰와 평가를 더욱 효과적으로 관리하고, 원하는 방식으로 콘텐츠를 탐색할 수 있게 되었습니다.
'Backend > LUNAR' 카테고리의 다른 글
회원정보 수정 모달 추가, 이메일 유효성검사 (0) | 2024.11.24 |
---|---|
상세페이지 UI 수정, Chart.js 추가 (0) | 2024.11.22 |
헤더 pixed, 회원가입 유효성검사, 상세페이지 ing (1) | 2024.11.19 |
메인 페이지 데이터 구성 완, 스케줄러 적용 (0) | 2024.11.19 |
로그아웃, 회원가입, Messages.properties 적용! (0) | 2024.11.16 |