"인프런의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의를 듣고 작성한 글 입니다."
[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., 스프링 학습 첫 길잡이! 개발 공부의 길을 잃지 않도록 도와드립니다. 📣 확인해주세
www.inflearn.com
스프링 JdbcTemplate
- 환경설정은 순수 JDBC 와 동일하다.
스프링 JdbcTemplate 과 MyBatis 같은 라이브러리는 JDBC API 에서 본 반복 코드를 대부분 제거해준다. 하지만 SQL 은 직접 작성해야 한다.
(pro.gg 프로젝트 때도 마찬가지로 MyBatis 를 활용하면서 SQL 은 전부 다 직접 작성해 주었다.)
다음과 같이 코드를 작성해주자.
- JdbcTemplateMemberRepository.java
public class JdbcTemplateMemberRepository implements MemberRepository{
private final JdbcTemplate jdbcTemplate;
@Autowired // 생성자가 하나만 있을 경우 어노테이션 생략 가능
public JdbcTemplateMemberRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
return null;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.empty();
}
@Override
public Optional<Member> findByName(String name) {
return Optional.empty();
}
@Override
public List<Member> findAll() {
return null;
}
}
DataSource 객체 의존성을 주입 받고 추후에 설정파일 까지 변경하여 JdbcTemplate 클래스 객체를 스프링 빈으로 등록해야 한다.
JdbcTemplate 의 경우 조회부터 먼저 실습해보자.
- JdbcTemplateMemberRepository.java
@Override
public Optional<Member> findById(Long id) {
List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id);
return result.stream().findAny();
}
private RowMapper<Member> memberRowMapper(){ // 객체 생성에 대한 콜백 메소드
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
// return new RowMapper<Member>() { // alt + enter 를 통해 위와 같이 람다 식으로 변환해줄 수 있다.
// @Override
// public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
// Member member = new Member();
// member.setId(rs.getLong("id"));
// member.setName(rs.getString("name"));
// return member;
//
// }
// };
}
* 여기서 RowMapper 클래스는 SQL 의 실행 결과를 객체에 맞게 매핑해주는 역할을 해준다.
* Resultset object -> DTO Object
지금 위에서 작성한 코드와 순수 JDBC 를 사용했을 때의 코드를 비교해보면 코드의 길이에 있어서 굉장히 큰 차이가 있는 것을 발견할 수 있다.
데이터 저장 메소드의 경우 코드가 좀 복잡하다.
아래의 코드를 보자.
- JdbcTemplateMemberRepository.java
@Override
public Member save(Member member) {
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", member.getName());
Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
member.setId(key.longValue());
return member;
}
코드에 대해 간략하게 설명을 해보자면,
SimpleJdbcInsert 는 테이블 명과 컬럼 명을 명시해주면 개발자가 쿼리를 작성해줄 필요 없이 쉽게 insert 쿼리를 만들 수 있게끔 해주는 클래스이다.
위의 코드를 보면 SimpleJdbcInsert 클래스로 만들어진 객체에 테이블 명(member)과 컬럼 명(id)을 명시하여 insert 쿼리를 만듬과 동시에, member 테이블에서 키 값에 설정해둔 auto_increment 설정으로 인해 값이 자동으로 커진 기본 키 값을 컬럼 명에 자동으로 입력 시켜 주고 있다.
이후 삽입해야 하는 name 데이터를 Map 클래스 객체에 name 키 값의 value 로 적재해준 다음, 해당 객체를 파라미터로 하는 MapSqlParameterSource 객체를 만들어준다.
그렇게 만들어준 객체를 다시 파라미터로 하는 executeAndReturnKey 메소드를 실행시킬 때 해당 객체에 SQL 파라미터 값으로 실려있는 name 데이터 값을 생성한 insert 쿼리에 적재한다.
insert 쿼리에 name 데이터가 적재되고 나면 executeAndReturnKey 메소드로 인해 해당 쿼리가 데이터베이스로 전달되어 실행되고, 실행 결과를 반환받을 때 삽입된 데이터의 키 값을 반환받아서 key 변수에 적재해주고,
해당 변수를 long 타입으로 캐스팅 한 다음 Member 클래스 객체에 세팅해주고 난 뒤 해당 객체를 리턴해준다.
(만약 실무에서 이런것들을 써야 하는 상황이 와도 관련 문서를 확인해보면 어떻게 사용하면 되는지 친절하게 잘 나와있다고 하니 참고 하도록 하자.)
사실 JdbcTemplate 도 상세한 것들을 보자면 어마어마 하다. 이번 강의 동안은 대충 이런거다 하고 감만 잡는 수준으로 넘어가도 된다.
이제 나머지 메소드들도 코드를 작성해주자.
- JdbcTemplateMemberRepository.java
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return jdbcTemplate.query("select * from memeber", memberRowMapper());
}
Repository 에서 필요한 코드들은 모두 작성해 줬으니 이제 설정파일 에서 스프링 빈에 등록하는 Repository 클래스 객체를 바꿔주자.
- SpringConfig.java
@Bean
public MemberRepository memberRepository(){
// return new MemoryMemberRepository();
// return new JdbcMemberRepository(dataSource);
return new JdbcTemplateMemberRepository(dataSource);
}
그럼 이제 어플리케이션을 동작시켜 코드가 잘 돌아가는지 확인만 하면 되는걸까?
그러기에 앞서 이전 글에서 스프링 통합 테스트를 해봤던 것을 기억할 것이다.
Repository 는 바뀌었으나 여전히 여기서 작성했던 테스트 코드를 돌리는 것 만으로도 코드들이 잘 동작하는지 확인이 가능하다.
왜냐, MemberServiceIntegrationTest 에서 주입받은 의존성에 JdbcTemplateMemberRepository 로 변경된 객체가 그대로 주입되기 때문이다.
(SpringConfig 에서 해당 객체를 스프링 빈으로 등록하도록 바꾸었기 때문에 가능하다.)
그럼 이번엔 어플리케이션이 아니라 앞전에 작성했던 테스트 코드들을 전체적으로 수행시켜서 테스트가 잘 통과되는지를 확인해보자.
그 결과, 테스트가 잘 통과되는 것을 확인해 볼 수 있을 것이다.
* 번외
현업에서는 작은 버그가 수억, 수십억의 피해로 돌아올 수 있다.
그렇기에 테스트 코드를 잘 작성하는 것이 정말 중요하고, 테스트 코드를 잘 작성하기 위한 노력을 많이 기울여야 한다.
실제 현업을 봐도 개발하는 시간을 100 이라고 놓았을 때, 60은 테스트 코드를 작성하는 경우가 많다고 한다.
개발하고 있는 프로젝트가 크면 클수록 테스트 코드를 잘 작성하는 것이 정말 중요해진다.
다음 시간부터는 JPA 에 대해 알아보자.(작년에 JPA 에 대해 많은 공부를 했었는데 스스로 얼마나 기억을 하고 있을지가 궁금해진다.)
'Spring basic' 카테고리의 다른 글
스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - Spring Data JPA (0) | 2021.11.16 |
---|---|
스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - JPA (0) | 2021.11.15 |
스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 스프링 통합 테스트 (0) | 2021.11.10 |
스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 순수 JDBC (0) | 2021.11.10 |
스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - H2 데이터베이스 설치 (0) | 2021.11.09 |