좋은 책이나 너무 오래되어서 따라하기가 쉽지 않을 때가 있다. 그 시대의 IDE, 자바 버전, 컴퓨터를 사용하지 않는 한 조금씩 삐그덕대기는 한다. 그나저나 2회독인데, 스프링을 그 동안 안 써서 그런지 처음 읽는 거 같은 느낌이 들었다 ㅎㅎ;;; 작년에는 스프링 버전을 책과 최대한 맞춰가며 진행했는데, 이번에는 스프링 부트로 진행을 해 보았다.
- Spring Boot Repository (2021.03) : https://github.com/youngminz/toby-spring-boot
- Spring 3.1 Repository (2020.08) : https://github.com/youngminz/toby-spring
-
최근에는 Spring Boot가 대세다
- 의존성 라이브러리의 버전을 자동으로 Gradle이 찾아준다는 것은 엄청난 행운. maven repository를 직접 찾아가며 책에 있는 의존성들을 일일히 검색하고 다운로드해가며 의존성 추가 하는 것은 시간이 너무 오래 걸린다
- Gradle을 사용하는 덕분에 CI도 손쉽게 설정할 수 있었다
- MySQL 5.7 이하는 유니코드를 제대로 지원하지 않기 때문에 매번 설정하기 진짜 짜증난다
- 테스트 커버리지를 알아내는 CodeCov도 설정해 보았다
- Spring 버전이 3.1 에서 5.3.4 으로 올라왔지만, 몇 가지 API의 변화 말고는 큰 변화는 없다
- 1권 전체를 따라해 보았다. 주말 + 3.1절 연휴 내내 1권을 달라붙어서 어떻게든 끝냈다!
- 높은 자바 버전을 사용하면서 책에 있는 낮은 스프링이나 라이브러리 버전을 사용하면 크래시가 발생하기도 한다. 스프링 3.1 + 자바 8에서는 괜찮았음. 자바 11에서는 가끔 크래시.
- 180p :
@Before
어노테이션이@BeforeEach
어노테이션으로 변경되었다. - ???p : assertThat 보다는 assertEquals, assertTrue, assertIsNull 등으로 사용하였다. 개인 취향.
- 185p :
@RunWith(SpringJUnit4ClassRunner.class)
가@ExtendWith(SpringExtension.class)
으로 변경되었다. 그런데@SpringBootTest
어노테이션을 사용하면 더 깔끔하게 적용된다. - ???p :
assertThrows(EmptyResultDataAccessException.class, () -> dao.get("unknown_id"));
으로 사용법이 바뀌었다. - ???p : Auto resource management 기능을 이용해 close 를 자동 호출할 수 있다.
- 231p : 익명 내부 클래스보다 더 간결한 람다를 사용할 수 있다.
- 235p : 테스트에서 ContextConfiguration 써야된다.
- 253p : resources 폴더에 numbers.txt 파일을 만들었으면,
getResource("numbers.txt")
가 아닌getResource("/numbers.txt")
처럼/
를 붙여야 한다. - 264p : queryForInt 가 Deprecated 되었다. queryForObject로 넘기고 두번째 인자를 Integer.class 넘기면 된다.
-
265p : queryForObject(java.lang.String, java.lang.Object[], org.springframework.jdbc.core.RowMapper
)’ is deprecated. https://stackoverflow.com/a/65301152/3096304 위에서 아래처럼 varargs를 이용하면 된다.
public User get(String id) { return this.jdbcTemplate.queryForObject( "select * from users where id = ?", new Object[]{id}, (rs, rowNum) -> { User user = new User(); user.setId(rs.getString("id")); user.setName(rs.getString("name")); user.setPassword(rs.getString("password")); return user; } ); }
public User get(String id) { return this.jdbcTemplate.queryForObject( "select * from users where id = ?", (rs, rowNum) -> { User user = new User(); user.setId(rs.getString("id")); user.setName(rs.getString("name")); user.setPassword(rs.getString("password")); return user; }, id ); }
-
352p - 위의 코드는 아래로 대체할 수 있다.
try { testUserService.upgradeLevels(); fail("TestUserServiceException expected"); } catch (TestUserServiceException e) {}
assertThrows(TestUserServiceException.class, testUserService::upgradeLevels);
-
381p - javax.mail.Session 클래스는 자바11에서는 쓸 수 없다. 따라서 javax.mail 사용하는 코드는 실습해볼 수 없다. 대신 스프링부트가 제공하는 API 사용할것.
implementation 'org.springframework.boot:spring-boot-starter-mail'
- 452p -
@ContextConfiguration
에서 설정파일 이름을 지정하지 않으면-context.xml
을 사용한다고 했지만, 스프링 부트 기본 설정에는 그렇게 되어 있지 않나 보다.Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test
오류가 발생한다. 그냥@ContextConfiguration(locations = "/FactoryBeanTest-context.xml")
으로 경로를 붙여 주었다. - 어드바이저 = 포인트컷(메소드 선정 알고리즘) + 어드바이스(부가기능)
- AOP는 정말 어렵군…
-
492p - 아래 의존성 추가해줘야 한다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
-
492p - 책의 org.aspectj:aspectjweaver:1.6.6 에서 버그 동작이 1.6.7 에서 수정되었다. https://www.eclipse.org/aspectj/doc/released/README-167.html 를 읽어 봐도 이거에 대한 내용은 없는 거 같은데.. 리팩토링 하다가 버그가 잡혔나?
assertFalse(pointcut.getClassFilter().matches(Bean.class));
implementation 'org.aspectj:aspectjweaver:1.6.6' // Error implementation 'org.aspectj:aspectjweaver:1.6.7' // OK
-
535p - (5, 6) → (4) → (2, 3) → (1) 순서로 Transactional 정책이 적용된다고 했는데, 실제로 적용해보니 그렇지 않더라. (5, 6) → (2, 3) → (4) → (1) 순서로 적용되는 것 같다. https://github.com/youngminz/toby-spring-boot/commit/9c9f8a8cbfe9a5ead8fda390ec8e458f7b8d6921
- 570p : xjc가 자바 11부터 제공되지 않는다. https://nieldw.medium.com/compile-xsd-files-into-java-classes-using-xjc-with-gradle-and-kotlin-d468f8ae33fa 이 방법대로 하면 package com.epril.sqlmap 에 생성된다. https://github.com/youngminz/toby-spring-boot/commit/38a545e920674271293fed5fa348c76c10dfb124
- 589p : throw new SqlRetrievalFailureException(e) → …(e.getMessage())
-
599p : 아래 의존성을 추가해줘야 한다.
implementation 'org.springframework.integration:spring-integration-xml'
- 601p : Castor 스프링 지원이 스프링 5.2에서 제거되었다. 매핑 XML 타이핑 열심히 했는데.. https://github.com/spring-projects/spring-framework/commit/89a7e752efcacdc07558c03a3e1dcb6dd981acf1
-
632p : SimpleJdbcTemplate이 Deprecated 되어서 결국 제거되었다. JdbcTemplate으로 대체해도 아무런 문제가 없다. https://devday.tistory.com/entry/SimpleJdbcTemplate-deprecated 또한
@After
가@AfterEach
으로 변경되었다. 또한 아래 의존성을 추가해줘야 한다.runtimeOnly 'org.hsqldb:hsqldb'
-
659p : 자바 코드에서
import com.mysql.cj.jdbc.Driver;
를 진행하려면 아래와 같이 변경해야 한다runtimeOnly 'mysql:mysql-connector-java' -> implementation 'mysql:mysql-connector-java'
-
665p : embeddedDatabase 타입을 EmbeddedDatabase로 하면 아래 오류가 발생하면서 안됨. 어차피 EmbeddedDatabase는 DataSource를 상속받았으니, DataSource로 변경해주자(
public interface EmbeddedDatabase extends DataSource
). 그래도 테스트 로그에 hsqldb 관련 로그가 찍히니 잘 동작하는 거 같다.@Resource EmbeddedDatabase embeddedDatabase; -> Error creating bean with name 'testApplicationContext': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'embeddedDatabase' is expected to be of type 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabase' but was actually of type 'org.springframework.jdbc.datasource.SimpleDriverDataSource'
- 669p :
@AutoWired SqlService sqlService
는 아래에public SqlService sqlService()
를 구현했기 때문에 불필요하다. -
708p :
@Import(SqlServiceContext.class)
를@EnableSqlService
으로 빼내었다. 그런데 이렇게 어노테이션을 바꾸자 SqlServiceContext 가 적용이 안된다. 왜 그럴까? https://github.com/youngminz/toby-spring-boot/commit/a9817bf5e745fc6e33ccb2eca738cef5db585dd7Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDaoJdbc': Unsatisfied dependency expressed through field 'sqlService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'springbook.user.sqlservice.SqlService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}