본문 바로가기
  • think normal
새로워지기/서른의 생활코딩

스프링 부트에서 토비 3.1 따라하기 : 1장 - 1.3 DAO 확장

by 청춘만화 2019. 2. 21.

스프링 부트에서 토비 3.1 따라하기 : 1장 오브젝트와 의존관계 - 1.3 DAO 확장

개발환경 

- OS : mac

- STS : 4.0.1

- MySQL : Server version: 8.0.13 Homebrew

- Frame-Work : 일단 최대한 의존성 없이 운영될 수 있도록 jar로 만 진행


관련 링크 

- 스프링 사용자 모임_홈페이지     http://www.ksug.org/

- 스프링 사용자 모임 질의응답     https://groups.google.com/forum/#!topic/ksug/13vB4tCFqrI

- 2017 스프링캠프                       https://www.youtube.com/playlist?list=PLdHtZnJh1KdZ6NDO9zc9hF4tONDLTSEUV



1장 오브젝트와 의존관계 

1.3 DAO 확장 

관심사의 분리. 관심이 같은 것끼리는 하나의 객체 안으로 또는 친한 객체로 모이게 하고, 관심이 다른 것은 가능한 한 따로 떨어져서 서로 영향을 주지 않도록 분리하는 것이라고 생각할 수 있다. 


1.3.0 그동안의 리뷰 

1. 초난감 DAO를 독립된 메소드를 만들어 분리했다. 

2. 상-하 클래스 분리했다.

1) 상위 클래스 - 추상 클래스 

2) 하위 클래스 - 추상 클래스를 상속한 서브 클래스 

3. 이러한 일련의 과정을 반영하는 개발 방법론들 

1) 디자인 패턴

2) 템플릿 메소드 패턴

3) 팩토리 메소드 패턴 


1.3.1 클래스의 분리 - SimpleConnectionMaker클래스 독립

1. 서브 클래스가 아닌 별도 클래스를 새로 만들어 DB 커넥션 정보를 담는다. 

2. 이 클래스를 UserDao 내에 있는 add(), get()메소드에서 사용하도록 new 키워드로 해당 클래스의 오브젝트를 만든다.

이 부분을 매번 오브젝트를 생성하지않도록 하기위해 

-> 생성자에서 new로 오브젝트를 생성하고 private(전역변수)로 클래스 오브젝트를 선언한다. 

3. 실행 결과 : 썩쎄스 

export_1.3.1 클래스의 분리 :  study_spring190220_1.zip


   1) 왼쪽 프로젝트 목록 영역 내 -> 빈 공간에서 오른쪽마우스 클릭 ->  import -> Existing Projects into Workspace -> 다운로드한 파일 선택 

   2) 혹시, 이전 포스팅 파일 import 하신 분은 이전 프로젝트를 삭제하시거나 이름을 바꾸신 후 다시 import 하셔야 합니다.  

+ 하지만(이슈 추가요~)  

처음 원하는 그림은 

인터페이스 도입 구조

이었지만 ... 결과는 아래 챕쳐 이미지에 블록처리한 부분과 같이 

이렇게 클래스를 분리한 경우에도 상속을 이용했을 때와 마찬가지로 자유로운 확장이 가능하게 하려면 두 가지 문제를 해결해야 한다. 

첫째는 makeNewConnection() 메소드가 문제이다. 

N사나 D사에서 다른 메소드 이름을 사용한다면 UserDao의 생성자에서는 물론 add(), get()  안에서의 코드를 매번 바꿔줘야 한다. 

둘째는 DB 커넥션을 제공하는 클래스가 어떤 것인지 UserDao가 구체적으로 알고 있어야한다는 점이다. 

UserDao에서 SimpleConnectionMaker라는 클래스타입의 인스턴스 변수까지 정의하고 있으니 만약 N사나 D사에서 다른 (이름의) 클래스를 구현하면 어쩔 수 없이 UserDao자체를 다시 수정해야한다. 

   근본적인 원인은 UserDao가 바뀔 수 있는 정보, 즉 DB 커넥션을 가져오는 클래스에 대해 너무 많이 알고 있기 때문이다. 

어떤 클래스가 쓰일지(이름이 뭔지), 그 클래스에서 커넥션 정보를 가져오는 메소드의 이름이 무엇인지 모두를 알고 있어야 한다. 다시말해 UserDao는 DB 커넥션을 가저오는 구체적인 방법에 종속되어 버린다. 





1.3.2 인터페이스 도입

1. 구조 <그림 1-4> 75.p  사실 진짜 구조는 <그림 1-5> 이다.

2. 두 개의 클래스가 서로 긴밀하게 연결되어 있지 않도록 중간에 추상적인 느슨한 연결고리를 만들어주는 것이다.

3. 추상화란 어떤 것들의 공통적인 성격을 뽑아내어 이를 따로 분리해내는 작업이다. 

자바는 이를 인터페이스라는 도구를 제공한다. 인터페이스는 자신을 구현한 클래스에 대한 구체적인 정보는 모두 감춰버린다.  

-> 그래서 그동안 클래스와 클래스를 직접 연결하지 않고 중간에 인터페이스로 연결했던 거구나 !

결국 이전에는 오브젝트를 만들려면 구체적인 클래스 하나를 선택해야했지만 인터페이스로 추상화 해놓은 최소한의 통로를 통해 접근하는 쪽에서는 오브젝트를 만들때 사용하는 클래스가 무엇인지 몰라도 된다. 인터페이스를 통해 접근하게 되면 실제 구현 클래스를 바꿔도 신경 쓸 일이 없다.

4. 과정 

1) 인터페이스 생성 

2) 고객사의 DB 커넥션을 담을 전용 class 생성

3) 인터페이스를 구현한 고객사 DB 커넥션 class를 생성하여  DB 커넥션 코드 작성 

4) UserDao에 인터페이스 오브젝트 선언 및 고객사 DB 커넥션 class의 메소드를 통해 DB 커넥션 

5) User.class의 main()에 UserDao 오브젝트 정의 및  프로젝트 실행하기위해 insert 되는 데이터의 PK값을 조정

export_1.3.2 인터페이스 도입 :  study_spring190220_2.zip


   1) 왼쪽 프로젝트 목록 영역 내 -> 빈 공간에서 오른쪽마우스 클릭 ->  
import -> Existing Projects into Workspace -> 다운로드한 파일 선택 

   2) 혹시, 이전 포스팅 파일 import 하신 분은 이전 프로젝트를 삭제하시거나 이름을 바꾸신 후 다시 import 하셔야 합니다. 



+ 하지만(이슈 추가요~)  

아래와 같이 본 프로젝트의 구성을 살펴보면 

클래스 사이에 불필요한 의존관계를 갖고 있는 구조

인터페이스를 적용한 후에도 UserDao 생성자에는 여전히 N사나 D사에서 선언한 클래스 이름이 들어갈 수 밖에 없는 구조이다. 

connectionMaker = new DConnectionMaker();

코드는 짧지만 이 코드가 의미하는 것은 UserDao 가 어떤 ConnectionMaker 구현 클래스의 오브젝트를 이용하게 할지를 결정하는 것이다. 다시말해 UserDao 와 UserDao 가 사용할 onnectionMaker의 특정 구현 클래스 사이의 관계를 설정해주는 것에 대한 관심이다. 



1.3.3 관계설정 책임의 분리 - 클라이언트와 서비스를 분리하는 과정 


1. 클래스 사이의 관계가 만들어 진다는 것은 한 클래스가 인터페이스 없이 다른 클래스를 직접 사용한다는 뜻이다. 따라서 클래스가 아니라 오브젝트와 오브젝트 사이의 관계를 설정해줘야한다.


2. 오브젝트 사이의 관계는 런타임 시에 한 쪽이 다른 오브젝트의 레퍼런스를 갖고 있는 방식으로 만들어진다.

connectionMaker = new DConnectionMaker();

= DConnectionMaker의 오브젝트의 레퍼런스를 UserDao의 connectionMaker 변수에 넣어서 사용하게 함으로써, 이 두개의 오브젝트가 ‘사용’이라는 관계를 맺어준다.

오브젝트 사이에 관계가 만들어지려면 일단 만들어진 오브젝트가 있어야하는데 이처럼 직접 생성자를 호출해서 사용하는 방법도 있지만 외부에서 만들어 준 것을 사용하는 방법도 있다.

오브젝트는 얼마든지 메소드 파라미터 등을 이용해 전달할 수 있으니 외부에서 만든 걸 가져올 수도 있다. 외부에서 만든 오브젝트를 전달받으려면 메소드 파라미터나 생성자 파라미터를 이용하면 된다. 


3. 클래스 사이에 관계가 만들어진 것과 단지 오브젝트 사이에 다이내믹한 관계가 만들어지는 것의 차이를 잘 구분할 수 있어야 한다. 

클래스 사이의 관계는 코드에 다른 클래스 이름이 나타나기 때문에 만들어지는 것이다. 

하지만 오브젝트 사이의 관계는 그렇지 않다. 코드에서는 특정 클래스를 전혀알지 못하더라도 그 특정 클래스가 구현한 인터페이스를 사용했다면 그 클래스의 오브젝트를 인터페이스 타입으로 받아서  사용할 수 있다. 바로 객체지향 프로그램에서는 다형성이라는 특징이 있기 때문이다. 

따라서 UserDao 오브젝트가 의 오브젝트를 사용하게 하려면 두 클래스의 오브젝트 사이에 런타임 사용관계 또는 링크, 또는 의존관계라고 불리는 관계를 맺어주면 된다. 


4. 결론. 클라이언트의 역할 만들기  

1) 구조 변경

사실, 앞에 언급한 <그림 1-4>와 같은 구조의 클래스들을 이용해 <그림 1-6>과 같은 런타임 오브젝트 관계를 갖는 구조로 만들어준 게 바로 클라이언트의 책임이다.


2) 구성에 재반영

클라이언트 자신이 UserDao를 사용해야하는 입장이기 때문에 그동안 UserDao 생성자가 했던 ‘자신이 사용할 오브젝트를 직덥 만들어서 자신과 관계를 만들어버리는 것’에 대한 관심을 분리해서 클라이언트에게 떠념겨보자. 이를 위해 UserDao에 있던 main()을 새로운 UserDaoTest.class를 만들어 옮겨 보자. 

그리고 UserDao에 있는 생성자를 수정하여 클라이언트가 미리 만들어 준 ConnectionMaker의 오브잭트를 전달받을 수 있도록 파라미터를 하나 추가 한다. 드디어 <그림 1-4>와 같은 구조를 기반으로 <그림 1-7>와 같은 구조가 만들어졌다. 

관계 설정 책임을 담당한 클라이언트가 추가된 구성


-> 기승전, 관계설정을 담당하는 클라이언트를 생성한 것이다.


3) 코딩 결과 

 export_1.3.3 관계설정 책임의 분리 : study_spring190220_3.zip

   1) 왼쪽 프로젝트 목록 영역 내 -> 빈 공간에서 오른쪽마우스 클릭 ->  import -> Existing Projects into Workspace -> 다운로드한 파일 선택


   2) 혹시, 이전 포스팅 파일 import 하신 분은 이전 프로젝트를 삭제하시거나 이름을 바꾸신 후 다시 import 하셔야 합니다.





1.3.4 원칙과 패턴

1. 객체지향 설계 원칙 SOLID

 단일 책임원칙     SRP The Single Responsibility Principle

 개방 폐쇄원칙     OCP Open Close Principle 

 리스코프 치환 원칙     LSP The Lishov Substitution Principle

 인터페이스 분리 원칙   ISP The Interface Segregation Principle

 의존관계 역전 원칙     DIP The Dependency Inversion Principle


2. 저자는 이 부분에서 초난감 DAO 개선 과정을 통해 알게 모르게 활용해온 개방 폐쇄의 원칙에 대해 구체적으로 설명한다.

1) 클래스나 모듈은, 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다. 

2) 인터페이스를 사용해 확장 기능을 정의한 대부분의 api는 이 원칙을 따른다고 볼 수 있다.

3) 높은 응집도와 낮은 결합도를 가져야한다. 

- 응집도 : 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어 있다. 

- 결합도 : 클래스 레벨뿐만아니라 패키지, 컴포넌트, 모듈에 이르기까지 그 대상의 크기가 달라도 동일한 원리로 적용할 수 있다.


댓글