Home > Spring > ๐Ÿƒ[Spring] ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ž€ ๋ฌด์—‡์ผ๊นŒ์š”?

๐Ÿƒ[Spring] ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ž€ ๋ฌด์—‡์ผ๊นŒ์š”?
Spring Framework

๐Ÿƒ[Spring] ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ž€ ๋ฌด์—‡์ผ๊นŒ์š”?

  • ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋Š” ๋‘ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ด€๊ณ„๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
    • ์ฆ‰, ํ•œ ์—”ํ‹ฐํ‹ฐ์—์„œ ๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๋ฐ˜๋Œ€๋กœ ๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ์—์„œ๋„ ์ด๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‘ ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ํƒ์ƒ‰์ด ์–‘์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ๋ชจ๋‘ ๊ฐ€๋Šฅํ•ด์ง€๋ฉฐ, ์ด๋Š” JPA์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ด€๊ณ„ ์–ด๋…ธํ…Œ์ด์…˜ ์กฐํ•ฉ์œผ๋กœ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค.
    • @OneToMany + @ManyToOne
    • @OneToOne + @OneToOne
    • @ManyToMany + @ManyToMany

1๏ธโƒฃ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„์˜ ํŠน์ง•.

1๏ธโƒฃ ์–‘์ชฝ์—์„œ ์ฐธ์กฐ ๊ฐ€๋Šฅ.

  • ์–‘์ชฝ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ๋ฐ์ดํ„ฐ ํƒ์ƒ‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์—์„œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋“ค์„ ์กฐํšŒํ•˜๊ฑฐ๋‚˜, ์ž์‹ ์—”ํ‹ฐํ‹ฐ์—์„œ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2๏ธโƒฃ ์ฃผ์ธ(Owner)๊ณผ ๋น„์ฃผ์ธ(Inverse) ์„ค์ •.

  • JPA์—์„œ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ๋•Œ, ๋ฐ˜๋“œ์‹œ ์ฃผ์ธ(Owner)๊ณผ ๋น„์ฃผ์ธ(Inverse)์„ ๋ช…์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ์ธ(Owner) : ์™ธ๋ž˜ ํ‚ค(Foreign Key)๋ฅผ ์‹ค์ œ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ์ชฝ.
  • ๋น„์ฃผ์ธ(Inverse) : ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์ฐธ์กฐ๋งŒ ๊ฐ€๋Šฅํ•˜๋ฉฐ, mappedBy ์†์„ฑ์„ ํ†ตํ•ด ์ฃผ์ธ์„ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.

3๏ธโƒฃ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์™ธ๋ž˜ ํ‚ค๋Š” ํ•œ์ชฝ์—๋งŒ ์กด์žฌ.

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์™ธ๋ž˜ ํ‚ค(Foreign Key)๋Š” ๊ด€๊ณ„์˜ ์ฃผ์ธ์— ํ•ด๋‹นํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ํ…Œ์ด๋ธ”์—๋งŒ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

2๏ธโƒฃ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„์˜ ๊ตฌํ˜„ ํ˜•ํƒœ.

1๏ธโƒฃ @OneToMany + @ManyToOne

  • ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉฐ, ๋ถ€๋ชจ๋Š” ์ž์‹์˜ ์ปฌ๋ ‰์…˜์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ์ž์‹์€ ๋ถ€๋ชจ๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ œ: User์™€ UserSaveHistory

  • User ์—”ํ‹ฐํ‹ฐ(๋ถ€๋ชจ)
    @Entity
    public class User {
        
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
        
      @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
      private List<UserSaveHistory> saveHistories = new ArrayList<>();
        
      public void addSaveHistory(UserSaveHistory history) {
          saveHistories.add(history);
          history.setUser(this);
      }
        
      public void removeSaveHistory(UserSaveHistory history) {
          saveHistories.remove(history);
          history.setUser(null);
      }
    }
    
  • UserSaveHistory ์—”ํ‹ฐํ‹ฐ(์ž์‹)
    @Entity
    public class UserSaveHistory {
        
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
        
      @ManyToOne(fetch = FetchType.LAZY)
      @JoinColumn(name = "user_id")
      private User user;
    }
    

2๏ธโƒฃ @OneToOne + @OneToOne

  • 1:1 ๊ด€๊ณ„๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉฐ, ์–‘์ชฝ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ œ: Passport์™€ Person

  • Person ์—”ํ‹ฐํ‹ฐ.
    @Entity
    public class Person {
        
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
        
      @OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
      private Passport passport;
    }
    
  • Passport ์—”ํ‹ฐํ‹ฐ.
    @Entity
    public class Passport {
        
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
        
      @OneToOne
      @JoinColumn(name = "person_id")
      private Person person;
    }
    

3๏ธโƒฃ @ManyToMany + @ManyToMany

  • ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉฐ, ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์„ ํ†ตํ•ด ๋‘ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

์˜ˆ์ œ: Student์™€ Course

  • Student ์—”ํ‹ฐํ‹ฐ.
    @Entity
    public class Student {
        
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
        
      @ManyToMany
      @JoinTable(
          name = "student_course",
          joinColumns = @JoinColumn(name = "student_id"),
          inverseJoinColumns = @JoinColumn(name = "course_id")
      )
      private List<Course> courses = new ArrayList<>();
    }
    
  • Course ์—”ํ‹ฐํ‹ฐ.
    @Entity
    public class Course {
        
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
        
      @ManyToMany(mappedBy = "courses")
      private List<Student> students = new ArrayList<>();
    }
    

3๏ธโƒฃ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„์—์„œ ์ฃผ์˜์ .

1๏ธโƒฃ ์ฃผ์ธ(Owner)๊ณผ ๋น„์ฃผ์ธ(Inverse)

  • JPA์—์„œ๋Š” ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ๋•Œ ๋ฐ˜๋“œ์‹œ ์ฃผ์ธ์„ ๋ช…์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ์ธ์€ ์™ธ๋ž˜ ํ‚ค(Foreign Key)๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ(INSERT, UPDATE)์— ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.
  • ๋น„์ฃผ์ธ์€ mappedBy ์†์„ฑ์„ ์‚ฌ์šฉํ•ด ์ฃผ์ธ์„ ์ง€์ •ํ•˜๋ฉฐ, ์ฝ๊ธฐ ์ „์šฉ์ž…๋‹ˆ๋‹ค.

2๏ธโƒฃ ์—ฐ๊ด€ ๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ.

  • ๋ถ€๋ชจ์™€ ์ž์‹ ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ์œ ์ง€ํ•˜๋ ค๋ฉด ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ถ€๋ชจ์˜ addChild ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ž์‹์˜ ๋ถ€๋ชจ๋„ ์ž๋™์œผ๋กœ ์„ค์ •๋˜๋„๋ก ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
      public void addSaveHistory(UserSaveHistory history) {
        saveHistories.add(history);
        history.setUser(this);
      }
      

๐Ÿ™‹โ€โ™‚๏ธ ํŽธ์˜ ๋ฉ”์„œ๋“œ

๊ด€๊ณ„๋ฅผ ๋™๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•œ ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ž€ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€ ๊ด€๊ณ„์—์„œ ๋‘ ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

3๏ธโƒฃ ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)

  • ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„์—์„œ๋Š” @OneToMany์™€ @ManyToOne ๋ชจ๋‘ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•„์š”์‹œ fetch = FetchType.EAGER๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ด๋Š” ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹œ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4๏ธโƒฃ ๋ฌดํ•œ ๋ฃจํ”„ ๋ฌธ์ œ

  • ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ JSON์œผ๋กœ ์ง๋ ฌํ™”ํ•  ๋•Œ, ๋ถ€๋ชจ์™€ ์ž์‹์ด ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•˜๋ฉฐ ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
      • @JsonIgnore : ํŠน์ • ํ•„๋“œ๋ฅผ ์ง๋ ฌํ™”ํ•˜์ง€ ์•Š๋„๋ก ์„ค์ •.
      • @JsonManagedReference์™€ @JsonBackReference: Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ด€๊ณ„๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜.

4๏ธโƒฃ ์žฅ์ ๊ณผ ๋‹จ์ .

1๏ธโƒฃ ์žฅ์ .

1๏ธโƒฃ ์–‘์ชฝ ํƒ์ƒ‰ ๊ธฐ๋Šฅ.

  • ๋ถ€๋ชจ์™€ ์ž์‹ ๋ชจ๋‘์—์„œ ๊ด€๊ณ„๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2๏ธโƒฃ ๋ช…ํ™•ํ•œ ๊ด€๊ณ„ ํ‘œํ˜„.

  • ๊ฐ์ฒด ๋ชจ๋ธ์—์„œ ๋‘ ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2๏ธโƒฃ ๋‹จ์ .

1๏ธโƒฃ ์„ค๊ณ„ ๋ณต์žก์„ฑ ์ฆ๊ฐ€.

  • ๊ด€๊ณ„๋ฅผ ์–‘์ชฝ์—์„œ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2๏ธโƒฃ ์„ฑ๋Šฅ ๋ฌธ์ œ.

  • ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋กœ๋”ฉ ์ „๋žต์„ ์‹ ์ค‘ํžˆ ์„ ํƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

5๏ธโƒฃ ๊ฒฐ๋ก .

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