├── 1장_오브젝트와_의존관계 ├── 1장_오브젝트와_의존관계 │ └── 오브젝트와 의존관계1_이형원.md ├── 1장_오브젝트와_의존관계_김석래.md ├── 1장_오브젝트와_의존관계_안송이.pdf ├── images │ └── user_dao_abstract.png ├── 오브젝트와 의존관계1_조수진.md └── 오브젝트와 의존관계2_조수진.md ├── 2장_테스트 ├── 2장_테스트_김석래.md ├── 2장_테스트_안송이.pdf ├── 2장_테스트_이형원.md └── images │ ├── _1.png │ ├── _10.png │ ├── _11.png │ ├── _12.png │ ├── _13.png │ ├── _2.png │ ├── _3.png │ ├── _4.png │ ├── _5.png │ ├── _6.png │ ├── _7.png │ ├── _8.png │ └── _9.png ├── 3장_템플릿 ├── 3장 템플릿_조수진.md ├── 3장_템플릿_안송이.pdf ├── 3장_템플릿_이형원.md ├── README.md └── images │ ├── 001.jpeg │ ├── 002.jpeg │ ├── 003.jpeg │ ├── 004.jpeg │ ├── 005.jpeg │ ├── 006.jpeg │ ├── 007.jpeg │ ├── 008.jpeg │ ├── 009.jpeg │ ├── 010.jpeg │ ├── 011.jpeg │ ├── 012.jpeg │ ├── 013.jpeg │ ├── 014.jpeg │ ├── 015.jpeg │ ├── 016.jpeg │ └── 017.jpeg ├── 4장_예외 ├── 4장 예외_조수진.md ├── 4장_예외_김석래.md ├── 4장_예외_안송이.pdf ├── 4장_예외_이형원.md └── images │ ├── 001.jpeg │ ├── 002.jpeg │ ├── 003.jpeg │ ├── 004.jpeg │ ├── 005.jpeg │ └── 006.jpeg ├── 5장_서비스_추상화 ├── 5장 서비스 추상화_조수진.md ├── 5장_서비스_추상화_김석래.md ├── 5장_서비스_추상화_안송이.pdf └── images │ ├── 001.jpeg │ ├── 002.jpeg │ ├── 003.jpeg │ ├── 004.jpeg │ ├── 005.jpeg │ ├── 006.jpeg │ ├── 007.jpeg │ └── 008.jpeg ├── 6장_AOP ├── 6장 AOP 1_조수진.md ├── 6장_AOP1_안송이.pdf ├── 6장_AOP2_안송이.pdf ├── 6장_AOP_김석래.md ├── 6장_이형원.md └── images │ ├── 001.jpeg │ ├── 002.jpeg │ ├── 003.jpeg │ ├── 004.jpeg │ ├── 005.jpeg │ ├── 006.jpeg │ ├── 007.jpeg │ ├── 008.jpeg │ ├── 009.jpeg │ ├── 010.jpeg │ ├── 011.png │ ├── 012.png │ └── 013.png ├── 7장_스프링_핵심_기술의_응용 ├── 7.md ├── 7장_스프링_핵심_기술의_응용1_안송이.pdf ├── 7장_스프링_핵심_기술의_응용2_안송이.pdf └── 7장_스프링_핵심_기술의_응용_김석래.md ├── 8장_스프링이란_무엇인가? ├── 8장_스프링이란_무엇인가_김석래.md └── 8장_스프링이란_무엇인가_안송이.pdf ├── 9장_스프링_프로젝트_시작하기 ├── 9장_스프링_프로젝트_시작하기_김석래.md ├── 9장_스프링_프로젝트_시작하기_안송이.pdf └── images │ ├── 001.jpeg │ ├── 002.jpeg │ ├── 003.jpeg │ ├── 004.jpeg │ ├── 005.jpeg │ ├── 006.jpeg │ └── 007.jpeg ├── README.md └── img ├── list1-48(1).png ├── list1-48(2).png ├── pic3-1.png ├── pic3-2.png ├── pic3-3.png ├── pic3-7.png ├── pic4-1.png ├── pic6-13.png ├── pic6-14.png ├── pic6-18.png ├── pic6-3.png └── table1-2.png /1장_오브젝트와_의존관계/1장_오브젝트와_의존관계/오브젝트와 의존관계1_이형원.md: -------------------------------------------------------------------------------- 1 | # 1장 오브젝트와 의존관계 2 | 3 | *** 4 | 5 | 스프링은 자바를 기반으로 한 기술이다. 6 | 스프링을 이해하려면 먼저 오브젝트에 깊은 관심을 가져야 한다. 7 | *** 8 | 9 | ## 1.1 초난감 DAO 10 | 11 | DAO 12 | > Data Access Object : DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 object. 13 | 14 | 15 | 16 | ### JDBC를 이용하는 작업의 일반적인 순서 17 | 1. DB 연결을 위한 connection 불러온다. 18 | 2. SQL을 담은 Statement 를 만든다. 19 | 3. 만들어진 statement 실행. 20 | 4. 조회의 경우 SQL 쿼리의 실행결과를 ResultSet으로 받아서 정보를 저장할 오브젝트에 옮겨준다. 21 | 5. 작업 중에 생성된 connection, statement, ResultSet 같은 리소스 작업을 마친 후 반드시 닫아준다. 22 | 6. JDBC API 가 만들어내는 예외를 잡아서 직접 처리하거나, 메소드에 throws를 선언해서 예외가 발생하는 메소드 밖으로 던지게 한다. 23 | 24 | *** 25 | 26 | ## 1.2 DAO의 분리 27 | 28 | *** 29 | 30 | ### 관심사의 분리 31 | 32 | - 개발자가 객체를 설계할 때 가장 염두에 둬야 할 사항은 바로 미래의 변화를 어떻게 대비할 것인가이다. 33 | - 미래를 위해 설계하고 개발해야 한다. 34 | - 객체지향 설게와 프로그래밍이 이전의 절차적 프로그래밍 패러다임에 비해 초기에 좀 더 많은, 번거러운 작업을 요구 -> 객체지향 기술 자체가 지니는, 변화에 효과적으로 대처할 수 있다는 기술적인 특징. 35 | 36 | ### "분리"와 "확장"을 고려한 설계를 해야한다!! 37 | 38 | 39 | ## 분리 40 | 41 | - 모든 변경과 발전은 한 번에 **한 가지 관심사항** 에 집중해서 일어난다. 42 | - 문제는, 변화는 대체로 집중된 한 가지 관심에 대해 일어나지만 그에 따른 작업은 한 곳에 집중되지 않는 경우가 많다. 43 | 44 | ### " 관심사의 분리 Separation of Concerns" 45 | 46 | 47 | > 예시) DB 커넥션 분리-상속을 통한 분리 48 | > > ``` java 49 | > > public abstract class UserDao { 50 | > > 51 | > > public void add(User user) throws ClassNotFoundException, SQLException {> > 52 | > > Connection c = getConnection(); 53 | > > ... 54 | > > } 55 | > > 56 | > > public User get(String id) throws ClassNotFoundException, SQLException { 57 | > > Connection C = getConnection(); 58 | > > ... 59 | > > } 60 | > > 61 | > > public abstact Connection getConnection() throws ClassNotFoundException, SQLException; 62 | > > 63 | > > } 64 | > > 65 | > > ``` 66 | > > 67 | > > ``` java 68 | > > public class NUserDao extends UserDao { 69 | > > 70 | > > public Connection getConnection() throws ClassNotFoundException, SQLException { 71 | > > //N사 DB connection 생성코드 72 | > > } 73 | > > 74 | > > public class DUserDao extends UserDao { 75 | > > 76 | > > public Connection getConnection() throws ClassNotFoundException, SQLException { 77 | > > // D 사 DB connection 생성코드 78 | > > } 79 | > > } 80 | 81 | 82 | 83 | 84 | - **템플릿 메소드 패턴 (template method pattern )** 85 | > 이렇게 슈퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 86 | 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법을 87 | 88 | 89 | - **팩토리 메소드 패턴 (Factory Method Pattern)** 90 | 91 | > UserDao 의 서브클래스의 getConnection 메소드는 어떤 Connection 클래스의 오브젝트를 어떻게 생성할 것인지를 결정한다. 이렇게 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 방법 92 | 93 | 94 | *** 95 | 96 | ## 1.3 DAO확장 97 | - 모든 오브젝트는 변화의 이유와 시기, 주기 등이 다르다. 98 | *** 99 | ### 1.3.1 클래스의 분리 100 | 101 | - 상속관계도 아닌 완전히 독립적인 클래스 102 | 103 | > ``` java 104 | > public abstract class UserDao { 105 | > private SimpleConnectionMaker simpleConnectionMaker; 106 | > 107 | > public void add(User user) throws ClassNotFoundException, SQLException { 108 | > 109 | > ... 110 | > } 111 | > 112 | > public User get(String id) throws ClassNotFoundException, SQLException { 113 | > 114 | > Connection c = simpleConnectionMaker.makeNewConnection(); 115 | > 116 | > ... 117 | > 118 | > } 119 | > } 120 | > 121 | 122 | > 독립시킨 DB 연결 기능 SimpleConnectionMaker 123 | > ``` java 124 | > public class SimpleConnectionMaker { 125 | > public Connection makeNewConnection() throws ClassNotFoundException, SQLException { 126 | > Class.forName("org.mariadb.jdbc.Driver"); 127 | > Connection c = DriverManager 128 | > .getConnection("jdbc:mysql://127.0.0.1:3306/springbook", "root", "password"); 129 | > return c; 130 | > } 131 | > } 132 | 133 | 134 | ### 문제점 135 | - 상속처럼 자유로운 확장이 가능하게 하려면 DB 커넥션 메소드의 인터페이스가 동일해야한다. 136 | - 커넥션을 제공하는 클래스의 이름을 구체적으로 알아야한다. 137 | 138 | 139 | 140 | ### 1.3.2 인터페이스의 도입 141 | 142 | - 두 개의 클래스가 서로 긴밀하게 연결되지 않도록 중간에 추상적인 연결고리 만들어주자! => 추상화 143 | 144 | **추상화** 145 | > 어떤 것들의 공통적인 성격을 뽑아내어 이를 따로 분리해내는 작업. 146 | 147 | **자바가 추상화를 위해 제공하는 가장 유용한 도구는 **인터페이스** 이다.** 148 | 149 | - 인터페이스를 사용하면 인터페이스의 메소드르 통해 알 수 있는 기능에만 관심을 가지면 되지, 그 기능을 어떻게 구현했는지에 관심을 둘 필요 없다. 150 | 151 | 152 | > ConnectionMaker 인터페이스 153 | > ``` java 154 | > public interface ConnectionMaker { 155 | > public Connection makeConnection() throws ClassNotFoundException, SQLException; 156 | > } 157 | > 158 | 159 | > ConnectionMaker 구현체 160 | > ``` java 161 | > public class DConnectionMaker implements ConnecctionMaker { 162 | > ... 163 | > public Connection makeConnection() throws ClassNotFoundException, SQLException { 164 | > // D 사의 Connection 코드 165 | > } 166 | > } 167 | 168 | > ConnectionMaker 인터페이스 사용하도록 구현한 UserDao구현체 169 | > ``` java 170 | > public class UserDao { 171 | > private ConnectionMaker connectionMaker; 172 | > 173 | > public UserDao() { 174 | > connectionMaker = new DConnectionMaker(); 175 | > } 176 | > 177 | > public void add(User user) throws ClassNotFoundException, SQLException { 178 | > Connection c = connectionMaker.makeConnection(); 179 | > ... 180 | > } 181 | > 182 | > public User get(String id) throws ClassNotFoundException, SQLException { 183 | > Connection c = connectionMaker.makeConnection(); 184 | > ... 185 | > 186 | > } 187 | 188 | 189 | ### 문제점 190 | > connectionMaker = new DConnectionMaker(); 191 | - UserDao 에 DConnection 클래스의 생성자를 호출해서 오브젝트를 생성하는 코드가 남아있다. 192 | - UserDao 와 UserDao가 사용할 ConnectionMaker의 특정 구현 클래스 사이에 관계를 설정해주는 것에 관한 관심을 분리하자. 193 | 194 | > ``` java 195 | > ... 196 | > public static void main(String[] args) throws ClassNotFoundException, SQLException { 197 | > 198 | > ConnectionMaker connectionMaker = new DConnectionMaker(); 199 | > 200 | > UserDao dao = new UserDao(connectionMaker); 201 | > ... 202 | > 203 | > } 204 | 205 | *** 206 | 207 | ### 1.3.4 원칙과 패턴. 208 | 209 | **객체 지향 설계 원칙 SOLID** 210 | > SRP(Single Responsibility Principle) : 단일 책임 원칙 211 | > OCP(Open Closed Principle) : 개방 폐쇄 원칙 212 | > LSR(Liskov Substituition Principle) : 리스코프 치환 원칙 213 | > ISP(Interface Segeregtion Principle) : 인터페이스 분리 원칙 214 | > DIP(Dependency Inversion Principle) : 의존관계 역전 원칙 215 | 216 | 217 | 218 | **개방 페쇄 원칙 OCP(Open-Closed Principle)** 219 | - 깔끔한 설계를 위해 적용 가능한 객체지향 설계 원칙 중 하나이다. 220 | - " 클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다." 221 | - **인터페이스** 를 통해 제공되는 확장 포인트는 확장을 위해 개방되어 있지만, **인터페이스**를 이용하는 클래스는 자신의 변화가 불필요하게 일어나지 않도록 폐쇄. 222 | 223 | 224 | 225 | **높은 응집도와 낮은 결합도** 226 | 227 | 228 | > 높은 응집도 229 | >- 응집도가 높다 => 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어 있다. 230 | 231 | > 낮은 결합도 232 | >- 책임과 관심사가 다른 오브젝트 또는 모듈과는 낮은 결합도, 즉 느슨한 연결 형태를 유지해야 한다. 233 | 234 | 235 | 236 | **전략 패턴 (Strategy Pattern)** 237 | - 자신의 기능 맥락에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 **디자인 패턴** 이다. 238 | 239 | *** 240 | 241 | ## 1.4 제어의 역전 (IOC) 242 | - 일반적인 프로그램의 흐름 : main 메소드와 같이 프로그램이 시작되는 지점에서 다음에 사용할 오브젝트를 결정 -> 결정한 오브젝트 생성 -> 오브젝트에 있는 메소드 호출 등의 흐름이 반복된다. 243 | 244 | - 제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 선택하지 않는다. 생성하지도 않고 어떻게 만들어지고 어디서 사용하는지 알 수 없다. 245 | 246 | ### 프레임워크 VS 라이브러리 247 | - 라이브러리를 사용하는 어플리케이션 코드는 어플리케이션의 흐름을 직접 제어한다. 248 | - 프레임워크는 거꾸로 어플리케이션의 코드가 프레임워크에 의해 사용된다. 어플리케이션 코드는 프레임워크가 짜놓은 틀에서 수동적으로 동작해야한다. 249 | 250 | *** 251 | 252 | ## 1.5 스프링의 IoC 253 | 254 | ### 1.5.1 오브젝트 팩토리를 이용한 스프링 IoC 255 | 256 | > 팩토리 257 | > - 객체의 생성방법을 결정하고, 그렇게 만들어진 오브젝트를 돌려주는 오브젝트. 258 | 259 | # 빈 bean 260 | 261 | - 스프링에서 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 **빈** 이라고 한다. 262 | - 동시에 스프링 빈은 스프링컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역전이 적용된 오브젝트. 263 | 264 | > 265 | >**빈 팩토리** 266 | >>빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 **빈 팩토리**라고 한다. 267 | >>보통 빈 팩토리 보다는 이를 좀 더 확장한 **애플리케이션 컨테스트**를 주로 사용한다. 268 | 269 | 270 | ### DaoFacotry를 사용하는 어플리케이션 컨텍스트 271 | - DaoFactory를 스프링의 빈 팩토리가 사용할 수 있는 본격적인 설정정보로 만들자. 272 | 273 | > @Configuration : 스프링이 빈 팩토리를 위한 오브젝트 설정을 담당하는 클래스라고 인식. 274 | > @Bean : 오브젝트를 만들어주는 메소드 275 | > ``` java 276 | > @Configuration 277 | > public class DaoFactory { 278 | > @Bean 279 | > public UserDao userDao() { 280 | > return new UserDao(connectionMaker()); 281 | > } 282 | > 283 | > ... 284 | > 285 | > @Bean 286 | > public ConnectionMaker connectionMaker() { 287 | > return new DConenctionMaker(); 288 | > } 289 | > ``` 290 | 291 | ### 1.5.2 애플리케이션 컨텍스트의 동작방식 292 | 293 | - @Configuration 이 붙은 DaoFactory 는 이 애플리케이션 컨테스트를 활용하는 **IoC 설정정보** 294 | - 애플리케이션 컨텍스트는 DaoFactory 클래스를 설정정보로 등록해두고 @Bean이 붙은 메소드의 이름을 가져와 빈 목록을 만든다 295 | - 클라이언트가 애플리케이션 컨텍스트의 getBean() 메소드를 호출하면 자신의 빈 목록에서 요청한 이름을 찾는다 296 | - 있다면 빈을 생성하는 메소드를 호출해서 오브젝트를 생성시킨 후 클라이언트에 돌려준다. 297 | 298 | 299 | 300 | ### 1.5.3 스프링 IoC 용어 정리 301 | 302 | ### 빈 303 | - 스프링이 IoC방식으로 관리하는 오브젝트. 304 | - 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 빈이라고 부른다. 305 | 306 | ### 빈 팩토리 307 | - 스프링의 IoC를 담당하는 핵심 컨테이너. 308 | - 빈을 등록,생성,조회 그 외에 부가적인 빈을 관리한다. 309 | - 보통은 이 빈 팩토리를 바로 사용하지 않고, 이를 확장한 **어플리케이션 컨텍스트**를 이용한다. 310 | 311 | ### 애플리케이션 컨텍스트 312 | - 빈 팩토리를 확장한 IoC 컨테이너. 313 | - 기본적인 기능은 빈 팩토리와 동일하다, + 스프링이 제공하는 각종 부가 서비스를 추가로 제공. 314 | - BeanFactory를 상속한다. 315 | 316 | ### 설정정보/설정 메타정보 317 | - 애플리케이션 컨텍스트 또는 빈 팩토리가 IoC를 적용하기 위해 사용하는 메타정보를 말한다. 318 | 319 | ### 컨테이너 320 | - IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너라고도 한다. 321 | 322 | *** 323 | 324 | ## 1.6 싱글톤 레지스트리와 오브젝트 스코프 325 | 326 | > **오브젝트의 동일성과 동등성** 327 | >> 자바에서 두 개의 오브젝트가 같다? 328 | >> 완전히 같은 동일한 오브젝트 vs 동일한 정보를 담고 있는 오브젝트 329 | >> 동일성은 == 연산자, 동등성은 equals() 메소드. 330 | > 331 | >두 개의 오브젝트가 동일하다 332 | > > 사실은 하나의 오브젝트만 존재. 두 개의 오브젝트 레퍼런스 변수를 갖고 있을 뿐. 333 | > 334 | > 두 개의 오브젝트가 동등하다 335 | > > 두 개의 각기 다른 오브젝트가 메모리상에 존재하는 것, 두 오브젝트의 정보가 동등하다. 336 | > 337 | > 자바 클래스만들 때, 따로 equals() 를 구현하지 않았다면, 최상위 클래스인 Object 클래스에 구현되어있는 equals() 메소드가 사용된다. 338 | > > Object의 equals() 메소드는 두 오브젝트의 동일성을 비교하기 때문에 이때는 동일한 오브젝트여야 동등한 오브젝트로 여겨진다. 339 | 340 | *** 341 | ## 1.6.1 싱글톤 레지스트리로서의 애플리케이션 컨텍스트 342 | - 스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 **싱글톤** 으로 만든다. 343 | 344 | 345 | ## 서버 애플리케이션과 싱글톤 346 | - 태생적으로 스프링은 엔터프라이즈 시스템을 위해 고안된 기술이기 때문에 서버 환경에서 사용될 때 그 가치가 있다. 실제로 스프링은 대부분 서버환경에서 사용된다. 347 | 348 | 이전의 문제점 349 | - 매번 클라이언트에서 요청이 올 떄마다 각 로직을 담당하는 오브젝트를 새로 만들어서 사용한다고 생각해보자. 350 | - 요청 한번에 5개의 오브젝트가 새로 만들어지고 초당 500개의 요청이 들어오면, 초당 2500개의 새로운 오브젝트가 생성된다. 351 | 352 | ### 싱글톤 패턴의 한계 353 | 354 | 자바에서 싱글톤을 구현하는 방법. 355 | - 클래스 밖에서는 오브젝트를 생성하지 못하도록 생성자를 private 로 만든다. 356 | - 생성된 싱글톤 오브젝트를 저장할 수 있는 자신과 같은 타입의 스태틱 필드 정의. 357 | - 스태틱 팩토리 메소드인 getInstance() 를 만들고 이 메소드가 최초로 호출되는 시점에서 한번만 오브젝트를 만들어지게 한다. 358 | - 생성된 오브젝트는 스태틱 필드에 저장된다. 359 | - 한번 오브젝트(싱글톤) 가 만들어지고 난 후에는 getInstance()를 이용해 이미 만들어져 스태틱 필드에 저장해둔 오브젝틀르 넘겨준다. 360 | 361 | >싱글톤 패턴을 적용한 UserDao 362 | >```java 363 | >public class UserDao { 364 | > private static UserDao INSTANCE; 365 | > 366 | > ... 367 | > 368 | > public static synchronized UserDao getInstance() { 369 | > if(INSTANCE == null) INSTANCE = new UserDao(???); 370 | > return INSTANCE; 371 | > } 372 | >} 373 | >``` 374 | 375 | ### 싱글톤 패턴의 문제점 376 | - private 생성자를 갖고 있기 떄문에 상속불가. 377 | - 객체지향의 장점인 상속과 이를 이용한 다형성을 적용할 수 없다. 378 | - 싱글톤은 테스트하기가 힘들다. 379 | - 서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다. 380 | - 싱글톤의 사용은 전역 상태를 만들수 있기 때문에 바람직하지 못하다. 381 | 382 | # 싱글톤 레지스트리 383 | - 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다. 이를 싱글톤 레지스트리 라고 한다. 384 | - 스프링 컨테이너는 싱글톤을 생성하고, 관리하고, 공급하는 싱글톤 관리 컨테이너이기도 하다. 385 | 386 | ## 싱글톤 레지스트리 장점 387 | - 스태틱 메소드와 Private 생성자를 사용해야 하는 비정상적인 클래스가 아니라 평범한 자바클래스를 싱글톤으로 활용하게 해준다. 388 | - 평범한 자바 클래스라도 IoC 방식의 컨테이너를 사용해서 생성과 관계설정, 사용 등에 대한 제어권을 컨테이너에게 넘기면 손쉽게 싱글톤 방식으로 만들어져 관리할 수 있게 한다. 389 | - **싱글톤 패턴과 달리 스프링이 지지하는 객체지향적인 설계 방식과 원칙, 디자인 패턴 등을 적용하는데 아무런 제약이 없다**. 390 | 391 | *** 392 | 393 | ### 1.6.2 싱글톤과 오브젝트의 상태 394 | 395 | - 싱글톤은 멀티스레드 환경이라면 여러 스레드가 동시에 접근해서 사용할 수 있다. 따라서 상태관리 중요!! 396 | >기본적으로 싱글톤이 멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은 **무상태 stateless** 방식으로 만들어져야 한다. 397 | > > 이유 398 | > > 다중 사용자의 요청을 한꺼번에 처리하는 스레드들이 동시에 싱글톤 오브젝트의 인스턴스 변수를 수정하는 것은 매우위험하다. 저장할 공간이 하나뿐이니 서로 값을 덮어쓰고 자신이 저장하지 않은 값을 읽어올 수 있기 때문이다. 399 | >- 따라서 싱글톤은 기본적으로 인스턴스 필드 값을 변경하고 유지하는 **상태유지 stateful** 방식으로 만들지 않는다. 400 | 401 | 402 | *** 403 | 404 | ## 1.7 의존관계 주입(DI) 405 | 406 | ### 1.7.1 제어의 역전과 의존관계 주입 407 | 408 | - 객체를 생성하고 관계를 맺어주는 등의 작업을 담당하는 기능을 일반화한것이 스프링의 IoC컨테이너이다. 409 | - 스프링의 IoC 기능의 대표적인 동작원리는 주로 의존관계주입이라 불린다. 410 | 411 | 412 | ### 1.7.2 런타임 의존관계 설정 413 | 414 | ### 의존관계 415 | 416 | - A가 B에 의존하고 있다. -> B가 변하면 그것이 A에 영향을 미친다. 반대로 B는 A의 영향을 받지 않는다. 417 | > 의존관계 주입 세가지 조건 418 | 1. 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스에만 의존하고 있어야한다. 419 | 2. 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다. 420 | 3. 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공해줌으로써 만들어진다. 421 | 422 | 423 | 의존관계 주입의 핵심은 설계 시점에는 알지 못했던 두 오브젝트의 관계를 맺도록 도와주는 제3의 존재. 424 | 425 | *** 426 | 427 | ### 1.7.4 의존관계 주입의 응용 428 | 429 | ### 기능 구현의 교환 430 | 431 | > 상황 : 로컬 개발중에 로컬 DB에 연결하여 작업중이여서 LocalDBConnectionMaker 라는 클래스를 만들고 사용하다가 다시 서버에 배치하는 시점에는 ProductionDBConnectionMaker 라는 클래스로 변경해줘야한다. 432 | 433 | - DI 방식을 적용해서 만들어보자. 434 | - 모든 DAO는 생성 시점에 ConnectionMaker 타입의 오브젝트를 컨테이너로부터 제공받는다. 435 | - 단지 DI의 설정정보에 해당하는 DaoFactory 만 다르게 만들어 두면 나머지 코드에는 전혀 손대지 않고 개발 시와 운영시에 각각 다른 런타임 오브젝트에 의존관계를 갖게 해줘서 문제를 해결할 수 있다. 436 | 437 | > 개발용 ConnectionMaker 생성 코드 438 | > ```java 439 | > @Bean 440 | > public ConnectionMakwer connectionMaker() { 441 | > return new LocalDBConnectionMaker(); 442 | > } 443 | >``` 444 | > 운영용 ConnectionMaker 생성 코드 445 | > ```java 446 | > @Bean 447 | > public ConnectionMaker connectionMaker() { 448 | > return new ProductionDBConnectionMaker(); 449 | > } 450 | >``` 451 | 452 | ### 부가기능 추가 453 | 454 | - DB연결횟수를 카운팅 하고싶다! 455 | 456 | 457 | *** 458 | 459 | 460 | ### 1.7.5 메소드를 이용한 의존관계 주입 461 | 462 | - 이전까지는 생성자를 통해 의존관계 주입을 하였다. 463 | - 생성자가 아닌 일반 메소드를 사용할 수도 있을 뿐만 아니라, 생성자를 사용하는 방법보다 더 자주 사용된다. 464 | 465 | 466 | *** 467 | 468 | ## 정리 469 | 470 | - 책임이 다른 코드 분리해서 두 개의 클래스로 만들었다 (관심사의 분리 , 리팩토링) 471 | - 그 중에서 바뀔수 있는 쪽의 클래스는 인터페이스를 구현하도록 하고, 다른 클래스에서 인터페이스를 통해서만 접근하도록 만들었다. 472 | - 자신의 책임 자체가 변경되는 경우 외에는 불필요한 변화가 발생하지 않도록 막아주고, 자신이 사용하는 외부 오브젝트의 기능은 자유롭게 확장하거나 변경할 수 있게 만들었다. (개방 폐쇄 원칙) 473 | - 결국 한 쪽의 기능 변화가 다른 쪽의 변경을 요구하지 않아도 되게 했고, 자신의 책임과 관심사에만 순수하게 집중하도록. (낮은 결합도와 높은 응집도) 474 | - 오브젝트 팩토리의 기능을 일반화한 IoC 컨테이너로 넘겨서 오브젝트가 자신이 사용할 대상의 생성이나 선택에 관한 책임으로부터 자유롭게 만들어줬다. 475 | - 전통적인 싱글톤 패턴 구현 방식의 단점 파악, 싱글톤 레지스트리. 476 | - 구체적인 의존 오브젝트를 DI 컨테이너의 도움으로 주입받아, 다이내믹한 의존관계를 갖게 해주는 IoC의 특별한 케이스 477 | - 의존 오브젝트를 주입할 때 생성자를 이용한 방법과 수정자 메소드를 이용하는 방법을 알아봄. 478 | -------------------------------------------------------------------------------- /1장_오브젝트와_의존관계/1장_오브젝트와_의존관계_김석래.md: -------------------------------------------------------------------------------- 1 | # 1장 오브젝트와 의존관계 2 | 3 | 스프링은 `자바를 기반`으로 한 기술이다. 그렇기 때문에 가장 중요하게 `가치`를 두는 것이 바로 `객체지향 프로그래밍이 가능한 언어`라는 점이다. 4 | 5 | > **스프링의 관심사** 6 | 7 | 스프링이 가장 `관심`을 많이 두는 대상은 `오브젝트`이다. 애플리케이션에서 오브젝트가 `생성`되고 다른 오브젝트와 `관계를 맺고`, `사용`되고 `소멸`하기까지의 `전 과정`을 진지하게 생각해볼 필요가 있다. 8 | 9 | 결국 `오브젝트에 대한 관심`은 오브젝트의 `기술적인 특징`과 `사용 방법`을 넘어서 `오브젝트의 설계`로 발전하게 된다. 10 | 11 | * 1장에서는 스프링이 어떤 것이고, 무엇을 제공하는지 보다는 스프링이 `관심`을 갖는 대상인 `오브젝트의 설계와 구현`, `동작원리`에 더 집중해야 한다. 12 | 13 | ## 1.1 초난감 DAO 14 | 15 | ### 1.1.1 User 16 | 17 | 사용자 정보를 JDBC API를 통해 DB에 저장하고 조회할 수 있는 DAO를 구현해보기 18 | 19 | > **사용자의 정보를 저장하는 User 오브젝트 생성** 20 | 21 | ```java 22 | 23 | @Setter 24 | @Getter 25 | @NoArgsConstructor 26 | public class User { 27 | private String id; 28 | private String name; 29 | private String password; 30 | } 31 | ``` 32 | 33 | * `DAO(Data Access Object)`란 DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트를 말한다. 34 | * `자바 빈(JavaBean)`은 두 가지 관례를 따라 만들어진 오브젝트를 가리킨다. 35 | * `디폴트 생성자`: 파라미터가 없는 디폴트 생성자를 갖고 있다. 36 | * 툴이나 프레임워크에서 리플렉션을 이용해 오브젝트를 생성하기 때문에 필요하다. 37 | * `프로퍼티`: 자바빈이 노출하는 이름을 가진 속성을 프로퍼티라 한다. 38 | * 프로퍼티는 `set`으로 시작하는 `수정자 메서드(setter)`와 `get`으로 시작하는 `접근자 메서드(getter)`를 이용해 수정 또는 조회가 가능하다. 39 | 40 | ### 1.1.2 UserDao 41 | 42 | 사용자의 정보를 DB에 넣고 관리할 수 있는 DAO 클래스 작성하기 43 | 44 | > **JDBC를 이용하는 작업의 일반적인 순서** 45 | 46 | 1. DB 연결을 위한 Connection을 가져온다. 47 | 2. SQL을 담은 Statement(또는 PreparedStatement)를 만든다. 48 | 3. 만들어진 Statement를 실행한다. 49 | 4. 조회의 경우 SQL 쿼리의 실행 결괴를 ResultSet으로 받아서 정보를 저장할 오브젝트(여기서는 User)에 옮겨준다. 50 | 5. 작업 중에 생성된 Connection, Statement, ResultSet 같은 리소스는 작업을 마친 후 반드시 닫아준다. 51 | 6. JDBC API가 만들어내는 예외 exception를 잡아서 직접 처리하거나, 메소드에 throws를 선언해서 예외가 발생하면 메소드 밖으로 던지게 한다. 52 | 53 | > **UserDao**: 새로운 사용자를 생성하는 메서드, 아이디를 가지고 사용자 정보를 읽어오는 메서드 54 | 55 | ```java 56 | public class UserDao { 57 | public void add(User user) throws ClassNotFoundException, SQLException { 58 | Class.forName("com.mysql.cj.jdbc.Driver"); 59 | Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3309/user-db", "root", "1234"); 60 | 61 | PreparedStatement ps = c.prepareStatement("INSERT INTO USERS(id, name, password) values (?, ?, ?)"); 62 | ps.setString(1, user.getId()); 63 | ps.setString(2, user.getName()); 64 | ps.setString(3, user.getPassword()); 65 | 66 | ps.executeUpdate(); 67 | 68 | ps.close(); 69 | c.close(); 70 | } 71 | 72 | public User get(String id) throws SQLException, ClassNotFoundException { 73 | Class.forName("com.mysql.cj.jdbc.Driver"); 74 | Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3309/user-db", "root", "1234"); 75 | 76 | PreparedStatement ps = c.prepareStatement("SELECT * FROM USERS WHERE id = ?"); 77 | ps.setString(1, id); 78 | ResultSet rs = ps.executeQuery(); 79 | 80 | rs.next(); 81 | 82 | User user = new User(); 83 | user.setId(rs.getString("id")); 84 | user.setName(rs.getString("name")); 85 | user.setPassword(rs.getString("password")); 86 | 87 | rs.close(); 88 | ps.close(); 89 | c.close(); 90 | 91 | return user; 92 | } 93 | } 94 | ``` 95 | 96 | ### 1.1.3 main()을 이용한 DAO 테스트 코드 97 | 98 | > **여기까지 작성한 내용에 대해서 테스트를 진행** 99 | 100 | 여기서는 mysql을 [docker-compose](src/main/resources/docker-compose.yml) 를 통해 가상 컨테이너로 올린 뒤에 테스트를 진행하도록 한다. 101 | 102 | 1. docker-compose up 명령어를 통해 컨테이너를 실행 103 | 2. ChapterOneApplication 를 실행 시 schema.sql를 통해 테이블 생성 104 | 3. UserDao 테스트 실행 105 | 106 | ```java 107 | public class UserDao { 108 | public static void main(String[] args) throws SQLException, ClassNotFoundException { 109 | UserDao dao = new UserDao(); 110 | 111 | User user = new User(); 112 | user.setId("seok_id"); 113 | user.setName("seok"); 114 | user.setPassword("1234"); 115 | 116 | dao.add(user); 117 | 118 | System.out.println("등록 성공 : " + user.getId()); 119 | 120 | User user2 = dao.get(user.getId()); 121 | System.out.println("사용자 명: " + user2.getName()); 122 | System.out.println("사용자 비밀번호: " + user2.getPassword()); 123 | 124 | System.out.println(user2.getId() + " : 조회 성공"); 125 | } 126 | } 127 | ``` 128 | 129 | ```text 130 | 등록 성공 : seok_id 131 | 사용자 명: seok 132 | 사용자 비밀번호: 1234 133 | seok_id : 조회 성공 134 | ``` 135 | 136 | * 정상적으로 테스트를 성공하였으나 고민해볼 거리가 있다. 137 | 138 | > **테스트에 대한 고민** 139 | 140 | 만들어진 코드의 기능을 검증하고자 할 때 사용할 수 있는 가장 간단한 방법은 `오브젝트 스스로 자신을 검증`하도록 만들어주는 것이다. 141 | 142 | - 테스트는 정상적으로 동작하고 성공하였으나 `객체지향 기술의 원리`에 충실하지 못하였다. 143 | 144 | - 그러므로 우리는 이를 개선해야 한다. 145 | - 동작을 개선해야 하는 이유? 146 | - 개선하였을 때의 장점? 147 | - 장점들이 앞으로 어떠한 유익함을 줄 것인가? 148 | - 객체지향 설계의 원칙과 어떠한 상관이 있을까? 149 | - DAO를 개선하는 경우와 그대로 사용하는 경우, 스프링을 사용하는 개발에서 무슨 차이가 있을까? 150 | 151 | ## 1.2 DAO의 분리 152 | 153 | ### 1.2.1 관심사의 분리 154 | 155 | > **객체지향의 세계에 대한 고민** 156 | 157 | - 시간에 따라 사용자의 비즈니스 프로세스는 변한다. 158 | - 애플리케이션이 기반을 두고 있는 기술도 변하고, 운영되는 환경도 변한다. 159 | 160 | - 개발자가 객체를 설계할 때 가장 염두에 둬야 할 사항이 바로 `미래의 변화를 어떻게 대비`할 것인가이다. 161 | 162 | > **객체 지향은 `분리와 확장`을 고려한 설계를 갖기 때문에 변화에 대비할 수 있다.** 163 | 164 | - 분리 165 | - 변화는 대체로 집중된 한 가지 관심에 대해 일어난다. 166 | - 우리가 준비해야 할 일은 한 가지 관심이 한 군데에 집중되도록 하는 것이다. 167 | - 이를 `관심사의 분리(Seperation of Concerns)`라 한다. 168 | - 관심이 같은 것은 하나의 객체 안으로 또는 친한 객체로 모이게 하고, 관심이 다른 것은 가능한 따로 떨어져서 서로 영향을 주지 않도록 분리하는 것이라 생각할 수 있다. 169 | 170 | ### 1.2.2 커넥션 만들기의 추출 171 | 172 | 지금까지 작성한 내용 중에 add() 메서드안에 세 가지 관심사항이 존재한다는 것을 발견할 수 있을까? 173 | 174 | > **UserDao의 관심사항** 175 | 176 | 1. **커넥션 연결** 177 | - DB와 연결을 위한 커넥션을 어떻게 가져올까라는 관심 178 | 2. **파라미터 바인딩** 179 | - 파라미터로 넘어온 사용자 정보를 Statement에 바인딩시키고, Statement에 담긴 SQL을 DB를 통해 실행시키는 관심 180 | 3. **리소스 반환** 181 | - 작업이 끝나면 리소스를 닫아 시스템에 돌려주는 관심 182 | 183 | > **핵심 관심사** 184 | 185 | DB 연결을 위해 Connection 오브젝트를 가져오는 부분은 필수로 추가되어야 하는 핵심적인 공통 코드라 볼 수 있다. 186 | 187 | - 이러한 핵심 관심사를 분리하기 위해 `리펙토링` 하는 방식을 `메서드 추출`이라 한다. 188 | - 관심 내용이 독립적으로 존재하므로 변경에 대한 수정이 간단해졌다. 189 | 190 | * 리펙토링 191 | - 기존의 코드를 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 재구성하는 작업 또는 기술 192 | 193 | ```java 194 | public class UserDao { 195 | public void add(User user) throws ClassNotFoundException, SQLException { 196 | Connection c = getConnection(); 197 | // ... 198 | } 199 | 200 | public User get(String id) throws SQLException, ClassNotFoundException { 201 | Connection c = getConnection(); 202 | // ... 203 | } 204 | 205 | // 추출된 공통 관심 메서드 206 | private Connection getConnection() throws SQLException, ClassNotFoundException { 207 | Class.forName("com.mysql.cj.jdbc.Driver"); 208 | return DriverManager.getConnection("jdbc:mysql://localhost:3309/user-db", "root", "1234"); 209 | } 210 | } 211 | ``` 212 | 213 | ### 1.2.3 DB 커넥션 만들기의 독립 214 | 215 | > **서비스를 확장하는 방법** 216 | 217 | - UserDao에 대한 확장성을 필요로 하는 상황 218 | - UserDao를 N사와 D사에 사용자 관리 시스템으로 납품하게 되어 각각의 회사에 맞는 DB로 연결할 수 있도록 제공해야 한다. 219 | - 핵심은 `서로 다른 DB`를 사용하고 있고, 각 회사마다 DB 커넥션을 가져오는 방법 또한 다르다. 220 | 221 | > **상속을 통한 확장** 222 | 223 | - UserDao 코드를 한 단계 더 분리하는 방식으로 리펙토링한다. 224 | - getConnection()을 추상 메서드로 만들어 놓는다. 225 | 226 | ![UserDao의 추상화.png](images/user_dao_abstract.png) 227 | 228 | > **템플릿 메서드 패턴** 229 | 230 | - UserDao 추상 클래스 231 | 232 | 슈퍼 클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메서드나 오버라이딩이 가능한 protected 메서드 등으로 만든 뒤 서브 클래스에서 이런 메서드를 필요에 맞게 구현해서 사용하도록 하는 방법 233 | 234 | > **팩토리 메서드 패턴** 235 | 236 | - NUserDao, DUserDao 237 | 238 | 서브 클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것 239 | 240 | > **중간 정리** 241 | 242 | 상위 클래스의 관심사는 Connection 인터페이스 타입의 오브젝트라는 것 외에는 관심을 두지 않는다. 하위 클래스의 관심사는 Connection 오브젝트를 만드는 방법에 대한 관심 만을 갖는다. 하위 클래스들이 243 | 모두 같은 종류의 Connection 구현 클래스의 오브젝트를 리턴하되 오브젝트를 생성하는 방식이 다른 경우, 이는 `팩토리 메서드 패턴`이라 이해할 수 있다. 244 | 245 | * `중요한건 상속구조를 통해 성격이 다른 관심사항을 분리한 코드를 만들어내고, 서로 영향을 덜 주도록 했는지를 이해하는 것` 246 | * 결국에 지금까지 한 작업을 정리하자면 `UserDao에 팩토리 메서드 패턴을 적용하여 getConnection()을 분리한 작업을 하였다.`라 볼 수 있다. 247 | 248 | > **디자인 패턴** 249 | 250 | 디자인 패턴은 주로 `객체지향 설계`에 관한 것이고, 대부분 객체지향적 설계 원칙을 이용해 문제를 해결한다. 패턴의 설계 구조를 보면 대부분 비슷한데, 그 이유는 객체지향적인 설계로부터 문제를 해결하기 위해 적용할 251 | 수 있는 `확장성 추구 방법`이 대부분 `두 가지 구조(상속과 오브젝트 합성)`로 정리되기 때문이다. 핵심은 `패턴이 갖는 목적 또는 의도`이다. 패턴을 적용해야 하는 `상황`, `해결해야 하는 문제`, 252 | 솔루션의 `구조`와 `각 요소의 역할`과 함께 핵심 의도가 무엇인지를 기억해야 한다. 253 | 254 | 255 | > **템플릿 메서드 패턴** 256 | 257 | 상속을 통해 슈퍼 클래스의 기능을 확장할 때 사용하는 방법 258 | 259 | - `변하지 않는 기능`은 `슈퍼클래스`에 만들어두고, `자주 변경되며 확장할 기능`은 `서브 클래스`에서 만들도록 한다. 260 | - 슈퍼 클래스에서는 미리 추상 메서드 또는 오버라이드 가능한 메서드를 정의하고, 이를 활용해 코드의 기본 알고리즘을 담고 있는 `템플릿 메서드`를 만든다. 261 | 262 | * 슈퍼클래스에서 디폴트 기능을 정의 해두거나 비워뒀다가 서브클래스에서 선택적으로 오버라이드할 수 있도록 만들어둔 메소드를 `훅(hook)` 메소드라고 한다. 263 | 264 | > **팩토리 메서드 패턴** 265 | 266 | `팩토리 메서드 패턴`도 `템플릿 메서드 패턴`과 마찬가지로 `상속`을 통해 기능을 확장하게 하는 패턴이다. 슈퍼클래스 코드에서는 서브클래스에서 구현할 메서드를 호출해서 필요한 타입의 오브젝트를 가져와 사용한다. 267 | 268 | 서브클래스에서 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해둔 메소드를 `팩토리 메소드`라고 하고, 이 방식을 통해 오브젝트 생성 방법을 나머지 로직, 즉 슈퍼클래스의 기본 코드에서 독립시키는 269 | 방법을 `팩토리 메소드 패턴` 이라고 한다 270 | 271 | * 자바에서는 종종 오브젝트를 생성하는 기능을 가진 메소드를 일반적으로 `팩토리 메소드`라고 부르기도 하지만 의미가 다르다. 272 | 273 | > **상속의 문제점** 274 | 275 | 상속의 문제점은 다중상속을 허용하지 않는다는 문제점이 있다. 이는 후에 다른 목적으로 UserDao에 상속을 적용하기 힘들다. 276 | 277 | 또 다른 문제점은 상속을 통한 상하위 클래스의 관계는 생각보다 밀접하다. 278 | `상속`을 통해 `관심이 다른 기능을 분리`하고, 필요에 따라 다양한 변신이 가능하도록 확장성도 줬지만 여전히 `상속관계`는 두 가지 다른 관심사에 대한 긴밀한 결합을 허용한다. 279 | 이는 `슈퍼클래스에 변경사항이 일어나는 경우` 서브 클래스를 함께 수정하거나 다시 개발해야 하는 문제를 발생시킬 수 있다. 280 | 281 | -------------------------------------------------------------------------------- /1장_오브젝트와_의존관계/1장_오브젝트와_의존관계_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/1장_오브젝트와_의존관계/1장_오브젝트와_의존관계_안송이.pdf -------------------------------------------------------------------------------- /1장_오브젝트와_의존관계/images/user_dao_abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/1장_오브젝트와_의존관계/images/user_dao_abstract.png -------------------------------------------------------------------------------- /1장_오브젝트와_의존관계/오브젝트와 의존관계1_조수진.md: -------------------------------------------------------------------------------- 1 | # 1.1 초난감 DAO 2 | ### 최초의 UserDao 클래스 3 | ``` 4 | public class UserDao { 5 | public void add(User user) throws ClassNotFoundException, SQLException { 6 | Class.forName("com.mysql.jdbc.Driver"); 7 | Connection c = DriverManaber.getConnection("jdbc:mysql://localhost/springbook", "spring", "book"); 8 | 9 | PreparedStatement ps = c.preparedStatement("insert into users(id, name, passdword) values (?, ?, ?)"); 10 | ps.setString(1, user.getId()); 11 | ps.setString(2, user.getName()); 12 | ps.setString(3, user.getPassword()); 13 | 14 | ps.executeUpdate(); 15 | 16 | ps.close(); 17 | c.close(); 18 | } 19 | 20 | public void get(String id) throws ClassNotFoundException, SQLException { 21 | Class.forName("com.mysql.jdbc.Driver"); 22 | Connection c = DriverManaber.getConnection("jdbc:mysql://localhost/springbook", "spring", "book"); 23 | 24 | PreparedStatement ps = c.preparedStatement("select * from users where id = ?"); 25 | ps.setString(1, id); 26 | 27 | ResultSet rs = ps.executeQuery(); 28 | rs.nest(); 29 | User user = new User(); 30 | user.setId(rs.getStrig(id)); 31 | user.setName(rs.getStrig(name)); 32 | user.setPassword(rs.getStrig(password)); 33 | 34 | rs.close(); 35 | ps.close(); 36 | c.close(); 37 | } 38 | } 39 | ``` 40 | # 1.2 DAO의 분리 41 | 비지니스 프로세스와 그에 따른 요구사항은 끊임없이 바뀌고 발전한다. 개발자가 객체를 설계할 때 가장 염두에 두어야할 것은 바로 `미래에 대한 대비` 42 | 변화의 폭을 최소한으로 줄일 수 있도록 `분리와 확장`을 고려한 설계를 해야한다. 43 | 44 | `관심사의 분리`: 한 가지 관심사가 한 군데에 집중되기 하면 변화가 일어날 때 한 한 부분에서만 집중적으로 일어나고 관계 없는 곳은 안전해진다. 45 | 46 | ### UserDao의 관심 사항 47 | - DB 연결을 위한 커넥션 생성 48 | - SQL 생성 실행. 파라미터 바인딩 49 | - 작업이 끝난 리소스(Statement, Connection) 정리 50 | 51 | ### UserDao 리팩토링 방안 52 | 1. 중복 코드의 메소드 추출 53 | 2. 클래스 분리(상속) 54 | 55 | : 클라이언트에 따라 Connection 맺는 방법이 다르다면 1번 리팩토링 과정을 거친 UserDao는 고객에 맞게 매번 수정이 필요하다. 56 | 상속을 이용해 템플릿 메소드 패턴과 팩토리 메소드 패턴으로 UserDao를 리팩토링 할 수 있다. 57 | ``` 58 | public abstract class USerDao { 59 | public void add(User user) throwsd ClassNotFoundException, SQLExcweption { 60 | Connectionn c = getConnection(); 61 | ... 62 | } 63 | 64 | public User get(String id) throws ClassNotFoundException, SQLException { 65 | Connection c = getConnection(); 66 | ... 67 | } 68 | 69 | public abstrac Connection getConnectino() throws ClassNotFoundException, SQLException; 70 | } 71 | 72 | public class NUserDao extends UserDao { 73 | public Connection getConnection() throws ClassNotFoundException, SQLException { 74 | N사 DB Connection 생성코드 75 | } 76 | } 77 | 78 | public class DUserDao extends UserDao { 79 | public Connection getConnection() throws ClassNotFoundException, SQLException { 80 | D사 DB Connection 생성코드 81 | } 82 | } 83 | 84 | ``` 85 | 86 | # 1.3 DAO의 확장 87 | 모든 오브젝트는 변하고 관심사에 따라서 분리한 오브젝트들은 제각기 변하는 이유, 시기, 주기 등 변화의 성격이 다르다. 88 | 89 | ### UserDao 리팩토링 방안 90 | 3.클래스 분리(합성) 91 | 92 | : 2번 리팩토링 과정을 지난 UserDao의 경우, 아래와 같은 단점이 있다. 93 | - 자바는 다중 상속을 허용하지 않아서 다른 목적으로 UserDao 상속을 적용하기 어렵다. 94 | - 다른 Dao가 필요해지면 확장이 어렵다. 95 | - 동일한 방법으로 다른 Dao 클래스를 계속 만들면 getConnection 구현 코드가 Dao마다 중복된다. 96 | 97 | ``` 98 | public class UserDao { 99 | private SimpleConnectionMaker simpleConnectinMaker; 100 | 101 | public UserDao() { 102 | simpleConnectinMaker = new SimpleConnectinMaker(); 103 | } 104 | 105 | public void add(User user) throws throws ClassNotFoundException, SQLException { 106 | Connection c = simpleConnectinMaker.makeNewConnection(); 107 | ... 108 | } 109 | ... 110 | } 111 | 112 | public class SimpleConnectinMaker { 113 | public Connection makeNewConnection() throws ClassNotFoundException, SQLException { 114 | Class.forName("com.mysql.jdbc.Driver"); 115 | Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book"); 116 | 117 | return c; 118 | } 119 | } 120 | 121 | ``` 122 | 4.합성 & 인터페이스 도입 123 | 124 | : 3번 과정을 지나고나면 다시 DB 연결 방법을 변경하기 위해 UserDao를 직접 수정해야한다. 125 | 126 | 상속처럼 자유로운 확장이 가능하려면 SimpleConnectionMaker의 DB 커넥션 생선 기능의 인터페이스가 동일해야한다. 127 | 또한, 커넥션을 제공하는 클래스를 알아야한다. 128 | 129 | -> 두 클래스가 긴밀하게 연결되어 있지 않도록 추상적인 느슨한 연결고리를 만들어준다! = `인터페이스` 130 | 131 | `추상화`: 어떤 것들의 공통적인 성격을 뽑아내어 이를 따로 분리해내는 작업 132 | 133 | ``` 134 | public interface ConnectionMaker { 135 | public Connectin makeConnection() throws ClassNotFoundException, SQLException; 136 | } 137 | 138 | public class UserDao { 139 | private ConnectinMaker connectinMaker; 140 | 141 | public UserDao() { 142 | connectinMaker = new DConnectinMaker(); 143 | } 144 | 145 | public void add(User user) throws throws ClassNotFoundException, SQLException { 146 | Connection c = connectinMaker.makeConnection(); 147 | ... 148 | } 149 | ... 150 | } 151 | ``` 152 | 153 | 5.관계 설정을 클라이언트로 분리 154 | 155 | : 4번 UserDao에 ConnectionMaker 구현체 사이의 관계 설정 관심이 포함되어 있어서 `connectinMaker = new DConnectinMaker();`, 다른 구현체를 사용하기 위해서 여전히 UserDao 클래스 수정이 필요하다. 156 | 157 | -> `다형성`을 이용해서 런타임에 오브젝트 사이의 다이나믹한 관계를 생성해준다. 158 | ``` 159 | public class UserDao { 160 | private ConnectinMaker connectinMaker; 161 | 162 | public UserDao(ConnectionMaker connectionMAker) { 163 | this.connectinMaker = connectionMaker; 164 | } 165 | 166 | ``` 167 | => 인터페이스와 클라이언트 도움을 이용해 상속을 사용하는 것 보다 유연하게 리팩토링 할 수 있다. 168 | 169 | ### 객체지향 설계 SOLID 원칙 170 | - SRP: 단일 책임 원칙 171 | - OCP: 개방 폐쇄 원칙 172 | - LSP: 리스코프 치환 원칙 173 | - ISP: 인터페이스 분리 원칙 174 | - DIP: 의존관계 역전 원칙 175 | 176 | ### OCP와 높은 응집도, 낮은 결합도 177 | - 위의 5단계 리팩토링을 통해서 UserDao는 DB 연결 방법의 확장에 열려있고, 변경에 닫혀있다.(인터페이스의 역할!) 178 | - 모듈, 클래스(패캐지, 컴포넌트 등)가 하나의 책임 또는 관심사에만 집중되도록 변경되었다 -> 응집도가 높다 179 | - 책임과 관심사가 다른 오브젝트 또는 모듈과는 낮은 결합도를 유지한다. 느슨하게 꼭 필요한 방법만 간접적인 형태로 제공하고 나머지는 독립적이고 알 필요도 없게 만들었다 -> 결합도가 낮다 180 | 181 | # 기타 182 | ### 소개된 디자인 패턴 183 | 1. 템플릿 메소드 패턴 184 | 185 | : 변하지 않는 기능은 슈퍼 클래스에 만들고, 자주 변경되고 확장할 기능은 서브클래스에 두는 디자인 패턴. 선택적으로 오버라이드하거나 디폴트 기능을 제공하는 메서드는 hook 메소드라고 한다. 186 | 2. 팩토리 메소드 패턴 187 | 188 | : 서브 클래스에서 오브젝트 생성 메서드를 재정의한다. 슈퍼 클레스에서 재정의한 메서드를 호출해서 필요한 타입의 오브젝트를 생성받는다. 인터페이스로 오브젝트를 리턴하므로 슈퍼 클래스는 어떤 오브젝트를 리턴받았는지 모른다. 자바에서 팩토리 메서드와는 다른 것!(유의) 189 | 3. 전략 패턴 190 | 191 | : 자신의 기능 맥락에서 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리. 구현체를 필요에 따라 바꿔 사용할 수 있게 하는 디자인 패턴. 클라이언트가 필요하다. 192 | 193 | ### 용어 정리 194 | **엔트리 포인트**: 진입점 = main() 메소드 195 | -------------------------------------------------------------------------------- /1장_오브젝트와_의존관계/오브젝트와 의존관계2_조수진.md: -------------------------------------------------------------------------------- 1 | # 1.4 제어의 역전(IoC) 2 | 3 | ### 팩토리 4 | 5 | 객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 일을 하는 오브젝트 6 | 7 | UserDao, ConnectinMaker는 실제 로직 컴포넌트, 팩토리는 컴포넌트의 구조와 관계 설정 역할(역할 분리) 8 | 9 | ```jsx 10 | public class DaoFactory { 11 | public UserDao userDao() { 12 | ConnectionMaker connectionMaker = new DConnectionMaker(); 13 | UserDao userDao = new UserDao(connectinMaker); 14 | return userDao; 15 | } 16 | } 17 | ``` 18 | 19 | ### 제어의 역전 20 | 21 | 오브젝트가 자신이 사용할 오브젝트 스스로 선택, 생성하지 않고 제어권을 다른 대상에게 위임하는 것 22 | 23 | 템플릿 메소드 패턴, 프레임워크, UserDao와 DaoFactory도 모두 제어의 역전이 적용된 것 24 | 25 | 제어의 역전에서는 프레임워크 또는 컨테이너와 같이 애플리케이션 컴포넌트의 생성과 관계 설정, 사용, 생명주기 관리 등을 관장하는 존재가 필요하다 26 | 27 | # 1.5 스프링의 IoC 28 | 29 | 스프링 핵심인 `빈 팩토리`, `애플리케이션 컨텍스트`는 앞장에서 만든 DaoFactory 같은 역할을 하는 IoC 컨테이너를 일반화 한 것! 30 | 31 | ### 용어 정리 32 | 33 | - 빈: 스프링이 IoC 방식으로 관리(직접 생성, 관계 설정 등)하는 오브젝트 34 | - 빈 팩토리: 스프링의 IoC(빈 생성, 관계 설정 등)를 담당하는 핵심 컨테이너 35 | - 어플리케이션 컨텍스트: 빈 팩토리를 확장한 IoC 컨테이너 36 | - 설정 정보/설정 메타정보: 애플리케이션 컨텍스트 혹은 빈 팩토리가 IoC를 적용하기 위해 사용하는 메타정보 37 | - 컨테이너 또는 IoC 컨테이너: IoC 방식으로 빈을 관리하는 컨테이너. 여기서는 빈 팩토리 or 애플리케이션 컨텍스트 38 | 39 | ### 애플리케이션 컨텍스트 설정 40 | 41 | - `@Configuration`: 빈 팩토리를 위한 설정 정보 표시 42 | - `@Bean`: 오브잭트 생성을 담당하는 IoC용 메소드 표시 43 | 44 | → @Bean이 붙은 메서드의 이름이 빈의 이름 45 | 46 | - `getBean`: 메소드로 빈을 가져올 수 있음(첫 번째 ) 47 | 48 | → 두 번째 파라미터는 제네릭을 이용한 캐스팅(getBean 디폴트는 Ojbect 타입으로 리턴함) 49 | 50 | - 자바 어노테이션으로 설정을 한 경우 ApplicationContext 구현체 중 AnnotationConfigApplicationContext 사용 51 | - ApplicationContext는 BeanFactory 상속 52 | - 자바로 표현했지만 XML과 가튼 스프링 전용 설정 정보 53 | 54 | ```jsx 55 | @Configuration 56 | public class DaoFactory { 57 | 58 | @Bean 59 | public UserDao userDao() { 60 | return new UserDao(connectionMaker()); 61 | } 62 | 63 | @Bean 64 | public ConnectinMaker connectionMaker() { 65 | return new ConnectionMaker(); 66 | } 67 | } 68 | 69 | public class UserDaoTest { 70 | public static void main(Sring[] arsg) throws ClassNotFoundException, SQLException{ 71 | ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class); 72 | UserDao dao = context.getBean("userDao", UserDao.class); 73 | } 74 | } 75 | ``` 76 | 77 | ### 어플리케이션 컨텍스트의 장점 78 | 79 | 직접 IoC컨테이너(DaoFactory)를 구현하는 것과 달리, 80 | 81 | 1. 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다 82 | 2. 애플리케이션 컨텍스트는 종합 IoC 서비스를 제공한다 83 | 3. 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다 84 | 85 | # 1.6 싱글톤 레지스트리와 오브젝트 스코프 86 | 87 | 어플리케이션 컨텍스트를 통해 생성한 빈은 여러 번에 걸쳐 요청하더라도 동일한 오브젝트를 돌려준다 88 | 89 | → `어플리케이션 컨텍스트`는 싱글톤을 저장하고 관리하는 `싱글톤 레지스트리`이다 90 | 91 | **Tip!** 엔터프라이즈 서버 환경에서 클라이언트 요청이 들어올 때마다 필요한 오브젝트를 생성한다면 1분에 몇 십만개의 객체가 생길 수 있다. GC 성능이 좋아지더라도 이렇게 부하가 걸리면 서버가 감당하기 어려우므로 서버 환경에서는 서비스 싱글톤 사용이 권장된다. 92 | 93 | ### 싱글톤 패턴의 단점 94 | 95 | - 생성자를 private으로 작성하고 스태틱 필드에 인스턴스를 가지고 있음 96 | 97 | → 상속과 다형성을 활용할 수 없다 98 | 99 | → 객체 지향의 특징이 적용되지 않는다 100 | 101 | → 초기화 과정에서 관계를 맺는 오브젝트를 다이나믹하게 주입하기 어렵다 102 | 103 | → 전역의 상태를 만들 수 있기 때문에 바람직하지 못하다 104 | 105 | - 생성 방법이 제한적이기 때문에 테스트가 어렵다. 목 오브젝트 대체도 힘들다 106 | - 서버 환경에서 싱글톤이 하나만 만들어지는 것을 보장하지 못한다 107 | 108 | ### 스프링 싱글톤 레지스트리 109 | 110 | 자바의 기본적인 싱글톤 패턴 구현 방식의 단점 때문에 스프링은 직접 싱글톤 형태로 오브젝트를 생성하고 관리하는 기능을 제공하는 데, 그것이 바로 싱글톤 레지스트리! 111 | 112 | 싱글톤 레지스트리는 스태틱 메소드와 private 생성자를 이용한 비정상적인 방법이 아닌 평범한 자바 클래스를 싱글톤으로 활용하게 해준다 113 | 114 | - public 생성자를 가질 수 있다 115 | - 테스트 환경에서 자유롭게 오브젝트 생성 가능 & 목처리 가능 116 | - 생성자 파라미터를 이용해서 오브젝트 주입 가능 117 | - 스프링이 지지하는 객체지향적인 설계 방식과 원칙, 디자인 패턴을 적용하는데 아무 제약이 없다 118 | 119 | ### 싱글톤과 오브젝트의 상태 120 | 121 | 싱글톤은 멀티스레드 환경에서 동시 접근이 가능하므로 상태 관리를 주의해야한다 122 | 123 | 서비스 형태의 오브젝트로 사용되는 경우 무상태 방식으로 만들어져야 한다 124 | 125 | 자신이 사용하는 다른 싱글톤 빈을 저장하는 경우에는 인스턴스 변수로 사용해도 좋다 126 | 127 | 읽기 전용 속성을 가진 정보라면 인스턴스 변수로 사용해도 좋다. 다만 static final 혹은 final 사용할 것 128 | 129 | ### 스프링 빈의 스코프 130 | 131 | `빈의 스코프`: 빈이 생성되고, 존재하고 적용되는 범위 132 | 133 | - 기본 스코프는 싱글톤 134 | - 프로토타입: 빈에 요청할 때마다 새로운 오브젝트 생성 135 | - Request 스코프: HTTP 요청이 생길 때 마다 생성 136 | - Session 스코프: 웹의 세션과 유사 137 | 138 | # 1.7 의존관계 주입(DI) 139 | 140 | IoC는 제어의 역전이라는 범용적인 용어 141 | 142 | 스프링에서 IoC를 이용하는 핵심을 '의존관계 주입(DI)'라고 표현 143 | 144 | 모델이나 코드에서 드러나는 의존관계 vs 런타임 시 오브젝트 사이의 의존관계 145 | 146 | ### 의존관계 주입 147 | 148 | 의존 오브젝트와 그것을 사용할 클라이언트 오브젝트를 런타입 시에 연결해주는 작업 149 | 150 | DI는 자신이 사용할 오브젝트에 대한 선택과 생성 제어권을 외부로 넘기고 수동적으로 주입받은 오브젝트 사용 151 | 152 | → 스프링을 DI 컨테이너, DI 프레임워크라고 부르기도 한다 153 | 154 | ### 의존관계 주입 조건 155 | 156 | - 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서 인터페이스에만 의존하고 있어야 한다 157 | - 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제 3의 존재가 결정한다 158 | - 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다 159 | 160 | ### 의존관계 주입 방법 161 | 162 | - 생성자 주입 163 | - 수정자 주입(setter) 164 | - 일반 메소드를 이용한 주입(여러개의 파라미터를 받을 수 있음) 165 | - 이 외 기타 등등 166 | 167 | ### 의존관계 검색 168 | 169 | 의존주입과 마찬가지로 코드에서는 구체적인 클래스에 의존하지 않고 런타임 시에 의존관계를 결정 170 | 171 | But, 외부로부터의 주입이 아닌 스스로 검색하는 방법 172 | 173 | `getBean()` 메서드를 사용한다 174 | 175 | ```jsx 176 | public UserDao{ 177 | AnnotationConfigApplicationConext context = 178 | new AnnotationConfigApplicationContext(DaoFactory.class); 179 | this.connectionMaker = context.getBean("connectionMaker", ConnectionMaker.class); 180 | } 181 | ``` 182 | 183 | ### 의존관계 주입 vs 검색 184 | 185 | - 일반적으로 의존관계 주입 코드가 단순하고 깔끔 186 | - 의존관계 검색 코드는 팩토리 클래스나 스프링 API가 노출됨. 애플리케이션 컴포넌트가 컨테이너처럼 성격이 다른 오브젝트에 의존하는 것이 바람직하지 않음 187 | - 애플리케이션 기동 시점에서 적어도 한 번은 의존관계 검색 방식을 사용해 오브젝트를 가져와야 한다 188 | 189 | (스태틱 메소드인 main()에서는 DI를 이용해 오브젝트를 주입받을 방법이 없기 때문) 190 | 191 | - 의존관계 검색 방식에서는 검색하는 오브젝트는 자신이 스프링의 빈일 필요가 없다 192 | - DI를 원하는 오브젝트는 자기가 먼저 컨테이너가 관리하는 빈이 돼어야 한다 193 | 194 | # 1.8 XML을 이용한 설정 195 | 196 | 어플리케이션 컨텍스트가 이용하는 DI 의존 관계 설정 정보를 만드는 방법은 자바 클래스 뿐만 아니라 다양! 197 | 198 | XML이 대표적 199 | 200 | ### XML 설정 파일의 장점 201 | 202 | - 단순 텍스트파일이라 다루기 쉽고 이해하기 쉽다 203 | - 컴파일 같은 별도 빌드 작업이 필요없다 204 | - 환경이 달라져서 오브젝트 관계가 바뀌는 경우 빠르게 변경사항을 반영할 수 있다 205 | - 스키마나 DTD를 이용해서 정해진 포맷을 따라 작성됐는지 손쉽게 확인할 수 있다 206 | 207 | ### XML 설정 방법 208 | 209 | - 필요한 내용: 빈의 이름, 빈의 클래스, 빈의 의존 오브젝트 210 | - 파일명: applicationContext.xml(관례) 211 | - XML 이용 컨텍스트: (보통)GenericXmlApplicationContext, ClassPathXmlApplicationContext 212 | - ClassPathXmlApplicationContext: 클래스패스의 경로를 클래스에서 가져오도록 도와줌 213 | 214 | ![Untitled](/img/table1-2.png) 215 | 216 | ### 예시 217 | 218 | ![Untitled](/img/list1-48(1).png) 219 | 220 | ![Untitled](/img/list1-48(2).png) -------------------------------------------------------------------------------- /2장_테스트/2장_테스트_김석래.md: -------------------------------------------------------------------------------- 1 | # 2장 테스트 2 | 3 | ## 테스트 4 | 5 | ![표지](images/_1.png) 6 | 7 | ![2장 테스트 내용](images/_2.png) 8 | 9 | ![지금까지 테스트 코드의 상황](images/_3.png) 10 | 11 | ## 테스트 코드 개선하기 12 | 13 | ![테스트 코드 개선하기 - 설정 정보](images/_4.png) 14 | 15 | ![테스트 코드 개선하기 - 성공하는 테스트](images/_5.png) 16 | 17 | ![테스트 코드 개선하기 - 성공하는 테스트](images/_6.png) 18 | 19 | ![테스트 코드 개선하기 - 중복 코드의 제거](images/_7.png) 20 | 21 | ![테스트 코드 개선하기 - 픽스처](images/_8.png) 22 | 23 | ## 스프링 테스트 적용 24 | 25 | ![스프링 테스트 적용 - 스프링 테스트 컨텍스트 프레임워크 적용](images/_9.png) 26 | 27 | ![스프링 테스트 적용 - 직접적으로 빈을 DI 받아 사용하기](images/_10.png) 28 | 29 | ![스프링 테스트 적용 - 직접적으로 빈을 DI 받아 사용하기](images/_11.png) 30 | 31 | ![스프링 테스트 적용 - 테스트를 위한 수동 DI 적용하기](images/_11.png) 32 | 33 | ![스프링 테스트 적용 - 컨테이너 없는 DI 적용하기](images/_12.png) 34 | 35 | ## 학습 테스트로 배우는 스프링 36 | 37 | ![학습 테스트로 배우는 스프링](images/_13.png) -------------------------------------------------------------------------------- /2장_테스트/2장_테스트_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/2장_테스트_안송이.pdf -------------------------------------------------------------------------------- /2장_테스트/2장_테스트_이형원.md: -------------------------------------------------------------------------------- 1 | #2장 테스트 2 | 3 | 스프링이 개발자에게 제공하는 가장 중요한 가치 -> 객체지향과 테스트. 4 | 5 | 6 | *** 7 | 8 | ## 2.1 UserDaoTest 다시보기 9 | 10 | ### 2.1.1 테스트의 유용성 11 | 12 | - 테스트란 결국 내가 예상하고 의도했던 대로 코드가 정확히 동작하는지를 확인해서, 만든 코드를 확신할 수 있게 해주는 작업이다. 13 | - 또한, 테스트의 결과가 원하는 대로 나오지 않는 경우에는 코드나 설계에 결함이 있음을 알 수 있다. 14 | 15 | ### 2.1.2 UserDAOTest 특징 16 | 17 | ## UnitTest 18 | - 작은 단위의 테스트 : 테스트하고자 하는 대상이 명확하다면 그 대상에만 집중해서 테스트하는 것이 바람직하다. 19 | - 일반적으로 단위는 작을수록 좋다. 20 | 21 | ### 단위 테스트를 하는 이유 22 | - 개발자가 설계하고 만든 코드가 원래 의도한 대로 동작하는지를 개발자 스스로 빨리 확인받기 위해서이다. 23 | 24 | ## 단위 테스트는 항상 일관성 있는 결과가 보장돼야 한다. 25 | *** 26 | ## 2.2 UserDaoTest 개선 27 | 28 | ## JUnit 테스트로 전환 29 | - Junit은 프레임워크이다. 30 | - 프레임워크의 기본 동작원리가 바로 제어의 역전이다. 프레임워크는 개발자가 만든 클래스에 대한 제어 권한을 넘겨받아서 주도적으로 애플리케이션의 흐름을 제어한다. 31 | 32 | 33 | ### JUnit 프레임워크 요구하는 두가지 조건 34 | 1. 메소드가 public 선언 35 | 2. @Test 메소드에 붙여주기 36 | 37 | 38 | *** 39 | ## 2.3 개발자를 위한 테스팅 프레임워크 JUnit 40 | 41 | 42 | ### 테스트 주도 개발 43 | 44 | - 테스트 코드를 먼저 만들고, 테스트를 성공하게 해주는 코드를 작성하는 방식의 개발 방법. 45 | - 테스트를 작성하고 이를 성공시키는 코드를 만드는 작업의 주기를 가능한 한 짧게 권장. 46 | 47 | ### 테스트 코드 개선 48 | 49 | - @Before 50 | 51 | > JUnit 테스트 수행 방식 52 | 1. 테스트 클래스에서 @Test 가 붙은 public 이고 void형이며 파라미터가 없는 테스트 메서드를 모두 찾는다. 53 | 2. 테스트 클래스의 오브젝트를 하나 만든다. 54 | 3. @Before 가 붙은 메소드가 있으면 실행한다. 55 | 4. @Test가 붙은 메소드를 하나 호출하고 테스트 결과를 저장해둔다. 56 | 5. @After가 붙은 메소드가 있으면 실행한다. 57 | 6. 나머지 테스트 메소드에 대해 2~5번 반복 58 | 7. 모든 테스트의 결과 종합 후 돌려준다. 59 | 60 | - 각 테스트 메소드를 실행할 때 마다 테스트 클래스의 오브젝트를 새로 만든다. 한번 만들어진 테스트 클래스의 오브젝트는 하나의 테스트 메소드를 사용하고 나면 버려진다. 61 | - JUnit은 개발자는 각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 확실히 보장해주기 위해 매번 새로운 오브젝트를 만들게 했다. 62 | 63 | ### 픽스쳐 : 테스트를 수행하는 데 필요한 정보나 오브젝트. 64 | 65 | *** 66 | 67 | ## 2.4 스프링 테스트 적용 68 | 69 | - @Before 메소드가 테스트 메소드 개수만큼 반복되기 때문에 어플리케이션 컨텍스트도 세번 만들어진다. 70 | - 빈이 많아지고 복잡해지면 어플리케이션 컨텍스트 생성에 적지 않은 시간이 걸릴 수 있다. 71 | - 어플리케이션 컨텍스트가 만들어질 때는 모든 싱글톤 빈 오브젝트를 초기화한다. 72 | - 스태틱 필드에 어플리케이션 컨텍스트를 저장해주자! 73 | 74 | 75 | ### 2.4.1 테스트를 위한 어플리케이션 컨텍스트 관리. 76 | 77 | > ```java 78 | > @RunWith(SpringJunit$ClassRunner.class) 79 | > @ContextConfiguration(locations= "/applicationContext.xml") 80 | > public class UserDaoTest { 81 | > @Autowired 82 | > private ApplicationContext context; 83 | > 84 | > ... 85 | > 86 | > } 87 | >``` 88 | 89 | - 인스턴스 변수인 context는 어디에서도 초기화해주는 코드가 없다. 90 | 91 | - @Runwith은 Junit 프레임워크의 테스트 실행 방법을 확장할 때 사용하는 어노테이션이다. 92 | - SpringJUnit4ClassRunner 라는 JUnit용 테스트 컨텍스트 프레임워크 확장 클래스를 지정해주면 테스트를 진행하는 중에 테스트가 사용할 어플리케이션 컨텍스트를 만들고 관리 해주는 작업을 해준다. 93 | - @ContextConfiguration 은 자동으로 만들어줄 어플리케이션 컨텍스트의 설정파일 위치를 지정한 것이다. 94 | 95 | ### 테스트 메소드의 컨텍스트 공유 96 | - context 는 매번 동일 한 오브젝트 값. 97 | - 첫 번째 테스트가 실행될 떄 최초로 어플리케이션 컨텍스트가 처음 만들어지면서 가장 오랜 시간이 소모되고, 그 다음부터는 이미 만들어진 어플리케이션 컨텍스트를 재사용 하기 떄문에 테스트 시간이 매우 짧아진다. 98 | 99 | ### 테스트 클래스의 컨텍스트 공유 100 | - 두 개의 테스트 클래스가 같은 설정파일을 사용하는 경우에는 테스트 수행중에 단 한개의 어플리케이션 컨텍스트만 만들어지고 공유한다. 101 | 102 | ### @Autowired 103 | - 스프링의 DI에 사용되는 특별한 어노테이션. 104 | - @autowired가 붙은 인스턴스 변수가 있으면, 변수 타입과 일치하는 컨텍스트 내의 빈을 찾아 주입해준다. 105 | - @Autowired 는 변수에 할당 가능한 타입을 가진 빈을 자동으로 찾는다. 106 | - 타입으로 가져올 빈 하나를 선택할 수 없는 경우에는 변수의 이름과 같은 이름의 빈이 있는지 확인한데 107 | 108 | 109 | ##DI 와 테스트 110 | 111 | > " 우리는 절대로 DataSource의 구현 클래스를 바꾸지 않을 것이다. 이럴 때도 굳이 인터페이스를 사용하고 DI를 통해 주입하여야하는가?" 112 | 1. 소프트웨어 개발에서 절대로 바뀌지 않는 것은 없기 때문이다. 113 | 2. 클래스의 구현 방식은 바뀌지 않는다고 하더라도 인터페이스를 두고 DI를 적용하게 해두면 다른 차원의 서비스 기능을 도입할 수 있기 때문이다. 114 | 3. 테스트 떄문이다. 단지 효율적인 테스트를 손쉽게 만들기 위해서는 DI를 적용해야 한다. 115 | 116 | 117 | *** 118 | -------------------------------------------------------------------------------- /2장_테스트/images/_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_1.png -------------------------------------------------------------------------------- /2장_테스트/images/_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_10.png -------------------------------------------------------------------------------- /2장_테스트/images/_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_11.png -------------------------------------------------------------------------------- /2장_테스트/images/_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_12.png -------------------------------------------------------------------------------- /2장_테스트/images/_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_13.png -------------------------------------------------------------------------------- /2장_테스트/images/_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_2.png -------------------------------------------------------------------------------- /2장_테스트/images/_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_3.png -------------------------------------------------------------------------------- /2장_테스트/images/_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_4.png -------------------------------------------------------------------------------- /2장_테스트/images/_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_5.png -------------------------------------------------------------------------------- /2장_테스트/images/_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_6.png -------------------------------------------------------------------------------- /2장_테스트/images/_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_7.png -------------------------------------------------------------------------------- /2장_테스트/images/_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_8.png -------------------------------------------------------------------------------- /2장_테스트/images/_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/2장_테스트/images/_9.png -------------------------------------------------------------------------------- /3장_템플릿/3장 템플릿_조수진.md: -------------------------------------------------------------------------------- 1 | # 3장. 템플릿 2 | ### 개방 폐쇄 원칙(OCP) 3 | 코드의 어떤 부분은 변경을 통해 확장되려는 성질, 어떤 부분은 고정되어 변하지 않는 성질을 가짐 4 | 5 | → 변화의 특성이 다른 부분을 구분하고, 각각 다른 목적과 이유에 의해 다른 시점에 독립적으로 변경될 수 있도록 효율적인 구조를 만들어주는 것 6 | 7 | 8 | ## 3.1 다시 보는 초난감 DAO 9 | 10 | 제한적인 리소스를 공유해 사용하는 서버에서 동작하는 JDBC 코드는, 예외가 발생하면 사용한 리소스를 반드시 반환하도록 예외처리를 해주어야한다 (close 메서드 호출) 11 | 12 | → try, catch, finally 블록과 리소스의 null 체크, finally 블록 내에서 close 호출을 위한 try-catch 블록 필요 13 | 14 | **Tip!** Connection과 PreparedStatement는 보통 풀 방식으로 운영됨 15 | 16 | ## 3.2 변하는 것과 변하지 않는 것 17 | 18 | try-catch-finally 블록이 반복됨 19 | 20 | → 메서드마다 복사할 때 잘못 복사하거나, 잘못 삭제할 가능성 21 | 22 | → 커넥션이 반환되지 않고 쌓이다가 어느 순간 리소스가 꽉찼다는 에러가 나며 서비스가 중단 될 것 23 | 24 | ⇒ 중복을 분리하고 재사용하기 위한 방안 필요 25 | 26 | ⇒ 핵심은 변하지 않지만 많은 곳에서 중복되는 코드와 로직에 따라 확장되고 자주 변하는 코드를 분리하는 것 27 | 28 | <개선할 기존의 deleteAll 메서드> 29 | 30 | ```java 31 | Connection c = null; 32 | PreparedStatement ps = null; 33 | 34 | try { 35 | c = dataSource.getConnection(); 36 | ps = c.prepareStatement("delete from users"); -> 여기만 변하는 부분 37 | ps.executeUpdate(); 38 | } catch (SQLException e) { 39 | throw e; 40 | } finally { 41 | if (ps != null) { try { ps.close(); } catch (SQLException e) } 42 | if (c != null) { try { c.close(); } catch (SQLException e) } 43 | } 44 | ``` 45 | 46 | <각 메서드에서 변경되는 부분들> 47 | 48 | ```java 49 | // deleteAll() 50 | ps = c.prepareStatement("delete from users"); 51 | 52 | // add() 53 | ps = c.prepareStatement("insert into users(id,name, password) values (?, ?, ?)"); 54 | ps.setString(1, user.getId()); 55 | ps.setString(2, user.getName()); 56 | ps.setString(3, user.getPassword()); 57 | ``` 58 | 59 | 1. 메서드 추출 60 | 61 | → Nope. 변경되는 부분이 메서드로 추출되어서 재사용 할 수 없다 62 | 63 | 2. 템플릿 메소드 패턴 적용 64 | 65 | → OCP 지켜짐. 하지만 DAO 메소드마다 상속을 통해 새로운 클래스를 만들어야한다. 또한 확장 구조가 이미 클래스를 설계하는 시점에 고정되어 버림(템플릿 메소드 패턴의 단점) 66 | 67 | ![](/img/pic3-1.png) 68 | 69 | 3. 전략 패턴 적용 70 | 71 | → OCP를 지키면서 템플릿 메소드 패턴보다 유연하고 확장성이 뛰어남. 하지만 컨텍스트 내부에서 전략 구체 클래스를 생성하면 적절하지 않다 72 | 73 | deleteAll()에서 변하지 않는 부분이 contextMethod, 변하는 부분이 전략 74 | 75 | ![](/img/pic3-2.png) 76 | 77 | 4. DI 적용을 위한 클라이언트/컨텍스트 분리 78 | 79 | → 컨텍스트가 어떤 전략을 사용할지 Client가 결정해서 전달하도록 수정 80 | 81 | ⇒ DI는 전략 패턴의 장점을 일반적으로 활용할 수 있도록 만든 구조 82 | 83 | ![](/img/pic3-3.png) 84 | 85 | 86 | ```java 87 | public void deleteAll() throws SQLException { // 클라이언트 메소드 88 | StatementStrategy st = new DeleteAllStatement(); // 전략 89 | jdbcContextWithStatementStrategy(st); // 컨텍스트를 메서드로 분리, 전략 오브젝트 전달 90 | } 91 | ``` 92 | 93 | **Tip!** 마이크로 DI(수동 DI): 의존관계에 있는 두 오브젝트, 팩토리, 클라이언트가 구분되어 있지 않고 부분적으로 결합되어 있는 상태. IoC 컨테이너의 도움없이 매우 작은 단위의 코드나 메서드 내에서 DI를 적용한 것 94 | 95 | ## 3.3 JDBC 전략 패턴의 최적화 96 | 97 | ### 전략패턴과 DI로 리팩토링한 코드의 문제점 98 | 99 | - 기존 UserDao때보다 클래스 파일의 개수가 아주 많이 늘어남. 템플릿 메소드 패턴에 비해 딱히 이점이 없음 100 | - 전략 객체에 전달해야할 정보가 있는 경우(insert 메서드에 user정보 등) 생성자와 변수를 만들어야한다 101 | 102 | ### 해결방법 1 - 로컬 클래스 103 | 104 | DeleteAllStatement, ADdStatement 등은 UserDao에서만 사용되고 한 메서드에 강하게 결합되어 있다 105 | 106 | → 클래스 내부에서 로컬 클래스로 적용 107 | 108 | ⇒ 클래스 파일이 줄고, 전략과 메서드를 함께 볼 수 있으니 코드 이해가 좋아짐, 생성자와 변수 선언이 불필요해짐 109 | 110 | - 주의: 내부 클래스에서 외부 변수를 사용할 때 외부 변수는 반드시 final로 선언해줘야 한다 111 | 112 | ### 해결 방법2 - 익명 내부 클래스 113 | 114 | AddStatement는 add()에서만 사용되니 굳이 이름도 필요 없음 115 | 116 | → 익명 클래스로 파라미터로 전달하기 117 | 118 | ### 참고) 중첩 클래스(nested class) 종류 119 | 120 | 1. static class: 독립적으로 오브젝트로 만들어질 수 있음 121 | 2. inner class: 자신이 정의된 크랠스의 오브젝트 안에서만 만들어질 수 있음. 범위(scope)에 따라 아래로 구분 122 | - member inner class: 멤버 필드처럼 오브젝트 레벨에 정의되는 클래스 123 | - local class: 메소드 레벨에 정의되는 클래스 124 | - anonymous inner class: 이름을 갖지 않는 클래스 125 | 126 | ## 3.4 컨텍스트와 DI 127 | 128 | UserDao 내부에 메서드로 만든 context는 다른 Dao에서도 사용이 가능하다 129 | 130 | → 클래스 분리, 재사용, DI 적용 131 | 132 | ### DI 방법1 - 스프링 빈으로 DI 133 | 134 | - JdbcContext는 상태가 없고 메소드를 제공하는 서비스 오브젝트 → 싱글톤으로 사용하는 것이 적절 135 | - DI를 위해서는 주입받는 오브젝트, 주입하는 오브젝트 모두 빈이어야함 136 | 137 | ### DI 방법 2 - 코드를 이용하는 수동 DI 138 | 139 | - 싱글톤으로 만들어 사용하는 것을 포기해야함 140 | - 의존하는 DataSource를 Dao가 DI받고 그걸로 JdbcContext생성 141 | 142 | ### 인터페이스를 사용하지 않는 DI 143 | 144 | - 인터페이스 없이 빈으로 주입하는 것도 DI인가? 145 | - 인터페이스를 사이에 두고 클래스 레벨의 의존관계를 고정하지 않는다는 DI 개념을 충실히 따르진 X 146 | - 하지만 넓은 범위에서 객체의 생성과 고나계 설정에 대한 제어권한을 외부로 위임했다는 IoC 개념에는 부합 147 | - 인터페이스를 사용하지 않는 다는 것은 두 클래스가 매우 긴밀한 관계를 가지고 강하게 결합되어 있다는 것 148 | 149 | → UserDao는 JdbcContext와 항상 함께 사용된다, 클래스는 구분되어 있지만 응집도가 높다 150 | 151 | → JDBC 방식 대신 ORM으로 변경된다면 JdbcContext를 통채로 바꿔야한다 152 | 153 | → 테스트에서도 다른 JdbcContext 구현체가 필요하지 않다 154 | 155 | ⇒ 굳이 인터페이스를 두지 않고 강력한 결합을 허용하는 것도 적절 156 | 157 | 158 | ## 3.5 템플릿과 콜백 159 | 160 | 전략 패턴 + 익명 내부 클래스 → 스프링에서는 템플릿/콜백 패턴이라고 부른다 161 | 162 | ### 템플릿 163 | 164 | 어떤 목적을 위해 만들어둔 모양이 있는 틀 ex. JSP의 HTML, 템플릿 메소드 패턴의 템플릿 메소드 165 | 166 | ### 콜백 167 | 168 | 특정 로직을 담은 메소드가 실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트 169 | 170 | ### 템플릿/콜백 패턴 특징 171 | 172 | - 여러 메소드를 가진 전략과 달리 콜백은 단일 메소드 인터페이스인 경우가 많음 173 | - 콜백은 일반적으로 익명 내부 클래스 174 | - 콜백은 템플릿의 흐름 중 만들어지는 어떤 정보를 받는 파라미터를 가지는 경우가 많다 175 | - 메서드 단위로 사용할 오브젝트를 새롭게 전달하는 메서드 레벨의 DI 적용 176 | - 콜백 오브젝트가 클라이언트 메소드 내의 정보를 직접 참조함 177 | - 클라이언트와 콜백이 강하게 결합되는 것이 일반 DI와 차이 178 | 179 | ![](/img/pic3-7.png) 180 | 181 | 182 | ### 콜백의 재사용 183 | 184 | 콜백 익명 클래스도 반복되는 것이 있다. 분리해서 재사용 가능! 185 | 186 | ```java 187 | public void deleteAll() throws SQLException { 188 | this.jdbcContext.workWithStatementStrategy( 189 | new StatementStrategy() { 190 | public PreparedStatement makePreparedStatement(Connection c) throws SQLException { 191 | return c.prepareStatement("delete from users"); // 여기만 변함! 192 | } 193 | } 194 | ); 195 | } 196 | ``` 197 | 198 | 1. 메소드로 분리 199 | 2. 콜백과 템플릿 결합 200 | - 분리된 부분이 UserDao 외에 다른 Dao에서도 공유할 수 있음 201 | - JdbcContext 안에 클라이언트와 템플릿, 콜백이 공존하는 구조 202 | - 하나의 목적을 위해 긴밀하게 연관되어 동작하는 응집력 강한 코드이기 때문에 한 군데 모여 있는게 유리 203 | - 구체적인 구현, 내부 전략 패턴, 코드에 의한 DI, 익명 내부 클래스 등의 기술은 감추고 외부에는 꼭 필요한 기능만 제공 204 | 205 | ```java 206 | public class JdbcContext { 207 | ... 208 | public void executeSql(final String query) throws SQLException { 209 | workWithStatementStrategy( 210 | new StatementStrategy() { 211 | public PreparedStatement makePreparedStatement(Connection c) throws SQLException { 212 | return c.prepareStatement(query); 213 | } 214 | } 215 | ); 216 | } 217 | } 218 | 219 | public void deleteAll() throws SQLException { 220 | this.jdbcContext.executeSql("delete from users"); 221 | } 222 | ``` 223 | 224 | ### 템플릿/콜백 패턴 설계 225 | 226 | - 고정된 작업 흐름을 갖고 있으며 여기저기서 자주 반복되고 일부만 면경되는 경우. 그 중 변경되는 부분이 한 애플리케이션 안에서 동시에 여러 종류가 만들어질 수 있따면 고려해볼 것 227 | - 템플릿과 콜백의 경계를 정하고 템플릿이 콜백에게, 콜백에 템플릿에게 전달하는 것이 무엇인지 파악 228 | - 콜백 인터페이스에 **제네릭**을 적용해서 결과의 타입을 다양하게 가져갈 수 있다 229 | 230 | ## 3.6 스프링의 Jdbc Template 231 | 232 | 스프링은 JDBC를 이용하는 Dao에서 사용할 수 있도록 다양한 템플릿과 콜백 제공 233 | 234 | - PreparedStatementCreator의 createPreparedStatement(): makePreparedStatement() 대응 235 | - update(): executeSql()대응 236 | - queryForInt() 237 | - queryForObject() → 하나의 로우 238 | - query() → 여러 로우 리턴 239 | - RowMapper<> 콜백 240 | 241 | ### 추가 개선 방안 242 | 243 | - RowMapper는 상태가 없기 때문에 공유해서 싱글톤으로 사용하도록 변경 가능 244 | - RowMapper는 한 번 만들어지면 변경되지 않는 프로퍼티 같은 성격이라 빈의 DI용 프로퍼티로 만들 수 있음 245 | 246 | → User의 프로퍼티나 테이플 필드 이름이 바뀌거나 매핑 방식이 바뀌어도 UserDao 코드 수정 X 247 | 248 | - Dao 메서드에서 사용하는 SQL을 외부 리소스에 담고 읽어와 사용하게 하기 249 | 250 | → 쿼리 최적화 시에도 UserDao는 변경X 251 | 252 | 253 | ### 참고 254 | 255 | - JdbcTemplate은 Dao 안에서 직접 만들어 사용하는 게 스프링의 관례이긴 하지만 원한다면 얼마든지 싱글톤 빈으로 등록하고 DI 가능 -------------------------------------------------------------------------------- /3장_템플릿/3장_템플릿_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/3장_템플릿_안송이.pdf -------------------------------------------------------------------------------- /3장_템플릿/3장_템플릿_이형원.md: -------------------------------------------------------------------------------- 1 | # 3장 템플릿 2 | 3 | ### 템플릿 4 | - 바뀌는 성질이 다른 코드 중에서 변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성을 가진 부분을 자유롭게 변경되는 성질을 가진 부분으로부터 독립시켜서 효과적으로 5 | 활용할 수 있도록 하는 방법. 6 | 7 | *** 8 | 9 | ## 3.1 다시 보는 초난감 DAO 10 | 11 | ### 3.1.1 예외처리 기능을 갖춘 DAO 12 | 13 | - 이러한 JDBC 코드에서는 어떤 상황에서도 가져온 리소스를 반환하도록 try/catch/finally 구문 사용 권장. 14 | 15 | *** 16 | 17 | ## 3.2 변하는 것과 변하지 않는 것 18 | 19 | 20 | ### try/catch/finally 문제점 21 | - 중첩되고, 모든 메소드마다 반복된다. 22 | 23 | ### 3.2.2 분리와 재사용을 위한 디자인 패턴 적용. 24 | 25 | #### 템플릿 메소드 패턴의 적용 26 | - 상속을 통해 기능을 확장해서 사용하는 부분. 27 | - 변하지 않는 부분은 슈퍼클래스에 두고 변하는 부분은 추상 메소드로 정의해둬서 서브클래스에서 오버라이드하여 새롭게 정의해 쓰도록 하는 것이다. 28 | 29 | 30 | 이러한 방법은 DAO 로직마다 상속을 통해 새로운 클래스를 만들어야 한다. 31 | 32 | ### 전략 패턴의 적용. 33 | 34 | - 개방 폐쇄 원칙을 잘 지키는 구조이면서도 템플릿 메소드 패턴보다 유연하고 확장성이 뛰어난 것이, 오브젝트를 아예 둘로 분리하고 클래스 레벨에서는 인터페이스를 통해서만 35 | 의존하도록 만드는 전략 패턴. 36 | 37 | 컨텍스트가 인터페이스 뿐만 아니라 특정 구현 클래스를 직접 알고 있다는 건, 전략 패턴에도 OCP에도 잘 들어맞는다고 볼 수 없다. 38 | 39 | ### DI 적용을 위한 클라이언트 / 컨텍스트 분리 40 | 41 | > 마이크로 DI 42 | > > 의존관계 주입의 가장 중요한 개념은 제 3자의 도움을 통해 두 오브젝트 사이의 유연한 관계가 설정되도록 만드는 것이다. 43 | 44 | 45 | 46 | *** 47 | 48 | ## 3.3 JDBC 전략 패턴의 최적화. 49 | 50 | 51 | ### 3.3.2 전략과 클라이언트의 동거 52 | 53 | 문제점 54 | - DAO 메소드마다 새로운 StatementStrategy 구현 클래스르 만들어야 한다는 점 -> 클래스 파일의 개수가 많이 늘어난다. 55 | - 부가적인 정보가 있는 경우, 이를 위해 오브젝트를 전달받는 생성자와 이를 저장해둘 인스턴스 변수를 번거롭게 만들어야 한다는 점. 56 | 57 | 58 | ### 로컬클래스 59 | - 클래스 파일이 많아지는 문제의 간단한 해결 방법. 클래스 안에 내부 클래스로 정의해버리는 것이다. 60 | 61 | 62 | > 중첩클래스의 종류 (다른 클래스 내부에 정의되는 클래스) 63 | > > 독립적으로 오브젝트로 만들어 질 수 있는 ***스태틱 클래스*** 64 | > > 자신이 정의된 클래스의 오브젝트 안에서만 만들어질 수 있는 ***내부 클래스*** 65 | 66 | 67 | *** 68 | ## 3.4 컨텍스트와 DI 69 | 70 | 71 | ### 스프링 빈으로 DI 72 | 73 | > 인터페이스를 사용하지 않고 DI를 적용하는 것은 문제가 없을까? 236p 74 | - 인터페이스를 사이에 둬서 클래스레벨에서는 의존관계가 고정되지 않게 하고, 런타임 시에 의존할 오브젝트와의 관계를 다이나믹 하게 주입해주는것이 맞다. 75 | - 그러나 스프링의 DI는 넓게 보자면 객체의 생성과 관계설정에 대해 제어권한을 오브젝트에서 제거하고 외부로 위임했다는 IoC 라는 개념을 포괄한다. 76 | 77 | 78 | *** 79 | ## 3.5 템플릿과 콜백 80 | 81 | 82 | ### 템플릿 콜백 패턴 83 | 84 | - 템플릿-콜백패턴이란 결국 전략패턴의 변형된 형태라고 봐야합니다. 전략패턴의 기본적 구조에 변화되는 부분을 매번 클래스로 만들지 않고, '익명 내부 클래스' 바로 생성하여 이용합니다. 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /3장_템플릿/README.md: -------------------------------------------------------------------------------- 1 | # 3장 템플릿 2 | 3 | ## 템플릿 4 | 5 | ![표지](images/001.jpeg) 6 | 7 | ![키워드 그래프](images/002.jpeg) 8 | 9 | - 템플릿이라는 개념을 현재 코드에 적용하기 위한 생각하기 10 | - 템플릿을 적용하기 위해서는 컨텍스트와 전략에 속하는 코드를 구분할 줄 알아야 한다. 11 | - 컨텍스트와 전략을 구분하고 클래스 간의 관계를 설정할 때 DI를 사용한다. 12 | - 클라이언트와 템플릿, 전략 패턴 간의 클래스를 구성하는 방식은 템플릿 / 콜백의 설계 방식으로 볼 수 있다. 13 | 14 | ### 템플릿 15 | 16 | ![템플릿?](images/003.jpeg) 17 | 18 | - 템플릿의 개념은 어떠한 부분에 관심을 갖고 있을까? 19 | - 템플릿도 추구하는 바는 OCP이다. 20 | - 확장에는 열려 있다는 개념에는 전략 패턴을 적용, 변경에는 닫혀 있다는 개념에는 컨텍스트로 정의한다. 21 | - 결국, 변경 가능성이 적은 코드와 다양한 기능을 제공할 가능성이 높은 코드를 구분할 줄 아는 것이 중요하다. 22 | 23 | ![문제 상황 파악하기](images/004.jpeg) 24 | 25 | ### 템플릿 패턴을 통한 예외처리 개선 26 | 27 | > 성격이 다른 코드를 찾아내는 것 28 | 29 | ![자원 관리를 위한 예외처리 개선하기](images/005.jpeg) 30 | 31 | > 성격이 다른 코드를 잘 분리하는 것 32 | 33 | ![메서드 추출로 시작하자](images/006.jpeg) 34 | 35 | > 다양한 기능을 제공하는 코드를 전략 패턴으로 구성하는 것 36 | 37 | ![컨텍스트와 전략의 분리 설계](images/007.jpeg) 38 | 39 | > 컨텍스트를 메서드로 분리 및 전략 패턴의 적용 40 | 41 | ![컨텍스트와 전략의 분리](images/008.jpeg) 42 | 43 | ### 확장을 위한 의존성 주입 44 | 45 | > 컨텍스트를 공통 클래스로 구현로 구현, 전략 패턴을 익명 내부 클래스로 구현 46 | 47 | ![컨텍스트를 메서드에서 클래스로 구현](images/009.jpeg) 48 | 49 | > 컨텍스트와 전략 패턴으로 설계와 DI의 방식을 정의하기 50 | 51 | ![DI의 두 가지 방식](images/010.jpeg) 52 | 53 | > UserDao에서 JdbcContext의 팩토리와 DI 역할을 위임하기 54 | 55 | ![코드로 관리하는 DI](images/011.jpeg) 56 | 57 | > DI의 정석적인 정의와 `특이 케이스`에 대한 DI 58 | 59 | ![DI에 대한 정의를 되새기기](images/012.jpeg) 60 | 61 | ### 템플릿과 콜백 62 | 63 | > 템플릿과 콜백의 동작방식 이해 64 | 65 | ![템플릿과 콜백의 방식](images/013.jpeg) 66 | 67 | > 템플릿과 콜백의 동작방식 적용 68 | 69 | ![템플릿과 콜백의 방식 적용](images/014.jpeg) 70 | 71 | > 템플릿 / 콜백의 동작방식 확인 72 | 73 | ![템플릿과 콜백 코드 구현](images/015.jpeg) 74 | 75 | > 최종적으로 구현된 템플릿 / 콜백 패턴을 갖는 JdbcContext 클래스 76 | 77 | ![템플릿의 재사용성을 위한 리펙토링](images/016.jpeg) 78 | 79 | ### 정리 80 | 81 | ![정리](images/017.jpeg) -------------------------------------------------------------------------------- /3장_템플릿/images/001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/001.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/002.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/003.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/003.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/004.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/004.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/005.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/005.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/006.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/006.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/007.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/007.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/008.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/008.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/009.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/009.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/010.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/010.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/011.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/011.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/012.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/012.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/013.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/013.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/014.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/014.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/015.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/015.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/016.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/016.jpeg -------------------------------------------------------------------------------- /3장_템플릿/images/017.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/3장_템플릿/images/017.jpeg -------------------------------------------------------------------------------- /4장_예외/4장 예외_조수진.md: -------------------------------------------------------------------------------- 1 | # 4장. 예외 2 | 예외 코드는 무성의하게 만들어지기 쉽지만 잘못도니 예외처리 코드 때문에 찾기 힘든 버그를 낳을 수도 있고, 생각지 않았던 예외상황이 발생했을 때 상상 이상으로 난처해질 수 도 있다. 3 | 4 | ## 4.1 사라진 SQLException 5 | 6 | - JdbcTemplate을 적용하면 JdbcTemplate을 사용하는 메소드들에 `thows SQLException`이 사라진다 7 | 8 | → 스프링의 Exception 처리 전략 9 | 10 | - 예외는 적절하게 복구되거나 아니면 작업을 중단시키고 운영자 혹은 개발자에게 분명하게 통보되어야 한다 11 | 12 | ### 예외 처리 안티 패턴 13 | 14 | 1. 예외 catch 후, 아무처리하지 않기 혹은 화면에 메세지 출력만하기 15 | 16 | → 프로그램 실행 중 어딘가에서 문제가 생겼는데 무시하고 계속 진행 17 | 18 | → 비정상적으로 동작, 메모리나 리소스 낭비가 지속되어서 소진, 엉뚱한 곳에서 오류 발생 가능 19 | 20 | ⇒ 찾기 어려운 버그가 된다 21 | 22 | 2. 최상위 Exception으로 계속 던지는 방식 23 | 24 | → catch 하기 귀찮고, 매번 정확한 이름을 작성해서 throw 하기 귀찮으니 throw Exception으로 던짐 25 | 26 | → Exception을 던지는 메서드를 사용하는 클라이언트에서 유의미한 정보를 얻을 수 없다 27 | 28 | → 결과적으로 복구 가능한 예외도 제대로 다룰 수 있는 기회 박탈 29 | 30 | 31 | ### 예외의 종류 32 | 33 | 1. Error 34 | 35 | → JVM 등 시스템에 비정상적인 상황이 발생했을 때. 복구할 수 있는 방법이 없음. 36 | 37 | ⇒ 어플리케이션에서 잡으면 안됨 38 | 39 | 2. Checked Exception 40 | - RuntimeException을 상속하지 않은 Exception 41 | 42 | → 발생 가능한 예외에 예외 상황 처리할 수 있는 기회를 주기 위해서 적용 43 | 44 | ⇒ 적절히 처리하지 않으면 컴파일 에러 발생(catch, throw) 45 | 46 | → 초기 자바는 예상되는 예외 상황을 Checkecd Exception으로 발생시켰으나 실제로는 할 수 있는 처리가 없는 경우가 많음(ex. IO Exception). 특히 서버 프로그램이 되면서. 47 | 48 | 3. Unchecked Exception(Runtime Exception) 49 | 50 | → 프로그래머의 실수로 프로그램에 오류가 있을 때 발생하는 예외(ex. Null 체크 안한 레퍼런스 변수) 51 | 52 | → catch, throw 하지 않아도 됨 53 | 54 | 55 | ![](/img/pic4-1.png) 56 | 57 | ### 예외 처리 방법 58 | 59 | 1. 예외 복구 60 | - 예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 것 61 | - 네트워크 불안정으로 SQLException 발생 62 | 63 | → 재시도 64 | 65 | 2. 예외 처리 회피 66 | - 현재 클래스에서 할 수 있는 작업이 없을 때, 적절한 클래스에서 예외를 처리할 수 있도록 throw 67 | - 예외 회피 시, 의도가 분명해야 함(Ex. 콜백/템플릿처럼 긴밀한 관계에 있는 다른 오브젝트에게 예외처리 책임 지게 등) 68 | 3. 예외 전환 69 | 70 | → 예외를 복구할 수 없을 때 적절한 예외로 바꿔서 던짐 71 | 72 | → 보통 중첩 예외로 만드는 것이 좋음 73 | 74 | - API가 발생하는 기술적인 로우 레벨을 상황에 적합한 의미를 가진 예외로 변경 75 | - 예외 처리를 쉽고 단순하게 하기 위해 Checked Exception → RuntimeException으로 래핑 76 | - 의미있는 시스템 내부의 어플리케이션 예외를 CheckedException으로(반드시 처리하도록 강제) 77 | 78 | ### 예외 처리 전략 79 | 80 | 자바의 환경이 서버로 이동하면서 예외 활용도와 가치가 떨어짐 81 | 82 | → 대응 불가능한 체크 예외라면 빨리 런타임 예외로 전환해서 던지는 것이 낫다 83 | 84 | ### 애플리케이션 예외 85 | 86 | 시스템 또는 외부의 예외 상황이 원인이 아니라 애플리케이션 자체의 로직에 의해 의도적으로 발생시키고 반드시 catch 해서 무언가 조치를 취하도록 요구하는 예외 87 | 88 | - 리턴 값을 다르게 줌 → if 문이 자주 등장하고, 코드의 의미를 통일하기 어렵다 89 | - 예외 상황에서 비지니스적 의미를 띤 예외를 던지도록 함 90 | 91 | ### 스프링의 예외 처리 전략 92 | 93 | SQLException 등 대부분의 예외를 복구할 수 있는 방법이 없다 94 | 95 | → 기계적이고 무의미한 throw 코드가 늘지 않도록 런타임 예외로 전환 96 | 97 | ## 4.2 예외 전환 98 | 99 | - 스프링은 예외 전환 전략으로 예외를 처리한다 100 | 101 | ### JDBC의 한계 102 | 103 | 추상화 된 JDBC API를 통해 일관적으로 프로그램을 개발할 수 있다. 하지만, DB 종류에 자유로운 데이터 엑세스 코드 작성하기는 쉽지 않다 104 | 105 | - 비표준 SQL을 쓸 수 밖에 없다 → 데이터 엑세스 기술에 종속적인 DAO가 된다 106 | - SQLException의 DB 에러 정보가 호환성이 없다 107 | - SQLState는 정확하지 않다 108 | - SQLErrorCode는 비교적 믿을만 하지만, 같은 상황에 대해 DB별로 다른 값을 내려준다 109 | 110 | ### 스프링의 문제 해결 방법 111 | 112 | DB 에러 코드를 매핑해서 계층화된 예외로 전환 113 | 114 | → DB 종류에 상관없이 동일 상황에서 일관된 예외를 전달하도록 115 | 116 | → 전환되는 JdbcTemplate의 예외는 모두 DataAccessException의 서브클래스 타입 117 | 118 | **참고!** JDK 1.6에 포함된 JDBC 4.0부터는 SQLException을 스프링 DataAccessException 처럼 세분화 시킴. 하지만, CheckedException이며 세분화 하는 기준이 SQLState를 이용하므로 정확하지 않음. 스프링의 Data Access Exception 사용이 이상적임 119 | 120 | ### 데이터 액세스 기술과 구현 방법에 독립적인 이상적인 DAO 121 | 122 | - DAO를 인터페이스로 구현하기 위해 Exception을 던지는 부분도 메소드에 포함되어야 한다 123 | 124 | → JDBC, JPA 등 데이터 엑세스 기술에 따라 달라짐 125 | 126 | → 스프링 DataAccessException을 이용해서 런타임으로 포장 127 | 128 | ⇒ throw XXXException 부분 생략 가능 & 명시적으로 표현할 필요 있을 때도 선언 가능 129 | 130 | 131 | ⇒ 인터페이스, 런타임 예외 전환, DataAccessException 예외 추상화 적용으로 독립적인 DAO 생성 가능 132 | 133 | **주의사항!** JDBC의 경우 SQLException에 담긴 DB 에러 코드를 바로 해석할 수 있지만, JPA, 하이버네이트, JDP 등은 각 기술이 재정의한 예외를 가져와서 스프링이 최종적으로 변환함 134 | 135 | - JDBC는 스프링이 DuplicatedKeyException 발생 136 | - 하이버네이트는 ConstraintViolationException 발생 137 | 138 | → 포괄적인 DataIntegrityViolationException으로 변환 139 | 140 | - DuplicatedKeyException도 DataIntegrityViolationException의 한 종류 141 | 142 | ⇒ 어느정도 추상화된 공통 예외로 받거나, 커스텀 예외를 생성해 동일한 상황에서 중첩 예외로 전환 143 | 144 | ### SQLErrorCodeSQLExceptionTranslator 145 | 146 | - SQLException을 코드에서 직접 전환할 수 있도록 도와주는 클래스 147 | 148 | ```java 149 | SQLException sqlEx = (SQLException)ex.getRootCause(); 150 | SQLExceptionTranslator set = new SQLErrorCodeSQLExceptionTranslator(this.dataSource); 151 | 152 | assertThat(set.translate(null, null, sqlEx)).is(DuplicateKeyException.class); 153 | ``` -------------------------------------------------------------------------------- /4장_예외/4장_예외_김석래.md: -------------------------------------------------------------------------------- 1 | # 4. 예외 2 | 3 | ## 예외 4 | 5 | ![](images/001.jpeg) 6 | 7 | ![](images/002.jpeg) 8 | 9 | ### 예외를 처리하는 방식 10 | 11 | ![](images/003.jpeg) 12 | 13 | ### 예외를 처리하는 방법 14 | 15 | ![](images/004.jpeg) 16 | 17 | ### 예외 추상화와 예외 계층 구조 18 | 19 | ![](images/005.jpeg) 20 | 21 | ### 정리 22 | 23 | ![](images/006.jpeg) -------------------------------------------------------------------------------- /4장_예외/4장_예외_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/4장_예외/4장_예외_안송이.pdf -------------------------------------------------------------------------------- /4장_예외/4장_예외_이형원.md: -------------------------------------------------------------------------------- 1 | # 4.예외처리 2 | 3 | *** 4 | 5 | ### 예외를 처리할 때 반드시 지켜야 할 핵심 원칙 6 | - 모든 예외는 적절하게 복구되든지 아니면 작업을 중단시키고 운영자 또는 개발자에게 분명히 통보돼야한다. 7 | 8 | 9 | ### 예외의 종류와 특징 10 | - Error 11 | - 에러는 시스템에 뭔가 비정상적인 상황이 발생했을 경우에 사용된다. 12 | - 주로 VM에서 발생시키는 것이고 애플리케이션 코드에서 잡으려고 하면 안 된다. 13 | - 따라서 시스템 레벨에서 특별한 작업을 하는게 아니라면 애플리케이션에서는 이런 에러에 대한 처리는 신경쓰지 않아도 된다. 14 | - Exception 과 체크 예외 15 | - Exception 클래스는 **체크 예외** 와 **언체크 예외**로 구분된다. 16 | >체크 예외 17 | > > Exception 클래스의 서브클래스이면서 RuntimeException 클래스를 상속하지 않은 것. 18 | > 19 | > 언체크 예외 20 | > > RuntimeException 을 상속한 클래스들을 말한다. 21 | - 일반적으로 예외라고 하면 Exception 클래스의 서브클래스 중에서 RuntimeException 을 상속하지 않은 것만을 말하는 체크 예외라고 생각해도 된다. 22 | - 체크 예외가 발생할 수 있는 메소드를 사용할 경우 반드시 예외를 처리하는 코드를 함께 작성해야 한다. 23 | - 사용할 메소드가 체크 예외를 던진다면 이를 catch문으로 잡든 아니면 다시 throw를 정의해서 메소드 밖으로 던져야 한다. 24 | 25 | - RuntimeException과 언체크/런타임 예외 26 | - 예외처리를 강제하지 않기 때문에 언체크 예외라고 불린다. 27 | - ex) NPE, IAE 28 | 29 | 30 | 31 | ### 예외처리 방법 32 | 33 | - 예외복구 34 | - 예외상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 것. 35 | - 예외처리 회피 36 | - 예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것. 37 | - 예외전환 38 | - 발생한 예외를 그대로 넘기는게 아니라 적절한 예외로 전환해서 던지는 것. 39 | 40 | 41 | -------------------------------------------------------------------------------- /4장_예외/images/001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/4장_예외/images/001.jpeg -------------------------------------------------------------------------------- /4장_예외/images/002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/4장_예외/images/002.jpeg -------------------------------------------------------------------------------- /4장_예외/images/003.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/4장_예외/images/003.jpeg -------------------------------------------------------------------------------- /4장_예외/images/004.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/4장_예외/images/004.jpeg -------------------------------------------------------------------------------- /4장_예외/images/005.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/4장_예외/images/005.jpeg -------------------------------------------------------------------------------- /4장_예외/images/006.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/4장_예외/images/006.jpeg -------------------------------------------------------------------------------- /5장_서비스_추상화/5장 서비스 추상화_조수진.md: -------------------------------------------------------------------------------- 1 | # 5장 서비스 추상화 2 | - 서비스 추상화? 특정 기술 환경에 종속되지 않는, 일관된 방법으로 사용할 수 있도록 지원하는 것 3 | - 기능은 유사하나 사용 방법이 다른 로우 레벨의 다양한 기술에 대해 추상 인터페이스와 일관성 있는 접근 방법을 제공 4 | - 추상화? 하위 시스템의 공통접을 뽑아내서 분리시키는 것 5 | - 트랜잭션 경계: 트랜잭션이 시작하고 끝나는(commit, rollback) 위치 6 | - 일반적으로 트랜잭션은 커넥션보다 존재 범위가 짧다 7 | - UserService와 Dao 메서드 마다 Connection을 파라미터로 공유해서 트랜잭션 구현 가능 8 | 9 | → JdbcTemplate 사용 불가, Dao는 데이터 액세스 기술에 독립적일 수 없음, 테스트 코드에도 영향 10 | 11 | ⇒ 트랜잭션 동기화 사용 12 | 13 | - 로컬 트랜잭션은 하나의 DB Connection에 종속됨 14 | 15 | → 글로벌 트랜잭션을 사용해서 여러 DB가 참여하는 작업을 하나의 트랜잭션으로 만들 수 있다 16 | 17 | - 하이버네이트는 Connection을 직접 사용하지 않고 Session을 사용 18 | 19 | → 트랜잭션 추상하 기능으로 애플리케이션에서 직접 각 기술의 트랜잭션 API 사용하지 않고 일관된 방식으로 트랜잭션을 제어하는 트랜젝션 경졔 설정 가능 20 | 21 | → PlatformTransactionManager 추상 인터페이스 & TransactionStatus 22 | 23 | - 수평적 분리: 같은 계층 내에서 기능적인 관심에 따라 분리(UserDao, UserService) 24 | - 수직적 분리: 계층 특성에 따라 분리(애플리케이션 비지니스 로직과 하위에서 동작하는 로우레벨 기술, DataSource 인터페이스를 이용한 UserDao &DB, PlatformTransactionManager 인터페이스르 리용한 UserService와 트랜잭션 기술 분리 등) 25 | - 적절한 분리는 SRP라는 특징으로 나타나고, 스프링에서는 DI를 이용해 적용! 26 | 27 | - 테스트 대역 28 | 29 | JavaMail은 추상화된 계층이 없다 30 | 31 | → 테스트를 위해서 실제 객체를 이용하면 비용이 크다, 테스트에서 관심을 갖는 영역도 아니다 32 | 33 | → 서비스 추상화(MailSender 인터페이스)를 이용해서 테스트 용이하게 만들 수 있음! 34 | 35 | ⇒ 테스트 대역 사용 36 | 37 | - 테스트 stub: 의존 객체의 입력, 출력으로 결과 확인 38 | - 테스트 mock: 테스트 대상 오브젝트와 의존 오브젝트 사이에 일어나는 일을 검증(행위 검증) -------------------------------------------------------------------------------- /5장_서비스_추상화/5장_서비스_추상화_김석래.md: -------------------------------------------------------------------------------- 1 | # 5. 서비스 추상화 2 | 3 | ![](images/001.jpeg) 4 | 5 | ![](images/002.jpeg) 6 | 7 | ## 서비스 추상화 8 | 9 | ![](images/003.jpeg) 10 | 11 | ## 트랜잭션 서비스 추상화 12 | 13 | ![](images/004.jpeg) 14 | 15 | ## 이메일 서비스 추상화 16 | 17 | ![](images/005.jpeg) 18 | 19 | ![](images/006.jpeg) 20 | 21 | ![](images/007.jpeg) 22 | 23 | ## 정리 24 | 25 | ![](images/008.jpeg) 26 | -------------------------------------------------------------------------------- /5장_서비스_추상화/5장_서비스_추상화_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/5장_서비스_추상화_안송이.pdf -------------------------------------------------------------------------------- /5장_서비스_추상화/images/001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/images/001.jpeg -------------------------------------------------------------------------------- /5장_서비스_추상화/images/002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/images/002.jpeg -------------------------------------------------------------------------------- /5장_서비스_추상화/images/003.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/images/003.jpeg -------------------------------------------------------------------------------- /5장_서비스_추상화/images/004.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/images/004.jpeg -------------------------------------------------------------------------------- /5장_서비스_추상화/images/005.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/images/005.jpeg -------------------------------------------------------------------------------- /5장_서비스_추상화/images/006.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/images/006.jpeg -------------------------------------------------------------------------------- /5장_서비스_추상화/images/007.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/images/007.jpeg -------------------------------------------------------------------------------- /5장_서비스_추상화/images/008.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/5장_서비스_추상화/images/008.jpeg -------------------------------------------------------------------------------- /6장_AOP/6장 AOP 1_조수진.md: -------------------------------------------------------------------------------- 1 | ### 6.1 트랜잭션 코드의 분리 2 | 3 | 비지니스 로직을 사이에 두고 트랜잭션 시작과 종료 로직이 위치 4 | 5 | → 분리 가능 6 | 7 | → 메서드 분리 → 클래스 분리 & DI 적용 8 | 9 | ⇒ 인터페이스 추출 & 프록시 패턴으로 트랜잭션 기능 분리 및 비지니스 로직 위임 10 | 11 | ![](/img/pic6-3.png) 12 | 13 | → 비지니스 로직의 코드를 작성할 때 트랜잭션에 대해 전혀 신경 쓸 필요가 없다 14 | 15 | → 비지니스 로직에 대한 테스트를 손쉽게 만들 수 있다 16 | 17 | ### 6.2 고립된 단위 테스트 18 | 19 | 테스트는 가능한 작아야 실패 원인을 찾기 쉽고 테스트의 의도나 내용이 분명해진다. 만들기도 쉽다. 20 | 21 | → 단위 테스트 먼저 생각할 것! 22 | 23 | → 복잡한 의존 관계를 가진 객체 테스트를 위해 테스트 대역으로 대상을 고립시킬 수 있다 24 | 25 | - 스텁(상태 검증 - 리턴값 있는 경우 유용) vs 목(행위 검증) 26 | - 단위 테스트 vs 통합 테스트 27 | - Mockito 프레임워크 28 | 29 | ### 6.3 다이내믹 프록시와 팩토리 빈 30 | 31 | - 프록시 패턴 vs 데코레이터 패턴은 같은 모양이지만, 사용하는 “목적”에 따라 분류 32 | - 데코레이터: 부가 기능을 런타임 시 다이나믹하게 부여하기 위해 사용 33 | - 프록시: 타깃에 대한 접근 방법을 제어하는 목적 34 | - 기존 프록시를 만드는 것의 문제점 35 | - 매번 프록시 클래스 정의하고 인터페이스 구현하고, 메서드 작성하기 번거롭다 36 | - 부가기능 코드가 중복될 가능성이 많다 37 | 38 | → 리플렉션을 이용한 다이나믹 프록시 사용 39 | 40 | ![Untitled](/img/pic6-13.png) 41 | 42 | - InvocationHandler에 부가 기능 작성 (`public Object invoke(Object proxy, Method method, Object[] args)` 메서드 가짐) 43 | 44 | ![Untitled](/img/pic6-14.png) 45 | 46 | - 한 번에 여러 클래스에 공통적인 부가기능을 제공하는 것이 불가능 47 | 48 | (Handler가 target을 필드에 갖고 있기 때문) 49 | 50 | - 한 타깃에 여러 부가기능을 적용하기 어렵다 51 | 52 | ### 6.4 스프링의 프록시 팩토리 빈 53 | 54 | - ProxyFactoryBean: 프록시를 생성해서 빈 오브젝트로 등록하게 해주는 팩토리 빈 55 | - 어드바이스(부가 기능)은 MethodInterceptor 인터페이스를 구현 56 | 57 | → 타깃 상관없이 독립적으로 만들 수 있음 → 싱글톤 빈 등록 가능 58 | 59 | - 포인트컷: 부가기능 적용 대상 메소드 선정 방법 60 | - 어드바이저 = 포인트컷 + 어드바이스 61 | 62 | ![Untitled](/img/pic6-18.png) 63 | 64 | → 템플릿/콜백 구조 -------------------------------------------------------------------------------- /6장_AOP/6장_AOP1_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/6장_AOP1_안송이.pdf -------------------------------------------------------------------------------- /6장_AOP/6장_AOP2_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/6장_AOP2_안송이.pdf -------------------------------------------------------------------------------- /6장_AOP/6장_AOP_김석래.md: -------------------------------------------------------------------------------- 1 | # 6. AOP 2 | 3 | 날짜: 2021년 12월 6일 → 2021년 12월 19일 속성: SR 주제: 6장 키워드: AOP, 관점 지향 프로그래밍 4 | 5 | # AOP 6 | 7 | ![Untitled](images/001.jpeg) 8 | 9 | ## Intro 10 | 11 | 관점 지향 프로그래밍에 대한 내용을 이해해본다. 12 | 13 | ![Untitled](images/002.jpeg) 14 | 15 | ### 단위 테스트 (고립된 테스트) 16 | 17 | > **기존에 작성했던 코드의 기능에 대해 AOP로 분리할 수 있는 부분을 확인** 18 | 19 | - 테스트 대상의 의존 오브젝트를 파악하여 **`테스트 해야 하는 성격을 분리`**해본다. 20 | - UserService의 기능이 동작하기 위해서 `**세 가지 기능**`이 필요하다. 21 | - `**데이터 접근**`에 필요한 로직인 UserDao 22 | - 서비스 레이어에서 DBMS의 `**트랜잭션의 관리**`를 위한 TransactionManager 23 | - **`메일 발송`**을 위한 MailSender 24 | 25 | ![Untitled](images/003.jpeg) 26 | 27 | > **테스트 대상의 의존성을 최대한 줄이기 위한 목 오브젝트** 28 | 29 | - MockMailSender라는 목 오브젝트를 통해 메일 발송 테스트하는 경우 30 | 31 | ![Untitled](images/004.jpeg) 32 | 33 | - MockUserDao라는 목 오브젝트를 통해 데이터 수정 테스트 하는 경우 34 | 35 | ![Untitled](images/005.jpeg) 36 | 37 | ### 단위 테스트와 통합 테스트 38 | 39 | 단위 테스트와 통합 테스트를 작성하는 목적과 방식에 대해서 주의 깊게 살펴보아야 한다. 40 | 41 | ![Untitled](images/006.jpeg) 42 | 43 | > **통합 테스트를 단위 테스트로 작성을 돕는 목 프레임워크** 44 | 45 | - 두 가지 이상의 성격을 갖는 코드를 테스트 하기 위해서는 필히 통합 테스트로 작성해야 하나 목 프레임워크를 통해 단위 테스트로 작성할 수 있다. 46 | 47 | ![Untitled](images/007.jpeg) 48 | 49 | ## 6.3 다이나믹 프록시와 팩토리 빈 50 | 51 | ### 6.3.1 프록시와 프록시 패턴, 데코레이터 패턴 52 | 53 | > **프록시와 사용 목적에 따른 패턴구분** 54 | 55 | ![Untitled](images/008.jpeg) 56 | 57 | ### 6.3.2 다이나믹 프록시 58 | 59 | > **다이나믹 프록시** 60 | 61 | - 기본적인 다이나믹 프록시의 동작방식 62 | 63 | ![Untitled](images/009.jpeg) 64 | 65 | - 다이나믹 프록시 생성 66 | 67 | ![Untitled](images/010.jpeg) 68 | 69 | ## 6.4 스프링 프록시 팩토리 빈 70 | 71 | ### 6.4.1 ProxyFactoryBean 72 | 73 | - JDK 다이나믹 프록시와 스프링 ProxyFactoryBean의 구분 74 | 75 | ![Untitled](images/011.png) 76 | 77 | ## 6.5 스프링 AOP 78 | 79 | ### 6.5.6 AOP의 용어 80 | 81 | > 타깃 82 | 83 | 타깃은 **부가기능**을 부여할 대상이다. 84 | 85 | `**핵심기능**`을 담은 클래스일 수도 있지만 경우에 따라 다른 **`부가기능`**을 제공하는 프록시 오브젝트일 수 있다. 86 | 87 | > 어드바이스 88 | 89 | 타깃에게 제공할 부가기능을 담은 모듈이다. 90 | 91 | 어드바이스는 오브젝트로 정의하기도 하지만 메서드 레벨에서 정의할 수도 있다. 92 | 93 | 어드바이스의 종류는 MethodInterceptor처럼 메서드 호출 과정에 전반적으로 참여하는 것도 있지만, 예외가 발생했을 때만 동작하는 어드바이스처럼 메서드 호출 과정의 일부에서만 동작하는 어드바이스도 있다. 94 | 95 | > 조인 포인트 96 | 97 | 조인 포인트란 어드바이스가 적용될 수있는 위치를 말한다. 98 | 99 | 스프링의 프록시 AOP에서 조인 포인트는 메서드의 실행 단계뿐이다. 100 | 101 | 타깃 오브젝트가 구현한 인터페이스의 모든 메서드는 조인 포인트가 된다. 102 | 103 | > 포인트컷 104 | 105 | 포인트컷이란 어드바이스를 적용할 조인 포인트를 선별하는 작업 또는 그 기능을 정의한 모률을 말한다. 106 | 107 | 스프링 AOP의 조인 포인트는 메소드의 실행이므로 스프링의 포인트컷은 메소드를 선정하는 기능을 갖고 있다. 108 | 109 | 포인트컷 표현식은 메소드의 실행이라는 의미인 execution으로 시작하고, 메소드의 시그니처를 비교하는 방법을 주로 이용한다. 110 | 111 | 메소드는 클래스 안에 존재하는 것이기 때문에 메소드 선정이란 결국 클래스를 선정하고 그 안의 메소드를 선정하는 과정을 거치게 된다. 112 | 113 | > 프록시 114 | 115 | 프록시는 클라이언트와 타깃 사이에 투명하게 존재하면서 부가기능을 제공하는 오브젝트다. 116 | 117 | DI를 통해 타깃 대신 클라이언트에게 주입되며 클라이언트의 메소드 호출을 대신 받아서 타깃에 위임해주면서, 그 과정에서 부가기능을 부여한다. 118 | 119 | 스프링은 프록시를 이용해 AOP를 지원한다. 120 | 121 | > 어드바이저 122 | 123 | 어드바이저는 포인트컷과 어드바이스를 하나씩 갖고 있는 오브젝트다. 124 | 125 | 어드바이저는 어떤 부가기능(어드바이스)을 어디에(포인트컷) 전달할 것인가를 알고 있는 AOP의 가장 기본이 되는 모듈이다. 126 | 127 | 스프링은 `자동 프록시 생성기`가 어드바이저를 AOP 작업의 정보로 활용한다. 128 | 129 | 어드바이저는 스프링 AOP에서만 사용되는 특별한 용어이고, 일반적인 AOP에서는사용되지 않는다. 130 | 131 | > 애스펙트 132 | 133 | OOP의 클래스와 마찬가지로 애스펙트는 AOP의 기본 모듈이다. 134 | 135 | 한 개 또는 그 이상 의 포인트컷과 어드바이스의 조합으로 만들어지며 보통 싱글톤 형태의 오브젝트로 존재한다. 136 | 137 | 따라서 클래스와 같은 모듈 정의와 오브젝트와 같은 실체(인스턴스)의 구분이 특별히 없다. 두 가지 모두 애스펙트라고 불린다. 138 | 139 | 스프링의 어드바이저는 아주 단순한 애스펙트라고 볼 수도 있다. 140 | 141 | ### 6.5.7 AOP 네임스페이스 142 | 143 | 스프링의 프록시 방식 AOP 144 | 145 | - 자동 프록시 생성기 146 | 147 | 스프링의 DefaultAdvisorAutoProxyCreator 클래스를 빈으로 등록한다. 148 | 149 | 다른 빈을 DI 하지도 않고 자신도 DI 되지 않으며 독립적으로 존재한다. 150 | 151 | 애플리케이션 컨텍스트가 빈 오브젝트를 생성하는 과정에 빈 후처리기 로 참여한다. 152 | 153 | 빈으로 등록된 어드바이저를 이용해서 프록시를 자동으로 생성하는 기능을 담당한다. 154 | 155 | - 어드바이스 156 | 157 | 부가기능을 구현한 클래스를 빈으로 등록한다. 158 | 159 | TransactionAdvice는 AOP 관련 빈 중에서 유일하게 직접 구현한 클래스를 사용한다. 160 | 161 | - 포인트컷 162 | 163 | 스프링의 AspectJExpressionPointcut을 빈으로 등록하고 expression 프로퍼티에 포인트컷 표현식을 넣어주면 된다. 164 | 165 | - 어드바이저 166 | 167 | 스프링의 DefaultPointcutAdvisor 클래스를 빈으로 등록해서 사용한다. 168 | 169 | 어드바이스와 포인트컷을 프로퍼티로 참조히는 것 외에는 기능은 없다. 170 | 171 | 자동 프록시 생성기에 의해 자동 검색되어 사용된다. 172 | 173 | 어드바이스를 제외한 나머지 클래스는 스프링이 제공하는 클래스를 빈으로 등록하고 프로퍼티 등록만 하면 사용이 가능하다. 174 | 175 | ## 6.6 트랜잭션 속성 176 | 177 | 현재 챕터는 트랜잭션과 AOP의 기능인 어드바이스, 포인트컷 또는 어드바이저에 대한 지식이 있어야 이해가 쉽다. 178 | 179 | `트랜잭션 매니저`에서 트랜잭션을 가져올 때 `DefaultTransactionDefinition` 오브젝트를 사용한다. 180 | 181 | TransactionAdvice의 트랜잭션 경계설정 코드를 확인해 본다. 182 | 183 | ```java 184 | public class TransactionAdvice implements MethodInterceptor { 185 | 186 | private PlatformTransactionManager transactionManager; 187 | 188 | public void setTransactionManager(PlatformTransactionManager transactionManager) { 189 | this.transactionManager = transactionManager; 190 | } 191 | 192 | @Override 193 | public Object invoke(MethodInvocation invocation) throws Throwable { 194 | TransactionStatus transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition()); 195 | try { 196 | Object ret = invocation.proceed(); 197 | this.transactionManager.commit(transactionStatus); 198 | return ret; 199 | } catch (RuntimeException e) { 200 | this.transactionManager.rollback(transactionStatus); 201 | throw e; 202 | } 203 | } 204 | } 205 | ``` 206 | 207 | `트랜잭션의 경계`는 트랜잭션 매니저에서 트랜잭션을 가져오는 것과 `commit()`, `rollback()` 중의 하나를 호출하는 것으로 설정되고 있다. 208 | 209 | - 트랜잭션을 가져올 때 파라미터로 `트랜잭션 매니저`에게 전달하는 `DefaultTransactionDefinition` 용도가 무엇일까? 210 | 211 | ### 6.6.1 트랜잭션 정의 212 | 213 | 트랜잭션이라고 모두 같은 방식으로 동작하는 것은 아니다. 214 | 215 | 트랜잭션 경계 안에서 진행된 작업은 commit()을 통해 모두 성공하든지 아니면 rollback()을 통해 모두 취소되어야 한다. 216 | 217 | 트랜잭션의 동작방식을 제어할 수 있는 몇 가지 조건이 있다. 218 | 219 | DefaultTransactionDefinition이 구현하고 있는 TransactionDefinition 인터페이스는 트랜잭션의 동작방식에 영향을 줄 수 있는 네 가지 속성을 정의하고 있다. 220 | 221 | > 트랜잭션 전파 222 | 223 | - 트랜잭션 전파(transaction propagation)란 224 | - 트랜잭션의 경계에서 이미 진행 중인 트랜잭션이 있을 때 또는 없을 때 어떻게 동작할 것인가를 결정하는 방식을 말한다. 225 | 226 | - PROPAGATION_REQUIRED 227 | 228 | 가장 많이 사용되는 `DefaultTransactionDefinition`의 트랜잭션 전파 속성이다. 229 | 230 | 진행 중인 트랜잭션이 없으면 새로 시작하고, 이미 시작된 트랜잭션이 있으면 이에 참여 한다. 231 | 232 | 이 트랜잭션 전파 속성을 갖는 코드는 다양한 방식으로 결합해서 하나의 트랜잭션으로 구성하기 쉽다. 233 | 234 | A와 B가 모두 PROPAGATION_REQUIRED로 선언되어 있다면, A, B, A → B, B → A와 같은 네 가지의 조합된 트랜잭션이 모두 가능하다. 235 | 236 | - PROPAGATION_REQUIRES_NEW 237 | 238 | 항상 새로운 트랜잭션을 시작하는 전파 속성으로 앞에서 시작된 트랜잭션이 있든 없든 상관없이 새로운 트랜잭션을 만들어서 독자적으로 동작하게 한다. 239 | 240 | 독립적인 트랜잭션이 보장되어야 하는 코드에 적용할 수 있다. 241 | 242 | - PROPAGATION_NOT_SUPPORTED 243 | 244 | 트랜잭션 없이 동작하도록 하는 트랜잭션 전파 속성으로 진행 중인 트랜잭션이 있어도 무시한다. 245 | 246 | 트랜잭션 없이 동작해야 하는 상황은 보통 트랜잭션 경계설정을 AOP를 이용해 한 번에 많은 메서드에 동시에 적용하게 되는데, 특정 메서드의 트랜잭션 전파 속성만 PROPAGATION_NOT_SUPPORTED로 247 | 설정하여 트랜잭션이 적용되지 않도록 할 수 있다. 248 | 249 | 전파 속성에 대한 정리를 해보면 250 | 251 | 트랜잭션 매니저를 통해 트랜잭션을 시작하려고 할 때 getTransaction()이라는 메서드를 사용하는 이유는 트랜잭션 전파 속성이 있기 때문이다. 252 | 253 | 트랜잭션 전파 속성과 현재 진행 중인 트랜잭션이 존재하는지 여부에 따라 동작 방식을 다르게 할 수 있다. 254 | 255 | 트랜잭션 전파 속성과 현재 진행 중인 트랜잭션이 존재하는지 여부에 따라서 새로운 트랜잭션을 시작할 수도 있고, 이미 진행중인 트랜잭션에 참여하기만 할 수도 있다. 256 | 257 | 진행 중인 트랜잭션에 참여하는 경우 트랜잭션 경계의 끝에서 트랜잭션을 커밋시키지도 않는다. 최초로 트랜잭션을 시작한 경계까지 정상적으로 진행되어야 비로소 커밋될 수 있다. 258 | 259 | > 격리수준 260 | 261 | 모든 DB는 트랜잭션 격리수준(isolation level)을 갖고 있어야 한다. 262 | 263 | 기능하다면 모든 트랜잭션이 순차적으로 진행되어 다른 트랜잭션의 작업에 독립적인 것이 좋겠지만 그러자면 성능이 크게 떨어질 수 밖에 없다. 264 | 265 | 적절하게 격리수준을 조정해서 가능한 한 많은 트랜잭션을 동시에 진행시키면서도 문제가 발생하지 않게 하는 제어가 필요하다. 266 | 267 | 격리수준은 기본적으로 DB에 설정되어 있지만 JDBC 드라이버나 DataSource 등에서 재설정할수 있고, 필요하다면 트랜잭션 단위로 격리 수준을 조정할 수 있다. 268 | 269 | > 제한시간 270 | 271 | 트랜잭션을 수행하는 `제한시간`을 설정할 수 있다. DefaultTransactionDefinition의 기본 설정은 `제한시간이 없는 것`이다. 272 | 273 | 제한시간은 트랜잭션을 직접 시작할 수 있는 `PROPAGATION_REQUIRED`나 `PROPAGATION_REQUIRES_NEW` 와 함께 사용해야 만 의미가 있다. 274 | 275 | > 읽기전용 276 | 277 | 읽기전용(readOnly)으로 설정해두면 트랜잭션 내에서 데이터를 조작하는 시도를 막아줄 수 있을 뿐더러 일부 데이터 액세스 기술에 따라서는 성능면에서 향상시킬 수도 있다. 278 | 279 | TransactionDefinition 타입 오브젝트를 사용하면 네 가지 속성을 이용해 트랜잭션의 동작방식을 제어할 수 있다. 280 | 281 | TransactionAdvice는 트랜잭션 경계설정 기능을 갖고 있으며 TransactionDefinition 오브젝트를 생성하고 사용할 수 있다. 282 | 283 | 디폴트 속성 외에 다른 속성을 사용하려면 외부에서 정의된 TransactionDefinition 오브젝트를 DI 받아서 사용하면 된다. 284 | 285 | 여기 까지 내용으로는 트랜잭션 속성을 변경하면 TransactionAdvice를 사용하는 모든 트랜잭션의 속성이 한번에 바뀌는 문제가 있다. 286 | 287 | 고민해 볼 것은 원하는 메서드만 선택하여 독자적인 트랜잭션 정의를 정의할 방법을 찾아야 한다. 288 | 289 | ### 6.6.2 트랜잭션 인터셉터와 트랜잭션 속성 290 | 291 | 메소드별로 다른 트랜잭션 정의를 적용하려면 어드바이스의 기능을 확장해야 한다. 292 | 293 | > TransactionInterceptor 294 | 295 | 기존에 만들었던 TransactionAdvice를 다시 설계할 필요는 없이 TransactionInterceptor를 통해 트랜잭션 경계설정 어드바이스를 사용할 수 있다. 296 | 297 | TransactionInterceptor 어드바이스의 동작방식은 TransactionAdvice와 다르지 않고, 다만 트랜잭션 정의를 메소드 이름 패턴을 이용해서 다르게 지정할 수 있는 방법을 추가로 제공해준다. 298 | 299 | TransactionInterceptor는 PlatformTransactionManager와 Properties 타입의 두 가지 프로퍼티를 갖고 있다. 300 | 301 | Properties 타입인 두 번째 프로퍼티 이름은 transactionAttributes로, 트랜잭션 속성을 정의한 프로퍼티다. 302 | 303 | 트랜잭션 속성은 TransactionDefinition의 네 가지 기본 항목에 rollbackOn()이라는 메소드를 하나 더 갖고 있는 TransactionAttribute 인터페이스로 정의된다. 304 | 305 | rollbackOn() 메소드는 어떤 예외가 발생하면 롤백을 할 것인가를 결정하는 메소드다. 306 | 307 | TransactionAttribute를 이용하면 트랜잭션 부가기능의 동작 방식을 모두 제어할 수 있다. 308 | 309 | TransactionAdvice 트랜잭션 경계설정 코드를 다시 살펴보면 트랜잭션 부가기능의 동작방식을 변경할 수 있는 곳이 두 군데 있다는 사실을 알 수 었다. 310 | 311 | ```java 312 | public class TransactionAdvice implements MethodInterceptor { 313 | 314 | private PlatformTransactionManager transactionManager; 315 | 316 | public void setTransactionManager(PlatformTransactionManager transactionManager) { 317 | this.transactionManager = transactionManager; 318 | } 319 | 320 | @Override 321 | public Object invoke(MethodInvocation invocation) throws Throwable { 322 | TransactionStatus transactionStatus = this.transactionManager.getTransaction( 323 | // 트랜잭션 정의를 통한 네 가지 조건 324 | new DefaultTransactionDefinition()); 325 | try { 326 | Object ret = invocation.proceed(); 327 | this.transactionManager.commit(transactionStatus); 328 | return ret; 329 | } catch (RuntimeException e) { // 롤백 대상인 예외 종류 330 | this.transactionManager.rollback(transactionStatus); 331 | throw e; 332 | } 333 | } 334 | } 335 | ``` 336 | 337 | 주석의 두 가지 항목이 결합되어 트랜잭션 부가기능의 행동을 결정하는 TransactionAttribute 속성이 된다. 338 | 339 | TransactionAdvice는 RuntimeException이 발생하는 경우에만 트랜잭션을 롤백시킨다. 340 | 341 | 하지만 런타임 예외가 아닌 경우 트랜잭션이 제대로 처리되지 않고 메서드를 빠져나가게 되어 있다. 342 | 343 | 문제가 되는 상황은 체크 예외를 던지는 타깃에 TransactionAdvice를 사용하는 경우 런타임 예외가 아니기 때문에 트랜잭션이 제대로 처리되지 않는다는 것이다. 344 | 345 | 하지만 모든 일은 정해진대로 되는 법이 없고, 비즈니스 로직상의 예외 경우를 나타내기 위해서 타킷 오브젝트가 체크 예외를 던지는 경우에 트랜잭션이 올바로 동작하는 것을 기대하기도 한다. 346 | 347 | - TransactionInterceptor의 두 가지 종류의 `예외 처리 방식` 348 | 349 | `런타임이 발생`하면 트랜잭션은 롤백이 된다. 350 | 351 | 반면 타깃 메서드가 런타임 예외가 아닌 `체크 예외가 발생`하면 이것을 예외 상황으로 해석하지 않고 일종의 비즈니스 로직에 따른, 의미가 있는 리턴 방식의 한 가지로 인식해서 트랜잭션을 커밋해버린다. 352 | 353 | `스프링의 기본 예외처리 원칙`에 따라 비즈니스적인 의미가 있는 예외상황에만 `체크예외`를 사용하고, 그 외의 모든 복구 불가능한 순수한 예외의 경우 `런타임 예외`로 포장하여 전달하는 방식에 따른 것이다. 354 | 355 | TransactionInterceptor는 이러한 예외처리 기본 원칙의 변칙적인 경우에 대해서 TransactionAttribute는 `rollbackOn()`이라는 속성을 통해 기본 원칙에서 벗어나는 다른 356 | 예외처리를 가능하도록 한다. 357 | 358 | > 메서드 이름 패턴을 이용한 트랜잭션 속성 지정 359 | 360 | Properties 타입의 transactionAttributes 프로퍼티는 메서드 패턴과 트랜잭션 속성을 키와 값으로 갖는 컬렉션이다. 361 | 362 | ```text 363 | PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,-Exception1,+Exception2 364 | ``` 365 | 366 | - PROPAGATION`_NAME` 367 | - 트랜잭션 전파 방식, `필수항목`으로 PROPAGATION_으로 시작한다. 368 | - ISOLATION`_NAME` 369 | - 격리수준, ISOLATION_으로 시작하며 `생략이 가능`하다. 370 | - 생략되면 디폴트 격리 수준으로 지정한다. 371 | - readOnly 372 | - 읽기전용 항목으로 `생략이 가능`하다. 373 | - 기본 값은 읽기전용이 아니다. 374 | - 트랜잭션이 처음 시작할 때만 적용 375 | - timeout_NNNN 376 | - 제한시간 설정으로 timeout_으로 시작하고 초 단위 시간을 뒤에 붙인다. 377 | - `생략이 가능`하다. 378 | - 트랜잭션이 처음 시작할 때만 적용 379 | - -Exception1 380 | - `체크 예외` 중에서 롤백 대상을 추가할 것을 넣는다. 381 | - 한 개 이상을 등록할 수 있다. 382 | - +Exception2 383 | - `런 타임 예외`지만 롤백시키지 않을 예외들을 넣는다. 384 | - 한 개 이상을 등록할 수 있다. 385 | 386 | 트랜잭션 속성 정의 예 387 | 388 | - get으로 시작하는 모든 메서드에 대한 속성을 `PROPAGATION_REQUIRED`, `readOnly`, `timeout`은 30초 389 | - 일반적으로 읽기전용 메서드는 get 또는 find 같은 규칙의 이름으로 시작한다. 390 | - 조회용 메서드의 트랜잭션은 읽기전용으로 설정하여 성능향상을 기대할 수 있다. 391 | - 여기서 readOnly나 timeout 등은 트랜잭션이 처음 시작될 때가 아니라면 적용되지 않는다. 392 | - 따라서 get으로 시작하는 메서드에서 트랜잭션을 시작하는 경우 `읽기전용에 제한시간이 적용`되지만 그 외에 경우에는 `진행중인 트랜잭션의 속성을 따르게 되어 있다.` 393 | - upgrade로 시작하는 모든 메서드는 `PROPAGATION_REQUIRES_NEW`, `ISOLATION_SERIALIZABLE` 설정 394 | - 항상 독립적인 트랜잭션으로 동작하도록 트랜잭션 전파 항목을 `PROPAGATION_REQUIRES_NEW`로 설정한다. 395 | - 다른 동시 작업에 영향을 받지 않도록 완벽하게 고립된 상태에서 트랜잭션이 동작하도록 격리 수준을 최고 수준인 `ISOLATION_SERIALIZABLE`으로 설정한다. 396 | - 그 외에 모든 메서드는 `PROPAGATION_REQUIRED`만 설정 397 | - 트랜잭션의 전파 레벨은 필수 항목이기 때문에 `PROPAGATION_REQUIRED`를 설정한다. 398 | - 나머지는 기본 설정을 따르도록 한다. 399 | 400 | ```xml 401 | 402 | 403 | 404 | 405 | 406 | PROPAGATION_REQUIRED,readOnly,timeout_30 407 | PROPAGATION_REQURIES_NEW,ISOLATION_SERIALIZABLE 408 | PROPAGATION_REQUIRED 409 | 410 | 411 | 412 | ``` 413 | 414 | - 메서드 이름이 하나 이상의 패턴과 일치하는 경우 415 | 416 | 메서드 이름 패턴 중에서 가장 정확히 일치하는 것이 적용되어 메서드 이름 패턴을 사용하는 트랜잭션 속성을 활용하여 하나의 트랜잭션 어드바이스를 정의하여 다양한 트랜잭션 설정이 가능하다. 417 | 418 | ### 6.6.3 포인트컷과 트랜잭션 속성에 대한 전용 전략 419 | 420 | XML 설정으로 AOP와 tx 스키마의 전용 태그를 사용하면 어플리케이션 어드바이저, 어드바이스, 포인트컷 기본 설정 방법은 바뀌지 않는다. 421 | 422 | expression 어트리뷰트에 넣는 포인트 컷 표현식과 로 정의하는 트랜잭션 속성만 결정하면 된다. 423 | 424 | 스프링 부트를 사용하고 Java Config를 사용하더라도 표현식을 사용할 수있으므로 `포인트컷 표현식`과 `트랜잭션 속성을 정의`할 때 따르면 좋은 몇 가지 전략 등을 알아본다. 425 | 426 | > 트랜잭션 포인트컷 표현식은 `타입 패턴`이나 `빈 이름`을 이용한다. 427 | 428 | 일반적으로 트랜잭션을 적용할 타깃 클래스의 메서드는 모두 트랜잭션 적용 후보가 되는 것이 바람직하다. 429 | 430 | 쓰기 작업이 없는 단순한 `조회 작업만 하는 메서드`에도 모두 트랜잭션을 적용하는 것이 좋다. 왜냐하면 읽기 전용으로 트랜잭션 속성을 설정하여 성능 향상을 가져올 수도 있고, 복잡한 조회의 경우에는 timeout 431 | 설정을 지정할 수도 있고 격리 수준에 따라 조회도 반드시 트랜잭션 안에서 진행해야 하는 경우에 대해서 처리할 수도 있다. 432 | 433 | - 트랜잭션용 포인트컷 표현식 규칙 434 | - 메소드나 파라미터, 예외에 대한 패턴을 정의하지 않는 것이 바람직하다. 435 | - 트랜잭션 포인트컷 등록 방법 436 | - 표현식으로 등록하는 방법 437 | - 트랜잭션의 경계로 선정할 클래스를 추려내어 패키지를 통째로 선택하거나 클래스의 일정한 패턴을 찾아 표현식을 작성한다. 438 | - 관례적으로 비즈니스 로직 서비스를 담당하는 클래스 이름은 Service또는 ServiceImpl 일 때 `execution(**..*ServiceImpl.*(..))`와 같이 정의한다. 439 | - 가능하면 클래스보다는 변경 빈도가 적고 일정한 패턴을 유지하는 인터페이스 타입을 기준으로 타입 패턴을 적용하는 것이 좋다. 440 | - 스프링의 빈 이름을 이용하는 bean() 표현식을 사용하는 방법 441 | - bean() 표현식은 빈 이름을 기준으로 선정하기 때문에 클래스나 인터페이스 이름에 일정한 규칙을 만들기가 어려운 경우 유용하다. 442 | - 포인트컷 표현식 자체가 간단해서 가독성에서 좋다. 443 | - 어노테이션을 이용한 포인트컷 표현식을 만드는 방법 444 | 445 | > 공통된 메서드 이름 규칙을 통해 `최소한의 트랜잭션 어드바이스와 속성`을 정의한다. 446 | 447 | 기준이 되는 몇 가지 트랜잭션 속성을 정의하고 그에 따라 적절한 메소드 명명 규칙을 만들어 두면, 하나의 어드바이스만으로 애플리케이션의 모든 서비스 빈에 트랜잭션 속성을 지정할수있다. 448 | 449 | 가끔 예외적인 경우 트랜잭션 어드바이스와 포인트컷을 새롭게 추가해야 하는 상황이 생길 수 있다. 450 | 451 | 이러한 상황들이 생기는 것을 위해 가장 먼저 트랜잭션 속성 부여를 모든 메서드에 디폴트 속성으로 지정하는 것으로 시작하여 개발이 진행될 수록 단계적으로 속성을 추가하는 것이다. 452 | 453 | - 디폴트 트랜잭션 속성 부여 454 | - 모든 타깃 메서드에 기본 트랜잭션 속성을 지정 455 | 456 | ```xml 457 | 458 | 459 | 460 | 461 | 462 | 463 | ``` 464 | 465 | - 읽기전용 속성 추가 466 | - get으로 시작하는 메서드에 대해 읽기전용 속성을 부여 467 | - 메서드가 실제 시작 위치가 아니면 읽기전용 속성은 무시 468 | - 나머지 메서드에는 기본 트랜잭션 속성을 지정한다. 469 | - 이러한 `advice에 트랜잭션 속성을 지정할 때 순서가 중요`하다. 470 | 471 | ```xml 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | ``` 480 | 481 | - 두 개 이상의 포인트컷과 어드바이스를 적요하는 경우 482 | - 트랜잭션의 성격이 다른 배치 작업용 클래스를 위해 트랜잭션 어드바이스를 별도로 정의하여 독자적인 트랜잭션 어드바이스를 별도로 정의해서 독자적인 트랜잭션 속성을 지정한다. 483 | 484 | ```xml 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | ... 493 | 494 | 495 | 496 | ... 497 | 498 | ``` 499 | 500 | 요즘에도 이와 같은 내용이 적용되는지는 모르겠다. 트랜잭션을 휘황찬란하게 사용해야 하는 상황이 있는 사이트 들도 있어서 이는 참고만 하고 넘어간다. 501 | 502 | > 프록시 방식 AOP는 같은 타깃 오브젝트 내의 메서드를 호출할 때는 적용되지 않는다. 503 | 504 | 이 부분은 주의사항으로 고려해야 하는 내용이다. 505 | 506 | **`타깃 오브젝트가 자기 자신의 메서드를 호출할 때 프록시를 통한 부가기능의 적용이 일어나지 않는다.`** 507 | 508 | 이는 프록시가 적용되는 방식을 생각해봐야 한다. 509 | 510 | 프록시 방식의 AOP에서는 프록시를 통한 부가 기능의 적용은 클라이언트로부터 호출이 일어날 때만 가능하다. 511 | 512 | ![](images/013.png) 513 | 514 | - 타깃 안에서의 호출에는 프록시가 적용되지 않는 문제를 해결할 수 있는 방법 두 가지 515 | 1. 스프링 API를 이용해 프록시 오브젝트에 대한 레퍼런스를 가져온 뒤 같은 오브젝트의 메서드 호출도 프록시를 이용하도록 강제하는 방법 516 | 2. AspectJ와 같은 타깃의 바이트코드를 직접 조작하는 방식의 AOP 기술을 적용하는 방법 517 | 518 | ### 6.6.4 트랜잭션 속성 적용 519 | 520 | 트랜잭션 속성과 트랜잭션 전략을 UserService에 적용하기 521 | 522 | > 트랜잭션 경계설정의 일원화 523 | 524 | 일반적으로 비즈니스 로직을 담고 있는 서비스 계층 오브젝트의 메서드가 트랜잭션 경계를 부여하기에 가장 적절한 대상이다. 525 | 526 | 서비스 계층을 트랜잭션의 경계선으로 정했다면, 테스트와 같은 특별한 이유가 아니고서는 다른 계층 또는 모듈에서 DAO에 직접 접근하는 것을 차단해야 한다. 527 | 528 | 트랜잭션은 보통 서비스 계층의 메서드 조합을 통해 만들어지기 때문에 DAO가 제공하는 주요 기능은 서비스 계층에 위임 메서드를 만들어둘 필요가 있다. 529 | 530 | 서비스 계층을 통해서 DAO에 접근하도록 해야 부가 로직을 적용할 수도 있고, 트랜잭션 속성을 제어할 수 있기 때문이다. 531 | 532 | > 서비스 빈에 적용되는 포인트컷 표현식 등록 533 | 534 | upgradeLevels()에만 트랜잭션이 적용되게 했던 기존 포인트컷 표현식을 모든 비즈니 스 로직의 서비스 빈에 적용되도록 수정한다. 535 | 536 | aop 스키마의 태그를 이용해 포인트컷, 어드바이저 등의 설정한다. 537 | 538 | ```xml 539 | 540 | 541 | 542 | 543 | ``` 544 | 545 | > 트랜잭션 속성을 가진 트랜잭션 어드바이스 등록 546 | 547 | TransactionAdvice 클래스로 정의했던 어드바이스 빈을 스프링의 Transactionlnterceptor를 이용하도록 변경한다. 548 | 549 | ```xml 550 | 551 | 552 | 553 | 554 | 555 | PROPAGATION_REQUIRED, readOnly 556 | PROPAGATION_REQUIRED 557 | 558 | 559 | 560 | ``` 561 | 562 | tx 스키마를 통한 정의 시 트랜잭션 어드바이스와 속성을 쉽게 작성할 수 있다. 563 | 564 | ```xml 565 | 566 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | ``` 585 | 586 | > 트랜잭션 속성 테스트 587 | 588 | 트랜잭션 부가기능의 적용 전략을 수정, 새로운 메서드 추가를 확인 하여 학습 테스트를 작성한다. 589 | 590 | 읽기 전용 메서드에 쓰기 작업을 추가하여 관련 예외가 발생하는 지를 확인하도록 한다. 591 | 592 | ```java 593 | class UserServiceImplTest { 594 | 595 | @Autowired 596 | private UserServiceImpl.TestUserService testUserService; 597 | 598 | @DisplayName("읽기전용 메서드에 쓰기 작업을 추가하여 읽기전용 속성 위반에 대한 예외 확인 테스트") 599 | @Test 600 | void testCase1() { 601 | assertThatExceptionOfType(TransientDataAccessResourceException.class) 602 | .isThrownBy(() -> testUserService.getAll()); 603 | } 604 | } 605 | ``` 606 | 607 | ### 6.7 어노테이션 트랜잭션 속성과 포인트컷 608 | 609 | `포인트컷 표현식`과 `트랜잭션 속성`을 이용해 트랜잭션을 일괄적으로 적용하는 방식은 대부분의 상황에 적용할 수 있다. 610 | 611 | 보다 세밀하게 튜닝된 트랜잭션 속성을 적용해야 하는 경우에는 메서드 이름 패턴을 통해 일괄 적용하는 방식은 적합하지 않다. 612 | 613 | 스프링은 이러한 `문제점`을 개선하기 위해 `직접 타깃에 트랜잭션 속성정보를 갖는 어노테이션을 지정하는 방법`을 제공한다. 614 | 615 | > **6.7.1 트랜잭션 어노테이션** 616 | 617 | `타깃 부여할 수 있는 트랜잭션 어노테이션의 종류`에 대해서 알고 이해하기 전에 `메타 어노테이션`에 대해서 알고 있어야 한다. 618 | 619 | - `@Transactional` 620 | 621 | `@Transactional` 어노테이션의 타깃은 `메서드`와 `타입`이다. 622 | 623 | 따라서 `메서드`, `클래스`, `인터페이스`에 적용할 수 있으며, @Transactional 어노테이션을 트랜잭션 속성정보로 사용하도록 지정하면 모든 오브젝트를 자동으로 타깃 오브젝트로 인식한다. 624 | 625 | 이때 사용되는 포인트컷은 `TransactionAttributeSourcePointcut` 클래스로 표현식과 같은 선정기준을 갖지 않는다. 626 | 627 | 대신 @Transactional 어노테이션이 적용되어 있는 메서드, 클래스 레벨 상관없이 부여된 빈 오브젝트를 모두 찾아 포인트컷의 선정 결과로 돌려준다. 628 | 629 | 결국 `@Transactional 어노테이션`으로 `Transaction 속성을 정의하는 것`과 동시에 `포인트컷의 자동등록에도 사용되는 것`이다. 630 | 631 | ```java 632 | import java.lang.annotation.*; 633 | 634 | /** 635 | * 트랜잭션 속성의 모든 항목을 엘리먼트로 지정할 수 있으며, 디폴트 값이 설정되어 있어 있으므로 모두 생략이 가능하다. 636 | */ 637 | @Target({ElementType.METHOD, ElementType.TYPE}) 638 | @Retention(RetentionPolicy.RUNTIME) 639 | @Inherited 640 | @Documented 641 | public @interface Transactional { 642 | String value() default ""; 643 | 644 | Propagation propagation() default Propagation.REQUIRED; 645 | 646 | Isolation isolation() default Isolation.DEFAULT; 647 | 648 | int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; 649 | 650 | boolean readOnly() default false; 651 | 652 | Class[] rollbackFor() default {}; 653 | 654 | String[] rollbackForClassName() default {}; 655 | 656 | Class[] noRollbackFor() default {}; 657 | 658 | String[] noRollbackForClassName() default {}; 659 | } 660 | ``` 661 | 662 | - `트랜잭션 속성을 이용하는 포인트컷` 663 | 664 | @Transactional 어노테이션을 적용할 때 어드바이저의 동작방식을 보면 TransactionInterceptor는 메서드 이름 패턴을 통해 부여되는 일괄적인 트랜잭션 속성정보 대신 @Transactional 665 | 어노테이션의 엘리먼트에서 트랜잭션 속성을 가져오는 AnnotationTransactionAttributeSource를 사용하게 된다. 666 | 667 | 이 방식은 포인트컷과 트랜잭션 속성을 어노테이션 하나로 지정할 수 있지만 트랜잭션 부가기능 적용 단위가 메서드 임을 생각하면 668 | 669 | 메서드 마다 @Transactional 어노테이션과 트랜잭션 속성을 설정해야 하는 중복코드를 생상하게 된다. 670 | 671 | 이를 개선하기 위해 스프링은 대체정책이라는 개념을 제공하여 중복코드를 줄일 수 있도록 돕는다. 672 | 673 | ![](images/012.png) 674 | 675 | - `대체 정책` 676 | 677 | 스프링은 @Trnsactional을 적용할 때 4 단계의 대체정책을 이용하여 해준다. 678 | 679 | 메서드의 속성을 확인할 때 `타깃 메서드` -> `타킷 클래스` -> `선언 메서드` -> `선언 타입`의 순서에 따라 @Transactional이 적용되었는지 차례로 확인하고 가장 먼저 발견되는 속성정보를 사용하게 680 | 하는 방법이다. 681 | 682 | `@Transactional`은 먼저 타입 레벨에 정의되고 `공통 속성`을 따르지 않는 메서드에 대해서만 메서드 레벨에 다시 `@Transactional`을 부여해주는 식으로 사용해야 한다. 683 | 684 | 기본적으로 @Transactional 적용 대상은 클라이언트가 사용하는 인터페이스에 정의한 메서드이므로 @Transactional도 타깃 클래스보다는 인터페이스에 두는게 바람직하다. 685 | 686 | 하지만 인터페이스를 사용하는 프록시 방식의 AOP가 아닌 방식으로 트랜잭션을 적용하면 인터페이스에 정의한 @Transactional은 무시되기 때문에 안전하게 타깃 클래스에 @Transactional을 두는 687 | 방법을 `권장`한다. 688 | 689 | 프록시 방식 AOP의 종류와 특징, 또는 비 프록시 방식 AOP의 동작 원리를 잘 이해하고 있고 그에따라 @Transactional의 적용 대상을 적절하게 변경해줄 확신이 있거나, 반드시 인터페이스를 사용하는 690 | 타깃에만 트랜잭션을 적용하겠다는 확신이 있다면 인터페이스에 @Transactional을 적용하고, 그게 아니라면 타깃 클래스와 타깃 메서드에 적용하는 편이 좋다. 691 | 692 | - `트랜잭션 어노테이션 사용을 위한 설정` 693 | 694 | @Transactional을 이용한 트랜잭션 속성을 사용하는데 필요한 설정은 어드바이저, 어드바이스, 포인트컷, 어노테이션을 이용하는 트랜잭션 속성정보가 한번에 등록된다. 695 | 696 | xml 설정의 경우 `` Java Config의 경우 `@EnableTransactionManagement`를 사용한다. 697 | 698 | > **6.7.2 트랜잭션 어노테이션 적용** 699 | 700 | 어노테이션에 대한 대체 정책의 순서는 타킷 클래스가 인터페이스보다 우선하므로 모든 메서드의 트랜잭션은 디폴트 속성을 갖게 된다. 701 | 702 | ```java 703 | 704 | @Transactional // 인터페이스 레벨에 디폴트 속성으로 적용 705 | public interface UserService { 706 | void add(User user); 707 | 708 | void deleteAll(); 709 | 710 | void update(User user); 711 | 712 | void upgradeLevels(); 713 | 714 | @Transactional(readOnly = true) 715 | // 읽기 전용 속성의 트랜잭션이 필요한 경우 메서드 레벨에 적용 716 | User get(String id); 717 | 718 | @Transactional(readOnly = true) 719 | List getAll(); 720 | } 721 | ``` 722 | 723 | ```java 724 | 725 | @Transactiional // 클래스 레벨에 트랜잭션 디폴트 속성으로 적용 726 | public class UserServiceImpl implements UserService { 727 | // ... 728 | } 729 | ``` 730 | 731 | 위 트랜잭션 적용에 대해서 필히 테스트 코드를 작성하여 트랜잭션의 대체정책이 기대한 대로 동작하는지 확인해야 한다. 732 | 733 | 예를 들어 인터페이스에는 getAll() 메서드를 읽기전용 속성을 갖고 있으나 타깃 클래스에 디폴트 속성을 갖고 있는 점을 확인하기 위해 734 | 735 | 읽기 전용 속성을 검증하는 테스트 코드를 작성해 보는 것이다. 736 | 737 | ### 6.8 트랜잭션 지원 테스트 738 | 739 | > **6.8.1 선언적 트랜잭션과 트랜잭션 전파 속성** 740 | 741 | `트랜잭션 전파 속성`은 `REQUIRED`를 기본 속성으로 사용하고 있고 이는 앞서 진행 중인 트랜잭션이 있으면 참여하고, 없으면 자동으로 새로운 트랜잭션을 시작한다. 742 | 743 | 일반적으로는 `DB 트랜잭션`은 `단위 업무`와 일치하도록 해야 한다. 하지만 `단위 업무`가 `작업 단위가 다른 비즈니스 로직으로 구성`될 수 있다. 744 | 745 | 그런 경우 앞선 작업이 정상적으로 수행되고, 뒤 이어 후속 작업이 어떠한 이유 인하여 실패하는 경우 앞선 작업까지 롤백이 되어야 한다. 746 | 747 | 이것이 가능하도록 돕는 것이 `트랜잭션 전파`라는 기법으로 연속적인 작성된 비즈니스 로직에 대한 트랜잭션의 범위를 확장할 수 있다. 748 | 749 | `트랜잭션 전파 기법`이 없는 경우, DB 트랜잭션을 어플리케이션 레벨에서 여러 비즈니스 로직에 대한 트랜잭션을 관리할 수 없기 때문에 결국에는 DB 쿼리에 하나 이상의 비즈니스 로직을 담게 될 것이다. 이는 코드의 750 | 중복, 유지보수에 대한 부분에서 효율적이지 못한 결과를 낼 수 있다. 751 | 752 | 스프링에서는 트랜잭션을 사용할 수 있도록 지원하는 방법이 2가지가 있는데 첫 번째는 `선언적 트랜잭션(declarative transaction)`과 두 753 | 번째는 `프로그램에 의한 트랜잭션(programmatic transaction)`을 제공하며 선언적 방식의 트랜잭션을 사용하는 것이 바람직하다. 754 | 755 | - 선언적 트랜잭션 756 | - AOP를 이용해 코드 외부에서 트랜잭션의 기능을 부여해주고 속성을 지정할 수 있도록 제공한다. 757 | - 프로그램에 의한 트랜잭션 758 | - `TransactionTemplate`이나 개별 데이터 기술의 트랜잭션 API를 사용해 직접 코드에서 사용하는 방법이다. 759 | 760 | > **6.8.2 트랜잭션 동기화와 테스트** 761 | 762 | 트랜잭션의 자유로운 `전파`와 그로 인한 유연한 개발이 가능할 수 있었던 배경에 AOP가 있다. 763 | 764 | 한 가지 중요한 기술적인 기반은 `스프링의 트랜잭션 추상화`이다. 765 | 766 | 트랜잭션 기술에 상관없이 DAO에서 일어나는 작업들을 하나의 트랜잭션으로 묶어서 추상 레벨에서 관리하게 해주는 트랜잭션 추상화로 인해 767 | 768 | 비로소 선언적 트랜잭션이나 트랜잭션 전파 등이 가능해진 것이다. 769 | 770 | - `트랜잭션 매니저와 트랜잭션 동기화` 771 | 772 | 트랜잭션 추상화 기술의 핵심은 `트랜잭션 매니저`와 `트랜잭션 동기화`이다. 773 | 774 | 스프링는 `PlatformTransactionManager 인터페이스`를 구현한 트랜잭션 매니저를 통해 구체적인 트랜잭션 기술의 종류에 상관없이 일관된 트랜잭션 제어를 가능하게 한다. 775 | 776 | `트랜잭션의 동기화 기술`은 진행중인 트랜잭션이 있는지 확인하고, `트랜잭션 전파 속성`에 따라서 `트랜잭션 전파 기능`의 속성 별 동작하게 한다. 777 | 778 | `트랜잭션의 전파 속성`은 `REQUIRED`, `SUPPORTS`, `MANDATORY`, `REQUIRES_NEW`, `NOT_SUPPORTED`, `NEVER`, `NESTED`등이 있으며 기본적인 트랜잭션의 779 | 전파 속성은 REQUIRED이다. 780 | 781 | 일반적으로는 AOP를 통해 트랜잭션의 전파 속성에 따라 동기화할 수도 있지만, 특별한 경우 TransactionManager를 통해 트랜잭션을 제어할 수도 있다. 782 | 783 | 스프링 테스트 컨텍스트를 사용하는 경우 PlatformTransactionManager Bean을 가져와 핸들링할 수도 있다. 784 | 785 | ```java 786 | public class UserServiceTest { 787 | @Autowired 788 | private PlatformTransactionManager platformTransactionManager; 789 | @Autowired 790 | private UserService userService; 791 | 792 | @Test 793 | public void transactionSync() { 794 | userService.deleteAll(); 795 | 796 | userService.add(users.get(0)); 797 | userService.add(users.get(1)); 798 | } 799 | } 800 | ``` 801 | 802 | 위 테스트 메서드가 실행 되는 동안 몇 개의 트랜잭션이 만들어 졌을까? UserService의 모든 메서드에는 트랜잭션이 적용되었다면 총 3개의 트랜잭션이 만들어졌을 것이다. 803 | 804 | 모두 독립적인 트랜잭션 안에서 실행이 되었을 것이다. 왜냐하면 기본적인 트랜잭션 전파 속성이 REQUIRED 이기 때문에 새로운 트랜잭션으로 각각 수행되었다. 805 | 806 | - `트랜잭션 매니저를 이용한 테스트용 트랜잭션 제어` 807 | 808 | 여기서 시도해 볼 내용은 각각의 트랜잭션을 하나의 트랜잭션으로 동작하게 하는 것이다. 809 | 810 | 결국 트랜잭션의 전파 속성이 REQUIRED라는 속성을 이용하여 3개의 트랜잭션을 묶기 위해 메서드 호출 전 트랜잭션 시작되도록 한다는 것이다. 811 | 812 | 테스트 코드로 `트랜잭션 매니저`를 이용해 `트랜잭션을 시작시키고 이를 동기화` 하도록 한다. 813 | 814 | ```java 815 | public class UserServiceTest { 816 | @Autowired 817 | private PlatformTransactionManager platformTransactionManager; 818 | @Autowired 819 | private UserService userService; 820 | 821 | @Test 822 | public void transactionSync() { 823 | DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); 824 | TransactionStatus txStatus = platformTransactionManager.getTransaction(txDefinition); 825 | userService.deleteAll(); 826 | 827 | userService.add(users.get(0)); 828 | userService.add(users.get(1)); 829 | 830 | platformTransactionManager.commit(txStatus); 831 | } 832 | } 833 | ``` 834 | 835 | 위 코드는 트랜잭션 매니저를 이용해 트랜잭션을 미리 시작하게 만드는 테스트이다. 836 | 837 | - `트랜잭션 동기화 검증` 838 | 839 | 트랜잭션 매니저를 통해 코드를 구현 한 것까지는 좋지만 이에 대해 검증하는 코드는 필수이다. 840 | 841 | 정말 트랜잭션 매니저를 통해 세 개의 메서드의 트랜잭션이 동기화 되었는지 확인하는 작업이 필요하다. 842 | 843 | 이를 테스트 하는 방법은 트랜잭션의 속성에서 읽기전용이라는 속성을 통해 검증하는 것이다. 844 | 845 | 즉, 쓰기 가능의 트랜잭션 속성을 갖는 deleteAll() 전에 앞서 시작된 트랜잭션 상태를 읽기전용으로 하여 진행해보도록 한다. 846 | 847 | 읽기 전용 트랜잭션이 시작되고나서, 쓰기 작업이 수행되면 `TransientDataAccessResourceException` 이라는 예외가 발생하게 되는 것을 이용하여 트랜잭션 동기화를 검증한다. 848 | 849 | `TransientDataAccessResourceException`는 읽기전용 트랜잭션에 대해 쓰기 작업을 했을때 발생하는 예외이다. 850 | 851 | ```java 852 | public class UserServiceTest { 853 | @Autowired 854 | private PlatformTransactionManager platformTransactionManager; 855 | @Autowired 856 | private UserService userService; 857 | 858 | @Test 859 | public void transactionSync() { 860 | DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); 861 | txDefinition.setReadOnly(true); // 읽기전용 트랜잭션으로 정의 862 | TransactionStatus txStatus = platformTransactionManager.getTransaction(txDefinition); 863 | 864 | userService.deleteAll(); // 읽기전용 속성을 위반하여 예외가 발생하는 라인 865 | 866 | userService.add(users.get(0)); 867 | userService.add(users.get(1)); 868 | 869 | platformTransactionManager.commit(txStatus); 870 | } 871 | } 872 | ``` 873 | 874 | 예외가 발생하는 것을 확인함으로써 테스트 코드 내에 시작한 트랜잭션에 deleteAll() 메서드가 참여하고 있다는 확신을 얻을 수 있다. 875 | 876 | `스프링의 트랜잭션 추상화`가 제공하는 `트랜잭션 동기화 기술`과 `트랜잭션 전파 속성` 덕분에 테스트도 트랜잭션으로 묶을 수 있다. 877 | 878 | 이러한 방식은 선언적 트랜잭션이 적용된 서비스 메서드에만 적용되는 것이 아니라 JdbcTemplate과 같이 스프링이 제공하는 데이터 엑세스 추상화를 적용한 DAO에도 동일하게 사용 가능하다. 879 | 880 | ```java 881 | public class UserServiceTest { 882 | @Autowired 883 | private PlatformTransactionManager platformTransactionManager; 884 | @Autowired 885 | private UserService userService; 886 | 887 | @Test 888 | public void transactionSync() { 889 | DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); 890 | txDefinition.setReadOnly(true); // 읽기전용 트랜잭션으로 정의 891 | TransactionStatus txStatus = platformTransactionManager.getTransaction(txDefinition); 892 | 893 | userDao.deleteAll(); // JdbcTemplate을 사용하더라도 이미 시작된 트랜잭션이 있다면 자동으로 참여하게 되어 예외가 발생한다. 894 | 895 | // ... 896 | } 897 | } 898 | ``` 899 | 900 | 트랜잭션의 롤백 테스트도 수행되는지 확인해보는 코드를 작성한다. 901 | 902 | ```java 903 | public class UserServiceTest { 904 | @Autowired 905 | private PlatformTransactionManager platformTransactionManager; 906 | @Autowired 907 | private UserService userService; 908 | 909 | @Test 910 | public void transactionSync() { 911 | userService.deleteAll(); 912 | assertThat(userDao.getCount()).isOne(); // 트랜잭션 시작 전 초기화 913 | 914 | DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); 915 | TransactionStatus txStatus = platformTransactionManager.getTransaction(txDefinition); 916 | 917 | userService.add(users.get(0)); 918 | userService.add(users.get(1)); 919 | assertThat(userDao.getCount()).isEqualTo(2); // userDao.getCount()를 통해 두개의 데이터가 들어갔는지 확인 920 | 921 | platformTransactionManager.rollback(txStatus); 922 | 923 | assertThat(userDao.getCount()).isZero(); // 롤백 후 트랜잭션 시작 이전의 상태임을 확인 924 | } 925 | } 926 | ``` 927 | 928 | 지금까지 작업을 통해 테스트 안에서 트랜잭션을 조작할 수 있는 방법을 이해했다면 올바르게 이해한 것이다. 929 | 930 | 이 방식을 통해 `ORM에서 세션에서 분리된(detached) 엔티티의 동작`을 확인할 때도 유용하다. 931 | 932 | 테스트 메서드 안에서 `트랜잭션을 여러 번 만들 수`도 있고, `트랜잭션의 속성에 따라서 여러 메서드를 조합해 사용`할 때 어떠한 결과가 나오는지 미리 검증도 가능하다. 933 | 934 | - 롤백 테스트 935 | 936 | 롤백 테스트의 목표는 테스트 내에 모든 DB 작업을 하나의 트랜잭션 안에서 동작하게 하고 테스트가 끝나면 무조건 롤백하는 것이다. 937 | 938 | ```java 939 | public class UserServiceTest { 940 | @Autowired 941 | private PlatformTransactionManager platformTransactionManager; 942 | @Autowired 943 | private UserService userService; 944 | 945 | @Test 946 | public void transactionSync() { 947 | DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); 948 | TransactionStatus txStatus = platformTransactionManager.getTransaction(txDefinition); 949 | 950 | try { 951 | userService.deleteAll(); 952 | userService.add(users.get(0)); 953 | userService.add(users.get(1)); 954 | assertThat(userDao.getCount()).isEqualTo(2); // userDao.getCount()를 통해 두개의 데이터가 들어갔는지 확인 955 | } finally { 956 | platformTransactionManager.rollback(txStatus); 957 | assertThat(userDao.getCount()).isZero(); // 롤백 후 트랜잭션 시작 이전의 상태임을 확인 958 | } 959 | } 960 | } 961 | ``` 962 | 963 | 테스트를 작성할 때는 대상 메서드 외에 DB를 사용하게 되는 경우 DB의 데이터와 상태가 중요한데, 더 큰 문제는 DB의 데이터가 바뀌는 것이 가장 문제이다. 964 | 965 | 테스트가 어떤 순서로 동작할 지 알 수 없고, 성공 했을 대와 실패 했을 때 DB에 다른 방식으로 영향을 줄 수 있기 때문에, 테스트 할 때 마다 DB 데이터를 초기화 하는 것이 중요하다. 966 | 967 | 결국 롤백 테스트는 테스트를 진행하는 동안에 조작한 데이터를 모두 롤백하고 테스트를 시작하기 전 상태로 만들어 주기 때문에 테스트에 대한 성공, 실패, 예외 모두 상관이 없다. 968 | 969 | 테스트에서 트랜잭션을 제어할 수 있기 때문에 롤백 테스트가 가능한 것이다. 970 | 971 | > **6.8.3 테스트를 위한 트랜잭션 애노테이션** 972 | 973 | @Transactional 어노테이션은 타깃 클래스 또는 인터페이스에 부여하여 트랜잭션을 적용할 수 있는데 이는 테스트 코드를 작성하는데도 동일하게 사용할 수 있다. 974 | 975 | 스프링의 컨텍스트 테스트 프레임워크에서 제공하는 `@ContextConfiguration`을 클래스에 부여하여 스프링 컨테이너를 초기화하고, 필요한 빈만을 등록하여 테스트에 필요한 빈을 `@Autowired`를 통해 976 | 자유롭게 접근할 수 있다. 977 | 978 | - `@Transactional` 979 | 980 | 어노테이션을 적용한 메서드에 트랜잭션 경계가 자동으로 설정되어 트랜잭션 관련 작업을 하나로 묶을 수 있다. 981 | 982 | ```java 983 | public class UserServiceTest { 984 | @Autowired 985 | private UserService userService; 986 | 987 | @Test 988 | @Transactional 989 | public void transactionSync() { 990 | userService.deleteAll(); 991 | userService.add(users.get(0)); 992 | userService.add(users.get(1)); 993 | } 994 | } 995 | ``` 996 | 997 | 트랜잭션의 적용 여부를 확인하는 작업이 필요한 경우 트랜잭션의 속성을 읽기 전용으로 설정하여 동일한 테스트를 진행해본다. 998 | 999 | ```java 1000 | public class UserServiceTest { 1001 | @Autowired 1002 | private UserService userService; 1003 | 1004 | @Test 1005 | @Transactional(readOnly = true) 1006 | public void transactionSync() { 1007 | userService.deleteAll(); // 읽기 전용 트랜잭션에서 쓰기 요청을 하는 경우 예외 발생 1008 | userService.add(users.get(0)); 1009 | userService.add(users.get(1)); 1010 | } 1011 | } 1012 | ``` 1013 | 1014 | - `@Rollback` 1015 | 1016 | 테스트용 트랜잭션은 테스트가 끝나면 자동으로 롤백이 된다. 1017 | 1018 | 혹시나 DB에 반영하고 싶은 경우 @Rollback 어노테이션을 사용하여 속성을 false 로 갖게 하면 트랜잭션이 모두 종료되더라도 DB가 롤백 되지 않는다. 1019 | 1020 | 다만 테스트 데이터가 DB에 데이터를 넣는건 신중하게 생각해야 한다. 물론 테스트 DB가 따로 있는 경우, 개발자들 간의 서로 예약된 내용이 있는 경우 사용하도록 한다. 1021 | 1022 | ```java 1023 | public class UserServiceTest { 1024 | @Autowired 1025 | private UserService userService; 1026 | 1027 | @Test 1028 | @Transactional 1029 | @Rollback(false) 1030 | public void transactionSync() { 1031 | userService.deleteAll(); 1032 | userService.add(users.get(0)); 1033 | userService.add(users.get(1)); 1034 | } 1035 | } 1036 | ``` 1037 | 1038 | - `@TransactionConfiguration` 1039 | 1040 | @Transactional 어노테이션은 테스트 클래스에 적용하여 모든 메서드에 일괄 적용할 수 있고, @Rollback 어노테이션은 메서드 레벨에만 적용이 가능하다. 1041 | 1042 | 테스트 클래스의 모든 메서드에 트랜잭션을 적용하면서 모든 트랜잭션이 롤백되지 않고 커밋되게 하려면 @TransactionConfiguration 어노테이션을 이용하여 롤백에 대한 공통 속성을 지정할 수 있다. 1043 | 1044 | ```java 1045 | 1046 | @Transactional 1047 | @TransactionConfiuguration(defaultRollback = false) 1048 | public class UserServiceTest { 1049 | @Autowired 1050 | private UserService userService; 1051 | 1052 | @Test 1053 | @Transactional 1054 | @Rollback(false) 1055 | public void transactionSync() { 1056 | userService.deleteAll(); 1057 | userService.add(users.get(0)); 1058 | userService.add(users.get(1)); 1059 | } 1060 | } 1061 | ``` 1062 | 1063 | - `NotTransactional과 Propagation.NEVER` 1064 | 1065 | 테스트 클래스 안에서 일부 메서드에만 트랜잭션이 필요한 경우 메서드 레벨에 @Transactional을 적용한다. 1066 | 1067 | 또는 대부분 메서드에 트랜잭션이 필요한 경우 클래스 레벨에 @Transactional을 적용한다. 1068 | 1069 | 굳이 트랜잭션이 필요 없는 메서드도 굳이 적용해야 할까? 해당 메서드에만 테스트 메서드에 의한 트랜잭션이 시작되지 않도록 트랜잭션의 전파 속성을 변경한다. 1070 | 1071 | @Transactional(propagation = Propagation.NEVER)는 트랜잭션이 시작되지 않도록 하는 전파 속성으로 위의 목적을 달성할 수 있다. 1072 | 1073 | - `효과적인 DB 테스트` 1074 | 1075 | 테스트 내에서 트랜잭션을 제어할 수 있는 네 가지 어노테이션을 잘 활용하게 되면 DB가 사용되는 통합 테스트를 만들 때 편리하다. 1076 | 1077 | 일반적으로 의존, 협력 오브젝트를 사용하지 않고 고립된 상태에서 테스트를 진행하는 `단위 테스트`, DB 같은 외부의 리소스나 여러 계층의 클래스가 참여하는 `통합 테스트`는 아예 클래스를 구분해서 따로 만드는 것이 1078 | 좋다. 1079 | 1080 | 통합 테스트와 단위 테스트를 구분하게 되면 보다 효율적으로 어노테이션을 활용할 수 있다. 1081 | 1082 | 테스트는 어떤 경우에도 서로 의존하면 안된다. 테스트가 진행되는 순서나 앞의 테스트의 성공 여부가 다음 테스트에 영향을 주는 것도 허용하지 않는다. 1083 | 1084 | 어떠한 순서로 진행되더라도 일정한 결과를 내야 한다. 1085 | 1086 | ### 6.9 정리 1087 | 1088 | > 책 읽은 척하기 1089 | 1090 | **트랜잭션 경계설정 기능**을 성격이 다른 **비즈니스 로직 클래스**에서 분리하여 적용할 수 있는 방법을 찾아 보면서 최종적으로는 모듈화할 수 있는 AOP 기술을 통해 해결하였다. 1091 | 1092 | > 6장에서 무엇을 했고 어떤 부분을 정리했는지 살펴보기 1093 | 1094 | - **트랜잭션 경계설정 코드**를 분리해서 별도의 클래스를 만들고 **비즈니스 로직 클래스**와 동일한 인터페이스를 구현하면 DI의 확장기능을 이용해 클라이언트의 변경없이 깔끔하게 분리된 트랜잭션 **부가기능**을 1095 | 만들 수 있다. 1096 | - `데코레이터 패턴` 및 `프록시 패턴`을 통해 트랜잭션 기능을 적용하는 방식으로 앞에서 설명되었다. 1097 | - 트랜잭션처럼 환경과 외부 리소스에 영향을 받는 코드를 분리하면 `비즈니스 로직에만 충실한 테스트`를 만들 수 있다. 1098 | - **목 오브젝트**를 활용하면 의존관계 속에 있는 오브젝트도 손쉽게 **고립된 테스트**로 만들 수 있다. 1099 | - `DI를 이용한 트랜잭션의 분리`는 `데코레이터 패턴`과 `프록시 패턴`으로 이해될 수 있다. 1100 | - 번거로운 프록시 클래스 작성은 **JDK의 다이내믹 프록시**를 사용하면 간단하게 만들 수 있다. 1101 | - **다이내믹 프록시**는 **스태틱 팩토리 메소드**를 사용하기 때문에 빈으로 등록하기 번거롭다. 따라서 **팩토리 빈**으로 만들어야 한다. 스프링은 자동 프록시 생성 기술에 대한 추상화 서비스를 제공하는 ** 1102 | 프록시 팩토리 빈(ProxyFactoryBean)**을 제공한다. 1103 | - **프록시 팩토리 빈**의 설정이 반복되는 문제를 해결하기 위해 **자동 프록시 생성기**와 **포인트컷** 을 활용할 수 있다. 자동 프록시 생성기는 부가기능이 담긴 어드바이스를 제공하는 프록시를 스프링 1104 | 컨테이너 초기화 시점에 자동으로 만들어준다. 1105 | - 포인트컷은 AspectJ 포인트컷 표현식을 사용해서 작성하면 펀리하다. 1106 | - **AOP**는 **OOP**만으로는 모듈화하기 힘든 부가기능을 효과적으로 모듈화하도록 도와주는 기 술이다. 1107 | - 스프링은 자주 사용되는 AOP 설정과 **트랜잭션** 속성을 지정하는 데 사용할 수 있는 **전용 태 그**를 제공한다. 1108 | - **AOP**를 이용해 **트랜잭션 속성을 지정하는 방법**에는 포인트컷 표현식과 메소드 이름 패턴 을 이용하는 방법과 타깃에 직접 부여하는 @Transactional 애노테이션을 사용하는 방법이 있다. 1109 | - **@Transactional**을 이용한 트랜잭션 속성을 테스트에 적용하면 손쉽게 DB를 사용하는 코드의 테스트를 만들 수 있다. 1110 | -------------------------------------------------------------------------------- /6장_AOP/6장_이형원.md: -------------------------------------------------------------------------------- 1 | #AOP 2 | *** 3 | #다이내믹 프록시와 팩토리 빈 4 | 5 | ## 프록시와 프로시 패턴, 데코레이터 패턴. 6 | 7 | ### 프록시 8 | - 마치 자신이 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서 클라이언트의 요청을 받아주는 것을 대리자, 대리인 같은 역할 9 | - 프록시는 사용 목적에 따라 두가지로 구분 10 | - 클라이언트가 타깃에 접근하는 방법 제어. 11 | - 타깃에 부가적인 기능을 부여해주기 위해. 12 | 13 | ### 데코레이터 패턴. 14 | - 타깃에 부가적인 기능을 런타임 시 다이나믹하게 부여해주기 위해 프록시를 사용하는 패턴. 15 | 16 | ### 프록시 패턴 17 | - 프록시라는 용어와 디자인 패턴에서 말하는 프롟 패턴은 구분해야한다. 18 | - 프록시 : 클라이언트와 사용 대상 사이에 대리역할을 맡은 오브젝트를 두는 방법. 19 | - 프록시 패턴 : 타깃에 대한 접근 방법을 제어하는 목적. 20 | 21 | ### 다이내믹 프록시 22 | 23 | - 리플렉션 : 자바의 코드 자체를 추상화해서 접근하도록 만든 것. 24 | - 다이나믹프록시 : 프록시 팩토리에 의해 런타임 시 다이나믹하게 만들어지는 오브젝트. 25 | - InvocationHandler 구현을 통해, 리플렉션 API를 이용해 타깃 오브젝트의 메소드 호출. 26 | 27 | *** 28 | 29 | # 트랜잭션 30 | 31 | ```java 32 | @Target({ElementType.METHOD, ElementType.TYPE}) // 애노테이션을 사용할 대상을 지정 33 | @Retention(RetentionPolicy.RUNTIME) // 애노테이션 정보가 언제까지 유지되는지 34 | @Inherited // 상속을 통해서도 애노테이션 정보를 얻을 수 있도록 35 | @Documented 36 | // 트랜잭션 속성의 모든 항목을 엘리먼트로 지정 가능, 디폴트 값이 설정되어 있으므로 모두 생략이 가능 37 | public @interface Transactional { 38 | String value() default ""; 39 | Propagation propagation() default Propagation.REQUIRED; 40 | Isolation isolation() default Isolation.DEFAULT; 41 | int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; 42 | boolean readOnly() default false; 43 | Class[] rollbackFor() default {}; 44 | String[] rollbackForClassName() default {}; 45 | Class[] noRollbackFor() default {}; 46 | String[] noRollbackForClassName() default {}; 47 | } 48 | 49 | ``` 50 | ### 선언적 @Transactional 51 | - 메인 비즈니스 코드에는 전혀 영향을 주지 않으면서 특정 메소드 실행 전후에 트랜잭션이 시작되고 종료되거나 기존 트랜잭션에 참여하도록 만들 수 있따. 52 | - 이를 위해서는 **데코레이터 패턴** 적용한 트랜잭션 프록시 빈을 사용해야한다. 53 | - 경계설정은 트랜잭션 프록시 빈 덕분에 가능. 54 | 55 | ### Dynamic Proxy 56 | - 스프링의 AOP는 기본적으로 Dynamic Proxy 기법을 이용해 동작. 57 | 58 | ### 프록시를 이용한 AOP 59 | - 프록시로 만들어서 DI로 연결된 빈 사이에 적용해 타깃의 메소드 호출 과정에 참여해서 부가기능 제공. 60 | - 프록시는 클라이언트가 타깃 오브젝트를 호출하는 과정에서만 동작한다. 61 | - 따라서 타깃 오브젝트의 메소드가 자기 자신의 다른 메소드를 호출할 때는 프록시가 동작하지 않음. 62 | 63 | ### 바이트코드 생성과 조작을 통한 AOP _ AspectJ 64 | - AOP 프레임워크인 AspectJ 65 | - 바이트코드를 조작해서 타깃 오브젝트를 직접 수정. 66 | - 프록시 방식보다 훨씬 강력하고 유연한 AOP. 67 | 68 | *** 69 | ## AOP 용어 70 | 71 | ### 타깃 72 | - 부가기능을 부여할 대상. 73 | 74 | ### 어드바이스 75 | - 타깃에게 제공할 부가기능을 담은 모듈 76 | 77 | ### 어드바이저 78 | - 어드바이저는 포인트 컷과 어드바이스를 하나씩 갖고 있는 옵젝트. 79 | 80 | ### 조인 포인트 81 | - 어드바이스가 적용될수 있는 위치를 말한다. 82 | 83 | ### 포인트컷 84 | - 어드바이스를 적용할 조인 포인트를선별하는 작업. 85 | -------------------------------------------------------------------------------- /6장_AOP/images/001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/001.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/002.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/003.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/003.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/004.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/004.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/005.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/005.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/006.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/006.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/007.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/007.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/008.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/008.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/009.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/009.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/010.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/010.jpeg -------------------------------------------------------------------------------- /6장_AOP/images/011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/011.png -------------------------------------------------------------------------------- /6장_AOP/images/012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/012.png -------------------------------------------------------------------------------- /6장_AOP/images/013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/6장_AOP/images/013.png -------------------------------------------------------------------------------- /7장_스프링_핵심_기술의_응용/7.md: -------------------------------------------------------------------------------- 1 | # 스프링 핵심 기술의 응용 2 | 3 | 7장은 글로 정리하는 의미가 없어서 읽기만 했습니당.. -------------------------------------------------------------------------------- /7장_스프링_핵심_기술의_응용/7장_스프링_핵심_기술의_응용1_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/7장_스프링_핵심_기술의_응용/7장_스프링_핵심_기술의_응용1_안송이.pdf -------------------------------------------------------------------------------- /7장_스프링_핵심_기술의_응용/7장_스프링_핵심_기술의_응용2_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/7장_스프링_핵심_기술의_응용/7장_스프링_핵심_기술의_응용2_안송이.pdf -------------------------------------------------------------------------------- /7장_스프링_핵심_기술의_응용/7장_스프링_핵심_기술의_응용_김석래.md: -------------------------------------------------------------------------------- 1 | # 7. 스프링 핵심 기술의 응용 2 | 3 | ## Intro 4 | 5 | 스프링의 3대 핵심 기술인 IoC/DI, 서비스 추상화, AOP에 대해 정리해보았다. 6 | 7 | 스프링이 가장 가치를 두고 적극적으로 활용하려고 하는 것은 객체지향 기술이다. 8 | 9 | 스프링의 모든 기술이 결국에 객체지향적인 언어의 장점을 적극적으로 활용하여 코드를 작성하도록 도와주는 것이다. 10 | 11 | 스프링을 사용하는 개발자는 스프링이 이러한 목표를 갖고 있고, 설계되어있기 때문에 세 가지 기술을 필요에 따라 스스로 응용할 수 있어야 한다. -------------------------------------------------------------------------------- /8장_스프링이란_무엇인가?/8장_스프링이란_무엇인가_김석래.md: -------------------------------------------------------------------------------- 1 | ## Intro 2 | 3 | 스프링은 뭘까? 4 | 5 | 스프링은 기본적으로 IoC와 DI를 위 한 컨테이너로서 동작하지만 그렇다고 `“스프링은 단지 IoC/DI 프레임워크다”`라고는 말 할 수 없다. 6 | 7 | IoC/DI를 편하게 적용하도록 돕는 단계를 넘어서 엔터프라이즈 애플리케이션 개발의 전 영역에 걸쳐 다양한 종류의 기술 8 | 9 | ## 8.1 스프링의 정의 10 | 11 | 스프링의 정의를 통해 스프링이 어떤 것인지 큰 그림을 이해해보는 것이 중요하다. 12 | 13 | > 스프링의 정의 14 | > 15 | 16 | 자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크 17 | 18 | > 정의의 해석 19 | > 20 | 21 | - 애플리케이션 프레임워크 22 | 23 | 일반적으로 라이브러리나 프레임워크는 특정 업무 분야나 한 가지 기술에 특화된 목표를 가지고 만들어진다. 24 | 25 | 예를 들어 웹 계층을 MVC 구조로 손쉽게 만들 수 있게 한다거나, 포맷과 출력장치를 유연하게 변경할 수 있는 애플리케이션 `로그 기능`을 제공한다거나, 간단한 설정만으로 관계형 DB와 자바 오브젝트를 26 | 매핑해주는 `ORM 기술`을 제공하는 것들이다. 27 | 28 | 하지만 스프링은 `애플리케이션 프레임워크`라는 특징을 갖는다. 29 | 30 | 애플리케이션 프레임워크는 `특정 계층`이나, `기술`, `업무 분야`에 국한되지 않고 애플리케이션의 전 영역에 포괄하는 범용적인 프레임워크를 말한다. 31 | 32 | 애플리케이션 프레임워크는 애플리케이션 개발의 전 과정을 빠르고 편리하며 효율적으로 진행하는 데 일차적인 목표를 갖는 프레임워크이다. 33 | 34 | 스프링을 애플리케이션 프레임워크라 불리는 이유는 35 | 36 | 단순하게 여러 계층의 다양한 기술을 한데 모아뒀기 때문에 애플리케이션 프레임워크로 불리는 것이 아니라 애플리케이션 전 영역을 관통하는 일관된 프로그래밍 모델과 핵심 기술을 바탕으로 해서 각 분야의 특성에 맞는 37 | 요구사항을 채워주기 때문에, 애플리케이션을 빠르고 효과적으로 개발할 수 있다. 38 | 39 | 스프링의 IoC / DI 프레임워크나 AOP은 `핵심 기술`이고 그 핵심 기술에 담긴 프로그래밍 모델을 일관되게 적용해서 엔터프라이즈 애플리케이션 전 계층과 전 영역에 `전략`과 `기능`을 40 | 제공해줌으로써 애플리케이션을 편하게 개발하게 해주는 애플리케이션 프레임워크로 사용되는 것이 스프링의 일차적인 존재 목적이다. 41 | 42 | - 경량급 43 | 44 | 경량급이라는 표현을 쓴 것은 코드 자체가 아주 가볍다거나 작은 규모의 코드로 이루어졌다는 것이 아니라 스프링을 기반으로 제작되는 코드가 기존의 EJB이나 여타 프레임워크에서 동작하기 위해 만들어진 코드에 비해 45 | 상대적으로 작고 단순하다는 뜻이기도 하다. 46 | 47 | 같은 기능을 수행하는 코드임에도 스프링 기반의 코드가 가벼운 이유는 코드에 불필요하게 등장하던, 프레임워크와 서버환경에 의존적인 부분을 제거해주기 때문이다. 48 | 49 | 만들어진 코드가 지원하는 기술수준은 비슷하더라도 훨씬 빠르고 간편하게 작성하게 해줌으로써 생산성과 품질 면에서 유리하다는 것이 경량급이라는 말로 표현되는 스프링의 특징이다. 50 | 51 | - 자바 엔터프라이즈 개발을 편하게 52 | 53 | 스프링은 근본적인 부분에서 엔터프라이즈 개발의 `복잡함을 제거`하고, 진정으로 개발을 편하게 해주는 해결책을 제시한다. 54 | 55 | 엔터프라이즈 개발의 `근본적인 문제점`에 도전해서 해결책을 제시한다는 것이 기존 기술의 접근 방법과 스프링의 접근 방법의 차이점이다. 56 | 57 | `편리한 애플리케이션 개발`이란 개발자가 복잡하고 실수하기 쉬운 로우레벨 기술에 많은 신경을 쓰지 않으면서도 애플리케이션의 핵심인 사용자의 요구사항, 즉 비즈니스 로직을 빠르고 효과적으로 구현하는 것을 58 | 말한다. 59 | 60 | 애플리케이션 개발자들이 스프링이라는 프레임워크가 제공하는 기술이 아니라 자신이 작성하는 애플리케이션의 로직에 더 많은 관심과 시간을 쏟을 수 있도록 돕는다. 61 | 62 | - 오픈소스 63 | 64 | 오픈소스란 말 그대로 소스가 모두에게 공개되고 특별한 라이선스를 취득할 필요없이 얼마든지 가져다 자유롭게 이용해도 된다는 뜻이다. 65 | 66 | 스프링에 적용된 오픈소스 라이선스는 오픈소스 라이선스 중에서도 비교적 제약이 적고 사용이 매우 자유로운 편인 아파치 라이선스 버전2.0이다. 67 | 68 | 아파치 라이선스에 따르면 스프링을 상업적인 목적의 제품에 포함시키거나 비공개 프로젝트에 자유롭게 이용해도 된다. 다만 스프링을 사용한다는 점과 원 저작자를 밝히고 제품을 패커징할 때 라이선스 정보를 포함시키는 등의 69 | 기본적인 의무사항을 따르면 된다. 70 | 71 | 또한 필요한 경우 스프링 소스코드를 가져와 수정해서 사용할 수 있고, 수정을 했더라도 수정한 소스를 공개해야 하는 의무는 없다. 72 | 73 | 스프링은 대부분의 오픈소스 프로젝트처럼 스프링도 오픈소스 개발과 사용자를 위한 온라인 커뮤니티가 있다. 74 | 75 | 커뮤니티를 통해 자유롭게 개발에 관한 의견을 공유하거나 토론할 수도 있고 자신이 발견한 버그를 신고하거나 새로운 기능을 추가해달라고 요청 할 수도 있다. 그런 요청이나 버그 신고가 어떻게 처리되고 있는지도 이슈트래커 76 | 시스템을 통해 공개적으로 확인이 가능하며, 수정된 코드도 언제든지 살펴볼 수 있다. 77 | 78 | 기존 오픈소스 개발 방식의 단점을 극복할 수 있는 대안은 기업이나 기관의 지원을 받는 전문 개발자가 오픈소스 개발을 책임지게 하는 것이다. 79 | 80 | 스프링을 개발하는 SpringSource는 스프링에 대한 전문적인 기술지원과 컨설팅, 스프링을 기반으로 개발된 시스템을 안정적으로 운용할 수 있도록 돕는 상용 제품을 제공함으로써 수익을 얻고, 한편으로는 오픈소스 81 | 프로젝트로서 스프링이 효율적으로 개발되도록 지원하다. 82 | 83 | 스프링은 오픈소스의 장점을 충분히 취하면서 동시에 오픈소스 제품의 단점과 한계를 잘 극복하고 있는, 전문적이고 성공적인 오픈소스 소프트웨어라고 할 수 있다. 84 | 85 | --- 86 | 87 | ## 8.2 스프링의 목적 88 | 89 | 스프링의 개발 철학과 궁극적인 목표가 무엇인지 생각해볼때이다. 90 | 91 | 스프링은 목표를 분명히 알고 사용하지 않으면 그 가치를 제대로 얻기 힘들다. 92 | 93 | 스프링을 사용하기는 해도 스프링이 주는 혜택을 전혀 누리지 못하고 오히려 사용하지 않느니만 못한 경우도 적지 않다. 94 | 95 | 스프링은 그 기능과 API 시용 방법을 잘 안다고 해서 잘 쓸수 있는 게 아니다. 96 | 97 | 스프링도 목적을 바로 이해하고 그 목적을 이루는 도구로 스프링을 잘 활용해야만 스프링으로부터 제대로 된 가치를 얻을 수 있다. 98 | 99 | ### 8.2.1 엔터프라이즈 개발의 복잡함 100 | 101 | 엔터프라이즈 프로젝트 개발은 실패할 확률이 높은데 그 이유는 엔터프라이즈 시스템 개발이 너무 복잡하기 때문이라고 한다. 102 | 103 | > 복잡함의 근본적인 이유 104 | > 105 | 106 | 엔터프라이즈 시스템 개발이 복잡한 이유는 크게 두 가지가 있다고 한다. 107 | 108 | - 첫 번째는 기술적인 제약 조건과 요구사항이 늘어가기 때문이다. 109 | 110 | 엔터프라이즈 시스템이란 서버에서 동작하며 기업과 조직의 업무를 처리해주는 시스템을 말한다. 111 | 112 | 엔터프라이즈 시스템은 서버의 자원을 효율적으로 공유하고 분배해서 사용해야하고, 보안과 안정성, 확장성 면에서도 뛰어나야 하며, 뛰어난 성능과 서비스의 안정성에 대해 고려한 개발 기술이 필요하다. 113 | 114 | 즉, 엔터프라이즈 시스템을 개발하는데에 비즈니스 로직을 구현하는 것도 중요하지만 기술적으로 고려해야 할 사항이 많다. 115 | 116 | - 엔터프라이즈 애플리케이선이 구현해야 할 핵심기능인 비즈니스 로직의 복잡함이 증가하기 때문이다. 117 | 118 | 서비스의 변화를 소비자에게 맞추기 위해 업계는 수시로 업무 프로세스를 변경하고 조정하는 것을 상시화할 만큼 변화의 속도가 빨라 졌다. 119 | 120 | 이러한 업무 구조와 프로세스의 변화를 뒷받침 해주기 위해 엔터프라이즈 시스템의 변경을 요구할 수 밖에 없고, 이전과 다르게 시스템 개발과 유지보, 추가 개발 등의 작업에 대한 부담이 커지고 그에 따른 개발 난이도가 121 | 더욱 증가하게 되었다. 122 | 123 | > 복잡함을 가중시키는 원인 124 | > 125 | 126 | 엔터프라이즈 애플리케이션 개발이 실패하는 주요 원인은 `비즈니스 로직의 복잡함`과 `기술적인 복잡함`이다. 127 | 128 | ### 8.2.2 복잡함을 해결하려는 도전 129 | 130 | > 제거될 수 없는 근본적인 복잡함 131 | > 132 | 133 | 엔터프라이즈 개발의 근본적인 복잡함의 원인은 제거할 대상은 아니다. 134 | 135 | 복잡함을 효과적으로 상대할 수 있는 전략과 기법이 필요하다. 136 | 137 | 문제는 비즈니스 로직의 복잡함을 효과적으로 다루기 위한 방법과 기술적인 복잡함을 효과적으로 처리하는 데 적용되는 방법이 다르다는 점이다. 138 | 139 | 따라서 가장 먼저 할 일은 성격이 다른 이 두 가지 복잡함을 분리해내는 것이다. 140 | 141 | > 실패한 해결책: EJB 142 | > 143 | 144 | EJB가 처음 등장했을 때 내세웠던 목표를 봐도 알 수 있듯이 EJB의 기본 전략도 이 두 가지 종류의 복잡함을 분리하는 것 이었다. 145 | 146 | 기존 EJB는 결과적으로 그런 목표를 달성하는 데 실패했다. 147 | 148 | 선언적 트랜잭션이나 선언적 보안, 컨테이너를 통한 리모팅 기술의 적용, 컴포넌트 단위의 배치, JNDI를 통한 서비스 검색 지원 서비스 오브젝트의 풀링 컴포넌트 생명주기 관리 등은 EJB의 목표를 어느 정도 149 | 충족시켰다. 150 | 151 | 애플리케이션 로 직을 담은 핵심 코드에서 일부 기술적인 코드가 제거된 건 사실이지만, 오히려 EJB라는 환경과 스펙에 종속되는 코드로 만들어져야 하는 더 큰 부담을 안게 됐다. 152 | 153 | EJB의 발전주기는 너무 느려서 엔터프라이즈 개발 기술의 발전을 따라잡지 못하는 것도 문제점이다. 154 | 155 | > 비침투적인 방식을 통한 효과적인 해결책: 스프링 156 | > 157 | 158 | EJB처럼 어떤 기술을 적용했을 때 그 기술과 관련된 코드나 규약 등이 코드에 등장히는 경우를 침투적인(Invaslve) 기술이라고 한다. 159 | 160 | 꼭 필요한 기능을 시용하는 것도 아니면서 단지 어떤 기술을 바탕으로 만들어진다고 해서 특정 클래스나 인터페이스 API 등의 코드에 마구 등장한다면 그것은 침투적인 기술이 되며 복잡함을 가중시키는 원인이 된다. 161 | 162 | 반면에 비침투적인(non-invasive) 기술은 기술의 적용 사실이 코드에 직접 반영되지 않는다는 특정이 있다. 어딘가에는 기술의 적용에 따라 필요한 작업을 해줘야 하겠지만, 애플리케이션 코드 여기저기에 불쑥 163 | 등장하거나, 코드의 설계와 구현 방식을 제한하지는 않는다는 게 비침투적인 기술의 특정이다. 164 | 165 | 중요한 점은 그 과정에서 스프링 스스로가 애플리케이션 코드에 불필요하게 나타나지 않도록 하는 것이다. 166 | 167 | 스프링을 통해 성격이 다른 복잡함들을 깔끔하게 분리해줬기 때문에 각각을 효과적으로 상대할 수 있는 기반이 마련됐다. 168 | 169 | 동시에 스프링이 모드에 불필요하게 등장해서 부가적인 복잡함을 가져오지도 않았다. 이러한 전략 덕분에 비효율적인 개발에 대한 문제를 해결할 수 있게 되었다. 170 | 171 | ### 8.2.3 복잡함을 상대하는 스프링의 전략 172 | 173 | 스프링의 기본적인 전략은 비즈니스 로직을 담은 애플리케이션 코드와 엔터프라이즈 기술을 처리하는 코드를 분리시키는 것이다. 174 | 175 | 이 분리를 통해 두 가지 복잡함의 문제를 효과적으로 공략하게 해준다. 176 | 177 | > 기술적 복잡함을 상대하는 전략 178 | > 179 | 180 | 스프링은 엔터프라이즈 기술을 적용했을 때 발생하는 복잡함의 문제를 두 가지로 분류하고 각각에 대한 적절한 대응 방법을 제공한다. 181 | 182 | - 첫 번째 문제: 기술에 대한 접근 방식이 일관성이 없고, 특정 환경에 종속적인 문제 183 | 184 | 일관성 없는 기술과 서버환경의 변화에 대한 스프링의 공략 방법은 바로 서비스 추상화이다. 185 | 186 | 기술적인 복잡함은 일단 추상회를 통해 로우레벨의 기술 구현 부분과 기술을 시용하는 인터페이스를 분리하고, 환경과 세부 기술에 독립적인 접근 인터페이스를 제공하는 것이 가장 좋은 해결책이다. 187 | 188 | 표준으로 자리 잡은 기술에 대해서도 서비스 추상화를 적용 할 필요가 있다. 이를 통해 태스트 편의성을 증대시키고 기술에 대한 세부 설정과 환경으로부터 독립적인 코드를 만들 수 있다. 189 | 190 | 스프링이 제공하는 템플릿/콜백 패턴은 판에 박힌 반복적인 작업 흐름과 API 사용 코드를 제거해준다. 이를 통해 기술을 사용하는 코드도 최적화된 핵심 로직에만 집중하도록 도외준다. 191 | 192 | - 두 번째 문제: 기술적인 처리를 담당하는 코드가 성격이 다른 코드에 섞여서 등장하는 문제 193 | 194 | 책임에 따라 계층을 구분하고 그 사이에 서로의 기술과 특성에 의존적인 인터페이스나 예외처리 등을 최대한 제거한다고 할지라도 근본적으로 엔터프라이즈 서비스를 적용하는 한 이런 문제는 쉽게 해결할 수 없다. 이런 기술과 195 | 비즈니스 로직의 혼재로 발생하는 복잡함을 해결하기 위한 스프링의 접근 방법은 바로 AOP다. 196 | 197 | AOP는 최후까지 애플리케이션 로직을 담당하는 코드에 남아 있는 기술 관련 코 드를 깔끔하게 분리해서 별도의 모률로 관리하게 해주는 강력한 기술이다. 198 | 199 | AOP는 기술을 다루는 코드로 인한 복잡함이 기술 그 자체 이상으로 불필요하 게 증대되지 않도록 도와주는 가장 강력한 수단이다. 200 | 201 | > 비즈니스와 애플리케이션 로직의 복잡함을 상대하는 전략 202 | > 203 | 204 | 기술적인 코드, 침투적인 기술이 가져온 불필요한 흔적 등을 제거하고 나면 순수하게 애플리케이션의 주요 기능과 비즈니스 로직을 담은 코드만 독립적으로 존재하게 된다. 205 | 206 | 반면에 비즈니스 로직을 다루는 핵심 코드에 오류가 있으면 엔터프라이즈 시스템을 사용하는 업무 자체에 큰 지장을 주거나 치명적인 손실을 끼칠 수도 있다. 207 | 208 | 그래서 비즈니스 로직은 가장 중요하게 다뤄져야 하고 가장 많이 신경 써야한다. 209 | 210 | 엔터프라이즈 시스템의 규모가 커지고, 복잡함이 증가하면서 DB에 비즈니스 로직을 두는 것은 매우 불편할뿐더러 위험한 일이라고 여겨지기 시작했다. 211 | 212 | 가장 확장하기 힘들고 확장하더라도 많은 비용이 드는 공유 자원인 DB에 커다란 부담 을 주는 것도 문제고, 데이터 액세스를 중심으로 로직을 다루면 개발과 유지보수는 물론이고 테스트도 매우 어렵다. 213 | 214 | 엔터프라이즈 시스템 개발의 흐름은 점차로 비즈니스 로직은 애플라케이션 안에서 처리하도록 만드는추세다. DB는 단지 데이터의 영구적인 저장과 복잡한 조건을 가진 검색과 같은 자체적으로 특화된 기능에만 활용하고, 215 | 데이터를 분석하고 가공하고 그에 따라 로직을 처리하는 부분은 확장하기 쉽고, 비용도 싼 애플리케이션 서버 쪽으로 이동히는 것이다. 216 | 217 | 객체지향 프로그래밍 기법과 언어가 주는 장점인 유연한 설계가 가능하고 재사용성이 높다는 점을 잘 활용하면 자주 바뀌고 조건이 까다로운 비즈니스 로직을 효과적으로 구현해낼 수 있다. 218 | 219 | 환경에 종속적인 기술과 침투적인 기법으로 인해 추가된 군더더기에 방해만 받지 않는다면 객체지향 언어로서의 장점을 잘 살려 비즈니스 로직의 복잡함을 최대한 효과적으로 다룰수 있는 깔끔한 코드를 만드는건 어렵지 않다. 220 | 221 | 비침투적인 기술인 스프링은 핵심 로직을 다루는 코드인 비즈니스 로직을 담당하는 오브젝트들에게 적절한 엔터프라이즈 기술 서비스가 간접적으로 돕는다. 222 | 223 | > 핵심 도구: 객체지향과 DI 224 | > 225 | 226 | 기술과 비즈니스 로직의 복잡함을 해결하는데 스프링이 공통적으로 사용하는 도구가 있다. 바로 객제지향(OOP)이다. 227 | 228 | 자바의 기본인 객체지향에 충실한 설계가 가능하도록 단순한 오브젝트로 개발할 수 있고 객체지향의 설계 기법을 잘 적 용할 수 있는 구조를 만들기 위해 DI 같은 유용한 기술을 편하게 적용하도록 도와주는 것이 스프링의 229 | 기본 전략이다. 230 | 231 | 기술적인 복잡함을 효과적으로 다루게 해주는 기법은 모두 DI를 바탕으로 하고 있다. 서비스 추상화 템플릿/콜백 AOP와 같은 스프링의 기술은 DI 없이는 존재 할 수 없는 것 들이다. 232 | 233 | DI는 자연스럽게 객체지향적인 설계와 개발로 이꿀어주는 좋은 동반자다. 234 | 235 | DI를 의식하다 보면 오브젝트를 설계할 때 자주 DI를 적용할 후보가 더 이상 없을까를 생각해보게 된다. 236 | 237 | 결국 DI는 좋은 오브젝트 설계의 결과물이기도 하지만, 반대로 DI를 열심히 적 용하다 보면 객체지향 설계의 원칙을 잘 따르고 그 장점을 살린 설계가 나올 수도 있다. 238 | 239 | 기술적인 복잡함을 해결하는 문제나 기술적인 복잡함이 비즈니스 로직에 침범하지 못하도록 분리하는 경우에도 DI가 바탕이 된 여러 가지 기법이 활용된다. 240 | 241 | 비즈니스 로직 자체의 복잡함을 해결하려면 DI보다는 객체지향 설계 기법이 더 중요하다. 242 | 243 | 객체지향적인 특성을 잘 살린 설계는 상속과 다형성, 위임을 포함해서 많은 객체지향 디자인 패턴과 설계 기법이 잘 녹아들어갈 수있다. 기술적인 코드에 침범당하지 않았다면 이런 설계를 비즈니스 로직을 구현하는 코드에 244 | 그대로 반영할수 있다. 245 | 246 | 결국 모든 스프링의 기술과 전략은 객체지향이라는 자바 언어가 가진 강력한 도구를 극대화해서 사용할 수 있도록 돕는 것이라고 볼 수 있다. 247 | 248 | 현장의 업무를 잘 지원하고 유연하게 대응할 수 있는 뛰어난 애플라케이션을 만드는 것은 객체지향을 잘 활용해서 복잡한 문제를 풀어나갈 줄 아는 개발자의 능력에 달려 있다는 사실을 잊지 말아야 한다. 249 | 250 | ## 8.3 POJO 프로그래밍 251 | 252 | 스프링이 지향하는 목적이 무엇인지 정의해보기 253 | 254 | 스프링 핵심 개발자들은 “스프링의 정수는 엔터프라이즈 서비스 기능을 POJO에 제공하는 것”이라 한다. 255 | 256 | 엔터프라이즈 서비스라 하는 것은 보안, 트랜잭션과 같은 엔터프라이즈 시스템에서 요구되는 기술을 말한다. 257 | 258 | 기술을 POJO에 제공한다는 말은, 엔터프라이즈 서비스 기술과 POJO라는 애플리케이션 로직을 담은 코드를 분리했다는 뜻이기도 하다. 259 | 260 | “분리됐지만 반드시 필요한 엔터프라이즈 서비스 기술을 POJO 방식으로 개발된 애플리케이션 핵심 로직을 담은 코드에 제공한다”는 것이 `스프링의 가장 강력한 특징과 목표`이다. 261 | 262 | ### 8.3.1 스프링의 핵심: POJO 263 | 264 | `스프링의 핵심`이 POJO 프로그래밍이라는 사실은 스프링 삼각형이라는 이미지를 통해 알 수 있다. 265 | 266 | 스프링으로 개발한 애플리케이션의 기본 구조를 보면 스프링 애플리케이션은 POJO를 이용해서 만든 애플리케이션 코드와 POJO가 어떻게 관계를 맺고 동작하는지를 정의해놓은 설계정보로 구분된다. 267 | 268 | 스프링의 주요 기술인 IoC/DI, AOP와 PSA(Portable Service Abstraction)는 애플리케이션을 POJO로 개발할 수 있게 해주는 가능기술(enabling technology)이라고 불린다. 269 | 270 | ### 8.3.2 POJO란 무엇인가? 271 | 272 | POJO는 Plain Old Java Object의 첫 글자를 따서 만든 약자다. 273 | 274 | ### 8.3.3 POJO의 조건 275 | 276 | 단순하게 보자면 그냥 평범한 자바 오브젝트라고 할 수 있지만, 좀 더 명확하게 하자면 적어도 디음의 세 가지 조건을 충족해야 POJO라고 불릴 수 있다. 277 | 278 | > 특정 규약에 종속되지 않는다. 279 | > 280 | 281 | POJO는 자바 언어와 꼭 필요한 API 외에는 종속되지 않아야 한다. 282 | 283 | 특정 규약을 따라 만들게 하는 경우는 대부분 규약에서 제시하는 특정 클래 스를 상속하도록 요구한다. 284 | 285 | 자바의 단일 상속 제한 때문에 더 이상 해당 클래스에 객체지향적인 설계 기법을 적용하기가 어려워지는 문제가 생긴다. 286 | 287 | 규약이 적용된 환경에 종속적이 되기 때문에 다른 환경으로 이전이 힘들다는 문제점이 있다. 288 | 289 | 규약에 종속되지 않고, 객체지향 설계의 자유로운 적용이 가능한 오브젝트여야만 POJO라고 불릴 수 있다. 290 | 291 | > 특정 환경에 종속되지 않는다. 292 | > 293 | 294 | 특정 환경에 종속적이어야만 동작하는 오브젝트도 POJO라고 할 수 없다. 295 | 296 | JDNI와 같은 특정 환경이 의존대상 검색 방식에 종속적이라면 POJO라고 할 수 없다. 297 | 298 | 특히 비즈니스 로직을 담고 있는 POJO 클래스는 웹이라는 환경정보나 웹 기술을 담고 있는 클래스나 인터페이스를 시용해서는 안 된다. 299 | 300 | 설령 나중에는 웹 컨트롤러와 연결돼서 사용될 것이 뻔하다고 할지라도 직접적으로 웹이라는 환경으로 제한해버리는 오브젝트나 API에 의존해선 안 된다. 301 | 302 | 웹 서버에 올리지 않고 독립적으로 태스트하기도 힘들어진다. 303 | 304 | 비즈니스 로직을 담은 코드에 HttpServletRequest나 HttpSession, 캐시와 관련된 API가 등장하거나 웹 프레임워크의 클래스를 직접 이용하는 부분이 있다면 그것은 진정한 POJO라고 볼 수 없다. 305 | 306 | 애노테이션이나 엘리먼트 값에 특정 기술과 환경에 종속적인 정보를 담고 있다면 그때는 POJO로서의 가치를 잃어버린다고 할 수 있다. 307 | 308 | 그렇다고 해서 특정 기술규약과 환경에 종속되지 않는다고해서 모두 POJO라고 할 수도 없다. 309 | 310 | 자바는 객체지향 프로그래밍을 가능하게 해주는 언어이지만, 자바 언어 문법을 사용했다고 해서 자동으로 객체지향 프로그래밍과 객체지향 설계가 적용됐다고 볼 수는 없다. 311 | 312 | 진정한 POJO란 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 말한다. 313 | 314 | ### 8.3.4 POJO의 장점 315 | 316 | POJO의 장점은 POJO가 될 수 있는 조건 그 자체가 장점이 된다. 317 | 318 | 특정한 기술과 환경에 종속되지 않는 오브젝트는 그 만큼 낄끔한 코드가 될 수 있다. 로우레벨의 기술과 환경에 종속적인 코드가 비즈니스 로직과 함께 섞여 나오는 것만큼 지저분하고 복잡한 코드도 없다. 319 | 320 | POJO로 개발된 코드는 지동화된 테스트에 매우 유리하다. 어떤 환경에도 종속 되지 않은 POJO 코드는 매우 유연한 방식으로 원히는 레벨에서 코드를 빠르고 명확하게 테스트 할 수 있다. 321 | 322 | 객체지향적인 설계를 자유롭게 적용할 수 있다는 것도 큰 장점이다. 자바와 객체지향 프로그래밍, 모델링과 설계에 대해 배울 때 그려봤던 도메인 모댈과, 오랜 경험을 통해 쌓여온 재활용 가능한 설계 모델인 디자인 패턴 323 | 등은 POJO가 아니고는 적용하기 힘들다. 324 | 325 | ### 8.3.5 POJO 프레임워크 326 | 327 | 스프링은 POJO를 이용한 엔터프라이즈 애플리케이션 개발을 목적으로 하는 프레임워크라고 했다. 328 | 329 | 스프링 프레임워크와 하이버네이트를 대표적인 POJO 프레임워크로 꼽을 수 있다. 330 | 331 | 스프링은 엔터프라이즈 애플리케이션 개발의 모든 영역과 계층 에서 POJO 방식의 구현이 가능하게 하려는 목적으로 만들어졌다. 332 | 333 | 스프링을 이용하면 POJO 프로그래밍의 장점을 그대로 살려서 엔터프라이즈 애플라 케이션의 핵심 로직을 객체지향적인 POJO를 기반으로 깔끔하게 구현하고, 동시에 엔터프라이즈 환경의 각종 서비스와 기술적인 필요를 334 | POJO 방식으로 만들어진 코드에 적용 할 수 있다. 335 | 336 | 스프링은 비즈니스 로직의 복잡함과 엔터프라이즈 기술의 복잡함을 분리해서 구성 할 수 있게 도와준다. 337 | 338 | 객체지향적 POJO 프로그래밍을 어떻게 효과적으로 적용할지는 개발자에게 또 하나의 숙제이고 부담이다. 개발자는 객체지향 분석과 설계에 대한 지식을 습득하고 훈련해야 한다. 객체지향 기술의 선구자들이 잘 정리해 놓은 339 | 디자인 패턴과 구현 패턴, 좀 더 나은 코드구조를 만들기 위한 리팩토링 기술 또한 필요하다. 340 | 341 | 스프링은 개발자들이 복잡한 엔터프라이즈 기술보다는 이러한 객체지향적인 설계와 개발의 원리에 좀 더 집중할 수 있도록 기회를 준다. 스프링이 제공하는 기술과, 프레임워크 API 및 확장 포인트는 그것을 이용하는 코드가 342 | 자연스럽게 객체지향적인 설계원리를 따라 가도록 이꿀어주기도 한다. 좋은 코드와 좋은 프레임워크의 특징은 그것을 사용해서 만들어지는 코드가 나쁜코드가 되기 어렵다는 점이다. 343 | 344 | 스프링은 자연스럽게 개발자가 좋은 코드를 만들 수 있도록 지원해주는 역할을 하고 있다. 345 | 346 | ## 8.4 스프링의 기술 347 | 348 | “기술과 비즈니스 로직을 분리하고 POJO 방식의 애플리케이션 개발을 가능하게 한다”는 스프링의 목적을 쉽게 이루려면 스프링과 같은 POJO 프레임워크가 필요하다. 349 | 350 | 엔터프라이즈 개발에서 POJO 개발이 가능하려면 삼각형의 각 변을 이루고 있는 기술들이 뒷받침돼야 한다. 그 세 가지 기술은 바로 IoC/DI, AOP, PSA다. 351 | 352 | 객체지향의 설계와 개발 원리를 잘 적용하다보면 자연스럽게 만들어지는 것이기도 하다. 스프링을 특정 기술을 지원해주는 단순한 프레임워크로 이해하면 스프링의 목적과 가치를 놓치기 쉽다. 353 | 354 | IoC/DI, AOP, PSA라는 것 자체가 이미 스프링이 중요한 가치를 두는 객체지향의 원리를 충실히 적용해서 나온 결과이기도 하다. 355 | 356 | ### 8.4.1 제어의 역전(IoC) / 의존관계 주입(DI) 357 | 358 | IoC/DI는 스프링의 가장 기본이 되는 기술이자 스프링의 핵심 개발 원칙이기도 하다. 나머지 두 가지 기술인 AOP와 PSA도 IoC/DI에 바탕을 두고 있다. 359 | 360 | 3대 기술은 아니지만 자주 등장하는 템플릿/콜백 패턴이 적용된 부분도 IoC/DI가 그 핵심 원리다. 361 | 362 | IoC/DI의 활용 방법을 생각해 보기 위해서는 이런 질문을 생각해 볼 수 있다. 왜 두 개의 오브젝트를 분리해서 만들고, 인터페이스를 두고 느슨하게 연결한 뒤, 실제 사용할 대상은 DI를 통해 외부에서 지정하는 363 | 것일까? 이렇게 DI방식으로 하는 것이 그렇지 않은 경우 즉, 직접 자신이 사용할 오브젝트를 new 키워드로 생성해서 사용하는 강한 결합을 쓰는 방법보다 나은 점은 무엇일까? 364 | 365 | 368 | 369 | DI는 개방 폐쇄 원칙 OCP이라는 객체지향 설계 원칙으로 잘 설명될 수 있다. 370 | 371 | 유연한 확장이라는 장점은 OCP의 ‘확장에는 열려 있다(개방)’에 해당한다. 372 | 373 | DI는 역시 OCP의 ‘변경에는 닫혀 있다(폐쇄)’라는 말로도 설명이 가능하다. 374 | 375 | 폐쇄 관점에서 볼 때 장점은 ‘재사용이 가능하다’라고 볼 수있다. 376 | 377 | > DI의 활용 방법 378 | > 379 | 380 | 조금 더 디테일한 DI의 활용 방식을 통해 그 장점을 생각해 보기 381 | 382 | - 핵심기능의 변경 383 | 384 | DI의 가장 대표적인 적용 방법은 바로 의존 대상의 구현을 바꾸는 것이다. 실제 의존하는 대상이 가진 핵심기능을 DI 설정을 통해 변경하는 것이 대표적인 DI의 활용방법이다. 385 | 386 | - 핵심기능의 동적인 변경 387 | 388 | 의존 오브젝트의 핵심기능 자체를 바꾸는 것이다. 일반적인 DI를 이용한 변경 방법과는 달리, 동적으로 매번 다르게 변경할 수 있다. DI도 기본적으로는 런타임 시에 동적으로 의존 오브젝트를 연결해주는 것이긴 하지만, 389 | 일단 DI 되고 나면 그 후로는 바뀌지 않는다. DI를 잘 활용하면 애플리케이션이 동작하는 중간에 그 의존 대상을 다이내믹하게 변경할수 있다. 390 | 391 | - 부가기능의 추가 392 | 393 | DI의 세 번째 활용 방법은 핵심기능은 그대로 둔 채로 부가기능을 추가하는 것이다. 인터페이스를 두고 사용하게 하고, 실제 사용 할 오브젝트는 외부에서 주입하는 DI를 적용해두면 데코레이터 패턴을 쉽게 적용할 수 394 | 있다. 핵심기능과 클라이언트 코드에는 전혀 영향을 주지 않으면서 부가적인 기능을 얼마든지 추가할 수 있다. 395 | 396 | 부가 기능의 추가 방식을 특정 오브젝트가 아니라 좀 더 많은 대상으로 일반화해서 적용 하면 AOP가 된다. 397 | 398 | 부가기능을 추가할 수 있는 것도 바로 DI 덕분이다. DI 구조로 만들어놨기 때문에 가능한 것이며 DI의 핵심 원칙인 OCP에도 충실하게 잘 들어맞는다. 399 | 400 | - 인터페이스의 변경 401 | 402 | 여러 종류의 인터페이스를 가졌지만 사실은 비슷한 기능을 담당하는 오브젝트를 바꿔가면서 사용하고 싶을 때도 있다. 클라이언트가 사용하는 인터페이스와 실제 오브젝트 사이에 인터페이스가 일치하지 않는 경우에도 DI가 403 | 유용하다. 404 | 405 | 디자인 패턴에서 말하는 오브젝트 방식의 어댑터 패턴의 응용이라고 볼 수있다. 406 | 407 | 인터페이스가 다른 다양한 구현을 같은 방식으로 사용하도록, 중간에 인터페이스 어댑터 역할을 해주는 레이어를 하나 추가하는 방법도 있다. 408 | 409 | DI의 응용 방법 중 하나이자 스프링의 대표적인 기술로도 분류되는 일관성 있는 서비스 추상화( PSA )가 그런 방법이다. 410 | 411 | PSA는 클라이언트가 일관성 있게 사용할 수 있는 인터페이스를 정의해주고 DI를 통해 어댑터 역할을 하는 오브젝트를 이용하게 해준다. 이를 통해서 다른 인터페이스를 가진 로우레벨의 기술을 변경하거나 확장해 가면서 412 | 사용할 수 있는 것이다. 413 | 414 | - 프록시 415 | 416 | 프록시 패턴의 전형적인 응용 방법도 있다. 필요한 시점에서 실제 사용할 오브젝트를 초기화하고 리소스를 준비하게 해주는 지연된 로딩(lazy loading)을 적용하려면 프록시가 필요하다. 417 | 418 | 원격 오브젝트를 호출할 때 마치 로컬에 존재하는 오브젝트처럼 사용할 수 있게 해주는 원격 프록시를 적용하려고 할 때도 프록시가 필요하다. 419 | 420 | 두 가지 방식 모두 DI를 통해 이루어진다. 421 | 422 | - 템플릿과 콜백 423 | 424 | 템플릿/콜백 패턴은 DI의 특별한 적용 방법이다. 반복적으로 등장하지만 항상 고정적인 작업 흐름과 그 사이에서 지주 바뀌는 부분을 분리해서 템플릿과 콜백으로 만들고 이를 DI 원리를 응용해 적용하면 지저분하게 매번 425 | 만들어야 하는 코드를 간결하게 만들 수 있다. 426 | 427 | 콜백을 템플릿에 주입하는 방식으로 동작하게 하는 것은 DI의 원리에 가장 충실한 응용 방법이다. 428 | 429 | 콜백을 얼마든지 만들어서 사용할 수 있다는 건 개방을 통한 유연한 확장성을 보여주는 것이며, 템플릿은 한 번 만들어두면 계속 재사용할 수 있다는 건 기능의 확장에도 변하지 않는다는 OCP의 폐쇄 원칙에 가장 잘 430 | 들어맞는 것이다. 431 | 432 | - 싱글톤과 오브젝트 스코프 433 | 434 | DI가 필요한 중요한 이유 중 한 가지는 DI할 오브젝트의 생명주기를 제어할 수 있다는 것이다. DI를 프레임워크로 이용한다는 건 DI 대상 오브젝트를 컨테이너가 관리한다는 의미다. 435 | 436 | 오브젝트의 생성부터 관계설정, 이용, 소멸에 이르기까지의 모든 과정을 DI 컨테이너가 주관하기 때문에 그 오브젝트의 스코프를 자유롭게 제어할수 있다. 437 | 438 | 상태를 갖지 않도록 만든 오브젝트가 동시에 여러 스레드의 요청을 처리하는 이런 방식을 적용하려면, 만들어지는 오브젝트의 개수를 제어하는 일이 매우 중요하다. 439 | 440 | 전통적인 싱글톤 패턴은 오브젝트에 많은 제약을 가해서 만들어지기 때문에 그다지 권장되지 않는다. 그보다는 컨테이너가 오브젝트를 관리하는 IoC 방식이 유용하다. 441 | 442 | 스프링의 DI는 기본적으로 싱글톤으로 오브젝트를 만들어서 사용하게 한다. 컨테이너가 알아서 싱글톤을 만들고 관리하기 때문에 클래스 자체는 싱글톤을 고려하지 않고 자유롭게 설계해도 된다는 장점이 있다. 443 | 444 | 스프링에서는 싱글톤 외에도 다양한 스코프를 갖는 오브젝트를 만들어 DI에 사용할 수도 있다. HTTP 요청당 하나의 오브젝트가 만들어지거나, HTTP 세션당 하나씩 오브젝트가 만들어지게 할 수 있다. 개발자 스스로 445 | 일정한 스코프를 갖는 오브젝트를 만들고 이를 DI에 적용하는 것도 기능하다. 오브젝트 스코프를 제어하는 방법 또한 DI를 적용했기 때문에 가능한 활용 방법이다. 446 | 447 | - 테스트 448 | 449 | 마지막으로 살펴볼 DI의 중요한 용도는 바로 테스트다. 오브젝트와 협력해서 동작히는 오브젝트를 효과적으로 테스트하는 벙법은 기능한 한 고립시키는 것이다. 450 | 451 | 테스트할 대상이 의존하는 오브젝트를, 테스트를 목적으로 만들어진 목 오브젝트로 대체하면 유용하다. 452 | 453 | 의존 오브젝트를 대신해서 스텁 또는 목 오브젝트 같은 테스트 대역을 활용해야 한다. 이때도 DI는 중요한 역할을 한다. DI를 위해 만든 수정자 메소드를 사용하면 테스트 코드 안에서 수동으로 목 오브젝트를 주입할 수 454 | 있다. 또는 테스트용 으로 설정을 별도로 만드는 방법도 있다.DI 없이는 이런 테스트 기법을 적용하기란 불가능하다. 455 | 456 | DI의 활용 방법은 다양하다. DI의 용도는 디자인 패턴 중에서 오브젝트 합성 방식을 따르는 패턴과 관련이 있음을 알 수 있다. 457 | 458 | GoF의 디자인 패턴 중에서 인터페이스를 두고 오브젝트를 분리하는 구조를 가진 오브젝트 스코프의 패턴은 DI의 구조에 대부분 잘 들어맞는다. 459 | 460 | 그런 패턴의 장점들을 애플리케이션 전 영역에서 간단한 설정만으로 자연스럽게 적용할 수 있게 만들어주는 것이 바로 DI다. 461 | 462 | 하나의 DI 대상에 대해 핵심기능도 업무 변화에 따라 바꾸면서, 부가기능도 여러 개 추가해넣고, 테스트에서도 활용하는 식으로 사용해도 된다. 463 | 464 | ### 8.4.2 애스펙트 지향 프로그래밍(AOP) 465 | 466 | 애스펙트 지향 프로그래밍은 객체지향 프로그래밍(OOP)처럼 독립적인 프로그래밍 패러다임이 아니다. AOP와 OOP는 서로 배타적이 아니라는 말이다. 467 | 468 | 객체지향 기술은 매우 성공적인 프로그래밍 방식임에 분명하다. 하지만 한편으로는 점점 복잡해져 가는 애플리케이션의 요구조건과 기술적인 난해함을 모두 해결하는 데 한계가 있기도 하다. AOP는 바로 이러한 객체지향 469 | 기술의 한계와 단점을 극복하도록 도와주는 보조적인 프로그래밍 기술이다. 470 | 471 | 스프링의 목적인 POJO만으로 엔터프라이즈 애플리케이션을 개발하면서도 엔터프라이즈 서비스를 선언적으로 제공하는 데 반드시 필요한 것이 바로 AOP 기술이다. IoC/DI를 이용해서 POJO에 선언적인 엔터프라이즈 472 | 서비스를 제공할 수 있지만 일부 서비스는 순수한 객체지향 기법만으로는 POJO의 조건을 유지한 채로 적용하기 힘들다. 바로 이런 문제를 해결하기 위해 AOP가 필요하다. 473 | 474 | > AOP의 적용 기법 475 | > 476 | 477 | AOP를 자바 언어에 적용하는 기법은 크게 두 가지로 분류할 수 있다. 478 | 479 | - 첫 번째 스프링과 같이 다이내믹 프록시를 사용하는 방법 480 | 481 | 기존 코드에 영향을 주지 않고 부가기능을 적용하게 해주는 테코레이터 패턴을 응용한 것이다. 자바의 객체지향 패턴을 활용한 방법이기 때문에 만들기 쉽고 적용하기 간편하다. 482 | 483 | 대신 부가기능을 부여할 수 있는 곳은 메소드의 호출이 일어나는 지점뿐이라는 제약이 있다. 부가기능을 구현한 코드나 기능을 적용할 대상을 찾는 방법 모두 평범한 자바 클래스로 만들면 된다. 484 | 485 | 엔터프라이즈 개발에서 필요로 하는 AOP는 대부분이 다이나믹 프록시를 이용하는 프록시 방식의 AOP이다. 486 | 487 | - 두 번째 자바 언어의 한계를 넘어서는 언어의 확장을 이용하는 방법 488 | 489 | AspectJ는 강력한 고급 기능을 가진 AOP를 제공한다. AspectJ는 프록시 방식의 AOP에서는 불가능한 다양한 조인 포인트를 제공한다. 메소드 호출뿐 아니라 인스턴스 생성, 필드 액세스, 특정 호출 경로를 490 | 가진 메소드 호출 등에도 부가기능을 제공할 수 있다. 491 | 492 | 하지만 AOP 컴파일러를 이용한 빌드 과정을 거치거나, 클래스가 메모리로 로딩될 때 그 바이트 코드를 조작하는 위빙과 같은 별도의 방법을 이용해야 한다. 493 | 494 | 스프링은 프록시 방식의 AOP를 기본으로 하고 있지만, 원한다면 AspectJ를 이용한 AOP로 바꿔서 시용할 수 있다. 스프링의 특별한 기능 중에는 AspectJ를 꼭 시용해야 하는 것도 있다. 495 | 496 | > AOP의 적용 단계 497 | > 498 | 499 | AOP의 장점이 많다고해서 무작정 사용 하면 심각한 문제가 발생할 위험이 있다. 개발자 개개인이 아무렇게나 AOP를 남발해 서 사용하다 보면 다른 개발자가 만든 코드가 예상하지 않은 방식으로 돌아가는 등의 혼란을 500 | 초래할 수 있기 때문이다. 501 | 502 | AOP에 익숙하지 않은 상태라면 차근차근 단계를 밟아 AOP를 도입하는 접근 방법이 좋다. 503 | 504 | - AOP 적용 1단계: 미리 준비된 AOP 이용 505 | 506 | 처음에는 스프링이 미리 만들어서 제공하는 AOP 기능을 그대로 가져다 적용하는 방식으로 학습한다. 스프링이 직접 제공하는 대표적인 AOP는 바로 트랜잭션이다. 507 | 508 | AOP 설정을 통해서 트랜잭션이 어떻게 많은 오브젝트에 투명하게 적용되는지 관찰해보고, AOP의 특성과 동작원리를 이해하는 식으로 접근한다. 509 | 510 | 스프링에는 트랜잭션만큼 자주 사용되진 않지만 특정 아커텍처를 선택했을 때, 사용할 수 있도록 준비된 AOP 기능이 하나 더 있다. @Configurable 애노테이션을 이용해서 도메인 오브젝트에 DI를 자동 511 | 적용해주는 AOP 기능이다. 512 | 513 | 도메인 오브젝트를 전용 계층에 두고 접근하는 아키텍처 방식을 따를 때 반드시 필요하다. 프록시 AOP면 충분한 트랜잭션과 달리, @Configurable을 위해서는 AspectJ를 이용한 AOP가 반드시 필요하다. 514 | 515 | 두 가지 모두 간단한 설정을 추가하는 것만으로도 쉽게 적용되기 때문에 AOP에 대한 지식이나 경험이 많지 않더라도 간단히 이용가능하다는 장점이 있다. 516 | 517 | - AOP 적용 2단계: 전담팀을 통한 정책 AOP 적용 518 | 519 | 애플라케이션 전체적으로 이용 가능한 것을 소수의 AOP 담당자 관리하에 적용해볼 수 있다. 520 | 521 | 대표적으로 비즈니스 로직을 가진 오브젝트에 대한 보안, 특정 계층의 오브젝트 이용 전후의 작업 기록을 남 기는 로깅, 데이터 추적을 위한 트레이싱, 특정 구간의 실시간 성능 모니터링과 같은 정책적으로 적용할 만한 522 | 기능에 AOP를 이용하는 것이다. 523 | 524 | 기존 코드에는 당연히 아무런 영향을주지 않으면서 AOP를 언제든지 기능을 추가하거나 제거할 수 있다. 이런 특징을잘 이용하면 운영 중에 필요한기능 외에 개발 가이드라인이나 표준을 따라서 코드가 작성되어 있는지를 525 | 검증하는 작업을 AOP를 이용해 할 수 있다. 526 | 527 | AOP는 동적으로 동작하면서 개발 정책을 위반한 코드를 잡아내는 데도 유용하다. 528 | 529 | - AOP 적용 3단계: AOP의 자유로운 이용 530 | 531 | 애플리케이션 전체적으로 적용되는 정책 AOP를 위주로 했다면 이제는 개발자가 구현하는 기능에 적용하면 유용한 세부적인 AOP를 이용할 수 있다. 532 | 533 | ### 8.4.3 포터블 서비스 추상화(PSA) 534 | 535 | 세 번째 가능기술은 환경과 세부 기술의 변화에 관계없이 `일관된 방식`으로 기술에 접근할 수 있게 해주는 PSA(Portable Service Abstraction)다. 536 | 537 | POJO로 개발된 모드는 특정 환경이나 구현 방식에 종속적이지 않아야 한다. 538 | 539 | 특정 환경과 기술에 종속적이지 않다는게 그런 기술을 사용하지 않는다는 뜻은 아니다. 다만 POJO 코드가 그런 기술에 직접 노출되어 만들어지지 않는다는 말이다. 540 | 541 | 이를 위해 스프링이 제공하는 대표적인 기술이 바로 일관성 있는 서비스 추상화 기술이다. 542 | 543 | 스프링은 엔터프라이즈 개발에 사용되는 다양한 기술에 대한 서비스 추상화 기능을 제공한다. 544 | 545 | AOP나 탬플릿/콜백 패턴과 결합돼서 사용되기 때문에 직접적으로 서비스를 이용할 필요가 없다. 546 | 547 | 트랜잭션 서비스 추상회는 코드를 이용해 트랜잭션을 제어하지 않는다면 직접 이용할 이유가 없다. 트랜잭션은 대부분 AOP를 이용해 적용하기 때문에 직접 코드를 만들지 않는다. 대신 설정에서 스프링의 트랜잭션 추상화 548 | 인터페이스인 PlatformTransactionManager를 구현한 구체적인 서비스 클래스를 빈으로 등록해줘야 한다. 549 | 550 | 직접 스프링이 제공하는 API를 사용해서 만드는 경우도 있다. OXM이나 JavaMail을 이용한다면 스프링이 정의한 추상 API를 이용해 코드를 작성한다. 그리고 구체적인 기술과 설정은 XML 파일 안에서 551 | 지정한다. 552 | 553 | 스프링의 서비스 추상화의 개념과 장점을 잘 이해한다면 때에 따라 직접 서비스 추상화 기법을 적용할 필요도 있다. 554 | 555 | 서비스 추상회를 위해 펼요한 기술은 DI뿐이다. 결국 DI 응용 방법의 한 가지이므로 DI를 적극 활용해서 개발한다면 서비스 추상화는 자연스럽게 만들어 쓸 수 있다. 556 | 557 | 서비스 추상회는 단지 구체적인 기술에 종속되지 않게 하기 위해서만 사용되는 건 아니다. 테스트가 어렵게 만들어진 API나 설정을 통해 주요 기능을 외부에서 제어하게 만들고 싶을 때도 이용할 수 있다. 558 | 559 | ## 8.5 정리 560 | 561 | 스프링의 기본적인 정의와 그 목적 그리고 그것을 이루기 위해 제공하는 기술을 정리했다. 562 | 563 | 스프링의 상세한 기술을 공부하기 전에 먼저 이해하고 기억해야 할 사항 564 | 565 | - 스프링은 개발 철학과 목표를 분명히 이해하고 사용해야 한다. 566 | - 스프링은 오픈소스 소프트웨어이며, 애플리케이션 개발의 모든 기술과 영역을 종합적으로 다루는 애플리케이션 프레임워크이다. 567 | - 엔터프라이즈 애플리케이션 개발의 `복잡함`은 `비즈니스 로직`과 `엔터프라이즈 시스템의 기술적인 요구`에 의해 발생한다. 기존의 접근 방법은 이러한 복잡도를 낮추지 못하며 자바의 객체지향적인 568 | 장점을 포기해야 한다는 문제점이 있다. 569 | - 자바의 근본인 객체지향적인 원리에 충실하게 개발할 수 있으며, 환경과 규약에 의존적이지 않은 `POJO를 이용한 애플리케이션 개발`은 엔터프라이즈 시스템 개발의 복잡함이 주는 많은 문제를 해결할 수 있다. 570 | - 스프링의 목적은 POJO를 이용해 엔터프라이즈 애플리케이션을 쉽고 효과적으로 개발 할 수 있도록 지원해주는 데 있다. 571 | - POJO 방식의 개발을 돕기 위해 스프링은 IoC/DI, AOP, PSA와 같은 가능기술을 프레임워크와 컨테이너라는 방식을 통해 제공한다. 572 | 573 | 스프링을 가장 잘 이해하기 위한 방법은 `엔터프라이즈 개발이 주는 복잡함을 제거`하고, `POJO 프로그래밍`이라는 효과적인 방법을 사용할 수 있게 하는지 관심을 갖는 것이다. -------------------------------------------------------------------------------- /8장_스프링이란_무엇인가?/8장_스프링이란_무엇인가_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/8장_스프링이란_무엇인가?/8장_스프링이란_무엇인가_안송이.pdf -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/9장_스프링_프로젝트_시작하기_김석래.md: -------------------------------------------------------------------------------- 1 | # 9. 스프링 프로젝트 시작하기 2 | 3 | 스프링을 이용해 애플리케이션 프로젝트를 처음 구성할 때 알아야 하는 기본적인 내용에 대해서 알아보는 장이다. 4 | 5 | 스프링 개발에 도움이 되는 개발 툴과 빌드 방법에 대해서 살펴 보고, 스프링을 애플리케이션에 적용할 수 있는 아키텍처의 종류와 특징에 대해서 알아본다. 6 | 7 | 이전의 개발 툴은 크게 변함 없기도 하고, 환경에 따라 제한적인 부분이 있어서 휙 읽어도 된다. 빌드 방법에 대한 부분은 Maven에 대해서 훑어볼 수 있는 기회가 될 수 있다. 아키텍처에 대한 내용은 꼭 읽어봐야 8 | 하는 내용이라 생각한다. 9 | 10 | 책을 읽는 사람의 경력이나 경험에 따라서 다를 수도 있지만 경험이 부족한 경우 많은 프로젝트를 보지 못했을 수도 있기 때문에 책에서 언급하는 부분은 꼭 정독하고 고민해볼 것을 추천한다. 11 | 12 | ## 9.1 자바 엔터프라이즈 플랫폼과 스프링 애플리케이션 13 | 14 | 자바 언어를 사용하는 모든 종류의 프로젝트라면 스프링으로 만들 수 있다. 15 | 16 | 자바 엔터프라이즈 애플리케이션은 서버에서 동작하며, 클라이언트를 상대로 서비스를 제공하도록 되어 있다. 즉, 클라이언트의 요청을 받아서 그에 대한 작업을 수행하고 그 결과를 돌려주는 것이 기본적인 동작 방식이다. 17 | 하지만 클라이언트의 요청 없이도 정해진 시간이나 특정 이벤트 발생에 따라 독자적으로 작업을 수행하기도 한다. 18 | 19 | ### 9.1.1 클라이언트와 백엔드 시스템 20 | 21 | 클라이언트와 백엔드 시스템 구성으로 가장 많이 사용되는 구조는 클라이언트가 웹 브라우저이고, 백엔드 시스템이 DB인 구성이다. 웹 클라이언트와 DB가 사용되지 않는 시스템은 거의 없으니, 이를 스프링이 사용되는 22 | 애플리케이션의 기본 구조라고 생각 할 수 있다. 23 | 24 | 이 때 스프링의 주요 기능은 웹 브라우저에서 DB의 데이터를 저장, 조회하는 데 집중된다. 하나의 스프링 애플리케이션이 동시에 여러 종류의 클라이언트를 상대로 서비스를 제공하기도 하고, 메시징 서버, 메일 서버 등등 25 | 사용하기도 한다. 26 | 27 | 한 번에 여러 종류의 백엔드 시스템을 이용하는 것도 가능하고, 여러 개의 DB를 동시에 이용하는 것도 가능하다. 28 | 29 | ### 9.1.2 애플리케이션 서버 30 | 31 | 스프링으로 만든 애플리케이션을 자바 서버환경에 배포하기 위해서는 JavaEE 서버가 필요하다. JavaEE 표준을 따르는 애플리케이션 서버는 크게 두 가지로 구분할 수 있다. 32 | 33 | - 경량급 WAS / 서블릿 컨테이너 34 | 35 | 스프링은 기본적으로 톰캣(Tomcat)이나 제티(Jetty) 같은 가벼운 서블릿 컨테이너만 있어도 충분하다. 36 | 37 | 서블릿 컨테이너로도 엔터프라이즈 애플리케이션에 필요한 핵심기능을 모두 이용할 수 있다. 기존에 EJB와 WAS를 사용해야 가능했던 선언적인 트랜잭션이나 선언적 보안, DB 연결 풀링, 리모팅이나 웹 서비스는 물론이고 38 | 추가적인 라이브러리의 도움을 받으면 분산/글로벌 트랜잭션까지도 가능하다. 39 | 40 | - WAS 41 | 42 | 고가의 WAS를 사용하면 그만큼 장점이 있다. 미션 크리티컬한 시스템에서 요구하는 고도의 안정성이나 고성능 시스템에서 필수적인 안정적인 리소스 관리, 레거시 시스템의 연동이나 기존 EJB로 개발된 모듈을 함께 43 | 사용하는 등의 필요가 있다면 상용 또는 오픈소스 WAS를 이용할 수 있다. 44 | 45 | WAS는 상대적으로 관리 기능이나 모니터링 기능이 뛰어나서 여러 대의 서버를 동시에 운영할 때 유리한 점이 많다. 46 | 47 | 스프링의 개발팀은 필요할 떄는 비용을 들이더라도 적합한 조건을 가진 WAS를 사용할 것을 권장한다. 무조건 해야 한다는 아니고, WAS를 사용할 때 분명한 이유와 근거가 있는지 먼저 충분히 검토해야 한다. 48 | 49 | 훨씬 가볍고 빠르며 저렴한 비용으로 사용할 수 있는 서블릿 컨테이너로도 대개는 충분한데 특별한 이유도 없이 무겁고 다루기 힘든데다 비싸기까지한 WAS를 사용할 필요는 없기 때문이다. 50 | 51 | > 스프링 소스 tcServer 52 | 53 | 일반적으로 개발환경과 운영환경에서 가장 많이 사용되는 자바 서버는 웹 모듈만 지원하는 서블릿 컨테이너인 아파치 톰캣이다. 54 | 55 | 스프링 개발에 책임지고 있는 기업인 스프링소스의 전문가들이 톰캣을 기반으로 엔터프라이즈 스프링 애플리케이션에 최적화된 경량급 애플리케이션 서버인 tcServer를 개발했다. 56 | 57 | tcServer를 이용하면 기존 톰캣에서는 아쉬웠던 고급 서버 관리 기능, 배포 기능과 진단 기능을 포함해서 톰캣 전문가에게 받는 기술지원도 함께 제공받을 수 있다. 58 | 59 | tcServer의 큰 장점은 스프링 개발회사가 개발하는 것인 만큼 스프링 애플리케이션 개발과 운영에 꼭 필요한 중요한 기능이 많이 제공된다는 점이다. 60 | 61 | tcServer는 개발자 버전이 따로 있어서 개발을 위해서라면 자유롭게 이용 가능하다. 62 | 63 | ### 9.1.3 스프링 애플리케이션의 배포 단위 64 | 65 | 스프링으로 만든 애플리케이션은 세 가지 단위로 배포할 수 있다. 66 | 67 | - 독립 웹 모듈 68 | 69 | 스프링은 보통 war로 패키징된 독립 웹 모듈로 배포된다. 톰캣 같은 서블릿 컨테이너를 쓴다면 독립 웹 모듈이 유일한 방법이다. 70 | 71 | WAS로 배포한다고 하더라도 독립 웹 모듈을 사용하는 경우가 대부분이다. 여러 개의 웹 모듈을 묶어서 하나의 웹 어플리케이션 모듈로 만들지 않는 한 독립 웹 모듈이 가장 단순하고 편리한 배포 단위이다. 72 | 73 | - 엔터프라이즈 애플리케이션 74 | 75 | 경우에 따라 확장자가 ear인 엔터프라이즈 애플리케이션으로 패키징해서 배포할 수 있다. 76 | 77 | 스프링 애플리케이션에서 EJB 모듈을 긴밀하게 사용하거나 반대로 EJB 모듈에서 스프링으로 만든 애플리케이션을 이용해야 한다면, EJB와 스프링 웹 모듈을 엔터프라이즈 애플리케이션으로 통합해야 한다. EJB 모듈은 78 | 없지만 엔터프라이즈 애플리케이션 배포 방식을 선택하는 경우가 있다. 하나 이상의 웹 모듈과 별도로 분리된 공유 가능한 스프링 컨텍스트를 엔터프라이즈 애플리케이션으로 묶어주는 방법이다. 79 | 80 | - 백그라운드 서비스 모듈 81 | 82 | rar 패키징 방법 또한 존재한다. rar는 리소스 커넥터를 만들어 배포할 때 사용하는 방식으로, 스프링으로 만든 애플리케이션이 UI를 가질 필요는 없고, 서버 내에서 백그라운드 서비스처럼 동작할 필요가 있다면 83 | rar 모듈로 만들어서 배포할 수 있다. 이때는 J2EE 1.4나 그 이상의 표준을 따르는 WAS가 필요하다. 84 | 85 | 운영 플랫폼이나 서버의 종류는 개발 중에라도 언제든지 필요에 따라 변경이 가능하다. 86 | 87 | 어차피 서블릿 컨테이너나 웹 모듈 모두 JavaEE 표준의 일부일 뿐이기 때문에 설정만 바꾸면 어렵지 않게 이전이 가능하다. 88 | 89 | 다만 특정 서버환경에서만 제공하는 기능을 사용한다면 변경이 힘들 수도 있다. 장기적으로 서버를 변경하거나 서버의 종류를 바꿀 가능성이 있다면, 서버의 기능에 종속되지 않도록 주의하거나 손쉽게 다른 서버의 기능으로 90 | 변경 가능하도록 추상화해서 사용해야 한다. 91 | 92 | ## 9.2 개발도구와 환경 93 | 94 | 개발 시에 퍼포먼스를 향상 시킬 수 있는 방법 중 하나는 개발 도구이다. 개발 도구는 사용하는 언어의 자동완성이나 디버깅에 도움을 주어 보다 높은 분석력과 개발 속도를 높일 수 있다. 95 | 96 | ### 9.2.1 JavaSE와 JavaEE 97 | 98 | > JavaSE / JDK 99 | 100 | 스프링 4, 5를 사용할 때 필요한 JDK 버전에 대해서 이야기 하는 내용으로 현재 스프링 5를 사용하기 위해서는 JDK 1.8버전을 사용해야 한다. 라고 정리 할 수 있을 것 같다. 101 | 102 | > JavaEE / J2EE 103 | 104 | J2EE를 잘모르는 듯.. 일단 책의 내용을 적어보면.. 105 | 106 | 스프링 3.0이 사용될 자바 엔터프라이즈 플랫폼으로는 J2EE 1.4 버전이나 JavaEE 5.0이 필요하고 한다. 스프링 3.0 자체는 JDK6.0과 JavaEE 5.0을 기준으로 개발되었지만, 주요 기능은 JDK 107 | 5.0에서 동작하는 J2EE 1.4버전과 호환되게 제작되어 있다. 108 | 109 | 다만 J2EE 1.4 버전 서버를 사용할 때는 JDK 5.0에서 동작하는지 반드시 확인해야 한다. 만약 WAS 자체가 JDK 5.0에서는 사용할 수 없다면 스프링 3.0의 이용은 불가능하다. 110 | 111 | 그러니까 스프링 3.0의 최소 사양은 JDK 5.0이기 때문에 JDK 5.0이 호환되지 않는 WAS를 사용한다면 문제가 발생할 수 있다. 112 | 113 | ### 9.2.2 IDE 114 | 115 | 스프링 애플리케이션은 자바 5 또는 그 이상의 언어를 지원하는 자바 개발도구와 스키마를 지원하는 XML 편집기 정도만 있다면 어떤 개발환경에서는 불편 없이 개발이 가능하다. 116 | 117 | ANT나 Maven 같은 빌드 툴을 지원하고 톰캣이나 자바 서버로 바로 배포해서 실행해 볼 수 있는 환경이라면 더 좋다. 118 | 119 | 옛날 notepad로 개발하는 시대가 아니기 때문에 모든 일을 처리할 수 있도록 만들어진 통합개발환경(IDE)을 사용하는 것이 좋다. 120 | 121 | 이클립스와 IntelliJ IDEA의 점유율이 높고 사용할 수 있는 환경에 따라서 도입해서 사용하면 될 것 같다. 122 | 123 | 둘다 장단점이 존재하며 IntelliJ는 무료 버전과 유료버전이 존재하는데 유료 버전을 사용하는 경우 많은 기능을 통합 툴로 한번에 사용할 수 있어서 좋다. 124 | 125 | ### 9.2.3 SpringSource Tool Suite 126 | 127 | 여기서는 이클립스를 사용하는 환경에서 STS를 사용해 볼 것을 추천하며 어떤 부분에서 이클립스보다 나은 기능을 제공하는지 소개한다. 128 | 129 | STS는 이클립스를 기반으로 주요한 스프링 지원 플러그인과 관련 도구를 모아 스프링 개발에 최적화되도록 만들어진 IDE이다. 이클립스와 같이 플러그인 방식을 지원하는 툴을 사용하면 원하는 기능을 필요에 따라 추가할 130 | 수 있다는 장점이 있다. 반면 플러그인을 사용한다는 말은 플러그인과 이클립스의 버전이 호환되도록 계속 관리해야 한다는 것을 감안해야 한다. 131 | 132 | 이클립스의 문제점이 플러그인의 버전이 올라가서 업데이트 했을때 의존성에 대해서 고려하여 설치한다는 것을 문제점으로 들어 STS의 경우 플러그인의 의존성에 대해서 호환성 문제 또는 버전 이슈를 충분히 검증한다는 장점이 133 | 있다. 134 | 135 | > SpringIDE 플러그인 136 | 137 | SpringIDE는 스프링 개발에 유용한 기능을 제공하는 플러그인의 모음이다. 스프링 프로젝트와 설정파일 생성 위저드, 편리한 스프링의 XML 설정파일 에디터, 빈의 의존관계 그래프, 네임스페이스 관리 기능 등의 138 | 편리한 기능과 도구를 제공한다. 139 | 140 | XML 설정파일을 사용하게 되면 빈의 클래스이름이나 레퍼런스 이름을 입력하면서 오타가 발생하기 쉽다. 빈의 클래스 이름은 패키지를 포함해서 전체 클래스 이름을 입력해야 하기 때문에 번거롭다. 하지만 141 | SpringIDE가 제공하는 XML 설정파일 에디터를 이용하면 클래스 이름이나 참조하는 빈의 이름을 실시간으로 검증해서 오류를 확인해주기 때문에 오타로 인한 에러의 위험을 사전에 방지해준다. 142 | 143 | 클래스 이름이나 프로퍼티 이름을 자바 편집기 내의 자동완성 기능과 비슷하게 찾을 수 있게 해줘서 매우 편리하다. SpringIDE의 XML 에디터의 자동완성 기능 몇 가지를 정리한다. 144 | 145 | - 빈 클래스 이름 자동완성 146 | 147 | 이클립스보다 유용한 기능은 bean 태그의 class 애트리뷰트를 입력 시 클래스 이름에 대한 자동완성을 지원한다. 148 | 149 | 클래스 이름이 긴 경우, 빈의 프로퍼티, ref 어트리뷰트로 다른 빈을 참조할 때도 자동완성 기능을 통해 손 쉽게 추가가 설정이 가능하다. 150 | 151 | - 빈 설정 오류검증 기능 152 | 153 | 자동완성 기능은 처음 입력할 때 유용하고, 개발 중에 클래스나 프로퍼티 이름이 변경됐거나 XML 파일을 직접 수정하는 경우에도 오류 검증 기능이 도움이 된다. 154 | 155 | - 프로젝트 생성, 설정파일 생성, 빈 등록 위저드 156 | 157 | 스프링 프로젝트 생성을 위해 스프링 프로젝트 위저드를 이용할 수 있다. 하지만 스프링 프로젝트를 만드는데 필요한 라이브러리 선정이나 빌드 스크립트 추가, 프로젝트 구조 등을 지정할 수 있는 고급 기능등이 있다. 또한 158 | 미리 준비된 프로젝트 템플릿을 이용해 프로젝트를 생성하는 기능도 제공된다. 159 | 160 | - 빈 의존관계 그래프 161 | 162 | SpringIDE는 XML 설정 파일을 읽어서 자동으로 그래프를 그려주는 기능을 제공한다. 각각의 빈이 어떻게 서로 참조하고 있는지, 어떤 프로퍼티를 갖고 있는지 한 눈에 볼 수 있다. 163 | 164 | - AOP 적용 대상 표시 165 | 166 | AOP가 어려운 이유는 부가기능을 담은 어드바이스가 어느 오브젝트에 적용될 지 한 눈에 보이지 않기 때문이다. @Transactional처럼 직접 AOP 적용 클래스나 인터페이스에 어노테이션을 추가하는 특별한 방법을 167 | 사용하는 경우는 그나마 낫다. 하지만 포인트 컷 표현식을 사용하거나 이름 패턴을 이용하는 포인트컷 빈을 이용하는 경우에는 적용 대상을 파악하기가 쉽지 않다. 168 | 169 | 그래서 가능한 단순한 포인트컷 표현식을 사용하고 명확한 규칙을 가지고 적용 대상 빈을 선정해야 한다. 170 | 171 | STS에는 SpringIDE 외에도 AJDT라는 AspectJ 개발 플러그인이 함께 설치되어 포인트컷이 적용되는 대상 빈을 설정파일 안에서 한눈에 확인 할 수 있다. 172 | 173 | 빈의 이름을 사용해 트랜잭션 적용 대상을 선정하는 방식은 메서드 시그니처를 이용한 포인트컷 표현식 보다 만들기 쉽고 대상을 확인하기가 명확해서 자주 사용된다. 174 | 175 | AOP 적용 마커는 포인트컷을 XML에서 정의하지 않고 @Transactional 어노테이션을 사용한 경우에도 동일하게 나타나 개발 및 분석에 도움을 준다. STS에서 AOP마커를 보이게 하기 위해서는 프로젝트를 176 | 스프링 프로젝트로 만들고, 프로젝트 설정의 Spring-Bean Support 항목에 XML을 추가해줘야 한다. 177 | 178 | > STS 플러그인 179 | 180 | STS 플러그인은 스프링 개발과 설정파일 편집을 지원하는 SpringIDE에 더해서 스프링 애플리케이션의 서버 배치와 같은 추가 기능을 제공해준다. 또 VMWare 가상화 클라우드 서비스에도 손쉽게 스프링 181 | 애플리케이션 배치할 수 있도록 지원한다. 스프링 개발 시 자주 발생하는 예외에 대한 지식 베이스를 검색해볼 수 있는 기능도 제공한다. 182 | 183 | > 기타 플러그인 184 | 185 | STS가 추가로 제공하는 플러그인 중 유용한 플러그인을 소개한다. 186 | 187 | - M2Eclipse 188 | 189 | Maven을 지원하는 이클립스 플러그인으로 STS도 동일하게 지원하여 Maven 관리를 더욱 효율적으로 할 수 있도록 한다. 190 | 191 | - AJDT 192 | 193 | AspectJ Development Tool의 약자로, 이클립스에서 AspectJ AOP를 이용한 개발을 지원하는 편리한 툴이다. 스프링 애플리케이션에서 AspectJ 방식의 AOP를 사용하는 경우 AspectJ와 194 | 관련된 편집 기능과 AOP 적용 대상에 대한 마커 등의 AJDT기능을 활용할 수 있다. 195 | 196 | - VMCI 197 | 198 | VMCI(Virtual Machine Core Integration) 플러그인은 VMWare 서버 또는 워크스테이션과의 연동을 지원하는 플러그인이다. STS의 VMWare 배치 기능에 주로 사용되기 위해 추가된 199 | 것으로 가상화 서버나 클라우드 서비스에 관한 기능이 추가되는 경우 유용할 것으로 예상한다고 한다. 200 | 201 | - 이클립스 표준 플러그인 202 | 203 | 이클립스 플랫폼에서 제공하는 주요 표준 플러그인에 포함된 것으로는 웹 개발을 지원하는 WTP(Web Tool Platform), EMP(Eclipse Modeling Project), Mylyn, DSDP( 204 | Device Software Development Platform)등이 있다. 205 | 206 | ### 9.2.4 라이브러리 관리와 빌드 툴 207 | 208 | 애플리케이션의 아키텍처를 결정하고 사용 기술을 선정하고 나면 애플리케이션 프로젝트를 IDE에 구성하게 되는데, 이때 가장 어려운 부분은 바로 필요한 프레임워크 모듈과 라이브러리 파일을 선택해서 프로젝트의 빌드 패스를 209 | 넣어주는 일이다. 210 | 211 | > 라이브러리 관리의 어려움 212 | 213 | 하나의 프로젝트를 만들기 위해서 사용하는 라이브러리는 적게는 한자리 수에서 많게는 세 자리 수까지 포함해야 할 때가 있다. 그리고 라이브러리마다 여러 개의 버전이 있어 각 라이브러리마다 정확히 어떤 버전을 사용해야 214 | 하는지 알아야 한다. 215 | 216 | 또한 필요한 라이브러리 조합을 만들다보면 복잡한 의존관계 속에서 같은 라이브러리의 다른 버전이 동시에 필요한 경우가 생겨 문제가 발생하는 경우도 있다. 217 | 218 | 자바는 모듈이라는 개념이 없는데, jar는 기본적으로 압축 패키징 방법일 뿐이지 구분 가능한 독립된 모듈이 아니다. 만약 같은 패키지와 이름을 가진 클래스지만 구현이 다른 클래스 파일 a.jar, b.jar가 219 | 존재하며 모두 클래스 패스에 있다고 가정할 때, 이 경우 모듈 a.jar 안에 있는 클래스를 사용하겠다거나 b.jar 안에 있는 클래스를 사용하겠다고 선택할 수 있을까? 220 | 221 | a.jar, b.jar가 같은 웹 모듈의 라이브러리에 등록되어 있다면 jar 파일 구분은 별 의미가 없다. 222 | 223 | 다시 정리하면 이러한 문제를 해결하기 위해서 재패키징 방법을 사용하여 한쪽 버전의 클래스를 다른 패키지로 옮겨 서로 구별되는 클래스로 만들게 된다. 224 | 225 | 재패키징 방법은 모든 소스 또는 바이트코드를 일일이 찾아서 수정해주는 작업으로 쉽지 않다. 226 | 227 | > 라이브러리 선정 228 | 229 | 다행히 위 문제가 없는 상태에서 프로젝트의 기본 틀이 잡혀 있고, 사용할 라이브러리와 프로젝트 폴더 구조까지 다 결정된 프로젝트에 참여해서 순수한 애플리케이션 개발에 전념할 수 있다면 걱정하지 않아도 된다. 하지만 230 | 자신이 직접 프로젝트를 구성하고 필요한 라이브러리를 선정하거나 추가, 제거 하는 등의 관리를 해야 하는 상황이라면 여러모로 신경써야 할 것들이 많다. 231 | 232 | 가장 먼저 해야 할 작업은 스프링으로 만드는 애플리케이션에서 정확히 어떤 기능이 필요한 지 정리하는 것이다. 각 기능을 지원하는 기술이 여러 종류가 있다면 그 중에 어떤 것을 사용할지도 결정해야 한다. 233 | 234 | - 스프링 모듈 235 | 236 | 사용할 기능과 기술 목록이 정의되었다면 스프링 모듈부터 선정한다. 스프링 모듈의 일부는 모든 애플리케이션에서 공통적으로 사용되는 필수 모듈이고 그 외에는 애플리케이션의 아키텍처와 사용 기술에 따라서 선택적으로 적용할 237 | 수 있다. 238 | 239 | 스프링 모듈 사이에도 의존관계가 존재하며 의존 관계에는 필수와 선택으로 나뉘어진다. 240 | 241 | - 라이브러리 242 | 243 | 스프링의 각 모듈은 또 다른 모듈에 의존하기도 하지만 오픈소스 라이브러리 또는 표준 API를 필요로 하기도 하고 경우에 따라서는 상용 제품의 라이브러리에 의존한다. 244 | 245 | 때로는 각 라이브러리를 활용하는 방법에 따라서 다른 서드파티 라이브러리를 필요로 하는 경우가 있다. 이 때는 해당 프레임워크나 라이브러리의 문서를 참조하여 필요한 라이브러리가 어떤 것인지 직접 찾아봐야 한다. 246 | 247 | 라이브러리 관리는 어떤 방법을 쓰더라도 간단하지 않다. 이미 만들어진 다양한 프레임워크와 라이브러리의 조합으로 애플리케이션을 만드는 시대이기 때문이다. 248 | 249 | > 빌드 툴과 라이브러리 관리 250 | 251 | Maven과 Ant는 자바의 대표적인 빌드 툴이다. (gradle도 있다.) 빌드 툴은 개발팀이나 조직의 정책 또는 경험에 따라서 결정하면 될 것이고, 여기서는 빌드 툴이 지원하는 의존 라이브러리 관리 기능에 대해서 252 | 알아본다. 253 | 254 | IDE는 코드만 작성하면 자동으로 컴파일 해주는 자동빌드 기능이 있고, 관련 빌더를 추가하거나 확장함으로써 복잡한 빌드 작업도 간단하게 진행할 수 있다. 255 | 256 | 하지만 IDE를 사용할 수 있는 환경이 아닌 경우에도 일관된 빌드가 가능하도록 만드는 것이 중요하다. 257 | 258 | 예를 들어 서버에 배치했을 때나 통합 테스트 환경에서 애플리케이션을 직접 빌드해야 하는 경우가 있다. 이러한 경우에 IDE에서와 동일하게 애플리케이션 빌드 작업이 수행되어야 하는데 이러한 경우를 대비해서 IDE를 259 | 사용하되 ant, maven, gradle 같은 환경에서 독립적인 빌드 툴을 함께 사용하는 것이 바람직하다. 260 | 261 | ant가 이클립스에 기본 내장되어 제공될 정도로 표준 자바 빌드 툴로 자리잡을 때 maven이라는 새로운 개념의 빌드 툴이 등장 했다. Maven은 단순 빌드 툴을 넘어서 개발 과정에서 필요한 빌드, 테스트, 배치, 262 | 문서화, 리포팅 등의 다양한 작업을 지원하는 종합 프로젝트 관리 툴의 성격을 띠고 있다. 263 | 264 | Maven의 특징은 POM이라고 불리는 프로젝트 모델 정보를 이용한다는 것이다. 265 | 266 | 절차적인 스크립트와 구조가 비슷한 ant와는 달리 maven은 선언적이다. 프로젝트의 주요한 구조와 특징, 필요한 정보를 POM의 프로젝트 모델 정보로 만들어두면, 이를 참조해서 Maven에 미리 정해진 절차에 따라 267 | 빌드 또는 프로젝트 관리 작업을 진행할 수 있다. 268 | 269 | Maven POM이 가진 독특한 특징 중의 하나는 애플리케이션이 필요로 하는 의존 라이브러리를 선언해두기만 하면 원격 서버에서 이를 자동으로 다운로드 받아서 사용할 수 있게 해주는 것이다. 예를 들어 애플리케이션의 270 | 의존정보를 Pom.xml에 작성해두면 Maven으로 빌드 할 때 필요한 라이브러리가 개발 PC의 공통 저장소에 있는 지 확인하고 없으면 Maven의 원격 서버에서 자동으로 다운로드 받아서 설치해준다. 271 | 272 | Maven의 의존 라이브러리 관리 기능이 제공하는 유용한 기능은 전이적 의존 라이브러리 추적 기능이다. POM의 의존정보에 하나의 라이브러리를 지정하면, 지정된 라이브러리가 동작하는데 여타 라이브러리까지 함께 273 | 다운로드 해주는 기능이다. 274 | 275 | 프로젝트에 의존 라이브러리 정보만 갖게 하고 필요한 라이브러리는 Maven 빌드 과정 중에 자동으로 다운로드 받거나 로컬 공통 리포지토리에서 가져오게 하여 효율적으로 관리하게끔 돕는다. 276 | 277 | 프로젝트마다 사용 기술과 아키텍처를 모두 뒤집어서 다르게 구성하는 경우는 드문데, 이때 활용할 수 있는 것이 공통 의존 라이브러리 정보로 만들어 의존정보를 정의하여 의존성 관리를 하는 것도 가능하다. 278 | 279 | > 스프링 모듈의 두 가지 이름과 리포지토리 280 | 281 | 스프링 모듈 jar 파일의 이름을 보면 두 가지 종류가 있다. `spring-`으로 시작하는 모듈과 `org.`로 시작하는 모듈 두 가지 이름의 파일이 존재한다. 282 | 283 | `spring-`으로 시작하는 파일 명은 Maven에서 사용하는 명명 규칙을 따른것으로 아티펙트 아이디와 버전을 조합해서 파일이름으로 사용한다. 만약 일반 Maven 리포지토리에서 스프링 모듈을 검색하면 이와 같은 284 | 이름의 파일을 발견할 수 있다. 285 | 286 | `org.`로 시작하는 파일 명은 OSGi의 모듈 명명 규칙을 따른것으로 OSGi 플랫폼에서 사용하지 않는다고 할지라도 OSGi 스타일의 모듈 이름을 사용하도록 권장한다. 그래서 스프링 배포 버전의 dist 폴더에 287 | 들어있는 모듈 이름도 모두 OSGi 스타일의 이름이다. 288 | 289 | OSGi 호환 이름을 갖는 스프링 모듈을 사용하는 경우 Maven 표준 리포지토리 대신 스프링 소스가 제공하는 엔터프라이즈 번들 리포지토리를 사용해야 한다. 290 | 291 | ```xml 292 | 293 | 294 | org.springframework 295 | org.springframework.core 296 | 297 | ``` 298 | 299 | ## 9.3 애플리케이션 아키텍처 300 | 301 | 클라이언트와 백엔드 시스템의 종류와 사용 기술, 연동 방법을 결정했다면 시스템 레벨의아키텍처는 대략 구성된 셈이다. 다음으로 결정할 사항은 스프링 웹 애플리케이션의 아키텍처이다. 302 | 303 | 아키텍처는 여러가지 방식으로 정의되고 이해될 수 있는 용어로 가장 단순한 정의를 보면 어떤 경계 안에 있는 내부 구성요소들이 어떤 책임을 갖고 있고, 어떤 방식으로 서로 관계를 맺고 동작하는지 규정하는 것이라 할 수 304 | 있다. 305 | 306 | 아키텍처는 단순하게 정적인 구조를 나타내는 것으로만 생각하기 쉽지만 실제로는 그 구조에서 일어나는 동적인 행위와 깊은 관계가 있다. 307 | 308 | ![](images/001.jpeg) 309 | 310 | ### 9.3.1 계층형 아키텍처 311 | 312 | 관심, 책임, 성격, 변하는 이유와 방식이 서로 다른 것들을 분리함으로써 분리된 각 요소의 응집도는 높여주고, 서로의 결합도를 낮춰줬을 때의 장점과 유익이 무엇인지 살펴봤다. 313 | 314 | 성격이 다른 모듈이 강하게 결합되어 한데 모여 있으면 한 가지 이유로 변경이 일어날 때 그와 상관이 없는 요소도 함께 영향을 받게 된다. 따라서 불필요한 부분까지 변경이 일어나고 그로 인해 작업은 더뎌지고 오류가 315 | 발생할 가능성이 높아진다. 316 | 317 | 어느 부분을 수정해야 할지를 파악하기도 쉽지 않기 때문에 인터페이스와 같은 유연한 경계를 만들어두고 분리하거나 모아주는 작업이 필요하다. 318 | 319 | > 아키텍처와 SoC 320 | 321 | 이전까지는 오브젝트 레벨에서 분리의 문제에 대해서 생각해 봤다. 성격이 다른 코드가 얽혀있는 것을 두 개의 오브젝트로 분리하고, 그 사이에 유연한 결합을 가질 수 있도록 인터페이스를 두고, 그 관계를 맺어주는 제 322 | 3의 존재인 DI 컨테이너를 둬서 오브젝트끼리는 직접적인 관계를 알지 못하도록 만드는 것이 지금까지 해온 DI를 기반으로한 유연한 설계와 구현 전략이었다. 323 | 324 | 이러한 원리는 아키텍처 레벨에서도 동일하게 적용할 수 있다. 오브젝트를 하나의 모듈 단위라고 생각해보면 때론 그보다 작은 단위, 예를 들면 하나의 클래스 안에 있는 메서드 레벨에서도 같은 원리를 적용할 수 있다. 325 | 심지어 하나의 메서드 안의 코드에도 같은 방식의 접근이 가능하다. 반대로 모듈의 단위를 크게 확장해 볼 수도 있다. 326 | 327 | 애플리케이션을 구성하는 오브젝트들을 비슷한 성격과 책임을 가진 것들끼리 묶을 수도 있다. 또 비즈니스 로직을 구현해놓은 비즈니스 서비스 오브젝트들도 같은 성격으로 묶을 수 있다. 서비스 오브젝트들은 특정 기술과 328 | 환경에 종속되지 않으면서 도메인의 업무에는 밀접하게 관련을 갖고 있는 POJO로 만들어진다. 329 | 330 | 서블릿 컨테이너로부터 받은 사용자의 요청정보를 해석해서 그것을 POJO 기반의 서비스 오브젝트에 전달해주고, 결과를 받아서 다시 웹 사용자 인터페이스에 표시 가능한 형태로 변환해주는 일을 한다. 331 | 332 | 성격과 책임이 분명히 다른 것들이 한데 모여있으면 많은 문제가 발생한다. 그래서 성격이 다른 것은 아키텍처 레벨에서 분리해주는 것이 좋다. 분리된 각 오브젝트는 독자적으로 개발과 테스트가 가능해서 개발과 변경 작업이 333 | 모두 빨라질 수 있다. 334 | 335 | 이렇게 책임과 성격이 다른 것을 크게 그룹으로 만들어 분리해두는 것을 아키텍처 차원에서 계층형 아키텍처(layered architecture)라 부른다. 또는 계층이라는 의미를 가진 티어(tier)를 사용하여 멀티 336 | 티어 아키텍처라고도 한다. 337 | 338 | 보통 웹 기반의 엔터프라이즈 애플리케이션은 일반적으로 세 개의 계층을 갖는다고 해서 3계층(3-tier 또는 3-layer) 애플리케이션이라고 한다. 339 | 340 | 344 | 345 | 물론 모든 엔터프라이즈 애플리케이션을 3계층으로 만들어야만 하는 것은 아니다. 경우에 따라서 전통적인 3계층 방식이 아닌 다른 구분 방법을 선택하기도 한다. 346 | 347 | 경우에 따라서 전통적인 3계층 방식이 아닌 다른 구분 방법을 선택하기도 한다. 또 각 계층을 좀 더 세분화해서 더 작은 단위의 계층으로 나눌 수 있다. 348 | 349 | > 3계층 아키텍처와 수직 계층 350 | 351 | 3계층 아키텍처는 백엔드의 DB나 레거시 시스템과 연동하는 인터페이스 역할을 하는데 `데이터 액세스계층`, 비즈니스 로직을 담고 있는 `서비스 계층`, 주로 웹 기반의 UI를 만들어내고 그 흐름을 352 | 관리하는 `프레젠테이션 계층`으로 구분한다. 353 | 354 | - 프레젠테이션 계층: 웹 계층 , UI 계층, MVC 계층 355 | - 서비스 계층: 매니저 계층, 비즈니스 로직 계층 356 | - 데이터 액세스 계층: DAO 계층, EIS 계층 357 | 358 | ![](images/002.jpeg) 359 | 360 | - 데이터 액세스 계층 361 | 362 | 데이터 액세스 계층은 DB 외에도 ERP, 레거시 시스템, 메인프레임 등에 접근하는 역할을 하기 때문에 EIS(Enterprise Information System) 계층이라고도 한다. 하지만 대개는 장기적인 데이터 363 | 저장을 목적으로 하는 DB 이용이 주된 책임이다. 또 외부 시스템을 호출해서 서비스를 이용하는 것은 기반(infrastructure) 계층으로 따로 분류하기도 한다. 364 | 365 | 데이터 액세스 계층은 사용 기술에 따라서 다시 세분화된 계층으로 구분될 수 있다. 애플리케이션에서 담당하는 역할에 따라 분류한 3계층 구조와 달리, 데이터 액세스 계층 안에서 다시 세분화하는 경우에는 추상화 수준에 366 | 따른 구분이기 때문에 `수직적인 계층`이라고 부르기도 한다. 367 | 368 | 기본 3계층은 `기술 계층`보다는 역할에 따라 구분한 것으로 보통 그림으로 나타낼 때도 가로로 배열한다. 반면에 같은 책임을 가졌지만 `추상화 레벨`에 따라 구분하는 경우 세로로 배열해서 표현한다. 369 | 370 | 만약 추상 계층을 새로 추가하는 것은 부담스럽고 경우에 따라 유연하게 하위 계층의 API를 활용할 필요가 있다면, 공통적인 기능을 분리해서 유틸리티나 헬퍼 메서드 또는 오브젝트로 제공해주는 것도 좋은 방법이다. 371 | 372 | - 서비스 계층 373 | 374 | 잘 만들어진 스프링 애플리케이션의 서비스 계층 클래는 이상적인 POJO로 작성된다. POJO로 잘 만든다면 객체지향적인 설계 기법이 적용된 코드를 통해서 비즈니스 로직의 핵심을 잘 담아내고, 이를 쉽게 테스트하고 375 | 유연하게 확장할 수 있다. 376 | 377 | 서비스 계층은 DAO 계층을 호출하고 이를 활용해서 만들어지는데 때로는 서버나 시스템 레벨에서 제공하는 기반 서비스를 활용할 필요도 있다. 예를 들면 웹 서비스와 같은 원격 호출을 통해 정보를 가져오거나 메일 또는 378 | 메시징 서비스를 이용하는 것이 대표적인 예이다 . 379 | 380 | 기반 서비스는 3계층 어디서나 접근이 가능하도록 만들 수도 있고, 아키텍처를 설계하기에 따라서 반드시 서비스 계층을 통해 사용되도록 제한할 수도 있다. 381 | 382 | 서비스 계층은 특별한 경우가 아니라면 추상화 수직 계층구조를 가질 필요가 없다. 단순하게 POJO 레벨에서 비즈니스 로직을 모델링하다가 상속 구조를 만들 수 있을 진 몰라도 기술 API를 직접 다루는 코드가 아니기 383 | 때문에 기술에 일관된 방식으로 접근하게 하거나 편하게 사용하게 해주는 추상화는 필요 없기 때문이다. 384 | 385 | 일반적으로 서비스 계층이 필요에 따라 기반 서비스 계층의 API를 호출해서 이용한다. 하지만 반대로 서비스 계층의 코드를 기반 서비스 계층에서 실행시키는 경우가 있다. 스케줄링이 대표적인 경우이다. 미래 정해진 386 | 시간에 특정 서비스 계층의 로직이 동작하게 만드는 백그라운드 서비스가 필요하다면 그때는 기반 서비스 계층에서 서비스 계층의 오브젝트를 이용하게 할 수도 있다. 387 | 388 | 원칙적으로는 서비스 계층 코드가 기반 서비스 계층의 구현에 종속되면 안된다. 서비스 계층의 코드는 추상화된 기반 서비스 인터페이스를 통해서만 접근하도록 만들어 특정 구현과 기술에 대한 종속성을 제거해야 한다. 또는 389 | AOP를 통해서 서비스 계층의 코드를 침범하지 않고 부가기능을 추가하는 방법을 활용해야 한다. 390 | 391 | 이상적인 서비스 계층은 백엔드 시스템과 연결되는 데이터 액세스 계층이 바뀌고, 클라이언트와 연결되는 프레젠테이션 계층이 모두 바뀌어도 그대로 유지될 수 있어야 한다. 392 | 393 | 엔터프라이즈 애플리케이션에서 가장 중요한 자산은 도메인의 핵심 비즈니스 로직이 들어있는 서비스 계층이어야 한다. 394 | 395 | - 프레젠테이션 계층 396 | 397 | 프레젠테이션 계층은 매우 다양한 기술과 프레임워크의 조합을 가질 수 있다. 엔터프라이즈 애플리케이션의 프레젠테이션 계층은 클라이언트의 종류와 상관없이 HTTP 프로토콜을 사용하는 서블릿이 바탕이 된다. 물론 HTTP 398 | 외의 프로토콜을 사용하는 엔터프라이즈 서비스가 전혀 없는 것은 아니다. 399 | 400 | 프레젠테이션 계층은 다른 계층과 달리 클라이언트까지 그 범위를 확장될 수도 있다. 화면의 흐름을 결정하는 것이나 사용자 입력 값에 대한 검증, 서비스 계층의 호출과 전달되는 값의 포맷의 변화, 뷰라고 불리는 화면을 401 | 어떻게 그릴지에 대한 로직 등이 모두 서버에서 처리된다. 402 | 403 | > 계층형 아키텍처 설계의 원칙 404 | 405 | 오브젝트와 그 관계에 적용했던 대부분의 객체지향 설계의 원칙은 아키텍처 레벨의 계층과 그 관계에도 동일하게 적용할 수 있다. 각 계층은 응집도가 높으면서 다른 계층과 낮은 결합도를 유지할 수 있어야 한다. 406 | 407 | 각 계층은 자신의 계층의 책임에만 충실해야 한다. 데이터 액세스 계층은 비즈니스 로직을 담거나 웹 파라미터를 파싱하는 코드나 결과를 화면에 어떻게 뿌릴지 결정하는 코드가 들어간다면 응집도가 낮아진다. 408 | 409 | 결과적으로 변화에 대한 유연성이 떨어지고 이해하기 힘든 코드를 가진 계층이 되고 말 것이다. 각 계층은 자신의 역할에만 충실해야 하고 자신과 관련된 기술이 아닌 다른 기술 API의 사용을 삼가해야 한다. 410 | 411 | 각 계층이 자신의 책임에 충실하게 작성되어 있다면 필요한 그 밖의 작업은 다른 계층에 요청하게 될 것이다. 이때 계층 레벨에 정의한 인터페이스를 통해서 요청하게 되고, 계층 간에 사용되는 인터페이스 메서드에는 특정 412 | 계층의 기술이 최대한 드러나지 않게 만들어야 한다. 그렇지 않으면 계층 사이에 결합도가 높아질 뿐만 아니라, 계층 간에 기술이나 역할이 서로 침범하는 일이 일어난다. 413 | 414 | 계층 간에 강한 결합이 생기면 유연성이 떨어지기 때문에 각 계층의 내부 구현이 변화되면 다른 계층의 코드도 함께 수정해줘야 한다. 또한 코드의 중복이 일어날 가능성이 높고 전체 코드를 이해하기는 힘들어진다. 오브젝트 415 | 간의 강한 결합이 있을 때 발생하는 문제와 성격이 유사하고 그 파장은 훨신 심각하다. 416 | 417 | 서비스 계층의 메서드에 JDBC 기술 종속적인 체크 예외를 던지는 경우, 프레젠테이션 계층의 오브젝트(HttpServletRequest, HttpServletResponse)를 그대로 서비스 계층으로 전달하면 418 | 안된다. 419 | 420 | 계층의 결계를 넘어갈 때는 반드시 특정 계층에 종속되지 않는 오브젝트 형태로 변환해줘야 한다. 특정 기술에 종속적인 내용이 계층을 넘어서 전달되는 순간 코드의 재사용이 불가능해지고, POJO기반 단위 테스트 또한 421 | 작성하기 힘들어진다. 422 | 423 | 어떠한 경우에라도 계층 사이의 낮은 결합도를 깨뜨리지 않도록 설계 해야 한다. 당연히 계층 사이의 호출은 인터페이스를 통해 이루어져야 한다. 인터페이스를 사용하게 한다는 것은 각 계층의 경계를 넘어서 들어오는 요청을 424 | 명확하게 정의하겠다는 의미이다. 425 | 426 | 인터페이스는 다른 계층에서 꼭 필요한 메서드만 정의하여야 한다. 427 | 428 | 스프링의 DI는 기본적으로 오브젝트 사이의 관계를 다루는데 계층 사이의 경계나 그 관계에 직접적으로 관여하지 않는다. 하지만 모든 경계에는 오브젝트가 존재하고 그 사이의 관계도 오브젝트 대 오브젝트로 정의되기 429 | 마련이다. 430 | 431 | 그런 면에서 `스프링의 DI가 계층 사이의 관계에도 적용`된다고 볼 수 있다. 하지만 DI는 계층을 구분해주지는 않기 때문에 `빈 사이의 의존관계를 만들 때 주의`해야 한다. 한 계층의 내부에서만 432 | 사용되도록 만든 빈 오브젝트가 있는데, 이를 DI를 통해 함부로 가져다 쓰는 일은 피해야 한다는 말이다. 또 중간 계층을 건너뛰어 관계를 갖지 않는 계층의 빈을 직접 DI 하지 않도록 주의해야 한다. 433 | 434 | ### 9.3.2 애플리케이션 정보 아키텍처 435 | 436 | 엔터프라이즈 시스템은 본질적으로 동시에 많은 작업이 빠르게 수행되어야 하는 시스템이다. 일반적으로 사용자의 요청을 처리하는 동안만 간단한 상태를 유지라기 때문에 애플리케이션의 주요 상태정보는 클라이언트나 백엔드 437 | 시스템에 분산되어 보관된다. 438 | 439 | 하나의 업무 작업이 여러 번의 요청과 페이지에 걸쳐 일어나는 경우에 유지되어야 하는 임시 상태정보는 클라이언트에 일시적으로 보관되기도 하고, 서버의 사용자별 세션 메모리에 저장되기도 한다. 440 | 441 | 애플리케이션을 사이에 두고 흘러다니는 정보를 어떤 식으로 다룰 지를 결정하는 일도 아키텍처를 결정할 때 매우 중요한 기준이 된다. 엔터프라이즈 애플리케이션에 존재하는 정보를 단순히 데이터로 다루는 경우와 오브젝트로 442 | 다루는 경우, 두 가지 기준으로 구분해볼 수 있다. 443 | 444 | 데이터 중심 아키텍처는 애플리케이션에 흘러다니는 정보를 단순히 값이나 값을 담기 위한 목적의 오브젝트 형태로 취급하는 구조다. 데이터 중심 설계의 특징은 비즈니스 로직이 DB 내부의 저장 프로시저나 SQL에 담겨 445 | 있는 경우가 많다는 점이다. 보통 DB에서 돌려주는 내용을 그대로 Map이나 단순 결과 저장용 오브젝트에 넣어서 전달한다. 446 | 447 | 데이터 중심 아키텍처는 핵심 비즈니스 로직을 어디에 많이 두는지에 따라서 `DB에 무게를 두는 구조`와 `서비스 계층의 코드에 무게를 두는 구조`로 구분할 수 있다. 448 | 449 | > DB/SQL 중심의 로직 구현 방식 450 | 451 | 데이터 중심 구조의 특징은 하나의 업무 트랜잭션에 모든 계층의 코드가 종속되는 경향이 있다는 점이다. 요구사항에 따라 만들어진 SQL의 결과는 컬럼 이름을 키로 갖는 Map에 저장되거나 조회 페이지에 필요한 정보를 452 | 담을 수 있는 단순한 오브젝트를 저장되어 전달된다. 453 | 454 | SQL의 내용이 바뀌면 모든 계층의 코드가 함께 변경되어 종속적일 뿐 아니라 배타적이어서 다른 단위 업무에 재사용되기 힘들다. 455 | 456 | 대용량 데이터를 다루면서 빠른 처리가 필요한 경우 일부 로직을 DB 내에 존재하는 PL/SQL 과 같은 저장 프로시저 형태로 만들기도 한다. 457 | 458 | ![](images/003.jpeg) 459 | 460 | 이런 식의 아키텍처는 자바 기술이 발전하기 전의 엔터프라이즈 시스템에서 흔하게 발견할 수 있다. 굳이 자바라는 객체지향 언어와 프로그래밍 기술을 사용하지 않더라도 3계층 구조로 만드는 전통적인 개발 방법에서, 또는 461 | 서비스 계층이 별 의미가 없으므로 주요 로직을 클라이언트의 독립 프로그램에 담아두고 DB 처리 로직만 DB에 분리해둔 2계층 구조에서도 비슷하게 발견할 수 있다. 462 | 463 | 개발은 쉬우나 자바 코드를 단지 DB와 화면을 연결해주는 단순한 인터페이스 도구로 전락시키는 것이며, 자바를 쓰지 않아도 별 차이가 없다. 464 | 465 | 이러한 개발 방식은 변화에 매우 취약하다. 객체지향의 장점이 활용되지 못한데다 각 계층의 코드가 긴밀하게 연결되어 있기 때문이다. 중복을 제거하기도 쉽지 않다. 466 | 467 | 로직을 DB와 SQL에 많이 담으면 담을수록 점점 확장성이 떨어진다. DB에는 확장에 한계가 있을 뿐 아니라 확장한다 하더라도 매우 큰 비용이 든다. 468 | 469 | 상대적으로 애플리케이션 서버와 그 안에 담긴 오브젝트는 비용이 적게 든다. 서버를 늘려 쉽게 확장할 수도 있고, 손쉽게 여러 대의 서버를 클러스터로 묶어서 하나의 서버처럼 동작하게 만들 수 있다. 오브젝트를 만들고 470 | 코드를 동작시키는 비용은 DB에서 비슷한 작업을 할 때에 비해 저렴하다. 따라서 로직을 DB보다는 애플리케이션으로 가져오는 편이 유리하다. 비용도 저렴해질 뿐 아니라 안정성도 높아지고 코드를 검증하기도 매우 편하다. 471 | 472 | SQL이나 저장 프로시저에 담긴 로직은 테스트 하기 힘드나 오브젝트에 담긴 로직은 간단히 검증이 가능하다. DB에는 부하를 가능한 주지 않는 간단한 작업만 하고 복잡한 로직은 오브젝트에 담아서 애플리케이션 내에서 473 | 처리하도록 만드는 편이 낫다. 474 | 475 | 결국 정책적인 부부이나 현실적인 부분으로 인하여 아키텍처와 접근 방법을 어쩔 수 없이 선택해야 할 수도 있지만, 단순히 익숙하고 편하다는 이유로 스프링 애플리케이션 개발에도 여전히 DB 중심의 아키텍처를 선택한다면 476 | 스프링의 장점을 제대로 누릴 수 있는 기회를 얻지 못할 것이다. 477 | 478 | > 거대한 서비스 계층 방식 479 | 480 | DB에서 가져온 데이터가 애플리케이션에 흘러다니는 정보의 중심이 되는 아키텍처이긴 하지만 DB에 많은 로직을 두는 개발 방법의 단점을 피하면서 애플리케이션 코드의 비중을 높이는 방법이 있다. 481 | 482 | DB에는 부하가 걸리지 않도록 저장 프로시저의 사용을 자제하고 복잡한 SQL을 피하면서, 주요 로직은 서비스 계층의 코드에서 처리하도록 만드는 것이다. 483 | 484 | 많은 비즈니스 로직을 DB의 저장 프로시저나 SQL에서 서비스 계층의 오브젝트로 옮겨왔기 때문에 어플리케이션 코드의 비중이 커진다. 그만큼 구조는 단순해지고 객체지향 개발의 장점을 살릴 기회가 많아진다. 485 | 486 | DAO와 SQL은 상대적으로 단순해지고, 그 중 일부는 여러 서비스 계층 코드에서 재사용이 가능해진다. 487 | 488 | 상대적으로 단순한 DAO 로직을 사용하고, 비즈니스 로직의 대부분을 서비스 계층에 집중하는 접근 방법은 결국 거대한 서비스 계층(fat service layer)을 만들게 된다. 데이터 처리 및 분석 그리고 비즈니스 489 | 로직의 대부분이 서비스 계층 코드에 집중되기 때문이다. 490 | 491 | 서비스 계층의 코드는 여전히 업무 트랜잭션 단위로 집중되어 만들어지기 때문에 DAO를 공유할 수 있는 것을 제외하면 코드의 중복도 적지 않게 발생한다. 492 | 493 | ![](images/004.jpeg) 494 | 495 | 거대 서비스 계층 방식의 아키텍처는 데이터 액세스 계층이나 DB는 비즈니스 로직을 직접 담고 있지 않기 때문에 이전보다 훨씬 가벼워진다. 대신 DB가 돌려주는 데이터를 가지고 비즈니스 로직을 구현하는 서비스 계층이 496 | 매우 두터워진다. 497 | 498 | 거대 서비스 계층 방식의 장점은 애플리케이션의 코드에 비즈니스 로직이 담겨 있기 때문에 자바 언어의 장점을 활용해 로직을 구현할 수 있고, 테스트 하기도 수월하다. DAO가 다루는 SQL이 복잡하지 않고, 499 | 프레젠테이션 계층과 뷰가 1:1로 매핑되지 않아도 되기 때문에 일부 DAO 코드는 여러 비즈니스 로직에서 공유해서 사용할 수 있다 . 500 | 501 | 데이터 액세스 계층의 SQL은 서비스 계층의 비즈니스 로직의 필요에 따라 만들어지기 쉬우며 그에 따른 계층 간의 결합도도 여전히 크다. 서비스 계층의 메서드는 크기가 큰 업무 트랜잭션 단위로 만들어지기 때문에 비슷한 502 | 기능의 코드가 여러 메서드에서 중복되어 나타나기 쉽다. 503 | 504 | 자주 사용되는 세부 로직을 추출해서 공통기능으로 뽑아내는 일도 불가능하진 않지만 일반화하기는 쉽지 않다. DAO가 제공해주는 값의 포맷에 따라 이를 취급하는 방법이 달라지기 때문이다. 505 | 506 | DAO에 복잡한 조건의 로직을 넣지는 않더라도 서비스 계층에서 필요한 정보를 `한 번에 SQL을 통해 가져오고`, 그 값을 저장하는 방식이 일관성이 없이 매번 달라질 수 있기 때문에 서비스 계층의 코드나 507 | DAO에 적지 않은 중복이 발생하는 것이다. 508 | 509 | 반면 장점은 각 단위 업무별로 독립적인 개발이 가능하므로 초기 개발속도가 빠르고, 개발자 사이에 간섭 없이 독립적인 개발이 가능하다는 장점이 있다. 또한 핵심 로직이 자바 코드 안에 있으므로 테스트 하기가 상대적으로 510 | 수월하다. 하지만 이는 본격적으로 객체지향적 설계를 적용하기도 힘들고, 개발자 개개인의 코딩 습관이나 실력에 따라 비슷한 로직이더라도 전혀 다른 스타일의 코드가 나오기 쉽다. 511 | 512 | 또한 계층별로 독립된 설계와 개발이 어렵고, 개발이 진행되면서 구현할 비즈니스 로직이나 설계에 변경이 생기는 경우, 유지보수 중에 수정할 필요가 있다면 코드에 손대는게 것이 쉽지 않다. 철저한 테스트를 만들어 둔 513 | 경우라면 다행이지만 테스트가 불충분하다거나 아예 없다면 오히려 SQL이나 저장 프로시저보다 더 다루기 힘든 코드로 전락할 위험이 있다. 514 | 515 | 데이터 중심 아키텍처의 특징은 계층 사이의 결합도가 높은 편이고 응집도는 떨어진다는 점이다. 화면 중심으로 하는 업무 트랜잭션 다위로 코드가 모이기 때문에 처음에는 개발하기 편하지만 중복이 많아지기 쉽고 장기적으로 516 | 코드를 관리하고 발전시키기 힘들다는 단점이 있다. 517 | 518 | ### 9.3.3 오브젝트 중심 아키텍처 519 | 520 | 오브젝트 중심 아키텍처가 데이터 중심 아키텍처와 다른 가장 큰 특징은 도메인 모델을 반영하는 오브젝트 구조를 만들어두고 그것을 각 계층 사이에서 정보를 전송하는데 사용한다는 것이다. 521 | 522 | 그래서 오브젝트 중심 아키텍처는 객체지향 분석과 모델링의 결과로 나오는 도메인 모델을 오브젝트 모델로 활용한다. 도메인 모델은 DB의 엔티티 설계에도 반영되기 때문에 관계형 DB의 엔티티 구조와도 유사한 형태일 523 | 가능성이 높다. 524 | 525 | 물론 DB에는 없지만 비즈니스 로직에만 존재하는 모델도 있기 때문에 항상 일치하는 것은 아니다. 526 | 527 | > 데이터와 오브젝트 528 | 529 | 어떠한 업무를 분석하여 엔티티를 도출해보니 카테고리와 상품이라는 두 가지 엔티티가 나온다고 했을 때, 카테고리 하나에는 여러 개의 상품이 포함된다. 각 상품은 하나의 카테고리에 소속되어 전형적인 1:N의 관계를 530 | 갖는다. 531 | 532 | 이를 DB 테이블로 만들면 두 테이블 사이의 관계를 위해서 Product 쪽에 Category와 관계를 만들어주는 외래키(FK)를 두어야 한다. 533 | 534 | 그리고나서 데이터 중심 아키텍처의 관점에서는 SQL과 DB를 중심으로 생각하여 두 개의 정보를 조합해서 가져오는 방법(JOIN)을 통해 2차원 구조의 정보를 만들게 된다. 535 | 536 | DAO에서는 JDBC로 SQL을 실행하고 받은 결과를 리스트에 담아 서비스 계층으로 넘겨줄 것이다. 리스트를 사용하게 되면 서비스 계층이나 프레젠테이션 계층은 이 리스트 안에 어떤 내용이 담겨 있는지 어떤 목적으로 537 | 넘어온 데이터인지 정확하게 알 수가 없다. 538 | 539 | 데이터 중심의 아키텍처에서는 DAO가 만드는 SQL의 결과에 모든 계층의 코드가 의존하게 된다. 도메인 분석을 통해 작성된 모델정보는 DB에 대한 SQL을 작성할 때 외에는 코드에 반영되는 일이 없다. 540 | 541 | 반면 오브젝트 방식에서는 애플리케이션에서 사용되는 정보가 도메인 모델의 구조를 반영해서 만들어진 오브젝트 안에 담긴다. 도메인 모델은 애플리케이션 전 계층에서 동일한 의미를 갖는다. 따라서 도메인 모델이 반영된 542 | 도메인 오브젝트도 전 계층에서 일관된 구조를 유지한 채로 사용할 수 있다. 543 | 544 | SQL이나 웹 페이지의 출력 포맷, 입력 폼 등에 종속되지 않는 일관된 형식의 애플리케이션의 정보를 다룰 수 있게 된다. 545 | 546 | 데이터를 다룰 때 데이터 중심 방식에서는 Category와 그에 대응되는 Product를 찾아 SQL을 이용해 조인한 다음 하나의 맵에 담아서 사용해왔지만, 오브젝트 중심 방식에서는 테이블의 정보와 그 관계를 유지한 547 | 채로 정확한 개수의 Category 오브젝트와 그에 대응되는 Product 오브젝트로 만들어 사용한다. 548 | 549 | 하지만 이러한 도메인 모델을 따르는 오브젝트 구조를 만들기 위해서는 DB에서 가져온 데이터를 도메인 오브젝트 구조에 맞게 변환해줄 필요가 있다. DAO는 자신이 DB에서 가져와서 도메인 모델 오브젝트에 담아주는 550 | 정보가 어떤 업무 트랜잭션에서 어떻게 사용될지는 신경 쓰지 않아도 된다. 서비스 계층 또한 DAO에서 어떤 SQL을 사용했는지는 몰라도 된다. 551 | 552 | > 도메인 오브젝트를 사용하는 코드 553 | 554 | - 일관성있는 데이터 조회 방법 555 | - 높은 코드의 재사용성 → 중복 코드 제거 556 | 557 | 오브젝트 중심 방식에서 비즈니스 로직의 구현이 얼마나 간단하고 명확한지 살펴보도록 한다. 558 | 559 | 어떤 카테고리에 포함된 상품의 모든 가격을 계산해야 하는 로직이 필요한 경우 서비스 계층의 오브젝트 안에 관련 메서드를 작성하여 사용하면 된다. 어떤 DAO를 이용해서 Category를 가져왔는지는 중요하지 않고, 560 | Category 오브젝트를 갖고 있다면 해당 카테고리에 담긴 모든 상품 가격의 합을 구하는 메서드에서 해당 작업만 처리하면 된다. 561 | 562 | 테스트를 만들어 검증하기도 간단하고, 로직이 변경될 때 코드를 수정하기도 수월하다. Category 자체가 독립된 오브젝트이므로 서비스 계층 어디에서든지 Category의 상품 가격을 계산해야 할때는 해당 메서드를 563 | 사용하면 된다. 564 | 565 | 반면에 데이터 중심 방식에서는 재사용 가능한 메서드를 만들어 사용하기가 쉽지 않다. 아마도 처리 방식을 SQL의 집계 함수를 사용하여 데이터를 가져오거나, 카테고리에 포함된 상품의 가격 값의 목록을 배열에 담아서 566 | 돌려주는 DAO를 만들고 배열을 이용해 합계를 계산하는 코드를 작성할 수도 있다. 567 | 568 | 문제는 하나의 Category에 대해 상품 가격의 합을 계산하는 로직이라면 이런 방법이 가능하지만, 이미 DAO에서 여러 개의 Category와 Product를 조인해서 가져온 경우라면 별도의 합계 계산 코드를 569 | 만들어야 한다는 점이다. 570 | 571 | 목표는 같지만 SQL을 통해 데이터를 어떻게 가져왔는지에 따라 이를 처리하는 다른 코드가 만들어지므로 코드에 중복이 생길 가능성이 높다. 572 | 573 | 오브젝트 구조로 정보를 갖고 있으면 어떤 식으로든 활용하기가 편리하다. 자바에서는 dot(.)을 이용해 레퍼런스 변수를 따라가면 관련된 정보를 쉽게 이용할 수 있다. 574 | 575 | SQL에 담긴 로직을 테스트 하는 것은 복잡하고 불편하다. 하지마 도메인 오브젝트를 사용하는 코드는 간단히 테스트 값을 담은 도메인 오브젝트를 생성해서 쉽게 검증할 수 있다. 576 | 577 | > 도메인 오브젝트 사용의 문제점 578 | 579 | 일관된 의미를 갖고 있고 유연하며 애플리케이션 전반에 공유 가능한 도메인 모델을 따르는 오브젝트로 정보를 다루게 되면 많은 장점이 존재한다. 코드는 이해하기 쉽고 로직을 작성하기도 수월하며 프레젠테이션 영역에서도 580 | 이미 정의된 도메인 오브젝트 구조만 알고 있다면 아직 DAO가 작성되지 않았더라도 뷰를 미리 만들 수도 있다. 581 | 582 | 코드의 재사용성은 높아지고 DAO는 더 작고 효율적으로 만들 수 있다. 583 | 584 | 하지만 단점도 존재하는데, 최적화된 SQL을 매번 만들어 사용하는 경우에 비 성능 면에서 조금은 손해를 감수해야 할 수도 있다. DAO는 비즈니스 로직의 사용방식을 알지 못하므로 도메인 오브젝트의 모든 필드 값을 다 585 | 채워서 전달하는 경우가 대부분이다. 그런데 하나의 오브젝트에 담긴 필드의 개수가 많아지다 보면 그중에는 드물게 사용되는 필드도 있을 것이다. 비즈니스 로직에 따라서 필요한 정보가 달라질 수 있기 때문에 이러한 문제가 586 | 발생할 수 있다. 587 | 588 | 오브젝트 관계에도 문제가 발생할 수 있는데, 단순히 Product 정보만 필요한 비즈니스 로직이 있다고 가정했을 때 DAO가 돌려준 Product 오브젝트에는 관계를 갖고 있는 Category 오브젝트도 함꼐 담겨 589 | 있을 것이다. 무조건 조회 했을 때 연관된 오브젝트의 정보까지 들고 있는 것은 비효율적이다. 590 | 591 | 물론 Product 정보를 가져올 때 Category가 필요한 경우와 그렇지 않은 경우를 구분해서 DAO를 만들어 줄 수 있다. 하지만 문제는 DAO에서 Product만 가져오게 하면 Product의 categoty 592 | 필드에는 null이 들어간다는 점이다. 593 | 594 | 불필요한 오브젝트를 생성하는 일을 피할 수 있으서 좋기는 한데, 자칫 비즈니스 로직 코드를 작성하다가 그런 사실을 깜박하고 product의 category필드를 사용할 경우 예상하지 못했던 595 | NullPointerException을 만날 수 있다. 596 | 597 | 결국 최적화를 고려해서 DAO를 작성하려면 DAO는 비즈니스 로직에서 각 오브젝트를 어디까지 사용해야 하는지 어느정도 알고 있어야 한다. 그래서 데이터 중심 접근 방법의 단점이라고 했던 DAO와 비즈니스 로직의 598 | 코드의 결합도가 높아지는 문제가 발생할 수 있다. 599 | 600 | 위에서 말하는 문제점을 정리해보면 도메인 오브젝트 중심의 방법은 데이터를 DB에서 가져올 때 모든 데이터를 다 들고 오기 때문에 비효율적일 수 있다. 그 수준이 필드 레벨에서도 그렇지만 오브젝트 레벨에서도 연관된 601 | 오브젝트를 전부 들고오기 때문에 코드의 재사용성을 위해서 사용했다가 더 많은 비용을 소모할 수도 있다. 602 | 603 | 그래서 도메인 오브젝트의 문제점을 해결하기 위해서 지연된 로딩(Lazy Loading)이라는 기법을 이용한다. 604 | 605 | 일단 최소한의 오브젝트 정보만 읽어두고 관계된 오브젝트가 필요한 경우에만 다이나믹하게 DB에서 다시 조회 할 수 있다. 물론 도메인 오브젝트를 사용하는 코드는 이러한 사실을 전혀 의식하지 않고 처음부터 모든 606 | 오브젝트의 정보가 다 제공된다고 생각하고 작성하기만 하면된다. 607 | 608 | 필드가 너무 많은 테이블이 있다면 그 중에 자주 사용되는 것을 골라 별도의 오브젝트로 정의해두고, 필요에 따라 구분해서 사용할 수 있다. 물론 그에 따라 DAO 메서드가 추가되어야 하고, 어떤 DAO를 사용할지를 609 | 서비스 계층에서 알고 있어야 하기 때문에, 약하긴 하지만 계층 사이의 결합이 발생한다. 610 | 611 | 이런 문제에 대한 가장 이상적인 방법은 JPA나 JDO, 하이버네이트, TopLink와 같은 오브젝트/RDB 매핑(ORM) 기술을 사용하는 것이다. 이러한 데이터 액세스 기술은 기본적으로 지연된 로딩 기법을 제공하기 612 | 때문에 번거로운 코드를 만들지 않고 도메인 오브젝트의 생성을 최적화 할 수 있다. 613 | 614 | 또한 SQL 결과를 가지고 도메인 오브젝트를 만들고 값을 채우는 등의 복잡한 DAO 코드를 만들지 않아도 된다. 내부적으로 `최적화된 SQL을 사용하도록 세밀히 튜닝`할 수도 있다. 또, 자주 변경되지 않으면서 많은 615 | 로직에서 참조하는 레퍼런스 테이블이 있다면 이를 ORM이 제공하는 `오브젝트 캐시`에 담아두고 사용할 수 있다. 616 | 617 | 그래서 도메인 오브젝트를 사용하는 오브젝트 중심 아키텍처에서는 가능하다면 ORM과 같은 오브젝트 중심 데이터 액세스 기술을 사용하는 것을 권장한다. ORM을 사용하지 않고 JDBC를 이용하는 경우라면 지연된 로딩 618 | 기법을 제공하는 코드를 추가해주거나, 사용되는 필드의 종류와 사용되는 관련 오브젝트 범위에 따라서 여러 개의 DAO 메서드를 만들어 사용해야 할 수도 있다. 이런 경우 DAO 코드나 도메인 오브젝트 코드의 중복이 619 | 일부 발생하고 계층 사이의 결합도도 증가될 수는 있지만, 데이터 중심 아키텍처에 비하면 미미한 정도이다. 620 | 621 | 오브젝트 중심의 아키텍처는 도메인 모델을 따르는 오브젝트를 사용해 각 계층 사이에 정보를 전달하고, 이를 이용해 비즈니스 로직이나 프레젠테이션 로직을 작성한다. 계층 간의 결합도는 낮아지고, 일관된 정보 모델을 622 | 사용하기 때문에 개발 생산성과 코드의 품질, 테스트 편의성도 향상시킬 수 있다. 623 | 624 | 추가적으로 도메인 오브젝트는 자바 오브젝트로 데이터를 저장하기 위한 것만이 아니라 내부의 정보를 이용하는 기능도 함께 존재한다. 클래스는 속성(attribute)와 행위(operation)의 조합으로 필드와 그에 625 | 대한 접근자, 수정자만 갖고 있는 오브젝트는 반쪽짜리라 볼 수 있다. 물론 도메인 모델을 반영하는 오브젝트 구조에 정보를 담는 것만으로도 많은 장점이 있지만, 가능하다면 더 적극적으로 활용하게 만들어야 한다. 626 | 627 | > 빈약한 도메인 오브젝트 방식 628 | 629 | 도메인은 오브젝트에 정보만 담겨 있고, 정보를 활용하는 아무런 기능도 갖고 있지 않다면 이는 온전한 오브젝트로 보기 힘들다. 이런 오브젝트를 `빈약한(anemic) 오브젝트`라 부른다. 각 계층 사이로 부터의 630 | 독립성을 확보하기 위해서는 특정 계층에 종속되지 않으면서 애플리케이션 전반에서 사용될 수 있는 정보를 담은 오브젝트가 필요하기 마련이고, 이런 빈약한 도메인 오브젝트 방식도 많이 사용된다. 631 | 632 | 도메인 모델을 반영해서 만들어진 도메인 오브젝트이니 그 기능이라고 하면 도메인의 비즈니스 로직이라 볼 수 있다. 빈약한 도메인 오브젝트 방식에서 비즈니스 로직은 서비스 계층에 존재하게 된다. 633 | 634 | 다루는 정보의 구조가 다를 뿐이지 빈약한 도메인 오브젝트 방식은 데이터 중심 아키텍처의 거대 서비스 계층 구조와 비슷하다. 빈약한 도메인 오브젝트 방식도 거대 서비스 계층 방식의 하나로 보면 된다. 635 | 636 | ![](images/005.jpeg) 637 | 638 | 빈약한 도메인 오브젝트 방식의 한계는 거대 서비스 계층 방식과 유사하다. 비록 도메인 오브젝트라는 일관된 오브젝트를 활용하기 때문에 SQL에 의존적인 데이터 방식보다는 훨씬 유연하고 간결하지만, 여전히 서비스 계층의 639 | 메서드에 대부분의 비즈니스 로직이 들어있기 때문에 로직의 재활용성이 떨어지고 중복의 문제가 발생하기 쉽다. 640 | 641 | 비즈니스 로직이 복잡하지 않다면 가장 만들기 쉽고 3계층 구조의 특징을 잘 살려서 개발할 수 있는 유용한 아키텍처이다. 642 | 643 | > 풍성한 도메인 오브젝트 방식 644 | 645 | 풍성한 도메인 오브젝트 또는 영리한 도메인 오브젝트 방식은 빈약한 도메인 오브젝트의 단점을 극복하고, 도메인 오브젝트의 객체지향적인 특징을 잘 사용할 수 있도록 개선한 것이다. 646 | 647 | 어떤 비즈니스 로직은 특정 도메인 오브젝트나 그 관련 오브젝트가 가진 정보와 관련이 있다. 이런 로직을 서비스 계층의 코드가 아니라 도메인 오브젝트에 넣어주고, 서비스 계층의 비즈니스 로직에서 재사용하게 만드는 648 | 것이다. 649 | 650 | 자신의 정보를 활용하는 로직을 담은 도메인 오브젝트는 서비스 계층의 메서드에 따로 만드는 경우보다 응집도가 높다. 데이터와 그것을 사용하는 기능이 한곳에 모여 있기 때문이다. 651 | 652 | 풍성한 도메인 오브젝트 방식은 도메인 오브젝트를 사용한다는 면에서 빈약한 도메인 오브젝트 방식과 비슷하지만, 실제 작성된 코드를 살펴보면 훨씬 간결하고 객체지향적이라는 사실을 알 수 있다. 객체지향 분석과 설계를 653 | 통해 만들어진 도메인 모델의 정보를 정적인 구조뿐 아니라 동적인 동작 방식에도 적극 활용할 수 있다. 654 | 655 | 도메인 오브젝트 안에 메서드로 들어가는 로직들은 대부분 해당 오브젝트나, 긴밀한 연관관계를 맺고 있는 관련 오브젝트의 정보와 기능만을 활용한다. 여러 종류의 도메인 오브젝트의 기능을 조합해서 복잡한 비즈니스 로직을 656 | 만들었다면 특정 도메인 오브젝트에 넣기는 힘들다. 이런 비즈니스 로직은 서비스 계층의 오브젝트에 두는 것이 적당하다. 657 | 658 | 도메인 오브젝트는 직접 데이터액세스 계층이나 기반 계층 또는 다른 서비스 계층의 오브젝트에 접근할 수 없기 때문에 서비스 계층이 필요하기도 하다. 대개는 비즈니스 로직을 처리하는 중에 DB에 담긴 정보를 가져와 659 | 활용하거나 결과를 다시 DB나 외부 시스템에 전송하는 등의 작업이 필요하다. 그러기 위해서는 서비스 계층의 오브젝트와 같이 DAO 오브젝트를 DI 받아서 사용할 수 있어야 하는데, 도메인 오브젝트는 그럴 수 없다. 660 | 661 | 도메인 오브젝트는 스프링 컨테이너가 관리하는 오브젝트, 즉 빈이 아니다. 서비스 계층의 오브젝트나 데이터 액세스 계층의 오브젝트는 모두 스프링의 빈으로 등록되어있기 때문에 필요에 따라 서로 DI 할 수 있다. DI를 662 | 받으려면 자신도 역시 스프링 컨테이너에서 관리되는 빈이어야 한다. 663 | 664 | `도메인 오브젝트`는 애플리케이션의 코드 또는 기타 프레임워크나 라이브러리, JDBC 템플릿 등에 의해 필요할 때마다 새롭게 만들어진다. 따라서 스프링이 생성하거나 관리하는 오브젝트가 아니므로 DI를 받을 수 없다. 665 | 결국 이런 도메인 오브젝트는 DAO나 서비스 오브젝트 같은 스프링의 빈의 기능을 사용할 수 없다. 666 | 667 | 도메인 오브젝트는 자기가 가진 정보에 대해 활용하는 로직에 대한 것은 도메인 오브젝트 안에 구현할 수 있지만, 그 결과를 DB에 저장하거나 메일로 발송하거나 DB를 검색해서 원하는 정보를 가져와 활용하는 작업은 668 | 도메인 오브젝트에서 불가능하다. 그래서 DAO와 기반계층 오브젝트를 DI받아 사용할 수 있는 서비스 계층의 코드가 필요하다. 669 | 670 | 서비스 계층은 도메인 오브젝트를 DB나 외부 리소스에서 가져오고 변경된 정보나 새로 등록된 정보를 DB에 반영하는 등의 작업과 함께 도메인 오브젝트가 갖고 있는 기능이 있다면 이를 활용해서 비즈니스 로직을 처리해야 671 | 한다. 672 | 673 | ![](images/006.jpeg) 674 | 675 | 풍성한 도메인 오브젝트 방식은 빈약한 도메인 오브젝트 방식보다 `서비스 계층의 코드가 간결`하다. 비즈니스 로직 코드를 이해하기도 쉬워, 빈약한 도메인 오브젝트 방식을 피하고 도메인 오브젝트가 스스로 처리 676 | 가능한 비즈니스 로직을 갖도록 만드는 것이 바람직하다. 677 | 678 | 절대적으로 빈약한 도메인 오브젝트 방식이 나쁘다는 것은 아니다. 도메인 모델링과 기능 분석을 통해 이상적인 도메인 오브젝트 설계를 하고, 사전에 내부 기능을 충분히 구현해두지 않았다면 도메인 오브젝트를 활용해서 679 | 비즈니스 로직을 구현해야 하는 개발자는 혼란을 느낄 수 있다. 어떤 경우는 도메인 오브젝트에 이미 구현된 코드가 있는데도 이를 활용하지 못하는 상황이 발생할 수 있다. 680 | 681 | 그래서 충실한 도메인 모델링과 도메인 오브젝트 개발이 선행되고 그 내용이 개발자에게 사전에 충분히 공유되지 않았다면 차라리 데이터 구조만 오브젝트 방식으로 정의해놓은 빈약한 도메인 오브젝트 방식이 혼란을 피할 수 682 | 있고, 쉽게 접근할 수 있는 대안이 될 수 있다. 물론 처음은 쉽지만 시간이 지나고 시스템이 복잡해지면 빈약한 도메인 오브젝트 방식의 단점이 드러날 것을 각오해야 한다. 683 | 684 | > 도메인 계층 방식 685 | 686 | 도메인 모델을 다루는 방식들의 한계를 보면 도메인 오브젝트에 담을 수 있는 비즈니스 로직은 데이터 액세스 계층에서 가져온 내부 데이터를 분석하거나, 조건에 따라 오브젝트 정보를 변경, 생성하는 정도만 활용할 수 687 | 있다. 688 | 689 | 이렇게 변경된 정보가 다시 DB 등에 반영되기 위해서는 서비스 계층 오브젝트의 부가적인 작업이 필요하다. 690 | 691 | 이런 상황에 드는 의문은 “도메인 오브젝트가 스스로 필요한 정보는 DAO를 통해 가져올 수 있고, 생성이나 변경이 일어났을 때 직접 DAO에게 변경사항을 반영해달라고 요청할 수 없을까?” ”DAO외에도 다양한 692 | 기반계층의 서비스를 이용하도록 할 방법은 없을까?” ”만약에 도메인 오브젝트가 기존 3계층의 오브젝트를 DI 받아서 직접 이용할 수 있게 된다면 어떤 일이 일어날까?” 693 | 694 | 도메인 계층의 역할과 비중을 극대화하려다 보면 기존의 풍성한 도메인 오브젝트 방식으로는 만족하기 힘들다. 그래서 도메인 오브젝트가 기존 3계층과 같은 레벨로 격상되어 하나의 계층을 이루게 하는 도메인 계층 방식이다. 695 | 696 | 개념은 도메인 오브젝트들이 하나의 독립된 계층을 이뤄서 서비스 계층과 데이터 액세스 계층 사이에 존재하는 것이다. 도메인 오브젝트가 독립된 계층을 이루게 되면 기존 방식과는 다른 두 가지 특징을 갖게 된다. 697 | 698 | 1. 도메인에 종속적인 비즈니스 로직의 처리는 서비스 계층이 아니라 도메인 계층의 오브젝트 안에서 이루어지게 된다는 점이다. 699 | 700 | 서비스 계층에서 사용자가 입력한 정보를 바탕으로 새로운 도메인 오브젝트를 만들던지, 데이터 액세스 계층을 통해 도메인 오브젝트를 가져왔던지 상관없이 도메인 오브젝트에게 비즈니스 로직의 처리를 요청할 수 있다. 701 | 702 | 해당 도메인 오브젝트를 중심으로 만들어진 로직이라면 그 이후의 작업은 도메인 오브젝트와 그 관련 오브젝트 사이에서 진행된다. 일단 도메인 계층으로 들어가면 서비스 계층의 도움 없이 비즈니스 로직의 대부분의 703 | 작업을 수행할 수 있다는 뜻이다. 704 | 705 | 2. 도메인 오브젝트가 기존 데이터 액세스 계층이나 기반 계층의 기능을 직접 활용할 수 있다는 것이다. 706 | 707 | 도메인 오브젝트는 스프링에 등록되어 싱글턴으로 관리되는 빈이 아니기 때문에 다른 빈을 DI받을 수 없다. 그렇다면 “도메인 계층의 도메인 오브젝트들은 어떻게 다른 빈을 이용할 수 있을까?” 708 | 709 | 다른 빈을 사용하는 방법은 DI 밖에 없다. 도메인 오브젝트는 스프링이 직접 만들고 관리하는 오브젝트, 즉 빈은 아니지만 스프링이 관리하지 않는 오브젝트에도 DI를 적용할 수 있다. 710 | 711 | 스프링이 관리하지 않는 도메인 오브젝트에 DI를 적용하기 위해서는 AOP가 필요하다. 스프링 AOP는 부가기능을 추가할 수 있는 위치가 메서드 호출 과정으로 한정되고 AOP의 적용 대상도 스프링의 빈 712 | 오브젝트뿐이다. 713 | 714 | 하지만 스프링 AOP 대신 AspectJ AOP를 사용하면 클래스의 생성자가 호출되면서 오브젝트가 만들어지는 시점을 조인 포인트로 사용할 수 있고, 스프링 빈이 아닌 일반 오브젝트에도 AOP 부가기능을 적용할 715 | 수 있다. 716 | 717 | 이를 이용해 `도메인 오브젝트가 생성되는 시점`에 특별한 부가기능을 추가하게 만들어 줄 수 있다. 이 부가기능은 오브젝트의 수정자 메서드나 DI용 어노테이션을 참고해서 DI 가능한 대상을 스프링 718 | 컨테이너에서 찾아 DI 해주는 기능이다. 719 | 720 | 스프링이 직접 관리하지 않는 오브젝트에 대한 DI 서비스가 일종의 AOP 부가기능으로 도메인 오브젝트에 적용될 수 있다. 이러한 방법을 이용하면 도메인 오브젝트가 만들어질 때 스프링의 빈 오브젝트를 DI 받게 721 | 할 수 있다. 722 | 723 | 결국 도메인 오브젝트가 데이터 액세스 계층이나 기반 계층의 오브젝트를 이용하도록 만들 수 있다. 이 덕분에 도메인 오브젝트 긴능의 제약이 사라진다. 물론 도메인 오브젝트에 담긴 기능을 자신과 관련 오브젝트에 724 | 대한 작업으로 한정되어야 한다. 725 | 726 | 도메인 계층 방식은 이전의 어떤 방식보다 도메인 오브젝트에 많은 비즈니스 로직을 담아낼 수 있다. 그럼에도 서비스 계층의 역할이 완전히 사라지는 것은 아니다. 때로는 여러 도메인 오브젝트의 기능을 조합해서 727 | 복잡한 작업을 진행해야 하는 경우가 있다. 특정 도메인 오브젝트에 담길 수 없는 이런 작업은 서비스 계층에서 도메인 계층과 협력을 통해 진행하는 것이 바람직하다. 또는 도메인 계층을 거치지 않고 바로 데이터 728 | 액세스 계층으로부터 정보를 가져와 클라이언트에 제공해야 하는 경우도 있다. 이럴 때는 서비스 계층이 인터페이스 역할을 담당한다. 729 | 730 | `트랜잭션 경계를 설정`하거나 특정 도메인 로직에 포함되지는 않지만 애플리케이션에서 필요로 하는 `기반 서비스를 이용해야 하는 작업`을 위해서 서비스 계층은 필요하다. 731 | 732 | 서비스 계층의 비중과 규모는 단순히 도메인 오브젝트를 사용하는 방식에 비해 훨씬 작다. 복잡하지 않은 애플리케이션에서는 아예 서비스 계층을 제거하고 모든 비즈니스 로직을 도메인 오브젝트에 담을 수도 있다. 733 | 이때는 트랜잭션 경계가 프레젠테이션 계층에서 최초로 호출되는 도메인 오브젝트의 메서드에 설정되어야 한다. 734 | 735 | 도메인 오브젝트를 독립적인 계층으로 만들려고 할 때 고려해야 할 중요한 사항이 있다. 도메인 오브젝트가 도메인 계층을 벗어나서도 사용되게 할 지 말지 결정해야 한다. 도메인 오브젝트가 계층을 이루기 전에는 모든 736 | 계층에 걸쳐 사용되는 일종의 정보 전달 도구 같은 역할을 했다. 하지만 독자적인 계층을 이뤘을 때 상황이 달라질 수 있다. 이때 선택할 수 있는 방법은 두 가지가 있다. 737 | 738 | 1. 모든 계층에서 도메인 오브젝트를 사용하게 하는 것 739 | 740 | 도메인 계층은 물론이고, 서비스 계층이나 그 앞의 프레젠테이션 계층, 화면 출력을 위한 뷰에서도 직접 도메인 오브젝트를 전달받아 사용할 수 있게 하는 것으로 가장 편한 방법이다. 도메인 모델을 따르는 741 | 오브젝트 구조를 활용하는 면에서 오브젝트 중심 아키텍처의 장점을 그대로 누릴 수 있다. 742 | 743 | 하지만 이 방식은 주의하지 않으면 심각한 문제를 초래할 수 있다. 도메인 오브젝트의 메서드는 단순한 값의 조작이나 분석, 변환 정도가 아니라 중요한 도메인 / 비즈니스 로직을 담당하고 있고 심지어 DB나 744 | 백엔드 시스템에 작업 결과를 반영할 수 있다. 745 | 746 | 이러한 책임이 있는 기능을 가진 도메인 오브젝트를 프레젠테이션 계층이나 뷰 등에서 사용하게 하면 위험이 따를 수 있다. 이러한 문제를 방지하기 위해서는 철저한 가이드 또는 개발 정책이 필요하게 되고, 747 | 코딩 정책의 적용을 분석할 수 있는 툴을 이용해 검증하거나 AspectJ의 정책/표준 강제화 기능을 사용하는 등의 추가적인 비용이 더 들게 된다. 748 | 749 | 2. 도메인 오브젝트는 도메인 계층을 벗어나지 못하게 하는 것 750 | 751 | 도메인 계층 박으로 전달될 때는 별도로 준비된 정보 전달용 오브젝트에 도메인 오브젝트의 내용을 복사해서 넘겨줘야 한다. 이러한 오브젝트는 데이터 전달을 위해 사용된다고 해서 DTO라 불린다. DTO는 752 | 상태의 변화를 허용하지 않고 읽기전용으로 만들어지기도 한다. 753 | 754 | 반대로 사용자가 등록한 값이나 외부 시스템으로부터 전달받은 정보를 도메인 계층으로 전달하는 경우에도 DTO를 이용할 수 있다. 755 | 756 | DTO는 기능을 갖지 않으므로 사용하기 안전하다. 또 도메인 오브젝트를 외부 계층의 코드로부터 보호해준다. 반면에 도메인 오브젝트와 비슷한 구조를 가진 오브젝트를 따로 만들어야 하고, 이를 매번 757 | 변환해줘야 하는 번거로움이 있다. 따라서 AOP와 같은 방법을 이용해 변환을 자동으로 해주도록 만들 필요가 있다. 758 | 759 | 두 가지 방법 중에 어느 것이 더 나은 방법이라고 말하기는 힘들다. 기존에 DTO를 이용한 개발에 익숙해져있다면, 후자의 방법을 사용하는 것도 좋다. 번거로운 작업은 최소화하고 대신 표준 개발 정책을 잘 760 | 따라 개발하도록 관리한다면 전자의 방법이 편리하다. 761 | 762 | ![](images/007.jpeg) 763 | 764 | 도메인 계층은 기존 3계층과 비슷한 수준에서 독립적인 역할을 하고 있지만 그 특성은 확실히 다르다. 서비스를 제공하는 싱글턴으로 계속 존재하는 다른 계층의 오브젝트와 달리, 도메인 계층의 오브젝트는 매우 짧은 765 | 시간동안만 존재하다가 사라지는 것을 반복한다. 766 | 767 | 각 사용자의 요청 별로 독립적으로 도메인 계층을 이루는 오브젝트들이 생성됐다가 해당 요청을 처리하고 나면 버려진다. 때로는 하나의 복잡한 작업 흐름을 따라서 오래 존재하는 경우도 있지만 여전히 그 생명주기는 짧다. 768 | 도메인 오브젝트는 사용자별 요청에 대해 독립적인 상태를 유지하고 있어야 하기 때문이다. 769 | 770 | 상태정보를 담고 있기 때문에 여러 스레드가 공유하는 싱글턴이 될 수가 없다. 또 DAO나 컨트롤러, 또는 스프링 외의 라이브러리를 통해 오브젝트가 만들어지는 경우가 많기 때문에 스프링이 관리하는 빈으로 등록조차 771 | 불가능하다. 그렇기 때문에 특별한 방법으로 DI를 해줘야지만 다른 3계층의 빈들과 협력해서 일을 처리할 수 있다. 772 | 773 | 만약 여러 가지 제약과 불편을 감수하면서라도 이 방식을 택해야 하는 경우는 매우 복잡하고 변경이 잦은 도메인을 가졌을 때이다. 복잡한 도메인의 구조와 로직을 최대한 도메인 계층의 오브젝트에 반영하고, 도메인 모델과 774 | 설계에 변경이 발생했을 때 도메인 계층의 오브젝트도 빠르게 대응해서 변경해주기 위해서이다. 775 | 776 | 도메인 계층은 응집도가 매우 높기 때문에 단위 테스트를 작성하기가 매우 편리하다. DAO나 외부 서비스 오브젝트와 연동할 때도 도메인 오브젝트 타입을 유지할 수 있기 때문에 목 오브젝트 등을 이용해 단위 테스트로 777 | 만들기도 쉽다. 도메인이 가진 복잡함을 객체지향적인 설계의 모든 장점을 동원하여 가장 유연한 방법으로 대응할 수 있다. 778 | 779 | 반면에 그만큼 복잡하지 않은 애플리케이션이라면 이런 방법을 선택하는 것 자체가 오히려 과도한 부담을 줄 수 있다. 따라서 도메인 계층을 이용하는 방식을 선택할 때는, 오브젝트 중심 아키텍처의 기본 두 가지 방법을 780 | 충분히 경험해보고 오브젝트 중심의 개발 방식에 익숙해진 뒤에 조심스럽게 접근해야 한다. 781 | 782 | > DTO와 리포트 쿼리 783 | 784 | 도메인 계층 방식의 경우 도메인 계층을 벗어난 정보를 DTO라 불리는 특정 계층에 종속되지 않은 정보 전달의 목적을 가진 단순 오브젝트에 담아 사용하기도 한다. 그 외의 방법에서도 DTO의 사용이 꼭 필요할 때가 785 | 있다. 786 | 787 | 대표적으로 리포트 쿼리(Report Query)라 불리는 DB 쿼리의 실행 결과를 담는 경우이다. 이는 종합 분석 리포트처럼 여러 테이블에 걸쳐 존재하는 자료를 분석하고 그에 따른 분석 / 통계 결과를 생성하는 788 | 쿼리이다. 이런 쿼리의 결과는 DB 테이블에 담긴 필드의 내용보다는 그 합계, 평균과 같은 계산 값이거나 아니면 여러 테이블의 필드를 다양한 방식으로 조합하여 만들어진다. 따라서 DB 쿼리의 실행 결과를 담을 만한 789 | 적절한 도메인 오브젝트를 찾을 수 없다. 그래서 이런 리포트 쿼리의 결괄는 DTO라 불리우는 단순한 자바 빈이나 키와 값 쌍을 갖는 맵에 담아 전달해야 한다. 790 | 791 | 때로는 DB의 쿼리 하나로 최종 결과를 만들어내기 힘들기 때문에 코드를 통해 데이터를 분석하고 가공하는 작업이 필요하다. 이런 경우에도 최종 결과는 DTO나 맵, 컬렉션에 담겨 전달되어야 한다. 792 | 793 | 또는 웹 서비스 등의 시스템과 자료를 주고 받을 때 전송 규약에 맞춰서 도메인 오브젝트에 담긴 정보를 가공해야 할 때가 있다. 이런 경우에도 DTO나 맵을 이용해 해당 형식에 맞도록 변경하는 작업이 필요하다. 794 | 795 | ### 9.3.4 스프링 애플리케이션을 위한 아키텍처 설계 796 | 797 | 위에서 3단계로 역할을 분리하는 계층형 아키텍처와 정보를 다루는 방법에 따른 아키텍처의 종류를 알아봤다. 798 | 799 | 그 외에도 다양한 기술 조합과 업무조건, 시스템 환경에 따른 많은 결정요소와 변수가 있다. 그 중에서도 계층구조를 어떻게 나눌 것인가와 애플리케이션 정보를 어떻게 다룰지를 결정하는 것이 기본이 된다. 그리고 그 위에 800 | 각 계층에 사용될 구체적인 기술의 종류와 수직 추상화 계층의 도입, 세세한 기술적인 조건을 결정하는 일이 남는다. 801 | 802 | > 계층형 아키텍처 803 | 804 | 3계층 구조는 스프링을 사용하는 엔터프라이즈 애플리케이션에서 가장 많이 사용되는 구조이다. 주요 모듈과 기술을 살펴보면 3계층 구조에 적합하도록 설계되어 있기 때문에 그렇다. 단, 3계층이라는 것은 논리적이고 805 | 개념적인 구분이지 꼭 오브젝트 단위로 딱 끊어서 만들어지는 게 아니다. 806 | 807 | 때로는 하나의 계층이 다시 수평으로 세분화될 수도 있다. 반대로 3계층에서 두 개의 계층이 통합되어 하나의 오브젝트에 담기는 일도 가능하다. 보통 이 경우는 서비스 계층과 데이터엑세스 계층이 합쳐지며 트랜잭션 808 | 경계설정 위치는 DAO 메서드가 된다. 반대로 프레젠테이션 계층에 서비스 계층이 통합되는 경우도 있다. DAO는 순수한 DB 인터페이스 역할을 하고 조건에 따른 간단한 로직의 적용을 프레젠테이션 계층의 컨트롤러에서 809 | 담당하는 것이다. 이러한 방법이 불가능한 것은 아니지만 권장하진 않는다. 이유는 스프링 AOP를 이용해 트랜잭션 경계나 트랜잭션 전파의 조합이 애매해진다. 810 | 811 | 스프링을 처음 학습하고 도입하는 입장이라면 전통적인 서버 기반의 3계층 구조에 먼저 익숙해지는 것이 좋다. 812 | 813 | 프레젠테이션 계층은 SpringMVC를 이용하고 서비스 계층은 POJO로 구현하면서 트랜잭션 AOP를 적용하고, 데이터 액세스 계층은 JDBC를 비록해 스프링의 데이터 액세스 전략이 적용된 JPA, 하이버네이트, 814 | JDO 등을 활용하는 것이다. 815 | 816 | > 정보 전송 아키텍처 817 | 818 | 스프링의 기본 기술에 가장 잘 들어맞고 쉽게 적요해볼 수 있는 것이 오브젝트 중심 아키텍처의 도메인 오브젝트 방식이다. 819 | 820 | 일단은 빈약한 도메인 오브젝트 방식으로 시작하고 도메인 오브젝트를 계층 간의 정보 전송을 위해 사용하고, 이를 각 계층의 코드에서 활용하는 방식으로 발전해 나간다. DAO는 그 기술이 어떤 것이든지 상관없이 서비스 821 | 계층에서 요청을 받거나 결과를 돌려줄 때 도메인 오브젝트 형태를 유지하게 만든다. 822 | 823 | 서비스 계층의 비즈니스 로직 또한 도메인 오브젝트를 이용해 작성한다. 가능하다면 도메인 오브젝트에 단순한 기능이라도 추가하도록 노력해보는 것이 좋다. 824 | 825 | 도메인 오브젝트를 사용해 애플리케이션의 정보를 일관된 형태로 유지하는게 스프링에 가장 잘 들어맞는 방식이다. 스프링에 포함된 예제나, 스프링 개발자들이 저술한 책에 나오는 샘플 애플리케이션, 그리고 스프링의 각 826 | 기술의 API 사용 방법을 살펴보면 거의 대부분 이러한 방식을 사용하고 있다. 827 | 828 | DB와 SQL에 많은 비즈니스 로직을 담고 있는 레거시 시스템을 스프링으로 전환하는 경우라면, 데이터 중심의 아키텍처를 사용해도 무방하다. 3계층의 기본 구조로 잘 분리할 수만 있다면 DB 중심의 접근 방법을 829 | 사용해서 기존에 만들어 사용했던 SQL을 재사용한다. 그리고 전환 작업을 마치고 검증이 끝난 후에 단계적으로 로직을 DB에서 애플리케이션으로 가져오고, 오브젝트 중심으로 전환하는 시도를 하면된다. 830 | 831 | 레거시 시스템의 기능은 가져오되 구현은 새롭게 처음부터 시작할 수 있다면 스프링의 철학과 개념에 잘 들어맞는 도메인 오브젝트 중심의 아키텍처에 도전해보는 편이 나을 것이다. 832 | 833 | 객체지향적인 도메인 분석과 모델링에 자신이 있고 도메인 오브젝트 설계와 구현, 독립적인 테스트를 자유롭게 적용할 수 있다면 과감하게 도메인 계층 방식을 도입할 수도 있다. 다만 도메인 계층에 DI를 적용하기 위해 834 | 스프링의 고급 기술을 활용해야 하고 여러 가지 고려할 점이 많으므로 충분한 사전 학습과 검증이 먼저 진행되어야 한다. 835 | 836 | > 상태 관리와 빈 스코프 837 | 838 | 아키텍처 설계에서 가장 신경써야 하는 부분은 상태 관리이다. 크게는 사용자 로그인 세션 관리부터 작게는 하나의 작업 단위지만 여러 페이지에 걸쳐 진행되는 위저드 기능까지 애플리케이션은 하나의 HTTP 요청의 범윌르 839 | 넘어서 유지해야 하는 상태정보가 있다. 840 | 841 | 엔터프라이즈 애플리케이션은 특정 사용자가 독점해서 배타적으로 사용되지 않는다. 하나의 애플리케이션이 동시에 수많은 사용자의 요청을 처리하게 하기 위해 매번 간단한 요청을 받아 그 결과를 돌려주는 방식으로 동작한다. 842 | 따라서 서버의 자원이 특정 사용자에게 일정하게 할당되지 않는다. 그래서 서버 기반의 애플리케이션은 원래 지속적으로 유지되는 상태를 갖지 않는다.(stateless)는 특징이 있다. 843 | 844 | 클라이언트로부터의 요청을 처리하는 매우 짧은 시간 동안만 도메인 오브젝트와 같은 정보저장소에 현재 상태정보가 보관되지만, 이는 요청 결과를 클라이언트에 돌려주고 나면 바로 폐기된다. 그 덕에 수많은 동시 사용자의 845 | 요청을 제한된 서버 리소스를 가지고 처리할 수 있다. 846 | 847 | 어떤 식으로든 애플리케이션의 상태와 장시간 진행되는 작업 정보는 유지되어야 한다. 이를 위해 웹 클라이언트에 URL, 파라미터 폼 히든 필드, 쿠키 등을 이용해야 상태정보 또는 서버에 저장된 상태 정보에 키 값 등을 848 | 전달해야 한다. 849 | 850 | 클라이언트와 서버 사이에서 많은 양의 정보를 계속해서 주고받을 수는 없으므로 중요한 상태정보는 파일 시스템, 데이터그리드, DB 등에 저장되기도 한다. 또는 제약이 있긴 하지만 HTTP 세션과 같은 서블릿 컨테이너가 851 | 제공하는 저장공간을 활용하기도한다. 852 | 853 | 이렇게 상태를 저장, 유지하는 데 어떤 방식을 사용할지 결정하는 일은 매우 중요하다. 스프링은 기본적으로 상태가 유지되지 않는 빈과 오브젝트를 사용하는 것을 권장한다. 웹의 생리에 가장 잘 들어맞고 개발하기 쉽기 854 | 때문이다. 또, 서버를 여러대로 확장 하기가 매우 쉽다. 반면에 웹 클라이언트에 폼 정보를 출력하고 이를 수정하는 등의 작업을 위해서는 HTTP 세션을 적극 활용하기도 한다. 대부분의 폼 등록, 수정 작업은 한 855 | 페이지짜리 폼이라도 여러 번의 HTTP 요청에 걸쳐 일어나기 때문에 작업 중인 폼의 내 용을 짧은 동안에라도 서버에 저장해둘 필요가 있다. 856 | 857 | 상태는 클라이언트, 백엔드에 저장해두거나 서블릿의 HTTP 세션 정도에 일시적으로 저장해두는 것이 대부분이지만 경우에 따라서는 장기간 유지되며 중첩될 수 있는 상태를 다루는 고급 상태 관리 기법을 이용할 수도 있다. 858 | 어플리케이션의 특징에 따라서 스프링을 이용해 상태유지(stateful) 스타일의 어플리케이션을 얼마든지 만들 수 있다. 859 | 860 | 스프링에서는 싱글톤 외에도 다른 스코프를 갖는 빈을 간단히 만들 수 있다. 빈의 스코프를 잘 861 | 활용하면 `스프링이 관리하는 빈이면서 사용자별로 또는 단위 작업 별로 독립적으로 생성되고 유지되는 오브젝트를 만들어 상태를 저장하고 이를 DI를 통해 서비스 빈에서 사용하게 만들 수 있다.` 862 | 863 | > 서드파티 프레임워크, 라이브러리 적용 864 | 865 | 스프링은 거의 대부분의 자바 표준 기술과 함께 사용될 수 있다. 스프링 애플라케이션은 기본적으로 서블릿을 기반으로 하는 독립 웹 모듈로 만들어진다. 표준 기술 외에도 많이 사용되는 오픈소스 프레임워크, 라이브러리나 866 | 상용 제품도 스프링과 함께 사용할 수 있다. 867 | 868 | 스프링이 지원하는 기술이란 무슨 의미일까? 869 | 870 | - 첫째, 해당 기술을 스프링의 DI 패턴을 따라 사용할 수 있다. 871 | 872 | 프레임워크나 라이브러리의 핵심 클래스를 빈으로 등록할 수 있게 지원해주는 것이 라고 생각해도 좋다. 프레임워크의 핵심 오브젝트를 빈의 형태로 등록해둘 수 있다면 프로퍼티를 이용해 세부 설정을 조정할 수도 있고, 873 | DI를 통해 다른 오브젝트에서 손쉽게 활용할 수도 있다. 또 스프링이 제공하는 추상화 서비스를 통해 다른 리소스에 투명하게 접근할 수도 있다. 874 | 875 | 스프링 외의 기술을 접목할 때는 가장 먼저 스프링의 빈으로 등록해서 DI 방식을 통해 사용 가능한지 살펴봐야 한다. 만약 빈으로 등록해서 바로 사용할 수 있는 구조로 핵심 API나 클래스가 만들어져 있지 않은 876 | 경우라면, 위의 예처럼 스프링 빈으로 등록돼서 사용하기에 적합하도록 만들어주는 팩토리 빈을 도입해야 한다. 877 | 878 | 스프링이 지원하는 프레임워크나 라이브러리는 대부분 이와 같이 스프링 빈의 설정 만으로 등록하고 사용 가능하도록 팩토리 빈 클래스가 제공된다. 879 | 880 | - 둘째, 스프링의 서비스 추상화가 적용됐다. 881 | 882 | 서비스 추상화를 적용하는 경우는 비슷한 기능을 제공하는 기술에 대한 일관된 접근 방법을 정의해준다. 이를 통해 서드파티 프레임워크를 적용할 수 있을 뿐만 아니라 필요에 따라 호환 가능한 기술로 손쉽게 교체해서 사용할 883 | 수 있다. 884 | 885 | 스프링이 제공하는 서비스 추상화는 표준 기술 스펙과는 다르다. 스프링이 제공하는 추상화는 이미 존재하는 다양한 기술의 공통점을 분석하여 추상화한 것이다. 따라서 추상 서비스 인터페이스를 구현해서 각 기술과 연동하게 886 | 해주는 어댑터 클래스가 필요하다. 서비스 추상화 인터페이스를 구현한 클래스들은 모두 스프링의 빈으로 등록되도록 만들어졌고, 세부 기술의 특성에 맞는 설정이 손쉽게 기능하도록 다양한 프로퍼티를 제공하고 있다. 887 | 888 | - 셋째, 스프링이 지지하는 프로그래밍 모델을 적용했다. 889 | 890 | 스프링이 지지하는 프로그래밍 모델이 적용된 대표적인 예는 스프링의 데이터 액세스 기술에 대한 일관된 예외 적용이다. 891 | 892 | 스프링의 데이터 액세스 지원 기능을 사용하면 데이터 액세스 기술의 종류에 상관없이 일관된 예외 계층구조를 따라서 예외가 던져진다. 기술에 독립적인 DAO를 만들 수 있도록 데이터 액세스 예외를 추상화하고, 불필요하게 893 | 예외를 처리하는 코드를 피하도록 런타임 위주의 예외를 사용한다는 스프링의 개발철학이 적용된 것이다 894 | 895 | 서비스 계층의 비즈니스 로직을 담은 코드가 데이터 액세스 계층의 기술에 종속되지 않도록 만들어준다. 896 | 897 | - 넷째, 템플릿 / 콜백이 지원된다. 898 | 899 | 스프링은 20여 가지 기술을 지원히는 템플릿/콜백을 제공한다. 이런 기술은 그대로 사용하면 반복적으로 등장하는 판에 박힌 코드 때문에 전체 코드가 지저분해지고 이해하기 힘들고 추상화하기도 어려운 구조가 돼버린다. 900 | 스프링은 이런 기술을 간편하게 사용할 수 있도록 템플릿/콜백 기능을 제공한다. 템플릿 클래스는 빈으로 등록해서 필요한 빈에서 DI 받아 사용할 수 있다. 901 | 902 | 스프링이 직접 지원하는 기술이 아니라도 앞에서 소개한 네 가지 방법을 따라서 사용하도록 만들 수 있다. 903 | 904 | 스프링 개발팀이나 스프링 커뮤니티에서 지원 기능을 제공하지 않더라도 서드파티 기술이나 프레임워크가 자체적으로 스프링에서 손쉽게 시용될 수 있도록 관련 기능을 제 공하는 경우도 적지 않다. 905 | 906 | 개발팀이나 기업에서 자체적으로 만든 사내 기술이나 스프링 지원 기능이 제공되지 않는 새로운 서드파티 기술을 사용해야 한다면 어떻게 해야 할까? 빈으로 등록되도록 만들지도 않고 필요할 때마다 여기저기서 오브젝트를 직접 907 | 만들고 초기화 하는 코드를 넣어서 사용한다거나, 다른 기술로 대체될 가능성이 충분히 있음에도 추상화를 적용하지 않고 특정 기술에 종속되는 코드에서 직접 사용한다거나. try/catch/ finally블록으로 범벅된 908 | 코드를 마구 중복해가며 사용하는 것이다. 당장 기능은 돌아 가겠지만 기껏 스프링을 사용하면서 깔끔하고 유연하게 만들었던 코드가 금새 지저분 해지고 코드의 일관성도 없어지며 유연성도 떨어질 것이 분명하다. 909 | 910 | 스프링에 새로운 기술을 연동하려면 이 책에서 설명했던 스프링의 프로그래밍 모델과 지지하는 개발철학을 따르면서 앞에서 설명한 네 가지 방법을 이용하면 된다. 필요에 따라 팩토리 빈을 사용해서 오브젝트 생성과 초기화 911 | 작업, 프로퍼티 노출 등을 해줘야 한다. 애플리케이션 내의 빈들이 새로운 기술에 대해 빈을 DI 하는 방법으로 접근할 수 있게 하는 것이다. 반대로 기존 빈 오브젝트를 새로 추가할 기술에서 사용할 수 있게 해줄 912 | 펼요도 있다. 어떤 경우든 코드에 의한 초기화 작업이 필요하므로 팩토리 빈을 만들어 사용하면 편리하다. 913 | 914 | 어떤 경우는 스프링이 이미 제공하는 추상 인터페이스의 새로운 구현으로 만들어질 수 있고 아예 새로운 추상화 인터페이스 를 정의해서 적용할 수도 있다. 915 | 916 | 네트워크 접근이나 파일 IO처럼 실패할 가능성이 있는 시도를 하는 기술이라면 템플릿/콜백 방식의 적용도 고려해볼만하다. 기술을 사용할 때마다 반복적으로 try/catch/ finally 블록이 필요한 기술이라면 917 | 템플릿/콜백이 제격이다. 918 | 919 | AOP나 예외 전환을 적용할 수도 있다. 보통 외부에서 가져와서 시용히는 기술은 내부 구현 방식에 손을 대기 쉽지 않다. 가능하면 외부 기술의 코드에는 손을 대지 않고 사용 방법을 개선하는 작업이 필요하다. 예외 920 | 변환은 종종 AOP를 통해 많이 이뤄진다. 921 | 922 | 스프링을 시용하려면 스프링의 프로그래밍 모델과 그에 담긴 개발 철학을 따르는 일관된 코드를 만드는 데 많은 관심을 기울여야 한다. 923 | 924 | ## 9.4 정리 925 | 926 | 스프링 애플리케이션 프로젝트를 만들면서 초기에 결정할 내용에 대해서 정리 후에 최적의 개발환경과 도구, 플랫폼을 결정해서 준비해두고, 애플리케이션의 아키텍처와 사용할 기술 등을 신중하게 선택해야 한다. 또한 스프링 927 | 애플리케이션에 적용할 서드파티 기술과 라이브러리에 대한 접근 방법을 개발이 시작되기 전에 충분히 검토하고 준비해둘 필요가 있다 . 928 | 929 | 9장의 내용을 정리해보면 아래와 같다. 930 | 931 | - 스프링은 어떤 플랫폼에서도 사용될 수 있지만, 자바 엔터프라이즈 플랫폼에 최적화되어 있다. HTTP를 통해 접근하는 웹 클라이언트와 백엔드 DB를 사용하는 애플리케이션에 적합하다. 932 | - 스프링 개발의 생선성을 증대시키고 품질을 높이려면 SpringIDE 플러그인이나 STS 같은 적절한 툴의 지원이 필요하다. 933 | - 스프링은 의존 라이브러리가 방대하기 때문에 라이브러리 관리와 의존관계를 관리하는데 많은 노력을 기울여야 한다. 가능하면 스프링이 의존관계 정보를 제공해주는 Maven이나 Ivy 같은 빌드 툴을 사용해 의존 934 | 라이브러리를 관리하는 것이 바람직하다. 935 | - 스프링 애플리케이션은 역할에 따라 3계층으로 구분되고, 다시 기술의 추상도에 따라 세분화되는 계층형 아키텍처를 사용하는 것이 좋다. 936 | - 아키텍처는 애플리케이션이 다루는 정보의 관점에서 데이터 중심과 오브젝트 중심으로 구분할 수 있다. 937 | - 스프링에 가장 잘 어울리는 아키텍처는 오브젝트 중심의 아키텍처이다. 938 | - 스프링이 직접 지원하지 않는 서드파티 기술도 스프링 스타일의 접근 방식을 따라서 사용할 수 있도록 준비해둬야 한다. -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/9장_스프링_프로젝트_시작하기_안송이.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/9장_스프링_프로젝트_시작하기/9장_스프링_프로젝트_시작하기_안송이.pdf -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/images/001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/9장_스프링_프로젝트_시작하기/images/001.jpeg -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/images/002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/9장_스프링_프로젝트_시작하기/images/002.jpeg -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/images/003.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/9장_스프링_프로젝트_시작하기/images/003.jpeg -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/images/004.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/9장_스프링_프로젝트_시작하기/images/004.jpeg -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/images/005.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/9장_스프링_프로젝트_시작하기/images/005.jpeg -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/images/006.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/9장_스프링_프로젝트_시작하기/images/006.jpeg -------------------------------------------------------------------------------- /9장_스프링_프로젝트_시작하기/images/007.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/9장_스프링_프로젝트_시작하기/images/007.jpeg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 운영방안 2 | 1. 스터디 시간: 매주 월요일 오후 2시 3 | 2. 책 정보 링크 [토비의 스프링 3.1 1](http://www.yes24.com/Product/Goods/7516911) 4 | 3. 스터디 기간: 11/1 ~ 2022/1/10(11주 예정) 5 | 4. 감상평 또는 정리 내용을 스터디 시간 (매주 월요일 오후 2시) 이전까지 올려야 한다. 6 | 7 | ### 벌금 8 | 1. 5회 누적시 기프티콘(스타벅스 아메리카노) 2개 9 | -------------------------------------------------------------------------------- /img/list1-48(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/list1-48(1).png -------------------------------------------------------------------------------- /img/list1-48(2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/list1-48(2).png -------------------------------------------------------------------------------- /img/pic3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic3-1.png -------------------------------------------------------------------------------- /img/pic3-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic3-2.png -------------------------------------------------------------------------------- /img/pic3-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic3-3.png -------------------------------------------------------------------------------- /img/pic3-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic3-7.png -------------------------------------------------------------------------------- /img/pic4-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic4-1.png -------------------------------------------------------------------------------- /img/pic6-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic6-13.png -------------------------------------------------------------------------------- /img/pic6-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic6-14.png -------------------------------------------------------------------------------- /img/pic6-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic6-18.png -------------------------------------------------------------------------------- /img/pic6-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/pic6-3.png -------------------------------------------------------------------------------- /img/table1-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorningBirds/TobySpring/7e2fba01e4f5b1530ace2260624fbfdf78d16db3/img/table1-2.png --------------------------------------------------------------------------------