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์ ๋ํด์๋ ์๋ ์๋ฃ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
- 2. ํ์์ ์ฌ๋ฌ ๊ฐ์ ๊ถํ์ ๊ฐ์ง ์ ์์ต๋๋ค. ๋ฐ๋ผ์ Role๊ณผ ๋ค๋๋ค ๊ด๊ณ๋ฅผ ๊ฐ๋๋ฐ, MemberRole ์ค๊ฐ ๋ค๋ฆฌ ์ํฐํฐ๋ฅผ ์ค๊ณํ์ฌ ์ผ๋๋ค-๋ค๋์ผ ๋งคํ์ผ๋ก ํ์ด๋์ต๋๋ค.
- @ManyToMany๋ก ์์ฑ๋ ์ค๊ฐ ํ ์ด๋ธ์ ๊ด๊ณ ์ค์ ์ ํ์์ ์ผ๋ก ํ์ํ ์ ๋ณด๋ค๋ง ๋ด๊ฒจ์์ ๋ฟ ๋น์ฆ๋์ค ๋ก์ง์ ํ์ํ ์ ๋ณด๋ค์ ๋ด๊ธฐ์ง ์๊ธฐ ๋๋ฌธ์ ์ค๋ฌด์์๋ @ManyToMany์ ์ ์ฌ์ฉํ์ง ์๊ณ ์ง์ ์ค๊ฐ ํ ์ด๋ธ์ ๋ง๋ค์ด OneToMany - ManyToOne์ผ๋ก ํ์ด๋ด์ ์ฐ๊ด๊ด๊ณ๋ฅผ ๋งคํํ์์ต๋๋ค.
- Member๊ฐ ์ ์ฅ๋ ๋ MemberRole ๋ํ ์ฐ์์ ์ผ๋ก ์ ์ฅ๋๊ฑฐ๋ ์ ๊ฑฐ๋ ์ ์๋๋ก cascade ์ต์ ์ ALL๋ก ์ค์ ํ์ต๋๋ค
- orphanRemoval=true๋ก ์ค์ ํด์ MemberRole์ด ๊ณ ์ ๊ฐ์ฒด๊ฐ ๋์์ ๋, MemberRole์ ์๋์ผ๋ก ์ญ์ ํ๋๋ก ์ค์
- ์๋ณ๊ด๊ณ ์ฌ์ฉ : ๋ค๋๋ค ๋งคํ์ ์ค๊ฐ ํ ์ด๋ธ์ @IdClass๋ฅผ ์ฌ์ฉํ์ฌ ์ค๊ฐํ ์ด๋ธ ์ ์์ PK๋ฅผ ๋ณตํฉํค๋ก ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
์์์ฑ ์ ์ด, ๊ณ ์๊ฐ์ฒด, ๋ค๋๋ค ๊ด๊ณ ๋งคํ์ ๋ํด์๋ ์๋ ์๋ฃ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
๐ 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 ์ต์ ์ฌ์ฉ์ ์ ์ฝ์ด ์์ด์ ์๋ณ์์ ๊ฐ์ด ์๋ณ๊ด๊ณ๋ก ๋ณ๊ฒฝํ์ฌ ์งํํ์์ต๋๋ค.
์๋ณ๊ด๊ณ, ๋น์๋ณ๊ด๊ณ ๋งคํ์ ๋ํด์๋ ์๋๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
๐ 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 ์ด๋
ธํ
์ด์
์ ๋ํด์๋ ์๋ ์๋ฃ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
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์ ๋ํด์๋ ์๋ ์๋ฃ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
'๐ Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422