STS3 스프링 게시판 예제

계리·2024년 2월 11일
0

예제를 통해 컴포넌트 구조와 코드 구성에 대해 공부를 해보려고 한다.

프로젝트마다 조금씩 다르겠지만 일반적으로는 비즈니스 컴포넌트는 네 개의 자바 파일로 구성된다. 그리고 자파 파일을 작성하는 순서와 이름 규칙도 어느 정도는 정해져 있는 것이 일반적이다.

BoardVO, BoardDAO BoardService BoardServiceImpl 클래스로 게시판 예제를 구성해본다.

파일 위치도 중요하기 때문에 아래 이미지를 통해 패키지 이름과 자바 파일 이름과 위치를 잘 확인하고 실습을 진행했다.

BoardVO.java

VO(Value Object) 클래스는 레이어와 레이어 사이에서 관련된 데이터를 한꺼번에 주고받을 목적으로 사용하는 클래스이다. DTO(Data Transfer Object)라 하기도 하는데 데이터 전달을 목적으로 사용하는 객체이므로 결국 같은 의미의 용어라고 생각하면 된다라고 설명이 되어있다.

내가 이해하기로는 예를 들어서 이름, 핸드폰번호, 이메일 주소 항목들을 사용하자고 클라이언트와 서버가 약속을 하고 클라이언트에서 서버로 요청을 회원가입을 하기 위해 서로 약속한 항목들을 전달 받아 회원가입 정보를 데이터베이스에 저장하기 위한 객체 클래스라고 이해를 했다.

그러면 데이터베이스에 저장하기 위한 테이블도 클라이언트와 서버가 약속한 항목들을 데이터베이스에도 똑같이 생성을 해주면 해당 항목에 맞게 저장되고 그것들을 가지고 조회하거나 수정할 수 있다라고 내 스스로 이해를 했다.

그렇다면 어떠한 객체를 사용하기 위해 VO(DTO)클래스 구조 데이터베이스의 테이블 구조가 같아야 한다라고 볼 수 있을 것이다. 실제로도 앞에서 H2 데이터베이스에서 테이블 생성할 때 Board 테이블 항목과 지금 BoardVO 클래스에서 선언된 멤버변수들을 보면 구조와 이름이 똑같이 되어있다.

package com.springbook.biz.board;

import java.sql.Date;

public class BoardVO {
	private int seq;
	private String title;
	private String writer;
	private String content;
	private Date regDate;
	private int cnt;
	
	public int getSeq() {
		return seq;
	}
	public void setSeq(int seq) {
		this.seq = seq;
	}
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	
	public String getWriter() {
		return writer;
	}
	public void setWriter(String writer) {
		this.writer = writer;
	}
	
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	
	public Date getRegDate() {
		return regDate;
	}
	public void setRegDate(Date regDate) {
		this.regDate = regDate;
	}
	
	public int getCnt() {
		return cnt;
	}
	public void setCnt(int cnt) {
		this.cnt = cnt;
	}
	
	@Override
	public String toString() {
		return "BoardVO [seq=" + seq + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regDate="
				+ regDate + ", cnt=" + cnt + "]";
	}

}

JDBCUtil.java

Mybatis나 JPA와 같은 프레임워크를 사용하기 전까지는 JDBC로 데이터베이스에 접근할 것이다. 그래서 모든 DAO 클래스에서 공통으로 사용할 JDBCUtil 클래스로 DB Connection 획득과 해제 공통 작업을 처리한다.

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

public class JDBCUtil {
	public static Connection getConnection() {
		try {
			Class.forName("org.h2.Driver");
			return DriverManager.getConnection("jdbc:h2:tcp://localhost/~/test", "sa", "");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static void close(PreparedStatement stmt, Connection conn) {
		if(stmt != null) {
			try {
				if(!stmt.isClosed()) stmt.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				stmt = null;
			}
		}
		
		if(conn != null) {
			try {
				if(!conn.isClosed()) conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				conn = null;
			}
		}
	}
	
	public static void close(ResultSet rs, PreparedStatement stmt, Connection conn) {
		if(rs != null) {
			try {
				if(!rs.isClosed()) rs.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				rs = null;
			}
		}
		
		if(stmt != null) {
			try {
				if(!stmt.isClosed()) stmt.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				stmt = null;
			}
		}
		
		if(conn != null) {
			try {
				if(!conn.isClosed()) conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				conn = null;
			}
		}
	}
}

BoardDAO.java

BoardVO 객체를 매개변수와 리턴타입으로 사용하면서 Board 테이블과 CRUD 기능을 처리할 클래스이다.

package com.springbook.biz.board.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Repository;

import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.JDBCUtil;

// DAO(Data Acess Object)
@Repository
public class BoardDAO {
	// JDBC 관련 변수
	private Connection conn = null;
	private PreparedStatement stmt = null;
	private ResultSet rs = null;
	
	// SQL 명령어들
	private final String BOARD_INSERT = "INSERT INTO BOARD(SEQ, TITLE, WRITER, CONTENT) VALUES((select nvl(max(seq), 0)+1 from board), ?, ?, ?)";
	private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
	private final String BOARD_DELETE = "delete board where seq=?";
	private final String BOARD_GET = "select * from board where seq=?";
	private final String BOARD_LIST = "select * from board order by seq desc";
	
	// CROUD 기능의 메소드 구현
	// 글 등록
	public void insertBoard(BoardVO vo) {
		System.out.println("===> JDBC로 insertBoard() 기능 처리");
		try {
			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_INSERT);
			stmt.setString(1, vo.getTitle());
			stmt.setString(2, vo.getWriter());
			stmt.setString(3, vo.getContent());
			stmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
	}
	
	// 글 수정
	public void updateBoard(BoardVO vo) {
		System.out.println("===> JDBC로 updateBoard() 기능 처리");
		try {
			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_UPDATE);
			stmt.setString(1, vo.getTitle());
			stmt.setString(2, vo.getContent());
			stmt.setInt(3, vo.getSeq());
			stmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
	}
	
	// 글 삭제
	public void deleteBoard(BoardVO vo) {
		System.out.println("===> JDBC로 deleteBoard() 기능 처리");
		try {
			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_DELETE);
			stmt.setInt(1, vo.getSeq());
			stmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
	}
	
	// 글 상세 조회 
	public BoardVO getBoard(BoardVO vo) {
		System.out.println("===> JDBC로 getBoard() 기능 처리");
		BoardVO board = null;
		try {
			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_GET);
			stmt.setInt(1, vo.getSeq());
			rs = stmt.executeQuery();
			if(rs.next()) {
				board = new BoardVO();
				board.setSeq(rs.getInt("SEQ"));
				board.setTitle(rs.getString("TITLE"));
				board.setWriter(rs.getString("WRITER"));
				board.setContent(rs.getString("CONTENT"));
				board.setRegDate(rs.getDate("REGDATE"));
				board.setCnt(rs.getInt("CNT"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
		return board;
	}
	
	// 글 목록 조회 
	public List<BoardVO> getBoardList(BoardVO vo) {
		System.out.println("===> JDBC로 getBoardList() 기능 처리");
		List<BoardVO> boardList = new ArrayList<BoardVO>();
		try {
			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_LIST);
			rs = stmt.executeQuery();
			while(rs.next()) {
				BoardVO board = new BoardVO();
				board.setSeq(rs.getInt("SEQ"));
				board.setTitle(rs.getString("TITLE"));
				board.setWriter(rs.getString("WRITER"));
				board.setContent(rs.getString("CONTENT"));
				board.setRegDate(rs.getDate("REGDATE"));
				board.setCnt(rs.getInt("CNT"));
				boardList.add(board);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
		return boardList;
	}
}

BoardService.java

package com.springbook.biz.board;

import java.util.List;

public interface BoardService {
	// CRUD 기능의 메서드 구현
	// 글 등록
	void insertBoard(BoardVO vo);
	
	// 글 수정
	void updateBoard(BoardVO vo);
	
	// 글 삭제
	void deleteBoard(BoardVO vo);
	
	// 글 상세 조회
	BoardVO getBoard(BoardVO vo);
	
	// 글 목록 조회
	List<BoardVO> getBoardList(BoardVO vo);
}

BoardServiceImpl.java

BoardService의 구현체이므로 BoardService 인터페이스에 든 추상 메서드를 재정의(오버라이딩)하여 구현해야 한다.

클래스 선언부에 객체 생성을 위한 @Service가 선언 되어있으며 클라이언트 프로그램에서 boardService라는 이르므로 요청할 수 있도록 아이디("boardService")도 설정했다.

package com.springbook.biz.board.impl;

import java.util.List;

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

import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;

@Service("boardService")
public class BoardServiceImpl implements BoardService{

	@Autowired
	private BoardDAO boardDAO;
	
	public void insertBoard(BoardVO vo) {
		boardDAO.insertBoard(vo);
		
	}

	public void updateBoard(BoardVO vo) {
		boardDAO.updateBoard(vo);
		
	}

	public void deleteBoard(BoardVO vo) {
		boardDAO.deleteBoard(vo);
		
	}

	public BoardVO getBoard(BoardVO vo) {
		return boardDAO.getBoard(vo);
		
	}

	public List<BoardVO> getBoardList(BoardVO vo) {
		return boardDAO.getBoardList(vo);
	}

}

applicationContext.xml (스프링 설정 파일 수정)

작성된 BoardService 컴포넌트를 스프링 기반으로 테스트하려면 우선 스프링 설정 파일에 <context:component-scan> 설정을 해야한다.

나는 프로젝트 생성할 때 applicationContext.xml 파일은 생성 되지 않았다. 그래서 직접 생성하고 설정을 했기 때문에 xml파일 생성과 설정 하는 과정도 설명하려고 한다.

  • src/main/resources 폴더 선택
  • command + n
  • 검색 창에 xml 검색 -> XML File 선택 -> Next

파일 이름을 applicationContext.xml으로 작성 후 Finish 하면 된다.(나는 이미 있기 때문에 비활성화 되어있는 것이다.) 그러면 아무 것도 설정이 안되어 있는 핀 파일이 생성된다.

그리고 src/main/webapp/WEB-INF/spring/root-context.xml에 설정되어 있는 코드를 전부 applicationContext.xml로 복붙한다. 그리고 <context:component-scan base-package="com.springbook.biz"> </context:component-scan> 추가한다.


실습에 필요한 applicationContext.xml 설정 파일 완성본

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="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">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	<context:component-scan base-package="com.springbook.biz">
	</context:component-scan>
</beans>

BoardServiceClient.java

BoardService 컴포넌트 테스트를 위한 클래스이다. 클래스 실행 전에 H2 데이터베이스를 먼저 실행 시키고나서 클래스를 실생시켜야 한다.

import java.util.List;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;

public class BoardServiceClient {

	public static void main(String[] args) {
		// 1. Spring 컨테이너를 구동한다.
		AbstractApplicationContext container =
				new GenericXmlApplicationContext("root-context.xml");
		
		// 2. Spring 컨테이너로부터 BoardServiceImp 객체를 Lookup한다.
		BoardService boardService = (BoardService) container.getBean("boardService");
		
		// 3. 글 등록 기능 테스트
		BoardVO vo = new BoardVO();
		vo.setTitle("임시 제목");
		vo.setWriter("홍길동 테스트 11111");
		vo.setContent("임시 내용 테스트 11111");
		boardService.insertBoard(vo);
		
		// 4. 글 목록 검색 기능 테스트
		List<BoardVO> boardList = boardService.getBoardList(vo);
		for(BoardVO board : boardList) {
			System.out.println("---> " + board.toString());
		}
		
		// 5. Spring 컨테이너 종료
		container.close();
	}
}

BoardServiceClient.java 클래스가 실행되지 않으면(아마 ClassNotFoundException이 발생한다.) src/test/java의 BoardServiceClient.java 삭제하고 그 자리에 다시 작성한다. 그래도 계속 똑같은 Exception이 난다면

BoardServiceClient.java 파일에서 마우스 우클릭 -> Run As -> Run Configura-tions... 선택


왼쪽 탭에서 Java Application 메뉴를 확장 -> 그 밑에 있는 모든 프로그램 선택 후 삭제

그리고 나서 Java Application 메뉴 더블클릭하면 BoardServiceClient가 등록된다. 이 상태에서 Run 버튼 클릭하면 정상적으로 작동된다.

이렇게 한 번 실행되는 클래스는 이후부터 바로 실행할 수 있다.


참고

profile
gyery

0개의 댓글