๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
TIL

REST API | ๋ฐ์ดํ„ฐ ์ˆ˜์ • api ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ• ๊นŒ? (PATCH์™€ @DynamicUpdate ์ฐจ์ด์ )

by bamDal 2025. 2. 19.

 

 

์„œ๋ก 

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์€ ์ด๊ฑฐ๋‹ค........................