최근 AngularJS에 대한 공부와, Yeoman에 대한 공부를 마치고, Yeoman을 사용한 프론트 세팅과 실행, 그리고 갑작스럽지만 내게는 딱 맞다 생각하는 Spring-Boot의 약간의 공부를 진행했다. 뭐 스프링 부트는 공부할 것도 없었지만..
Gradle이란 놈이 참으로 훌륭하더라. 여기에 Front/Backend의 프로젝트와 의존성을 물려놓고, 서로 커멘드 등의 명령어를 통해 실행하니 정말 잘 모듈별로 독립적이면서도 잘 맞물린다.
이제는 기존 Spring MVC와 Velocity, MyBatis로 구성된 소스를 SpringBoot – Hibernate(JPA)그리고 AngularJS를 통해 구성하기. 가장 기본적인 것은 로그인인데, 이 참에 Spring-Security를 적용해 보기로 하고, 가장 적절한 강좌를 찾았다.(아래 링크)
https://spring.io/guides/tutorials/spring-security-and-angular-js/
그런데 문제는, 이 강좌의 경우는 리소스를 WEB-INF에 넣고 AngularJS를 Spring Boot와 함께 돌리는데, 내 경우는 Spring-Boot는 Embedded Tomcat으로 8080포트를, AngularJS는 Yeoman으로 생성했기 때문에 node.js를 통해 9000포트가 되버렸다. 물론 나도 함께 구동하고 싶은 욕심은 있지만, 이걸 함께 구동하면 모듈화의 의미가 없어지기 때문에..
결국, 그렇게 해서 저 튜토리얼을 따라가다 보니 Cross-Domain문제에 직면했다. 인터넷을 찾아보니 아래 처럼 Filter를 implement해서 access-control을 정의하란다.
@Component public class FiammCORSFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with"); chain.doFilter(req, res); } public void init(FilterConfig filterConfig) {} public void destroy() {} }
그런데 이 필터가 전혀! 먹히지 않는다. 크롬 디버거로 HTTP헤더를 봐도, 적용이 안되었다..
그래서 혹시나 해서, 위 강좌에 나온 CsrfHeaderFilter 안에다가 저 내용을 넣어봤더니, 그제서야 먹는다..
public class CsrfHeaderFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class .getName()); if (csrf != null) { Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN"); String token = csrf.getToken(); if (cookie==null || token!=null && !token.equals(cookie.getValue())) { cookie = new Cookie("XSRF-TOKEN", token); cookie.setPath("/"); response.addCookie(cookie); } } response.setHeader("Access-Control-Allow-Origin", "http://localhost:9000"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with"); filterChain.doFilter(request, response); } }
저렇게, Access-Control-Allow-Origin에 나의 경우는 http://localhost:9000을 넣어주고,
angular.module('frontendApp') .controller('LoginCtrl', function($rootScope, $scope, $http, $location) { // Authentication var authenticate = function(credentials, callback) { var headers = credentials ? { authorization:"Basic " + btoa(credentials.userId + ":" + credentials.passwd) } : {}; $http.get($rootScope.serverUrl+'/doLogin', {headers:headers}).success(function(data) { if(data.userName) { $rootScope.authenticated = true; }else{ $rootScope.authenticated = false; } }).error(function(data) { $rootScope.authenticated = false; callback && callback(); }); } authenticate(); $scope.credentials = {}; $scope.login = function() { authenticate($scope.credentials, function() { if($rootScope.authenticated){ $location.path("/"); $scope.error = false; }else{ $location.path("/login"); $scope.error = true; } }); } });
위에서처럼 $rootScope.serverUrl+'/doLogin' 를 통해 통신하게 했다. $rootScope.serverUrl은 App.js에
angular .module('frontendApp', [ 'ngAnimate', 'ngCookies', 'ngResource', 'ngRoute', 'ngSanitize', 'ngTouch', 'ui.sortable' ]) .run(['$rootScope','$http', function($rootScope, $http){ // Definition to Global Variables and Functions $rootScope.serverUrl = "http://localhost:8080" }); }]);
이런식으로 마치 전역변수처럼 서버 URL을 생성해 두었다. 물론, 나중에 배포 문제 때문에 직접 URL을 쓰지 않고 $location 을 사용하여 바꿀 예정이다.