공부한것들을 정리하는 블로그 입니다.
토비의스프링3.1 1장. 오브젝트와 의존관계 본문
1장 오브젝트와 의존관계
1.1 초난감 DAO
- DAO(Data Access Object) 는 DB 를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트를 말한다.
- JDBC 를 이용한 등록과 조회 기능이 있는 날 것 그대로의 UserDao 클래스를 만든다.
1.2 DAO 의 분리
- 관심사의 분리(Separation of Concerns): 관심이 같은 것끼리는 하나의 객체 안으로 또는 친한 객체로 모이게 하고, 관심이 다른 것은 가능한 한 따로 떨어져서 서로 영향을 주지 않도록 분리하는 것.
- 중복 코드의 메소드 추출하기 : 관심이 다른 코드가 있는 메소드에는 영향을 주지도 않을뿐더러, 관심 내용이 독립적으로 존재하므로 수정도 간단해졌다.
- 상속을 통한 확장 : 클래스 계층 구조를 통해 두 개의 관심이 독립적으로 분리되면서 변경 작업은 한층 용이해졌다.
- 템플릿 메소드 패턴 : 슈퍼클래스에 기본적인 로직의 흐름(커넥션 가져오기, SQL 생성, 실행, 반환 등..)을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법을 디자인 패턴에서 템플릿 메소드 패턴이라고 한다.
- 팩토리 메소드 패턴 : 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것을 팩토리 메소드 패턴 이라고 부르기도 한다.
- 문제점
- 상속을 사용했기 때문에 다중 상속이 불가능하고, 후에 다른 목적으로 UserDao 에 상속을 적용하기 힘들다.
- 상속관계는 두 가지 다른 관심사에 대해 긴밀한 결합을 허용한다. 서브클래스는 슈퍼클래스의 기능을 직접 사용할 수 있다. 그래서 슈퍼클래스 내부의 변경이 있을 때 모든 서브클래스를 함께 수정하거나 다시 개발해야 할 수도 있다.
1.3 DAO 의 확장
- 클래스 분리 : 관심사가 다르고 변화의 성격이 다른 이 두 가지 코드를 각각 분리한다.
- 문제점
- DB 커넥션을 제공하는 클래스가 어떤 것인지를 UserDao 가 구체적으로 알고 있어야 한다.
- 만약 커넥션을 제공하는 다른 클래스를 사용하고 메소드 명이 다를 경우 add(), get() 메소드의 커넥션을 가져오는 코드를 바뀐 메소드명으로 다 수정해줘야 한다.
- 문제의 근본적인 원인은 UserDao 가 바뀔 수 있는 정보, 즉 DB 커넥션을 가져오는 클래스에 대해 너무 많이 알고 있기 때문이다.
- 인터페이스 도입
- 두 개의 클래스가 서로 긴밀하게 연결되어 있지 않도록 중간에 추상적인 느슨한 연결고리를 만들어 준다.
- 인터페이스는 자신을 구현한 클래스에 대한 구체적인 정보는 모두 감춰 버리기 때문에 추상화 해놓은 최소한의 통로를 통해 접근하는 쪽에서는 오브젝트를 만들 때 사용할 클래스가 무엇인지 몰라도 된다.
- 문제점
- 초기에 한 번 어떤 클래스의 오브젝트를 사용할지를 결정하는 생성자의 코드는 제거되지 않고 남아 있다.
UserDao 가 인터페이스가 아니라 구체적인 클래스까지 알아야 한다.
- 관계설정 책임의 분리
- 클래스 사이에 관계가 만들어 지는것이 아니고, 단지 오브젝트 사이에 다이내믹한 관계가 만들어 지도록 한다.
- 클래스 사이의 관계는 코드에 다른 클래스 이름이 나타나기 때문에 만들어지는 것이다. 하지만 오브젝트 사이의 관계는 그렇지 않다. 코드에서는 특정 클래스를 전혀 알지 못하더라도 해당 클래스가 구현한 인터페이스를 사용했다면, 그 클래스의 오브젝트를 인터페이스 타입으로 받아서 사용할 수 있다.
- 서비스 오브젝트의 생성자를 인터페이스 타입으로 받도록 수정하고, 구현체를 생성하는 책임은 클라이언트 오브젝트로 넘긴다.
- 지금까지 개선하면서 사용한 객체지향의 기술들
- 개방 폐쇄 원칙(OCP, Open-Closed Principle): 깔끔한 설계를 위해 적용 가능한 객체지향 설계 원칙 중의 하나로, 간단히 정의하자면 ‘클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.’ 라고 할 수 있다.
- 높은 응집도와 낮은 결합도
- 변경이 일어날 때 모듈의 많은 부분이 함께 바뀐다면 응집도가 높다고 말할 수있다.
- 결합도가 높아지면 변경에 따르는 작업량이 많아지고, 변경으로 인해 버그가 발생할 가능성이 높아진다.
- 전략 패턴
- 자신의 기능 맥락(context)에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴이다.
- 간단히 말해서 객체가 할 수 있는 행위들 각각을 전략으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 행위의 수정이 가능하도록 만든 패턴이다.
- UserDao 는 전략 패턴의 컨텍스트에 해당한다. 컨텍스트는 자신의 기능을 수행하는데 필요한 기능 중에서 변경 가능한, DB 연결 방식이라는 알고리즘을 ConnectionMaker 라는 인터페이스로 정의하고, 이를 구현한 클래스, 즉 전략을 바꿔가면서 사용할 수 있게 분리 됐다.
1.4 제어의 역전(IoC)
- 제어의 역전이란 : 제어 흐름의 개념을 거꾸로 뒤집는 것이다. 제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지도, 생성하지도 않는다. 또 자신도 어떻게 만들어지고 어디서 사용되는지를 알 수 없다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하기 때문이다.
- 서블릿: 서블릿에 대한 제어 권한을 가진 컨테이너(tomcat, jetty 등등)가 적절한 시점에 서블릿 클래스의 오브젝트를 만들고 그 안의 메소드를 호출 한다.
- 템플릿 메소드 패턴: 제어권을 상위 템플릿 메소드에 넘기고 자신은 필요할 때 호출되어 사용되도록 한다는 제어의 역전 개념이 들어 있다.
- 어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴
- 즉, 전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화 할 때 유용하다.
- 프레임 워크
- 애플리케이션 코드가 프레임워크에 의해 사용된다. 프레임워크 위에 개발한 클래스를 등록해두고, 프레임워크가 흐름을 주도하는 중에 개발자가 만든 애플리케이션 코드를 사용하도록 만드는 방식이다.
- 애플리케이션 코드는 프레임워크가 짜놓은 틀에서 수동적으로 동작해야 한다.
- 라이브러리와 프레임워크는 다르다. 라이브러리를 사용하는 애플리케이션 코드는 애플리케이션이 흐름을 직접 제어한다.
1.5 스프링의 IoC
- 용어 정리
- 빈(bean)
- 빈 또는 빈 오브젝트는 스프링이 IoC 방식으로 관리하는 오브젝트라는 뜻이다. 관리되는 오브젝트라고 부르기도 한다.
- 스프링을 사용하는 애플리케이션에서 만들어지는 모든 오브젝트가 다 빈은 아니고 그중에서 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 빈이라고 부른다.
- 빈 팩토리(bean factory)
- 스프링의 IoC 를 담당하는 핵심 컨테이너를 가리킨다. 빈을 등록하고, 생성하고, 조회하고 돌려주고, 그 외에 부가적인 빈을 관리하는 기능을 담당한다.
- 보통은 이 빈 팩토리를 바로 사용하지 않고 이를 확장한 애플리케이션 컨텍스트를 이용한다.
- 애플리케이션 컨텍스트(application context)
- 빈 팩토리를 확장한 IoC 컨테이너다. 기본적인 기능은 빈팩토리와 동일하고 여기에 스프링이 제공하는 각종 부가 서비스를 추가로 제공한다.
- 빈 팩토리라고 부를 때는 주로 빈의 생성과 제어의 관점에서 이야기하는 것이고, 애플리케이션 컨텍스트라고 할 때는 스프링이 제공하는 애플리케이션 지원 기능을 모두 포함해서 이야기하는 것이라고 보면 된다.
- 컨테이너(container) 또는 IoC 컨테이너
- IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 또는 IoC 컨테이너라고도 한다.
- DaoFactory 를 스프링에서 사용하기
- 스프링이 빈 팩토리를 위한 오브젝트 설정을 담당하는 클래스라고 인식할 수 있도록 @Configuration 이라는 애노테이션을 추가한다.
- 오브젝트를 만들어주는 메소드에는 @Bean 이라는 애노테이션을 붙여준다
- 애플리케이션 컨텍스트 적용
- 기존의 DaoFactory 와 같은 팩토리 오브젝트에 대응되는 것이 스프링의 애플리케이션 컨텍스트다.
- DaoFactory 와 달리 직접 오브젝트를 생성하고 관계를 맺어주는 코드가 없고, 그런 생성정보와 연관관계 정보를 별도의 설정정보를 통해 얻는다.
- 애플리케이션 컨텍스트의 동작방식
- 애플리케이션 컨텍스트는 DaoFactory 클래스를 설정정보로 등록해두고 @Bean 이 붙은 메소드의 이름을 가져와 빈 목록을 만들어 둔다.
- 클라이언트가 애플리케이션 컨텍스트의 getBean() 메소드를 호출하면 자신의 빈 목록에서 요청한 이름이 있는지 찾고, 있다면 빈을 생성하는 메소드를 호출해서 오브젝트를 생성시킨 후 클라이언트에 돌려준다.
1.6 싱글톤 레지스트리와 오브젝트 스코프
- 오브젝트의 동일성과 동등성
- 자바에서는 두 개의 오브젝트가 완전히 같은 동일한(identical) 오브젝트라고 말하는 것과, 동일한 정보를 담고 있는(equivalent) 오브젝트라고 말하는 것은 분명한 차이가 있다.
- 전자는 동일성(identity)비교라고 하고, 후자를 동등성(equality) 비교라고 한다. 동일성은 == 연산자로, 동등성은 equals() 메소드를 이용해 비교한다.
- 스프링은 여러 번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 돌려준다.
- 스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.
- 애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이기도 하다.
- 싱글톤 레지스트리의 장점은 보통의 싱글톤 패턴에서와 같이 스태틱 메소드와 private 생성자를 사용해야 하는 비정상적인 클래스가 아니라 평범한 자바 클래스를 싱글톤으로 활용하게 해준다.
- 싱글톤 패턴 : 어플리케이션이 시작될 때 어떤 클래스가 최초 한번만 메모리를 할당하고(static) 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴이다. 객체의 인스턴스가 오직 1개만 생성되는 패턴. 주의해야 할 점은 싱글턴을 만들 때 동시성(Concurrency) 문제를 고려해서 설계해야 한다.
- 싱글톤과 오브젝트의 상태
- 기본적으로 싱글톤이 멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은 무상태(stateless) 방식으로 만들어져야 한다.
- 다중 사용자의 요청을 한꺼번에 처리하는 스레드들이 동시에 싱글톤 오브젝트의 변수를 수정하는 것은 매우 위험하다.
- 싱글톤은 기본적으로 인스턴스 필드의 값을 변경하고 유지하는 상태유지(stateful) 방식으로 만들지 않는다. 물론 읽기전용의 값이라면 초기화 시점에서 인스턴스 변수에 저장해두고 공유하는 것은 아무 문제가 없다.
- 스프링 빈의 스코프
- 싱글톤 스코프 : 컨테이너 내에 한 개의 오브젝트만 만들어져서, 강제로 제거하지 않는 한 스프링 컨테이너가 존재하는 동안 계속 유지된다.
- 프로토타입 스코프 : 싱글톤과 달리 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트를 만들어 준다.
- 그 외에 웹을 통해 새로운 HTTP 요청이 생길 때마다 생성되는 요청 스코프가 있고, 웹의 세션과 스코프가 유사한 세션 스코프도 있다.
1.7 의존관계 주입(DI)
- 의존관계 주입(DI, Dependency Injection) : 오브젝트 레퍼런스를 외부로부터 제공(주입)받고 이를 통해 여타 오브젝트와 다이내믹하게 의존관계가 만들어지는 것이 핵심이다.
- IoC 라는 용어는 매우 폭넓게 사용되는데, 스프링이 제공하는 IoC 방식을 좀 더 의도가 명확히 드러나는 의존관계 주입(DI, Dependency Injection)이라는 이름으로 부른다.
- 의존관계
- A 가 B 에 의존하고 있을때(A가 B를 알고있을때, 사용중일때), B 가 변하면 그것이 A 에 영향을 미친다. 반대로 B 는 A 에 의존하고 있지 않기 때문에 B 는 A 의 변화에 영향을 받지 않는다.
- 인터페이스에 대해서만 의존관계를 만들어두면 인터페이스 구현 클래스와의 관계는 느슨해지면서 변화에 영향을 덜 받는 상태가 된다. 결합도가 낮다고 설명할 수 있다.
- 클래스로부터 인터페이스를 분리 : 역할(IF)과 구현(Impl)을 분리
- 의존관계란 한쪽의 변화가 다른 족에 영향을 주는 것이라고 했으니, 인터페이스를 통해 의존관계를 제한해주면 그만큼 변경에서 자유로워 진다.
- 의존관계 주입
- DI 컨테이너에 의해 런타임 시에 의존 오브젝트를 사용할 수 있도록 그 레퍼런스를 전달받는 과정이 마치 메소드(생성자)를 통해 DI 컨테이너가 주입해주는 것과 같다고 해서 이를 의존관계 주입이라고 부른다.
- 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않아야 한다. 그러기 위해서는 인터페이스에만 의존하고 있어야 한다.
- 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.
- 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.
'Spring > 공부' 카테고리의 다른 글
토비의스프링3.1 5장. 서비스 추상화 (0) | 2022.07.23 |
---|---|
토비의스프링3.1 4장. 예외 (0) | 2022.07.19 |
토비의스프링3.1 3장. 템플릿 (0) | 2022.07.19 |
토비의스프링3.1 2장. 테스트 (0) | 2022.07.19 |
스프링과 싱글톤 컨테이너 (0) | 2022.06.16 |
객체 지향 설계와 스프링 (0) | 2022.04.24 |
Spring에서 @RequestParam과 @PathVariable (0) | 2019.12.19 |
Spring Boot에서 정적 요소(Js,html...) 변동 시 자동 반영 처리하기(with devtools, IntelliJ) (0) | 2019.11.19 |