프로젝트 주제 (팀 프로젝트로 진행)
영화 정보를 제공 하는 영화 리뷰 웹 사이트 제작
http://film-e.pe.kr
Film-E | Main
film-e.pe.kr
주제 선정 이유
- With Corona로 전환을 앞두고, 영화관을 찾는 고객의 증가가 예상.
- 따라서 다양한 영화 정보를 얻기 위한 사용자들의 니즈가 늘어나게 될 것으로 예측
- 영화에 관한 다양한 시각들을 공유 - 해외에는 로튼토마토, IMDB 같은 영화 리뷰를 보여주는 전문적인 사이트가 존재.
- 국내 : 전문 영화 리뷰 플랫폼의 부재
- 해외 : 언어적인 문제와 대한민국 정서에 특화된 디자인 구현 가능
- Film-E (영화리뷰) 프로그램 기획 - 기생충, 미나리, 오징어게임 등 전세계적으로 OTT 플랫폼 서비스를 통한 K영화,드라마에 대한 해외의 관심 증
- K문화의 전문적인 사이트로서 - 추후 예고편 및 예매사이트 연결 등으로 다양한 개발의 방향성 제시 가능
목표
- MVC 패턴 기반 (본인 담당 : Controller)
- 크롤링을 활용한 샘플 데이터 삽입
- 파일 입출력
- Ajax를 활용한 비동기적 처리 방식으로 SPA 구현
- 반응형 웹 앱 구현
주요 기능
- 리뷰 작성 및 별점 기능
- 영화 정보 추가/수정 시 파일 입출력 및 미리보기 기능
- 회원 가입 시 사용자로부터 입력 받은 값에 대하여 실시간으로 유효성 검사 실행
- 랜덤 영화 추천
- 보안 기능 - Admin 계정 진입시 유효성 체크와 uri입력을 통한 진입을 방지
내가 맡은 역할
- Controller - ClientDB, ReviewDB 처리 담당
- Crawling - Jsoup을 이용하여 샘플 데이터로 사용하기 위한 영화 데이터와 리뷰 데이터 크롤링
- 별점 기능 구현
개발 환경
- Language : JAVA (Eclipse)
- Server : Apache Tomcat
- DBMS : MySQL
- Version Control : GitHub
- Front-End : JSP / Java Script / jQuery / CSS
개발 일정 (2021-09/02 ~ 2021-10-18)
ERD
각 테이블은 참조되지 않고 트랜잭션 처리
ex) 회원 탈퇴 시 해당 회원의 리뷰 작성자명을 '알 수 없음'으로 처리
User Flow
Logic Process
- 카테고리 페이지에 진입시 DB의 movie 테이블을 조회하여
검색, 페이징, 장르선택을 고려하여 별점순으로 영화 리스트를 출력 - 영화를 선택하여 리뷰페이지에 진입시, DB의 review 테이블을
조회하여 페이징을 고려하여 최신순으로 리뷰 리스트를 출력 - 리뷰는 영화당 1개만 입력가능하고, 별점과 리뷰를 입력하면
현재시간으로 DB의 review 테이블에 저장
- 관리자 페이지에 진입시 DB의 movie 테이블을 조회하여 검색, 페이징 고려하여 영화 리스트를 출력
- 영화 등록 또는 영화 수정을 선택시 암호를 통해 관리자 인증을 한 후에 관리 페이지로 이동
- 영화 등록, 수정, 삭제시 DB의 movie 테이블에 추가
- 로그인 페이지에서 DB의 client 테이블을 조회하여 아이디와 비밀번호를 비교하여 로그인을 수행하고, 세션에 아이디 정보를 저장
- 회원가입 페이지에서 정보를 입력받아 DB의 client테이블에 추가
- my페이지에 진입시 세션에 저장된 아이디를 비교하여 DB의 client에서 회원정보 출력
Version
ver. | 업데이트 기능 | 개발 일자 |
Film-E 1.1.1 | 1차 버그 리포트 MVC 취합 |
21.09.26 |
Film-E 1.1.4 | 파일 입출력 구현 | 21.09.29 |
Film-E 1.1.5 | 영화 크롤링 구현 페이징 구현 |
21.09.30 |
Film-E 1.2.1 | 리뷰 크롤링 구현 | 21.10.04 |
Film-E 1.2.4 | 미리보기 구현 | 21.10.06 |
Film-E 2.1.1 | 2차 버그 리포트 | 21.10.09 |
Film-E 2.1.3 | 별점 기능 구현 | 21.10.12 |
Film-E 2.1.6 | Admin 보안 기능 구현 | 21.10.15 |
페이지 별 기능
메인 페이지
① 로고 이미지 클릭 시 메인 페이지 이동
② 활성 페이지
③ 각 메뉴 클릭 시 해당 페이지 이동
④ 영화 제목으로 검색
⑤ 로그인 페이지 이동
⑥ 스와이프 방식
이미지 클릭 시 해당 영화 리뷰로 이동
⑦ 네이버 영화에서 크롤링한 데이터를
평점 기준 내림차순으로 정렬
로그인 페이지
① 아이디 입력창
② 비밀번호 입력창
③ 로그인 버튼
④ 회원가입 페이지로 이동
회원가입 페이지
① 아이디 입력창
- Ajax를 이용한 비동기처리 방식으로 아이디 중복 검사
② 비밀번호 입력창
③ 비밀번호 확인 입력창
④ 이메일 입력창
> ①~④ jQuery를 이용한 실시간 유효성 검사
⑤ 회원가입 버튼
⑥ 초기화 버튼
⑦ 개인 정보 보호 정책 동의
카테고리 페이지
① 카테고리 표시
② 영화 이미지
- 제목 순으로 정렬
- 영화 상세 정보 페이지로 이동
③ 페이지네이션
- 한 페이지 당 8개 출력
- 현재 페이지 표시
④ 카테고리 메뉴
- 아코디언 형식으로 장르 표시
리뷰 페이지
① 각 영화 별 제목, 장르, 평점, 개봉일, 줄거리
표시
② 비 로그인 시 댓글 달기 불가능 및
로그인 시 1000자 이내 댓글 달기 가능
(이미 댓글을 달았을 시 댓글 삭제 가능)
③ 랜덤으로 영화 추천 기능
④ 해당 영화 리뷰 최신 순으로 정렬
관리자 페이지
① 관리자 메뉴
- 관리자 계정으로 로그인 시 메뉴 표시
② 영화 이미지
- 클릭 시 영화 수정 페이지로 이동
③ 영화 등록 버튼
- 영화 등록 페이지로 이동
관리자 보안 설정
① 관리 비밀번호
- 영화 삽입, 수정 페이지 진입 시 Tomcat
서버에 설정된 암호를 입력해야 진입 가능
② 확인 버튼
영화 등록 페이지
① 이미지 파일 등록 ( jpeg, jpg, png 가능)
② 썸네일 미리 보기
③ 영화 제목 작성
④ 영화 장르 선택
⑤ 영화 개봉일 선택
⑥ 영화 줄거리 작성
⑦ 영화 정보 등록 버튼
영화 수정/삭제 페이지
① 사진 파일 등록 가능( jpeg, jpg, png 가능)
② 썸네일 미리 보기
③ 영화 제목 작성
④ 영화 개봉일 선택
⑤ 영화 줄거리 작성
⑥ 영화 정보 수정 버튼
⑦ 영화 삭제 버튼
주요 기능 코드
1. 보안 설정
1-1. 관리자 전용 ID/PW 설정 - tomcat 서버의 tomcat-users.xml 파일 내 <tomcat-users> 태그 안에 아래 코드 작성
<tomcat-users>
<role rolename="admin"/>
<user username="admin" password="1234" roles="admin"/>
</tomcat-users>
1-2. 프로젝트의 web.xml 파일에 관리자 페이지들 중 영화 추가/수정 페이지 진입 시 보안 설정이 작동하도록 설정
<security-role>
<role-name>admin</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>JspBook</web-resource-name>
<url-pattern>/Admin.do</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<description></description>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
1-3. 관리자 로그인 폼을 통해 정확한 암호를 입력 시 해당 보안 통과
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/adminlogin.jsp</form-login-page>
<form-error-page>/adminlogin.jsp</form-error-page>
</form-login-config>
</login-config>
2. 파일 입출력 및 이미지 파일 미리보기
2-1. Form Data
enctype : form data가 서버로 제출될 때 해당 데이터가 인코딩되는 방식을 명시하는 속성
multipart/form-data: formdata가 여러부분으로 나뉘어서 서버로 전송됨
2-2. Controller
① 실제로 서버에 저장되는 path를 “img”로 설정 + 파일의 최대 크기를 16mb로 설정
② 파라미터로 request, 파일 저장 경로, 최대 용량, 인코딩, 중복 파일명에 대한 정책을 받음.
DefaultFilerenamePolicy() : 중복되는 파일 이름이 존재하는 경우 파일명 뒤에 숫자(n)를 붙여줌으로써 파일명 중복 방지
③ view에서 form형식을 enctype="multipart/form-data"로 보내주기때문에 컨트롤러에서 값을 받을 때 request가 아닌 선언한 multi로 받아줘야 함.
④ 영화 데이터 insert가 실패될 경우 등록 시 넣은 이미지 파일 데이터 삭제
2-3. 이미지 파일 미리보기
- 이미지 파일 미리보기를 위한 form
- 이미지 파일만 필터링해서 보이도록 accept 속성을 부여
- 선택한 파일의 객체를 파라미터로 넘겨 파일 타입 비교
document.addEventListener('DOMContentLoaded', function(){
// 이미지 객체 타입으로 이미지 확장자 밸리데이션
var validateType = function(img){
return (['image/jpeg','image/jpg','image/png'].indexOf(img.type) > -1);
}
var validateName = function(fname){
let extensions = ['jpeg','jpg','png'];
let fparts = fname.split('.');
let fext = '';
if(fparts.length > 1){
fext = fparts[fparts.length-1];
}
let validated = false;
if(fext != ''){
extensions.forEach(function(ext){
if(ext == fext){
validated = true;
}
});
}
return validated;
}
- 파일 입력 필드는 파일을 선택하면 "change" 이벤트 발생
-> "change" 이벤트 리스너를 등록하여 파일 선택 시 썸네일 미리보기 처리
// 파일 선택 필드에 이벤트 리스너 등록
document.getElementById('imageSelector').addEventListener('change', function(e){
let elem = e.target;
if(validateType(elem.files[0])){
let preview = document.querySelector('.thumb');
preview.src = URL.createObjectURL(elem.files[0]); // 파일 객체에서 이미지 데이터 가져옴.
document.querySelector('.dellink').style.display = 'block'; // 이미지 삭제 링크 표시
preview.onload = function() {
URL.revokeObjectURL(preview.src); // URL 객체 해제
}
}else{
console.log('이미지 파일이 아닙니다.');
}
});
document.querySelector('.dellink').addEventListener('click', function(e){
let dellink = e.target;
let preview = dellink.previousElementSibling;
preview.src = ''; // 썸네일 이미지 src 데이터 해제
document.querySelector('.dellink').style.display = 'none';
});
});
3. 별점 기능
3-1. 라디오 버튼을 이용하여 별점 입력
<fieldset style="float: right;">
<input type="radio" name="rating" value="5" id="rate1">
<label for="rate1">⭐</label>
<input type="radio" name="rating" value="4" id="rate2">
<label for="rate2">⭐</label>
<input type="radio" name="rating" value="3" id="rate3" checked>
<label for="rate3">⭐</label>
<input type="radio" name="rating" value="2" id="rate4">
<label for="rate4">⭐</label>
<input type="radio" name="rating" value="1" id="rate5">
<label for="rate5">⭐</label>
</fieldset>
3-2. CSS를 이용하여 라디오버튼을 감추고 ☆를 클릭할수 있도록 변경
@charset "UTF-8";
#myform fieldset{
display: inline-block; /* 하위 별점 이미지들이 있는 영역만 자리를 차지함.*/
border: 0; /* 필드셋 테두리 제거 */
}
#myform input[type=radio]{
display: none; /* 라디오박스 감춤 */
}
#myform label{
font-size: 1.5em; /* 이모지 크기 */
color: transparent; /* 기존 이모지 컬러 제거 */
text-shadow: 0 0 0 #f0f0f0; /* 새 이모지 색상 부여 */
}
#myform label:hover{
color:black; /* text 컬러 원상태로 */
opacity:0.5; /* text 투명도 50% */
}
#myform label:hover ~ label{
opacity:0.5;
color:black;
}
#myform fieldset{
display: inline-block; /* 하위 별점 이미지들이 있는 영역만 자리를 차지함.*/
direction: rtl; /* 이모지 순서 반전 */
border: 1px; /* 필드셋 테두리 제거 */
}
#myform fieldset legend{
text-align: left;
}
#myform input[type=radio]:checked ~ label{
opacity:1; /* text 투명도 100% */
color:black;
}
3-3. 별점 등록 시 소수점 첫번째 자리 수 까지만 저장 + SQL AVG 함수를 이용하여 별점의 평균 계산 - 트래잭션 처리
String starAVG= "select avg(rating) from review where mpk = ?"; // 별점 평균 계산
String mUpdate= "update movie set ratingavg=? where mpk=?"; // 별점 평균을 해당 영화에 저장
public boolean r_insertDB(ReviewVO vo) throws SQLException{ // 리뷰 등록
conn =JNDI.connect();
boolean result =false;
try {
conn.setAutoCommit(false);
pstmt=conn.prepareStatement(insert); // 리뷰 등록
pstmt.setString(1, vo.getCmt());
pstmt.setString(2, vo.getId());
pstmt.setString(3, vo.getMpk());
pstmt.setDouble(4, vo.getRating()); // 별점 추가
pstmt.executeUpdate();
pstmt=conn.prepareStatement(starAVG); // 별점 평균
pstmt.setString(1, vo.getMpk());
ResultSet rs=pstmt.executeQuery();
if(rs.next()) {
ratingavg=rs.getDouble(1);
}
rs.close();
ratingavg = Math.round(ratingavg*10)/10.0;
pstmt=conn.prepareStatement(mUpdate); // 별점 평균을 해당 영화에 저장
pstmt.setDouble(1, ratingavg);
pstmt.setString(2, vo.getMpk());
pstmt.executeUpdate();
conn.commit();
conn.setAutoCommit(true);
result = true;
} catch (SQLException e) {
e.printStackTrace();
conn.rollback();
conn.setAutoCommit(true);
} finally {
JNDI.disconnect(pstmt, conn);
}
return result;
}
개선 사항
현재 랜덤으로 영화를 추천해주고 있지만 많은 데이터가 쌓일 경우 회원들의 리뷰와 별점 데이터를 분석하여
각자 취향에 맞는 영화를 추천해주는 서비스로 개선할 수 있을거 같다.
느낀점
MVC 기반의 웹 제작에 대한 전반적인 흐름을 확실히 이해할 수 있는 유익한 시간이었다.
크롤링, 파일 입출력, 별점 처리 등 전에 해보지 않았던 기능들을 직접 구현하고 시도해 보면서
다양한 기능을 배울 수 있었고, 팀원들과 함께 소통하고 협력하며 즐겁게 프로젝트에 임할 수 있었다.
내가 구현한 기능들 외에 다른 팀원들의 코드를 분석하며 공부할 수 있는 시간이기도 했다.
현재 프로젝트 이전에 맨 처음 팀 프로젝트를 진행했을 때에는 소통의 부재와 부족한 설계로 난관에 부딪히기도 했지만,
이를 계기로 서로 간에 의사소통의 중요성을 느끼고 배울 수 있는 기회가 되었고,
여러 번의 팀 프로젝트를 거듭해 나가면서 탄탄한 설계를 통해 기대보다 더 완성도 있는 결과물이 만들어진 것 같아
만족스러운 프로젝트였다. (아주 재밌었음!)
Git : https://github.com/sngynhy/Fiim-E
Hosting : http://film-e.pe.kr
2021.10.01 - [PROJECT] - [JAVA] 네이버 영화 크롤링하여 DB에 저장하기 (영화 정보 및 리뷰)
[JAVA] 네이버 영화 크롤링하여 DB에 저장하기 (영화 정보 및 리뷰)
https://movie.naver.com/movie/running/current.naver 현재상영작 : 네이버 영화 상영 중 영화의 예매율/평점/좋아요 순 정보 제공. movie.naver.com 크롤링 과정 1. 아래 페이지에서 각 포스터 이미지 클릭을 하..
sngynhy.tistory.com
'PROJECT' 카테고리의 다른 글
[React] 넷플릭스 클론 프로젝트 [1] - 들어가며 (0) | 2025.01.20 |
---|---|
[Spring] 스프링 프로젝트 - 웹 커뮤니티 'WANDERIZM' (0) | 2021.12.08 |
[JAVA] 네이버 영화 크롤링하여 DB에 저장하기 (영화 정보 및 리뷰) (0) | 2021.10.01 |
[HTML/CSS/JavaScript/jQuery] 미디어쿼리를 이용한 반응형 웹 제작 - 메뉴바 / Footer (0) | 2021.08.23 |
[JAVA] 항공권 예매 프로젝트 (M-V-C) (0) | 2021.08.04 |