웹 애플리케이션에서 요청 처리와 화면 출력을 명확히 분리한 구조로, 서블릿이 컨트롤러 역할을 하여 클라이언트의 요청을 받아 비즈니스 로직을 처리한 뒤, 데이터를 JSP에 전달하고 JSP는 오직 결과 화면만 출력하는 방식
클라이언트 > 컨트롤러 서블릿 > DAO/DTO > 결과 저장 > JSP 포워딩
| 항목 | MVC1 | MVC2 |
|---|---|---|
| 컨트롤러 | 없음(jsp가 처리) | 있음(서블릿이 처리) |
| 뷰 | JSP가 로직 + 출력 | JSP는 오직 뷰 |
| 코드 재사용 | 낮음 | 높음 |
| 유지보수성 | 낮음 | 높음 |
| 사용 기술 | JSP 중심 | Servlet 중심 + JSP |
JNDI(Java Naming and Directory Interface)
: 이름으로 객체를 찾기 위한 표준.
DB연결, 자원(리소스), 환경 설정 등을 이름으로 등록해두고, 자바 코드에서 그 이름으로 가져다 쓰는 방식
C:\tomcat9\conf 이 경로 안에 context.xml 내용을 이렇게 바꾸었다.
<Context>
<!-- JNDI 이름 정의 -->
<Resource name="jdbc/mydb"
auth="Container"
type="javax.sql.DataSource"
maxTotal="20"
maxIdle="10"
maxWaitMillis="-1"
username="root"
password="1234"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/java?serverTimezone=Asia/Seoul"/>
</Context>
package com.koreait.mvc2.util;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class DBUtil {
private static DataSource ds;
static {
try {
Context ctx = new InitialContext();
Context env = (Context) ctx.lookup("java:comp/env");
ds = (DataSource) env.lookup("jdbc/mydb"); // context.xml의 name
} catch (NamingException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return ds.getConnection(); // 커넥션 풀에서 하나를 꺼냄
}
}
package com.koreait.mvc2.controller;
import com.koreait.mvc2.service.MemberService;
import com.koreait.mvc2.service.MemberServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("*.member")
public class MemberController extends HttpServlet {
private static final long serialVersionUID = 1L;
private MemberService service = new MemberServiceImpl();
public MemberController() {}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doAction(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doAction(req, resp);
}
protected void doAction(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String uri = req.getRequestURI();
String context = req.getContextPath();
String command = uri.substring(context.length());
switch (command) {
case "/join.member":
req.getRequestDispatcher("/WEB-INF/views/join.jsp").forward(req, resp);
break;
case "/joinForm.member":
service.join(req, resp);
break;
case "/login.member":
req.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(req, resp);
break;
case "/loginForm.member":
service.login(req, resp);
break;
case "/logout.member":
service.logout(req, resp);
break;
case "/mypage.member":
req.getRequestDispatcher("/WEB-INF/views/mypage.jsp").forward(req, resp);
break;
case "/modifyForm.member":
if(req.getMethod().equals("GET")){
req.getRequestDispatcher("/WEB-INF/views/edit.jsp").forward(req, resp);
}else if(req.getMethod().equalsIgnoreCase("POST")){
service.modify(req, resp);
}
break;
case "/delete.member":
service.delete(req, resp);
break;
default:
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
}
package com.koreait.mvc2.service;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface MemberService {
void join(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
void modify(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
}
package com.koreait.mvc2.service;
import com.koreait.mvc2.dao.MemberDAO;
import com.koreait.mvc2.dto.MemberDTO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Member;
public class MemberServiceImpl implements MemberService {
private MemberDAO dao = new MemberDAO();
@Override
public void join(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
MemberDTO dto = new MemberDTO();
dto.setUserid(req.getParameter("userid"));
dto.setUserpw(req.getParameter("userpw"));
dto.setName(req.getParameter("name"));
dto.setHp(req.getParameter("hp"));
dto.setEmail(req.getParameter("email"));
dto.setGender(req.getParameter("gender"));
dto.setSsn1(req.getParameter("ssn1"));
dto.setSsn2(req.getParameter("ssn2"));
dto.setZipcode(req.getParameter("zipcode"));
dto.setAddress1(req.getParameter("address1"));
dto.setAddress2(req.getParameter("address2"));
dto.setAddress3(req.getParameter("address3"));
boolean success = dao.insertMember(dto);
req.setAttribute("success", success);
req.getRequestDispatcher("/WEB-INF/views/result.jsp").forward(req, resp);
}
@Override
public void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userid = req.getParameter("userid");
String userpw = req.getParameter("userpw");
MemberDTO dto = dao.login(userid, userpw);
if(dto!=null){
req.getSession().setAttribute("user", dto);
req.setAttribute("loginUser", dto);
}
req.getRequestDispatcher("/WEB-INF/views/result.jsp").forward(req, resp);
}
@Override
public void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().invalidate();
resp.sendRedirect("login.member");
}
@Override
public void modify(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
MemberDTO sessionDto = (MemberDTO) req.getSession().getAttribute("user");
MemberDTO dto = new MemberDTO();
dto.setUserid(sessionDto.getUserid());
dto.setName(req.getParameter("name"));
dto.setHp(req.getParameter("hp"));
dto.setEmail(req.getParameter("email"));
dto.setGender(req.getParameter("gender"));
dto.setZipcode(req.getParameter("zipcode"));
dto.setAddress1(req.getParameter("address1"));
dto.setAddress2(req.getParameter("address2"));
dto.setAddress3(req.getParameter("address3"));
boolean isModify = dao.updateMember(dto);
if(isModify){
req.getSession().setAttribute("user", dto);
}
req.setAttribute("isModify", isModify);
req.getRequestDispatcher("/WEB-INF/views/mypage.jsp").forward(req, resp);
}
@Override
public void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
MemberDTO sessionDto = (MemberDTO) req.getSession().getAttribute("user");
if(sessionDto != null){
dao.deleteMember(sessionDto.getUserid());
req.getSession().invalidate();
}
resp.sendRedirect("login.member");
}
}
🔹 req.getSession().setAttribute("user", dto)
세션 영역에 저장 → 여러 페이지(요청)에서 쓸 수 있음.
로그인 유지가 필요할 때 사용.
ex: 게시판 글쓰기, 댓글 작성, 마이페이지 등 로그인 사용자 확인할 때 씀.
🔹 req.setAttribute("loginUser", dto)
요청(request) 영역에 저장 → 현재 페이지 요청이 끝나면 사라짐.
페이지에 바로 값을 전달하고 싶을 때 사용.
ex: 로그인 성공 후 main.jsp로 loginUser 값을 넘기고 싶을 때.
package com.koreait.mvc2.dao;
import com.koreait.mvc2.dto.MemberDTO;
import com.koreait.mvc2.util.DBUtil;
import com.mysql.cj.x.protobuf.MysqlxPrepare;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class MemberDAO {
public boolean insertMember(MemberDTO member) {
String sql = """
insert into member (userid, userpw, name, hp, email,
gender, ssn1, ssn2, zipcode, address1, address2, address3)
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""";
try(Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)){
pstmt.setString(1, member.getUserid());
pstmt.setString(2, member.getUserpw());
pstmt.setString(3, member.getName());
pstmt.setString(4, member.getHp());
pstmt.setString(5, member.getEmail());
pstmt.setString(6, member.getGender());
pstmt.setString(7, member.getSsn1());
pstmt.setString(8, member.getSsn2());
pstmt.setString(9, member.getZipcode());
pstmt.setString(10, member.getAddress1());
pstmt.setString(11, member.getAddress2());
pstmt.setString(12, member.getAddress3());
return pstmt.executeUpdate() == 1;
}catch(Exception e){
e.printStackTrace();
return false;
}
}
public MemberDTO login(String userid, String userpw){
String sql = "SELECT * FROM member WHERE userid = ? AND userpw = ?";
try(Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)){
pstmt.setString(1, userid);
pstmt.setString(2, userpw);
ResultSet rs = pstmt.executeQuery();
if(rs.next()){
MemberDTO member = new MemberDTO();
member.setIdx(rs.getInt("idx"));
member.setUserid(rs.getString("userid"));
member.setName(rs.getString("name"));
member.setHp(rs.getString("hp"));
member.setEmail(rs.getString("email"));
member.setGender(rs.getString("gender"));
member.setZipcode(rs.getString("zipcode"));
member.setAddress1(rs.getString("address1"));
member.setAddress2(rs.getString("address2"));
member.setAddress3(rs.getString("address3"));
member.setPoint(rs.getInt("point"));
return member;
}
} catch (Exception e){
e.printStackTrace();
return null;
}
return null;
}
public boolean updateMember(MemberDTO dto) {
String sql = """
update member set name=?, hp=?, email=?, gender=?,
zipcode=?, address1=?, address2=?, address3=?
where userid=?
""";
try(Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)){
pstmt.setString(1, dto.getName());
pstmt.setString(2, dto.getHp());
pstmt.setString(3, dto.getEmail());
pstmt.setString(4, dto.getGender());
pstmt.setString(5, dto.getZipcode());
pstmt.setString(6, dto.getAddress1());
pstmt.setString(7, dto.getAddress2());
pstmt.setString(8, dto.getAddress3());
pstmt.setString(9, dto.getUserid());
return pstmt.executeUpdate() == 1;
}catch(Exception e){
e.printStackTrace();
return false;
}
}
public boolean deleteMember(String userid) {
String sql = "delete from member where userid=?";
try(Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)){
pstmt.setString(1, userid);
return pstmt.executeUpdate() == 1;
}catch(Exception e){
e.printStackTrace();
return false;
}
}
}
package com.koreait.mvc2.dto;
public class MemberDTO {
private int idx;
private String userid;
private String userpw;
private String name;
private String hp;
private String email;
private String gender;
private String ssn1;
private String ssn2;
private String zipcode;
private String address1;
private String address2;
private String address3;
private String regdate;
private int point;
public MemberDTO() {
}
public MemberDTO(int idx, String userid, String userpw, String name, String hp, String email, String gender, String ssn1, String ssn2, String zipcode, String address1, String address2, String address3, String regdate, int point) {
this.idx = idx;
this.userid = userid;
this.userpw = userpw;
this.name = name;
this.hp = hp;
this.email = email;
this.gender = gender;
this.ssn1 = ssn1;
this.ssn2 = ssn2;
this.zipcode = zipcode;
this.address1 = address1;
this.address2 = address2;
this.address3 = address3;
this.regdate = regdate;
this.point = point;
}
public int getIdx() {
return idx;
}
public void setIdx(int idx) {
this.idx = idx;
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getUserpw() {
return userpw;
}
public void setUserpw(String userpw) {
this.userpw = userpw;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHp() {
return hp;
}
public void setHp(String hp) {
this.hp = hp;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getSsn1() {
return ssn1;
}
public void setSsn1(String ssn1) {
this.ssn1 = ssn1;
}
public String getSsn2() {
return ssn2;
}
public void setSsn2(String ssn2) {
this.ssn2 = ssn2;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public String getAddress1() {
return address1;
}
public void setAddress1(String address1) {
this.address1 = address1;
}
public String getAddress2() {
return address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
public String getAddress3() {
return address3;
}
public void setAddress3(String address3) {
this.address3 = address3;
}
public String getRegdate() {
return regdate;
}
public void setRegdate(String regdate) {
this.regdate = regdate;
}
public int getPoint() {
return point;
}
public void setPoint(int point) {
this.point = point;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>회원가입</title>
</head>
<body>
<h2>회원가입</h2>
<form method="post" action="joinForm.member">
<p>아이디: <input type="text" name="userid"></p>
<p>비밀번호: <input type="password" name="userpw"></p>
<p>이름: <input type="text" name="name"></p>
<p>휴대폰: <input type="text" name="hp"></p>
<p>이메일: <input type="email" name="email"></p>
<p>성별: <select name="gender">
<option value="남자">남자</option>
<option value="여자">여자</option>
</select></p>
<p>주민등록번호: <input type="text" name="ssn1"> - <input type="password" name="ssn2"></p>
<p>우편번호: <input type="text" name="zipcode"></p>
<p>주소: <input type="text" name="address1"></p>
<p>상세주소: <input type="text" name="address2"></p>
<p>참고항목: <input type="text" name="address3"></p>
<p><button type="submit">완료</button></p>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>로그인</title>
</head>
<body>
<h2>로그인</h2>
<form method="post" action="loginForm.member">
<input type="hidden" name="action" value="login">
<p>아이디: <input type="text" name="userid"></p>
<p>비밀번호: <input type="password" name="userpw"></p>
<p><button type="submit">로그인</button></p>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
com.koreait.mvc2.dto.MemberDTO user = (com.koreait.mvc2.dto.MemberDTO) session.getAttribute("user");
session.getAttribute("user");
%>
<html>
<head>
<title>마이페이지</title>
</head>
<body>
<h2>${user.name}님 마이페이지</h2>
<p>아이디: ${user.userid}</p>
<p>이메일: ${user.email}</p>
<form method="get" action="modifyForm.member">
<button type="submit">회원정보 수정</button>
</form>
<form method="post" action="delete.member" onsubmit="return confirm('정말 탈퇴하시겠습니까?')">
<button type="submit">회원 탈퇴</button>
</form>
<a href="logout.member">로그아웃</a>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
com.koreait.mvc2.dto.MemberDTO user = (com.koreait.mvc2.dto.MemberDTO) session.getAttribute("user");
%>
<html>
<head>
<title>회원정보 수정</title>
</head>
<body>
<h2>회원정보 수정</h2>
<form method="post" action="modifyForm.member">
<p>아이디: ${user.userid}</p>
<p>이름: <input type="text" name="name" value="${user.name}"></p>
<p>휴대폰: <input type="text" name="hp" value="${user.hp}"></p>
<p>이메일: <input type="email" name="email" value="${user.email}"></p>
<p>성별: <select name="gender">
<option ${user.gender == "남자" ? "selected" : ""} value="남자">남자</option>
<option ${user.gender == "여자" ? "selected" : ""} value="여자">여자</option>
</select></p>
<p>우편번호: <input type="text" name="zipcode" value="${user.zipcode}"></p>
<p>주소: <input type="text" name="address1" value="${user.address1}"></p>
<p>상세주소: <input type="text" name="address2" value="${user.address2}"></p>
<p>참고항목: <input type="text" name="address3" value="${user.address3}"></p>
<p><button type="submit">완료</button></p>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Result</title>
</head>
<body>
<c:choose>
<c:when test="${not empty success}">
<c:choose>
<c:when test="${success}">
<h2>회원가입이 성공적으로 처리되었습니다!</h2>
<p><a href="mypage.member">마이페이지</a></p>
</c:when>
<c:otherwise>
<h2>회원가입 중 문제가 발생했습니다.</h2>
</c:otherwise>
</c:choose>
</c:when>
<c:when test="${not empty loginUser}">
<h2>${loginUser.userid}(${loginUser.name})님, 로그인 성공!</h2>
<p><a href="mypage.member">마이페이지</a> | <a href="logout.member">로그아웃</a></p>
</c:when>
<c:otherwise>
<h2>로그인 실패</h2>
<p>아이디 또는 비밀번호를 확인하세요 🤢🤢🤢</p>
</c:otherwise>
</c:choose>
</body>
</html>
Filter
웹 애플리케이션에서 요청이나 응답을 가로채서 전처리 또는 후처리할 수 있는 기능을 제공하는 서블릿 컴포넌트
init() : 필터가 초기화될 때 1번만 호출됨(설정 읽기 등 가능)
doFIlter() : 핵심 메서드. 요청이 필터를 통과할 때마다 실행됨
destroy() : 필터가 제거될 때 실햄됨(자원 정리 등)
적용방식


web.xml에 이렇게 내용을 추가했다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<!--로그인 필터 등록-->
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.koreait.mvc2.filter.LoginFilter</filter-class>
</filter>
<!--로그인 필터 적용-->
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/mypage.member</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/modifyForm.member</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/delete.member</url-pattern>
</filter-mapping>
</web-app>
package com.koreait.mvc2.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession();
boolean loggedIn = session != null && session.getAttribute("user") != null;
if(loggedIn){
chain.doFilter(request, response); // 다음 필터나 서블릿으로 진행
}else{
resp.sendRedirect(req.getContextPath()+"/login.member");
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
로그인 되어 있지 않으면, 바로 로그인 페이지로 넘어가게끔 필터로 걸러주었다. 이로써 로그인도 안 된 상태에서 마이페이지, 회원수정, 회원탈퇴 페이지에 들어갈 수 없게 된다.

