Upgrade to Pro — share decks privately, control downloads, hide ads and more …

추가 배포 없이 API의 case 통일시키기

kakao
November 01, 2024

추가 배포 없이 API의 case 통일시키기

#Spring #MSA #JAVA #DTO #Parsing

운영이력이 쌓이면서 DTO들의 case들이 다양해진 상황에서, 개발 효율을 높이기 위해 case를 snake case로 일원화하기 위한 과정을 소개합니다. 24/7 상용환경과 MSA로 구성된 서버환경에서 Jackson 라이브러리를 활용해 서비스 중단없이 변경하려 고군분투한 일화입니다.

발표자 : luis.kim
카카오톡 마이구독에서 백엔드 개발을 맡고 있는 루이스입니다.

kakao

November 01, 2024
Tweet

More Decks by kakao

Other Decks in Programming

Transcript

  1.  %50ղࠗী$"4&о׮ܲ%50ܳ ನೣदெঠೡٸդх $"4& .4"  public class OutDTO {

    private InnerDTO innerDto; private String outerField; } @JsonNaming(PropertyNamingStrategies. SnakeCaseStrategy.class) public class InnerDTO { private String fieldOne; private String fieldTwo; }
  2. $"4& .4"  $BMMFS $BMMFF WBQJVTFST WBQJVTFST WBQJVTFST $BMMFS $BMMFF

    WBQJVTFST WBQJVTFST WBQJVTFST $BMMFS $BMMFF WBQJVTFST WBQJVTFST $BMMFFଃীࢲ߸҃ೡ$"4&੄"1*ܳ ࢜۽਍ߡ੹ਵ۽ߓನ $BMMFSଃীࢲߡ੹߸҃ೠ"1*ߓನ $BMMFFଃ੄੉੹ߡ੹"1*࢏ઁ
  3. $"4& .4"  $BMMFS $BMMFF WBQJVTFST WBQJVTFST WBQJVTFST $BMMFS $BMMFF

    WBQJVTFST WBQJVTFST WBQJVTFST $BMMFS $BMMFF WBQJVTFST WBQJVTFST $BMMFFଃীࢲ߸҃ೡ$"4&੄"1*ܳ ࢜۽਍ߡ੹ਵ۽ߓನ $BMMFSଃীࢲߡ੹߸҃ೠ"1*ߓನ $BMMFFଃ੄੉੹ߡ੹"1*࢏ઁ  ߓನࣽࢲী੄ઓࢿ੉ࢤӣ  ߣױ҅ܳ੕ӝए਑
  4. 3FRVFTU୊ܻ %JTQBUDIFS4FSWMFUDMBTT protected void doDispatch( HttpServletRequest request, HttpServletResponse response )

    throws Exception { ... if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } mv = ha.handle( processedRequest, response, mappedHandler.getHandler() ); ... }
  5.  4QSJOH.7$ীࢲࢎਊೡࣻ੓ח 3FRVFTU#PEZ"EWJDFܳࢎਊ  $POUSPMMFSܳ଺ই 3FRVFTU#PEZী7BMVFܳݗೝೞ ӝ૒੹੸ਊؽ 3FRVFTU୊ܻ public interface

    RequestBodyAdvice { boolean supports(…); HttpInputMessage beforeBodyRead(…); Object afterBodyRead(…); … }
  6.  ୊਺दبद4OBLF߸ജदبറ$BNFM ۽߸ജ  6OLOPXO1SPQFSUZࢸ੿ٸޙীपಁ ౸ױ੉ݽഐ೧ࢲತӝ 3FRVFTU୊ܻ try { snakeToCamel(request);

    return new ModifiedHttpInputMessage(inputMessage, request); } catch (Exception e) { camelToSnake(request); } try { return new ModifiedHttpInputMessage(inputMessage, request); } catch (Exception e) { log.error(e, e.getMessage()); }
  7.  4OBLF߸ജदݽف!+TPO/BNJOH1SPQFSUZ  !+TPO1SPQFSUZࢎਊ  ੉ܳ੉ਊ೧5BSHFU$MBTTীفо૑੄য֢ప੉࣌੉ ઓ੤ೞח૑ܳࠁҊ4OBLF߸҃ৈࠗܳ౸ױ 3FRVFTU୊ܻ @JsonNaming(PropertyNamingStrategies .SnakeCaseStrategy.class)

    public class InnerDTO { private String fieldOne; private String fieldTwo; } public class InnerDTO { @JsonProperty("field_one") private String fieldOne; @JsonProperty("field_two") private String fieldTwo; }
  8.  +TPO੄,FZ݅߸҃೧ঠೣ  +TPO/PEF۽LFZ݅DPOWFSUоמ 3FRVFTU୊ܻ private static JsonNode convertObjectKeys( ObjectNode

    objectNode, String type ) { var newNode = objectMapper.createObjectNode(); objectNode.fields().forEachRemaining((entry) -> { String convertedKey = camelToSnake(entry.getKey()) var value = convertKeys(entry.getValue(), type); newNode.set(convertedKey, value); }); return newNode; }
  9.  (FOFSJDੌ҃਋߹ب୊ܻ೙ਃ  о੢উଃ௿ېझӝળਵ۽ࢸ੿  ఋੑਸفѐ੉࢚о૓ઁցܼੌ ҃਋ઁ৻ 3FRVFTU୊ܻ while (type

    instanceof ParameterizedType) { var generics = ((ParameterizedType)type).getActualTypeArguments(); if (generics.length > 1) { return new ModifiedHttpInputMessage( inputMessage, origin); } type = generics[0]; } List<OuterDto<InnerDto>> Map<FirstDto, SecondDto>
  10.  8SBQQFSDMBTTղࠗ੄ఋੑਸঌই ղӝਤ೧ࢲח $POUFYUVBM%FTFSJBMJ[FSҳഅ೙ਃ  ੉ۧѱঌইմఋੑਸݯߡ߸ࣻ۽о૑ Ҋ੓যঠ߸ജ۽૒ীࢲࢎਊоמ 3FTQPOTF୊ܻ public class

    CustomDeserializer extends JsonDeserializer<ApiResponse<?>> implements ContextualDeserializer { private JavaType javaType; public CustomDeserializer(JavaType javaType) { this.javaType = javaType; }
  11. 3FTQPOTF୊ܻ  +BDLTPOղࠗীࢲEFTFSJBMJ[FSח NBQীநयറ੤ࢎਊ  زदীৈ۞ࣁ࣌੉੽Ӕೡ҃਋ݯߡ߸ ࣻ۽ࢶ঱ػUZQF੉҅ࣘ߄Շӝٸޙ ী౵यী۞ߊࢤ @Override public

    JsonDeserializer<?> createContextual( DeserializationContext ctxt, BeanProperty property ) { var type = property != null? property.getType() : ctxt.getContextualType(); // return this; ੌ ҃਋ error ߊࢤ return new CustomDeserializer(type); }
  12.  PWFSSJEFػEFTFSJBMJ[Fݫࣗ٘ࢎਊ  3FRVFTU#PEZ"EWJDFী੸ਊೠ۽૒ ਸӒ؀۽੸ਊ ↟ ӝળ(FOFSJD୶୹ ↟ .BQఋੑઁ৻ ↟

    য֢ప੉࣌ਸాೠ$BTF౸ױ 3FTQPOTF୊ܻ @Override public ApiResponse<?> deserialize( JsonParser p, DeserializationContext ctxt ) throws IOException { // target class ഋకী ݏب۾ case ੹ജ
  13.  ୨ѐо۝੄%50ܳࢲ࠺झ઺ױহ੉ੌਗച  $"4&৉द೐۽ష௒੄ੌࠗ۽ࠂ੟ࢿਸ֫੉חਗੋ੉ؼࣻ੓਺  ౱ղ௏٬ஶ߮࣌੉੓঻ӝী੘সоמ  ੿ӏ಴അधਸ੉ਊ೧DBTF߸҃ਸ೮ӝٸޙীं੗ա৔য؀ޙ੗оٜযр҃਋੄ب஖ঋ਷Ѿҗоաৢࣻ੓਺  /BNJOHܙ

    8SBQQFSDMBTTܙ੉੓যࢲоמ  ౱ղ௏٬ஶ߮࣌਷ए਍߸҃ਸਤೠӝୡ ઁೠࢎ೦  ߸ജҗ੿ীࢲSF fl FDUJPOਸࢎਊೞӝٸޙীࣘبী޹хೠࢲ࠺झۄݶ੸ਊҊ۰೙ਃ  4QSJOH8FC fl VY੸ਊࠛо Ѿۿ
  14. 2"