Backend/Spring

[Spring] 게시판 intelliJ+SpringMVC+Maven+Tomcat+JPA+H2 DB 환경으로 만들어 보자 ↗

dokylee 2020. 3. 4. 16:27

 

 

 

 

개발 환경


intelliJ IDEA (Ultimate)

SpringMVC 5.2.3.RELEASE

Maven

Tomcat 9

JPA 5.4.12.Final

H2 database 1.4.200

 

 

 

 

게시판을 구현한 2-Layered 아키텍처


2-Layered 아키텍처

  1. 톰캣 서버를 구동하면
  2. web.xml 파일을 로딩하여 Servlet Container 구동
  3. Servlet Container는 web.xml 파일에 등록된 ContextLoaderListener 객체를 생성(Pre Loading)한다. 
  4. ContextLoaderListener 객체는 applicationContext.xml 파일을 로딩하여 Spring Container(ROOT)를 구동한다. 이때 ServiceImpl 클래스나 DAO 객체들이 메모리에 생성된다.
  5. 사용자가 ".do" 요청을 서버에 전달하면 Servlet Container는 DispatcherServlet 객체를 생성한다.
  6. DispatcherServlet 객체는 dispatcher-servlet.xml 파일을 로딩하여 두 번째 Spring Container(자식 컨테이너)를 구동한다. 
  7. 두 번째 Spring Container가 Controller 객체를 메모리에 생성한다.
  8. Spring Container(ROOT)가 생성한 비즈니스 객체를 Controller에서 참조하여 사용할 수 있다.

 

 

 

파일별 개발 순서


  • Maven으로 intelliJ Project 생성

 

  • Spring MVC & JPA configuration 추가

 

  • H2 DB 서버 구동
./h2.sh -webAllowOthers

 

  • pom.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dokylee</groupId>
<artifactId>day1_ex</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- JPA, Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.12.Final</version>
</dependency>
<!-- spring orm library -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!-- h2 database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- DBCP -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- FileUpload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- JSTL -->
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
</dependency>
<!-- Jackson2 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!-- Mybatis Spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</project>
view raw pom.xml hosted with ❤ by GitHub

 

 

  • src/main/java/com/springbook/biz/board/BoardVO.java

 

package com.springbook.biz.board;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.web.multipart.MultipartFile;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlTransient;
import java.util.Date;
// VO(Value Object)
@Entity
@Table(name = "BOARD")
@XmlAccessorType(XmlAccessType.FIELD)
public class BoardVO {
@Id
@GeneratedValue
@XmlAttribute // seq를 속성으로 표현하라는 의미
private int seq;
private String title;
private String writer;
private String content;
@Temporal(TemporalType.DATE)
private Date regDate = new Date();
private int cnt;
@Transient
@XmlTransient
private String searchCondition;
@Transient
@XmlTransient
private String searchKeyword;
@Transient
@XmlTransient
private MultipartFile uploadFile;
@JsonIgnore
public MultipartFile getUploadFile() {
return uploadFile;
}
public void setUploadFile(MultipartFile uploadFile) {
this.uploadFile = uploadFile;
}
@JsonIgnore
public String getSearchCondition() {
return searchCondition;
}
public void setSearchCondition(String searchCondition) {
this.searchCondition = searchCondition;
}
@JsonIgnore
public String getSearchKeyword() {
return searchKeyword;
}
public void setSearchKeyword(String searchKeyword) {
this.searchKeyword = searchKeyword;
}
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
this.seq = seq;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
@Override
public String toString() {
return "BoardVO [ seq = "+seq+", title = "+title
+", writer = "+writer+", content = "+content
+", regDate = "+regDate+", cnt = "+cnt+" ]";
}
}
view raw BoardVO.java hosted with ❤ by GitHub

 

 

  • src/main/java/com/springbook/biz/board/impl/BoardDAOJPA.java

 

package com.springbook.biz.board.impl;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.JDBCUtil;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
// DAO(Data Access Object)
@Repository
public class BoardDAOJPA {
@PersistenceContext
private EntityManager em;
// CRUD 기능의 메소드 구현
// 글 등록
public void insertBoard(BoardVO vo) {
System.out.println("===> JPA로 insertBoard() 기능 처리");
em.persist(vo);
}
// 글 수정
public void updateBoard(BoardVO vo) {
System.out.println("===> JPA로 updateBoard() 기능 처리");
em.merge(vo);
}
// 글 삭제
public void deleteBoard(BoardVO vo) {
System.out.println("===> JPA로 deleteBoard() 기능 처리");
em.remove(em.find(BoardVO.class, vo.getSeq()));
}
// 글 상세 조회
public BoardVO getBoard(BoardVO vo) {
System.out.println("===> JPA로 getBoard() 기능 처리");
return (BoardVO) em.find(BoardVO.class, vo.getSeq());
}
// 글 목록 조회
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("===> JPA로 getBoardList() 기능 처리");
return em.createQuery("from BoardVO b order by b.seq desc").getResultList();
}
}

 

 

  • src/main/java/com/springbook/biz/board/BoardService.java (BoardDAOJPA.java로부터 생성 "Refactor->Extract->Interface")

 

package com.springbook.biz.board;
import java.util.List;
public interface BoardService {
// CRUD 기능의 메소드 구현
// 글 등록
void insertBoard(BoardVO vo);
// 글 수정
void updateBoard(BoardVO vo);
// 글 삭제
void deleteBoard(BoardVO vo);
// 글 상세 조회
BoardVO getBoard(BoardVO vo);
List<BoardVO> getBoardList(BoardVO vo);
// List<BoardVO> getBoardList();
}

 

 

  • src/main/java/com/springbook/biz/board/impl/BoardServiceImpl.java

 

package com.springbook.biz.board.impl;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("boardService")
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardDAOJPA boardDAO;
public void insertBoard(BoardVO vo) {
// System.out.println(vo.getSeq());
// if(vo.getSeq()==0){
// throw new IllegalArgumentException("0번 글 노노해");
// }
boardDAO.insertBoard(vo);
}
public void updateBoard(BoardVO vo) {
boardDAO.updateBoard(vo);
}
public void deleteBoard(BoardVO vo) {
boardDAO.deleteBoard(vo);
}
public BoardVO getBoard(BoardVO vo) {
return boardDAO.getBoard(vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
return boardDAO.getBoardList(vo);
}
}

 

 

  • src/main/java/com/springbook/biz/user/UserVO.java - JPA로 수정 전

 

package com.springbook.biz.user;
// VO(Value Object)
public class UserVO {
private String id;
private String password;
private String name;
private String role;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString() {
return "UserVO [ id = "+id+", password = "+password+", name = "+name+", role = "+role+" ]";
}
}
view raw UserVO.java hosted with ❤ by GitHub

 

 

  • src/main/java/com/springbook/biz/user/impl/UserDAO.java - JPA로 수정 전

 

package com.springbook.biz.user.impl;
import com.springbook.biz.common.JDBCUtil;
import com.springbook.biz.user.UserService;
import com.springbook.biz.user.UserVO;
import org.springframework.stereotype.Repository;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
//@Repository("userDAO")
public class UserDAO {
// JDBC 관련 변수
private Connection conn = null;
private PreparedStatement stmt = null;
private ResultSet rs = null;
// SQL 명령어들
private final String USER_GET = "select * from users where id=? and password=?";
// CRUD 기능의 메소드 구현
//회원 등록
public UserVO getUser(UserVO vo) {
UserVO user = null;
try {
System.out.println("===> JDBC로 getUser() 기능 처리");
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(USER_GET);
stmt.setString(1, vo.getId());
stmt.setString(2, vo.getPassword());
rs = stmt.executeQuery();
if (rs.next()) {
user = new UserVO();
user.setId(rs.getString("ID"));
user.setPassword(rs.getString("PASSWORD"));
user.setName(rs.getString("NAME"));
user.setRole(rs.getString("ROLE"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
return user;
}
}
view raw UserDAO.java hosted with ❤ by GitHub

 

 

  • src/main/java/com/springbook/biz/user/UserService.java (UserDAO.java 로부터 생성) - JPA로 수정 전

 

package com.springbook.biz.user;
public interface UserService {
// CRUD 기능의 메소드 구현
//회원 등록
UserVO getUser(UserVO vo);
}

 

 

  • src/main/java/com/springbook/biz/user/impl/UserServiceImpl.java - JPA로 수정 전

 

package com.springbook.biz.user.impl;
import com.springbook.biz.user.UserService;
import com.springbook.biz.user.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDAOSpring userDAO;
// public void setUserDAO(UserDAO userDAO) {
// this.userDAO = userDAO;
// }
public UserVO getUser(UserVO vo) {
return userDAO.getUser(vo);
}
}

 

 

  • src/main/resources/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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.springbook.biz"></context:component-scan>
<!-- AOP 어노테이션 알아먹게 하기 in 어드바이스 클래스-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- DataSource 설정 -->
<context:property-placeholder location="classpath:config/database.properties" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- spring과 JPA 연동 설정 -->
<bean id="jpaVendorAdaptor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="jpaVendorAdapter" ref="jpaVendorAdaptor"></property>
<property name="packagesToScan" value="com.springbook.biz.board"></property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.id.new_generator_mappings">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- Transaction 설정 -->
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<!-- Spring JDBC 설정 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<aop:config>
<aop:pointcut id="allPointcut" expression="execution(* com.springbook.biz..*Impl.*(..))" />
<aop:advisor pointcut-ref="allPointcut" advice-ref="txAdvice" />
</aop:config>
<!-- Spring과 Mybatis 연동 설정 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:sql-map-config.xml"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSession"></constructor-arg>
</bean>
</beans>

 

 

  • 설정
Build > Build Artifacts > war exploded > Edit > Output directory > [view(jsp) 파일들의 루트 폴더]로 설정

 

  • web/WEB-INF/dispatcher-servlet.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.springbook.view"></context:component-scan>
<!-- http 응답 body 변환 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 어노테이션 기반의 예외 일괄 처리 @ExceptionHandler를 class에서 쓰기 위한 설정 -->
<!-- <mvc:annotation-driven></mvc:annotation-driven>-->
<!-- 파일 업로드 설정 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000" />
</bean>
<!-- 예외 처리 설정 -->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">
common/arithmeticError.jsp
</prop>
<prop key="java.lang.NullPointerException">
common/nullPointerError.jsp
</prop>
</props>
</property>
<property name="defaultErrorView" value="common/error.jsp" />
</bean>
<!-- 다국어 설정 -->
<!-- MessageSource 등록 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>message.messageSource</value>
</list>
</property>
</bean>
<!-- LocaleResolver 등록 -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
<!-- LocaleChangeInterceptor 등록 -->
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
</mvc:interceptors>
</beans>

 

 

  • web/WEB-INF/web.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
view raw web.xml hosted with ❤ by GitHub

 

 

  • 다국어 처리 파일들 -> dispatcher-servlet.xml에 설정

- src/main/resources/message/messageSource_en.properties

 

# login.jsp
message.user.login.title=LOGIN
message.user.login.id=ID
message.user.login.password=PASSWORD
message.user.login.loginBtn=LOG-IN
message.user.login.language.en=English
message.user.login.language.ko=Korean
# getBoardList.jsp
message.board.list.mainTitle=BOARD LIST
message.board.list.welcomeMsg=! Welcome to my BOARD
message.board.list.search.condition.title=TITLE
message.board.list.search.condition.content=CONTENT
message.board.list.search.condition.btn=Search
message.board.list.table.head.seq=SEQ
message.board.list.table.head.title=TITLE
message.board.list.table.head.writer=WRITER
message.board.list.table.head.regDate=REGDATE
message.board.list.table.head.cnt=CNT
message.board.list.link.insertBoard=Insert Board

 

 

- src/main/resources/message/messageSource_ko_KR.properties

 

# login.jsp
message.user.login.title=로그인
message.user.login.id=아이디
message.user.login.password=비밀번호
message.user.login.loginBtn=로그인
message.user.login.language.en=영어
message.user.login.language.ko=한글
# getBoardList.jsp
message.board.list.mainTitle=게시글 목록
message.board.list.welcomeMsg=님! 게시판에 오신 걸 환영합니다.
message.board.list.search.condition.title=제목
message.board.list.search.condition.content=내용
message.board.list.search.condition.btn=검색
message.board.list.table.head.seq=번호
message.board.list.table.head.title=제목
message.board.list.table.head.writer=작성자
message.board.list.table.head.regDate=등록일
message.board.list.table.head.cnt=조회수
message.board.list.link.insertBoard=새글 등록

 

 

  • View 파일들

- index.jsp (Main Page)

 

<%--
Created by IntelliJ IDEA.
User: dokylee
Date: 01/03/2020
Time: 6:32 오후
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Main Page</title>
</head>
<body>
<center>
<h1>게시판 프로그램</h1>
<hr>
<a href="login.do">로그인</a><br><br><br>
<a href="getBoardList.do">글 목록 바로가기</a><br><br><br>
<a href="dataTransform.do">글 목록 변환 처리</a><br>
<hr>
</center>
</body>
</html>
view raw index.jsp hosted with ❤ by GitHub

 

- login.jsp

 

<%--
Created by IntelliJ IDEA.
User: dokylee
Date: 28/02/2020
Time: 12:10 오전
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title><spring:message code="message.user.login.title" /></title>
</head>
<body>
<center>
<h1><spring:message code="message.user.login.title" /></h1>
<a href="login.do?lang=en">
<spring:message code="message.user.login.language.en" />
</a>&nbsp;&nbsp;
<a href="login.do?lang=ko">
<spring:message code="message.user.login.language.ko" />
</a>
<hr>
<form action="login.do" method="post">
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange"><spring:message code="message.user.login.id" /></td>
<td><input type="text" name="id" value="${user.id}"/></td>
</tr>
<tr>
<td bgcolor="orange"><spring:message code="message.user.login.password" /></td>
<td><input type="password" name="password" value="${user.password}"/></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="<spring:message code="message.user.login.loginBtn" />" />
</td>
</tr>
</table>
</form>
</center>
</body>
</html>
view raw login.jsp hosted with ❤ by GitHub

 

- getBoardList.jsp

 

<%--
Created by IntelliJ IDEA.
User: dokylee
Date: 28/02/2020
Time: 12:49 오전
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title><spring:message code="message.board.list.mainTitle" /></title>
</head>
<body>
<center>
<h1><spring:message code="message.board.list.mainTitle" /></h1>
<h3>${userName}<spring:message code="message.board.list.welcomeMsg" /><a href="logout.do">Log-out</a></h3>
<!--검색 시작-->
<form action="getBoardList.do" method="post">
<table border="1" cellpadding="0" cellspacing="0" width="700">
<tr>
<td align="right">
<select name="searchCondition">
<c:forEach items="${conditionMap}" var="option">
<option value="${option.value}">${option.key}</option>
</c:forEach>
</select>
<input name="searchKeyword" type="text" />
<input type="submit" value="<spring:message code="message.board.list.search.condition.btn" />" />
</td>
</tr>
</table>
</form>
<!--검색 종료-->
<table border="1" cellpadding="0" cellspacing="0" width="700">
<tr>
<th bgcolor="orange" width="100"><spring:message code="message.board.list.table.head.seq" /></th>
<th bgcolor="orange" width="200"><spring:message code="message.board.list.table.head.title" /></th>
<th bgcolor="orange" width="150"><spring:message code="message.board.list.table.head.writer" /></th>
<th bgcolor="orange" width="150"><spring:message code="message.board.list.table.head.regDate" /></th>
<th bgcolor="orange" width="100"><spring:message code="message.board.list.table.head.cnt" /></th>
</tr>
<c:forEach items="${boardList}" var="board">
<tr>
<td>${board.seq}</td>
<td align="left"><a href="getBoard.do?seq=${board.seq}">${board.title}</a></td>
<td>${board.writer}</td>
<td><fmt:formatDate value="${board.regDate}" pattern="yyyy-MM-dd" /></td>
<td>${board.cnt}</td>
</tr>
</c:forEach>
</table>
<br>
<a href="insertBoard.jsp"><spring:message code="message.board.list.link.insertBoard" /></a>
</center>
</body>
</html>

 

- getBoard.jsp

 

<%--<%@ page import="com.springbook.biz.board.BoardVO" %>--%>
<%--<%@ page import="com.springbook.biz.board.impl.BoardDAO" %>--%>
<%--
Created by IntelliJ IDEA.
User: dokylee
Date: 28/02/2020
Time: 2:36 오전
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%--<%--%>
<%-- BoardVO board = (BoardVO) session.getAttribute("board");--%>
<%--%>--%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" >
<title>글 상세</title>
</head>
<body>
<center>
<h1>글 상세</h1>
<a href="logout.do">Log-out</a>
<hr>
<form action="updateBoard.do" method="post">
<input name="seq" type="hidden" value="${board.seq}">
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left"><input name="title" type="text" value="${board.title}"></td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left">${board.writer}</td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left">
<textarea name="content" rows="10" cols="40">${board.content}</textarea>
</td>
</tr>
<tr>
<td bgcolor="orange">등록일</td>
<td align="left"><fmt:formatDate value="${board.regDate}" pattern="yyyy-MM-dd" /></td>
</tr>
<tr>
<td bgcolor="orange">조회수</td>
<td align="left">${board.cnt}</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="글 수정" >
</td>
</tr>
</table>
</form>
<hr>
<a href="insertBoard.jsp">글등록</a>&nbsp;&nbsp;&nbsp;
<a href="deleteBoard.do?seq=${board.seq}">글삭제</a>&nbsp;&nbsp;&nbsp;
<a href="getBoardList.do">글목록</a>
</center>
</body>
</html>
view raw getBoard.jsp hosted with ❤ by GitHub

 

- insertBoard.jsp

 

<%--
Created by IntelliJ IDEA.
User: dokylee
Date: 28/02/2020
Time: 3:02 오전
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>새글 등록</title>
</head>
<body>
<center>
<h1>글 등록</h1>
<a href="logout.do">Log-out</a>
<hr>
<form action="insertBoard.do" method="post" enctype="multipart/form-data">
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left">
<input type="text" name="title" >
</td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left">
<input type="text" name="writer" size="10">
</td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left">
<textarea name="content" rows="10" cols="40" ></textarea>
</td>
</tr>
<tr>
<td bgcolor="orange" width="70">업로드</td>
<td align="left">
<input type="file" name="uploadFile">
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value=" 새글 등록 " />
</td>
</tr>
</table>
</form>
<a href="getBoardList.do">글 목록 가기</a>
</center>
</body>
</html>
view raw insertBoard.jsp hosted with ❤ by GitHub

 

 

  • Controller들

- src/main/java/com/springbook/view/board/BoardController.java

 

package com.springbook.view.board;
import com.springbook.biz.board.BoardListVO;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@SessionAttributes("board") // model에 "board"라는 이름으로 저장되는 데이터가 있다면, 그 데이터를 세션(HttpSession)에도 자동으로 저장하라는 설정
public class BoardController {
@Autowired
private BoardService boardService;
@RequestMapping("/dataTransform.do")
@ResponseBody
public BoardListVO dataTransform(BoardVO vo) {
vo.setSearchCondition("TITLE");
vo.setSearchKeyword("");
List<BoardVO> boardList = boardService.getBoardList(vo);
BoardListVO boardListVO = new BoardListVO();
boardListVO.setBoardList(boardList);
return boardListVO;
}
// 검색 조건 목록 설정
@ModelAttribute("conditionMap")
public Map<String, String> searchConditionMap() {
Map<String, String> conditionMap = new HashMap<String, String>();
conditionMap.put("제목", "TITLE");
conditionMap.put("내용", "CONTENT");
return conditionMap;
}
// 글 등록
@RequestMapping(value = "/insertBoard.do")
public String insertBoard(BoardVO vo) throws IOException {
System.out.println("글 등록 처리");
MultipartFile uploadFile = vo.getUploadFile();
if(!uploadFile.isEmpty()) {
String fileName = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("/Users/dokylee/Desktop/java_proj/upload-imgs/"+fileName));
}
boardService.insertBoard(vo);
// return "getBoardList.do"; // 포워딩 방식: 브라우저의 URL 변하지 않음 => "/insertBoard.do" 그대로
return "redirect:getBoardList.do"; // URL => "/getBoardList.do"
}
// 글 수정
@RequestMapping(value = "/updateBoard.do")
public String updateBoard(@ModelAttribute("board") BoardVO vo) {
// ㄴ> 세션에 board로 저장된 데이터가 있으면 vo에 할당한 후, 사용자가 입력한 파라미터값을 vo 객체에 할당 ==> 그래서 이전정보+새로운정보 모두 가능
boardService.updateBoard(vo);
return "getBoardList.do";
}
// 글 삭제
@RequestMapping(value = "/deleteBoard.do")
public String deleteBoard(BoardVO vo) {
boardService.deleteBoard(vo);
return "getBoardList.do";
}
//글 상세 조회
@RequestMapping(value = "/getBoard.do")
public String getBoard(BoardVO vo, Model model) {
model.addAttribute("board", boardService.getBoard(vo));
return "getBoard.jsp";
}
// 글 목록 검색
@RequestMapping(value = "/getBoardList.do")
// public String getBoardList(@RequestParam(value = "searchCondition", defaultValue = "TITLE", required = false) String condition,
// @RequestParam(value = "searchKeyword", defaultValue = "", required = false) String keyword,
// BoardVO vo, Model model) {
public String getBoardList(BoardVO vo, Model model) {
// Null Check
if(vo.getSearchCondition() == null) vo.setSearchCondition("TITLE");
if(vo.getSearchKeyword() == null) vo.setSearchKeyword("");
System.out.println("검색 조건: "+vo.getSearchCondition());
System.out.println("검색 단어: "+vo.getSearchKeyword());
// Model 정보 저장
model.addAttribute("boardList", boardService.getBoardList(vo));
return "getBoardList.jsp"; // View 이름 리턴
}
}

 

- src/main/java/com/springbook/view/user/LoginController.java

 

package com.springbook.view.user;
import com.springbook.biz.user.UserVO;
import com.springbook.biz.user.impl.UserDAO;
import com.springbook.biz.user.impl.UserDAOSpring;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@RequestMapping(value = "/login.do", method = RequestMethod.GET)
public String loginView(@ModelAttribute("user") UserVO vo) {
System.out.println("로그인 화면으로 이동");
vo.setId("admin");
vo.setPassword("1234");
return "login.jsp";
}
@RequestMapping(value = "/login.do", method = RequestMethod.POST)
public String login(UserVO vo, UserDAO userDAO, HttpSession session) {
if(vo.getId() == null || vo.getId().equals("")) {
throw new IllegalArgumentException("아이디는 반드시 입력해야 합니다.");
}
System.out.println("로그인 처리");
UserVO user = userDAO.getUser(vo);
if(user != null) {
session.setAttribute("userName", user.getName());
return "getBoardList.do";
}
else return "login.jsp";
}
}

 

- src/main/java/com/springbook/view/user/LogoutController.java

 

package com.springbook.view.user;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LogoutController {
@RequestMapping(value = "/logout.do")
public String logout(HttpSession session) {
session.invalidate();
return "login.jsp";
}
}

 

 

  • 예외처리 화면 핸들러

- src/main/java/com/springbook/view/common/CommonExceptionHandler.java

 

package com.springbook.view.common;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
public class CommonExceptionHandler {
@ExceptionHandler(ArithmeticException.class)
public ModelAndView handleArithmeticException(Exception e) {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.setViewName("/common/arithmeticError.jsp");
return mav;
}
@ExceptionHandler(NullPointerException.class)
public ModelAndView handleNullPointerException(Exception e) {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.setViewName("/common/nullPointerError.jsp");
return mav;
}
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception e) {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.setViewName("/common/error.jsp");
return mav;
}
}

 

 

  • 어드바이스 (AOP 구현)

추가 구현 후 업데이트 예정

 

 

 

 

 

전체 프로젝트 파일


Github> https://github.com/dokylee54/java-spring-boardEx/tree/master/day3

 

dokylee54/java-spring-boardEx

Contribute to dokylee54/java-spring-boardEx development by creating an account on GitHub.

github.com

 

 

 

 

스프링 퀵 스타트
국내도서
저자 : 채규태
출판 : 루비페이퍼 2016.06.30
상세보기