# Spring Boot๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฒŒ์‹œํŒ API ์„œ๋ฒ„ ์ œ์ž‘ (2) - ์—”ํ‹ฐํ‹ฐ ์„ค๊ณ„, Repository ๊ตฌํ˜„
Study Repository

Spring Boot๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฒŒ์‹œํŒ API ์„œ๋ฒ„ ์ œ์ž‘ (2) - ์—”ํ‹ฐํ‹ฐ ์„ค๊ณ„, Repository ๊ตฌํ˜„

by rlaehddnd0422

๐ŸšฉOverview

๋กœ๊ทธ์ธ์— ์‚ฌ์šฉ๋  Member ์—”ํ‹ฐํ‹ฐ๋ฅผ ์„ค๊ณ„ํ•˜๊ณ , Member๊ฐ€ ์ €์žฅ๋  Repository๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
 

๐ŸšฉMember Entity ์„ค๊ณ„

๐Ÿ“ Member

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member extends BaseEntity { // (1)

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "member_id")
    private Long id;

    private String username;
    private String password;
    private String nickname;
    private String email;
	
    // (2)
    @OneToMany(mappedBy = "member", orphanRemoval = true)
    private List<MemberRole> roles;

    public Member(String username, String password, String nickname, String email, List<Role> roles) {
        this.username = username;
        this.password = password;
        this.nickname = nickname;
        this.email = email;
        this.roles = roles.stream()
                .map(r -> new MemberRole(r)).collect(Collectors.toList());
    }

    public void updateNickName(String nickname) {
        this.nickname = nickname;
    }

    public void updateEmail(String email) {
        this.email = email;
    }

    public void updatePassword(String password) {
        this.email = email;
    }
}
 
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdTime;

    @LastModifiedDate
    private LocalDateTime lastModified;

    @PreUpdate
    public void onPreUpdate() {
        this.lastModified = LocalDateTime.now();
    }
}
  • 1. ํšŒ์› ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ ๋ฐ ๋ณ€๊ฒฝ ์‹œ ์ƒ์„ฑ, ๋ณ€๊ฒฝ ๋‚ ์งœ๋ฅผ ๊ฐ–๋Š” ์ปฌ๋Ÿผ์„ ๊ฐ–๋„๋ก ์„ค๊ณ„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. 
    • BaseEntity๋ฅผ ์„ค๊ณ„ํ•˜๊ณ  @MappedSuperClass๋กœ ๋“ฑ๋ก
    • @EnableJpaAuditing : ์Šคํ”„๋ง ๋ถ€ํŠธ ์„ค์ • ํด๋ž˜์Šค์— ์ ์šฉ
    • @EntityListeners(AuditingEntityListener.class) : Base ์—”ํ‹ฐํ‹ฐ์— ์ ์šฉ

Spring Data JPA์˜ ํ™•์žฅ ๊ธฐ๋Šฅ์ธ Auditing์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜ ์ž๋ฃŒ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.
 

[JPA] Spring Data JPA - ํ™•์žฅ ๊ธฐ๋Šฅ

์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ์— ์ด์–ด ์ถ”๊ฐ€์ ์ธ ํ™•์žฅ ๊ธฐ๋Šฅ(์‚ฌ์šฉ์ž ์ •์˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ตฌํ˜„, Auditing)์— ๋Œ€ํ•ด ์•Œ์•„๋ด…์‹œ๋‹ค. ์‚ฌ์šฉ์ž ์ •์˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ตฌํ˜„ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋กœ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ฐœ๋ฐœ ์‹œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ •

rlaehddnd0422.tistory.com

 

  • 2. ํšŒ์›์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ถŒํ•œ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Role๊ณผ ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ๊ฐ–๋Š”๋ฐ, MemberRole ์ค‘๊ฐ„ ๋‹ค๋ฆฌ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์„ค๊ณ„ํ•˜์—ฌ ์ผ๋Œ€๋‹ค-๋‹ค๋Œ€์ผ ๋งคํ•‘์œผ๋กœ ํ’€์–ด๋ƒˆ์Šต๋‹ˆ๋‹ค.
    • @ManyToMany๋กœ ์ƒ์„ฑ๋œ ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์€ ๊ด€๊ณ„ ์„ค์ •์— ํ•„์ˆ˜์ ์œผ๋กœ ํ•„์š”ํ•œ ์ •๋ณด๋“ค๋งŒ ๋‹ด๊ฒจ์žˆ์„ ๋ฟ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ƒ ํ•„์š”ํ•œ ์ •๋ณด๋“ค์€ ๋‹ด๊ธฐ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‹ค๋ฌด์—์„œ๋Š” @ManyToMany์„ ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ง์ ‘ ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค์–ด OneToMany - ManyToOne์œผ๋กœ ํ’€์–ด๋‚ด์„œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • Member๊ฐ€ ์ €์žฅ๋  ๋•Œ MemberRole ๋˜ํ•œ ์—ฐ์‡„์ ์œผ๋กœ ์ €์žฅ๋˜๊ฑฐ๋‚˜ ์ œ๊ฑฐ๋  ์ˆ˜ ์žˆ๋„๋ก cascade ์˜ต์…˜์„ ALL๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค 
    • orphanRemoval=true๋กœ ์„ค์ •ํ•ด์„œ MemberRole์ด ๊ณ ์•„ ๊ฐ์ฒด๊ฐ€ ๋˜์—ˆ์„ ๋•Œ, MemberRole์„ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•˜๋„๋ก ์„ค์ •
    • ์‹๋ณ„๊ด€๊ณ„ ์‚ฌ์šฉ : ๋‹ค๋Œ€๋‹ค ๋งคํ•‘์€ ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์— @IdClass๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ค‘๊ฐ„ํ…Œ์ด๋ธ” ์–‘ ์˜†์˜ PK๋ฅผ ๋ณตํ•ฉํ‚ค๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

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

[JPA] ์˜์†์„ฑ ์ „์ด by Cascade ์˜ต์…˜

์˜์†์„ฑ ์ „์ด ์˜์†์„ฑ ์ „์ด๋Š” ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“ค ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์˜์†์ƒํƒœ๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. JPA๋Š” ๋‹ค์ค‘์„ฑ ์–ด๋…ธํ…Œ์ด์…˜(@ManyToOne, @OneToMany ๋“ฑ)์— cascade

rlaehddnd0422.tistory.com

 
 
๐Ÿ“ MemberRole 

@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode
@IdClass(MemberRoleId.class)
public class MemberRole {

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "role_id")
    private Role role;
}
  • ๋ฐ›์•„์˜จ ์‹๋ณ„์ž๋Š” ์™ธ๋ž˜ํ‚ค๋กœ๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ์ƒˆ๋กœ์šด ์‹๋ณ„์ž๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๋น„์‹๋ณ„๊ด€๊ณ„๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
  • ๋น„์‹๋ณ„ ๊ด€๊ณ„์—์„œ๋Š” ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ญ์ œํ•˜๋”๋ผ๋„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‚ญ์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋น„์‹๋ณ„๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์‹๋ณ„์ž ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ๋‹จ์ˆœํ•˜๊ฒŒ ORM ๋งคํ•‘์„ ํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋”๋Ÿฌ ๋น„์ฆˆ๋‹ˆ์Šค ํ‚ค์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค๋ฌด์—์„œ ๋˜ํ•œ ๋‹ค๋Œ€๋‹ค ๋งคํ•‘์—์„œ ๋น„์‹๋ณ„๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ „๋žต์„ ์„ ํ˜ธํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์ € ๋˜ํ•œ ๋น„์‹๋ณ„๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ „๋žต์œผ๋กœ ๋งคํ•‘ํ•˜์˜€์—ˆ๋Š”๋ฐ, ๋น„์‹๋ณ„๊ด€๊ณ„๋กœ ํ•˜๊ฒŒ๋˜๋ฉด cascade ์˜ต์…˜ ์‚ฌ์šฉ์— ์ œ์•ฝ์ด ์žˆ์–ด์„œ ์‹๋ณ„์œ„์™€ ๊ฐ™์ด  ์‹๋ณ„๊ด€๊ณ„๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

์‹๋ณ„๊ด€๊ณ„, ๋น„์‹๋ณ„๊ด€๊ณ„ ๋งคํ•‘์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

[JPA] ๋‹ค๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ, ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘

๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ •๊ทœํ™”๋œ ํ…Œ์ด๋ธ” 2๊ฐœ๋กœ ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ…Œ์ด๋ธ”์—์„œ๋Š” ๋ณดํ†ต ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ์ผ๋Œ€๋‹ค + ๋‹ค๋Œ€์ผ ๊ด€๊ณ„๋กœ ํ’€์–ด๋‚ด๋Š” ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€

rlaehddnd0422.tistory.com

๐Ÿ“ Role

@Entity
@Getter
@NoArgsConstructor
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long id;

    @Enumerated(value = EnumType.STRING)
    private RoleType roleType;
}

Role์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด RoleType์„ ์ •์˜ํ•˜์—ฌ @Enumerated ์†์„ฑ์— EnumType.STRING์œผ๋กœ ์ง€์ • ์‹œ ENUM ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋„๋ก ์„ค์ •ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
 
@Enumerated ์–ด๋…ธํ…Œ์ด์…˜์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜ ์ž๋ฃŒ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

[JPA] Entity Mapping : ํ•„๋“œ โžก๏ธ ์ปฌ๋Ÿผ ๋งคํ•‘

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ•„๋“œ์™€ ์ปฌ๋Ÿผ ๋งคํ•‘์šฉ ์–ด๋…ธํ…Œ์ด์…˜์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. @Column ๊ธฐ๋ณธ์ ์œผ๋กœ ์—”ํ‹ฐํ‹ฐ์˜ ํ•„๋“œ์™€ ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ์„ ๋งคํ•‘ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ž…๋‹ˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ์˜

rlaehddnd0422.tistory.com

 
RoleType

@RequiredArgsConstructor
public enum RoleType {
    USER("ROLE_NORMAL"), MANAGER("ROLE_MANAGER"), ADMIN("ROLE_ADMIN");
    private final String value;
}

 
๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์— ํ•„์š”ํ•œ Member ์—”ํ‹ฐํ‹ฐ์˜ ์„ค๊ณ„๊ฐ€ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค.
์„œ๋ฒ„๋ฅผ ์ผœ์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ํ…Œ์ด๋ธ”์ด ์ž˜ ๋งŒ๋“ค์–ด์กŒ๋Š”์ง€ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค.
(application.yml์˜ spirng.jpa.hibernate.ddl-auto ์˜ต์…˜์„ create๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์„ค๊ณ„ํ•œ ์—”ํ‹ฐํ‹ฐ์˜ ํ…Œ์ด๋ธ”์„ DB์— ์ž๋™ ์ƒ์„ฑํ•˜๋„๋ก ์„ค์ •)

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์„ค๊ณ„ํ•œ๋Œ€๋กœ ํ…Œ์ด๋ธ”์ด ์ž˜ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค.
์ด์ œ Member ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ•  Repository๋ฅผ ๊ตฌํ˜„ํ•ด๋ด…์‹œ๋‹ค.


๐ŸšฉMemberRepository ๊ตฌํ˜„

Repository๋Š” Spring Data JPA๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

public interface MemberRepository extends JpaRepository<Member,Long> {

    @EntityGraph(attributePaths = "roles")
    Optional<Member> findOneWithRolesByUsername(String username);
    Optional<Member> findByUsername(String username);
    List<Member> findAllByNickname(String nickname); 
    boolean existsByUsername(String username);
    boolean existsByEmail(String email);
}
  • @EntityGraph์„ ์‚ฌ์šฉํ•ด์„œ Member์˜ username์œผ๋กœ ํ•ด๋‹น ์œ ์ €์˜ Role๊นŒ์ง€ ํ•จ๊ป˜ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ๋ฅผ ์ƒ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ๊ทธ ์™ธ์—๋„ ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ๋ฅผ ์ƒ์„ฑํ•ด๋ณด์•˜๋Š”๋ฐ, Repository Test์—์„œ ์‚ฌ์šฉํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ์™€, @EntityGraph์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜ ์ž๋ฃŒ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

[JPA] Spring Data JPA - ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ

[Spring Data JPA] ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA Spring Data JPA๋ž€? ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ์ฃผ ๊ธฐ๋Šฅ - CRUD ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณต - Repository

rlaehddnd0422.tistory.com

 
 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ