Spring & Springboot/올인원 스프링 프레임워크

스마트폰 연락처 -1

YJ_ma 2023. 9. 17. 04:02

스마트폰 연락처의 개요

스마트폰 연락처 프로젝트

스마트폰의 연락처로 특정 인물에 대한 연락처가 출력되는 프로그램

 

시나리오

① 연락처 3개의 샘플 데이터를 데이터베이스에 등록한다.

② 전체 연락처 정보를 출력한다.

③ 특정 인물에 대한 연락처를 출력한다.

 

 프로그램 흐름도

MainClass ContactRegisterService
ContactSearchService
ContactDao
IoC 컨테이너 생성 연락처 등록
연락처 조회
연락처 등록, 조회

 

클래스와 appCtx.xml 내용

MainClass 클래스
MainClass main() 메서드가 명시되어 있는 클래스
Service 클래스
ContactRegisterService 연락처 등록
ContactSearchService 연락처 조회
DAO 클래스
ContactDao DAO
ContactSet 연락처 1개 정보
Util 서비스  
InitSampleData 샘플 데이터 초기화
IoC 컨테이너  
appCtx.xml 객체 생성과 조립

· 연락처를 등록, 조회하는 ○○○Service 클래스들이 있고 이러한 ○○○Service는 DAO(WordDao)를 이용해서 연락처 정보를 관리한다.

· IcC 컨테이너 설정은 appCtx.xml이 한다.

· IoC 컨테이너 역할

- Service 객체를 생성

- Service가 생성될 때 필요한 DAO 객체를 생성하여 Service에 주입

 

의존 객체 자동 주입을 통해 Service에 DAO객체가 자동으로 주입되는 방법을 알아본다!

 

스마트폰 연락처 만들기

다음과 같이 자바 프로젝트 ch05_pjt_01에 패키지와 클래스 파일을 만든다.

이제 각각의 class의 코드를 살펴본다.

1. ContactSet 클래스

· 연락처 정보를 담고 있다.

· name 필드 : 연락처 이름

· phoneNumber 필드 : 전화번호

· 생성자에서 필드 초기화에 필요한 모든 값을 받는다.

· 모든 필드에 대한 getter와 setter 메서드가 있다.

package ch05_pjt_01.contact;

public class ContactSet {
	private String name;
	private String phoneNumber;

	public ContactSet(String name, String phoneNumber) {
		this.name = name;
		this.phoneNumber = phoneNumber;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhoneNumber() {
		return phoneNumber;
	}

	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}

}

 

2. ContactDao 클래스

· 데이터베이스에 접속하고, Service에 의해서 호출된다.

· 데이터의 insert, search 기능을 수행한다.

· HashMap을 이용해서 데이터를 관리한다. (contactDB 필드에 단어 정보가 저장)

· key : 연락처 정보(ContactSet 클래스)에서 고윳값으로 부여되는 name / 특정 연락처 정보 조회시 사용

package ch05_pjt_01.contact.dao;

import java.util.HashMap;
import java.util.Map;

import ch05_pjt_01.contact.ContactSet;

public class ContactDao {
	private Map<String, ContactSet> contactDB = new HashMap<String, ContactSet>();

	public void insert(ContactSet contactSet) {
		contactDB.put(contactSet.getName(), contactSet);
	}

	public ContactSet select(String name) {
		return contactDB.get(name);
	}

	public Map<String, ContactSet> getContactDB(){
		return contactDB;
	}
}

 

3. ContactRegisterService 클래스

· 연락처 정보를 데이터베이스(지금은 Hashmap)에 저장하는 기능

· 생성자에서 DAO객체(ContactDao)를 받아서 contactDao 필드를 초기화하는 코드

· IoC 컨테이너에서 생성자에 ContactDao를 주입한다.

· verity() : 연락처를 등록하기 전에 name을 이용해서 기존에 등록된 연락처인지 판단

package ch05_pjt_01.contact.service;

import ch05_pjt_01.contact.ContactSet;
import ch05_pjt_01.contact.dao.ContactDao;

public class ContactRegisterService {
	private ContactDao contactDao;

	public ContactRegisterService(ContactDao contactDao) {
		this.contactDao = contactDao;
	}

	public void register(ContactSet contactSet) {
		String name = contactSet.getName();
		if (verify(name)) {
			contactDao.insert(contactSet);
		} else {
			System.out.println("The name has already registered.");
		}
	}

	public boolean verify(String name) {
		ContactSet contactSet = contactDao.select(name);
		return contactSet == null ? true : false;
	}

	public void setWordDao(ContactDao contactDao) {
		this.contactDao = contactDao;
	}
}

 

4. ContactSearchService 클래스

· 특정 연락처의 정보를 얻는다.

· ContactRegisterService와 마찬가지로 생성자에서 전달받은 DAO 객체를 contactDao 필드에 저장한다. 

· serchContact() : DAO를 이용해서 데이터베이스에서 단어 정보를 가져온다.

package ch05_pjt_01.contact.service;

import ch05_pjt_01.contact.ContactSet;
import ch05_pjt_01.contact.dao.ContactDao;

public class ContactSearchService {
	private ContactDao contactDao;

	public ContactSearchService(ContactDao contactDao) {
		this.contactDao = contactDao;
	}

	public ContactSet searchContact(String name) {
		if (verify(name)) {
			return contactDao.select(name);
		} else {
			System.out.println("Contact information is avilable.");
		}
		return null;
	}

	public boolean verify(String name) {
		ContactSet contactSet = contactDao.select(name);
		return contactSet != null ? true : false;
	}

	public void setContactDao(ContactDao contactDao) {
		this.contactDao = contactDao;
	}

}

 

5. InitSampleData 클래스

· 프로그램 실행에 필요한 샘플 데이터

· 실제 데이터는 appCtx.xml에서 설정한다.

package ch05_pjt_01.contact.utils;

public class InitSampleData {
	private String[] names;
	private String[] phoneNumbers;

	public String[] getNames() {
		return names;
	}
	public void setNames(String[] names) {
		this.names = names;
	}
	public String[] getPhoneNumbers() {
		return phoneNumbers;
	}
	public void setPhoneNumbers(String[] phoneNumbers) {
		this.phoneNumbers = phoneNumbers;
	}
}

 

6. appCtx.xml

· src/main/resources → appCtx.xml 생성

· InitSampleData 빈을 생성하는 코드로 3개의 샘플 연락처를 이용한다.

· appCtx.xml에 DAO, Service, 빈을 생성하는 코드를 작성한다.

<?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 
    https://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="initSampleData" class="ch05_pjt_01.contact.utils.InitSampleData">
    	<property name="names">
    		<array>
    			<value>류현진</value>
    			<value>손흥민</value>
    			<value>김연경</value>
    		</array>
    	</property>
    	<property name="phoneNumbers">
    		<array>
    			<value>010-0000-1111</value>
    			<value>010-0000-2222</value>
    			<value>010-0000-3333</value>
    		</array>
    	</property>
    </bean>
    
    <bean id="contactDao" class="ch05_pjt_01.contact.dao.ContactDao" />
    
    <bean id="registerService" class="ch05_pjt_01.contact.service.ContactRegisterService">
    	<constructor-arg ref="contactDao" />
    </bean>
    
    <bean id="searchService" class="ch05_pjt_01.contact.service.ContactSearchService">
    	<constructor-arg ref="contactDao" />
    </bean>
</beans>

· registerService와 searchService는 <constructor-arg/>를 이용해서 생성자에 의존 객체(DAO)를 주입받는다.

 

자동 의존 객체 주입

· registerService와 searchService는 생성자에서 contactDao를 주입받는다.

· contactDao 의존 객체를 주입받기 위해서는 다음과 같이 코드를 정형화했었다.

<constructor-arg ref="contactDao" />

매번 <constructor-arg/> 또는 <property/>을 작성하는 것은 불편하므로 스프링의 '의존 객체 자동 주입' 기능을 이용하면 자동으로 의존 객체가 주입된다.

 

자동으로 의존 객체 주입하기

1. context 네임스페이스와 스키마 추가하기

<context:annotation-config />를 사용하기 위한 네임스페이스와 스키마를 appCtx.xml에 추가한다.

<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 
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

 

2. <context:annotation-config /> 추가

애너테이션(annotation)을 사용하기 위한 <context:annotation-config />를 추가한다.

IoC 컨테이너애 생성된 빈이 필요로 하는 곳에 DAO객체가 사용될 수 있게 해준다.

ContactRegisterService와 ContactSearchService는 ContactDao를 필요로 하므로 <constructor-arg ref="contactDao" /> 없이도 생성자에 ContactDao가 자동으로 주입된다.

<context:annotation-config/>

 

3. <constructor-arg> 제거

<constructor-arg>을 제거한다.

<bean id="registerService" class="ch05_pjt_01.contact.service.ContactRegisterService"/>
    
<bean id="searchService" class="ch05_pjt_01.contact.service.ContactSearchService"/>

 

⚠️에러 : The prefix "context" for element "context:annotation-config" is not bound

Description Resource Path Location Type
The prefix "context" for element "context:annotation-config" is not bound. appCtx.xml /ch05_pjt_01/src/main/resources line 10 Language Servers

💡이 문제는 일반적으로 context 네임스페이스가 포함되지 않기 때문에 발생한다.

beans코드에 다음 내용을 추가해준다.

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

그리고 spring-context.xsd를 불러오는 코드에 http → https로 변경해준다.

https://www.springframework.org/schema/context/spring-context.xsd">

따라서 총 코드는 다음과 같다.

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

 

참고 사이트 :

"context:comComponent-scan" 또는 "context:annotation-config" 요소의 접두사 "context"가 바인딩되지 않았습니다. 스프링 오류

 

❓<context:annotation-config />는 어떻게 ContactDao를 필요로 하는 곳에 자동으로 객체를 주입할까?

💡 <context:annotation-config />는 데이터 타입을 보고 의존 객체를 자동으로 주입한다.

ContactRegisterService와 ContactSearchService 모두 생성자에서 ContactDao를 필요로 하기 때문에 <context:annotation-config/>는 ContactRegisterService와 ContactSearchService 빈을 생성할 때 IoC 컨테이너에서 ContactDao를 찾고 이를 ContactRegisterService와 ContactSerachService 생성자에 주입한다.

 

ContactDao 자동 주입 확인

1. pom.xml

MainClass 클래스를 작성하기 전에 메이븐 설정 파일을 수정해준다.

<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>spring5</groupId>
  <artifactId>ch04_pjt_01</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <!-- spring-context 모듈 -->
  <dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
  </dependencies>
  
  <!-- 빌드 설정 -->
  <build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>11</source>
                <target>11</target>
                <encoding>utf-8</encoding>
            </configuration>
        </plugin>
    </plugins>
  </build>
  
</project>

 

2. MainClass 클래스

package ch05_pjt_01.contact;

import org.springframework.context.support.GenericXmlApplicationContext;

import ch05_pjt_01.contact.service.ContactRegisterService;
import ch05_pjt_01.contact.service.ContactSearchService;
import ch05_pjt_01.contact.utils.InitSampleData;

public class MainClass {
	public static void main(String[] args) {

		// IoC 컨테이너 생성
		GenericXmlApplicationContext ctx = 
				new GenericXmlApplicationContext("classpath:appCtx.xml");

		// 샘플 데이터
		InitSampleData initSampleData = 
				ctx.getBean("initSampleData", InitSampleData.class);
		String[] names = initSampleData.getNames();
		String[] phoneNumbers = initSampleData.getPhoneNumbers();

		// 데이터 등록
		ContactRegisterService registerService = 
				ctx.getBean("registerService", ContactRegisterService.class);
		for(int i = 0; i < names.length; i++) {
			ContactSet contactSet = new ContactSet(names[i], phoneNumbers[i]);
			registerService.register(contactSet);
		}

		// 데이터 조회
		ContactSearchService searchService = 
				ctx.getBean("searchService", ContactSearchService.class);

		ContactSet contactSet = searchService.searchContact("류현진");		
		System.out.println("name: " + contactSet.getName());
		System.out.println("phoneNubmer: " + contactSet.getPhoneNumber());
		System.out.println("------------------------------------");

		contactSet = searchService.searchContact("손흥민");		
		System.out.println("name: " + contactSet.getName());
		System.out.println("phoneNubmer: " + contactSet.getPhoneNumber());
		System.out.println("------------------------------------");

		contactSet = searchService.searchContact("김연경");		
		System.out.println("name: " + contactSet.getName());
		System.out.println("phoneNubmer: " + contactSet.getPhoneNumber());
		System.out.println("------------------------------------");

		ctx.close();

	}

}

실행 결과

3. 의존 객체 자동 주입 확인

· ContactRegisterService와 ContactSearchService의 생성자 내부에 contactDao를 출력하는 코드를 추가하여 동일한 ContactDao객체가 자동 주입되는지 확인해본다.

 

■ ContactRegisterService 생성자

public void setWordDao(ContactDao contactDao) {
    System.out.println("contactDao: " + contactDao);
    this.contactDao = contactDao;
}

■ ContactSearchService 생성자

public void setContactDao(ContactDao contactDao) {
    System.out.println("contactDao: " + contactDao);
    this.contactDao = contactDao;
}

실행 결과