์๋ก
myBatis ์ฌ์ฉํ ๋ ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ๊ธฐ ๋๋ฌธ์ CRUD ๋ชจ๋ ๋ฐ์ดํฐ ๋ฃ๋ ๋ถ๋ถ์์ ๊ตฌํ ๊ณ ๋ฏผ์ ๋ณ๋ก ์์๋ค.
์ด๋ฒ์ ๊ณ ๋ฏผํ๊ฒ ๋ ์ด์ ๋ Spring Boot + JPA ํ๊ฒฝ์์ PATCH ๋ฉ์๋๋ก ๋ฐ์ดํฐ ์์ ์ ์์ฒญ๋ฐ์์ update ํ๋ ค๊ณ ํ๋๋ฐ
๊ตฌํ์ ์ด๋ป๊ฒ ํ ๊น.. ๊ณ ๋ฏผํ๋ค๊ฐ ์ด์ ์๋ Patch + @Dynamic ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ ๊ฒ ๊ธฐ์ต์ด ๋ฌ๋ค. ๊ทธ๋ฐ๋ฐ ๋น์์๋ ํ์์ด ์ฐ๊ธธ๋ ๋ฐ๋ผ ์ผ๋ ๊ฑฐ๋ผ ๊ฐ๊ฐ์ ์ ํํ ๋์๊ณผ ๋ชฉ์ ? (์ด๋จ ๋ ์ฌ์ฉํด์ผ ์ ํฉํ์ง..) ์ ๋ํด์ ์์ฃผ ํ ๋ฐ์ง ๋ ๋๊ฐ๋ณด๊ณ ์ถ์๋ค.
PATCH๋?
โ ์ ์
PATCH HTTP ๋ฉ์๋๋ ๋ฆฌ์์ค์ ์ผ๋ถ ํ๋๋ง ๋ณ๊ฒฝํ ๋ ์ฌ์ฉํ๋ HTTP ๋ฉ์๋์ด๋ค. ๊ธฐ์กด ๋ฐ์ดํฐ ์ค ์ผ๋ถ๋ง ์์ ํด์ผ ํ๋ ๊ฒฝ์ฐ, ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์ ์กํ๋ PUT๊ณผ ๋ฌ๋ฆฌ PATCH๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ํ ํ๋๋ง ๋ณ๊ฒฝํ ์ ์๋ค.
โ ๏ธ ์ฃผ์ํ ์
์ผ๋ถ์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๊ณ , ๋ค์ด์ค์ง ์์ ๊ฐ์ ๋ํด์๋ null๋ก ์ธ์ํ๋ค.
๋ฐ๋ผ์ null ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ ๋ณ๋๋ก ๊ณ ๋ คํด์ฃผ์ด์ผ ํ๋ค. โญ
PATCH์ PUT ์ฐจ์ด์ ์ด ๊ต์ฅํ ์ ๋งคํ๊ณ ,,, ์...
PATCH๊ฐ ๋ฆฌ์์ค์ ์ผ๋ถ ํ๋๋ง ๋ณ๊ฒฝํ๋ ๋ฉ์๋๋ผ๊ณ ํ๊ธดํด๋ ๊ฒฐ๊ตญ.. ๊ทธ๋ฐ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๊ฑด ๊ฐ๋ฐ์๋๊น(null ์ฒ๋ฆฌ ๋ก์ง ์ถ๊ฐ ๊ฐ์ ๊ฒ) ์ด๊ฑด ๋ญ๋ผํด์ผํ์ง ๊ฒฐ๊ตญ ๊ทธ๋ฅ ์ด๋ฆ์ ์ฐจ์ด์ผ ๋ฟ์ธ๊ฑฐ๊ตฌ๋.
๋ฉ์๋๊ฐ PATCH๋ฉด null์ฒ๋ฆฌ ํด๋จ์ผ๋ ํ๋ ํธํ๊ฒ ๋ณด๋ด~ ์ธ๊ฑฐ๊ณ PUT์ด๋ฉด ๋ชจ๋ ๊ฐ์ ํฌํจํด์ ๋ณด๋ด์ง ์์ผ๋ฉด null๋ก ์ ๋ฐ์ดํธ ๋ ํ ๋ ์์์ ํด๋ผ~~^-^ ์ด๋ฐ ๋๋์ธ๊ฑด๊ฐ..
โ Spring Boot ์์ PATCH ์ ์ฉ ์์
Controller
@Operation(summary = "์ํ ์์ ", description = "์ํ ์์ ์ OWNER์ MANAGER๋ง ๊ฐ๋ฅ")
@PatchMapping("/{menu_id}")
@PreAuthorize("hasAnyRole('OWNER', 'MANAGER')")
public ResponseEntity<CommonResponse<MenuResponseDto>> modifyMenu(@PathVariable("menu_id") UUID menuId,
@RequestBody @Validated UpdateMenuRequestDto requestDto,
@AuthenticationPrincipal UserDetailsImpl userDetails) {
MenuResponseDto responseDto = menuService.modifyMenu(menuId, requestDto, userDetails);
return ResponseEntity.ok(new CommonResponse<>(SuccessCode.MENU_MODIFY, responseDto));
}
requestDto
@Getter
@AllArgsConstructor
public class UpdateMenuRequestDto {
@Length(min = 1, max = 100)
private String rmName;
@Min(value = 0)
private BigDecimal rmPrice;
private String rmImageUrl;
@Length(max = 100)
private String rmDescription;
private Boolean isPublic;
}
์์ฒญ json ๋ฐ์ดํฐ ์์
{
"rmPrice": 15000,
"rmDescription": "Updated menu description"
}
requestDto์๋ 5๊ฐ์ง์ ํ๋๊ฐ ์์ง๋ง, ์ด ์ค์์ 3๊ฐ๋ง ๋ค์ด์จ๋ค๊ณ ํ๋ฉด ์์ฒญ์์ ์ ๋ฌ๋์ง ์์ ํ๋ 2๊ฐ๋ requestDto์์ null๋ก ์ธ์๋๋ค.
๋ฐ๋ผ์, ์์์๋ ์ผ๋ฏ์ด null๋ก ๋ค์ด์จ ๊ฐ์ null๋ก ์ ๋ฐ์ดํธ ํ ๊ฒ์ธ์ง ํน์ ๊ธฐ์กด ๊ฐ์ ์ ์งํ ๊ฒ์ธ์ง ์ฒ๋ฆฌํด์ฃผ์ด์ผ ํ๋ค.
@DynamicInsert์ @DynamicUpdate
โ ์ ์
JPA์ ์ด๋
ธํ
์ด์
์ผ๋ก JPA์ ๋ณ๊ฒฝ ๊ฐ์ง(Dirty Checking) ๊ธฐ๋ฅ์ผ๋ก, ๊ฐ์ฒด์ ๋ณ๊ฒฝ๋ ํ๋๋ง ํฌํจ๋ Insert ์ฟผ๋ฆฌ ํน์ Update ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ค.
null๊ฐ์ด ์ ๋ฌ๋์ด๋ JPA ๋ ๋ฒจ์์ null์ ์ ์ธํ๊ธฐ ๋๋ฌธ์ null ์ฒดํฌ ๋ก์ง ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ง ์์๋ ๋๋ค.
โ ๏ธ ์ฃผ์ํ ์
๋งค๋ฒ ๋์ ์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ ์ค๋ฒํค๋๊ฐ ์์ ์ ์๋ค.
์ ์ฑ๋ฅ ์ด์๊ฐ ์๋์ง ์ ํํ ์ดํดํ์ง๋ ๋ชปํ๊ธฐ ๋๋ฌธ์ ์ฐธ๊ณ ์๋ฃ๋ฅผ ์ฒจ๋ถํ๋ค. ์ธ์ ๊ฐ.. ์ดํดํ ๋ ์ด ์ค๊ฒ ์ง ใ
์ธํ๋ฐ| ์ ๋ฐ์ดํธ ๊ณ ๊ฒฌ ๊ตฌํฉ๋๋ค. ๊น์ํ๋ ๋ต๋ณ
โ ์ ์ฉ ์์
@Entity
@DynamicUpdate
// ๋ค๋ฅธ ์ด๋
ธํ
์ด์
์๋ต
public class Menu extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID rmId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "res_id")
private Restaurant restaurant;
@Column(length = 100, nullable = false)
private String rmName;
@Column(nullable = false)
private BigDecimal rmPrice;
private String rmImageUrl;
@Column(length = 100)
private String rmDescription;
@ColumnDefault("true")
private Boolean isPublic;
PATCH Method ์ @DynamicInsert, @DynamicUpdate ์ ์ฐจ์ด์
ํน์ง๊ณผ ์ฐจ์ด์ ์ ์ ๋ฆฌํ์๋ฉด ์๋์ ๊ฐ๋ค. (feat. ์ ๋ฆฌ์ ์ gpt)
PATCH (HTTP Method) | @DynamicUpdate (JPA Annotation) | |
---|---|---|
๊ธฐ๋ฅ | ํด๋ผ์ด์ธํธ๊ฐ ์ผ๋ถ ํ๋๋ง ์์ ๊ฐ๋ฅํ๋๋ก ์ง์ | ๋ณ๊ฒฝ๋ ํ๋๋ง ์ ๋ฐ์ดํธํ๋๋ก JPA๊ฐ ์ฟผ๋ฆฌ ์ต์ ํ |
์ ์ฉ ๋์ | API ์์ฒญ ์์ค | Entity ์์ค |
์ ๋ฐ์ดํธ ๋ฐฉ์ | JSON์ผ๋ก ์ ๋ฌ๋ ํ๋๋ง ๋ณ๊ฒฝ | ๊ฐ์ฒด์ ๋ณ๊ฒฝ๋ ํ๋๋ง ๊ฐ์งํ์ฌ SQL ์์ฑ |
SQL ์คํ ๋ฐฉ์ | ๊ฐ๋ฐ์๊ฐ ํ์ํ ํ๋๋ง ์๋์ผ๋ก ์ ๋ฐ์ดํธ | Hibernate๊ฐ ๋ณ๊ฒฝ ๊ฐ์งํ์ฌ ์๋ ์ต์ ํ |
DTO ํ์ฉ | ๋ณดํต UpdateDto ๋ฅผ ์ฌ์ฉ |
Entity ๊ทธ๋๋ก ์ฌ์ฉ ๊ฐ๋ฅ |
์ฌ์ฉ ๊ธฐ์ | Controller → Service → Repository | JPA, Hibernate |
๊ฒฐ๋ก
๊ฒฐ๊ตญ์ ํ์์ ๋ฐ๋ผ ์ ํํด์ ์ฌ์ฉํ๋ฉด ๋๊ฒ ๋ค.
์ด์ ํ๋ก์ ํธ์์๋ PATCH์ @DynamicInsert๋ฅผ ํจ๊ป ์ฌ์ฉํ์ฌ
ํ์ํ ํ๋๋ง ๋ฐ์์จ ํ Entity์ ๋ณ๊ฒฝ ๋ฉ์๋๋ฅผ ๊ตฌํํ๋ ๋ฐฉ์์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
์ด๋ฒ์๋ PATCH๋ก ํ์ํ ํ๋๋ง ๋ฐ์์จ ํ null๊ฐ์ด๋ฉด ๊ธฐ์กด ๊ฐ์ผ๋ก ๋์ฒดํด์ฃผ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ๋ค.
...๊ทธ๋ฐ๋ฐ..... ์................... ๋ ๋ฐฉ์์ ์ฐจ์ด๋ฅผ ์ดํดํ๊ธฐ๋ ํ ๊ฒ ๊ฐ์๋ฐ
๊ตฌํ์์๋ ์ฐจ์ด๊ฐ.. ์ ๋๋ ๊ฒ ๊ฐ์๋ฐ...?
์ด๋ฒ์๋ Entity์ ๋ณ๊ฒฝ ๋ฉ์๋๋ฅผ ๊ตฌํํ๊ณ ๊ทธ ๋ด๋ถ์ null ์ฒดํนํ๋ ๋ฐฉ์์ด๋ผ์ ๊ฐ์๊ธฐ ๊ธ ์๋ฌธ์ด ๋ค์๋ค.
Entity์ ๋ณ๊ฒฝ ๋ฉ์๋ ๊ตฌํํ๋ ๊ฑด ๊ฐ์๋ฐ ์ด๋
ธํ
์ด์
๋ถ์ด๊ณ ์ ๋ถ์ด๊ณ ์ฐจ์ด๊ฐ ๋ญ์ง?
๋ด๊ฐ ์๋ชป ์ดํดํ๊ณ ๊ตฌํํ๊ฑธ๊น..
๋ค์ TIL์ ์ด๊ฑฐ๋ค........................
'TIL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
WebClient์ RestTemplate ๊ทธ๋ฆฌ๊ณ RestClient (0) | 2025.02.21 |
---|---|
ํ๋ก์ ํธ | ํ๋ก์ ํธ ์ค ํด๊ฒฐ/๊ณ ๋ฏผํ ๋ฌธ์ ๋ค ์ ๋ฆฌ! (feat. JPQL, Querydsl) (1) | 2025.02.20 |
REST API ์์์ HTTP ์๋ต ์ฝ๋ (HTTP 201 Created) (2) | 2025.02.18 |
Spring JPA | Entity์ Dto ๋ถ๋ฆฌ (0) | 2025.02.17 |
ํ๋ก์ ํธ | ์ค๊ณ ๋จ๊ณ์์ ํ์ฅ์ฑ์ ๋ํ ๊ณ ๋ฏผ (0) | 2025.02.12 |