๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Spring

์Šคํ”„๋ง JPA, ํŽ˜์ด์ง•, ์—ฐ๊ด€๊ด€๊ณ„

์Šคํ”„๋ง JPA, ํŽ˜์ด์ง•, ์—ฐ๊ด€๊ด€๊ณ„

์ŠคํŒŒ๋ฅดํƒ€ ์ฝ”๋”ฉ ํด๋Ÿฝ์˜ Spring ์‹ฌํ™”๋ฐ˜ 4์ฃผ ์ฐจ ๋‚ด์šฉ์ธ ์Šคํ”„๋ง JPA, ํŽ˜์ด์ง•, ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์ •๋ฆฌํ•œ๋‹ค.

ํ•ต์‹ฌ ๋‚ด์šฉ

  • Spring Data JPA
  • ํŽ˜์ด์ง•
  • JPA์˜ ์—ฐ๊ด€ ๊ด€๊ณ„

Spring Data JPA

  • ORM : ORM์€ Object-Relation Mapping์ด๋ฉฐ ๋ง ๊ทธ๋Œ€๋กœ ๊ฐ์ฒด(์—ฌ๊ธฐ์„  ์ž๋ฐ”)์™€ DB(H2, MySQL)๋ฅผ ์ด์–ด์ฃผ๋Š” ๊ธฐ์ˆ ์ด๋‹ค.
    • SQL์˜ ์ž‘์„ฑ ์ค‘ ์‹ค์ˆ˜ํ•˜๊ธฐ๋„ ์‰ฝ๊ณ  ๊ฐœ๋ฐœ์ž๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฐœ๋ฐœ๋ณด๋‹ค SQL ์ž‘์„ฑ์— ๋” ๋งŽ์€ ๋…ธ๋ ฅ์„ ๋“ค์–ด์•ผ ํ–ˆ์Œ
    • ๊ฐ์ฒด์ง€ํ–ฅ๊ณผ ๊ด€๊ณ„ํ˜• DB๋Š” ์‚ฌ์šฉ ๋ชฉ์  ๋ฐ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์ด ์• ์ดˆ์— ๋‹ค๋ฆ„
    • ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ORM์ด ํ•„์š”
  • JPA : JPA๋Š” Java Persistience API๋กœ์จ ์ž๋ฐ” ORM ๊ธฐ์ˆ ์— ๋Œ€ํ•œ ํ‘œ์ค€ ๋ช…์„ธ์ด๋‹ค.
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ : JPA๋Š” ํ‘œ์ค€ ๋ช…์„ธ์ด๊ณ  ์ด๋ฅผ ์‹ค์ œ ๊ตฌํ˜„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ•„์š”ํ•œ๋ฐ, ์ด ์ค‘ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์‚ฌ์‹ค์ƒ ํ‘œ์ค€์ด๋‹ค.
  • Spring Data JPA : Repository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ ๊ตฌํ˜„์€ ์Šคํ”„๋ง์ด ๋Œ€์‹  ํ•ด์ฃผ๋Š”, JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋งคํ•‘ํ•œ ๊ฒƒ
    • Spring Data JPA ์˜ˆ์‹œ ์ฝ”๋“œ
      @Entity
      public class Product extends Timestamped {
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Id
        private Long id;
        private Long userId;
        private String title;
        private String image;
        private String link;
        private int lprice;
        private int myprice;
      }
      public interface ProductRepository extends JpaRepository<Product, Long> {
      }

ํŽ˜์ด์ง•

  • ํ•œ ๋ฒˆ์— ์ˆ˜๋ฐฑ ~ ์ˆ˜์–ต ๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ ๋ฆฌ๋ฉด ์„œ๋ฒ„๋‚˜ ํด๋ผ์ด์–ธํŠธ๋‚˜ ๊ณผ๋ถ€ํ™” -> ๋‚˜๋ˆ ์„œ ๋ณด๋‚ด์•ผ๋Œ
  • ํŽ˜์ด์ง€๋„ค์ด์…˜(๊ตฌ๊ธ€ ๊ฒ€์ƒ‰), ์ธํ”ผ๋‹ˆํŠธ ์Šคํฌ๋กค(์œ ํˆฌ๋ธŒ) ๋“ฑ์ด ์žˆ์Œ

์„ค๊ณ„ (Server์—์„œ)

  • number : ์กฐํšŒํ•  ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ (0๋ถ€ํ„ฐ ์‹œ์ž‘)
  • size : ํ•œ ํŽ˜์ด์ง€์— ๋ณด์—ฌ์ค„ ์ž๋ฃŒ์˜ ๊ฐœ์ˆ˜
  • numberOfElements: ์‹ค์ œ ์กฐํšŒ๋œ ์ž๋ฃŒ์˜ ๊ฐœ์ˆ˜
  • totalElement : ์ „์ฒด ์ž๋ฃŒ ๊ฐœ์ˆ˜
  • totalPages : ์ „์ฒด ํŽ˜์ด์ง€ ์ˆ˜
  • first : ์ฒซ ํŽ˜์ด์ง€์ธ์ง€? (boolean)
  • last : ๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€์ธ์ง€? (boolean)

๊ตฌํ˜„

Controller

@GetMapping("/api/products")
    public Page<Product> getProducts(
            @RequestParam("page") int page,
            @RequestParam("size") int size,
            @RequestParam("sortBy") String sortBy,
            @RequestParam("isAsc") boolean isAsc,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        Long userId = userDetails.getUser().getId();
        page = page - 1;
        return productService.getProducts(userId, page , size, sortBy, isAsc);
    }

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์›ํ•˜๋Š” ํŽ˜์ด์ง€ ์ˆ˜, ํŽ˜์ด์ง€๋‹น ์‚ฌ์ด์ฆˆ, ๋ฐฐ์—ด์— ๋Œ€ํ•œ ์˜ต์…˜์„ ๋ฐ›๋Š”๋‹ค.

Service

public Page<Product> getProducts(Long userId, int page, int size, String sortBy, boolean isAsc) {
        Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);

        return productRepository.findAllByUserId(userId, pageable);
    }

์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ฐ›์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ Sort๋กœ ํ†ตํ•ด Pageable ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.

Repository

public interface ProductRepository extends JpaRepository<Product, Long> {
    Page<Product> findAllByUserId(Long userId, Pageable pageable);
}

Page<Product>๋ฅผ ๋ฐ˜ํ™˜ํ˜•์œผ๋กœ, Pageable ๊ฐ์ฒด๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด๊ฐ€๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์—์„œ ๋งŒ๋“ค์–ด์ค€๋‹ค.

JPA์˜ ์—ฐ๊ด€๊ด€๊ณ„

  • JPA๋Š” Entity ํด๋ž˜์Šค์˜ ํ•„๋“œ ์œ„์— ์—ฐ๊ด€๊ด€๊ณ„ ์–ด๋…ธํ…Œ์ด์…˜(@)์„ ์„ค์ •ํ•ด ์ฃผ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ํ˜•์„ฑ๋œ๋‹ค.
  • ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ๊ฐ์ฒด๋ผ๋ฆฌ์˜ ๊ด€๊ณ„๋ฅผ ๋งบ์–ด์ฃผ๋ฉด, DB์—์„œ๋Š” ์™ธ๋ž˜ํ‚ค๋ฅผ ํ†ตํ•œ ๊ด€๊ณ„๋ฅผ ํ˜•์„ฑํ•ด์ค€๋‹ค.

๊ตฌํ˜„์— ์“ฐ์ด๋Š” ์–ด๋…ธํ…Œ์ด์…˜

  • @OneToMany : ์ผ๋Œ€๋‹ค ๊ด€๊ณ„
    • ex) ํšŒ์› 1๋ช…์ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํด๋”๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Œ
  • @OneToOne : ์ผ๋Œ€์ผ ๊ด€๊ณ„
    • ex) ๋ฐฐ๋‹ฌ ์ฃผ๋ฌธ 1๊ฐœ ์ฃผ๋ฌธ ์‹œ, ์ฟ ํฐ 1๊ฐœ๋งŒ ํ• ์ธ ์ ์šฉ ๊ฐ€๋Šฅ
  • @ManyToOne : ๋‹ค๋Œ€์ผ ๊ด€๊ณ„
    • ex) ํด๋” ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ํšŒ์› 1๋ช…์ด ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Œ
  • @ManyToMany : ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„
    • ex) ๊ณ ๊ฐ์€ ์Œ์‹์  ์—ฌ๋Ÿฌ๊ฐœ ์ฐœ ๊ฐ€๋Šฅ, ์Œ์‹์ ์€ ๊ณ ๊ฐ ์—ฌ๋Ÿฌ๋ช…์—๊ฒŒ ์ฐœ ๊ฐ€๋Šฅ

ManyToMany

๊ณ ๊ฐ๊ณผ ์Œ์‹์ ๊ณผ ๊ฐ™์€ ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„์—์„œ๋Š” ๊ณ ๊ฐ์˜ ID, ์Œ์‹์ ์˜ ID์™€ ๋‹ค๋ฅธ ๋ถ€๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ๋‹ด์€ ํ…Œ์ด๋ธ”์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์ค˜์•ผํ•œ๋‹ค.

@ManyToMany ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์ƒ์„ฑ ์‹œ, JPA๊ฐ€ ์ƒ์„ฑํ•ด์ค€๋‹ค.

์—ฐ๊ด€๊ด€๊ณ„ ์นผ๋Ÿผ ์„ค์ •

@ManyToOne
@JoinColumn(name = "USER_ID", nullable = false)
private User user;
  • name: ์™ธ๋ž˜ํ‚ค ๋ช…
  • nullable: ์™ธ๋ž˜ํ‚ค null ํ—ˆ์šฉ ์—ฌ๋ถ€