๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring Boot/์Šคํ”„๋ง ๋ถ€ํŠธ์™€ AWS๋กœ ํ˜ผ์ž ๊ตฌํ˜„ํ•˜๋Š” ์›น ์„œ๋น„์Šค

3์žฅ: JPA๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‹ค๋ฃจ๊ธฐ (1)

by oliviarla 2022. 6. 15.

Table of Contents

     

    JPA

    JPA์™€ Spring Data JPA ๊ฐœ๋…์„ ์ดํ•ดํ•œ๋‹ค.

    ORM vs. SQL Mapper

    - ORM์€ ๊ฐ์ฒด๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ, ๋Œ€ํ‘œ์ ์ธ ๊ธฐ์ˆ ๋กœ๋Š” JPA๊ฐ€ ์žˆ์Œ

    - SQL Mapper๋Š” ์ฟผ๋ฆฌ๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ, ๋Œ€ํ‘œ์ ์ธ ๊ธฐ์ˆ ๋กœ๋Š” MyBatis๊ฐ€ ์žˆ์Œ

    ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ๋ถˆ์ผ์น˜

    ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ์ง€ ์ดˆ์ ์„ ๋งž์ถ”์ง€๋งŒ

    ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ธฐ๋Šฅ๊ณผ ์†์„ฑ์„ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ค‘์‹ฌ

     

    JPA๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ๋ถˆ์ผ์น˜๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ์ฒด์ง€ํ–ฅ์ ์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•œ ์ฝ”๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ SQL์„ ์ƒ์„ฑํ•ด ์‹คํ–‰ํ•ด์ฃผ๋ฏ€๋กœ SQL์— ์ข…์†์ ์œผ๋กœ ๊ฐœ๋ฐœํ•˜์ง€ ์•Š์•„๋„ ๋จ

    Spring Data JPA

    - JPA๋Š” ์ธํ„ฐํŽ˜์ด์Šค, ์ž๋ฐ” ํ‘œ์ค€ ๋ช…์„ธ์„œ์ด๋ฏ€๋กœ ๊ตฌํ˜„์ฒด๊ฐ€ ํ•„์š”ํ•จ. ๊ตฌํ˜„์ฒด๋กœ๋Š” Hibernates, Eclipse Link ๋“ฑ์ด ์žˆ์Œ

    - Spring Data JPA๋Š” ์ด ๊ตฌํ˜„์ฒด๋“ค์„ ์ข€ ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ ์ž ์ถ”์ƒํ™”ํ•ด๋†“์€ ๊ฒƒ

     

    - ์žฅ์ 

    •  ๊ตฌํ˜„์ฒด ๊ต์ฒด์˜ ์šฉ์ด์„ฑ: ์ƒˆ๋กœ์šด ๊ตฌํ˜„์ฒด๊ฐ€ ๋– ์˜ค๋ฅผ ๋•Œ Spring Data JPA ๋‚ด๋ถ€์—์„œ ๊ตฌํ˜„์ฒด ๋งคํ•‘์„ ์ง€์›ํ•ด์ฃผ๋ฏ€๋กœ ๊ต์ฒด๊ฐ€ ์šฉ์ด
    • ์ €์žฅ์†Œ ๊ต์ฒด์˜ ์šฉ์ด์„ฑ: ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์™ธ์— MongoDB์™€ ๊ฐ™์€ Spring Data ํ•˜์œ„ ๊ตฌํ˜„์ฒด๋“ค์˜ ๋‹ค๋ฅธ ์ €์žฅ์†Œ๋กœ ์‰ฝ๊ฒŒ ๊ต์ฒด ๊ฐ€๋Šฅ
    • CRUD ์ฟผ๋ฆฌ ์ž‘์„ฑํ•  ํ•„์š” ์—†์Œ
    • ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„ ํ‘œํ˜„, 1:N ๊ด€๊ณ„ ํ‘œํ˜„ ์šฉ์ด
    • ์ƒํƒœ์™€ ํ–‰์œ„๋ฅผ ํ•œ๊ณณ์—์„œ ๊ด€๋ฆฌํ•˜์—ฌ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์šฉ์ด

    Spring Data JPA ์ ์šฉํ•ด๋ณด๊ธฐ

    ์˜์กด์„ฑ ์ถ”๊ฐ€ ํ›„ ์—”ํ‹ฐํ‹ฐ์™€ Repository ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ํ…Œ์ŠคํŠธํ•˜์—ฌ JPA ์ ์šฉํ•˜๋Š” ๊ณผ์ •์„ ์‚ดํŽด๋ณธ๋‹ค.

    1. build.gradle์— ์˜์กด์„ฑ ์ถ”๊ฐ€

    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'com.h2database:h2'

     

    ๋„๋ฉ”์ธ ํŒจํ‚ค์ง€: ๊ฒŒ์‹œ๊ธ€, ๋Œ“๊ธ€, ๊ฒฐ์ œ ๋“ฑ ์†Œํ”„ํŠธ์›จ์–ด์— ๋Œ€ํ•œ ์š”๊ตฌ์‚ฌํ•ญ ํ˜น์€ ๋ฌธ์ œ ์˜์—ญ

    2. ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค ์ƒ์„ฑ

    package com.oliviarla.springboot.domain.posts;
    
    @Getter
    @NoArgsConstructor
    @Entity
    public class Posts extends BaseTimeEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private long id;
    
        @Column(length = 500, nullable = false)
        private String title;
    
        @Column(columnDefinition = "TEXT", nullable = false)
        private String content;
    
        private String author;
    
        @Builder
        public Posts(String title, String content, String author){
            this.title=title;
            this.content=content;
            this.author=author;
        }
    
        public void update(String title, String content){
            this.title=title;
            this.content=content;
        }
    }
    @Entity
    - ํ…Œ์ด๋ธ”๊ณผ ๋งํฌ๋  ํด๋ž˜์Šค์ž„์„ ๋ช…์‹œ
    - ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ํด๋ž˜์Šค์˜ CamelCase ์ด๋ฆ„์„ under_score ๋„ค์ด๋ฐ์œผ๋กœ ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋งค์นญ
    @Id: ํ•ด๋‹น ํ…Œ์ด๋ธ”์˜ PK ํ•„๋“œ
    @GeneratedValue: PK์˜ ์ƒ์„ฑ ๊ทœ์น™์„ ๋ช…์‹œ

    @Column: ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ์˜ ๊ธฐ๋ณธ๊ฐ’ ์™ธ์— ์ถ”๊ฐ€๋กœ ๋ณ€๊ฒฝ ํ•„์š”ํ•œ ์˜ต์…˜ ์žˆ์„๋•Œ๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ๋จ

     

    @Builder
    - ํ•ด๋‹น ํด๋ž˜์Šค์˜ ๋นŒ๋” ํŒจํ„ด ํด๋ž˜์Šค ์ƒ์„ฑ
    - ์ƒ์„ฑ์ž ์ƒ๋‹จ์— ์„ ์–ธ ์‹œ ์ƒ์„ฑ์ž์— ํฌํ•จ๋œ ํ•„๋“œ๋งŒ ๋นŒ๋”์— ํฌํ•จ
    - ๋นŒ๋”๋ฅผ ํ†ตํ•ด ์ตœ์ข… ๊ฐ’์„ ์ฑ„์šด ํ›„ DB์— ๊ฐ’์„ insertํ•˜๊ฒŒ ๋จ

    ๐Ÿ’ก ๋นŒ๋” vs ์ƒ์„ฑ์ž

    ๋นŒ๋”๋Š” ์ง€๊ธˆ ์ฑ„์›Œ์•ผ ํ•  ํ•„๋“œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด ์ƒ์„ฑ์ž๋ณด๋‹ค ๋ช…ํ™•ํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค
    ex) ๋นŒ๋”๋Š” a(a).b(b).build(); ๋กœ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ˜๋ฉด ์ƒ์„ฑ์ž๋Š” new (a,b);๋กœ ํ‘œํ˜„๋จ

     

    3. Repository ์ธํ„ฐํŽ˜์ด์Šค

    - MyBatis ๋“ฑ์—์„œ Dao๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” DB Layer ์ ‘๊ทผ์ž๋ฅผ JPA์—์„œ๋Š” Repository๋ผ๊ณ  ๋ถ€๋ฅด๋ฉฐ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ƒ์„ฑํ•จ

    - Entity ํด๋ž˜์Šค์™€ ๊ธฐ๋ณธ Entity Repository๋Š” ๊ฐ™์€ ๋„๋ฉ”์ธ ํŒจํ‚ค์ง€์—์„œ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•ด์•ผ ํ•จ

    - JpaRepository<Entityํด๋ž˜์Šค, PKํƒ€์ž…> ์„ ์ƒ์†ํ•˜๋ฉด ๊ธฐ๋ณธ์ ์ธ CRUD ๋ฉ”์†Œ๋“œ๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋จ

     

    4. Repository ํ…Œ์ŠคํŠธ

    package com.oliviarla.springboot.web.domain.posts;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class PostRepositoryTest {
    
        @Autowired
        PostsRepository postsRepository;
    
        @After //ํ…Œ์ŠคํŠธ์šฉ DB์ธ H2์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ์‚ญ์ œํ•ด ํ…Œ์ŠคํŠธ ๊ฐ„ ๋ฐ์ดํ„ฐ ์นจ๋ฒ”์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
        public void cleanup(){
            postsRepository.deleteAll();
        }
    
        @Test
        public void load_post(){
            //given
            String title="ํ…Œ์ŠคํŠธ ๊ฒŒ์‹œ๊ธ€";
            String content="ํ…Œ์ŠคํŠธ ๋ณธ๋ฌธ";
    		
            // insert/update ์ฟผ๋ฆฌ ์‹คํ–‰
            postsRepository.save(Posts.builder()
                    .title(title)
                    .content(content)
                    .author("me")
                    .build());
    
            //when
            List<Posts> postsList=postsRepository.findAll(); //ํ…Œ์ด๋ธ”์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์กฐํšŒ
    
            //then
            Posts posts = postsList.get(0);
            assertThat(posts.getTitle()).isEqualTo(title);
            assertThat(posts.getContent()).isEqualTo(content);
        }
    }

     

    ๐Ÿ’ก ์ฝ˜์†”์—์„œ ์‹ค์ œ ์‹คํ–‰๋œ ์ฟผ๋ฆฌ๋ฅผ MySQL ๋ฒ„์ ผ์œผ๋กœ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•

    application.properties์— ๋‹ค์Œ ์ฝ”๋“œ ์ถ”๊ฐ€

    spring.jpa.show_sql = true
    spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

     

    ๋Œ“๊ธ€