본문 바로가기
  • 일하면서 배운 내용 끄적이는 블로그
개인 프로젝트

부스트코스 - 네이버 예약

by dhl7799 2024. 3. 13.

부스트코스의 마지막 프로젝트인 네이버 예약

 

다만 전부 만든건 아니고 챕터3 부분까지만 진행했다.

 

더 할지는 모르겠다

 

개발하면서 가장 어려웠던 부분은 상품이 로딩될때 벽돌구조처럼 imgBox끼리 크기가 달라도 딱딱 맞게 로딩되는 부분인데

 

이걸 masonry라는 라이브러리를 사용해서 구현하니 가장 원하는 형태로 구현이 됐지만 문제는 동적으로 로딩하다가 아이템끼리 겹쳐버리는 오류가 있는데

 

찾아보니 걍 라이브러리 문제같아서 내가 어떻게 고칠수가 없었다

 

그래서 그거 빼버리고 그냥 다중컬럼으로 구현했는데

 

이러니 이미지 로딩 순서가 맘에 안든다...

 

아무튼 중단이라 거기서 더 수정하진 않았다.

 

2024-03-18

이미지 로딩 순서 수정 - mainPage.js

전체 이미지를 .empty() 함수를 통해 한번 다 비운 다음

for문을 startIndex부터 endIndex가 아닌 0부터 endIndex까지 돌리는 방식으로 변경하고

for문 내에서 바로 append() 시키는것이 아닌 각각 짝수 배열 홀수 배열에 담은 다음

짝수, 홀수 배열 순으로 append() 시켜서(번호가 0부터 시작하므로 짝수 홀수 순서)

좌측 컬럼에는 짝수 product들이, 우측엔 홀수 product들이 위치하게 만듬

 

상단

 

 

하단

 

 

더보기 버튼 클릭시 4개 추가로 로딩

 

 

백엔드

Controller

데이터를 가져오는 api는 따로 RestApiController로 구현했다.

 

MainController.java

더보기

package kr.or.connect.reserve.controller;

import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import kr.or.connect.reserve.service.ProductService;
import kr.or.connect.reserve.service.PromotionService;
import kr.or.connect.reserve.vo.ProductVo;
import kr.or.connect.reserve.vo.PromotionVo;

@Controller
public class MainController {

@Autowired
PromotionService promotionservice;

@RequestMapping("/main")
public String main(Model model) {
System.out.println("메인페이지로");

ObjectMapper objectMapper = new ObjectMapper();

List<String> filelist = promotionservice.selectAllPromotionFiles();

String filelistJson = null;

try {

filelistJson = objectMapper.writeValueAsString(filelist);

} catch (JsonProcessingException e) {

e.printStackTrace();

}

model.addAttribute("filelist", filelistJson);

return "/main/mainPage";
}


}

RestApiController.java

더보기

package kr.or.connect.reserve.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import kr.or.connect.reserve.service.ProductService;
import kr.or.connect.reserve.vo.ProductVo;

@RestController
public class RestApiController {

@Autowired
ProductService productservice;

@GetMapping("/getProductList/all")
public List<ProductVo> ProductListAll(Model model) {

List<ProductVo> productlist = productservice.selectAllProduct();

return productlist;
}

@GetMapping("/getProductList/exhibit")
public List<ProductVo> ProductListExhibit(Model model) {

List<ProductVo> productlist = productservice.selectExhibitProduct();

return productlist;
}

@GetMapping("/getProductList/musical")
public List<ProductVo> ProductListMusical(Model model) {

List<ProductVo> productlist = productservice.selectMusicalProduct();

return productlist;
}

@GetMapping("/getProductList/concert")
public List<ProductVo> ProductListConcert(Model model) {

List<ProductVo> productlist = productservice.selectConcertProduct();

return productlist;
}

@GetMapping("/getProductList/classic")
public List<ProductVo> ProductListClassic(Model model) {

List<ProductVo> productlist = productservice.selectClassicProduct();

return productlist;
}

@GetMapping("/getProductList/drama")
public List<ProductVo> ProductListDrama(Model model) {

List<ProductVo> productlist = productservice.selectDramaProduct();

return productlist;
}

}

각 탭을 눌렀을때 해당 카테고리에 해당되는 상품들을 불러온다

 

Service & ServiceImpl

서비스는 인터페이스 구조로 분리하여 개발했다.

 

ProductService.java

더보기

package kr.or.connect.reserve.service;

import java.util.List;

import kr.or.connect.reserve.vo.ProductVo;

public interface ProductService {

public List<ProductVo> selectAllProduct();

public List<ProductVo> selectExhibitProduct();

public List<ProductVo> selectMusicalProduct();

public List<ProductVo> selectConcertProduct();

public List<ProductVo> selectClassicProduct();

public List<ProductVo> selectDramaProduct();

}

ProductServiceImpl.java

더보기

package kr.or.connect.reserve.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import kr.or.connect.reserve.repository.ProductDao;
import kr.or.connect.reserve.service.ProductService;
import kr.or.connect.reserve.vo.ProductVo;

@Service
public class ProductServiceImpl implements ProductService{

@Autowired
ProductDao productdao;

public List<ProductVo> selectAllProduct() {
return productdao.selectAllProduct();
}

public List<ProductVo> selectExhibitProduct(){
return productdao.selectExhibitProduct();
}

public List<ProductVo> selectMusicalProduct(){
return productdao.selectMusicalProduct();
}

public List<ProductVo> selectConcertProduct(){
return productdao.selectConcertProduct();
}

public List<ProductVo> selectClassicProduct(){
return productdao.selectClassicProduct();
}

public List<ProductVo> selectDramaProduct(){
return productdao.selectDramaProduct();
}
}

사실 서비스도 별다른 처리를 해줄게 없어서 그져 Dao를 그대로 불러올뿐..

 

PromotionService.java

더보기

package kr.or.connect.reserve.service;

import java.util.List;

import kr.or.connect.reserve.vo.PromotionVo;

public interface PromotionService {

public List<String> selectAllPromotionFiles();
}

PromotionServiceImpl.java

더보기

package kr.or.connect.reserve.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import kr.or.connect.reserve.repository.PromotionDao;
import kr.or.connect.reserve.service.PromotionService;
import kr.or.connect.reserve.vo.PromotionVo;

@Service
public class PromotionServiceImpl implements PromotionService{

@Autowired
PromotionDao promotiondao;

public List<String> selectAllPromotionFiles() {
return promotiondao.selectAllPromotionFiles();
}
}

 

Repository

DB쪽은 Mybatis를 사용하여 개발했다.

 

ProductDao.java

더보기

package kr.or.connect.reserve.repository;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.or.connect.reserve.vo.ProductVo;

@Repository
public class ProductDao {

@Autowired
SqlSession sqlsession;

public List<ProductVo> selectAllProduct() {
return sqlsession.selectList("product.selectAllProduct");
}

public List<ProductVo> selectExhibitProduct() {
return sqlsession.selectList("product.selectExhibitProduct");
}

public List<ProductVo> selectMusicalProduct() {
return sqlsession.selectList("product.selectMusicalProduct");
}

public List<ProductVo> selectConcertProduct() {
return sqlsession.selectList("product.selectConcertProduct");
}

public List<ProductVo> selectClassicProduct() {
return sqlsession.selectList("product.selectClassicProduct");
}

public List<ProductVo> selectDramaProduct() {
return sqlsession.selectList("product.selectDramaProduct");
}
}

 

PromotionDao.java

더보기

package kr.or.connect.reserve.repository;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.or.connect.reserve.vo.PromotionVo;

@Repository
public class PromotionDao {

@Autowired
SqlSession sqlsession;

public List<String> selectAllPromotionFiles(){
return sqlsession.selectList("promotion.selectAllPromotionFiles");
}

}

 

DTO

VO 자체는 여러개를 생성했는데 중간에 멈춰버려서 쓴건 ProductVo밖에 없는듯..

 

ProductVo.java

더보기

package kr.or.connect.reserve.vo;

public class ProductVo {
private int id;
private int category_id;
private String description;
private String content;
private String event;
private String create_date;
private String modify_date;
private String file_name;
private String place_name;

public ProductVo() {

}

public ProductVo(int id, int category_id, String description, String content, String event, String create_date,
String modify_date, String file_name, String place_name) {
super();
this.id = id;
this.category_id = category_id;
this.description = description;
this.content = content;
this.event = event;
this.create_date = create_date;
this.modify_date = modify_date;
this.file_name = file_name;
this.place_name = place_name;
}

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getCategory_id() {
return category_id;
}
public void setCategory_id(int category_id) {
this.category_id = category_id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
public String getCreate_date() {
return create_date;
}
public void setCreate_date(String create_date) {
this.create_date = create_date;
}
public String getModify_date() {
return modify_date;
}
public void setModify_date(String modify_date) {
this.modify_date = modify_date;
}
public String getFile_name() {
return file_name;
}
public void setFile_name(String file_name) {
this.file_name = file_name;
}

public String getPlace_name() {
return place_name;
}

public void setPlace_name(String place_name) {
this.place_name = place_name;
}

@Override
public String toString() {
return "ProductVo [id=" + id + ", category_id=" + category_id + ", description=" + description + ", content="
+ content + ", event=" + event + ", create_date=" + create_date + ", modify_date=" + modify_date
+ ", file_name=" + file_name + ", place_name=" + place_name + "]";
}

}

PromotionVo.java

더보기

package kr.or.connect.reserve.vo;

public class PromotionVo {
private int id;
private int product_id;

public PromotionVo(int id, int product_id) {
super();
this.id = id;
this.product_id = product_id;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public int getProduct_id() {
return product_id;
}

public void setProduct_id(int product_id) {
this.product_id = product_id;
}

@Override
public String toString() {
return "PromotionVo [id=" + id + ", product_id=" + product_id + "]";
}
}

 

설정파일들

Mybatis 쓸거라고 설정하는데 애먹었던것 같다

(부스트코스 따라가면서 만든 프로젝트에다가 하려니 어떻게 하는지를 모르겠...)

그래서 예전에 비트교육센터에서 6개월 과정 들을때 썻던 프로젝트 들고와서 버전만 뜯어고쳤다

(가능하면 그때 개발했던 프로젝트도 정리해서 올려보고싶다)

 

applicationContext.xml

더보기

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- auto proxy -->
<aop:aspectj-autoproxy />

<!-- Connection Pool DataSource -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="cohttp://m.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/connectdb?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC" />
<property name="username" value="connectuser" />
<property name="password" value="connectdb" />
</bean>

<!-- MyBatis SqlSessionFactoryBean -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation"
value="classpath:mybatis/configuration.xml" />
</bean>

<!-- MyBatis SqlSessionTemplate -->
<bean id="sqlSession"
class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

<context:annotation-config />
<context:component-scan
base-package="kr.or.connect.reserve.repository, kr.or.connect.reserve.service, kr.or.connect.reserve.controller">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Repository" />
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service" />
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Component" />
</context:component-scan>

</beans>

pom.xml

더보기

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>kr.or.connect.reserve</groupId>
<artifactId>reservation</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.springframework-version>4.3.5.RELEASE</org.springframework-version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<jackson2.version>2.8.6</jackson2.version>
</properties>
<dependencies>
<!-- Jackson Module -->
<dependency>
<groupId>cohttp://m.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson2.version}</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>${jackson2.version}</version>
</dependency>

<!-- spring jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>

<!-- Spring Context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
<!-- JCL 제외 -->
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-jcl</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- jackson -->
<dependency>
<groupId>cohttp://m.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4.1</version>
</dependency>

<!-- validation -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.3.Final</version>
</dependency>

<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- spring aspect -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${org.springframework-version}</version>
</dependency>

<!-- MariaDB JDBC Driver -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.0.8</version>
</dependency>
<!-- Common DBCP -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!-- common fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
</dependencies>
<build>
<finalName>reservation</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven 
defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

 

Mybatis

여기서부턴 Mybatis 관련 파일이다

 

configuration.xml

더보기

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- <settings>
프로젝션 컬럼 이름의 _를 대문자로 자동 변경 
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
-->
<typeAliases>
<typeAlias type="kr.or.connect.reserve.vo.PromotionVo" alias="promotion"/>
<typeAlias type="kr.or.connect.reserve.vo.ProductVo" alias="product"/>
    </typeAliases>
<mappers>
<mapper resource="mybatis/mappers/promotion.xml" />
<mapper resource="mybatis/mappers/product.xml" />
</mappers>
</configuration>

주석처리한 부분은 예를들어 컬럼이 user_name 이라고 되어있으면

해당부분을 userName이라고 인식하게 하는 부분

필요없어서 주석처리 했다

 

product.xml

더보기

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="product">
<select id="selectAllProduct" resultType="product">
<![CDATA[
SELECT product.id,
product.category_id,
product.description,
product.content,
product.event,
product.create_date,
product.modify_date,
file_info.file_name,
display_info.place_name
FROM product
LEFT OUTER JOIN display_info
ON product.id = display_info.product_id
LEFT OUTER JOIN product_image
ON product.id = product_image.product_id
LEFT OUTER JOIN file_info
ON product_image.file_id = file_info.id
WHERE product_image.type = 'th'
]]>
</select>

<select id="selectExhibitProduct" resultType="product">
<![CDATA[
SELECT product.id,
product.category_id,
product.description,
product.content,
product.event,
product.create_date,
product.modify_date,
file_info.file_name,
display_info.place_name
FROM product
LEFT OUTER JOIN display_info
ON product.id = display_info.product_id
LEFT OUTER JOIN product_image
ON product.id = product_image.product_id
LEFT OUTER JOIN file_info
ON product_image.file_id = file_info.id
WHERE product_image.type = 'th'
AND product.category_id = 1
]]>
</select>

<select id="selectMusicalProduct" resultType="product">
<![CDATA[
SELECT product.id,
product.category_id,
product.description,
product.content,
product.event,
product.create_date,
product.modify_date,
file_info.file_name,
display_info.place_name
FROM product
LEFT OUTER JOIN display_info
ON product.id = display_info.product_id
LEFT OUTER JOIN product_image
ON product.id = product_image.product_id
LEFT OUTER JOIN file_info
ON product_image.file_id = file_info.id
WHERE product_image.type = 'th'
AND product.category_id = 2
]]>
</select>

<select id="selectConcertProduct" resultType="product">
<![CDATA[
SELECT product.id,
product.category_id,
product.description,
product.content,
product.event,
product.create_date,
product.modify_date,
file_info.file_name,
display_info.place_name
FROM product
LEFT OUTER JOIN display_info
ON product.id = display_info.product_id
LEFT OUTER JOIN product_image
ON product.id = product_image.product_id
LEFT OUTER JOIN file_info
ON product_image.file_id = file_info.id
WHERE product_image.type = 'th'
AND product.category_id = 3
]]>
</select>

<select id="selectClassicProduct" resultType="product">
<![CDATA[
SELECT product.id,
product.category_id,
product.description,
product.content,
product.event,
product.create_date,
product.modify_date,
file_info.file_name,
display_info.place_name
FROM product
LEFT OUTER JOIN display_info
ON product.id = display_info.product_id
LEFT OUTER JOIN product_image
ON product.id = product_image.product_id
LEFT OUTER JOIN file_info
ON product_image.file_id = file_info.id
WHERE product_image.type = 'th'
AND product.category_id = 4
]]>
</select>

<select id="selectDramaProduct" resultType="product">
<![CDATA[
SELECT product.id,
product.category_id,
product.description,
product.content,
product.event,
product.create_date,
product.modify_date,
file_info.file_name,
display_info.place_name
FROM product
LEFT OUTER JOIN display_info
ON product.id = display_info.product_id
LEFT OUTER JOIN product_image
ON product.id = product_image.product_id
LEFT OUTER JOIN file_info
ON product_image.file_id = file_info.id
WHERE product_image.type = 'th'
AND product.category_id = 5
]]>
</select>
</mapper>

promotion.xml

더보기

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="promotion">
<select id="selectAllPromotionFiles" resultType="string">
<![CDATA[
SELECT file_info.file_name
FROM promotion
INNER JOIN product_image
ON promotion.product_id = product_image.product_id
INNER JOIN file_info
ON product_image.file_id = file_info.id
WHERE product_image.type = 'th'
]]>
</select>
</mapper>

 

프론트엔드

mainPage.jsp

더보기

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<!DOCTYPE html>
<html>
<head>

<link rel="icon" href="<c:url value='/favicon.ico'/>" type="image/x-icon"/>
<title>reservation</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<link
href="${pageContext.request.contextPath }/assets/css/mainPage.css?after"
rel="stylesheet" type="text/css">
<script
src="${pageContext.request.contextPath}/assets/js/mainPage.js?ver=220685"></script>
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css" />
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick-theme.min.css" />
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"></script>

<script src="https://cdn.jsdelivr.net/npm/muuri/dist/muuri.min.js"></script>
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.js"></script>
</head>
<body>
<script>
var images = ${filelist};
</script>
<div class="bodydiv">
<header>
<div class="logoimg">
<a href="https://m.naver.com/" class="naver-logo"></a> <a
href="http://localhost:8080/reservation/main" class="reserve-link"></a>
</div>
<div class="email">donghyundhl@naver.com</div>
</header>
<div class="promotionArea">
<div class="promotion"></div>
</div>
<div class="tab">
<ul class="tabs">
<li class="active" rel="tab1">전체리스트</li>
<li rel="tab2">전시</li>
<li rel="tab3">뮤지컬</li>
<li rel="tab4">콘서트</li>
<li rel="tab5">클래식</li>
<li rel="tab6">연극</li>
</ul>

<div class="tabCount">
<p class="countComment">
바로 예매 가능한 전시, 공연, 행사가 <span class="redNumber">1</span>개 있습니다
</p>
</div>

<div class="tab_container">
<div id="tab1" class="tab_content">
</div>
<div id="tab2" class="tab_content">
</div>
<div id="tab3" class="tab_content">
</div>
<div id="tab4" class="tab_content">
</div>
<div id="tab5" class="tab_content">
</div>
<div id="tab6" class="tab_content">
</div>
</div>

</div>
<footer>
<button class="more" id="more">더보기</button>
<div class="gotop">
<a href="#" class="top_link"> <span class="top_span">TOP</span>
<span class="top_img"></span>
</a>
</div>
<p class="dsc_footer">네이버(주)는 통신판매의 당사자가 아니며, 상품의정보, 거래조건, 이용 및
환불 등과 관련한 의무와 책임은 각 회원에게 있습니다.</p>
<span class="copyright">© NAVER Corp.</span>
</footer>
</div>
</body>
</html>

mainPage.css

더보기

@charset "UTF-8";

@font-face {
    font-family: "Naver booking Icons";
    src: url('../fonts/Naver-booking-Icons.woff') format('woff');
    font-weight: 400;
    font-style: normal
}

html, body {
height: 100%;
margin: 0;
padding: 0;
}

body {
background-color: #d4dadf;
}

.bodydiv {
margin: 0 auto;
width: 20%;
height: auto;
background-color: white;
}

header {
    position: relative;
    background-color: #00c73c;
    height: 40px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.logoimg {
display: flex;
}

.naver-logo {
    display: block;
    width: 38px; 
    height: 33px;
    background-image: url(../img/spr_bi.png); 
    background-size: 130px 120px;
    background-repeat: no-repeat; 
    text-indent: -9999px; 
    margin-right: auto;
    margin-left: 10px;
    background-position: 5px 5px;
    filter: alpha(opacity=100000);
}

.promotionArea {
width: 100%;
height: 17.7%;
max-height: 17.7%;
overflow: hidden;
}


.reserve-link {
    display: block;
    width: 45px; 
    height: 33px;
    background-image: url(../img/spr_bi.png); 
    background-size: 130px 120px;
    background-repeat: no-repeat; 
    text-indent: -9999px; 
    margin-right: auto;
    margin-left: 0px;
    background-position: -28px 5px;
}

.email {
color: white;
background-color: transparent;
    margin-right: 5%;
}

.title {
margin-top: 3px;
}


.tab {
height: 100%;
width: 100%;
margin-top: 0%;
display: block;
}

.tab .tabCount {
margin-top:1%;
justify-content: center;
align-items: center;
display: flex;
background-color: white;
}

.redNumber {
    color: red;
}

.tab .tabCount .countComment {
font-size: 13px;
}

ul.tabs {
width: 100%;
color: #555;
margin: 0;
padding: 0;
height: 30px;
max-width: 900px;
border-bottom: 1px solid #dedede;

}

ul.tabs li {
position: relative;
float: left;
text-align: center;
cursor: pointer;
width: 15%;
padding: 0;
line-height: 30px;
border-left: 0;
border-bottom: 0;
list-style-type: none;
}

ul.tabs li:nth-child(1) {
margin-left: 13px;
    min-width: 20%;
}

ul.tabs li:first-child {
color: #00c73c;
}

ul.tabs li.active {
background-color: white;
color: #00c73c;
border-bottom: 3px solid #00c73c;
}

.tab_container {
    max-width: 100%;
    width: 100%;
    height: 100%; 
    background-color: #ebecf0;
}

.tab_content {
    column-count: 2; 
    padding: 3px;
    column-width: 110%; 
    column-gap: 5px;
    column-fill: inherit;
}

.imgBox {
    float: left;
    margin-top: 5px;
    margin-left: 0px;
width: auto;
    background-color: white;
    box-sizing: border-box;
    height: auto;
    break-inside: avoid;
}

.imgBox img {
    max-width: 100%; 
    height: auto;
    border-bottom: 5px solid #ebecf0;
}

.img_title {
width: 100%;
height: auto;
padding-left: 6%;
}

.img_title h5 {
margin-top: 6%;
margin-right: 30px;
}

.img_place p {
padding-left: 6%;
margin-top: -17px;
font-size: 13px;
margin-right: 30px;
}

.img_expl {
width: 100%;
height: auto;
padding-left: 6%;
padding-bottom: 6%;
}

.img_expl p{
margin-right: 30px;
font-size: 13px;
color: #c8d2d8;
height: auto;
}

.img_expl_hr {
background: #ebecf0;
    height:1px;
    border:0;
width: 90%;
margin-top: -5px !important;
}


footer {
display: block;
height: 200px;
margin-top: -0.1%;
display: flex;
flex-direction: column;
align-items: center;
background-color: #ebecf0;
}

footer .dsc_footer {
    margin: 0 15px 11.5px;
    font-size: 13px;
    line-height: 18px;
    color: #999;
    text-align: center
}

footer .copyright {
    display: block;
    position: relative
}

footer .copyright {
    padding-right: 10px;
    padding-bottom: 15px;
    font-size: 11px;
    line-height: 15px;
    color: #999;
    text-align: center
}

.more {
width: 99%;
height: 22%;
margin-top: 2%;
display: block;
background-color: white;
border: 1px solid #c8d2d8;
font-weight: bold;
}

.gotop {
margin-top: 5% !important;
height: 30%;

}

.top_link {
    display: inline-block;
    position: relative;
    padding-left: 15px;
    text-decoration-line: none !important;
    align-items: center !important;
    justify-content: center !important;
}

.top_link .top_span {
    display: inline-block;
    vertical-align: middle;
}

.top_link .top_img {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 12px;
    height: 15px; 
    background-size: 445px 540px;
    background-image: url(../img/spr_book2.png);
    background-repeat: no-repeat;
    background-position: -403px -33.75px;
    margin-top: 3px;
}

mainPage.js

더보기

$(function () {

var products;
var exhibitProducts;
var musicalProducts;
var concertProducts;
var classicProducts;
var dramaProducts;

const promotionDiv = $(".promotion");
var product_count = 0;
var startIndex = 0;
var productsPerPage = 4;

var $tab1 = $('#tab1');
var $tab2 = $('#tab2');
var $tab3 = $('#tab3');
var $tab4 = $('#tab4');
var $tab5 = $('#tab5');
var $tab6 = $('#tab6');
var $more = $('#more');

var currentTab = "tab1";
getProductList("all");

// $('.tab_content').masonry({
// itemSelector: '.imgBox',
// columnWidth: '.imgBox',
// horizontalOrder: true 
// });

$(".tab_content").hide();
$(".tab_content:first").show();

$("ul.tabs li").click(function () {
$("ul.tabs li").removeClass("active").css("color", "#000");
$(this).addClass("active").css("color", "#00c73c");
$(".tab_content").hide();
var activeTab = $(this).attr("rel");
currentTab = activeTab;
$("#" + activeTab).fadeIn();
if(activeTab == "tab1"){
products = null;
exhibitProducts = null;
musicalProducts = null;
concertProducts = null;
classicProducts = null;
dramaProducts = null;

$tab1.find('.imgBox').remove();
$tab2.find('.imgBox').remove();
$tab3.find('.imgBox').remove();
$tab4.find('.imgBox').remove();
$tab5.find('.imgBox').remove();
$tab6.find('.imgBox').remove();

startIndex = 0;
productsPerPage = 4;
getProductList("all");
$more.show();
}
else if(activeTab == "tab2") {
products = null;
exhibitProducts = null;
musicalProducts = null;
concertProducts = null;
classicProducts = null;
dramaProducts = null;

$tab1.find('.imgBox').remove();
$tab2.find('.imgBox').remove();
$tab3.find('.imgBox').remove();
$tab4.find('.imgBox').remove();
$tab5.find('.imgBox').remove();
$tab6.find('.imgBox').remove();

startIndex = 0;
productsPerPage = 4;
getProductList("exhibit");
$more.show();
}
else if(activeTab == "tab3") {
products = null;
exhibitProducts = null;
musicalProducts = null;
concertProducts = null;
classicProducts = null;
dramaProducts = null;

$tab1.find('.imgBox').remove();
$tab2.find('.imgBox').remove();
$tab3.find('.imgBox').remove();
$tab4.find('.imgBox').remove();
$tab5.find('.imgBox').remove();
$tab6.find('.imgBox').remove();

startIndex = 0;
productsPerPage = 4;
getProductList("musical");
$more.show();
}
else if(activeTab == "tab4") {
products = null;
exhibitProducts = null;
musicalProducts = null;
concertProducts = null;
classicProducts = null;
dramaProducts = null;

$tab1.find('.imgBox').remove();
$tab2.find('.imgBox').remove();
$tab3.find('.imgBox').remove();
$tab4.find('.imgBox').remove();
$tab5.find('.imgBox').remove();
$tab6.find('.imgBox').remove();

startIndex = 0;
productsPerPage = 4;
getProductList("concert");
$more.show();
}
else if(activeTab == "tab5") {
products = null;
exhibitProducts = null;
musicalProducts = null;
concertProducts = null;
classicProducts = null;
dramaProducts = null;

$tab1.find('.imgBox').remove();
$tab2.find('.imgBox').remove();
$tab3.find('.imgBox').remove();
$tab4.find('.imgBox').remove();
$tab5.find('.imgBox').remove();
$tab6.find('.imgBox').remove();

startIndex = 0;
productsPerPage = 4;
getProductList("classic");
$more.show();
}
else if(activeTab == "tab6") {
products = null;
exhibitProducts = null;
musicalProducts = null;
concertProducts = null;
classicProducts = null;
dramaProducts = null;

$tab1.find('.imgBox').remove();
$tab2.find('.imgBox').remove();
$tab3.find('.imgBox').remove();
$tab4.find('.imgBox').remove();
$tab5.find('.imgBox').remove();
$tab6.find('.imgBox').remove();

startIndex = 0;
productsPerPage = 4;
getProductList("drama");
$more.show();
}
});

images.forEach(function(image) {
const imageUrl = "assets/img/" + image;
const img = $("<img>").attr("src", imageUrl).attr("alt", "Image").css({
'max-width': '100%',
'max-height': '100%',
'height': '186px',
'overflow': 'hidden'
});
promotionDiv.append(img);
});


$('.promotion').slick({
slidesToShow: 1,
slidesToScroll: 1,
autoplay: true,
variableHeight: true,
autoplaySpeed: 2000,
arrows: false,
dots: false, 
adaptiveHeight: true 
});

function loadProducts() {
var endIndex = startIndex + productsPerPage;

let oddItems = [];
let evenItems = [];
$tab1.empty();

for (var i = 0; i < endIndex && i < products.length; i++) {
var product = products[i];
var imageUrl = "assets/img/" + product.file_name;

var $imgBox = $('<div class="imgBox">');
var $img = $('<img>').attr('src', imageUrl);
var $imgTitle = $('<div class="img_title">').append($('<h5>').text(product.description));
var $imgSubtitle = $('<div class="img_place">').append($('<p>').text(product.place_name));

var maxTextLength = 30;
var text = product.content;
if (text.length > maxTextLength) {
text = text.substring(0, maxTextLength) + "...";
}
var $imgExpl = $('<div class="img_expl">').append($('<p>').text(text));

$imgBox.append($img, $imgTitle, $imgSubtitle, $('<hr class="img_expl_hr">'), $imgExpl);

if (i % 2 === 0) {
evenItems.push($imgBox);
    } else {
     oddItems.push($imgBox);
    }
}

$tab1.append(evenItems);
$tab1.append(oddItems);

startIndex = endIndex;

if (startIndex >= products.length) {
$more.hide();
}
}

function loadExhibitProducts() {
var endIndex = startIndex + productsPerPage;

let oddItems = [];
let evenItems = [];
$tab2.empty();

for (var i = 0; i < endIndex && i < exhibitProducts.length; i++) {
var product = exhibitProducts[i];
var imageUrl = "assets/img/" + product.file_name;

var $imgBox = $('<div class="imgBox">');
var $img = $('<img>').attr('src', imageUrl);
var $imgTitle = $('<div class="img_title">').append($('<h5>').text(product.description));
var $imgSubtitle = $('<div class="img_place">').append($('<p>').text(product.place_name));

var maxTextLength = 30;
var text = product.content;
if (text.length > maxTextLength) {
text = text.substring(0, maxTextLength) + "...";
}
var $imgExpl = $('<div class="img_expl">').append($('<p>').text(text));

$imgBox.append($img, $imgTitle, $imgSubtitle, $('<hr class="img_expl_hr">'), $imgExpl);

if (i % 2 === 0) {
evenItems.push($imgBox);
    } else {
     oddItems.push($imgBox);
    }
}

$tab2.append(evenItems);
$tab2.append(oddItems);

startIndex = endIndex;

if (startIndex >= exhibitProducts.length) {
$more.hide();
}
}

function loadMusicalProducts() {
var endIndex = startIndex + productsPerPage;

let oddItems = [];
let evenItems = [];
$tab3.empty();

for (var i = 0; i < endIndex && i < musicalProducts.length; i++) {
var product = musicalProducts[i];
var imageUrl = "assets/img/" + product.file_name;

var $imgBox = $('<div class="imgBox">');
var $img = $('<img>').attr('src', imageUrl);
var $imgTitle = $('<div class="img_title">').append($('<h5>').text(product.description));
var $imgSubtitle = $('<div class="img_place">').append($('<p>').text(product.place_name));

var maxTextLength = 30;
var text = product.content;
if (text.length > maxTextLength) {
text = text.substring(0, maxTextLength) + "...";
}
var $imgExpl = $('<div class="img_expl">').append($('<p>').text(text));

$imgBox.append($img, $imgTitle, $imgSubtitle, $('<hr class="img_expl_hr">'), $imgExpl);

if (i % 2 === 0) {
evenItems.push($imgBox);
    } else {
     oddItems.push($imgBox);
    }
}

$tab3.append(evenItems);
$tab3.append(oddItems);

startIndex = endIndex;
if (startIndex >= musicalProducts.length) {
$more.hide();
}
}

function loadConcertProducts() {
var endIndex = startIndex + productsPerPage;

let oddItems = [];
let evenItems = [];
$tab4.empty();

for (var i = 0; i < endIndex && i < concertProducts.length; i++) {
var product = concertProducts[i];
var imageUrl = "assets/img/" + product.file_name;

var $imgBox = $('<div class="imgBox">');
var $img = $('<img>').attr('src', imageUrl);
var $imgTitle = $('<div class="img_title">').append($('<h5>').text(product.description));
var $imgSubtitle = $('<div class="img_place">').append($('<p>').text(product.place_name));

var maxTextLength = 30;
var text = product.content;
if (text.length > maxTextLength) {
text = text.substring(0, maxTextLength) + "...";
}
var $imgExpl = $('<div class="img_expl">').append($('<p>').text(text));

$imgBox.append($img, $imgTitle, $imgSubtitle, $('<hr class="img_expl_hr">'), $imgExpl);

if (i % 2 === 0) {
evenItems.push($imgBox);
    } else {
     oddItems.push($imgBox);
    }
}

$tab4.append(evenItems);
$tab4.append(oddItems);

startIndex = endIndex;
if (startIndex >= concertProducts.length) {
$more.hide();
}
}

function loadClassicProducts() {
var endIndex = startIndex + productsPerPage;

let oddItems = [];
let evenItems = [];
$tab5.empty();

for (var i = 0; i < endIndex && i < classicProducts.length; i++) {
var product = classicProducts[i];
var imageUrl = "assets/img/" + product.file_name;

var $imgBox = $('<div class="imgBox">');
var $img = $('<img>').attr('src', imageUrl);
var $imgTitle = $('<div class="img_title">').append($('<h5>').text(product.description));
var $imgSubtitle = $('<div class="img_place">').append($('<p>').text(product.place_name));

var maxTextLength = 30;
var text = product.content;
if (text.length > maxTextLength) {
text = text.substring(0, maxTextLength) + "...";
}
var $imgExpl = $('<div class="img_expl">').append($('<p>').text(text));

$imgBox.append($img, $imgTitle, $imgSubtitle, $('<hr class="img_expl_hr">'), $imgExpl);

if (i % 2 === 0) {
evenItems.push($imgBox);
    } else {
     oddItems.push($imgBox);
    }
}

$tab5.append(evenItems);
$tab5.append(oddItems);

startIndex = endIndex;
if (startIndex >= classicProducts.length) {
$more.hide();
}
}

function loadDramaProducts() {
var endIndex = startIndex + productsPerPage;

let oddItems = [];
let evenItems = [];
$tab6.empty();

for (var i = 0; i < endIndex && i < dramaProducts.length; i++) {
var product = dramaProducts[i];
var imageUrl = "assets/img/" + product.file_name;

var $imgBox = $('<div class="imgBox">');
var $img = $('<img>').attr('src', imageUrl);
var $imgTitle = $('<div class="img_title">').append($('<h5>').text(product.description));
var $imgSubtitle = $('<div class="img_place">').append($('<p>').text(product.place_name));

var maxTextLength = 30;
var text = product.content;
if (text.length > maxTextLength) {
text = text.substring(0, maxTextLength) + "...";
}
var $imgExpl = $('<div class="img_expl">').append($('<p>').text(text));

$imgBox.append($img, $imgTitle, $imgSubtitle, $('<hr class="img_expl_hr">'), $imgExpl);

if (i % 2 === 0) {
evenItems.push($imgBox);
    } else {
     oddItems.push($imgBox);
    }
}

$tab6.append(evenItems);
$tab6.append(oddItems);

startIndex = endIndex;
if (startIndex >= dramaProducts.length) {
$more.hide();
}
}

function adjustImgBoxHeight() {
$('.imgBox').each(function() {
var $imgBox = $(this);
var totalHeight = 0;
$imgBox.children().each(function() {
totalHeight += $(this).outerHeight(true);
});
totalHeight += 30;
$imgBox.height(totalHeight);
});
}

$more.on('click', function() {
if(currentTab == "tab1"){
loadProducts();
}
else if(currentTab == "tab2"){
loadExhibitProducts();
}
else if(currentTab == "tab3"){
loadMusicalProducts();
}
else if(currentTab == "tab4"){
loadConcertProducts();
}
else if(currentTab == "tab5"){
loadClassicProducts();
}
else if(currentTab == "tab6"){
loadDramaProducts();
}
});

function adjustContainerHeight() {
var $container = $('.tab_content');
var containerHeight = $container.height();

var newContentHeight = 0;
$container.children(':gt(-' + productsPerPage + ')').each(function() {
newContentHeight += $(this).outerHeight(true);
});

containerHeight += newContentHeight;

$container.height(containerHeight);
}

function getProductList (tabType){
$.ajax({
url: "/reservation/getProductList/" + tabType,
type: "GET",
success: function(data){
if(tabType == "all"){
products = data;
loadProducts();
}
else if(tabType == "exhibit"){
exhibitProducts = data;
loadExhibitProducts();
}
else if(tabType == "musical"){
musicalProducts = data;
loadMusicalProducts();
}
else if(tabType == "concert"){
concertProducts = data;
loadConcertProducts();
}
else if(tabType == "classic"){
classicProducts = data;
loadClassicProducts();
}
else if(tabType == "drama"){
dramaProducts = data;
loadDramaProducts();
}
product_count = data.length;
$('.redNumber').text(product_count);
},
error: function(){
alert("데이터 통신중 에러발생");
}
});
}

});