WEB(35) - 스프링(2) Maven 적용 - 수업 73일차
목차
Maven
: 의존 라이브러리(dependency library)를 일괄적으로 관리하기 위한 도구입니다.
기존에는 사용할 라이브러리 수가 많지 않았기에 직접 jar
파일을 추가해서 사용했지만, 규모가 커질수록 사용해야 할 라이브러리들이 많아질 수 밖에 없기 때문에 더욱 Maven
또는 Gradle
과 같은 도구들이 중요하게 사용되고 있습니다.
pom.xml
설정 파일(Project Object Model의 약자입니다)에 필요한 라이브러리를 추가해서 사용하며, 처음 라이브러리를 가져올 때에는 원격 저장소에 있는 라이브러리를 로컬 저장소(.m2
디렉토리)에 받아오고, 그 뒤로는 로컬 저장소에 받아온 라이브러리를 재사용합니다.
Maven repository에서 <dependency>
코드를 가져오면 이클립스에서 원격지의 라이브러리들을 받아올 수 있습니다.
Maven 적용
이제 본격적으로 프로젝트를 생성해서 Maven을 적용해보도록 하겠습니다.
java project 생성 후 Package Explorer에서 프로젝트에 우클릭을 하면 Configure - convert to maven project 항목이 있습니다.
여기서 설정 파일인 pom.xml
을 생성합니다.(생성할 때 다른 항목은 수정할 필요가 없습니다)
생성된 pom.xml
하단의 </build>
태그와 </project>
태그 사이에 다음과 같이 코드를 추가하면 라이브러리를 가져올 수 있습니다.
1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
</dependencies>
(현재 레거시 연습을 위해 Maven 4.3.14 버전을 사용하지만, 추후 스프링 부트 사용 시 5.x 버전을 사용할 예정입니다)
Maven 오류 발생 시 대처 방안
위와 같이 적용을 해도 Maven이 제대로 적용되지 않거나 에러가 발생할 수 있습니다. 그럴 경우 아래의 조치를 취해주시면 됩니다.
- 해당 프로젝트 우클릭 후 Maven - Update Project
-
- 업데이트로도 안될 경우
- Eclipse Restart
-
- 재시작으로도 안될 경우
- 이클립스를 끈 상태에서 m2 디렉토리를 직접 삭제한 뒤 다시 작업
예제
xml 설정 방식의 DI 사용 연습을 해보겠습니다.
우선 MemberDAO
와 MemberService
인터페이스를 생성할텐데, 이클립스에서는 편의 상 Impl 클래스를 먼저 생성한 뒤 refactor - Extract Inerface 기능을 사용하면 편리하게 작성할 수 있습니다.
interface를 구현하는 구현체는 관례적으로 끝에
Impl
을 붙입니다
또한 싱글톤 패턴을 적용할 때 기존 mvc 구조에서는 직접 명시해야만 했지만, 스프링 프레임워크에서는 IOC 컨테이너(or DI 컨테이너)가 기본적으로 싱글톤 적용을 해주기에 별도의 설정이 필요하지 않습니다.
다음과 같이 간단하게 MemberDAOImpl
클래스를 작성한 뒤, 인터페이스를 추출합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
package model;
public class MemberDAOImpl implements MemberDAO {
@Override
public String findMemberById(String id) {
if(id.equals("java")) {
return "java 아이유 오리";
} else {
return null;
}
}
}
1
2
3
4
5
package model;
public interface MemberDAO {
String findMemberById(String id);
}
이제 MemberServiceImpl
클래스도 작성해보겠습니다. (흔히 Service 계열의 클래스는 트랜잭션 처리를 위한 비즈니스 계층의 컴포넌트로 사용됩니다)
1
2
3
4
5
6
7
8
9
10
11
12
13
package model;
public class MemberServiceImpl implements MemberService {
private MemberDAO memberDAO;
public MemberServiceImpl(MemberDAO memberDAO) {
super();
this.memberDAO = memberDAO;
}
@Override
public String findMemberById(String id) {
return memberDAO.findMemberById(id);
}
}
MemberDAO
를 인터페이스 타입으로 참조했기에 결합도를 느슨하게 할 수 있습니다.
스프링 IOC(or DI) Container에서 memberDAO 구현체를 주입합니다.
위의 구현체를 주입하는 것을 Dependency Injection이라고 부르며 xml
, annotation
두 가지 방식으로 구현할 수 있습니다.(실무에서는 애너테이션 방식이 주로 사용되지만, 우선 xml 방식을 배운 뒤 애너테이션 방식으로 넘어갈 예정입니다)
spring-config.xml
파일은 다음과 같이 작성합니다.
1
2
3
4
5
6
7
8
9
10
11
12
<?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="memberDAO" class="model.MemberDAOImplVer2"></bean>
<bean id="memberService" class="model.MemberServiceImpl">
<constructor-arg>
<ref bean="memberDAO"/>
</constructor-arg>
</bean>
</beans>
<beans>
태그 안에 저희에게 필요한 <bean>
들을 작성하며, 의존관계가 필요한 memberService
빈에 memberDAO
빈을 주입받을 수 있도록 <constructor-arg>
태그를 사용했습니다.
의존관계 주입(DI)은 위에서와 같이 생성자에 주입받거나, Setter 메서드 또는 Filed에 주입받는 방법이 있습니다.
Setter 메서드에 의존관계를 주입받는다면 다음과 같이 MemberDAO
생성자 부분을 set()
메서드로 변경한 뒤 spring-config.xml
설정 파일에서 생성자 태그인 <constructor-arg>
태그를 <property>
태그로 변경하면 됩니다.
1
2
3
public void setMemberDAO (MemberDAO memberDAO) {
this.memberDAO = memberDAO;
}
1
2
3
4
5
6
7
8
9
10
11
<bean id="memberService" class="model.MemberServiceImpl">
<constructor-arg>
<ref bean="memberDAO"/>
</constructor-arg>
</bean>
<!-- 위 태그를 아래 태그로 변경(생성자 -> setter) -->
<bean id="memberService" class="model.MemberServiceImpl">
<property name="memberDAO">
<ref bean="memberDAO"/>
</property>
</bean>
MemberService
인터페이스는 다음과 같이 간단히 작성했습니다.
1
2
3
4
5
package model;
public interface MemberService {
String findMemberById(String id);
}
마지막으로 위에서 작성한 기능들을 동작시킬 Test 클래스를 작성해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import model.MemberService;
public class TestDI {
public static void main(String[] args) {
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext("spring-config.xml");
MemberService memberService = (MemberService) factory.getBean("memberService");// DL(Dependency Lookup)
System.out.println(memberService.findMemberById("java"));
factory.close();
}
}
실행해보면 다음과 같이 정상 작동하는 것을 확인할 수 있습니다.
이전 수업에서도 말씀드렸지만, 위와 같은 구조가 복잡해보일 수 있습니다. 그럼에도 이러한 방식이 많이 사용되고 강력한 이유는 유지보수와 확장성, 그리고 보다 본질적으로는 프로그램을 더욱 객체지향스럽게 만들 수 있기 떄문입니다.
외부에서 필요한 의존관계를 주입받음으로써 구현체들은 자신들의 역할에만 충실할 수 있게 되며 이는 규모가 커질수록 유지보수성, 확장성이 크게 향상됩니다.
지금보다 개선할 여지가 많이 남았지만 이후의 수업에서 차차 개선해보도록 하겠습니다.
댓글남기기