Spring

@Builder๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋”๋‹ˆ ์žญ์Šจ์—์„œ ์—๋Ÿฌ๊ฐ€?

91MS 2022. 6. 29. 01:32
๐Ÿ’ฃ
๐Ÿ“Œ
@RequestBody ์—์„œ DTO ๋ณ€ํ™˜์‹œ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ํ•„์š”๋กœํ•œ๋‹ค.
  1. ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. (๋ฆฌํ”Œ๋ ‰์…˜ ํ†ตํ•ด ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— Private ๊ฐ€๋Šฅ)
  1. @JsonProperty ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœํผํ‹ฐ ๊ธฐ๋ฐ˜ ๊ฐ์ฒด๋Š” ํ•„์š”์—†๋‹ค.
  1. ๋กฌ๋ณต์˜ @Builder๋Š” ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์ง€ ์•Š๋Š”๋‹ค.


๋ฌธ์ œ ๋ฐœ์ƒ

ํ•œ๊ฐ€๋กœ์šด ์˜คํ›„ ๊ฐœ๋ฐœํ™˜๊ฒฝ์— api ์‘๋‹ต ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค๊ณ  ๋ฌธ์˜๊ฐ€ ๋“ค์–ด์™”์Šต๋‹ˆ๋‹ค.

ES์— ์—๋Ÿฌ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด๋ณด๋‹ˆ ์ด๋Ÿฌํ•œ ์—๋Ÿฌ ๋กœ๊ทธ๊ฐ€.. ์ค„์ค„..

(์‹ค์ œ ํ™˜๊ฒฝ์„ ํฌ์ŠคํŒ…ํ•  ์ˆ˜ ์—†์–ด ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ์œ ๋„ํ•จ)

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.lomboktest.SampleDto` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); 
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.example.lomboktest.SampleDto` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)<EOL> at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 2, column: 5]]

๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ dto๋ฅผ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค.

@Getter
public class SampleDto {
    @Builder.Default
    private String test = "tesT";

    @Builder
    public SampleDto(String test) {
        this.test = test;
    }
}

์Œ.. ์ƒ์„ฑ์ž๋ฅผ ์„ ์–ธํ•˜๊ณ  ๋นŒ๋”๋ฅผ ์„ ์–ธํ•˜์˜€๋Š”๋ฐ ๋ฌด์—‡์ด ๋ฌธ์ œ์ผ๊นŒ?

์—๋Ÿฌ ๋กœ๊ทธ๋ฅผ ์ž์„ธํžˆ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค.

JSON parse error:
Cannot construct instance of `com.example.lomboktest.SampleDto` 
(although at least one Creator exists): 
cannot deserialize from Object value (no delegate- or property-based Creator);

์ง์—ญํ•˜์ž๋ฉด,

JSON ํŒŒ์‹ฑ ์—๋Ÿฌ

SampleDto๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†๋‹ค.

(์ตœ์†Œ ํ•œ๊ฐœ์˜ ์ƒ์„ฑ์ž๊ฐ€ ์กด์žฌํ•˜์ง€๋งŒ)

๊ฐ์ฒด๊ฐ’์œผ๋กœ๋ถ€ํ„ฐ ์—ญ์ง๋ ฌํ™”๋ฅผ ํ•  ์ˆ˜ ์—†๋‹ค. (delegate ๋˜๋Š” property ๊ธฐ๋ฐ˜์˜ ์ƒ์„ฑ์ž๊ฐ€ ์—†์Œ)

์‚ฌ์‹ค ๋”ฑ ๋ณด์ž๋งˆ์ž ์•Œ์•„๋ดค๋‹ค. ํ•˜์ง€๋งŒ ์ง€์‹์€ output์„ ํ•ด์•ผ ์˜ค๋ž˜ ๊ธฐ์–ต์— ๋‚จ๋Š”๋ฒ•.. ๊ทธ๋ž˜์„œ ์ •๋ฆฌํ•˜๋ฉฐ ํฌ์ŠคํŒ…ํ•œ๋‹ค.

์ƒ์„ฑ์ž๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ, ์™œ?

๋ˆˆ์น˜์ฑ„์‹ ๋ถ„๋“ค์€ ์•„์‹œ๊ฒ ์ง€๋งŒ, spring์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™”์— ์žญ์Šจ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ณ  ๊ทธ ์žญ์Šจ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ƒ์„ฑ์ž๊ฐ€ ์—†์œผ๋ฉด ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ž, ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ์ถœ๋ ฅํ•œ ํด๋ž˜์Šค๋ฅผ ์ถ”์ ํ•ด๋ด…์‹œ๋‹ค.

protected Object deserializeFromObjectUsingNonDefault(JsonParser p,
            DeserializationContext ctxt) throws IOException
{
    final JsonDeserializer<Object> delegateDeser = _delegateDeserializer();
    if (delegateDeser != null) {
        final Object bean = _valueInstantiator.createUsingDelegate(ctxt,
                delegateDeser.deserialize(p, ctxt));
        if (_injectables != null) {
            injectValues(ctxt, bean);
        }
        return bean;
    }
    if (_propertyBasedCreator != null) {
        return _deserializeUsingPropertyBased(p, ctxt);
    }
    // 25-Jan-2017, tatu: We do not actually support use of Creators for non-static
    //   inner classes -- with one and only one exception; that of default constructor!
    //   -- so let's indicate it
    Class<?> raw = _beanType.getRawClass();
    if (ClassUtil.isNonStaticInnerClass(raw)) {
        return ctxt.handleMissingInstantiator(raw, null, p,	
"non-static inner classes like this can only by instantiated using default, no-argument constructor");
    }
    return ctxt.handleMissingInstantiator(raw, getValueInstantiator(), p,	
"cannot deserialize from Object value (no delegate- or property-based Creator)");
}

์—๋Ÿฌ๋กœ๊ทธ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด deserializeFromObjectUsingNonDefault ์š”๋†ˆ์ด๋‹ค.

์ด๋ฆ„๋ถ€ํ„ฐ ์‹ฌ์ƒ์น˜์•Š๋‹ค.

์ข€๋” ์ฐพ์•„๋ณด์ž.

/*
/**********************************************************
/* Public API implementation; instantiation from JSON Object
/**********************************************************
 */

@Override
public Object createUsingDefault(DeserializationContext ctxt) throws IOException
{
    if (_defaultCreator == null) { // sanity-check; caller should check
        return super.createUsingDefault(ctxt);
    }
    try {
        return _defaultCreator.call();
    } catch (Exception e) { // 19-Apr-2017, tatu: Let's not catch Errors, just Exceptions
        return ctxt.handleInstantiationProblem(_valueClass, null, rewrapCtorProblem(ctxt, e));
    }
}

๋ฉ”์„œ๋“œ๋ฅผ ๊พธ์ค€ํžˆ ๋”ฐ๋ผ๊ฐ€๋‹ค๋ณด๋‹ˆ ๋ฌธ์ œ ๋ฐœ์ƒ ๊ทผ์›์ง€๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

if (_defaultCreator == null) { // sanity-check; caller should check

์ด ๋ถ€๋ถ„์ด ๋ฐ”๋กœ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๊ฐ€ null ์ธ์ง€ ํ™•์ธํ•˜๊ณ  null์ด๋ฉด super.createUsingDefault(ctxt); ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๊ฒƒ์ด๊ณ  ์—๋Ÿฌ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ ๊ณณ์œผ๋กœ ๋ฐ”์ธ๋”ฉ๋˜์–ด ์‹ค์ œ ์—๋Ÿฌ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฐ˜๋Œ€๋กœ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๊ฐ€ ์žˆ๋‹ค๋ฉด ํ˜ธ์ถœ๋˜๋Š” _defaultCreator.call(); ๋ฅผ ๋“ค์–ด๊ฐ€๋ณด๋ฉด ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๋ฉด ํ™•์‹คํžˆ ์•Œ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋„ค์š”.

@Override
public final Object call() throws Exception {
    // 31-Mar-2021, tatu: Note! This is faster than calling without arguments
    //   because JDK in its wisdom would otherwise allocate `new Object[0]` to pass
    return _constructor.newInstance((Object[]) null);
}

๊ทธ๋ ‡๋‹ค๋ฉด ์—๋Ÿฌ๋กœ๊ทธ์— ์žˆ๋˜ no delegate- or property-based ๋Š” ๋ญ์ง€?

์šฐ๋ฆฌ๊ฐ€ ์ง€๋‚˜์ณ์˜จ ์ฝ”๋“œ์— ๋‹ต์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

์œ„์˜ ์ฝ”๋“œ ์ค‘ ์—๋Ÿฌ๋ฉ”์„ธ์ง€ ์ถœ๋ ฅ๋œ ๋ฉ”์„œ๋“œ๋ฅผ ํ™•์ธํ•ด๋ณผ๊นŒ์š”?

protected Object deserializeFromObjectUsingNonDefault(JsonParser p,
            DeserializationContext ctxt) throws IOException
{
    final JsonDeserializer<Object> delegateDeser = _delegateDeserializer();
    if (delegateDeser != null) {
        final Object bean = _valueInstantiator.createUsingDelegate(ctxt,
                delegateDeser.deserialize(p, ctxt));
        if (_injectables != null) {
            injectValues(ctxt, bean);
        }
        return bean;
    }
    if (_propertyBasedCreator != null) {
        return _deserializeUsingPropertyBased(p, ctxt);
    }
    // 25-Jan-2017, tatu: We do not actually support use of Creators for non-static
    //   inner classes -- with one and only one exception; that of default constructor!
    //   -- so let's indicate it
    Class<?> raw = _beanType.getRawClass();
    if (ClassUtil.isNonStaticInnerClass(raw)) {
        return ctxt.handleMissingInstantiator(raw, null, p, 
"non-static inner classes like this can only by instantiated using default, no-argument constructor");
    }
    return ctxt.handleMissingInstantiator(raw, getValueInstantiator(), p, 
"cannot deserialize from Object value (no delegate- or property-based Creator)");
}

deserializeFromObjectUsingNonDefault ๋ฉ”์„œ๋“œ๋ช…์„ ์‚ดํŽด๋ณด๋ฉด ๊ธฐ๋ณธ์ƒ์„ฑ์ž ์—†์ด ์—ญ์ง๋ ฌํ™”ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.

์ด ๋ฉ”์„œ๋“œ๊ฐ€ ํƒ”๋‹ค๋ฉด ๋ฐ”๋กœ ์—๋Ÿฌ๋ฅผ ์ฐ์–ด์•ผํ•˜๋Š”๋ฐ ์„ธ๋ถ€ ๋กœ์ง์„ ๋ณด๋ฉด

final JsonDeserializer<Object> delegateDeser = _delegateDeserializer();
if (delegateDeser != null) {
    final Object bean = _valueInstantiator.createUsingDelegate(ctxt,
            delegateDeser.deserialize(p, ctxt));
    if (_injectables != null) {
        injectValues(ctxt, bean);
    }
    return bean;
}

delegateDeser != null delegate๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด createUsingDelegate ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋กœ์ง์ด ๋“ค์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ ์ €์˜ ์ƒํ™ฉ์€ delegateDeser == null ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋งˆ์ง€๋ง‰ ๊ตฌ๋ฌธ์˜ ์—๋Ÿฌ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ทธ ๋ฐ‘์˜ ๋กœ์ง์ธ

if (_propertyBasedCreator != null) {
    return _deserializeUsingPropertyBased(p, ctxt);
}

_propertyBasedCreator ๋„ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ €๋Ÿฐ ์—๋Ÿฌ๊ฐ€ ์ฐํžŒ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ •๋ฆฌ

๊ฒฐ๊ตญ, ์ €์˜ ๋ฌธ์ œ๋Š” Controller์—์„œ API ์š”์ฒญ ์ŠคํŽ™์„ ์ „๋‹ฌํ•˜๋Š” DTO์˜ ๊ฐ์ฒด๋กœ์จ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๊ฐ€ ์—†์—ˆ๊ธฐ์— ๋‚˜ํƒ€๋‚œ ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํžˆ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์—๋Ÿฌ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋กฌ๋ณต์˜ @builder๋Š” ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์ง€ ์•Š๊ธฐ์— ์ด๋Ÿฐ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.. ๐Ÿ˜ฑ

์šฐ๋ฆฌ ๋ชจ๋‘ ํ•˜๋‚˜๋งŒ ๊ธฐ์–ตํ•ฉ์‹œ๋‹ค.

์žญ์Šจ์˜ object mapper๋Š” ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ํ•„์š”๋กœํ•œ๋‹ค! (๊ธฐ๋ณธ์ƒ์„ฑ์ž๋Š” private ์ด์–ด๋„ ๋œ๋‹ค! ๋ฆฌํ”Œ๋ ‰์…˜)

๋ฒˆ์™ธ๋กœ @JsonProperty, @JsonAutoDetect ๋“ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœํผํ‹ฐ ๊ธฐ๋ฐ˜ ๊ฐ์ฒด or ์ƒ์„ฑ์ž๋ฅผ ์œ„์ž„ํ•œ ๊ฒฝ์šฐ๋ผ๋ฉด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค!

๋.


Uploaded by N2T