Bibi's DevLog ๐Ÿค“๐ŸŽ

[Spring] ์ธํ”„๋Ÿฐ ์Šคํ”„๋ง ์ž…๋ฌธ(๊น€์˜ํ•œ ๋‹˜) - ์ˆœ์ˆ˜JDBC, ํ†ตํ•ฉํ…Œ์ŠคํŠธ, ์Šคํ”„๋งJdbcTemplate, JPA, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๋ณธ๋ฌธ

๐Ÿ–ฅ BE ๋ฐฑ์—”๋“œ/Spring ์Šคํ”„๋ง

[Spring] ์ธํ”„๋Ÿฐ ์Šคํ”„๋ง ์ž…๋ฌธ(๊น€์˜ํ•œ ๋‹˜) - ์ˆœ์ˆ˜JDBC, ํ†ตํ•ฉํ…Œ์ŠคํŠธ, ์Šคํ”„๋งJdbcTemplate, JPA, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA

๋น„๋น„ bibi 2021. 3. 6. 00:33

์ด ๊ธ€์€ ์ธํ”„๋Ÿฐ ๊น€์˜ํ•œ ๋‹˜์˜ ์Šคํ”„๋ง ์ž…๋ฌธ ๊ฐ•์˜ ๋ฅผ ๋“ฃ๊ณ  ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

(์ด์–ด์„œ)

์Šคํ”„๋ง DB ์ ‘๊ทผ ๊ธฐ์ˆ 

...

์ˆœ์ˆ˜ JDBC

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ DB๋ฅผ ์—ฐ๊ฒฐํ•ด ์ €์žฅํ•˜๋Š” ๋ฒ•์„ ๋ฐฐ์›Œ๋ณผ ๊ฒƒ์ด๋‹ค.

์ฟผ๋ฆฌ๋ฌธ์„ ์ด์šฉํ•ด DB์— ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ณ  ๋นผ๋Š” ๋ฐฉ๋ฒ•

๊ทธ ์ค‘์—์„œ๋„ 20๋…„ ์ „ ๋ฐฉ์‹์ธ ์ˆœ์ˆ˜ JDBC API๋งŒ์„ ์‚ฌ์šฉํ•œ ๋ฐฉ๋ฒ•์„ ๋จผ์ € ๋ณด๊ณ , ์ ์ฐจ ๋ฐœ์ „๋˜๋Š” ๊ธฐ์ˆ ๋“ค์„ ๋ฐฐ์šธ ๊ฒƒ์ด๋‹ค.

์ž๋ฐ”๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ DB์™€ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด JDBC๋ผ๋Š” ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ๊ผญ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

1

build.gradle

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

dependencies {
    // codes ...
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    runtimeOnly 'com.h2database:h2'
}
  • implementation 'org.springframework.boot:spring-boot-starter-jdbc' : DB์™€ ์—ฐ๊ฒฐ์„ ์œ„ํ•ด, JDBC ๋“œ๋ผ์ด๋ฒ„ ์ถ”๊ฐ€
  • runtimeOnly 'com.h2database:h2' : DB์™€ ์—ฐ๊ฒฐํ•  ๋•Œ, DB๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•ด์„œ ์ถ”๊ฐ€ํ•œ ์ฝ”๋“œ

application.properties

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. (src/main/java/resources/application.properties)

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
  • url : ์ ‘๊ทผํ•  DB์˜ ๊ฒฝ๋กœ. H2๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘์†ํ•  ๋•Œ ๋„ฃ์—ˆ๋˜ ์ฃผ์†Œ์™€ ๋™์ผํ•˜๋‹ค.

  • driver-class-name : ์ ‘๊ทผํ•  DB๋“œ๋ผ์ด๋ฒ„์˜ ์ด๋ฆ„

    • importํ•˜์ง€ ์•Š์œผ๋ฉด ๋นจ๊ฐ„์ƒ‰์ด๋‹ค. build.gradle์— ๊ฐ€์„œ ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ•ด๊ฒฐ๋œ๋‹ค.

      gradle ์ƒˆ๋กœ๊ณ ์นจ

  • spring.datasource.username=sa : ์Šคํ”„๋ง๋ถ€ํŠธ2.4๋ถ€ํ„ฐ๋Š” ์ด๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค(Wrong user name or password). ๊ณต๋ฐฑ์ด ์ „ํ˜€ ์—†์–ด์•ผ ํ•œ๋‹ค.

2

์ด์ œ JDBC๋กœ ์—ฐ๊ฒฐ๋œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด ๋ณธ๋‹ค. ๊ธฐ์กด MemoryMemberRepository๊ฐ€ ์•„๋‹Œ ์ƒˆ๋กœ์šด ๊ตฌํ˜„์ฒด JdbcMemberRepository๋ฅผ ๋งŒ๋“ ๋‹ค.

์˜›๋‚  ๊ธฐ์ˆ ์ด๋ฏ€๋กœ ์ด๋ ‡๊ฒŒ ํ–ˆ์—ˆ๊ตฌ๋‚˜~ ์ •๋„๋ง ์•Œ๊ณ  ์ฐธ๊ณ ๋งŒ ํ•˜์ž!!

JdbcMemberRepository.java

(src/main/java/ํ”„๋กœ์ ํŠธ๋ช…/repository์— ์ƒ์„ฑ)

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.jdbc.datasource.DataSourceUtils;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class JdbcMemberRepository implements MemberRepository{

    private final DataSource dataSource;

    public JdbcMemberRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Member save(Member member) {
        String sql = "insert into member(name) values(?)";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1, member.getName());
            pstmt.executeUpdate();
            rs = pstmt.getGeneratedKeys();
            if (rs.next()) {
                member.setId(rs.getLong(1));
            } else {
                throw new SQLException("id ์กฐํšŒ ์‹คํŒจ");
            }
            return member;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public Optional<Member> findById(Long id) {
        String sql = "select * from member where id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setLong(1, id);
            rs = pstmt.executeQuery();
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            } else {
                return Optional.empty();
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public List<Member> findAll() {
        String sql = "select * from member";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            List<Member> members = new ArrayList<>();
            while(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                members.add(member);
            }
            return members;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public Optional<Member> findByName(String name) {
        String sql = "select * from member where name = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            }
            return Optional.empty();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    private Connection getConnection() {
        return DataSourceUtils.getConnection(dataSource);
    }
    private void close(Connection conn, PreparedStatement pstmt, ResultSet rs)
    {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (pstmt != null) {
                pstmt.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) {
                close(conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    private void close(Connection conn) throws SQLException {
        DataSourceUtils.releaseConnection(conn, dataSource);
    }
}
  • DataSource : DB์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์š”์†Œ. importํ•œ๋‹ค (javax.sql.DataSource)

    • datasource๋Š” ์Šคํ”„๋ง์œผ๋กœ๋ถ€ํ„ฐ ์ฃผ์ž…๋ฐ›์•„์•ผ ํ•œ๋‹ค.

      ์Šคํ”„๋ง์ด application.properties์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ DataSource๋ฅผ ๋งŒ๋“ค์–ด ๋‘๋Š”๋ฐ, ์ด๋ฅผ ์ฃผ์ž…๋ฐ›์•„์•ผ ํ•œ๋‹ค.

  • Connection connection = dataSource.getConnection(sql)

    • DB๋กœ๋ถ€ํ„ฐ์˜ ์ปค๋„ฅ์…˜(์†Œ์ผ“)์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

    • ์ด์ œ ์—ฌ๊ธฐ์— SQL๋ฌธ์„ ๋งŒ๋“ค์–ด DB์— ์ „๋‹ฌํ•˜๋ฉด ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

  • pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)

    • Connection์— ๋„ฃ์„ SQL๋ฌธ์„ ์ค€๋น„ํ•œ๋‹ค.

    • Statement.RETURN_GENERATED_KEYS : ์˜ต์…˜. DB์—์„œ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ ํ‚ค๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

      DB์—์„œ generated by default as identity๋กœ ์ƒ์„ฑ๋˜๋Š” ๊ฐ’, ์ฆ‰ id๊ฐ’์„ ๋งํ•จ.

  • pstmt.setString(1, member.getName());

    • 1, member.getName() : sql๋ฌธ์˜ ์ฒซ ๋ฒˆ์งธ ์™€์ผ๋“œ์นด๋“œ(?)๋ฅผ member.getName()์œผ๋กœ ์…‹ํŒ…ํ•œ๋‹ค.
  • pstmt.executeUpdate();

    • DB์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด DB์— sql์ฟผ๋ฆฌ๋ฌธ์„ ๋ณด๋‚ธ๋‹ค.
  • rs = pstmt.getGeneratedKeys();

    • Statement.RETURN_GENERATED_KEYS๋กœ ์–ป์€ ํ‚ค ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค.
  • try-catch๋ฌธ์œผ๋กœ Exception์„ ์žก์•„ ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

  • close(conn, pstmt, rs) : ์‚ฌ์šฉํ•œ ์ž์› (Connection, PreparedStatement, ResultSet)์„ ๋ฆด๋ฆฌ์ฆˆํ•ด์ค˜์•ผ ํ•œ๋‹ค.

    • โ— ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ์ด์šฉํ•œ ์ž์›๋“ค์€ ์‚ฌ์šฉ ํ›„ ๋ฐ˜๋“œ์‹œ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

      ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ž์›์ด ๊ณ„์† ์Œ“์—ฌ์„œ ์„œ๋น„์Šค ์žฅ์• ๊ฐ€ ๋‚  ์ˆ˜ ์žˆ๋‹ค.

  • pstmt.executeQuery();

    • DB๋ฅผ ์กฐํšŒํ•  ๋•Œ๋Š” executeQuery()๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

์Šคํ”„๋ง์œผ๋กœ DB ์ปค๋„ฅ์…˜ ์—ฐ๊ฒฐ ๋ฐ ํ•ด์ œํ•˜๊ธฐ

private Connection getConnection() {
    return DataSourceUtils.getConnection(dataSource);
}    
  • ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ†ตํ•ด DB ์ปค๋„ฅ์…˜์„ ์–ป์„ ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ์ด๋ ‡๊ฒŒ ์–ป์–ด์•ผ ํ•œ๋‹ค. (ํ˜•์‹)

    • DataSourceUtils๋ฅผ ํ†ตํ•ด getConnection()์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

      ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ  ๋˜‘๊ฐ™์€ ์ปค๋„ฅ์…˜์„ ์œ ์ง€ํ•˜๋„๋ก ๋„์™€ ์ค€๋‹ค.

private void close(Connection conn) throws SQLException {
    DataSourceUtils.releaseConnection(conn, dataSource);
}
  • ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ†ตํ•ด DB ์ปค๋„ฅ์…˜์„ ๋‹ซ์„ ๋•Œ๋„ ๋ฐ˜๋“œ์‹œ ์ด๋ ‡๊ฒŒ ๋‹ซ์•„์•ผ ํ•œ๋‹ค.
    • DataSourceUtils๋ฅผ ํ†ตํ•ด releaseConnection()์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

3

์ด์ œ JDBC๋กœ ์—ฐ๊ฒฐ๋˜๋„๋ก ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์™„์„ฑํ–ˆ์œผ๋ฏ€๋กœ ์‚ฌ์šฉํ•ด ๋ณด์ž.

SpringConfig.java

(src/main/java/ํ”„๋กœ์ ํŠธ/service/SpringConfig)

์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•œ๋‹ค.

package hello.hellospring;

import hello.hellospring.repository.JdbcMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class SpringConfig {

    private DataSource dataSource;

    @Autowired
    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);
    }
}
  • DataSource
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ํš๋“ํ•  ๋–„ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด.
    • ์Šคํ”„๋ง์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜(application.properties) ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ, ์ž์ฒด์ ์œผ๋กœ DataSource๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ์ƒ์„ฑํ•ด ์ค€๋‹ค. ์ด๋ฅผ ์ƒ์„ฑ์ž๋กœ ์ฃผ์ž…ํ•ด ์‚ฌ์šฉํ•œ๋‹ค(DI).
      • @Autowired DataSource dataSource๋กœ ํ•ด๋„ ๋˜์ง€๋งŒ, DI๋กœ ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์€ ๋ฐฉ๋ฒ•.

โ— ๋‹ค๋ฅธ ์–ด๋–ค ์ฝ”๋“œ๋„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™•์žฅํ•ด JdbcMemberRepository.java๋ฅผ ๋งŒ๋“ค๊ณ  SpringConfig.java๋งŒ ์ˆ˜์ •ํ–ˆ๋‹ค.

4

์ด์ œ ์ˆ˜์ •๋œ DB๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด ๋ณธ๋‹ค.

  • H2๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ์Šคํ”„๋ง ์•ฑ์„ ์‹คํ–‰ํ•œ ๋’ค, localhost:8080์œผ๋กœ ์ ‘์†ํ•ด ํšŒ์› ๋ชฉ๋ก์„ ์กฐํšŒํ•œ๋‹ค.
    • H2 DB์— ์ €์žฅํ–ˆ๋˜ ๋‚ด์—ญ์ด ์กฐํšŒ๋˜๋ฉด ์„ฑ๊ณต์ด๋‹ค.
  • ์ด๋ฒˆ์—๋Š” ํšŒ์› ๊ฐ€์ž…์œผ๋กœ ํšŒ์›์„ ์ถ”๊ฐ€ํ•œ ๋’ค ๋‹ค์‹œ ํšŒ์› ๋ชฉ๋ก์„ ์กฐํšŒํ•œ๋‹ค.
    • ์ถ”๊ฐ€ํ•œ ํšŒ์›์ด ๋ชฉ๋ก์œผ๋กœ ์กฐํšŒ๋˜๋ฉด ์„ฑ๊ณต์ด๋‹ค.

์ด์ œ ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ €์žฅํ•˜๋ฏ€๋กœ, ์Šคํ”„๋ง ์„œ๋ฒ„๋ฅผ ๊ป๋‹ค ์ผœ๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅ๋œ๋‹ค.

(ํ•ต์‹ฌ) ์ค‘์š”ํ•œ ๋ถ€๋ถ„!

์Šคํ”„๋ง์„ ์™œ ์“ฐ๋Š”๊ฐ€?? ๊ฐ์ฒด์ง€ํ–ฅ์ด ์ข‹์•„์„œ.

โ— ๊ธฐ์กด์˜ ๋‹ค๋ฅธ ์–ด๋–ค ์ฝ”๋“œ๋„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™•์žฅํ•ด JdbcMemberRepository.java๋ฅผ ๋งŒ๋“ค๊ณ  SpringConfig.java๋งŒ ์ˆ˜์ •ํ–ˆ๋‹ค.

์ฆ‰ ๊ฐ์ฒด์ง€ํ–ฅ์˜ ๋‹คํ˜•์„ฑ์„ ํŽธํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค(MemberRepository)๋ฅผ ๋‘๊ณ , ์„œ๋กœ ๋‹ค๋ฅธ ๊ตฌํ˜„์ฒด(MemoryMemberRepository ๋˜๋Š” JdbcMemberRepository)๋“ค์„ ์‰ฝ๊ณ  ํŽธํ•˜๊ฒŒ ๋ฐ”๊ฟ” ๋ผ์šธ ์ˆ˜ ์žˆ๋‹ค.

๋˜, DI๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ๋‹ค๋ฅธ ์†Œ์Šค์ฝ”๋“œ๋“ค์˜ ๋‚ด์šฉ์€ ์ „ํ˜€ ์† ๋Œˆ ํ•„์š”๊ฐ€ ์—†์–ด์กŒ๋‹ค.

์Šคํ”„๋ง์€ ์ด ๊ณผ์ •๋“ค์ด ๋” ํŽธํ•˜๋„๋ก ์ง€์›ํ•ด ์ค€๋‹ค.

๊ตฌํ˜„ ํด๋ž˜์Šค ์ด๋ฏธ์ง€

  • MemberService๋Š” MemberRepository๋ฅผ ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.
  • MemberRepository๋Š” ๊ทธ ๊ตฌํ˜„์ฒด๋กœ MemoryMemberRepository์™€ JdbcMemberRepository๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ

๋ฐ”๋€ ์Šคํ”„๋ง ์„ค์ •์€ ์œ„์™€ ๊ฐ™๋‹ค.

  • ๊ธฐ์กด์— ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ MemoryMemberRepository๋ฅผ ์‚ญ์ œํ•˜๊ณ ,
  • ์ƒˆ๋กœ memberRepository๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ–ˆ๋‹ค.
  • ๊ทธ ์™ธ์— ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋Š” ์† ๋Œˆ ๊ฒŒ ์—†๋‹ค.

์ด๋ฅผ SOLID์›์น™ ์ค‘ '๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™(OCP, Open-Closed Principle)'์ด๋ผ๊ณ  ํ•œ๋‹ค. (SOLID๋Š” ๋‚˜์ค‘์— ๊ฒ€์ƒ‰ํ•ด ๋ณด๋ผ)

  • ํ™•์žฅ์—๋Š” ์—ด๋ ค ์žˆ๊ณ , ์ˆ˜์ •(๋ณ€๊ฒฝ)์—๋Š” ๋‹ซํ˜€ ์žˆ๋‹ค.

  • ํ™•์žฅ(๊ธฐ๋Šฅ์ถ”๊ฐ€)์„ ํ•˜๋ ค๋ฉด ์ˆ˜์ •์„ ํ•ด์•ผ ํ•˜๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ์ด๊ฒŒ ๊ฐ€๋Šฅํ•œ๊ฐ€?

    • ์šฐ๋ฆฌ๊ฐ€ ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ, ๊ฐ์ฒด์ง€ํ–ฅ์˜ ๋‹คํ˜•์„ฑ์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๊ธฐ๋Šฅ์„ ์™„์ „ํžˆ ๋ณ€๊ฒฝํ•˜๋ฉด์„œ๋„ ๊ธฐ์กด ์ฝ”๋“œ ์ „์ฒด๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

      (๋ฌผ๋ก  ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์กฐ๋ฆฝํ•˜๋Š” ์ฝ”๋“œ ์ •๋„๋Š” ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค)

    • ์Šคํ”„๋ง์˜ DI๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, "๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์ „ํ˜€ ์†๋Œ€์ง€ ์•Š๊ณ , ์„ค์ •๋งŒ์œผ๋กœ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ๋ณ€๊ฒฝ"ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๊ฒƒ์ด ๊ฐ์ฒด์ง€ํ–ฅ์˜ ๋งค๋ ฅ ์ค‘ ํ•˜๋‚˜์ด๋‹ค.

์Šคํ”„๋ง ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์™€ DB๊นŒ์ง€ ์—ฐ๊ฒฐ๋œ ''ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ''๋ฅผ ์ง„ํ–‰ํ•ด ๋ณด์ž.

์ง€๊ธˆ๊นŒ์ง€ ํ–ˆ๋˜ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋Š” ์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ์ˆœ์ˆ˜ํ•œ ์ž๋ฐ” ์ฝ”๋“œ๋งŒ ๊ฐ€์ง€๊ณ  ๋งŒ๋“  ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ ์ด์ œ ์Šคํ”„๋ง์ด ๊ด€์—ฌํ•ด์„œ ๋ฐ์ดํ„ฐ์†Œ์Šค ๋“ฑ์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋„ ์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

(์›๋ž˜๋Š” ๋ณดํ†ต TEST ์ „์šฉ DB๋ฅผ ์“ฐ๊ฑฐ๋‚˜, ๋กœ์ปฌ DB๋ฅผ ์—ฐ๊ฒฐํ•œ ๋‹ค์Œ ์ง„ํ–‰ํ•œ๋‹ค.)

MemberServiceIntegrationTest.java

๋ผ๋Š” ์ƒˆ๋กœ์šด ํ…Œ์ŠคํŠธํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์ง„ํ–‰ํ•œ๋‹ค.

(src/test/java/ํ”„๋กœ์ ํŠธ/service/MemberServiceIntegrationTest.java)

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.*;

@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {

    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;

    @Test
    void ํšŒ์›๊ฐ€์ž…() {
        // given - ํ…Œ์ŠคํŠธํ•  ๋ฐ์ดํ„ฐ
        Member member = new Member();
        member.setName("spring");
        // when - ๊ฒ€์ฆํ•˜๋ ค๋Š” ๋กœ์ง : MemberService์˜ join().
        Long saveId = memberService.join(member);

        // then - ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ (๊ฒ€์ฆ๋ถ€)
        Member findMember = memberService.findOne(saveId).get();
        assertThat(member.getName()).isEqualTo(findMember.getName());
    }

    @Test
    void ์ค‘๋ณตํšŒ์›์˜ˆ์™ธ() {
        Member member1 = new Member();
        member1.setName("spring");

        Member member2 = new Member();
        member2.setName("spring");

        memberService.join(member1);

        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.");

    }
}

@SpringBootTest

  • ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์™€ ํ…Œ์ŠคํŠธ๋ฅผ ํ•จ๊ป˜ ์‹คํ–‰ํ•œ๋‹ค. (์Šคํ”„๋ง์„ ์ง์ ‘ ๋„์›Œ์„œ ํ…Œ์ŠคํŠธํ•œ๋‹ค)

@Transactional

  • ํ…Œ์ŠคํŠธ์ผ€์ด์Šค์— ๋ถ™์ด๋ฉด, ํ…Œ์ŠคํŠธ ์‹คํ–‰์‹œ ํŠธ๋žœ์žญ์…˜์„ ๋จผ์ € ์‹คํ–‰ํ•˜๊ณ  ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ๋กค๋ฐฑ์„ ํ•ด ์ค€๋‹ค.

    ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด DB์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์Œ ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.

    ์ผ๋ฐ˜ ํด๋ž˜์Šค์— ๋ถ™์ด๋ฉด ์‹คํ–‰ ์‹œ ํŠธ๋žœ์žญ์…˜๋งŒ ์‹คํ–‰ํ•œ๋‹ค. (๋กค๋ฐฑ์€ ํ•˜์ง€ ์•Š๋Š”๋‹ค)

  • ํ…Œ์ŠคํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฐ˜๋ณต ์‹คํ–‰ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ DB์— ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ณ„์† ์Œ“์ด๋ฉด ๊ฐ™์€ ํ…Œ์ŠคํŠธ๋ฅผ ๋ฐ˜๋ณตํ–ˆ์„ ๋•Œ ์ค‘๋ณต ๋ฐ์ดํ„ฐ๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ์ •ํ™•ํ•˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค.

  • ์Šคํ”„๋ง์ด @BeforeEach, @AfterEach ๋Œ€์‹  ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•ด ์ด๋ฅผ ํ•ด๊ฒฐํ•ด ์ฃผ์—ˆ๋‹ค.

    • ํŠธ๋žœ์žญ์…˜(Transcation) : ๋กค๋ฐฑ? - ๊ณต๋ถ€ํ•˜๊ธฐ..
  • ๋ฐ˜๋Œ€๋กœ @Commit ์„ ๋ถ™์ธ ํ…Œ์ŠคํŠธ๋Š” ๋กค๋ฐฑํ•˜์ง€ ์•Š๊ณ  ๋ณ€๊ฒฝ๋‚ด์šฉ์„ DB์— ๋ฐ˜์˜ํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
  • ํ…Œ์ŠคํŠธ์ฝ”๋“œ์ด๋ฏ€๋กœ DI๋Š” ์ƒ๋žตํ•˜๊ณ  ๊ณง๋ฐ”๋กœ ์—ฐ๊ฒฐํ•ด ์ค€๋‹ค.
  • MemoryMemberRepository๊ฐ€ ์•„๋‹Œ, ์ธํ„ฐํŽ˜์ด์Šค์ธ MemberRepository๋กœ ๋ฐ”๊พผ๋‹ค.

๋‹จ์œ„ ํ…Œ์ŠคํŠธ

์Šคํ”„๋ง์„ ๋„์šฐ์ง€ ์•Š๊ณ  ์ž๋ฐ” ์ฝ”๋“œ๋กœ๋งŒ ์ง„ํ–‰ํ•˜๋ฉฐ ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„์˜ ํ…Œ์ŠคํŠธ๋กœ๋งŒ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์„ ''๋‹จ์œ„ํ…Œ์ŠคํŠธ''๋ผ๊ณ  ํ•œ๋‹ค. (์ด์ „์— ํ–ˆ๋˜ MemberServiceTest.java์ฒ˜๋Ÿผ)

๋ฐ˜๋Œ€๋กœ ์Šคํ”„๋ง๋„ ๋„์šฐ๊ณ  DB๋„ ์—ฐ๋™ํ•ด ์ง„ํ–‰ํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ''ํ†ตํ•ฉํ…Œ์ŠคํŠธ''๋ผ๊ณ  ํ•œ๋‹ค (์ง€๊ธˆ ํ•œ MemberServiceIntegrationTest.java์ฒ˜๋Ÿผ)

๋ณดํ†ต์€ ์ˆœ์ˆ˜ํ•œ ๋‹จ์œ„ํ…Œ์ŠคํŠธ๊ฐ€ ๋” ์ข‹์€ ํ…Œ์ŠคํŠธ์ผ ํ™•๋ฅ ์ด ๋†’๋‹ค.

  • ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„์–ด ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰.

  • ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ์—†์ด ํ…Œ์ŠคํŠธ.

    (ํ…Œ์ŠคํŠธ ํ•˜๋Š” ๋ฐ์— ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ํ•„์š”ํ•ด์ง€๋Š” ์ƒํ™ฉ ์ž์ฒด๊ฐ€ ์ข‹์ง€ ์•Š์€ ์ƒํ™ฉ์ผ ํ™•๋ฅ ์ด ๋†’๋‹ค)

๋”ฐ๋ผ์„œ ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋ฅผ ๋” ์ž˜ ๋งŒ๋“œ๋Š” ์—ฐ์Šต์„ ํ•˜์ž.

์Šคํ”„๋ง JdbcTemplate

  • ์ˆœ์ˆ˜ JDBC์™€ ๋™์ผํ•œ ํ™˜๊ฒฝ์„ค์ •์„ ๊ฐ–๋Š”๋‹ค.
  • ์Šคํ”„๋งJdbcTemplate ๋ฐ MyBatis๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” JDBC API์—์„œ ๋ณธ ๋ฐ˜๋ณต ์ฝ”๋“œ๋ฅผ ๋Œ€๋ถ€๋ถ„ ์ œ๊ฑฐํ•ด์ค€๋‹ค.
  • ํ•˜์ง€๋งŒ SQL์€ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.
  • ์‹ค๋ฌด์—์„œ๋„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

์Šคํ”„๋งJdbcTemplate์˜ ์ด๋ฆ„์€ ๋””์ž์ธํŒจํ„ด์˜ ํ…œํ”Œ๋ฆฟ๋ฉ”์†Œ๋“œํŒจํ„ด์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ ๋ฐ์„œ ์œ ๋ž˜ํ–ˆ๋‹ค.

1

JdbcTemplateMemberRepository.java

๋ผ๋Š” ์ƒˆ๋กœ์šด ํ…Œ์ŠคํŠธํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์ง„ํ–‰ํ•œ๋‹ค.

(src/main/java/ํ”„๋กœ์ ํŠธ/repository/JdbcTemplateMemberRepository.java)

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
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;

    @Autowired
    public JdbcTemplateMemberRepository(DataSource dataSource) {
        this.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 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 member", memberRowMapper());
    }

    private RowMapper<Member> memberRowMapper() {
        return new RowMapper<Member>() {
            @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;
            }
        };
    }
}

JdbcTemplate

  • import ํ•ด์„œ ์‚ฌ์šฉ (org.springframework.jdbc.core.JdbcTemplate)
  • ์ƒ์„ฑ์ž์—์„œ JdbcTemplate ๋Œ€์‹  DataSource๋ฅผ ์ธ์ ์…˜๋ฐ›๋Š”๋‹ค.
  • JdbcTemplate๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋„ (dataSource)๋ฅผ ์ธ์ ์…˜๋ฐ›๋Š”๋‹ค.
  • ์œ„์˜ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•  ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.

์ฐธ๊ณ  : ์ƒ์„ฑ์ž๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ์„ ๋•Œ๋Š” @Autowired๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค. (์Šคํ”„๋ง์ด ์ž๋™์œผ๋กœ ๋„ฃ์–ด ์ค€๋‹ค) ๋‘ ๊ฐœ ์ด์ƒ์ผ ๋•Œ๋Š” ์•ˆ ๋œ๋‹ค.

List<Member> result = jdbcTemplate.query(sql, ๊ฒฐ๊ณผ, ํŒŒ๋ผ๋ฏธํ„ฐ)

  • query()๋Š” DB์— sql๋ฌธ์„ ๋ณด๋‚ด๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค. ๋ฆฌํ„ดํƒ€์ž…์€ List์ด๋‹ค.
  • ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” sql๋ฌธ์˜ ?์— ๋Œ€์‘ํ•˜๋Š” ๊ฐ’์„ ๋„ฃ๋Š”๋‹ค.

SimpleJdbcInsert

  • JdbcTemplate๋ฅผ ๋„ฃ์–ด์„œ ๋งŒ๋“œ๋Š” ๊ฐ์ฒด.

  • ์ฟผ๋ฆฌ๋ฌธ์„ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ฒŒ ๋„์™€์ค€๋‹ค.

private RowMapper<Member> memberRowMapper()

  • ๊ฐ์ฒด ์ƒ์„ฑ์„ ๋„์™€์ฃผ๋Š” ๋งตํผ?
  • ์—ฌ๊ธฐ์„œ๋Š” query()์˜ ๊ฒฐ๊ณผ๋ฅผ Member๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ ๋Œ๋ ค์ฃผ๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค.

JdbcTemplate ์ž์ฒด๋„ ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์€ ๊ธฐ๋Šฅ๋“ค์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฒ€์ƒ‰ํ•ด ์•Œ์•„๋ณด์ž. (๋งค๋‰ด์–ผ)

2

JdbcTemplateMemberRepository.java ๋ฅผ ์™„์„ฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— SpringConfig.java์—์„œ ๋‹ค์‹œ ๋ฐ”๊ฟ” ๋ผ์›Œ ์‚ฌ์šฉํ•ด ๋ณด์ž.

SpringConfig.java

์•„๋ž˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.

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

3

MemberServiceIntegrationTest.java๋กœ ์ž˜ ๋ฐ”๊ฟ” ๋ผ์›Œ์กŒ๋Š”์ง€ ํ…Œ์ŠคํŠธ ํ•ด ๋ณธ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์ค‘์š”์„ฑ

์‹ค๋ฌด์—์„œ๋„ ๊ทผ๋ฌด์‹œ๊ฐ„์˜ 60-70%๋Š” ํ…Œ์ŠคํŠธ์ฝ”๋“œ ๊ฐœ๋ฐœ์„, 30-40%๋Š” Production Code(์‹ค์ œ ์‚ฌ์šฉํ•  ์ฝ”๋“œ) ๊ฐœ๋ฐœ์„ ํ•œ๋‹ค.

์‹ค๋ฌด์—์„œ๋Š” ์ž‘์€ ๋ฒ„๊ทธ ํ•˜๋‚˜๋„ ์ „์ฒด ์‹œ์Šคํ…œ์— ์น˜๋ช…์ ์ด๊ธฐ ๋•Œ๋ฌธ์— (์ˆ˜์‹ญ์–ต์›์˜ ํ”ผํ•ด),

ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ๊ผผ๊ผผํ•˜๊ฒŒ ์ž˜ ์งœ๋Š” ๊ฒƒ์ด ์—„์ฒญ๋‚˜๊ฒŒ ์ค‘์š”ํ•˜๋‹ค.

JPA

JPA : Java Persistence API์˜ ์•ฝ์ž.

JPA๋Š” '์ž๋ฐ”์˜ ํ‘œ์ค€ ์ธํ„ฐํŽ˜์ด์Šค'์ด๊ณ , ๊ทธ ๊ตฌํ˜„์ฒด๋กœ ๋ณดํ†ต hibernate๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

JPA์˜ ์žฅ์  ๋ฐ ์‚ฌ์šฉ ์ด์œ 

  • JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ๋ณธ์ ์ธ SQL๋ฌธ์„ JAP๊ฐ€ ์ง์ ‘ ๋งŒ๋“ค์–ด ์‹คํ–‰ํ•ด ์ค€๋‹ค.
    • ์Šคํ”„๋ง JdbcTemplate์„ ์‚ฌ์šฉํ•ด ๋ฐ˜๋ณต ์ฝ”๋“œ๋Š” ์ค„์—ˆ์ง€๋งŒ, ์—ฌ์ „ํžˆ SQL๋ฌธ์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค. JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด SQL์„ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
    • ํŠนํžˆ ๊ธฐ๋ณธ CRUD ๊ธฐ๋Šฅ์˜ SQL๋ฌธ์„ ์ง์ ‘ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • JPA ์‚ฌ์šฉ์„ ํ†ตํ•ด SQL ๋ฐ ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ ์„ค๊ณ„์—์„œ ๊ฐ์ฒด ์ค‘์‹ฌ์˜ ์„ค๊ณ„๋กœ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์„ ํฌ๊ฒŒ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค.

ํ˜„์žฌ JPA์™€ MyBatis๋ผ๋Š” API๋ฅผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์•„์ง๊นŒ์ง€ ๊ตญ๋‚ด์—์„œ๋Š” MyBatis๋ฅผ ๋” ์“ฐ์ง€๋งŒ ์ „ ์„ธ๊ณ„์ ์ธ ์ถ”์„ธ๋กœ๋Š” JPA(68%)๋ฅผ ํ›จ์”ฌ ๋” ๋งŽ์ด ์“ด๋‹ค. ๊ตญ๋‚ด ๋„์ž…์ด ๋Šฆ์–ด์ง„ ๊ฒƒ.

์Šคํ”„๋ง ๊ธฐ์ˆ ๋„ ๋งค์šฐ ๊ฑฐ๋Œ€ํ•˜์ง€๋งŒ JPA๋„ ๊ทธ๋งŒํผ ๊ฑฐ๋Œ€ํ•œ ๊ธฐ์ˆ ์ด๋‹ค. ์Šคํ”„๋ง๋งŒํผ ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

์Šคํ”„๋ง๊ณผ JPA๊ฐ€ ๋น„์Šทํ•œ ์‹œ๊ธฐ์— ๋‚˜์™€์„œ ์Šคํ”„๋ง์ด JPA๋ฅผ ์ง€์›ํ•˜๋Š” ๋ถ€๋ถ„์ด ๋งŽ๋‹ค.

1

build.gradle

์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•œ๋‹ค.

...
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    // implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
...

*org.springframework.boot:spring-boot-starter-data-jpa๊ฐ€ Jdbc ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํฌํ•จํ•˜๊ธฐ ๋–„๋ฌธ์— jdbc๋Š” ์ œ๊ฑฐํ•ด๋„ ๋œ๋‹ค.

์ˆ˜์ • ํ›„ gradle์„ ์ƒˆ๋กœ๊ณ ์นจ ํ•œ๋‹ค.

gradle ์ƒˆ๋กœ๊ณ ์นจ

2

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.jpa.show-sql=true : JPA๊ฐ€ ์ƒ์„ฑํ•˜๋Š” sql์„ ์ถœ๋ ฅํ•œ๋‹ค.
  • spring.jpa.hibernate.ddl-auto=none : JPA๋Š” ์›๋ž˜ ๊ฐ์ฒด๋ฅผ ๋ณด๊ณ  table(DB)์„ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด ์ฃผ์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” H2์— ์ด๋ฏธ ๋งŒ๋“ค์–ด ๋†“์•˜๊ณ  ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ฏ€๋กœ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๋ˆ๋‹ค.
    • none๋Œ€์‹  create๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ…Œ์ด๋ธ”์„ ์ง์ ‘ ์ƒ์„ฑํ•ด ์ค€๋‹ค.

3

JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € ์—”ํ‹ฐํ‹ฐ Entity ๋ผ๋Š” ๊ฒƒ์„ ๋งตํ•‘ํ•ด์•ผ ํ•œ๋‹ค.

JPA๋Š” ORM : Object Relational Mapping ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • ๊ฐ์ฒด(Object)์™€ ๊ด€๊ณ„ํ˜•(Relational) ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ”์„ ๋งตํ•‘(Mapping)ํ•˜๋Š” ๊ธฐ์ˆ .

JPA์˜ ๋งตํ•‘ ๊ณผ์ •์€ ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง„๋‹ค.

์ฆ‰ ์ž๋ฐ” ์ฝ”๋“œ์— ์„ ์–ธํ•œ ์–ด๋…ธํ…Œ์ด์…˜๋“ค์„ ๋ณด๊ณ , ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์š”์†Œ์™€ ๋น„๊ตํ•œ๋‹ค.

Member.java

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

  • javax.persistence.Entity ๋ฅผ import
  • @Entity ๋ฅผ ์„ ์–ธํ•˜๋ฉด ํ•ด๋‹น ํด๋ž˜์Šค ๊ฐ์ฒด๋Š” 'JPA๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ'๋ผ๊ณ  ํ‘œํ˜„ํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  PK (Primary Key)๋ฅผ ๋งตํ•‘ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์šฐ๋ฆฌ ์˜ˆ์ œ์—์„œ๋Š” ID์ด๋‹ค.

@Id

  • import javax.persistence.Id

  • ID๊ฐ’ ์œ„์— ์„ ์–ธํ•ด ์ค€๋‹ค.

    @GeneratedValue

  • SQL์—์„œ ์„ ์–ธํ•œ generated by default as identity์ฒ˜๋Ÿผ, DB๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด์ฃผ๋Š” ๊ฐ’์— ์„ ์–ธํ•œ๋‹ค.

    ์ด๋ฅผ Identity ์•„์ด๋ดํ‹ฐํ‹ฐ์ด๋ผ๊ณ  ํ•œ๋‹ค.

  • ID๊ฐ’์ด ์ž๋™ ์ƒ์„ฑ ๊ฐ’์ด๋ฏ€๋กœ @Id์™€ ๋‚˜๋ž€ํžˆ ์„ ์–ธํ•ด ์ค€๋‹ค.

JpaMemberRepository.java

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;
    }

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

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

    @Override
    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();
    }

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

    • JPA๋Š” EntityManager๋กœ ๋™์ž‘ํ•œ๋‹ค. ๋ฐ˜๋“œ์‹œ ์„ ์–ธํ•ด์•ผ ํ•˜๋Š” ์š”์†Œ์ด๋‹ค.
    • build.gradle์—์„œ data-jpa๋ฅผ ์„ค์ •ํ•ด ๋‘๋ฉด, ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ์ž๋™์œผ๋กœ EntityManager๋ผ๋Š” ๊ฒƒ์„ ๋งŒ๋“ค์–ด ์„ค์ •ํ•œ DB์™€์˜ ํ†ต์‹ ์„ ํŽธํ•˜๊ฒŒ ํ•ด ์ค€๋‹ค.
    • ์ƒ์„ฑ์ž์—์„œ ์ด ๋งŒ๋“ค์–ด์ง„ EntityManager๋ฅผ ์ฃผ์ž…๋ฐ›๋Š” ๊ฒƒ์ด๋‹ค.
  • persist(e)

    • ์ €์žฅ. DB์— ํ•ด๋‹น ์š”์†Œ๋ฅผ ์˜์†์ ์œผ๋กœ ์ €์žฅํ•จ.
  • find(์กฐํšŒํ•  ํƒ€์ž…, PK)

    • ์กฐํšŒ. DB ํ…Œ์ด๋ธ”์—์„œ ํ•ด๋‹น ํƒ€์ž…์— PK๋ฅผ ๊ฐ–๋Š” ์š”์†Œ๋ฅผ ์ฐพ์•„ ์˜ด.
  • createQuery("JPQL์ฟผ๋ฆฌ๋ฌธ", ํƒ€์ž…)

    • JPQL์ฟผ๋ฆฌ๋ฌธ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ?
    • findByName(), findAll()์ฒ˜๋Ÿผ PK๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์กฐํšŒํ•  ๋•Œ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด JPQL์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

JPQL

์ผ๋ฐ˜ ์ฟผ๋ฆฌ๋ฌธ์€ ํ…Œ์ด๋ธ”์„ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฌธ์„ ๋ณด๋‚ด์ง€๋งŒ,

JPQL์€ ๊ฐ์ฒด(์ •ํ™•ํžˆ๋Š” ์—”ํ‹ฐํ‹ฐ)๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฌธ์„ ๋ณด๋‚ธ๋‹ค. JPQL๋กœ ๋ณด๋‚ธ ์ฟผ๋ฆฌ๋ฌธ์ด SQL๋กœ ๋ฒˆ์—ญ๋˜์–ด ๋ณด๋‚ด์ง„๋‹ค.

select m from Member m : Member(m) ์ž์ฒด๋ฅผ ์กฐํšŒํ•˜๊ณ  ์žˆ๋‹ค.

๋‹ค์Œ ์‹œ๊ฐ„์— ๋ฐฐ์šธ ''์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA''๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด JPQL๋„ ์ง์ ‘ ์งœ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

4

โ— JPA๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜์ 

: ๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ๋ณ€๊ฒฝํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ํ•ด๋‹น ํด๋ž˜์Šค/๋ฉ”์„œ๋“œ์— @Transsactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค.

JPA๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์ €์žฅ/๋ณ€๊ฒฝ์ด Transaction ์•ˆ์—์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค.

MemberService.java

  • ํด๋ž˜์Šค ์„ ์–ธ๋ถ€ ์œ„์— @Transactional์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

5

์ด์ œ ์ƒ์„ฑํ•œ JpaMemberRepository.java๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก SpringConfig.java๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.

SpringConfig.java

package hello.hellospring;

import hello.hellospring.repository.JpaMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;

@Configuration
public class SpringConfig {

    private EntityManager em;

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

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

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

private EntityManager em

  • ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์„ ์–ธ ํ›„ ์ƒ์„ฑ์ž์—์„œ ์ฃผ์ž…ํ•ด ์ค€๋‹ค

  • JpaMemberRepository(EntityManager em) ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์‚ฌ์šฉํ•œ๋‹ค

6

์—ฐ๊ฒฐํ–ˆ์œผ๋‹ˆ MemberServiceIntegrationTest.java์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด ๋ณธ๋‹ค.

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ๋ฉ”์‹œ์ง€ ๋กœ๊ทธ๋ฅผ ์ž์„ธํžˆ ๋ณด๋ฉด, ์Šคํ”„๋ง ์ ‘์† ํ›„ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๊ฐ€ ์žˆ๋‹ค.

Hibernate: select member0_.id as id1_0_, member0_.name as name2_0_ from member member0_ where member0_.name=?

Hibernate: insert into member (id, name) values (null, ?)

  • JPA๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๊ณ , ๊ตฌํ˜„์ฒด๋กœ Hibernate๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ฆ‰ ๊ฒฐ๊ตญ DB์— ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ์€ SQL๋ฌธ์ธ๋ฐ, ์ง์ ‘ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๊ฒŒ ํŽธ๋ฆฌ์„ฑ์„ ๋”ํ•œ ๊ฒƒ.

+

์š”์ฆ˜ JPA๋Š” ํฐ ์Šคํƒ€ํŠธ์—… ๋“ฑ์—์„œ๋„ ์‚ฌ์šฉํ•˜๊ณ  ํ•ด์™ธ์—์„œ๋Š” ์€ํ–‰์—์„œ๋„ ์“ธ ๋งŒํผ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค.

JPA๋Š” ์Šคํ”„๋ง๋งŒํผ ๊ณต๋ถ€ํ•  ๊ฒŒ ๋งŽ๋‹ค. ์‹ค๋ฌด์—์„œ ์‚ฌ์šฉํ•  ์ •๋„๊ฐ€ ๋˜๋ ค๋ฉด ๊นŠ์ด ์žˆ๊ฒŒ ๊ณต๋ถ€ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA

์Šคํ”„๋ง ๋ถ€ํŠธ์™€ JPA๋งŒ ์‚ฌ์šฉํ•ด๋„ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ๋งค์šฐ ๋†’์•„์ง€๊ณ , ๊ฐœ๋ฐœํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋„ ํฌ๊ฒŒ ์ค„์–ด๋“ ๋‹ค.

์—ฌ๊ธฐ์— ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊นŒ์ง€ ์‚ฌ์šฉํ•˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๋งŒ์œผ๋กœ ๊ฐœ๋ฐœ์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํŠนํžˆ, ๊ธฐ๋ณธ CRUD๊ธฐ๋Šฅ์„ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ์ œ๊ณตํ•œ๋‹ค!

์Šคํ”„๋ง ๋ถ€ํŠธ & JPA ๋ผ๋Š” ๊ธฐ๋ฐ˜ ์œ„์—, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋”ํ•˜๋ฉด ๊ฐœ๋ฐœ์ด ์ฆ๊ฑฐ์›Œ์ง„๋‹ค.

๋‹จ์ˆœ ๋ฐ˜๋ณต ์ฝ”๋“œ๋ฅผ ์ค„์—ฌ ์ฃผ์–ด, ๊ฐœ๋ฐœ์ž๋Š” ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฐœ๋ฐœ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹ค๋ฌด์—์„œ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ํ•„์ˆ˜์ด๋‹ค.

โ—

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์“ฐ๋„๋ก ๋„์™€์ฃผ๋Š” ๋„๊ตฌ์ผ ๋ฟ์ด๋‹ค.

๋”ฐ๋ผ์„œ ๋ฐ˜๋“œ์‹œ! JPA๋ฅผ ๋จผ์ € ์—ฐ์Šตํ•œ ๋‹ค์Œ์— ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ํ•™์Šตํ•ด์•ผ ํ•œ๋‹ค.

JPA๋ฅผ ๋ชจ๋ฅด๋Š” ์ƒํƒœ์—์„œ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ์“ฐ๋Š” ๊ฒƒ์€ ์œ„ํ—˜ํ•œ ํ–‰์œ„์ด๋‹ค. ์‹ค์ œ ์šด์˜์„ ํ•˜๋ฉฐ ๋ถ€๋”ชํžˆ๋Š” ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.

1

ํ™˜๊ฒฝ ์„ค์ •์€ JPA์˜ ์„ค์ •์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

src/main/java/ํ”„๋กœ์ ํŠธ/repository์— "์ธํ„ฐํŽ˜์ด์Šค"๋ฅผ ๋งŒ๋“ ๋‹ค.

(์ง€๊ธˆ๊นŒ์ง€์™€ ๋‹ฌ๋ฆฌ '์ธํ„ฐํŽ˜์ด์Šค'๋ฅผ ๋งŒ๋“ ๋‹ค๋Š” ๊ฒƒ์— ์œ ์˜ํ•œ๋‹ค)

SpringDataJpaMemberRepository.java

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
    @Override
    Optional<Member> findByName(String name);
}

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository

  • ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฐ›์„ ๋•Œ๋Š” implements๊ฐ€ ์•„๋‹Œ extends๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค!
    • JpaRepository์™€ MemberRepository๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฐ›์•„ ์˜ค๋ฏ€๋กœ extends๋ฅผ ์‚ฌ์šฉ.
    • โœ…์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋‹ค์ค‘์ƒ์†์ด ๊ฐ€๋Šฅํ•˜๋‹ค!!
  • JpaRepository<Member, Long>
    • <ํ‚ค, ๊ฐ’>์ธ๋ฐ, ํ‚ค๋Š” Member, ๊ฐ’์€ Entity์˜ ์‹๋ณ„์ž์ธ PK(id)์˜ ํƒ€์ž…์ธ Long์„ ๋„ฃ๋Š”๋‹ค.

2

โ— ์ค‘์š” ์›๋ฆฌ

: JpaRepository๋ฅผ ์ƒ์†๋ฐ›์€ ์ธํ„ฐํŽ˜์ด์Šค๋Š”, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ๊ทธ ๊ตฌํ˜„์ฒด๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค๊ณ  ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋กํ•ด ์ค€๋‹ค. ์ฆ‰, ๋‚ด๊ฐ€ SpringDataJpaMemberRepository๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋กํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ทธ ๊ตฌํ˜„์ฒด๋ฅผ ๊ฐ€์ ธ๋‹ค ์“ฐ๋ฉด ๋œ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

SpringConfig.java

package hello.hellospring;

import hello.hellospring.repository.JpaMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {

    private final MemberRepository memberRepository;

    @Autowired
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository);
    }

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

โ“ ์ƒ์„ฑ์ž์— ์ฃผ์ž…๋˜๋Š” MemberRepository๋Š” ๋ฌด์—‡์ธ๊ฐ€?

โ— ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด ๋‘” ์ธํ„ฐํŽ˜์ด์Šค SpringDataJpaMemberRepository์˜ ๊ตฌํ˜„์ฒด์ด๋‹ค.

๊ฒŒ๋‹ค๊ฐ€ MemberRepository๋ฅผ ๋‹ค์ค‘์ƒ์†๋ฐ›์•˜๊ธฐ ๋•Œ๋ฌธ์— MemberRepository๋กœ ์ฃผ์ž…๋  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

3

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ MemberServiceIntegrationTest.java๋กœ ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธ ํ•ด ๋ณธ๋‹ค.

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA์˜ ์ œ๊ณต ๊ธฐ๋Šฅ

  • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•œ ๊ธฐ๋ณธ์ ์ธ CRUD
    • ์šฐ๋ฆฌ๊ฐ€ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์ƒ์„ฑ, ์กฐํšŒ, ์ˆ˜์ •, ์‚ญ์ œ ๋ฉ”์„œ๋“œ๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.
  • findByName() , findByEmail() ์ฒ˜๋Ÿผ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ๋งŒ์œผ๋กœ ์กฐํšŒ ๊ธฐ๋Šฅ ์ œ๊ณต
  • ํŽ˜์ด์ง• ๊ธฐ๋Šฅ ์ž๋™ ์ œ๊ณต

๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ์œผ๋กœ ์กฐํšŒ ๊ธฐ๋Šฅ?

๊ฐ ๋น„์ฆˆ๋‹ˆ์Šค๋งˆ๋‹ค ๋‹ฌ๋ผ์ง€๋Š” ๋‚ด์šฉ์€ ๊ณตํ†ต์ ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์—, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ๋ฉ”์„œ๋“œ๋กœ ์ œ๊ณตํ•  ์ˆ˜ ์—†๋‹ค.

์ด๋Ÿฐ ๋ถ€๋ถ„์€ SpringDataJpaMemberRepository ์ธํ„ฐํŽ˜์ด์Šค์ฒ˜๋Ÿผ ์ง์ ‘ ๋งŒ๋“ค์–ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

(์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฌธ ๊ฐœ๋ฐœ์„ ๋๋‚ผ ์ˆ˜ ์žˆ๋‹ค)

์˜ˆ๋ฅผ ๋“ค์–ด , ์•„๊นŒ ๋งŒ๋“ค์—ˆ๋˜ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์•„๋ž˜ ๋ถ€๋ถ„์„ ํ™•์ธํ•˜์ž๋ฉด,

@Override
    Optional<Member> findByName(String name);

๊ทœ์น™ : findBy***()ํ˜•์‹์˜ ๋ฉ”์„œ๋“œ๋Š” ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ด ์ค€๋‹ค.

  • select m from Member m where m.*** = ?

  • ์šฐ๋ฆฌ๋Š” findByName()์ด๋ฏ€๋กœ select m from Member m where m.name = ?์ด๋ผ๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋  ๊ฒƒ์ด๋‹ค.

์‘์šฉ : findByNameAndId(String name, Long id) ์ด๋Ÿฐ ์‹์œผ๋กœ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

+

์ฐธ๊ณ : ์‹ค๋ฌด์—์„œ๋Š” JPA์™€ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๋ณต์žกํ•œ ๋™์  ์ฟผ๋ฆฌ๋Š” Querydsl์ด๋ผ๋Š”
๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. Querydsl์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ๋„ ์ž๋ฐ” ์ฝ”๋“œ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ณ , ๋™์ 
์ฟผ๋ฆฌ๋„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์กฐํ•ฉ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ค์šด ์ฟผ๋ฆฌ๋Š” JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ
์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์•ž์„œ ํ•™์Šตํ•œ ์Šคํ”„๋ง JdbcTemplate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.