비트교육센터/Spring

[비트교육센터][Spring] 스프링 31일차 jsp 게시판 만들기(1)

달의요정루나 2023. 7. 13. 17:52

1. board 프로젝트 만들기

1. Maven Project 만들기

프로젝트 구조

https://julian5383.tistory.com/212

 

[비트교육센터][Spring] 스프링 30일차 AOP, 스프링 MVC

1. AOP 프로그래밍 - AOP: 여러 객체에 공통으로 적용할 수 있는 기능을 분리해 재사용성을 높여주는 프로그래밍 기법이다. 1) pom.xml 설정하기 4.0.0 com.julian5383 AopProject 0.0.1-SNAPSHOT UTF-8 17 17 org.springfra

julian5383.tistory.com

- 해당 게시물의 2. 스프링 MVC - 2) 항목을 참고하면 된다. firstWeb이 아닌 board 프로젝트를 생성하고 Convert to Maven Project를 해주면 된다.

1. Dynamic Web Project로 board를 생성한다. 톰캣은 9.0으로 설정한다.

(다른 말이지만 톰캣 10버전부터는 jsp를 지원하지 않는다.)

2. web.xml 파일생성을 클릭한후 Finish를 누른다.
3. Covert to Maven을 해준다.

 

2. pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.julian5383</groupId>
  <artifactId>board</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>17</release>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.3</version>
      </plugin>
    </plugins>
  </build>
   <dependencies>
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>javax.servlet-api</artifactId>
	    <version>4.0.1</version>
	    <scope>provided</scope>
	</dependency>
	<dependency>
	    <groupId>javax.servlet.jsp</groupId>
	    <artifactId>javax.servlet.jsp-api</artifactId>
	    <version>2.3.3</version>
	    <scope>provided</scope>
	</dependency>
	<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>jstl</artifactId>
	    <version>1.2</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-webmvc</artifactId>
	    <version>5.3.28</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
	<dependency>
	    <groupId>mysql</groupId>
	    <artifactId>mysql-connector-java</artifactId>
	    <version>8.0.33</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis</artifactId>
	    <version>3.5.13</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis-spring</artifactId>
	    <version>2.1.1</version>
	</dependency>

  </dependencies>
</project>

1. MySQL Connector Java의 8.0.33 Maven을 추가한다.
2. MyBatis 3.5.13 Maven을 추가한다.
3. MyBatis Spring 2.2.1 Maven을 추가한다.

- 스프링 30일차 FirstWeb 프로젝트의 pom.xml에서 3개의 종속성을 더 추가한다.

- MyBatis: 자바 퍼시스턴스 프레임워크의 하나로 XML 서술자나 애너테이션을 사용하여 저장 프로시저나 SQL 문으로 객체들을 연결시킨다.

- MySQL Connector Java: 프로그램과 MySQL 데이터베이스를 연결해주는 프로그램이다.

 

3. Missing required sources folder

- 프로젝트를 불러오는 과정에서 Build Path의 문제로 새로운 환경에서 경로가 맞지 않아 뜨는 문제이다.

[1] 프로젝트 우클릭->Build Path->Configure Build Path...를 클릭한다.

[2] resources 폴더에 missing이라고 뜬다.

[3] 탐색기에서 해당 프로젝트 경로에 resources 폴더를 선언한다.

[4] 오류가 없어진 것을 확인할 수 있다.

4. MvcConfig.java

package com.julian5383.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer{

	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		// TODO Auto-generated method stub
		configurer.enable();
	}

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		// TODO Auto-generated method stub
		registry.jsp("/WEB-INF/views/",".jsp");//브라우저 상에서 접근 불가로 직접선언
	}
	
}

- 30일차에서 jsp 파일경로에 대한 점두사, 점미사 선언 부분이다.

- WebMvcConfigurer 인터페이스를 상속하고 있다. @Configuration 어노테이션을 붙인 클래스 역시 컨테이너에 빈으로 등록되므로 MvcConfig 클래스는 WebMvcConfigurer 타입의 빈이 된다.

- @EnableWebMvc 어노테이션을 사용시 WebMvcConfigurer 타입인 빈 객체의 메소드를 호출해 MVC 설정을 추가한다.

 

4. 데이터베이스 만들기

create database onboard default character set utf8mb4;
grant all privileges on onboard.* to 'lastcoder'@'%';
grant all privileges on onboard.* to 'lastcoder'@'localhost';
flush privileges;

- MySql Workbench에서 onboard 데이터 베이스를 선언하고 lastcoder 계정에 권한을 할당한다.

use onboard;

CREATE TABLE noticeboard
(   article_no   INT auto_increment primary key,
   title      VARCHAR(100),
   content   VARCHAR(2000),
   write_date   timestamp DEFAULT current_timestamp,
   write_id      VARCHAR(50)
);

INSERT INTO noticeboard (title, content, write_date, write_id)
values('안녕하세요1','테스트 글1',default,'kim');

INSERT INTO noticeboard (title, content, write_date, write_id)
values('안녕하세요2','테스트 글2',default,'park');

INSERT INTO noticeboard (title, content, write_date, write_id)
values('안녕하세요3','테스트 글3',default,'lee');

select * from noticeboard;

- onboard 데이터베이스에 noticeboard 테이블을 만들고 데이터 3개를 넣는다.

 

5. 데이터베이스 연결, 쿼리문 만들기

[1] mybatis-config.xml

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

<configuration>
	<typeAliases>
		<typeAlias type="com.julian5383.model.OnboardDto" alias="onboardDto" />
	</typeAliases>
    <!--자료형별 별칭 선언으로 OnboardDto 클래스(패키지 경로까지)를 onboardDto 별칭으로 기재한다.-->
    
	<environments default="dev">
		<environment id="dev">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/onboard?useUnicode=true&amp;characterEncoding=utf8" />
				<property name="username" value="lastcoder" />
				<property name="password" value="1234" />
			</dataSource>
		</environment>
	</environments>
	<!--mybatis에서 연동할 데이터베이스 정보(DB유형, DB이름, MySql id, MySql 비번)를 등록한다.-->
    
	<mappers>
		<mapper resource="noticeboard.xml" />
	</mappers>
    <!--사용하고자 하는 쿼리가 정의된 mapper파일을 등록한다.-->
</configuration>

- mybatis에서 사용될 데이터베이스를 연동하기 위한 설정값들이다.

[2] noticeboard.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 = "mapper.notice">
	<resultMap id="noticeResult" type="onboardDto">
		<result property="article_no" column="article_no"/>
		<result property="title" column="title"/>
		<result property="content" column="content"/>
		<result property="write_date" column="write_date"/>
		<result property="write_id" column="write_id"/>
		<!-- property가 dto의 필드명과 일치해야함,
		column은 데이터베이스의 칼럼명과 정확히 일치해야함 -->
	</resultMap>

	<select id="selectAllArticles" resultMap="noticeResult">
		<![CDATA[
			select * from noticeboard order by write_date desc
		]]> <!-- xml로 인지하지 않고 패스한다. -->
	</select>
	<insert id="insertArticle" parameterType="onboardDto">
		<![CDATA[
			INSERT INTO noticeboard (title, content, write_date, write_id)
			values(#{title},#{content},default,#{write_id})
		]]>
	</insert>
	<select id="selectArticle" resultType="onboardDto" parameterType="int">
		<![CDATA[
			select * from noticeboard where article_no=#{article_no}
		]]> <!-- xml로 인지하지 않고 패스한다. -->
	</select>
</mapper>

- resultmap 태그는 dto 필드명들을 리스트 형태로 리턴해준다.

 

- resultType: select 된 데이터를 onboardDto 객체로 반환해준다.

- parameterType: 쿼리문에서 #{}에 들어갈 값이 타입을 정해준다.

- CDATA 안에 있는 쿼리는 XML로 인지하지 않고 문자열로 인식한다. 쿼리를 작성할때 xml에서 <,>,&을 태그로 인식해 에러를 발생시킬수 있는데 CDATA를 통해 해결할 수 있다.

 

6. 게시판 만들기

[1] OnboardDto.java

package com.julian5383.model;

public class OnboardDto {
	private int article_no;
	private String title;
	private String content;
	private String write_date;
	private String write_id;
	public int getArticle_no() {
		return article_no;
	}
	public void setArticle_no(int article_no) {
		this.article_no = article_no;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getWrite_date() {
		return write_date;
	}
	public void setWrite_date(String write_date) {
		this.write_date = write_date;
	}
	public String getWrite_id() {
		return write_id;
	}
	public void setWrite_id(String write_id) {
		this.write_id = write_id;
	}
	

}

- Dto(data transfer object): 프로세스 사이에서 데이터를 전송하는 객체이다.
- Persistence framework(지속성 프레임워크): 데이터의 저장, 조회, 변경, 삭제를 다루는 클래스 및 설정 파일들의 집합이다.
종류 1. sql mapping 방법

ex) mybatis: 자바 퍼시스턴스 프레임워크의 하나로 XML 서술자나 애너테이션(annotation)을 사용하여 저장 프로시저 SQL 문으로 객체들을 연결시킨다.


종류 2. orm(object relational mapping) 방법

ex) Hibernate: 자바 언어를 위한 객체 관계 매핑 프레임워크이다. DB의 데이터와 코드를 매핑시켜주기 위한 프레임워크다.

- spring data jpa(java persistence api): 스프링에서 jpa를 사용할수 있게 해주는 프레임워크이다. 데이터베이스 사용시 쿼리가 아닌 객체로 다룬다.

 

[2] BoardDao.java

package com.julian5383.model;

import java.io.Reader;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class BoardDao {
	private static SqlSessionFactory sessionFactory = null;
	//DB와의 연결과 SQL 실행을 담당해주는 객체이다.
    
	public static SqlSessionFactory getInstance() {
		if (sessionFactory == null) {
			try {
				String resource = "mybatis-config.xml";
				Reader reader = Resources.getResourceAsReader(resource);
				sessionFactory = new SqlSessionFactoryBuilder().build(reader);
                //mybatis-config.xml 파일을 사용한다.
				reader.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return sessionFactory;
	}
	
	public List<OnboardDto> selectAllArticles(){
		sessionFactory = getInstance();
		SqlSession session = sessionFactory.openSession();
		
		List<OnboardDto> articleList = session.selectList("mapper.notice.selectAllArticles");
		session.close();
		return articleList;
	}
    //게시판에서 게시글리스트를 출력하는 클래스이다.
    //noticeboard.xml에서 selectAllArticles id값을 가진 select 태그를 실행해 리스트를 출력한다.
	
	public void insertArticle(OnboardDto onboardDto) {
		sessionFactory = getInstance();
		SqlSession session = sessionFactory.openSession();
		session.insert("mapper.notice.insertArticle",onboardDto);
		session.commit();
	}
	//게시판에서 게시물을 입력하는 메소드이다.
    //세션: 스프링 애플리케이션과 데이터베이스의 연결통로
    
	public OnboardDto selectArticle(int articleNo) {
		sessionFactory = getInstance();
		SqlSession session = sessionFactory.openSession();
		OnboardDto article = session.selectOne("mapper.notice.selectArticle", articleNo);
		session.close();
		return article;
	}
    //게시판에서 상세페이지를 담당하는 메소드이다.
}

- Dao(data access object): 데이터베이스의 데이터에 접근하기 위한 객체이다.

[3] BoardService.java

package com.julian5383.service;

import java.util.List;

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

import com.julian5383.model.BoardDao;
import com.julian5383.model.OnboardDto;

public class BoardService {
	@Autowired
	BoardDao boardDao;
	
	public List<OnboardDto> listArticles(){
		List<OnboardDto> articleList = boardDao.selectAllArticles();
		return articleList;
	}
    //게시글 리스트 조회를 담당하는 메소드이다.
	
	public void addArticle(OnboardDto onboardDto) {
		boardDao.insertArticle(onboardDto);
	}
	//게시물 입력을 담당하는 메소드이다.
    
	public OnboardDto viewArticle(int articleNo) {
		OnboardDto article = boardDao.selectArticle(articleNo);
		return article;
	}
    //게시물 조회를 담당하는 메소드이다.
}

- Service: Dao가 DB에서 받아온 데이터를 전달받아 가공하는 클래스이다.

[4] BoardController.java

package com.julian5383.controller;

import java.util.List;

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

import com.julian5383.model.OnboardDto;
import com.julian5383.service.BoardService;

@Controller //내부적으로 컨트롤러 역할을 할수 있게한다.
@RequestMapping("/board") //board 주소를 시작으로 다른 메소드 주소에 접근한다.
public class BoardController {
	@Autowired
	BoardService boardService;
	@Autowired
	OnboardDto onboardDto;
	
	List<OnboardDto> articleList;
	
    //list경로(list가 없어도 됨)로 요청이 들어오면 해당 메소드와 매칭
    //boardService 객체의 listArticles 메소드를 호출한다.
    //해당 메소드에 있는 게시글들을 list.jsp의 items="data_list"인 태그에 출력한다.
	@RequestMapping({"/list", "/"})
	public String getArticleList(Model model) {
		articleList = boardService.listArticles();
		model.addAttribute("data_list",articleList);
		return "list";
	}
	
    //글쓰는 페이지이다.
	@RequestMapping("/add")
	public String writeArticle() {
		return "write";
	}
	
    //글을 쓰고 저장하는 페이지이다.
    //onboardDto 객체에 title, content, bit값을 넣고
    //boardService의 addArticle로 입력한다.
    //입력후 list페이지로 돌아간다.
    //@RequestParem을 통해 write.jsp에 i_title, i_content name을 가진 태그에 입력된 내용을 가져온다.
	@PostMapping("/addArticle")
	public String addArticle(@RequestParam(value="i_title")String title, @RequestParam(value="i_content")String content) {
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		onboardDto.setWrite_id("bit");
		boardService.addArticle(onboardDto);
		
		return "redirect:list";
	}
	
    //게시글 조회 메소드이다.
    //articleNo를 통해서 페이지 번호로 받는다.
    //ModelAndView를 통해 view.jsp파일에 해당 게시글을 출력한다.
    //"article"라는 키를 통해서 onboardDto의 값들을 출력한다.
	@GetMapping("/view")
	public ModelAndView viewArticle(@RequestParam(value="no")String articleNo) {
		onboardDto = boardService.viewArticle(Integer.parseInt(articleNo));
		ModelAndView mv = new ModelAndView();
		mv.setViewName("view");
		mv.addObject("article", onboardDto);
		return mv;
	}
	
    //model.addAttribute를 통해서 게시글을 출력할 수 있다.
//	@GetMapping("/view")
//	public String viewArticle2(Model model,@RequestParam(value="no")String articleNo) {
//		onboardDto = boardService.viewArticle(Integer.parseInt(articleNo));
//		model.addAttribute("article", onboardDto);
//		return "view";
//	}//둘다 똑같음
}

- Controller: View와 Model(비즈니스 로직)을 연결해주는 다리 역할을 한다.

[5] BoardConfig.java

package com.julian5383.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.julian5383.controller.BoardController;
import com.julian5383.model.BoardDao;
import com.julian5383.model.OnboardDto;
import com.julian5383.service.BoardService;

@Configuration
public class BoardConfig {
	@Bean
	public OnboardDto onboardDto() {
		return new OnboardDto();
	}
	
	@Bean
	public BoardDao boardDao() {
		return new BoardDao();
	}
	
	@Bean
	public BoardService boardService() {
		return new BoardService();
	}
	
	@Bean
	public BoardController boardController() {
		return new BoardController();
	}
}

- 컨트롤러에 선언한 @Autowired가 스프링 컨테이너에 등록한 빈에게 의존관계주입이 필요하기 때문에 BoardConfig.java에 onboardDto, boardDao, boardService, boardController를 빈으로 추가한다.

- @autowired 사용시 @bean을 이용해 해당 자리에 주입된 메소드가 무엇인지 알아야 한다.

 

7. jsp 파일 만들기

[1] list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!--http://localhost:8080이 context주소-->
<%
request.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 목록</title>
<style>
    .cls1{
        text-decoration: none;
    }
    .cls2{
        text-align: center;
        font-size: 30px;
        display: block;
    }
</style>
</head>
<body>
    <table align="center" border="1" width="80%">
        <thead>
            <tr height="10" align="center" bgcolor="lightgreen">
                <th>글번호</th>
                <th>작성자</th>
                <th>제목</th>
                <th>작성일</th>
            </tr>
            <c:choose>
                <c:when test="${empty data_list}">
                <!--게시물이 없을 경우 해당 <c>태그를 출력한다.-->
                    <tbody>
                        <tr height="10">
                            <td colspan="4">
                                <p align="center">
                                    <b><span style="font-size: 9pt;">등록된 글이 없습니다.</span></b>
                                </p>
                            </td>
                        </tr>
                    </tbody>
                </c:when>
                <c:otherwise>
                    <tbody>
                        <c:forEach var="article" items="${data_list}" varStatus="articleNum">
                        <!--게시물이 있을 경우 해당 <c>태그를 출력한다.-->
                            <tr align="center">
                                <td width="5%">${articleNum.count}</td>
                                <td width="10%">${article.write_id}</td>
                                <td align="left">
                                    <span style="padding-right: 30px;"></span>
                                    <a class="cls1" href="${contextPath}/board/view?no=${article.article_no}">
                                        ${article.title}
                                    </a>
                                    <!--게시물 상세페이지로 이동한다.-->
                                </td>
                                <td width="10%">${article.write_date}</td>
                            </tr>
                        </c:forEach>
                    </tbody>
                </c:otherwise>
            </c:choose>
        </thead>
    </table>
    <a class="cls1" href="${contextPath}/board/add"><span class="cls2">글쓰기</span></a>
    <!--write.jsp로 이동한다.-->
</body>
</html>

- contextpath에는 board가 있다.

- contextpath는 절대경로를 의미한다.

- article은 컨트롤러에 선언한 article 키값을 통해서 게시판 리스트를 출력한다.

- <a>태그가 있는 곳에는 ${article.article_no}은 데이터베이스에 입력된 열의 id값이다. 이 id를 통해서 특정 게시물로 이동한다.

[2] view.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<%
request.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상세 조회</title>
<script>
    function backToList(frm){
        frm.action="${contextPath}/board/list";
        //contextPath = board
        frm.submit();
    }//list.jsp로 이동한다.
    //frm.submit에 수정된 게시물을 제출하는 것이다.(다음 게시물에 작성예정)
    
    function fn_enable(frm){
        document.querySelector('#i_title').disabled=false;
        document.querySelector('#i_content').disabled=false;
        document.querySelector('#tr_btn').style.display='none';
        document.querySelector('#tr_btn_modify').style.display='table-row';
    }//fn_enable 함수가 있는 태그를 클릭시 i_title, i_content의 disabled가 해제된다.
    //tr_btn의 id를 가진 태그들이 사라진다.
    //tr_btn_modify의 id를 가진 태그들이 띄어진다.
</script>
<style>
    #tr_btn_modify{
        display:none;
    }
</style>
</head>
<body>
    <form name="articleForm" method="post" action="">
        <table border="0" align="center">
            <tbody> <!--list.jsp에 이동했을때 <a>태그의 id값에 따라 특정 게시물 내용을 출력한다.-->
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">글번호</td>
                    <td>
                        <input type="text" value="${article.article_no}" disabled>
                        <input type="hidden" value="${article.article_no}" name="articleNo">
                    </td>
                </tr>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">작성자</td>
                    <td>
                        <input type="text" value="${article.write_id}" name="writer" disabled>
                    </td>
                </tr>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">제목</td>
                    <td>
                        <input type="text" value="${article.title}" name="title" id="i_title" disabled>
                    </td>
                </tr>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">내용</td>
                    <td>
                        <textarea rows="20" cols="60" name="content" id="i_content" disabled>
                            ${article.content}
                        </textarea>
                    </td>
                </tr>
                <tr>
                    <td width="20%" align="center" bgcolor="#FF9933">작성일</td>
                    <td>
                        <input type="text" value="${article.write_date}" disabled>
                    </td>
                </tr>
                <tr id="tr_btn">
                    <td colspan="2" align="center">
                        <input type="button" value="수정" onclick="fn_enable(articleForm)">
                        <!--수정 버튼을 눌렀을때 fn_enable 함수에 있는 내용들이 동작한다.-->
                        
                        <input type="button" value="삭제" 
                        onclick="fn_remove('${contextPath}/board/remove','${article.article_no}')">
                        <!--해당 게시물을 삭제한다.(다음 게시글에서 설명예정)-->
                        <input type="button" value="목록보기" onclick="backToList(articleForm)">
                        <!--list.jsp로 이동한다.-->
                    </td>
                </tr>
                <tr id="tr_btn_modify">
                    <td colspan="2" align="center">
                        <input type="button" value="저장" onclick="fn_modify_article(articleForm)">
                        <!--fn_modify_article 함수가 동작한다.(다음 게시물에서 설명예정)-->
                        
                        <input type="button" value="취소" onclick="backToList(articleForm)">
                        <!--list.jsp로 이동한다.-->
                    </td>
                </tr>
            </tbody>
        </table>
    </form>
</body>
</html>

[3] write.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<%
request.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 쓰기</title>
<script>
    function backToList(frm){
        frm.action="${contextPath}/board/list";
        //contextPath = board
        frm.submit();
    }
    //list.jsp로 이동하는 함수이다.
</script>
<style>
    .class-caption {
        width:100px;
    }
    .class-content {
        width:500px;
    }
</style>
</head>
<body>
    <h1 style="text-align: center;">글 쓰기</h1>
    <form name="articleForm" method="post" action="${contextPath}/board/addArticle">
    <!--<form> 태그의 action 속성은 폼 데이터(form data)를 서버로 보낼 때 해당 데이터가 도착할 URL을 명시한다.-->
        <table border="0" align="center">
            <tbody>
                <tr>
                    <td align="right" class="class-caption">글제목: </td>
                    <td colspan="2">
                        <input type="text" maxlength="100" name="i_title" class="class-content">
                    </td>
                </tr>
                <tr>
                    <td align="right" valign="top" class="class-caption">글내용: </td>
                    <td colspan="2">
                        <textarea name="i_content" rows="10" maxlength="2000" class="class-content"></textarea>
                    </td>
                </tr>
                <!--i_title, i_content는 controller의 addArticle 메소드에 값을 가져갈때 어느태그에서 가져갈지 명시해주는 역할을 한다. -->
                <tr>
                    <td align="right"></td>
                    <td colspan="2">
                        <input type="submit" value="저장">
                        <!--게시물 입력완료시 제출하는 버튼이다.-->
                        <input type="button" value="목록보기" onclick="backToList(articleForm)">
                        <!--list.jsp로 이동한다.-->
                    </td>
                </tr>
            </tbody>
        </table>
    </form>
</body>
</html>

- 새로운 게시글을 입력한다.

 

-->결과

1) 리스트

2) 글쓰기

3) 수정하기(다음 게시물에서 수정하기 구현 예정)