๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป๐Ÿ“/ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

[Trouble Shooting] Jackson์˜ ์ง๋ ฌํ™” ๊ด€๋ จ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

_silver 2024. 11. 28. 21:10

๋ฒŒ์จ ๋‹ค์„ฏ ๋ฒˆ์งธ ๊ฐœ์ธ๊ณผ์ œ๋ฅผ ์ง„ํ–‰ ์ค‘์ด๋‹ค.

์ด๋ฒˆ ๊ณผ์ œ๋Š” [์€ํ–‰ ํ™˜์ „]์„ ์ฃผ์ œ๋กœ ๊ณผ์ œ๋ฅผ ํ•˜๋ฉด์„œ Jackson์˜ ์ง๋ ฌํ™” ๊ด€๋ จํ•˜์—ฌ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…์„ ๋‹ค๋ฃจ๋ ค๊ณ  ํ•œ๋‹ค.

 

GitHub ๋งํฌ

 

1. ๋ฐฐ๊ฒฝ

1) ๋ฌธ์ œ ์ƒํ™ฉ

๐Ÿšš ์š”์ฒญ URL : PUT localhost:8080/exhange/1/cancel

๐Ÿšš ํ˜„์ƒ

→ IntelliJ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค: exchange_request ํ…Œ์ด๋ธ”์˜ ์ƒํƒœ๊ฐ€ NORMAL์—์„œ CANCELED๋กœ ์ •์ƒ์ ์œผ๋กœ ๋ฐ˜์˜๋˜๋Š” ๊ฒƒ์„ ํ™•์ธ

→ POSTMAN ์‘๋‹ต : 500 Internal Server Error ๋ฐœ์ƒ

๋”๋ณด๊ธฐ

<์ •์ƒ ์‘๋‹ต ๋‚ด์šฉ>

{
    "createdAt": "2024-11-28T23:09:31.424224",
    "modifiedAt": "2024-11-28T23:11:50.460609",
    "id": 1,
    "amountInKrw": 10000,
    "amountAfterExchange": 6.99,
    "status": "CANCELED"
}

 

<๋ฌธ์ œ์˜ ์˜ค๋ฅ˜ ๋ฉ”์„ธ์ง€>

 

2) ์›์ธ ๋ถ„์„

1๏ธโƒฃ ExchangeRequest ์—”ํ‹ฐํ‹ฐ๋Š” User์™€ Currency ์—”ํ‹ฐํ‹ฐ์™€ ManyToOne ๊ด€๊ณ„๋ฅผ ๊ฐ€์ง

2๏ธโƒฃ User์™€ Currency ์—”ํ‹ฐํ‹ฐ๋Š” ๊ฐ๊ฐ ExchangeRequest๋ฅผ ์ฐธ์กฐ - ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„

3๏ธโƒฃ Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ Entity์˜ getter๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์ง๋ ฌํ™”๋ฅผ ์ด์šฉํ•ด json์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ฒŒ ์ „์†ก๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋ฐ”๊พธ์–ด์ค€๋‹ค.

Entity์˜ getter๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ณผ์ •์—์„œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ณ„์†ํ•ด์„œ ๋ถˆ๋Ÿฌ์˜ค๋‹ค ๋ณด๋ฉด ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ Error๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

 

์ฆ‰, JPA์—์„œ ์—ฐ๊ด€๊ด€๊ณ„์—์„œ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ๊ฐ€์งˆ ๋•Œ ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ตฌ์กฐ์—์„œ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” ์‹œ JSON ๋ณ€ํ™˜ ๊ณผ์ •์—์„œ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

-> ์ด๋ฅผ ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ์„ฑ๋Šฅ ์ €ํ•˜ ๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ๋ฅผ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฐธ๊ณ  ๋ธ”๋กœ๊ทธ(1)


2. ์ ˆ์ •

โ–ถ ์ˆ˜์ • ์ „ ๋กœ์ง

@ManyToOne(fetch = FetchType.LAZY)          // ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ๋•Œ ์กฐํšŒ(์ง€์—ฐ๋กœ๋”ฉ ๋ฐฉ์‹)
@JoinColumn(name = "user_id", nullable = false)
@JsonBackReference              // ์ˆœํ™˜ ์ฐธ์กฐ ํ•ด๊ฒฐ
private User user;                          // ๊ณ ๊ฐ ID -> user์™€ N:1 ๊ด€๊ณ„ ์„ค์ •

@ManyToOne(fetch = FetchType.LAZY)          // ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ๋•Œ ์กฐํšŒ(์ง€์—ฐ๋กœ๋”ฉ ๋ฐฉ์‹)
@JoinColumn(name = "to_currency_id", nullable = false)
private Currency currency;                // ํ™˜์ „ ๋Œ€์ƒ ํ†ตํ™” ID -> currency์™€ N:1 ๊ด€๊ณ„ ์„ค์ •

- Currency ํ•„๋“œ์—๋Š” ์ˆœํ™˜ ์ฐธ์กฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†์–ด Jackson์ด JSON ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ

 

โ–ถ ์ˆ˜์ • ๋กœ์ง(๋ฐฉ๋ฒ• 1)

@OneToMany(mappedBy = "currency", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference                  // ๋ถ€๋ชจ ๊ด€๊ณ„๋กœ ์„ค์ •
private List<ExchangeRequest> exchangeRequests = new ArrayList<>();
@OneToMany(mappedBy = "user",cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference                    //๋ถ€๋ชจ ๊ด€๊ณ„๋กœ ์„ค์ •
private List<ExchangeRequest> exchangeRequests = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)         
@JoinColumn(name = "user_id", nullable = false)
@JsonBackReference                          // ์ž์‹ ๊ด€๊ณ„๋กœ ์„ค์ •
private User user;                          

@ManyToOne(fetch = FetchType.LAZY)        
@JoinColumn(name = "to_currency_id", nullable = false)
@JsonBackReference                          // ์ž์‹ ๊ด€๊ณ„๋กœ ์„ค์ •
private Currency currency;

โžก๏ธ @JsonManagedReference์™€ @JsonBackReference: ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ์ง๋ ฌํ™” ๋ฐฉํ–ฅ ์ œ์–ด ํ•˜๋Š” ๋ฐฉ๋ฒ• ์„ ํƒ ์ฐธ๊ณ ๋ธ”๋กœ๊ทธ(2)

 

โ–ถ ์ˆ˜์ • ๋กœ์ง(๋ฐฉ๋ฒ• 2)

@ManyToOne(fetch = FetchType.LAZY)          // ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ๋•Œ ์กฐํšŒ(์ง€์—ฐ๋กœ๋”ฉ ๋ฐฉ์‹)
@JoinColumn(name = "user_id", nullable = false)
@JsonIgnore// ์ˆœํ™˜ ์ฐธ์กฐ ์—๋Ÿฌ ํ•ด๊ฒฐ
private User user;                          // ๊ณ ๊ฐ ID -> user์™€ N:1 ๊ด€๊ณ„ ์„ค์ •

@ManyToOne(fetch = FetchType.LAZY)          // ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ๋•Œ ์กฐํšŒ(์ง€์—ฐ๋กœ๋”ฉ ๋ฐฉ์‹)
@JoinColumn(name = "to_currency_id", nullable = false)
@JsonIgnore                                 // ์ˆœํ™˜ ์ฐธ์กฐ ์—๋Ÿฌ ํ•ด๊ฒฐ
private Currency currency; 

โžก๏ธ @JsonBackReference๋ฅผ @JsonIgnore๋กœ ๋ณ€๊ฒฝ

โžก๏ธ Currency ํ•„๋“œ์—๋„ @JsonIgnore ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€

โžก๏ธ @JsonIgnore๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ User์™€ Currency ํ•„๋“œ๋ฅผ JSON ์ง๋ ฌํ™” ๊ณผ์ •ํ•ด์„œ ์™„์ „ํžˆ ๋ฌด์‹œํ•˜๋„๋ก ์„ค์ •

โžก๏ธ ์ˆœํ™˜ ์ฐธ์กฐ ๋ฌธ์ œ์™€ ์ง๋ ฌํ™” ์˜ค๋ฅ˜๋ฅผ ๋ชจ๋‘ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ


3. ๊ฒฐ๊ณผ

โžก๏ธ IntelliJ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค : exchange_request ํ…Œ์ด๋ธ”์˜ ์ƒํƒœ๊ฐ€ NORMAL์—์„œ CANCELED๋กœ ์ •์ƒ์ ์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธ

โžก๏ธ POSTMAN ์‘๋‹ต : Error ์—๋Ÿฌ ๋ฐœ์ƒ ์—†์ด ์ •์ƒ ํ˜ธ์ถœ ํ™•์ธ


4. ๊ฒฐ๋ก 

โญ๏ธ @JsonBackReference VS @JsonIgnore

 

1. @JsonBackReference, @JsonManagedReference ์‚ฌ์šฉ  :

- @JsonBackReference๋Š” ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ ํ•ด๋‹น ํ•„๋“œ๋ฅผ ์ œ์™ธํ•˜์ง€๋งŒ, ๋ฐ˜๋Œ€์ชฝ ํ•„๋“œ์— @JsonManagedReference์ด ์ง๊ฟ์œผ๋กœ ํ•จ๊ป˜ ํ•„์š”ํ•˜๋‹ค!

  -> @JsonManagedReference์™€ @JsonBackReference๋Š” ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ์ง๋ ฌํ™” ๋ฐฉํ–ฅ ์ œ์–ด ํ•˜๋Š” ๋ฐฉ๋ฒ•

  -> @JsonManagedReference๋Š” ์—ญ์ฐธ์กฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด์—์„œ ์‚ฌ์šฉ, @JsonBackReference๋Š” ์—ญ์ฐธ์กฐ๋ฅผ ํ•˜์ง€ ์•Š๋Š” ๊ฐ์ฒด์—์„œ ์‚ฌ์šฉ

  -> ์ด ๋ฐฉ๋ฒ•์€ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง๋ ฌํ™”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฌดํ•œ ์ˆœํ™˜ ์ฐธ์กฐ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

2. @JsonIgnore ์‚ฌ์šฉ:

- JSON ์ง๋ ฌํ™”์™€ ์—ญ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ ํ•„๋“œ๋ฅผ ์™„์ „ํžˆ ๋ฌด์‹œํ•œ๋‹ค.

  -> ๊ฐ„๋‹จํ•˜๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด์ง€๋งŒ @JsonIgnore ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ํ•„๋“œ๋ฅผ ์™„์ „ํžˆ ๋ฌด์‹œํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋Š” ๋ถ€์ ํ•ฉํ•œ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ํ•œ๋‹ค.