본문 바로가기

java/spring

[spring] 개발 환경 및 스프링 프로젝트 세팅

주제
이클립스 IDE를 이용해서 스프링 프로젝트를 수동으로 세팅하는 방법에 대해 다룬다.
STS를 이용한 자동 세팅은 다루지 않는다. 

 

 

개발 환경 준비물


java 설치 및 환경 변수 설정(JDK1.8버전을 사용했다)

Eclipse IDE

Apache tomcat(8.5버전을 사용했다)

 

 

새 프로젝트 생성 방법1


이클립스에서 스프링으로 웹 프로젝트를 만들어 볼 것이다.

 

dynamic web project 생성

웹프로젝트에 필요한 src/main/java, src/main/webapp을 자동 세팅해준다.

이 때, generate web.xml 추가를 추천한다.
이 xml 파일은 유저가 요청하는 url에 서블릿 파일을 매핑하거나 필터 파일을 세팅할 때 유용하다.

웹 설명 파일 추가

 

메이븐 프로젝트 전환

방금 만든 동적 웹 프로젝트를 maven으로 전환한다.

maven은 java 프로젝트의 의존성 관리 및 컴파일과 빌드 관리를 위한 대표적인 도구다.

 

방법은 프로젝트를 우클릭 - configure - convert to maven project 를 클릭한다.

이 떄, groupId와 artifactId를 입력하라고 나온다.

각 부분의 차이는 group은 프로젝트 전체를 포괄하는 최고위 프로젝트의 이름이고 artifact는 group 내 하위 프로젝트이다. 일반적으로 프로젝트 규모가 커지면, 이를 하위 프로젝트로 분할하여 관리하는데, group과 artifact는 그 위계의 차이다.

maven 프로젝트 전환 시 설정

 

설정이 끝나면 pom.xml이 생성된다.

이 파일은 maven 프로젝트를 관리하는 핵심 파일이며, 여기서 의존성을 정의하면 Maven이 중앙 저장소에서 해당 라이브러리를 자동으로 다운로드한다. 이외에도 빌드 방식 설정 등의 maven 프로젝트 관련 설정은 pom.xml에서 관리한다.

 

새 프로젝트 생성 방법2


다른 방법으로는 새 프로젝트 - maven project를 선택, 설정창에서 create a simple project를 선택하고 groupId, artifactId를 입력하여 생성한다. 

이 경우에는 동적 웹 프로젝트에 필요한 webapp 디렉토리를 직접 만들어줘야 한다.

 

기존 maven 프로젝트 가져오기


기존의 프로젝트를 가져올 때는 import - existing maven project를 사용한다. 

이 때, pom.xml 파일이 존재해야 import가 가능하다.

 

 

웹 프로젝트에 필요한 서버 세팅


아파치 톰캣 WAS를 가져왔다.
https://chartist1206.tistory.com/128

 

[jsp, servlet] Apache Tomcat의 사용 이유 & 서버 설정(WS, WAS)

배경 지식jsp는 servlet과 함께 동적 웹 페이지를 제작할 때 사용되는 자바 템플릿 엔진이다. jsp는 컴파일될 시 servlet파일로 변경되는데, 파일 내에 삽입했던 java 코드는 servlet의 service 메소드로 삽

chartist1206.tistory.com

 

pom.xml 작성


내 프로젝트의 pom.xml 예시다.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>testPjt</groupId>
  <artifactId>testPjt5</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  
  <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
  </dependencies>
  
  
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>utf-8</encoding>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.3</version>
      </plugin>
    </plugins>
  </build>
</project>


구성

dependencies 태그와 build 태그로 구성된다.

dependencies  | 프로젝트에서 사용할 종속성을 정의한다. 그러면 maven 프로젝트에서 자동으로 해당 종속성을 다운로드한다. 다운로드된 종속성은 file explorer에서 확인가능하다.




build | 프로젝트를 빌드하기 위한 설정을 적는다. 특히 jre버전을 명시할 때 현재 프로젝트에 세팅된  jre와 맞지 않아서 충돌이 발생할 수 있다.
이 때는 maven 프로젝트 업데이트가 필요하다.(프로젝트 우클릭 – maven – update project)

 

  • maven-compiler-plugin
    • maven이 자바 프로젝트를 바이너리 코드로 컴파일하기 위한 플러그인이다.
    • source | 컴파일 대상이 되는 자바 프로젝트 버전
    • target | 컴파일을 원하는 jre 환경에 맞춰서 적는 버전
  • maven-war-plugin
    • maven 프로젝트를 war 파일로 패키징하기 위한 플러그인이다.
war파일
war은 자바 프로젝트를 웹 어플리케이션으로 배포하기 위한 표준 패키징 형식이다.
이는 배포된 파일이 서블릿 컨테이너(apatch tomcat)에서 돌아가기 위해 필수다.

war은 다음 형식으로 배포된다.
myapp.war
|-- META-INF/
|-- WEB-INF/
|   |-- web.xml
|   |-- classes/
|   |-- lib/

이를 위해 다음 배포 이전의 자바 웹 프로젝트 역시 루트 디렉토리(ex: webapp)에 위 디렉토리 구조를 준수해야 한다. 각 디렉토리의 기능은 다음과 같다.

META-INF: 메타데이터 파일
WEB-INF: 웹 애플리케이션 설정 파일과 클래스 파일, 라이브러리 파일
WEB-INF/web.xml: 웹 애플리케이션 배포 설명자
WEB-INF/classes: 컴파일된 클래스 파일
.WEB-INF/lib: 종속 JAR 파일

스프링 프로젝트에서 전체 디렉토리 구조
my-web-app/
|-- src/
|   |-- main/
|       |-- java/          (Java 소스 파일)
|       |-- resources/     (기타 리소스 파일)
|       |-- webapp/        (웹 애플리케이션 리소스)
|           |-- META-INF/  (메타데이터 파일)
|           |-- WEB-INF/   (웹 애플리케이션 설정 파일)
|               |-- web.xml  (웹 애플리케이션 배포 설명자)
|               |-- lib/    (종속 라이브러리)
|               |-- classes/ (컴파일된 클래스 파일)
|           |-- index.html (정적 리소스 파일)
|           |-- styles.css (정적 리소스 파일)
|           |-- ...        (기타 정적 파일들)
|-- pom.xml

 

중간 점검 테스트

여기까지 세팅 점검을 위해 src/main에 servlet 파일을 만들고 콘솔을 찍어보았다.

 도메인: 포트번호/프로젝트/main으로 접속에 성공하면 성공이다.

@WebServlet("/main")
public class Main extends HttpServlet {
	private static final long serialVersionUID = 1L;
       


	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {	
		System.out.println("main");
		response.getWriter().append("Served at: ").append(request.getContextPath());
		
	}

}

 

 

스프링 컨테이너 설정


스프링 컨테이너는 스프링에서 핵심적인 기능 중 하나이다.

이 기능은 스프링 프레임워크에서 자동으로 객체 파일을 생성하고 의존성 주입을 하며 프레임워크 자체에서 이들을 관리할 수 있게 해준다.

 

이해를 위해 예시를 보자.

 

스프링 컨테이너가 없는 경우

스프링 컨테이너가 없는 일반 웹 프로젝트에서는 다른 모듈을 가져오기 위해서 new 생성자를 사용한다.

//모듈
public class Introduce {

	public Introduce() {
		// TODO Auto-generated constructor stub
	}
	
	public void printHello() {
		System.out.println("hello java user!");
	}
}

// /main
@WebServlet("/Main")
public class Main extends HttpServlet {
	private static final long serialVersionUID = 1L;
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		
        // new 생성자로 모듈 인스턴스 호출
        Introduce intro = new Introduce();
		intro.printHello();
        ctx.close();
	}


}

 

하지만 이 방식에는 몇 가지 단점이 있다.

  • 모듈 간에 의존성이 있는 경우, 개발자가 직접 의존성 주입을 관리해야 한다.
  • 서블릿이 호출되고 해제되는 경우의 생명 주기 관리 역시 많은 부분 관여해야 한다.
  • 여러 곳에서 동일한 객체를 중복 호출하며 메모리 낭비가 발생하고 분산식 관리로 디버깅이 어려워진다.

 

스프링 컨테이너가 있는 경우

스프링 컨테이너란 상위 모듈에서 사용되는 의존성 모듈을 컨테이너라는 곳에서 호출, 의존성 주입, 관리해주는 기능이다.

 

스프링 설정 파일 만들기(applicationContext.xml)

src/main/resources/ applicationContext.xml 파일 생성

  •  일반적인 스프링 mvc 구조에서는 web-int/spring 내에 만들지만, 지금은 연습으로 여기 만든다.
  • 파일 이름은 다른 것도 상관없다
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
 		http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="introduce" class="testPjt5.user.Introduce" />
	
</beans>

 

여기서 bean 태그를 제외한 나머지 부분은 보일러 플레이트 코드다.

bean은 스프링 컨테이너에 호출된 객체를 의미한다. 이는 각 파일에서 직접 객체를 호출하던 패턴 대신, 한 곳에서 모든 객체를 호출한 후 각 파일에서 가져가 쓰는 방식으로 싱글 톤 패턴, 중앙집중식 관리 패턴 등으로 불린다.

 

bean에서 class는 해당 클래스의 경로이며 src/main/java가 루트 경로이다.

id는 해당 클래스를 다른 파일에서 호출할 때 사용할 이름이다.

 

설정 파일과 빈 객체 가져오기

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		//		Introduce intro = new Introduce();
		//		intro.printHello();
		
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml");
		
        //	이렇게도 가능 Introduce intro = (Introduce) ctx.getBean("introduce"); 
        Introduce intro = ctx.getBean("introduce",Introduce.class);
		intro.printHello();
        
        ctx.close();
        
        
        }

 

classpath

classpath는 maven 같은 빌드 도구에서 클래스와 리소스 경로를 지정하는 방식이다.

maven 프로젝트에서는 기본적으로 src/main/resources 디렉토리가 루트 경로로 지정된다.

즉, "classpath:applicationContext.xml"는 src/main/resources/ applicationContext.xml에 접근한다.

이렇게 해서 설정파일을 가져오면 파일 내에 있는 빈 객체들이 생성된다.

 

getBean

스프링 컨테이너 설정파일에서 생성된 bean을 가져온다. 이 때, 유연하게 여러 방식이 가능한데, 이름 없이 바로 특정 클래스를 찾을 수 있고, 이름과 클래스를 한번에 지정할 수도 있다. 보통은 후자의 방식을 사용한다.

 

ctx.close()

작업 후엔 자원을 해제한다.

 

실행

프로젝트를 서버에 올리고 사용자가 요청을 하면 다음같이 스프링설정 파일과 빈이 호출된다.

org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
정보: Loading XML bean definitions from class path resource [applicationContext.xml]
org.springframework.context.support.GenericXmlApplicationContext prepareRefresh
정보: Refreshing org.springframework.context.support.GenericXmlApplicationContext

// 빈 사용 후


// ctx.close() 로 해제
org.springframework.context.support.GenericXmlApplicationContext doClose
정보: Closing org.springframework.context.support.GenericXmlApplicationContext@4fc2bc1f: startup date [Sun Jun 02 14:29:02 KST 2024]; root of context hierarchy

이처럼 스프링 컨테이너 빈 기반 객체 생성 방식은 스프링의 주된 패턴이다.

 

참고: 오류

Cannot find class [testPjt5.Introduce] for bean with name 'introduce' defined in class path resource [applicationContext.xml]; nested exception is java.lang.ClassNotFoundException: testPjt5.Introduce

해당 클래스를 찾을 수 없을 때 발생하는 오류다.

해결방법으로는 xml 파일에서 클래스 파일의 경로가 틀렸는지 혹은  java 파일에서 bean을 불러올 때 이름이 틀렸는지 확인한다.

 

스프링 컨테이너 설정 파일 분리하기


한 프로젝트의 모든 빈을 하나의 파일에서 관리하면 너무 길고 복잡해진다. 따라서, 모듈 및 기능에 따라 설정파일을 분리하는 것이 일반적이다. 분리는 이들을 호출하는 방식에 따라 두 가지 방법으로 구분된다.

 

방법1: 사용장소에서 한꺼번에 호출하기

우선 기능별로 컨테이너 설정 파일을 분리한다.(ex: DAO , service, bbs, message 등) 

사용할 장소에서 호출할 때는 다음같이 배열로 한꺼번에 로드 가능하다.

일반적으로 많이 사용하는 방식이다.

(배열 대신, GenericXmlApplicationContext에 인자로 하나씩 나열해줘도 된다.)

String[] appCtxs = {"classpath:student/ctxDAO.xml", "classpath:ctxService.xml", "classpath:.commmon/ctxMessage.xml"};

GenericXmlApplicationContext ctx =

new GenericXmlApplicationContext(appCtxs);

 

 

 

방법2: 분리 후 하나의 xml 파일로 import하기

빈들을 기능별로 서로 다른 파일에 분리시킨 후, 스프링 컨테이너 설정 파일에서 import로 합치는 방식이다.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
 		http://www.springframework.org/schema/beans/spring-beans.xsd">

	<import resource="classpath:xmls/appCtx1.xml" />
	<import resource="classpath:xmls/appCtx2.xml" />
</beans>
	GenericXmlApplicationContext ctx = 
    new GenericXmlApplicationContext("classpath:xmls/appCtxImport.xml");

 

빈 객체의 생성방식(싱글톤, 프로토타입)


스프링컨테이너에서는 설정에 따라 빈 객체를 하나만 만들 수도 있고, 호출처에서 호출마다 매번 새로운 객체를 생성할 수 도 있다.

 

싱글톤

빈 객체가 하나만 생성되며 getBean()으로 호출할 때는 동일한 객체가 반환된다.

동일한 객체를 사용해도 될 때 쓰며 메모리 낭비를 피할 수 있다.

스프링 컨테이너 설정에서 빈 객체에 기본으로 적용되는 방식이다.

 

프로토 타입

getBean을 호출할 때 마다 스프링 컨테이너에서 새 객체를 생성해주는 방식이다.

<bean id="chararter" class="testPjt4.Stat" scope="prototype" />