티스토리 뷰

웹/Spring

스프링 볶음밥 - 8, 9장

세댕댕이 2022. 7. 12. 19:00

 

 

 

 

[이전 글]

더보기

볶음밥 1장 - 1,2,3: 자바빈, 디자인 패턴(템플릿 메소드, 팩토리 메소드, 전략 패턴), 관심사의 분리, SOLID 및 객체지향 약간

볶음밥 1장 - 4,5,6: 제어의 역전, 프레임워크 vs 라이브러리, 스프링 IoC 및 용어 정리, 싱글톤, 동일성 vs 동등성, 빈의 스코프

볶음밥 1장 - 7: 의존관계 주입(DI), DL, IoC

볶음밥 2장: 테스트, TDD, jUnit

볶음밥 3장: 템플릿/콜백

볶음밥 4장: 예외

볶음밥 5장: 서비스 추상화, 테스트 대역

볶음밥 6장 - 1: AOP (1)

볶음밥 6장 - 2: AOP (2)

볶음밥 7장

 

 

[8장] 스프링이란 무엇인가?

무려 713페이지 동안 스프링에 대해 배워왔다. 그래서, 스프링이란 무엇인가??

 

자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크

 

1. 애플리케이션 프레임워크

애플리케이션 프레임워크는 특정 계층이나 기술, 업무 분야에 국한되지 않고 애플리케이션의 전 영역을 포괄하는 범용적인 프레임워크이다. 애플리케이션 개발의 전 과정을 빠르고 편리하며 효율적으로 진행하는데에 일차적인 목표를 둔다.

- MVC, Ioc/DI, AOP 등등 애플리케이션 전 계층과 전 영역에 전략 및 기능을 제공해줌으로써 편리한 개발을 지원.

 

2. 경량급 (lightweight)

- 스프링 프레임워크 자체가 가볍다거나 작은 규모의 코드로 구성되어 있다는 것은 아니다. 기능이 부실하다는 것도 전혀 아니다.

- 단지 스프링 등장 이전에 사용되던 EJB와 같은 애들에 비해서는 선녀라는 뜻으로... 군더더기 없이 빠르고 간편하게 개발할 수 있도록 해준다는 것에서 경량급이라고 붙음.

- 가장 단순한 서버 환경인 Tomcat에서도 완벽하게 동작

 

3. 자바 엔터프라이즈 개발을 편하게

- 개발자가 복잡하고 실수하기 쉬운 로우레벨 기술까지 신경쓰지 않고서도 비즈니스 로직을 효과적으로 구현할 수 있도록 해준다.

 

 

스프링 창시자 로드 존슨좌... 그저 대단하다..

 

 

[문제점 및 스프링의 전략]

문제점 1. 기술에 대한 접근방식이 일관성이 없고, 특정 환경에 종속적이다

<전략>

1. 서비스 추상화를 통한 기술에 독립적인 개발

- 기술적 복잡함을 추상화를 통해 로우레벨의 기술 구현 부분과 기술을 사용하는 인터페이스를 분리하고, 환경과 세부 기술에 독립적인 접근 인터페이스를 제공하는 방식으로 진행

2. 템플릿/콜백 패턴을 이용한 반복적인 작업흐름 및 API 사용 코드 분리

 

문제점 2. 기술적인 처리를 담당하는 코드가 성격이 다른 코드에 섞여서 등장한다.

<전략>

1. AOP.

- 애플리케이션 로직을 담당하는 코드에 남아있는 기술 관련 코드를 깔끔하게 분리하고 별도의 모듈로 관리하도록 한다.

 

 

= 객체지향 프로그래밍 기법과 자바를 이용해 까다로운 비즈니스 로직을 구현한다.

스프링은 여기서 자바가 객체지향 언어의 장점을 제대로 살리지 못하게 방해했던 요소를 제거해주는 역할을 해주는 것.

 

스프링의 모토는 바로 "기본으로 돌아가자"는 것.

- 자바의 기본인 객체지향에 충실한 설계가 가능하도록.

-> 바로 >>>> DI <<<< 를 통해!!

서비스 추상화, 템플릿/콜백, AOP와 같은 스프링 기술은 모두 DI를 기반으로 만들어진 기술. DI 없는 스프링이란 있을 수 없어

결국 객체지향과 DI는 빼놓을 수 없는 관계.

 

 

[POJO 프로그래밍]

POJO(Plain Old Java Object) = 단순한 자바 오브젝트 = "단순하게 가자"

-> 단순한 자바의 오브젝트를 이용해 애플리케이션의 비즈니스 로직을 구현하자.

 

스프링 애플리케이션은 POJO를 이용해 만든 애플리케이션 코드

+ POJO가 어떻게 관계를 맺고 동작하는지를 정의해둔 설계정보로 구분된다.

 

<POJO를 위한 조건>

1. 특정 규약에 종속되지 않는다

- 자바 언어 및 꼭 필요한 API 이외에는 종속되지 않아야 한다. 자유로운 객체지향 설계가 가능한 오브젝트만이 POJO.

 

2. 특정 환경에 종속되지 않는다

- 특정 서버 환경, 특정 벤더 기술에 종속되지 않는다

 

3. 객체지향적인 자바 언어의 기본에 충실해야 한다.

- 특정 기술 규약 및 환경에 종속되지 않는다고 전부 POJO는 아니다

- 진정한 POJO란, 객체지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고, 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 뜻한다.

 

POJO에다가 애플리케이션 핵심 로직 및 기능을 담아 설계하고 개발하는 방법을 POJO 프로그래밍이라고 한다.

- 스프링 IoC/DI, AOP, PSA = 애플리케이션을 POJO로 개발할 수 있게 해주는 가능기술(enabling tech-)

 

POJO 프로그래밍의 장점

- 특정한 기술과 환경에 종속되지 않는 오브젝트로 인해 깔끔한 코드 작성 가능.

- 자동화된 테스트에 매우 유리 (환경의 제약이 없기 때문

- 객체지향적인 설계를 자유롭게 적용 가능

 

스프링 프레임워크는 POJO 프로그래밍이 가능하도록 기술적 기반을 제공하는 대표적인 POJO 프레임워크.

 

[ IoC/DI ]

왜 두 개의 오브젝트를 분리해서 만들고, 인터페이스를 둬서 느는하게 연결한 뒤 실제 사용할 대상은 DI를 통해 외부에서 지정하는 것일까? 직접 자신이 사용할 오브젝트를 new 키워드로 생성하는 것보다 나은점이 무엇일까?

=> "유연한 확장이 가능하도록 하기 위해서"

 

DI는 OCP라는 객체지향 원칙으로 잘 설명될 수 있다.

확장에는 열려있고 (유연한 확장), 변경에는 닫혀있다. (재사용 가능)

 

A -> B라는 의존관계를 갖는 오브젝트 구조가 있을 때, 확장이란 B가 자유롭게 변경될 수 있다는 것을 의미한다.

이는 B가 변경되어도 A에게는 아무런 영향을 받지 않고 그대로 유지 가능하다는 것.

= B의 관점에서는 유연한 확장이고, A의 관점에서는 변경 없는 재사용이다.

 

<< DI의 활용 방법 >>

1. 핵심 기능의 변경

- 의존 대상의 구현을 바꾸는 것. (디자인 패턴 중 전략 패턴)

- A -> B 구조에서 A의 기능 일부를 B에게 위임한다고 했을 때, B의 구현 방식을 필요에 따라 B1, B2, B3으로 바꾸는 것.

- 실제 의존하는 대상이 가진 핵심 기능을 DI 설정을 통해 변경할 수 있다.

 

2. 핵심 기능의 동적인 변경

- 1번과 비슷. 의존 오브젝트의 핵심 기능 자체를 바꾸는 것이나, 일반적인 DI와는 달리 동적으로 변경할 수도 있다

- DI는 기본적으로는 런타임 시에 동적으로 의존 오브젝트를 연결해 주는 것이긴 하지만, 일단 한번 DI되고 나면 그 이후로는 바뀌지 않는다. 한번 동적으로 DI되면 바뀌지 않는 정적인 관계를 맺어주는 것이다.

- 하지만 DI를 잘 활용하면, 애플리케이션이 동작하는 중간에도 그 의존 관계를 동적으로 변경할 수 있다.

ex) 사용자의 등급에 따라 DAO를 다르게 설정 등...

(기술적으로만 보자면 다이내믹 라우팅 프록시나 프록시 오브젝트 기법을 활용해서 가능한 것.)

 

3. 부가 기능의 추가

- 데코레이터 패턴과 같이 인터페이스를 두고 실제로 사용하게 하고, 실제 사용할 오브젝트는 외부에서 주입하는 DI를 적용해두는 방식으로 부가기능을 추가시킬 수 있다.

- 핵심기능과 클라이언트 코드에는 전혀 영향을 주지 않으면서도 부가기능을 추가할 수 있었다.

 

4. 인터페이스의 변경

A가 C 오브젝트를 사용하려고 하는데, A는 원래 B 인터페이스를 사용하도록 만들어져 있고 C는 B 인터페이스를 구현하지 않았다고 했을 때, A가 DI를 통해 B의 구현 오브젝트를 받도록 만들어져 있었다면 B 인터페이스를 구현했으면서 내부에서 C 를 호출해주는 기능을 가진 "어댑터 오브젝트"를 만들어 A에 DI 해줌으로써 사용할 수 있다 

- A --> B(C로 위임) --> C

- 어댑터라는 말과 같이 인터페이스가 다른 오브젝트를 클라이언트가 사용하는 인터페이스로 바꿔주는 기능을 만들어줄 수 있다.

 

5. 프록시

- 프록시가 가능한 이유는 바로 DI가 있기 때문

- 지연 로딩(Lazy Loading)을 위해서는 프록시가 꼭 필요.

 

6. 템플릿/콜백

- 반복적으로 등장하지만 항상 고정적인 작업 흐름과 그 사이에서 자주 바뀌는 부분을 분리해서 템플릿과 콜백으로 만들고, 이를 DI 원리를 통해 간결하게 만들 수 있었다.

- 콜백을 얼마든지 만들어서 사용할 수 있기에 개방을 통한 유연한 확장이 가능한 것

- 템플릿은 한번만 만들어두면 얼마든지 재사용이 가능하기에 확장에도 변하지 않는다는 것. 

-> OCP 원칙에 찰떡

 

7. 싱글톤과 오브젝트 스코프

- DI가 중요한 이유 중 한가지 = DI 할 오브젝트의 생명주기를 제어할 수 있다.

- DI를 프레임워크로 이용한다는 것은 DI 대상 오브젝트를 컨테이너가 관리한다는 뜻이다. 오브젝트의 생성부터 관계설정, 이용, 소멸에 이르는 전 과정을 DI 컨테이너가 주관하기에 그 오브젝트의 스코프를 자유롭게 제어할 수 있다.

- 대표적인 스코프가 바로 "싱글톤"

 

8. 테스트

- 여러 오브젝트와 협력해서 동작하는 오브젝트를 효과적으로 테스트 하기 위해서는 가능한 한 고립시키고, 단위테스트를 만드는 것이다. 

- 이 때 DI를 통해 의존 오브젝트 대신 테스트 대역(스텁 / 목 오브젝트)을 넣어줌으로써 의존 오브젝트가 있더라도 단위테스트를 가능하게 해준다.

 

 

[ AOP ]

- AOP(관점 지향 프로그래밍)와 OOP(객체 지향 프로그래밍)는 서로 배타적인 개념이 아니다. AOP는 객체지향 기술의 한계와 단점을 극복하도록 도와주는 보조적인 프로그래밍 기술이다.

 

<적용 방법>

1. 스프링 AOP (다이내믹 프록시를 이용하는 방법)

- 데코레이터 패턴을 응용한 것. 

- 부가기능을 부여할 수 있는 곳이 메소드의 호출이 일어나는 지점 뿐이라는 제약조건

 

2. AspectJ (바이트코드 변경(위빙, Weaving)을 통한 강력한 방법)

- 프록시 방식 AOP에서는 불가능한 다양한 조인 포인트 제공.

- JDK만으로는 불가능하고 별도의 AOP 컴파일러를 이용한 빌드 혹은 바이트코드 조작(=위빙)이 필요.

 

<적용 단계>

1. 미리 준비된 AOP 이용

- 스프링이 미리 만들어서 제공하는 AOP 기능을 그대로 가져다 쓴다 (@Transactional 등..)

 

2. 전담팀을 통한 정책 AOP 적용

- AOP 담당 관리자 관리 하에 적용

- 비즈니스 로직 오브젝트에 대한 보안, 로깅, 트레이싱, 성능 모니터링 등 정책적으로 적용할만한 기능에 AOP 도입

 

3. 자유로운 AOP 이용

- AOP에 완전 숙달되었다 싶으면 개발자 개개인이 AOP를 활용한다

- 타인이 만든 코드에 몰래 적용되는 AOP 기능을 만들지 말라

 

 

[9장] 스프링 프로젝트 시작하기

[라이브러리 관리의 어려움]

개발에 사용되는 라이브러리의 종류가 상당히 많고, 또 라이브러리마다 버전도 제각각이다

-> 라이브러리의 종류와 버전을 적절히 선정하고 개발하면서 추가적으로 필요로 하는 라이브러리를 추가/제거 하는 관리 작업은 보통 쉬운 일이 아니다.

 

그래서 사용하는 것이 바로 빌드 툴(Maven, Gradle...) 의존 라이브러리 관리 기능.

- Maven은 단순 빌드 툴을 넘어서 개발 과정에서 필요한 빌드, 테스트, 배치, 문서화, 리포팅 등 다양한 작업을 지원하는 종합 프로젝트 관리 툴의 성격을 띈다. 

- POM이라고 불리는 프로젝트 모델 정보를 이용, 미리 정해진 절차에 따라 프로젝트 관리 작업을 수행 (그래서 pom.xml)

- 애플리케이션이 필요로 하는 의존 라이브러리를 선언해두기만 하면, 원격 서버에서 이를 자동으로 다운로드 받아서 사용할 수 있게 해준다. (선언적 성격)

+ 전이적(Transitive) 의존 라이브러리 추적 기능에 의해, POM 의존정보에 하나의 라이브러리를 지정하면, 지정한 라이브러리가 동작하는데 필요한 여타 다른 라이브러리들까지 같이 다운로드 해주는 기능을 갖고있다.

 

 

[애플리케이션 아키텍쳐]

아키텍쳐: 어떤 경계 안에 있는 내부 구성요소들이 어떤 책임을 갖고 있고, 어떤 방식으로 서로 관계를 맺고 동작하는지를 규정하는 것.

- 단순히 정적인 구조를 떠나 그 구조에서 일어나는 동적인 행위와 깊은 관계가 있다.

- 관심, 책임, 성격, 변하는 이유와 방식이 다른 것을을 분리함으로써 응집도는 높이고 서로의 결합도는 낮추는 것이 좋다고 했다. 이는 아키텍쳐 레벨에서도 동일하게 적용된다. 성격과 책임이 다른 것은 분리하자.

 

1. 계층형 아키텍쳐 (3계층 아키텍쳐와 수직 계층)

책임과 성격이 다른 것을 크게 그룹으로 만들어 분리해두는 것을 아키텍쳐 차원에서는 계층형(Layered) 아키텍쳐라 한다.

일반적으로 웹 기반 애플리케이션은 세 개의 계층을 가져 "3-Layer 애플리케이션"이라고도 한다.

 

클라이언트 <--> 프레젠테이션 계층 <--> 서비스 계층 <--> 데이터 액세스 계층 <--> DB/레거시

 

* 데이터 액세스 계층 (DAO 계층)

- DAO 패턴을 보편적으로 사용하는 계층. 

- 장기적인 데이터 저장을 목적으로 하는 DB 이용을 주된 책임으로 갖는다.

- 외부 시스템을 호출해서 서비스를 이용하기 때문에, 기반(Infrastructure) 계층이라 분류되기도 한다.

- 계층 내부에서도 다시 사용 기술에 따라 세분화된 계층으로 구분할 수 있다. (추상화 레벨에 따라 수직적으로 분류)

 

* 서비스 계층

- 구조로만 보면 가장 단순한 계층이며, 이상적인 POJO로 작성되는 것이 바람직하다.

- DAO 계층을 호출하고, 이를 활용해서 만들어진다.

- 이상적인 서비스 계층은, 백엔드 시스템과 연결되는 데이터 액세스 계층이 바뀌고, 클라이언트와 연결되는 프레젠테이션 계층이 모두 바뀌어도 그대로 유지될 수 있어야 한다.

- 애플리케이션에서 가장 중요한 자산이 바로 도메인의 핵심 비즈니스 로직이 담겨있는 서비스 계층이어야 한다

 

* 프레텐테이션 계층

- 가장 복잡한 계층. 매우 다양한 기술과 프레임워크의 조합을 갖는다.

- 클라이언트의 종류에 상관없이 HTTP 프로토콜을 사용하는 서블릿의 바탕이 된다.

- 화면 흐름을 결정하고 사용자 입력값에 대한 검증, 서비스 계층의 호출과 전달되는 값의 포맷의 변화, 뷰 생성 등...

 

계층 설계도 응집도는 높게, 타 계층과 결합도는 낮게 설계해야 한다.

= 각 계층은 자신의 계층의 책임에만 충실해야 한다.

 

* 프레젠테이션 계층의 오브젝트를 그대로 서비스 계층으로 전달하지 마라

- 계층의 경계를 넘어갈 때는 반드시 특정 계층에 종속되지 않는 오브젝트 형태로 변환해 줘야한다.

- HttpServletRequest/Response/Session 등.. 근데 여기에 DTO가 포함되는지는 아리까리하다. 

 

* 계층 사이의 호출은 인터페이스를 통해 이뤄져야 한다

- 귀찮다고 그냥 클래스로 때려박지 마라...;;;

- 계층 레벨에 정의된 인터페이스를 통해서 요청하고, 계층 간에 사용되는 인터페이스 메소드는 특정 계층의 기술이 최대한 드러나지 않게끔 만들어라. (계층 간의 기술이나 역할이 서로 침범하지 않도록 해라)

- 인터페이스를 사용하라는 것이 그저 클래스의 모든 public 메소드를 다 집어넣으라는 의미가 아니다. 한번 정의되고 사용되기 시작한 인터페이스를 수정하는 것은 매우 까다롭다. 다른 계층에서 꼭 필요한 메소드만 노출해라

 

* 중간 계층을 건너뛰어서 관계를 갖지 않는 계층의 빈을 직접 DI 해서 사용하지 말자

 

 

[애플리케이션 정보 아키텍쳐]

앤터프라이즈 시스템은 본질적으로 동시에 많은 작업이 빠르게 수행되어야 한다.

-> 대개 사용자의 요청을 처리하는 동안에만 간단한 상태를 유지하고 바로 끊어버린다 (무상태, Stateless) 

 

애플리케이션의 주요 상태정보는 주로 DB와 같은 시스템에 저장되고, 하나의 업무 작업이 여러번의 요청과 페이지에 걸쳐서 일어나는 경우에는 클라이언트에 일시적으로 보관하거나, 서버의 사용자별 세션 메모리에 저장하기도 한다.

이와 같이 애플리케이션을 사이에 두고 흘러다니는 정보를 어떤 식으로 다룰지를 결정하는 것도 아키텍쳐 결정에 중요한 기준이 된다.

-> 애플리케이션에 존재하는 정보를 단순히 데이터로 다루는 경우 vs 오브젝트로 다루는 경우.

 

1. 데이터 중심 아키텍쳐

- 애플리케이션에 흘러다니는 정보를 단순히 값이나, 값을 담기 위한 목적의 오브젝트 형태로 취급하는 구조.

- DB나 백엔드 시스템에서 가져온 정보를 값으로 다루고, 그 값을 취급하는 코드를 만들어 로직을 구현, 값을 그대로 프레젠테이션 계층의 뷰와 연결하는 방식.

- DB/SQL 중심의 로직 구현 방식

- 하나의 업무 트랜잭션에 모든 계층의 코드가 종속되는 경향이 있다. 대부분 재사용이 불가능하고, 독립적인 테스트도 어렵다.

- 변화에 취약하다. 객체지향의 장점이 별로 활용되지 못하고 각 계층의 코드가 긴밀하게 연결된다. = 비추

 

2. 오브젝트 중심 아키텍쳐

- 도메인 모델을 반영하는 오브젝트 구조를 만들어두고 그것을 각 계층 사이에서 정보를 전송하는데 사용하는 것

- 객체지향 분석과 모델링의 결과로 나오는 도메인 모델을 오브젝트 모델로 활용한다. (대개 도메인 모델은 DB의 엔티티 설계에도 반영되기에 DB의 엔티티 구조와 유사한 형태를 갖는다)

- 애플리케시연 어디에서나 사용될 수 있는 일관된 형식의 도메인 정보를 이용. 

- 이해하기 쉬운 코드, 로직 작성 수월, 재사용성 향상, 효율적인 개발. 그냥 이걸 써라는 뜻.

 

* 단점: 하나의 오브젝트에 담긴 필드의 개수가 많아지다보면 그중에는 드물게 사용되는 필드가 발생하기 마련이다. 하지만 DAO는 어떤 비즈니스 로직에서 어떤 필드를 사용할 것인지를 미리 알지 못하기에 모든 필드를 다 채워서 전달할 수밖에 없다. 조금 낭비가 생길 수 밖에 없음.

+ 지연 로딩 기법을 활용하면 최소한의 오브젝트 정보만 읽어두고 관계하고 있는 오브젝트가 필요한 경우에만 다이내믹하게 DB에서 읽어오도록 할 수 있다.

- 가장 이상적인 방법은 JPA와 같은 ORM 기술을 사용하는 것.

 

도메인 오브젝트는 자바 오브젝트다. 오브젝트는 원래 데이터를 저장하기 위해서만 사용하는 것이 아니다. 내부의 정보를 이용하는 기능도 함께 가지고 있어야 한다. 

- 클래스는 속성과 행위의 조합이다. 필드와 게터/세터만 존재하는 오브젝트는 그저 반쪽짜리일 뿐이다. 

 

* 빈약한(anemic) 오브젝트: 도메인 오브젝트에 정보만 담겨있고, 정보를 활용하는 아무런 기능도 갖고있지 않는 경우.

<-> 풍성한 도메인 오브젝트: 도메인 안에 로직을 담아둠으로써 서비스 계층에 메소드를 만드는 것 보다 응집도 높은 설계

 

* 도메인 오브젝트 안에 비즈니스 로직을 포함한다고 해서 서비스 계층이 사라지는 것이 아니다

- 도메인 오브젝트 안에 메소드로 들어가는 로직들은 대부분 해당 오브젝트나 긴말한 연관관계를 맺고있는 관련 오브젝트의 정보나 기능만을 활용하도록 한다.

- 도메인 오브젝트는 DAO 오브젝트를 DI받을 수 없다. 도메인 오브젝트가 스프링 빈이 아니니 때문.

- 자세한 내용은 도메인 주도 설계...를 참고

 

* 도메인 오브젝트를 프레젠테이션 계층이나 뷰에서 사용하게 하지 마라

- 도메인 오브젝트는 도메인 계층을 벗어나지 못하게 만들자. 

- 도메인 계층 밖으로 전달될 때에는 정보 전달용 오브젝트, DTO(Data Transfer Object)에 담아 전달해라

- DTO는 기능을 갖지 않으므로 사용하기에 안전하다.

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함