Front Controller는 맵핑 주소를 관리하는 java 파일이 된다.


맵핑 주소로 접근 할 때 모든 접근을 Front Controller를 통하여 해당하는 컨트롤러에 이동시켜준다.


예전에 했던 필터와 유사




예를 들어 주소가 me로 끝나는 맵핑 주소는 항상 위의 컨트롤러를 지나게 된다.


다음 예를 보자.



package com.kh.mb.frontController;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("*.me")
public class MemberFrontController extends HttpServlet {
    private static final long serialVersionUID = 1L;
      
    public MemberFrontController() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
       
        String uri = request.getRequestURI();
        System.out.println(uri);
       
        String action = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf(".me"));
        System.out.println("action : " + action);
       
        RequestDispatcher rd = null;
        switch (action) {
        case "login":
            rd = request.getRequestDispatcher("login");
            break;
        case "logout":
            rd = request.getRequestDispatcher("logout");
            break;
        case "showInsertForm":
            rd = request.getRequestDispatcher("showInsertForm");
            break;
        case "minsert":
            rd = request.getRequestDispatcher("minsert");
            break;
        }
       
        rd.forward(request, response);
       
    }

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

}





필터 클래스에서 했던것처럼 문자 형식을 UTF-8로 처리해주고


맵핑 주소 문자열을 분리해서 Switch Case 문으로 해당하는 컨트롤러로 연결시킨다.






















프로젝트를 진행하게 되면 절대 혼자서 하지 않고 여러명에서 잡고 진행하게 된다.


하지만 지역마다 사투리가 있는것 처럼 사람마다 다양한 코딩 스타일이 존재하고


메소드 이름을 짓는 방식이 다르고 등등 여러가지 일치하지 않는 부분이 생기게 된다.


이를 해결하기 위해 핵심 기능 구현부분에 Interface를 적용한다.







Interface를 상속받는 클래스는 Interface에 존재하는 메소드 형식을 사용해야 하는 제약이 걸리기 때문에


틀(Frame)을 어느정도 일치 시킬 수 있다.


한번 확인해보자.



public interface MemberService {
    // 로그인용 메소드
    // public abstract가 전부 들어가기 때문에 애초에 생략해도 된다.(Interface)
    Member selectMember(Member m) throws LoginFailException;

    // 회원 가입용 메소드
    void insertMember(Member m) throws InsertFailException;
   
}


먼저 Member Service라는 인터페이스이고


throw가 붙은 두가지의 메소드가 존재한다.








다음은 MemberServiceImpl 클래스는 인터페이스 MemberService를 상속받는다.



public class MemberServiceImpl implements MemberService{
   
    // 자동으로 더 넓거나 같은 접근제한자가 잡힌다.
    // 이러한 인터페이스를 사용한 구조는 딱 정해져있는 메소드를 사용하므로(매개변수도 바꿀 수 없다.)
    // 행위를 강제한다고 한다.
    @Override
    public Member selectMember(Member m) throws LoginFailException {
        // SqlSession은 MyBatis에서 커넥션 대신 사용
        SqlSession session = getSqlSession();
       
        Member member = new MemberDao().selectMember(session, m);
       
        session.close();
       
        return member;
    }

    // 회원 가입용 메소드
    @Override
    public void insertMember(Member m) throws InsertFailException {
        SqlSession session = getSqlSession();
       
        new MemberDao().insertMember(session, m);
       
        session.commit();
        session.close();
    }
   
}



어노테이션을 통해 인터페이스에 해당하는 메소드를 생성하지 않으면 오류가 발생하고


리턴 값이나 throw 같은 형식 또한 완전 일치된다.


이를 통해 팀 프로젝트 설계 시 충돌나는 부분을 최소화 할 수 있다.








또한 이와 같은 구조를 사용하는 이점으로는 유지보수가 훨신 수월하게 된다.


사용할 때 다음과 같이 사용하게 되는데


MemberService ms = new MemberServiceImpl();



        try {
            Member member = ms.selectMember(m);
           

        } catch (LoginFailException e) {

        }




생성을 MemberService ms = new MemberServiceImpl(); 이와같이 Interface로 선언하고


일반 클래스로 생성하는 방식으로 사용하게 된다.


이러한 방식을 의존성을 낮춘다고 한다.(의존성 역전, IoC라고 한다.)


(나중에 DI, Dependency Injection, 의존성 주입이라는 개념이 Spring Framework에 나온다.)


의존성이란 클래스 선언과 생성과의 관계로 한쪽이 바뀔 경우 한쪽도 같이 바꿔야 하는데 이러한 경우를 의존성이 강하다고 한다.


프로젝트 구조에 관해서는 다음에 자세히 설명.










해당 커스텀 Exception 처리를 통해 에러페이지로 포워딩 하는 부분이다.




순서는 ( controller -> Service -> dao ) 에서 데이터 처리 과정에 오류가 생기면 throw하여 controller에서 에러 페이지로 포워딩 하게 된다.







이 중 먼저 LoginFailException 부분이다.


public class LoginFailException extends Exception {
    public LoginFailException(String msg) {
        super(msg);
    }
}


Exception을 상속받는 클래스로 생성자를 통해 오류 메세지를 Exception 생성자로 전달.









Controller 부분으로 페이지에서 받은 값을 Service로 전달하여 DB를 조회한다.

try catch문에서 커스텀 예외처리를 적용한다.


        MemberService ms = new MemberServiceImpl();
        try {
            Member member = ms.selectMember(m);
           
            HttpSession session = request.getSession();
           
            session.setAttribute("loginUser", member);
           
            response.sendRedirect("index.jsp");
        } catch (LoginFailException e) {
            RequestDispatcher error = request.getRequestDispatcher("WEB-INF/views/common/errorPage.jsp");
            request.setAttribute("message", e.getMessage());
           
            error.forward(request, response);
        }









Service 부분의 인터페이스 부분.

    // 로그인용 메소드
    // public abstract가 전부 들어가기 때문에 애초에 생략해도 된다.(Interface)
    Member selectMember(Member m) throws LoginFailException;


왜 인터페이스를 만들어 메소드를 관리하는지는 다음 페이지에서 설명(https://qdgbjsdnb.tistory.com/226)










이 인터페이스를 받는 Service java 파일

public class MemberServiceImpl implements MemberService{
   
    @Override
    public Member selectMember(Member m) throws LoginFailException {
        // SqlSession은 MyBatis에서 커넥션 대신 사용
        SqlSession session = getSqlSession();
       
        Member member = new MemberDao().selectMember(session, m);
       
        session.close();
       
        return member;
    }
}

throws LoginFailException을 통해서 처리해준다.







Dao에서 DB를 조회하는 부분.

XML 파일을 통해 Mybatis 기능을 이용하는 부분

    public Member selectMember(SqlSession session, Member m) throws LoginFailException {
        Member member = null;
       
        member = session.selectOne("Member.loginMember", m);
       
        if (member == null) {
            session.close();
            throw new LoginFailException("로그인 실패");
           
        }
       
        return member;
    }


member = session.selectOne("Member.loginMember", m); - DB를 조회하는 Mybatis 기능

throw new LoginFailException("로그인 실패"); 을 통해서 조회한 DB가 없을 경우 커스텀 예외처리 생성자를 호출한다.

throws LoginFailException을 통해서 Exception으로 전달










에러가 발생한다면 계속 Throw 해서 처음 메소드를 호출했던 Mybatis 까지 와서 try catch 문에서

catch문이 실행되게 한다.

catch (LoginFailException e) {
            RequestDispatcher error = request.getRequestDispatcher("WEB-INF/views/common/errorPage.jsp");
            request.setAttribute("message", e.getMessage());
           
            error.forward(request, response);
        }




+ Recent posts