pf) 개인적으로 공부한 내용을 정리했습니다. 여러 블로그도 참고하였지만 주로 참고한 것은 "코드로 배우는 스프링 웹 프로젝트"(남가람북스) 책을 공부한 후 참고해 작성하였음을 미리 말씀드립니다.

[    1. 인터셉터란?    ]


특정 URI로 요청시 Controller로 가는 요청을 가로채는 역할을 한다.


[    2. Interceptor와 JSP Filter의 차이??    ]


- 공통점 : 둘 다 Controller로 들어가는 요청을 가로채 특정 작업을 하기 위한 용도로 사용된다.

- 차이점 : 케어할 수 있는 영역(범위)가 다르다. Filter는 같은 웹 어플리케이션 내에서만 접근이 가능하며,

  Interceptor의 경우 스프링에서 관리되기 때문에 스프링내의 모든 객체에 접근이 가능하다.

-> JSP Filter의 경우 주로 한글처리에 이용되고

-> Interceptor의 경우 "로그인 처리"에 이용이 된다.

- why 로그인 처리에 이용?? )

: 만약 인터셉터를 이용하지 않고, 로그인 처리를 한다면, 게시물을 작성("/board/register"), 게시물 수정("/board/modify"),

 게시물 삭제("/board/delete") 등 모든 요청마다 Controller에서 session을 통해 로그인 정보가 남아 있는지를 확인하는 코드

 를 중복해서 입력해야 할 것이다. 

 하지만!, 인터셉터를 이용하면, A, B, C 작업(A,B,C 경로로 요청)을 할 경우에는 ~~Interceptor를 먼저 수행해 session에서 

 로그인 정보가 있는지 확인해 주는 역할을 한다면, 중복 코드가 확 줄어들 수 있을 것이다. 이러한 장점 때문에 사용!


[    3. 인터셉터를 지원하는 인터페이스와 클래스, 메서드    ]


: Spring에서 인터셉터를 지원하기 위해서 

- HandlerInterceptor 인터페이스

- HandlerInterceptorAdapter 추상 클래스를 지원한다. => 요녀석은 위의 인터페이스를 사용하기 쉽게 구현해 놓은 추상클래스.


=> 이때, HandlerInterceptorAdaptor는 3가지 메서드를 제공한다. 이 3가지 메서드를 오버라이딩해서 우리가 이용할 수 있다.

1) public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

: Controller로 요청이 들어가기 전!!에 수행된다.

 : request, response, handler 등의 매개변수를 이용가능한데 우리가 아는 HttpServletRequest, HttpServletResponse,

  이고, 나머지 하나는 이 preHandle() 메서드를 수행하고 수행될 컨트롤러 메서드에 대한 정보를 담고 있는 handle

  이다.

2) postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,

ModelAndView modelAndView)

: 컨트롤러의 메서드의 처리가 끝나 return 되고 화면을 띄워주는 처리가 되기 직전에 이 메서드가 수행된다.

: ModelAndView 객체에 컨트롤러에서 전달해 온 Model 객체가 전달됨으로 컨트롤러에서 작업 후 

: postHandle() 에서 작업할 것이 있다면 ModelAndView를 이용하면 된다.

3) afterCompletion()

: 컨트롤러가 수행되고 화면처리까지 끝난 뒤 호출된다.



[    4. 인터셉터를 이용한 로그인 구현해 보기    ]



1) 로그인 처리를 위한 DB table을 만들자

: 여기서는 mysql을 이용해서 아래와 같이 테이블을 생성하였다.

: 이와 같이 id, pw, name, point 를 갖는 테이블을 만들고, 4개 정도 회원 정보를 insert 해 놓았다.


2) 로그인에 사용될 UserVO 클래스를 정의하자.

: DAO(Data Access Object)와 Controller의 ModelAttribute 부분에 사용하기 위해 UserVO를 id와 pwd, name, point 정보 등을

 넣어 아래와 같이 만들어 보자.


3) 로그인처리를 할 MyBatis userMapper.xml 을 작성하자.

     : 살펴보면, namespace명은 고유한 값을 지정하고 select 문에 호출시 전달받은 userId와 userPw를 통해

 DB에서 select한 뒤 해당 튜플 정보를 resultType = UserVO 로 반환한다.

 이때, #{userId} 부분은 전달 받은 객체에서 getter메서드를 통해 호출됨으로 UserVO의 getUserId() 를 호출해 자동 대입이 될

 것임을 알 수 있다.


4) UserDAO 인터페이스와 이를 구현한 UserDAOImpl 클래스를 작성하자.

먼저) UserDAO

UserDAOImpl 

UserDAOImpl은 UserDAO인터페이스를 구현하였고 DAO(Data Access Object)로 DB에 직접 접근하는 처리를 담당하는 

클래스이다. 이 클래스에서는 스프링 환경설정에서 만들어 놓은 mybatis 접근 객체 : 즉, SqlSessionTemplate 객체를 

이용해야함으로 sqlSession.selectOne("MyBatis Mapper의 namespace명.지정한id",넘겨줄 데이터 객체); 를 이용해서 

MyBatis를 통해 작성한 쿼리를 실행해 UserVO 객체를 얻어오는 작업을 해준다. 이때, @Inject나 @Autowired를 통해서

자동 주입을 한다. @Inject와 @Autowired는 ByType(타입이 같은 것을 자동 대입)하는 성질이며, 환경설정 파일에서 우리가

sqlSessionTemplate 빈을 생성해 놓았음으로 자동 대입이 가능하다.(이후, 이런 설명은 생략한다.)

마찬가지로 서비스 객체에서 DAO를 자동주입할 것임으로 UserDAOImpl 클래스 위에 @Repository 어노테이션을 붙였다.

이는, 이 클래스는 DAO 클래스에요! 라는 의미 + 자동으로 new해서 이 객체를 생성해 주세요라는 의미를 가지고 있다.

이후, Service 객체에서도 @Inject를 이용해 자동 주입하기 위해 생성해 놓는다. 단, @Component, @Repoisitory, 

@Service(서비스 자동생성) 등의 어노테이션을 인식하기 위해서는 환경설정 파일에서 <context:component-scan 부분을

잘 지정해야한다. 여기서 지정한 패키지 내에서 해당 어노테이션이 탐색되기 때문이다.


여하튼...

하나의 UserVO 객체가 반환되야 함으로 selectOne() 메서드를 사용한 것이고, 여러 객체를 담고 있는 List로 반환을 받을

경우 selectList() 메서드를 호출한다. 이런 내용은 기본 적인 내용이니까... 이해가 안되면 MyBatis 연동 부분을 살펴보길 바란다.



5) UserService 인터페이스와 UserServiceImpl 클래스를 정의한다.

별거 없다... 단순한 서비스 클래스이다...

 dao와 마찬가지로 @Service 어노테이션을 사용해 이 클래스는 서비스 클래스로 사용될 녀석이에요 !! + 자동 객체 생성을 해준다.

 컨트롤러에서 사용되어야 하니까... 역시 이 서비스 클래스가 있는 패키지 경로도 <context:component-scan 이 잘 지정되어 있어야하

 는 것은 당연하다...


자... 여기까지가 기본 세팅이다... 수고했다... 이제 본격적으로 로그인과 관련된 기능을 만들어 보자.

---------------------------------------------------------------------------------------------------------------------------------

6) UserController에서 로그인 처리를 작성해 보자.

: 먼저 로그인 요청시 로그인 폼 화면을 띄워주는 부분을 작성해야 한다.

  loginForm() 부분이 바로 그 부분! RequestMapping된 메서드의 return한 부분은 환경설정 파일의 ViewResolver에 맵핑

  됨으로 loginForm.jsp 파일을 view아래에 적당한 위치에 만든다음에 return을 잘해서 http://localhost:8080/login으로 요청시

  해당 웹페이지가 뜨도록 설정한다. 주소에 url 요청시 GET 방식임으로 GET으로 설정한다.


 그 다음은, 로그인을 처리하는 부분이다. 로그인을 처리할 때 해야할 작업은 2가지이다.

1) 새로 로그인 요청이 온다면, 기존에 세션에 저장되어 있던 이전 로그인 사용자의 정보를 제거해 주어야 한다.

2) 새로 로그인 하는 사용자의 정보가 일치한다면, 해당 사용자 정보를 DB에서 가져와 세션에 저장해 놓아야 한다.

 

service.login()을 수행->dao.login()->UserMapper.xml 순으로 수행되며 id와 pw가 일치하면 사용자 정보를 담은 UserVO를 반환

할 것이고, 틀렸다면 null이 반환될 것이다. 

null 이 아닐 경우, 세션을 통해 사용자 객체를 저장해 놓는다.


이때, 마지막으로 작업해 주어야 하는 부분이 어디로 이동하느냐에 대한 정보이다.

로그인에 실패할 경우 return "redirect:/login"; 을 통해 다시 로그인 폼으로 이동시켜 주어야 할 것이고,

로그인에 성공해 session에 사용자 객체를 저장한 이후에는 return "redirect:/board/listPage" 와 같이 최초 페이지로 이동시켜주어

야 할 것이다.


로그아웃 부분은 그냥 로그아웃 url 요청이 있으면 해당 브라우저의 세션을 통째로 날려버리면 된다.

물론, session.removeAttribute('변수명") 해서 하나를 날려도 되지만, 만약 날려야하는 정보가 여러개라면 일일이 하기 힘들 수 있

기 때문에 invalidate() 를 많이 사용한다.


===> 여기까지 작업을 완료했다면 로그인, 로그아웃 기능은 다 만든 것이다. 하지만, 가장 중요하게 처리해 주어야 할 부분이

남아 있다.!!


** 게시물을 등록, 수정, 삭제 등을 할 때는 반드시 로그인 된 사용자만 할 수 있도록 "인터셉터"를 적용하는 것 말이다.!! ***


7) AuthenticationInterceptor 를 만들어 게시물 등록, 수정, 삭제 요청 전에 로그인 여부를 확인하는 인터셉터 클래스를 작성하자.

먼저 Interceptor를 구현하기 위해서는 가장 쉽게 사용할 수 있는 것이 HandlerInterceptor 인터페이스를 구현해 놓은 HandlerInterceptorAdapter이다.


이를 extends해서 preHandle()과 postHandle() 을 오버라이딩한다. shift + alt + s + v 를 하면... 알죠...?

보통, 세션에 로그인 사용자 정보를 컨트롤러에서 저장하지 않고 여기서 저장할 것이었으면 postHandle() 부분에서 수행해 주어야 겠

지만, 우리는 간편하게.....(그냥 귀찮았어...ㅠㅠ) 컨트롤러에서 다 해버렸음으로 인터셉터 클래스에서는 preHandle() 메서드만 필요하다.


그럼, preHandle() 에서는 request에서 세션 객체를 가지고 오고 세션에서 login변수에 사용자 정보 객체가 담겨 있나 확인을 한다.

없으면?? response.sendRedirect("uri") 를 통해 uri경로로 날려버리면 된다. 사용자 정보가 세션에 없다는건 지금 로그인이 되어 있지

않다는 것을 의미함으로 response.sendRedirect("/login") 을 통해서 다시 로그인하는 폼으로 이동시켜버리면 된다.

단, 이때 이 메서드를 수행 후 return false를 한 이유는, 이 preHandle() 메서드의 return 이 의미하는 바는 true일 경우 preHandle() 

메서드를 수행한 뒤에 본래 요청한 Controller 를 수행한다는 의미이고 false를 주면 수행하지 않는다는 의미를 담고 있다.

따라서, 로그인 안된 상태에서 요청시 해당 컨트롤러로 요청이 가지 않도록 false를 해주자.


반대로 사용자 정보가 세션에 담겨 있는 경우에는 단순히 return true를 해줘서 본래 사용자가 요청했던 Controller의 RequestMapping부분이 수행될 수 있도록 해주면 된다.


8) 가장 중요한... servlet-context.xml에 인터셉터 설정 정보를 등록해야한다..


: 일단, 인터셉터 자체는 웹 관련 설정임으로 root-context.xml이 아닌 servlet-context.xml에 작성하자. 물론, 요소마다 설정파일을

  다 나누어서 설정하셨다면... 본인에 맞는 환경설정 파일에서 작업을 해주세요...


이때, 인터셉터 빈 객체를 생성해 놓고, 인터셉터 정보를 등록할 때 해당 객체와 어떤 url이 요청시에 인터셉터가 작동할지를

설정해 놓아야 한다.


여기서는 필요한 uri를 하나하나 지정했는대 이렇게 하지 않고, 전체 경로 /** 로 잡아 놓고

<exclue-mapping path="예외url" /> 을 지정해서 모든 페이지에 인터셉터를 적용하지만 예외는 ~다 라고 지정할 수도 있다.



9) 죄송합니다... 기존거에서 빠트린 부분이 있어 이부분 추가합니다. -> "자기 글만 수정, 삭제가 가능하도록"


여러분... 제가 이 부분을 빠트렸었내요 ㅠㅠ... 지금은 로그인만하면, 모든 게시글들을 다 삭제하고 수정할 수 있죠...

그러면 큰일나자나요... ㅠㅠ

자기가 작성한 글만 수정, 삭제를 할 수 있도록 구성해야합니다. 어떻게하면 좋을까요... 여러 방법이 있겠지만 전 2가지

정도 생각이 나내요... 인터셉터의 postHandler() 메서드를 통해서 요청 uri를 분석한다음 수정일 경우 postHandler()에서 전달

받은 ModelAndView로 사용자 정보를 전달받아서 하는 방법이 있겠고... 근대 이건 좀 귀찮고...


사실... 훨씬 간단한 방법이 있어요... 뷰 페이지에서 처리하면 말이죠...


머냐....


일단, 수정버튼과 삭제 버튼은 해당 글을 클릭했을 때만 뜬답니다. 그렇죠? 그러면 글을 읽는 /views/board/read.jsp 페이지에서

<c:if test="${login.userId eq boardVO.writer}">


</c:if>

인 경우에만 수정하고 삭제 버튼이 보이도록하면 어떨까요? 세션에 login 변수로 userVO 객체를 담고 있으니까

현재 로그인한 사용자 userId와 현재 게시글의 boardVO 에서 writer를 비교해서 eq(같은경우)만 수정과 삭제 버튼이 보이고

목록으로가는 버튼은 항상 보이도록 하는거죠.

그러면 별다른 처리없이 안보이면 사용자는 수정과 삭제버튼은 클릭조차 할 수 없기에 모든 처리가 끝나게 되죠..

아래처럼 /views/board/read.jsp 페이지를 수정합시다.

아직 댓글에 대한 처리를 안했지만, 댓글도 마찬가지로 이와 같은 방식으로 해주면 되겠죠? 


10) 결과를 확인해 보자!!!

메인페이지)



로그인이 되어 있지 않은 상태에서 글등록을 누르면, 로그인 폼 페이지로 이동된다.


로그인에 성공하면, 게시글 첫 페이지로 이동된다.



이제 로그인이 된 상태임으로 "글등록"을 누르면 정상적으로 글 등록 폼으로 이동된다.


그리고 마지막으로, 로그인은 한 상태지만 다른 사람의 글을 클릭했을 때 수정과 삭제가 보이지 않고 목록으로 버튼만 보이는 화면입니다.



이상입니다.


열심히 끝까지 읽어주셔서 감사합니다.



+ Recent posts