Page tree
Skip to end of metadata
Go to start of metadata

목표

  • 스프링 프레임워크의 IoC를 구현한 DI의 필요성에 대한 안내
  • 스프링 MVC 프레임워크 적용 (3.0) 
  • 스프링 DI를 이용한 datasource 주입 및 jdbctemplate 활용 (3.0)
  • 스프링 3.0 버전을 3.1 버전으로 마이그레이션

내용

스프링 프레임워크가 제공하는 IoC와 DI. 무엇이고 왜 필요한가?
  • 우선 IoC란 프로그래머가 구현하던 제어를 프레임워크와 같은 것이 가진 컨테이너에게 넘기는 것으로 간략히 이해할 수 있다. 
    이 말을 달리하자면 기존에는 코드레벨에서 의존관계가 정의되어있었으며 이는 어떠한 구현 클래스를 선택하여 사용할 것인지 기술되어야 했는데
    IoC 를 이용하게 된다면 어떠한 구현클래스를 사용할 것인지가 아니라 컨테이너가 어떠한 구현 클래스를 사용할지를 찾아서 조립한다는 의미이다.
  • 스프링 프레임워크에서는 위 IoC를 DI 라는 방법으로 구현하였다. 
    구현 클래스를 조립해야하는 컨테이너는 구현클래스간의 의존관계를 xml 파일로부터 읽어들일 수도 있고 (스프링 프레임워크 버전 3.0 미만)
    annotation 기술된 코드들을 찾을 수도 있다. (스프링 프레임워크 버전 3.0 이상) 
    스프링은 이러한 DI를 이용하여 xml 이나 annotation 을 활용하여 각 클래스들의 객체를 생성한 후에 이를 컨테이너에 담으며 각 의존관계에 따라 조립을 한다.
  • 이러한 스프링 프레임워크의 DI 는 런타임시에 의존관계가 이해되고, 또한 코드레벨에서 의존관계가 매우 루즈하므로 코드의 수정이나 확장에 있어서 매우 용이한 프로그래밍 환경을 제공해준다.

스프링mvc 프레임워크 적용 (step1 - https://github.com/kimmunsu/ slipp-user/tree/step1 )
  • spring mvc 적용에 필요한 의존관계 기술을 위해 pom.xml 에 spring-webmvc 기술
  • Handler interface 제거 및 Handler interface 의 구현 클래스에 annotation 정의(controller)
  • web.xml 에 기술되어있던 각 servlet 항목 제거 및 dispatcher servlet 기술 및 controlloer - service - model 역할에 필요한 bean 들을 알려주는 application-context 설정 파일 기술
  • web.xml 에 기술된 application-context 설정 파일을 작성. (mvc:annotation-driven 사용으로 bean을 찾아서 컨테이너에 담도록 작업.
  • factory 에 의존하는 모든 클래스들을 POJO 로 리팩토링.
    • dao 구현체같은 경우 좀 억지스럽게 코드를 재작성하게 되었습니다. 
       

      public class DaoFactory {
          private static ConnectionManager connectionManager;
          
      	public static UserDao getUserDao() throws FileNotFoundException, ConfigurationException {
      		if(ConfigManager.getEnvironment().equals("test"))
      			return new TestUserDaoImpl();
      		Map<String, Object> config = ConfigManager.getDatabaseConfig();
      		if(config == null)
      			throw new ConfigurationException("해당 config 없음요!");
      		if ( connectionManager == null ) 
      		    connectionManager = getConnectionManager(config);
      		return new UserDaoImpl(connectionManager);
      	}
      
      	private static ConnectionManager getConnectionManager(Map<String, Object> config) {
      		ConnectionManager connectionManager = null;
      		String adapter = (String)config.get("adapter");
      		if(adapter.equals("h2")) 
      			connectionManager = new H2ConnectionManagerImpl(config);
      		else if(adapter.equals("mysql"))
      			connectionManager = new MySQLConnectionManagerImpl(config);
      		return connectionManager;
      	}
      }

       

      위의 코드에서 DAO구현체가 의존하게 되는 connectionManager 를 DAO 구현체 코드 내에 삽입하도록 구현을 하였습니다.

      public class UserDaoImpl implements UserDao{
      	ConnectionManager connectionManager;
      	public UserDaoImpl() throws FileNotFoundException, ConfigurationException{
      		Map<String, Object> config = ConfigManager.getDatabaseConfig();
      		if(config == null)
      			throw new ConfigurationException("해당 config 없음요!");
      		connectionManager = getConnectionManager(config);
      	}
      	private static ConnectionManager getConnectionManager(Map<String, Object> config){
      		ConnectionManager connectionManager = null;
      		String adapter = (String)config.get("adapter");
      		if(adapter.equals("h2"))
      			connectionManager = new H2ConnectionManagerImpl(config);
      		else if(adapter.equals("mysql"))
      			connectionManager = new MySQLConnectionManagerImpl(config);
      		return connectionManager;
      	}
            ...
      }

      위 처럼 생성자에서 어떠한 connectionManager 를 가질 것인지 정의되도록 구현하게 되었습니다.

      DAOFactory 의 naming 을 좀 수정하고 메소드를 좀 수정하여 connectionManager 를 return 하도록 하였다면

      Dao 구현체들마다 중복될 수 있는 바로 위와 같은 코드는 없었으리라 반성합니다 ㅠㅠ  

       

  •  Dao에 대한 Factory 제거는 1단계에서는 그대로 유지하고 2단계에서 하면 되지 않을까? - 헉. 그럼 service layer 까지만 spring mvc 적용할 걸 그랬나봐요..;;

  • 위 코드의 설정 파일 읽는 부분과  adapter도 모두 제거해도 될 듯하다. - 넵 아래 코멘트 주신 부분으로 처리하면서 정리가 되었습니다. 바로 윗줄 코멘트대로 진행했다면 깔끔했을텐데 지금도 아쉬워하고 있습니다 ㅠ
    스프링 3.1 로 버전업까지 끝나면 step1 코드는 다시 손보도록 하겠습니다. (시간만 허락된다면... ㅋ) 
  • JdbcTemplate에 각 DB에 맞는 DataSource 전달해 주면 해결될 듯한데... 아래 step2에 네가 계획하고 있는 부분도 그렇게 진행하려고 하는 듯하다. 너무 복잡하게 접근하지 말고 기존에 네가 프로젝트에서처럼 쉽게 접근하면 좋겠다.
    .- 아무래도 내일 DI 로 인해 런타임시에 의존관계가 설정되는 환경에선 어떻게 테스트 코드를 작성하는지 진행해보면 재밌는 시간이 될 것 같습니다. (저의 역량부족으로 준비를 모두 못했어요 ㅠㅠ 책에서 얼핏 보긴 했는데 적용하다보면 step2도 못나갈거 같아 미루었습니다... ㅠㅠㅠㅠㅠㅠㅠㅠ) 
스프링 DI 를 이용한 datasource 주입 및 jdbctemplate 활용 (step2 - https://github.com/kimmunsu/slipp-user/tree/step2 / 코드리뷰를 보고 바로 step2_2 작성 - https://github.com/kimmunsu/slipp-user/tree/step2_2 )
  • UserDao interface 의 getConnection() 메소드 삭제 및 declare 된 모든 exception throws 삭제
  • Dao 구현체의 인스턴스 변수를 spring framework 에서 제공하는 jdbctemplate 으로 선언 및 connectionManager 의존 모두 삭제
  • Dao 구현클래스의 jdbcTemplate 사용에 필요한 dataSource 주입을 위한 dataSource 생성 및 DI 코드 작성 (slipp-user-model.xml)
  • 스프링 jdbctemplate 사용에 필요한 코드로 dao 구현체 리팩토링
  • 기존 jdbctemplate 역할을 하던 모든 코드 삭제.
스프링 3.0 버전을 3.1 버전으로 마이그레이션
  • bean 을 관리하기 위한 config 클래스 생성(xml 대체)
    • config 클래스를 만들고 각 config의 의존도도 설정해주었으나 bean 생성이 안되며, 그 이전에 이 config 구현 클래스들이 생성되지 않았음.
      이를 미루어 짐작하여 WebApplicationInitialize 이라는 interface 를 상속받아 web app 의 환경을 설정하는데에 필요한 web.xml 을 대체하는 구현클래스를 작성하려고 하였으나,
      현재 스터디에 진행중인 servlet 버전으로는 진행이 불가하였음. (각종 리스너에 대해서 add 해주어야 하는데 현재 버전에서는 그것이 불가) 
      따라서 servlet 버전을 3.0으로 pom.xml 을 수정하여 진행하려 했으나 현재 maven repository 에 있는 servlet 3.0 버전은 alpha 버전으로 release 되지 않은 상태임.
      이건 더 리서치를 해봐야 알겠음. 
  • ?

작업하면서의 정리.

  • TDD 를 제대로 하려면 아직 멀었음을 느꼈습니다.
    step1 진행하는 가운데 메인 소스들을 우선 수정하다보니 테스트코드들이 모두 에러나게 되면서 삽질 ing ...ㅠㅠ
    런타임에 의존관계가 정의되도록 환경설정 파일을 기준으로 코드작업을 하게 되는 경우에는 테스트 코드를 어떻게 작성해야 할까요;;;
    테스트 작업까지는(배포 전) 인스턴스 변수들을 public 으로 접근제한자를 설정해주어 작업해야할까요?
    그럼 앞으로 step2 에서는 스프링 jdbctemplate 을 작업해야할텐데, 그땐 datasource 작업을 테스트코드에 모두 작성해주어야 할까요? ㅎㅎ 
  • 루즈 커플링을 위해 작성되었던 factory 로 인하여 오히려 Model layer 에 프레임워크 적용하기가 어려웠습니다.
    service factory 가 dao factory 에 의존하고 있으며 dao factory 가 connection manager 에 의존하고 있습니다.
    factory 만 걷어내면 되겠다 싶었으나 factory 에서 의존하고 있는 connection manager를 DAO 구현체의 생성자도 의존하고 있다보니
    이 dao factory 에서 connection manager 주입 로직을 따로 빼거나 구현체가 하도록 작업중입니다.

코드 리뷰

  • UserJoin.java => ModelAndView mav = new ModelAndView("/user/join_action.jsp");

    • 좀 더 단순하게 구현할 수 있다. 어떻게 하면 좋을까?
      • view resolver bean 정의시에 prefix, suffix 선언. (이거 말씀하시는게 맞는지 모르겠으나 일반적으로 작업을 저렇게 해왔던거 생각하여 작업하였습니다 ㅋ)
  • UserJoin.java에서 request => User 객체 생성하는 부분을 Spring MVC 활용해 자동화할 수 없을까?
    • 커스텀 태그까지 활용해 봤으면 좋겠다.
      • request parameter 들을 활용하여 바로 object 생성하도록 우선 User 클래스에 set method를 추가하였으며 커스텀태그는 개인정보수정 페이지에 작업하였습니다. (기능구현을 모두 마치진 않았습니다. 3.1 마이그레이션 전에 이걸 먼저 작업할게요~)
  • UserJoin.java와 UserLogin.java, 여러 개의 Form.java와 같이 여러 개의 클래스를 유지할 필요가 있을까? 하나로 통합하면 좋지 않을까?
    • UserController 클래스를 만들어서 하나의 파일에 각 기능은 메소드단위로 수정하였으며 패키지 네이밍도 net.slipp.controller 로 수정하였습니다.
  • 서블릿 표준으로 봤을 때 jsp 파일의 위치는 어디가 좋을까?
    • 헛...? 내일 아침에 다시 스펙 찾아보겠습니다 ㅎㅎ;
  • /user/Login.do, /user/Join.do와 같이 처리하지 않고 /user/login, /user/join과 같이 url을 변경할 경우 고려할 사항은?
    • 우선 web.xml 에 불완전하게나마 url pattern을 수정하여놓았습니다.
  • UserDaoImpl.java의 findByUserId가 마음에 들지 않는다. 리팩토링해보자.
  • findByUserId의 return (User)jdbctemplate.queryForObject( 보면 User 객체 캐스팅하고 있다. 다음에 들지 않는다. 캐스팅 없이 구현하자.

    • 아 실수;;;; 하였어요;;; 캐스팅할 필요가 없도록 rowMapper 써놓고는;;; 
  • Controller에서 Dao를 바로 접근하는 것에 대해 어떻게 생각하는가?
  • TestUserDaoImpl.java와 같은 Mock 클래스의 패키지 위치는 어디가 좋을까?
  • UserServiceTest.java는 DI를 사용할 경우 어떻게 개선할 수 있을까?

고마워요 재성이형~ 생각해보니 그냥 프레임워크 쓸 줄 안다고 작성한 코드들이네요;;;; 부끄러워라 ㅠㅠ

  • No labels

7 Comments

  1. 준비하느라 고생이 많지?ㅋ 화이팅 (smile) 

    1. 고마워 연석아 ㅠㅠㅠ

      게을러서 부랴부랴 작업중이야 ㅠㅠㅠㅠㅠ 반성반성 ㅠㅠㅠㅠㅠㅠ

  2. 내가 내일 6시간 수업이 있어서 수업 준비하느라 많이 도와주지 못하네.

    각 step별로 시도할 수 있는데까지 진행해봐. 내일 스터디에서 부족한 부분들 보완해 나가는 것으로 하자. 너무 완벽하게 스터디에서 할 이야기가 없으니까.

    1. 형 도움 많이 되었어요~! ㅎㅎㅎ 내일 스터디 시간이 부족하면 어쩌죠? 저의 역량이 부족하다보니 으허허허헝 ㅠㅠㅠㅠㅠㅠ

      1. 소스 코드 확인했는데 잘 했네. 몇 가지 개선 사항은 내일 스터디할 때 논의하면 될 듯하다.

        내일은 여유 시간 가지고 step3 진행할 수 있으면 step3까지 함 진행하면 좋겠다.

    1. 고마워요 형!!! ㅋ