카테고리 없음

[Spring project] 멀티 게시판

yun000 2025. 1. 10. 16:58

 

개발환경

Windows10

JavaSE8(JDK8)

전자정부표준프레임워크개발환경3.9

SpringFramework4.3.9.RELEASE

apache-tomcat9.0.6

Oracle21cExpressionEdition

SQLDeveloper

 


테이블 명세서

 


테이블 SQL

board_category 카테고리 정보 저장
board 게시글 정보 저장
board_upload_file 게시글 첨부파일 저장

 

🍏테이블 생성

DROP TABLE BOARD_UPLOAD_FILE;
DROP TABLE BOARD_CATEGORY;
DROP TABLE BOARD;
drop table member;

CREATE TABLE BOARD (
    BOARD_ID NUMBER CONSTRAINT PK_BOARD_BOARD_ID PRIMARY KEY,
    CATEGORY_ID NUMBER,
    WRITER VARCHAR2(20) NOT NULL,
    EMAIL VARCHAR2(100),
    PASSWORD VARCHAR2(20) NOT NULL,
    TITLE VARCHAR2(500) NOT NULL,
    CONTENT CLOB,
    WRITE_DATE DATE DEFAULT SYSDATE NOT NULL,
    MASTER_ID NUMBER,
    REPLY_NUMBER NUMBER,
    REPLY_STEP NUMBER,
    READ_COUNT NUMBER DEFAULT 0
);

CREATE TABLE BOARD_CATEGORY (
    CATEGORY_ID NUMBER CONSTRAINT PK_BOARD_CATEGORY_ID PRIMARY KEY,
    CATEGORY_NAME VARCHAR2(100) NOT NULL,
    CATEGORY_DESCRIPTION VARCHAR2(100)
);

CREATE TABLE BOARD_UPLOAD_FILE (
    FILE_ID NUMBER CONSTRAINT PK_BOARD_FILE_ID PRIMARY KEY,
    BOARD_ID NUMBER,
    FILE_NAME VARCHAR2(235),
    FILE_SIZE VARCHAR2(45),
    FILE_CONTENT_TYPE VARCHAR2(500),
    FILE_DATA BLOB
);

create table member(
    userid varchar2(50) primary key,
    name varchar2(50) not null,
    password varchar2(150) not null,
    email varchar2(100) not null,
    phone varchar2(50),
    role varchar2(20) default 'role_user'
);

 

 

🍏테이블 제약조건

ALTER TABLE BOARD_CATEGORY
    ADD CONSTRAINT UK_BOARD_CATEGORY_NAME UNIQUE (CATEGORY_NAME)
    USING INDEX;

ALTER TABLE BOARD
    ADD CONSTRAINT FK_BOARD_CATEGORY_ID
        FOREIGN KEY (CATEGORY_ID) REFERENCES BOARD_CATEGORY (CATEGORY_ID);

ALTER TABLE BOARD_UPLOAD_FILE
    ADD CONSTRAINT FK_BOARD_BOARD_ID
        FOREIGN KEY (BOARD_ID) REFERENCES BOARD (BOARD_ID);
        
alter table member 
	add constraint uk_member_email 
    	unique(email) using index;        
        
--BOARD        
CREATE SEQUENCE BOARD_SEQ
START WITH 1
INCREMENT BY 1
NOCACHE;

CREATE OR REPLACE TRIGGER BOARD_ID_TRIGGER
BEFORE INSERT ON BOARD
FOR EACH ROW
BEGIN
  IF :NEW.BOARD_ID IS NULL THEN
    SELECT BOARD_SEQ.NEXTVAL INTO :NEW.BOARD_ID FROM DUAL;
  END IF;
END;
/

 

 

🍏sample data 삽입

board_category 테이블에 sample data 입력

INSERT INTO BOARD_CATEGORY (CATEGORY_ID, CATEGORY_NAME, CATEGORY_DESCRIPTION)
VALUES (1, '게시판', '답변형 멀티게시판');

INSERT INTO BOARD_CATEGORY (CATEGORY_ID, CATEGORY_NAME, CATEGORY_DESCRIPTION)
VALUES (2, '자료실', '파일 업로드 자료실');

INSERT INTO BOARD_CATEGORY (CATEGORY_ID, CATEGORY_NAME, CATEGORY_DESCRIPTION)
VALUES (3, '갤러리', '이미지 갤러리');

COMMIT;

 

board테이블에 sample data 입력

INSERT INTO BOARD (BOARD_ID, CATEGORY_ID, WRITER, EMAIL, PASSWORD, TITLE, CONTENT, 
WRITE_DATE, MASTER_ID, REPLY_NUMBER, REPLY_STEP)
VALUES (1, 1, '홍길동', 'hong@hong.com', '1234', '방가요', '내용없음', '2015-12-20', 
1, 0, 0);

INSERT INTO BOARD (BOARD_ID, CATEGORY_ID, WRITER, EMAIL, PASSWORD, TITLE, CONTENT, 
WRITE_DATE, MASTER_ID, REPLY_NUMBER, REPLY_STEP)
VALUES (2, 1, '이순신', 'lee@lee.com', '1234', '나도', '내용없음', '2015-12-21', 2, 
0, 0);

INSERT INTO BOARD (BOARD_ID, CATEGORY_ID, WRITER, EMAIL, PASSWORD, TITLE, CONTENT, 
WRITE_DATE, MASTER_ID, REPLY_NUMBER, REPLY_STEP)
VALUES (3, 1, '홍길동', 'hong@hong.com', '1234', '오랜만이야~ 순신', '그렇지', 
'2015-12-22', 2, 4, 1);

INSERT INTO BOARD (BOARD_ID, CATEGORY_ID, WRITER, EMAIL, PASSWORD, TITLE, CONTENT, 
WRITE_DATE, MASTER_ID, REPLY_NUMBER, REPLY_STEP)
VALUES (4, 1, '무명씨', 'noname@name.com', '1234', '할루', '재미없음', '2015-12-23', 
4, 0, 0);

INSERT INTO BOARD (BOARD_ID, CATEGORY_ID, WRITER, EMAIL, PASSWORD, TITLE, CONTENT, 
WRITE_DATE, MASTER_ID, REPLY_NUMBER, REPLY_STEP)
VALUES (5, 1, '홍길서', 'seo@hong.com', '1234', '나도야 순신', '나도나도', 
'2015-12-24', 2, 1, 1);

INSERT INTO BOARD (BOARD_ID, CATEGORY_ID, WRITER, EMAIL, PASSWORD, TITLE, CONTENT, 
WRITE_DATE, MASTER_ID, REPLY_NUMBER, REPLY_STEP)
VALUES (6, 1, '조심씨', 'josim@josim.com', '1234', '조심해 길서', '안전하게', 
'2015-12-25', 2, 2, 2);

INSERT INTO BOARD (BOARD_ID, CATEGORY_ID, WRITER, EMAIL, PASSWORD, TITLE, CONTENT, 
WRITE_DATE, MASTER_ID, REPLY_NUMBER, REPLY_STEP)
VALUES (7, 1, '안전씨', 'an@anjeon.com', '1234', '자나깨나', '불조심', '2015-12-26', 
4, 1, 1);

INSERT INTO BOARD (BOARD_ID, CATEGORY_ID, WRITER, EMAIL, PASSWORD, TITLE, CONTENT, 
WRITE_DATE, MASTER_ID, REPLY_NUMBER, REPLY_STEP)
VALUES (8, 1, '소심씨', 'so@so.com', '1234', '조심해는 잘삐져', '조심씨', 
'2015-12-27', 2, 3, 3);

COMMIT;

 

⚠️ 변경후 commit; 필수!!


파일 다운로드

리소스 파일 다운 

https://javaspecialist.co.kr/board/1155/

헤더 파일 다운

https://javaspecialist.co.kr/board/1156/

국제화 설정 파일 다운 

https://javaspecialist.co.kr/board/1157/

 


 

프로젝트 구조

Spring Legacy Project

Templates : Spring MVC Project

Project name: MultiBoard

top-level package: com.example.myapp

 

 

 


코드

 

🍏설정파일

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 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example</groupId>
	<artifactId>myapp</artifactId>
	<name>Ch7MultiBoard</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.3.39</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	
	
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				 </exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		
		
		<!-- ADD LIBRARY ===================================-->
		
		<!-- AOP -->
		<dependency>
		    <groupId>org.aspectj</groupId>
		    <artifactId>aspectjweaver</artifactId>
		    <version>1.9.22</version>
		</dependency>
		
		<!-- Spring JDBC -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-jdbc</artifactId>
		    <version>${org.springframework-version}</version>
		</dependency>
		
		<!-- Connection Pool -->
		<dependency>
		    <groupId>org.apache.commons</groupId>
		    <artifactId>commons-dbcp2</artifactId>
		    <version>2.9.0</version>
		</dependency>
		
		<!-- Oracle JDBC Driver -->
		<dependency>
		    <groupId>com.oracle.database.jdbc</groupId>
		    <artifactId>ojdbc8</artifactId>
		    <version>21.1.0.0</version>
		</dependency>
		
		<!-- MyBatis & MyBatis-Spring -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis</artifactId>
		    <version>3.4.0</version>
		</dependency>
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis-spring</artifactId>
		    <version>1.3.0</version>
		</dependency>
		
		<!-- File upload -->
		<dependency>
		    <groupId>commons-fileupload</groupId>
		    <artifactId>commons-fileupload</artifactId>
		    <version>1.4</version>
		</dependency>
		
		<!-- XSS Filter -->
		<dependency>
		    <groupId>org.jsoup</groupId>
		    <artifactId>jsoup</artifactId>
		    <version>1.15.3</version>
		</dependency>
		
		<!-- XSS Filter -->
		<dependency>
		    <groupId>org.jsoup</groupId>
		    <artifactId>jsoup</artifactId>
		    <version>1.15.3</version>
		</dependency>
		
		<!-- ======================================== -->
        
        
        생략...

 

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- Root spring container = 설정 파일 지정 (root-context.xml) -->
	<context-param>
	    <param-name>contextConfigLocation</param-name>
	    <param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>
	
	<!-- Creates the Spring Container -->
	<listener>
	    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- Encoding Filter = 모든 요청을 UTF-8로 encoding -->
	<filter>
	    <filter-name>encodingFilter</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>encodingFilter</filter-name>
	    <url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- SERVLET = Processes application requests -->
	<servlet>
	    <servlet-name>appServlet</servlet-name>
	    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	    <init-param>
	        <param-name>contextConfigLocation</param-name>
	        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
	    </init-param>
	    <load-on-startup>1</load-on-startup>
	</servlet>
	
	<servlet-mapping>
	    <servlet-name>appServlet</servlet-name>
	    <url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

 

root-context.xml

=애플리케이션 전체에서 사용되는 설정이나 bean 정의

<?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:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring
                           http://mybatis.org/schema/mybatis-spring-1.2.xsd
                           http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-4.3.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- DataSource = connect with Database -->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
        <property name="username" value="hr" />
        <property name="password" value="hr" />
    </bean>
	
    <!-- JdBC Tamplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>
	
    <!-- SQL Session Factory = create MyBatis SQL session -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:mapper/**/*.xml" />
    </bean>
	
    <!-- Transaction Manager = database transaction management-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
	
    <!-- Transaction 적용 활성화 -->
    <tx:annotation-driven />
	
    <!-- Exception Resolver = 예외 처리-->
    <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="java.lang.RuntimeException">error/runtime</prop>
            </props>
        </property>
        <property name="defaultErrorView" value="error/default" />
    </bean>
	
    <!-- MyBatis, Spring sccan -->
	<!-- BOARD -->
	<mybatis-spring:scan base-package="com.example.myapp.board.dao"/>
	<context:component-scan base-package="com.example.myapp.board.service"/>
	
	
	<!-- MEMBER -->
	<mybatis-spring:scan base-package="com.example.myapp.member.dao"/>
	<context:component-scan base-package="com.example.myapp.member.service"/>
	
</beans>

 

 

 

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:mvc="http://www.springframework.org/schema/mvc"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/mvc
                                 http://www.springframework.org/schema/mvc/spring-mvc.xsd
                                 http://www.springframework.org/schema/beans
                                 http://www.springframework.org/schema/beans/spring-beans.xsd
                                 http://www.springframework.org/schema/context
                                 http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- activate Spring MVC annotation -->
    <annotation-driven />
	
    <!-- view resolver = controller에서 반환하는 viewName을 실제 경로로 변환 -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>
	
    <!-- 특정 URL은 controller 거치지 않고 바로 view 반환  ex) /요청시 home.jsp반환 -->
    <view-controller path="/" view-name="home" />
	
    <!-- static resource mapping -->
	<mvc:annotation-driven/>
	<mvc:resources mapping="/**" location="/resources/" />
	<mvc:resources mapping="/js/**" location="/resources/js/" />
	<mvc:resources mapping="/css/**" location="/resources/css/" />
	<mvc:resources mapping="/images/**" location="/resources/images/" />

	<!-- Spring에서 다중 파일 업로드 가능하게 -->
    <beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <beans:property name="maxUploadSize" value="50000000" />
    </beans:bean>
	
	<!-- Controller 자동 등록 -->
	<!-- BOARD -->
	<context:component-scan base-package="com.example.myapp.board.controller"/>
	
	<!-- MEMBER -->
	<context:component-scan base-package="com.example.myapp.member.controller"/>
	
	<!-- INTERCEPTOR -->
	<interceptors>
	    <interceptor>
	        <mapping path="/file/**"/>
	        <mapping path="/board/write/**"/>
	        <mapping path="/board/update/**"/>
	        <mapping path="/board/reply/**"/>
	        <mapping path="/board/delete/**"/>
	        <beans:bean class="com.example.myapp.common.filter.LoginInterceptor"/>
	    </interceptor>
	</interceptors>
	
</beans:beans>

 

 

🍏BOARD

Board Model

Board.java

package com.example.myapp.board.model;

import java.sql.Timestamp;

import org.springframework.web.multipart.MultipartFile;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString(exclude = "file")
public class Board {
    private int boardId;
    private int categoryId;
    private String writer;
    private String email;
    private String password;
    private String title;
    private String content;
    private Timestamp writeDate;
    private int masterId;
    private int readCount;
    private int replyNumber;
    private int replyStep;
    private int page;

    private MultipartFile file;

    private int fileId;
    private String fileName;
    private long fileSize;
    private String fileContentType;
}

 

BoardCategory.java

package com.example.myapp.board.model;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
public class BoardCategory {
	private int categoryId;
	private String categoryName;
	private String categoryDescription;
}

 

BoardUploadFile.java

package com.example.myapp.board.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString(exclude = "fileData")
public class BoardUploadFile {
    private int fileId; // 파일 아이디, 1씩 증가
    private int boardId; // 첨부파일이 있는 게시글의 아이디(글번호)
    private String fileName; // 파일 이름
    private long fileSize; // 파일 크기
    private String fileContentType; // 파일 타입
    private byte[] fileData; // 파일 데이터
}

 

Board DAO

IBoardCategoryRepository.java

package com.example.myapp.board.dao;

import java.util.List;

import com.example.myapp.board.model.BoardCategory;

public interface IBoardCategoryRepository {
    int selectMaxCategoryId();
    List<BoardCategory> selectAllCategory();
    void insertNewCategory(BoardCategory boardCategory);
    void updateCategory(BoardCategory boardCategory);
    void deleteCategory(int categoryId);
}

IBoardRepository.java

package com.example.myapp.board.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.example.myapp.board.model.Board;
import com.example.myapp.board.model.BoardUploadFile;

public interface IBoardRepository {

    List<Board> selectArticleListByCategory(@Param("categoryId") int categoryId, 
                                            @Param("start") int start, 
                                            @Param("end") int end);

    Board selectArticle(int boardId);

    void updateReadCount(int boardId);

    int selectMaxArticleNo();
    int selectMaxFileId();

    void insertArticle(Board board);
    void insertFileData(BoardUploadFile file);

    BoardUploadFile getFile(int fileId);
    void updateReplyNumber(@Param("masterId") int masterId, 
                           @Param("replyNumber") int replyNumber);

    void replyArticle(Board board);

    String getPassword(int boardId);

    void updateArticle(Board board);
    void updateFileData(BoardUploadFile file);

    Board selectDeleteArticle(int boardId);

    void deleteFileData(int boardId);
    void deleteArticleByBoardId(int boardId);
    void deleteReplyFileData(int boardId);
    void deleteArticleByMasterId(int boardId);

    int selectTotalArticleCount();
    int selectTotalArticleCountByCategoryId(int categoryId);
    int selectTotalArticleCountByKeyword(String keyword);

    List<Board> searchListByContentKeyword(@Param("keyword") String keyword, 
                                           @Param("start") int start, 
                                           @Param("end") int end);
}

 

 

Board Service

BoardCategoryService.java

package com.example.myapp.board.service;

import java.util.List;

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

import com.example.myapp.board.dao.IBoardCategoryRepository;
import com.example.myapp.board.model.BoardCategory;

@Service
public class BoardCategoryService implements IBoardCategoryService {

    @Autowired
    IBoardCategoryRepository boardCategoryRepository;

    @Override
    public List<BoardCategory> selectAllCategory() {
        return boardCategoryRepository.selectAllCategory();
    }

    @Override
    public void insertNewCategory(BoardCategory boardCategory) {
        int newCategoryId = boardCategoryRepository.selectMaxCategoryId() + 1;
        boardCategory.setCategoryId(newCategoryId);
        boardCategoryRepository.insertNewCategory(boardCategory);
    }

    @Override
    public void updateCategory(BoardCategory boardCategory) {
        boardCategoryRepository.updateCategory(boardCategory);
    }

    @Override
    public void deleteCategory(int categoryId) {
        boardCategoryRepository.deleteCategory(categoryId);
    }
}

 

BoardService.java

package com.example.myapp.board.service;

import java.util.List;

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

import com.example.myapp.board.dao.IBoardRepository;
import com.example.myapp.board.model.Board;
import com.example.myapp.board.model.BoardUploadFile;

@Service
public class BoardService implements IBoardService {

    @Autowired
    IBoardRepository boardRepository;

    @Transactional
    public void insertArticle(Board board) {
        board.setBoardId(boardRepository.selectMaxArticleNo() + 1);
        boardRepository.insertArticle(board);
    }

    @Transactional
    public void insertArticle(Board board, BoardUploadFile file) {
        board.setBoardId(boardRepository.selectMaxArticleNo() + 1);
        boardRepository.insertArticle(board);
        if (file != null && file.getFileName() != null && !file.getFileName().equals("")) {
            file.setBoardId(board.getBoardId());
            file.setFileId(boardRepository.selectMaxFileId() + 1);
            boardRepository.insertFileData(file);
        }
    }

    @Override
    public List<Board> selectArticleListByCategory(int categoryId, int page) {
        int start = (page - 1) * 10 + 1;
        return boardRepository.selectArticleListByCategory(categoryId, start, start + 9);
    }

    @Transactional
    public Board selectArticle(int boardId) {
        boardRepository.updateReadCount(boardId);
        return boardRepository.selectArticle(boardId);
    }

    @Override
    public BoardUploadFile getFile(int fileId) {
        return boardRepository.getFile(fileId);
    }

    @Transactional
    public void replyArticle(Board board) {
        boardRepository.updateReplyNumber(board.getMasterId(), board.getReplyNumber());
        board.setBoardId(boardRepository.selectMaxArticleNo() + 1);
        board.setReplyNumber(board.getReplyNumber() + 1);
        board.setReplyStep(board.getReplyStep() + 1);
        boardRepository.replyArticle(board);
    }

    @Transactional
    public void replyArticle(Board board, BoardUploadFile file) {
        boardRepository.updateReplyNumber(board.getMasterId(), board.getReplyNumber());
        board.setBoardId(boardRepository.selectMaxArticleNo() + 1);
        board.setReplyNumber(board.getReplyNumber() + 1);
        board.setReplyStep(board.getReplyStep() + 1);
        boardRepository.replyArticle(board);
        if (file != null && file.getFileName() != null && !file.getFileName().equals("")) {
            file.setBoardId(board.getBoardId());
            file.setFileId(boardRepository.selectMaxFileId() + 1);
            boardRepository.insertFileData(file);
        }
    }

    @Override
    public String getPassword(int boardId) {
        return boardRepository.getPassword(boardId);
    }

    @Override
    public void updateArticle(Board board) {
        boardRepository.updateArticle(board);
    }

    @Transactional
    public void updateArticle(Board board, BoardUploadFile file) {
        boardRepository.updateArticle(board);
        if (file != null && file.getFileName() != null && !file.getFileName().equals("")) {
            file.setBoardId(board.getBoardId());
            if (file.getFileId() > 0) {
                boardRepository.updateFileData(file);
            } else {
                file.setFileId(boardRepository.selectMaxFileId() + 1);
                boardRepository.insertFileData(file);
            }
        }
    }

    @Override
    public Board selectDeleteArticle(int boardId) {
        return boardRepository.selectDeleteArticle(boardId);
    }

    @Transactional
    public void deleteArticle(int boardId, int replyNumber) {
        if (replyNumber > 0) {
            boardRepository.deleteFileData(boardId);
            boardRepository.deleteArticleByBoardId(boardId);
        } else if (replyNumber == 0) {
            boardRepository.deleteReplyFileData(boardId);
            boardRepository.deleteArticleByMasterId(boardId);
        } else {
            throw new RuntimeException("WRONG_REPLYNUMBER");
        }
    }

    @Override
    public int selectTotalArticleCount() {
        return boardRepository.selectTotalArticleCount();
    }

    @Override
    public int selectTotalArticleCountByCategoryId(int categoryId) {
        return boardRepository.selectTotalArticleCountByCategoryId(categoryId);
    }

    @Override
    public List<Board> searchListByContentKeyword(String keyword, int page) {
        int start = (page - 1) * 10 + 1;
        return boardRepository.searchListByContentKeyword("%" + keyword + "%", start, start + 9);
    }

    @Override
    public int selectTotalArticleCountByKeyword(String keyword) {
        return boardRepository.selectTotalArticleCountByKeyword("%" + keyword + "%");
    }
}

IBoardCategoryService.java

package com.example.myapp.board.service;

import java.util.List;

import com.example.myapp.board.model.BoardCategory;

public interface IBoardCategoryService {

	List<BoardCategory> selectAllCategory();
	void insertNewCategory(BoardCategory boardCategory);
	void updateCategory(BoardCategory boardCategory);
	void deleteCategory(int categoryId);
}

IBoardService.java

package com.example.myapp.board.service;

import java.util.List;

import com.example.myapp.board.model.Board;
import com.example.myapp.board.model.BoardUploadFile;

public interface IBoardService {
    void insertArticle(Board board);
    void insertArticle(Board board, BoardUploadFile file);

    List<Board> selectArticleListByCategory(int categoryId, int page);

    Board selectArticle(int boardId);

    BoardUploadFile getFile(int fileId);

    void replyArticle(Board board);
    void replyArticle(Board board, BoardUploadFile file);

    String getPassword(int boardId);

    void updateArticle(Board board);
    void updateArticle(Board board, BoardUploadFile file);

    Board selectDeleteArticle(int boardId);
    void deleteArticle(int boardId, int replyNumber);

    int selectTotalArticleCount();
    int selectTotalArticleCountByCategoryId(int categoryId);

    List<Board> searchListByContentKeyword(String keyword, int page);
    int selectTotalArticleCountByKeyword(String keyword);
}

 

 

Board Controller

 

boardController.java

=Spring MVC Framework로 Board 기능 구현.

클라이언트 요청 처리 데이터 가져와 JSP에전달.,

package com.example.myapp.board.controller;

@Controller
public class BoardController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    IBoardService boardService;

    @Autowired
    IBoardCategoryService categoryService;

	//상품 조회
    @GetMapping("/board/cat/{categoryId}/{page}")
    public String getListByCategory(@PathVariable int categoryId, @PathVariable int page, HttpSession session, Model model) {
        session.setAttribute("page", page);
        model.addAttribute("categoryId", categoryId);
        List<Board> boardList = boardService.selectArticleListByCategory(categoryId, page);
        model.addAttribute("boardList", boardList);
        int bbsCount = boardService.selectTotalArticleCountByCategoryId(categoryId);
        int totalPage = bbsCount > 0 ? (int) Math.ceil(bbsCount / 10.0) : 0;
        model.addAttribute("totalPageCount", totalPage);
        model.addAttribute("page", page);
        return "board/list";
    }

    @GetMapping("/board/cat/{categoryId}")
    public String getListByCategory(@PathVariable int categoryId, HttpSession session, Model model) {
        return getListByCategory(categoryId, 1, session, model);
    }

    @GetMapping("/board/{boardId}/{page}")
    public String getBoardDetails(@PathVariable int boardId, @PathVariable int page, Model model) {
        Board board = boardService.selectArticle(boardId);
        model.addAttribute("board", board);
        model.addAttribute("page", page);
        model.addAttribute("categoryId", board.getCategoryId());
        logger.info("getBoardDetails " + board.toString());
        return "board/view";
    }

    @GetMapping("/board/{boardId}")
    public String getBoardDetails(@PathVariable int boardId, Model model) {
        return getBoardDetails(boardId, 1, model);
    }
	
    //WHRITE
    @GetMapping(value = "/board/write/{categoryId}")
    public String writeArticle(@PathVariable int categoryId, HttpSession session, Model model) {
        String csrfToken = UUID.randomUUID().toString();
        session.setAttribute("csrfToken", csrfToken);
        List<BoardCategory> categoryList = categoryService.selectAllCategory();
        model.addAttribute("categoryList", categoryList);
        model.addAttribute("categoryId", categoryId);
        return "board/write";
    }

    @PostMapping(value = "/board/write")
    public String writeArticle(Board board, String csrfToken, HttpSession session, RedirectAttributes redirectAttrs) {
        logger.info("/board/write : " + board.toString() + csrfToken);
        String sessionToken = (String) session.getAttribute("csrfToken");
        if (csrfToken == null || !csrfToken.equals(sessionToken)) {
            throw new RuntimeException("CSRF Token Error.");
        }
        try {
            board.setContent(board.getContent().replace("\r\n", "<br>"));
            board.setTitle(Jsoup.clean(board.getTitle(), Safelist.basic()));
            board.setContent(Jsoup.clean(board.getContent(), Safelist.basic()));
            MultipartFile mfile = board.getFile();
            if (mfile != null && !mfile.isEmpty()) {
                BoardUploadFile file = new BoardUploadFile();
                file.setFileName(mfile.getOriginalFilename());
                file.setFileSize(mfile.getSize());
                file.setFileContentType(mfile.getContentType());
                file.setFileData(mfile.getBytes());
                boardService.insertArticle(board, file);
            } else {
                boardService.insertArticle(board);
            }
        } catch (Exception e) {
            e.printStackTrace();
            redirectAttrs.addFlashAttribute("message", e.getMessage());
        }
        return "redirect:/board/cat/" + board.getCategoryId();
    }

	//File upload
    @GetMapping("/file/{fileId}")
    public ResponseEntity<byte[]> getFile(@PathVariable int fileId) {
        BoardUploadFile file = boardService.getFile(fileId); //file 정보 조회
        
        //응답 헤더 설정
        final HttpHeaders headers = new HttpHeaders();
        String[] mtypes = file.getFileContentType().split("/");
        headers.setContentType(new org.springframework.http.MediaType(mtypes[0], mtypes[1]));
        headers.setContentLength(file.getFileSize());
        
        //파일 이름 encoding, 첨부 파일 설정
        try {
            String encodedFileName = java.net.URLEncoder.encode(file.getFileName(), "UTF-8");
            headers.setContentDispositionFormData("attachment", encodedFileName);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        
        //파일 데이터 return
        return new ResponseEntity<byte[]>(file.getFileData(), headers, HttpStatus.OK);
    }
	
    //답글 작성
    @GetMapping(value = "/board/reply/{boardId}")
    public String replyArticle(@PathVariable int boardId, Model model) {
        Board board = boardService.selectArticle(boardId);
        board.setWriter("");
        board.setEmail("");
        board.setTitle("[Re]" + board.getTitle());
        board.setContent("\n\n\n----------\n" + board.getContent().replaceAll("<br>", "\n"));
        model.addAttribute("board", board);
        model.addAttribute("next", "reply");
        return "board/reply";
    }

    @PostMapping(value = "/board/reply")
    public String replyArticle(Board board, RedirectAttributes redirectAttrs, HttpSession session) {
        logger.info("/board/reply : " + board.toString());
        try {
            board.setContent(board.getContent().replace("\r\n", "<br>")); // \r,\n을 <br>로 바꾸어 줄바꿈 유지
            board.setTitle(Jsoup.clean(board.getTitle(), Safelist.basic()));
            board.setContent(Jsoup.clean(board.getContent(), Safelist.basic()));
            
            //업로드한 파일 처리
            MultipartFile mfile = board.getFile();
            if (mfile != null && !mfile.isEmpty()) {
                BoardUploadFile file = new BoardUploadFile();
                file.setFileName(mfile.getOriginalFilename());
                file.setFileSize(mfile.getSize());
                file.setFileContentType(mfile.getContentType());
                file.setFileData(mfile.getBytes());
                boardService.replyArticle(board, file); //게시글과 파일 데이터 저장
            } else {
                boardService.replyArticle(board); //게시글만 저장
            }
        } catch (Exception e) {
            e.printStackTrace();
            redirectAttrs.addFlashAttribute("message", e.getMessage());
        }
        
        //Redirection
        if (session.getAttribute("page") != null) {
        	//page값이 있을 경우
            return "redirect:/board/cat/" + board.getCategoryId() + "/" + (Integer) session.getAttribute("page");
        } else {
        	//page값 없을 경우
            return "redirect:/board/cat/" + board.getCategoryId();
        }
    }


	// UPDATE
    @GetMapping(value = "/board/update/{boardId}")
    public String updateArticle(@PathVariable int boardId, Model model) {
    	//모든 카테고리 정보
        List<BoardCategory> categoryList = categoryService.selectAllCategory();
        
        //게시물 정보
        Board board = boardService.selectArticle(boardId);
        model.addAttribute("categoryList", categoryList);
        model.addAttribute("categoryId", board.getCategoryId());
        board.setContent(board.getContent().replaceAll("<br>", "\r\n")); //<br>을 다시 \r \n으로
        model.addAttribute("board", board);
        return "board/update";
    }


    @PostMapping(value = "/board/update")
    public String updateArticle(Board board, RedirectAttributes redirectAttrs) {
        logger.info("/board/update " + board.toString());
        
        String dbPassword = boardService.getPassword(board.getBoardId());
        
        //wrong password!
        if (!board.getPassword().equals(dbPassword)) {
            redirectAttrs.addFlashAttribute("passwordError", "게시글 비밀번호가 다릅니다");
            return "redirect:/board/update/" + board.getBoardId();
        }
        
        //correct password!
        try {
            board.setContent(board.getContent().replace("\r\n", "<br>"));
            board.setTitle(Jsoup.clean(board.getTitle(), Safelist.basic()));
            board.setContent(Jsoup.clean(board.getContent(), Safelist.basic()));
            
            //file upload
            MultipartFile mfile = board.getFile();
            if (mfile != null && !mfile.isEmpty()) {
                logger.info("/board/update : " + mfile.getOriginalFilename());
                BoardUploadFile file = new BoardUploadFile();
                file.setFileId(board.getFileId());
                file.setFileName(mfile.getOriginalFilename());
                file.setFileSize(mfile.getSize());
                file.setFileContentType(mfile.getContentType());
                file.setFileData(mfile.getBytes());
                logger.info("/board/update : " + file.toString());
                boardService.updateArticle(board, file);
            } else {
                boardService.updateArticle(board);
            }
        } catch (Exception e) {
            e.printStackTrace();
            redirectAttrs.addFlashAttribute("message", e.getMessage());
        }
        return "redirect:/board/" + board.getBoardId();
    }
	
    
    //DELETE
    @GetMapping(value = "/board/delete/{boardId}")
    public String deleteArticle(@PathVariable int boardId, Model model) {
        Board board = boardService.selectDeleteArticle(boardId);
        model.addAttribute("categoryId", board.getCategoryId());
        model.addAttribute("boardId", boardId);
        model.addAttribute("replyNumber", board.getReplyNumber());
        return "board/delete";
    }
	
    
    @PostMapping(value = "/board/delete")
    public String deleteArticle(Board board, HttpSession session, RedirectAttributes model) {
        try {
            String dbpw = boardService.getPassword(board.getBoardId());
            
            //correct password
            if (dbpw.equals(board.getPassword())) {
                boardService.deleteArticle(board.getBoardId(), board.getReplyNumber());
                return "redirect:/board/cat/" + board.getCategoryId() + "/" + (Integer) session.getAttribute("page");
            }
            //wrong password
            else {
                model.addFlashAttribute("message", "WRONG_PASSWORD_NOT_DELETED");
                return "redirect:/board/delete/" + board.getBoardId();
            }
            
        } catch (Exception e) {
            model.addAttribute("message", e.getMessage());
            e.printStackTrace();
            return "error/runtime";
        }
    }     
	
    
    //SEARCH
    @GetMapping("/board/search/{page}")
    //keyword=검색 키워드(공백이어도 됨), page=페이지 번호
    public String search(@RequestParam(required = false, defaultValue = "") String keyword, @PathVariable int page, HttpSession session, Model model) {
        try {
            List<Board> boardList = boardService.searchListByContentKeyword(keyword, page);
            model.addAttribute("boardList", boardList);
            int bbsCount = boardService.selectTotalArticleCountByKeyword(keyword);
            int totalPage = bbsCount > 0 ? (int) Math.ceil(bbsCount / 10.0) : 0;
            model.addAttribute("totalPageCount", totalPage);
            model.addAttribute("page", page);
            model.addAttribute("keyword", keyword);
            //logger.info(totalPage + ":" + page + ":" + keyword);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "board/search";
    }
}

 

 

🍏Member

Member.java

package com.example.myapp.member.model;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
public class Member {
	private String userid;
	private String name;
	private String password;
	private String password2;
	private String phone;
	private String email;
	private String role;
}

IMemberService.java

package com.example.myapp.member.service;

import java.util.List;

import com.example.myapp.member.model.Member;

public interface IMemberService {
	void insertMember(Member member);
	Member selectMember(String userid);
	List<Member> selectAllmembers();
	void updateMember(Member member);
	void deleteMember(Member member);
	String getPassword(String userid);
}

MemberService.java

package com.example.myapp.member.service;

import java.util.List;

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

import com.example.myapp.member.dao.IMemberRepository;
import com.example.myapp.member.model.Member;

@Service
public class MemberService implements IMemberService{
	@Autowired
	IMemberRepository memberDao;
	
	@Override
	public void insertMember(Member member)
	{
		memberDao.insertMember(member);
	}
	
	@Override
	public Member selectMember(String userid)
	{
		return memberDao.selectMember(userid);
	}
	
	@Override
	public List<Member> selectAllmembers() {
	    return memberDao.selectAllMembers();
	}

	@Override
	public void updateMember(Member member) {
	    memberDao.updateMember(member);
	}

	@Override
	public void deleteMember(Member member) {
	    memberDao.deleteMember(member);
	}

	@Override
	public String getPassword(String userid) {
	    return memberDao.getPassword(userid);
	}


}

IMemberRepository.java

package com.example.myapp.member.dao;

import java.util.List;

import com.example.myapp.member.model.Member;

public interface IMemberRepository {
	void insertMember(Member member);
	Member selectMember(String userid);
	List<Member> selectAllMembers();
	void updateMember(Member member);
	void deleteMember(Member member);
	String getPassword(String userid);
}

 

MemberController.java

UUID=universally unique Identifier. 고유한 ID생성.

CSRF Token=공격 방어 보안 메커니즘. 각 요청에 고유 tocken을 포함시켜 이것이 실제 사용자 의도인지 확인

package com.example.myapp.member.controller;

@Controller
public class MemberController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    IMemberService memberService;
	
    
    //INSERT
    @GetMapping(value = "/member/insert")
    public String insertMember(HttpSession session, Model model) {
        String csrfToken = UUID.randomUUID().toString(); //고유한 UUID생성
        session.setAttribute("csrfToken", csrfToken);
        logger.info("/member/insert, GET - CSRF Token: {}", csrfToken);
        model.addAttribute("member", new Member());
        return "member/form";
    }

    @PostMapping(value = "/member/insert")
    public String insertMember(Member member, String csrfToken, HttpSession session, Model model) {
        String sessionToken = (String) session.getAttribute("csrfToken");
        
        //ERROR
        if (csrfToken == null || !csrfToken.equals(sessionToken)) {
            throw new RuntimeException("CSRF Token Error.");
        }
        
        //NO ERROR
        try {
        	//different password
            if (!member.getPassword().equals(member.getPassword2())) {
                model.addAttribute("member", member);
                model.addAttribute("message", "MEMBER_PW_RE");
                return "member/form";
            }
            
            //correct password
            memberService.insertMember(member);
        } catch (DuplicateKeyException e) {
            //ID check
            member.setUserid(null);
            model.addAttribute("member", member);
            model.addAttribute("message", "ID_ALREADY_EXIST");
            return "member/form";
        }
        session.invalidate();
        return "home";
    }
	
    
    //LOGIN
    @GetMapping(value = "/member/login")
    public String login() {
        return "member/login";
    }

    @PostMapping(value = "/member/login")
    public String login(String userid, String password, HttpSession session, Model model) {
        Member member = memberService.selectMember(userid);
        
        //check member
        if (member != null) {
            logger.info(member.toString());
            
            //check password
            String dbPassword = member.getPassword();
            if (dbPassword.equals(password)) {
                session.setMaxInactiveInterval(600);
                session.setAttribute("userid", userid);
                session.setAttribute("name", member.getName());
                session.setAttribute("email", member.getEmail());
            } else {
                session.invalidate();
                model.addAttribute("message", "WRONG_PASSWORD");
            }
        } else {
            session.invalidate();
            model.addAttribute("message", "USER_NOT_FOUND");
        }
        return "member/login";
    }
	
    
    //LOGOUT
    @GetMapping(value = "/member/logout")
    public String logout(HttpSession session, HttpServletRequest request) {
        session.invalidate();
        return "home";
    }
	
    
    //UPDATE(CHANGE USER INFO)
    @GetMapping(value = "/member/update")
    public String updateMember(HttpSession session, Model model) {
        String userid = (String) session.getAttribute("userid");
        
        //check userid
        if (userid != null && !userid.equals("")) {
            Member member = memberService.selectMember(userid);
            model.addAttribute("member", member);
            model.addAttribute("message", "UPDATE_USER_INFO");
            return "member/update";
        } else {
            model.addAttribute("message", "NOT_LOGIN_USER");
            return "member/login";
        }
    }

    @PostMapping(value = "/member/update")
    public String updateMember(Member member, HttpSession session, Model model) {
        try {
            memberService.updateMember(member);
            model.addAttribute("message", "UPDATED_MEMBER_INFO");
            model.addAttribute("member", member);
            session.setAttribute("email", member.getEmail());
            return "member/login";
        } catch (Exception e) {
            model.addAttribute("message", e.getMessage());
            e.printStackTrace();
            return "member/error";
        }
    }
	
    //DELETE
    @GetMapping(value = "/member/delete")
    public String deleteMember(HttpSession session, Model model) {
        String userid = (String) session.getAttribute("userid");
        
        //check userid
        if (userid != null && !userid.equals("")) {
            Member member = memberService.selectMember(userid);
            model.addAttribute("member", member);
            model.addAttribute("message", "MEMBER_PW_RE");
            return "member/delete";
        } else {
            model.addAttribute("message", "NOT_LOGIN_USER");
            return "member/login";
        }
    }

    @PostMapping(value = "/member/delete")
    public String deleteMember(String password, HttpSession session, Model model) {
        try {
            Member member = new Member();
            member.setUserid((String) session.getAttribute("userid"));
            String dbpw = memberService.getPassword(member.getUserid());
            
            //check password
            if (password != null && password.equals(dbpw)) {
                member.setPassword(password);
                memberService.deleteMember(member);
                session.invalidate();
                return "member/login";
            } else {
                model.addAttribute("message", "WRONG_PASSWORD");
                return "member/delete";
            }
        } catch (Exception e) {
            model.addAttribute("message", "DELETE_FAIL");
            e.printStackTrace();
            return "member/delete";
        }
    }
}

 

 

🍏Filter

loginInterceptor.java

package com.example.myapp.common.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
        	//get email
            String email = (String) request.getSession().getAttribute("email");
            
            //check email
            if (email == null || email.equals("")) {
                response.sendRedirect(request.getContextPath() + "/member/login");
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

 

🍏Mapper

BoardCategoryMaper.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="com.example.myapp.board.dao.IBoardCategoryRepository">
	
    <!-- select all categories -->
    <select id="selectAllCategory" resultType="com.example.myapp.board.model.BoardCategory">
        SELECT 
            category_id AS categoryId, 
            category_name AS categoryName, 
            category_description AS categoryDescription
        FROM board_category
        ORDER BY category_id
    </select>
	
    <!-- select max category id -->
    <select id="selectMaxCategoryId" resultType="int">
        SELECT 
            NVL(MAX(category_id), 0) AS categoryId
        FROM board_category
    </select>
	
    
    <!-- insert new category -->
    <insert id="insertNewCategory" parameterType="com.example.myapp.board.model.BoardCategory">
        INSERT INTO board_category
            (category_id, category_name, category_description)
        VALUES
            (#{categoryId}, #{categoryName}, #{categoryDescription})
    </insert>
	
    <!-- update category -->
    <update id="updateCategory" parameterType="com.example.myapp.board.model.BoardCategory">
        UPDATE board_category
        SET
            category_name = #{categoryName}, 
            category_description = #{categoryDescription}
        WHERE
            category_id = #{categoryId}
    </update>
	
    <!-- delete category -->
    <delete id="deleteCategory" parameterType="int">
        DELETE FROM board_category
        WHERE category_id = #{categoryId}
    </delete>

</mapper>

 

BoardMapper.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="com.example.myapp.board.dao.IBoardRepository">

    <select id="selectArticleListByCategory" parameterType="hashmap" resultType="com.example.myapp.board.model.Board">
        SELECT
            board_id AS "boardId",
            category_id AS "categoryId",
            writer AS "writer",
            email AS "email",
            title AS "title",
            write_date AS "writeDate",
            master_id AS "masterId",
            reply_number AS "replyNumber",
            reply_step AS "replyStep",
            read_count AS "readCount"
        FROM (
            SELECT
                board_id, category_id, writer, email, title, write_date,
                master_id, reply_number, reply_step, read_count,
                rownum AS rnum
            FROM (
                SELECT * FROM board
                WHERE category_id=#{categoryId}
                ORDER BY master_id DESC, reply_number, reply_step
            )
        )
        WHERE rnum BETWEEN #{start} AND #{end}
    </select>

    <select id="selectArticle" parameterType="int" resultType="com.example.myapp.board.model.Board">
        SELECT
            board.board_id AS "boardId",
            category_id AS "categoryId",
            writer AS "writer",
            email AS "email",
            title AS "title",
            content AS "content",
            read_count AS "readCount",
            write_date AS "writeDate",
            master_id AS "masterId",
            reply_number AS "replyNumber",
            reply_step AS "replyStep",
            board_upload_file.file_id AS "fileId",
            board_upload_file.file_name AS "fileName",
            board_upload_file.file_size AS "fileSize",
            board_upload_file.file_content_type AS "fileContentType"
        FROM board
        LEFT OUTER JOIN board_upload_file
        ON board.board_id = board_upload_file.board_id
        WHERE board.board_id=#{boardId}
    </select>

    <update id="updateReadCount" parameterType="int">
        UPDATE board
        SET read_count = read_count + 1
        WHERE board_id = #{boardId}
    </update>

    <select id="selectMaxArticleNo" resultType="int">
        SELECT NVL(MAX(board_id), 0) AS "articleNo" FROM board
    </select>

    <select id="selectMaxFileId" resultType="int">
        SELECT NVL(MAX(file_id), 0) AS "fileId" FROM board_upload_file
    </select>

    <insert id="insertArticle" parameterType="com.example.myapp.board.model.Board">
        INSERT INTO board
        (board_id, category_id, writer, email, password, title, content, write_date, master_id, reply_number, reply_step)
        VALUES
        (#{boardId}, #{categoryId}, #{writer}, #{email}, #{password}, #{title}, #{content}, SYSDATE, #{boardId}, 0, 0)
    </insert>

    <insert id="insertFileData" parameterType="com.example.myapp.board.model.BoardUploadFile">
        INSERT INTO board_upload_file
        (file_id, board_id, file_name, file_size, file_content_type, file_data)
        VALUES
        (#{fileId}, #{boardId}, #{fileName}, #{fileSize}, #{fileContentType}, #{fileData})
    </insert>

    <select id="getFile" parameterType="int" resultType="com.example.myapp.board.model.BoardUploadFile">
        SELECT
            file_id AS "fileId",
            board_id AS "boardId",
            file_name AS "fileName",
            file_size AS "fileSize",
            file_content_type AS "fileContentType",
            file_data AS "fileData"
        FROM board_upload_file
        WHERE file_id = #{fileId}
    </select>

    <update id="updateReplyNumber" parameterType="hashmap">
        UPDATE board
        SET reply_number = reply_number + 1
        WHERE master_id = #{masterId} AND reply_number > #{replyNumber}
    </update>

    <insert id="replyArticle" parameterType="com.example.myapp.board.model.Board">
        INSERT INTO board
        (board_id, category_id, writer, email, password, title, content, write_date, master_id, reply_number, reply_step)
        VALUES
        (#{boardId}, #{categoryId}, #{writer}, #{email}, #{password}, #{title}, #{content}, SYSDATE, #{masterId}, #{replyNumber}, #{replyStep})
    </insert>

    <select id="getPassword" parameterType="int" resultType="string">
        SELECT password FROM board WHERE board_id = #{boardId}
    </select>

    <update id="updateArticle" parameterType="com.example.myapp.board.model.Board">
        UPDATE board
        SET
            category_id = #{categoryId},
            writer = #{writer},
            email = #{email},
            title = #{title},
            content = #{content},
            write_date = SYSDATE
        WHERE board_id = #{boardId}
    </update>

    <update id="updateFileData" parameterType="com.example.myapp.board.model.BoardUploadFile">
        UPDATE board_upload_file
        SET
            file_name = #{fileName},
            file_size = #{fileSize},
            file_content_type = #{fileContentType},
            file_data = #{fileData}
        WHERE file_id = #{fileId}
    </update>

    <select id="selectDeleteArticle" parameterType="int" resultType="com.example.myapp.board.model.Board">
        SELECT
            category_id AS "categoryId",
            master_id AS "masterId",
            reply_number AS "replyNumber"
        FROM board
        WHERE board_id = #{boardId}
    </select>

    <delete id="deleteFileData" parameterType="int">
        DELETE FROM board_upload_file
        WHERE EXISTS (
            SELECT board_id FROM board
            WHERE board.board_id = #{boardId}
            AND board.board_id = board_upload_file.board_id
        )
    </delete>

    <delete id="deleteReplyFileData" parameterType="int">
        DELETE FROM board_upload_file
        WHERE EXISTS (
            SELECT board_id FROM board
            WHERE board.master_id = #{boardId}
            AND board.board_id = board_upload_file.board_id
        )
    </delete>

    <delete id="deleteArticleByBoardId" parameterType="int">
        DELETE FROM board WHERE board_id = #{boardId}
    </delete>

    <delete id="deleteArticleByMasterId" parameterType="int">
        DELETE FROM board WHERE master_id = #{boardId}
    </delete>

    <select id="selectTotalArticleCount" resultType="int">
        SELECT COUNT(board_id) AS "count" FROM board
    </select>

    <select id="selectTotalArticleCountByCategoryId" parameterType="int" resultType="int">
        SELECT COUNT(board_id) AS "count" FROM board
        WHERE category_id = #{categoryId}
    </select>

    <select id="selectTotalArticleCountByKeyword" parameterType="string" resultType="int">
        SELECT COUNT(*) FROM board
        WHERE title LIKE #{keyword} OR content LIKE #{keyword}
    </select>

    <select id="searchListByContentKeyword" parameterType="hashmap" resultType="com.example.myapp.board.model.Board">
        SELECT
            board_id AS "boardId",
            category_id AS "categoryId",
            writer AS "writer",
            email AS "email",
            title AS "title",
            write_date AS "writeDate",
            master_id AS "masterId",
            reply_number AS "replyNumber",
            reply_step AS "replyStep",
            read_count AS "readCount"
        FROM (
            SELECT
                board_id, category_id, writer, email, title, write_date,
                master_id, reply_number, reply_step, read_count,
                rownum AS rnum
            FROM (
                SELECT * FROM board
                WHERE title LIKE #{keyword} OR content LIKE #{keyword}
                ORDER BY master_id DESC, reply_number, reply_step
            )
        )
        WHERE rnum BETWEEN #{start} AND #{end}
    </select>

</mapper>

 

MemberMapper.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="com.example.myapp.member.dao.IMemberRepository">

    <insert id="insertMember" parameterType="com.example.myapp.member.model.Member">
        INSERT INTO member (userid, name, password, phone, email)
        VALUES (#{userid}, #{name}, #{password}, #{phone}, #{email})
    </insert>

    <select id="selectMember" parameterType="string" resultType="com.example.myapp.member.model.Member">
        SELECT userid, name, password, phone, email, role
        FROM member
        WHERE userid = #{userid}
    </select>

    <select id="selectAllMembers" resultType="com.example.myapp.member.model.Member">
        SELECT userid, name, password, phone, address, role
        FROM member
    </select>

    <update id="updateMember" parameterType="com.example.myapp.member.model.Member">
        UPDATE member
        SET name = #{name}, password = #{password}, phone = #{phone}, email = #{email}
        WHERE userid = #{userid}
    </update>

    <delete id="deleteMember" parameterType="com.example.myapp.member.model.Member">
        DELETE FROM member
        WHERE userid = #{userid} AND password = #{password}
    </delete>

    <select id="getPassword" parameterType="string" resultType="string">
        SELECT password
        FROM member
        WHERE userid = #{userid}
    </select>

</mapper>

 

🍏Tags

paging.tag

=게시판에서 카테고리별로 글 목록을 10개씩 나누어서 보여준다.

categoryId, nowPage, totalPageCount값 동적으로 받아와 페이지 링크 성

<%@ tag language="java" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ tag body-content="empty" %>
<%@ attribute name="categoryId" type="java.lang.Integer" required="true" %>
<%@ attribute name="totalPageCount" type="java.lang.Integer" required="true" %>
<%@ attribute name="nowPage" type="java.lang.Integer" required="true" %>
<%
    int totalPageBlock = (int) Math.ceil(totalPageCount / 10.0);
    int nowPageBlock = (int) Math.ceil(nowPage / 10.0);
    int startPage = (nowPageBlock - 1) * 10 + 1;
    int endPage = Math.min(totalPageCount, nowPageBlock * 10);
    String contextPath = application.getContextPath();
    contextPath = (contextPath == null || contextPath.trim().isEmpty()) ? "" : contextPath;

    out.println("<nav aria-label=\"Page navigation\">");
    out.println("<ul class=\"pagination\">");
    
    if (nowPageBlock > 1) {
        out.print("<li>");
        out.print("<a href=\"" + contextPath + "/board/cat/" + categoryId + "/" + (startPage - 1) + "\" aria-label=\"Previous\">");
        out.print("◀</a>");
        out.println("</li>");
    }
    
    
    for (int i = startPage; i <= endPage; i++) {
        if (i == nowPage) {
            out.print("<li class=\"active\">");
        } else {
            out.print("<li>");
        }
        out.print("<a href=\"" + contextPath + "/board/cat/" + categoryId + "/" + i + "\">");
        out.print(i);
        out.print("</a>");
        out.println("</li>");
    }
    
    
    if (nowPageBlock < totalPageBlock) {
        out.print("<li>");
        out.print("<a href=\"" + contextPath + "/board/cat/" + categoryId + "/" + (endPage + 1) + "\" aria-label=\"Next\">");
        out.print("▶</a>");
        out.println("</li>");
    }
    
    
    out.println("</ul>");
    out.println("</nav>");
%>

reply.tag

<%@ tag pageEncoding="utf-8" trimDirectiveWhitespaces="true" %>
<%@ tag body-content="empty" %>
<%@ attribute name="replynum" type="java.lang.Integer" %>
<%@ attribute name="replystep" type="java.lang.Integer" %>
<%
    if (replynum == 0) {
        out.print(""); // 메인 글
    } else {
        for (int i = 0; i < replystep; i++) {
            out.print("&nbsp;"); // 공백
        }
        out.print("└"); // 답변글
    }
%>

search-paging.tag

<%@ tag language="java" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<%@ tag body-content="empty"%>
<%@ attribute name="totalPageCount" type="java.lang.Integer" required="true"%>
<%@ attribute name="nowPage" type="java.lang.Integer" required="true"%>
<%@ attribute name="keyword" type="java.lang.String" required="false"%>
<%
	//전체 페이지 블럭수
    int totalPageBlock = (int) (Math.ceil(totalPageCount / 10.0));
    //현재 페이지
    int nowPageBlock = (int) Math.ceil(nowPage / 10.0);
    
    int startPage = (nowPageBlock - 1) * 10 + 1;
    int endPage = 0;
    
    String contextPath = application.getContextPath();
	
    //check contextPath
    if (contextPath == null || contextPath.trim().equals("")) {
        contextPath = "";
    }
    
    //현재 페이지 블록의 마지막 페이지 계싼
    if (totalPageCount > nowPageBlock * 10) {
        endPage = nowPageBlock * 10;
    } else {
        endPage = totalPageCount;
    }
	
    //HTML////////////////////////////////////////////////////
    
    out.println("<nav aria-label=\"Page navigation\">");
    out.println("<ul class=\"pagination\">");
    
    //이전 페이지
    if (nowPageBlock > 1) {
        out.print("<li>");
        out.print("<a href=\"" + contextPath + "/board/search/" + (startPage - 1) + "?keyword=" + keyword + "\" aria-label=\"Previous\">");
        out.print("◀</a>");
        out.println("</li>");
    }
    
    //각 페이지 링크
    for (int i = startPage; i <= endPage; i++) {
        if (i == nowPage) {
            out.print("<li class=\"active\">");
        } else {
            out.print("<li>");
        }
        out.print("<a href=\"" + contextPath + "/board/search/" + i + "?keyword=" + keyword + "\">");
        out.print(i);
        out.print("</a>");
        out.println("</li>");
    }
    
    //다음 페이지
    if (nowPageBlock < totalPageBlock) {
        out.print("<li>");
        out.print("<a href=\"" + contextPath + "/board/search/" + (endPage + 1) + "?keyword=" + keyword + "\" aria-label=\"Next\">");
        out.print("▶</a>");
        out.println("</li>");
    }
    
    //END
    out.println("</ul>");
    out.println("</nav>");
%>

 

🍏VIEW

 

board

delete.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/board"/>
<!DOCTYPE html>
<html>
<jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
<body>
    <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
    <div class="container">
        <div class="pg-opt">
            <div class="row">
                <div class="col-md-6 pc">
                    <h2><fmt:message key="DELETE_ARTICLE"/></h2>
                </div>
                <div class="col-md-6">
                    <ol class="breadcrumb">
                        <li><fmt:message key="BOARD"/></li>
                        <li class="active"><fmt:message key="DELETE_ARTICLE"/></li>
                    </ol>
                </div>
            </div>
        </div>
        <div class="content">
            <h3><fmt:message key="DELETE_MSG"/></h3>
            <form action='<c:url value="/board/delete"/>' class="form-inline" method="post">
                <input type="hidden" name="boardId" value="${boardId}">
                <input type="hidden" name="replyNumber" value="${replyNumber}">
                <input type="hidden" name="categoryId" value="${categoryId}">
                <div class="form-group">
                    <div class="col-sm-8">
                        <input type="password" name="password" class="form-control" required>
                        <c:if test="${!empty message}">
                            <br><span style="color:red;"><fmt:message key="${message}"/></span>
                        </c:if>
                    </div>
                    <div class="col-sm-2">
                        <input type="submit" class="btn btn-danger" value="<fmt:message key='DELETE_ARTICLE'/>">
                    </div>
                </div>
            </form>
        </div>
    </div>
    <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
</body>
</html>

list.jsp

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:setBundle basename="i18n/board" />
<%@ taglib prefix="jk" tagdir="/WEB-INF/tags" %>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp" />
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp" />
        <div class="container">
            <div class="pg-opt">
                <div class="row">
                    <div class="col-md-6">
                        <h2>
                            <fmt:message key="BOARD_LIST" />
                            <c:if test="${empty name}">
                                <small style="color:red;">
                                    <fmt:message key="LOGIN" />
                                </small>
                            </c:if>
                        </h2>
                    </div>
                    <div class="col-md-6">
                        <ol class="breadcrumb">
                            <li><fmt:message key="BOARD" /></li>
                            <li class="active"><fmt:message key="BOARD_LIST" /></li>
                        </ol>
                    </div>
                </div>
            </div>
            ${message}
            <div class="content">
                <form action="<c:url value='/board/search/1' />" method="get">
                    <div class="pull-right" style="margin-bottom: 5px;">
                        <div class="col-xs-9">
                            <input type="text" name="keyword" class="form-control" />
                        </div>
                        <input type="submit" class="btn btn-warning" value="<fmt:message key='SEARCH' />" />
                    </div>
                </form>
                <table class="table table-hover table-bordered">
                    <thead>
                        <tr>
                            <td><fmt:message key="BOARD_ID" /></td>
                            <td class="pc"><fmt:message key="WRITER" /></td>
                            <td><fmt:message key="SUBJECT" /></td>
                            <td class="pc"><fmt:message key="WRITE_DATE" /></td>
                            <td class="pc"><fmt:message key="READ_COUNT" /></td>
                        </tr>
                    </thead>
                    <c:forEach var="board" items="${boardList}">
                        <tr>
                            <td>${board.boardId}</td>
                            <td class="pc">${board.writer}</td>
                            <td>
                                <jk:reply replynum="${board.replyNumber}" replystep="${board.replyStep}" />
                                <c:url var="viewLink" value="/board/${board.boardId}/${page}" />
                                <a href="${viewLink}">${board.title}</a>
                            </td>
                            <td class="pc">
                                <fmt:formatDate value="${board.writeDate}" pattern="YYYY-MM-dd" />
                            </td>
                            <td class="pc">${board.readCount}</td>
                        </tr>
                    </c:forEach>
                </table>
                <table class="table">
                    <tr>
                        <td align="left">
                            <jk:paging categoryId="${categoryId}" totalPageCount="${totalPageCount}" nowPage="${page}" />
                        </td>
                        <td align="right">
                            <a href='<c:url value="/board/write/${categoryId}" />'>
                                <button type="button" class="btn btn-info">
                                    <fmt:message key="WRITE_NEW_ARTICLE" />
                                </button>
                            </a>
                        </td>
                    </tr>
                </table>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp" />
    </body>
</html>

reply.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/board"/>
<!DOCTYPE html>
<html>
<jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
<body>
    <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
    <div class="container">
        <div class="pg-opt">
            <div class="row">
                <div class="col-md-6 pc">
                    <h2><fmt:message key="REPLY_ARTICLE"/></h2>
                </div>
                <div class="col-md-6">
                    <ol class="breadcrumb">
                        <li><fmt:message key="BOARD"/></li>
                        <li class="active"><fmt:message key="REPLY_ARTICLE"/></li>
                    </ol>
                </div>
            </div>
        </div>
        <div class="content">
            <form action="<c:url value='/board/reply'/>" method="post" enctype="multipart/form-data" class="form-horizontal">
                <div class="form-group">
                    <label class="control-label col-sm-2" for="writer"><fmt:message key="WRITER"/></label>
                    <div class="col-sm-2">
                        <input type="text" name="writer" id="writer" class="form-control" value="${sessionScope.name}" ${!empty sessionScope.name ? "readonly" : ""} required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="email"><fmt:message key="EMAIL"/></label>
                    <div class="col-sm-4">
                        <input type="text" name="email" id="email" class="form-control" value="${sessionScope.email}" ${!empty sessionScope.email ? "readonly" : ""} autocomplete="off" required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="password"><fmt:message key="PASSWORD"/></label>
                    <div class="col-sm-2">
                        <input type="password" name="password" id="password" class="form-control" required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="title"><fmt:message key="TITLE"/></label>
                    <div class="col-sm-8">
                        <input type="text" name="title" id="title" class="form-control" value="${board.title}" required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="content"><fmt:message key="CONTENT"/></label>
                    <div class="col-sm-8">
                        <textarea name="content" id="content" rows="10" cols="100" class="form-control">${board.content}</textarea>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="file"><fmt:message key="FILE"/></label>
                    <div class="col-sm-8">
                        <input type="file" id="file" name="file">
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-8">
                        <input type="hidden" name="boardId" value="${board.boardId}">
                        <input type="hidden" name="categoryId" value="${board.categoryId}">
                        <input type="hidden" name="masterId" value="${board.masterId}">
                        <input type="hidden" name="replyNumber" value="${board.replyNumber}">
                        <input type="hidden" name="replyStep" value="${board.replyStep}">
                        <input type="submit" class="btn btn-info" value="<fmt:message key='SAVE'/>">
                        <input type="reset" class="btn btn-info" value="<fmt:message key='CANCEL'/>">
                    </div>
                </div>
            </form>
        </div>
    </div>
    <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
</body>
</html>

search.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/board"/>
<%@ taglib prefix="jk" tagdir="/WEB-INF/tags"%>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
        <div class="container">
            <div class="pg-opt">
                <div class="row">
                    <div class="col-md-6 pc">
                        <h2><fmt:message key="BOARD_LIST"/>
                            <c:if test="${empty name}">
                                <small style="color:red;"><fmt:message key="LOGIN"/></small>
                            </c:if>
                        </h2>
                    </div>
                    <div class="col-md-6">
                        <ol class="breadcrumb">
                            <li><fmt:message key="BOARD"/></li>
                            <li class="active"><fmt:message key="BOARD_LIST"/></li>
                        </ol>
                    </div>
                </div>
            </div>
            ${message}
            <div class="content">
                <form action="<c:url value='/board/search/1'/>" method="get">
                    <div class="pull-right" style="margin-bottom: 5px;">
                        <div class="col-xs-9">
                            <input type="text" name="keyword" class="form-control">
                        </div>
                        <input type="submit" class="btn btn-warning" value="<fmt:message key='SEARCH'/>">
                    </div>
                </form>
                <table class="table table-hover table-bordered">
                    <thead>
                        <tr>
                            <td><fmt:message key="BOARD_ID"/></td>
                            <td class="pc"><fmt:message key="WRITER"/></td>
                            <td><fmt:message key="SUBJECT"/></td>
                            <td class="pc"><fmt:message key="WRITE_DATE"/></td>
                            <td class="pc"><fmt:message key="READ_COUNT"/></td>
                        </tr>
                    </thead>
                    <c:forEach var="board" items="${boardList}">
                        <tr>
                            <td>${board.boardId}</td>
                            <td class="pc">${board.writer}</td>
                            <td>
                                <a href='<c:url value="/board/${board.boardId}"/>'>${board.title}</a>
                            </td>
                            <td class="pc"><fmt:formatDate value="${board.writeDate}" pattern="YYYY-MM-dd"/></td>
                            <td class="pc">${board.readCount}</td>
                        </tr>
                    </c:forEach>
                </table>
                <table class="table">
                    <tr>
                        <td align="left">
                            <jk:search-paging totalPageCount="${totalPageCount}" nowPage="${page}" keyword="${keyword}"/>
                        </td>
                        <td align="right">
                            &nbsp;
                        </td>
                    </tr>
                </table>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
    </body>
</html>

update.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/board"/>
<!DOCTYPE html>
<html>
<jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
<body>
    <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
    <div class="container">
        <div class="pg-opt">
            <div class="row">
                <div class="col-md-6 pc">
                    <h2><fmt:message key="UPDATE_ARTICLE"/></h2>
                </div>
                <div class="col-md-6">
                    <ol class="breadcrumb">
                        <li><fmt:message key="BOARD"/></li>
                        <li class="active"><fmt:message key="UPDATE_ARTICLE"/></li>
                    </ol>
                </div>
            </div>
        </div>
        <div class="content">
            <form action="<c:url value='/board/update'/>" method="post" enctype="multipart/form-data" class="form-horizontal">
                <c:if test="${!empty categoryList}">
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="categoryId"><fmt:message key="CATEGORY"/></label>
                        <div class="col-sm-4">
                            <select name="categoryId" id="categoryId" class="form-control" required>
                                <c:forEach var="category" items="${categoryList}">
                                    <option value="${category.categoryId}" ${category.categoryId eq board.categoryId ? "selected" : ""}>${category.categoryName}</option>
                                </c:forEach>
                            </select>
                        </div>
                    </div>
                </c:if>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="writer"><fmt:message key="WRITER"/></label>
                    <div class="col-sm-2">
                        <input type="text" name="writer" id="writer" class="form-control" value="${board.writer}" readonly>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="email"><fmt:message key="EMAIL"/></label>
                    <div class="col-sm-4">
                        <input type="text" name="email" id="email" class="form-control" value="${board.email}" autocomplete="off" required readonly>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="password"><fmt:message key="PASSWORD"/></label>
                    <div class="col-sm-2">
                        <input type="password" name="password" id="password" class="form-control" required>
                    </div>${passwordError}
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="title"><fmt:message key="TITLE"/></label>
                    <div class="col-sm-8">
                        <input type="text" name="title" id="title" class="form-control" value="${board.title}" required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="content"><fmt:message key="CONTENT"/></label>
                    <div class="col-sm-8">
                        <textarea name="content" id="content" rows="15" cols="100" class="form-control">${board.content}</textarea>
                    </div>
                </div>
                <div class="form-group">
                    <label class="control-label col-sm-2" for="file"><fmt:message key="FILE"/></label>
                    <div class="col-sm-8">
                        <input type="hidden" name="fileId" value="${board.fileId}">
                        <input type="file" id="file" name="file">${board.fileName}
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-8">
                        <input type="hidden" name="boardId" value="${board.boardId}">
                        <input type="hidden" name="masterId" value="${board.masterId}">
                        <input type="hidden" name="replyNumber" value="${board.replyNumber}">
                        <input type="hidden" name="replyStep" value="${board.replyStep}">
                        <input type="submit" class="btn btn-info" value="<fmt:message key='UPDATE'/>">
                        <input type="reset" class="btn btn-info" value="<fmt:message key='CANCEL'/>">
                    </div>
                </div>
            </form>
        </div>
    </div>
    <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
</body>
</html>

view.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/board"/>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
        <div class="container">
            <div class="pg-opt">
                <div class="row">
                    <div class="col-md-6 pc">
                        <h2><fmt:message key="CONTENT"/></h2>
                    </div>
                    <div class="col-md-6">
                        <ol class="breadcrumb">
                            <li><fmt:message key="BOARD"/></li>
                            <li class="active"><fmt:message key="CONTENT"/></li>
                        </ol>
                    </div>
                </div>
            </div>
            <div class="content">
                <table class="table table-bordered">
                    <tr class="pc">
                        <td colspan=2 align="right">
                            <a href='<c:url value="/board/cat/${categoryId}/${page}"/>'><button type="button" class="btn btn-info"><fmt:message key="BOARD_LIST"/></button></a>
                            <a href='<c:url value="/board/write/${categoryId}"/>'><button type="button" class="btn btn-info"><fmt:message key="WRITE_NEW_ARTICLE"/></button></a>
                            <a href='<c:url value="/board/reply/${board.boardId}"/>'><button type="button" class="btn btn-info"><fmt:message key="REPLY"/></button></a>
                            <a href='<c:url value="/board/update/${board.boardId}"/>'><button type="button" class="btn btn-info"><fmt:message key="UPDATE"/></button></a>
                            <a href='<c:url value="/board/delete/${board.boardId}"/>'><button type="button" class="btn btn-info"><fmt:message key="DELETE"/></button></a>
                        </td>
                    </tr>
                    <tr>
                        <td width="20%"><fmt:message key="BOARD_ID"/></td>
                        <td>${board.boardId}</td>
                    </tr>
                    <tr>
                        <td width="20%"><fmt:message key="WRITER"/></td>
                        <td>${board.writer}</td>
                    </tr>
                    <tr>
                        <td width="20%"><fmt:message key="WRITE_DATE"/></td>
                        <td><fmt:formatDate value="${board.writeDate}" pattern="YYYY-MM-dd HH:mm:ss"/></td>
                    </tr>
                    <tr>
                        <td><fmt:message key="SUBJECT"/> </td>
                        <td>${board.title}</td>
                    </tr>
                    <tr>
                        <td><fmt:message key="CONTENT"/></td>
                        <td class="board_content">${board.content}</td>
                    </tr>
                    <c:if test="${!empty board.fileName}">
                        <tr>
                            <td><fmt:message key="FILE"/></td>
                            <td>
                                <c:set var="len" value="${fn:length(board.fileName)}"/>
                                <c:set var="filetype" value="${fn:toUpperCase(fn:substring(board.fileName, len-4, len))}"/>
                                <c:if test="${(filetype eq '.JPG') or (filetype eq 'JPEG') or (filetype eq '.PNG') or (filetype eq '.GIF')}"><img src='<c:url value="/file/${board.fileId}"/>' class="img-thumbnail"><br></c:if>
                                <a href='<c:url value="/file/${board.fileId}"/>'>${board.fileName} (<fmt:formatNumber>${board.fileSize}</fmt:formatNumber>byte)</a>
                            </td>
                        </tr>
                    </c:if>
                    <tr>
                        <td colspan=2 align="right">
                            <a href='<c:url value="/board/cat/${categoryId}/${page}"/>'><button type="button" class="btn btn-info"><fmt:message key="BOARD_LIST"/></button></a>
                            <a href='<c:url value="/board/write/${categoryId}"/>'><button type="button" class="btn btn-info"><fmt:message key="WRITE_NEW_ARTICLE"/></button></a>
                            <a href='<c:url value="/board/reply/${board.boardId}"/>'><button type="button" class="btn btn-info"><fmt:message key="REPLY"/></button></a>
                            <a href='<c:url value="/board/update/${board.boardId}"/>'><button type="button" class="btn btn-info"><fmt:message key="UPDATE"/></button></a>
                            <a href='<c:url value="/board/delete/${board.boardId}"/>'><button type="button" class="btn btn-info"><fmt:message key="DELETE"/></button></a>
                        </td>
                    </tr>
                </table>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
    </body>
</html>

write.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/board"/>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
        <div class="container">
            <div class="pg-opt">
                <div class="row">
                    <div class="col-md-6 pc">
                        <h2><fmt:message key="WRITE_NEW_ARTICLE"/></h2>
                    </div>
                    <div class="col-md-6">
                        <ol class="breadcrumb">
                            <li><fmt:message key="BOARD"/></li>
                            <li class="active"><fmt:message key="WRITE_NEW_ARTICLE"/></li>
                        </ol>
                    </div>
                </div>
            </div>
            <div class="content">
                <form action="<c:url value='/board/write'/>" method="post" enctype="multipart/form-data" class="form-horizontal">
                    <input type="hidden" name="csrfToken" value="${sessionScope.csrfToken}">
                    <c:if test="${!empty categoryList}">
                        <div class="form-group">
                            <label class="control-label col-sm-2" for="categoryId"><fmt:message key="CATEGORY"/></label>
                            <div class="col-sm-4">
                                <select name="categoryId" id="categoryId" class="form-control" required>
                                    <c:forEach var="category" items="${categoryList}">
                                        <option value="${category.categoryId}" ${category.categoryId eq requestScope.categoryId ? "selected" : ""}>${category.categoryName}</option>
                                    </c:forEach>
                                </select>
                            </div>
                        </div>
                    </c:if>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="writer"><fmt:message key="WRITER"/></label>
                        <div class="col-sm-2">
                            <input type="text" name="writer" id="writer" class="form-control" value="${sessionScope.name}" ${!empty sessionScope.name ? "readonly" : ""} autocomplete="off" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="email"><fmt:message key="EMAIL"/></label>
                        <div class="col-sm-4">
                            <input type="text" name="email" id="email" class="form-control" value="${sessionScope.email}" ${!empty sessionScope.email ? "readonly" : ""} autocomplete="off" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="password"><fmt:message key="PASSWORD"/></label>
                        <div class="col-sm-2">
                            <input type="password" name="password" id="password" class="form-control" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="title"><fmt:message key="SUBJECT"/></label>
                        <div class="col-sm-8">
                            <input type="text" name="title" id="title" class="form-control" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="content"><fmt:message key="CONTENT"/></label>
                        <div class="col-sm-8">
                            <textarea name="content" id="content" rows="10" cols="100" class="form-control"></textarea>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="file"><fmt:message key="FILE"/></label>
                        <div class="col-sm-8">
                            <input type="file" id="file" name="file"><span id="droparea" class="help-block"><fmt:message key="FILESIZE_ERROR"/></span>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-8">
                            <input type="hidden" name="boardId" value="${board.boardId != null ? board.boardId : 0}">
                            <input type="hidden" name="masterId" value="${board.masterId != null ? board.masterId : 0}" />
							<input type="hidden" name="replyNumber" value="${board.replyNumber != null ? board.replyNumber : 0}" />
							<input type="hidden" name="replyStep" value="${board.replyStep != null ? board.replyStep : 0}" />
                            <input type="submit" class="btn btn-info" value="<fmt:message key="SAVE"/>">
                            <input type="reset" class="btn btn-info" value="<fmt:message key="CANCEL"/>">
                        </div>
                    </div>
                </form>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
    </body>
</html>

 

error

runtime.jsp

<%@ page contentType="text/html; charset=utf-8" isErrorPage="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
    response.setStatus(200);
%>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp" />
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp" />
        <div class="container">
            <div class="content">
                <div class="jumbotron">
                    <h2 style="color:red;">${exception.message}</h2>
                    <p>
                        <!--
                        Failed URL: ${url}
                        Exception: ${exception.message}
                        <c:forEach items="${exception.stackTrace}" var="ste">
                            ${ste}
                        </c:forEach>
                        -->
                    </p>
                    <p><a class="btn btn-primary" href="<c:url value='/' />">Home</a></p>
                </div>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp" />
    </body>
</html>

 

 

include

bodyHeader.jsp

<%@page import="com.example.myapp.board.service.BoardCategoryService"%>
<%@page import="java.util.List"%>
<%@page import="com.example.myapp.board.model.BoardCategory"%>
<%@page import="com.example.myapp.board.service.IBoardCategoryService"%>
<%@page import="org.springframework.web.context.support.WebApplicationContextUtils"%>
<%@page import="org.springframework.web.context.WebApplicationContext"%>
<%@page contentType="text/html; charset=UTF-8" trimDirectiveWhitespaces="true" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<fmt:setBundle basename="i18n/header" />
<!-- HEADER -->
<div class="container">
<div id="divHeaderWrapper">
	<header class="header-standard-2">     
    <!-- MAIN NAV -->
    <div class="navbar navbar-wp navbar-arrow mega-nav" role="navigation">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <i class="fa fa-bars icon-custom"></i>
            </button>
            <a class="navbar-brand" href="<c:url value="/"/>" title="">
            	<fmt:message key="TITLE"/>
            </a><div style="padding-left:15px; width:200px">http://www.happy.co.kr</div>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                <li class="hidden-md hidden-lg">
                    <div class="bg-light-gray">
                        <form class="form-horizontal form-light p-15" role="form">
                            <div class="input-group input-group-lg">
                                <input type="text" class="form-control" placeholder="I want to find ...">
                                <span class="input-group-btn">
                                    <button class="btn btn-white" type="button">
                                        <i class="fa fa-search"></i>
                                    </button>
                                </span>
                            </div>
                        </form>
                    </div>
                </li>
				<li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown"><strong><fmt:message key="BOARD"/></strong></a>
                    <ul class="dropdown-menu">
                    	<li><a href="<c:url value='/board/cat/1'/>">게시판</a>
			        	<li><a href="<c:url value='/board/cat/2'/>">자료실</a>
			        	<li><a href="<c:url value='/board/cat/3'/>">겔러리</a>
					</ul>
				</li>
                <li class="dropdown">
                    <a href='<c:url value="/member/login"/>' class="dropdown-toggle" data-toggle="dropdown"><strong><fmt:message key="MEMBER"/></strong></a>
                    <ul class="dropdown-menu">
                    	<li><a href="<c:url value='/member/login'/>"><fmt:message key="MY_INFO"/></a>
                        <li><a href="<c:url value='/member/update'/>"><fmt:message key="UPDATE_USER_INFO"/></a>
                        <li><a href="<c:url value='/member/delete'/>"><fmt:message key="EXIT_MEMBER"/></a>
                        <li><a href="<c:url value='/member/logout'/>"><fmt:message key="SIGN_OUT"/></a>
                        <li role="separator" class="divider"></li>
                        <li><a href="<c:url value='/member/insert'/>"><fmt:message key="JOIN_MEMBER"/></a>
                    </ul>
                </li>
                <li class="dropdown dropdown-aux animate-click" data-animate-in="animated" data-animate-out="animated fadeOutDown" style="z-index:500;">
                    <a href="#" class="dropdown-form-toggle" data-toggle="dropdown"><i class="fa fa-search"></i></a>
                    <ul class="dropdown-menu dropdown-menu-user">
                        <li id="dropdownForm">
                            <div class="dropdown-form">
                                <form class="form-horizontal form-light p-15" action="<c:url value='/board/search'/>" method="post" role="form">
                                    <div class="input-group">
                                        <input type="text" class="form-control" name="keyword" placeholder="키워드를 입력하세요.">
                                        <span class="input-group-btn">
                                            <input type="submit" class="btn btn-base" value="Go">
                                        </span>
                                    </div>
                                </form>
                            </div>
                        </li>
                    </ul>
                </li>
               <li><div>
               <c:if test="${empty email}">
               <br><a href="<c:url value="/member/login"/>" class="btn btn-danger"><fmt:message key="SIGN_IN"/></a>
               </c:if>
               <c:if test="${!empty email}">
               <br><a href="<c:url value='/member/login'/>" class="btn btn-danger"><fmt:message key="MY_INFO"/></a>
               </c:if>
               </div>
                </li>
            </ul>
        </div><!--/.nav-collapse -->
    </div>
	</header>
</div>
</div>

footer.jsp

<%@ page contentType="text/html; charset=utf-8" trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- FOOTER -->
<footer class="footer">
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <div class="col">
                   <h4>Contact us</h4>
                   <ul>
                        <li>Phone: 010 1234 5678</li>
                        <li>Email: <a href="mailto:xxx@yyy.com" title="Email Us">xxx@yyy.com</a></li>
                        <li><a href="http://www.yourhost.com">http://www.yourhost.com</a></li>
                   </ul>
                 </div>
            </div>
            
            <div class="col-md-3">
                <div class="col">
                    <h4>Mailing list</h4>
                    <p>Sign up if you would like to receive</p>
                    <form action='#' method="post" class="form-horizontal form-light">
                        <div class="input-group">
                            <input type="email" name="email" class="form-control" placeholder="Your email address..." required>
                            <span class="input-group-btn">
                                <input type="submit" class="btn btn-base" value="GO!">
                            </span>
                        </div>
                    </form>
                </div>
            </div>
            
            <div class="col-md-3">
                <div class="col col-social-icons">
                    <h4>Follow us</h4>
                    <a href="#"><i class="fa fa-facebook"></i></a>
                    <a href="#"><i class="fa fa-google-plus"></i></a>
                    <a href="#"><i class="fa fa-linkedin"></i></a>
                    <a href="#"><i class="fa fa-twitter"></i></a>
                
                </div>
			</div>

			<div class="col-md-3">
                <div class="col">
                    <h4>About us</h4>
                    <p class="no-margin">
                    Java developer please
                    <a href="<c:url value="/"/>" class="btn btn-block btn-base btn-icon fa-check"><span>Try it now</span></a>
                    </p>
                </div>
            </div>
        </div>
	</div>
</footer>

staticFiles.jsp

<%@ page contentType="text/html; charset=utf-8" trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:setBundle basename="i18n/header" />
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="robots" content="index, follow">
    
    <title><fmt:message key="TITLE"/></title>

    <link href="<c:url value='/favicon.png'/>" rel="icon" type="image/png">
	
	<link rel="stylesheet" href="<c:url value='/css/default.css'/>">
    
    <link rel="stylesheet" href="<c:url value='/css/bootstrap.css'/>">
    <link rel="stylesheet" href="<c:url value='/css/global-style.css'/>" media="screen">  

    <script src="<c:url value='/js/jquery-3.6.3.js'/>"></script>
    <script src="<c:url value='/js/bootstrap.js'/>"></script>
</head>

 

member

delete.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/member"/>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
        <div class="container">
            <div class="pg-opt">
                <div class="row">
                    <div class="col-md-6 pc">
                        <h2><fmt:message key="EXIT_MEMBER"/></h2>
                    </div>
                    <div class="col-md-6">
                        <ol class="breadcrumb">
                            <li><fmt:message key="MEMBER"/></li>
                            <li class="active"><fmt:message key="EXIT_MEMBER"/></li>
                        </ol>
                    </div>
                </div>
            </div>
            <div class="content">
                <form action="<c:url value='/member/delete'/>" method="post" class="form-horizontal">
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="password"><fmt:message key="MEMBER_PW"/></label>
                        <div class="col-sm-4">
                            <input type="password" name="password" id="password" class="form-control">
                            <h4 style="color:red;"><fmt:message key="${message}"/></h4>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-8">
                            <input type="submit" class="btn btn-info" value="<fmt:message key='DELETE_USER_INFO'/>">
                        </div>
                    </div>
                </form>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
    </body>
</html>

form.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/member"/>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
        <div class="container">
            <div class="pg-opt">
                <div class="row">
                    <div class="col-md-6 pc">
                        <h2><fmt:message key="INSERT_USER_INFO"/></h2>${message}
                    </div>
                    <div class="col-md-6">
                        <ol class="breadcrumb">
                            <li><fmt:message key="MEMBER"/></li>
                            <li class="active"><fmt:message key="INSERT_USER_INFO"/></li>
                        </ol>
                    </div>
                </div>
            </div>
            <div class="content">
                <form action="<c:url value='/member/insert'/>" method="post" id="joinForm" class="form-horizontal">
                    <input type="hidden" name="csrfToken" value="${sessionScope.csrfToken}">
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="userid"><fmt:message key="MEMBER_ID"/></label>
                        <div class="col-sm-4">
                            <input type="text" name="userid" id="userid" value="${member['userid']}" ${empty member.userid ? "" : "readonly"} title="<fmt:message key='USERID_TITLE'/>" pattern="\w+" class="form-control" placeholder="<fmt:message key="MEMBER_ID"/>" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="password"><fmt:message key="MEMBER_PW"/></label>
                        <div class="col-sm-4">
                            <input type="password" name="password" id="password" value="${member.password}" class="form-control" title="<fmt:message key='PASSWORD_TITLE'/>" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="password2"><fmt:message key="MEMBER_PW_RE"/></label>
                        <div class="col-sm-4">
                            <input type="password" name="password2" id="password2" class="form-control" required>
                            <span id="passwordConfirm"></span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="name"><fmt:message key="MEMBER_NAME"/></label>
                        <div class="col-sm-4">
                            <input type="text" name="name" id="name" value="${member.name}" class="form-control" autocomplete="off" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="phone"><fmt:message key="MEMBER_PHONE"/></label>
                        <div class="col-sm-6">
                            <input type="text" name="phone" id="phone" value="${member.phone}" class="form-control" autocomplete="off" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label col-sm-2" for="email"><fmt:message key="MEMBER_EMAIL"/></label>
                        <div class="col-sm-8">
                            <input type="email" name="email" id="email" value="${member.email}" class="form-control" autocomplete="off" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-8">
                            <input type="submit" class="btn btn-info" value="<fmt:message key="SAVE"/>">
                            <input type="reset" class="btn btn-info" value="<fmt:message key="CANCEL"/>">
                        </div>
                    </div>
                </form>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
    </body>
    <script type="text/javascript">
        var pw1 = document.querySelector("#password");
        var pw2 = document.querySelector("#password2");
        var pwConfirm = document.querySelector("#passwordConfirm");
        pw2.onkeyup = function(event) {
            if (pw1.value !== pw2.value) {
                pwConfirm.innerText = "비밀번호가 일치하지 않습니다.";
            } else {
                pwConfirm.innerText = "";
            }
        }
    </script>
</html>

login.jsp

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/member"/>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
        <div class="container">
            <div class="pg-opt">
                <div class="row">
                    <div class="col-md-6 pc">
                        <h2><fmt:message key="LOGIN"/><small style="color:red"><fmt:message key="${not empty message ? message : 'BLANK'}"/></small></h2>
                    </div>
                    <div class="col-md-6">
                        <ol class="breadcrumb">
                            <li><fmt:message key="MEMBER"/></li>
                            <li class="active"><fmt:message key="LOGIN"/></li>
                        </ol>
                    </div>
                </div>
            </div>
            <div class="content">
                <c:if test="${empty sessionScope.userid}">
                    <form action="<c:url value='/member/login'/>" method="post" class="form-horizontal">
                        <div class="form-group">
                            <label class="control-label col-sm-2" for="id"><fmt:message key="MEMBER_ID"/></label>
                            <div class="col-sm-8">
                                <input type="text" name="userid" id="id" class="form-control" placeholder="<fmt:message key='MEMBER_ID'/>">
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="control-label col-sm-2" for="pw"><fmt:message key="MEMBER_PW"/></label>
                            <div class="col-sm-8">
                                <input type="password" name="password" id="pw" class="form-control" placeholder="<fmt:message key='MEMBER_PW'/>">
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-sm-offset-2 col-sm-8">
                                <input type="submit" class="btn btn-info" value="<fmt:message key='SIGN_IN'/>">
                                <input type="reset" class="btn btn-info" value="<fmt:message key='CANCEL'/>">
                                <a href="<c:url value='/member/insert'/>" class="btn btn-success"><fmt:message key="INSERT_USER_INFO"/></a>
                            </div>
                        </div>
                    </form>
                </c:if>
                <c:if test="${not empty sessionScope.userid}">
                    <h4>${userid}</h4>
                    <h4>${email}</h4>
                    <a href="<c:url value='/member/update'/>">[<fmt:message key="UPDATE_USER_INFO"/>]</a>
                    <a href="<c:url value='/member/logout'/>">[<fmt:message key="SIGN_OUT"/>]</a>
                    <a href="<c:url value='/member/delete'/>">[<fmt:message key="EXIT_MEMBER"/>]</a>
                </c:if>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
    </body>
</html>

update.jsp

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:setBundle basename="i18n/member" />
<!DOCTYPE html>
<html>
<jsp:include page="/WEB-INF/views/include/staticFiles.jsp" />
<body>
<jsp:include page="/WEB-INF/views/include/bodyHeader.jsp" />
<div class="container">
    <div class="pg-opt">
        <div class="row">
            <div class="col-md-6 pc">
                <h2>
                    <fmt:message key="UPDATE_USER_INFO" />
                    <small><fmt:message key="${message}" /></small>
                </h2>
            </div>
            <div class="col-md-6">
                <ol class="breadcrumb">
                    <li><fmt:message key="MEMBER" /></li>
                    <li class="active"><fmt:message key="UPDATE_USER_INFO" /></li>
                </ol>
            </div>
        </div>
    </div>
    <div class="content">
        <form action="<c:url value='/member/update' />" method="post" id="joinForm" class="form-horizontal">
            <div class="form-group">
                <label class="control-label col-sm-2" for="userid">
                    <fmt:message key="MEMBER_ID" />
                </label>
                <div class="col-sm-4">
                    <input type="text" name="userid" id="userid" value="${member['userid']}" ${empty member.userid ? "" : "readonly"} class="form-control" placeholder="<fmt:message key='MEMBER_ID' />" required>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label col-sm-2" for="password">
                    <fmt:message key="MEMBER_PW" />
                </label>
                <div class="col-sm-4">
                    <input type="password" name="password" id="password" class="form-control" title="<fmt:message key='PASSWORD_TITLE' />" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}" required>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label col-sm-2" for="password2">
                    <fmt:message key="MEMBER_PW_RE" />
                </label>
                <div class="col-sm-4">
                    <input type="password" name="password2" id="password2" class="form-control" required>
                    <span id="passwordConfirm"></span>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label col-sm-2" for="name">
                    <fmt:message key="MEMBER_NAME" />
                </label>
                <div class="col-sm-4">
                    <input type="text" name="name" id="name" value="${member.name}" class="form-control" required>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label col-sm-2" for="phone">
                    <fmt:message key="MEMBER_PHONE" />
                </label>
                <div class="col-sm-6">
                    <input type="text" name="phone" id="phone" value="${member.phone}" class="form-control" required>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label col-sm-2" for="email">
                    <fmt:message key="MEMBER_EMAIL" />
                </label>
                <div class="col-sm-8">
                    <input type="text" name="email" id="email" value="${member.email}" class="form-control" required>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-8">
                    <input type="submit" class="btn btn-info" value="<fmt:message key='SAVE' />">
                    <input type="reset" class="btn btn-info" value="<fmt:message key='CANCEL' />">
                </div>
            </div>
        </form>
    </div>
</div>
<jsp:include page="/WEB-INF/views/include/footer.jsp" />
</body>
<script type="text/javascript">
    var pw1 = document.querySelector("#password");
    var pw2 = document.querySelector("#password2");
    var pwConfirm = document.querySelector("#passwordConfirm");
    pw2.onkeyup = function(event) {
        if (pw1.value !== pw2.value) {
            pwConfirm.innerText = "비밀번호가 일치하지 않습니다.";
        } else {
            pwConfirm.innerText = "";
        }
    };
</script>
</html>

 

 

home

home.jsp

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setBundle basename="i18n/header"/>
<!DOCTYPE html>
<html>
    <jsp:include page="/WEB-INF/views/include/staticFiles.jsp"/>
    <body>
        <jsp:include page="/WEB-INF/views/include/bodyHeader.jsp"/>
        <div class="container">
            <div class="pg-opt">
                <div class="row">
                    <div class="col-md-6 pc">
                        <h2><fmt:message key="HOME"/></h2>
                    </div>
                    <div class="col-md-6">
                        <ol class="breadcrumb">
                            <li><fmt:message key="DASHBOARD"/></li>
                            <li class="active"><fmt:message key="HOME"/></li>
                        </ol>
                    </div>
                </div>
            </div>
            <div class="content">
                <div class="alert alert-warning page-header">
                    <h3><fmt:message key="WELCOME_MESSAGE"/></h3>
                </div>
                <div class="row">
                    <div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
                        <a href="board/cat/1">카테고리1 게시판</a><br>
                        <a href="board/cat/2">카테고리2 게시판</a><br>
                        <a href="board/cat/3">카테고리3 게시판</a><br>
                    </div>
                    <div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
                        Das ist nicht einfach!!!
                    </div>
                    <div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
                        hallo
                    </div>
                    <div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
                        hallo
                    </div>
                </div>
                <div class="progress">
                    <div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%;">
                        <span class="sr-only"></span>
                    </div>
                </div>
                <div class="progress">
                    <div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 80%;">
                        <span class="sr-only"></span>
                    </div>
                </div>
                <div class="alert alert-info">
                    <ol>
                        <li>welcome welcome</li>
                    </ol>
                </div>
            </div>
        </div>
        <jsp:include page="/WEB-INF/views/include/footer.jsp"/>
    </body>
</html>

 


결과