게시판 해체 분석 100%!! 코드들의 설명과 동선 완벽하게 이해하기
국비 수업 내용으로 진행한 BoardWeb의 소스 코드를 파일별로 하나씩 보여드리고, 각 코드에서 메서드, 명령어, 주요 구문, 흐름 등을 상세하게 해설한 글입니다.
📦 JDBCUtil.java
코드 원문
package com.springbook.biz.board;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
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(Statement 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, Statement stmt, Connection conn) {
if(rs != null) {
try {
if(!rs.isClosed()) rs.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
rs = null;
}
}
close(stmt, conn);
}
}
상세 설명
1. 목적
- JDBC 관련 자주 쓰는 기능을 묶어놓은 도우미 클래스입니다.
- DB 연결(Connect), DB 관련 자원 닫기(Close) 기능 제공
2. getConnection()
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;
}
}
- Class.forName("org.h2.Driver"): H2 데이터베이스 드라이버를 JVM에 등록(로드)
- DriverManager.getConnection:
- 실제 DB와 연결 생성
- URL: "jdbc:h2:tcp://localhost/~/test"
- H2: 자바에서 자주 쓰는 가벼운 DB
- tcp://localhost: 내 PC에서 H2 DB 서버 실행 중이어야 접속 가능
- ~/test: 사용자 홈 폴더 아래 test DB 파일 사용
- "sa": 사용자 아이디 (H2의 기본값)
- "": 비밀번호 없음
- 예외처리: 오류 발생시 e.printStackTrace()로 로그 출력, null 반환
3. close() 메서드 (자원 닫기)
- DB 작업이 끝난 후 꼭! Statement/Connection/ResultSet을 닫아야 자원이 낭비되지 않음
public static void close(Statement stmt, Connection conn) {
if(stmt != null) {
try {
if(!stmt.isClosed()) stmt.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
stmt = null;
}
}
...
}
- stmt.isClosed(): 이미 닫힌 상태인지 확인 후, 아니면 닫기
- finally: try-catch 이후 무조건 실행됨 (stmt, conn 참조 해제)
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if(rs != null) { ... }
close(stmt, conn);
}
- ResultSet도 닫고, Statement/Connection도 순서대로 닫음
📦 BoardDAO.java
코드 원문
package com.springbook.biz.board;
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;
@Repository("boradDAO")
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 FROM 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";
// 글 등록
public void insertBoard(BoardVO vo) {
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) {
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) {
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) {
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(rs, stmt, conn);
}
return board;
}
// 글 목록 조회
public List<BoardVO> getBoardList() {
List<BoardVO> boardList = new ArrayList<>();
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(rs, stmt, conn);
}
return boardList;
}
}
상세 해설
1. 클래스 선언
@Repository("boradDAO")
public class BoardDAO {
// ...
}
- @Repository("boradDAO") : 스프링 컨테이너에 'boradDAO'라는 이름으로 이 객체를 등록합니다. (오타이지만 'boradDAO'로 등록됨.)
- BoardDAO : 실제로 데이터베이스와 연결해 게시글을 저장/수정/삭제/조회하는 역할입니다.
2. 필드 선언
private Connection conn = null;
private PreparedStatement stmt = null;
private ResultSet rs = null;
- DAO에서 DB 작업할 때 자주 쓰는 객체들입니다.
- conn: DB에 접속할 때 사용 (전화선).
- stmt: SQL을 전달하고 실행하는 역할 (명령어 입력창).
- rs: SELECT 결과를 받아오는 테이블(엑셀처럼 행/열 데이터).
3. SQL 쿼리 상수
private final String BOARD_INSERT = "...";
private final String BOARD_UPDATE = "...";
private final String BOARD_DELETE = "...";
private final String BOARD_GET = "...";
private final String BOARD_LIST = "...";
- final : 값이 바뀌지 않는 '상수'입니다.
- 각 상수는 DB에 요청할 SQL 문장(글 등록/수정/삭제/조회/목록).
- 예시) BOARD_INSERT : 새 글을 등록하는 SQL입니다.
NVL(MAX(seq),0)+1은 "가장 큰 번호+1"을 계산해 새 글의 글 번호를 정함.
→ Oracle, H2에서 NULL일 때 0으로 바꾸는 함수가 NVL입니다.
4. 주요 메서드 (기능별 설명)
1) insertBoard(BoardVO vo)
- BoardVO vo: 게시글 한 건의 정보를 담은 객체를 입력받음
- 동작 순서:
- DB 연결 : JDBCUtil.getConnection() 호출로 DB 접속
- SQL 준비 : conn.prepareStatement(BOARD_INSERT)로 SQL을 준비
- 값 바인딩 : stmt.setString() 등으로 SQL의 ?에 값 넣기
- vo.getTitle(), vo.getWriter(), vo.getContent()
- 실행 : stmt.executeUpdate()로 DB에 명령 실행 (INSERT)
- 자원 해제 : finally 블록에서 연결 닫기 (JDBCUtil.close)
- 예외처리 : try-catch로 에러 발생 시 로그 출력
2) updateBoard(BoardVO vo)
글 수정 기능
- 글 번호(seq)에 해당하는 게시글의 제목, 내용을 수정
- 동작 순서:
- DB 연결
- SQL 준비 (UPDATE board SET title=?, content=? WHERE seq=?)
- ? 자리에 값 넣기 (setString, setInt)
- SQL 실행
- 자원 해제
3) deleteBoard(BoardVO vo)
글 삭제 기능
- 글 번호(seq)로 해당 글을 삭제
- SQL: DELETE FROM board WHERE seq=?
4) getBoard(BoardVO vo)
글 한 건 조회(상세 보기)
- 글 번호(seq)로 1개의 글만 SELECT
- SQL: SELECT * FROM board WHERE seq=?
- rs.next(): 결과가 있으면 BoardVO 객체 만들어 각각의 필드 값을 set으로 저장
- setInt, setString, setDate 등: DB에서 꺼낸 값을 BoardVO 필드에 대입
5) getBoardList()
글 목록 전체 조회
- SQL: SELECT * FROM board ORDER BY seq DESC
- while(rs.next()): 결과가 여러 줄이면 한 줄씩 BoardVO 객체로 만들어 리스트에 추가
- 마지막에 모든 글을 담은 List<BoardVO>를 반환
공통 설명
- try-catch-finally:
- try: 실제 DB 작업을 실행
- catch: 에러가 나면 예외 내용 출력
- finally: 무조건 실행되는 부분 (자원 닫기)
- JDBCUtil.getConnection():
- H2 데이터베이스와 연결하는 유틸 메서드(다른 클래스에 구현되어 있음)
- JDBCUtil.close(…):
- Connection, Statement, ResultSet 등의 DB 자원을 닫아줌
📦 BoardVO.java
코드 원문
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 + "]";
}
}
상세 해설
1. 클래스 선언과 목적
public class BoardVO {
...
}
- VO(Value Object): 데이터(값)를 담는 그릇입니다.
- 이 클래스는 게시글 한 개의 정보를 저장하는 데 사용합니다.
2. 필드(멤버 변수)
private int seq; // 글 번호 (번호를 매김)
private String title; // 글 제목
private String writer; // 작성자(이름)
private String content; // 글 내용
private Date regDate; // 등록일 (java.sql.Date)
private int cnt; // 조회수(얼마나 많이 봤는지)
- private: 클래스 외부에서 바로 접근하지 못하게 하는 접근 제한자(정보 은닉).
3. Getter/Setter 메서드
public int getSeq() { ... }
public void setSeq(int seq) { ... }
...
- Getter: 필드 값을 읽어올 때 사용 ("get~")
- Setter: 값을 저장/변경할 때 사용 ("set~")
- 모든 필드마다 각각 존재함 (자바Beans 규약)
4. toString() 오버라이딩
@Override
public String toString() {
return "BoardVO [seq=" + seq + ...
}
- 객체를 문자열로 표현할 때 자동으로 호출됨
- 예: System.out.println(boardVO객체);
→ "BoardVO [seq=1, title=예시, ...]" 이런 식의 문자열로 표시
5. 용도 예시
- DB에서 한 줄의 게시글을 읽으면, 이 BoardVO에 값을 담아서 컨트롤러/JSP로 넘깁니다.
- 여러 개의 글이 있다면 BoardVO의 리스트(List<BoardVO>)로 저장해서 처리합니다.
📦 UserDAO.java
코드 원문
package com.springbook.biz.user;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.JDBCUtil;
@Repository("userDAO")
public class UserDAO {
private Connection conn = null;
private PreparedStatement stmt = null;
private ResultSet rs = null;
private final String USER_GET = "SELECT * FROM users WHERE id=? AND password=?";
public UserVO getUser(UserVO vo) {
UserVO user = null;
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(USER_GET);
stmt.setString(1, vo.getId());
stmt.setString(2, vo.getPassword());
rs = stmt.executeQuery();
if(rs.next()) {
user = new UserVO();
user.setId(rs.getString("id"));
user.setPassword(rs.getString("password"));
user.setName(rs.getString("name"));
user.setRole(rs.getString("role"));
}
} catch(Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, stmt, conn);
}
return user;
}
}
상세 해설
1. 클래스 역할
- UserDAO: 사용자(로그인 등) 관련 DB 처리 담당 클래스
- DB에서 로그인 정보를 확인(아이디, 비번)
2. 필드
- DB 연결(Connection), SQL 명령 준비(PreparedStatement), 결과(ResultSet) 변수
3. SQL 상수
private final String USER_GET = "SELECT * FROM users WHERE id=? AND password=?";
- sers 테이블에서 id와 password가 모두 일치하는 사용자 정보를 찾는 쿼리
4. getUser(UserVO vo)
- 로그인 검증 메서드
- 동작:
- JDBCUtil로 DB 연결
- SQL 준비 (id, password ?에 바인딩)
- SQL 실행, 결과(rs)
- 사용자가 존재하면 UserVO에 각 값 저장 (id, password, name, role)
- 없으면 user는 null 반환
📦 UserVO.java
코드 원문
package com.springbook.biz.user;
public class UserVO {
// CREATE TABLE users(
// id VARCHAR2(8) PRIMARY KEY,
// password VARCHAR2(20),
// name VARCHAR2(20),
// role VARCHAR2(5)
// );
private String id;
private String password;
private String name;
private String role;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString() {
return "[id=" + id + ", password=" + password + ", name=" + name + ", role=" + role + "]";
}
}
상세해설
1. 역할
- UserVO: 사용자의 한 명 정보를 담는 VO (Value Object)
- 로그인, 인증, 권한 처리 등에 사용
2. 필드
private String id; // 사용자 아이디 (기본키)
private String password; // 비밀번호
private String name; // 사용자 이름
private String role; // 역할(관리자/일반 등)
- 필드명과 DB 컬럼명이 일치
3. Getter/Setter
- 각 필드를 읽고/저장하는 메서드 (get/set)
4. toString()
- 객체를 문자열로 표현
- 예: [id=test, password=1234, name=홍길동, role=ADMIN]
5. 참고: 클래스 맨 위의 주석
// CREATE TABLE users(...
- 실제 DB에 users 테이블을 어떻게 만들었는지(DDL) 주석으로 설명
📦 DispatchServlet.java
코드 원문
package com.springbook.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DispatchServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private HandlerMapping handlerMapping;
private ViewResolver viewResolver;
public void init() throws ServletException {
handlerMapping = new HandlerMapping();
viewResolver = new ViewResolver();
viewResolver.setPrefix("/");
viewResolver.setSuffix(".jsp");
}
// 수업대로라면 doGet doPost가 있을 자리이지만
// 실제로 service가 있으면 두개를 합쳐서 사용할 수 있음.
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/"));
Controller ctrl = handlerMapping.getController(path);
String viewName = ctrl.handleRequest(request, response);
String view = null;
if (viewName.contains(".do")) {
response.sendRedirect(viewName);
} else {
view = viewResolver.getView(viewName);
response.sendRedirect(view);
}
}
}
상세 해설
1. 클래스 역할
- DispatchServlet:
- 모든 웹 요청을 한 곳에서 받아서,
- 어떤 Controller가 처리할지 "분배"해주는 프론트 컨트롤러입니다.
- Spring MVC의 DispatcherServlet 역할과 유사
2. 주요 필드/멤버 변수
private HandlerMapping handlerMapping;
private ViewResolver viewResolver;
- HandlerMapping:
- 요청 URL에 따라 어떤 Controller를 쓸지 결정해주는 클래스
- ViewResolver:
- 컨트롤러가 리턴한 "논리 이름"을 실제 JSP 파일 경로로 바꿔주는 역할
3. init() 메서드
public void init() throws ServletException {
handlerMapping = new HandlerMapping();
viewResolver = new ViewResolver();
viewResolver.setPrefix("/");
viewResolver.setSuffix(".jsp");
}
- init(): 서블릿(이 클래스)이 처음 만들어질 때 자동 실행
- HandlerMapping, ViewResolver 객체를 미리 준비
- ViewResolver의 접두어/접미어를 지정:
- 예: "login" → "/login.jsp"
- 예: "getBoardList" → "/getBoardList.jsp"
4. service() 메서드
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/"));
Controller ctrl = handlerMapping.getController(path);
String viewName = ctrl.handleRequest(request, response);
String view = null;
if (viewName.contains(".do")) {
response.sendRedirect(viewName);
} else {
view = viewResolver.getView(viewName);
response.sendRedirect(view);
}
}
- service():
- 웹 요청이 올 때마다 호출됨 (doGet/doPost 대신)
- 동작 순서
- 요청 URI에서 경로(path) 추출
- 예: "/Boadweb/getBoardList.do" → "/getBoardList.do"
- HandlerMapping으로 해당 컨트롤러 객체 찾기
- 예: "/login.do" → LoginController
- 예: "/getBoardList.do" → getBoardListController
- 컨트롤러의 handleRequest 실행
- 컨트롤러가 비즈니스 로직 수행 & 결과 화면(논리 이름) 반환
- 뷰 이동(리다이렉트)
- viewName이 ".do"를 포함(다음 컨트롤러 호출이면):
→ sendRedirect(viewName) (브라우저가 새로 해당 URL로 요청) - 아니면(JSP로 바로 이동이면):
→ ViewResolver로 실제 JSP 경로 얻어서
→ sendRedirect(view) (JSP 파일로 이동)
- viewName이 ".do"를 포함(다음 컨트롤러 호출이면):
- 요청 URI에서 경로(path) 추출
📦 HandlerMapping.java
코드 원문
package com.springbook.controller;
import java.util.HashMap;
import java.util.Map;
public class HandlerMapping {
private Map<String, Controller> mappings;
public HandlerMapping() {
mappings = new HashMap<>();
mappings.put("/login.do", new LoginController());
mappings.put("/logout.do", new LogoutController());
mappings.put("/getBoardList.do", new getBoardListController());
mappings.put("/getBoard.do", new getBoardController());
mappings.put("/insertBoard.do", new insertBoardController());
mappings.put("/updateBoard.do", new updateBoardController());
mappings.put("/deleteBoard.do", new deleteBoardController());
}
public Controller getController(String path) {
return mappings.get(path);
}
}
상세 해설
1. 클래스 역할
- HandlerMapping:
- "어떤 URL(주소)에 어떤 Controller 객체를 연결할지"를 저장해두는 매니저
- 즉, "/login.do" → LoginController 객체처럼 매핑
2. 주요 변수
private Map<String, Controller> mappings;
- Map: key-value(키-값) 구조의 자료구조
- 여기서 key는 요청 경로("/login.do" 등), value는 Controller 객체
3. 생성자
public HandlerMapping() {
mappings = new HashMap<>();
mappings.put("/login.do", new LoginController());
...
}
- 생성자: HandlerMapping 객체가 처음 만들어질 때 자동 실행
- mappings.put:
- 각각의 URL에 알맞은 Controller 객체를 미리 연결
- 예시:
- "/login.do" 요청 오면 LoginController 실행
- "/getBoardList.do" → getBoardListController
- "/insertBoard.do" → insertBoardController
- ... (CRUD 각각 등록)
4. getController()
public Controller getController(String path) {
return mappings.get(path);
}
- 입력된 경로(path)에 연결된 Controller 객체를 찾아서 리턴
- 예시: path가 "/getBoardList.do"면, getBoardListController 객체 반환
📦 ViewResolver.java
코드 원문
package com.springbook.controller;
public class ViewResolver {
private String prefix;
private String suffix;
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String getView(String viewName) {
return prefix + viewName + suffix;
}
}
상세 해설
1. 클래스 역할
- ViewResolver:
- "논리적인 뷰 이름" → "실제 JSP 파일 경로"로 바꿔주는 클래스
- 예: "login" → "/login.jsp"
2. 주요 변수
private String prefix;
private String suffix;
- prefix: 접두어(앞에 붙는 문자열)
- suffix: 접미어(뒤에 붙는 문자열)
3. 메서드
public void setPrefix(String prefix) {...}
public void setSuffix(String suffix) {...}
- 접두어/접미어 값 지정 (예: "/"와 ".jsp"를 지정)
public String getView(String viewName) {
return prefix + viewName + suffix;
}
- 실제로는
- "login" → "/" + "login" + ".jsp" = "/login.jsp"
- "getBoardList" → "/getBoardList.jsp"
📦 Contorller.java
코드 원문
package com.springbook.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Controller {
public String handleRequest(HttpServletRequest request, HttpServletResponse response);
}
상세 해설
1. 인터페이스 역할
- Controller 인터페이스:
- "모든 컨트롤러는 반드시 handleRequest()라는 메서드를 구현해야 한다"는 규칙
- 실제 개별 컨트롤러들이 이 규칙을 반드시 따라야 함
2. handleRequest()
public String handleRequest(HttpServletRequest request, HttpServletResponse response);
- 모든 컨트롤러 클래스에서 구현
- 파라미터:
- request: 브라우저가 보낸 정보, 폼 입력 등
- response: 브라우저로 내보낼 정보, 응답 설정 등
- 리턴값:
- "어떤 화면(JSP 등)으로 이동할지"에 해당하는 논리적인 뷰 이름
📦 LoginController.java
코드 원문
package com.springbook.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.springbook.biz.user.UserVO;
import com.springbook.biz.user.UserDAO;
public class LoginController implements Controller {
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
String id = request.getParameter("id");
String pw = request.getParameter("pw");
UserVO vo = new UserVO();
vo.setId(id);
vo.setPassword(pw);
UserDAO userDAO = new UserDAO();
UserVO user = userDAO.getUser(vo);
if (user != null) {
return "getBoardList.do";
} else {
return "login";
}
}
}
상세 해설
1. 클래스 역할
- LoginController:
- 로그인 처리 전담 컨트롤러
- 로그인 폼에서 아이디, 비밀번호를 받아서
DB에서 맞는 사용자인지 검사
2. handleRequest() 구현
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
String id = request.getParameter("id");
String pw = request.getParameter("pw");
- 로그인 폼에서 입력한 값(id, pw) 받아오기
UserVO vo = new UserVO();
vo.setId(id);
vo.setPassword(pw);
- UserVO 객체에 아이디/비밀번호를 담음
UserDAO userDAO = new UserDAO();
UserVO user = userDAO.getUser(vo);
- UserDAO를 통해 DB에서 해당 아이디/비번이 맞는지 조회
- 결과가 있으면 user에 값이, 없으면 null
if (user != null) {
return "getBoardList.do";
} else {
return "login";
}
- 로그인 성공: 게시글 목록(getBoardList.do)로 이동
- 실패: 다시 로그인 화면(login.jsp)으로 이동
📦 LogoutController.java
코드 원문
package com.springbook.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LogoutController implements Controller {
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
// 현재 로그인 세션을 끊는다(로그아웃)
request.getSession().invalidate();
// 로그인 페이지로 이동
return "login";
}
}
상세 해설
- LogoutController: 사용자가 로그아웃할 때 실행되는 컨트롤러
- request.getSession().invalidate();
- 현재 로그인한 사용자의 세션을 끊음(모든 정보 삭제, 로그아웃)
- "login" 반환
- 로그인 화면으로 이동하라는 뜻
📦 GetBoardListController.java
코드 원문
package com.springbook.controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.BoardDAO;
public class GetBoardListController implements Controller {
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
BoardDAO boardDAO = new BoardDAO();
List<BoardVO> boardList = boardDAO.getBoardList();
request.getSession().setAttribute("boardList", boardList);
return "getBoardList";
}
}
상세 해설
- getBoardListController: 게시글 전체 목록을 보여주는 컨트롤러
- BoardDAO boardDAO = new BoardDAO();
- DB에서 게시글을 가져오기 위한 객체 생성
- boardDAO.getBoardList();
- 게시글 전체 목록을 가져옴(List로 반환)
- request.getSession().setAttribute("boardList", boardList);
- 가져온 게시글 목록을 세션에 저장 (JSP에서 꺼내 쓸 수 있게)
- "getBoardList" 반환
- 게시글 목록 화면(JSP)으로 이동
📦 GetBoardController.java
코드 원문
package com.springbook.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.BoardDAO;
public class getBoardController implements Controller {
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
String seq = request.getParameter("seq");
BoardVO vo = new BoardVO();
vo.setSeq(Integer.parseInt(seq));
BoardDAO boardDAO = new BoardDAO();
BoardVO board = boardDAO.getBoard(vo);
request.getSession().setAttribute("board", board);
return "getBoard";
}
}
상세 해설
- getBoardController: 게시글 하나를 상세보기로 보여주는 컨트롤러
- request.getParameter("seq");
- 글 번호(seq)를 폼/URL에서 받아옴
- BoardVO vo = new BoardVO();
- 번호를 VO에 저장 (DB 조회에 사용)
- BoardVO board = boardDAO.getBoard(vo);
- 해당 번호의 글 내용을 DB에서 조회
- request.getSession().setAttribute("board", board);
- 상세 정보(BoardVO)를 세션에 저장 (JSP에서 읽을 수 있게)
- "getBoard" 반환
- 게시글 상세 화면(JSP)으로 이동
📦 InsertBoardController.java
코드 원문
package com.springbook.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.BoardDAO;
public class InsertBoardController implements Controller {
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
String title = request.getParameter("title");
String writer = request.getParameter("writer");
String content = request.getParameter("content");
BoardVO vo = new BoardVO();
vo.setTitle(title);
vo.setWriter(writer);
vo.setContent(content);
BoardDAO boardDAO = new BoardDAO();
boardDAO.insertBoard(vo);
return "getBoardList.do";
}
}
상세 해설
- insertBoardController: 게시글 등록(새 글쓰기) 컨트롤러
- request.getParameter("title"), "writer", "content"
- 폼에서 사용자가 입력한 값 받아오기
- vo.setTitle(title), ...
- 입력값을 BoardVO에 저장
- boardDAO.insertBoard(vo);
- DB에 새 글 추가(INSERT)
- "getBoardList.do" 반환
- 등록 후, 최신 글 목록 페이지로 이동
📦 UpdateBoardController.java
코드 원문
package com.springbook.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.BoardDAO;
public class UpdateBoardController implements Controller {
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
String seq = request.getParameter("seq");
String title = request.getParameter("title");
String content = request.getParameter("content");
BoardVO vo = new BoardVO();
vo.setSeq(Integer.parseInt(seq));
vo.setTitle(title);
vo.setContent(content);
BoardDAO boardDAO = new BoardDAO();
boardDAO.updateBoard(vo);
return "getBoardList.do";
}
}
상세 해설
- updateBoardController: 게시글 수정 컨트롤러
- seq, title, content 값을 폼에서 받아옴
- vo.setSeq(Integer.parseInt(seq)), ...
- 수정할 글의 번호, 제목, 내용 VO에 저장
- boardDAO.updateBoard(vo);
- 해당 글을 DB에서 UPDATE
- "getBoardList.do" 반환
- 수정 후 글 목록으로 이동
📦 DeleteBoardController.java
코드 원문
package com.springbook.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.BoardDAO;
public class DeleteBoardController implements Controller {
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
String seq = request.getParameter("seq");
BoardVO vo = new BoardVO();
vo.setSeq(Integer.parseInt(seq));
BoardDAO boardDAO = new BoardDAO();
boardDAO.deleteBoard(vo);
return "getBoardList.do";
}
}
상세 해설
- deleteBoardController: 게시글 삭제 컨트롤러
- request.getParameter("seq");
- 삭제할 글 번호 받기
- vo.setSeq(Integer.parseInt(seq));
- VO에 번호 저장
- boardDAO.deleteBoard(vo);
- 해당 글을 DB에서 삭제
- "getBoardList.do" 반환
- 삭제 후 글 목록으로 이동
📄 login.jsp
코드 원문
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Login Form</title>
</head>
<body>
<center>
<h1>로그인 화면</h1>
<hr>
<!--본래 테이블을 만들어야 하지만 간단한 이해를 위해 관략화함.-->
<!--이 코드 이후로 따로 관략화 했다는 주석은 따로 달지 않을 예정-->
<form action="login.do" method="post">
아이디: <input type="text" name="id"><br>
비밀번호: <input type="password" name="pw"><br>
<input type="submit" value="로그인">
</form>
</center>
</body>
</html>
상세 해설
- JSP(Java Server Pages): HTML과 자바를 섞어 동적인 웹페이지를 만드는 기술
- 이 파일은 로그인 폼 화면을 보여줍니다.
주요 부분 설명
- <%@ page ... %>
- JSP 파일의 환경/인코딩 설정
- 한글 깨짐 방지용 charset=EUC-KR
- <form action="login.do" method="post">
- 로그인 폼 제출 시, login.do로 POST 요청(컨트롤러에서 처리)
- <input type="text" name="id">
- 아이디 입력란
- <input type="password" name="pw">
- 비밀번호 입력란
- <input type="submit" value="로그인">
- 로그인 버튼
- <center>, <h1>, <hr>
- 화면 정렬, 제목, 구분선(HTML)
📄 getBoardList.jsp
코드 원문
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<%@ page import="java.util.*, com.springbook.biz.board.BoardVO" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Board List</title>
</head>
<body>
<center>
<h3>
<!-- 로그인한 사용자 이름 출력은 실제로는 세션에서 꺼내는 코드 필요 -->
<a href="logout.do">로그아웃</a>
</h3>
<h1>게시글 목록</h1>
<a href="insertBoard.jsp">글쓰기</a>
<hr>
<table border="1">
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>등록일</th>
<th>조회수</th>
</tr>
<%
List<BoardVO> boardList = (List<BoardVO>) session.getAttribute("boardList");
if (boardList != null) {
for (BoardVO board : boardList) {
%>
<tr>
<td><%= board.getSeq() %></td>
<td>
<a href="getBoard.do?seq=<%= board.getSeq() %>"><%= board.getTitle() %></a>
</td>
<td><%= board.getWriter() %></td>
<td><%= board.getRegDate() %></td>
<td><%= board.getCnt() %></td>
</tr>
<% }
}
%>
</table>
</center>
</body>
</html>
상세 해설
- 게시글 목록을 표로 출력하는 화면
- session.getAttribute("boardList")
- 컨트롤러에서 세션에 저장한 게시글 목록(List<BoardVO>)을 꺼냄
- <% ... %>
- JSP의 자바 코드 영역(스크립틀릿)
- <a href="insertBoard.jsp">글쓰기</a>
- 글쓰기 버튼, 새 글 작성 화면으로 이동
- <a href="getBoard.do?seq=번호">제목</a>
- 제목 클릭 시 해당 글 상세보기로 이동
- <th>, <td>
- HTML 표의 제목/내용 셀
📄 insertBoard.jsp
코드 원문
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert Board</title>
</head>
<body>
<center>
<h1>게시글 등록</h1>
<hr>
<form action="insertBoard.do" method="post">
제목: <input type="text" name="title"><br>
작성자: <input type="text" name="writer"><br>
내용: <textarea name="content"></textarea><br>
<input type="submit" value="등록">
</form>
</center>
</body>
</html>
상세 해설
- 새 게시글을 작성할 때 입력하는 폼 화면
- <form action="insertBoard.do" method="post">
- 등록 버튼을 누르면 insertBoard.do로 POST 요청(컨트롤러가 처리)
- <input name="title">, <input name="writer">
- 글 제목/작성자 입력란
- <textarea name="content"></textarea>
- 글 내용 입력란
📄 getBoard.jsp
코드 원문
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<%@ page import="com.springbook.biz.board.BoardVO" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Board Detail</title>
</head>
<body>
<center>
<h1>게시글 상세 보기</h1>
<hr>
<%
BoardVO board = (BoardVO) session.getAttribute("board");
if (board != null) {
%>
<form action="updateBoard.do" method="post">
<input type="hidden" name="seq" value="<%= board.getSeq() %>">
제목: <input type="text" name="title" value="<%= board.getTitle() %>"><br>
작성자: <input type="text" name="writer" value="<%= board.getWriter() %>" readonly><br>
내용: <textarea name="content"><%= board.getContent() %></textarea><br>
<input type="submit" value="수정">
<a href="deleteBoard.do?seq=<%= board.getSeq() %>">삭제</a>
</form>
<% } %>
</center>
</body>
</html>
상세 해설
- 특정 글의 상세 내용 확인/수정 화면
- session.getAttribute("board")
- 컨트롤러에서 세션에 저장한 BoardVO 객체를 꺼냄
- <input type="hidden" name="seq"...>
- 수정/삭제할 때 글 번호가 함께 넘어가도록 숨김필드로 전달
- <input type="text" ... readonly>
- 작성자는 수정할 수 없게 읽기 전용 처리
- <input type="submit" value="수정">
- 수정 버튼: 폼을 updateBoard.do로 제출
- <a href="deleteBoard.do?seq=...">삭제</a>
- 삭제 버튼: 해당 글 번호로 삭제 컨트롤러 실행
📄 web.xml
코드 원문
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>com.springbook.controller.DispatchServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
상세 해설
- 웹 애플리케이션의 환경/라우팅 설정 파일
- <servlet>, <servlet-mapping>
- .do로 끝나는 모든 요청을 DispatchServlet이 받도록 지정
- 예: /login.do, /getBoardList.do, ...
- .do로 끝나는 모든 요청을 DispatchServlet이 받도록 지정
- <servlet-class>
- 실제 실행할 자바 클래스(프론트 컨트롤러) 지정
📄 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"
xsi:schemaLocation="
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">
<!-- 컴포넌트 스캔: @Repository, @Service 등을 자동 등록 -->
<context:component-scan base-package="com.springbook.biz" />
</beans>
상세 해설
- 스프링 빈 설정 파일
- <context:component-scan base-package="...">
- 지정한 패키지 안에서 @Repository, @Service, @Component 어노테이션이 붙은 클래스를 자동 등록
✅ 해당 코드의 동선을 파악해보자
1. [login.jsp] 화면에서 "로그인" 버튼 클릭
- 사용자가 아이디/비밀번호 입력
(예시: 아이디 hong, 비밀번호 1234)
<form action="login.do" method="post">
아이디: <input type="text" name="id">
비밀번호: <input type="password" name="pw">
<input type="submit" value="로그인">
</form>
- 여기서 일어나는 일
- 사용자가 입력한 아이디/비번이 브라우저 → 서버로 전송됨
- 전송 방식: POST 방식, 목적지는 login.do
2. [웹서버] web.xml의 라우팅 규칙
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
- login.do로 끝나는 모든 요청은
com.springbook.controller.DispatchServlet이 받도록 설정됨
3. [DispatchServlet]이 요청을 받음
protected void service(HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/")); // "/login.do"
Controller ctrl = handlerMapping.getController(path); // LoginController
String viewName = ctrl.handleRequest(request, response);
...
}
- 여기서 일어나는 일
- URL(여기서는 /login.do)에서 경로 추출 → "/login.do"
- HandlerMapping에서 "/login.do"에 해당하는 컨트롤러 객체 찾기 → LoginController
- LoginController의 handleRequest() 호출
4. [LoginController]에서 로그인 처리
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
String id = request.getParameter("id"); // "hong"
String pw = request.getParameter("pw"); // "1234"
UserVO vo = new UserVO(); // 사용자 정보 VO 생성
vo.setId(id);
vo.setPassword(pw);
UserDAO userDAO = new UserDAO(); // DB 처리용 DAO 생성
UserVO user = userDAO.getUser(vo); // DB에서 해당 id/pw가 맞는지 확인
if (user != null) {
return "getBoardList.do"; // 성공시 게시판 목록으로 이동
} else {
return "login"; // 실패시 다시 로그인 화면으로
}
}
→ 내부적으로 호출되는 UserDAO.getUser(vo)
public UserVO getUser(UserVO vo) {
...
String sql = "SELECT * FROM users WHERE id=? AND password=?";
// DB 연결, 쿼리 준비, 값 바인딩, 쿼리 실행
if(rs.next()) {
// 일치하는 사용자가 있으면 UserVO에 정보 저장
}
// 없으면 null 반환
}
- DB에서 아이디/비밀번호가 일치하는 사용자를 검색
- 일치하면 UserVO 객체를,
- 아니면 null을 반환
5. [로그인 성공] → "getBoardList.do"로 리다이렉트
- 로그인에 성공했다면
다시 DispatchServlet이 /getBoardList.do 요청을 받음
6. [DispatchServlet] → HandlerMapping → getBoardListController 실행
Controller ctrl = handlerMapping.getController("/getBoardList.do"); // getBoardListController
String viewName = ctrl.handleRequest(request, response);
- HandlerMapping이 getBoardListController를 연결
7. [getBoardListController] → 게시글 목록 가져오기
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
BoardDAO boardDAO = new BoardDAO();
List<BoardVO> boardList = boardDAO.getBoardList(); // DB에서 모든 글 읽기
request.getSession().setAttribute("boardList", boardList); // 세션에 저장
return "getBoardList"; // getBoardList.jsp로 이동
}
- BoardDAO가 DB에서 게시글 전체 목록을 읽어와
List<BoardVO>에 담고,
이걸 세션("boardList")에 저장
8. [ViewResolver]가 "getBoardList" → "/getBoardList.jsp"로 변환
public String getView(String viewName) {
return prefix + viewName + suffix;
// 예: "/" + "getBoardList" + ".jsp" → "/getBoardList.jsp"
}
- 실질적으로 getBoardList.jsp 화면으로 이동
9. [getBoardList.jsp]가 화면에 게시글 목록을 보여줌
<%
List<BoardVO> boardList = (List<BoardVO>) session.getAttribute("boardList");
if (boardList != null) {
for (BoardVO board : boardList) {
%>
<tr>
<td><%= board.getSeq() %></td>
<td>
<a href="getBoard.do?seq=<%= board.getSeq() %>"><%= board.getTitle() %></a>
</td>
<td><%= board.getWriter() %></td>
<td><%= board.getRegDate() %></td>
<td><%= board.getCnt() %></td>
</tr>
<% }
}
%>
- 세션에 저장된 boardList(여러 게시글 정보)를 꺼내
표 형태로 한 줄씩 출력 - 제목 클릭 시 getBoard.do?seq=글번호로 상세보기 이동 가능
10. [게시글 상세/글쓰기/수정/삭제 등]
위와 비슷하게, 각각의 .do 요청 → 컨트롤러 → DAO → DB → 결과를 JSP에 세션에 담아 넘기는 방식으로 동작
❗ 요약 설명
- 사용자가 버튼/폼을 전송
- → DispatchServlet(프론트 컨트롤러)이 받음
- → URL에 맞는 컨트롤러를 호출
- → 컨트롤러가 DAO/DB와 통신
- → 결과를 세션에 저장
- → ViewResolver가 JSP 경로로 변환
- → JSP가 화면에 출력
- (이 흐름이 "로그인", "글 목록", "글 상세", "글 등록/수정/삭제"까지 모두 동일하게 반복!)