[방학 스터디] 스프링 입문(5)

김지후·2026년 2월 19일

섹션 6. 웹 MVC 개발
섹션 7. 스프링 DB 접근 기술

[회원 웹 기능 - 홈 화면 추가]

홈 컨트롤러 추가

package hello.hellospring.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
    
    @GetMapping("/")
	public String home() {
		return "home";
    }
}

회원 관리용 홈

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
    <div>
        <h1>Hello Spring</h1>
        <p>회원 기능</p>
        <p>
            <a href="/members/new">회원 가입</a>
            <a href="/members">회원 목록</a>
        </p>
    </div>
</div> <!-- /container -->
</body>
</html>

컨트롤러가 정적 파일보다 우선순위가 높다.

[회원 웹 기능 - 등록]

회원 등록 폼 개발

회원 등록 폼 컨트롤러

@Controller
public class MemberController {
private final MemberService memberService;
    
    @Autowired
	public MemberController(MemberService memberService) {
		this.memberService = memberService;
    }
    
    @GetMapping(value = "/members/new")
	public String createForm() {
		return "members/createMemberForm";
    }
}

회원 등록 폼 HTML
(resources/templates/members/createMemberForm)

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
    <form action="/members/new" method="post">
        <div class="form-group">
            <label for="name">이름</label>
          	<input type="text" id="name" name="name" placeholder="이름을 입력하세요">
        </div>
        <button type="submit">등록</button>
    </form>

</div> <!-- /container -->
</body>
</html>

회원 등록 컨트롤러

웹 등록 화면에서 데이터를 전달 받을 폼 객체

package hello.hellospring.controller;

public class MemberForm {
	private String name;

	public String getName() {
		return name;
    }
	
    public void setName(String name) {
		this.name = name;
    }
}

회원 컨트롤러에서 회원을 실제 등록하는 기능

@PostMapping(value = "/members/new")
public String create(MemberForm form) {

	Member member = new Member();
    member.setName(form.getName());
    
    memberService.join(member);
	
    return "redirect:/";
}

[회원 웹 기능 - 조회]

회원 컨트롤러에서 조회 기능

@GetMapping(value = "/members")
public String list(Model model) {
	List<Member> members = memberService.findMembers();
    model.addAttribute("members", members);
	return "members/memberList";
}

회원 리스트 HTML

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
  
<div class="container">
    <div>
        <table>
            <thead>
            <tr>
                <th>#</th>
                <th>이름</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="member : ${members}">
                <td th:text="${member.id}"></td>
                <td th:text="${member.name}"></td>
            </tr>
            </tbody>
        </table>
    </div>
  
</div> <!-- /container -->
</body>
</html>

[스프링 DB 접근 기술]

  1. 순수 JDBC
  2. 스프링 JdbcTemplate
  3. JPA
  4. 스프링 데이터 JPA

이 중 현재 실무에서 핵심적으로 사용되는 기술은 JPA와 스프링 데이터 JPA이다.

H2 데이터베이스 설치: H2는 개발 및 테스트 용도로 사용되는 경량 데이터베이스이다.

1. 순수 JDBC - 자바에서 관계형 DB에 접근하기 위한 표준 API

DataSource(또는 DriverManager)로 Connection 획득
-> SQL 문자열 작성-> PreparedStatement 생성 + setString, setLong 등 파라미터 세팅
-> executeUpdate() / executeQuery() 실행
-> ResultSet 읽고 Member 같은 도메인 객체로 매핑-> 예외 처리 + 리소스 정리
개발자가 직접 다 처리함...

장점

  • 가장 직관적으로 DB와 1:1 대응 (무슨 SQL이 실행되는지 명확).
  • 모든 기능을 원하는 대로 제어 가능.

단점

  • 반복 코드가 너무 많다.
  • 실수 위험 ↑

2. 스프링 JdbcTemplate - 스프링이 제공하는 JDBC 추상화 도구

JDBC를 쓰지만, 그 과정의 반복(커넥션/예외/리소스 정리)을 스프링이 대신 해준다.

장점

  • JDBC의 반복 코드 대폭 제거
  • 코드 길이 감소, 가독성↑

단점

  • SQL은 여전히 직접 작성해야 한다.
  • 객체 중심 설계로 가기 어렵고 SQL 관리 비용이 커진다.
    //왜 객체 중심 설계로 가야 하는가?

3. JPA

JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환힐 수 있다.

build.gradle 파일에 JPA, h2 데이터베이스 관련 라이브러리 추가

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
}

spring-boot-starter-data-jpa는 내부에 jdbc 관련 라이브러리를 포함한다.

스프링 부트에 JPA 설정 추가

resources/application.properties

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

spring.datasource.*
-> DB 연결 정보
spring.jpa.show-sql=true
-> JPA가 생성한 SQL을 콘솔에 출력
spring.jpa.hibernate.ddl-auto
-> JPA가 테이블을 자동으로 생성할지 여부를 설정

JPA 엔티티 매핑

package hello.hellospring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
	
    @Entity
		public class Member {
    
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	private String name;
	public Long getId() {
		return id;
    }
	public void setId(Long id) {
		this.id = id;
    }
	public String getName() {
		return name;
    }
	public void setName(String name) {
		this.name = name;
    }
}

@Entity
-> 이 클래스는 DB 테이블과 매핑됨
@Id
-> PK 지정
@GeneratedValue
-> ID 자동 생성

JPA 회원 리포지토리

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;

public class JpaMemberRepository implements MemberRepository {
	
    private final EntityManager em;

	public JpaMemberRepository(EntityManager em) {
		this.em = em;
    }

	public Member save(Member member) {
        em.persist(member);
    }
		
        return member;

public Optional<Member> findById(Long id) {
	Member member = em.find(Member.class, id);
	return Optional.ofNullable(member);
}

public List<Member> findAll() {
	return em.createQuery("select m from Member m", Member.class)
                .getResultList();
}

public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where 
m.name = :name", Member.class)
                .setParameter("name", name)
                .getResultList();
		return result.stream().findAny();
    }
}

persist → 저장
find → PK 조회
createQuery → JPQL 실행

서비스 계층에 트랜잭션 추가

import org.springframework.transaction.annotation.Transactional

@Transactional
public class MemberService {}

트랜잭션(Transaction)
여러 개의 작업을 하나의 논리적인 단위로 묶어서
모두 성공하거나 모두 실패하도록 보장하는 것

JPA를 사용하도록 스프링 설정 변경

@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
return new JdbcTemplateMemberRepository(dataSource);
return new JpaMemberRepository(em);
}

4. 스프링 데이터 JPA

  • JPA를 편리하게 사용하도록 도와주는 기술.
  • 리포지토리 구현체를 직접 만들지 않고 인터페이스만 선언해도 스프링이 구현을 제공.
  • 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공.

0개의 댓글