-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[8주차](박준영, 김수환, 오형석) #11
Comments
토비의 스프링 7장 스프링 핵심 기술의 응용1️⃣ DI를 이용해 다양한 구현 방법 적용하기운영 중인 시스템에서 사용하는 정보를 실시간으로 변경하는 작업을 만들 때, 가장 먼저 고려해야 할 사항은 동시성 문제다. 1. ConcurrentHashMap지금까지는 JDK의 HashMap을 사용해왔다. 하지만 HashMap의 경우 멀티 스레드 환경에서 동시에 수정을 시도하거나, 수정과 읽기를 동시에 요청하는 경우 예상치 못한 결과가 발생할 수 있다.
이러한 이유로 어느 정도 안전하면서 성능이 보장되는 동기화된 HashMap으로 이용하기에 적당하다. 하지만 동시성에 대한 부분을 테스트하기엔 간단하지 않다. 2. 내장형 데이터베이스
그렇다고 해서 고작 DAO가 사용할 SQL을 저장하고 관리할 목적으로 별도의 DB를 구성하면 주객전도가 발생하는 상황이다.
내장형 DB는 데이터가 메모리에 저장되기 때문에 IO로 인해 발생하는 부하가 적어서 성능이 뛰어나다. 자바에서는 Derby, HSQL, H2의 내장형 DB가 널리 사용된다. 왜냐하면 모두 JDBC 드라이버를 제공하고 표준 DB와 호환되는 기능을 제공하기 때문에 JDBC 프로그래밍 모델을 그대로 따라서 사용할 수 있다. 특별한 기능으로는 내장형 DB는 애플리케이션 안에서 직접 DB 종료 요청을 할 수 있어야 한다. new EmbeddedDatabaseBuilder()
.setType(내장형 DB 종류)
.addScript(초기화에 사용할 DB 스크립트의 리소스 )
...
.build();
3. 트랜잭션 적용내장형 DB를 통해 조회가 빈번하게 일어나는 중에도 데이터가 깨지는 일 없이 SQL을 수정하도록 보장했다. 하나의 SQL을 수정할 때는 문제가 없지만, 만약 여러 개의 SQL을 한 번에 수정해야 하는 경우에는 심각한 문제가 발생할 수 있다. 한 번에 여러 개의 SQL을 수정하는 이유로는, SQL들이 서로 관련이 있을 가능성이 크다. 비즈니스 로직이 변경되었다면, 그에 영향을 받는 모든 SQl이 변경돼야 하기 때문이다. HashMap과 같은 컬렉션은 트랜잭션 개념을 적용하기 매우 힘들다. 여러 개의 엘리먼트를 트랜잭션과 같은 원자성이 보장된 상태에서 변경하려면 매우 복잡한 과정이 필요하기 때문이다. 기존 updateSql()
public class EmbeddedDbSqlRegistry implements UpdatableSqlRegistry {
SimpleJdbcTemplate jdbc;
public void setDataSource(DataSource dataSource) {
jdbc = new SimpleJdbcTemplate(dataSource); // 인터페이스 분리 원칙을 지키기 위해 DataSource를 주입
}
public void updateSql(Map<String, String> sqlmap) throws SqlUpdateFailureException {
for (Map.Entry<String, String> entry : sqlmap.entrySet()) {
updateSql(entry.getKey(), entry.getValue());
}
}
} 트랜잭션 테스트
public class EmbeddedDbSqlRegistryTest extends AbstractUpdatableSqlRegitstryTest {
// …
@Test
public void transactionlUpdate(){
checkFind(“SQL1”,”SQL2”,”SQL3”);
// 초기상태 확인
Map<String, String> sqlmap = new HashMap<String,String>();
sqlmap.put(“KEY1”,”Modified1”);
sqlmap.put(“KEY9999!@#$”,”Modified9999”);
// 존재하지 않는 키를 수정하도록 하면 예외가 발생할 것이다/
// 트랜잭션 테스트를 위해 일부러 실패하도록 설정했다. 롤백을 체크하기 위한 테스트코드.
try{
sqlRegistry.updateSql(sqlmap);
fail();
}
catch(SqlUpdateFailureException e){}
checkFind(“SQL1”,”SQL2”,”SQL3”); // 초기상태와 동일한지 검증한다.
}
} public class EmbeddedDbSqlRegistry implements UpdateSqlRegistry {
SimpleJdbcTemplate jdbc;
TransactionTemplate transactionTemplate;
// JdbcTemplate과 트랜잭션을 동기화해주는 트랜잭션 템플릿. 멀티 스레드 환경에서 공유 가능.
public void setDataSource(DataSource dataSource) {
jdbc = new SimpleJdbcTemplate(dataSource);
transactionTemplate = new TransactionTemplate(new DataSourceTransactionManager(dataSource));
// dataSource로 TransactionManager를 만들고 이를 이용해 TransactionTemplate을 생성한다.
}
// 익명 내부 클래스로 만들어지는 콜백 오브젝트 안에서 사용되기 때문에 final로 선언
public void updateSql(final Map<String, String> sqlmap) throws SqlUpdateFailureException {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
//트랜잭션 템플릿이 만드는 트랜잭션 경계 안에서 동작할 코드를 콜백 형태로 만들고
// TransactionTemplate의 excute() 메서드에 전달
for (Map.Entry<String, String> entry : sqlmap.entrySet()) {
updateSql(entry.getKey(), entry.getValue());
}
}
});
}
} 2️⃣ 스프링 3.1의 DI스프링이 등장한 이후 많은 변화를 겪었다. 하지만 스프링이 근본적으로 지지하는, 객체지향 언어인 자바의 특징과 장점을 극대화하는 프로그래밍 스타일과 이를 지원하는 도구로서의 정체성은 변하지 않았다. 1. 애노테이션의 메타정보 활용자바는 소스코드가 컴파일된 후 클래스 파일에 저장됐다가, JVM에 의해 메모리로 로딩되어 실행된다. 그런데 때로는 자바 코드가 실행되는 것이 목적이 아니라 다른 자바 코드에 의해 데이터처럼 취급되기도 한다. 리플렉션 API가 본래 목적보다 자바 코드의 메타 정보를 데이터로 활용하는 스타일의 프로그래밍 방식에 더 많이 활용되고 있다.
애노테이션 자체가 클래스의 타입에 영향을 주지도 못하고, 일반 코드에서 활용되지 못한다. 그럼에도 애노테이션을 이용하는 표준 기술과 프레임워크가 빠르게 증가했다. @Special
public class MyClass{
...
}
원한다면 클래스의 필드나 메서드 구성도 확인이 가능하다. 단순한 애노테이션 하나를 추가하는 것만으로, 다양한 정보를 얻어낼 수 있다. 리팩토링을 할 때도 차이점이 있다. 리팩토링 중 MyClass의 패키지를 변경하거나 클래스 이름을 바꿨다면 IDE를 활용하여 간단히 이름이나 위치를 바꾸는 리팩토링을 할 수 있다. 반면에, XML은 어느 환경에서나 손쉽게 편집이 가능하고, 내용을 변경하더라도 다시 빌드를 할 필요가 없지만, 애노테이션은 자바 코드에 존재하므로 변경할 때마다 매번 클래스를 새로 컴파일해야 한다. 2. 정책과 관례를 이용한 프로그래밍애너테이션 같은 메타정보를 활용하는 프로그래밍 방식은 코드를 이용해 명시적으로 동작 내용을 기술하는 대신 코드 없이도 미리 약속한 규칙 또는 관례를 따라서 프로그램이 동작하도록 만드는 프로그래밍 스타일을 적극적으로 포용해왔다.
@ContextConfiguration(locations="/test-applicationContext.xml")
@ContextConfiguration(classes=TestApplicationContext.class)
XML로 정의한 transactionManager 빈
<bean id="transactionManager"
class ="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
자바 코드로 정의한 transactionManager 빈
@Bean
public PlaformTransactionManager transactionManager(){
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(dataSource());
return tm;
}
@Component
public @interface SomeAnnotation{
...
}
@SomeAnnotation
public class SomeClass{
...
}
지금껏 책을 읽으며 작성해온 코드들은 애플리케이션이 바르게 동작하는 데 필요한 DI 정보와 테스트를 수행하기 위해 만든 DI 정보가 하나의 파일 안에 혼재해 있다. @ContextConfiguration(classes={TestAppContext.class, AppContext.class})
public class UserDaoTest{
...
}
@ContextConfiguration(classes=AppContext.class)
public class UserDao{
...
} 그다음으로 SQL 서비스와 관련된 빈들을 분리해보자. @Configuration
public class SqlServiceContext{
...
}
@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages="~~")
@Import(SqlServiceContext)
public class AppContext{
...
} 이러한 방식으로 AppContext가 메인 설정 정보가 되고, SqlServiceContex는 보조 설정 정보로 사용할 수 있다.
테스트 환경과 운영 환경에서 다른 빈 정의가 필요한 경우가 있다. 양쪽에 모두 필요하면서 내용만 다른 것들은 설정 정보를 변경하고 조합하는 것 만으로는 한계가 있다. 실행 환경에 따라 빈 구성이 달라지는 내용을 프로파일로 정의해서 만들어두고, 실행 시점에 어떤 프로파일의 빈 설정을 사용할지 지정하는 것이다. 운영 환경에서만 필요한 빈을 담은 빈 설정 클래스
@Configuration
@Profile("test")
public class TestAppContext{
...
}
@Configuration
@Profile("production")
public class ProductionAppContext {
...
}
@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages="~~~")
@Import({SqlServiceContext.class, TestAppContext.class, ProductionAppContext.class})
public class AppContext {
...
}
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test") // 프로파일 사용
@ContextConfiguration(classes=AppContext.class)
public class UserServiceTest {
...
} test 프로파일이 지정된 TestAppContext의 빈 설정은 포함되고, ProductionAppContext의 빈 설정은 production 프로파일로 선언되어 있어 무시된다. |
The text was updated successfully, but these errors were encountered: