Home > Spring > ๐Ÿƒ[Spring] JPA ์—ฐ๊ด€๊ด€๊ณ„์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ๊ธฐ๋Šฅ๋“ค์—๋Š” ๋ฌด์—‡์ด ์žˆ์„๊นŒ์š”? - ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ํšจ๊ณผ

๐Ÿƒ[Spring] JPA ์—ฐ๊ด€๊ด€๊ณ„์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ๊ธฐ๋Šฅ๋“ค์—๋Š” ๋ฌด์—‡์ด ์žˆ์„๊นŒ์š”? - ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ํšจ๊ณผ
Spring Framework

๐Ÿƒ[Spring] JPA ์—ฐ๊ด€๊ด€๊ณ„์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ๊ธฐ๋Šฅ๋“ค์—๋Š” ๋ฌด์—‡์ด ์žˆ์„๊นŒ์š”? - ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ํšจ๊ณผ

1๏ธโƒฃ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ํšจ๊ณผ.

  • JPA์—์„œ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์œผ๋กœ ์„ค์ •๋œ ์—”ํ‹ฐํ‹ฐ๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์™ธ๋ž˜ ํ‚ค๋ฅผ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ๋Š” ํšจ๊ณผ๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค.
  • JPA์—์„œ๋Š” ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ(Owner)๊ณผ ์ฃผ์ธ์ด ์•„๋‹Œ ์—”ํ‹ฐํ‹ฐ(Non-owner)๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์™ธ๋ž˜ ํ‚ค๋ฅผ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•  ๋•Œ ์ฃผ์ธ์œผ๋กœ ์„ค์ •๋œ ์—”ํ‹ฐํ‹ฐ๋งŒ ์‹ค์ œ SQL์— ๋ฐ˜์˜๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

2๏ธโƒฃ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ํšจ๊ณผ์˜ ๋™์ž‘ ๋ฐฉ์‹.

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

๐Ÿ‘‰ ์˜ˆ์‹œ: 1:1 ๊ด€๊ณ„์—์„œ์˜ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ํšจ๊ณผ.

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToOne
    @JoinColumn(name = "user_profile_id") // ์™ธ๋ž˜ ํ‚ค ๊ด€๋ฆฌ
    private UserProfile userProfile;
    
    // getter, setter ๋“ฑ
}

@Entity
public class UserProfile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String address;
    private String phoneNumber;
    
    @OneToOne(mappedBy = "userProfile") // ์ฃผ์ธ์ด ์•„๋‹˜์„ ๋ช…์‹œ
    private User user;
    
    // getter, setter ๋“ฑ
}
  • ์œ„ ์ฝ”๋“œ์—์„œ User ์—”ํ‹ฐํ‹ฐ๊ฐ€ UserProfile ์—”ํ‹ฐํ‹ฐ์™€ ์—ฐ๊ด€๊ด€๊ณ„์—์„œ ์ฃผ์ธ์ž…๋‹ˆ๋‹ค.
    • userProfile ํ•„๋“œ์— ๋Œ€ํ•œ ์„ค์ •๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

๐Ÿ‘‰ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ํšจ๊ณผ์˜ ์˜ˆ์‹œ ์ƒํ™ฉ.

User user = new User();
UserProfile profile = new UserProfile();

user.setUserProfile(profile);
profile.setUser(user);

entityManager.persist(user);
entityManager.persist(profile);
  • ์œ„์™€ ๊ฐ™์ด user์˜ userProfile์„ ์„ค์ •ํ•˜๊ณ  profile์˜ user๋ฅผ ์„ค์ •ํ•ด๋„, ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” User ์—”ํ‹ฐํ‹ฐ์˜ userProfile์— ์„ค์ •๋œ ๊ฐ’๋งŒ ์™ธ๋ž˜ ํ‚ค๋กœ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.
    • UserProfile ์—”ํ‹ฐํ‹ฐ์—์„œ user ํ•„๋“œ์˜ ๋ณ€๊ฒฝ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
      • ์ด๋ฅผ ํ†ตํ•ด ๋ถˆํ•„์š”ํ•œ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ , ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3๏ธโƒฃ ์š”์•ฝ.

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

4๏ธโƒฃ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ.

  • ์™ธ๋ž˜ ํ‚ค(Foreign Key)๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—ฐ๊ด€๊ด€๊ณ„์™€ ๊ด€๋ จ๋œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•  ๋•Œ ์–ด๋–ค ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์™ธ๋ž˜ ํ‚ค์˜ ์ˆ˜์ •, ์‚ฝ์ž…, ์‚ญ์ œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š”์ง€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

5๏ธโƒฃ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์˜ ์—ญํ• .

  • ์—ฐ๊ด€๊ด€๊ณ„์—์„œ ์ฃผ์ธ์œผ๋กœ ์ง€์ •๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์™ธ๋ž˜ ํ‚ค ์ปฌ๋Ÿผ์„ ๊ด€๋ฆฌํ•˜๋ฉฐ, ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์—์„œ๋Š” ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„์—์„œ @OneToOne, @OneToMany, @ManyToMany, @ManyToOne, @ManyToMany ์ค‘ ์ฃผ์ธ์ด ๋˜๋Š” ์ชฝ์—์„œ๋งŒ ์™ธ๋ž˜ ํ‚ค์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

6๏ธโƒฃ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ์„ค์ •์˜ ์ค‘์š”์„ฑ.

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

๐Ÿ‘‰ ์˜ˆ์ œ: 1:1 ๊ด€๊ณ„์—์„œ์˜ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ์„ค์ •.

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToOne
    @JoinColumn(name = "user_profile_id") // ์™ธ๋ž˜ ํ‚ค ๊ด€๋ฆฌ
    private UserProfile userProfile;
    
    // getter, setter ๋“ฑ
}

@Entity
public class UserProfile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String address;
    private String phoneNumber;
    
    @OneToOne(mappedBy = "userProfile") // ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์ด ์•„๋‹˜์„ ๋ช…์‹œ
    private User user;
    
    // getter, setter ๋“ฑ
}
  • ์œ„ ์ฝ”๋“œ์—์„œ User ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด๋ฉฐ, UserProfile์€ mappedBy ์†์„ฑ์„ ํ†ตํ•ด ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์ด ์•„๋‹˜์„ ๋ช…์‹œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ด ๊ฒฝ์šฐ ์™ธ๋ž˜ ํ‚ค๋Š” User ์—”ํ‹ฐํ‹ฐ์˜ user_profile_id ์ปฌ๋Ÿผ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

7๏ธโƒฃ ์š”์•ฝ.

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