원지의 개발
article thumbnail
728x90

게시판 기본

header / footer

  • jsp 파일에 <div> </div> 태그 기준 위, 아래로 header, footer 넣기 (상대경로 사용)
<%@ include file ="../include/header.jsp" %>

header, footer 부분 확인


게시판 작성

게시글 작성

1. 데이터베이스 만들기

-- 테이블 생성
create table board (
    bno number(10) primary key,
    writer varchar2(30) not null, --암묵적인 fk
    title varchar2(50) not null,
    content varchar2(500),
    regdate date default sysdate,
    hit number(10) default 0

);

-- 시퀀스 생성
-- bno에 들어갈 시퀀스
create sequence board_seq nocache;
  • 테이블 긁어와서 메모로 저장 후 확인하면서 쓰기

2. 게시판 기본값 확인 후 name 적기

board_write.jsp

  • name (key로 사용) 적기: name은 각각 데이터의 컬럼명으로 설정
  • required: 반드시 입력되야 하는 값
  • submit: form 태그를 보내주는 역할
  • button: 단순한 push 버튼, 연결되는 페이지를 onclick 속성으로 넣어줘야 함
<input type="submit" value="작성 완료" >
<input type="button" value="목록" onclick="location.href= '컨트롤러이용(board_list.board)' ">
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file ="../include/header.jsp" %>

<div align="center" class="div_center">
	<h3>게시판 글 작성 페이지</h3>
	<hr>
	
	<%-- name은 각각 데이터의 컬럼명으로 설정 --%>
	<form action="registForm.board" method="post">
		<table border="1" width="500">
			<tr>
				<td>작성자</td>
				<td>
					<input type="text" name="writer" size="10" required>
				</td>
			</tr>
			<tr>
				<td>글 제목</td>
				<td>
					<input type="text" name="title" required>
				</td>
			</tr>
			<tr>
				<td>글 내용</td>
				<td>
					<textarea rows="10" style="width: 95%;" name="content"></textarea>
				</td>
			</tr>
			<tr>
				<td colspan="2">
					<input type="submit" value="작성 완료" >
					&nbsp;&nbsp;
					<input type="button" value="목록" onclick="location.href= 'board_list.board' ">         
				</td>
			</tr>
			
		</table>
	</form>
	
</div>

<%@ include file ="../include/footer.jsp" %>

3. 컨트롤러 생성

@WebServlet("*.board")
public class BoardController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);	
	}
	
	protected void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
			//한글처리
			request.setCharacterEncoding("utf-8");
			//요청분기
			String uri = request.getRequestURI();
			String path = request.getContextPath();
			
			String command = uri.substring(path.length());
			
			System.out.println("요청경로: " + command);

			BoardService service = new BoardServiceImpl();
//			HttpSession session = null;
			
			if ( command.equals("/board/board_write.board") ) { //등록화면
				request.getRequestDispatcher("board_write.jsp").forward(request, response);
				
			} else if( command.equals("/board/board_list.board") ) { //목록화면
				request.getRequestDispatcher("board_list.jsp").forward(request, response);
				
			} else if( command.equals("/board/board_content.board") ) { //상세내용화면
				request.getRequestDispatcher("board_content.jsp").forward(request, response);
				
			} else if( command.equals("/board/board_modify.board") ) { //수정화면
				request.getRequestDispatcher("board_modify.jsp").forward(request, response);

			} else if( command.equals("/board/registForm.board") ) { //글 등록
				service.regist(request, response);
				response.sendRedirect("board_list.board"); //상대경로여서 /가 필요없음
			}
	}

}

  • 컨트롤러에 doAction 메서드 생성 후 각 화면 포워딩
  • board_write.board 화면에서 form으로 submit하면 registForm.board로 넘어감
  • 컨트롤러에서는 서비스 객체 생성 후 세션 null로 작업 ▶ 후에 세션 사용 예정
게시글 등록
1. service의 regist메서드로 연결
2. service에서 등록에 필요한 파라미터를 받음
3. dao의 void regist() 메서드를 생성하고 insert작업
4. insert 이후에 컨트롤러에서 리스트(목록화면)로 리다이렉트(여기서는 리다이렉트가 국룰)

4. service 인터페이스, serviceImpl 오버라이딩

  • form태그에 들어가 있는 데이터라면, 무조건 getParameter 사용!
  • 클라이언트에서 가져온 값을 DAO로 넘김  (void형이라 반환값 없음)

5. VO 설정 후 DAO 데이터베이스 변수 선언 및 연결

package com.example.board.model;

import java.sql.Timestamp;

public class BoardVO {

	private int bno;
	private String writer;
	private String title;
	private String content;
	private Timestamp regdate;
	private int hit;
	
	public BoardVO() { //ctrl + space
	}
	
	public BoardVO(int bno, String writer, String title, String content, Timestamp regdate, int hit) {
		super();
		this.bno = bno;
		this.writer = writer;
		this.title = title;
		this.content = content;
		this.regdate = regdate;
		this.hit = hit;
	}


	public int getBno() {
		return bno;
	}

	public void setBno(int bno) {
		this.bno = bno;
	}

	public String getWriter() {
		return writer;
	}

	public void setWriter(String writer) {
		this.writer = writer;
	}

	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 Timestamp getRegdate() {
		return regdate;
	}

	public void setRegdate(Timestamp regdate) {
		this.regdate = regdate;
	}

	public int getHit() {
		return hit;
	}

	public void setHit(int hit) {
		this.hit = hit;
	}
}
package com.example.board.model;
public class BoardDAO {
	
	//1. 나 자신의 객체를 생성해서 1개로 제한
	private static BoardDAO instance = new BoardDAO();
	
	//2. 직접 객체를 생성할 수 없도록 생성자에 private
	private BoardDAO() {
		//드라이버 클래스는 어차피 생성되어야 하므로 생성자에 포함
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
		} catch (Exception e) {
			System.out.println("드라이버클래스 로드에러");
		}
	}
	
	//3. 외부에서 객체 생성을 요구할 때 getter 메서드를 통해 1번의 객체를 반환
	public static BoardDAO getInstance() {
		return instance;
	}
	
	//4. 필요한 데이터베이스 변수 선언
	public String url = "jdbc:oracle:thin:@localhost:1521:xe";
	public String uid = "JSP";
	public String upw = "JSP";
	Connection conn;
	PreparedStatement pstmt;
	ResultSet rs;
	
	public void regist(String writer, String title, String content) {
		
		String sql = "insert into board (bno, writer, title, content) values (board_seq.nextval, ?, ?, ?)";
		
		try {
			conn = DriverManager.getConnection(url, uid, upw);
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, writer);
			pstmt.setString(2, title);
			pstmt.setString(3, content);
			
			pstmt.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(conn, pstmt, rs);
		}
		
	}
}

6. controller에서 화면 보내주기

  • 상대경로여서 /가 필요없음

select: 값 필요한 것 (VO객체 반환)
insert, update, delete: 성공, 실패 (정수형 또는 void 반환)

게시글 조회 (서비스 영역의 조회 메서드)

else if( command.equals("/board/board_list.board") ) { //목록화면
				
				//조회메서드 - list를 화면으로 가지고 나감
				ArrayList<BoardVO> list = service.getList(request, response);
				request.setAttribute("list", list); //한번만 나갈꺼니까 request
				
				request.getRequestDispatcher("board_list.jsp").forward(request, response);
 }
  • 컨트롤러에서는 list로 가지고 나감
//조회메서드
	public ArrayList<BoardVO> getList() {
		
		ArrayList<BoardVO> list = new ArrayList<>();
		String sql = "select * from board order by bno desc";
		
		try {
			conn = DriverManager.getConnection(url, uid, upw);
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.executeQuery();
			
			//rs결과를 list에 저장
			while(rs.next()) {
				//rs가 여러번 실행할 때마다 (= 다음 row가 있다면)
				//한 행에 대한 처리(getInt, getString, getDouble, getTimestamp, getDate)
				int bno = rs.getInt("bno");
				String writer = rs.getString("writer");
				String title = rs.getString("title");
				String content = rs.getString("content");
				Timestamp regdate = rs.getTimestamp("regdate");
				int hit = rs.getInt("hit");
				
				//BoardVO타입 객체 생성후 생성자로 한번에 담기 
				BoardVO vo = new BoardVO(bno, writer, title, content, regdate, hit);
				
				//list에 객체 저장
				list.add(vo);
			}
			
//			위와 같은 구문
//			그냥 객체 생성 후 vo.set으로 저장
//			화면에서 쓸거면 getTimestamp가 편하고, 자바에서 쓰려면 getDate가 편함
//			while(rs.next()) {
//				BoardVO vo = new BoardVO();
//				vo.setBno( rs.getInt("bno"));
//				vo.setTitle( rs.getString("title"));
//				vo.setWriter( rs.getString("writer"));
//				vo.setContent( rs.getString("content"));
//				vo.setRegdate( rs.getTimestamp("regdate"));
//				vo.setBno( rs.getInt("hit"));
//			
//				list.add(vo);
//				
//			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(conn, pstmt, rs);
		}
		
		return list;
	}
  • DAO에서 vo에 담는 방법 2가지 (한 행에 대한 처리 후 담기 or 객체 생성 후 set으로 저장)
  • 화면에서 쓸거면 getTimestamp가 편하고, 자바에서 쓰려면 getDate가 편함

  • 컨트롤러에서 설정한 list라는 이름으로 화면에서 처리

출처: chat gpt

  • forEach로 돌리면서 게시글 리스트 보여주기

  • fmt 사용하여 날짜 형식 설정

상세화면 - get방식

BoardServiceImpl

  • board_list.jsp에서는 제목을 누르면 bno를 가지고 나가는 get방식 사용
  • 컨트롤러로 가서 글에 대한 정보 조회 내용 적기
  • ServiceImpl에서  a태그로 넘어오는 param인 bno를 받아서 sql문으로 넘겨줌
  • bno에 맞는 boardVO를 가지고 나와서 화면에 vo로 넘겨줌
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>    

<%@ include file ="../include/header.jsp" %>

<div align="center" class="div_center">
	<h3>게시글 내용 보기</h3>
	<hr>
	<table border="1" width="600">
		<tr>
			<td width="20%">글번호</td>
			<td width="30%">${vo.bno }</td>
			
			<td width="20%">조회수</td>
			<td width="30%">${vo.hit }</td>
		</tr>
		<tr>
			<td>작성자</td>
			<td>${vo.writer }</td>
			
			<td>작성일</td>
			<td ><fmt:formatDate value="${vo.regdate }" pattern="yyyy-MM-dd HH시mm분"/></td>
		</tr>
		
		<tr>
			<td width="20%">글제목</td>
			<td colspan="3">${vo.title }</td>
		</tr>
		<tr>
			<td width="20%">글내용</td>
			<td colspan="3" height="120px">${vo.content }</td>
		</tr>
		
		<tr>
			<td colspan="4" align="center">
				<input type="button" value="목록" onclick="location.href='board_list.board' ">&nbsp;&nbsp;
				
				<!-- 로그인 되어 있고, 글 작성자여야 글 수정시 수정, 삭제 버튼이 보임 -->
				<c:if test="${sessionScope.user_id != null && sessionScope.user_id === vo.writer}">
				<input type="button" value="수정" onclick="location.href='board_modify.board?bno=${vo.bno}&writer=${vo.writer }' ">&nbsp;&nbsp;
				<input type="button" value="삭제" onclick="location.href='board_delete.board?bno=${vo.bno}&writer=${vo.writer }' ">&nbsp;&nbsp;
				</c:if>
				
			</td>
		</tr>
	</table>
</div>

<%@ include file ="../include/footer.jsp" %>
  • 상세정보 화면에서는 vo로 값 넘겨주기
  • 로그인 되어 있고 로그인한 아이디와 글 작성자가 같을 경우 수정, 삭제 버튼 보이게 해야함

게시글 수정

  • 수정화면으로 넘어갈 때는 get 방식으로 사용 (수정, 삭제는 post 방식으로 대체 가능)

  • 수정화면에서는 현재 조회한 글에 대한 정보가 필요하기 때문에 service.getContent 재사용
  • 수정하기 버튼을 누르면 post 방식으로 form을 통해 submit됨 (나갈때 hidden태그로 bno 가지고 가야함)

  • updateForm으로 보내면서 update함 (결과 필요없으므로 void형)
  • 게시글 수정 후 1. 게시글 리스트로 가거나 2. bno를 가지고 상세화면으로 보내기

게시글 삭제

  • 삭제 버튼 누르면 get 방식으로 컨트롤러 탐 (자바 스크립트 알아야해서 그냥 get으로 지움)
  • get 방식으로 만들면 URL 주소로 삭제 가능하므로 하면 X (post 사용해야함)

  • 삭제 후 PrintWriter로 삭제, 실패 alert 띄우고, 'board_list.board'라는 URL로 이동
  • 서버 측에서 클라이언트에게 전송되는 응답에 js 코드를 포함하는 방식 (클라이언트에서 실행)

Filter

  • 디스패처 서블릿(FrontController)을 거치기 전에 처리 (요청 들어오기 전에)

생성 방법

1. 필터 클래스는 일반 자바 파일로 생성
2. Filter 인터페이스 상속 받음
3. 일반적으로 doFilter 메서드 오버라이딩
4. 사용 후 doFilter(request, response); 메서드를 반드시 사용함
public class AuthFilter implements Filter {
    @Override 
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    	//컨트롤러(서블릿)으로 들어가기 전에 가로채 처리할 작업을 선언
        
    	//사용을 마치면 반드시 사용
		chain.doFilter(response, request);
    }
}
  • chain: 필터를 다른 필터로 연결
public class MyFilter implements Filter {
    
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 작업 수행
        System.out.println("필터 초기화됨");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
        throws IOException, ServletException {
        // 필터 체인 처리
        chain.doFilter(request, response);
    }

    public void destroy() {
        // 필터 종료 시 정리 작업 수행
        System.out.println("필터 종료됨");
    }
}
  • init, destroy 메서드로 필터 전, 후 처리 가능
  • 각각 한번만 실행

사용 방법

1. @WebFilter 어노테이션

//필터를 실행할 요청 경로
//@WebFilter("*.board") .board로 끝나는 모든 요청
@WebFilter({"/board/board_write.board",
		   	"/board/board_modify.board",
			"/board/registForm.board",
			"/board/updateForm.board",
			"/board/board_delete.board"
			})
public class AuthFilter extends HttpFilter implements Filter {
    @Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
	}
}

ctrl+t 하면 확인 가능

2. Web.xml 선언

  • 필터를 2개, 3개씩 거쳐나가면 무조건 web.xml 사용 (순서대로 진행)
  • 필터 A, B를 각각 등록 후, 각 필터의 클래스를 지정
  • A, B가 /board/board_write.board 경로에 매핑되어 있기 때문에 여기에 접근하는 모든 요청에 대해 필터 A, B가 동시에 실행
  • 필터 순서는 <filter-mapping>이 정의된 순서를 기준으로 필터 체인의 정렬 순서를 정의함

로그인 여부 확인 - 인증

  • Authentication: 로그인이 되어있는지 확인하는 작업
package com.example.util.filter;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

//필터를 실행할 요청 경로
@WebFilter({"/board/board_write.board",
		   	"/board/board_modify.board",
			"/board/registForm.board",
			"/board/updateForm.board", //글 작성하고 수정(submit)
			"/board/board_delete.board"
			})

public class AuthFilter extends HttpFilter implements Filter {
       
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		//한글 깨질 수도 있어서 인코딩 필수
		request.setCharacterEncoding("utf-8");
		
		//권한 검사
		HttpServletRequest req = (HttpServletRequest)request;
		HttpServletResponse res = (HttpServletResponse)response;
		
		//req에서 세션을 얻음
		HttpSession session = req.getSession();
		String user_id = (String)session.getAttribute("user_id");
		
		//user_id == null 이라면 권한이 없다는 의미
		if(user_id == null) {
			String path = req.getContextPath(); //컨텍스트패스 (/JSP.web)
			
			res.setContentType("text/html; charset=utf-8");
			PrintWriter out = res.getWriter();
			out.println("<script>");
			out.println("alert('권한이 필요한 기능입니다');");
			out.println("location.href='" + path + "/user/user_login.user" + "';");
			out.println("</script>");
			
			return; // 함수를 종료하면 컨트롤러로 연결 되지 않음, 반드시 종료해야 정상흐름으로 흘러감
		}
		chain.doFilter(request, response); //필터가 여러개라면 다음 필터로 연결
	}

}
  • FilterChain 클래스의 객체인 chain을 이용해서 다른 필터나 서블릿과 연결하는 코드를 반드시 작성해야 함
  • 모든 작성자에게 필요한 페이지에 접근할 때 사용자가 로그인 되어있는지 확인
  • 로그인 되어 있지 않으면 메세지가 뜨고, 로그인 페이지로 리디렉션

작성자 권한 확인 - 인가

  • Authorization: (로그인 후) 인증받은 사용자가 서비스의 기능을 사용할 때 그걸 알아보고 허가해 주는 것
package com.example.util.filter;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebFilter({"/board/board_modify.board",
			"/board/updateForm.board", //사용자와 session값이 같아야 업데이트, 삭제 되니까 넣어줌(없어도 되긴함)
			"/board/board_delete.board" }) //경로
public class AuthFilter2 implements Filter{

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {

		//한글 깨질 수도 있어서 인코딩 필수
		request.setCharacterEncoding("utf-8");
		
		//권한 검사
		HttpServletRequest req = (HttpServletRequest)request;
		HttpServletResponse res = (HttpServletResponse)response;

		//각 요청에 넘어오는 writer 파라미터
		String writer = request.getParameter("writer");
		
		//세션에 저장된 user_id
		HttpSession session = req.getSession();
		String user_id = (String)session.getAttribute("user_id");
		
//		System.out.println("작성자: " + writer);
//		System.out.println("세션ID: " + user_id);
		
		//세션이 없어나 or 작성자와 세션이 다른 경우
		if(!user_id.equals(writer) ) { //user_id == null || !writer.equals(user_id)
			String path = req.getContextPath(); //컨텍스트패스 (/JSP.web)
		
			res.setContentType("text/html; charset=utf-8");
			PrintWriter out = res.getWriter();
			out.println("<script>");
			out.println("alert('권한이 필요한 기능입니다');");
			out.println("location.href='" + path + "/board/board_list.board" + "';");
			out.println("</script>");
			return;
		}
		chain.doFilter(request, response);//필터가 여러개라면 다음 필터로 연결, 그렇게해서 연결된 필터가 없으면 컨트롤러로 간다
	}

}
  • 특정 작성자가 필요한 페이지에 접근할 때 해당 글을 작성한 작성자인지 확인
  • 작성자와 세션에 저장된 사용자가 같지 않으면 메세지를 표시하고 게시글 목록 페이지로 리디렉션

2023.02.01 - [Server/Spring] - [Spring] JSP-Spring 조립(톰캣), Spring project 연결, homecontroller, error, context, contextPath

 

[Spring] JSP-Spring 조립(톰캣), Spring project 연결, homecontroller, error, context, contextPath

Tomcat 서버 다운 및 기본 setting 더보기 분리된 spring project 생성 Spring Legacy Project : Spring 의 기본 프로젝트 Spring Starter Project : Spring-Boot의 기본 프로젝트 Finish 누르고 좀 기다리기 (모듈 다운로드 기

j-won950101.tistory.com


JDBCUtil

package com.example.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class JDBCUtil {
	
	public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs)	{
		
		try {
			if(conn != null) conn.close();
			if(pstmt != null) pstmt.close();
			if(rs != null) rs.close();
		} catch (Exception e2) {
			System.out.println("close에러");
		}
		
	}
}
  • DAO에서 JDBC 연결을 꼭 닫아줘야 함

오늘 하루

더보기

기억에 남는 부분

 

어려운 부분

 

문제 해결 부분

 

728x90
profile

원지의 개발

@원지다

250x250