MapleStory Finger Point

๐ŸŸค JAVA/๐ŸŸค Spring

[SpringBoot] JPA

HYEJU01 2024. 8. 26. 22:05

โ—ˆ JPA ๋ž€?

JPA(Java Persistence API)๋Š” ์ž๋ฐ”(Java) ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ‘œ์ค€ API์ž…๋‹ˆ๋‹ค. JPA๋Š” ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์ด์˜ ๋ถˆ์ผ์น˜๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์„ค๊ณ„๋œ ORM(Object-Relational Mapping) ๊ธฐ์ˆ ์˜ ํ•œ ์ข…๋ฅ˜์ž…๋‹ˆ๋‹ค.

JPA์˜ ์ฃผ์š” ๊ฐœ๋…

  1. ORM(Object-Relational Mapping): ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์ธ ์ž๋ฐ”์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ”์„ ๋งคํ•‘ํ•˜๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. Entity: JPA์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋˜๋Š” ์ž๋ฐ” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์˜ ํ–‰์„ ํ‘œํ˜„ํ•˜๋ฉฐ, ๊ฐ ํ•„๋“œ๋Š” ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ์— ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค.
  3. Persistence Context: ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํ™˜๊ฒฝ์ž…๋‹ˆ๋‹ค. Persistence Context๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†์„ฑ ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๊ณ , ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๋™๊ธฐํ™”ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  4. EntityManager: ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œ, ์กฐํšŒ ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” JPA์˜ ํ•ต์‹ฌ ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ์ œ์–ดํ•˜๊ณ , Persistence Context๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  5. JPQL(Java Persistence Query Language): JPA์—์„œ ์ œ๊ณตํ•˜๋Š” SQL๊ณผ ์œ ์‚ฌํ•œ ๊ฐ์ฒด ์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด์ž…๋‹ˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, SQL์— ๋น„ํ•ด ๋” ๊ฐ์ฒด ์ง€ํ–ฅ์ ์ด๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋…๋ฆฝ์ ์ž…๋‹ˆ๋‹ค.

JPA์˜ ์žฅ์ 

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋…๋ฆฝ์„ฑ: JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ข…์†์ ์ด์ง€ ์•Š์€ ์ฝ”๋“œ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜์—ฌ, ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ข…์†๋˜์ง€ ์•Š๊ณ  ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์˜ ์ „ํ™˜์ด ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.
  • ์ƒ์‚ฐ์„ฑ ํ–ฅ์ƒ: SQL์„ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ , ๊ฐ์ฒด ์ง€ํ–ฅ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.
  • ์œ ์ง€๋ณด์ˆ˜ ์šฉ์ด์„ฑ: ๊ฐ์ฒด ์ง€ํ–ฅ์ ์ธ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๊ฐœ์„ ๋ฉ๋‹ˆ๋‹ค.
  • ์„ฑ๋Šฅ ์ตœ์ ํ™”: 1์ฐจ ์บ์‹œ, ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading) ๋“ฑ ๋‹ค์–‘ํ•œ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

JPA์™€ ๊ด€๋ จ๋œ ๊ธฐ์ˆ 

JPA๋Š” ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜์˜ ํ‘œ์ค€ API๋กœ, ์ด๋ฅผ ๊ตฌํ˜„ํ•œ ์—ฌ๋Ÿฌ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ๊ตฌํ˜„์ฒด๋กœ๋Š” Hibernate, EclipseLink, DataNucleus ๋“ฑ์ด ์žˆ์œผ๋ฉฐ, Spring Data JPA๋Š” ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์™€ JPA๋ฅผ ํ†ตํ•ฉํ•ด ๋ณด๋‹ค ๊ฐ„ํŽธํ•˜๊ฒŒ JPA๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

JPA๋Š” ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๋‹จ์ˆœํ™”ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋…๋ฆฝ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ORM ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด ์ง€ํ–ฅ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


 

 

 


 

package com.example.jpa.memo.repository;

import com.example.jpa.entity.MemberMemoDTO;
import com.example.jpa.entity.Memo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

public interface MemoRepository extends JpaRepository<Memo, Long>, //์—”ํ‹ฐํ‹ฐํƒ€์ž…, ID์— ๋Œ€ํ•œ ํƒ€์ž…
                                        MemoCustomRepository, //์ปค์Šคํ…€๋ ˆํฌ์ง€ํ† ๋ฆฌ
                                        QuerydslPredicateExecutor<Memo> { //์ฟผ๋ฆฌDSL์—์„œ ์ œ๊ณต๋˜๋Š” ๋ช‡๋ช‡ ํ•จ์ˆ˜๋“ค์„ ์ œ๊ณตํ•ด์ฃผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค
    
    //JpaRepository๋กœ ๋ถ€ํ„ฐ, ๋ช‡๊ฐœ์˜ ์ถ”์ƒ๋ฉ”์„œ๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์†๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

    //์ฟผ๋ฆฌ๋ฉ”์„œ๋“œ
    Memo findByWriterAndText(String writer, String text);

    List<Memo> findByMnoLessThan(Long mno);

//    SELECT * FROM MEMO WHERE MNO = 11;
    Memo findByMno(Long mno);
//    SELECT * FROM MEMO WHERE MNO BETWEEN 10 AND 20;
    List<Memo> findByMnoBetween(Long start, Long end);
//    SELECT * FROM MEMO WHERE WRITER LIKE '%10%';
    List<Memo> findByWriterLike(String str);
//    SELECT * FROM MEMO WHERE WRITER = 'example1' ORDER BY WRITER DESC;
    List<Memo> findByWriterOrderByWriterDesc(String writer);
//    SELECT * FROM MEMO WHERE MNO IN (10,20,30,40,50);
    List<Memo> findByMnoIn(List<Long> list);
//์ฟผ๋ฆฌ๋ฉ”์„œ๋“œ์˜ ๋งˆ์ง€๋ง‰ ๋งค๊ฐœ๋ณ€์ˆ˜์— Pageable์„ ์ฃผ๋ฉด, ํŽ˜์ด์ง•์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    List<Memo> findByMnoLessThanEqual(Long mno, Pageable pageable);

    //////////////////////////////////////////////////////////////
    //JPQL - SQL๊ณผ ๋น„์Šทํ•˜๋‚˜, ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ sql๋ฌธ์„ ์ž‘์„ฑ
    //select, update, delete๋Š” ์ œ๊ณต๋˜๋Š”๋ฐ, insert๋Š” ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    //1. ํ…Œ์ด๋ธ”๋ช…์ด ์•„๋‹ˆ๋ผ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์‚ฌ์šฉ๋จ
    //2. ์†์„ฑ(ํ•„๋“œ)์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์ „๋ถ€ ๊ตฌ๋ถ„
    //3. ๋ณ„์นญ์€ ํ•„์ˆ˜
    //4. SQLํ‚ค์›Œ๋“œ ๊ตฌ๋ถ„ x
    @Query("select m from Memo m order by m.mno desc")
    List<Memo> getListDesc(); //๋ฉ”์„œ๋“œ๋ช… ์ž์œ 
    
    //JPQLํŒŒ๋ผ๋ฏธํ„ฐ ์ „๋‹ฌ @Param(์ด๋ฆ„), :์ด๋ฆ„
    @Query("select m from Memo m where m.mno > :num order by m.mno desc")
    List<Memo> getListDesc2(@Param("num") Long mno);

    //JPQL select๋ฌธ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ์„ ๋ณ„์ ์œผ๋กœ ๋ฐ›์œผ๋ ค๋ฉด Object[]์‚ฌ์šฉ ํ•ฉ๋‹ˆ๋‹ค.
    @Query("select m.writer, m.text from Memo m where m.mno > :num order by m.mno desc")
    List<Object[]> getListDesc3(@Param("num") Long mno);

    //JPQL์—…๋ฐ์ดํŠธ
    @Transactional //ํŠธ๋žœ์žญ์…˜ ๋ฐ˜์˜
    @Modifying //์—…๋ฐ์ดํŠธ์ž„
    @Query("update Memo m set m.writer = :a where m.mno = :b")
    int updateMemo(@Param("a") String a, @Param("b") Long b);
    
    //JPQL์—…๋ฐ์ดํŠธ - ๊ฐ์ฒดํŒŒ๋ผ๋ฏธํ„ฐ ๋ฅผ ๋„˜๊ธฐ๋Š” ๋ฐฉ๋ฒ• #{๊ฐ์ฒด}
    @Transactional
    @Modifying
    @Query("update Memo m set m.writer = :#{#a.writer}, m.text = :#{#a.text} where m.mno = :#{#a.mno}")
    int updateMemo(@Param("a") Memo memo);

    //delete from memo where mno = 10;
    //JPQL๋”œ๋ฆฌํŠธ๋ฌธ -
    @Transactional
    @Modifying
    @Query("delete from Memo m where m.mno = :a")
    int deleteMemo(@Param("a") Long mno);

    //JPQL ๋งˆ์ง€๋ง‰๋งค๊ฐœ๋ณ€์ˆ˜์— Pageable์„ ์ฃผ๋ฉด, ํŽ˜์ด์ง€์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    //page์ฒ˜๋ฆฌ์—๋Š” countQuery๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค (countQuery๊ตฌ๋ฌธ์€ ์ง์ ‘ ์ž‘์„ฑํ•˜๋Š”๊ฒŒ ๊ฐ€๋Šฅํ•จ)
    @Query(value = "select m from Memo m where m.mno <= :a",
           countQuery = "select count(m) from Memo m where m.mno <= :a")
    Page<Memo> getListJPQL(@Param("a") Long mno, Pageable pageable);
    
//    select mno, writer, text, concat(writer, text) as col, current_timestamp
//    from memo
//    where mno <= 100;
//  ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ฒ˜๋ฆฌํ•˜๋Š” JPQL์œผ๋กœ
    @Query("select m.mno, m.writer, m.text, concat(m.writer, m.text) as col, current_timestamp " +
            "from Memo m where m.mno <= :a")
    Page<Object[]> getListJPQL2(@Param("a") Long mno, Pageable pageable);

    //๋„ค์ดํ‹ฐ๋ธŒ์ฟผ๋ฆฌ - JPQL์ด ๋„ˆ๋ฌด ์–ด๋ ค์šฐ๋ฉด, SQL๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์ œ๊ณตํ•ด์ค๋‹ˆ๋‹ค.
    @Query(value = "select * from memo where mno = ?", nativeQuery = true)
    Memo getNative(Long mno);

    //๊ตฌํ˜„์ฒด์— ๋งŒ๋“œ๋Š” ๊ตฌ๋ฌธ์€ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ด๋ ‡๊ฒŒ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
//    @Query("select m from Memo m inner join m.member x where m.mno >= :a")
//    List<Memo> mtoJoin1(@Param("a") long a);

    @Query(value = "select new com.example.jpa.entity.MemberMemoDTO(x.id, x.name, x.signDate, m.mno, m.writer, m.text) " +
            "from Memo m left join m.member x where m.text like %:text%"
            ,countQuery = "select count(m) from Memo m left join m.member x where m.text like %:text%"
    )
    Page<MemberMemoDTO> joinPage(@Param("text") String text, Pageable pageable);









}

 

 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>


    <style>
        table {
            width: 100%;
            border-collapse: collapse;
        }

        th, td {
            padding: 8px;
            text-align: left;
            border: 1px solid #ddd;
        }

        th {
            background-color: #f2f2f2;
        }

        tbody tr:nth-child(even) {
            background-color: #f9f9f9;
        }

        tbody tr:hover {
            background-color: #f1f1f1;
        }

        .pagination {
            display: flex;
            justify-content: center;
            margin-top: 20px;
            list-style: none;
            padding: 0;
        }

        .pagination li {
            margin: 0 5px;
        }

        .pagination a {
            display: block;
            padding: 8px 16px;
            text-decoration: none;
            color: #007bff;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        .pagination a.active {
            background-color: #007bff;
            color: white;
            border-color: #007bff;
        }

        .pagination a:hover {
            background-color: #ddd;
        }
    </style>


</head>
<body>

    <select name="searchType">
        <option value="mno">๋ฒˆํ˜ธ</option>
        <option value="text">๋‚ด์šฉ</option>
        <option value="writer">์ž‘์„ฑ์ž</option>
        <option value="textWriter">๋‚ด์šฉ + ์ž‘์„ฑ์ž</option>
    </select>
    <input type="text" name="searchName">
    <button type="button" onclick="searchList()">๊ฒ€์ƒ‰</button>

    <table>
        <thead>
        <tr>
            <th>MNO</th>
            <th>Writer</th>
            <th>Text</th>
            <th>Id</th>
            <th>Name</th>
            <th>Sign Date</th>
        </tr>
        </thead>
        <tbody id="tableBody">
        <!-- Rows will be inserted here by JavaScript -->
        </tbody>
    </table>

    <ul class="pagination" id="pagination">
        <!-- Pagination buttons will be inserted here by JavaScript -->
    </ul>


    <script>

        var pagination = document.getElementById("pagination"); //ํŽ˜์ด์ง€๋„ค์ด์…˜ ํƒœ๊ทธ
        var page = 1;
        var amount = 10;
        var start = 0; //์‹œ์ž‘ํŽ˜์ด์ง€๋„ค์ด์…˜๊ฐ’
        var end = 0; //๋ํŽ˜์ด์ง€๋„ค์ด์…˜๊ฐ’

        //๋ฐ์ดํ„ฐ ajax์š”์ฒญ
        function getList() {

            //searchName๊ฐ’
            var searchName = document.querySelector("input[name=searchName]").value;

            var url = "/getList?page=" + page + "&amount=" + amount + "&searchName=" + searchName;
            fetch(url)
            .then( function(response) {
                return response.json()
            })
            .then( function(data) {
                createBody(data.pageData) //๋ฐ”๋”” ์ƒ์„ฑํ•จ์ˆ˜ ํ˜ธ์ถœ
                createPage(data); //ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ƒ์„ฑํ•จ์ˆ˜
            })
        } //end

        //๋‚ด์šฉ ์ƒ์„ฑ ํ•จ์ˆ˜
        function createBody(list) { //item์€ list์ผ ๊ฒƒ์ž„
            var tableBody = document.getElementById("tableBody");

            var str = "";
            list.forEach( function(data) {
                str += "<tr>";
                str += "<td>" + data.mno + "</td>";
                str += "<td>" + data.writer + "</td>";
                str += "<td>" + data.text + "</td>";
                str += "<td>" + data.id + "</td>";
                str += "<td>" + data.name + "</td>";
                str += "<td>" + data.signDate + "</td>";
                str += "</tr>";
            })
            tableBody.innerHTML = str; //tbody๋ฐ‘์— ์ถ”๊ฐ€

        }

        //ํŽ˜์ด์ง€ ๋„ค์ด์…˜ ์ƒ์„ฑํ•จ์ˆ˜
        function createPage(item) {
            console.log(item);

            var pageList = item.pageList; //ํŽ˜์ด์ง€๋„ค์ด์…˜๊ฐ’
            var next = item.next; //๋‹ค์Œ๋ฒ„ํŠผ
            var prev = item.prev; //์ด์ „๋ฒ„ํŠผ
            start = item.start; //ํŽ˜์ด์ง€๋„ค์ด์…˜ ์‹œ์ž‘
            end = item.end; //ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋

            var str = "";
            //์ด์ „๋ฒ„ํŠผ
            if(prev) {
                str += "<a href='#' class='prev'>์ด์ „</a>";
            }
            //ํŽ˜์ด์ง€ ๋„ค์ด์…˜
            pageList.forEach( function(data) {
                str += "<a href='#' class='number' >" + data + "</a>";
            })
            //๋‹ค์Œ๋ฒ„ํŠผ
            if(next) {
                str += "<a href='#' class='next'>๋‹ค์Œ</a>";
            }

            pagination.innerHTML = str;
        } //end


        //ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ด๋ฒคํŠธ -> ํŽ˜์ด์ง€๋„ค์ด์…˜์€ ๋’ค๋Šฆ๊ฒŒ ๊ทธ๋ ค์ง€๋Š” ํƒœ๊ทธ ( ๋ถ€๋ชจ์— ๊ฑธ๊ณ  ์ž์‹์—์„œ ์œ„์ž„ )
        //์ด๋ฒคํŠธ๋Š” ๋ถ€๋ชจ์— ๊ฒ€~
        pagination.addEventListener('click', function(e) {

            e.preventDefault(); //a์ด๋ฒคํŠธ ์ค‘๋‹จ
            if(e.target.className == 'pagination' ) return; //pagenationํด๋ฆญ์‹œ ํ•จ์ˆ˜ ์ข…๋ฃŒ

            if(e.target.className == 'prev') { //์ด์ „๋ฒ„ํŠผ
                page = start - 1; //์‹œ์ž‘๋ฒˆํ˜ธ์˜ 1
            } else if(e.target.className == 'next') { //๋‹ค์Œ๋ฒ„ํŠผ
                page = end + 1; //๋๋ฒˆํ˜ธ์˜ + 1
            } else if(e.target.className == 'number') { //ํŽ˜์ด์ง€๋ฒˆํ˜ธ
                page = e.target.innerHTML; //aํƒœ๊ทธ ์‚ฌ์ด๊ฐ’์„ ์ „์—ญ๋ณ€์ˆ˜ ์ €์žฅ
            }
            getList(); //๋ฐ์ดํ„ฐ ์กฐํšŒ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
        });

        //๊ฒ€์ƒ‰๊ธฐ๋Šฅ - ๊ฒ€์ƒ‰์„ ํ•œ๋‹ค๋Š” ๊ฒƒ์€~? ๋‹ค์‹œ ํƒœ์ดˆ๋กœ ๋Œ์•„๊ฐ„๋‹ค๋Š” ์˜๋ฏธ.
        function searchList() {
            page = 1;
            amount = 10;
            getList();
        }

        //์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜
        (function() {
            getList(); //๋ฐ์ดํ„ฐ ์š”์ฒญ ํ˜ธ์ถœ
        })();



    </script>




</body>
</html>
package com.example.jpa.controller;

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

@Controller
public class JpaController {

    @GetMapping("/memoList")
    public String main() {
        return "memoList";
    }
}
package com.example.jpa.entity;

import lombok.*;

import javax.persistence.*;

@Entity //jpa๊ฐ€ ์—”ํ‹ฐํ‹ฐ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค๋Š” ์˜๋ฏธ
@Table(name = "MEMO") //MEMOํ…Œ์ด๋ธ”
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Memo {

    //์—”ํ‹ฐํ‹ฐ๋ฅผ ์ •์˜ํ•˜๋ฉด, ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ DDL๊ตฌ๋ฌธ์„ ๋Œ€์‹  ์‹คํ–‰ํ•ด์ฃผ๋Š”๋ฐ, spring.jpa.hibernate.ddl-auto=update ์˜ต์…˜

    @Id //pk
    @GeneratedValue(strategy = GenerationType.IDENTITY) //auto_increment๋™์ž‘
//    ์˜ค๋ผํด์ „๋žต
//    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "์ด๋ฆ„")
//    @SequenceGenerator(name = "์ด๋ฆ„", sequenceName = "์‹œํ€€์Šค๋ช…", initialValue = 1, allocationSize = 1)
    private long mno;
    @Column(length = 200, nullable = false)
    private String writer;
    @Column(columnDefinition = "varchar(200) default 'y' ") //๋งŒ๋“ค๊ณ  ์‹ถ์€ ์ œ์•ฝ์„ ์ง์ ‘ ๋ช…์‹œ
    private String text;

    //N:1
    //FK ์ปฌ๋Ÿผ๋ช…์„ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด Member์—”ํ‹ฐํ‹ฐ์— member_์ฃผํ‚ค ๋กœ ์ž๋™ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
//    @ManyToOne(fetch = FetchType.LAZY) //manyToOne ๊ธฐ๋ณธ๊ฐ’์€ EAGER๋ฐฉ์‹
//    @JoinColumn(name = "member_id") //Member์—”ํ‹ฐํ‹ฐ์˜ ์ฃผํ‚ค๋ฅผ member_id์ปฌ๋Ÿผ์— ์ €์žฅํ•˜๊ฒ ๋‹ค(FK)
//    private Member member; //๋ฉค๋ฒ„ ์—”ํ‹ฐํ‹ฐ

    
    //์–‘๋ฐฉํ–ฅ ๋งตํ•‘
    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;
    
    
}