H2 데이터베이스 설치
https://www.h2database.com/html/main.html
H2 Database Engine
H2 Database Engine Welcome to H2, the Java SQL database. The main features of H2 are: Very fast, open source, JDBC API Embedded and server modes; in-memory databases Browser based Console application Small footprint: around 2.5 MB jar file size Supp
www.h2database.com
권한 주기 및 실행
chmod 755 h2.sh // 권한 주기
./h2.sh // 실행
테이블 생성
drop table if exits member CASCADE;
create table member //H2 데이터베이스에 접근해서 member 테이블 생성
(
id bigint generated by default as identity, //Member 클래스의 id라는 변수
name varchar(255),
primary key (id)
);
순수 JDBC
아주 옛날에 썼던 기술이므로 자세히 적지는 않겠다.
환경 설정
"build.gradle"에 JDBC와 H2 라이브러리 추가:
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
"application.properties"에 DB 접속 정보 설정:
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
JDBC 리포지토리 구현
JdbcMemberRepository 클래스는 MemberRepository 인터페이스를 구현.
save, findById, findAll, findByName 등의 메서드를 JDBC API로 직접 구현.
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
연결 및 자원 해제를 위한 getConnection(), close() 메소드 구현.
스프링 설정 변경 (SpringConfig)
@Configuration 클래스에서 @Bean으로 리포지토리와 서비스 등록.
MemoryMemberRepository → JdbcMemberRepository로 변경만 하면 사용 가능.
@Bean
public MemberRepository memberRepository() {
return new JdbcMemberRepository(dataSource);
}
구조 및 원칙 설명
- OCP (Open-Closed Principle): 확장에는 열려 있고, 수정에는 닫혀 있다.
- DI (Dependency Injection) 덕분에 코드 수정 없이 설정만으로 구현체 교체 가능.
실행 후 확인 사항
회원 등록 → DB에 정상 저장되는지 확인.
서버 재시작 후에도 데이터가 유지되는지 확인.
스프링 통합 테스트
스프링 컨테이너와 DB까지 모두 연결한 통합 테스트
@SpringBootTest: 스프링 컨테이너와 테스트를 함께 실행
@Transactional: 테스트 케이스에 이 노테이션이 있으면, 테스트 시작 전에 트랜잭션을 시작하고, 테스트 완료 후에 항상 ROLLBACK함 -> DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지 않음.
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {
//테스트할 때는 보통 한 번만 하고 끝나기 때문에 Autowired 옆에 바로 써준다.
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository; //MemoryMemberRepository가 아니라 MemberRepository로 해줘야 한다.
@Test
public void 회원가입() throws Exception {
//Given
Member member = new Member();
member.setName("hello"); //hello 라는 이름의 회원이 이미 있으면 에러 -> Test 를 위한 데이터베이스에서는 hello 동명이인 회원을 지워줘야 함
//When
Long saveId = memberService.join(member);
//Then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
@Test
public void 중복_회원_예외() throws Exception {
//Given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//When
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class,
() -> memberService.join(member2));//예외가 발생해야 한다. assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
스프링 JdbcTemplate
실무에서 종종 사용
jdbc 코드를 아주 압축해놓은 것이 jdbcTemplate 라이브러리
템플릿 메소드 패턴이 많이 들어가있기 때문에 template으로 간주된다.
- 환경설정은 순수JDBC와 동일
- 스프링 JdbcTemplate과 MyBatis 등의 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해준다.
하지만 SQL은 직접 작성해야 한다.
다음과 같이 매개변수로 DataSource를 인젝션받아야 한다.
private final JdbcTemplate jdbcTemplate;
@Autowired //생성자가 하나만 있으면 스프링 빈으로 등록될 시 Autowired 생략 가능
public JdbcTemplateMemberRepository(DataSource dataSource){
jdbcTemplate = new JdbcTemplate(dataSource);
}
<JdbcTemplateMemberRepository>
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
public JdbcTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
@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;
}
@Override
public Optional<Member> findById(Long id) {
List<Member> result = jdbcTemplate.query("select * from member where id
= ?", memberRowMapper(), id);
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where
name = ?", memberRowMapper(), name);
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;
}; }
}
<SpringConfig>
jdbcTemplate을 사용하도록 스프링 설정 변경(repository 부분 jdbc로 수정)
package hello.hellospring;
import hello.hellospring.repository.JdbcMemberRepository;
import hello.hellospring.repository.JdbcTemplateMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
private final DataSource dataSource;
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
//return new MemoryMemberRepository();
//return new JdbcMemberRepository(dataSource);
return new JdbcTemplateMemberRepository(dataSource); // 변경 !!!!
}
}
=> db까지 연결된 테스트 성공.
'학교 강의 > sw자가학습' 카테고리의 다른 글
[Day6] 코딩 자율학습 스프링 부트 3 자바 백엔드 개발 입문 3~4장 (0) | 2025.07.08 |
---|---|
[day5] 코딩 자율학습 스프링 부트 3 자바 백엔드 개발 입문 1~2장 (1) | 2025.07.07 |
[Day4]스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 섹션 7장 (0) | 2025.07.05 |
[Day2]스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 섹션 3~4장 (0) | 2025.07.03 |
[Day1]스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 섹션 1~2장 (0) | 2025.07.02 |