Spring/개인 공부

게시판 해체 분석 100%!! 코드들의 설명과 동선 완벽하게 이해하기

코딩하는냥이 2025. 6. 26. 12:54
반응형

국비 수업 내용으로 진행한 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: 게시글 한 건의 정보를 담은 객체를 입력받음
  • 동작 순서:
    1. DB 연결 : JDBCUtil.getConnection() 호출로 DB 접속
    2. SQL 준비 : conn.prepareStatement(BOARD_INSERT)로 SQL을 준비
    3. 값 바인딩 : stmt.setString() 등으로 SQL의 ?에 값 넣기
      • vo.getTitle(), vo.getWriter(), vo.getContent()
    4. 실행 : stmt.executeUpdate()로 DB에 명령 실행 (INSERT)
    5. 자원 해제 : finally 블록에서 연결 닫기 (JDBCUtil.close)
  • 예외처리 : try-catch로 에러 발생 시 로그 출력

  2) updateBoard(BoardVO vo)

  글 수정 기능

  • 글 번호(seq)에 해당하는 게시글의 제목, 내용을 수정
  • 동작 순서:
    1. DB 연결
    2. SQL 준비 (UPDATE board SET title=?, content=? WHERE seq=?)
    3. ? 자리에 값 넣기 (setString, setInt)
    4. SQL 실행
    5. 자원 해제

  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)

  • 로그인 검증 메서드
  • 동작:
    1. JDBCUtil로 DB 연결
    2. SQL 준비 (id, password ?에 바인딩)
    3. SQL 실행, 결과(rs)
    4. 사용자가 존재하면 UserVO에 각 값 저장 (id, password, name, role)
    5. 없으면 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 대신)
  • 동작 순서
    1. 요청 URI에서 경로(path) 추출
      • 예: "/Boadweb/getBoardList.do" → "/getBoardList.do"
    2. HandlerMapping으로 해당 컨트롤러 객체 찾기
      • 예: "/login.do" → LoginController
      • 예: "/getBoardList.do" → getBoardListController
    3. 컨트롤러의 handleRequest 실행
      • 컨트롤러가 비즈니스 로직 수행 & 결과 화면(논리 이름) 반환
    4. 뷰 이동(리다이렉트)
      • viewName이 ".do"를 포함(다음 컨트롤러 호출이면):
        → sendRedirect(viewName) (브라우저가 새로 해당 URL로 요청)
      • 아니면(JSP로 바로 이동이면):
        → ViewResolver로 실제 JSP 경로 얻어서
        → sendRedirect(view) (JSP 파일로 이동)

📦 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, ...
  • <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);
    ...
}
  • 여기서 일어나는 일
    1. URL(여기서는 /login.do)에서 경로 추출 → "/login.do"
    2. HandlerMapping에서 "/login.do"에 해당하는 컨트롤러 객체 찾기 → LoginController
    3. 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가 화면에 출력
  • (이 흐름이 "로그인", "글 목록", "글 상세", "글 등록/수정/삭제"까지 모두 동일하게 반복!)