Category: Spring framework

Spring Framework 에 대한 기사들의 모음.

Spring boot 에 systemd 유닛 만들기

요즘 프로젝트를 하고 있는데, 역시나 자바 시스템이 있다. Spring Boot3 을 사용하고 있고 자바 17을 쓰는등 나름대로 괜찮은 환경에서 개발이 이루어지고 있다. 그런데, 이것을 서버에서 배포를 하고 Spring Boot 를 실행해야 하는데, 어떻게 하나 봤더니 초보자 수준도 못 벗어나는 설정을 하고 있으니… 안타까운 마음에 어떻게 하는 것이 좋은 것인지 한번 적어봤다.

Spring boot, jar 실행 파일

Spring Boot3 를 컴파일 하면 jar 파일 나온다. 그리고 별다른 서버 없이도 바로 실행하고 접속이 가능해 진다. 한가지 재미있는 사실은 많은 사람들이 Spring boot3 에 실행 파일 jar 이 내장된 WAS 서버가 구동되면서 실행된다는 걸 모른다는 거다. 심지여 그것이 Tomcat 이라는 것도. 물론 어떤 프로그래밍 모델인지에 따라서 Tomcat 이 되기도 하고 Netty 되기도 하지만, 과연 Reactive Programming Model 로 짜는 사람이 몇이나 있나 싶다. 대부분 Spring MVC 이고 Servlet 이면 기본적으로 Tomcat 이 구동된다. 내장형 Tomcat

프로젝트에서 쓰는 방법

현재 프로젝트에서는 jar 실행을 위해서 스크립트 파일을 만들었다. 대략 다음과 같다.

자바 옵션인 -D 옵션과 jar 실행 파일을 인자로 주고 있다. 그런데, 특이하게도 데몬화를 하지 않았고 많은 초보자식 설정인 nohup 도 없다. 이렇게되면 프로그램이 포그라운드(Foreground) 실행이되어서 쉘이 묶이게 된다.

어떻게 했지? 봤더니 systemd 에 이 실행파일을 넣고 systemd 를 통해서 시작/중지를 하고 있었다. 다음과 같다.

‘ExecStart=’ 에 쉘 스크립트 실행 파일을 지정해 주고 있다.

왜 이렇게 해야 하나? 왜? 이해 할 수 없는 구조다.

Spring 공식 문서

Systemd 유닛에 등록하는 방법은 Spring 공식문서에도 나와 있다.

57.1.2 Installation as a systemd service

이 문서에 나온 systemd 유닛 파일은 프로젝트 유닛 파일과 다르다. 공식 문서에 내용은 그냥 아주 기초적인 내용만 적혀 있다. 실제 프로젝트에 적용하기에는 무리가 있다.

설정파일 작성

Spring boot 를 위한 설정을 systemd 유닛에서 사용할 수 있는 별도의 파일로 작성한다. 이 설정은 JAVA_OPTS 의 내용을 담으면 된다.

이것을 이제 systemd 에서 사용하면 다음과 같다.

공식문서에 나와 있는 내용과 EnvironmentFile 속성으로 외부에서 JAVA_OPTS 를 지정한 내용을 읽어오도록 하면 충분히 사용가능해 진다.

프로젝트에서 처럼 스크립트로 한번 감싸고 이것을 다시 systemd 유닛에서 실행되도록 할 필요가 없다는 것이다.

몇가지 개선 설정

공식문서는 간단한 예이다. 실행이 가능한 정도의 유닛 파일일 뿐이다. systemd 를 이용해 자바 애플리케이션을 실행할 때에는 여러가지를 고려해야 한다.

일단 자바 애플리케이션은 네트워크를 기반으로 한다. 따라서 네트워크 온라인 상태여야 한다. 또, StdOut, StdErr 를 Journal 로 내보내도록 해야 한다.

Spring5 Security 기초 템플릿 – Session

이 글은 Spring5 에 Spring-Security 에 대한 기초 템플릿이다. Session 유지를 위해서 Redis 를 필요로 한다. 또, ElasticSearch 의 RestHighLevelClient 로 ElasticSearch를 연결 한다. 데이터베이스는 JNDI 설정으로 연결 된다.

다음과 같은 내용을 담았다.

  1. JDK 11
  2. Tomcat 서버 9 를 이용.
  3. spring-session.xml 을 작성해 Redis 를 세션으로 사용하도록 했다.
  4. JNDI 를 이용해 MySQL 에 연결된다.

Redis 설정한 이유는 Spring Security 에 인증을 InMemory 를 요구 한다. 이는 다음과 같은 설정 때문이다.

인증을 위한 패스워드가 {bcrypt} 로 되어 있다. 이 설정은 InMemory 세션을 요구한다. 이를 위해서 spring-session.xml 을 작성해 web.xml 에서 다음과 같이 인식을 시켜 준다.

Spring5 Security 기초 템플릿

이 문서는 Spring5 Security 기초 템플릿에 대한 것이다. Spring5 에 Spring-Security 를 이용해 기초적인 로그인을 구현 했다. Session 은 InMemory 가 아닌 WAS 서버에 세션을 저장한다. 로그인 정보는 spring-security.xml 에 정의된 것을 그대로 사용했다. 그야말로 기초적인 내용만 담았다.

이 템플릿이 동작하기 위한 조건은 다음과 같다.

  1. Java 1.8 기반.
  2. 데이터베이스가 연결되어 있어야 한다. 데이터베이스는 MySQL 이다.
  3. WAS 서버에 JNDI 를 연결된다.
  4. users 테이블이 필요한데, user.sql 파일에 내용이 있다.
  5. spring security 의 Role 기반 권한 접근 제어.
  6. context root 는 malluser 이다.

작동방식

Login 화면

권한을 체크하기 때문에 로그인 화면이 먼저 나온다. 권한 체크는 다음과 같이 Controller 에서 이루어진다.

ROLE_USER 권한을 가지고 있다면 초기화면에 접근이 가능하며 그렇지 않다면 로그인 화면으로 돌아가게 된다.

인증을 위한 정보는 다음과 같이 구현 됐다.

{noop} 은 암화화 되지 않는 text 기반의 암호을 말하는 것이며 위 설정은 외부 정보 저장소를 이용하지 않는 것이다.

로그인을 하면 다음과 같은 화면이 나온다.

로그인 화면

LOGOUT 버튼은 다음과 JSP 에서 Security tags 를 이용해 구현 했다.

Eclipse 에서 제작되었다.

Mybatis에 DAO 와 Mapper

SpringFramework 을 이용하다보면 데이터베이스 액세스를 위해서 MyBatis 를 사용하곤 한다. SQL 매퍼라고 불리기도 하는 것인데, 이를 이용하면 손쉽게 자바 코드와 SQL 문을 분리해줄 수 있을뿐만 아니라 MyBatis 에서 제공하는 여러가지 추가적인 기능을 이용해 데이터베이스를 좀 더 유연하게 사용할 수 있다.

DAO

보통 MyBatis 를 이용할대는 DAO 를 구조를 사용하곤 했다. Data Access Object 라고 불리는 것으로 말 그대로 데이터 접근을 위한 객체로서 sqlSession 객체를 이용해 데이터베이스 조회만 전담하는 객체다.

이 구조는 인터페이스와 그것을 구현한 구현체 클래스가 있어야 한다. 이를 위해서 Spring의 컨텍스트 설정을 해줘야 하는데 대략 다음과 같다.

sqlSession 을 빈으로 등록해준다. 그리고 다음과 같이 DAO 를 작성해 준다.

UserDAOImpl 은 UserDAO 인터페이스의 구현체다. SqlSession 의 빈을 가지고 오기 위해서 와이어를 걸어서 가지고 왔고, SQL 매퍼를 찾기위한 네임스페이스와 파라메터 인자를 주고 selectOne 메소드를 호출하고 있다.

이제 이것을 Service 계층에서, 역시나, 와이어를 걸어서 가지와서 데이터베이스에 데이터를 값을 호출하고 있다.

많은 프로젝트에서 이와 유사한 구조를 자주 보게 된다.

Mapper

그런데, 신기하게도 Mapper 을 이용해서 DAO 를 사용할 수도 있다. 이 기능은 MyBatis 3.0 이상부터 지원하기 시작한 것으로 Mapper 인터페이스만 구현하고 Service 계층에서 바로 와이어를 걸어서 사용할 수 있도록 했다.

하지만 DAO 구조에서도 얼마든지 Mapper 를 사용할 수 있다. sqlSession 에 메소드의 파라메터를 Mapper 클래스로 넘기면 된다. 먼저 Mapper 인터페이스를 다음과 같이 만든다.

그리고 앞에 DAO 구현체에 메소드를 다음과 같이 바꾼다.

sqlSession 객체에는 getMapper 는 메소드가 존재하고 이는 MyBatis 가 지원하는 Mapper 인터페이스를 받게 되어 있다.

문제는 MyBatis 의 namespace 인데, Mapper 인터페이스의 경로를 적어주면 되며 id 가 Mapper 인터페이스의 메소드 이름과 매핑된다.

이렇게 함으로써 Mapper 를 이용하면서도 DAO 구조를 그대로 유지할 수 있다. 문제는 DAO 에서 Mapper 인터페이스 객체를 매번 생성해 줘야 한다는 것이다. sqlSession.getMapper 를 이용해서 매번 가지고 오게 되는데, 이렇게 하는 것보다는 Spring Context 에서 빈으로 등록해 와이어로 가지고 오게 되면 DAO 가 아닌 Service 계층에서 호출이 가능하다.

다음과 같이 빈으로 등록한다.

이제 Service 계층에서 다음과 같이 와이어로 가지고 오고 바로 사용할 수 있다.

하지만 이렇게 할 경우에 Mapper 인터페이스 객체 하나하나 전부 Spring Context 에서 빈으로 만들어 줘야 한다. 그래서 이렇게 하지말고 Mapper 인터페이스를 스캔하도록 할 수 있는데 이를 위해서 mybatis-spring 라이브러리 패키지와 xsd 설정을 해줘야 한다. xsd 과 설정은 Spring Context 파일에 다음과 같이 해준다.

Mapper 인터페이스 클래스 파일들을 전부 위 설정에 나온 패키지 경로로 모두 옮긴다. 그리고 각 Mapper 인터페이스에 @Mapper 어노테이션을 붙여준다. 또한 매퍼 XML 에 네임스페이스 경로도 모두 위 설정으로 바꿔 준다.

이렇게 하면 DAO 관련 파일들은 전부 삭제해도 잘 동작 한다.

HTTP 응답 헤더 보안

의외로 웹 프로그래밍을 하는 사람들 조차도 HTTP 응답 헤더에 대해서 생각을 하지 않는다. HTTP 응답 헤더에 대한 생각을 하지 않는다는 것은 웹 프로그래밍을 깊게 공부한 사람이 아니라고 말할 수도 있다.

Cache Control

대부분의 사람들은 이 부분에 대해서는 잘 안다. Cache Control 헤더는 Client 에 컨텐츠를 어떻게 캐시할 것인지에 대한 제어를 할 수 있다.

하지만 고려해야할 사항이 존재한다. 만약 AWS Cloud 를 사용한다면 더 더욱 고려해야할 사항이 존재한다. AWS 의 CloudFront 는 CDN 서비스로서 컨텐츠 캐시를 기반으로 한다.

만일 특정 컨텐츠에 대해서 캐시를 하고 싶지 않다면 어떻게 해야할까? AWS 에서는 다음과 문서에 기술해 놨다.

사용자 지정 오리진 웹 서버 애플리케이션에서, Cache-Control no-cacheno-store 또는 private 명령을 CloudFront에서 캐시하지 않으려는 객체에 추가합니다. 또는 Expires 명령을 CloudFront에서 캐시하지 않으려는 객체에 추가합니다.

CloudFront에서 특정 파일 캐시를 방지하려면 어떻게 해야 합니까?

오리진(origin) 의 웹 서버나 애플리케이션에서 Cache-Control 헤더를 조작하도록 권고 하고 있다.

위는 응답 캐싱 정책을 정의하는 사용하는 헤더 지시문이다. Cache-Control 은 HTTP/1.1 사양의 일부로 정의 되었다. Expire 는 Cache-Control 이전에 사용했던 지시문이다. 현대의 대부분 브라우저는 HTTP/1.1 를 지원하지만 구형을 쓰는 곳이 있을 수 있음으로 Expire 지시문을 함께 사용한다.

no-cache 는 말그대로 캐시를 하지 말라는 것을 지시한다. 이 지시자를 쓸 경우에 매번 요청할 때마다 문서의 유효성을 검사한다. 그래서 컨텐츠가 변경된 경우에 최신 버전을 가지고 오게 된다. 컨텐츠의 변화를 감지하는 매커니즘을 가진다는 것이 매우 중요하다. 또 모든 것을 캐시를 하지 않는다. 예를들어, 비공계 개인 데이터와같은 것은 이 지시자로 캐시를 제어를 할 수 없다.

no-store 는 비공개 개인 데이터를 포함해 모든 캐시를 하지 않다록 해준다.

public, private 이라는 것도 있다. 이는 중간 캐시를 이용할 경우에 유용하다. 중간 캐시는 CDN 을 의미한다. 만일 CDN 과 같은 중간 캐시 서버가 캐시 데이터를 가지고 있기를 원하지 않는다면 private 를 써야 한다.

그리고 위 코드에 보이는과 같이 모든 것을 다 포함하도록 지시문을 사용해서는 안된다. Pragma, Expire 는 HTTP/1.0 스펙에서 유효하다. 거기다 Cache-Control 지시문에서 no-cache, no-store, max-age 를 한꺼번에 쓰는건 잘못된 것이다.

그래서 cache 전략이 필요하게된다. 이는 HTML 구조를 봐야 한다.

index.html 파일 안에 구조가 저렇게 되어 있다고 가정하면, 대략 다음과 같은 형태의 캐시 전략을 쓸 수 있다.

ContentsCache-Control
index.htmlCache-Control: no-cache
common.cssCache-Control: max-age=86400
a.jsCache-Control: private, max-age=86400

CSS 파일은 최대 1일까지만 캐시하도록 지시했고, Javascript 파일은 CDN 에서 캐시를 하지 못하도록 하고 브라우져에서 최대 1일까지만 캐시하도록 했다. index.html 은 no-cache 를 주어서 캐시를 하지 못하게 했지만 매번 요청할 때마다 변경이 되었는지 체크하고 변경되었다면 최신판을 새로 받도록 했다.

must-revalidate 가 있는데, 강제 사항을 적용할 때 사용 한다. 이는 다음과 같이 사용한다.

이 경우에 1일동안 캐시를 하고 그 이후에는 서버에 반드시 유효성검사를 하도록 강제한다. 만일 네트워크 상황으로 유효성 검사를 못할 경우에는 절대로 캐시 데이터를 사용하지 못하도록 한다.

이론을 기반으로 현실에서 사용 전략을 다음과 같이 세울 수 있다.

경우의 수Cache-Control
금융Cache-Control: no-store
장바구니, 결재 페이지Cache-Control: no-store
개인정보 수정 페이지Cache-Control: no-store
동적 웹 페이지Cache-Control: no-cache
정적 파일Cache-Control: max-age=86400

금융 관련된 웹 애플리케이션을 사용할 때에는 캐시를 되도록이면 하게 하면 안된다.

Nginx 와 같은 경우에 다음과 같이 HTTP Header 에 Cache-Control 을 붙일 수 있다.

Nginx 에서 max-age 설정은 expires 해도 된다. 이렇게 하면 구형의 Expire 지시자도 함께 지정된다.

Spring에서는 Spring-Security 에서 다음과 같이 지원한다.

위 설정은 다음과 같은 상태로 만든다.

만일 직접 캐시를 조작하고 싶다면 다음과 같이 해주면 된다.

Content Type Options

브라우저는 content sniffing 같은 기술을 사용해 다운받을려는 컨텐츠 타입을 추정하고 특정하게 된다. 하지만 이러한 것은 XSS 와 같은 공격을 제공하는 빌미가 된다. 그래서 이것을 방지하기 위해서 다음과 같이 헤더응답을 넣는다.

Spring Security 에서는 간단하게 설정할 수 있다.

HTTP Strict Transport Security(HSTS)

보통 사이트에 접속하는데에 프로토콜을(HTTPS, HTTP) 붙이지 않는다. 이럴 경우 브라우저는 HTTP 로 먼저 접속해보고 서버의 설정에 따라서 HTTPS 로 리다이렉트를 하게 된다.

만일 중간에 해커가 Proxy Server 를 두게되면 클라이언트는 HTTP 로만, Proxy Server 가 이를 받아서 HTTPS 로 뒷단 서버에 연결을 하게된다. 이렇게 되면 모든 정보를 볼 수 있게 된다. 이러한 해킹 기법을 “SSL Stripping” 공격 혹은 SSL/TLS Hijacking 이라고 부른다.

HSTS 는 애초에 접속을 HTTPS 로만 하라고 강제한다. 이는 브라우저와 서버 모두 HSTS 를 모두 지원해야 한다는 제한이 있지만 2010에 HSTS 에 대한 논의로 인해서 현재에는 모든 서버와 브라우저가 지원하고 있다.

HSTS 를 지원하는 브라우저는 도메인 목록을 유지한다. HTTPS 로만 접속 해야만 하는 도메인을 가지고 있게된다. 만일 HSTS 목록에 있는 도메인 주소를 억지로 HTTP 로 접속하게 되면 서버가 리다이렉트를 하는게 아니라 HTTPS 로 다시 접속하라고 브라우저에 요청하게 된다. HSTS 는 목록에 도메인을 유지하는 시간을 지정할 수 있다.

무조건 HTTPS 로만 접속하도록 강제하기 때문에 SSL 도메인 인증서가 있는 사이트에서만 사용 가능하다.

Spring Security 에서는 다음과 같이 간단히 적용할 수 있다.

X-Frame-Options

HTML 내에 또 다른 frame 을 넣는 기술은 자주 사용 되었었다. 최근에는 사용이 줄었지만 그래도 여전히 사용되어지는 기술이다. 하지만 이러한 frame 을 이용할 경우에 그것이 변조에 취약하다는 문제가 있다.

그래서 이 frame 을 실행하지 못하도록 브라우저에게 요청할 수 있는데, 이것이 X-Frame-Options 다. 기본 값은 DENY 이지만 같은 도메인의 frame 일 경우에는 허용할 수도 있다.

Spring Security 에서는 다음과 같이 설정할 수 있다.

SAMEORIGIN 일 경우에는 같은 도메인의 frame 일 경우에는 허용하게 한다.

X-XSS-Protection

XSS 를 브라우저에서 실행하도록 한다. 주의해야할 것은 서버의 웹 애플리케이션에서 하는게 아니라 브라우저의 XSS 필터에서 하는 것이다. 그러니까 서버에 요청을 넣기전에 브라우저가 사전에 XSS를 한번 차단하게 하는 것이다.

Spring Security 에서는 다음과 같이 설정할 수 있다.

CONTENT-SECURITY-POLICY

웹에 자원은 동일한 도메인에서만 작동되도록 설계되었다. 하지만 자신만의 스크립트를 삽입하는 XSS 공격을 고안해 냈다. 이를 방어하기 위해서 고안된 것이 CSP(Content Security Policy) 이다. 이것을 HTTP 헤더에 정의하면 브라우저에는 이곳에서 받은 리소스만 실행허거나 렌더링하도록 강제 한다.

Spring Seuciryt 에서는 다음과 같이 설정할 수 있다.

Spring 에서 기본

많은 웹 애플리케이션이 Spring 을 이용해서 만들어진다. 그리고 인증을 위해서 Spring Security 를 사용한다. 하지만 대부분의 소스를 보면 위에서 언급한 기본적인 보안조차 되어 있지 안되어 있는 곳이 대부분이다. 대기업의 프로젝트에서조차도 이런것을 고려하지도 않는다.

위에서 언급한 모든 내용은 다음과 같이 Spring Security 에서 간단하게 설정할 수 있다.

아주 간단하다. 그냥 가져다 붙여넣기만 해도 된다. 하지만 이것조차도 사용하지 않는 대기업 프로젝트가 부지기수다. 뭐가 기술력이 좋다는 건지… IT 선진국이라는 것이 좋은 인프라에 작동하기만 하면 되는 앱만으로 얻을 수 있는 거였다니..

Spring5 requirements

Spring5 를위한 필요사항들을 정리.

  1. java 8. 원래 Spring5 는 Java 9를 기반으로 하려고 했지만 변경됐다. 이로 인해서 Reactive Programming을 하기 위해서는 의존성 라이브러리를 필요로 한다. 현재Java 9 은 지원이 중단된 상태다. Java 10 을 사용해도 된다.
  2. Java EE 8 호환. Servlet 4.0, Bean Validation 2.0 등을 지원한다.
  3. HTTP/2 지원한다.
  4. Jackson 2.9, Protobuf 3.0 지원.

 

MyBatis Error – The content of elements must consist of well-formed character data or markup.

MyBatis 의 Mapper XML 파일에서 다음과 같은 오류가 발생 했다.

MyBatis Mapper XML Error
“The content of elements must consist of well-formed character data or markup.”

이는 SQL 연산자인 <, =, > 와 같은 엔터티가 MyBatis 문법과 혼동되서 나오는 문제다. 이럴때는 CDATA 를 적용해주면 된다.

이렇게하면 에러를 없앨 수 있다.

스프링 디렉토리 구조 재정의하기

이클립스(Eclipse)에서 STS 를 설치하면 아주 간단하게 Spring MVC 샘플 프로젝트를 생성할 수 있다. 이 샘플 프로젝트의 디렉토리는 다음과 같다.

Directory Structure of Spring MVC
Directory Structure of Spring MVC

엄밀히 말하면 사실 이 디렉토리 구조는 Maven 이 관리 한다. Maven 에서는 기본적으로 Java 소스 디렉토리, Resource 디렉토리, webapp 디렉토리를 위와  같이 정의해 놓고 있다. STS 의 Spring MVC 샘플 프로젝트도 Maven을 기반으로 만들어 졌기 때문에 위와 같은 디렉토리 구조를 가진다.

하지만 한국에서는 다음과 같은 디렉토리 구조를 주로  사용한다.

완성된 디렉토리 구조
완성된 디렉토리 구조

webapp 의 루트(root) 디렉토리를 WebContent 로 바꾸고 스프링의 설정 파일인 root-context.xml, servlet-context.xml 를 Resource 디렉토리의 config 디렉토리 아래로 옮겨준다.

이러한 구조로 바꾸기 위해서는 Maven 설정을 만줘져야 하는데, 이클립스를 이용하면 손쉽게 위와같은 구조로 변경할 수 있다.

WebContent 디렉토리 생성

webapp 디렉토리를 WebContent 로 바꾸기 위해서는 먼저 디렉토리를 만들어야 한다. 이클립스에서 프로젝트 아이콘에서 오른쪽 버튼을 클릭해서 WebContent 를 만들어 준다.

생성이 되었다면 이제 기존의 webapp 디렉토리 아래에 모든 디렉토리와 파일을 WebContent 디렉토리로 옮겨준다. 그리고 webapp 디렉토리를 삭제해 준다.

webapp 파일 옮기기
webapp 파일 옮기기

이렇게 옮겨 놓고 보면 pom.xml 파일이 오류가 난다. 오류의 내용은 다음과 같다.

Maven 에서 정해진 위치에 있어야 할 web.xml 이 없기 때문에 나오는 오류다. 따라서 Maven 에서 web.xml 디렉토리 위치를 바꿔 주면 되는데 먼저 pom.xml 에 maven-war-plugin 를 추가해 준다.

그리고 이클립스에 설정으 바꿔 줍니다. 프로젝트이름에서 마오스 오른쪽 클릭 후에 ‘Properties -> Deployment Assembly’ 를 보면 /src/main/webapp 이 보인다. 이것을 삭제하고 WebContent 를 추가해준다.

WebContent 를 위한 이클립스 설정
WebContent 를 위한 이클립스 설정

이렇게 한 후에 pom.xml 파일의 오류가 사라지는것을 볼 수 있다. 만일 그렇지 않다면 Maven Update 를 한번 해준다.

이제 Maven 을 이용해서 프로젝트를 빌드 해본다. 성공적으로 빌드가 됐다면 다음과 같이 나온다.

webapp 디렉토리 변경후에 빌드 성공한 디렉토리 구조
webapp 디렉토리 변경후에 빌드 성공한 디렉토리 구조

Spring 설정을 WebContent 와 분리

WebContent 이름에서 보이듯이 이 디렉토리는 Web 관련 파일들만 모아놓기위한 것이다. Web 관련 파일이라면 CSS, JS, Images, JSP 로 대표되는 Web Frontend 파일을 말한다. 하지만 자세히 보면 Spring 에 관련된 파일 들도 있어서 이것을 Resource 디렉토리로 옮겨 놓는게 적절해 보인다.

먼저 src/main/resources 디렉토리에 ‘spring’ 이름의 디렉토리를 생성해준다. 디렉토리를 생성했는데 패키지 아이콘이 나온다면 ‘Build Path’ 에서 Exclude 해주면 된다. 그리고 spring-root.xml, spring-context.xml 두개의 파일을 모두 resources/spring 으로 옮겨주고 기존의 WEB-INF/spring 디렉토리는 삭제해준다.

Spring 설정파일 옮기기
Spring 설정파일 옮기기

Web.xml 파일에서 spring 설정 파일의 경로를 변경해 준다.

그리고 pom.xml 파일을 다음과 같이 변경해 준다.

 

이렇게 디렉토리를 변경하는 이유는 앞에서 잠깐 언급했지만 Web 컨텐츠와 Spring 설정파일들을 분리하는데 있다. 또, 이렇게 함으로써 자바 관련 파일과 Web 컨텐츠 파일을 손쉽게 분리해서 배포할 수 있게 된다.

web.xml 스키마 예제 헤더

web.xml 스키마 헤더 예제는 버전별로 다음과 같다.

Servlet 2.5

Servlet 3.0

Servlet 3.1

Servlet 4.0

Servlet 5.0

Servlet 6.0

참고: IBM Knowledge Center