본문 바로가기

Spring

[Spring] Annotation을 이용한 Spring 설정을 이용한 학생 관리 프로젝트 구현

 

[해당 포스트는 개인적으로 공부를 하고 차후에 참고용으로 하고자 작성한 것입니다.
따라서 잘못된 부분이나 부족한 부분이 있을 수 있기에 참고하시기 바랍니다.]

저번 포스팅에선 XML 파일을 이용하여 Container를 생성하고 Bean을 관리하였다.

이번 포스팅에선 XML파일 대신 Java파일을 이용하여 의존관계를 관리해보도록 하자.

 

Spring 3.0 이전에는 Spring 설정을 XML로 대부분 처리를 하였다. 3.0 버전 이후부터 Java 파일을 통해 Spring 설정이 가능해졌다. 이후 Spring Boot부터 대부분 Spring 설정은 Java 코드로 많이 설정하는 추세가 되었다고 한다. 꼭 Java 파일로만 설정을 안 해도 되며 본인에 취향 또는 팀 개발 환경에 맞게 XML이나 Java 파일로 맞춰서 개발하도록 하자.

 

사용하는 Annotation

@Configuration : Annotation기반 환경구성을 돕는다. 해당 Annotation을 구현함으로써 하나 이상의 @Bean Annotation을 구현하여 Bean을 정의할 수 있다.

@Bean : Application Container에 저장시킬 Bean을 정의한다.

 

Example 1

그렇다면 XML과 Java로 만들어진 속성을 비교해보도록 하자.

 

<bean id="studentDao" class="ems.member.dao.StudentDao"/>

 

id가 studentDao고 참조 Class가 StudentDao인 Bean을 XML에서 생성하는 방식이다.

이것을 Java파일로 바꾸면 다음과 같다.

 

@Bean
public StudentDao studentDao() {
	return new StudentDao();
}

 

서로 비교하면 생성자 Type이 참조 Class가 되며, 생성자 이름이 Bean id가 된다.

해당 Bean은 StudentDao 객체를 생성하여 반환한다. 이렇게 함으로써 생성된 객체를 이용하여 기능을 구현할 수 있게 되는 것이다.

 

Example 2

이번엔 생성자에 다른 Bean을 주입하는 코드를 보도록 하자.

 

<bean id="updateService" class="ems.service.StudentUpdateService">
   <constructor-arg ref="studentDao"/>
</bean>

 

id가 modifyService며 참조 Class가 StudentUpdateService인 Bean이 존재한다.

<constructor-arg> 태그를 이용하여 생성자에 studentDao를 주입한 것을 볼 수 있다.

이것을 Java파일로 바꾸면 다음과 같다.

 

@Autowired
StudentDAO studentDao;

@Bean
public StudentUpdateService updateService() {
	return new StudentUpdateService(studentDao());
}

 

studentDao를 @Autowired를 통해 주입시키고 생성자에서 StudentUpdateService의 인자로 넘겨주었다.

 

Example 3

Bean을 주입하는 방식에 대해 알았으니 이번엔 <property>를 통해 값을 삽입하는 방법에 대해 알아보자.

 

<bean id="dataBaseConnectionInfoDB" class="ems.member.DataBaseConnectionInfo">
	<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe" />
	<property name="userId" value="root" />
	<property name="userPw" value="root" />
</bean>

 

id가 dataBaseConnectionInfoDB며 참조 Class가 DataBaseConnectionInfo인 Bean이 존재한다.

<property> 태그를 이용하여 변수 이름이 jdbcUrl인 값엔 jdbc:oracle:thin:@localhost:1521:xe을 삽입했다.

나머지 변수들도 property 태그를 이용하여 주입한 것을 볼 수 있다.

이것을 Java파일로 바꾸면 다음과 같다.

 

@Bean
public DataBaseConnectionInfo dataBaseConnectionInfoDB() {
	DataBaseConnectionInfo infoDB = new DataBaseConnectionInfo();
	infoDB.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:xe");
	infoDB.setUserId("root");
	infoDB.setUserPw("root");
		
	return infoDB;
}

 

DataBaseConnectionInfo에는 해당 값을 저장하는 변수들의 Setter와 Getter가 구현되어 있다.

 

public class DataBaseConnectionInfo {
	
	private String jdbcUrl;
	private String userId;
	private String userPw;
	
	public String getJdbcUrl() {
		return jdbcUrl;
	}
	public void setJdbcUrl(String jdbcUrl) {
		this.jdbcUrl = jdbcUrl;
	}
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getUserPw() {
		return userPw;
	}
	public void setUserPw(String userPw) {
		this.userPw = userPw;
	}
	
}

 

지금까지 Java파일을 이용하여 Spring 설정을 하는 방법에 대해 간략히 알아봤다.

이번엔 이것을 이용한 학생 관리 프로젝트를 만들어 보자.

 

프로젝트 구조

 

Package

ems.member : Main 실행 Class를 담는 Package

ems.member.configuration : 각종 Spring 설정을 위한 Class를 담는 Package

ems.member.dao : DB 연결을 담당하는 Class를 담는 Package

ems.member.dto : 학생 정보 저장을 위한 Class를 담는 package

ems.member.service : Business Logic을 위한 Class를 담는 Package

 

Class

ems.member

MainClass : Java가 실행될 main Class

ProjectInfo : Project 정보를 담는 Class

 

ems.member.configuration

InfoClass : ProjectInfo에 관련된 모든 Bean을 저장하는 Class

InfoServiceConf : ProjectInfoService에 관련된 Bean을 생성하는 Class

MemberConf : Student에 관련된 모든 Bean을 저장하는 Class

MemberDaoConf : StudentDAO에 관련된 Bean을 생성하는 Class

MemberServiceConf : MemberService에 관련된 Bean을 생성하는 Class

 

ems.member.dao

StudentDAO : 학생 정보를 CRUD 하기위한 DB Connection Class

 

ems.member.dto

StudentDTO : 학생 정보를 저장하는 Model Class

 

ems.member.service

ProjectInfoService : ProjectInfo의 Business Logic을 구현하는 Class

StudentService : Student의 Business Loginc을 구현하는 Class

 

소스코드

ProjectInfo.class

public class ProjectInfo {

	private String author;
	private String discribe;
	
	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getDiscribe() {
		return discribe;
	}

	public void setDiscribe(String discribe) {
		this.discribe = discribe;
	}

}

 

Proejct 정보를 담수 있도록 author, discribe 변수로 생성하고 Getter / Setter를 생성한다.

 

InfoConf.class

@Configuration
@Import({InfoServiceConf.class})
public class InfoConf {

	@Bean
	public ProjectInfo projectInfo() {
		ProjectInfo proInfo = new ProjectInfo();
		proInfo.setAuthor("LDH");
		proInfo.setDescribe("This is a Spring Configuration test project.");
		
		return proInfo;
	}
}

 

ProjectInfo 에 관한 Bean을 생성하는 생성자이다.

ProjectInfo 객체를 생성하여 <property> 역할을 하는 setter 메서드를 입력하여 값을 주입하였다.

InfoConf는 ProjectInfo에 관한 모든 정보를 담기위해 @Import를 이용하여 InfoServiceConf.class를 Import 하였다. 따라서 Bean을 역할에 맞게 분리하여 작성하여 병합시킬 수 있다.

 

InfoServiceConf.class

import ems.member.ProjectInfo;
import ems.member.service.ProjectInfoService;

@Configuration
public class InfoServiceConf {

	@Autowired
	ProjectInfo proInfo;
	
	@Bean
	public ProjectInfoService infoService() {
		return new ProjectInfoService(proInfo);
	}
}

 

ProjectInfo를 @Autowired를 통해 Bean을 자동주입하였다.

ProjectInfoService를 @Bean을 사용하여 Bean을 생성한다. proInfo를 인자로 사용하여 ProjecdtInfo를 주입시켰다.

 

ProjectInfoService.class

public class ProjectInfoService {

	ProjectInfo proInfo;
	
	public ProjectInfoService(ProjectInfo proInfo) {
		this.proInfo = proInfo;
	}
	
	public void showInfo() {
		System.out.println(proInfo.getAuthor());
		System.out.println(proInfo.getDescribe());
		System.out.println();
	}
}

 

proInfo 객체를 주입받아 작성자명과 프로젝트를 설명하는 Method를 호출하는 showInfo()를 생성한다.

 

MemberDaoConf.class

@Configuration
public class MemberDaoConf {
	
	@Bean
	public StudentDAO studentDao() {
		return new StudentDAO();
	}
}

 

StudentDAO Bean을 생성하기 위해 studentDao 메서드를 생성한다.

 

StudentDAO.class

public class StudentDAO {

	private Map<String, StudentDTO> database = new HashMap<String, StudentDTO>();
	
	public StudentDTO select(String sNums) {
		return database.get(sNums);
	}
	
	public void insert(StudentDTO student) {
		database.put(student.getsNum(), student);
	}
	
	public void delete(String sNums) {
		database.remove(sNums);
	}
	
	public void update(StudentDTO student) {
		database.put(student.getsNum(), student);
	}
	
	public Map<String, StudentDTO> getStudentDB() {
		return database;
	}
}

 

DAO를 통해 외부 DB에 접근해야 하지만 임시로 HashMap을 이용하여 데이터를 저장하도록 만들었다.

Key는 User ID값이 저장되며 Value는 StudentDTO 객체가 저장된다.

 

StudentDTO.class

package ems.member.dto;

public class StudentDTO {

	private String sNum;
	private String sId;
	private String sPw;
	private String sName;
	private int sAge;
	private String sGender;
	private String sMajor;

	public StudentDTO(String sNum, String sId, String sPw, String sName,
			int sAge, String sGender, String sMajor) {
		setsNum(sNum);
		setsId(sId);
		setsPw(sPw);
		setsName(sName);
		setsAge(sAge);
		setsGender(sGender);
		setsMajor(sMajor);
	}

	...


	public String getsMajor() {
		return sMajor;
	}

	public void setsMajor(String sMajor) {
		this.sMajor = sMajor;
	}
}

 

생성자를 호출할 때 학생 정보를 변수에 저장한다.

... 이후론 변수에 관한 getter/setter 메서드를 정의한다.

 

MemberServiceConf.class

@Configuration
public class MemberServiceConf {

	@Autowired
	StudentDAO studentDao;
	
	@Bean
	public StudentService studentService() {
		return new StudentService(studentDao);
	}
}

 

StudentDAO를 주입받아 StudentService Bean의 생성자의 인자로 넣어준다.

 

MemberService.class

public class StudentService {

	private StudentDAO studentDao;

	public StudentService(StudentDAO studentDao) {
		this.studentDao = studentDao;
	}

	public Map<String, StudentDTO> getStudentDB() {
		return studentDao.getStudentDB();
	}

	public StudentDTO select(String sNums) {
		StudentDTO student = studentDao.select(sNums);

		if (student != null) {
			return studentDao.select(sNums);
		} else {
			System.out.println("There is no information.");
		}

		return null;
	}

	public void delete(String sNums) {
		StudentDTO student = studentDao.select(sNums);

		if (student != null) {
			studentDao.delete(sNums);
		} else {
			System.out.println("There is no information.");
		}
	}
	
	public void update(StudentDTO studentDto) {
		StudentDTO student = studentDao.select(studentDto.getsNum());

		if (student != null) {
			studentDao.update(studentDto);
		} else {
			System.out.println("There is no information.");
		}
	}
	
	public void insert(StudentDTO studentDto) {
		StudentDTO student = studentDao.select(studentDto.getsNum());
		
		if (student == null) {
			studentDao.insert(studentDto);
		} else {
			System.out.println("Database occurs a error!");
		}
	}
}

 

Student에 관한 Business Loginc을 작성한다.

DB에 접근하기 전 데이터가 유효한지 검사한 후 StudentDAO Class로 값을 넘겨준다.

 

MemberConf.class

@Configuration
@Import({MemberDaoConf.class, MemberServiceConf.class})
public class MemberConf {
	
}

MemberConf는 Student Class에 관한 모든 정보를 담기 위해 @Import를 이용하여 MemberDaoConf.class, MemberServiceConf.class를 Import 하였다.

 

MainClass.class

public class MainClass {

	public static void showUser(StudentService service) {
		Set<String> keys = service.getStudentDB().keySet();
		Iterator<String> iterator = keys.iterator();

		System.out.println("sNum" + "\t\t" + "sId" + "\t" + "sPw" +
		"\t" + "sName" + "\t" + "sAge" + "\t" + "sGender"
		+ "\t" + "sMajor" + "\t");

		while (iterator.hasNext()) {
			String key = iterator.next();
			StudentDTO dto = service.select(key);

			System.out.print(dto.getsNum() + "\t");
			System.out.print(dto.getsId() + "\t");
			System.out.print(dto.getsPw() + "\t");
			System.out.print(dto.getsName() + "\t");
			System.out.print(dto.getsAge() + "\t");
			System.out.print(dto.getsGender() + "\t");
			System.out.println(dto.getsMajor());
		}
		
		System.out.println();
	}

	public static void main(String[] args) {

		String[] sNums = { "Hmck24sD35lM1", "Lc1em901MNe1d", "MCB939K1kN330" };
		String[] sIds = { "apple", "banana", "orange" };
		String[] sPws = { "1234", "2345", "3456" };
		String[] sNames = { "Lee", "Gong", "Jang" };
		int[] sAges = { 27, 25, 24 };
		String[] sGenders = { "M", "M", "M" };
		String[] sMajors = { "Computer", "Computer", "Computer" };

		AnnotationConfigApplicationContext container =
				new AnnotationConfigApplicationContext(MemberConf.class, InfoConf.class);

		ProjectInfoService infoService = 
				container.getBean("infoService", ProjectInfoService.class);
		infoService.showInfo();
		
		StudentService service =
				container.getBean("studentService", StudentService.class);

		/* Insert */
		for (int i = 0; i < sNums.length; i++) {
			StudentDTO dto = new StudentDTO(sNums[i], sIds[i], sPws[i],
					sNames[i], sAges[i], sGenders[i], sMajors[i]);

			service.insert(dto);
		}

		System.out.println("<Insert>");
		showUser(service);

		/* Delete */
		service.delete("Lc1em901MNe1d");

		System.out.println("<Delete>");
		showUser(service);

		/* Update */
		service.update(new StudentDTO("MCB939K1kN330", "Berry", "4567",
				"Byun", 23, "W", "Conputer"));

		System.out.println("<Update>");
		showUser(service);
		
		container.close();
	}
}

 

해당 프로젝트를 실행하는 Main Class이다.

여기서 중요한 포인트는 AnnotationConfigApplicationContext이다.

XML 파일을 이용해서 Container를 생성할 땐 GenericXmlApplicationContext를 사용했지만 지금은 Java Annotation으로 Spring을 설정하였기에 해당 객체를 통해 Container를 생성해야 한다.

인자값은 ProjectInfo에 관한 모든 정보가 담긴 InfoConf.class와 Student에 관한 모든 정보가 담긴 MemberConf.class를 넣었다. 

 

나머지 Logic은 이전 포스팅과 비슷하므로 설명은 생략한다.

 

결과는 다음과 같다.